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 }