1 /* 2 * QEMU PowerNV PNOR simple model 3 * 4 * Copyright (c) 2015-2019, IBM Corporation. 5 * 6 * This code is licensed under the GPL version 2 or later. See the 7 * COPYING file in the top-level directory. 8 */ 9 10 #include "qemu/osdep.h" 11 #include "qapi/error.h" 12 #include "qemu/error-report.h" 13 #include "qemu/log.h" 14 #include "sysemu/block-backend.h" 15 #include "sysemu/blockdev.h" 16 #include "hw/loader.h" 17 #include "hw/ppc/pnv_pnor.h" 18 #include "hw/qdev-properties.h" 19 20 static uint64_t pnv_pnor_read(void *opaque, hwaddr addr, unsigned size) 21 { 22 PnvPnor *s = PNV_PNOR(opaque); 23 uint64_t ret = 0; 24 int i; 25 26 for (i = 0; i < size; i++) { 27 ret |= (uint64_t) s->storage[addr + i] << (8 * (size - i - 1)); 28 } 29 30 return ret; 31 } 32 33 static void pnv_pnor_update(PnvPnor *s, int offset, int size) 34 { 35 int offset_end; 36 int ret; 37 38 if (s->blk) { 39 return; 40 } 41 42 offset_end = offset + size; 43 offset = QEMU_ALIGN_DOWN(offset, BDRV_SECTOR_SIZE); 44 offset_end = QEMU_ALIGN_UP(offset_end, BDRV_SECTOR_SIZE); 45 46 ret = blk_pwrite(s->blk, offset, s->storage + offset, 47 offset_end - offset, 0); 48 if (ret < 0) { 49 error_report("Could not update PNOR: %s", strerror(-ret)); 50 } 51 } 52 53 static void pnv_pnor_write(void *opaque, hwaddr addr, uint64_t data, 54 unsigned size) 55 { 56 PnvPnor *s = PNV_PNOR(opaque); 57 int i; 58 59 for (i = 0; i < size; i++) { 60 s->storage[addr + i] = (data >> (8 * (size - i - 1))) & 0xFF; 61 } 62 pnv_pnor_update(s, addr, size); 63 } 64 65 /* 66 * TODO: Check endianness: skiboot is BIG, Aspeed AHB is LITTLE, flash 67 * is BIG. 68 */ 69 static const MemoryRegionOps pnv_pnor_ops = { 70 .read = pnv_pnor_read, 71 .write = pnv_pnor_write, 72 .endianness = DEVICE_BIG_ENDIAN, 73 .valid = { 74 .min_access_size = 1, 75 .max_access_size = 4, 76 }, 77 }; 78 79 static void pnv_pnor_realize(DeviceState *dev, Error **errp) 80 { 81 PnvPnor *s = PNV_PNOR(dev); 82 int ret; 83 84 if (s->blk) { 85 uint64_t perm = BLK_PERM_CONSISTENT_READ | 86 (blk_is_read_only(s->blk) ? 0 : BLK_PERM_WRITE); 87 ret = blk_set_perm(s->blk, perm, BLK_PERM_ALL, errp); 88 if (ret < 0) { 89 return; 90 } 91 92 s->size = blk_getlength(s->blk); 93 if (s->size <= 0) { 94 error_setg(errp, "failed to get flash size"); 95 return; 96 } 97 98 s->storage = blk_blockalign(s->blk, s->size); 99 100 if (blk_pread(s->blk, 0, s->storage, s->size) != s->size) { 101 error_setg(errp, "failed to read the initial flash content"); 102 return; 103 } 104 } else { 105 s->storage = blk_blockalign(NULL, s->size); 106 memset(s->storage, 0xFF, s->size); 107 } 108 109 memory_region_init_io(&s->mmio, OBJECT(s), &pnv_pnor_ops, s, 110 TYPE_PNV_PNOR, s->size); 111 } 112 113 static Property pnv_pnor_properties[] = { 114 DEFINE_PROP_INT64("size", PnvPnor, size, 128 << 20), 115 DEFINE_PROP_DRIVE("drive", PnvPnor, blk), 116 DEFINE_PROP_END_OF_LIST(), 117 }; 118 119 static void pnv_pnor_class_init(ObjectClass *klass, void *data) 120 { 121 DeviceClass *dc = DEVICE_CLASS(klass); 122 123 dc->realize = pnv_pnor_realize; 124 dc->props = pnv_pnor_properties; 125 } 126 127 static const TypeInfo pnv_pnor_info = { 128 .name = TYPE_PNV_PNOR, 129 .parent = TYPE_SYS_BUS_DEVICE, 130 .instance_size = sizeof(PnvPnor), 131 .class_init = pnv_pnor_class_init, 132 }; 133 134 static void pnv_pnor_register_types(void) 135 { 136 type_register_static(&pnv_pnor_info); 137 } 138 139 type_init(pnv_pnor_register_types) 140