aspeed_hace.c (11740B)
1/* 2 * ASPEED Hash and Crypto Engine 3 * 4 * Copyright (C) 2021 IBM Corp. 5 * 6 * Joel Stanley <joel@jms.id.au> 7 * 8 * SPDX-License-Identifier: GPL-2.0-or-later 9 */ 10 11#include "qemu/osdep.h" 12#include "qemu/log.h" 13#include "qemu/error-report.h" 14#include "hw/misc/aspeed_hace.h" 15#include "qapi/error.h" 16#include "migration/vmstate.h" 17#include "crypto/hash.h" 18#include "hw/qdev-properties.h" 19#include "hw/irq.h" 20 21#define R_CRYPT_CMD (0x10 / 4) 22 23#define R_STATUS (0x1c / 4) 24#define HASH_IRQ BIT(9) 25#define CRYPT_IRQ BIT(12) 26#define TAG_IRQ BIT(15) 27 28#define R_HASH_SRC (0x20 / 4) 29#define R_HASH_DEST (0x24 / 4) 30#define R_HASH_SRC_LEN (0x2c / 4) 31 32#define R_HASH_CMD (0x30 / 4) 33/* Hash algorithm selection */ 34#define HASH_ALGO_MASK (BIT(4) | BIT(5) | BIT(6)) 35#define HASH_ALGO_MD5 0 36#define HASH_ALGO_SHA1 BIT(5) 37#define HASH_ALGO_SHA224 BIT(6) 38#define HASH_ALGO_SHA256 (BIT(4) | BIT(6)) 39#define HASH_ALGO_SHA512_SERIES (BIT(5) | BIT(6)) 40/* SHA512 algorithm selection */ 41#define SHA512_HASH_ALGO_MASK (BIT(10) | BIT(11) | BIT(12)) 42#define HASH_ALGO_SHA512_SHA512 0 43#define HASH_ALGO_SHA512_SHA384 BIT(10) 44#define HASH_ALGO_SHA512_SHA256 BIT(11) 45#define HASH_ALGO_SHA512_SHA224 (BIT(10) | BIT(11)) 46/* HMAC modes */ 47#define HASH_HMAC_MASK (BIT(7) | BIT(8)) 48#define HASH_DIGEST 0 49#define HASH_DIGEST_HMAC BIT(7) 50#define HASH_DIGEST_ACCUM BIT(8) 51#define HASH_HMAC_KEY (BIT(7) | BIT(8)) 52/* Cascaded operation modes */ 53#define HASH_ONLY 0 54#define HASH_ONLY2 BIT(0) 55#define HASH_CRYPT_THEN_HASH BIT(1) 56#define HASH_HASH_THEN_CRYPT (BIT(0) | BIT(1)) 57/* Other cmd bits */ 58#define HASH_IRQ_EN BIT(9) 59#define HASH_SG_EN BIT(18) 60/* Scatter-gather data list */ 61#define SG_LIST_LEN_SIZE 4 62#define SG_LIST_LEN_MASK 0x0FFFFFFF 63#define SG_LIST_LEN_LAST BIT(31) 64#define SG_LIST_ADDR_SIZE 4 65#define SG_LIST_ADDR_MASK 0x7FFFFFFF 66#define SG_LIST_ENTRY_SIZE (SG_LIST_LEN_SIZE + SG_LIST_ADDR_SIZE) 67#define ASPEED_HACE_MAX_SG 256 /* max number of entries */ 68 69static const struct { 70 uint32_t mask; 71 QCryptoHashAlgorithm algo; 72} hash_algo_map[] = { 73 { HASH_ALGO_MD5, QCRYPTO_HASH_ALG_MD5 }, 74 { HASH_ALGO_SHA1, QCRYPTO_HASH_ALG_SHA1 }, 75 { HASH_ALGO_SHA224, QCRYPTO_HASH_ALG_SHA224 }, 76 { HASH_ALGO_SHA256, QCRYPTO_HASH_ALG_SHA256 }, 77 { HASH_ALGO_SHA512_SERIES | HASH_ALGO_SHA512_SHA512, QCRYPTO_HASH_ALG_SHA512 }, 78 { HASH_ALGO_SHA512_SERIES | HASH_ALGO_SHA512_SHA384, QCRYPTO_HASH_ALG_SHA384 }, 79 { HASH_ALGO_SHA512_SERIES | HASH_ALGO_SHA512_SHA256, QCRYPTO_HASH_ALG_SHA256 }, 80}; 81 82static int hash_algo_lookup(uint32_t reg) 83{ 84 int i; 85 86 reg &= HASH_ALGO_MASK | SHA512_HASH_ALGO_MASK; 87 88 for (i = 0; i < ARRAY_SIZE(hash_algo_map); i++) { 89 if (reg == hash_algo_map[i].mask) { 90 return hash_algo_map[i].algo; 91 } 92 } 93 94 return -1; 95} 96 97static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode) 98{ 99 struct iovec iov[ASPEED_HACE_MAX_SG]; 100 g_autofree uint8_t *digest_buf; 101 size_t digest_len = 0; 102 int i; 103 104 if (sg_mode) { 105 uint32_t len = 0; 106 107 for (i = 0; !(len & SG_LIST_LEN_LAST); i++) { 108 uint32_t addr, src; 109 hwaddr plen; 110 111 if (i == ASPEED_HACE_MAX_SG) { 112 qemu_log_mask(LOG_GUEST_ERROR, 113 "aspeed_hace: guest failed to set end of sg list marker\n"); 114 break; 115 } 116 117 src = s->regs[R_HASH_SRC] + (i * SG_LIST_ENTRY_SIZE); 118 119 len = address_space_ldl_le(&s->dram_as, src, 120 MEMTXATTRS_UNSPECIFIED, NULL); 121 122 addr = address_space_ldl_le(&s->dram_as, src + SG_LIST_LEN_SIZE, 123 MEMTXATTRS_UNSPECIFIED, NULL); 124 addr &= SG_LIST_ADDR_MASK; 125 126 iov[i].iov_len = len & SG_LIST_LEN_MASK; 127 plen = iov[i].iov_len; 128 iov[i].iov_base = address_space_map(&s->dram_as, addr, &plen, false, 129 MEMTXATTRS_UNSPECIFIED); 130 } 131 } else { 132 hwaddr len = s->regs[R_HASH_SRC_LEN]; 133 134 iov[0].iov_len = len; 135 iov[0].iov_base = address_space_map(&s->dram_as, s->regs[R_HASH_SRC], 136 &len, false, 137 MEMTXATTRS_UNSPECIFIED); 138 i = 1; 139 } 140 141 if (qcrypto_hash_bytesv(algo, iov, i, &digest_buf, &digest_len, NULL) < 0) { 142 qemu_log_mask(LOG_GUEST_ERROR, "%s: qcrypto failed\n", __func__); 143 return; 144 } 145 146 if (address_space_write(&s->dram_as, s->regs[R_HASH_DEST], 147 MEMTXATTRS_UNSPECIFIED, 148 digest_buf, digest_len)) { 149 qemu_log_mask(LOG_GUEST_ERROR, 150 "aspeed_hace: address space write failed\n"); 151 } 152 153 for (; i > 0; i--) { 154 address_space_unmap(&s->dram_as, iov[i - 1].iov_base, 155 iov[i - 1].iov_len, false, 156 iov[i - 1].iov_len); 157 } 158 159 /* 160 * Set status bits to indicate completion. Testing shows hardware sets 161 * these irrespective of HASH_IRQ_EN. 162 */ 163 s->regs[R_STATUS] |= HASH_IRQ; 164} 165 166static uint64_t aspeed_hace_read(void *opaque, hwaddr addr, unsigned int size) 167{ 168 AspeedHACEState *s = ASPEED_HACE(opaque); 169 170 addr >>= 2; 171 172 if (addr >= ASPEED_HACE_NR_REGS) { 173 qemu_log_mask(LOG_GUEST_ERROR, 174 "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n", 175 __func__, addr << 2); 176 return 0; 177 } 178 179 return s->regs[addr]; 180} 181 182static void aspeed_hace_write(void *opaque, hwaddr addr, uint64_t data, 183 unsigned int size) 184{ 185 AspeedHACEState *s = ASPEED_HACE(opaque); 186 AspeedHACEClass *ahc = ASPEED_HACE_GET_CLASS(s); 187 188 addr >>= 2; 189 190 if (addr >= ASPEED_HACE_NR_REGS) { 191 qemu_log_mask(LOG_GUEST_ERROR, 192 "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n", 193 __func__, addr << 2); 194 return; 195 } 196 197 switch (addr) { 198 case R_STATUS: 199 if (data & HASH_IRQ) { 200 data &= ~HASH_IRQ; 201 202 if (s->regs[addr] & HASH_IRQ) { 203 qemu_irq_lower(s->irq); 204 } 205 } 206 break; 207 case R_HASH_SRC: 208 data &= ahc->src_mask; 209 break; 210 case R_HASH_DEST: 211 data &= ahc->dest_mask; 212 break; 213 case R_HASH_SRC_LEN: 214 data &= 0x0FFFFFFF; 215 break; 216 case R_HASH_CMD: { 217 int algo; 218 data &= ahc->hash_mask; 219 220 if ((data & HASH_HMAC_MASK)) { 221 qemu_log_mask(LOG_UNIMP, 222 "%s: HMAC engine command mode %"PRIx64" not implemented", 223 __func__, (data & HASH_HMAC_MASK) >> 8); 224 } 225 if (data & BIT(1)) { 226 qemu_log_mask(LOG_UNIMP, 227 "%s: Cascaded mode not implemented", 228 __func__); 229 } 230 algo = hash_algo_lookup(data); 231 if (algo < 0) { 232 qemu_log_mask(LOG_GUEST_ERROR, 233 "%s: Invalid hash algorithm selection 0x%"PRIx64"\n", 234 __func__, data & ahc->hash_mask); 235 break; 236 } 237 do_hash_operation(s, algo, data & HASH_SG_EN); 238 239 if (data & HASH_IRQ_EN) { 240 qemu_irq_raise(s->irq); 241 } 242 break; 243 } 244 case R_CRYPT_CMD: 245 qemu_log_mask(LOG_UNIMP, "%s: Crypt commands not implemented\n", 246 __func__); 247 break; 248 default: 249 break; 250 } 251 252 s->regs[addr] = data; 253} 254 255static const MemoryRegionOps aspeed_hace_ops = { 256 .read = aspeed_hace_read, 257 .write = aspeed_hace_write, 258 .endianness = DEVICE_LITTLE_ENDIAN, 259 .valid = { 260 .min_access_size = 1, 261 .max_access_size = 4, 262 }, 263}; 264 265static void aspeed_hace_reset(DeviceState *dev) 266{ 267 struct AspeedHACEState *s = ASPEED_HACE(dev); 268 269 memset(s->regs, 0, sizeof(s->regs)); 270} 271 272static void aspeed_hace_realize(DeviceState *dev, Error **errp) 273{ 274 AspeedHACEState *s = ASPEED_HACE(dev); 275 SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 276 277 sysbus_init_irq(sbd, &s->irq); 278 279 memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_hace_ops, s, 280 TYPE_ASPEED_HACE, 0x1000); 281 282 if (!s->dram_mr) { 283 error_setg(errp, TYPE_ASPEED_HACE ": 'dram' link not set"); 284 return; 285 } 286 287 address_space_init(&s->dram_as, s->dram_mr, "dram"); 288 289 sysbus_init_mmio(sbd, &s->iomem); 290} 291 292static Property aspeed_hace_properties[] = { 293 DEFINE_PROP_LINK("dram", AspeedHACEState, dram_mr, 294 TYPE_MEMORY_REGION, MemoryRegion *), 295 DEFINE_PROP_END_OF_LIST(), 296}; 297 298 299static const VMStateDescription vmstate_aspeed_hace = { 300 .name = TYPE_ASPEED_HACE, 301 .version_id = 1, 302 .minimum_version_id = 1, 303 .fields = (VMStateField[]) { 304 VMSTATE_UINT32_ARRAY(regs, AspeedHACEState, ASPEED_HACE_NR_REGS), 305 VMSTATE_END_OF_LIST(), 306 } 307}; 308 309static void aspeed_hace_class_init(ObjectClass *klass, void *data) 310{ 311 DeviceClass *dc = DEVICE_CLASS(klass); 312 313 dc->realize = aspeed_hace_realize; 314 dc->reset = aspeed_hace_reset; 315 device_class_set_props(dc, aspeed_hace_properties); 316 dc->vmsd = &vmstate_aspeed_hace; 317} 318 319static const TypeInfo aspeed_hace_info = { 320 .name = TYPE_ASPEED_HACE, 321 .parent = TYPE_SYS_BUS_DEVICE, 322 .instance_size = sizeof(AspeedHACEState), 323 .class_init = aspeed_hace_class_init, 324 .class_size = sizeof(AspeedHACEClass) 325}; 326 327static void aspeed_ast2400_hace_class_init(ObjectClass *klass, void *data) 328{ 329 DeviceClass *dc = DEVICE_CLASS(klass); 330 AspeedHACEClass *ahc = ASPEED_HACE_CLASS(klass); 331 332 dc->desc = "AST2400 Hash and Crypto Engine"; 333 334 ahc->src_mask = 0x0FFFFFFF; 335 ahc->dest_mask = 0x0FFFFFF8; 336 ahc->hash_mask = 0x000003ff; /* No SG or SHA512 modes */ 337} 338 339static const TypeInfo aspeed_ast2400_hace_info = { 340 .name = TYPE_ASPEED_AST2400_HACE, 341 .parent = TYPE_ASPEED_HACE, 342 .class_init = aspeed_ast2400_hace_class_init, 343}; 344 345static void aspeed_ast2500_hace_class_init(ObjectClass *klass, void *data) 346{ 347 DeviceClass *dc = DEVICE_CLASS(klass); 348 AspeedHACEClass *ahc = ASPEED_HACE_CLASS(klass); 349 350 dc->desc = "AST2500 Hash and Crypto Engine"; 351 352 ahc->src_mask = 0x3fffffff; 353 ahc->dest_mask = 0x3ffffff8; 354 ahc->hash_mask = 0x000003ff; /* No SG or SHA512 modes */ 355} 356 357static const TypeInfo aspeed_ast2500_hace_info = { 358 .name = TYPE_ASPEED_AST2500_HACE, 359 .parent = TYPE_ASPEED_HACE, 360 .class_init = aspeed_ast2500_hace_class_init, 361}; 362 363static void aspeed_ast2600_hace_class_init(ObjectClass *klass, void *data) 364{ 365 DeviceClass *dc = DEVICE_CLASS(klass); 366 AspeedHACEClass *ahc = ASPEED_HACE_CLASS(klass); 367 368 dc->desc = "AST2600 Hash and Crypto Engine"; 369 370 ahc->src_mask = 0x7FFFFFFF; 371 ahc->dest_mask = 0x7FFFFFF8; 372 ahc->hash_mask = 0x00147FFF; 373} 374 375static const TypeInfo aspeed_ast2600_hace_info = { 376 .name = TYPE_ASPEED_AST2600_HACE, 377 .parent = TYPE_ASPEED_HACE, 378 .class_init = aspeed_ast2600_hace_class_init, 379}; 380 381static void aspeed_hace_register_types(void) 382{ 383 type_register_static(&aspeed_ast2400_hace_info); 384 type_register_static(&aspeed_ast2500_hace_info); 385 type_register_static(&aspeed_ast2600_hace_info); 386 type_register_static(&aspeed_hace_info); 387} 388 389type_init(aspeed_hace_register_types);