pnv_pnor.c (3630B)
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/units.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#include "hw/qdev-properties-system.h" 20 21static uint64_t pnv_pnor_read(void *opaque, hwaddr addr, unsigned size) 22{ 23 PnvPnor *s = PNV_PNOR(opaque); 24 uint64_t ret = 0; 25 int i; 26 27 for (i = 0; i < size; i++) { 28 ret |= (uint64_t) s->storage[addr + i] << (8 * (size - i - 1)); 29 } 30 31 return ret; 32} 33 34static void pnv_pnor_update(PnvPnor *s, int offset, int size) 35{ 36 int offset_end; 37 int ret; 38 39 if (s->blk) { 40 return; 41 } 42 43 offset_end = offset + size; 44 offset = QEMU_ALIGN_DOWN(offset, BDRV_SECTOR_SIZE); 45 offset_end = QEMU_ALIGN_UP(offset_end, BDRV_SECTOR_SIZE); 46 47 ret = blk_pwrite(s->blk, offset, s->storage + offset, 48 offset_end - offset, 0); 49 if (ret < 0) { 50 error_report("Could not update PNOR offset=0x%" PRIx32" : %s", offset, 51 strerror(-ret)); 52 } 53} 54 55static void pnv_pnor_write(void *opaque, hwaddr addr, uint64_t data, 56 unsigned size) 57{ 58 PnvPnor *s = PNV_PNOR(opaque); 59 int i; 60 61 for (i = 0; i < size; i++) { 62 s->storage[addr + i] = (data >> (8 * (size - i - 1))) & 0xFF; 63 } 64 pnv_pnor_update(s, addr, size); 65} 66 67/* 68 * TODO: Check endianness: skiboot is BIG, Aspeed AHB is LITTLE, flash 69 * is BIG. 70 */ 71static const MemoryRegionOps pnv_pnor_ops = { 72 .read = pnv_pnor_read, 73 .write = pnv_pnor_write, 74 .endianness = DEVICE_BIG_ENDIAN, 75 .valid = { 76 .min_access_size = 1, 77 .max_access_size = 4, 78 }, 79}; 80 81static void pnv_pnor_realize(DeviceState *dev, Error **errp) 82{ 83 PnvPnor *s = PNV_PNOR(dev); 84 int ret; 85 86 if (s->blk) { 87 uint64_t perm = BLK_PERM_CONSISTENT_READ | 88 (blk_supports_write_perm(s->blk) ? BLK_PERM_WRITE : 0); 89 ret = blk_set_perm(s->blk, perm, BLK_PERM_ALL, errp); 90 if (ret < 0) { 91 return; 92 } 93 94 s->size = blk_getlength(s->blk); 95 if (s->size <= 0) { 96 error_setg(errp, "failed to get flash size"); 97 return; 98 } 99 100 s->storage = blk_blockalign(s->blk, s->size); 101 102 if (blk_pread(s->blk, 0, s->storage, s->size) != s->size) { 103 error_setg(errp, "failed to read the initial flash content"); 104 return; 105 } 106 } else { 107 s->storage = blk_blockalign(NULL, s->size); 108 memset(s->storage, 0xFF, s->size); 109 } 110 111 memory_region_init_io(&s->mmio, OBJECT(s), &pnv_pnor_ops, s, 112 TYPE_PNV_PNOR, s->size); 113} 114 115static Property pnv_pnor_properties[] = { 116 DEFINE_PROP_INT64("size", PnvPnor, size, 128 * MiB), 117 DEFINE_PROP_DRIVE("drive", PnvPnor, blk), 118 DEFINE_PROP_END_OF_LIST(), 119}; 120 121static void pnv_pnor_class_init(ObjectClass *klass, void *data) 122{ 123 DeviceClass *dc = DEVICE_CLASS(klass); 124 125 dc->realize = pnv_pnor_realize; 126 device_class_set_props(dc, pnv_pnor_properties); 127} 128 129static const TypeInfo pnv_pnor_info = { 130 .name = TYPE_PNV_PNOR, 131 .parent = TYPE_SYS_BUS_DEVICE, 132 .instance_size = sizeof(PnvPnor), 133 .class_init = pnv_pnor_class_init, 134}; 135 136static void pnv_pnor_register_types(void) 137{ 138 type_register_static(&pnv_pnor_info); 139} 140 141type_init(pnv_pnor_register_types)