github.com/usbarmory/tamago@v0.0.0-20240508072735-8612bbe1e454/soc/nxp/dcp/dcp.go (about)

     1  // NXP Data Co-Processor (DCP) driver
     2  // https://github.com/usbarmory/tamago
     3  //
     4  // Copyright (c) WithSecure Corporation
     5  // https://foundry.withsecure.com
     6  //
     7  // Use of this source code is governed by the license
     8  // that can be found in the LICENSE file.
     9  
    10  // Package dcp implements a driver for the NXP Data Co-Processor (DCP)
    11  // cryptographic accelerator adopting the following reference specifications:
    12  //   - MCIMX28RM - i.MX28 Applications Processor Reference Manual - Rev 2 2013/08
    13  //
    14  // This package is only meant to be used with `GOOS=tamago GOARCH=arm` as
    15  // supported by the TamaGo framework for bare metal Go on ARM SoCs, see
    16  // https://github.com/usbarmory/tamago.
    17  package dcp
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/binary"
    22  	"errors"
    23  	"fmt"
    24  	"sync"
    25  
    26  	"github.com/usbarmory/tamago/bits"
    27  	"github.com/usbarmory/tamago/dma"
    28  	"github.com/usbarmory/tamago/internal/reg"
    29  )
    30  
    31  // DCP registers
    32  const (
    33  	DCP_CTRL     = 0x00
    34  	CTRL_SFTRST  = 31
    35  	CTRL_CLKGATE = 30
    36  
    37  	DCP_STAT     = 0x10
    38  	DCP_STAT_CLR = 0x18
    39  	DCP_STAT_IRQ = 0
    40  
    41  	DCP_CHANNELCTRL = 0x0020
    42  
    43  	DCP_KEY     = 0x0060
    44  	KEY_INDEX   = 4
    45  	KEY_SUBWORD = 0
    46  
    47  	DCP_KEYDATA   = 0x0070
    48  	DCP_CH0CMDPTR = 0x0100
    49  	DCP_CH0SEMA   = 0x0110
    50  
    51  	DCP_CH0STAT        = 0x0120
    52  	CHxSTAT_ERROR_CODE = 16
    53  	CHxSTAT_ERROR_MASK = 0b1111110
    54  
    55  	DCP_CH0STAT_CLR = 0x0128
    56  )
    57  
    58  // DCP channels
    59  const (
    60  	DCP_CHANNEL_0 = iota + 1
    61  	DCP_CHANNEL_1
    62  	DCP_CHANNEL_2
    63  	DCP_CHANNEL_3
    64  )
    65  
    66  // DCP control packet settings
    67  const (
    68  	// p1068, 13.2.6.4.2 Control0 Field, MCIMX28RM
    69  
    70  	DCP_CTRL0_HASH_TERM       = 13
    71  	DCP_CTRL0_HASH_INIT       = 12
    72  	DCP_CTRL0_OTP_KEY         = 10
    73  	DCP_CTRL0_CIPHER_INIT     = 9
    74  	DCP_CTRL0_CIPHER_ENCRYPT  = 8
    75  	DCP_CTRL0_ENABLE_HASH     = 6
    76  	DCP_CTRL0_ENABLE_CIPHER   = 5
    77  	DCP_CTRL0_CHAIN           = 2
    78  	DCP_CTRL0_DECR_SEMAPHORE  = 1
    79  	DCP_CTRL0_INTERRUPT_ENABL = 0
    80  
    81  	// p1070, 13.2.6.4.3 Control1 Field, MCIMX28RM
    82  	// p1098, 13.3.11 DCP_PACKET2 field descriptions, MCIMX28RM
    83  
    84  	DCP_CTRL1_HASH_SELECT = 16
    85  	HASH_SELECT_SHA1      = 0x00
    86  	HASH_SELECT_CRC32     = 0x01
    87  	HASH_SELECT_SHA256    = 0x02
    88  
    89  	DCP_CTRL1_KEY_SELECT  = 8
    90  	KEY_SELECT_UNIQUE_KEY = 0xfe
    91  
    92  	DCP_CTRL1_CIPHER_MODE = 4
    93  	CIPHER_MODE_CBC       = 0x01
    94  
    95  	DCP_CTRL1_CIPHER_SELECT = 0
    96  	CIPHER_SELECT_AES128    = 0x00
    97  )
    98  
    99  const WorkPacketLength = 32
   100  
   101  // WorkPacket represents a DCP work packet
   102  // (p1067, 13.2.6.4 Work Packet Structure, MCIMX28RM).
   103  type WorkPacket struct {
   104  	NextCmdAddr              uint32
   105  	Control0                 uint32
   106  	Control1                 uint32
   107  	SourceBufferAddress      uint32
   108  	DestinationBufferAddress uint32
   109  	BufferSize               uint32
   110  	PayloadPointer           uint32
   111  	Status                   uint32
   112  }
   113  
   114  // DCP represents the Data Co-Processor instance.
   115  type DCP struct {
   116  	sync.Mutex
   117  
   118  	// Base register
   119  	Base uint32
   120  	// Clock gate register
   121  	CCGR uint32
   122  	// Clock gate
   123  	CG int
   124  
   125  	// DeriveKeyMemory represents the DMA memory region used for exchanging DCP
   126  	// derived keys when the derivation index points to an internal DCP key RAM
   127  	// slot. The memory region must be initialized before DeriveKey().
   128  	//
   129  	// It is recommended to use a DMA region within the internal RAM (e.g.
   130  	// i.MX6 On-Chip OCRAM/iRAM) to avoid exposure to external RAM.
   131  	//
   132  	// The DeriveKey() function uses DeriveKeyMemory only if the default
   133  	// DMA region start does not overlap with it.
   134  	DeriveKeyMemory *dma.Region
   135  
   136  	// control registers
   137  	ctrl        uint32
   138  	stat        uint32
   139  	stat_clr    uint32
   140  	chctrl      uint32
   141  	key         uint32
   142  	keydata     uint32
   143  	ch0cmdptr   uint32
   144  	ch0sema     uint32
   145  	ch0stat     uint32
   146  	ch0stat_clr uint32
   147  }
   148  
   149  // Bytes converts the DCP work packet structure to byte array format.
   150  func (pkt *WorkPacket) Bytes() []byte {
   151  	buf := new(bytes.Buffer)
   152  	binary.Write(buf, binary.LittleEndian, pkt)
   153  	return buf.Bytes()
   154  }
   155  
   156  // Init initializes the DCP module.
   157  func (hw *DCP) Init() {
   158  	hw.Lock()
   159  	defer hw.Unlock()
   160  
   161  	if hw.Base == 0 || hw.CCGR == 0 {
   162  		panic("invalid DCP instance")
   163  	}
   164  
   165  	hw.ctrl = hw.Base + DCP_CTRL
   166  	hw.stat = hw.Base + DCP_STAT
   167  	hw.stat_clr = hw.Base + DCP_STAT_CLR
   168  	hw.chctrl = hw.Base + DCP_CHANNELCTRL
   169  	hw.key = hw.Base + DCP_KEY
   170  	hw.keydata = hw.Base + DCP_KEYDATA
   171  	hw.ch0cmdptr = hw.Base + DCP_CH0CMDPTR
   172  	hw.ch0sema = hw.Base + DCP_CH0SEMA
   173  	hw.ch0stat = hw.Base + DCP_CH0STAT
   174  	hw.ch0stat_clr = hw.Base + DCP_CH0STAT_CLR
   175  
   176  	// enable clock
   177  	reg.SetN(hw.CCGR, hw.CG, 0b11, 0b11)
   178  
   179  	// soft reset DCP
   180  	reg.Set(hw.ctrl, CTRL_SFTRST)
   181  	reg.Clear(hw.ctrl, CTRL_SFTRST)
   182  
   183  	// enable DCP
   184  	reg.Clear(hw.ctrl, CTRL_CLKGATE)
   185  
   186  	// enable channel 0
   187  	reg.Write(hw.chctrl, DCP_CHANNEL_0)
   188  }
   189  
   190  func (hw *DCP) cmd(ptr uint, count int) (err error) {
   191  	hw.Lock()
   192  	defer hw.Unlock()
   193  
   194  	if reg.Read(hw.chctrl) != DCP_CHANNEL_0 {
   195  		return errors.New("co-processor is not initialized")
   196  	}
   197  
   198  	// clear channel status
   199  	reg.Write(hw.ch0stat_clr, 0xffffffff)
   200  
   201  	// set command address
   202  	reg.Write(hw.ch0cmdptr, uint32(ptr))
   203  	// activate channel
   204  	reg.SetN(hw.ch0sema, 0, 0xff, uint32(count))
   205  	// wait for completion
   206  	reg.Wait(hw.stat, DCP_STAT_IRQ, DCP_CHANNEL_0, 1)
   207  	// clear interrupt register
   208  	reg.Set(hw.stat_clr, DCP_CHANNEL_0)
   209  
   210  	chstatus := reg.Read(hw.ch0stat)
   211  
   212  	// check for errors
   213  	if bits.Get(&chstatus, 0, CHxSTAT_ERROR_MASK) != 0 {
   214  		code := bits.Get(&chstatus, CHxSTAT_ERROR_CODE, 0xff)
   215  		sema := reg.Read(hw.ch0sema)
   216  		return fmt.Errorf("DCP channel 0 error, status:%#x error_code:%#x sema:%#x", chstatus, code, sema)
   217  	}
   218  
   219  	return
   220  }