The Aspeed SoC are generally booted from one of the flash modules behind the FMC controller. The FMC CS0 flash module is mapped at a specific address depending on the SoC revision and also at 0x0, the default boot-up address.
To support this second mapping, we add a new 'ROM' like memory region under the FMC flash module model and activate support for MMIO execution with a 'request_ptr' handler. The latter fills up a cache of flash content to be executed or read by the boot up process. Also add a 'mmio_exec' bool to activate the feature which still has some issues. Signed-off-by: Cédric Le Goater <c...@kaod.org> --- include/hw/ssi/aspeed_smc.h | 7 +++ hw/ssi/aspeed_smc.c | 122 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 128 insertions(+), 1 deletion(-) diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h index 1f557313fa93..5e853afe725d 100644 --- a/include/hw/ssi/aspeed_smc.h +++ b/include/hw/ssi/aspeed_smc.h @@ -48,6 +48,8 @@ typedef struct AspeedSMCController { uint32_t nregs; } AspeedSMCController; +#define ASPEED_SMC_CACHE_SIZE 1024 /* 1K is the minimum */ + typedef struct AspeedSMCFlash { struct AspeedSMCState *controller; @@ -56,6 +58,10 @@ typedef struct AspeedSMCFlash { MemoryRegion mmio; DeviceState *flash; + + MemoryRegion mmio_rom; + uint8_t cache[ASPEED_SMC_CACHE_SIZE]; + hwaddr cache_addr; } AspeedSMCFlash; #define TYPE_ASPEED_SMC "aspeed.smc" @@ -79,6 +85,7 @@ typedef struct AspeedSMCState { MemoryRegion mmio; MemoryRegion mmio_flash; + bool mmio_exec; qemu_irq irq; int irqline; diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index 5808181568c4..d599eebc7d21 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -604,6 +604,96 @@ static const MemoryRegionOps aspeed_smc_flash_ops = { }, }; +static bool aspeed_smc_flash_rom_is_cached(AspeedSMCFlash *fl, hwaddr addr) +{ + return (addr >= fl->cache_addr && + addr <= fl->cache_addr + ASPEED_SMC_CACHE_SIZE - 4); +} + +static void aspeed_smc_flash_rom_load_cache(AspeedSMCFlash *fl, hwaddr addr) +{ + AspeedSMCState *s = fl->controller; + hwaddr cache_addr = addr & ~(ASPEED_SMC_CACHE_SIZE - 1); + int i; + + if (fl->cache_addr != ~0ULL) { + memory_region_invalidate_mmio_ptr(&fl->mmio_rom, fl->cache_addr, + ASPEED_SMC_CACHE_SIZE); + } + + aspeed_smc_flash_select(fl); + aspeed_smc_flash_setup(fl, cache_addr); + + for (i = 0; i < ASPEED_SMC_CACHE_SIZE; i++) { + fl->cache[i] = ssi_transfer(s->spi, 0x0); + } + + aspeed_smc_flash_unselect(fl); + + fl->cache_addr = cache_addr; +} + +static void *aspeed_smc_flash_rom_request_ptr(void *opaque, hwaddr addr, + unsigned *size, unsigned *offset) +{ + AspeedSMCFlash *fl = opaque; + + if (!aspeed_smc_flash_rom_is_cached(fl, addr)) { + aspeed_smc_flash_rom_load_cache(fl, addr); + } + + *size = ASPEED_SMC_CACHE_SIZE; + *offset = fl->cache_addr; + return fl->cache; +} + +static uint64_t aspeed_smc_flash_rom_read(void *opaque, hwaddr addr, + unsigned size) +{ + AspeedSMCFlash *fl = opaque; + AspeedSMCState *s = fl->controller; + uint64_t ret = 0; + int i; + + /* + * Transfer or use the cache if possible. Reloading the cache + * while loading from the flash can break the TCG execution flow. + */ + if (!aspeed_smc_flash_rom_is_cached(fl, addr)) { + aspeed_smc_flash_select(fl); + aspeed_smc_flash_setup(fl, addr); + + for (i = 0; i < size; i++) { + ret |= (uint64_t) ssi_transfer(s->spi, 0x0) << (8 * i); + } + + aspeed_smc_flash_unselect(fl); + } else { + for (i = 0; i < size; i++) { + ret |= (uint64_t) fl->cache[addr - fl->cache_addr + i] << (8 * i); + } + } + return ret; +} + +static void aspeed_smc_flash_rom_write(void *opaque, hwaddr addr, uint64_t data, + unsigned size) +{ + qemu_log_mask(LOG_GUEST_ERROR, "%s: flash is not writable at 0x%" + HWADDR_PRIx "\n", __func__, addr); +} + +static const MemoryRegionOps aspeed_smc_flash_rom_ops = { + .read = aspeed_smc_flash_rom_read, + .write = aspeed_smc_flash_rom_write, + .request_ptr = aspeed_smc_flash_rom_request_ptr, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + static void aspeed_smc_flash_update_cs(AspeedSMCFlash *fl) { const AspeedSMCState *s = fl->controller; @@ -778,21 +868,51 @@ static void aspeed_smc_realize(DeviceState *dev, Error **errp) fl, name, fl->size); memory_region_add_subregion(&s->mmio_flash, offset, &fl->mmio); offset += fl->size; + + /* + * The system is generally booted from one of the flash + * modules behind the FMC controller. Initialize the 'ROM' + * region which supports MMIO execution and let the board + * decide how to use them. + */ + if ((s->ctrl->segments == aspeed_segments_ast2500_fmc || + s->ctrl->segments == aspeed_segments_fmc) && s->mmio_exec) { + snprintf(name, sizeof(name), "%s.%d-rom", s->ctrl->name, i); + + memory_region_init_io(&fl->mmio_rom, OBJECT(s), + &aspeed_smc_flash_rom_ops, + fl, name, fl->size); + fl->cache_addr = ~0ULL; + } } } +static const VMStateDescription vmstate_aspeed_smc_flash = { + .name = TYPE_ASPEED_SMC "/flash", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField []) { + VMSTATE_UINT8_ARRAY(cache, AspeedSMCFlash, ASPEED_SMC_CACHE_SIZE), + VMSTATE_UINT64(cache_addr, AspeedSMCFlash), + VMSTATE_END_OF_LIST() + }, +}; + static const VMStateDescription vmstate_aspeed_smc = { - .name = "aspeed.smc", + .name = TYPE_ASPEED_SMC, .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, AspeedSMCState, ASPEED_SMC_R_MAX), + VMSTATE_STRUCT_VARRAY_POINTER_UINT32(flashes, AspeedSMCState, num_cs, + vmstate_aspeed_smc_flash, AspeedSMCFlash), VMSTATE_END_OF_LIST() } }; static Property aspeed_smc_properties[] = { DEFINE_PROP_UINT32("num-cs", AspeedSMCState, num_cs, 1), + DEFINE_PROP_BOOL("mmio-exec", AspeedSMCState, mmio_exec, false), DEFINE_PROP_END_OF_LIST(), }; -- 2.13.6