diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index a2a8b97330..c3ac0d6d5b 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -569,6 +569,7 @@ static void pnv_reset(MachineState *machine) obj = object_resolve_path_type("", "ipmi-bmc-sim", NULL); if (obj) { pnv->bmc = IPMI_BMC(obj); + pnv_bmc_hiomap(pnv->bmc); } fdt = pnv_dt_create(machine); diff --git a/hw/ppc/pnv_bmc.c b/hw/ppc/pnv_bmc.c index dc5e918cb7..aa5c89586c 100644 --- a/hw/ppc/pnv_bmc.c +++ b/hw/ppc/pnv_bmc.c @@ -114,3 +114,105 @@ void pnv_dt_bmc_sensors(IPMIBmc *bmc, void *fdt) sdr->sensor_type))); } } + +/* + * HIOMAP protocol handler + */ +#define HIOMAP_C_RESET 1 +#define HIOMAP_C_GET_INFO 2 +#define HIOMAP_C_GET_FLASH_INFO 3 +#define HIOMAP_C_CREATE_READ_WINDOW 4 +#define HIOMAP_C_CLOSE_WINDOW 5 +#define HIOMAP_C_CREATE_WRITE_WINDOW 6 +#define HIOMAP_C_MARK_DIRTY 7 +#define HIOMAP_C_FLUSH 8 +#define HIOMAP_C_ACK 9 +#define HIOMAP_C_ERASE 10 +#define HIOMAP_C_DEVICE_NAME 11 +#define HIOMAP_C_LOCK 12 + +#define BLOCK_SHIFT 12 /* 4K */ + +static uint16_t bytes_to_blocks(uint32_t bytes) +{ + return bytes >> BLOCK_SHIFT; +} + +static void hiomap_cmd(IPMIBmcSim *ibs, uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine()); + PnvPnor *pnor = pnv->pnor; + uint32_t pnor_size = pnor->size; + uint32_t pnor_addr = PNOR_SPI_OFFSET; + bool readonly = false; + + rsp_buffer_push(rsp, cmd[2]); + rsp_buffer_push(rsp, cmd[3]); + + switch (cmd[2]) { + case HIOMAP_C_MARK_DIRTY: + case HIOMAP_C_FLUSH: + case HIOMAP_C_ERASE: + case HIOMAP_C_ACK: + break; + + case HIOMAP_C_GET_INFO: + rsp_buffer_push(rsp, 2); /* Version 2 */ + rsp_buffer_push(rsp, BLOCK_SHIFT); /* block size */ + rsp_buffer_push(rsp, 0); /* Timeout */ + rsp_buffer_push(rsp, 0); /* Timeout */ + break; + + case HIOMAP_C_GET_FLASH_INFO: + rsp_buffer_push(rsp, bytes_to_blocks(pnor_size) & 0xFF); + rsp_buffer_push(rsp, bytes_to_blocks(pnor_size) >> 8); + rsp_buffer_push(rsp, 0x01); /* erase size */ + rsp_buffer_push(rsp, 0x00); /* erase size */ + break; + + case HIOMAP_C_CREATE_READ_WINDOW: + readonly = true; + /* Fall through */ + + case HIOMAP_C_CREATE_WRITE_WINDOW: + memory_region_set_readonly(&pnor->mmio, readonly); + memory_region_set_enabled(&pnor->mmio, true); + + rsp_buffer_push(rsp, bytes_to_blocks(pnor_addr) & 0xFF); + rsp_buffer_push(rsp, bytes_to_blocks(pnor_addr) >> 8); + rsp_buffer_push(rsp, bytes_to_blocks(pnor_size) & 0xFF); + rsp_buffer_push(rsp, bytes_to_blocks(pnor_size) >> 8); + rsp_buffer_push(rsp, 0x00); /* offset */ + rsp_buffer_push(rsp, 0x00); /* offset */ + break; + + case HIOMAP_C_CLOSE_WINDOW: + memory_region_set_enabled(&pnor->mmio, false); + break; + + case HIOMAP_C_DEVICE_NAME: + case HIOMAP_C_RESET: + case HIOMAP_C_LOCK: + default: + qemu_log_mask(LOG_GUEST_ERROR, "HIOMAP: unknow command %02X\n", cmd[2]); + break; + } +} + +#define HIOMAP 0x5a + +static const IPMICmdHandler hiomap_cmds[] = { + [HIOMAP] = { hiomap_cmd, 3 }, +}; + +static const IPMINetfn hiomap_netfn = { + .cmd_nums = ARRAY_SIZE(hiomap_cmds), + .cmd_handlers = hiomap_cmds +}; + +int pnv_bmc_hiomap(IPMIBmc *bmc) +{ + return ipmi_sim_register_netfn(IPMI_BMC_SIMULATOR(bmc), + IPMI_NETFN_OEM, &hiomap_netfn); +} diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c index c5a85c38c7..dd5374c838 100644 --- a/hw/ppc/pnv_lpc.c +++ b/hw/ppc/pnv_lpc.c @@ -810,6 +810,7 @@ ISABus *pnv_lpc_isa_create(PnvLpcController *lpc, bool use_cpld, Error **errp) ISABus *isa_bus; qemu_irq *irqs; qemu_irq_handler handler; + PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine()); /* let isa_bus_new() create its own bridge on SysBus otherwise * devices speficied on the command line won't find the bus and @@ -834,5 +835,17 @@ ISABus *pnv_lpc_isa_create(PnvLpcController *lpc, bool use_cpld, Error **errp) irqs = qemu_allocate_irqs(handler, lpc, ISA_NUM_IRQS); isa_bus_irqs(isa_bus, irqs); + + /* + * TODO: Map PNOR on the LPC FW address space on demand ? + */ + memory_region_add_subregion(&lpc->isa_fw, PNOR_SPI_OFFSET, + &pnv->pnor->mmio); + /* + * Start disabled. The HIOMAP protocol will activate the mapping + * with HIOMAP_C_CREATE_WRITE_WINDOW + */ + memory_region_set_enabled(&pnv->pnor->mmio, false); + return isa_bus; } diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h index 5ecd3ba6ed..07c56c05ad 100644 --- a/include/hw/ppc/pnv.h +++ b/include/hw/ppc/pnv.h @@ -198,6 +198,7 @@ static inline bool pnv_is_power9(PnvMachineState *pnv) */ void pnv_dt_bmc_sensors(IPMIBmc *bmc, void *fdt); void pnv_bmc_powerdown(IPMIBmc *bmc); +int pnv_bmc_hiomap(IPMIBmc *bmc); /* * POWER8 MMIO base addresses diff --git a/include/hw/ppc/pnv_pnor.h b/include/hw/ppc/pnv_pnor.h index dec811695c..c3dd28643c 100644 --- a/include/hw/ppc/pnv_pnor.h +++ b/include/hw/ppc/pnv_pnor.h @@ -9,6 +9,11 @@ #ifndef _PPC_PNV_PNOR_H #define _PPC_PNV_PNOR_H +/* + * PNOR offset on the LPC FW address space + */ +#define PNOR_SPI_OFFSET 0x0c000000UL + #define TYPE_PNV_PNOR "pnv-pnor" #define PNV_PNOR(obj) OBJECT_CHECK(PnvPnor, (obj), TYPE_PNV_PNOR)