diff options
author | José Pekkarinen <jose.pekkarinen@nokia.com> | 2016-05-18 13:18:31 +0300 |
---|---|---|
committer | José Pekkarinen <jose.pekkarinen@nokia.com> | 2016-05-18 13:42:15 +0300 |
commit | 437fd90c0250dee670290f9b714253671a990160 (patch) | |
tree | b871786c360704244a07411c69fb58da9ead4a06 /qemu/hw/ppc | |
parent | 5bbd6fe9b8bab2a93e548c5a53b032d1939eec05 (diff) |
These changes are the raw update to qemu-2.6.
Collission happened in the following patches:
migration: do cleanup operation after completion(738df5b9)
Bug fix.(1750c932f86)
kvmclock: add a new function to update env->tsc.(b52baab2)
The code provided by the patches was already in the upstreamed
version.
Change-Id: I3cc11841a6a76ae20887b2e245710199e1ea7f9a
Signed-off-by: José Pekkarinen <jose.pekkarinen@nokia.com>
Diffstat (limited to 'qemu/hw/ppc')
29 files changed, 1916 insertions, 928 deletions
diff --git a/qemu/hw/ppc/Makefile.objs b/qemu/hw/ppc/Makefile.objs index c8ab06e7f..c1ffc7771 100644 --- a/qemu/hw/ppc/Makefile.objs +++ b/qemu/hw/ppc/Makefile.objs @@ -3,7 +3,7 @@ obj-y += ppc.o ppc_booke.o # IBM pSeries (sPAPR) obj-$(CONFIG_PSERIES) += spapr.o spapr_vio.o spapr_events.o obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o -obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o +obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy) obj-y += spapr_pci_vfio.o endif diff --git a/qemu/hw/ppc/e500.c b/qemu/hw/ppc/e500.c index d300846c3..ee1c60b82 100644 --- a/qemu/hw/ppc/e500.c +++ b/qemu/hw/ppc/e500.c @@ -14,7 +14,8 @@ * (at your option) any later version. */ -#include "config.h" +#include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu-common.h" #include "e500.h" #include "e500-ccsr.h" @@ -751,8 +752,8 @@ static qemu_irq *ppce500_init_mpic(MachineState *machine, PPCE500Params *params, dev = ppce500_init_mpic_kvm(params, irqs, &err); } if (machine_kernel_irqchip_required(machine) && !dev) { - error_report("kernel_irqchip requested but unavailable: %s", - error_get_pretty(err)); + error_reportf_err(err, + "kernel_irqchip requested but unavailable: "); exit(1); } } @@ -1017,7 +1018,7 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); bios_size = load_elf(filename, NULL, NULL, &bios_entry, &loadaddr, NULL, - 1, ELF_MACHINE, 0); + 1, PPC_ELF_MACHINE, 0, 0); if (bios_size < 0) { /* * Hrm. No ELF image? Try a uImage, maybe someone is giving us an @@ -1048,10 +1049,6 @@ void ppce500_init(MachineState *machine, PPCE500Params *params) boot_info->entry = bios_entry; boot_info->dt_base = dt_base; boot_info->dt_size = dt_size; - - if (kvm_enabled()) { - kvmppc_init(); - } } static int e500_ccsr_initfn(SysBusDevice *dev) diff --git a/qemu/hw/ppc/e500plat.c b/qemu/hw/ppc/e500plat.c index 14b14eaa7..b00565c3d 100644 --- a/qemu/hw/ppc/e500plat.c +++ b/qemu/hw/ppc/e500plat.c @@ -9,7 +9,7 @@ * (at your option) any later version. */ -#include "config.h" +#include "qemu/osdep.h" #include "qemu-common.h" #include "e500.h" #include "hw/boards.h" @@ -57,17 +57,12 @@ static void e500plat_init(MachineState *machine) ppce500_init(machine, ¶ms); } -static QEMUMachine e500plat_machine = { - .name = "ppce500", - .desc = "generic paravirt e500 platform", - .init = e500plat_init, - .max_cpus = 32, - .has_dynamic_sysbus = true, -}; - -static void e500plat_machine_init(void) +static void e500plat_machine_init(MachineClass *mc) { - qemu_register_machine(&e500plat_machine); + mc->desc = "generic paravirt e500 platform"; + mc->init = e500plat_init; + mc->max_cpus = 32; + mc->has_dynamic_sysbus = true; } -machine_init(e500plat_machine_init); +DEFINE_MACHINE("ppce500", e500plat_machine_init) diff --git a/qemu/hw/ppc/mac.h b/qemu/hw/ppc/mac.h index 8bdba30c1..5764b86c2 100644 --- a/qemu/hw/ppc/mac.h +++ b/qemu/hw/ppc/mac.h @@ -103,11 +103,16 @@ typedef struct CUDAState { uint8_t last_b; uint8_t last_acr; + /* MacOS 9 is racy and requires a delay upon setting the SR_INT bit */ + QEMUTimer *sr_delay_timer; + int data_in_size; int data_in_index; int data_out_index; qemu_irq irq; + uint16_t adb_poll_mask; + uint8_t autopoll_rate_ms; uint8_t autopoll; uint8_t data_in[128]; uint8_t data_out[16]; @@ -131,7 +136,6 @@ typedef struct MACIOIDEState { MemoryRegion mem; IDEBus bus; - BlockAIOCB *aiocb; IDEDMA dma; void *dbdma; bool dma_active; diff --git a/qemu/hw/ppc/mac_newworld.c b/qemu/hw/ppc/mac_newworld.c index 77d5c819e..32e88b378 100644 --- a/qemu/hw/ppc/mac_newworld.c +++ b/qemu/hw/ppc/mac_newworld.c @@ -46,6 +46,8 @@ * 0001:05:0c.0 IDE interface [0101]: Broadcom K2 SATA [1166:0240] * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/hw.h" #include "hw/ppc/ppc.h" #include "hw/ppc/mac.h" @@ -62,12 +64,14 @@ #include "hw/ide.h" #include "hw/loader.h" #include "elf.h" +#include "qemu/error-report.h" #include "sysemu/kvm.h" #include "kvm_ppc.h" #include "hw/usb.h" #include "sysemu/block-backend.h" #include "exec/address-spaces.h" #include "hw/sysbus.h" +#include "qemu/cutils.h" #define MAX_IDE_BUS 2 #define CFG_ADDR 0xf0000510 @@ -207,7 +211,7 @@ static void ppc_core99_init(MachineState *machine) /* allocate and load BIOS */ memory_region_init_ram(bios, NULL, "ppc_core99.bios", BIOS_SIZE, - &error_abort); + &error_fatal); vmstate_register_ram_global(bios); if (bios_name == NULL) @@ -219,14 +223,14 @@ static void ppc_core99_init(MachineState *machine) /* Load OpenBIOS (ELF) */ if (filename) { bios_size = load_elf(filename, NULL, NULL, NULL, - NULL, NULL, 1, ELF_MACHINE, 0); + NULL, NULL, 1, PPC_ELF_MACHINE, 0, 0); g_free(filename); } else { bios_size = -1; } if (bios_size < 0 || bios_size > BIOS_SIZE) { - hw_error("qemu: could not load PowerPC bios '%s'\n", bios_name); + error_report("could not load PowerPC bios '%s'", bios_name); exit(1); } @@ -242,7 +246,8 @@ static void ppc_core99_init(MachineState *machine) kernel_base = KERNEL_LOAD_ADDR; kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL, - NULL, &lowaddr, NULL, 1, ELF_MACHINE, 0); + NULL, &lowaddr, NULL, 1, PPC_ELF_MACHINE, + 0, 0); if (kernel_size < 0) kernel_size = load_aout(kernel_filename, kernel_base, ram_size - kernel_base, bswap_needed, @@ -252,7 +257,7 @@ static void ppc_core99_init(MachineState *machine) kernel_base, ram_size - kernel_base); if (kernel_size < 0) { - hw_error("qemu: could not load kernel '%s'\n", kernel_filename); + error_report("could not load kernel '%s'", kernel_filename); exit(1); } /* load initrd */ @@ -261,8 +266,8 @@ static void ppc_core99_init(MachineState *machine) initrd_size = load_image_targphys(initrd_filename, initrd_base, ram_size - initrd_base); if (initrd_size < 0) { - hw_error("qemu: could not load initial ram disk '%s'\n", - initrd_filename); + error_report("could not load initial ram disk '%s'", + initrd_filename); exit(1); } cmdline_base = round_page(initrd_base + initrd_size); @@ -344,7 +349,7 @@ static void ppc_core99_init(MachineState *machine) break; #endif /* defined(TARGET_PPC64) */ default: - hw_error("Bus model not supported on mac99 machine\n"); + error_report("Bus model not supported on mac99 machine"); exit(1); } } @@ -371,12 +376,13 @@ static void ppc_core99_init(MachineState *machine) /* 970 gets a U3 bus */ pci_bus = pci_pmac_u3_init(pic, get_system_memory(), get_system_io()); machine_arch = ARCH_MAC99_U3; - machine->usb |= defaults_enabled() && !machine->usb_disabled; } else { pci_bus = pci_pmac_init(pic, get_system_memory(), get_system_io()); machine_arch = ARCH_MAC99; } + machine->usb |= defaults_enabled() && !machine->usb_disabled; + /* Timebase Frequency */ if (kvm_enabled()) { tbfreq = kvmppc_get_tbfreq(); @@ -508,7 +514,6 @@ static void core99_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); - mc->name = "mac99"; mc->desc = "Mac99 based PowerMAC"; mc->init = ppc_core99_init; mc->max_cpus = MAX_CPUS; @@ -517,7 +522,7 @@ static void core99_machine_class_init(ObjectClass *oc, void *data) } static const TypeInfo core99_machine_info = { - .name = "mac99-machine", + .name = MACHINE_TYPE_NAME("mac99"), .parent = TYPE_MACHINE, .class_init = core99_machine_class_init, }; diff --git a/qemu/hw/ppc/mac_oldworld.c b/qemu/hw/ppc/mac_oldworld.c index 06fdbaf58..a9bb1c27d 100644 --- a/qemu/hw/ppc/mac_oldworld.c +++ b/qemu/hw/ppc/mac_oldworld.c @@ -23,6 +23,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/hw.h" #include "hw/ppc/ppc.h" #include "mac.h" @@ -38,10 +40,12 @@ #include "hw/ide.h" #include "hw/loader.h" #include "elf.h" +#include "qemu/error-report.h" #include "sysemu/kvm.h" #include "kvm_ppc.h" #include "sysemu/block-backend.h" #include "exec/address-spaces.h" +#include "qemu/cutils.h" #define MAX_IDE_BUS 2 #define CFG_ADDR 0xf0000510 @@ -135,7 +139,7 @@ static void ppc_heathrow_init(MachineState *machine) /* allocate and load BIOS */ memory_region_init_ram(bios, NULL, "ppc_heathrow.bios", BIOS_SIZE, - &error_abort); + &error_fatal); vmstate_register_ram_global(bios); if (bios_name == NULL) @@ -147,13 +151,13 @@ static void ppc_heathrow_init(MachineState *machine) /* Load OpenBIOS (ELF) */ if (filename) { bios_size = load_elf(filename, 0, NULL, NULL, NULL, NULL, - 1, ELF_MACHINE, 0); + 1, PPC_ELF_MACHINE, 0, 0); g_free(filename); } else { bios_size = -1; } if (bios_size < 0 || bios_size > BIOS_SIZE) { - hw_error("qemu: could not load PowerPC bios '%s'\n", bios_name); + error_report("could not load PowerPC bios '%s'", bios_name); exit(1); } @@ -168,7 +172,8 @@ static void ppc_heathrow_init(MachineState *machine) #endif kernel_base = KERNEL_LOAD_ADDR; kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL, - NULL, &lowaddr, NULL, 1, ELF_MACHINE, 0); + NULL, &lowaddr, NULL, 1, PPC_ELF_MACHINE, + 0, 0); if (kernel_size < 0) kernel_size = load_aout(kernel_filename, kernel_base, ram_size - kernel_base, bswap_needed, @@ -178,8 +183,7 @@ static void ppc_heathrow_init(MachineState *machine) kernel_base, ram_size - kernel_base); if (kernel_size < 0) { - hw_error("qemu: could not load kernel '%s'\n", - kernel_filename); + error_report("could not load kernel '%s'", kernel_filename); exit(1); } /* load initrd */ @@ -188,8 +192,8 @@ static void ppc_heathrow_init(MachineState *machine) initrd_size = load_image_targphys(initrd_filename, initrd_base, ram_size - initrd_base); if (initrd_size < 0) { - hw_error("qemu: could not load initial ram disk '%s'\n", - initrd_filename); + error_report("could not load initial ram disk '%s'", + initrd_filename); exit(1); } cmdline_base = round_page(initrd_base + initrd_size); @@ -246,7 +250,8 @@ static void ppc_heathrow_init(MachineState *machine) ((qemu_irq *)env->irq_inputs)[PPC6xx_INPUT_INT]; break; default: - hw_error("Bus model not supported on OldWorld Mac machine\n"); + error_report("Bus model not supported on OldWorld Mac machine"); + exit(1); } } @@ -259,7 +264,8 @@ static void ppc_heathrow_init(MachineState *machine) /* init basic PC hardware */ if (PPC_INPUT(env) != PPC_FLAGS_INPUT_6xx) { - hw_error("Only 6xx bus is supported on heathrow machine\n"); + error_report("Only 6xx bus is supported on heathrow machine"); + exit(1); } pic = heathrow_pic_init(&pic_mem, 1, heathrow_irqs); pci_bus = pci_grackle_init(0xfec00000, pic, @@ -357,21 +363,17 @@ static int heathrow_kvm_type(const char *arg) return 2; } -static QEMUMachine heathrow_machine = { - .name = "g3beige", - .desc = "Heathrow based PowerMAC", - .init = ppc_heathrow_init, - .max_cpus = MAX_CPUS, +static void heathrow_machine_init(MachineClass *mc) +{ + mc->desc = "Heathrow based PowerMAC"; + mc->init = ppc_heathrow_init; + mc->max_cpus = MAX_CPUS; #ifndef TARGET_PPC64 - .is_default = 1, + mc->is_default = 1; #endif - .default_boot_order = "cd", /* TOFIX "cad" when Mac floppy is implemented */ - .kvm_type = heathrow_kvm_type, -}; - -static void heathrow_machine_init(void) -{ - qemu_register_machine(&heathrow_machine); + /* TOFIX "cad" when Mac floppy is implemented */ + mc->default_boot_order = "cd"; + mc->kvm_type = heathrow_kvm_type; } -machine_init(heathrow_machine_init); +DEFINE_MACHINE("g3beige", heathrow_machine_init) diff --git a/qemu/hw/ppc/mpc8544_guts.c b/qemu/hw/ppc/mpc8544_guts.c index a10abe978..ba69178d6 100644 --- a/qemu/hw/ppc/mpc8544_guts.c +++ b/qemu/hw/ppc/mpc8544_guts.c @@ -17,6 +17,9 @@ * */ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/hw.h" #include "sysemu/sysemu.h" #include "hw/sysbus.h" diff --git a/qemu/hw/ppc/mpc8544ds.c b/qemu/hw/ppc/mpc8544ds.c index 3a3b141e4..27b828901 100644 --- a/qemu/hw/ppc/mpc8544ds.c +++ b/qemu/hw/ppc/mpc8544ds.c @@ -9,7 +9,7 @@ * (at your option) any later version. */ -#include "config.h" +#include "qemu/osdep.h" #include "qemu-common.h" #include "e500.h" #include "hw/boards.h" @@ -50,16 +50,11 @@ static void mpc8544ds_init(MachineState *machine) } -static QEMUMachine ppce500_machine = { - .name = "mpc8544ds", - .desc = "mpc8544ds", - .init = mpc8544ds_init, - .max_cpus = 15, -}; - -static void ppce500_machine_init(void) +static void ppce500_machine_init(MachineClass *mc) { - qemu_register_machine(&ppce500_machine); + mc->desc = "mpc8544ds"; + mc->init = mpc8544ds_init; + mc->max_cpus = 15; } -machine_init(ppce500_machine_init); +DEFINE_MACHINE("mpc8544ds", ppce500_machine_init) diff --git a/qemu/hw/ppc/ppc.c b/qemu/hw/ppc/ppc.c index b77e30357..38ff2e159 100644 --- a/qemu/hw/ppc/ppc.c +++ b/qemu/hw/ppc/ppc.c @@ -21,6 +21,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/hw.h" #include "hw/ppc/ppc.h" #include "hw/ppc/ppc_e500.h" @@ -462,7 +465,7 @@ void ppce500_set_mpic_proxy(bool enabled) uint64_t cpu_ppc_get_tb(ppc_tb_t *tb_env, uint64_t vmclk, int64_t tb_offset) { /* TB time in tb periods */ - return muldiv64(vmclk, tb_env->tb_freq, get_ticks_per_sec()) + tb_offset; + return muldiv64(vmclk, tb_env->tb_freq, NANOSECONDS_PER_SECOND) + tb_offset; } uint64_t cpu_ppc_load_tbl (CPUPPCState *env) @@ -503,7 +506,9 @@ uint32_t cpu_ppc_load_tbu (CPUPPCState *env) static inline void cpu_ppc_store_tb(ppc_tb_t *tb_env, uint64_t vmclk, int64_t *tb_offsetp, uint64_t value) { - *tb_offsetp = value - muldiv64(vmclk, tb_env->tb_freq, get_ticks_per_sec()); + *tb_offsetp = value - + muldiv64(vmclk, tb_env->tb_freq, NANOSECONDS_PER_SECOND); + LOG_TB("%s: tb %016" PRIx64 " offset %08" PRIx64 "\n", __func__, value, *tb_offsetp); } @@ -637,11 +642,11 @@ static inline uint32_t _cpu_ppc_load_decr(CPUPPCState *env, uint64_t next) diff = next - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); if (diff >= 0) { - decr = muldiv64(diff, tb_env->decr_freq, get_ticks_per_sec()); + decr = muldiv64(diff, tb_env->decr_freq, NANOSECONDS_PER_SECOND); } else if (tb_env->flags & PPC_TIMER_BOOKE) { decr = 0; } else { - decr = -muldiv64(-diff, tb_env->decr_freq, get_ticks_per_sec()); + decr = -muldiv64(-diff, tb_env->decr_freq, NANOSECONDS_PER_SECOND); } LOG_TB("%s: %08" PRIx32 "\n", __func__, decr); @@ -673,7 +678,8 @@ uint64_t cpu_ppc_load_purr (CPUPPCState *env) diff = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - tb_env->purr_start; - return tb_env->purr_load + muldiv64(diff, tb_env->tb_freq, get_ticks_per_sec()); + return tb_env->purr_load + + muldiv64(diff, tb_env->tb_freq, NANOSECONDS_PER_SECOND); } /* When decrementer expires, @@ -749,7 +755,7 @@ static void __cpu_ppc_store_decr(PowerPCCPU *cpu, uint64_t *nextp, /* Calculate the next timer event */ now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - next = now + muldiv64(value, get_ticks_per_sec(), tb_env->decr_freq); + next = now + muldiv64(value, NANOSECONDS_PER_SECOND, tb_env->decr_freq); *nextp = next; /* Adjust timer */ @@ -834,7 +840,7 @@ static void cpu_ppc_set_tb_clk (void *opaque, uint32_t freq) static void timebase_pre_save(void *opaque) { PPCTimebase *tb = opaque; - uint64_t ticks = cpu_get_real_ticks(); + uint64_t ticks = cpu_get_host_ticks(); PowerPCCPU *first_ppc_cpu = POWERPC_CPU(first_cpu); if (!first_ppc_cpu->env.tb_env) { @@ -878,7 +884,7 @@ static int timebase_post_load(void *opaque, int version_id) NANOSECONDS_PER_SECOND); guest_tb = tb_remote->guest_timebase + MIN(0, migration_duration_tb); - tb_off_adj = guest_tb - cpu_get_real_ticks(); + tb_off_adj = guest_tb - cpu_get_host_ticks(); tb_off = first_ppc_cpu->env.tb_env->tb_offset; trace_ppc_tb_adjust(tb_off, tb_off_adj, tb_off_adj - tb_off, @@ -1010,7 +1016,7 @@ static void cpu_4xx_fit_cb (void *opaque) /* Cannot occur, but makes gcc happy */ return; } - next = now + muldiv64(next, get_ticks_per_sec(), tb_env->tb_freq); + next = now + muldiv64(next, NANOSECONDS_PER_SECOND, tb_env->tb_freq); if (next == now) next++; timer_mod(ppc40x_timer->fit_timer, next); @@ -1041,7 +1047,7 @@ static void start_stop_pit (CPUPPCState *env, ppc_tb_t *tb_env, int is_excp) __func__, ppc40x_timer->pit_reload); now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); next = now + muldiv64(ppc40x_timer->pit_reload, - get_ticks_per_sec(), tb_env->decr_freq); + NANOSECONDS_PER_SECOND, tb_env->decr_freq); if (is_excp) next += tb_env->decr_next - now; if (next == now) @@ -1106,7 +1112,7 @@ static void cpu_4xx_wdt_cb (void *opaque) /* Cannot occur, but makes gcc happy */ return; } - next = now + muldiv64(next, get_ticks_per_sec(), tb_env->decr_freq); + next = now + muldiv64(next, NANOSECONDS_PER_SECOND, tb_env->decr_freq); if (next == now) next++; LOG_TB("%s: TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx "\n", __func__, diff --git a/qemu/hw/ppc/ppc405_boards.c b/qemu/hw/ppc/ppc405_boards.c index ec6c4cbaf..4b2f07aec 100644 --- a/qemu/hw/ppc/ppc405_boards.c +++ b/qemu/hw/ppc/ppc405_boards.c @@ -21,6 +21,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/hw.h" #include "hw/ppc/ppc.h" #include "ppc405.h" @@ -215,7 +219,8 @@ static void ref405ep_init(MachineState *machine) 33333333, &pic, kernel_filename == NULL ? 0 : 1); /* allocate SRAM */ sram_size = 512 * 1024; - memory_region_init_ram(sram, NULL, "ef405ep.sram", sram_size, &error_abort); + memory_region_init_ram(sram, NULL, "ef405ep.sram", sram_size, + &error_fatal); vmstate_register_ram_global(sram); memory_region_add_subregion(sysmem, 0xFFF00000, sram); /* allocate and load BIOS */ @@ -250,7 +255,7 @@ static void ref405ep_init(MachineState *machine) #endif bios = g_new(MemoryRegion, 1); memory_region_init_ram(bios, NULL, "ef405ep.bios", BIOS_SIZE, - &error_abort); + &error_fatal); vmstate_register_ram_global(bios); if (bios_name == NULL) @@ -368,10 +373,18 @@ static void ref405ep_init(MachineState *machine) #endif } -static QEMUMachine ref405ep_machine = { - .name = "ref405ep", - .desc = "ref405ep", - .init = ref405ep_init, +static void ref405ep_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "ref405ep"; + mc->init = ref405ep_init; +} + +static const TypeInfo ref405ep_type = { + .name = MACHINE_TYPE_NAME("ref405ep"), + .parent = TYPE_MACHINE, + .class_init = ref405ep_class_init, }; /*****************************************************************************/ @@ -399,7 +412,7 @@ struct taihu_cpld_t { uint8_t reg1; }; -static uint32_t taihu_cpld_readb (void *opaque, hwaddr addr) +static uint64_t taihu_cpld_read(void *opaque, hwaddr addr, unsigned size) { taihu_cpld_t *cpld; uint32_t ret; @@ -420,8 +433,8 @@ static uint32_t taihu_cpld_readb (void *opaque, hwaddr addr) return ret; } -static void taihu_cpld_writeb (void *opaque, - hwaddr addr, uint32_t value) +static void taihu_cpld_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) { taihu_cpld_t *cpld; @@ -438,48 +451,12 @@ static void taihu_cpld_writeb (void *opaque, } } -static uint32_t taihu_cpld_readw (void *opaque, hwaddr addr) -{ - uint32_t ret; - - ret = taihu_cpld_readb(opaque, addr) << 8; - ret |= taihu_cpld_readb(opaque, addr + 1); - - return ret; -} - -static void taihu_cpld_writew (void *opaque, - hwaddr addr, uint32_t value) -{ - taihu_cpld_writeb(opaque, addr, (value >> 8) & 0xFF); - taihu_cpld_writeb(opaque, addr + 1, value & 0xFF); -} - -static uint32_t taihu_cpld_readl (void *opaque, hwaddr addr) -{ - uint32_t ret; - - ret = taihu_cpld_readb(opaque, addr) << 24; - ret |= taihu_cpld_readb(opaque, addr + 1) << 16; - ret |= taihu_cpld_readb(opaque, addr + 2) << 8; - ret |= taihu_cpld_readb(opaque, addr + 3); - - return ret; -} - -static void taihu_cpld_writel (void *opaque, - hwaddr addr, uint32_t value) -{ - taihu_cpld_writel(opaque, addr, (value >> 24) & 0xFF); - taihu_cpld_writel(opaque, addr + 1, (value >> 16) & 0xFF); - taihu_cpld_writel(opaque, addr + 2, (value >> 8) & 0xFF); - taihu_cpld_writeb(opaque, addr + 3, value & 0xFF); -} - static const MemoryRegionOps taihu_cpld_ops = { - .old_mmio = { - .read = { taihu_cpld_readb, taihu_cpld_readw, taihu_cpld_readl, }, - .write = { taihu_cpld_writeb, taihu_cpld_writew, taihu_cpld_writel, }, + .read = taihu_cpld_read, + .write = taihu_cpld_write, + .impl = { + .min_access_size = 1, + .max_access_size = 1, }, .endianness = DEVICE_NATIVE_ENDIAN, }; @@ -579,7 +556,7 @@ static void taihu_405ep_init(MachineState *machine) bios_name = BIOS_FILENAME; bios = g_new(MemoryRegion, 1); memory_region_init_ram(bios, NULL, "taihu_405ep.bios", BIOS_SIZE, - &error_abort); + &error_fatal); vmstate_register_ram_global(bios); filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); if (filename) { @@ -664,16 +641,24 @@ static void taihu_405ep_init(MachineState *machine) #endif } -static QEMUMachine taihu_machine = { - .name = "taihu", - .desc = "taihu", - .init = taihu_405ep_init, +static void taihu_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "taihu"; + mc->init = taihu_405ep_init; +} + +static const TypeInfo taihu_type = { + .name = MACHINE_TYPE_NAME("taihu"), + .parent = TYPE_MACHINE, + .class_init = taihu_class_init, }; static void ppc405_machine_init(void) { - qemu_register_machine(&ref405ep_machine); - qemu_register_machine(&taihu_machine); + type_register_static(&ref405ep_type); + type_register_static(&taihu_type); } -machine_init(ppc405_machine_init); +type_init(ppc405_machine_init) diff --git a/qemu/hw/ppc/ppc405_uc.c b/qemu/hw/ppc/ppc405_uc.c index c77434ae0..d6d3fc2c4 100644 --- a/qemu/hw/ppc/ppc405_uc.c +++ b/qemu/hw/ppc/ppc405_uc.c @@ -21,6 +21,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/hw.h" #include "hw/ppc/ppc.h" #include "hw/boards.h" @@ -975,7 +979,7 @@ static void ppc405_ocm_init(CPUPPCState *env) ocm = g_malloc0(sizeof(ppc405_ocm_t)); /* XXX: Size is 4096 or 0x04000000 */ memory_region_init_ram(&ocm->isarc_ram, NULL, "ppc405.ocm", 4096, - &error_abort); + &error_fatal); vmstate_register_ram_global(&ocm->isarc_ram); memory_region_init_alias(&ocm->dsarc_ram, NULL, "ppc405.dsarc", &ocm->isarc_ram, 0, 4096); @@ -1352,7 +1356,7 @@ static uint32_t ppc4xx_gpt_readl (void *opaque, hwaddr addr) case 0x00: /* Time base counter */ ret = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + gpt->tb_offset, - gpt->tb_freq, get_ticks_per_sec()); + gpt->tb_freq, NANOSECONDS_PER_SECOND); break; case 0x10: /* Output enable */ @@ -1407,7 +1411,7 @@ static void ppc4xx_gpt_writel (void *opaque, switch (addr) { case 0x00: /* Time base counter */ - gpt->tb_offset = muldiv64(value, get_ticks_per_sec(), gpt->tb_freq) + gpt->tb_offset = muldiv64(value, NANOSECONDS_PER_SECOND, gpt->tb_freq) - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); ppc4xx_gpt_compute_timer(gpt); break; diff --git a/qemu/hw/ppc/ppc440_bamboo.c b/qemu/hw/ppc/ppc440_bamboo.c index 032fa803d..5c535b18a 100644 --- a/qemu/hw/ppc/ppc440_bamboo.c +++ b/qemu/hw/ppc/ppc440_bamboo.c @@ -11,7 +11,7 @@ * */ -#include "config.h" +#include "qemu/osdep.h" #include "qemu-common.h" #include "net/net.h" #include "hw/hw.h" @@ -256,7 +256,8 @@ static void bamboo_init(MachineState *machine) NULL, NULL); if (success < 0) { success = load_elf(kernel_filename, NULL, NULL, &elf_entry, - &elf_lowaddr, NULL, 1, ELF_MACHINE, 0); + &elf_lowaddr, NULL, 1, PPC_ELF_MACHINE, + 0, 0); entry = elf_entry; loadaddr = elf_lowaddr; } @@ -288,20 +289,12 @@ static void bamboo_init(MachineState *machine) exit(1); } } - - if (kvm_enabled()) - kvmppc_init(); } -static QEMUMachine bamboo_machine = { - .name = "bamboo", - .desc = "bamboo", - .init = bamboo_init, -}; - -static void bamboo_machine_init(void) +static void bamboo_machine_init(MachineClass *mc) { - qemu_register_machine(&bamboo_machine); + mc->desc = "bamboo"; + mc->init = bamboo_init; } -machine_init(bamboo_machine_init); +DEFINE_MACHINE("bamboo", bamboo_machine_init) diff --git a/qemu/hw/ppc/ppc4xx_devs.c b/qemu/hw/ppc/ppc4xx_devs.c index 2f38ff7d2..7d59018fc 100644 --- a/qemu/hw/ppc/ppc4xx_devs.c +++ b/qemu/hw/ppc/ppc4xx_devs.c @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/ppc/ppc.h" #include "hw/ppc/ppc4xx.h" diff --git a/qemu/hw/ppc/ppc4xx_pci.c b/qemu/hw/ppc/ppc4xx_pci.c index 0bb3cdb46..683218e5c 100644 --- a/qemu/hw/ppc/ppc4xx_pci.c +++ b/qemu/hw/ppc/ppc4xx_pci.c @@ -19,6 +19,7 @@ /* This file implements emulation of the 32-bit PCI controller found in some * 4xx SoCs, such as the 440EP. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/ppc/ppc.h" #include "hw/ppc/ppc4xx.h" diff --git a/qemu/hw/ppc/ppc_booke.c b/qemu/hw/ppc/ppc_booke.c index 8b94da6b0..ab8d026c3 100644 --- a/qemu/hw/ppc/ppc_booke.c +++ b/qemu/hw/ppc/ppc_booke.c @@ -21,6 +21,9 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/hw.h" #include "hw/ppc/ppc.h" #include "qemu/timer.h" @@ -162,7 +165,7 @@ static void booke_update_fixed_timer(CPUPPCState *env, ticks += delta_tick; } - *next = now + muldiv64(ticks, get_ticks_per_sec(), tb_env->tb_freq); + *next = now + muldiv64(ticks, NANOSECONDS_PER_SECOND, tb_env->tb_freq); if ((*next < now) || (*next > INT64_MAX)) { /* Overflow, so assume the biggest number the qemu timer supports. */ *next = INT64_MAX; diff --git a/qemu/hw/ppc/ppce500_spin.c b/qemu/hw/ppc/ppce500_spin.c index a99f7b039..76bd78bfd 100644 --- a/qemu/hw/ppc/ppce500_spin.c +++ b/qemu/hw/ppc/ppce500_spin.c @@ -27,6 +27,7 @@ * */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "sysemu/sysemu.h" #include "hw/sysbus.h" diff --git a/qemu/hw/ppc/prep.c b/qemu/hw/ppc/prep.c index 45b5f62d6..3ffb85e60 100644 --- a/qemu/hw/ppc/prep.c +++ b/qemu/hw/ppc/prep.c @@ -21,6 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "hw/timer/m48t59.h" #include "hw/i386/pc.h" @@ -33,6 +34,7 @@ #include "hw/pci/pci_host.h" #include "hw/ppc/ppc.h" #include "hw/boards.h" +#include "qemu/error-report.h" #include "qemu/log.h" #include "hw/ide.h" #include "hw/loader.h" @@ -42,10 +44,9 @@ #include "sysemu/arch_init.h" #include "sysemu/qtest.h" #include "exec/address-spaces.h" +#include "trace.h" #include "elf.h" - -//#define HARD_DEBUG_PPC_IO -//#define DEBUG_PPC_IO +#include "qemu/cutils.h" /* SMP is not enabled, for now */ #define MAX_CPUS 1 @@ -57,26 +58,6 @@ #define KERNEL_LOAD_ADDR 0x01000000 #define INITRD_LOAD_ADDR 0x01800000 -#if defined (HARD_DEBUG_PPC_IO) && !defined (DEBUG_PPC_IO) -#define DEBUG_PPC_IO -#endif - -#if defined (HARD_DEBUG_PPC_IO) -#define PPC_IO_DPRINTF(fmt, ...) \ -do { \ - if (qemu_loglevel_mask(CPU_LOG_IOPORT)) { \ - qemu_log("%s: " fmt, __func__ , ## __VA_ARGS__); \ - } else { \ - printf("%s : " fmt, __func__ , ## __VA_ARGS__); \ - } \ -} while (0) -#elif defined (DEBUG_PPC_IO) -#define PPC_IO_DPRINTF(fmt, ...) \ -qemu_log_mask(CPU_LOG_IOPORT, fmt, ## __VA_ARGS__) -#else -#define PPC_IO_DPRINTF(fmt, ...) do { } while (0) -#endif - /* Constants for devices init */ static const int ide_iobase[2] = { 0x1f0, 0x170 }; static const int ide_iobase2[2] = { 0x3f6, 0x376 }; @@ -199,8 +180,7 @@ static void PREP_io_800_writeb (void *opaque, uint32_t addr, uint32_t val) { sysctrl_t *sysctrl = opaque; - PPC_IO_DPRINTF("0x%08" PRIx32 " => 0x%02" PRIx32 "\n", - addr - PPC_IO_BASE, val); + trace_prep_io_800_writeb(addr - PPC_IO_BASE, val); switch (addr) { case 0x0092: /* Special port 92 */ @@ -327,8 +307,7 @@ static uint32_t PREP_io_800_readb (void *opaque, uint32_t addr) printf("ERROR: unaffected IO port: %04" PRIx32 " read\n", addr); break; } - PPC_IO_DPRINTF("0x%08" PRIx32 " <= 0x%02" PRIx32 "\n", - addr - PPC_IO_BASE, retval); + trace_prep_io_800_readb(addr - PPC_IO_BASE, retval); return retval; } @@ -336,15 +315,6 @@ static uint32_t PREP_io_800_readb (void *opaque, uint32_t addr) #define NVRAM_SIZE 0x2000 -static void cpu_request_exit(void *opaque, int irq, int level) -{ - CPUState *cpu = current_cpu; - - if (cpu && level) { - cpu_exit(cpu); - } -} - static void ppc_prep_reset(void *opaque) { PowerPCCPU *cpu = opaque; @@ -565,7 +535,7 @@ static void ppc_prep_init(MachineState *machine) kernel_size = load_image_targphys(kernel_filename, kernel_base, ram_size - kernel_base); if (kernel_size < 0) { - hw_error("qemu: could not load kernel '%s'\n", kernel_filename); + error_report("could not load kernel '%s'", kernel_filename); exit(1); } /* load initrd */ @@ -574,8 +544,9 @@ static void ppc_prep_init(MachineState *machine) initrd_size = load_image_targphys(initrd_filename, initrd_base, ram_size - initrd_base); if (initrd_size < 0) { - hw_error("qemu: could not load initial ram disk '%s'\n", - initrd_filename); + error_report("could not load initial ram disk '%s'", + initrd_filename); + exit(1); } } else { initrd_base = 0; @@ -602,7 +573,8 @@ static void ppc_prep_init(MachineState *machine) } if (PPC_INPUT(env) != PPC_FLAGS_INPUT_6xx) { - hw_error("Only 6xx bus is supported on PREP machine\n"); + error_report("Only 6xx bus is supported on PREP machine"); + exit(1); } dev = qdev_create(NULL, "raven-pcihost"); @@ -610,7 +582,7 @@ static void ppc_prep_init(MachineState *machine) bios_name = BIOS_FILENAME; } qdev_prop_set_string(dev, "bios-name", bios_name); - qdev_prop_set_uint32(dev, "elf-machine", ELF_MACHINE); + qdev_prop_set_uint32(dev, "elf-machine", PPC_ELF_MACHINE); pcihost = PCI_HOST_BRIDGE(dev); object_property_add_child(qdev_get_machine(), "raven", OBJECT(dev), NULL); qdev_init_nofail(dev); @@ -626,8 +598,6 @@ static void ppc_prep_init(MachineState *machine) cpu = POWERPC_CPU(first_cpu); qdev_connect_gpio_out(&pci->qdev, 0, cpu->env.irq_inputs[PPC6xx_INPUT_INT]); - qdev_connect_gpio_out(&pci->qdev, 1, - qemu_allocate_irq(cpu_request_exit, NULL, 0)); sysbus_connect_irq(&pcihost->busdev, 0, qdev_get_gpio_in(&pci->qdev, 9)); sysbus_connect_irq(&pcihost->busdev, 1, qdev_get_gpio_in(&pci->qdev, 11)); sysbus_connect_irq(&pcihost->busdev, 2, qdev_get_gpio_in(&pci->qdev, 9)); @@ -698,17 +668,12 @@ static void ppc_prep_init(MachineState *machine) graphic_width, graphic_height, graphic_depth); } -static QEMUMachine prep_machine = { - .name = "prep", - .desc = "PowerPC PREP platform", - .init = ppc_prep_init, - .max_cpus = MAX_CPUS, - .default_boot_order = "cad", -}; - -static void prep_machine_init(void) +static void prep_machine_init(MachineClass *mc) { - qemu_register_machine(&prep_machine); + mc->desc = "PowerPC PREP platform"; + mc->init = ppc_prep_init; + mc->max_cpus = MAX_CPUS; + mc->default_boot_order = "cad"; } -machine_init(prep_machine_init); +DEFINE_MACHINE("prep", prep_machine_init) diff --git a/qemu/hw/ppc/spapr.c b/qemu/hw/ppc/spapr.c index a6f19473c..b69995e0d 100644 --- a/qemu/hw/ppc/spapr.c +++ b/qemu/hw/ppc/spapr.c @@ -24,15 +24,19 @@ * THE SOFTWARE. * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "sysemu/sysemu.h" #include "sysemu/numa.h" #include "hw/hw.h" #include "hw/fw-path-provider.h" #include "elf.h" #include "net/net.h" +#include "sysemu/device_tree.h" #include "sysemu/block-backend.h" #include "sysemu/cpus.h" #include "sysemu/kvm.h" +#include "sysemu/device_tree.h" #include "kvm_ppc.h" #include "migration/migration.h" #include "mmu-hash64.h" @@ -60,6 +64,7 @@ #include "hw/nmi.h" #include "hw/compat.h" +#include "qemu/cutils.h" #include <libfdt.h> @@ -73,7 +78,7 @@ * * We load our kernel at 4M, leaving space for SLOF initial image */ -#define FDT_MAX_SIZE 0x40000 +#define FDT_MAX_SIZE 0x100000 #define RTAS_MAX_SIZE 0x10000 #define RTAS_MAX_ADDR 0x80000000 /* RTAS must stay below that */ #define FW_MAX_SIZE 0x400000 @@ -85,8 +90,6 @@ #define TIMEBASE_FREQ 512000000ULL -#define MAX_CPUS 255 - #define PHANDLE_XICP 0x00001111 #define HTAB_SIZE(spapr) (1ULL << ((spapr)->htab_shift)) @@ -110,7 +113,7 @@ static XICSState *try_create_xics(const char *type, int nr_servers, } static XICSState *xics_system_init(MachineState *machine, - int nr_servers, int nr_irqs) + int nr_servers, int nr_irqs, Error **errp) { XICSState *icp = NULL; @@ -121,13 +124,15 @@ static XICSState *xics_system_init(MachineState *machine, icp = try_create_xics(TYPE_KVM_XICS, nr_servers, nr_irqs, &err); } if (machine_kernel_irqchip_required(machine) && !icp) { - error_report("kernel_irqchip requested but unavailable: %s", - error_get_pretty(err)); + error_reportf_err(err, + "kernel_irqchip requested but unavailable: "); + } else { + error_free(err); } } if (!icp) { - icp = try_create_xics(TYPE_XICS, nr_servers, nr_irqs, &error_abort); + icp = try_create_xics(TYPE_XICS, nr_servers, nr_irqs, errp); } return icp; @@ -373,8 +378,16 @@ static void *spapr_create_fdt_skel(hwaddr initrd_base, qemu_uuid[14], qemu_uuid[15]); _FDT((fdt_property_string(fdt, "vm,uuid", buf))); + if (qemu_uuid_set) { + _FDT((fdt_property_string(fdt, "system-id", buf))); + } g_free(buf); + if (qemu_get_vm_name()) { + _FDT((fdt_property_string(fdt, "ibm,partition-name", + qemu_get_vm_name()))); + } + _FDT((fdt_property_cell(fdt, "#address-cells", 0x2))); _FDT((fdt_property_cell(fdt, "#size-cells", 0x2))); @@ -427,6 +440,10 @@ static void *spapr_create_fdt_skel(hwaddr initrd_base, _FDT((fdt_property_cell(fdt, "rtas-event-scan-rate", RTAS_EVENT_SCAN_RATE))); + if (msi_nonbroken) { + _FDT((fdt_property(fdt, "ibm,change-msix-capable", NULL, 0))); + } + /* * According to PAPR, rtas ibm,os-term does not guarantee a return * back to the guest cpu. @@ -481,10 +498,11 @@ static void *spapr_create_fdt_skel(hwaddr initrd_base, * Older KVM versions with older guest kernels were broken with the * magic page, don't allow the guest to map it. */ - kvmppc_get_hypercall(first_cpu->env_ptr, hypercall, - sizeof(hypercall)); - _FDT((fdt_property(fdt, "hcall-instructions", hypercall, - sizeof(hypercall)))); + if (!kvmppc_get_hypercall(first_cpu->env_ptr, hypercall, + sizeof(hypercall))) { + _FDT((fdt_property(fdt, "hcall-instructions", hypercall, + sizeof(hypercall)))); + } } _FDT((fdt_end_node(fdt))); } @@ -495,44 +513,7 @@ static void *spapr_create_fdt_skel(hwaddr initrd_base, return fdt; } -int spapr_h_cas_compose_response(sPAPRMachineState *spapr, - target_ulong addr, target_ulong size) -{ - void *fdt, *fdt_skel; - sPAPRDeviceTreeUpdateHeader hdr = { .version_id = 1 }; - - size -= sizeof(hdr); - - /* Create sceleton */ - fdt_skel = g_malloc0(size); - _FDT((fdt_create(fdt_skel, size))); - _FDT((fdt_begin_node(fdt_skel, ""))); - _FDT((fdt_end_node(fdt_skel))); - _FDT((fdt_finish(fdt_skel))); - fdt = g_malloc0(size); - _FDT((fdt_open_into(fdt_skel, fdt, size))); - g_free(fdt_skel); - - /* Fix skeleton up */ - _FDT((spapr_fixup_cpu_dt(fdt, spapr))); - - /* Pack resulting tree */ - _FDT((fdt_pack(fdt))); - - if (fdt_totalsize(fdt) + sizeof(hdr) > size) { - trace_spapr_cas_failed(size); - return -1; - } - - cpu_physical_memory_write(addr, &hdr, sizeof(hdr)); - cpu_physical_memory_write(addr + sizeof(hdr), fdt, fdt_totalsize(fdt)); - trace_spapr_cas_continue(fdt_totalsize(fdt) + sizeof(hdr)); - g_free(fdt); - - return 0; -} - -static void spapr_populate_memory_node(void *fdt, int nodeid, hwaddr start, +static int spapr_populate_memory_node(void *fdt, int nodeid, hwaddr start, hwaddr size) { uint32_t associativity[] = { @@ -555,6 +536,7 @@ static void spapr_populate_memory_node(void *fdt, int nodeid, hwaddr start, sizeof(mem_reg_property)))); _FDT((fdt_setprop(fdt, off, "ibm,associativity", associativity, sizeof(associativity)))); + return off; } static int spapr_populate_memory(sPAPRMachineState *spapr, void *fdt) @@ -620,11 +602,27 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, uint32_t cpufreq = kvm_enabled() ? kvmppc_get_clockfreq() : 1000000000; uint32_t page_sizes_prop[64]; size_t page_sizes_prop_size; - QemuOpts *opts = qemu_opts_find(qemu_find_opts("smp-opts"), NULL); - unsigned sockets = opts ? qemu_opt_get_number(opts, "sockets", 0) : 0; - uint32_t cpus_per_socket = sockets ? (smp_cpus / sockets) : 1; + uint32_t vcpus_per_socket = smp_threads * smp_cores; uint32_t pft_size_prop[] = {0, cpu_to_be32(spapr->htab_shift)}; + /* Note: we keep CI large pages off for now because a 64K capable guest + * provisioned with large pages might otherwise try to map a qemu + * framebuffer (or other kind of memory mapped PCI BAR) using 64K pages + * even if that qemu runs on a 4k host. + * + * We can later add this bit back when we are confident this is not + * an issue (!HV KVM or 64K host) + */ + uint8_t pa_features_206[] = { 6, 0, + 0xf6, 0x1f, 0xc7, 0x00, 0x80, 0xc0 }; + uint8_t pa_features_207[] = { 24, 0, + 0xf6, 0x1f, 0xc7, 0xc0, 0x80, 0xf0, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00 }; + uint8_t *pa_features; + size_t pa_size; + _FDT((fdt_setprop_cell(fdt, offset, "reg", index))); _FDT((fdt_setprop_string(fdt, offset, "device_type", "cpu"))); @@ -653,6 +651,7 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, _FDT((fdt_setprop_cell(fdt, offset, "timebase-frequency", tbfreq))); _FDT((fdt_setprop_cell(fdt, offset, "clock-frequency", cpufreq))); + _FDT((fdt_setprop_cell(fdt, offset, "slb-size", env->slb_nr))); _FDT((fdt_setprop_cell(fdt, offset, "ibm,slb-size", env->slb_nr))); _FDT((fdt_setprop_string(fdt, offset, "status", "okay"))); _FDT((fdt_setprop(fdt, offset, "64-bit", NULL, 0))); @@ -690,8 +689,21 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, page_sizes_prop, page_sizes_prop_size))); } + /* Do the ibm,pa-features property, adjust it for ci-large-pages */ + if (env->mmu_model == POWERPC_MMU_2_06) { + pa_features = pa_features_206; + pa_size = sizeof(pa_features_206); + } else /* env->mmu_model == POWERPC_MMU_2_07 */ { + pa_features = pa_features_207; + pa_size = sizeof(pa_features_207); + } + if (env->ci_large_pages) { + pa_features[3] |= 0x20; + } + _FDT((fdt_setprop(fdt, offset, "ibm,pa-features", pa_features, pa_size))); + _FDT((fdt_setprop_cell(fdt, offset, "ibm,chip-id", - cs->cpu_index / cpus_per_socket))); + cs->cpu_index / vcpus_per_socket))); _FDT((fdt_setprop(fdt, offset, "ibm,pft-size", pft_size_prop, sizeof(pft_size_prop)))); @@ -738,12 +750,162 @@ static void spapr_populate_cpus_dt_node(void *fdt, sPAPRMachineState *spapr) } +/* + * Adds ibm,dynamic-reconfiguration-memory node. + * Refer to docs/specs/ppc-spapr-hotplug.txt for the documentation + * of this device tree node. + */ +static int spapr_populate_drconf_memory(sPAPRMachineState *spapr, void *fdt) +{ + MachineState *machine = MACHINE(spapr); + int ret, i, offset; + uint64_t lmb_size = SPAPR_MEMORY_BLOCK_SIZE; + uint32_t prop_lmb_size[] = {0, cpu_to_be32(lmb_size)}; + uint32_t nr_lmbs = (machine->maxram_size - machine->ram_size)/lmb_size; + uint32_t *int_buf, *cur_index, buf_len; + int nr_nodes = nb_numa_nodes ? nb_numa_nodes : 1; + + /* + * Don't create the node if there are no DR LMBs. + */ + if (!nr_lmbs) { + return 0; + } + + /* + * Allocate enough buffer size to fit in ibm,dynamic-memory + * or ibm,associativity-lookup-arrays + */ + buf_len = MAX(nr_lmbs * SPAPR_DR_LMB_LIST_ENTRY_SIZE + 1, nr_nodes * 4 + 2) + * sizeof(uint32_t); + cur_index = int_buf = g_malloc0(buf_len); + + offset = fdt_add_subnode(fdt, 0, "ibm,dynamic-reconfiguration-memory"); + + ret = fdt_setprop(fdt, offset, "ibm,lmb-size", prop_lmb_size, + sizeof(prop_lmb_size)); + if (ret < 0) { + goto out; + } + + ret = fdt_setprop_cell(fdt, offset, "ibm,memory-flags-mask", 0xff); + if (ret < 0) { + goto out; + } + + ret = fdt_setprop_cell(fdt, offset, "ibm,memory-preservation-time", 0x0); + if (ret < 0) { + goto out; + } + + /* ibm,dynamic-memory */ + int_buf[0] = cpu_to_be32(nr_lmbs); + cur_index++; + for (i = 0; i < nr_lmbs; i++) { + sPAPRDRConnector *drc; + sPAPRDRConnectorClass *drck; + uint64_t addr = i * lmb_size + spapr->hotplug_memory.base;; + uint32_t *dynamic_memory = cur_index; + + drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_LMB, + addr/lmb_size); + g_assert(drc); + drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + + dynamic_memory[0] = cpu_to_be32(addr >> 32); + dynamic_memory[1] = cpu_to_be32(addr & 0xffffffff); + dynamic_memory[2] = cpu_to_be32(drck->get_index(drc)); + dynamic_memory[3] = cpu_to_be32(0); /* reserved */ + dynamic_memory[4] = cpu_to_be32(numa_get_node(addr, NULL)); + if (addr < machine->ram_size || + memory_region_present(get_system_memory(), addr)) { + dynamic_memory[5] = cpu_to_be32(SPAPR_LMB_FLAGS_ASSIGNED); + } else { + dynamic_memory[5] = cpu_to_be32(0); + } + + cur_index += SPAPR_DR_LMB_LIST_ENTRY_SIZE; + } + ret = fdt_setprop(fdt, offset, "ibm,dynamic-memory", int_buf, buf_len); + if (ret < 0) { + goto out; + } + + /* ibm,associativity-lookup-arrays */ + cur_index = int_buf; + int_buf[0] = cpu_to_be32(nr_nodes); + int_buf[1] = cpu_to_be32(4); /* Number of entries per associativity list */ + cur_index += 2; + for (i = 0; i < nr_nodes; i++) { + uint32_t associativity[] = { + cpu_to_be32(0x0), + cpu_to_be32(0x0), + cpu_to_be32(0x0), + cpu_to_be32(i) + }; + memcpy(cur_index, associativity, sizeof(associativity)); + cur_index += 4; + } + ret = fdt_setprop(fdt, offset, "ibm,associativity-lookup-arrays", int_buf, + (cur_index - int_buf) * sizeof(uint32_t)); +out: + g_free(int_buf); + return ret; +} + +int spapr_h_cas_compose_response(sPAPRMachineState *spapr, + target_ulong addr, target_ulong size, + bool cpu_update, bool memory_update) +{ + void *fdt, *fdt_skel; + sPAPRDeviceTreeUpdateHeader hdr = { .version_id = 1 }; + sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(qdev_get_machine()); + + size -= sizeof(hdr); + + /* Create sceleton */ + fdt_skel = g_malloc0(size); + _FDT((fdt_create(fdt_skel, size))); + _FDT((fdt_begin_node(fdt_skel, ""))); + _FDT((fdt_end_node(fdt_skel))); + _FDT((fdt_finish(fdt_skel))); + fdt = g_malloc0(size); + _FDT((fdt_open_into(fdt_skel, fdt, size))); + g_free(fdt_skel); + + /* Fixup cpu nodes */ + if (cpu_update) { + _FDT((spapr_fixup_cpu_dt(fdt, spapr))); + } + + /* Generate ibm,dynamic-reconfiguration-memory node if required */ + if (memory_update && smc->dr_lmb_enabled) { + _FDT((spapr_populate_drconf_memory(spapr, fdt))); + } + + /* Pack resulting tree */ + _FDT((fdt_pack(fdt))); + + if (fdt_totalsize(fdt) + sizeof(hdr) > size) { + trace_spapr_cas_failed(size); + return -1; + } + + cpu_physical_memory_write(addr, &hdr, sizeof(hdr)); + cpu_physical_memory_write(addr + sizeof(hdr), fdt, fdt_totalsize(fdt)); + trace_spapr_cas_continue(fdt_totalsize(fdt) + sizeof(hdr)); + g_free(fdt); + + return 0; +} + static void spapr_finalize_fdt(sPAPRMachineState *spapr, hwaddr fdt_addr, hwaddr rtas_addr, hwaddr rtas_size) { MachineState *machine = MACHINE(qdev_get_machine()); + sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine); const char *boot_device = machine->boot_order; int ret, i; size_t cb = 0; @@ -768,13 +930,20 @@ static void spapr_finalize_fdt(sPAPRMachineState *spapr, exit(1); } - QLIST_FOREACH(phb, &spapr->phbs, list) { - ret = spapr_populate_pci_dt(phb, PHANDLE_XICP, fdt); + if (object_resolve_path_type("", TYPE_SPAPR_RNG, NULL)) { + ret = spapr_rng_populate_dt(fdt); + if (ret < 0) { + fprintf(stderr, "could not set up rng device in the fdt\n"); + exit(1); + } } - if (ret < 0) { - fprintf(stderr, "couldn't setup PCI devices in fdt\n"); - exit(1); + QLIST_FOREACH(phb, &spapr->phbs, list) { + ret = spapr_populate_pci_dt(phb, PHANDLE_XICP, fdt); + if (ret < 0) { + error_report("couldn't setup PCI devices in fdt"); + exit(1); + } } /* RTAS */ @@ -814,6 +983,10 @@ static void spapr_finalize_fdt(sPAPRMachineState *spapr, spapr_populate_chosen_stdout(fdt, spapr->vio_bus); } + if (smc->dr_lmb_enabled) { + _FDT(spapr_drc_populate_dt(fdt, 0, NULL, SPAPR_DR_CONNECTOR_TYPE_LMB)); + } + _FDT((fdt_pack(fdt))); if (fdt_totalsize(fdt) > FDT_MAX_SIZE) { @@ -822,6 +995,7 @@ static void spapr_finalize_fdt(sPAPRMachineState *spapr, exit(1); } + qemu_fdt_dumpdtb(fdt, fdt_totalsize(fdt)); cpu_physical_memory_write(fdt_addr, fdt, fdt_totalsize(fdt)); g_free(bootlist); @@ -851,45 +1025,93 @@ static void emulate_spapr_hypercall(PowerPCCPU *cpu) #define CLEAN_HPTE(_hpte) ((*(uint64_t *)(_hpte)) &= tswap64(~HPTE64_V_HPTE_DIRTY)) #define DIRTY_HPTE(_hpte) ((*(uint64_t *)(_hpte)) |= tswap64(HPTE64_V_HPTE_DIRTY)) -static void spapr_reset_htab(sPAPRMachineState *spapr) +/* + * Get the fd to access the kernel htab, re-opening it if necessary + */ +static int get_htab_fd(sPAPRMachineState *spapr) { - long shift; - int index; + if (spapr->htab_fd >= 0) { + return spapr->htab_fd; + } + + spapr->htab_fd = kvmppc_get_htab_fd(false); + if (spapr->htab_fd < 0) { + error_report("Unable to open fd for reading hash table from KVM: %s", + strerror(errno)); + } - /* allocate hash page table. For now we always make this 16mb, - * later we should probably make it scale to the size of guest - * RAM */ + return spapr->htab_fd; +} - shift = kvmppc_reset_htab(spapr->htab_shift); +static void close_htab_fd(sPAPRMachineState *spapr) +{ + if (spapr->htab_fd >= 0) { + close(spapr->htab_fd); + } + spapr->htab_fd = -1; +} - if (shift > 0) { - /* Kernel handles htab, we don't need to allocate one */ - spapr->htab_shift = shift; - kvmppc_kern_htab = true; +static int spapr_hpt_shift_for_ramsize(uint64_t ramsize) +{ + int shift; + + /* We aim for a hash table of size 1/128 the size of RAM (rounded + * up). The PAPR recommendation is actually 1/64 of RAM size, but + * that's much more than is needed for Linux guests */ + shift = ctz64(pow2ceil(ramsize)) - 7; + shift = MAX(shift, 18); /* Minimum architected size */ + shift = MIN(shift, 46); /* Maximum architected size */ + return shift; +} - /* Tell readers to update their file descriptor */ - if (spapr->htab_fd >= 0) { - spapr->htab_fd_stale = true; +static void spapr_reallocate_hpt(sPAPRMachineState *spapr, int shift, + Error **errp) +{ + long rc; + + /* Clean up any HPT info from a previous boot */ + g_free(spapr->htab); + spapr->htab = NULL; + spapr->htab_shift = 0; + close_htab_fd(spapr); + + rc = kvmppc_reset_htab(shift); + if (rc < 0) { + /* kernel-side HPT needed, but couldn't allocate one */ + error_setg_errno(errp, errno, + "Failed to allocate KVM HPT of order %d (try smaller maxmem?)", + shift); + /* This is almost certainly fatal, but if the caller really + * wants to carry on with shift == 0, it's welcome to try */ + } else if (rc > 0) { + /* kernel-side HPT allocated */ + if (rc != shift) { + error_setg(errp, + "Requested order %d HPT, but kernel allocated order %ld (try smaller maxmem?)", + shift, rc); } + + spapr->htab_shift = shift; + spapr->htab = NULL; } else { + /* kernel-side HPT not needed, allocate in userspace instead */ + size_t size = 1ULL << shift; + int i; + + spapr->htab = qemu_memalign(size, size); if (!spapr->htab) { - /* Allocate an htab if we don't yet have one */ - spapr->htab = qemu_memalign(HTAB_SIZE(spapr), HTAB_SIZE(spapr)); + error_setg_errno(errp, errno, + "Could not allocate HPT of order %d", shift); + return; } - /* And clear it */ - memset(spapr->htab, 0, HTAB_SIZE(spapr)); + memset(spapr->htab, 0, size); + spapr->htab_shift = shift; - for (index = 0; index < HTAB_SIZE(spapr) / HASH_PTE_SIZE_64; index++) { - DIRTY_HPTE(HPTE(spapr->htab, index)); + for (i = 0; i < size / HASH_PTE_SIZE_64; i++) { + DIRTY_HPTE(HPTE(spapr->htab, i)); } } - - /* Update the RMA size if necessary */ - if (spapr->vrma_adjust) { - spapr->rma_size = kvmppc_rma_size(spapr_node0_size(), - spapr->htab_shift); - } } static int find_unknown_sysbus_device(SysBusDevice *sbdev, void *opaque) @@ -909,39 +1131,26 @@ static int find_unknown_sysbus_device(SysBusDevice *sbdev, void *opaque) return 0; } -/* - * A guest reset will cause spapr->htab_fd to become stale if being used. - * Reopen the file descriptor to make sure the whole HTAB is properly read. - */ -static int spapr_check_htab_fd(sPAPRMachineState *spapr) -{ - int rc = 0; - - if (spapr->htab_fd_stale) { - close(spapr->htab_fd); - spapr->htab_fd = kvmppc_get_htab_fd(false); - if (spapr->htab_fd < 0) { - error_report("Unable to open fd for reading hash table from KVM: " - "%s", strerror(errno)); - rc = -1; - } - spapr->htab_fd_stale = false; - } - - return rc; -} - static void ppc_spapr_reset(void) { - sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); + MachineState *machine = MACHINE(qdev_get_machine()); + sPAPRMachineState *spapr = SPAPR_MACHINE(machine); PowerPCCPU *first_ppc_cpu; uint32_t rtas_limit; /* Check for unknown sysbus devices */ foreach_dynamic_sysbus_device(find_unknown_sysbus_device, NULL); - /* Reset the hash table & recalc the RMA */ - spapr_reset_htab(spapr); + /* Allocate and/or reset the hash page table */ + spapr_reallocate_hpt(spapr, + spapr_hpt_shift_for_ramsize(machine->maxram_size), + &error_fatal); + + /* Update the RMA size if necessary */ + if (spapr->vrma_adjust) { + spapr->rma_size = kvmppc_rma_size(spapr_node0_size(), + spapr->htab_shift); + } qemu_devices_reset(); @@ -987,24 +1196,8 @@ static void spapr_cpu_reset(void *opaque) env->spr[SPR_HIOR] = 0; - env->external_htab = (uint8_t *)spapr->htab; - if (kvm_enabled() && !env->external_htab) { - /* - * HV KVM, set external_htab to 1 so our ppc_hash64_load_hpte* - * functions do the right thing. - */ - env->external_htab = (void *)1; - } - env->htab_base = -1; - /* - * htab_mask is the mask used to normalize hash value to PTEG index. - * htab_shift is log2 of hash table size. - * We have 8 hpte per group, and each hpte is 16 bytes. - * ie have 128 bytes per hpte entry. - */ - env->htab_mask = (1ULL << (spapr->htab_shift - 7)) - 1; - env->spr[SPR_SDR1] = (target_ulong)(uintptr_t)spapr->htab | - (spapr->htab_shift - 18); + ppc_hash64_set_external_hpt(cpu, spapr->htab, spapr->htab_shift, + &error_fatal); } static void spapr_create_nvram(sPAPRMachineState *spapr) @@ -1013,7 +1206,8 @@ static void spapr_create_nvram(sPAPRMachineState *spapr) DriveInfo *dinfo = drive_get(IF_PFLASH, 0, 0); if (dinfo) { - qdev_prop_set_drive_nofail(dev, "drive", blk_by_legacy_dinfo(dinfo)); + qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(dinfo), + &error_fatal); } qdev_init_nofail(dev); @@ -1033,7 +1227,7 @@ static void spapr_rtc_create(sPAPRMachineState *spapr) } /* Returns whether we want to use VGA or not */ -static int spapr_vga_init(PCIBus *pci_bus) +static bool spapr_vga_init(PCIBus *pci_bus, Error **errp) { switch (vga_interface_type) { case VGA_NONE: @@ -1041,11 +1235,12 @@ static int spapr_vga_init(PCIBus *pci_bus) case VGA_DEVICE: return true; case VGA_STD: + case VGA_VIRTIO: return pci_vga_init(pci_bus) != NULL; default: - fprintf(stderr, "This vga model is not supported," - "currently it only supports -vga std\n"); - exit(0); + error_setg(errp, + "Unsupported VGA mode, only -vga std or -vga virtio is supported"); + return false; } } @@ -1099,14 +1294,6 @@ static int htab_save_setup(QEMUFile *f, void *opaque) spapr->htab_first_pass = true; } else { assert(kvm_enabled()); - - spapr->htab_fd = kvmppc_get_htab_fd(false); - spapr->htab_fd_stale = false; - if (spapr->htab_fd < 0) { - fprintf(stderr, "Unable to open fd for reading hash table from KVM: %s\n", - strerror(errno)); - return -1; - } } @@ -1116,6 +1303,7 @@ static int htab_save_setup(QEMUFile *f, void *opaque) static void htab_save_first_pass(QEMUFile *f, sPAPRMachineState *spapr, int64_t max_ns) { + bool has_timeout = max_ns != -1; int htabslots = HTAB_SIZE(spapr) / HASH_PTE_SIZE_64; int index = spapr->htab_save_index; int64_t starttime = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); @@ -1149,7 +1337,8 @@ static void htab_save_first_pass(QEMUFile *f, sPAPRMachineState *spapr, qemu_put_buffer(f, HPTE(spapr->htab, chunkstart), HASH_PTE_SIZE_64 * n_valid); - if ((qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - starttime) > max_ns) { + if (has_timeout && + (qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - starttime) > max_ns) { break; } } @@ -1246,6 +1435,7 @@ static int htab_save_later_pass(QEMUFile *f, sPAPRMachineState *spapr, static int htab_save_iterate(QEMUFile *f, void *opaque) { sPAPRMachineState *spapr = opaque; + int fd; int rc = 0; /* Iteration header */ @@ -1254,13 +1444,12 @@ static int htab_save_iterate(QEMUFile *f, void *opaque) if (!spapr->htab) { assert(kvm_enabled()); - rc = spapr_check_htab_fd(spapr); - if (rc < 0) { - return rc; + fd = get_htab_fd(spapr); + if (fd < 0) { + return fd; } - rc = kvmppc_save_htab(f, spapr->htab_fd, - MAX_KVM_BUF_SIZE, MAX_ITERATION_NS); + rc = kvmppc_save_htab(f, fd, MAX_KVM_BUF_SIZE, MAX_ITERATION_NS); if (rc < 0) { return rc; } @@ -1281,6 +1470,7 @@ static int htab_save_iterate(QEMUFile *f, void *opaque) static int htab_save_complete(QEMUFile *f, void *opaque) { sPAPRMachineState *spapr = opaque; + int fd; /* Iteration header */ qemu_put_be32(f, 0); @@ -1290,18 +1480,20 @@ static int htab_save_complete(QEMUFile *f, void *opaque) assert(kvm_enabled()); - rc = spapr_check_htab_fd(spapr); - if (rc < 0) { - return rc; + fd = get_htab_fd(spapr); + if (fd < 0) { + return fd; } - rc = kvmppc_save_htab(f, spapr->htab_fd, MAX_KVM_BUF_SIZE, -1); + rc = kvmppc_save_htab(f, fd, MAX_KVM_BUF_SIZE, -1); if (rc < 0) { return rc; } - close(spapr->htab_fd); - spapr->htab_fd = -1; + close_htab_fd(spapr); } else { + if (spapr->htab_first_pass) { + htab_save_first_pass(f, spapr, -1); + } htab_save_later_pass(f, spapr, -1); } @@ -1320,15 +1512,19 @@ static int htab_load(QEMUFile *f, void *opaque, int version_id) int fd = -1; if (version_id < 1 || version_id > 1) { - fprintf(stderr, "htab_load() bad version\n"); + error_report("htab_load() bad version"); return -EINVAL; } section_hdr = qemu_get_be32(f); if (section_hdr) { - /* First section, just the hash shift */ - if (spapr->htab_shift != section_hdr) { + Error *local_err = NULL; + + /* First section gives the htab size */ + spapr_reallocate_hpt(spapr, section_hdr, &local_err); + if (local_err) { + error_report_err(local_err); return -EINVAL; } return 0; @@ -1339,8 +1535,8 @@ static int htab_load(QEMUFile *f, void *opaque, int version_id) fd = kvmppc_get_htab_fd(true); if (fd < 0) { - fprintf(stderr, "Unable to open fd to restore KVM hash table: %s\n", - strerror(errno)); + error_report("Unable to open fd to restore KVM hash table: %s", + strerror(errno)); } } @@ -1360,9 +1556,9 @@ static int htab_load(QEMUFile *f, void *opaque, int version_id) if ((index + n_valid + n_invalid) > (HTAB_SIZE(spapr) / HASH_PTE_SIZE_64)) { /* Bad index in stream */ - fprintf(stderr, "htab_load() bad index %d (%hd+%hd entries) " - "in htab stream (htab_shift=%d)\n", index, n_valid, n_invalid, - spapr->htab_shift); + error_report( + "htab_load() bad index %d (%hd+%hd entries) in htab stream (htab_shift=%d)", + index, n_valid, n_invalid, spapr->htab_shift); return -EINVAL; } @@ -1398,7 +1594,7 @@ static int htab_load(QEMUFile *f, void *opaque, int version_id) static SaveVMHandlers savevm_htab_handlers = { .save_live_setup = htab_save_setup, .save_live_iterate = htab_save_iterate, - .save_live_complete = htab_save_complete, + .save_live_complete_precopy = htab_save_complete, .load_state = htab_load, }; @@ -1409,26 +1605,24 @@ static void spapr_boot_set(void *opaque, const char *boot_device, machine->boot_order = g_strdup(boot_device); } -static void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu) +static void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu, + Error **errp) { CPUPPCState *env = &cpu->env; /* Set time-base frequency to 512 MHz */ cpu_ppc_tb_init(env, TIMEBASE_FREQ); - /* PAPR always has exception vectors in RAM not ROM. To ensure this, - * MSR[IP] should never be set. - */ - env->msr_mask &= ~(1 << 6); - - /* Tell KVM that we're in PAPR mode */ - if (kvm_enabled()) { - kvmppc_set_papr(cpu); - } + /* Enable PAPR mode in TCG or KVM */ + cpu_ppc_set_papr(cpu); if (cpu->max_compat) { - if (ppc_set_compat(cpu, cpu->max_compat) < 0) { - exit(1); + Error *local_err = NULL; + + ppc_set_compat(cpu, cpu->max_compat, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; } } @@ -1437,10 +1631,84 @@ static void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu) qemu_register_reset(spapr_cpu_reset, cpu); } +/* + * Reset routine for LMB DR devices. + * + * Unlike PCI DR devices, LMB DR devices explicitly register this reset + * routine. Reset for PCI DR devices will be handled by PHB reset routine + * when it walks all its children devices. LMB devices reset occurs + * as part of spapr_ppc_reset(). + */ +static void spapr_drc_reset(void *opaque) +{ + sPAPRDRConnector *drc = opaque; + DeviceState *d = DEVICE(drc); + + if (d) { + device_reset(d); + } +} + +static void spapr_create_lmb_dr_connectors(sPAPRMachineState *spapr) +{ + MachineState *machine = MACHINE(spapr); + uint64_t lmb_size = SPAPR_MEMORY_BLOCK_SIZE; + uint32_t nr_lmbs = (machine->maxram_size - machine->ram_size)/lmb_size; + int i; + + for (i = 0; i < nr_lmbs; i++) { + sPAPRDRConnector *drc; + uint64_t addr; + + addr = i * lmb_size + spapr->hotplug_memory.base; + drc = spapr_dr_connector_new(OBJECT(spapr), SPAPR_DR_CONNECTOR_TYPE_LMB, + addr/lmb_size); + qemu_register_reset(spapr_drc_reset, drc); + } +} + +/* + * If RAM size, maxmem size and individual node mem sizes aren't aligned + * to SPAPR_MEMORY_BLOCK_SIZE(256MB), then refuse to start the guest + * since we can't support such unaligned sizes with DRCONF_MEMORY. + */ +static void spapr_validate_node_memory(MachineState *machine, Error **errp) +{ + int i; + + if (machine->ram_size % SPAPR_MEMORY_BLOCK_SIZE) { + error_setg(errp, "Memory size 0x" RAM_ADDR_FMT + " is not aligned to %llu MiB", + machine->ram_size, + SPAPR_MEMORY_BLOCK_SIZE / M_BYTE); + return; + } + + if (machine->maxram_size % SPAPR_MEMORY_BLOCK_SIZE) { + error_setg(errp, "Maximum memory size 0x" RAM_ADDR_FMT + " is not aligned to %llu MiB", + machine->ram_size, + SPAPR_MEMORY_BLOCK_SIZE / M_BYTE); + return; + } + + for (i = 0; i < nb_numa_nodes; i++) { + if (numa_info[i].node_mem % SPAPR_MEMORY_BLOCK_SIZE) { + error_setg(errp, + "Node %d memory size 0x%" PRIx64 + " is not aligned to %llu MiB", + i, numa_info[i].node_mem, + SPAPR_MEMORY_BLOCK_SIZE / M_BYTE); + return; + } + } +} + /* pSeries LPAR / sPAPR hardware init */ static void ppc_spapr_init(MachineState *machine) { sPAPRMachineState *spapr = SPAPR_MACHINE(machine); + sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine); const char *kernel_filename = machine->kernel_filename; const char *kernel_cmdline = machine->kernel_cmdline; const char *initrd_filename = machine->initrd_filename; @@ -1459,7 +1727,7 @@ static void ppc_spapr_init(MachineState *machine) bool kernel_le = false; char *filename; - msi_supported = true; + msi_nonbroken = true; QLIST_INIT(&spapr->phbs); @@ -1494,30 +1762,23 @@ static void ppc_spapr_init(MachineState *machine) } if (spapr->rma_size > node0_size) { - fprintf(stderr, "Error: Numa node 0 has to span the RMA (%#08"HWADDR_PRIx")\n", - spapr->rma_size); + error_report("Numa node 0 has to span the RMA (%#08"HWADDR_PRIx")", + spapr->rma_size); exit(1); } /* Setup a load limit for the ramdisk leaving room for SLOF and FDT */ load_limit = MIN(spapr->rma_size, RTAS_MAX_ADDR) - FW_OVERHEAD; - /* We aim for a hash table of size 1/128 the size of RAM. The - * normal rule of thumb is 1/64 the size of RAM, but that's much - * more than needed for the Linux guests we support. */ - spapr->htab_shift = 18; /* Minimum architected size */ - while (spapr->htab_shift <= 46) { - if ((1ULL << (spapr->htab_shift + 7)) >= machine->ram_size) { - break; - } - spapr->htab_shift++; - } - /* Set up Interrupt Controller before we create the VCPUs */ spapr->icp = xics_system_init(machine, DIV_ROUND_UP(max_cpus * kvmppc_smt_threads(), smp_threads), - XICS_IRQS); + XICS_IRQS, &error_fatal); + + if (smc->dr_lmb_enabled) { + spapr_validate_node_memory(machine, &error_fatal); + } /* init CPUs */ if (machine->cpu_model == NULL) { @@ -1526,15 +1787,16 @@ static void ppc_spapr_init(MachineState *machine) for (i = 0; i < smp_cpus; i++) { cpu = cpu_ppc_init(machine->cpu_model); if (cpu == NULL) { - fprintf(stderr, "Unable to find PowerPC CPU definition\n"); + error_report("Unable to find PowerPC CPU definition"); exit(1); } - spapr_cpu_init(spapr, cpu); + spapr_cpu_init(spapr, cpu, &error_fatal); } if (kvm_enabled()) { /* Enable H_LOGICAL_CI_* so SLOF can talk to in-kernel devices */ kvmppc_enable_logical_ci_hcalls(); + kvmppc_enable_set_mode_hcall(); } /* allocate RAM */ @@ -1550,6 +1812,29 @@ static void ppc_spapr_init(MachineState *machine) memory_region_add_subregion(sysmem, 0, rma_region); } + /* initialize hotplug memory address space */ + if (machine->ram_size < machine->maxram_size) { + ram_addr_t hotplug_mem_size = machine->maxram_size - machine->ram_size; + + if (machine->ram_slots > SPAPR_MAX_RAM_SLOTS) { + error_report("Specified number of memory slots %" + PRIu64" exceeds max supported %d", + machine->ram_slots, SPAPR_MAX_RAM_SLOTS); + exit(1); + } + + spapr->hotplug_memory.base = ROUND_UP(machine->ram_size, + SPAPR_HOTPLUG_MEM_ALIGN); + memory_region_init(&spapr->hotplug_memory.mr, OBJECT(spapr), + "hotplug-memory", hotplug_mem_size); + memory_region_add_subregion(sysmem, spapr->hotplug_memory.base, + &spapr->hotplug_memory.mr); + } + + if (smc->dr_lmb_enabled) { + spapr_create_lmb_dr_connectors(spapr); + } + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "spapr-rtas.bin"); if (!filename) { error_report("Could not find LPAR rtas '%s'", "spapr-rtas.bin"); @@ -1610,13 +1895,17 @@ static void ppc_spapr_init(MachineState *machine) } /* Graphics */ - if (spapr_vga_init(phb->bus)) { + if (spapr_vga_init(phb->bus, &error_fatal)) { spapr->has_graphics = true; machine->usb |= defaults_enabled() && !machine->usb_disabled; } if (machine->usb) { - pci_create_simple(phb->bus, -1, "pci-ohci"); + if (smc->use_ohci_by_default) { + pci_create_simple(phb->bus, -1, "pci-ohci"); + } else { + pci_create_simple(phb->bus, -1, "nec-usb-xhci"); + } if (spapr->has_graphics) { USBBus *usb_bus = usb_bus_find(-1); @@ -1627,8 +1916,9 @@ static void ppc_spapr_init(MachineState *machine) } if (spapr->rma_size < (MIN_RMA_SLOF << 20)) { - fprintf(stderr, "qemu: pSeries SLOF firmware requires >= " - "%ldM guest RMA (Real Mode Area memory)\n", MIN_RMA_SLOF); + error_report( + "pSeries SLOF firmware requires >= %ldM guest RMA (Real Mode Area memory)", + MIN_RMA_SLOF); exit(1); } @@ -1636,16 +1926,18 @@ static void ppc_spapr_init(MachineState *machine) uint64_t lowaddr = 0; kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL, - NULL, &lowaddr, NULL, 1, ELF_MACHINE, 0); + NULL, &lowaddr, NULL, 1, PPC_ELF_MACHINE, + 0, 0); if (kernel_size == ELF_LOAD_WRONG_ENDIAN) { kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL, - NULL, &lowaddr, NULL, 0, ELF_MACHINE, 0); + NULL, &lowaddr, NULL, 0, PPC_ELF_MACHINE, + 0, 0); kernel_le = kernel_size > 0; } if (kernel_size < 0) { - fprintf(stderr, "qemu: error loading %s: %s\n", - kernel_filename, load_elf_strerror(kernel_size)); + error_report("error loading %s: %s", + kernel_filename, load_elf_strerror(kernel_size)); exit(1); } @@ -1658,8 +1950,8 @@ static void ppc_spapr_init(MachineState *machine) initrd_size = load_image_targphys(initrd_filename, initrd_base, load_limit - initrd_base); if (initrd_size < 0) { - fprintf(stderr, "qemu: could not load initial ram disk '%s'\n", - initrd_filename); + error_report("could not load initial ram disk '%s'", + initrd_filename); exit(1); } } else { @@ -1796,6 +2088,9 @@ static void spapr_set_kvm_type(Object *obj, const char *value, Error **errp) static void spapr_machine_initfn(Object *obj) { + sPAPRMachineState *spapr = SPAPR_MACHINE(obj); + + spapr->htab_fd = -1; object_property_add_str(obj, "kvm-type", spapr_get_kvm_type, spapr_set_kvm_type, NULL); object_property_set_description(obj, "kvm-type", @@ -1803,6 +2098,13 @@ static void spapr_machine_initfn(Object *obj) NULL); } +static void spapr_machine_finalizefn(Object *obj) +{ + sPAPRMachineState *spapr = SPAPR_MACHINE(obj); + + g_free(spapr->kvm_type); +} + static void ppc_cpu_do_nmi_on_cpu(void *arg) { CPUState *cs = arg; @@ -1820,22 +2122,177 @@ static void spapr_nmi(NMIState *n, int cpu_index, Error **errp) } } +static void spapr_add_lmbs(DeviceState *dev, uint64_t addr, uint64_t size, + uint32_t node, Error **errp) +{ + sPAPRDRConnector *drc; + sPAPRDRConnectorClass *drck; + uint32_t nr_lmbs = size/SPAPR_MEMORY_BLOCK_SIZE; + int i, fdt_offset, fdt_size; + void *fdt; + + /* + * Check for DRC connectors and send hotplug notification to the + * guest only in case of hotplugged memory. This allows cold plugged + * memory to be specified at boot time. + */ + if (!dev->hotplugged) { + return; + } + + for (i = 0; i < nr_lmbs; i++) { + drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_LMB, + addr/SPAPR_MEMORY_BLOCK_SIZE); + g_assert(drc); + + fdt = create_device_tree(&fdt_size); + fdt_offset = spapr_populate_memory_node(fdt, node, addr, + SPAPR_MEMORY_BLOCK_SIZE); + + drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + drck->attach(drc, dev, fdt, fdt_offset, !dev->hotplugged, errp); + addr += SPAPR_MEMORY_BLOCK_SIZE; + } + spapr_hotplug_req_add_by_count(SPAPR_DR_CONNECTOR_TYPE_LMB, nr_lmbs); +} + +static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev, + uint32_t node, Error **errp) +{ + Error *local_err = NULL; + sPAPRMachineState *ms = SPAPR_MACHINE(hotplug_dev); + PCDIMMDevice *dimm = PC_DIMM(dev); + PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); + MemoryRegion *mr = ddc->get_memory_region(dimm); + uint64_t align = memory_region_get_alignment(mr); + uint64_t size = memory_region_size(mr); + uint64_t addr; + + if (size % SPAPR_MEMORY_BLOCK_SIZE) { + error_setg(&local_err, "Hotplugged memory size must be a multiple of " + "%lld MB", SPAPR_MEMORY_BLOCK_SIZE/M_BYTE); + goto out; + } + + pc_dimm_memory_plug(dev, &ms->hotplug_memory, mr, align, &local_err); + if (local_err) { + goto out; + } + + addr = object_property_get_int(OBJECT(dimm), PC_DIMM_ADDR_PROP, &local_err); + if (local_err) { + pc_dimm_memory_unplug(dev, &ms->hotplug_memory, mr); + goto out; + } + + spapr_add_lmbs(dev, addr, size, node, &error_abort); + +out: + error_propagate(errp, local_err); +} + +static void spapr_machine_device_plug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(qdev_get_machine()); + + if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { + int node; + + if (!smc->dr_lmb_enabled) { + error_setg(errp, "Memory hotplug not supported for this machine"); + return; + } + node = object_property_get_int(OBJECT(dev), PC_DIMM_NODE_PROP, errp); + if (*errp) { + return; + } + if (node < 0 || node >= MAX_NODES) { + error_setg(errp, "Invaild node %d", node); + return; + } + + /* + * Currently PowerPC kernel doesn't allow hot-adding memory to + * memory-less node, but instead will silently add the memory + * to the first node that has some memory. This causes two + * unexpected behaviours for the user. + * + * - Memory gets hotplugged to a different node than what the user + * specified. + * - Since pc-dimm subsystem in QEMU still thinks that memory belongs + * to memory-less node, a reboot will set things accordingly + * and the previously hotplugged memory now ends in the right node. + * This appears as if some memory moved from one node to another. + * + * So until kernel starts supporting memory hotplug to memory-less + * nodes, just prevent such attempts upfront in QEMU. + */ + if (nb_numa_nodes && !numa_info[node].node_mem) { + error_setg(errp, "Can't hotplug memory to memory-less node %d", + node); + return; + } + + spapr_memory_plug(hotplug_dev, dev, node, errp); + } +} + +static void spapr_machine_device_unplug(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { + error_setg(errp, "Memory hot unplug not supported by sPAPR"); + } +} + +static HotplugHandler *spapr_get_hotpug_handler(MachineState *machine, + DeviceState *dev) +{ + if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { + return HOTPLUG_HANDLER(machine); + } + return NULL; +} + +static unsigned spapr_cpu_index_to_socket_id(unsigned cpu_index) +{ + /* Allocate to NUMA nodes on a "socket" basis (not that concept of + * socket means much for the paravirtualized PAPR platform) */ + return cpu_index / smp_threads / smp_cores; +} + static void spapr_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); + sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(oc); FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(oc); NMIClass *nc = NMI_CLASS(oc); + HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); + + mc->desc = "pSeries Logical Partition (PAPR compliant)"; + /* + * We set up the default / latest behaviour here. The class_init + * functions for the specific versioned machine types can override + * these details for backwards compatibility + */ mc->init = ppc_spapr_init; mc->reset = ppc_spapr_reset; mc->block_default_type = IF_SCSI; - mc->max_cpus = MAX_CPUS; + mc->max_cpus = MAX_CPUMASK_BITS; mc->no_parallel = 1; mc->default_boot_order = ""; mc->default_ram_size = 512 * M_BYTE; mc->kvm_type = spapr_kvm_type; mc->has_dynamic_sysbus = true; + mc->pci_allow_0_address = true; + mc->get_hotplug_handler = spapr_get_hotpug_handler; + hc->plug = spapr_machine_device_plug; + hc->unplug = spapr_machine_device_unplug; + mc->cpu_index_to_socket_id = spapr_cpu_index_to_socket_id; + smc->dr_lmb_enabled = true; fwc->get_dev_path = spapr_get_fw_dev_path; nc->nmi_monitor_handler = spapr_nmi; } @@ -1846,153 +2303,183 @@ static const TypeInfo spapr_machine_info = { .abstract = true, .instance_size = sizeof(sPAPRMachineState), .instance_init = spapr_machine_initfn, + .instance_finalize = spapr_machine_finalizefn, .class_size = sizeof(sPAPRMachineClass), .class_init = spapr_machine_class_init, .interfaces = (InterfaceInfo[]) { { TYPE_FW_PATH_PROVIDER }, { TYPE_NMI }, + { TYPE_HOTPLUG_HANDLER }, { } }, }; -#define SPAPR_COMPAT_2_3 \ - HW_COMPAT_2_3 \ - {\ - .driver = "spapr-pci-host-bridge",\ - .property = "dynamic-reconfiguration",\ - .value = "off",\ - }, - -#define SPAPR_COMPAT_2_2 \ - SPAPR_COMPAT_2_3 \ - HW_COMPAT_2_2 \ - {\ - .driver = TYPE_SPAPR_PCI_HOST_BRIDGE,\ - .property = "mem_win_size",\ - .value = "0x20000000",\ - }, - -#define SPAPR_COMPAT_2_1 \ - SPAPR_COMPAT_2_2 \ - HW_COMPAT_2_1 +#define DEFINE_SPAPR_MACHINE(suffix, verstr, latest) \ + static void spapr_machine_##suffix##_class_init(ObjectClass *oc, \ + void *data) \ + { \ + MachineClass *mc = MACHINE_CLASS(oc); \ + spapr_machine_##suffix##_class_options(mc); \ + if (latest) { \ + mc->alias = "pseries"; \ + mc->is_default = 1; \ + } \ + } \ + static void spapr_machine_##suffix##_instance_init(Object *obj) \ + { \ + MachineState *machine = MACHINE(obj); \ + spapr_machine_##suffix##_instance_options(machine); \ + } \ + static const TypeInfo spapr_machine_##suffix##_info = { \ + .name = MACHINE_TYPE_NAME("pseries-" verstr), \ + .parent = TYPE_SPAPR_MACHINE, \ + .class_init = spapr_machine_##suffix##_class_init, \ + .instance_init = spapr_machine_##suffix##_instance_init, \ + }; \ + static void spapr_machine_register_##suffix(void) \ + { \ + type_register(&spapr_machine_##suffix##_info); \ + } \ + type_init(spapr_machine_register_##suffix) -static void spapr_compat_2_3(Object *obj) +/* + * pseries-2.6 + */ +static void spapr_machine_2_6_instance_options(MachineState *machine) { - savevm_skip_section_footers(); - global_state_set_optional(); } -static void spapr_compat_2_2(Object *obj) +static void spapr_machine_2_6_class_options(MachineClass *mc) { - spapr_compat_2_3(obj); + /* Defaults for the latest behaviour inherited from the base class */ } -static void spapr_compat_2_1(Object *obj) -{ - spapr_compat_2_2(obj); -} +DEFINE_SPAPR_MACHINE(2_6, "2.6", true); + +/* + * pseries-2.5 + */ +#define SPAPR_COMPAT_2_5 \ + HW_COMPAT_2_5 \ + { \ + .driver = "spapr-vlan", \ + .property = "use-rx-buffer-pools", \ + .value = "off", \ + }, -static void spapr_machine_2_3_instance_init(Object *obj) +static void spapr_machine_2_5_instance_options(MachineState *machine) { - spapr_compat_2_3(obj); - spapr_machine_initfn(obj); } -static void spapr_machine_2_2_instance_init(Object *obj) +static void spapr_machine_2_5_class_options(MachineClass *mc) { - spapr_compat_2_2(obj); - spapr_machine_initfn(obj); + sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc); + + spapr_machine_2_6_class_options(mc); + smc->use_ohci_by_default = true; + SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_5); } -static void spapr_machine_2_1_instance_init(Object *obj) +DEFINE_SPAPR_MACHINE(2_5, "2.5", false); + +/* + * pseries-2.4 + */ +#define SPAPR_COMPAT_2_4 \ + SPAPR_COMPAT_2_5 \ + HW_COMPAT_2_4 + +static void spapr_machine_2_4_instance_options(MachineState *machine) { - spapr_compat_2_1(obj); - spapr_machine_initfn(obj); + spapr_machine_2_5_instance_options(machine); } -static void spapr_machine_2_1_class_init(ObjectClass *oc, void *data) +static void spapr_machine_2_4_class_options(MachineClass *mc) { - MachineClass *mc = MACHINE_CLASS(oc); - static GlobalProperty compat_props[] = { - SPAPR_COMPAT_2_1 - { /* end of list */ } - }; + sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc); - mc->name = "pseries-2.1"; - mc->desc = "pSeries Logical Partition (PAPR compliant) v2.1"; - mc->compat_props = compat_props; + spapr_machine_2_5_class_options(mc); + smc->dr_lmb_enabled = false; + SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_4); } -static const TypeInfo spapr_machine_2_1_info = { - .name = TYPE_SPAPR_MACHINE "2.1", - .parent = TYPE_SPAPR_MACHINE, - .class_init = spapr_machine_2_1_class_init, - .instance_init = spapr_machine_2_1_instance_init, -}; +DEFINE_SPAPR_MACHINE(2_4, "2.4", false); + +/* + * pseries-2.3 + */ +#define SPAPR_COMPAT_2_3 \ + SPAPR_COMPAT_2_4 \ + HW_COMPAT_2_3 \ + {\ + .driver = "spapr-pci-host-bridge",\ + .property = "dynamic-reconfiguration",\ + .value = "off",\ + }, -static void spapr_machine_2_2_class_init(ObjectClass *oc, void *data) +static void spapr_machine_2_3_instance_options(MachineState *machine) { - static GlobalProperty compat_props[] = { - SPAPR_COMPAT_2_2 - { /* end of list */ } - }; - MachineClass *mc = MACHINE_CLASS(oc); + spapr_machine_2_4_instance_options(machine); + savevm_skip_section_footers(); + global_state_set_optional(); + savevm_skip_configuration(); +} - mc->name = "pseries-2.2"; - mc->desc = "pSeries Logical Partition (PAPR compliant) v2.2"; - mc->compat_props = compat_props; +static void spapr_machine_2_3_class_options(MachineClass *mc) +{ + spapr_machine_2_4_class_options(mc); + SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_3); } +DEFINE_SPAPR_MACHINE(2_3, "2.3", false); -static const TypeInfo spapr_machine_2_2_info = { - .name = TYPE_SPAPR_MACHINE "2.2", - .parent = TYPE_SPAPR_MACHINE, - .class_init = spapr_machine_2_2_class_init, - .instance_init = spapr_machine_2_2_instance_init, -}; +/* + * pseries-2.2 + */ -static void spapr_machine_2_3_class_init(ObjectClass *oc, void *data) +#define SPAPR_COMPAT_2_2 \ + SPAPR_COMPAT_2_3 \ + HW_COMPAT_2_2 \ + {\ + .driver = TYPE_SPAPR_PCI_HOST_BRIDGE,\ + .property = "mem_win_size",\ + .value = "0x20000000",\ + }, + +static void spapr_machine_2_2_instance_options(MachineState *machine) { - static GlobalProperty compat_props[] = { - SPAPR_COMPAT_2_3 - { /* end of list */ } - }; - MachineClass *mc = MACHINE_CLASS(oc); + spapr_machine_2_3_instance_options(machine); + machine->suppress_vmdesc = true; +} - mc->name = "pseries-2.3"; - mc->desc = "pSeries Logical Partition (PAPR compliant) v2.3"; - mc->compat_props = compat_props; +static void spapr_machine_2_2_class_options(MachineClass *mc) +{ + spapr_machine_2_3_class_options(mc); + SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_2); } +DEFINE_SPAPR_MACHINE(2_2, "2.2", false); -static const TypeInfo spapr_machine_2_3_info = { - .name = TYPE_SPAPR_MACHINE "2.3", - .parent = TYPE_SPAPR_MACHINE, - .class_init = spapr_machine_2_3_class_init, - .instance_init = spapr_machine_2_3_instance_init, -}; +/* + * pseries-2.1 + */ +#define SPAPR_COMPAT_2_1 \ + SPAPR_COMPAT_2_2 \ + HW_COMPAT_2_1 -static void spapr_machine_2_4_class_init(ObjectClass *oc, void *data) +static void spapr_machine_2_1_instance_options(MachineState *machine) { - MachineClass *mc = MACHINE_CLASS(oc); - - mc->name = "pseries-2.4"; - mc->desc = "pSeries Logical Partition (PAPR compliant) v2.4"; - mc->alias = "pseries"; - mc->is_default = 1; + spapr_machine_2_2_instance_options(machine); } -static const TypeInfo spapr_machine_2_4_info = { - .name = TYPE_SPAPR_MACHINE "2.4", - .parent = TYPE_SPAPR_MACHINE, - .class_init = spapr_machine_2_4_class_init, -}; +static void spapr_machine_2_1_class_options(MachineClass *mc) +{ + spapr_machine_2_2_class_options(mc); + SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_1); +} +DEFINE_SPAPR_MACHINE(2_1, "2.1", false); static void spapr_machine_register_types(void) { type_register_static(&spapr_machine_info); - type_register_static(&spapr_machine_2_1_info); - type_register_static(&spapr_machine_2_2_info); - type_register_static(&spapr_machine_2_3_info); - type_register_static(&spapr_machine_2_4_info); } type_init(spapr_machine_register_types) diff --git a/qemu/hw/ppc/spapr_drc.c b/qemu/hw/ppc/spapr_drc.c index ee874326e..1f5f1d790 100644 --- a/qemu/hw/ppc/spapr_drc.c +++ b/qemu/hw/ppc/spapr_drc.c @@ -10,11 +10,16 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "cpu.h" +#include "qemu/cutils.h" #include "hw/ppc/spapr_drc.h" #include "qom/object.h" #include "hw/qdev.h" #include "qapi/visitor.h" #include "qemu/error-report.h" +#include "hw/ppc/spapr.h" /* for RTAS return codes */ /* #define DEBUG_SPAPR_DRC */ @@ -32,7 +37,7 @@ #define DRC_CONTAINER_PATH "/dr-connector" #define DRC_INDEX_TYPE_SHIFT 28 -#define DRC_INDEX_ID_MASK (~(~0 << DRC_INDEX_TYPE_SHIFT)) +#define DRC_INDEX_ID_MASK ((1ULL << DRC_INDEX_TYPE_SHIFT) - 1) static sPAPRDRConnectorTypeShift get_type_shift(sPAPRDRConnectorType type) { @@ -59,13 +64,23 @@ static uint32_t get_index(sPAPRDRConnector *drc) (drc->id & DRC_INDEX_ID_MASK); } -static int set_isolation_state(sPAPRDRConnector *drc, - sPAPRDRIsolationState state) +static uint32_t set_isolation_state(sPAPRDRConnector *drc, + sPAPRDRIsolationState state) { sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); DPRINTFN("drc: %x, set_isolation_state: %x", get_index(drc), state); + if (state == SPAPR_DR_ISOLATION_STATE_UNISOLATED) { + /* cannot unisolate a non-existant resource, and, or resources + * which are in an 'UNUSABLE' allocation state. (PAPR 2.7, 13.5.3.5) + */ + if (!drc->dev || + drc->allocation_state == SPAPR_DR_ALLOCATION_STATE_UNUSABLE) { + return RTAS_OUT_NO_SUCH_INDICATOR; + } + } + drc->isolation_state = state; if (drc->isolation_state == SPAPR_DR_ISOLATION_STATE_ISOLATED) { @@ -89,24 +104,35 @@ static int set_isolation_state(sPAPRDRConnector *drc, drc->configured = false; } - return 0; + return RTAS_OUT_SUCCESS; } -static int set_indicator_state(sPAPRDRConnector *drc, - sPAPRDRIndicatorState state) +static uint32_t set_indicator_state(sPAPRDRConnector *drc, + sPAPRDRIndicatorState state) { DPRINTFN("drc: %x, set_indicator_state: %x", get_index(drc), state); drc->indicator_state = state; - return 0; + return RTAS_OUT_SUCCESS; } -static int set_allocation_state(sPAPRDRConnector *drc, - sPAPRDRAllocationState state) +static uint32_t set_allocation_state(sPAPRDRConnector *drc, + sPAPRDRAllocationState state) { sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); DPRINTFN("drc: %x, set_allocation_state: %x", get_index(drc), state); + if (state == SPAPR_DR_ALLOCATION_STATE_USABLE) { + /* if there's no resource/device associated with the DRC, there's + * no way for us to put it in an allocation state consistent with + * being 'USABLE'. PAPR 2.7, 13.5.3.4 documents that this should + * result in an RTAS return code of -3 / "no such indicator" + */ + if (!drc->dev) { + return RTAS_OUT_NO_SUCH_INDICATOR; + } + } + if (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI) { drc->allocation_state = state; if (drc->awaiting_release && @@ -116,7 +142,7 @@ static int set_allocation_state(sPAPRDRConnector *drc, drc->detach_cb_opaque, NULL); } } - return 0; + return RTAS_OUT_SUCCESS; } static uint32_t get_type(sPAPRDRConnector *drc) @@ -150,6 +176,12 @@ static void set_configured(sPAPRDRConnector *drc) drc->configured = true; } +/* has the guest been notified of device attachment? */ +static void set_signalled(sPAPRDRConnector *drc) +{ + drc->signalled = true; +} + /* * dr-entity-sense sensor value * returned via get-sensor-state RTAS calls @@ -157,10 +189,8 @@ static void set_configured(sPAPRDRConnector *drc) * based on the current allocation/indicator/power states * for the DR connector. */ -static sPAPRDREntitySense entity_sense(sPAPRDRConnector *drc) +static uint32_t entity_sense(sPAPRDRConnector *drc, sPAPRDREntitySense *state) { - sPAPRDREntitySense state; - if (drc->dev) { if (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI && drc->allocation_state == SPAPR_DR_ALLOCATION_STATE_UNUSABLE) { @@ -169,7 +199,7 @@ static sPAPRDREntitySense entity_sense(sPAPRDRConnector *drc) * Otherwise, report the state as USABLE/PRESENT, * as we would for PCI. */ - state = SPAPR_DR_ENTITY_SENSE_UNUSABLE; + *state = SPAPR_DR_ENTITY_SENSE_UNUSABLE; } else { /* this assumes all PCI devices are assigned to * a 'live insertion' power domain, where QEMU @@ -177,39 +207,39 @@ static sPAPRDREntitySense entity_sense(sPAPRDRConnector *drc) * to the guest. present, non-PCI resources are * unaffected by power state. */ - state = SPAPR_DR_ENTITY_SENSE_PRESENT; + *state = SPAPR_DR_ENTITY_SENSE_PRESENT; } } else { if (drc->type == SPAPR_DR_CONNECTOR_TYPE_PCI) { /* PCI devices, and only PCI devices, use EMPTY * in cases where we'd otherwise use UNUSABLE */ - state = SPAPR_DR_ENTITY_SENSE_EMPTY; + *state = SPAPR_DR_ENTITY_SENSE_EMPTY; } else { - state = SPAPR_DR_ENTITY_SENSE_UNUSABLE; + *state = SPAPR_DR_ENTITY_SENSE_UNUSABLE; } } DPRINTFN("drc: %x, entity_sense: %x", get_index(drc), state); - return state; + return RTAS_OUT_SUCCESS; } -static void prop_get_index(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void prop_get_index(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj); sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); uint32_t value = (uint32_t)drck->get_index(drc); - visit_type_uint32(v, &value, name, errp); + visit_type_uint32(v, name, &value, errp); } -static void prop_get_type(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void prop_get_type(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj); sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); uint32_t value = (uint32_t)drck->get_type(drc); - visit_type_uint32(v, &value, name, errp); + visit_type_uint32(v, name, &value, errp); } static char *prop_get_name(Object *obj, Error **errp) @@ -219,23 +249,31 @@ static char *prop_get_name(Object *obj, Error **errp) return g_strdup(drck->get_name(drc)); } -static void prop_get_entity_sense(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void prop_get_entity_sense(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj); sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - uint32_t value = (uint32_t)drck->entity_sense(drc); - visit_type_uint32(v, &value, name, errp); + uint32_t value; + + drck->entity_sense(drc, &value); + visit_type_uint32(v, name, &value, errp); } -static void prop_get_fdt(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) +static void prop_get_fdt(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) { sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj); + Error *err = NULL; int fdt_offset_next, fdt_offset, fdt_depth; void *fdt; if (!drc->fdt) { + visit_start_struct(v, name, NULL, 0, &err); + if (!err) { + visit_end_struct(v, &err); + } + error_propagate(errp, err); return; } @@ -254,24 +292,39 @@ static void prop_get_fdt(Object *obj, Visitor *v, void *opaque, case FDT_BEGIN_NODE: fdt_depth++; name = fdt_get_name(fdt, fdt_offset, &name_len); - visit_start_struct(v, NULL, NULL, name, 0, NULL); + visit_start_struct(v, name, NULL, 0, &err); + if (err) { + error_propagate(errp, err); + return; + } break; case FDT_END_NODE: /* shouldn't ever see an FDT_END_NODE before FDT_BEGIN_NODE */ g_assert(fdt_depth > 0); - visit_end_struct(v, NULL); + visit_end_struct(v, &err); + if (err) { + error_propagate(errp, err); + return; + } fdt_depth--; break; case FDT_PROP: { int i; prop = fdt_get_property_by_offset(fdt, fdt_offset, &prop_len); name = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); - visit_start_list(v, name, NULL); + visit_start_list(v, name, &err); + if (err) { + error_propagate(errp, err); + return; + } for (i = 0; i < prop_len; i++) { - visit_type_uint8(v, (uint8_t *)&prop->data[i], NULL, NULL); - + visit_type_uint8(v, NULL, (uint8_t *)&prop->data[i], &err); + if (err) { + error_propagate(errp, err); + return; + } } - visit_end_list(v, NULL); + visit_end_list(v); break; } default: @@ -310,7 +363,18 @@ static void attach(sPAPRDRConnector *drc, DeviceState *d, void *fdt, drc->dev = d; drc->fdt = fdt; drc->fdt_start_offset = fdt_start_offset; - drc->configured = false; + drc->configured = coldplug; + /* 'logical' DR resources such as memory/cpus are in some cases treated + * as a pool of resources from which the guest is free to choose from + * based on only a count. for resources that can be assigned in this + * fashion, we must assume the resource is signalled immediately + * since a single hotplug request might make an arbitrary number of + * such attached resources available to the guest, as opposed to + * 'physical' DR resources such as PCI where each device/resource is + * signalled individually. + */ + drc->signalled = (drc->type != SPAPR_DR_CONNECTOR_TYPE_PCI) + ? true : coldplug; object_property_add_link(OBJECT(drc), "device", object_get_typename(OBJECT(drc->dev)), @@ -327,6 +391,26 @@ static void detach(sPAPRDRConnector *drc, DeviceState *d, drc->detach_cb = detach_cb; drc->detach_cb_opaque = detach_cb_opaque; + /* if we've signalled device presence to the guest, or if the guest + * has gone ahead and configured the device (via manually-executed + * device add via drmgr in guest, namely), we need to wait + * for the guest to quiesce the device before completing detach. + * Otherwise, we can assume the guest hasn't seen it and complete the + * detach immediately. Note that there is a small race window + * just before, or during, configuration, which is this context + * refers mainly to fetching the device tree via RTAS. + * During this window the device access will be arbitrated by + * associated DRC, which will simply fail the RTAS calls as invalid. + * This is recoverable within guest and current implementations of + * drmgr should be able to cope. + */ + if (!drc->signalled && !drc->configured) { + /* if the guest hasn't seen the device we can't rely on it to + * set it back to an isolated state via RTAS, so do it here manually + */ + drc->isolation_state = SPAPR_DR_ISOLATION_STATE_ISOLATED; + } + if (drc->isolation_state != SPAPR_DR_ISOLATION_STATE_ISOLATED) { DPRINTFN("awaiting transition to isolated state before removal"); drc->awaiting_release = true; @@ -365,6 +449,7 @@ static void reset(DeviceState *d) { sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(d); sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + sPAPRDREntitySense state; DPRINTFN("drc reset: %x", drck->get_index(drc)); /* immediately upon reset we can safely assume DRCs whose devices @@ -392,6 +477,11 @@ static void reset(DeviceState *d) drck->set_allocation_state(drc, SPAPR_DR_ALLOCATION_STATE_UNUSABLE); } } + + drck->entity_sense(drc, &state); + if (state == SPAPR_DR_ENTITY_SENSE_PRESENT) { + drck->set_signalled(drc); + } } static void realize(DeviceState *d, Error **errp) @@ -418,8 +508,7 @@ static void realize(DeviceState *d, Error **errp) object_property_add_alias(root_container, link_name, drc->owner, child_name, &err); if (err) { - error_report("%s", error_get_pretty(err)); - error_free(err); + error_report_err(err); object_unref(OBJECT(drc)); } g_free(child_name); @@ -439,8 +528,7 @@ static void unrealize(DeviceState *d, Error **errp) snprintf(name, sizeof(name), "%x", drck->get_index(drc)); object_property_del(root_container, name, &err); if (err) { - error_report("%s", error_get_pretty(err)); - error_free(err); + error_report_err(err); object_unref(OBJECT(drc)); } } @@ -451,14 +539,17 @@ sPAPRDRConnector *spapr_dr_connector_new(Object *owner, { sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(object_new(TYPE_SPAPR_DR_CONNECTOR)); + char *prop_name; g_assert(type); drc->type = type; drc->id = id; drc->owner = owner; - object_property_add_child(owner, "dr-connector[*]", OBJECT(drc), NULL); + prop_name = g_strdup_printf("dr-connector[%"PRIu32"]", get_index(drc)); + object_property_add_child(owner, prop_name, OBJECT(drc), NULL); object_property_set_bool(OBJECT(drc), true, "realized", NULL); + g_free(prop_name); /* human-readable name for a DRC to encode into the DT * description. this is mainly only used within a guest in place @@ -549,6 +640,11 @@ static void spapr_dr_connector_class_init(ObjectClass *k, void *data) drck->attach = attach; drck->detach = detach; drck->release_pending = release_pending; + drck->set_signalled = set_signalled; + /* + * Reason: it crashes FIXME find and document the real reason + */ + dk->cannot_instantiate_with_device_add_yet = true; } static const TypeInfo spapr_dr_connector_info = { @@ -632,6 +728,7 @@ int spapr_drc_populate_dt(void *fdt, int fdt_offset, Object *owner, { Object *root_container; ObjectProperty *prop; + ObjectPropertyIterator iter; uint32_t drc_count = 0; GArray *drc_indexes, *drc_power_domains; GString *drc_names, *drc_types; @@ -655,7 +752,8 @@ int spapr_drc_populate_dt(void *fdt, int fdt_offset, Object *owner, */ root_container = container_get(object_get_root(), DRC_CONTAINER_PATH); - QTAILQ_FOREACH(prop, &root_container->properties, node) { + object_property_iter_init(&iter, root_container); + while ((prop = object_property_iter_next(&iter))) { Object *obj; sPAPRDRConnector *drc; sPAPRDRConnectorClass *drck; diff --git a/qemu/hw/ppc/spapr_events.c b/qemu/hw/ppc/spapr_events.c index f626eb7b3..049fb1b32 100644 --- a/qemu/hw/ppc/spapr_events.c +++ b/qemu/hw/ppc/spapr_events.c @@ -24,6 +24,8 @@ * THE SOFTWARE. * */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "cpu.h" #include "sysemu/sysemu.h" #include "sysemu/char.h" @@ -35,7 +37,8 @@ #include "hw/pci/pci.h" #include "hw/pci-host/spapr.h" #include "hw/ppc/spapr_drc.h" - +#include "qemu/help_option.h" +#include "qemu/bcd.h" #include <libfdt.h> struct rtas_error_log { @@ -386,7 +389,16 @@ static void spapr_powerdown_req(Notifier *n, void *opaque) qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->check_exception_irq)); } -static void spapr_hotplug_req_event(sPAPRDRConnector *drc, uint8_t hp_action) +static void spapr_hotplug_set_signalled(uint32_t drc_index) +{ + sPAPRDRConnector *drc = spapr_dr_connector_by_index(drc_index); + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + drck->set_signalled(drc); +} + +static void spapr_hotplug_req_event(uint8_t hp_id, uint8_t hp_action, + sPAPRDRConnectorType drc_type, + uint32_t drc) { sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); struct hp_log_full *new_hp; @@ -395,8 +407,6 @@ static void spapr_hotplug_req_event(sPAPRDRConnector *drc, uint8_t hp_action) struct rtas_event_log_v6_maina *maina; struct rtas_event_log_v6_mainb *mainb; struct rtas_event_log_v6_hp *hp; - sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - sPAPRDRConnectorType drc_type = drck->get_type(drc); new_hp = g_malloc0(sizeof(struct hp_log_full)); hdr = &new_hp->hdr; @@ -427,13 +437,17 @@ static void spapr_hotplug_req_event(sPAPRDRConnector *drc, uint8_t hp_action) hp->hdr.section_length = cpu_to_be16(sizeof(*hp)); hp->hdr.section_version = 1; /* includes extended modifier */ hp->hotplug_action = hp_action; - + hp->hotplug_identifier = hp_id; switch (drc_type) { case SPAPR_DR_CONNECTOR_TYPE_PCI: - hp->drc.index = cpu_to_be32(drck->get_index(drc)); - hp->hotplug_identifier = RTAS_LOG_V6_HP_ID_DRC_INDEX; hp->hotplug_type = RTAS_LOG_V6_HP_TYPE_PCI; + if (hp->hotplug_action == RTAS_LOG_V6_HP_ACTION_ADD) { + spapr_hotplug_set_signalled(drc); + } + break; + case SPAPR_DR_CONNECTOR_TYPE_LMB: + hp->hotplug_type = RTAS_LOG_V6_HP_TYPE_MEMORY; break; default: /* we shouldn't be signaling hotplug events for resources @@ -443,19 +457,49 @@ static void spapr_hotplug_req_event(sPAPRDRConnector *drc, uint8_t hp_action) return; } + if (hp_id == RTAS_LOG_V6_HP_ID_DRC_COUNT) { + hp->drc.count = cpu_to_be32(drc); + } else if (hp_id == RTAS_LOG_V6_HP_ID_DRC_INDEX) { + hp->drc.index = cpu_to_be32(drc); + } + rtas_event_log_queue(RTAS_LOG_TYPE_HOTPLUG, new_hp, true); qemu_irq_pulse(xics_get_qirq(spapr->icp, spapr->check_exception_irq)); } -void spapr_hotplug_req_add_event(sPAPRDRConnector *drc) +void spapr_hotplug_req_add_by_index(sPAPRDRConnector *drc) +{ + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + sPAPRDRConnectorType drc_type = drck->get_type(drc); + uint32_t index = drck->get_index(drc); + + spapr_hotplug_req_event(RTAS_LOG_V6_HP_ID_DRC_INDEX, + RTAS_LOG_V6_HP_ACTION_ADD, drc_type, index); +} + +void spapr_hotplug_req_remove_by_index(sPAPRDRConnector *drc) +{ + sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + sPAPRDRConnectorType drc_type = drck->get_type(drc); + uint32_t index = drck->get_index(drc); + + spapr_hotplug_req_event(RTAS_LOG_V6_HP_ID_DRC_INDEX, + RTAS_LOG_V6_HP_ACTION_REMOVE, drc_type, index); +} + +void spapr_hotplug_req_add_by_count(sPAPRDRConnectorType drc_type, + uint32_t count) { - spapr_hotplug_req_event(drc, RTAS_LOG_V6_HP_ACTION_ADD); + spapr_hotplug_req_event(RTAS_LOG_V6_HP_ID_DRC_COUNT, + RTAS_LOG_V6_HP_ACTION_ADD, drc_type, count); } -void spapr_hotplug_req_remove_event(sPAPRDRConnector *drc) +void spapr_hotplug_req_remove_by_count(sPAPRDRConnectorType drc_type, + uint32_t count) { - spapr_hotplug_req_event(drc, RTAS_LOG_V6_HP_ACTION_REMOVE); + spapr_hotplug_req_event(RTAS_LOG_V6_HP_ID_DRC_COUNT, + RTAS_LOG_V6_HP_ACTION_REMOVE, drc_type, count); } static void check_exception(PowerPCCPU *cpu, sPAPRMachineState *spapr, @@ -556,7 +600,8 @@ out_no_events: void spapr_events_init(sPAPRMachineState *spapr) { QTAILQ_INIT(&spapr->pending_events); - spapr->check_exception_irq = xics_alloc(spapr->icp, 0, 0, false); + spapr->check_exception_irq = xics_alloc(spapr->icp, 0, 0, false, + &error_fatal); spapr->epow_notifier.notify = spapr_powerdown_req; qemu_register_powerdown_notifier(&spapr->epow_notifier); spapr_rtas_register(RTAS_CHECK_EXCEPTION, "check-exception", diff --git a/qemu/hw/ppc/spapr_hcall.c b/qemu/hw/ppc/spapr_hcall.c index 652ddf6e3..8f40602a5 100644 --- a/qemu/hw/ppc/spapr_hcall.c +++ b/qemu/hw/ppc/spapr_hcall.c @@ -1,3 +1,5 @@ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "sysemu/sysemu.h" #include "cpu.h" #include "helper_regs.h" @@ -37,40 +39,10 @@ static void set_spr(CPUState *cs, int spr, target_ulong value, run_on_cpu(cs, do_spr_sync, &s); } -static target_ulong compute_tlbie_rb(target_ulong v, target_ulong r, - target_ulong pte_index) +static bool has_spr(PowerPCCPU *cpu, int spr) { - target_ulong rb, va_low; - - rb = (v & ~0x7fULL) << 16; /* AVA field */ - va_low = pte_index >> 3; - if (v & HPTE64_V_SECONDARY) { - va_low = ~va_low; - } - /* xor vsid from AVA */ - if (!(v & HPTE64_V_1TB_SEG)) { - va_low ^= v >> 12; - } else { - va_low ^= v >> 24; - } - va_low &= 0x7ff; - if (v & HPTE64_V_LARGE) { - rb |= 1; /* L field */ -#if 0 /* Disable that P7 specific bit for now */ - if (r & 0xff000) { - /* non-16MB large page, must be 64k */ - /* (masks depend on page size) */ - rb |= 0x1000; /* page encoding in LP field */ - rb |= (va_low & 0x7f) << 16; /* 7b of VA in AVA/LP field */ - rb |= (va_low & 0xfe); /* AVAL field */ - } -#endif - } else { - /* 4kB page */ - rb |= (va_low & 0x7ff) << 12; /* remaining 11b of AVA */ - } - rb |= (v >> 54) & 0x300; /* B field */ - return rb; + /* We can test whether the SPR is defined by checking for a valid name */ + return cpu->env.spr_cb[spr].name != NULL; } static inline bool valid_pte_index(CPUPPCState *env, target_ulong pte_index) @@ -84,42 +56,44 @@ static inline bool valid_pte_index(CPUPPCState *env, target_ulong pte_index) return true; } +static bool is_ram_address(sPAPRMachineState *spapr, hwaddr addr) +{ + MachineState *machine = MACHINE(spapr); + MemoryHotplugState *hpms = &spapr->hotplug_memory; + + if (addr < machine->ram_size) { + return true; + } + if ((addr >= hpms->base) + && ((addr - hpms->base) < memory_region_size(&hpms->mr))) { + return true; + } + + return false; +} + static target_ulong h_enter(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { - MachineState *machine = MACHINE(spapr); CPUPPCState *env = &cpu->env; target_ulong flags = args[0]; target_ulong pte_index = args[1]; target_ulong pteh = args[2]; target_ulong ptel = args[3]; - target_ulong page_shift = 12; + unsigned apshift, spshift; target_ulong raddr; target_ulong index; uint64_t token; - /* only handle 4k and 16M pages for now */ - if (pteh & HPTE64_V_LARGE) { -#if 0 /* We don't support 64k pages yet */ - if ((ptel & 0xf000) == 0x1000) { - /* 64k page */ - } else -#endif - if ((ptel & 0xff000) == 0) { - /* 16M page */ - page_shift = 24; - /* lowest AVA bit must be 0 for 16M pages */ - if (pteh & 0x80) { - return H_PARAMETER; - } - } else { - return H_PARAMETER; - } + apshift = ppc_hash64_hpte_page_shift_noslb(cpu, pteh, ptel, &spshift); + if (!apshift) { + /* Bad page size encoding */ + return H_PARAMETER; } - raddr = (ptel & HPTE64_R_RPN) & ~((1ULL << page_shift) - 1); + raddr = (ptel & HPTE64_R_RPN) & ~((1ULL << apshift) - 1); - if (raddr < machine->ram_size) { + if (is_ram_address(spapr, raddr)) { /* Regular RAM - should have WIMG=0010 */ if ((ptel & HPTE64_R_WIMG) != HPTE64_R_M) { return H_PARAMETER; @@ -145,24 +119,24 @@ static target_ulong h_enter(PowerPCCPU *cpu, sPAPRMachineState *spapr, pte_index &= ~7ULL; token = ppc_hash64_start_access(cpu, pte_index); for (; index < 8; index++) { - if ((ppc_hash64_load_hpte0(env, token, index) & HPTE64_V_VALID) == 0) { + if (!(ppc_hash64_load_hpte0(cpu, token, index) & HPTE64_V_VALID)) { break; } } - ppc_hash64_stop_access(token); + ppc_hash64_stop_access(cpu, token); if (index == 8) { return H_PTEG_FULL; } } else { token = ppc_hash64_start_access(cpu, pte_index); - if (ppc_hash64_load_hpte0(env, token, 0) & HPTE64_V_VALID) { - ppc_hash64_stop_access(token); + if (ppc_hash64_load_hpte0(cpu, token, 0) & HPTE64_V_VALID) { + ppc_hash64_stop_access(cpu, token); return H_PTEG_FULL; } - ppc_hash64_stop_access(token); + ppc_hash64_stop_access(cpu, token); } - ppc_hash64_store_hpte(env, pte_index + index, + ppc_hash64_store_hpte(cpu, pte_index + index, pteh | HPTE64_V_HPTE_DIRTY, ptel); args[0] = pte_index + index; @@ -176,22 +150,23 @@ typedef enum { REMOVE_HW = 3, } RemoveResult; -static RemoveResult remove_hpte(CPUPPCState *env, target_ulong ptex, +static RemoveResult remove_hpte(PowerPCCPU *cpu, target_ulong ptex, target_ulong avpn, target_ulong flags, target_ulong *vp, target_ulong *rp) { + CPUPPCState *env = &cpu->env; uint64_t token; - target_ulong v, r, rb; + target_ulong v, r; if (!valid_pte_index(env, ptex)) { return REMOVE_PARM; } - token = ppc_hash64_start_access(ppc_env_get_cpu(env), ptex); - v = ppc_hash64_load_hpte0(env, token, 0); - r = ppc_hash64_load_hpte1(env, token, 0); - ppc_hash64_stop_access(token); + token = ppc_hash64_start_access(cpu, ptex); + v = ppc_hash64_load_hpte0(cpu, token, 0); + r = ppc_hash64_load_hpte1(cpu, token, 0); + ppc_hash64_stop_access(cpu, token); if ((v & HPTE64_V_VALID) == 0 || ((flags & H_AVPN) && (v & ~0x7fULL) != avpn) || @@ -200,22 +175,20 @@ static RemoveResult remove_hpte(CPUPPCState *env, target_ulong ptex, } *vp = v; *rp = r; - ppc_hash64_store_hpte(env, ptex, HPTE64_V_HPTE_DIRTY, 0); - rb = compute_tlbie_rb(v, r, ptex); - ppc_tlb_invalidate_one(env, rb); + ppc_hash64_store_hpte(cpu, ptex, HPTE64_V_HPTE_DIRTY, 0); + ppc_hash64_tlb_flush_hpte(cpu, ptex, v, r); return REMOVE_SUCCESS; } static target_ulong h_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { - CPUPPCState *env = &cpu->env; target_ulong flags = args[0]; target_ulong pte_index = args[1]; target_ulong avpn = args[2]; RemoveResult ret; - ret = remove_hpte(env, pte_index, avpn, flags, + ret = remove_hpte(cpu, pte_index, avpn, flags, &args[0], &args[1]); switch (ret) { @@ -256,7 +229,6 @@ static target_ulong h_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr, static target_ulong h_bulk_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { - CPUPPCState *env = &cpu->env; int i; for (i = 0; i < H_BULK_REMOVE_MAX_BATCH; i++) { @@ -278,7 +250,7 @@ static target_ulong h_bulk_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr, return H_PARAMETER; } - ret = remove_hpte(env, *tsh & H_BULK_REMOVE_PTEX, tsl, + ret = remove_hpte(cpu, *tsh & H_BULK_REMOVE_PTEX, tsl, (*tsh & H_BULK_REMOVE_FLAGS) >> 26, &v, &r); @@ -308,16 +280,16 @@ static target_ulong h_protect(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong pte_index = args[1]; target_ulong avpn = args[2]; uint64_t token; - target_ulong v, r, rb; + target_ulong v, r; if (!valid_pte_index(env, pte_index)) { return H_PARAMETER; } token = ppc_hash64_start_access(cpu, pte_index); - v = ppc_hash64_load_hpte0(env, token, 0); - r = ppc_hash64_load_hpte1(env, token, 0); - ppc_hash64_stop_access(token); + v = ppc_hash64_load_hpte0(cpu, token, 0); + r = ppc_hash64_load_hpte1(cpu, token, 0); + ppc_hash64_stop_access(cpu, token); if ((v & HPTE64_V_VALID) == 0 || ((flags & H_AVPN) && (v & ~0x7fULL) != avpn)) { @@ -329,12 +301,11 @@ static target_ulong h_protect(PowerPCCPU *cpu, sPAPRMachineState *spapr, r |= (flags << 55) & HPTE64_R_PP0; r |= (flags << 48) & HPTE64_R_KEY_HI; r |= flags & (HPTE64_R_PP | HPTE64_R_N | HPTE64_R_KEY_LO); - rb = compute_tlbie_rb(v, r, pte_index); - ppc_hash64_store_hpte(env, pte_index, + ppc_hash64_store_hpte(cpu, pte_index, (v & ~HPTE64_V_VALID) | HPTE64_V_HPTE_DIRTY, 0); - ppc_tlb_invalidate_one(env, rb); + ppc_hash64_tlb_flush_hpte(cpu, pte_index, v, r); /* Don't need a memory barrier, due to qemu's global lock */ - ppc_hash64_store_hpte(env, pte_index, v | HPTE64_V_HPTE_DIRTY, r); + ppc_hash64_store_hpte(cpu, pte_index, v | HPTE64_V_HPTE_DIRTY, r); return H_SUCCESS; } @@ -368,11 +339,111 @@ static target_ulong h_read(PowerPCCPU *cpu, sPAPRMachineState *spapr, return H_SUCCESS; } +static target_ulong h_set_sprg0(PowerPCCPU *cpu, sPAPRMachineState *spapr, + target_ulong opcode, target_ulong *args) +{ + cpu_synchronize_state(CPU(cpu)); + cpu->env.spr[SPR_SPRG0] = args[0]; + + return H_SUCCESS; +} + static target_ulong h_set_dabr(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { - /* FIXME: actually implement this */ - return H_HARDWARE; + if (!has_spr(cpu, SPR_DABR)) { + return H_HARDWARE; /* DABR register not available */ + } + cpu_synchronize_state(CPU(cpu)); + + if (has_spr(cpu, SPR_DABRX)) { + cpu->env.spr[SPR_DABRX] = 0x3; /* Use Problem and Privileged state */ + } else if (!(args[0] & 0x4)) { /* Breakpoint Translation set? */ + return H_RESERVED_DABR; + } + + cpu->env.spr[SPR_DABR] = args[0]; + return H_SUCCESS; +} + +static target_ulong h_set_xdabr(PowerPCCPU *cpu, sPAPRMachineState *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong dabrx = args[1]; + + if (!has_spr(cpu, SPR_DABR) || !has_spr(cpu, SPR_DABRX)) { + return H_HARDWARE; + } + + if ((dabrx & ~0xfULL) != 0 || (dabrx & H_DABRX_HYPERVISOR) != 0 + || (dabrx & (H_DABRX_KERNEL | H_DABRX_USER)) == 0) { + return H_PARAMETER; + } + + cpu_synchronize_state(CPU(cpu)); + cpu->env.spr[SPR_DABRX] = dabrx; + cpu->env.spr[SPR_DABR] = args[0]; + + return H_SUCCESS; +} + +static target_ulong h_page_init(PowerPCCPU *cpu, sPAPRMachineState *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong flags = args[0]; + hwaddr dst = args[1]; + hwaddr src = args[2]; + hwaddr len = TARGET_PAGE_SIZE; + uint8_t *pdst, *psrc; + target_long ret = H_SUCCESS; + + if (flags & ~(H_ICACHE_SYNCHRONIZE | H_ICACHE_INVALIDATE + | H_COPY_PAGE | H_ZERO_PAGE)) { + qemu_log_mask(LOG_UNIMP, "h_page_init: Bad flags (" TARGET_FMT_lx "\n", + flags); + return H_PARAMETER; + } + + /* Map-in destination */ + if (!is_ram_address(spapr, dst) || (dst & ~TARGET_PAGE_MASK) != 0) { + return H_PARAMETER; + } + pdst = cpu_physical_memory_map(dst, &len, 1); + if (!pdst || len != TARGET_PAGE_SIZE) { + return H_PARAMETER; + } + + if (flags & H_COPY_PAGE) { + /* Map-in source, copy to destination, and unmap source again */ + if (!is_ram_address(spapr, src) || (src & ~TARGET_PAGE_MASK) != 0) { + ret = H_PARAMETER; + goto unmap_out; + } + psrc = cpu_physical_memory_map(src, &len, 0); + if (!psrc || len != TARGET_PAGE_SIZE) { + ret = H_PARAMETER; + goto unmap_out; + } + memcpy(pdst, psrc, len); + cpu_physical_memory_unmap(psrc, len, 0, len); + } else if (flags & H_ZERO_PAGE) { + memset(pdst, 0, len); /* Just clear the destination page */ + } + + if (kvm_enabled() && (flags & H_ICACHE_SYNCHRONIZE) != 0) { + kvmppc_dcbst_range(cpu, pdst, len); + } + if (flags & (H_ICACHE_SYNCHRONIZE | H_ICACHE_INVALIDATE)) { + if (kvm_enabled()) { + kvmppc_icbi_range(cpu, pdst, len); + } else { + tb_flush(CPU(cpu)); + } + } + +unmap_out: + cpu_physical_memory_unmap(pdst, TARGET_PAGE_SIZE, 1, len); + return ret; } #define FLAGS_REGISTER_VPA 0x0000200000000000ULL @@ -753,7 +824,6 @@ static target_ulong h_set_mode_resource_addr_trans_mode(PowerPCCPU *cpu, { CPUState *cs; PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); - target_ulong prefix; if (!(pcc->insns_flags2 & PPC2_ISA207S)) { return H_P2; @@ -765,25 +835,12 @@ static target_ulong h_set_mode_resource_addr_trans_mode(PowerPCCPU *cpu, return H_P4; } - switch (mflags) { - case H_SET_MODE_ADDR_TRANS_NONE: - prefix = 0; - break; - case H_SET_MODE_ADDR_TRANS_0001_8000: - prefix = 0x18000; - break; - case H_SET_MODE_ADDR_TRANS_C000_0000_0000_4000: - prefix = 0xC000000000004000ULL; - break; - default: + if (mflags == AIL_RESERVED) { return H_UNSUPPORTED_FLAG; } CPU_FOREACH(cs) { - CPUPPCState *env = &POWERPC_CPU(cpu)->env; - set_spr(cs, SPR_LPCR, mflags << LPCR_AIL_SHIFT, LPCR_AIL); - env->excp_prefix = prefix; } return H_SUCCESS; @@ -808,10 +865,36 @@ static target_ulong h_set_mode(PowerPCCPU *cpu, sPAPRMachineState *spapr, return ret; } +/* + * Return the offset to the requested option vector @vector in the + * option vector table @table. + */ +static target_ulong cas_get_option_vector(int vector, target_ulong table) +{ + int i; + char nr_vectors, nr_entries; + + if (!table) { + return 0; + } + + nr_vectors = (ldl_phys(&address_space_memory, table) >> 24) + 1; + if (!vector || vector > nr_vectors) { + return 0; + } + table++; /* skip nr option vectors */ + + for (i = 0; i < vector - 1; i++) { + nr_entries = ldl_phys(&address_space_memory, table) >> 24; + table += nr_entries + 2; + } + return table; +} + typedef struct { PowerPCCPU *cpu; uint32_t cpu_version; - int ret; + Error *err; } SetCompatState; static void do_set_compat(void *arg) @@ -819,7 +902,7 @@ static void do_set_compat(void *arg) SetCompatState *s = arg; cpu_synchronize_state(CPU(s->cpu)); - s->ret = ppc_set_compat(s->cpu, s->cpu_version); + ppc_set_compat(s->cpu, s->cpu_version, &s->err); } #define get_compat_level(cpuver) ( \ @@ -828,27 +911,31 @@ static void do_set_compat(void *arg) ((cpuver) == CPU_POWERPC_LOGICAL_2_06_PLUS) ? 2061 : \ ((cpuver) == CPU_POWERPC_LOGICAL_2_07) ? 2070 : 0) +#define OV5_DRCONF_MEMORY 0x20 + static target_ulong h_client_architecture_support(PowerPCCPU *cpu_, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { - target_ulong list = args[0]; + target_ulong list = ppc64_phys_to_real(args[0]); + target_ulong ov_table, ov5; PowerPCCPUClass *pcc_ = POWERPC_CPU_GET_CLASS(cpu_); CPUState *cs; - bool cpu_match = false; + bool cpu_match = false, cpu_update = true, memory_update = false; unsigned old_cpu_version = cpu_->cpu_version; unsigned compat_lvl = 0, cpu_version = 0; unsigned max_lvl = get_compat_level(cpu_->max_compat); int counter; + char ov5_byte2; /* Parse PVR list */ for (counter = 0; counter < 512; ++counter) { uint32_t pvr, pvr_mask; - pvr_mask = rtas_ld(list, 0); + pvr_mask = ldl_be_phys(&address_space_memory, list); list += 4; - pvr = rtas_ld(list, 0); + pvr = ldl_be_phys(&address_space_memory, list); list += 4; trace_spapr_cas_pvr_try(pvr); @@ -890,8 +977,6 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu_, } } - /* For the future use: here @list points to the first capability */ - /* Parsing finished */ trace_spapr_cas_pvr(cpu_->cpu_version, cpu_match, cpu_version, pcc_->pcr_mask); @@ -902,27 +987,38 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu_, SetCompatState s = { .cpu = POWERPC_CPU(cs), .cpu_version = cpu_version, - .ret = 0 + .err = NULL, }; run_on_cpu(cs, do_set_compat, &s); - if (s.ret < 0) { - fprintf(stderr, "Unable to set compatibility mode\n"); + if (s.err) { + error_report_err(s.err); return H_HARDWARE; } } } if (!cpu_version) { - return H_SUCCESS; + cpu_update = false; } - if (!list) { + /* For the future use: here @ov_table points to the first option vector */ + ov_table = list; + + ov5 = cas_get_option_vector(5, ov_table); + if (!ov5) { return H_SUCCESS; } - if (spapr_h_cas_compose_response(spapr, args[1], args[2])) { + /* @list now points to OV 5 */ + ov5_byte2 = ldub_phys(&address_space_memory, ov5 + 2); + if (ov5_byte2 & OV5_DRCONF_MEMORY) { + memory_update = true; + } + + if (spapr_h_cas_compose_response(spapr, args[1], args[2], + cpu_update, memory_update)) { qemu_system_reset_request(); } @@ -971,7 +1067,8 @@ target_ulong spapr_hypercall(PowerPCCPU *cpu, target_ulong opcode, } } - hcall_dprintf("Unimplemented hcall 0x" TARGET_FMT_lx "\n", opcode); + qemu_log_mask(LOG_UNIMP, "Unimplemented SPAPR hcall 0x" TARGET_FMT_lx "\n", + opcode); return H_FUNCTION; } @@ -986,13 +1083,17 @@ static void hypercall_register_types(void) /* hcall-bulk */ spapr_register_hypercall(H_BULK_REMOVE, h_bulk_remove); - /* hcall-dabr */ - spapr_register_hypercall(H_SET_DABR, h_set_dabr); - /* hcall-splpar */ spapr_register_hypercall(H_REGISTER_VPA, h_register_vpa); spapr_register_hypercall(H_CEDE, h_cede); + /* processor register resource access h-calls */ + spapr_register_hypercall(H_SET_SPRG0, h_set_sprg0); + spapr_register_hypercall(H_SET_DABR, h_set_dabr); + spapr_register_hypercall(H_SET_XDABR, h_set_xdabr); + spapr_register_hypercall(H_PAGE_INIT, h_page_init); + spapr_register_hypercall(H_SET_MODE, h_set_mode); + /* "debugger" hcalls (also used by SLOF). Note: We do -not- differenciate * here between the "CI" and the "CACHE" variants, they will use whatever * mapping attributes qemu is using. When using KVM, the kernel will @@ -1009,8 +1110,6 @@ static void hypercall_register_types(void) /* qemu/KVM-PPC specific hcalls */ spapr_register_hypercall(KVMPPC_H_RTAS, h_rtas); - spapr_register_hypercall(H_SET_MODE, h_set_mode); - /* ibm,client-architecture-support support */ spapr_register_hypercall(KVMPPC_H_CAS, h_client_architecture_support); } diff --git a/qemu/hw/ppc/spapr_iommu.c b/qemu/hw/ppc/spapr_iommu.c index f61504e0c..7dd458846 100644 --- a/qemu/hw/ppc/spapr_iommu.c +++ b/qemu/hw/ppc/spapr_iommu.c @@ -16,6 +16,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see <http://www.gnu.org/licenses/>. */ +#include "qemu/osdep.h" #include "hw/hw.h" #include "sysemu/kvm.h" #include "hw/qdev.h" @@ -146,7 +147,7 @@ static int spapr_tce_table_realize(DeviceState *dev) tcet->table = kvmppc_create_spapr_tce(tcet->liobn, window_size, &tcet->fd, - tcet->vfio_accel); + tcet->need_vfio); } if (!tcet->table) { @@ -168,11 +169,43 @@ static int spapr_tce_table_realize(DeviceState *dev) return 0; } +void spapr_tce_set_need_vfio(sPAPRTCETable *tcet, bool need_vfio) +{ + size_t table_size = tcet->nb_table * sizeof(uint64_t); + void *newtable; + + if (need_vfio == tcet->need_vfio) { + /* Nothing to do */ + return; + } + + if (!need_vfio) { + /* FIXME: We don't support transition back to KVM accelerated + * TCEs yet */ + return; + } + + tcet->need_vfio = true; + + if (tcet->fd < 0) { + /* Table is already in userspace, nothing to be do */ + return; + } + + newtable = g_malloc(table_size); + memcpy(newtable, tcet->table, table_size); + + kvmppc_remove_spapr_tce(tcet->table, tcet->fd, tcet->nb_table); + + tcet->fd = -1; + tcet->table = newtable; +} + sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn, uint64_t bus_offset, uint32_t page_shift, uint32_t nb_table, - bool vfio_accel) + bool need_vfio) { sPAPRTCETable *tcet; char tmp[64]; @@ -192,7 +225,7 @@ sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn, tcet->bus_offset = bus_offset; tcet->page_shift = page_shift; tcet->nb_table = nb_table; - tcet->vfio_accel = vfio_accel; + tcet->need_vfio = need_vfio; snprintf(tmp, sizeof(tmp), "tce-table-%x", liobn); object_property_add_child(OBJECT(owner), tmp, OBJECT(tcet), NULL); diff --git a/qemu/hw/ppc/spapr_pci.c b/qemu/hw/ppc/spapr_pci.c index a8f79d800..573e635bf 100644 --- a/qemu/hw/ppc/spapr_pci.c +++ b/qemu/hw/ppc/spapr_pci.c @@ -22,6 +22,10 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/hw.h" #include "hw/sysbus.h" #include "hw/pci/pci.h" @@ -41,6 +45,8 @@ #include "hw/ppc/spapr_drc.h" #include "sysemu/device_tree.h" +#include "hw/vfio/vfio.h" + /* Copied from the kernel arch/powerpc/platforms/pseries/msi.c */ #define RTAS_QUERY_FN 0 #define RTAS_CHANGE_FN 1 @@ -140,7 +146,7 @@ static void rtas_ibm_read_pci_config(PowerPCCPU *cpu, sPAPRMachineState *spapr, return; } - buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + buid = rtas_ldq(args, 1); size = rtas_ld(args, 3); addr = rtas_ld(args, 0); @@ -206,7 +212,7 @@ static void rtas_ibm_write_pci_config(PowerPCCPU *cpu, sPAPRMachineState *spapr, return; } - buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + buid = rtas_ldq(args, 1); val = rtas_ld(args, 4); size = rtas_ld(args, 3); addr = rtas_ld(args, 0); @@ -269,16 +275,17 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong rets) { uint32_t config_addr = rtas_ld(args, 0); - uint64_t buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + uint64_t buid = rtas_ldq(args, 1); unsigned int func = rtas_ld(args, 3); unsigned int req_num = rtas_ld(args, 4); /* 0 == remove all */ unsigned int seq_num = rtas_ld(args, 5); unsigned int ret_intr_type; - unsigned int irq, max_irqs = 0, num = 0; + unsigned int irq, max_irqs = 0; sPAPRPHBState *phb = NULL; PCIDevice *pdev = NULL; spapr_pci_msi *msi; int *config_addr_key; + Error *err = NULL; switch (func) { case RTAS_CHANGE_MSI_FN: @@ -304,9 +311,10 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr, return; } + msi = (spapr_pci_msi *) g_hash_table_lookup(phb->msi, &config_addr); + /* Releasing MSIs */ if (!req_num) { - msi = (spapr_pci_msi *) g_hash_table_lookup(phb->msi, &config_addr); if (!msi) { trace_spapr_pci_msi("Releasing wrong config", config_addr); rtas_st(rets, 0, RTAS_OUT_HW_ERROR); @@ -315,10 +323,10 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr, xics_free(spapr->icp, msi->first_irq, msi->num); if (msi_present(pdev)) { - spapr_msi_setmsg(pdev, 0, false, 0, num); + spapr_msi_setmsg(pdev, 0, false, 0, 0); } if (msix_present(pdev)) { - spapr_msi_setmsg(pdev, 0, true, 0, num); + spapr_msi_setmsg(pdev, 0, true, 0, 0); } g_hash_table_remove(phb->msi, &config_addr); @@ -352,13 +360,20 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr, /* Allocate MSIs */ irq = xics_alloc_block(spapr->icp, 0, req_num, false, - ret_intr_type == RTAS_TYPE_MSI); - if (!irq) { - error_report("Cannot allocate MSIs for device %x", config_addr); + ret_intr_type == RTAS_TYPE_MSI, &err); + if (err) { + error_reportf_err(err, "Can't allocate MSIs for device %x: ", + config_addr); rtas_st(rets, 0, RTAS_OUT_HW_ERROR); return; } + /* Release previous MSIs */ + if (msi) { + xics_free(spapr->icp, msi->first_irq, msi->num); + g_hash_table_remove(phb->msi, &config_addr); + } + /* Setup MSI/MSIX vectors in the device (via cfgspace or MSIX BAR) */ spapr_msi_setmsg(pdev, SPAPR_PCI_MSI_WINDOW, ret_intr_type == RTAS_TYPE_MSIX, irq, req_num); @@ -375,7 +390,9 @@ out: rtas_st(rets, 0, RTAS_OUT_SUCCESS); rtas_st(rets, 1, req_num); rtas_st(rets, 2, ++seq_num); - rtas_st(rets, 3, ret_intr_type); + if (nret > 3) { + rtas_st(rets, 3, ret_intr_type); + } trace_spapr_pci_rtas_ibm_change_msi(config_addr, func, req_num, irq); } @@ -389,7 +406,7 @@ static void rtas_ibm_query_interrupt_source_number(PowerPCCPU *cpu, target_ulong rets) { uint32_t config_addr = rtas_ld(args, 0); - uint64_t buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + uint64_t buid = rtas_ldq(args, 1); unsigned int intr_src_num = -1, ioa_intr_num = rtas_ld(args, 3); sPAPRPHBState *phb = NULL; PCIDevice *pdev = NULL; @@ -428,8 +445,6 @@ static void rtas_ibm_set_eeh_option(PowerPCCPU *cpu, target_ulong rets) { sPAPRPHBState *sphb; - sPAPRPHBClass *spc; - PCIDevice *pdev; uint32_t addr, option; uint64_t buid; int ret; @@ -438,7 +453,7 @@ static void rtas_ibm_set_eeh_option(PowerPCCPU *cpu, goto param_error_exit; } - buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + buid = rtas_ldq(args, 1); addr = rtas_ld(args, 0); option = rtas_ld(args, 3); @@ -447,18 +462,11 @@ static void rtas_ibm_set_eeh_option(PowerPCCPU *cpu, goto param_error_exit; } - pdev = pci_find_device(PCI_HOST_BRIDGE(sphb)->bus, - (addr >> 16) & 0xFF, (addr >> 8) & 0xFF); - if (!pdev || !object_dynamic_cast(OBJECT(pdev), "vfio-pci")) { + if (!spapr_phb_eeh_available(sphb)) { goto param_error_exit; } - spc = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(sphb); - if (!spc->eeh_set_option) { - goto param_error_exit; - } - - ret = spc->eeh_set_option(sphb, addr, option); + ret = spapr_phb_vfio_eeh_set_option(sphb, addr, option); rtas_st(rets, 0, ret); return; @@ -473,7 +481,6 @@ static void rtas_ibm_get_config_addr_info2(PowerPCCPU *cpu, target_ulong rets) { sPAPRPHBState *sphb; - sPAPRPHBClass *spc; PCIDevice *pdev; uint32_t addr, option; uint64_t buid; @@ -482,14 +489,13 @@ static void rtas_ibm_get_config_addr_info2(PowerPCCPU *cpu, goto param_error_exit; } - buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + buid = rtas_ldq(args, 1); sphb = spapr_pci_find_phb(spapr, buid); if (!sphb) { goto param_error_exit; } - spc = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(sphb); - if (!spc->eeh_set_option) { + if (!spapr_phb_eeh_available(sphb)) { goto param_error_exit; } @@ -529,7 +535,6 @@ static void rtas_ibm_read_slot_reset_state2(PowerPCCPU *cpu, target_ulong rets) { sPAPRPHBState *sphb; - sPAPRPHBClass *spc; uint64_t buid; int state, ret; @@ -537,18 +542,17 @@ static void rtas_ibm_read_slot_reset_state2(PowerPCCPU *cpu, goto param_error_exit; } - buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + buid = rtas_ldq(args, 1); sphb = spapr_pci_find_phb(spapr, buid); if (!sphb) { goto param_error_exit; } - spc = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(sphb); - if (!spc->eeh_get_state) { + if (!spapr_phb_eeh_available(sphb)) { goto param_error_exit; } - ret = spc->eeh_get_state(sphb, &state); + ret = spapr_phb_vfio_eeh_get_state(sphb, &state); rtas_st(rets, 0, ret); if (ret != RTAS_OUT_SUCCESS) { return; @@ -573,7 +577,6 @@ static void rtas_ibm_set_slot_reset(PowerPCCPU *cpu, target_ulong rets) { sPAPRPHBState *sphb; - sPAPRPHBClass *spc; uint32_t option; uint64_t buid; int ret; @@ -582,19 +585,18 @@ static void rtas_ibm_set_slot_reset(PowerPCCPU *cpu, goto param_error_exit; } - buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + buid = rtas_ldq(args, 1); option = rtas_ld(args, 3); sphb = spapr_pci_find_phb(spapr, buid); if (!sphb) { goto param_error_exit; } - spc = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(sphb); - if (!spc->eeh_reset) { + if (!spapr_phb_eeh_available(sphb)) { goto param_error_exit; } - ret = spc->eeh_reset(sphb, option); + ret = spapr_phb_vfio_eeh_reset(sphb, option); rtas_st(rets, 0, ret); return; @@ -609,7 +611,6 @@ static void rtas_ibm_configure_pe(PowerPCCPU *cpu, target_ulong rets) { sPAPRPHBState *sphb; - sPAPRPHBClass *spc; uint64_t buid; int ret; @@ -617,18 +618,17 @@ static void rtas_ibm_configure_pe(PowerPCCPU *cpu, goto param_error_exit; } - buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + buid = rtas_ldq(args, 1); sphb = spapr_pci_find_phb(spapr, buid); if (!sphb) { goto param_error_exit; } - spc = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(sphb); - if (!spc->eeh_configure) { + if (!spapr_phb_eeh_available(sphb)) { goto param_error_exit; } - ret = spc->eeh_configure(sphb); + ret = spapr_phb_vfio_eeh_configure(sphb); rtas_st(rets, 0, ret); return; @@ -644,7 +644,6 @@ static void rtas_ibm_slot_error_detail(PowerPCCPU *cpu, target_ulong rets) { sPAPRPHBState *sphb; - sPAPRPHBClass *spc; int option; uint64_t buid; @@ -652,14 +651,13 @@ static void rtas_ibm_slot_error_detail(PowerPCCPU *cpu, goto param_error_exit; } - buid = ((uint64_t)rtas_ld(args, 1) << 32) | rtas_ld(args, 2); + buid = rtas_ldq(args, 1); sphb = spapr_pci_find_phb(spapr, buid); if (!sphb) { goto param_error_exit; } - spc = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(sphb); - if (!spc->eeh_set_option) { + if (!spapr_phb_eeh_available(sphb)) { goto param_error_exit; } @@ -955,6 +953,7 @@ static int spapr_populate_pci_child_dt(PCIDevice *dev, void *fdt, int offset, int pci_status, err; char *buf = NULL; uint32_t drc_index = spapr_phb_get_pci_drc_index(sphb, dev); + uint32_t max_msi, max_msix; if (pci_default_read_config(dev, PCI_HEADER_TYPE, 1) == PCI_HEADER_TYPE_BRIDGE) { @@ -1035,8 +1034,15 @@ static int spapr_populate_pci_child_dt(PCIDevice *dev, void *fdt, int offset, RESOURCE_CELLS_ADDRESS)); _FDT(fdt_setprop_cell(fdt, offset, "#size-cells", RESOURCE_CELLS_SIZE)); - _FDT(fdt_setprop_cell(fdt, offset, "ibm,req#msi-x", - RESOURCE_CELLS_SIZE)); + + max_msi = msi_nr_vectors_allocated(dev); + if (max_msi) { + _FDT(fdt_setprop_cell(fdt, offset, "ibm,req#msi", max_msi)); + } + max_msix = dev->msix_entries_nr; + if (max_msix) { + _FDT(fdt_setprop_cell(fdt, offset, "ibm,req#msi-x", max_msix)); + } populate_resource_props(dev, &rp); _FDT(fdt_setprop(fdt, offset, "reg", (uint8_t *)rp.reg, rp.reg_len)); @@ -1080,6 +1086,12 @@ static void spapr_phb_add_pci_device(sPAPRDRConnector *drc, void *fdt = NULL; int fdt_start_offset = 0, fdt_size; + if (object_dynamic_cast(OBJECT(pdev), "vfio-pci")) { + sPAPRTCETable *tcet = spapr_tce_find_by_liobn(phb->dma_liobn); + + spapr_tce_set_need_vfio(tcet, true); + } + if (dev->hotplugged) { fdt = create_device_tree(&fdt_size); fdt_start_offset = spapr_create_pci_child_dt(phb, pdev, fdt, 0); @@ -1123,14 +1135,21 @@ static void spapr_phb_remove_pci_device(sPAPRDRConnector *drc, drck->detach(drc, DEVICE(pdev), spapr_phb_remove_pci_device_cb, phb, errp); } -static sPAPRDRConnector *spapr_phb_get_pci_drc(sPAPRPHBState *phb, - PCIDevice *pdev) +static sPAPRDRConnector *spapr_phb_get_pci_func_drc(sPAPRPHBState *phb, + uint32_t busnr, + int32_t devfn) { - uint32_t busnr = pci_bus_num(PCI_BUS(qdev_get_parent_bus(DEVICE(pdev)))); return spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_PCI, (phb->index << 16) | (busnr << 8) | - pdev->devfn); + devfn); +} + +static sPAPRDRConnector *spapr_phb_get_pci_drc(sPAPRPHBState *phb, + PCIDevice *pdev) +{ + uint32_t busnr = pci_bus_num(PCI_BUS(qdev_get_parent_bus(DEVICE(pdev)))); + return spapr_phb_get_pci_func_drc(phb, busnr, pdev->devfn); } static uint32_t spapr_phb_get_pci_drc_index(sPAPRPHBState *phb, @@ -1154,6 +1173,8 @@ static void spapr_phb_hot_plug_child(HotplugHandler *plug_handler, PCIDevice *pdev = PCI_DEVICE(plugged_dev); sPAPRDRConnector *drc = spapr_phb_get_pci_drc(phb, pdev); Error *local_err = NULL; + PCIBus *bus = PCI_BUS(qdev_get_parent_bus(DEVICE(pdev))); + uint32_t slotnr = PCI_SLOT(pdev->devfn); /* if DR is disabled we don't need to do anything in the case of * hotplug or coldplug callbacks @@ -1171,13 +1192,44 @@ static void spapr_phb_hot_plug_child(HotplugHandler *plug_handler, g_assert(drc); + /* Following the QEMU convention used for PCIe multifunction + * hotplug, we do not allow functions to be hotplugged to a + * slot that already has function 0 present + */ + if (plugged_dev->hotplugged && bus->devices[PCI_DEVFN(slotnr, 0)] && + PCI_FUNC(pdev->devfn) != 0) { + error_setg(errp, "PCI: slot %d function 0 already ocuppied by %s," + " additional functions can no longer be exposed to guest.", + slotnr, bus->devices[PCI_DEVFN(slotnr, 0)]->name); + return; + } + spapr_phb_add_pci_device(drc, phb, pdev, &local_err); if (local_err) { error_propagate(errp, local_err); return; } - if (plugged_dev->hotplugged) { - spapr_hotplug_req_add_event(drc); + + /* If this is function 0, signal hotplug for all the device functions. + * Otherwise defer sending the hotplug event. + */ + if (plugged_dev->hotplugged && PCI_FUNC(pdev->devfn) == 0) { + int i; + + for (i = 0; i < 8; i++) { + sPAPRDRConnector *func_drc; + sPAPRDRConnectorClass *func_drck; + sPAPRDREntitySense state; + + func_drc = spapr_phb_get_pci_func_drc(phb, pci_bus_num(bus), + PCI_DEVFN(slotnr, i)); + func_drck = SPAPR_DR_CONNECTOR_GET_CLASS(func_drc); + func_drck->entity_sense(func_drc, &state); + + if (state == SPAPR_DR_ENTITY_SENSE_PRESENT) { + spapr_hotplug_req_add_by_index(func_drc); + } + } } } @@ -1200,12 +1252,51 @@ static void spapr_phb_hot_unplug_child(HotplugHandler *plug_handler, drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); if (!drck->release_pending(drc)) { + PCIBus *bus = PCI_BUS(qdev_get_parent_bus(DEVICE(pdev))); + uint32_t slotnr = PCI_SLOT(pdev->devfn); + sPAPRDRConnector *func_drc; + sPAPRDRConnectorClass *func_drck; + sPAPRDREntitySense state; + int i; + + /* ensure any other present functions are pending unplug */ + if (PCI_FUNC(pdev->devfn) == 0) { + for (i = 1; i < 8; i++) { + func_drc = spapr_phb_get_pci_func_drc(phb, pci_bus_num(bus), + PCI_DEVFN(slotnr, i)); + func_drck = SPAPR_DR_CONNECTOR_GET_CLASS(func_drc); + func_drck->entity_sense(func_drc, &state); + if (state == SPAPR_DR_ENTITY_SENSE_PRESENT + && !func_drck->release_pending(func_drc)) { + error_setg(errp, + "PCI: slot %d, function %d still present. " + "Must unplug all non-0 functions first.", + slotnr, i); + return; + } + } + } + spapr_phb_remove_pci_device(drc, phb, pdev, &local_err); if (local_err) { error_propagate(errp, local_err); return; } - spapr_hotplug_req_remove_event(drc); + + /* if this isn't func 0, defer unplug event. otherwise signal removal + * for all present functions + */ + if (PCI_FUNC(pdev->devfn) == 0) { + for (i = 7; i >= 0; i--) { + func_drc = spapr_phb_get_pci_func_drc(phb, pci_bus_num(bus), + PCI_DEVFN(slotnr, i)); + func_drck = SPAPR_DR_CONNECTOR_GET_CLASS(func_drc); + func_drck->entity_sense(func_drc, &state); + if (state == SPAPR_DR_ENTITY_SENSE_PRESENT) { + spapr_hotplug_req_remove_by_index(func_drc); + } + } + } } } @@ -1215,11 +1306,12 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) SysBusDevice *s = SYS_BUS_DEVICE(dev); sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(s); PCIHostState *phb = PCI_HOST_BRIDGE(s); - sPAPRPHBClass *info = SPAPR_PCI_HOST_BRIDGE_GET_CLASS(s); char *namebuf; int i; PCIBus *bus; uint64_t msi_window_size = 4096; + sPAPRTCETable *tcet; + uint32_t nb_table; if (sphb->index != (uint32_t)-1) { hwaddr windows_base; @@ -1350,10 +1442,12 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) /* Initialize the LSI table */ for (i = 0; i < PCI_NUM_PINS; i++) { uint32_t irq; + Error *local_err = NULL; - irq = xics_alloc_block(spapr->icp, 0, 1, true, false); - if (!irq) { - error_setg(errp, "spapr_allocate_lsi failed"); + irq = xics_alloc_block(spapr->icp, 0, 1, true, false, &local_err); + if (local_err) { + error_propagate(errp, local_err); + error_prepend(errp, "can't allocate LSIs: "); return; } @@ -1369,33 +1463,20 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) } } - if (!info->finish_realize) { - error_setg(errp, "finish_realize not defined"); - return; - } - - info->finish_realize(sphb, errp); - - sphb->msi = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, g_free); -} - -static void spapr_phb_finish_realize(sPAPRPHBState *sphb, Error **errp) -{ - sPAPRTCETable *tcet; - uint32_t nb_table; - - nb_table = SPAPR_PCI_DMA32_SIZE >> SPAPR_TCE_PAGE_SHIFT; + nb_table = sphb->dma_win_size >> SPAPR_TCE_PAGE_SHIFT; tcet = spapr_tce_new_table(DEVICE(sphb), sphb->dma_liobn, 0, SPAPR_TCE_PAGE_SHIFT, nb_table, false); if (!tcet) { error_setg(errp, "Unable to create TCE table for %s", sphb->dtbusname); - return ; + return; } /* Register default 32bit DMA window */ - memory_region_add_subregion(&sphb->iommu_root, 0, + memory_region_add_subregion(&sphb->iommu_root, sphb->dma_win_addr, spapr_tce_get_iommu(tcet)); + + sphb->msi = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, g_free); } static int spapr_phb_children_reset(Object *child, void *opaque) @@ -1413,6 +1494,10 @@ static void spapr_phb_reset(DeviceState *qdev) { /* Reset the IOMMU state */ object_child_foreach(OBJECT(qdev), spapr_phb_children_reset, NULL); + + if (spapr_phb_eeh_available(SPAPR_PCI_HOST_BRIDGE(qdev))) { + spapr_phb_vfio_reset(qdev); + } } static Property spapr_phb_properties[] = { @@ -1427,6 +1512,9 @@ static Property spapr_phb_properties[] = { SPAPR_PCI_IO_WIN_SIZE), DEFINE_PROP_BOOL("dynamic-reconfiguration", sPAPRPHBState, dr_enabled, true), + /* Default DMA window is 0..1GB */ + DEFINE_PROP_UINT64("dma_win_addr", sPAPRPHBState, dma_win_addr, 0), + DEFINE_PROP_UINT64("dma_win_size", sPAPRPHBState, dma_win_size, 0x40000000), DEFINE_PROP_END_OF_LIST(), }; @@ -1460,10 +1548,8 @@ static void spapr_pci_pre_save(void *opaque) gpointer key, value; int i; - if (sphb->msi_devs) { - g_free(sphb->msi_devs); - sphb->msi_devs = NULL; - } + g_free(sphb->msi_devs); + sphb->msi_devs = NULL; sphb->msi_devs_num = g_hash_table_size(sphb->msi); if (!sphb->msi_devs_num) { return; @@ -1490,10 +1576,8 @@ static int spapr_pci_post_load(void *opaque, int version_id) sizeof(sphb->msi_devs[i].value)); g_hash_table_insert(sphb->msi, key, value); } - if (sphb->msi_devs) { - g_free(sphb->msi_devs); - sphb->msi_devs = NULL; - } + g_free(sphb->msi_devs); + sphb->msi_devs = NULL; sphb->msi_devs_num = 0; return 0; @@ -1533,7 +1617,6 @@ static void spapr_phb_class_init(ObjectClass *klass, void *data) { PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - sPAPRPHBClass *spc = SPAPR_PCI_HOST_BRIDGE_CLASS(klass); HotplugHandlerClass *hp = HOTPLUG_HANDLER_CLASS(klass); hc->root_bus_path = spapr_phb_root_bus_path; @@ -1543,7 +1626,6 @@ static void spapr_phb_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_spapr_pci; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); dc->cannot_instantiate_with_device_add_yet = false; - spc->finish_realize = spapr_phb_finish_realize; hp->plug = spapr_phb_hot_plug_child; hp->unplug = spapr_phb_hot_unplug_child; } @@ -1553,7 +1635,6 @@ static const TypeInfo spapr_phb_info = { .parent = TYPE_PCI_HOST_BRIDGE, .instance_size = sizeof(sPAPRPHBState), .class_init = spapr_phb_class_init, - .class_size = sizeof(sPAPRPHBClass), .interfaces = (InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } @@ -1735,6 +1816,9 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb, sizeof(interrupt_map))); tcet = spapr_tce_find_by_liobn(SPAPR_PCI_LIOBN(phb->index, 0)); + if (!tcet) { + return -1; + } spapr_dma_dt(fdt, bus_off, "ibm,dma-window", tcet->liobn, tcet->bus_offset, tcet->nb_table << tcet->page_shift); @@ -1770,7 +1854,7 @@ void spapr_pci_rtas_init(void) rtas_ibm_read_pci_config); spapr_rtas_register(RTAS_IBM_WRITE_PCI_CONFIG, "ibm,write-pci-config", rtas_ibm_write_pci_config); - if (msi_supported) { + if (msi_nonbroken) { spapr_rtas_register(RTAS_IBM_QUERY_INTERRUPT_SOURCE_NUMBER, "ibm,query-interrupt-source-number", rtas_ibm_query_interrupt_source_number); diff --git a/qemu/hw/ppc/spapr_pci_vfio.c b/qemu/hw/ppc/spapr_pci_vfio.c index cca45ed31..cbd3d23c9 100644 --- a/qemu/hw/ppc/spapr_pci_vfio.c +++ b/qemu/hw/ppc/spapr_pci_vfio.c @@ -17,73 +17,51 @@ * along with this program; if not, see <http://www.gnu.org/licenses/>. */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" #include "hw/ppc/spapr.h" #include "hw/pci-host/spapr.h" #include "hw/pci/msix.h" #include "linux/vfio.h" #include "hw/vfio/vfio.h" +#include "qemu/error-report.h" -static Property spapr_phb_vfio_properties[] = { - DEFINE_PROP_INT32("iommu", sPAPRPHBVFIOState, iommugroupid, -1), - DEFINE_PROP_END_OF_LIST(), -}; +#define TYPE_SPAPR_PCI_VFIO_HOST_BRIDGE "spapr-pci-vfio-host-bridge" -static void spapr_phb_vfio_finish_realize(sPAPRPHBState *sphb, Error **errp) -{ - sPAPRPHBVFIOState *svphb = SPAPR_PCI_VFIO_HOST_BRIDGE(sphb); - struct vfio_iommu_spapr_tce_info info = { .argsz = sizeof(info) }; - int ret; - sPAPRTCETable *tcet; - uint32_t liobn = svphb->phb.dma_liobn; +#define SPAPR_PCI_VFIO_HOST_BRIDGE(obj) \ + OBJECT_CHECK(sPAPRPHBVFIOState, (obj), TYPE_SPAPR_PCI_VFIO_HOST_BRIDGE) - if (svphb->iommugroupid == -1) { - error_setg(errp, "Wrong IOMMU group ID %d", svphb->iommugroupid); - return; - } +typedef struct sPAPRPHBVFIOState sPAPRPHBVFIOState; - ret = vfio_container_ioctl(&svphb->phb.iommu_as, svphb->iommugroupid, - VFIO_CHECK_EXTENSION, - (void *) VFIO_SPAPR_TCE_IOMMU); - if (ret != 1) { - error_setg_errno(errp, -ret, - "spapr-vfio: SPAPR extension is not supported"); - return; - } +struct sPAPRPHBVFIOState { + sPAPRPHBState phb; - ret = vfio_container_ioctl(&svphb->phb.iommu_as, svphb->iommugroupid, - VFIO_IOMMU_SPAPR_TCE_GET_INFO, &info); - if (ret) { - error_setg_errno(errp, -ret, - "spapr-vfio: get info from container failed"); - return; - } + int32_t iommugroupid; +}; - tcet = spapr_tce_new_table(DEVICE(sphb), liobn, info.dma32_window_start, - SPAPR_TCE_PAGE_SHIFT, - info.dma32_window_size >> SPAPR_TCE_PAGE_SHIFT, - true); - if (!tcet) { - error_setg(errp, "spapr-vfio: failed to create VFIO TCE table"); - return; - } +static Property spapr_phb_vfio_properties[] = { + DEFINE_PROP_INT32("iommu", sPAPRPHBVFIOState, iommugroupid, -1), + DEFINE_PROP_END_OF_LIST(), +}; - /* Register default 32bit DMA window */ - memory_region_add_subregion(&sphb->iommu_root, tcet->bus_offset, - spapr_tce_get_iommu(tcet)); +static void spapr_phb_vfio_instance_init(Object *obj) +{ + error_report("spapr-pci-vfio-host-bridge is deprecated"); } -static void spapr_phb_vfio_eeh_reenable(sPAPRPHBVFIOState *svphb) +bool spapr_phb_eeh_available(sPAPRPHBState *sphb) { - struct vfio_eeh_pe_op op = { - .argsz = sizeof(op), - .op = VFIO_EEH_PE_ENABLE - }; + return vfio_eeh_as_ok(&sphb->iommu_as); +} - vfio_container_ioctl(&svphb->phb.iommu_as, - svphb->iommugroupid, VFIO_EEH_PE_OP, &op); +static void spapr_phb_vfio_eeh_reenable(sPAPRPHBState *sphb) +{ + vfio_eeh_as_op(&sphb->iommu_as, VFIO_EEH_PE_ENABLE); } -static void spapr_phb_vfio_reset(DeviceState *qdev) +void spapr_phb_vfio_reset(DeviceState *qdev) { /* * The PE might be in frozen state. To reenable the EEH @@ -91,19 +69,18 @@ static void spapr_phb_vfio_reset(DeviceState *qdev) * ensures that the contained PCI devices will work properly * after reboot. */ - spapr_phb_vfio_eeh_reenable(SPAPR_PCI_VFIO_HOST_BRIDGE(qdev)); + spapr_phb_vfio_eeh_reenable(SPAPR_PCI_HOST_BRIDGE(qdev)); } -static int spapr_phb_vfio_eeh_set_option(sPAPRPHBState *sphb, - unsigned int addr, int option) +int spapr_phb_vfio_eeh_set_option(sPAPRPHBState *sphb, + unsigned int addr, int option) { - sPAPRPHBVFIOState *svphb = SPAPR_PCI_VFIO_HOST_BRIDGE(sphb); - struct vfio_eeh_pe_op op = { .argsz = sizeof(op) }; + uint32_t op; int ret; switch (option) { case RTAS_EEH_DISABLE: - op.op = VFIO_EEH_PE_DISABLE; + op = VFIO_EEH_PE_DISABLE; break; case RTAS_EEH_ENABLE: { PCIHostState *phb; @@ -117,25 +94,24 @@ static int spapr_phb_vfio_eeh_set_option(sPAPRPHBState *sphb, phb = PCI_HOST_BRIDGE(sphb); pdev = pci_find_device(phb->bus, (addr >> 16) & 0xFF, (addr >> 8) & 0xFF); - if (!pdev) { + if (!pdev || !object_dynamic_cast(OBJECT(pdev), "vfio-pci")) { return RTAS_OUT_PARAM_ERROR; } - op.op = VFIO_EEH_PE_ENABLE; + op = VFIO_EEH_PE_ENABLE; break; } case RTAS_EEH_THAW_IO: - op.op = VFIO_EEH_PE_UNFREEZE_IO; + op = VFIO_EEH_PE_UNFREEZE_IO; break; case RTAS_EEH_THAW_DMA: - op.op = VFIO_EEH_PE_UNFREEZE_DMA; + op = VFIO_EEH_PE_UNFREEZE_DMA; break; default: return RTAS_OUT_PARAM_ERROR; } - ret = vfio_container_ioctl(&svphb->phb.iommu_as, svphb->iommugroupid, - VFIO_EEH_PE_OP, &op); + ret = vfio_eeh_as_op(&sphb->iommu_as, op); if (ret < 0) { return RTAS_OUT_HW_ERROR; } @@ -143,15 +119,11 @@ static int spapr_phb_vfio_eeh_set_option(sPAPRPHBState *sphb, return RTAS_OUT_SUCCESS; } -static int spapr_phb_vfio_eeh_get_state(sPAPRPHBState *sphb, int *state) +int spapr_phb_vfio_eeh_get_state(sPAPRPHBState *sphb, int *state) { - sPAPRPHBVFIOState *svphb = SPAPR_PCI_VFIO_HOST_BRIDGE(sphb); - struct vfio_eeh_pe_op op = { .argsz = sizeof(op) }; int ret; - op.op = VFIO_EEH_PE_GET_STATE; - ret = vfio_container_ioctl(&svphb->phb.iommu_as, svphb->iommugroupid, - VFIO_EEH_PE_OP, &op); + ret = vfio_eeh_as_op(&sphb->iommu_as, VFIO_EEH_PE_GET_STATE); if (ret < 0) { return RTAS_OUT_PARAM_ERROR; } @@ -203,30 +175,28 @@ static void spapr_phb_vfio_eeh_pre_reset(sPAPRPHBState *sphb) pci_for_each_bus(phb->bus, spapr_phb_vfio_eeh_clear_bus_msix, NULL); } -static int spapr_phb_vfio_eeh_reset(sPAPRPHBState *sphb, int option) +int spapr_phb_vfio_eeh_reset(sPAPRPHBState *sphb, int option) { - sPAPRPHBVFIOState *svphb = SPAPR_PCI_VFIO_HOST_BRIDGE(sphb); - struct vfio_eeh_pe_op op = { .argsz = sizeof(op) }; + uint32_t op; int ret; switch (option) { case RTAS_SLOT_RESET_DEACTIVATE: - op.op = VFIO_EEH_PE_RESET_DEACTIVATE; + op = VFIO_EEH_PE_RESET_DEACTIVATE; break; case RTAS_SLOT_RESET_HOT: spapr_phb_vfio_eeh_pre_reset(sphb); - op.op = VFIO_EEH_PE_RESET_HOT; + op = VFIO_EEH_PE_RESET_HOT; break; case RTAS_SLOT_RESET_FUNDAMENTAL: spapr_phb_vfio_eeh_pre_reset(sphb); - op.op = VFIO_EEH_PE_RESET_FUNDAMENTAL; + op = VFIO_EEH_PE_RESET_FUNDAMENTAL; break; default: return RTAS_OUT_PARAM_ERROR; } - ret = vfio_container_ioctl(&svphb->phb.iommu_as, svphb->iommugroupid, - VFIO_EEH_PE_OP, &op); + ret = vfio_eeh_as_op(&sphb->iommu_as, op); if (ret < 0) { return RTAS_OUT_HW_ERROR; } @@ -234,15 +204,11 @@ static int spapr_phb_vfio_eeh_reset(sPAPRPHBState *sphb, int option) return RTAS_OUT_SUCCESS; } -static int spapr_phb_vfio_eeh_configure(sPAPRPHBState *sphb) +int spapr_phb_vfio_eeh_configure(sPAPRPHBState *sphb) { - sPAPRPHBVFIOState *svphb = SPAPR_PCI_VFIO_HOST_BRIDGE(sphb); - struct vfio_eeh_pe_op op = { .argsz = sizeof(op) }; int ret; - op.op = VFIO_EEH_PE_CONFIGURE; - ret = vfio_container_ioctl(&svphb->phb.iommu_as, svphb->iommugroupid, - VFIO_EEH_PE_OP, &op); + ret = vfio_eeh_as_op(&sphb->iommu_as, VFIO_EEH_PE_CONFIGURE); if (ret < 0) { return RTAS_OUT_PARAM_ERROR; } @@ -253,23 +219,16 @@ static int spapr_phb_vfio_eeh_configure(sPAPRPHBState *sphb) static void spapr_phb_vfio_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - sPAPRPHBClass *spc = SPAPR_PCI_HOST_BRIDGE_CLASS(klass); dc->props = spapr_phb_vfio_properties; - dc->reset = spapr_phb_vfio_reset; - spc->finish_realize = spapr_phb_vfio_finish_realize; - spc->eeh_set_option = spapr_phb_vfio_eeh_set_option; - spc->eeh_get_state = spapr_phb_vfio_eeh_get_state; - spc->eeh_reset = spapr_phb_vfio_eeh_reset; - spc->eeh_configure = spapr_phb_vfio_eeh_configure; } static const TypeInfo spapr_phb_vfio_info = { .name = TYPE_SPAPR_PCI_VFIO_HOST_BRIDGE, .parent = TYPE_SPAPR_PCI_HOST_BRIDGE, .instance_size = sizeof(sPAPRPHBVFIOState), + .instance_init = spapr_phb_vfio_instance_init, .class_init = spapr_phb_vfio_class_init, - .class_size = sizeof(sPAPRPHBClass), }; static void spapr_pci_vfio_register_types(void) diff --git a/qemu/hw/ppc/spapr_rng.c b/qemu/hw/ppc/spapr_rng.c new file mode 100644 index 000000000..80515eb54 --- /dev/null +++ b/qemu/hw/ppc/spapr_rng.c @@ -0,0 +1,191 @@ +/* + * QEMU sPAPR random number generator "device" for H_RANDOM hypercall + * + * Copyright 2015 Thomas Huth, Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, + * or (at your option) any later version. + * + * 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 "qapi/error.h" +#include "qemu-common.h" +#include "cpu.h" +#include "qemu/error-report.h" +#include "sysemu/sysemu.h" +#include "sysemu/device_tree.h" +#include "sysemu/rng.h" +#include "hw/ppc/spapr.h" +#include "kvm_ppc.h" + +#define SPAPR_RNG(obj) \ + OBJECT_CHECK(sPAPRRngState, (obj), TYPE_SPAPR_RNG) + +struct sPAPRRngState { + /*< private >*/ + DeviceState ds; + RngBackend *backend; + bool use_kvm; +}; +typedef struct sPAPRRngState sPAPRRngState; + +struct HRandomData { + QemuSemaphore sem; + union { + uint64_t v64; + uint8_t v8[8]; + } val; + int received; +}; +typedef struct HRandomData HRandomData; + +/* Callback function for the RngBackend */ +static void random_recv(void *dest, const void *src, size_t size) +{ + HRandomData *hrdp = dest; + + if (src && size > 0) { + assert(size + hrdp->received <= sizeof(hrdp->val.v8)); + memcpy(&hrdp->val.v8[hrdp->received], src, size); + hrdp->received += size; + } + + qemu_sem_post(&hrdp->sem); +} + +/* Handler for the H_RANDOM hypercall */ +static target_ulong h_random(PowerPCCPU *cpu, sPAPRMachineState *spapr, + target_ulong opcode, target_ulong *args) +{ + sPAPRRngState *rngstate; + HRandomData hrdata; + + rngstate = SPAPR_RNG(object_resolve_path_type("", TYPE_SPAPR_RNG, NULL)); + + if (!rngstate || !rngstate->backend) { + return H_HARDWARE; + } + + qemu_sem_init(&hrdata.sem, 0); + hrdata.val.v64 = 0; + hrdata.received = 0; + + while (hrdata.received < 8) { + rng_backend_request_entropy(rngstate->backend, 8 - hrdata.received, + random_recv, &hrdata); + qemu_mutex_unlock_iothread(); + qemu_sem_wait(&hrdata.sem); + qemu_mutex_lock_iothread(); + } + + qemu_sem_destroy(&hrdata.sem); + args[0] = hrdata.val.v64; + + return H_SUCCESS; +} + +static void spapr_rng_instance_init(Object *obj) +{ + sPAPRRngState *rngstate = SPAPR_RNG(obj); + + if (object_resolve_path_type("", TYPE_SPAPR_RNG, NULL) != NULL) { + error_report("spapr-rng can not be instantiated twice!"); + return; + } + + object_property_add_link(obj, "rng", TYPE_RNG_BACKEND, + (Object **)&rngstate->backend, + object_property_allow_set_link, + OBJ_PROP_LINK_UNREF_ON_RELEASE, NULL); + object_property_set_description(obj, "rng", + "ID of the random number generator backend", + NULL); +} + +static void spapr_rng_realize(DeviceState *dev, Error **errp) +{ + + sPAPRRngState *rngstate = SPAPR_RNG(dev); + + if (rngstate->use_kvm) { + if (kvmppc_enable_hwrng() == 0) { + return; + } + /* + * If user specified both, use-kvm and a backend, we fall back to + * the backend now. If not, provide an appropriate error message. + */ + if (!rngstate->backend) { + error_setg(errp, "Could not initialize in-kernel H_RANDOM call!"); + return; + } + } + + if (rngstate->backend) { + spapr_register_hypercall(H_RANDOM, h_random); + } else { + error_setg(errp, "spapr-rng needs an RNG backend!"); + } +} + +int spapr_rng_populate_dt(void *fdt) +{ + int node; + int ret; + + node = qemu_fdt_add_subnode(fdt, "/ibm,platform-facilities"); + if (node <= 0) { + return -1; + } + ret = fdt_setprop_string(fdt, node, "device_type", + "ibm,platform-facilities"); + ret |= fdt_setprop_cell(fdt, node, "#address-cells", 0x1); + ret |= fdt_setprop_cell(fdt, node, "#size-cells", 0x0); + + node = fdt_add_subnode(fdt, node, "ibm,random-v1"); + if (node <= 0) { + return -1; + } + ret |= fdt_setprop_string(fdt, node, "compatible", "ibm,random"); + + return ret ? -1 : 0; +} + +static Property spapr_rng_properties[] = { + DEFINE_PROP_BOOL("use-kvm", sPAPRRngState, use_kvm, false), + DEFINE_PROP_END_OF_LIST(), +}; + +static void spapr_rng_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = spapr_rng_realize; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + dc->props = spapr_rng_properties; + dc->hotpluggable = false; +} + +static const TypeInfo spapr_rng_info = { + .name = TYPE_SPAPR_RNG, + .parent = TYPE_DEVICE, + .instance_size = sizeof(sPAPRRngState), + .instance_init = spapr_rng_instance_init, + .class_init = spapr_rng_class_init, +}; + +static void spapr_rng_register_type(void) +{ + type_register_static(&spapr_rng_info); +} +type_init(spapr_rng_register_type) diff --git a/qemu/hw/ppc/spapr_rtas.c b/qemu/hw/ppc/spapr_rtas.c index 2986f94f0..f07325831 100644 --- a/qemu/hw/ppc/spapr_rtas.c +++ b/qemu/hw/ppc/spapr_rtas.c @@ -24,6 +24,7 @@ * THE SOFTWARE. * */ +#include "qemu/osdep.h" #include "cpu.h" #include "sysemu/sysemu.h" #include "sysemu/char.h" @@ -34,9 +35,11 @@ #include "hw/ppc/spapr.h" #include "hw/ppc/spapr_vio.h" #include "qapi-event.h" +#include "hw/boards.h" #include <libfdt.h> #include "hw/ppc/spapr_drc.h" +#include "qemu/cutils.h" /* #define DEBUG_SPAPR */ @@ -111,6 +114,7 @@ static void rtas_power_off(PowerPCCPU *cpu, sPAPRMachineState *spapr, return; } qemu_system_shutdown_request(); + cpu_stop_current(); rtas_st(rets, 0, RTAS_OUT_SUCCESS); } @@ -214,7 +218,7 @@ static void rtas_stop_self(PowerPCCPU *cpu, sPAPRMachineState *spapr, CPUPPCState *env = &cpu->env; cs->halted = 1; - cpu_exit(cs); + qemu_cpu_kick(cs); /* * While stopping a CPU, the guest calls H_CPPR which * effectively disables interrupts on XICS level. @@ -227,6 +231,19 @@ static void rtas_stop_self(PowerPCCPU *cpu, sPAPRMachineState *spapr, env->msr = 0; } +static inline int sysparm_st(target_ulong addr, target_ulong len, + const void *val, uint16_t vallen) +{ + hwaddr phys = ppc64_phys_to_real(addr); + + if (len < 2) { + return RTAS_OUT_SYSPARM_PARAM_ERROR; + } + stw_be_phys(&address_space_memory, phys, vallen); + cpu_physical_memory_write(phys + 2, val, MIN(len - 2, vallen)); + return RTAS_OUT_SUCCESS; +} + static void rtas_ibm_get_system_parameter(PowerPCCPU *cpu, sPAPRMachineState *spapr, uint32_t token, uint32_t nargs, @@ -236,24 +253,30 @@ static void rtas_ibm_get_system_parameter(PowerPCCPU *cpu, target_ulong parameter = rtas_ld(args, 0); target_ulong buffer = rtas_ld(args, 1); target_ulong length = rtas_ld(args, 2); - target_ulong ret = RTAS_OUT_SUCCESS; + target_ulong ret; switch (parameter) { case RTAS_SYSPARM_SPLPAR_CHARACTERISTICS: { - char *param_val = g_strdup_printf("MaxEntCap=%d,MaxPlatProcs=%d", - max_cpus, smp_cpus); - rtas_st_buffer(buffer, length, (uint8_t *)param_val, strlen(param_val)); + char *param_val = g_strdup_printf("MaxEntCap=%d," + "DesMem=%llu," + "DesProcs=%d," + "MaxPlatProcs=%d", + max_cpus, + current_machine->ram_size / M_BYTE, + smp_cpus, + max_cpus); + ret = sysparm_st(buffer, length, param_val, strlen(param_val) + 1); g_free(param_val); break; } case RTAS_SYSPARM_DIAGNOSTICS_RUN_MODE: { uint8_t param_val = DIAGNOSTICS_RUN_MODE_DISABLED; - rtas_st_buffer(buffer, length, ¶m_val, sizeof(param_val)); + ret = sysparm_st(buffer, length, ¶m_val, sizeof(param_val)); break; } case RTAS_SYSPARM_UUID: - rtas_st_buffer(buffer, length, qemu_uuid, (qemu_uuid_set ? 16 : 0)); + ret = sysparm_st(buffer, length, qemu_uuid, (qemu_uuid_set ? 16 : 0)); break; default: ret = RTAS_OUT_NOT_SUPPORTED; @@ -365,12 +388,13 @@ static void rtas_set_indicator(PowerPCCPU *cpu, sPAPRMachineState *spapr, uint32_t sensor_type; uint32_t sensor_index; uint32_t sensor_state; + uint32_t ret = RTAS_OUT_SUCCESS; sPAPRDRConnector *drc; sPAPRDRConnectorClass *drck; if (nargs != 3 || nret != 1) { - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; + ret = RTAS_OUT_PARAM_ERROR; + goto out; } sensor_type = rtas_ld(args, 0); @@ -386,8 +410,8 @@ static void rtas_set_indicator(PowerPCCPU *cpu, sPAPRMachineState *spapr, if (!drc) { DPRINTF("rtas_set_indicator: invalid sensor/DRC index: %xh\n", sensor_index); - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; + ret = RTAS_OUT_PARAM_ERROR; + goto out; } drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); @@ -406,19 +430,20 @@ static void rtas_set_indicator(PowerPCCPU *cpu, sPAPRMachineState *spapr, spapr_ccs_remove(spapr, ccs); } } - drck->set_isolation_state(drc, sensor_state); + ret = drck->set_isolation_state(drc, sensor_state); break; case RTAS_SENSOR_TYPE_DR: - drck->set_indicator_state(drc, sensor_state); + ret = drck->set_indicator_state(drc, sensor_state); break; case RTAS_SENSOR_TYPE_ALLOCATION_STATE: - drck->set_allocation_state(drc, sensor_state); + ret = drck->set_allocation_state(drc, sensor_state); break; default: goto out_unimplemented; } - rtas_st(rets, 0, RTAS_OUT_SUCCESS); +out: + rtas_st(rets, 0, ret); return; out_unimplemented: @@ -435,13 +460,14 @@ static void rtas_get_sensor_state(PowerPCCPU *cpu, sPAPRMachineState *spapr, { uint32_t sensor_type; uint32_t sensor_index; + uint32_t sensor_state = 0; sPAPRDRConnector *drc; sPAPRDRConnectorClass *drck; - uint32_t entity_sense; + uint32_t ret = RTAS_OUT_SUCCESS; if (nargs != 2 || nret != 2) { - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; + ret = RTAS_OUT_PARAM_ERROR; + goto out; } sensor_type = rtas_ld(args, 0); @@ -451,22 +477,23 @@ static void rtas_get_sensor_state(PowerPCCPU *cpu, sPAPRMachineState *spapr, /* currently only DR-related sensors are implemented */ DPRINTF("rtas_get_sensor_state: sensor/indicator not implemented: %d\n", sensor_type); - rtas_st(rets, 0, RTAS_OUT_NOT_SUPPORTED); - return; + ret = RTAS_OUT_NOT_SUPPORTED; + goto out; } drc = spapr_dr_connector_by_index(sensor_index); if (!drc) { DPRINTF("rtas_get_sensor_state: invalid sensor/DRC index: %xh\n", sensor_index); - rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); - return; + ret = RTAS_OUT_PARAM_ERROR; + goto out; } drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); - entity_sense = drck->entity_sense(drc); + ret = drck->entity_sense(drc, &sensor_state); - rtas_st(rets, 0, RTAS_OUT_SUCCESS); - rtas_st(rets, 1, entity_sense); +out: + rtas_st(rets, 0, ret); + rtas_st(rets, 1, sensor_state); } /* configure-connector work area offsets, int32_t units for field @@ -481,6 +508,13 @@ static void rtas_get_sensor_state(PowerPCCPU *cpu, sPAPRMachineState *spapr, #define CC_VAL_DATA_OFFSET ((CC_IDX_PROP_DATA_OFFSET + 1) * 4) #define CC_WA_LEN 4096 +static void configure_connector_st(target_ulong addr, target_ulong offset, + const void *buf, size_t len) +{ + cpu_physical_memory_write(ppc64_phys_to_real(addr + offset), + buf, MIN(len, CC_WA_LEN - offset)); +} + static void rtas_ibm_configure_connector(PowerPCCPU *cpu, sPAPRMachineState *spapr, uint32_t token, uint32_t nargs, @@ -515,6 +549,12 @@ static void rtas_ibm_configure_connector(PowerPCCPU *cpu, drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); fdt = drck->get_fdt(drc, NULL); + if (!fdt) { + DPRINTF("rtas_ibm_configure_connector: Missing FDT for DRC index: %xh\n", + drc_index); + rc = SPAPR_DR_CC_RESPONSE_NOT_CONFIGURABLE; + goto out; + } ccs = spapr_ccs_find(spapr, drc_index); if (!ccs) { @@ -540,8 +580,7 @@ static void rtas_ibm_configure_connector(PowerPCCPU *cpu, /* provide the name of the next OF node */ wa_offset = CC_VAL_DATA_OFFSET; rtas_st(wa_addr, CC_IDX_NODE_NAME_OFFSET, wa_offset); - rtas_st_buffer_direct(wa_addr + wa_offset, CC_WA_LEN - wa_offset, - (uint8_t *)name, strlen(name) + 1); + configure_connector_st(wa_addr, wa_offset, name, strlen(name) + 1); resp = SPAPR_DR_CC_RESPONSE_NEXT_CHILD; break; case FDT_END_NODE: @@ -566,8 +605,7 @@ static void rtas_ibm_configure_connector(PowerPCCPU *cpu, /* provide the name of the next OF property */ wa_offset = CC_VAL_DATA_OFFSET; rtas_st(wa_addr, CC_IDX_PROP_NAME_OFFSET, wa_offset); - rtas_st_buffer_direct(wa_addr + wa_offset, CC_WA_LEN - wa_offset, - (uint8_t *)name, strlen(name) + 1); + configure_connector_st(wa_addr, wa_offset, name, strlen(name) + 1); /* provide the length and value of the OF property. data gets * placed immediately after NULL terminator of the OF property's @@ -576,9 +614,7 @@ static void rtas_ibm_configure_connector(PowerPCCPU *cpu, wa_offset += strlen(name) + 1, rtas_st(wa_addr, CC_IDX_PROP_LEN, prop_len); rtas_st(wa_addr, CC_IDX_PROP_DATA_OFFSET, wa_offset); - rtas_st_buffer_direct(wa_addr + wa_offset, CC_WA_LEN - wa_offset, - (uint8_t *)((struct fdt_property *)prop)->data, - prop_len); + configure_connector_st(wa_addr, wa_offset, prop->data, prop_len); resp = SPAPR_DR_CC_RESPONSE_NEXT_PROPERTY; break; case FDT_END: @@ -631,17 +667,11 @@ target_ulong spapr_rtas_call(PowerPCCPU *cpu, sPAPRMachineState *spapr, void spapr_rtas_register(int token, const char *name, spapr_rtas_fn fn) { - if (!((token >= RTAS_TOKEN_BASE) && (token < RTAS_TOKEN_MAX))) { - fprintf(stderr, "RTAS invalid token 0x%x\n", token); - exit(1); - } + assert((token >= RTAS_TOKEN_BASE) && (token < RTAS_TOKEN_MAX)); token -= RTAS_TOKEN_BASE; - if (rtas_table[token].name) { - fprintf(stderr, "RTAS call \"%s\" is registered already as 0x%x\n", - rtas_table[token].name, token); - exit(1); - } + + assert(!rtas_table[token].name); rtas_table[token].name = name; rtas_table[token].fn = fn; @@ -654,6 +684,9 @@ int spapr_rtas_device_tree_setup(void *fdt, hwaddr rtas_addr, int i; uint32_t lrdr_capacity[5]; MachineState *machine = MACHINE(qdev_get_machine()); + sPAPRMachineState *spapr = SPAPR_MACHINE(machine); + uint64_t max_hotplug_addr = spapr->hotplug_memory.base + + memory_region_size(&spapr->hotplug_memory.mr); ret = fdt_add_mem_rsv(fdt, rtas_addr, rtas_size); if (ret < 0) { @@ -703,8 +736,8 @@ int spapr_rtas_device_tree_setup(void *fdt, hwaddr rtas_addr, } - lrdr_capacity[0] = cpu_to_be32(((uint64_t)machine->maxram_size) >> 32); - lrdr_capacity[1] = cpu_to_be32(machine->maxram_size & 0xffffffff); + lrdr_capacity[0] = cpu_to_be32(max_hotplug_addr >> 32); + lrdr_capacity[1] = cpu_to_be32(max_hotplug_addr & 0xffffffff); lrdr_capacity[2] = 0; lrdr_capacity[3] = cpu_to_be32(SPAPR_MEMORY_BLOCK_SIZE); lrdr_capacity[4] = cpu_to_be32(max_cpus/smp_threads); diff --git a/qemu/hw/ppc/spapr_rtc.c b/qemu/hw/ppc/spapr_rtc.c index 34b27db70..3a17ac42e 100644 --- a/qemu/hw/ppc/spapr_rtc.c +++ b/qemu/hw/ppc/spapr_rtc.c @@ -25,11 +25,13 @@ * THE SOFTWARE. * */ +#include "qemu/osdep.h" #include "cpu.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" #include "hw/ppc/spapr.h" #include "qapi-event.h" +#include "qemu/cutils.h" #define SPAPR_RTC(obj) \ OBJECT_CHECK(sPAPRRTCState, (obj), TYPE_SPAPR_RTC) @@ -200,7 +202,6 @@ static const TypeInfo spapr_rtc_info = { .name = TYPE_SPAPR_RTC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(sPAPRRTCState), - .class_size = sizeof(XICSStateClass), .class_init = spapr_rtc_class_init, }; diff --git a/qemu/hw/ppc/spapr_vio.c b/qemu/hw/ppc/spapr_vio.c index c51eb8e24..8aa021fde 100644 --- a/qemu/hw/ppc/spapr_vio.c +++ b/qemu/hw/ppc/spapr_vio.c @@ -19,6 +19,8 @@ * License along with this library; if not, see <http://www.gnu.org/licenses/>. */ +#include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/hw.h" #include "sysemu/sysemu.h" #include "hw/boards.h" @@ -388,7 +390,7 @@ static void rtas_quiesce(PowerPCCPU *cpu, sPAPRMachineState *spapr, static VIOsPAPRDevice *reg_conflict(VIOsPAPRDevice *dev) { - VIOsPAPRBus *bus = DO_UPCAST(VIOsPAPRBus, bus, dev->qdev.parent_bus); + VIOsPAPRBus *bus = SPAPR_VIO_BUS(dev->qdev.parent_bus); BusChild *kid; VIOsPAPRDevice *other; @@ -430,6 +432,7 @@ static void spapr_vio_busdev_realize(DeviceState *qdev, Error **errp) VIOsPAPRDevice *dev = (VIOsPAPRDevice *)qdev; VIOsPAPRDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev); char *id; + Error *local_err = NULL; if (dev->reg != -1) { /* @@ -449,7 +452,7 @@ static void spapr_vio_busdev_realize(DeviceState *qdev, Error **errp) } } else { /* Need to assign an address */ - VIOsPAPRBus *bus = DO_UPCAST(VIOsPAPRBus, bus, dev->qdev.parent_bus); + VIOsPAPRBus *bus = SPAPR_VIO_BUS(dev->qdev.parent_bus); do { dev->reg = bus->next_reg++; @@ -462,9 +465,9 @@ static void spapr_vio_busdev_realize(DeviceState *qdev, Error **errp) dev->qdev.id = id; } - dev->irq = xics_alloc(spapr->icp, 0, dev->irq, false); - if (!dev->irq) { - error_setg(errp, "can't allocate IRQ"); + dev->irq = xics_alloc(spapr->icp, 0, dev->irq, false, &local_err); + if (local_err) { + error_propagate(errp, local_err); return; } @@ -523,13 +526,12 @@ VIOsPAPRBus *spapr_vio_bus_init(void) DeviceState *dev; /* Create bridge device */ - dev = qdev_create(NULL, "spapr-vio-bridge"); + dev = qdev_create(NULL, TYPE_SPAPR_VIO_BRIDGE); qdev_init_nofail(dev); /* Create bus on bridge device */ - qbus = qbus_create(TYPE_SPAPR_VIO_BUS, dev, "spapr-vio"); - bus = DO_UPCAST(VIOsPAPRBus, bus, qbus); + bus = SPAPR_VIO_BUS(qbus); bus->next_reg = 0x71000000; /* hcall-vio */ @@ -567,9 +569,8 @@ static void spapr_vio_bridge_class_init(ObjectClass *klass, void *data) } static const TypeInfo spapr_vio_bridge_info = { - .name = "spapr-vio-bridge", + .name = TYPE_SPAPR_VIO_BRIDGE, .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(SysBusDevice), .class_init = spapr_vio_bridge_class_init, }; diff --git a/qemu/hw/ppc/virtex_ml507.c b/qemu/hw/ppc/virtex_ml507.c index de86f7c64..b807a08c2 100644 --- a/qemu/hw/ppc/virtex_ml507.c +++ b/qemu/hw/ppc/virtex_ml507.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include "hw/sysbus.h" #include "hw/hw.h" #include "hw/char/serial.h" @@ -257,7 +258,8 @@ static void virtex_init(MachineState *machine) /* Boots a kernel elf binary. */ kernel_size = load_elf(kernel_filename, NULL, NULL, - &entry, &low, &high, 1, ELF_MACHINE, 0); + &entry, &low, &high, 1, PPC_ELF_MACHINE, + 0, 0); boot_info.bootstrap_pc = entry & 0x00ffffff; if (kernel_size < 0) { @@ -297,15 +299,10 @@ static void virtex_init(MachineState *machine) env->load_info = &boot_info; } -static QEMUMachine virtex_machine = { - .name = "virtex-ml507", - .desc = "Xilinx Virtex ML507 reference design", - .init = virtex_init, -}; - -static void virtex_machine_init(void) +static void virtex_machine_init(MachineClass *mc) { - qemu_register_machine(&virtex_machine); + mc->desc = "Xilinx Virtex ML507 reference design"; + mc->init = virtex_init; } -machine_init(virtex_machine_init); +DEFINE_MACHINE("virtex-ml507", virtex_machine_init) |