diff options
Diffstat (limited to 'hw/ppc/pegasos2.c')
-rw-r--r-- | hw/ppc/pegasos2.c | 162 |
1 files changed, 100 insertions, 62 deletions
diff --git a/hw/ppc/pegasos2.c b/hw/ppc/pegasos2.c index b8ce859f1a..e427ac2fe0 100644 --- a/hw/ppc/pegasos2.c +++ b/hw/ppc/pegasos2.c @@ -22,6 +22,7 @@ #include "hw/i2c/smbus_eeprom.h" #include "hw/qdev-properties.h" #include "sysemu/reset.h" +#include "sysemu/runstate.h" #include "hw/boards.h" #include "hw/loader.h" #include "hw/fw-path-provider.h" @@ -31,6 +32,8 @@ #include "sysemu/kvm.h" #include "kvm_ppc.h" #include "exec/address-spaces.h" +#include "qom/qom-qobject.h" +#include "qapi/qmp/qdict.h" #include "trace.h" #include "qemu/datadir.h" #include "sysemu/device_tree.h" @@ -52,11 +55,13 @@ #define BUS_FREQ_HZ 133333333 +#define PCI0_CFG_ADDR 0xcf8 #define PCI0_MEM_BASE 0xc0000000 #define PCI0_MEM_SIZE 0x20000000 #define PCI0_IO_BASE 0xf8000000 #define PCI0_IO_SIZE 0x10000 +#define PCI1_CFG_ADDR 0xc78 #define PCI1_MEM_BASE 0x80000000 #define PCI1_MEM_SIZE 0x40000000 #define PCI1_IO_BASE 0xfe000000 @@ -117,6 +122,10 @@ static void pegasos2_init(MachineState *machine) qemu_register_reset(pegasos2_cpu_reset, pm->cpu); /* RAM */ + if (machine->ram_size > 2 * GiB) { + error_report("RAM size more than 2 GiB is not supported"); + exit(1); + } memory_region_add_subregion(get_system_memory(), 0, machine->ram); /* allocate and load firmware */ @@ -190,62 +199,58 @@ static void pegasos2_init(MachineState *machine) if (!pm->vof) { warn_report("Option -kernel may be ineffective with -bios."); } + } else if (pm->vof) { + warn_report("Using Virtual OpenFirmware but no -kernel option."); } + if (!pm->vof && machine->kernel_cmdline && machine->kernel_cmdline[0]) { warn_report("Option -append may be ineffective with -bios."); } } -static uint32_t pegasos2_pci_config_read(AddressSpace *as, int bus, +static uint32_t pegasos2_mv_reg_read(Pegasos2MachineState *pm, + uint32_t addr, uint32_t len) +{ + MemoryRegion *r = sysbus_mmio_get_region(SYS_BUS_DEVICE(pm->mv), 0); + uint64_t val = 0xffffffffULL; + memory_region_dispatch_read(r, addr, &val, size_memop(len) | MO_LE, + MEMTXATTRS_UNSPECIFIED); + return val; +} + +static void pegasos2_mv_reg_write(Pegasos2MachineState *pm, uint32_t addr, + uint32_t len, uint32_t val) +{ + MemoryRegion *r = sysbus_mmio_get_region(SYS_BUS_DEVICE(pm->mv), 0); + memory_region_dispatch_write(r, addr, val, size_memop(len) | MO_LE, + MEMTXATTRS_UNSPECIFIED); +} + +static uint32_t pegasos2_pci_config_read(Pegasos2MachineState *pm, int bus, uint32_t addr, uint32_t len) { - hwaddr pcicfg = (bus ? 0xf1000c78 : 0xf1000cf8); - uint32_t val = 0xffffffff; - - stl_le_phys(as, pcicfg, addr | BIT(31)); - switch (len) { - case 4: - val = ldl_le_phys(as, pcicfg + 4); - break; - case 2: - val = lduw_le_phys(as, pcicfg + 4); - break; - case 1: - val = ldub_phys(as, pcicfg + 4); - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid length\n", __func__); - break; + hwaddr pcicfg = bus ? PCI1_CFG_ADDR : PCI0_CFG_ADDR; + uint64_t val = 0xffffffffULL; + + if (len <= 4) { + pegasos2_mv_reg_write(pm, pcicfg, 4, addr | BIT(31)); + val = pegasos2_mv_reg_read(pm, pcicfg + 4, len); } return val; } -static void pegasos2_pci_config_write(AddressSpace *as, int bus, uint32_t addr, - uint32_t len, uint32_t val) +static void pegasos2_pci_config_write(Pegasos2MachineState *pm, int bus, + uint32_t addr, uint32_t len, uint32_t val) { - hwaddr pcicfg = (bus ? 0xf1000c78 : 0xf1000cf8); - - stl_le_phys(as, pcicfg, addr | BIT(31)); - switch (len) { - case 4: - stl_le_phys(as, pcicfg + 4, val); - break; - case 2: - stw_le_phys(as, pcicfg + 4, val); - break; - case 1: - stb_phys(as, pcicfg + 4, val); - break; - default: - qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid length\n", __func__); - break; - } + hwaddr pcicfg = bus ? PCI1_CFG_ADDR : PCI0_CFG_ADDR; + + pegasos2_mv_reg_write(pm, pcicfg, 4, addr | BIT(31)); + pegasos2_mv_reg_write(pm, pcicfg + 4, len, val); } static void pegasos2_machine_reset(MachineState *machine) { Pegasos2MachineState *pm = PEGASOS2_MACHINE(machine); - AddressSpace *as = CPU(pm->cpu)->as; void *fdt; uint64_t d[2]; int sz; @@ -256,51 +261,51 @@ static void pegasos2_machine_reset(MachineState *machine) } /* Otherwise, set up devices that board firmware would normally do */ - stl_le_phys(as, 0xf1000000, 0x28020ff); - stl_le_phys(as, 0xf1000278, 0xa31fc); - stl_le_phys(as, 0xf100f300, 0x11ff0400); - stl_le_phys(as, 0xf100f10c, 0x80000000); - stl_le_phys(as, 0xf100001c, 0x8000000); - pegasos2_pci_config_write(as, 0, PCI_COMMAND, 2, PCI_COMMAND_IO | + pegasos2_mv_reg_write(pm, 0, 4, 0x28020ff); + pegasos2_mv_reg_write(pm, 0x278, 4, 0xa31fc); + pegasos2_mv_reg_write(pm, 0xf300, 4, 0x11ff0400); + pegasos2_mv_reg_write(pm, 0xf10c, 4, 0x80000000); + pegasos2_mv_reg_write(pm, 0x1c, 4, 0x8000000); + pegasos2_pci_config_write(pm, 0, PCI_COMMAND, 2, PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); - pegasos2_pci_config_write(as, 1, PCI_COMMAND, 2, PCI_COMMAND_IO | + pegasos2_pci_config_write(pm, 1, PCI_COMMAND, 2, PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 0) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 0) << 8) | PCI_INTERRUPT_LINE, 2, 0x9); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 0) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 0) << 8) | 0x50, 1, 0x2); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 1) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 1) << 8) | PCI_INTERRUPT_LINE, 2, 0x109); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 1) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 1) << 8) | PCI_CLASS_PROG, 1, 0xf); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 1) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 1) << 8) | 0x40, 1, 0xb); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 1) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 1) << 8) | 0x50, 4, 0x17171717); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 1) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 1) << 8) | PCI_COMMAND, 2, 0x87); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 2) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 2) << 8) | PCI_INTERRUPT_LINE, 2, 0x409); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 3) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 3) << 8) | PCI_INTERRUPT_LINE, 2, 0x409); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 4) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 4) << 8) | PCI_INTERRUPT_LINE, 2, 0x9); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 4) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 4) << 8) | 0x48, 4, 0xf00); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 4) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 4) << 8) | 0x40, 4, 0x558020); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 4) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 4) << 8) | 0x90, 4, 0xd00); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 5) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 5) << 8) | PCI_INTERRUPT_LINE, 2, 0x309); - pegasos2_pci_config_write(as, 1, (PCI_DEVFN(12, 6) << 8) | + pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 6) << 8) | PCI_INTERRUPT_LINE, 2, 0x309); /* Device tree and VOF set up */ @@ -362,6 +367,29 @@ static target_ulong pegasos2_rtas(PowerPCCPU *cpu, Pegasos2MachineState *pm, return H_PARAMETER; } switch (token) { + case RTAS_GET_TIME_OF_DAY: + { + QObject *qo = object_property_get_qobject(qdev_get_machine(), + "rtc-time", &error_fatal); + QDict *qd = qobject_to(QDict, qo); + + if (nargs != 0 || nrets != 8 || !qd) { + stl_be_phys(as, rets, -1); + qobject_unref(qo); + return H_PARAMETER; + } + + stl_be_phys(as, rets, 0); + stl_be_phys(as, rets + 4, qdict_get_int(qd, "tm_year") + 1900); + stl_be_phys(as, rets + 8, qdict_get_int(qd, "tm_mon") + 1); + stl_be_phys(as, rets + 12, qdict_get_int(qd, "tm_mday")); + stl_be_phys(as, rets + 16, qdict_get_int(qd, "tm_hour")); + stl_be_phys(as, rets + 20, qdict_get_int(qd, "tm_min")); + stl_be_phys(as, rets + 24, qdict_get_int(qd, "tm_sec")); + stl_be_phys(as, rets + 28, 0); + qobject_unref(qo); + return H_SUCCESS; + } case RTAS_READ_PCI_CONFIG: { uint32_t addr, len, val; @@ -372,7 +400,7 @@ static target_ulong pegasos2_rtas(PowerPCCPU *cpu, Pegasos2MachineState *pm, } addr = ldl_be_phys(as, args); len = ldl_be_phys(as, args + 4); - val = pegasos2_pci_config_read(as, !(addr >> 24), + val = pegasos2_pci_config_read(pm, !(addr >> 24), addr & 0x0fffffff, len); stl_be_phys(as, rets, 0); stl_be_phys(as, rets + 4, val); @@ -389,7 +417,7 @@ static target_ulong pegasos2_rtas(PowerPCCPU *cpu, Pegasos2MachineState *pm, addr = ldl_be_phys(as, args); len = ldl_be_phys(as, args + 4); val = ldl_be_phys(as, args + 8); - pegasos2_pci_config_write(as, !(addr >> 24), + pegasos2_pci_config_write(pm, !(addr >> 24), addr & 0x0fffffff, len, val); stl_be_phys(as, rets, 0); return H_SUCCESS; @@ -402,6 +430,16 @@ static target_ulong pegasos2_rtas(PowerPCCPU *cpu, Pegasos2MachineState *pm, qemu_log_mask(LOG_UNIMP, "%c", ldl_be_phys(as, args)); stl_be_phys(as, rets, 0); return H_SUCCESS; + case RTAS_POWER_OFF: + { + if (nargs != 2 || nrets != 1) { + stl_be_phys(as, rets, -1); + return H_PARAMETER; + } + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); + stl_be_phys(as, rets, 0); + return H_SUCCESS; + } default: qemu_log_mask(LOG_UNIMP, "Unknown RTAS token %u (args=%u, rets=%u)\n", token, nargs, nrets); |