aboutsummaryrefslogtreecommitdiff
path: root/hw/ppc
diff options
context:
space:
mode:
authorHervé Poussineau <hpoussin@reactos.org>2017-01-07 16:23:43 +0100
committerDavid Gibson <david@gibson.dropbear.id.au>2017-01-31 10:10:13 +1100
commit34b9b5575b7752ea13fd147b3e7a3e24b2975e8f (patch)
treee7f87c4ee53071fcd154500d97ac71f9af551f54 /hw/ppc
parent79623312c68fc07376ca7d3923137ad1abc7496c (diff)
downloadqemu-34b9b5575b7752ea13fd147b3e7a3e24b2975e8f.zip
qemu-34b9b5575b7752ea13fd147b3e7a3e24b2975e8f.tar.gz
qemu-34b9b5575b7752ea13fd147b3e7a3e24b2975e8f.tar.bz2
prep: add IBM RS/6000 7020 (40p) machine emulation
Machine supports both Open Hack'Ware and OpenBIOS. Open Hack'Ware is the default because OpenBIOS is currently unable to boot PReP boot partitions or PReP kernels. Signed-off-by: Hervé Poussineau <hpoussin@reactos.org> [dwg: Correct compile failure with KVM located by Thomas Huth] Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Diffstat (limited to 'hw/ppc')
-rw-r--r--hw/ppc/prep.c230
1 files changed, 230 insertions, 0 deletions
diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c
index 9fb89d3..ca7959c 100644
--- a/hw/ppc/prep.c
+++ b/hw/ppc/prep.c
@@ -2,6 +2,7 @@
* QEMU PPC PREP hardware System Emulator
*
* Copyright (c) 2003-2007 Jocelyn Mayer
+ * Copyright (c) 2017 Hervé Poussineau
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -43,17 +44,21 @@
#include "hw/isa/pc87312.h"
#include "sysemu/block-backend.h"
#include "sysemu/arch_init.h"
+#include "sysemu/kvm.h"
#include "sysemu/qtest.h"
#include "exec/address-spaces.h"
#include "trace.h"
#include "elf.h"
#include "qemu/cutils.h"
+#include "kvm_ppc.h"
/* SMP is not enabled, for now */
#define MAX_CPUS 1
#define MAX_IDE_BUS 2
+#define CFG_ADDR 0xf0000510
+
#define BIOS_SIZE (1024 * 1024)
#define BIOS_FILENAME "ppc_rom.bin"
#define KERNEL_LOAD_ADDR 0x01000000
@@ -316,6 +321,12 @@ static uint32_t PREP_io_800_readb (void *opaque, uint32_t addr)
#define NVRAM_SIZE 0x2000
+static void fw_cfg_boot_set(void *opaque, const char *boot_device,
+ Error **errp)
+{
+ fw_cfg_modify_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]);
+}
+
static void ppc_prep_reset(void *opaque)
{
PowerPCCPU *cpu = opaque;
@@ -677,4 +688,223 @@ static void prep_machine_init(MachineClass *mc)
mc->default_boot_order = "cad";
}
+static int prep_set_cmos_checksum(DeviceState *dev, void *opaque)
+{
+ uint16_t checksum = *(uint16_t *)opaque;
+ ISADevice *rtc;
+
+ if (object_dynamic_cast(OBJECT(dev), "mc146818rtc")) {
+ rtc = ISA_DEVICE(dev);
+ rtc_set_memory(rtc, 0x2e, checksum & 0xff);
+ rtc_set_memory(rtc, 0x3e, checksum & 0xff);
+ rtc_set_memory(rtc, 0x2f, checksum >> 8);
+ rtc_set_memory(rtc, 0x3f, checksum >> 8);
+ }
+ return 0;
+}
+
+static void ibm_40p_init(MachineState *machine)
+{
+ CPUPPCState *env = NULL;
+ uint16_t cmos_checksum;
+ PowerPCCPU *cpu;
+ DeviceState *dev;
+ SysBusDevice *pcihost;
+ Nvram *m48t59 = NULL;
+ PCIBus *pci_bus;
+ ISABus *isa_bus;
+ void *fw_cfg;
+ int i;
+ uint32_t kernel_base = 0, initrd_base = 0;
+ long kernel_size = 0, initrd_size = 0;
+ char boot_device;
+
+ /* init CPU */
+ if (!machine->cpu_model) {
+ machine->cpu_model = "604";
+ }
+ cpu = cpu_ppc_init(machine->cpu_model);
+ if (!cpu) {
+ error_report("could not initialize CPU '%s'",
+ machine->cpu_model);
+ exit(1);
+ }
+ env = &cpu->env;
+ if (PPC_INPUT(env) != PPC_FLAGS_INPUT_6xx) {
+ error_report("only 6xx bus is supported on this machine");
+ exit(1);
+ }
+
+ if (env->flags & POWERPC_FLAG_RTC_CLK) {
+ /* POWER / PowerPC 601 RTC clock frequency is 7.8125 MHz */
+ cpu_ppc_tb_init(env, 7812500UL);
+ } else {
+ /* Set time-base frequency to 100 Mhz */
+ cpu_ppc_tb_init(env, 100UL * 1000UL * 1000UL);
+ }
+ qemu_register_reset(ppc_prep_reset, cpu);
+
+ /* PCI host */
+ dev = qdev_create(NULL, "raven-pcihost");
+ if (!bios_name) {
+ bios_name = BIOS_FILENAME;
+ }
+ qdev_prop_set_string(dev, "bios-name", bios_name);
+ qdev_prop_set_uint32(dev, "elf-machine", PPC_ELF_MACHINE);
+ pcihost = SYS_BUS_DEVICE(dev);
+ object_property_add_child(qdev_get_machine(), "raven", OBJECT(dev), NULL);
+ qdev_init_nofail(dev);
+ pci_bus = PCI_BUS(qdev_get_child_bus(dev, "pci.0"));
+ if (!pci_bus) {
+ error_report("could not create PCI host controller");
+ exit(1);
+ }
+
+ /* PCI -> ISA bridge */
+ dev = DEVICE(pci_create_simple(pci_bus, PCI_DEVFN(11, 0), "i82378"));
+ qdev_connect_gpio_out(dev, 0,
+ cpu->env.irq_inputs[PPC6xx_INPUT_INT]);
+ sysbus_connect_irq(pcihost, 0, qdev_get_gpio_in(dev, 15));
+ sysbus_connect_irq(pcihost, 1, qdev_get_gpio_in(dev, 13));
+ sysbus_connect_irq(pcihost, 2, qdev_get_gpio_in(dev, 15));
+ sysbus_connect_irq(pcihost, 3, qdev_get_gpio_in(dev, 13));
+ isa_bus = ISA_BUS(qdev_get_child_bus(dev, "isa.0"));
+
+ /* Memory controller */
+ dev = DEVICE(isa_create(isa_bus, "rs6000-mc"));
+ qdev_prop_set_uint32(dev, "ram-size", machine->ram_size);
+ qdev_init_nofail(dev);
+
+ /* initialize CMOS checksums */
+ cmos_checksum = 0x6aa9;
+ qbus_walk_children(BUS(isa_bus), prep_set_cmos_checksum, NULL, NULL, NULL,
+ &cmos_checksum);
+
+ /* initialize audio subsystem */
+ audio_init();
+
+ /* add some more devices */
+ if (defaults_enabled()) {
+ isa_create_simple(isa_bus, "i8042");
+ m48t59 = NVRAM(isa_create_simple(isa_bus, "isa-m48t59"));
+
+ dev = DEVICE(isa_create(isa_bus, "cs4231a"));
+ qdev_prop_set_uint32(dev, "iobase", 0x830);
+ qdev_prop_set_uint32(dev, "irq", 10);
+ qdev_init_nofail(dev);
+
+ dev = DEVICE(isa_create(isa_bus, "pc87312"));
+ qdev_prop_set_uint32(dev, "config", 12);
+ qdev_init_nofail(dev);
+
+ dev = DEVICE(isa_create(isa_bus, "prep-systemio"));
+ qdev_prop_set_uint32(dev, "ibm-planar-id", 0xfc);
+ qdev_prop_set_uint32(dev, "equipment", 0xc0);
+ qdev_init_nofail(dev);
+
+ pci_create_simple(pci_bus, PCI_DEVFN(1, 0), "lsi53c810");
+
+ /* XXX: s3-trio at PCI_DEVFN(2, 0) */
+ pci_vga_init(pci_bus);
+
+ for (i = 0; i < nb_nics; i++) {
+ pci_nic_init_nofail(&nd_table[i], pci_bus, "pcnet",
+ i == 0 ? "3" : NULL);
+ }
+ }
+
+ /* Prepare firmware configuration for OpenBIOS */
+ fw_cfg = fw_cfg_init_mem(CFG_ADDR, CFG_ADDR + 2);
+
+ if (machine->kernel_filename) {
+ /* load kernel */
+ kernel_base = KERNEL_LOAD_ADDR;
+ kernel_size = load_image_targphys(machine->kernel_filename,
+ kernel_base,
+ machine->ram_size - kernel_base);
+ if (kernel_size < 0) {
+ error_report("could not load kernel '%s'",
+ machine->kernel_filename);
+ exit(1);
+ }
+ fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, kernel_base);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size);
+ /* load initrd */
+ if (machine->initrd_filename) {
+ initrd_base = INITRD_LOAD_ADDR;
+ initrd_size = load_image_targphys(machine->initrd_filename,
+ initrd_base,
+ machine->ram_size - initrd_base);
+ if (initrd_size < 0) {
+ error_report("could not load initial ram disk '%s'",
+ machine->initrd_filename);
+ exit(1);
+ }
+ fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_base);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size);
+ }
+ if (machine->kernel_cmdline && *machine->kernel_cmdline) {
+ fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, CMDLINE_ADDR);
+ pstrcpy_targphys("cmdline", CMDLINE_ADDR, TARGET_PAGE_SIZE,
+ machine->kernel_cmdline);
+ fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA,
+ machine->kernel_cmdline);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE,
+ strlen(machine->kernel_cmdline) + 1);
+ }
+ boot_device = 'm';
+ } else {
+ boot_device = machine->boot_order[0];
+ }
+
+ fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)max_cpus);
+ fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)machine->ram_size);
+ fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, ARCH_PREP);
+
+ fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_WIDTH, graphic_width);
+ fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_HEIGHT, graphic_height);
+ fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_DEPTH, graphic_depth);
+
+ fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_IS_KVM, kvm_enabled());
+ if (kvm_enabled()) {
+#ifdef CONFIG_KVM
+ uint8_t *hypercall;
+
+ fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, kvmppc_get_tbfreq());
+ hypercall = g_malloc(16);
+ kvmppc_get_hypercall(env, hypercall, 16);
+ fw_cfg_add_bytes(fw_cfg, FW_CFG_PPC_KVM_HC, hypercall, 16);
+ fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_KVM_PID, getpid());
+#endif
+ } else {
+ fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, NANOSECONDS_PER_SECOND);
+ }
+ fw_cfg_add_i16(fw_cfg, FW_CFG_BOOT_DEVICE, boot_device);
+ qemu_register_boot_set(fw_cfg_boot_set, fw_cfg);
+
+ /* Prepare firmware configuration for Open Hack'Ware */
+ if (m48t59) {
+ PPC_NVRAM_set_params(m48t59, NVRAM_SIZE, "PREP", ram_size,
+ boot_device,
+ kernel_base, kernel_size,
+ machine->kernel_cmdline,
+ initrd_base, initrd_size,
+ /* XXX: need an option to load a NVRAM image */
+ 0,
+ graphic_width, graphic_height, graphic_depth);
+ }
+}
+
+static void ibm_40p_machine_init(MachineClass *mc)
+{
+ mc->desc = "IBM RS/6000 7020 (40p)",
+ mc->init = ibm_40p_init;
+ mc->max_cpus = 1;
+ mc->pci_allow_0_address = true;
+ mc->default_ram_size = 128 * M_BYTE;
+ mc->block_default_type = IF_SCSI;
+ mc->default_boot_order = "c";
+}
+
+DEFINE_MACHINE("40p", ibm_40p_machine_init)
DEFINE_MACHINE("prep", prep_machine_init)