diff options
Diffstat (limited to 'hw/ppc')
| -rw-r--r-- | hw/ppc/Makefile.objs | 3 | ||||
| -rw-r--r-- | hw/ppc/e500.c | 124 | ||||
| -rw-r--r-- | hw/ppc/mac.h | 10 | ||||
| -rw-r--r-- | hw/ppc/mac_newworld.c | 56 | ||||
| -rw-r--r-- | hw/ppc/mac_oldworld.c | 50 | ||||
| -rw-r--r-- | hw/ppc/ppc440_pcix.c | 528 | ||||
| -rw-r--r-- | hw/ppc/ppc440_uc.c | 3 | ||||
| -rw-r--r-- | hw/ppc/sam460ex.c | 603 | ||||
| -rw-r--r-- | hw/ppc/spapr.c | 176 | ||||
| -rw-r--r-- | hw/ppc/spapr_caps.c | 153 | ||||
| -rw-r--r-- | hw/ppc/spapr_hcall.c | 5 | ||||
| -rw-r--r-- | hw/ppc/spapr_rtas.c | 1 | ||||
| -rw-r--r-- | hw/ppc/spapr_rtc.c | 2 | ||||
| -rw-r--r-- | hw/ppc/trace-events | 8 |
14 files changed, 1474 insertions, 248 deletions
diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index ad1928c5d8..86d82a6ec3 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -13,7 +13,8 @@ endif obj-$(CONFIG_PSERIES) += spapr_rtas_ddw.o # PowerPC 4xx boards obj-y += ppc4xx_devs.o ppc405_uc.o -obj-$(CONFIG_PPC4XX) += ppc4xx_pci.o ppc405_boards.o ppc440_bamboo.o +obj-$(CONFIG_PPC4XX) += ppc4xx_pci.o ppc405_boards.o +obj-$(CONFIG_PPC4XX) += ppc440_bamboo.o ppc440_pcix.o ppc440_uc.o sam460ex.o # PReP obj-$(CONFIG_PREP) += prep.o obj-$(CONFIG_PREP) += prep_systemio.o diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index a40d3ec3e3..43c15d18c4 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -29,6 +29,7 @@ #include "kvm_ppc.h" #include "sysemu/device_tree.h" #include "hw/ppc/openpic.h" +#include "hw/ppc/openpic_kvm.h" #include "hw/ppc/ppc.h" #include "hw/loader.h" #include "elf.h" @@ -119,7 +120,14 @@ static void dt_serial_create(void *fdt, unsigned long long offset, qemu_fdt_setprop_string(fdt, "/aliases", alias, ser); if (defcon) { + /* + * "linux,stdout-path" and "stdout" properties are deprecated by linux + * kernel. New platforms should only use the "stdout-path" property. Set + * the new property and continue using older property to remain + * compatible with the existing firmware. + */ qemu_fdt_setprop_string(fdt, "/chosen", "linux,stdout-path", ser); + qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", ser); } } @@ -784,8 +792,10 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) int initrd_size = 0; hwaddr cur_base = 0; char *filename; + const char *payload_name; + bool kernel_as_payload; hwaddr bios_entry = 0; - target_long bios_size; + target_long payload_size; struct boot_info *boot_info; int dt_size; int i; @@ -913,11 +923,6 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) /* Register spinning region */ sysbus_create_simple("e500-spin", params->spin_base, NULL); - if (cur_base < (32 * 1024 * 1024)) { - /* u-boot occupies memory up to 32MB, so load blobs above */ - cur_base = (32 * 1024 * 1024); - } - if (params->has_mpc8xxx_gpio) { qemu_irq poweroff_irq; @@ -952,8 +957,61 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) sysbus_mmio_get_region(s, 0)); } - /* Load kernel. */ - if (machine->kernel_filename) { + /* + * Smart firmware defaults ahead! + * + * We follow the following table to select which payload we execute. + * + * -kernel | -bios | payload + * ---------+-------+--------- + * N | Y | u-boot + * N | N | u-boot + * Y | Y | u-boot + * Y | N | kernel + * + * This ensures backwards compatibility with how we used to expose + * -kernel to users but allows them to run through u-boot as well. + */ + kernel_as_payload = false; + if (bios_name == NULL) { + if (machine->kernel_filename) { + payload_name = machine->kernel_filename; + kernel_as_payload = true; + } else { + payload_name = "u-boot.e500"; + } + } else { + payload_name = bios_name; + } + + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, payload_name); + + payload_size = load_elf(filename, NULL, NULL, &bios_entry, &loadaddr, NULL, + 1, PPC_ELF_MACHINE, 0, 0); + if (payload_size < 0) { + /* + * Hrm. No ELF image? Try a uImage, maybe someone is giving us an + * ePAPR compliant kernel + */ + payload_size = load_uimage(filename, &bios_entry, &loadaddr, NULL, + NULL, NULL); + if (payload_size < 0) { + error_report("qemu: could not load firmware '%s'", filename); + exit(1); + } + } + + g_free(filename); + + if (kernel_as_payload) { + kernel_base = loadaddr; + kernel_size = payload_size; + } + + cur_base = loadaddr + payload_size; + + /* Load bare kernel only if no bios/u-boot has been provided */ + if (machine->kernel_filename && !kernel_as_payload) { kernel_base = cur_base; kernel_size = load_image_targphys(machine->kernel_filename, cur_base, @@ -967,6 +1025,11 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) cur_base += kernel_size; } + if (cur_base < (32 * 1024 * 1024)) { + /* u-boot occupies memory up to 32MB, so load blobs above */ + cur_base = (32 * 1024 * 1024); + } + /* Load initrd. */ if (machine->initrd_filename) { initrd_base = (cur_base + INITRD_LOAD_PAD) & ~INITRD_PAD_MASK; @@ -983,47 +1046,16 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) } /* - * Smart firmware defaults ahead! - * - * We follow the following table to select which payload we execute. - * - * -kernel | -bios | payload - * ---------+-------+--------- - * N | Y | u-boot - * N | N | u-boot - * Y | Y | u-boot - * Y | N | kernel - * - * This ensures backwards compatibility with how we used to expose - * -kernel to users but allows them to run through u-boot as well. + * Reserve space for dtb behind the kernel image because Linux has a bug + * where it can only handle the dtb if it's within the first 64MB of where + * <kernel> starts. dtb cannot not reach initrd_base because INITRD_LOAD_PAD + * ensures enough space between kernel and initrd. */ - if (bios_name == NULL) { - if (machine->kernel_filename) { - bios_name = machine->kernel_filename; - } else { - bios_name = "u-boot.e500"; - } - } - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - - bios_size = load_elf(filename, NULL, NULL, &bios_entry, &loadaddr, NULL, - 1, PPC_ELF_MACHINE, 0, 0); - if (bios_size < 0) { - /* - * Hrm. No ELF image? Try a uImage, maybe someone is giving us an - * ePAPR compliant kernel - */ - kernel_size = load_uimage(filename, &bios_entry, &loadaddr, NULL, - NULL, NULL); - if (kernel_size < 0) { - error_report("could not load firmware '%s'", filename); + dt_base = (loadaddr + payload_size + DTC_LOAD_PAD) & ~DTC_PAD_MASK; + if (dt_base + DTB_MAX_SIZE > ram_size) { + error_report("qemu: not enough memory for device tree"); exit(1); - } } - g_free(filename); - - /* Reserve space for dtb */ - dt_base = (loadaddr + bios_size + DTC_LOAD_PAD) & ~DTC_PAD_MASK; dt_size = ppce500_prep_device_tree(machine, params, dt_base, initrd_base, initrd_size, diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h index 4702194f3f..a02f797598 100644 --- a/hw/ppc/mac.h +++ b/hw/ppc/mac.h @@ -47,9 +47,6 @@ /* MacIO */ -#define TYPE_OLDWORLD_MACIO "macio-oldworld" -#define TYPE_NEWWORLD_MACIO "macio-newworld" - #define TYPE_MACIO_IDE "macio-ide" #define MACIO_IDE(obj) OBJECT_CHECK(MACIOIDEState, (obj), TYPE_MACIO_IDE) @@ -76,12 +73,11 @@ void macio_ide_init_drives(MACIOIDEState *ide, DriveInfo **hd_table); void macio_ide_register_dma(MACIOIDEState *ide); void macio_init(PCIDevice *dev, - MemoryRegion *pic_mem, - MemoryRegion *escc_mem); + MemoryRegion *pic_mem); /* Heathrow PIC */ -qemu_irq *heathrow_pic_init(MemoryRegion **pmem, - int nb_cpus, qemu_irq **irqs); +DeviceState *heathrow_pic_init(int nb_cpus, qemu_irq **irqs, + qemu_irq **pic_irqs); /* Grackle PCI */ #define TYPE_GRACKLE_PCI_HOST_BRIDGE "grackle-pcihost" diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index 4e1298ee50..a749e2565d 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -60,6 +60,7 @@ #include "hw/boards.h" #include "hw/nvram/fw_cfg.h" #include "hw/char/escc.h" +#include "hw/misc/macio/macio.h" #include "hw/ppc/openpic.h" #include "hw/ide.h" #include "hw/loader.h" @@ -153,20 +154,18 @@ static void ppc_core99_init(MachineState *machine) hwaddr kernel_base, initrd_base, cmdline_base = 0; long kernel_size, initrd_size; PCIBus *pci_bus; - PCIDevice *macio; + NewWorldMacIOState *macio; MACIOIDEState *macio_ide; BusState *adb_bus; MacIONVRAMState *nvr; int bios_size, ndrv_size; uint8_t *ndrv_file; - MemoryRegion *pic_mem, *escc_mem; - MemoryRegion *escc_bar = g_new(MemoryRegion, 1); int ppc_boot_device; DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; void *fw_cfg; int machine_arch; SysBusDevice *s; - DeviceState *dev; + DeviceState *dev, *pic_dev; int *token = g_new(int, 1); hwaddr nvram_addr = 0xFFF04000; uint64_t tbfreq; @@ -333,11 +332,10 @@ static void ppc_core99_init(MachineState *machine) pic = g_new0(qemu_irq, 64); - dev = qdev_create(NULL, TYPE_OPENPIC); - qdev_prop_set_uint32(dev, "model", OPENPIC_MODEL_KEYLARGO); - qdev_init_nofail(dev); - s = SYS_BUS_DEVICE(dev); - pic_mem = s->mmio[0].memory; + pic_dev = qdev_create(NULL, TYPE_OPENPIC); + qdev_prop_set_uint32(pic_dev, "model", OPENPIC_MODEL_KEYLARGO); + qdev_init_nofail(pic_dev); + s = SYS_BUS_DEVICE(pic_dev); k = 0; for (i = 0; i < smp_cpus; i++) { for (j = 0; j < OPENPIC_OUTPUT_NB; j++) { @@ -346,7 +344,7 @@ static void ppc_core99_init(MachineState *machine) } for (i = 0; i < 64; i++) { - pic[i] = qdev_get_gpio_in(dev, i); + pic[i] = qdev_get_gpio_in(pic_dev, i); } if (PPC_INPUT(env) == PPC_FLAGS_INPUT_970) { @@ -368,36 +366,20 @@ static void ppc_core99_init(MachineState *machine) tbfreq = TBFREQ; } - /* init basic PC hardware */ - - dev = qdev_create(NULL, TYPE_ESCC); - qdev_prop_set_uint32(dev, "disabled", 0); - qdev_prop_set_uint32(dev, "frequency", ESCC_CLOCK); - qdev_prop_set_uint32(dev, "it_shift", 4); - qdev_prop_set_chr(dev, "chrA", serial_hds[0]); - qdev_prop_set_chr(dev, "chrB", serial_hds[1]); - qdev_prop_set_uint32(dev, "chnAtype", escc_serial); - qdev_prop_set_uint32(dev, "chnBtype", escc_serial); - qdev_init_nofail(dev); - - s = SYS_BUS_DEVICE(dev); - sysbus_connect_irq(s, 0, pic[0x24]); - sysbus_connect_irq(s, 1, pic[0x25]); - - escc_mem = &ESCC(s)->mmio; - - memory_region_init_alias(escc_bar, NULL, "escc-bar", - escc_mem, 0, memory_region_size(escc_mem)); - - macio = pci_create(pci_bus, -1, TYPE_NEWWORLD_MACIO); + /* MacIO */ + macio = NEWWORLD_MACIO(pci_create(pci_bus, -1, TYPE_NEWWORLD_MACIO)); dev = DEVICE(macio); qdev_connect_gpio_out(dev, 0, pic[0x19]); /* CUDA */ - qdev_connect_gpio_out(dev, 1, pic[0x0d]); /* IDE */ - qdev_connect_gpio_out(dev, 2, pic[0x02]); /* IDE DMA */ - qdev_connect_gpio_out(dev, 3, pic[0x0e]); /* IDE */ - qdev_connect_gpio_out(dev, 4, pic[0x03]); /* IDE DMA */ + qdev_connect_gpio_out(dev, 1, pic[0x24]); /* ESCC-B */ + qdev_connect_gpio_out(dev, 2, pic[0x25]); /* ESCC-A */ + qdev_connect_gpio_out(dev, 3, pic[0x0d]); /* IDE */ + qdev_connect_gpio_out(dev, 4, pic[0x02]); /* IDE DMA */ + qdev_connect_gpio_out(dev, 5, pic[0x0e]); /* IDE */ + qdev_connect_gpio_out(dev, 6, pic[0x03]); /* IDE DMA */ qdev_prop_set_uint64(dev, "frequency", tbfreq); - macio_init(macio, pic_mem, escc_bar); + object_property_set_link(OBJECT(macio), OBJECT(pic_dev), "pic", + &error_abort); + qdev_init_nofail(dev); /* We only emulate 2 out of 3 IDE controllers for now */ ide_drive_get(hd, ARRAY_SIZE(hd)); diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index d0d21d2392..935493c966 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -37,6 +37,7 @@ #include "hw/boards.h" #include "hw/nvram/fw_cfg.h" #include "hw/char/escc.h" +#include "hw/misc/macio/macio.h" #include "hw/ide.h" #include "hw/loader.h" #include "elf.h" @@ -92,19 +93,16 @@ static void ppc_heathrow_init(MachineState *machine) uint32_t kernel_base, initrd_base, cmdline_base = 0; int32_t kernel_size, initrd_size; PCIBus *pci_bus; - PCIDevice *macio; + OldWorldMacIOState *macio; MACIOIDEState *macio_ide; - DeviceState *dev; + DeviceState *dev, *pic_dev; BusState *adb_bus; int bios_size, ndrv_size; uint8_t *ndrv_file; - MemoryRegion *pic_mem; - MemoryRegion *escc_mem, *escc_bar = g_new(MemoryRegion, 1); uint16_t ppc_boot_device; DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; void *fw_cfg; uint64_t tbfreq; - SysBusDevice *s; linux_boot = (kernel_filename != NULL); @@ -259,46 +257,32 @@ static void ppc_heathrow_init(MachineState *machine) error_report("Only 6xx bus is supported on heathrow machine"); exit(1); } - pic = heathrow_pic_init(&pic_mem, 1, heathrow_irqs); + pic_dev = heathrow_pic_init(1, heathrow_irqs, &pic); pci_bus = pci_grackle_init(0xfec00000, pic, get_system_memory(), get_system_io()); pci_vga_init(pci_bus); - dev = qdev_create(NULL, TYPE_ESCC); - qdev_prop_set_uint32(dev, "disabled", 0); - qdev_prop_set_uint32(dev, "frequency", ESCC_CLOCK); - qdev_prop_set_uint32(dev, "it_shift", 4); - qdev_prop_set_chr(dev, "chrA", serial_hds[0]); - qdev_prop_set_chr(dev, "chrB", serial_hds[1]); - qdev_prop_set_uint32(dev, "chnBtype", escc_serial); - qdev_prop_set_uint32(dev, "chnAtype", escc_serial); - qdev_init_nofail(dev); - - s = SYS_BUS_DEVICE(dev); - sysbus_connect_irq(s, 0, pic[0x10]); - sysbus_connect_irq(s, 1, pic[0x0f]); - - escc_mem = &ESCC(s)->mmio; - - memory_region_init_alias(escc_bar, NULL, "escc-bar", - escc_mem, 0, memory_region_size(escc_mem)); - - for(i = 0; i < nb_nics; i++) + for (i = 0; i < nb_nics; i++) { pci_nic_init_nofail(&nd_table[i], pci_bus, "ne2k_pci", NULL); - + } ide_drive_get(hd, ARRAY_SIZE(hd)); - macio = pci_create(pci_bus, -1, TYPE_OLDWORLD_MACIO); + /* MacIO */ + macio = OLDWORLD_MACIO(pci_create(pci_bus, -1, TYPE_OLDWORLD_MACIO)); dev = DEVICE(macio); qdev_connect_gpio_out(dev, 0, pic[0x12]); /* CUDA */ - qdev_connect_gpio_out(dev, 1, pic[0x0D]); /* IDE-0 */ - qdev_connect_gpio_out(dev, 2, pic[0x02]); /* IDE-0 DMA */ - qdev_connect_gpio_out(dev, 3, pic[0x0E]); /* IDE-1 */ - qdev_connect_gpio_out(dev, 4, pic[0x03]); /* IDE-1 DMA */ + qdev_connect_gpio_out(dev, 1, pic[0x10]); /* ESCC-B */ + qdev_connect_gpio_out(dev, 2, pic[0x0F]); /* ESCC-A */ + qdev_connect_gpio_out(dev, 3, pic[0x0D]); /* IDE-0 */ + qdev_connect_gpio_out(dev, 4, pic[0x02]); /* IDE-0 DMA */ + qdev_connect_gpio_out(dev, 5, pic[0x0E]); /* IDE-1 */ + qdev_connect_gpio_out(dev, 6, pic[0x03]); /* IDE-1 DMA */ qdev_prop_set_uint64(dev, "frequency", tbfreq); - macio_init(macio, pic_mem, escc_bar); + object_property_set_link(OBJECT(macio), OBJECT(pic_dev), "pic", + &error_abort); + qdev_init_nofail(dev); macio_ide = MACIO_IDE(object_resolve_path_component(OBJECT(macio), "ide[0]")); diff --git a/hw/ppc/ppc440_pcix.c b/hw/ppc/ppc440_pcix.c new file mode 100644 index 0000000000..ab2626a9de --- /dev/null +++ b/hw/ppc/ppc440_pcix.c @@ -0,0 +1,528 @@ +/* + * Emulation of the ibm,plb-pcix PCI controller + * This is found in some 440 SoCs e.g. the 460EX. + * + * Copyright (c) 2016-2018 BALATON Zoltan + * + * Derived from ppc4xx_pci.c and pci-host/ppce500.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "hw/hw.h" +#include "hw/ppc/ppc.h" +#include "hw/ppc/ppc4xx.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_host.h" +#include "exec/address-spaces.h" +#include "trace.h" + +struct PLBOutMap { + uint64_t la; + uint64_t pcia; + uint32_t sa; + MemoryRegion mr; +}; + +struct PLBInMap { + uint64_t sa; + uint64_t la; + MemoryRegion mr; +}; + +#define TYPE_PPC440_PCIX_HOST_BRIDGE "ppc440-pcix-host" +#define PPC440_PCIX_HOST_BRIDGE(obj) \ + OBJECT_CHECK(PPC440PCIXState, (obj), TYPE_PPC440_PCIX_HOST_BRIDGE) + +#define PPC440_PCIX_NR_POMS 3 +#define PPC440_PCIX_NR_PIMS 3 + +typedef struct PPC440PCIXState { + PCIHostState parent_obj; + + PCIDevice *dev; + struct PLBOutMap pom[PPC440_PCIX_NR_POMS]; + struct PLBInMap pim[PPC440_PCIX_NR_PIMS]; + uint32_t sts; + qemu_irq irq[PCI_NUM_PINS]; + AddressSpace bm_as; + MemoryRegion bm; + + MemoryRegion container; + MemoryRegion iomem; + MemoryRegion busmem; +} PPC440PCIXState; + +#define PPC440_REG_BASE 0x80000 +#define PPC440_REG_SIZE 0xff + +#define PCIC0_CFGADDR 0x0 +#define PCIC0_CFGDATA 0x4 + +#define PCIX0_POM0LAL 0x68 +#define PCIX0_POM0LAH 0x6c +#define PCIX0_POM0SA 0x70 +#define PCIX0_POM0PCIAL 0x74 +#define PCIX0_POM0PCIAH 0x78 +#define PCIX0_POM1LAL 0x7c +#define PCIX0_POM1LAH 0x80 +#define PCIX0_POM1SA 0x84 +#define PCIX0_POM1PCIAL 0x88 +#define PCIX0_POM1PCIAH 0x8c +#define PCIX0_POM2SA 0x90 + +#define PCIX0_PIM0SAL 0x98 +#define PCIX0_PIM0LAL 0x9c +#define PCIX0_PIM0LAH 0xa0 +#define PCIX0_PIM1SA 0xa4 +#define PCIX0_PIM1LAL 0xa8 +#define PCIX0_PIM1LAH 0xac +#define PCIX0_PIM2SAL 0xb0 +#define PCIX0_PIM2LAL 0xb4 +#define PCIX0_PIM2LAH 0xb8 +#define PCIX0_PIM0SAH 0xf8 +#define PCIX0_PIM2SAH 0xfc + +#define PCIX0_STS 0xe0 + +#define PCI_ALL_SIZE (PPC440_REG_BASE + PPC440_REG_SIZE) + +static void ppc440_pcix_clear_region(MemoryRegion *parent, + MemoryRegion *mem) +{ + if (memory_region_is_mapped(mem)) { + memory_region_del_subregion(parent, mem); + object_unparent(OBJECT(mem)); + } +} + +/* DMA mapping */ +static void ppc440_pcix_update_pim(PPC440PCIXState *s, int idx) +{ + MemoryRegion *mem = &s->pim[idx].mr; + char *name; + uint64_t size; + + /* Before we modify anything, unmap and destroy the region */ + ppc440_pcix_clear_region(&s->bm, mem); + + if (!(s->pim[idx].sa & 1)) { + /* Not enabled, nothing to do */ + return; + } + + name = g_strdup_printf("PCI Inbound Window %d", idx); + size = ~(s->pim[idx].sa & ~7ULL) + 1; + memory_region_init_alias(mem, OBJECT(s), name, get_system_memory(), + s->pim[idx].la, size); + memory_region_add_subregion_overlap(&s->bm, 0, mem, -1); + g_free(name); + + trace_ppc440_pcix_update_pim(idx, size, s->pim[idx].la); +} + +/* BAR mapping */ +static void ppc440_pcix_update_pom(PPC440PCIXState *s, int idx) +{ + MemoryRegion *mem = &s->pom[idx].mr; + MemoryRegion *address_space_mem = get_system_memory(); + char *name; + uint32_t size; + + /* Before we modify anything, unmap and destroy the region */ + ppc440_pcix_clear_region(address_space_mem, mem); + + if (!(s->pom[idx].sa & 1)) { + /* Not enabled, nothing to do */ + return; + } + + name = g_strdup_printf("PCI Outbound Window %d", idx); + size = ~(s->pom[idx].sa & 0xfffffffe) + 1; + if (!size) { + size = 0xffffffff; + } + memory_region_init_alias(mem, OBJECT(s), name, &s->busmem, + s->pom[idx].pcia, size); + memory_region_add_subregion(address_space_mem, s->pom[idx].la, mem); + g_free(name); + + trace_ppc440_pcix_update_pom(idx, size, s->pom[idx].la, s->pom[idx].pcia); +} + +static void ppc440_pcix_reg_write4(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + struct PPC440PCIXState *s = opaque; + + trace_ppc440_pcix_reg_read(addr, val); + switch (addr) { + case PCI_VENDOR_ID ... PCI_MAX_LAT: + stl_le_p(s->dev->config + addr, val); + break; + + case PCIX0_POM0LAL: + s->pom[0].la &= 0xffffffff00000000ULL; + s->pom[0].la |= val; + ppc440_pcix_update_pom(s, 0); + break; + case PCIX0_POM0LAH: + s->pom[0].la &= 0xffffffffULL; + s->pom[0].la |= val << 32; + ppc440_pcix_update_pom(s, 0); + break; + case PCIX0_POM0SA: + s->pom[0].sa = val; + ppc440_pcix_update_pom(s, 0); + break; + case PCIX0_POM0PCIAL: + s->pom[0].pcia &= 0xffffffff00000000ULL; + s->pom[0].pcia |= val; + ppc440_pcix_update_pom(s, 0); + break; + case PCIX0_POM0PCIAH: + s->pom[0].pcia &= 0xffffffffULL; + s->pom[0].pcia |= val << 32; + ppc440_pcix_update_pom(s, 0); + break; + case PCIX0_POM1LAL: + s->pom[1].la &= 0xffffffff00000000ULL; + s->pom[1].la |= val; + ppc440_pcix_update_pom(s, 1); + break; + case PCIX0_POM1LAH: + s->pom[1].la &= 0xffffffffULL; + s->pom[1].la |= val << 32; + ppc440_pcix_update_pom(s, 1); + break; + case PCIX0_POM1SA: + s->pom[1].sa = val; + ppc440_pcix_update_pom(s, 1); + break; + case PCIX0_POM1PCIAL: + s->pom[1].pcia &= 0xffffffff00000000ULL; + s->pom[1].pcia |= val; + ppc440_pcix_update_pom(s, 1); + break; + case PCIX0_POM1PCIAH: + s->pom[1].pcia &= 0xffffffffULL; + s->pom[1].pcia |= val << 32; + ppc440_pcix_update_pom(s, 1); + break; + case PCIX0_POM2SA: + s->pom[2].sa = val; + break; + + case PCIX0_PIM0SAL: + s->pim[0].sa &= 0xffffffff00000000ULL; + s->pim[0].sa |= val; + ppc440_pcix_update_pim(s, 0); + break; + case PCIX0_PIM0LAL: + s->pim[0].la &= 0xffffffff00000000ULL; + s->pim[0].la |= val; + ppc440_pcix_update_pim(s, 0); + break; + case PCIX0_PIM0LAH: + s->pim[0].la &= 0xffffffffULL; + s->pim[0].la |= val << 32; + ppc440_pcix_update_pim(s, 0); + break; + case PCIX0_PIM1SA: + s->pim[1].sa = val; + ppc440_pcix_update_pim(s, 1); + break; + case PCIX0_PIM1LAL: + s->pim[1].la &= 0xffffffff00000000ULL; + s->pim[1].la |= val; + ppc440_pcix_update_pim(s, 1); + break; + case PCIX0_PIM1LAH: + s->pim[1].la &= 0xffffffffULL; + s->pim[1].la |= val << 32; + ppc440_pcix_update_pim(s, 1); + break; + case PCIX0_PIM2SAL: + s->pim[2].sa &= 0xffffffff00000000ULL; + s->pim[2].sa = val; + ppc440_pcix_update_pim(s, 2); + break; + case PCIX0_PIM2LAL: + s->pim[2].la &= 0xffffffff00000000ULL; + s->pim[2].la |= val; + ppc440_pcix_update_pim(s, 2); + break; + case PCIX0_PIM2LAH: + s->pim[2].la &= 0xffffffffULL; + s->pim[2].la |= val << 32; + ppc440_pcix_update_pim(s, 2); + break; + + case PCIX0_STS: + s->sts = val; + break; + + case PCIX0_PIM0SAH: + s->pim[0].sa &= 0xffffffffULL; + s->pim[0].sa |= val << 32; + ppc440_pcix_update_pim(s, 0); + break; + case PCIX0_PIM2SAH: + s->pim[2].sa &= 0xffffffffULL; + s->pim[2].sa |= val << 32; + ppc440_pcix_update_pim(s, 2); + break; + + default: + error_report("%s: unhandled PCI internal register 0x%lx", __func__, + (unsigned long)addr); + break; + } +} + +static uint64_t ppc440_pcix_reg_read4(void *opaque, hwaddr addr, + unsigned size) +{ + struct PPC440PCIXState *s = opaque; + uint32_t val; + + switch (addr) { + case PCI_VENDOR_ID ... PCI_MAX_LAT: + val = ldl_le_p(s->dev->config + addr); + break; + + case PCIX0_POM0LAL: + val = s->pom[0].la; + break; + case PCIX0_POM0LAH: + val = s->pom[0].la >> 32; + break; + case PCIX0_POM0SA: + val = s->pom[0].sa; + break; + case PCIX0_POM0PCIAL: + val = s->pom[0].pcia; + break; + case PCIX0_POM0PCIAH: + val = s->pom[0].pcia >> 32; + break; + case PCIX0_POM1LAL: + val = s->pom[1].la; + break; + case PCIX0_POM1LAH: + val = s->pom[1].la >> 32; + break; + case PCIX0_POM1SA: + val = s->pom[1].sa; + break; + case PCIX0_POM1PCIAL: + val = s->pom[1].pcia; + break; + case PCIX0_POM1PCIAH: + val = s->pom[1].pcia >> 32; + break; + case PCIX0_POM2SA: + val = s->pom[2].sa; + break; + + case PCIX0_PIM0SAL: + val = s->pim[0].sa; + break; + case PCIX0_PIM0LAL: + val = s->pim[0].la; + break; + case PCIX0_PIM0LAH: + val = s->pim[0].la >> 32; + break; + case PCIX0_PIM1SA: + val = s->pim[1].sa; + break; + case PCIX0_PIM1LAL: + val = s->pim[1].la; + break; + case PCIX0_PIM1LAH: + val = s->pim[1].la >> 32; + break; + case PCIX0_PIM2SAL: + val = s->pim[2].sa; + break; + case PCIX0_PIM2LAL: + val = s->pim[2].la; + break; + case PCIX0_PIM2LAH: + val = s->pim[2].la >> 32; + break; + + case PCIX0_STS: + val = s->sts; + break; + + case PCIX0_PIM0SAH: + val = s->pim[0].sa >> 32; + break; + case PCIX0_PIM2SAH: + val = s->pim[2].sa >> 32; + break; + + default: + error_report("%s: invalid PCI internal register 0x%lx", __func__, + (unsigned long)addr); + val = 0; + } + + trace_ppc440_pcix_reg_read(addr, val); + return val; +} + +static const MemoryRegionOps pci_reg_ops = { + .read = ppc440_pcix_reg_read4, + .write = ppc440_pcix_reg_write4, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void ppc440_pcix_reset(DeviceState *dev) +{ + struct PPC440PCIXState *s = PPC440_PCIX_HOST_BRIDGE(dev); + int i; + + for (i = 0; i < PPC440_PCIX_NR_POMS; i++) { + ppc440_pcix_clear_region(get_system_memory(), &s->pom[i].mr); + } + for (i = 0; i < PPC440_PCIX_NR_PIMS; i++) { + ppc440_pcix_clear_region(&s->bm, &s->pim[i].mr); + } + memset(s->pom, 0, sizeof(s->pom)); + memset(s->pim, 0, sizeof(s->pim)); + for (i = 0; i < PPC440_PCIX_NR_PIMS; i++) { + s->pim[i].sa = 0xffffffff00000000ULL; + } + s->sts = 0; +} + +/* All pins from each slot are tied to a single board IRQ. + * This may need further refactoring for other boards. */ +static int ppc440_pcix_map_irq(PCIDevice *pci_dev, int irq_num) +{ + int slot = pci_dev->devfn >> 3; + trace_ppc440_pcix_map_irq(pci_dev->devfn, irq_num, slot); + return slot - 1; +} + +static void ppc440_pcix_set_irq(void *opaque, int irq_num, int level) +{ + qemu_irq *pci_irqs = opaque; + + trace_ppc440_pcix_set_irq(irq_num); + if (irq_num < 0) { + error_report("%s: PCI irq %d", __func__, irq_num); + return; + } + qemu_set_irq(pci_irqs[irq_num], level); +} + +static AddressSpace *ppc440_pcix_set_iommu(PCIBus *b, void *opaque, int devfn) +{ + PPC440PCIXState *s = opaque; + + return &s->bm_as; +} + +/* The default pci_host_data_{read,write} functions in pci/pci_host.c + * deny access to registers without bit 31 set but our clients want + * this to work so we have to override these here */ +static void pci_host_data_write(void *opaque, hwaddr addr, + uint64_t val, unsigned len) +{ + PCIHostState *s = opaque; + pci_data_write(s->bus, s->config_reg | (addr & 3), val, len); +} + +static uint64_t pci_host_data_read(void *opaque, + hwaddr addr, unsigned len) +{ + PCIHostState *s = opaque; + uint32_t val; + val = pci_data_read(s->bus, s->config_reg | (addr & 3), len); + return val; +} + +const MemoryRegionOps ppc440_pcix_host_data_ops = { + .read = pci_host_data_read, + .write = pci_host_data_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static int ppc440_pcix_initfn(SysBusDevice *dev) +{ + PPC440PCIXState *s; + PCIHostState *h; + int i; + + h = PCI_HOST_BRIDGE(dev); + s = PPC440_PCIX_HOST_BRIDGE(dev); + + for (i = 0; i < ARRAY_SIZE(s->irq); i++) { + sysbus_init_irq(dev, &s->irq[i]); + } + + memory_region_init(&s->busmem, OBJECT(dev), "pci bus memory", UINT64_MAX); + h->bus = pci_register_root_bus(DEVICE(dev), NULL, ppc440_pcix_set_irq, + ppc440_pcix_map_irq, s->irq, &s->busmem, + get_system_io(), PCI_DEVFN(0, 0), 4, TYPE_PCI_BUS); + + s->dev = pci_create_simple(h->bus, PCI_DEVFN(0, 0), "ppc4xx-host-bridge"); + + memory_region_init(&s->bm, OBJECT(s), "bm-ppc440-pcix", UINT64_MAX); + memory_region_add_subregion(&s->bm, 0x0, &s->busmem); + address_space_init(&s->bm_as, &s->bm, "pci-bm"); + pci_setup_iommu(h->bus, ppc440_pcix_set_iommu, s); + + memory_region_init(&s->container, OBJECT(s), "pci-container", PCI_ALL_SIZE); + memory_region_init_io(&h->conf_mem, OBJECT(s), &pci_host_conf_le_ops, + h, "pci-conf-idx", 4); + memory_region_init_io(&h->data_mem, OBJECT(s), &ppc440_pcix_host_data_ops, + h, "pci-conf-data", 4); + memory_region_init_io(&s->iomem, OBJECT(s), &pci_reg_ops, s, + "pci.reg", PPC440_REG_SIZE); + memory_region_add_subregion(&s->container, PCIC0_CFGADDR, &h->conf_mem); + memory_region_add_subregion(&s->container, PCIC0_CFGDATA, &h->data_mem); + memory_region_add_subregion(&s->container, PPC440_REG_BASE, &s->iomem); + sysbus_init_mmio(dev, &s->container); + + return 0; +} + +static void ppc440_pcix_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + k->init = ppc440_pcix_initfn; + dc->reset = ppc440_pcix_reset; +} + +static const TypeInfo ppc440_pcix_info = { + .name = TYPE_PPC440_PCIX_HOST_BRIDGE, + .parent = TYPE_PCI_HOST_BRIDGE, + .instance_size = sizeof(PPC440PCIXState), + .class_init = ppc440_pcix_class_init, +}; + +static void ppc440_pcix_register_types(void) +{ + type_register_static(&ppc440_pcix_info); +} + +type_init(ppc440_pcix_register_types) diff --git a/hw/ppc/ppc440_uc.c b/hw/ppc/ppc440_uc.c index 4e2523a64f..976ab2b5d8 100644 --- a/hw/ppc/ppc440_uc.c +++ b/hw/ppc/ppc440_uc.c @@ -1050,6 +1050,9 @@ static void ppc460ex_pcie_realize(DeviceState *dev, Error **errp) case DCRN_PCIE1_BASE: id = 1; break; + default: + error_setg(errp, "invalid PCIe DCRN base"); + return; } snprintf(buf, sizeof(buf), "pcie%d-io", id); memory_region_init(&s->iomem, OBJECT(s), buf, UINT64_MAX); diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c new file mode 100644 index 0000000000..70b8e76d9c --- /dev/null +++ b/hw/ppc/sam460ex.c @@ -0,0 +1,603 @@ +/* + * QEMU aCube Sam460ex board emulation + * + * Copyright (c) 2012 François Revol + * Copyright (c) 2016-2018 BALATON Zoltan + * + * This file is derived from hw/ppc440_bamboo.c, + * the copyright for that material belongs to the original owners. + * + * This work is licensed under the GNU GPL license version 2 or later. + * + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qemu/cutils.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "hw/hw.h" +#include "sysemu/blockdev.h" +#include "hw/boards.h" +#include "sysemu/kvm.h" +#include "kvm_ppc.h" +#include "sysemu/device_tree.h" +#include "sysemu/block-backend.h" +#include "hw/loader.h" +#include "elf.h" +#include "exec/address-spaces.h" +#include "exec/memory.h" +#include "hw/ppc/ppc440.h" +#include "hw/ppc/ppc405.h" +#include "hw/block/flash.h" +#include "sysemu/sysemu.h" +#include "sysemu/qtest.h" +#include "hw/sysbus.h" +#include "hw/char/serial.h" +#include "hw/i2c/ppc4xx_i2c.h" +#include "hw/i2c/smbus.h" +#include "hw/usb/hcd-ehci.h" + +#define BINARY_DEVICE_TREE_FILE "canyonlands.dtb" +#define UBOOT_FILENAME "u-boot-sam460-20100605.bin" +/* to extract the official U-Boot bin from the updater: */ +/* dd bs=1 skip=$(($(stat -c '%s' updater/updater-460) - 0x80000)) \ + if=updater/updater-460 of=u-boot-sam460-20100605.bin */ + +/* from Sam460 U-Boot include/configs/Sam460ex.h */ +#define FLASH_BASE 0xfff00000 +#define FLASH_BASE_H 0x4 +#define FLASH_SIZE (1 << 20) +#define UBOOT_LOAD_BASE 0xfff80000 +#define UBOOT_SIZE 0x00080000 +#define UBOOT_ENTRY 0xfffffffc + +/* from U-Boot */ +#define EPAPR_MAGIC (0x45504150) +#define KERNEL_ADDR 0x1000000 +#define FDT_ADDR 0x1800000 +#define RAMDISK_ADDR 0x1900000 + +/* Sam460ex IRQ MAP: + IRQ0 = ETH_INT + IRQ1 = FPGA_INT + IRQ2 = PCI_INT (PCIA, PCIB, PCIC, PCIB) + IRQ3 = FPGA_INT2 + IRQ11 = RTC_INT + IRQ12 = SM502_INT +*/ + +#define SDRAM_NR_BANKS 4 + +/* FIXME: See u-boot.git 8ac41e, also fix in ppc440_uc.c */ +static const unsigned int ppc460ex_sdram_bank_sizes[] = { + 1024 << 20, 512 << 20, 256 << 20, 128 << 20, 64 << 20, 32 << 20, 0 +}; + +struct boot_info { + uint32_t dt_base; + uint32_t dt_size; + uint32_t entry; +}; + +/*****************************************************************************/ +/* SPD eeprom content from mips_malta.c */ + +struct _eeprom24c0x_t { + uint8_t tick; + uint8_t address; + uint8_t command; + uint8_t ack; + uint8_t scl; + uint8_t sda; + uint8_t data; + uint8_t contents[256]; +}; + +typedef struct _eeprom24c0x_t eeprom24c0x_t; + +static eeprom24c0x_t spd_eeprom = { + .contents = { + /* 00000000: */ 0x80, 0x08, 0xFF, 0x0D, 0x0A, 0xFF, 0x40, 0x00, + /* 00000008: */ 0x04, 0x75, 0x54, 0x00, 0x82, 0x08, 0x00, 0x01, + /* 00000010: */ 0x8F, 0x04, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, + /* 00000018: */ 0x00, 0x00, 0x00, 0x14, 0x0F, 0x14, 0x2D, 0xFF, + /* 00000020: */ 0x15, 0x08, 0x15, 0x08, 0x00, 0x00, 0x00, 0x00, + /* 00000028: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 00000030: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 00000038: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0xD0, + /* 00000040: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 00000048: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 00000050: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 00000058: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 00000060: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 00000068: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 00000070: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 00000078: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0xF4, + }, +}; + +static void generate_eeprom_spd(uint8_t *eeprom, ram_addr_t ram_size) +{ + enum { SDR = 0x4, DDR1 = 0x7, DDR2 = 0x8 } type; + uint8_t *spd = spd_eeprom.contents; + uint8_t nbanks = 0; + uint16_t density = 0; + int i; + + /* work in terms of MB */ + ram_size >>= 20; + + while ((ram_size >= 4) && (nbanks <= 2)) { + int sz_log2 = MIN(31 - clz32(ram_size), 14); + nbanks++; + density |= 1 << (sz_log2 - 2); + ram_size -= 1 << sz_log2; + } + + /* split to 2 banks if possible */ + if ((nbanks == 1) && (density > 1)) { + nbanks++; + density >>= 1; + } + + if (density & 0xff00) { + density = (density & 0xe0) | ((density >> 8) & 0x1f); + type = DDR2; + } else if (!(density & 0x1f)) { + type = DDR2; + } else { + type = SDR; + } + + if (ram_size) { + warn_report("SPD cannot represent final " RAM_ADDR_FMT "MB" + " of SDRAM", ram_size); + } + + /* fill in SPD memory information */ + spd[2] = type; + spd[5] = nbanks; + spd[31] = density; + + /* XXX: this is totally random */ + spd[9] = 0x10; /* CAS tcyc */ + spd[18] = 0x20; /* CAS bit */ + spd[23] = 0x10; /* CAS tcyc */ + spd[25] = 0x10; /* CAS tcyc */ + + /* checksum */ + spd[63] = 0; + for (i = 0; i < 63; i++) { + spd[63] += spd[i]; + } + + /* copy for SMBUS */ + memcpy(eeprom, spd, sizeof(spd_eeprom.contents)); +} + +static void generate_eeprom_serial(uint8_t *eeprom) +{ + int i, pos = 0; + uint8_t mac[6] = { 0x00 }; + uint8_t sn[5] = { 0x01, 0x23, 0x45, 0x67, 0x89 }; + + /* version */ + eeprom[pos++] = 0x01; + + /* count */ + eeprom[pos++] = 0x02; + + /* MAC address */ + eeprom[pos++] = 0x01; /* MAC */ + eeprom[pos++] = 0x06; /* length */ + memcpy(&eeprom[pos], mac, sizeof(mac)); + pos += sizeof(mac); + + /* serial number */ + eeprom[pos++] = 0x02; /* serial */ + eeprom[pos++] = 0x05; /* length */ + memcpy(&eeprom[pos], sn, sizeof(sn)); + pos += sizeof(sn); + + /* checksum */ + eeprom[pos] = 0; + for (i = 0; i < pos; i++) { + eeprom[pos] += eeprom[i]; + } +} + +/*****************************************************************************/ + +static int sam460ex_load_uboot(void) +{ + DriveInfo *dinfo; + BlockBackend *blk = NULL; + hwaddr base = FLASH_BASE | ((hwaddr)FLASH_BASE_H << 32); + long bios_size = FLASH_SIZE; + int fl_sectors; + + dinfo = drive_get(IF_PFLASH, 0, 0); + if (dinfo) { + blk = blk_by_legacy_dinfo(dinfo); + bios_size = blk_getlength(blk); + } + fl_sectors = (bios_size + 65535) >> 16; + + if (!pflash_cfi01_register(base, NULL, "sam460ex.flash", bios_size, + blk, (64 * 1024), fl_sectors, + 1, 0x89, 0x18, 0x0000, 0x0, 1)) { + error_report("qemu: Error registering flash memory."); + /* XXX: return an error instead? */ + exit(1); + } + + if (!blk) { + /*error_report("No flash image given with the 'pflash' parameter," + " using default u-boot image");*/ + base = UBOOT_LOAD_BASE | ((hwaddr)FLASH_BASE_H << 32); + rom_add_file_fixed(UBOOT_FILENAME, base, -1); + } + + return 0; +} + +static int sam460ex_load_device_tree(hwaddr addr, + uint32_t ramsize, + hwaddr initrd_base, + hwaddr initrd_size, + const char *kernel_cmdline) +{ + int ret = -1; + uint32_t mem_reg_property[] = { 0, 0, cpu_to_be32(ramsize) }; + char *filename; + int fdt_size; + void *fdt; + uint32_t tb_freq = 50000000; + uint32_t clock_freq = 50000000; + + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); + if (!filename) { + goto out; + } + fdt = load_device_tree(filename, &fdt_size); + g_free(filename); + if (fdt == NULL) { + goto out; + } + + /* Manipulate device tree in memory. */ + + ret = qemu_fdt_setprop(fdt, "/memory", "reg", mem_reg_property, + sizeof(mem_reg_property)); + if (ret < 0) { + error_report("couldn't set /memory/reg"); + } + + /* default FDT doesn't have a /chosen node... */ + qemu_fdt_add_subnode(fdt, "/chosen"); + + ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start", + initrd_base); + if (ret < 0) { + error_report("couldn't set /chosen/linux,initrd-start"); + } + + ret = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", + (initrd_base + initrd_size)); + if (ret < 0) { + error_report("couldn't set /chosen/linux,initrd-end"); + } + + ret = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", + kernel_cmdline); + if (ret < 0) { + error_report("couldn't set /chosen/bootargs"); + } + + /* Copy data from the host device tree into the guest. Since the guest can + * directly access the timebase without host involvement, we must expose + * the correct frequencies. */ + if (kvm_enabled()) { + tb_freq = kvmppc_get_tbfreq(); + clock_freq = kvmppc_get_clockfreq(); + } + + qemu_fdt_setprop_cell(fdt, "/cpus/cpu@0", "clock-frequency", + clock_freq); + qemu_fdt_setprop_cell(fdt, "/cpus/cpu@0", "timebase-frequency", + tb_freq); + + rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr); + g_free(fdt); + ret = fdt_size; + +out: + + return ret; +} + +/* Create reset TLB entries for BookE, mapping only the flash memory. */ +static void mmubooke_create_initial_mapping_uboot(CPUPPCState *env) +{ + ppcemb_tlb_t *tlb = &env->tlb.tlbe[0]; + + /* on reset the flash is mapped by a shadow TLB, + * but since we don't implement them we need to use + * the same values U-Boot will use to avoid a fault. + */ + tlb->attr = 0; + tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4); + tlb->size = 0x10000000; /* up to 0xffffffff */ + tlb->EPN = 0xf0000000 & TARGET_PAGE_MASK; + tlb->RPN = (0xf0000000 & TARGET_PAGE_MASK) | 0x4; + tlb->PID = 0; +} + +/* Create reset TLB entries for BookE, spanning the 32bit addr space. */ +static void mmubooke_create_initial_mapping(CPUPPCState *env, + target_ulong va, + hwaddr pa) +{ + ppcemb_tlb_t *tlb = &env->tlb.tlbe[0]; + + tlb->attr = 0; + tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4); + tlb->size = 1 << 31; /* up to 0x80000000 */ + tlb->EPN = va & TARGET_PAGE_MASK; + tlb->RPN = pa & TARGET_PAGE_MASK; + tlb->PID = 0; +} + +static void main_cpu_reset(void *opaque) +{ + PowerPCCPU *cpu = opaque; + CPUPPCState *env = &cpu->env; + struct boot_info *bi = env->load_info; + + cpu_reset(CPU(cpu)); + + /* either we have a kernel to boot or we jump to U-Boot */ + if (bi->entry != UBOOT_ENTRY) { + env->gpr[1] = (16 << 20) - 8; + env->gpr[3] = FDT_ADDR; + env->nip = bi->entry; + + /* Create a mapping for the kernel. */ + mmubooke_create_initial_mapping(env, 0, 0); + env->gpr[6] = tswap32(EPAPR_MAGIC); + env->gpr[7] = (16 << 20) - 8; /*bi->ima_size;*/ + + } else { + env->nip = UBOOT_ENTRY; + mmubooke_create_initial_mapping_uboot(env); + } +} + +static void sam460ex_init(MachineState *machine) +{ + MemoryRegion *address_space_mem = get_system_memory(); + MemoryRegion *isa = g_new(MemoryRegion, 1); + MemoryRegion *ram_memories = g_new(MemoryRegion, SDRAM_NR_BANKS); + hwaddr ram_bases[SDRAM_NR_BANKS]; + hwaddr ram_sizes[SDRAM_NR_BANKS]; + MemoryRegion *l2cache_ram = g_new(MemoryRegion, 1); + qemu_irq *irqs, *uic[4]; + PCIBus *pci_bus; + PowerPCCPU *cpu; + CPUPPCState *env; + PPC4xxI2CState *i2c[2]; + hwaddr entry = UBOOT_ENTRY; + hwaddr loadaddr = 0; + target_long initrd_size = 0; + DeviceState *dev; + SysBusDevice *sbdev; + int success; + int i; + struct boot_info *boot_info; + const size_t smbus_eeprom_size = 8 * 256; + uint8_t *smbus_eeprom_buf = g_malloc0(smbus_eeprom_size); + + cpu = POWERPC_CPU(cpu_create(machine->cpu_type)); + env = &cpu->env; + if (env->mmu_model != POWERPC_MMU_BOOKE) { + error_report("Only MMU model BookE is supported by this machine."); + exit(1); + } + +#ifdef TARGET_PPCEMB + if (!qtest_enabled()) { + warn_report("qemu-system-ppcemb is deprecated, " + "please use qemu-system-ppc instead."); + } +#endif + + qemu_register_reset(main_cpu_reset, cpu); + boot_info = g_malloc0(sizeof(*boot_info)); + env->load_info = boot_info; + + ppc_booke_timers_init(cpu, 50000000, 0); + ppc_dcr_init(env, NULL, NULL); + + /* PLB arbitrer */ + ppc4xx_plb_init(env); + + /* interrupt controllers */ + irqs = g_malloc0(sizeof(*irqs) * PPCUIC_OUTPUT_NB); + irqs[PPCUIC_OUTPUT_INT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT]; + irqs[PPCUIC_OUTPUT_CINT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_CINT]; + uic[0] = ppcuic_init(env, irqs, 0xc0, 0, 1); + uic[1] = ppcuic_init(env, &uic[0][30], 0xd0, 0, 1); + uic[2] = ppcuic_init(env, &uic[0][10], 0xe0, 0, 1); + uic[3] = ppcuic_init(env, &uic[0][16], 0xf0, 0, 1); + + /* SDRAM controller */ + memset(ram_bases, 0, sizeof(ram_bases)); + memset(ram_sizes, 0, sizeof(ram_sizes)); + /* put all RAM on first bank because board has one slot + * and firmware only checks that */ + machine->ram_size = ppc4xx_sdram_adjust(machine->ram_size, 1, + ram_memories, ram_bases, ram_sizes, + ppc460ex_sdram_bank_sizes); + + /* FIXME: does 460EX have ECC interrupts? */ + ppc440_sdram_init(env, SDRAM_NR_BANKS, ram_memories, + ram_bases, ram_sizes, 1); + + /* generate SPD EEPROM data */ + for (i = 0; i < SDRAM_NR_BANKS; i++) { + generate_eeprom_spd(&smbus_eeprom_buf[i * 256], ram_sizes[i]); + } + generate_eeprom_serial(&smbus_eeprom_buf[4 * 256]); + generate_eeprom_serial(&smbus_eeprom_buf[6 * 256]); + + /* IIC controllers */ + dev = sysbus_create_simple(TYPE_PPC4xx_I2C, 0x4ef600700, uic[0][2]); + i2c[0] = PPC4xx_I2C(dev); + object_property_set_bool(OBJECT(dev), true, "realized", NULL); + smbus_eeprom_init(i2c[0]->bus, 8, smbus_eeprom_buf, smbus_eeprom_size); + g_free(smbus_eeprom_buf); + + dev = sysbus_create_simple(TYPE_PPC4xx_I2C, 0x4ef600800, uic[0][3]); + i2c[1] = PPC4xx_I2C(dev); + + /* External bus controller */ + ppc405_ebc_init(env); + + /* CPR */ + ppc4xx_cpr_init(env); + + /* PLB to AHB bridge */ + ppc4xx_ahb_init(env); + + /* System DCRs */ + ppc4xx_sdr_init(env); + + /* MAL */ + ppc4xx_mal_init(env, 4, 16, &uic[2][3]); + + /* 256K of L2 cache as memory */ + ppc4xx_l2sram_init(env); + /* FIXME: remove this after fixing l2sram mapping in ppc440_uc.c? */ + memory_region_init_ram(l2cache_ram, NULL, "ppc440.l2cache_ram", 256 << 10, + &error_abort); + memory_region_add_subregion(address_space_mem, 0x400000000LL, l2cache_ram); + + /* USB */ + sysbus_create_simple(TYPE_PPC4xx_EHCI, 0x4bffd0400, uic[2][29]); + dev = qdev_create(NULL, "sysbus-ohci"); + qdev_prop_set_string(dev, "masterbus", "usb-bus.0"); + qdev_prop_set_uint32(dev, "num-ports", 6); + qdev_init_nofail(dev); + sbdev = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(sbdev, 0, 0x4bffd0000); + sysbus_connect_irq(sbdev, 0, uic[2][30]); + usb_create_simple(usb_bus_find(-1), "usb-kbd"); + usb_create_simple(usb_bus_find(-1), "usb-mouse"); + + /* PCI bus */ + ppc460ex_pcie_init(env); + /* FIXME: is this correct? */ + dev = sysbus_create_varargs("ppc440-pcix-host", 0xc0ec00000, + uic[1][0], uic[1][20], uic[1][21], uic[1][22], + NULL); + pci_bus = (PCIBus *)qdev_get_child_bus(dev, "pci.0"); + if (!pci_bus) { + error_report("couldn't create PCI controller!"); + exit(1); + } + memory_region_init_alias(isa, NULL, "isa_mmio", get_system_io(), + 0, 0x10000); + memory_region_add_subregion(get_system_memory(), 0xc08000000, isa); + + /* PCI devices */ + pci_create_simple(pci_bus, PCI_DEVFN(6, 0), "sm501"); + /* SoC has a single SATA port but we don't emulate that yet + * However, firmware and usual clients have driver for SiI311x + * so add one for convenience by default */ + if (defaults_enabled()) { + pci_create_simple(pci_bus, -1, "sii3112"); + } + + /* SoC has 4 UARTs + * but board has only one wired and two are present in fdt */ + if (serial_hds[0] != NULL) { + serial_mm_init(address_space_mem, 0x4ef600300, 0, uic[1][1], + PPC_SERIAL_MM_BAUDBASE, serial_hds[0], + DEVICE_BIG_ENDIAN); + } + if (serial_hds[1] != NULL) { + serial_mm_init(address_space_mem, 0x4ef600400, 0, uic[0][1], + PPC_SERIAL_MM_BAUDBASE, serial_hds[1], + DEVICE_BIG_ENDIAN); + } + + /* Load U-Boot image. */ + if (!machine->kernel_filename) { + success = sam460ex_load_uboot(); + if (success < 0) { + error_report("qemu: could not load firmware"); + exit(1); + } + } + + /* Load kernel. */ + if (machine->kernel_filename) { + success = load_uimage(machine->kernel_filename, &entry, &loadaddr, + NULL, NULL, NULL); + if (success < 0) { + uint64_t elf_entry, elf_lowaddr; + + success = load_elf(machine->kernel_filename, NULL, NULL, &elf_entry, + &elf_lowaddr, NULL, 1, PPC_ELF_MACHINE, 0, 0); + entry = elf_entry; + loadaddr = elf_lowaddr; + } + /* XXX try again as binary */ + if (success < 0) { + error_report("qemu: could not load kernel '%s'", + machine->kernel_filename); + exit(1); + } + } + + /* Load initrd. */ + if (machine->initrd_filename) { + initrd_size = load_image_targphys(machine->initrd_filename, + RAMDISK_ADDR, + machine->ram_size - RAMDISK_ADDR); + if (initrd_size < 0) { + error_report("qemu: could not load ram disk '%s' at %x", + machine->initrd_filename, RAMDISK_ADDR); + exit(1); + } + } + + /* If we're loading a kernel directly, we must load the device tree too. */ + if (machine->kernel_filename) { + int dt_size; + + dt_size = sam460ex_load_device_tree(FDT_ADDR, machine->ram_size, + RAMDISK_ADDR, initrd_size, + machine->kernel_cmdline); + if (dt_size < 0) { + error_report("couldn't load device tree"); + exit(1); + } + + boot_info->dt_base = FDT_ADDR; + boot_info->dt_size = dt_size; + } + + boot_info->entry = entry; +} + +static void sam460ex_machine_init(MachineClass *mc) +{ + mc->desc = "aCube Sam460ex"; + mc->init = sam460ex_init; + mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("460exb"); + mc->default_ram_size = 512 * M_BYTE; +} + +DEFINE_MACHINE("sam460ex", sam460ex_machine_init) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 83c9d66dd5..7e1c858566 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -105,12 +105,14 @@ */ static int spapr_vcpu_id(sPAPRMachineState *spapr, int cpu_index) { + assert(spapr->vsmt); return (cpu_index / smp_threads) * spapr->vsmt + cpu_index % smp_threads; } static bool spapr_is_thread0_in_vcore(sPAPRMachineState *spapr, PowerPCCPU *cpu) { + assert(spapr->vsmt); return spapr_get_vcpu_id(cpu) % spapr->vsmt == 0; } @@ -177,13 +179,13 @@ static void pre_2_10_vmstate_unregister_dummy_icp(int i) static int xics_max_server_number(sPAPRMachineState *spapr) { + assert(spapr->vsmt); return DIV_ROUND_UP(max_cpus * spapr->vsmt, smp_threads); } static void xics_system_init(MachineState *machine, int nr_irqs, Error **errp) { sPAPRMachineState *spapr = SPAPR_MACHINE(machine); - sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine); if (kvm_enabled()) { if (machine_kernel_irqchip_allowed(machine) && @@ -205,17 +207,6 @@ static void xics_system_init(MachineState *machine, int nr_irqs, Error **errp) return; } } - - if (smc->pre_2_10_has_unused_icps) { - int i; - - for (i = 0; i < xics_max_server_number(spapr); i++) { - /* Dummy entries get deregistered when real ICPState objects - * are registered during CPU core hotplug. - */ - pre_2_10_vmstate_register_dummy_icp(i); - } - } } static int spapr_fixup_cpu_smt_dt(void *fdt, int offset, PowerPCCPU *cpu, @@ -1062,7 +1053,14 @@ static void spapr_dt_chosen(sPAPRMachineState *spapr, void *fdt) } if (!spapr->has_graphics && stdout_path) { + /* + * "linux,stdout-path" and "stdout" properties are deprecated by linux + * kernel. New platforms should only use the "stdout-path" property. Set + * the new property and continue using older property to remain + * compatible with the existing firmware. + */ _FDT(fdt_setprop_string(fdt, chosen, "linux,stdout-path", stdout_path)); + _FDT(fdt_setprop_string(fdt, chosen, "stdout-path", stdout_path)); } spapr_dt_ov5_platform_support(fdt, chosen); @@ -2232,61 +2230,6 @@ static CPUArchId *spapr_find_cpu_slot(MachineState *ms, uint32_t id, int *idx) return &ms->possible_cpus->cpus[index]; } -static void spapr_init_cpus(sPAPRMachineState *spapr) -{ - MachineState *machine = MACHINE(spapr); - MachineClass *mc = MACHINE_GET_CLASS(machine); - const char *type = spapr_get_cpu_core_type(machine->cpu_type); - const CPUArchIdList *possible_cpus; - int boot_cores_nr = smp_cpus / smp_threads; - int i; - - possible_cpus = mc->possible_cpu_arch_ids(machine); - if (mc->has_hotpluggable_cpus) { - if (smp_cpus % smp_threads) { - error_report("smp_cpus (%u) must be multiple of threads (%u)", - smp_cpus, smp_threads); - exit(1); - } - if (max_cpus % smp_threads) { - error_report("max_cpus (%u) must be multiple of threads (%u)", - max_cpus, smp_threads); - exit(1); - } - } else { - if (max_cpus != smp_cpus) { - error_report("This machine version does not support CPU hotplug"); - exit(1); - } - boot_cores_nr = possible_cpus->len; - } - - for (i = 0; i < possible_cpus->len; i++) { - int core_id = i * smp_threads; - - if (mc->has_hotpluggable_cpus) { - spapr_dr_connector_new(OBJECT(spapr), TYPE_SPAPR_DRC_CPU, - spapr_vcpu_id(spapr, core_id)); - } - - if (i < boot_cores_nr) { - Object *core = object_new(type); - int nr_threads = smp_threads; - - /* Handle the partially filled core for older machine types */ - if ((i + 1) * smp_threads >= smp_cpus) { - nr_threads = smp_cpus - i * smp_threads; - } - - object_property_set_int(core, nr_threads, "nr-threads", - &error_fatal); - object_property_set_int(core, core_id, CPU_CORE_PROP_CORE_ID, - &error_fatal); - object_property_set_bool(core, true, "realized", &error_fatal); - } - } -} - static void spapr_set_vsmt_mode(sPAPRMachineState *spapr, Error **errp) { Error *local_err = NULL; @@ -2359,6 +2302,78 @@ out: error_propagate(errp, local_err); } +static void spapr_init_cpus(sPAPRMachineState *spapr) +{ + MachineState *machine = MACHINE(spapr); + MachineClass *mc = MACHINE_GET_CLASS(machine); + sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine); + const char *type = spapr_get_cpu_core_type(machine->cpu_type); + const CPUArchIdList *possible_cpus; + int boot_cores_nr = smp_cpus / smp_threads; + int i; + + possible_cpus = mc->possible_cpu_arch_ids(machine); + if (mc->has_hotpluggable_cpus) { + if (smp_cpus % smp_threads) { + error_report("smp_cpus (%u) must be multiple of threads (%u)", + smp_cpus, smp_threads); + exit(1); + } + if (max_cpus % smp_threads) { + error_report("max_cpus (%u) must be multiple of threads (%u)", + max_cpus, smp_threads); + exit(1); + } + } else { + if (max_cpus != smp_cpus) { + error_report("This machine version does not support CPU hotplug"); + exit(1); + } + boot_cores_nr = possible_cpus->len; + } + + /* VSMT must be set in order to be able to compute VCPU ids, ie to + * call xics_max_server_number() or spapr_vcpu_id(). + */ + spapr_set_vsmt_mode(spapr, &error_fatal); + + if (smc->pre_2_10_has_unused_icps) { + int i; + + for (i = 0; i < xics_max_server_number(spapr); i++) { + /* Dummy entries get deregistered when real ICPState objects + * are registered during CPU core hotplug. + */ + pre_2_10_vmstate_register_dummy_icp(i); + } + } + + for (i = 0; i < possible_cpus->len; i++) { + int core_id = i * smp_threads; + + if (mc->has_hotpluggable_cpus) { + spapr_dr_connector_new(OBJECT(spapr), TYPE_SPAPR_DRC_CPU, + spapr_vcpu_id(spapr, core_id)); + } + + if (i < boot_cores_nr) { + Object *core = object_new(type); + int nr_threads = smp_threads; + + /* Handle the partially filled core for older machine types */ + if ((i + 1) * smp_threads >= smp_cpus) { + nr_threads = smp_cpus - i * smp_threads; + } + + object_property_set_int(core, nr_threads, "nr-threads", + &error_fatal); + object_property_set_int(core, core_id, CPU_CORE_PROP_CORE_ID, + &error_fatal); + object_property_set_bool(core, true, "realized", &error_fatal); + } + } +} + /* pSeries LPAR / sPAPR hardware init */ static void spapr_machine_init(MachineState *machine) { @@ -2486,8 +2501,6 @@ static void spapr_machine_init(MachineState *machine) } /* init CPUs */ - spapr_set_vsmt_mode(spapr, &error_fatal); - spapr_init_cpus(spapr); if (kvm_enabled()) { @@ -3810,13 +3823,7 @@ static void spapr_pic_print_info(InterruptStatsProvider *obj, int spapr_get_vcpu_id(PowerPCCPU *cpu) { - CPUState *cs = CPU(cpu); - - if (kvm_enabled()) { - return kvm_arch_vcpu_id(cs); - } else { - return cs->cpu_index; - } + return cpu->vcpu_id; } void spapr_set_vcpu_id(PowerPCCPU *cpu, int cpu_index, Error **errp) @@ -3983,6 +3990,23 @@ static void spapr_machine_2_12_class_options(MachineClass *mc) DEFINE_SPAPR_MACHINE(2_12, "2.12", true); +static void spapr_machine_2_12_sxxm_instance_options(MachineState *machine) +{ + spapr_machine_2_12_instance_options(machine); +} + +static void spapr_machine_2_12_sxxm_class_options(MachineClass *mc) +{ + sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc); + + spapr_machine_2_12_class_options(mc); + smc->default_caps.caps[SPAPR_CAP_CFPC] = SPAPR_CAP_WORKAROUND; + smc->default_caps.caps[SPAPR_CAP_SBBC] = SPAPR_CAP_WORKAROUND; + smc->default_caps.caps[SPAPR_CAP_IBS] = SPAPR_CAP_FIXED_CCD; +} + +DEFINE_SPAPR_MACHINE(2_12_sxxm, "2.12-sxxm", false); + /* * pseries-2.11 */ diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c index 99a4b71d19..531e145114 100644 --- a/hw/ppc/spapr_caps.c +++ b/hw/ppc/spapr_caps.c @@ -32,6 +32,20 @@ #include "hw/ppc/spapr.h" +typedef struct sPAPRCapPossible { + int num; /* size of vals array below */ + const char *help; /* help text for vals */ + /* + * Note: + * - because of the way compatibility is determined vals MUST be ordered + * such that later options are a superset of all preceding options. + * - the order of vals must be preserved, that is their index is important, + * however vals may be added to the end of the list so long as the above + * point is observed + */ + const char *vals[]; +} sPAPRCapPossible; + typedef struct sPAPRCapabilityInfo { const char *name; const char *description; @@ -41,6 +55,8 @@ typedef struct sPAPRCapabilityInfo { ObjectPropertyAccessor *get; ObjectPropertyAccessor *set; const char *type; + /* Possible values if this is a custom string type */ + sPAPRCapPossible *possible; /* Make sure the virtual hardware can support this capability */ void (*apply)(sPAPRMachineState *spapr, uint8_t val, Error **errp); } sPAPRCapabilityInfo; @@ -73,41 +89,34 @@ static void spapr_cap_set_bool(Object *obj, Visitor *v, const char *name, spapr->eff.caps[cap->index] = value ? SPAPR_CAP_ON : SPAPR_CAP_OFF; } -static void spapr_cap_get_tristate(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) + +static void spapr_cap_get_string(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { sPAPRCapabilityInfo *cap = opaque; sPAPRMachineState *spapr = SPAPR_MACHINE(obj); char *val = NULL; uint8_t value = spapr_get_cap(spapr, cap->index); - switch (value) { - case SPAPR_CAP_BROKEN: - val = g_strdup("broken"); - break; - case SPAPR_CAP_WORKAROUND: - val = g_strdup("workaround"); - break; - case SPAPR_CAP_FIXED: - val = g_strdup("fixed"); - break; - default: + if (value >= cap->possible->num) { error_setg(errp, "Invalid value (%d) for cap-%s", value, cap->name); return; } + val = g_strdup(cap->possible->vals[value]); + visit_type_str(v, name, &val, errp); g_free(val); } -static void spapr_cap_set_tristate(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) +static void spapr_cap_set_string(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { sPAPRCapabilityInfo *cap = opaque; sPAPRMachineState *spapr = SPAPR_MACHINE(obj); - char *val; Error *local_err = NULL; - uint8_t value; + uint8_t i; + char *val; visit_type_str(v, name, &val, &local_err); if (local_err) { @@ -115,20 +124,20 @@ static void spapr_cap_set_tristate(Object *obj, Visitor *v, const char *name, return; } - if (!strcasecmp(val, "broken")) { - value = SPAPR_CAP_BROKEN; - } else if (!strcasecmp(val, "workaround")) { - value = SPAPR_CAP_WORKAROUND; - } else if (!strcasecmp(val, "fixed")) { - value = SPAPR_CAP_FIXED; - } else { - error_setg(errp, "Invalid capability mode \"%s\" for cap-%s", val, - cap->name); + if (!strcmp(val, "?")) { + error_setg(errp, "%s", cap->possible->help); goto out; } + for (i = 0; i < cap->possible->num; i++) { + if (!strcasecmp(val, cap->possible->vals[i])) { + spapr->cmd_line_caps[cap->index] = true; + spapr->eff.caps[cap->index] = i; + goto out; + } + } - spapr->cmd_line_caps[cap->index] = true; - spapr->eff.caps[cap->index] = value; + error_setg(errp, "Invalid capability mode \"%s\" for cap-%s", val, + cap->name); out: g_free(val); } @@ -180,38 +189,77 @@ static void cap_dfp_apply(sPAPRMachineState *spapr, uint8_t val, Error **errp) } } +sPAPRCapPossible cap_cfpc_possible = { + .num = 3, + .vals = {"broken", "workaround", "fixed"}, + .help = "broken - no protection, workaround - workaround available," + " fixed - fixed in hardware", +}; + static void cap_safe_cache_apply(sPAPRMachineState *spapr, uint8_t val, Error **errp) { + uint8_t kvm_val = kvmppc_get_cap_safe_cache(); + if (tcg_enabled() && val) { /* TODO - for now only allow broken for TCG */ - error_setg(errp, "Requested safe cache capability level not supported by tcg, try a different value for cap-cfpc"); - } else if (kvm_enabled() && (val > kvmppc_get_cap_safe_cache())) { - error_setg(errp, "Requested safe cache capability level not supported by kvm, try a different value for cap-cfpc"); + error_setg(errp, +"Requested safe cache capability level not supported by tcg, try a different value for cap-cfpc"); + } else if (kvm_enabled() && (val > kvm_val)) { + error_setg(errp, +"Requested safe cache capability level not supported by kvm, try cap-cfpc=%s", + cap_cfpc_possible.vals[kvm_val]); } } +sPAPRCapPossible cap_sbbc_possible = { + .num = 3, + .vals = {"broken", "workaround", "fixed"}, + .help = "broken - no protection, workaround - workaround available," + " fixed - fixed in hardware", +}; + static void cap_safe_bounds_check_apply(sPAPRMachineState *spapr, uint8_t val, Error **errp) { + uint8_t kvm_val = kvmppc_get_cap_safe_bounds_check(); + if (tcg_enabled() && val) { /* TODO - for now only allow broken for TCG */ - error_setg(errp, "Requested safe bounds check capability level not supported by tcg, try a different value for cap-sbbc"); - } else if (kvm_enabled() && (val > kvmppc_get_cap_safe_bounds_check())) { - error_setg(errp, "Requested safe bounds check capability level not supported by kvm, try a different value for cap-sbbc"); + error_setg(errp, +"Requested safe bounds check capability level not supported by tcg, try a different value for cap-sbbc"); + } else if (kvm_enabled() && (val > kvm_val)) { + error_setg(errp, +"Requested safe bounds check capability level not supported by kvm, try cap-sbbc=%s", + cap_sbbc_possible.vals[kvm_val]); } } +sPAPRCapPossible cap_ibs_possible = { + .num = 4, + /* Note workaround only maintained for compatibility */ + .vals = {"broken", "workaround", "fixed-ibs", "fixed-ccd"}, + .help = "broken - no protection, fixed-ibs - indirect branch serialisation," + " fixed-ccd - cache count disabled", +}; + static void cap_safe_indirect_branch_apply(sPAPRMachineState *spapr, uint8_t val, Error **errp) { + uint8_t kvm_val = kvmppc_get_cap_safe_indirect_branch(); + if (val == SPAPR_CAP_WORKAROUND) { /* Can only be Broken or Fixed */ - error_setg(errp, "Requested safe indirect branch capability level \"workaround\" not valid, try cap-ibs=fixed"); + error_setg(errp, +"Requested safe indirect branch capability level \"workaround\" not valid, try cap-ibs=%s", + cap_ibs_possible.vals[kvm_val]); } else if (tcg_enabled() && val) { /* TODO - for now only allow broken for TCG */ - error_setg(errp, "Requested safe indirect branch capability level not supported by tcg, try a different value for cap-ibs"); - } else if (kvm_enabled() && (val > kvmppc_get_cap_safe_indirect_branch())) { - error_setg(errp, "Requested safe indirect branch capability level not supported by kvm, try a different value for cap-ibs"); + error_setg(errp, +"Requested safe indirect branch capability level not supported by tcg, try a different value for cap-ibs"); + } else if (kvm_enabled() && val && (val != kvm_val)) { + error_setg(errp, +"Requested safe indirect branch capability level not supported by kvm, try cap-ibs=%s", + cap_ibs_possible.vals[kvm_val]); } } @@ -249,27 +297,31 @@ sPAPRCapabilityInfo capability_table[SPAPR_CAP_NUM] = { .name = "cfpc", .description = "Cache Flush on Privilege Change" VALUE_DESC_TRISTATE, .index = SPAPR_CAP_CFPC, - .get = spapr_cap_get_tristate, - .set = spapr_cap_set_tristate, + .get = spapr_cap_get_string, + .set = spapr_cap_set_string, .type = "string", + .possible = &cap_cfpc_possible, .apply = cap_safe_cache_apply, }, [SPAPR_CAP_SBBC] = { .name = "sbbc", .description = "Speculation Barrier Bounds Checking" VALUE_DESC_TRISTATE, .index = SPAPR_CAP_SBBC, - .get = spapr_cap_get_tristate, - .set = spapr_cap_set_tristate, + .get = spapr_cap_get_string, + .set = spapr_cap_set_string, .type = "string", + .possible = &cap_sbbc_possible, .apply = cap_safe_bounds_check_apply, }, [SPAPR_CAP_IBS] = { .name = "ibs", - .description = "Indirect Branch Serialisation (broken, fixed)", + .description = + "Indirect Branch Speculation (broken, fixed-ibs, fixed-ccd)", .index = SPAPR_CAP_IBS, - .get = spapr_cap_get_tristate, - .set = spapr_cap_set_tristate, + .get = spapr_cap_get_string, + .set = spapr_cap_set_string, .type = "string", + .possible = &cap_ibs_possible, .apply = cap_safe_indirect_branch_apply, }, }; @@ -283,15 +335,26 @@ static sPAPRCapabilities default_caps_with_cpu(sPAPRMachineState *spapr, caps = smc->default_caps; + if (!ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_3_00, + 0, spapr->max_compat_pvr)) { + caps.caps[SPAPR_CAP_CFPC] = SPAPR_CAP_BROKEN; + } + if (!ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_2_07, 0, spapr->max_compat_pvr)) { caps.caps[SPAPR_CAP_HTM] = SPAPR_CAP_OFF; } + if (!ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_2_06_PLUS, + 0, spapr->max_compat_pvr)) { + caps.caps[SPAPR_CAP_SBBC] = SPAPR_CAP_BROKEN; + } + if (!ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_2_06, 0, spapr->max_compat_pvr)) { caps.caps[SPAPR_CAP_VSX] = SPAPR_CAP_OFF; caps.caps[SPAPR_CAP_DFP] = SPAPR_CAP_OFF; + caps.caps[SPAPR_CAP_IBS] = SPAPR_CAP_BROKEN; } return caps; diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 1986560480..16bccdd5c0 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -1705,7 +1705,10 @@ static target_ulong h_get_cpu_characteristics(PowerPCCPU *cpu, } switch (safe_indirect_branch) { - case SPAPR_CAP_FIXED: + case SPAPR_CAP_FIXED_CCD: + characteristics |= H_CPU_CHAR_CACHE_COUNT_DIS; + break; + case SPAPR_CAP_FIXED_IBS: characteristics |= H_CPU_CHAR_BCCTRL_SERIALISED; break; default: /* broken */ diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index 4bb939d3d1..0ec5fa4cfe 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -38,7 +38,6 @@ #include "hw/ppc/spapr_vio.h" #include "hw/ppc/spapr_rtas.h" #include "hw/ppc/ppc.h" -#include "qapi-event.h" #include "hw/boards.h" #include <libfdt.h> diff --git a/hw/ppc/spapr_rtc.c b/hw/ppc/spapr_rtc.c index cfdb274bfd..a37360537e 100644 --- a/hw/ppc/spapr_rtc.c +++ b/hw/ppc/spapr_rtc.c @@ -30,8 +30,8 @@ #include "qemu/timer.h" #include "sysemu/sysemu.h" #include "hw/ppc/spapr.h" -#include "qapi-event.h" #include "qapi/error.h" +#include "qapi/qapi-events-misc.h" #include "qemu/cutils.h" void spapr_rtc_read(sPAPRRTCState *rtc, struct tm *tm, uint32_t *ns) diff --git a/hw/ppc/trace-events b/hw/ppc/trace-events index b7c3e64b5e..66ec7eda6e 100644 --- a/hw/ppc/trace-events +++ b/hw/ppc/trace-events @@ -99,3 +99,11 @@ mac99_uninorth_read(uint64_t addr, uint64_t value) "addr=0x%" PRIx64 " val=0x%"P # hw/ppc/ppc4xx_pci.c ppc4xx_pci_map_irq(int32_t devfn, int irq_num, int slot) "devfn 0x%x irq %d -> %d" ppc4xx_pci_set_irq(int irq_num) "PCI irq %d" + +# hw/ppc/ppc440_pcix.c +ppc440_pcix_map_irq(int32_t devfn, int irq_num, int slot) "devfn 0x%x irq %d -> %d" +ppc440_pcix_set_irq(int irq_num) "PCI irq %d" +ppc440_pcix_update_pim(int idx, uint64_t size, uint64_t la) "Added window %d of size=0x%" PRIx64 " to CPU=0x%" PRIx64 +ppc440_pcix_update_pom(int idx, uint32_t size, uint64_t la, uint64_t pcia) "Added window %d of size=0x%x from CPU=0x%" PRIx64 " to PCI=0x%" PRIx64 +ppc440_pcix_reg_read(uint64_t addr, uint32_t val) "addr 0x%" PRIx64 " = 0x%" PRIx32 +ppc440_pcix_reg_write(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " = 0x%" PRIx64 |