From 097a50d0d861680da24897b6003f8eea2333577f Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 3 Dec 2015 10:35:31 +0200 Subject: [PATCH 01/55] mmap-alloc: tweak a comment on ppc64 The comment I put in mmap-alloc to document the ppc64 rules refers to the previous revision of the patch: we don't look at memory alignment anymore, we check the fs from which the fd is mapped, instead. It's also not clear what does "in this case" refer to, rearrange text to make it clearer. Signed-off-by: Michael S. Tsirkin --- util/mmap-alloc.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/util/mmap-alloc.c b/util/mmap-alloc.c index 54793a5dcf..5cd7f71ac3 100644 --- a/util/mmap-alloc.c +++ b/util/mmap-alloc.c @@ -50,10 +50,11 @@ void *qemu_ram_mmap(int fd, size_t size, size_t align, bool shared) #if defined(__powerpc64__) && defined(__linux__) /* On ppc64 mappings in the same segment (aka slice) must share the same * page size. Since we will be re-allocating part of this segment - * from the supplied fd, we should make sure to use the same page size, - * unless we are using the system page size, in which case anonymous memory - * is OK. Use align as a hint for the page size. - * In this case, set MAP_NORESERVE to avoid allocating backing store memory. + * from the supplied fd, we should make sure to use the same page size, to + * this end we mmap the supplied fd. In this case, set MAP_NORESERVE to + * avoid allocating backing store memory. + * We do this unless we are using the system page size, in which case + * anonymous memory is OK. */ int anonfd = fd == -1 || qemu_fd_getpagesize(fd) == getpagesize() ? -1 : fd; int flags = anonfd == -1 ? MAP_ANONYMOUS : MAP_NORESERVE; From 7102fa7073b2cefb33ab4012a11f15fbf297a74b Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Tue, 1 Dec 2015 20:58:03 -0200 Subject: [PATCH 02/55] pc: Move compat boolean globals to PCMachineClass This way the compat flags can be initialized in the machine_options() function. This will help us to eventually eliminate the pc_compat_*() functions. Signed-off-by: Eduardo Habkost Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Marcel Apfelbaum --- hw/i386/pc.c | 8 +++++ hw/i386/pc_piix.c | 84 +++++++++++++++++++++----------------------- hw/i386/pc_q35.c | 54 +++++++++++++--------------- include/hw/i386/pc.h | 14 ++++++++ 4 files changed, 86 insertions(+), 74 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 426424a721..c2e1a7e2cc 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1953,6 +1953,14 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); pcmc->get_hotplug_handler = mc->get_hotplug_handler; + pcmc->pci_enabled = true; + pcmc->has_acpi_build = true; + pcmc->rsdp_in_ram = true; + pcmc->smbios_defaults = true; + pcmc->smbios_uuid_encoded = true; + pcmc->gigabyte_align = true; + pcmc->has_reserved_memory = true; + pcmc->kvmclock_enabled = true; mc->get_hotplug_handler = pc_get_hotpug_handler; mc->cpu_index_to_socket_id = pc_cpu_index_to_socket_id; mc->default_boot_order = "cad"; diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 319497e966..9e486c6341 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -61,26 +61,14 @@ static const int ide_iobase[MAX_IDE_BUS] = { 0x1f0, 0x170 }; static const int ide_iobase2[MAX_IDE_BUS] = { 0x3f6, 0x376 }; static const int ide_irq[MAX_IDE_BUS] = { 14, 15 }; -static bool pci_enabled = true; -static bool has_acpi_build = true; -static bool rsdp_in_ram = true; static int legacy_acpi_table_size; -static bool smbios_defaults = true; -static bool smbios_legacy_mode; -static bool smbios_uuid_encoded = true; -/* Make sure that guest addresses aligned at 1Gbyte boundaries get mapped to - * host addresses aligned at 1Gbyte boundaries. This way we can use 1GByte - * pages in the host. - */ -static bool gigabyte_align = true; -static bool has_reserved_memory = true; -static bool kvmclock_enabled = true; /* PC hardware initialisation */ static void pc_init1(MachineState *machine, const char *host_type, const char *pci_type) { PCMachineState *pcms = PC_MACHINE(machine); + PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); MemoryRegion *system_memory = get_system_memory(); MemoryRegion *system_io = get_system_io(); int i; @@ -109,7 +97,7 @@ static void pc_init1(MachineState *machine, * breaking migration. */ if (machine->ram_size >= 0xe0000000) { - lowmem = gigabyte_align ? 0xc0000000 : 0xe0000000; + lowmem = pcmc->gigabyte_align ? 0xc0000000 : 0xe0000000; } else { lowmem = 0xe0000000; } @@ -142,11 +130,11 @@ static void pc_init1(MachineState *machine, pc_cpus_init(pcms); - if (kvm_enabled() && kvmclock_enabled) { + if (kvm_enabled() && pcmc->kvmclock_enabled) { kvmclock_create(); } - if (pci_enabled) { + if (pcmc->pci_enabled) { pci_memory = g_new(MemoryRegion, 1); memory_region_init(pci_memory, NULL, "pci", UINT64_MAX); rom_memory = pci_memory; @@ -157,18 +145,19 @@ static void pc_init1(MachineState *machine, guest_info = pc_guest_info_init(pcms); - guest_info->has_acpi_build = has_acpi_build; + guest_info->has_acpi_build = pcmc->has_acpi_build; guest_info->legacy_acpi_table_size = legacy_acpi_table_size; - guest_info->isapc_ram_fw = !pci_enabled; - guest_info->has_reserved_memory = has_reserved_memory; - guest_info->rsdp_in_ram = rsdp_in_ram; + guest_info->isapc_ram_fw = !pcmc->pci_enabled; + guest_info->has_reserved_memory = pcmc->has_reserved_memory; + guest_info->rsdp_in_ram = pcmc->rsdp_in_ram; - if (smbios_defaults) { + if (pcmc->smbios_defaults) { MachineClass *mc = MACHINE_GET_CLASS(machine); /* These values are guest ABI, do not change */ smbios_set_defaults("QEMU", "Standard PC (i440FX + PIIX, 1996)", - mc->name, smbios_legacy_mode, smbios_uuid_encoded, + mc->name, pcmc->smbios_legacy_mode, + pcmc->smbios_uuid_encoded, SMBIOS_ENTRY_POINT_21); } @@ -183,14 +172,14 @@ static void pc_init1(MachineState *machine, gsi_state = g_malloc0(sizeof(*gsi_state)); if (kvm_ioapic_in_kernel()) { - kvm_pc_setup_irq_routing(pci_enabled); + kvm_pc_setup_irq_routing(pcmc->pci_enabled); gsi = qemu_allocate_irqs(kvm_pc_gsi_handler, gsi_state, GSI_NUM_PINS); } else { gsi = qemu_allocate_irqs(gsi_handler, gsi_state, GSI_NUM_PINS); } - if (pci_enabled) { + if (pcmc->pci_enabled) { pci_bus = i440fx_init(host_type, pci_type, &i440fx_state, &piix3_devfn, &isa_bus, gsi, @@ -218,13 +207,13 @@ static void pc_init1(MachineState *machine, gsi_state->i8259_irq[i] = i8259[i]; } g_free(i8259); - if (pci_enabled) { + if (pcmc->pci_enabled) { ioapic_init_gsi(gsi_state, "i440fx"); } pc_register_ferr_irq(gsi[13]); - pc_vga_init(isa_bus, pci_enabled ? pci_bus : NULL); + pc_vga_init(isa_bus, pcmc->pci_enabled ? pci_bus : NULL); assert(pcms->vmport != ON_OFF_AUTO__MAX); if (pcms->vmport == ON_OFF_AUTO_AUTO) { @@ -238,7 +227,7 @@ static void pc_init1(MachineState *machine, pc_nic_init(isa_bus, pci_bus); ide_drive_get(hd, ARRAY_SIZE(hd)); - if (pci_enabled) { + if (pcmc->pci_enabled) { PCIDevice *dev; if (xen_enabled()) { dev = pci_piix3_xen_ide_init(pci_bus, hd, piix3_devfn + 1); @@ -265,11 +254,11 @@ static void pc_init1(MachineState *machine, pc_cmos_init(pcms, idebus[0], idebus[1], rtc_state); - if (pci_enabled && usb_enabled()) { + if (pcmc->pci_enabled && usb_enabled()) { pci_create_simple(pci_bus, piix3_devfn + 2, "piix3-usb-uhci"); } - if (pci_enabled && acpi_enabled) { + if (pcmc->pci_enabled && acpi_enabled) { DeviceState *piix4_pm; I2CBus *smbus; @@ -290,7 +279,7 @@ static void pc_init1(MachineState *machine, PC_MACHINE_ACPI_DEVICE_PROP, &error_abort); } - if (pci_enabled) { + if (pcmc->pci_enabled) { pc_pci_device_init(pci_bus); } } @@ -316,7 +305,6 @@ static void pc_compat_2_3(MachineState *machine) static void pc_compat_2_2(MachineState *machine) { pc_compat_2_3(machine); - rsdp_in_ram = false; machine->suppress_vmdesc = true; } @@ -325,7 +313,6 @@ static void pc_compat_2_1(MachineState *machine) PCMachineState *pcms = PC_MACHINE(machine); pc_compat_2_2(machine); - smbios_uuid_encoded = false; x86_cpu_change_kvm_default("svm", NULL); pcms->enforce_aligned_dimm = false; } @@ -350,16 +337,12 @@ static void pc_compat_2_0(MachineState *machine) * QEMU 1.7 it is 6414. For RHEL/CentOS 7.0 it is 6418. */ legacy_acpi_table_size = 6652; - smbios_legacy_mode = true; - has_reserved_memory = false; pc_set_legacy_acpi_data_size(); } static void pc_compat_1_7(MachineState *machine) { pc_compat_2_0(machine); - smbios_defaults = false; - gigabyte_align = false; option_rom_has_mr = true; legacy_acpi_table_size = 6414; x86_cpu_change_kvm_default("x2apic", NULL); @@ -369,7 +352,6 @@ static void pc_compat_1_6(MachineState *machine) { pc_compat_1_7(machine); rom_file_has_mr = false; - has_acpi_build = false; } static void pc_compat_1_5(MachineState *machine) @@ -399,17 +381,10 @@ static void pc_compat_1_2(MachineState *machine) static void pc_compat_0_13(MachineState *machine) { pc_compat_1_2(machine); - kvmclock_enabled = false; } static void pc_init_isa(MachineState *machine) { - pci_enabled = false; - has_acpi_build = false; - smbios_defaults = false; - gigabyte_align = false; - smbios_legacy_mode = true; - has_reserved_memory = false; option_rom_has_mr = true; rom_file_has_mr = false; if (!machine->cpu_model) { @@ -511,9 +486,11 @@ DEFINE_I440FX_MACHINE(v2_3, "pc-i440fx-2.3", pc_compat_2_3, static void pc_i440fx_2_2_machine_options(MachineClass *m) { + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_i440fx_2_3_machine_options(m); m->hw_version = "2.2.0"; SET_MACHINE_COMPAT(m, PC_COMPAT_2_2); + pcmc->rsdp_in_ram = false; } DEFINE_I440FX_MACHINE(v2_2, "pc-i440fx-2.2", pc_compat_2_2, @@ -522,10 +499,12 @@ DEFINE_I440FX_MACHINE(v2_2, "pc-i440fx-2.2", pc_compat_2_2, static void pc_i440fx_2_1_machine_options(MachineClass *m) { + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_i440fx_2_2_machine_options(m); m->hw_version = "2.1.0"; m->default_display = NULL; SET_MACHINE_COMPAT(m, PC_COMPAT_2_1); + pcmc->smbios_uuid_encoded = false; } DEFINE_I440FX_MACHINE(v2_1, "pc-i440fx-2.1", pc_compat_2_1, @@ -535,9 +514,12 @@ DEFINE_I440FX_MACHINE(v2_1, "pc-i440fx-2.1", pc_compat_2_1, static void pc_i440fx_2_0_machine_options(MachineClass *m) { + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_i440fx_2_1_machine_options(m); m->hw_version = "2.0.0"; SET_MACHINE_COMPAT(m, PC_COMPAT_2_0); + pcmc->smbios_legacy_mode = true; + pcmc->has_reserved_memory = false; } DEFINE_I440FX_MACHINE(v2_0, "pc-i440fx-2.0", pc_compat_2_0, @@ -546,10 +528,13 @@ DEFINE_I440FX_MACHINE(v2_0, "pc-i440fx-2.0", pc_compat_2_0, static void pc_i440fx_1_7_machine_options(MachineClass *m) { + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_i440fx_2_0_machine_options(m); m->hw_version = "1.7.0"; m->default_machine_opts = NULL; SET_MACHINE_COMPAT(m, PC_COMPAT_1_7); + pcmc->smbios_defaults = false; + pcmc->gigabyte_align = false; } DEFINE_I440FX_MACHINE(v1_7, "pc-i440fx-1.7", pc_compat_1_7, @@ -558,9 +543,11 @@ DEFINE_I440FX_MACHINE(v1_7, "pc-i440fx-1.7", pc_compat_1_7, static void pc_i440fx_1_6_machine_options(MachineClass *m) { + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_i440fx_1_7_machine_options(m); m->hw_version = "1.6.0"; SET_MACHINE_COMPAT(m, PC_COMPAT_1_6); + pcmc->has_acpi_build = false; } DEFINE_I440FX_MACHINE(v1_6, "pc-i440fx-1.6", pc_compat_1_6, @@ -814,9 +801,11 @@ DEFINE_I440FX_MACHINE(v0_14, "pc-0.14", pc_compat_1_2, static void pc_i440fx_0_13_machine_options(MachineClass *m) { + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_i440fx_0_14_machine_options(m); m->hw_version = "0.13"; SET_MACHINE_COMPAT(m, PC_COMPAT_0_13); + pcmc->kvmclock_enabled = false; } DEFINE_I440FX_MACHINE(v0_13, "pc-0.13", pc_compat_0_13, @@ -1038,8 +1027,15 @@ void igd_passthrough_isa_bridge_create(PCIBus *bus, uint16_t gpu_dev_id) static void isapc_machine_options(MachineClass *m) { + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); m->desc = "ISA-only PC"; m->max_cpus = 1; + pcmc->pci_enabled = false; + pcmc->has_acpi_build = false; + pcmc->smbios_defaults = false; + pcmc->gigabyte_align = false; + pcmc->smbios_legacy_mode = true; + pcmc->has_reserved_memory = false; } DEFINE_PC_MACHINE(isapc, "isapc", pc_init_isa, diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 9a1206832b..c028748785 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -49,22 +49,11 @@ /* ICH9 AHCI has 6 ports */ #define MAX_SATA_PORTS 6 -static bool has_acpi_build = true; -static bool rsdp_in_ram = true; -static bool smbios_defaults = true; -static bool smbios_legacy_mode; -static bool smbios_uuid_encoded = true; -/* Make sure that guest addresses aligned at 1Gbyte boundaries get mapped to - * host addresses aligned at 1Gbyte boundaries. This way we can use 1GByte - * pages in the host. - */ -static bool gigabyte_align = true; -static bool has_reserved_memory = true; - /* PC hardware initialisation */ static void pc_q35_init(MachineState *machine) { PCMachineState *pcms = PC_MACHINE(machine); + PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); Q35PCIHost *q35_host; PCIHostState *phb; PCIBus *host_bus; @@ -76,7 +65,6 @@ static void pc_q35_init(MachineState *machine) MemoryRegion *ram_memory; GSIState *gsi_state; ISABus *isa_bus; - int pci_enabled = 1; qemu_irq *gsi; qemu_irq *i8259; int i; @@ -97,7 +85,7 @@ static void pc_q35_init(MachineState *machine) * breaking migration. */ if (machine->ram_size >= 0xb0000000) { - lowmem = gigabyte_align ? 0x80000000 : 0xb0000000; + lowmem = pcmc->gigabyte_align ? 0x80000000 : 0xb0000000; } else { lowmem = 0xb0000000; } @@ -134,7 +122,7 @@ static void pc_q35_init(MachineState *machine) kvmclock_create(); /* pci enabled */ - if (pci_enabled) { + if (pcmc->pci_enabled) { pci_memory = g_new(MemoryRegion, 1); memory_region_init(pci_memory, NULL, "pci", UINT64_MAX); rom_memory = pci_memory; @@ -145,19 +133,20 @@ static void pc_q35_init(MachineState *machine) guest_info = pc_guest_info_init(pcms); guest_info->isapc_ram_fw = false; - guest_info->has_acpi_build = has_acpi_build; - guest_info->has_reserved_memory = has_reserved_memory; - guest_info->rsdp_in_ram = rsdp_in_ram; + guest_info->has_acpi_build = pcmc->has_acpi_build; + guest_info->has_reserved_memory = pcmc->has_reserved_memory; + guest_info->rsdp_in_ram = pcmc->rsdp_in_ram; /* Migration was not supported in 2.0 for Q35, so do not bother * with this hack (see hw/i386/acpi-build.c). */ guest_info->legacy_acpi_table_size = 0; - if (smbios_defaults) { + if (pcmc->smbios_defaults) { /* These values are guest ABI, do not change */ smbios_set_defaults("QEMU", "Standard PC (Q35 + ICH9, 2009)", - mc->name, smbios_legacy_mode, smbios_uuid_encoded, + mc->name, pcmc->smbios_legacy_mode, + pcmc->smbios_uuid_encoded, SMBIOS_ENTRY_POINT_21); } @@ -170,7 +159,7 @@ static void pc_q35_init(MachineState *machine) /* irq lines */ gsi_state = g_malloc0(sizeof(*gsi_state)); if (kvm_irqchip_in_kernel()) { - kvm_pc_setup_irq_routing(pci_enabled); + kvm_pc_setup_irq_routing(pcmc->pci_enabled); gsi = qemu_allocate_irqs(kvm_pc_gsi_handler, gsi_state, GSI_NUM_PINS); } else { @@ -227,7 +216,7 @@ static void pc_q35_init(MachineState *machine) for (i = 0; i < ISA_NUM_IRQS; i++) { gsi_state->i8259_irq[i] = i8259[i]; } - if (pci_enabled) { + if (pcmc->pci_enabled) { ioapic_init_gsi(gsi_state, "q35"); } @@ -272,7 +261,7 @@ static void pc_q35_init(MachineState *machine) /* the rest devices to which pci devfn is automatically assigned */ pc_vga_init(isa_bus, host_bus); pc_nic_init(isa_bus, host_bus); - if (pci_enabled) { + if (pcmc->pci_enabled) { pc_pci_device_init(host_bus); } } @@ -298,7 +287,6 @@ static void pc_compat_2_3(MachineState *machine) static void pc_compat_2_2(MachineState *machine) { pc_compat_2_3(machine); - rsdp_in_ram = false; machine->suppress_vmdesc = true; } @@ -308,23 +296,18 @@ static void pc_compat_2_1(MachineState *machine) pc_compat_2_2(machine); pcms->enforce_aligned_dimm = false; - smbios_uuid_encoded = false; x86_cpu_change_kvm_default("svm", NULL); } static void pc_compat_2_0(MachineState *machine) { pc_compat_2_1(machine); - smbios_legacy_mode = true; - has_reserved_memory = false; pc_set_legacy_acpi_data_size(); } static void pc_compat_1_7(MachineState *machine) { pc_compat_2_0(machine); - smbios_defaults = false; - gigabyte_align = false; option_rom_has_mr = true; x86_cpu_change_kvm_default("x2apic", NULL); } @@ -333,7 +316,6 @@ static void pc_compat_1_6(MachineState *machine) { pc_compat_1_7(machine); rom_file_has_mr = false; - has_acpi_build = false; } static void pc_compat_1_5(MachineState *machine) @@ -409,9 +391,11 @@ DEFINE_Q35_MACHINE(v2_3, "pc-q35-2.3", pc_compat_2_3, static void pc_q35_2_2_machine_options(MachineClass *m) { + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_q35_2_3_machine_options(m); m->hw_version = "2.2.0"; SET_MACHINE_COMPAT(m, PC_COMPAT_2_2); + pcmc->rsdp_in_ram = false; } DEFINE_Q35_MACHINE(v2_2, "pc-q35-2.2", pc_compat_2_2, @@ -420,10 +404,12 @@ DEFINE_Q35_MACHINE(v2_2, "pc-q35-2.2", pc_compat_2_2, static void pc_q35_2_1_machine_options(MachineClass *m) { + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_q35_2_2_machine_options(m); m->hw_version = "2.1.0"; m->default_display = NULL; SET_MACHINE_COMPAT(m, PC_COMPAT_2_1); + pcmc->smbios_uuid_encoded = false; } DEFINE_Q35_MACHINE(v2_1, "pc-q35-2.1", pc_compat_2_1, @@ -432,9 +418,12 @@ DEFINE_Q35_MACHINE(v2_1, "pc-q35-2.1", pc_compat_2_1, static void pc_q35_2_0_machine_options(MachineClass *m) { + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_q35_2_1_machine_options(m); m->hw_version = "2.0.0"; SET_MACHINE_COMPAT(m, PC_COMPAT_2_0); + pcmc->has_reserved_memory = false; + pcmc->smbios_legacy_mode = true; } DEFINE_Q35_MACHINE(v2_0, "pc-q35-2.0", pc_compat_2_0, @@ -443,10 +432,13 @@ DEFINE_Q35_MACHINE(v2_0, "pc-q35-2.0", pc_compat_2_0, static void pc_q35_1_7_machine_options(MachineClass *m) { + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_q35_2_0_machine_options(m); m->hw_version = "1.7.0"; m->default_machine_opts = NULL; SET_MACHINE_COMPAT(m, PC_COMPAT_1_7); + pcmc->smbios_defaults = false; + pcmc->gigabyte_align = false; } DEFINE_Q35_MACHINE(v1_7, "pc-q35-1.7", pc_compat_1_7, @@ -455,9 +447,11 @@ DEFINE_Q35_MACHINE(v1_7, "pc-q35-1.7", pc_compat_1_7, static void pc_q35_1_6_machine_options(MachineClass *m) { + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_q35_machine_options(m); m->hw_version = "1.6.0"; SET_MACHINE_COMPAT(m, PC_COMPAT_1_6); + pcmc->has_acpi_build = false; } DEFINE_Q35_MACHINE(v1_6, "pc-q35-1.6", pc_compat_1_6, diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 4bf4faf050..969852fddb 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -75,6 +75,20 @@ struct PCMachineClass { bool broken_reserved_end; HotplugHandler *(*get_hotplug_handler)(MachineState *machine, DeviceState *dev); + + bool pci_enabled; + bool has_acpi_build; + bool rsdp_in_ram; + bool smbios_defaults; + bool smbios_legacy_mode; + bool smbios_uuid_encoded; + /* Make sure that guest addresses aligned at 1Gbyte boundaries get + * mapped to host addresses aligned at 1Gbyte boundaries. This way + * we can use 1GByte pages in the host. + */ + bool gigabyte_align; + bool has_reserved_memory; + bool kvmclock_enabled; }; #define TYPE_PC_MACHINE "generic-pc-machine" From 2b0ddf6612fe270a6db0394da6cf4d44ff48b3f5 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Tue, 1 Dec 2015 20:58:04 -0200 Subject: [PATCH 03/55] pc: Move legacy_acpi_table_size global to PCMachineClass This way we can set legacy_acpi_table_size on the machine_options() functions, instead of requirng code in pc_compat_*() functions. Signed-off-by: Eduardo Habkost Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Marcel Apfelbaum --- hw/i386/pc_piix.c | 40 +++++++++++++++++++--------------------- include/hw/i386/pc.h | 1 + 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 9e486c6341..fa6f507e3b 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -61,8 +61,6 @@ static const int ide_iobase[MAX_IDE_BUS] = { 0x1f0, 0x170 }; static const int ide_iobase2[MAX_IDE_BUS] = { 0x3f6, 0x376 }; static const int ide_irq[MAX_IDE_BUS] = { 14, 15 }; -static int legacy_acpi_table_size; - /* PC hardware initialisation */ static void pc_init1(MachineState *machine, const char *host_type, const char *pci_type) @@ -146,7 +144,7 @@ static void pc_init1(MachineState *machine, guest_info = pc_guest_info_init(pcms); guest_info->has_acpi_build = pcmc->has_acpi_build; - guest_info->legacy_acpi_table_size = legacy_acpi_table_size; + guest_info->legacy_acpi_table_size = pcmc->legacy_acpi_table_size; guest_info->isapc_ram_fw = !pcmc->pci_enabled; guest_info->has_reserved_memory = pcmc->has_reserved_memory; @@ -320,23 +318,6 @@ static void pc_compat_2_1(MachineState *machine) static void pc_compat_2_0(MachineState *machine) { pc_compat_2_1(machine); - /* This value depends on the actual DSDT and SSDT compiled into - * the source QEMU; unfortunately it depends on the binary and - * not on the machine type, so we cannot make pc-i440fx-1.7 work on - * both QEMU 1.7 and QEMU 2.0. - * - * Large variations cause migration to fail for more than one - * consecutive value of the "-smp" maxcpus option. - * - * For small variations of the kind caused by different iasl versions, - * the 4k rounding usually leaves slack. However, there could be still - * one or two values that break. For QEMU 1.7 and QEMU 2.0 the - * slack is only ~10 bytes before one "-smp maxcpus" value breaks! - * - * 6652 is valid for QEMU 2.0, the right value for pc-i440fx-1.7 on - * QEMU 1.7 it is 6414. For RHEL/CentOS 7.0 it is 6418. - */ - legacy_acpi_table_size = 6652; pc_set_legacy_acpi_data_size(); } @@ -344,7 +325,6 @@ static void pc_compat_1_7(MachineState *machine) { pc_compat_2_0(machine); option_rom_has_mr = true; - legacy_acpi_table_size = 6414; x86_cpu_change_kvm_default("x2apic", NULL); } @@ -520,6 +500,23 @@ static void pc_i440fx_2_0_machine_options(MachineClass *m) SET_MACHINE_COMPAT(m, PC_COMPAT_2_0); pcmc->smbios_legacy_mode = true; pcmc->has_reserved_memory = false; + /* This value depends on the actual DSDT and SSDT compiled into + * the source QEMU; unfortunately it depends on the binary and + * not on the machine type, so we cannot make pc-i440fx-1.7 work on + * both QEMU 1.7 and QEMU 2.0. + * + * Large variations cause migration to fail for more than one + * consecutive value of the "-smp" maxcpus option. + * + * For small variations of the kind caused by different iasl versions, + * the 4k rounding usually leaves slack. However, there could be still + * one or two values that break. For QEMU 1.7 and QEMU 2.0 the + * slack is only ~10 bytes before one "-smp maxcpus" value breaks! + * + * 6652 is valid for QEMU 2.0, the right value for pc-i440fx-1.7 on + * QEMU 1.7 it is 6414. For RHEL/CentOS 7.0 it is 6418. + */ + pcmc->legacy_acpi_table_size = 6652; } DEFINE_I440FX_MACHINE(v2_0, "pc-i440fx-2.0", pc_compat_2_0, @@ -535,6 +532,7 @@ static void pc_i440fx_1_7_machine_options(MachineClass *m) SET_MACHINE_COMPAT(m, PC_COMPAT_1_7); pcmc->smbios_defaults = false; pcmc->gigabyte_align = false; + pcmc->legacy_acpi_table_size = 6414; } DEFINE_I440FX_MACHINE(v1_7, "pc-i440fx-1.7", pc_compat_1_7, diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 969852fddb..f858b8b692 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -89,6 +89,7 @@ struct PCMachineClass { bool gigabyte_align; bool has_reserved_memory; bool kvmclock_enabled; + int legacy_acpi_table_size; }; #define TYPE_PC_MACHINE "generic-pc-machine" From cd4040ec1870f8ec49ec995d89d4862e0b6021b3 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Tue, 1 Dec 2015 20:58:05 -0200 Subject: [PATCH 04/55] pc: Move acpi_data_size global to PCMachineClass This way we don't need code in pc_compat_*() functions to set the legacy acpi_data_size value. Signed-off-by: Eduardo Habkost Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Marcel Apfelbaum --- hw/i386/pc.c | 17 ++++++----------- hw/i386/pc_piix.c | 2 +- hw/i386/pc_q35.c | 2 +- include/hw/i386/pc.h | 6 ++++-- 4 files changed, 12 insertions(+), 15 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index c2e1a7e2cc..e8dfc7013d 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -77,15 +77,6 @@ #define DPRINTF(fmt, ...) #endif -/* Leave a chunk of memory at the top of RAM for the BIOS ACPI tables - * (128K) and other BIOS datastructures (less than 4K reported to be used at - * the moment, 32K should be enough for a while). */ -static unsigned acpi_data_size = 0x20000 + 0x8000; -void pc_set_legacy_acpi_data_size(void) -{ - acpi_data_size = 0x10000; -} - #define BIOS_CFG_IOPORT 0x510 #define FW_CFG_ACPI_TABLES (FW_CFG_ARCH_LOCAL + 0) #define FW_CFG_SMBIOS_ENTRIES (FW_CFG_ARCH_LOCAL + 1) @@ -841,6 +832,7 @@ static void load_linux(PCMachineState *pcms, FILE *f; char *vmode; MachineState *machine = MACHINE(pcms); + PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); const char *kernel_filename = machine->kernel_filename; const char *initrd_filename = machine->initrd_filename; const char *kernel_cmdline = machine->kernel_cmdline; @@ -908,8 +900,8 @@ static void load_linux(PCMachineState *pcms, initrd_max = 0x37ffffff; } - if (initrd_max >= pcms->below_4g_mem_size - acpi_data_size) { - initrd_max = pcms->below_4g_mem_size - acpi_data_size - 1; + if (initrd_max >= pcms->below_4g_mem_size - pcmc->acpi_data_size) { + initrd_max = pcms->below_4g_mem_size - pcmc->acpi_data_size - 1; } fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_ADDR, cmdline_addr); @@ -1961,6 +1953,9 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) pcmc->gigabyte_align = true; pcmc->has_reserved_memory = true; pcmc->kvmclock_enabled = true; + /* BIOS ACPI tables: 128K. Other BIOS datastructures: less than 4K reported + * to be used at the moment, 32K should be enough for a while. */ + pcmc->acpi_data_size = 0x20000 + 0x8000; mc->get_hotplug_handler = pc_get_hotpug_handler; mc->cpu_index_to_socket_id = pc_cpu_index_to_socket_id; mc->default_boot_order = "cad"; diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index fa6f507e3b..82b8a5e74f 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -318,7 +318,6 @@ static void pc_compat_2_1(MachineState *machine) static void pc_compat_2_0(MachineState *machine) { pc_compat_2_1(machine); - pc_set_legacy_acpi_data_size(); } static void pc_compat_1_7(MachineState *machine) @@ -517,6 +516,7 @@ static void pc_i440fx_2_0_machine_options(MachineClass *m) * QEMU 1.7 it is 6414. For RHEL/CentOS 7.0 it is 6418. */ pcmc->legacy_acpi_table_size = 6652; + pcmc->acpi_data_size = 0x10000; } DEFINE_I440FX_MACHINE(v2_0, "pc-i440fx-2.0", pc_compat_2_0, diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index c028748785..cc85d3a475 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -302,7 +302,6 @@ static void pc_compat_2_1(MachineState *machine) static void pc_compat_2_0(MachineState *machine) { pc_compat_2_1(machine); - pc_set_legacy_acpi_data_size(); } static void pc_compat_1_7(MachineState *machine) @@ -424,6 +423,7 @@ static void pc_q35_2_0_machine_options(MachineClass *m) SET_MACHINE_COMPAT(m, PC_COMPAT_2_0); pcmc->has_reserved_memory = false; pcmc->smbios_legacy_mode = true; + pcmc->acpi_data_size = 0x10000; } DEFINE_Q35_MACHINE(v2_0, "pc-q35-2.0", pc_compat_2_0, diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index f858b8b692..3edae2bfa8 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -90,6 +90,10 @@ struct PCMachineClass { bool has_reserved_memory; bool kvmclock_enabled; int legacy_acpi_table_size; + /* Leave a chunk of memory at the top of RAM for the BIOS ACPI tables + * and other BIOS datastructures. + */ + unsigned acpi_data_size; }; #define TYPE_PC_MACHINE "generic-pc-machine" @@ -201,8 +205,6 @@ void pc_acpi_init(const char *default_dsdt); PcGuestInfo *pc_guest_info_init(PCMachineState *pcms); -void pc_set_legacy_acpi_data_size(void); - #define PCI_HOST_PROP_PCI_HOLE_START "pci-hole-start" #define PCI_HOST_PROP_PCI_HOLE_END "pci-hole-end" #define PCI_HOST_PROP_PCI_HOLE64_START "pci-hole64-start" From 16a9e8a5bcc1fb0796828eb654a4f926734e59af Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Tue, 1 Dec 2015 20:58:06 -0200 Subject: [PATCH 05/55] pc: Move enforce_aligned_dimm to PCMachineClass enforce_aligned_dimm never changes after the machine is initialized, so it can be simply set in PCMachineClass like all the other compat fields. Cc: Igor Mammedov Signed-off-by: Eduardo Habkost Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/pc.c | 12 +++++++----- hw/i386/pc_piix.c | 4 +--- hw/i386/pc_q35.c | 4 +--- include/hw/i386/pc.h | 6 +++--- 4 files changed, 12 insertions(+), 14 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index e8dfc7013d..5d8f7479bb 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1295,6 +1295,7 @@ FWCfgState *pc_memory_init(PCMachineState *pcms, MemoryRegion *ram_below_4g, *ram_above_4g; FWCfgState *fw_cfg; MachineState *machine = MACHINE(pcms); + PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); assert(machine->ram_size == pcms->below_4g_mem_size + pcms->above_4g_mem_size); @@ -1356,7 +1357,7 @@ FWCfgState *pc_memory_init(PCMachineState *pcms, pcms->hotplug_memory.base = ROUND_UP(0x100000000ULL + pcms->above_4g_mem_size, 1ULL << 30); - if (pcms->enforce_aligned_dimm) { + if (pcmc->enforce_aligned_dimm) { /* size hotplug region assuming 1G page max alignment per slot */ hotplug_mem_size += (1ULL << 30) * machine->ram_slots; } @@ -1609,12 +1610,13 @@ static void pc_dimm_plug(HotplugHandler *hotplug_dev, HotplugHandlerClass *hhc; Error *local_err = NULL; PCMachineState *pcms = PC_MACHINE(hotplug_dev); + PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); PCDIMMDevice *dimm = PC_DIMM(dev); PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); MemoryRegion *mr = ddc->get_memory_region(dimm); uint64_t align = TARGET_PAGE_SIZE; - if (memory_region_get_alignment(mr) && pcms->enforce_aligned_dimm) { + if (memory_region_get_alignment(mr) && pcmc->enforce_aligned_dimm) { align = memory_region_get_alignment(mr); } @@ -1865,9 +1867,9 @@ static void pc_machine_set_smm(Object *obj, Visitor *v, void *opaque, static bool pc_machine_get_aligned_dimm(Object *obj, Error **errp) { - PCMachineState *pcms = PC_MACHINE(obj); + PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(obj); - return pcms->enforce_aligned_dimm; + return pcmc->enforce_aligned_dimm; } static void pc_machine_initfn(Object *obj) @@ -1905,7 +1907,6 @@ static void pc_machine_initfn(Object *obj) "Enable vmport (pc & q35)", &error_abort); - pcms->enforce_aligned_dimm = true; object_property_add_bool(obj, PC_MACHINE_ENFORCE_ALIGNED_DIMM, pc_machine_get_aligned_dimm, NULL, &error_abort); @@ -1953,6 +1954,7 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) pcmc->gigabyte_align = true; pcmc->has_reserved_memory = true; pcmc->kvmclock_enabled = true; + pcmc->enforce_aligned_dimm = true; /* BIOS ACPI tables: 128K. Other BIOS datastructures: less than 4K reported * to be used at the moment, 32K should be enough for a while. */ pcmc->acpi_data_size = 0x20000 + 0x8000; diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 82b8a5e74f..f058b3cb00 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -308,11 +308,8 @@ static void pc_compat_2_2(MachineState *machine) static void pc_compat_2_1(MachineState *machine) { - PCMachineState *pcms = PC_MACHINE(machine); - pc_compat_2_2(machine); x86_cpu_change_kvm_default("svm", NULL); - pcms->enforce_aligned_dimm = false; } static void pc_compat_2_0(MachineState *machine) @@ -484,6 +481,7 @@ static void pc_i440fx_2_1_machine_options(MachineClass *m) m->default_display = NULL; SET_MACHINE_COMPAT(m, PC_COMPAT_2_1); pcmc->smbios_uuid_encoded = false; + pcmc->enforce_aligned_dimm = false; } DEFINE_I440FX_MACHINE(v2_1, "pc-i440fx-2.1", pc_compat_2_1, diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index cc85d3a475..2f644f951d 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -292,10 +292,7 @@ static void pc_compat_2_2(MachineState *machine) static void pc_compat_2_1(MachineState *machine) { - PCMachineState *pcms = PC_MACHINE(machine); - pc_compat_2_2(machine); - pcms->enforce_aligned_dimm = false; x86_cpu_change_kvm_default("svm", NULL); } @@ -409,6 +406,7 @@ static void pc_q35_2_1_machine_options(MachineClass *m) m->default_display = NULL; SET_MACHINE_COMPAT(m, PC_COMPAT_2_1); pcmc->smbios_uuid_encoded = false; + pcmc->enforce_aligned_dimm = false; } DEFINE_Q35_MACHINE(v2_1, "pc-q35-2.1", pc_compat_2_1, diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 3edae2bfa8..d1dc63ceb8 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -36,8 +36,6 @@ /** * PCMachineState: * @acpi_dev: link to ACPI PM device that performs ACPI hotplug handling - * @enforce_aligned_dimm: check that DIMM's address/size is aligned by - * backend's alignment value if provided */ struct PCMachineState { /*< private >*/ @@ -52,7 +50,6 @@ struct PCMachineState { uint64_t max_ram_below_4g; OnOffAuto vmport; OnOffAuto smm; - bool enforce_aligned_dimm; ram_addr_t below_4g_mem_size, above_4g_mem_size; }; @@ -66,6 +63,8 @@ struct PCMachineState { /** * PCMachineClass: * @get_hotplug_handler: pointer to parent class callback @get_hotplug_handler + * @enforce_aligned_dimm: check that DIMM's address/size is aligned by + * backend's alignment value if provided */ struct PCMachineClass { /*< private >*/ @@ -94,6 +93,7 @@ struct PCMachineClass { * and other BIOS datastructures. */ unsigned acpi_data_size; + bool enforce_aligned_dimm; }; #define TYPE_PC_MACHINE "generic-pc-machine" From cdedce0564a50a01d3121480e67b5b141c8e0c54 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Tue, 1 Dec 2015 20:58:07 -0200 Subject: [PATCH 06/55] pc: Remove enforce-aligned-dimm QOM property The property is read-only and not used for anything. Cc: Igor Mammedov Signed-off-by: Eduardo Habkost Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/pc.c | 11 ----------- include/hw/i386/pc.h | 1 - 2 files changed, 12 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 5d8f7479bb..00b59cdae0 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1865,13 +1865,6 @@ static void pc_machine_set_smm(Object *obj, Visitor *v, void *opaque, visit_type_OnOffAuto(v, &pcms->smm, name, errp); } -static bool pc_machine_get_aligned_dimm(Object *obj, Error **errp) -{ - PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(obj); - - return pcmc->enforce_aligned_dimm; -} - static void pc_machine_initfn(Object *obj) { PCMachineState *pcms = PC_MACHINE(obj); @@ -1906,10 +1899,6 @@ static void pc_machine_initfn(Object *obj) object_property_set_description(obj, PC_MACHINE_VMPORT, "Enable vmport (pc & q35)", &error_abort); - - object_property_add_bool(obj, PC_MACHINE_ENFORCE_ALIGNED_DIMM, - pc_machine_get_aligned_dimm, - NULL, &error_abort); } static void pc_machine_reset(void) diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index d1dc63ceb8..beb44ca23c 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -58,7 +58,6 @@ struct PCMachineState { #define PC_MACHINE_MAX_RAM_BELOW_4G "max-ram-below-4g" #define PC_MACHINE_VMPORT "vmport" #define PC_MACHINE_SMM "smm" -#define PC_MACHINE_ENFORCE_ALIGNED_DIMM "enforce-aligned-dimm" /** * PCMachineClass: From 71ae9e94d99240cd02926ad76fadb4963a873b09 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Tue, 1 Dec 2015 20:58:08 -0200 Subject: [PATCH 07/55] pc: Move option_rom_has_mr/rom_file_has_mr globals to MachineClass This way, these settings can be simply set on the corresponding machine_options() function, instead of requiring code in pc_compat_*() functions. Signed-off-by: Eduardo Habkost Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Marcel Apfelbaum --- hw/core/loader.c | 10 +++++----- hw/core/machine.c | 1 + hw/i386/pc_piix.c | 8 ++++---- hw/i386/pc_q35.c | 4 ++-- include/hw/boards.h | 2 ++ 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/hw/core/loader.c b/hw/core/loader.c index eb67f05ee2..6b69852479 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -51,12 +51,10 @@ #include "hw/nvram/fw_cfg.h" #include "exec/memory.h" #include "exec/address-spaces.h" +#include "hw/boards.h" #include -bool option_rom_has_mr = false; -bool rom_file_has_mr = true; - static int roms_loaded; /* return the size or -1 if error */ @@ -754,6 +752,7 @@ int rom_add_file(const char *file, const char *fw_dir, hwaddr addr, int32_t bootindex, bool option_rom) { + MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); Rom *rom; int rc, fd = -1; char devpath[100]; @@ -810,7 +809,7 @@ int rom_add_file(const char *file, const char *fw_dir, basename); snprintf(devpath, sizeof(devpath), "/rom@%s", fw_file_name); - if ((!option_rom || option_rom_has_mr) && rom_file_has_mr) { + if ((!option_rom || mc->option_rom_has_mr) && mc->rom_file_has_mr) { data = rom_set_mr(rom, OBJECT(fw_cfg), devpath); } else { data = rom->data; @@ -838,6 +837,7 @@ MemoryRegion *rom_add_blob(const char *name, const void *blob, size_t len, size_t max_len, hwaddr addr, const char *fw_file_name, FWCfgReadCallback fw_callback, void *callback_opaque) { + MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); Rom *rom; MemoryRegion *mr = NULL; @@ -855,7 +855,7 @@ MemoryRegion *rom_add_blob(const char *name, const void *blob, size_t len, snprintf(devpath, sizeof(devpath), "/rom@%s", fw_file_name); - if (rom_file_has_mr) { + if (mc->rom_file_has_mr) { data = rom_set_mr(rom, OBJECT(fw_cfg), devpath); mr = rom->mr; } else { diff --git a/hw/core/machine.c b/hw/core/machine.c index 82be54e751..c46ddc7d15 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -342,6 +342,7 @@ static void machine_class_init(ObjectClass *oc, void *data) /* Default 128 MB as guest ram size */ mc->default_ram_size = 128 * M_BYTE; + mc->rom_file_has_mr = true; } static void machine_class_base_init(ObjectClass *oc, void *data) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index f058b3cb00..7d847b247d 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -320,14 +320,12 @@ static void pc_compat_2_0(MachineState *machine) static void pc_compat_1_7(MachineState *machine) { pc_compat_2_0(machine); - option_rom_has_mr = true; x86_cpu_change_kvm_default("x2apic", NULL); } static void pc_compat_1_6(MachineState *machine) { pc_compat_1_7(machine); - rom_file_has_mr = false; } static void pc_compat_1_5(MachineState *machine) @@ -361,8 +359,6 @@ static void pc_compat_0_13(MachineState *machine) static void pc_init_isa(MachineState *machine) { - option_rom_has_mr = true; - rom_file_has_mr = false; if (!machine->cpu_model) { machine->cpu_model = "486"; } @@ -527,6 +523,7 @@ static void pc_i440fx_1_7_machine_options(MachineClass *m) pc_i440fx_2_0_machine_options(m); m->hw_version = "1.7.0"; m->default_machine_opts = NULL; + m->option_rom_has_mr = true; SET_MACHINE_COMPAT(m, PC_COMPAT_1_7); pcmc->smbios_defaults = false; pcmc->gigabyte_align = false; @@ -542,6 +539,7 @@ static void pc_i440fx_1_6_machine_options(MachineClass *m) PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_i440fx_1_7_machine_options(m); m->hw_version = "1.6.0"; + m->rom_file_has_mr = false; SET_MACHINE_COMPAT(m, PC_COMPAT_1_6); pcmc->has_acpi_build = false; } @@ -1026,6 +1024,8 @@ static void isapc_machine_options(MachineClass *m) PCMachineClass *pcmc = PC_MACHINE_CLASS(m); m->desc = "ISA-only PC"; m->max_cpus = 1; + m->option_rom_has_mr = true; + m->rom_file_has_mr = false; pcmc->pci_enabled = false; pcmc->has_acpi_build = false; pcmc->smbios_defaults = false; diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 2f644f951d..6aaa0eedb0 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -304,14 +304,12 @@ static void pc_compat_2_0(MachineState *machine) static void pc_compat_1_7(MachineState *machine) { pc_compat_2_0(machine); - option_rom_has_mr = true; x86_cpu_change_kvm_default("x2apic", NULL); } static void pc_compat_1_6(MachineState *machine) { pc_compat_1_7(machine); - rom_file_has_mr = false; } static void pc_compat_1_5(MachineState *machine) @@ -434,6 +432,7 @@ static void pc_q35_1_7_machine_options(MachineClass *m) pc_q35_2_0_machine_options(m); m->hw_version = "1.7.0"; m->default_machine_opts = NULL; + m->option_rom_has_mr = true; SET_MACHINE_COMPAT(m, PC_COMPAT_1_7); pcmc->smbios_defaults = false; pcmc->gigabyte_align = false; @@ -448,6 +447,7 @@ static void pc_q35_1_6_machine_options(MachineClass *m) PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_q35_machine_options(m); m->hw_version = "1.6.0"; + m->rom_file_has_mr = false; SET_MACHINE_COMPAT(m, PC_COMPAT_1_6); pcmc->has_acpi_build = false; } diff --git a/include/hw/boards.h b/include/hw/boards.h index f19df9769c..051db5ed25 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -93,6 +93,8 @@ struct MachineClass { GlobalProperty *compat_props; const char *hw_version; ram_addr_t default_ram_size; + bool option_rom_has_mr; + bool rom_file_has_mr; HotplugHandler *(*get_hotplug_handler)(MachineState *machine, DeviceState *dev); From d7fd0e69147dd6c73aeedd5b77738605fec641c9 Mon Sep 17 00:00:00 2001 From: Marcel Apfelbaum Date: Thu, 26 Nov 2015 18:00:26 +0200 Subject: [PATCH 08/55] hw/acpi: merge pxb adjacent memory/IO ranges A generic PCI Bus Expander doesn't necessary have a built-in PCI bridge. Int this case the ACPI will include IO/MEM ranges per device. Try to merge adjacent resources to reduce the ACPI tables length. Signed-off-by: Marcel Apfelbaum Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/acpi-build.c | 123 ++++++++++++++++++++++++++----------------- 1 file changed, 74 insertions(+), 49 deletions(-) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index fa866f6378..eb156cb116 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -762,16 +762,59 @@ static void crs_replace_with_free_ranges(GPtrArray *ranges, g_ptr_array_free(free_ranges, false); } +/* + * crs_range_merge - merges adjacent ranges in the given array. + * Array elements are deleted and replaced with the merged ranges. + */ +static void crs_range_merge(GPtrArray *range) +{ + GPtrArray *tmp = g_ptr_array_new_with_free_func(crs_range_free); + CrsRangeEntry *entry; + uint64_t range_base, range_limit; + int i; + + if (!range->len) { + return; + } + + g_ptr_array_sort(range, crs_range_compare); + + entry = g_ptr_array_index(range, 0); + range_base = entry->base; + range_limit = entry->limit; + for (i = 1; i < range->len; i++) { + entry = g_ptr_array_index(range, i); + if (entry->base - 1 == range_limit) { + range_limit = entry->limit; + } else { + crs_range_insert(tmp, range_base, range_limit); + range_base = entry->base; + range_limit = entry->limit; + } + } + crs_range_insert(tmp, range_base, range_limit); + + g_ptr_array_set_size(range, 0); + for (i = 0; i < tmp->len; i++) { + entry = g_ptr_array_index(tmp, i); + crs_range_insert(range, entry->base, entry->limit); + } + g_ptr_array_free(tmp, true); +} + static Aml *build_crs(PCIHostState *host, GPtrArray *io_ranges, GPtrArray *mem_ranges) { Aml *crs = aml_resource_template(); + GPtrArray *host_io_ranges = g_ptr_array_new_with_free_func(crs_range_free); + GPtrArray *host_mem_ranges = g_ptr_array_new_with_free_func(crs_range_free); + CrsRangeEntry *entry; uint8_t max_bus = pci_bus_num(host->bus); uint8_t type; int devfn; + int i; for (devfn = 0; devfn < ARRAY_SIZE(host->bus->devices); devfn++) { - int i; uint64_t range_base, range_limit; PCIDevice *dev = host->bus->devices[devfn]; @@ -794,26 +837,9 @@ static Aml *build_crs(PCIHostState *host, } if (r->type & PCI_BASE_ADDRESS_SPACE_IO) { - aml_append(crs, - aml_word_io(AML_MIN_FIXED, AML_MAX_FIXED, - AML_POS_DECODE, AML_ENTIRE_RANGE, - 0, - range_base, - range_limit, - 0, - range_limit - range_base + 1)); - crs_range_insert(io_ranges, range_base, range_limit); + crs_range_insert(host_io_ranges, range_base, range_limit); } else { /* "memory" */ - aml_append(crs, - aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED, - AML_MAX_FIXED, AML_NON_CACHEABLE, - AML_READ_WRITE, - 0, - range_base, - range_limit, - 0, - range_limit - range_base + 1)); - crs_range_insert(mem_ranges, range_base, range_limit); + crs_range_insert(host_mem_ranges, range_base, range_limit); } } @@ -832,15 +858,7 @@ static Aml *build_crs(PCIHostState *host, * that do not support multiple root buses */ if (range_base && range_base <= range_limit) { - aml_append(crs, - aml_word_io(AML_MIN_FIXED, AML_MAX_FIXED, - AML_POS_DECODE, AML_ENTIRE_RANGE, - 0, - range_base, - range_limit, - 0, - range_limit - range_base + 1)); - crs_range_insert(io_ranges, range_base, range_limit); + crs_range_insert(host_io_ranges, range_base, range_limit); } range_base = @@ -853,16 +871,7 @@ static Aml *build_crs(PCIHostState *host, * that do not support multiple root buses */ if (range_base && range_base <= range_limit) { - aml_append(crs, - aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED, - AML_MAX_FIXED, AML_NON_CACHEABLE, - AML_READ_WRITE, - 0, - range_base, - range_limit, - 0, - range_limit - range_base + 1)); - crs_range_insert(mem_ranges, range_base, range_limit); + crs_range_insert(host_mem_ranges, range_base, range_limit); } range_base = @@ -875,20 +884,36 @@ static Aml *build_crs(PCIHostState *host, * that do not support multiple root buses */ if (range_base && range_base <= range_limit) { - aml_append(crs, - aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED, - AML_MAX_FIXED, AML_NON_CACHEABLE, - AML_READ_WRITE, - 0, - range_base, - range_limit, - 0, - range_limit - range_base + 1)); - crs_range_insert(mem_ranges, range_base, range_limit); + crs_range_insert(host_mem_ranges, range_base, range_limit); } } } + crs_range_merge(host_io_ranges); + for (i = 0; i < host_io_ranges->len; i++) { + entry = g_ptr_array_index(host_io_ranges, i); + aml_append(crs, + aml_word_io(AML_MIN_FIXED, AML_MAX_FIXED, + AML_POS_DECODE, AML_ENTIRE_RANGE, + 0, entry->base, entry->limit, 0, + entry->limit - entry->base + 1)); + crs_range_insert(io_ranges, entry->base, entry->limit); + } + g_ptr_array_free(host_io_ranges, true); + + crs_range_merge(host_mem_ranges); + for (i = 0; i < host_mem_ranges->len; i++) { + entry = g_ptr_array_index(host_mem_ranges, i); + aml_append(crs, + aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED, + AML_MAX_FIXED, AML_NON_CACHEABLE, + AML_READ_WRITE, + 0, entry->base, entry->limit, 0, + entry->limit - entry->base + 1)); + crs_range_insert(mem_ranges, entry->base, entry->limit); + } + g_ptr_array_free(host_mem_ranges, true); + aml_append(crs, aml_word_bus_number(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE, 0, From 02b07434bed8360715198b4cbfdfebd17f7cac32 Mon Sep 17 00:00:00 2001 From: Marcel Apfelbaum Date: Thu, 26 Nov 2015 18:00:27 +0200 Subject: [PATCH 09/55] hw/pxb: introduce pxb-pcie expander for PCIe machines The pxb-pcie is the counterpart of pxb for PCI express machines. The new device re-uses the pxb code, but appears to the guests as a different device. The pxb-pcie device does not have an internal pci-pci bridge and exposes a PCIe root bus instead of a PCI one. Signed-off-by: Marcel Apfelbaum Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci-bridge/pci_expander_bridge.c | 98 ++++++++++++++++++++++++----- include/hw/pci/pci.h | 1 + 2 files changed, 84 insertions(+), 15 deletions(-) diff --git a/hw/pci-bridge/pci_expander_bridge.c b/hw/pci-bridge/pci_expander_bridge.c index 57f8a3762b..bb98fb2e46 100644 --- a/hw/pci-bridge/pci_expander_bridge.c +++ b/hw/pci-bridge/pci_expander_bridge.c @@ -23,6 +23,9 @@ #define TYPE_PXB_BUS "pxb-bus" #define PXB_BUS(obj) OBJECT_CHECK(PXBBus, (obj), TYPE_PXB_BUS) +#define TYPE_PXB_PCIE_BUS "pxb-pcie-bus" +#define PXB_PCIE_BUS(obj) OBJECT_CHECK(PXBBus, (obj), TYPE_PXB_PCIE_BUS) + typedef struct PXBBus { /*< private >*/ PCIBus parent_obj; @@ -34,6 +37,9 @@ typedef struct PXBBus { #define TYPE_PXB_DEVICE "pxb" #define PXB_DEV(obj) OBJECT_CHECK(PXBDev, (obj), TYPE_PXB_DEVICE) +#define TYPE_PXB_PCIE_DEVICE "pxb-pcie" +#define PXB_PCIE_DEV(obj) OBJECT_CHECK(PXBDev, (obj), TYPE_PXB_PCIE_DEVICE) + typedef struct PXBDev { /*< private >*/ PCIDevice parent_obj; @@ -43,13 +49,18 @@ typedef struct PXBDev { uint16_t numa_node; } PXBDev; +static PXBDev *convert_to_pxb(PCIDevice *dev) +{ + return pci_bus_is_express(dev->bus) ? PXB_PCIE_DEV(dev) : PXB_DEV(dev); +} + static GList *pxb_dev_list; #define TYPE_PXB_HOST "pxb-host" static int pxb_bus_num(PCIBus *bus) { - PXBDev *pxb = PXB_DEV(bus->parent_dev); + PXBDev *pxb = convert_to_pxb(bus->parent_dev); return pxb->bus_nr; } @@ -61,7 +72,7 @@ static bool pxb_is_root(PCIBus *bus) static uint16_t pxb_bus_numa_node(PCIBus *bus) { - PXBDev *pxb = PXB_DEV(bus->parent_dev); + PXBDev *pxb = convert_to_pxb(bus->parent_dev); return pxb->numa_node; } @@ -82,10 +93,18 @@ static const TypeInfo pxb_bus_info = { .class_init = pxb_bus_class_init, }; +static const TypeInfo pxb_pcie_bus_info = { + .name = TYPE_PXB_PCIE_BUS, + .parent = TYPE_PCIE_BUS, + .instance_size = sizeof(PXBBus), + .class_init = pxb_bus_class_init, +}; + static const char *pxb_host_root_bus_path(PCIHostState *host_bridge, PCIBus *rootbus) { - PXBBus *bus = PXB_BUS(rootbus); + PXBBus *bus = pci_bus_is_express(rootbus) ? + PXB_PCIE_BUS(rootbus) : PXB_BUS(rootbus); snprintf(bus->bus_path, 8, "0000:%02x", pxb_bus_num(rootbus)); return bus->bus_path; @@ -103,7 +122,7 @@ static char *pxb_host_ofw_unit_address(const SysBusDevice *dev) pxb_host = PCI_HOST_BRIDGE(dev); pxb_bus = pxb_host->bus; - pxb_dev = PXB_DEV(pxb_bus->parent_dev); + pxb_dev = convert_to_pxb(pxb_bus->parent_dev); position = g_list_index(pxb_dev_list, pxb_dev); assert(position >= 0); @@ -193,10 +212,10 @@ static gint pxb_compare(gconstpointer a, gconstpointer b) 0; } -static int pxb_dev_initfn(PCIDevice *dev) +static int pxb_dev_init_common(PCIDevice *dev, bool pcie) { - PXBDev *pxb = PXB_DEV(dev); - DeviceState *ds, *bds; + PXBDev *pxb = convert_to_pxb(dev); + DeviceState *ds, *bds = NULL; PCIBus *bus; const char *dev_name = NULL; @@ -211,18 +230,21 @@ static int pxb_dev_initfn(PCIDevice *dev) } ds = qdev_create(NULL, TYPE_PXB_HOST); - bus = pci_bus_new(ds, "pxb-internal", NULL, NULL, 0, TYPE_PXB_BUS); + if (pcie) { + bus = pci_bus_new(ds, dev_name, NULL, NULL, 0, TYPE_PXB_PCIE_BUS); + } else { + bus = pci_bus_new(ds, "pxb-internal", NULL, NULL, 0, TYPE_PXB_BUS); + bds = qdev_create(BUS(bus), "pci-bridge"); + bds->id = dev_name; + qdev_prop_set_uint8(bds, PCI_BRIDGE_DEV_PROP_CHASSIS_NR, pxb->bus_nr); + qdev_prop_set_bit(bds, PCI_BRIDGE_DEV_PROP_SHPC, false); + } bus->parent_dev = dev; bus->address_space_mem = dev->bus->address_space_mem; bus->address_space_io = dev->bus->address_space_io; bus->map_irq = pxb_map_irq_fn; - bds = qdev_create(BUS(bus), "pci-bridge"); - bds->id = dev_name; - qdev_prop_set_uint8(bds, PCI_BRIDGE_DEV_PROP_CHASSIS_NR, pxb->bus_nr); - qdev_prop_set_bit(bds, PCI_BRIDGE_DEV_PROP_SHPC, false); - PCI_HOST_BRIDGE(ds)->bus = bus; if (pxb_register_bus(dev, bus)) { @@ -230,7 +252,9 @@ static int pxb_dev_initfn(PCIDevice *dev) } qdev_init_nofail(ds); - qdev_init_nofail(bds); + if (bds) { + qdev_init_nofail(bds); + } pci_word_test_and_set_mask(dev->config + PCI_STATUS, PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK); @@ -240,9 +264,19 @@ static int pxb_dev_initfn(PCIDevice *dev) return 0; } +static int pxb_dev_initfn(PCIDevice *dev) +{ + if (pci_bus_is_express(dev->bus)) { + error_report("pxb devices cannot reside on a PCIe bus!"); + return -EINVAL; + } + + return pxb_dev_init_common(dev, false); +} + static void pxb_dev_exitfn(PCIDevice *pci_dev) { - PXBDev *pxb = PXB_DEV(pci_dev); + PXBDev *pxb = convert_to_pxb(pci_dev); pxb_dev_list = g_list_remove(pxb_dev_list, pxb); } @@ -276,11 +310,45 @@ static const TypeInfo pxb_dev_info = { .class_init = pxb_dev_class_init, }; +static int pxb_pcie_dev_initfn(PCIDevice *dev) +{ + if (!pci_bus_is_express(dev->bus)) { + error_report("pxb-pcie devices cannot reside on a PCI bus!"); + return -EINVAL; + } + + return pxb_dev_init_common(dev, true); +} + +static void pxb_pcie_dev_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->init = pxb_pcie_dev_initfn; + k->exit = pxb_dev_exitfn; + k->vendor_id = PCI_VENDOR_ID_REDHAT; + k->device_id = PCI_DEVICE_ID_REDHAT_PXB_PCIE; + k->class_id = PCI_CLASS_BRIDGE_HOST; + + dc->desc = "PCI Express Expander Bridge"; + dc->props = pxb_dev_properties; +} + +static const TypeInfo pxb_pcie_dev_info = { + .name = TYPE_PXB_PCIE_DEVICE, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PXBDev), + .class_init = pxb_pcie_dev_class_init, +}; + static void pxb_register_types(void) { type_register_static(&pxb_bus_info); + type_register_static(&pxb_pcie_bus_info); type_register_static(&pxb_host_info); type_register_static(&pxb_dev_info); + type_register_static(&pxb_pcie_dev_info); } type_init(pxb_register_types) diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index 379b6e1a45..dedf277627 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -93,6 +93,7 @@ #define PCI_DEVICE_ID_REDHAT_PCIE_HOST 0x0008 #define PCI_DEVICE_ID_REDHAT_PXB 0x0009 #define PCI_DEVICE_ID_REDHAT_BRIDGE_SEAT 0x000a +#define PCI_DEVICE_ID_REDHAT_PXB_PCIE 0x000b #define PCI_DEVICE_ID_REDHAT_QXL 0x0100 #define FMT_PCIBUS PRIx64 From 81ed6482a3478685c4fd27d766a92c4e0f8b34d1 Mon Sep 17 00:00:00 2001 From: Marcel Apfelbaum Date: Thu, 26 Nov 2015 18:00:28 +0200 Subject: [PATCH 10/55] hw/i386: extend pxb query for all PC machines Add bus property to PC machines and use it when looking for primary PCI root bus (bus 0). Signed-off-by: Marcel Apfelbaum Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Acked-by: Eduardo Habkost --- hw/i386/acpi-build.c | 3 +-- hw/i386/pc.c | 2 +- hw/i386/pc_piix.c | 1 + hw/i386/pc_q35.c | 1 + include/hw/i386/pc.h | 1 + 5 files changed, 5 insertions(+), 3 deletions(-) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index eb156cb116..a60436b5bc 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -950,8 +950,7 @@ build_ssdt(GArray *table_data, GArray *linker, /* Reserve space for header */ acpi_data_push(ssdt->buf, sizeof(AcpiTableHeader)); - /* Extra PCI root buses are implemented only for i440fx */ - bus = find_i440fx(); + bus = PC_MACHINE(machine)->bus; if (bus) { QLIST_FOREACH(bus, &bus->child, sibling) { uint8_t bus_num = pci_bus_num(bus); diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 00b59cdae0..48aa37a138 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1167,7 +1167,7 @@ void pc_guest_info_machine_done(Notifier *notifier, void *data) PcGuestInfoState *guest_info_state = container_of(notifier, PcGuestInfoState, machine_done); - PCIBus *bus = find_i440fx(); + PCIBus *bus = PC_MACHINE(qdev_get_machine())->bus; if (bus) { int extra_hosts = 0; diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 7d847b247d..9654628f09 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -185,6 +185,7 @@ static void pc_init1(MachineState *machine, pcms->below_4g_mem_size, pcms->above_4g_mem_size, pci_memory, ram_memory); + pcms->bus = pci_bus; } else { pci_bus = NULL; i440fx_state = NULL; diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 6aaa0eedb0..b87e7ec19c 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -181,6 +181,7 @@ static void pc_q35_init(MachineState *machine) qdev_init_nofail(DEVICE(q35_host)); phb = PCI_HOST_BRIDGE(q35_host); host_bus = phb->bus; + pcms->bus = phb->bus; /* create ISA bus */ lpc = pci_create_simple_multifunction(host_bus, PCI_DEVFN(ICH9_LPC_DEV, ICH9_LPC_FUNC), true, diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index beb44ca23c..bbebf1536a 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -51,6 +51,7 @@ struct PCMachineState { OnOffAuto vmport; OnOffAuto smm; ram_addr_t below_4g_mem_size, above_4g_mem_size; + PCIBus *bus; }; #define PC_MACHINE_ACPI_DEVICE_PROP "acpi-device" From 34be1e7c92a9e8942a23e7544356af634d233d83 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Fri, 11 Dec 2015 16:42:20 -0200 Subject: [PATCH 11/55] q35: Remove MCHPCIState.guest_info field The field is not used for anything. Signed-off-by: Eduardo Habkost Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Marcel Apfelbaum --- hw/i386/pc_q35.c | 1 - include/hw/pci-host/q35.h | 1 - 2 files changed, 2 deletions(-) diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index b87e7ec19c..342e9cfca4 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -176,7 +176,6 @@ static void pc_q35_init(MachineState *machine) q35_host->mch.address_space_io = get_system_io(); q35_host->mch.below_4g_mem_size = pcms->below_4g_mem_size; q35_host->mch.above_4g_mem_size = pcms->above_4g_mem_size; - q35_host->mch.guest_info = guest_info; /* pci */ qdev_init_nofail(DEVICE(q35_host)); phb = PCI_HOST_BRIDGE(q35_host); diff --git a/include/hw/pci-host/q35.h b/include/hw/pci-host/q35.h index dbe6dc05b5..c5c073ddea 100644 --- a/include/hw/pci-host/q35.h +++ b/include/hw/pci-host/q35.h @@ -59,7 +59,6 @@ typedef struct MCHPCIState { ram_addr_t below_4g_mem_size; ram_addr_t above_4g_mem_size; uint64_t pci_hole64_size; - PcGuestInfo *guest_info; uint32_t short_root_bus; IntelIOMMUState *iommu; } MCHPCIState; From 13fc834308668097d6fd09c85ae3ecad06360a64 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Fri, 11 Dec 2015 16:42:21 -0200 Subject: [PATCH 12/55] pc: Group and document related PCMachineState/PCMachineclass fields Group related PCMachineState and PCMachineClass fields into sections, and move existing field descriptions to doc comments. Signed-off-by: Eduardo Habkost Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Marcel Apfelbaum --- include/hw/i386/pc.h | 48 +++++++++++++++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index bbebf1536a..e5eb15c35e 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -42,16 +42,22 @@ struct PCMachineState { MachineState parent_obj; /* */ + + /* State for other subsystems/APIs: */ MemoryHotplugState hotplug_memory; + /* Pointers to devices and objects: */ HotplugHandler *acpi_dev; ISADevice *rtc; + PCIBus *bus; + /* Configuration options: */ uint64_t max_ram_below_4g; OnOffAuto vmport; OnOffAuto smm; + + /* RAM information (sizes, addresses, configuration): */ ram_addr_t below_4g_mem_size, above_4g_mem_size; - PCIBus *bus; }; #define PC_MACHINE_ACPI_DEVICE_PROP "acpi-device" @@ -62,38 +68,56 @@ struct PCMachineState { /** * PCMachineClass: + * + * Methods: + * * @get_hotplug_handler: pointer to parent class callback @get_hotplug_handler + * + * Compat fields: + * * @enforce_aligned_dimm: check that DIMM's address/size is aligned by * backend's alignment value if provided + * @acpi_data_size: Size of the chunk of memory at the top of RAM + * for the BIOS ACPI tables and other BIOS + * datastructures. + * @gigabyte_align: Make sure that guest addresses aligned at + * 1Gbyte boundaries get mapped to host + * addresses aligned at 1Gbyte boundaries. This + * way we can use 1GByte pages in the host. + * */ struct PCMachineClass { /*< private >*/ MachineClass parent_class; /*< public >*/ - bool broken_reserved_end; + + /* Methods: */ HotplugHandler *(*get_hotplug_handler)(MachineState *machine, DeviceState *dev); + /* Device configuration: */ bool pci_enabled; + bool kvmclock_enabled; + + /* Compat options: */ + + /* ACPI compat: */ bool has_acpi_build; bool rsdp_in_ram; + int legacy_acpi_table_size; + unsigned acpi_data_size; + + /* SMBIOS compat: */ bool smbios_defaults; bool smbios_legacy_mode; bool smbios_uuid_encoded; - /* Make sure that guest addresses aligned at 1Gbyte boundaries get - * mapped to host addresses aligned at 1Gbyte boundaries. This way - * we can use 1GByte pages in the host. - */ + + /* RAM / address space compat: */ bool gigabyte_align; bool has_reserved_memory; - bool kvmclock_enabled; - int legacy_acpi_table_size; - /* Leave a chunk of memory at the top of RAM for the BIOS ACPI tables - * and other BIOS datastructures. - */ - unsigned acpi_data_size; bool enforce_aligned_dimm; + bool broken_reserved_end; }; #define TYPE_PC_MACHINE "generic-pc-machine" From 23076bb34b049f5908fefae19266d3f25f55fd3e Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Thu, 17 Dec 2015 12:50:04 -0600 Subject: [PATCH 13/55] Add a base IPMI interface Add the basic IPMI types and infrastructure to QEMU. Low-level interfaces and simulation interfaces will register with this; it's kind of the go-between to tie them together. Signed-off-by: Corey Minyard Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- default-configs/i386-softmmu.mak | 1 + default-configs/x86_64-softmmu.mak | 1 + hw/Makefile.objs | 1 + hw/ipmi/Makefile.objs | 1 + hw/ipmi/ipmi.c | 125 ++++++++++++++++++++ include/hw/ipmi/ipmi.h | 178 +++++++++++++++++++++++++++++ qemu-doc.texi | 2 + 7 files changed, 309 insertions(+) create mode 100644 hw/ipmi/Makefile.objs create mode 100644 hw/ipmi/ipmi.c create mode 100644 include/hw/ipmi/ipmi.h diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak index 57ccff33e3..7435764869 100644 --- a/default-configs/i386-softmmu.mak +++ b/default-configs/i386-softmmu.mak @@ -9,6 +9,7 @@ CONFIG_VGA_CIRRUS=y CONFIG_VMWARE_VGA=y CONFIG_VIRTIO_VGA=y CONFIG_VMMOUSE=y +CONFIG_IPMI=y CONFIG_SERIAL=y CONFIG_PARALLEL=y CONFIG_I8254=y diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak index 30f5e751e6..df9d817996 100644 --- a/default-configs/x86_64-softmmu.mak +++ b/default-configs/x86_64-softmmu.mak @@ -9,6 +9,7 @@ CONFIG_VGA_CIRRUS=y CONFIG_VMWARE_VGA=y CONFIG_VIRTIO_VGA=y CONFIG_VMMOUSE=y +CONFIG_IPMI=y CONFIG_SERIAL=y CONFIG_PARALLEL=y CONFIG_I8254=y diff --git a/hw/Makefile.objs b/hw/Makefile.objs index 7e7c241104..4a07ed4344 100644 --- a/hw/Makefile.objs +++ b/hw/Makefile.objs @@ -13,6 +13,7 @@ devices-dirs-$(CONFIG_SOFTMMU) += ide/ devices-dirs-$(CONFIG_SOFTMMU) += input/ devices-dirs-$(CONFIG_SOFTMMU) += intc/ devices-dirs-$(CONFIG_IPACK) += ipack/ +devices-dirs-$(CONFIG_IPMI) += ipmi/ devices-dirs-$(CONFIG_SOFTMMU) += isa/ devices-dirs-$(CONFIG_SOFTMMU) += misc/ devices-dirs-$(CONFIG_SOFTMMU) += net/ diff --git a/hw/ipmi/Makefile.objs b/hw/ipmi/Makefile.objs new file mode 100644 index 0000000000..65bde11dd0 --- /dev/null +++ b/hw/ipmi/Makefile.objs @@ -0,0 +1 @@ +common-obj-$(CONFIG_IPMI) += ipmi.o diff --git a/hw/ipmi/ipmi.c b/hw/ipmi/ipmi.c new file mode 100644 index 0000000000..7d174692aa --- /dev/null +++ b/hw/ipmi/ipmi.c @@ -0,0 +1,125 @@ +/* + * QEMU IPMI emulation + * + * Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "hw/hw.h" +#include "hw/ipmi/ipmi.h" +#include "sysemu/sysemu.h" +#include "qmp-commands.h" +#include "qom/object_interfaces.h" +#include "qapi/visitor.h" + +static int ipmi_do_hw_op(IPMIInterface *s, enum ipmi_op op, int checkonly) +{ + switch (op) { + case IPMI_RESET_CHASSIS: + if (checkonly) { + return 0; + } + qemu_system_reset_request(); + return 0; + + case IPMI_POWEROFF_CHASSIS: + if (checkonly) { + return 0; + } + qemu_system_powerdown_request(); + return 0; + + case IPMI_SEND_NMI: + if (checkonly) { + return 0; + } + qemu_mutex_lock_iothread(); + qmp_inject_nmi(NULL); + qemu_mutex_unlock_iothread(); + return 0; + + case IPMI_POWERCYCLE_CHASSIS: + case IPMI_PULSE_DIAG_IRQ: + case IPMI_SHUTDOWN_VIA_ACPI_OVERTEMP: + case IPMI_POWERON_CHASSIS: + default: + return IPMI_CC_COMMAND_NOT_SUPPORTED; + } +} + +static void ipmi_interface_class_init(ObjectClass *class, void *data) +{ + IPMIInterfaceClass *ik = IPMI_INTERFACE_CLASS(class); + + ik->do_hw_op = ipmi_do_hw_op; +} + +static TypeInfo ipmi_interface_type_info = { + .name = TYPE_IPMI_INTERFACE, + .parent = TYPE_INTERFACE, + .class_size = sizeof(IPMIInterfaceClass), + .class_init = ipmi_interface_class_init, +}; + +static void isa_ipmi_bmc_check(Object *obj, const char *name, + Object *val, Error **errp) +{ + IPMIBmc *bmc = IPMI_BMC(val); + + if (bmc->intf) + error_setg(errp, "BMC object is already in use"); +} + +void ipmi_bmc_find_and_link(Object *obj, Object **bmc) +{ + object_property_add_link(obj, "bmc", TYPE_IPMI_BMC, bmc, + isa_ipmi_bmc_check, + OBJ_PROP_LINK_UNREF_ON_RELEASE, + &error_abort); +} + +static Property ipmi_bmc_properties[] = { + DEFINE_PROP_UINT8("slave_addr", IPMIBmc, slave_addr, 0x20), + DEFINE_PROP_END_OF_LIST(), +}; + +static void bmc_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->props = ipmi_bmc_properties; +} + +static TypeInfo ipmi_bmc_type_info = { + .name = TYPE_IPMI_BMC, + .parent = TYPE_DEVICE, + .instance_size = sizeof(IPMIBmc), + .abstract = true, + .class_size = sizeof(IPMIBmcClass), + .class_init = bmc_class_init, +}; + +static void ipmi_register_types(void) +{ + type_register_static(&ipmi_interface_type_info); + type_register_static(&ipmi_bmc_type_info); +} + +type_init(ipmi_register_types) diff --git a/include/hw/ipmi/ipmi.h b/include/hw/ipmi/ipmi.h new file mode 100644 index 0000000000..e4f773847d --- /dev/null +++ b/include/hw/ipmi/ipmi.h @@ -0,0 +1,178 @@ +/* + * IPMI base class + * + * Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef HW_IPMI_H +#define HW_IPMI_H + +#include "exec/memory.h" +#include "qemu-common.h" +#include "hw/qdev.h" + +#define MAX_IPMI_MSG_SIZE 300 + +enum ipmi_op { + IPMI_RESET_CHASSIS, + IPMI_POWEROFF_CHASSIS, + IPMI_POWERON_CHASSIS, + IPMI_POWERCYCLE_CHASSIS, + IPMI_PULSE_DIAG_IRQ, + IPMI_SHUTDOWN_VIA_ACPI_OVERTEMP, + IPMI_SEND_NMI +}; + +#define IPMI_CC_INVALID_CMD 0xc1 +#define IPMI_CC_COMMAND_INVALID_FOR_LUN 0xc2 +#define IPMI_CC_TIMEOUT 0xc3 +#define IPMI_CC_OUT_OF_SPACE 0xc4 +#define IPMI_CC_INVALID_RESERVATION 0xc5 +#define IPMI_CC_REQUEST_DATA_TRUNCATED 0xc6 +#define IPMI_CC_REQUEST_DATA_LENGTH_INVALID 0xc7 +#define IPMI_CC_PARM_OUT_OF_RANGE 0xc9 +#define IPMI_CC_CANNOT_RETURN_REQ_NUM_BYTES 0xca +#define IPMI_CC_REQ_ENTRY_NOT_PRESENT 0xcb +#define IPMI_CC_INVALID_DATA_FIELD 0xcc +#define IPMI_CC_BMC_INIT_IN_PROGRESS 0xd2 +#define IPMI_CC_COMMAND_NOT_SUPPORTED 0xd5 + +#define IPMI_NETFN_APP 0x06 + +#define IPMI_DEBUG 1 + +/* Specified in the SMBIOS spec. */ +#define IPMI_SMBIOS_KCS 0x01 +#define IPMI_SMBIOS_SMIC 0x02 +#define IPMI_SMBIOS_BT 0x03 +#define IPMI_SMBIOS_SSIF 0x04 + +/* IPMI Interface types (KCS, SMIC, BT) are prefixed with this */ +#define TYPE_IPMI_INTERFACE_PREFIX "ipmi-interface-" + +/* + * An IPMI Interface, the interface for talking between the target + * and the BMC. + */ +#define TYPE_IPMI_INTERFACE "ipmi-interface" +#define IPMI_INTERFACE(obj) \ + INTERFACE_CHECK(IPMIInterface, (obj), TYPE_IPMI_INTERFACE) +#define IPMI_INTERFACE_CLASS(class) \ + OBJECT_CLASS_CHECK(IPMIInterfaceClass, (class), TYPE_IPMI_INTERFACE) +#define IPMI_INTERFACE_GET_CLASS(class) \ + OBJECT_GET_CLASS(IPMIInterfaceClass, (class), TYPE_IPMI_INTERFACE) + +typedef struct IPMIInterface { + Object parent; +} IPMIInterface; + +typedef struct IPMIInterfaceClass { + InterfaceClass parent; + + void (*init)(struct IPMIInterface *s, Error **errp); + + /* + * Perform various operations on the hardware. If checkonly is + * true, it will return if the operation can be performed, but it + * will not do the operation. + */ + int (*do_hw_op)(struct IPMIInterface *s, enum ipmi_op op, int checkonly); + + /* + * Enable/disable irqs on the interface when the BMC requests this. + */ + void (*set_irq_enable)(struct IPMIInterface *s, int val); + + /* + * Handle an event that occurred on the interface, generally the. + * target writing to a register. + */ + void (*handle_if_event)(struct IPMIInterface *s); + + /* + * The interfaces use this to perform certain ops + */ + void (*set_atn)(struct IPMIInterface *s, int val, int irq); + + /* + * Got an IPMI warm/cold reset. + */ + void (*reset)(struct IPMIInterface *s, bool is_cold); + + /* + * Handle a response from the bmc. + */ + void (*handle_rsp)(struct IPMIInterface *s, uint8_t msg_id, + unsigned char *rsp, unsigned int rsp_len); + + /* + * Set by the owner to hold the backend data for the interface. + */ + void *(*get_backend_data)(struct IPMIInterface *s); +} IPMIInterfaceClass; + +/* + * Define a BMC simulator (or perhaps a connection to a real BMC) + */ +#define TYPE_IPMI_BMC "ipmi-bmc" +#define IPMI_BMC(obj) \ + OBJECT_CHECK(IPMIBmc, (obj), TYPE_IPMI_BMC) +#define IPMI_BMC_CLASS(obj_class) \ + OBJECT_CLASS_CHECK(IPMIBmcClass, (obj_class), TYPE_IPMI_BMC) +#define IPMI_BMC_GET_CLASS(obj) \ + OBJECT_GET_CLASS(IPMIBmcClass, (obj), TYPE_IPMI_BMC) + +typedef struct IPMIBmc { + DeviceState parent; + + uint8_t slave_addr; + + IPMIInterface *intf; +} IPMIBmc; + +typedef struct IPMIBmcClass { + DeviceClass parent; + + /* Called when the system resets to report to the bmc. */ + void (*handle_reset)(struct IPMIBmc *s); + + /* + * Handle a command to the bmc. + */ + void (*handle_command)(struct IPMIBmc *s, + uint8_t *cmd, unsigned int cmd_len, + unsigned int max_cmd_len, + uint8_t msg_id); +} IPMIBmcClass; + +/* + * Add a link property to obj that points to a BMC. + */ +void ipmi_bmc_find_and_link(Object *obj, Object **bmc); + +#ifdef IPMI_DEBUG +#define ipmi_debug(fs, ...) \ + fprintf(stderr, "IPMI (%s): " fs, __func__, ##__VA_ARGS__) +#else +#define ipmi_debug(fs, ...) +#endif + +#endif diff --git a/qemu-doc.texi b/qemu-doc.texi index ffc3e50abb..7bc388231f 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -195,6 +195,8 @@ PCI and ISA network adapters @item Serial ports @item +IPMI BMC, either and internal or external one +@item Creative SoundBlaster 16 sound card @item ENSONIQ AudioPCI ES1370 sound card From 8bfffbccad2b7a9836caee359bd5055ab6989ac5 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Thu, 17 Dec 2015 12:50:05 -0600 Subject: [PATCH 14/55] ipmi: Add a local BMC simulation This provides a minimal local BMC, basically enough to comply with the spec and provide a complete watchdog timer (including a sensor, SDR, and event). Signed-off-by: Corey Minyard Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- default-configs/i386-softmmu.mak | 1 + default-configs/x86_64-softmmu.mak | 1 + hw/ipmi/Makefile.objs | 1 + hw/ipmi/ipmi_bmc_sim.c | 1726 ++++++++++++++++++++++++++++ 4 files changed, 1729 insertions(+) create mode 100644 hw/ipmi/ipmi_bmc_sim.c diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak index 7435764869..3f01ed1583 100644 --- a/default-configs/i386-softmmu.mak +++ b/default-configs/i386-softmmu.mak @@ -10,6 +10,7 @@ CONFIG_VMWARE_VGA=y CONFIG_VIRTIO_VGA=y CONFIG_VMMOUSE=y CONFIG_IPMI=y +CONFIG_IPMI_LOCAL=y CONFIG_SERIAL=y CONFIG_PARALLEL=y CONFIG_I8254=y diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak index df9d817996..40685e4aa8 100644 --- a/default-configs/x86_64-softmmu.mak +++ b/default-configs/x86_64-softmmu.mak @@ -10,6 +10,7 @@ CONFIG_VMWARE_VGA=y CONFIG_VIRTIO_VGA=y CONFIG_VMMOUSE=y CONFIG_IPMI=y +CONFIG_IPMI_LOCAL=y CONFIG_SERIAL=y CONFIG_PARALLEL=y CONFIG_I8254=y diff --git a/hw/ipmi/Makefile.objs b/hw/ipmi/Makefile.objs index 65bde11dd0..875271ca56 100644 --- a/hw/ipmi/Makefile.objs +++ b/hw/ipmi/Makefile.objs @@ -1 +1,2 @@ common-obj-$(CONFIG_IPMI) += ipmi.o +common-obj-$(CONFIG_IPMI_LOCAL) += ipmi_bmc_sim.o diff --git a/hw/ipmi/ipmi_bmc_sim.c b/hw/ipmi/ipmi_bmc_sim.c new file mode 100644 index 0000000000..af01e1a813 --- /dev/null +++ b/hw/ipmi/ipmi_bmc_sim.c @@ -0,0 +1,1726 @@ +/* + * IPMI BMC emulation + * + * Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include +#include "qemu/timer.h" +#include "hw/ipmi/ipmi.h" +#include "qemu/error-report.h" + +#define IPMI_NETFN_CHASSIS 0x00 +#define IPMI_NETFN_CHASSIS_MAXCMD 0x03 + +#define IPMI_CMD_GET_CHASSIS_CAPABILITIES 0x00 +#define IPMI_CMD_GET_CHASSIS_STATUS 0x01 +#define IPMI_CMD_CHASSIS_CONTROL 0x02 + +#define IPMI_NETFN_SENSOR_EVENT 0x04 +#define IPMI_NETFN_SENSOR_EVENT_MAXCMD 0x2e + +#define IPMI_CMD_SET_SENSOR_EVT_ENABLE 0x28 +#define IPMI_CMD_GET_SENSOR_EVT_ENABLE 0x29 +#define IPMI_CMD_REARM_SENSOR_EVTS 0x2a +#define IPMI_CMD_GET_SENSOR_EVT_STATUS 0x2b +#define IPMI_CMD_GET_SENSOR_READING 0x2d + +/* #define IPMI_NETFN_APP 0x06 In ipmi.h */ +#define IPMI_NETFN_APP_MAXCMD 0x36 + +#define IPMI_CMD_GET_DEVICE_ID 0x01 +#define IPMI_CMD_COLD_RESET 0x02 +#define IPMI_CMD_WARM_RESET 0x03 +#define IPMI_CMD_RESET_WATCHDOG_TIMER 0x22 +#define IPMI_CMD_SET_WATCHDOG_TIMER 0x24 +#define IPMI_CMD_GET_WATCHDOG_TIMER 0x25 +#define IPMI_CMD_SET_BMC_GLOBAL_ENABLES 0x2e +#define IPMI_CMD_GET_BMC_GLOBAL_ENABLES 0x2f +#define IPMI_CMD_CLR_MSG_FLAGS 0x30 +#define IPMI_CMD_GET_MSG_FLAGS 0x31 +#define IPMI_CMD_GET_MSG 0x33 +#define IPMI_CMD_SEND_MSG 0x34 +#define IPMI_CMD_READ_EVT_MSG_BUF 0x35 + +#define IPMI_NETFN_STORAGE 0x0a +#define IPMI_NETFN_STORAGE_MAXCMD 0x4a + +#define IPMI_CMD_GET_SDR_REP_INFO 0x20 +#define IPMI_CMD_GET_SDR_REP_ALLOC_INFO 0x21 +#define IPMI_CMD_RESERVE_SDR_REP 0x22 +#define IPMI_CMD_GET_SDR 0x23 +#define IPMI_CMD_ADD_SDR 0x24 +#define IPMI_CMD_PARTIAL_ADD_SDR 0x25 +#define IPMI_CMD_DELETE_SDR 0x26 +#define IPMI_CMD_CLEAR_SDR_REP 0x27 +#define IPMI_CMD_GET_SDR_REP_TIME 0x28 +#define IPMI_CMD_SET_SDR_REP_TIME 0x29 +#define IPMI_CMD_ENTER_SDR_REP_UPD_MODE 0x2A +#define IPMI_CMD_EXIT_SDR_REP_UPD_MODE 0x2B +#define IPMI_CMD_RUN_INIT_AGENT 0x2C +#define IPMI_CMD_GET_SEL_INFO 0x40 +#define IPMI_CMD_GET_SEL_ALLOC_INFO 0x41 +#define IPMI_CMD_RESERVE_SEL 0x42 +#define IPMI_CMD_GET_SEL_ENTRY 0x43 +#define IPMI_CMD_ADD_SEL_ENTRY 0x44 +#define IPMI_CMD_PARTIAL_ADD_SEL_ENTRY 0x45 +#define IPMI_CMD_DELETE_SEL_ENTRY 0x46 +#define IPMI_CMD_CLEAR_SEL 0x47 +#define IPMI_CMD_GET_SEL_TIME 0x48 +#define IPMI_CMD_SET_SEL_TIME 0x49 + + +/* Same as a timespec struct. */ +struct ipmi_time { + long tv_sec; + long tv_nsec; +}; + +#define MAX_SEL_SIZE 128 + +typedef struct IPMISel { + uint8_t sel[MAX_SEL_SIZE][16]; + unsigned int next_free; + long time_offset; + uint16_t reservation; + uint8_t last_addition[4]; + uint8_t last_clear[4]; + uint8_t overflow; +} IPMISel; + +#define MAX_SDR_SIZE 16384 + +typedef struct IPMISdr { + uint8_t sdr[MAX_SDR_SIZE]; + unsigned int next_free; + uint16_t next_rec_id; + uint16_t reservation; + uint8_t last_addition[4]; + uint8_t last_clear[4]; + uint8_t overflow; +} IPMISdr; + +typedef struct IPMISensor { + uint8_t status; + uint8_t reading; + uint16_t states_suppt; + uint16_t assert_suppt; + uint16_t deassert_suppt; + uint16_t states; + uint16_t assert_states; + uint16_t deassert_states; + uint16_t assert_enable; + uint16_t deassert_enable; + uint8_t sensor_type; + uint8_t evt_reading_type_code; +} IPMISensor; +#define IPMI_SENSOR_GET_PRESENT(s) ((s)->status & 0x01) +#define IPMI_SENSOR_SET_PRESENT(s, v) ((s)->status = (s->status & ~0x01) | \ + !!(v)) +#define IPMI_SENSOR_GET_SCAN_ON(s) ((s)->status & 0x40) +#define IPMI_SENSOR_SET_SCAN_ON(s, v) ((s)->status = (s->status & ~0x40) | \ + ((!!(v)) << 6)) +#define IPMI_SENSOR_GET_EVENTS_ON(s) ((s)->status & 0x80) +#define IPMI_SENSOR_SET_EVENTS_ON(s, v) ((s)->status = (s->status & ~0x80) | \ + ((!!(v)) << 7)) +#define IPMI_SENSOR_GET_RET_STATUS(s) ((s)->status & 0xc0) +#define IPMI_SENSOR_SET_RET_STATUS(s, v) ((s)->status = (s->status & ~0xc0) | \ + (v & 0xc0)) +#define IPMI_SENSOR_IS_DISCRETE(s) ((s)->evt_reading_type_code != 1) + +#define MAX_SENSORS 20 +#define IPMI_WATCHDOG_SENSOR 0 + +typedef struct IPMIBmcSim IPMIBmcSim; + +#define MAX_NETFNS 64 +typedef void (*IPMICmdHandler)(IPMIBmcSim *s, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len); +typedef struct IPMINetfn { + unsigned int cmd_nums; + const IPMICmdHandler *cmd_handlers; +} IPMINetfn; + +typedef struct IPMIRcvBufEntry { + QTAILQ_ENTRY(IPMIRcvBufEntry) entry; + uint8_t len; + uint8_t buf[MAX_IPMI_MSG_SIZE]; +} IPMIRcvBufEntry; + +#define TYPE_IPMI_BMC_SIMULATOR "ipmi-bmc-sim" +#define IPMI_BMC_SIMULATOR(obj) OBJECT_CHECK(IPMIBmcSim, (obj), \ + TYPE_IPMI_BMC_SIMULATOR) +struct IPMIBmcSim { + IPMIBmc parent; + + QEMUTimer *timer; + + uint8_t bmc_global_enables; + uint8_t msg_flags; + + bool watchdog_initialized; + uint8_t watchdog_use; + uint8_t watchdog_action; + uint8_t watchdog_pretimeout; /* In seconds */ + bool watchdog_expired; + uint16_t watchdog_timeout; /* in 100's of milliseconds */ + + bool watchdog_running; + bool watchdog_preaction_ran; + int64_t watchdog_expiry; + + uint8_t device_id; + uint8_t ipmi_version; + uint8_t device_rev; + uint8_t fwrev1; + uint8_t fwrev2; + uint8_t mfg_id[3]; + uint8_t product_id[2]; + + IPMISel sel; + IPMISdr sdr; + IPMISensor sensors[MAX_SENSORS]; + + /* Odd netfns are for responses, so we only need the even ones. */ + const IPMINetfn *netfns[MAX_NETFNS / 2]; + + QemuMutex lock; + /* We allow one event in the buffer */ + uint8_t evtbuf[16]; + + QTAILQ_HEAD(, IPMIRcvBufEntry) rcvbufs; +}; + +#define IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK (1 << 3) +#define IPMI_BMC_MSG_FLAG_EVT_BUF_FULL (1 << 1) +#define IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE (1 << 0) +#define IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK_SET(s) \ + (IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK & (s)->msg_flags) +#define IPMI_BMC_MSG_FLAG_EVT_BUF_FULL_SET(s) \ + (IPMI_BMC_MSG_FLAG_EVT_BUF_FULL & (s)->msg_flags) +#define IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE_SET(s) \ + (IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE & (s)->msg_flags) + +#define IPMI_BMC_RCV_MSG_QUEUE_INT_BIT 0 +#define IPMI_BMC_EVBUF_FULL_INT_BIT 1 +#define IPMI_BMC_EVENT_MSG_BUF_BIT 2 +#define IPMI_BMC_EVENT_LOG_BIT 3 +#define IPMI_BMC_MSG_INTS_ON(s) ((s)->bmc_global_enables & \ + (1 << IPMI_BMC_RCV_MSG_QUEUE_INT_BIT)) +#define IPMI_BMC_EVBUF_FULL_INT_ENABLED(s) ((s)->bmc_global_enables & \ + (1 << IPMI_BMC_EVBUF_FULL_INT_BIT)) +#define IPMI_BMC_EVENT_LOG_ENABLED(s) ((s)->bmc_global_enables & \ + (1 << IPMI_BMC_EVENT_LOG_BIT)) +#define IPMI_BMC_EVENT_MSG_BUF_ENABLED(s) ((s)->bmc_global_enables & \ + (1 << IPMI_BMC_EVENT_MSG_BUF_BIT)) + +#define IPMI_BMC_WATCHDOG_USE_MASK 0xc7 +#define IPMI_BMC_WATCHDOG_ACTION_MASK 0x77 +#define IPMI_BMC_WATCHDOG_GET_USE(s) ((s)->watchdog_use & 0x7) +#define IPMI_BMC_WATCHDOG_GET_DONT_LOG(s) (((s)->watchdog_use >> 7) & 0x1) +#define IPMI_BMC_WATCHDOG_GET_DONT_STOP(s) (((s)->watchdog_use >> 6) & 0x1) +#define IPMI_BMC_WATCHDOG_GET_PRE_ACTION(s) (((s)->watchdog_action >> 4) & 0x7) +#define IPMI_BMC_WATCHDOG_PRE_NONE 0 +#define IPMI_BMC_WATCHDOG_PRE_SMI 1 +#define IPMI_BMC_WATCHDOG_PRE_NMI 2 +#define IPMI_BMC_WATCHDOG_PRE_MSG_INT 3 +#define IPMI_BMC_WATCHDOG_GET_ACTION(s) ((s)->watchdog_action & 0x7) +#define IPMI_BMC_WATCHDOG_ACTION_NONE 0 +#define IPMI_BMC_WATCHDOG_ACTION_RESET 1 +#define IPMI_BMC_WATCHDOG_ACTION_POWER_DOWN 2 +#define IPMI_BMC_WATCHDOG_ACTION_POWER_CYCLE 3 + + +/* Add a byte to the response. */ +#define IPMI_ADD_RSP_DATA(b) \ + do { \ + if (*rsp_len >= max_rsp_len) { \ + rsp[2] = IPMI_CC_REQUEST_DATA_TRUNCATED; \ + goto out; \ + } \ + rsp[(*rsp_len)++] = (b); \ + } while (0) + +/* Verify that the received command is a certain length. */ +#define IPMI_CHECK_CMD_LEN(l) \ + if (cmd_len < l) { \ + rsp[2] = IPMI_CC_REQUEST_DATA_LENGTH_INVALID; \ + goto out; \ + } + +/* Check that the reservation in the command is valid. */ +#define IPMI_CHECK_RESERVATION(off, r) \ + do { \ + if ((cmd[off] | (cmd[off + 1] << 8)) != r) { \ + rsp[2] = IPMI_CC_INVALID_RESERVATION; \ + goto out; \ + } \ + } while (0) + + +static void ipmi_sim_handle_timeout(IPMIBmcSim *ibs); + +static void ipmi_gettime(struct ipmi_time *time) +{ + int64_t stime; + + stime = qemu_clock_get_ns(QEMU_CLOCK_HOST); + time->tv_sec = stime / 1000000000LL; + time->tv_nsec = stime % 1000000000LL; +} + +static int64_t ipmi_getmonotime(void) +{ + return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); +} + +static void ipmi_timeout(void *opaque) +{ + IPMIBmcSim *ibs = opaque; + + ipmi_sim_handle_timeout(ibs); +} + +static void set_timestamp(IPMIBmcSim *ibs, uint8_t *ts) +{ + unsigned int val; + struct ipmi_time now; + + ipmi_gettime(&now); + val = now.tv_sec + ibs->sel.time_offset; + ts[0] = val & 0xff; + ts[1] = (val >> 8) & 0xff; + ts[2] = (val >> 16) & 0xff; + ts[3] = (val >> 24) & 0xff; +} + +static void sdr_inc_reservation(IPMISdr *sdr) +{ + sdr->reservation++; + if (sdr->reservation == 0) { + sdr->reservation = 1; + } +} + +static int sdr_add_entry(IPMIBmcSim *ibs, const uint8_t *entry, + unsigned int len, uint16_t *recid) +{ + if ((len < 5) || (len > 255)) { + return 1; + } + + if (entry[4] != len - 5) { + return 1; + } + + if (ibs->sdr.next_free + len > MAX_SDR_SIZE) { + ibs->sdr.overflow = 1; + return 1; + } + + memcpy(ibs->sdr.sdr + ibs->sdr.next_free, entry, len); + ibs->sdr.sdr[ibs->sdr.next_free] = ibs->sdr.next_rec_id & 0xff; + ibs->sdr.sdr[ibs->sdr.next_free+1] = (ibs->sdr.next_rec_id >> 8) & 0xff; + ibs->sdr.sdr[ibs->sdr.next_free+2] = 0x51; /* Conform to IPMI 1.5 spec */ + + if (recid) { + *recid = ibs->sdr.next_rec_id; + } + ibs->sdr.next_rec_id++; + set_timestamp(ibs, ibs->sdr.last_addition); + ibs->sdr.next_free += len; + sdr_inc_reservation(&ibs->sdr); + return 0; +} + +static int sdr_find_entry(IPMISdr *sdr, uint16_t recid, + unsigned int *retpos, uint16_t *nextrec) +{ + unsigned int pos = *retpos; + + while (pos < sdr->next_free) { + uint16_t trec = sdr->sdr[pos] | (sdr->sdr[pos + 1] << 8); + unsigned int nextpos = pos + sdr->sdr[pos + 4]; + + if (trec == recid) { + if (nextrec) { + if (nextpos >= sdr->next_free) { + *nextrec = 0xffff; + } else { + *nextrec = (sdr->sdr[nextpos] | + (sdr->sdr[nextpos + 1] << 8)); + } + } + *retpos = pos; + return 0; + } + pos = nextpos; + } + return 1; +} + +static void sel_inc_reservation(IPMISel *sel) +{ + sel->reservation++; + if (sel->reservation == 0) { + sel->reservation = 1; + } +} + +/* Returns 1 if the SEL is full and can't hold the event. */ +static int sel_add_event(IPMIBmcSim *ibs, uint8_t *event) +{ + event[0] = 0xff; + event[1] = 0xff; + set_timestamp(ibs, event + 3); + if (ibs->sel.next_free == MAX_SEL_SIZE) { + ibs->sel.overflow = 1; + return 1; + } + event[0] = ibs->sel.next_free & 0xff; + event[1] = (ibs->sel.next_free >> 8) & 0xff; + memcpy(ibs->sel.last_addition, event + 3, 4); + memcpy(ibs->sel.sel[ibs->sel.next_free], event, 16); + ibs->sel.next_free++; + sel_inc_reservation(&ibs->sel); + return 0; +} + +static int attn_set(IPMIBmcSim *ibs) +{ + return IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE_SET(ibs) + || IPMI_BMC_MSG_FLAG_EVT_BUF_FULL_SET(ibs) + || IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK_SET(ibs); +} + +static int attn_irq_enabled(IPMIBmcSim *ibs) +{ + return (IPMI_BMC_MSG_INTS_ON(ibs) && IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE_SET(ibs)) + || (IPMI_BMC_EVBUF_FULL_INT_ENABLED(ibs) && + IPMI_BMC_MSG_FLAG_EVT_BUF_FULL_SET(ibs)); +} + +static void gen_event(IPMIBmcSim *ibs, unsigned int sens_num, uint8_t deassert, + uint8_t evd1, uint8_t evd2, uint8_t evd3) +{ + IPMIInterface *s = ibs->parent.intf; + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + uint8_t evt[16]; + IPMISensor *sens = ibs->sensors + sens_num; + + if (!IPMI_BMC_EVENT_MSG_BUF_ENABLED(ibs)) { + return; + } + if (!IPMI_SENSOR_GET_EVENTS_ON(sens)) { + return; + } + + evt[2] = 0x2; /* System event record */ + evt[7] = ibs->parent.slave_addr; + evt[8] = 0; + evt[9] = 0x04; /* Format version */ + evt[10] = sens->sensor_type; + evt[11] = sens_num; + evt[12] = sens->evt_reading_type_code | (!!deassert << 7); + evt[13] = evd1; + evt[14] = evd2; + evt[15] = evd3; + + if (IPMI_BMC_EVENT_LOG_ENABLED(ibs)) { + sel_add_event(ibs, evt); + } + + if (ibs->msg_flags & IPMI_BMC_MSG_FLAG_EVT_BUF_FULL) { + goto out; + } + + memcpy(ibs->evtbuf, evt, 16); + ibs->msg_flags |= IPMI_BMC_MSG_FLAG_EVT_BUF_FULL; + k->set_atn(s, 1, attn_irq_enabled(ibs)); + out: + return; +} + +static void sensor_set_discrete_bit(IPMIBmcSim *ibs, unsigned int sensor, + unsigned int bit, unsigned int val, + uint8_t evd1, uint8_t evd2, uint8_t evd3) +{ + IPMISensor *sens; + uint16_t mask; + + if (sensor >= MAX_SENSORS) { + return; + } + if (bit >= 16) { + return; + } + + mask = (1 << bit); + sens = ibs->sensors + sensor; + if (val) { + sens->states |= mask & sens->states_suppt; + if (sens->assert_states & mask) { + return; /* Already asserted */ + } + sens->assert_states |= mask & sens->assert_suppt; + if (sens->assert_enable & mask & sens->assert_states) { + /* Send an event on assert */ + gen_event(ibs, sensor, 0, evd1, evd2, evd3); + } + } else { + sens->states &= ~(mask & sens->states_suppt); + if (sens->deassert_states & mask) { + return; /* Already deasserted */ + } + sens->deassert_states |= mask & sens->deassert_suppt; + if (sens->deassert_enable & mask & sens->deassert_states) { + /* Send an event on deassert */ + gen_event(ibs, sensor, 1, evd1, evd2, evd3); + } + } +} + +static void ipmi_init_sensors_from_sdrs(IPMIBmcSim *s) +{ + unsigned int i, pos; + IPMISensor *sens; + + for (i = 0; i < MAX_SENSORS; i++) { + memset(s->sensors + i, 0, sizeof(*sens)); + } + + pos = 0; + for (i = 0; !sdr_find_entry(&s->sdr, i, &pos, NULL); i++) { + uint8_t *sdr = s->sdr.sdr + pos; + unsigned int len = sdr[4]; + + if (len < 20) { + continue; + } + if ((sdr[3] < 1) || (sdr[3] > 2)) { + continue; /* Not a sensor SDR we set from */ + } + + if (sdr[7] > MAX_SENSORS) { + continue; + } + sens = s->sensors + sdr[7]; + + IPMI_SENSOR_SET_PRESENT(sens, 1); + IPMI_SENSOR_SET_SCAN_ON(sens, (sdr[10] >> 6) & 1); + IPMI_SENSOR_SET_EVENTS_ON(sens, (sdr[10] >> 5) & 1); + sens->assert_suppt = sdr[14] | (sdr[15] << 8); + sens->deassert_suppt = sdr[16] | (sdr[17] << 8); + sens->states_suppt = sdr[18] | (sdr[19] << 8); + sens->sensor_type = sdr[12]; + sens->evt_reading_type_code = sdr[13] & 0x7f; + + /* Enable all the events that are supported. */ + sens->assert_enable = sens->assert_suppt; + sens->deassert_enable = sens->deassert_suppt; + } +} + +static int ipmi_register_netfn(IPMIBmcSim *s, unsigned int netfn, + const IPMINetfn *netfnd) +{ + if ((netfn & 1) || (netfn > MAX_NETFNS) || (s->netfns[netfn / 2])) { + return -1; + } + s->netfns[netfn / 2] = netfnd; + return 0; +} + +static void next_timeout(IPMIBmcSim *ibs) +{ + int64_t next; + if (ibs->watchdog_running) { + next = ibs->watchdog_expiry; + } else { + /* Wait a minute */ + next = ipmi_getmonotime() + 60 * 1000000000LL; + } + timer_mod_ns(ibs->timer, next); +} + +static void ipmi_sim_handle_command(IPMIBmc *b, + uint8_t *cmd, unsigned int cmd_len, + unsigned int max_cmd_len, + uint8_t msg_id) +{ + IPMIBmcSim *ibs = IPMI_BMC_SIMULATOR(b); + IPMIInterface *s = ibs->parent.intf; + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + unsigned int netfn; + uint8_t rsp[MAX_IPMI_MSG_SIZE]; + unsigned int rsp_len_holder = 0; + unsigned int *rsp_len = &rsp_len_holder; + unsigned int max_rsp_len = sizeof(rsp); + + /* Set up the response, set the low bit of NETFN. */ + /* Note that max_rsp_len must be at least 3 */ + IPMI_ADD_RSP_DATA(cmd[0] | 0x04); + IPMI_ADD_RSP_DATA(cmd[1]); + IPMI_ADD_RSP_DATA(0); /* Assume success */ + + /* If it's too short or it was truncated, return an error. */ + if (cmd_len < 2) { + rsp[2] = IPMI_CC_REQUEST_DATA_LENGTH_INVALID; + goto out; + } + if (cmd_len > max_cmd_len) { + rsp[2] = IPMI_CC_REQUEST_DATA_TRUNCATED; + goto out; + } + + if ((cmd[0] & 0x03) != 0) { + /* Only have stuff on LUN 0 */ + rsp[2] = IPMI_CC_COMMAND_INVALID_FOR_LUN; + goto out; + } + + netfn = cmd[0] >> 2; + + /* Odd netfns are not valid, make sure the command is registered */ + if ((netfn & 1) || !ibs->netfns[netfn / 2] || + (cmd[1] >= ibs->netfns[netfn / 2]->cmd_nums) || + (!ibs->netfns[netfn / 2]->cmd_handlers[cmd[1]])) { + rsp[2] = IPMI_CC_INVALID_CMD; + goto out; + } + + ibs->netfns[netfn / 2]->cmd_handlers[cmd[1]](ibs, cmd, cmd_len, rsp, rsp_len, + max_rsp_len); + + out: + k->handle_rsp(s, msg_id, rsp, *rsp_len); + + next_timeout(ibs); +} + +static void ipmi_sim_handle_timeout(IPMIBmcSim *ibs) +{ + IPMIInterface *s = ibs->parent.intf; + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + + if (!ibs->watchdog_running) { + goto out; + } + + if (!ibs->watchdog_preaction_ran) { + switch (IPMI_BMC_WATCHDOG_GET_PRE_ACTION(ibs)) { + case IPMI_BMC_WATCHDOG_PRE_NMI: + ibs->msg_flags |= IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK; + k->do_hw_op(s, IPMI_SEND_NMI, 0); + sensor_set_discrete_bit(ibs, IPMI_WATCHDOG_SENSOR, 8, 1, + 0xc8, (2 << 4) | 0xf, 0xff); + break; + + case IPMI_BMC_WATCHDOG_PRE_MSG_INT: + ibs->msg_flags |= IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK; + k->set_atn(s, 1, attn_irq_enabled(ibs)); + sensor_set_discrete_bit(ibs, IPMI_WATCHDOG_SENSOR, 8, 1, + 0xc8, (3 << 4) | 0xf, 0xff); + break; + + default: + goto do_full_expiry; + } + + ibs->watchdog_preaction_ran = 1; + /* Issued the pretimeout, do the rest of the timeout now. */ + ibs->watchdog_expiry = ipmi_getmonotime(); + ibs->watchdog_expiry += ibs->watchdog_pretimeout * 1000000000LL; + goto out; + } + + do_full_expiry: + ibs->watchdog_running = 0; /* Stop the watchdog on a timeout */ + ibs->watchdog_expired |= (1 << IPMI_BMC_WATCHDOG_GET_USE(ibs)); + switch (IPMI_BMC_WATCHDOG_GET_ACTION(ibs)) { + case IPMI_BMC_WATCHDOG_ACTION_NONE: + sensor_set_discrete_bit(ibs, IPMI_WATCHDOG_SENSOR, 0, 1, + 0xc0, ibs->watchdog_use & 0xf, 0xff); + break; + + case IPMI_BMC_WATCHDOG_ACTION_RESET: + sensor_set_discrete_bit(ibs, IPMI_WATCHDOG_SENSOR, 1, 1, + 0xc1, ibs->watchdog_use & 0xf, 0xff); + k->do_hw_op(s, IPMI_RESET_CHASSIS, 0); + break; + + case IPMI_BMC_WATCHDOG_ACTION_POWER_DOWN: + sensor_set_discrete_bit(ibs, IPMI_WATCHDOG_SENSOR, 2, 1, + 0xc2, ibs->watchdog_use & 0xf, 0xff); + k->do_hw_op(s, IPMI_POWEROFF_CHASSIS, 0); + break; + + case IPMI_BMC_WATCHDOG_ACTION_POWER_CYCLE: + sensor_set_discrete_bit(ibs, IPMI_WATCHDOG_SENSOR, 2, 1, + 0xc3, ibs->watchdog_use & 0xf, 0xff); + k->do_hw_op(s, IPMI_POWERCYCLE_CHASSIS, 0); + break; + } + + out: + next_timeout(ibs); +} + +static void chassis_capabilities(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMI_ADD_RSP_DATA(0); + IPMI_ADD_RSP_DATA(ibs->parent.slave_addr); + IPMI_ADD_RSP_DATA(ibs->parent.slave_addr); + IPMI_ADD_RSP_DATA(ibs->parent.slave_addr); + IPMI_ADD_RSP_DATA(ibs->parent.slave_addr); + out: + return; +} + +static void chassis_status(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMI_ADD_RSP_DATA(0x61); /* Unknown power restore, power is on */ + IPMI_ADD_RSP_DATA(0); + IPMI_ADD_RSP_DATA(0); + IPMI_ADD_RSP_DATA(0); + out: + return; +} + +static void chassis_control(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMIInterface *s = ibs->parent.intf; + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + + IPMI_CHECK_CMD_LEN(3); + switch (cmd[2] & 0xf) { + case 0: /* power down */ + rsp[2] = k->do_hw_op(s, IPMI_POWEROFF_CHASSIS, 0); + break; + case 1: /* power up */ + rsp[2] = k->do_hw_op(s, IPMI_POWERON_CHASSIS, 0); + break; + case 2: /* power cycle */ + rsp[2] = k->do_hw_op(s, IPMI_POWERCYCLE_CHASSIS, 0); + break; + case 3: /* hard reset */ + rsp[2] = k->do_hw_op(s, IPMI_RESET_CHASSIS, 0); + break; + case 4: /* pulse diagnostic interrupt */ + rsp[2] = k->do_hw_op(s, IPMI_PULSE_DIAG_IRQ, 0); + break; + case 5: /* soft shutdown via ACPI by overtemp emulation */ + rsp[2] = k->do_hw_op(s, + IPMI_SHUTDOWN_VIA_ACPI_OVERTEMP, 0); + break; + default: + rsp[2] = IPMI_CC_INVALID_DATA_FIELD; + goto out; + } + out: + return; +} + +static void get_device_id(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMI_ADD_RSP_DATA(ibs->device_id); + IPMI_ADD_RSP_DATA(ibs->device_rev & 0xf); + IPMI_ADD_RSP_DATA(ibs->fwrev1 & 0x7f); + IPMI_ADD_RSP_DATA(ibs->fwrev2); + IPMI_ADD_RSP_DATA(ibs->ipmi_version); + IPMI_ADD_RSP_DATA(0x07); /* sensor, SDR, and SEL. */ + IPMI_ADD_RSP_DATA(ibs->mfg_id[0]); + IPMI_ADD_RSP_DATA(ibs->mfg_id[1]); + IPMI_ADD_RSP_DATA(ibs->mfg_id[2]); + IPMI_ADD_RSP_DATA(ibs->product_id[0]); + IPMI_ADD_RSP_DATA(ibs->product_id[1]); + out: + return; +} + +static void set_global_enables(IPMIBmcSim *ibs, uint8_t val) +{ + IPMIInterface *s = ibs->parent.intf; + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + bool irqs_on; + + ibs->bmc_global_enables = val; + + irqs_on = val & (IPMI_BMC_EVBUF_FULL_INT_BIT | + IPMI_BMC_RCV_MSG_QUEUE_INT_BIT); + + k->set_irq_enable(s, irqs_on); +} + +static void cold_reset(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMIInterface *s = ibs->parent.intf; + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + + /* Disable all interrupts */ + set_global_enables(ibs, 1 << IPMI_BMC_EVENT_LOG_BIT); + + if (k->reset) { + k->reset(s, true); + } +} + +static void warm_reset(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMIInterface *s = ibs->parent.intf; + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + + if (k->reset) { + k->reset(s, false); + } +} + +static void set_bmc_global_enables(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMI_CHECK_CMD_LEN(3); + set_global_enables(ibs, cmd[2]); + out: + return; +} + +static void get_bmc_global_enables(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMI_ADD_RSP_DATA(ibs->bmc_global_enables); + out: + return; +} + +static void clr_msg_flags(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMIInterface *s = ibs->parent.intf; + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + + IPMI_CHECK_CMD_LEN(3); + ibs->msg_flags &= ~cmd[2]; + k->set_atn(s, attn_set(ibs), attn_irq_enabled(ibs)); + out: + return; +} + +static void get_msg_flags(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMI_ADD_RSP_DATA(ibs->msg_flags); + out: + return; +} + +static void read_evt_msg_buf(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMIInterface *s = ibs->parent.intf; + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + unsigned int i; + + if (!(ibs->msg_flags & IPMI_BMC_MSG_FLAG_EVT_BUF_FULL)) { + rsp[2] = 0x80; + goto out; + } + for (i = 0; i < 16; i++) { + IPMI_ADD_RSP_DATA(ibs->evtbuf[i]); + } + ibs->msg_flags &= ~IPMI_BMC_MSG_FLAG_EVT_BUF_FULL; + k->set_atn(s, attn_set(ibs), attn_irq_enabled(ibs)); + out: + return; +} + +static void get_msg(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMIRcvBufEntry *msg; + + qemu_mutex_lock(&ibs->lock); + if (QTAILQ_EMPTY(&ibs->rcvbufs)) { + rsp[2] = 0x80; /* Queue empty */ + goto out; + } + rsp[3] = 0; /* Channel 0 */ + *rsp_len += 1; + msg = QTAILQ_FIRST(&ibs->rcvbufs); + memcpy(rsp + 4, msg->buf, msg->len); + *rsp_len += msg->len; + QTAILQ_REMOVE(&ibs->rcvbufs, msg, entry); + g_free(msg); + + if (QTAILQ_EMPTY(&ibs->rcvbufs)) { + IPMIInterface *s = ibs->parent.intf; + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + + ibs->msg_flags &= ~IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE; + k->set_atn(s, attn_set(ibs), attn_irq_enabled(ibs)); + } + + out: + qemu_mutex_unlock(&ibs->lock); + return; +} + +static unsigned char +ipmb_checksum(unsigned char *data, int size, unsigned char csum) +{ + for (; size > 0; size--, data++) { + csum += *data; + } + + return -csum; +} + +static void send_msg(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMIInterface *s = ibs->parent.intf; + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + IPMIRcvBufEntry *msg; + uint8_t *buf; + uint8_t netfn, rqLun, rsLun, rqSeq; + + IPMI_CHECK_CMD_LEN(3); + + if (cmd[2] != 0) { + /* We only handle channel 0 with no options */ + rsp[2] = IPMI_CC_INVALID_DATA_FIELD; + goto out; + } + + IPMI_CHECK_CMD_LEN(10); + if (cmd[3] != 0x40) { + /* We only emulate a MC at address 0x40. */ + rsp[2] = 0x83; /* NAK on write */ + goto out; + } + + cmd += 3; /* Skip the header. */ + cmd_len -= 3; + + /* + * At this point we "send" the message successfully. Any error will + * be returned in the response. + */ + if (ipmb_checksum(cmd, cmd_len, 0) != 0 || + cmd[3] != 0x20) { /* Improper response address */ + goto out; /* No response */ + } + + netfn = cmd[1] >> 2; + rqLun = cmd[4] & 0x3; + rsLun = cmd[1] & 0x3; + rqSeq = cmd[4] >> 2; + + if (rqLun != 2) { + /* We only support LUN 2 coming back to us. */ + goto out; + } + + msg = g_malloc(sizeof(*msg)); + msg->buf[0] = ((netfn | 1) << 2) | rqLun; /* NetFN, and make a response */ + msg->buf[1] = ipmb_checksum(msg->buf, 1, 0); + msg->buf[2] = cmd[0]; /* rsSA */ + msg->buf[3] = (rqSeq << 2) | rsLun; + msg->buf[4] = cmd[5]; /* Cmd */ + msg->buf[5] = 0; /* Completion Code */ + msg->len = 6; + + if ((cmd[1] >> 2) != IPMI_NETFN_APP || cmd[5] != IPMI_CMD_GET_DEVICE_ID) { + /* Not a command we handle. */ + msg->buf[5] = IPMI_CC_INVALID_CMD; + goto end_msg; + } + + buf = msg->buf + msg->len; /* After the CC */ + buf[0] = 0; + buf[1] = 0; + buf[2] = 0; + buf[3] = 0; + buf[4] = 0x51; + buf[5] = 0; + buf[6] = 0; + buf[7] = 0; + buf[8] = 0; + buf[9] = 0; + buf[10] = 0; + msg->len += 11; + + end_msg: + msg->buf[msg->len] = ipmb_checksum(msg->buf, msg->len, 0); + msg->len++; + qemu_mutex_lock(&ibs->lock); + QTAILQ_INSERT_TAIL(&ibs->rcvbufs, msg, entry); + ibs->msg_flags |= IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE; + k->set_atn(s, 1, attn_irq_enabled(ibs)); + qemu_mutex_unlock(&ibs->lock); + + out: + return; +} + +static void do_watchdog_reset(IPMIBmcSim *ibs) +{ + if (IPMI_BMC_WATCHDOG_GET_ACTION(ibs) == + IPMI_BMC_WATCHDOG_ACTION_NONE) { + ibs->watchdog_running = 0; + return; + } + ibs->watchdog_preaction_ran = 0; + + + /* Timeout is in tenths of a second, offset is in seconds */ + ibs->watchdog_expiry = ipmi_getmonotime(); + ibs->watchdog_expiry += ibs->watchdog_timeout * 100000000LL; + if (IPMI_BMC_WATCHDOG_GET_PRE_ACTION(ibs) != IPMI_BMC_WATCHDOG_PRE_NONE) { + ibs->watchdog_expiry -= ibs->watchdog_pretimeout * 1000000000LL; + } + ibs->watchdog_running = 1; +} + +static void reset_watchdog_timer(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + if (!ibs->watchdog_initialized) { + rsp[2] = 0x80; + goto out; + } + do_watchdog_reset(ibs); + out: + return; +} + +static void set_watchdog_timer(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMIInterface *s = ibs->parent.intf; + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + unsigned int val; + + IPMI_CHECK_CMD_LEN(8); + val = cmd[2] & 0x7; /* Validate use */ + if (val == 0 || val > 5) { + rsp[2] = IPMI_CC_INVALID_DATA_FIELD; + goto out; + } + val = cmd[3] & 0x7; /* Validate action */ + switch (val) { + case IPMI_BMC_WATCHDOG_ACTION_NONE: + break; + + case IPMI_BMC_WATCHDOG_ACTION_RESET: + rsp[2] = k->do_hw_op(s, IPMI_RESET_CHASSIS, 1); + break; + + case IPMI_BMC_WATCHDOG_ACTION_POWER_DOWN: + rsp[2] = k->do_hw_op(s, IPMI_POWEROFF_CHASSIS, 1); + break; + + case IPMI_BMC_WATCHDOG_ACTION_POWER_CYCLE: + rsp[2] = k->do_hw_op(s, IPMI_POWERCYCLE_CHASSIS, 1); + break; + + default: + rsp[2] = IPMI_CC_INVALID_DATA_FIELD; + } + if (rsp[2]) { + rsp[2] = IPMI_CC_INVALID_DATA_FIELD; + goto out; + } + + val = (cmd[3] >> 4) & 0x7; /* Validate preaction */ + switch (val) { + case IPMI_BMC_WATCHDOG_PRE_MSG_INT: + case IPMI_BMC_WATCHDOG_PRE_NONE: + break; + + case IPMI_BMC_WATCHDOG_PRE_NMI: + if (!k->do_hw_op(s, IPMI_SEND_NMI, 1)) { + /* NMI not supported. */ + rsp[2] = IPMI_CC_INVALID_DATA_FIELD; + goto out; + } + default: + /* We don't support PRE_SMI */ + rsp[2] = IPMI_CC_INVALID_DATA_FIELD; + goto out; + } + + ibs->watchdog_initialized = 1; + ibs->watchdog_use = cmd[2] & IPMI_BMC_WATCHDOG_USE_MASK; + ibs->watchdog_action = cmd[3] & IPMI_BMC_WATCHDOG_ACTION_MASK; + ibs->watchdog_pretimeout = cmd[4]; + ibs->watchdog_expired &= ~cmd[5]; + ibs->watchdog_timeout = cmd[6] | (((uint16_t) cmd[7]) << 8); + if (ibs->watchdog_running & IPMI_BMC_WATCHDOG_GET_DONT_STOP(ibs)) { + do_watchdog_reset(ibs); + } else { + ibs->watchdog_running = 0; + } + out: + return; +} + +static void get_watchdog_timer(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMI_ADD_RSP_DATA(ibs->watchdog_use); + IPMI_ADD_RSP_DATA(ibs->watchdog_action); + IPMI_ADD_RSP_DATA(ibs->watchdog_pretimeout); + IPMI_ADD_RSP_DATA(ibs->watchdog_expired); + if (ibs->watchdog_running) { + long timeout; + timeout = ((ibs->watchdog_expiry - ipmi_getmonotime() + 50000000) + / 100000000); + IPMI_ADD_RSP_DATA(timeout & 0xff); + IPMI_ADD_RSP_DATA((timeout >> 8) & 0xff); + } else { + IPMI_ADD_RSP_DATA(0); + IPMI_ADD_RSP_DATA(0); + } + out: + return; +} + +static void get_sdr_rep_info(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + unsigned int i; + + IPMI_ADD_RSP_DATA(0x51); /* Conform to IPMI 1.5 spec */ + IPMI_ADD_RSP_DATA(ibs->sdr.next_rec_id & 0xff); + IPMI_ADD_RSP_DATA((ibs->sdr.next_rec_id >> 8) & 0xff); + IPMI_ADD_RSP_DATA((MAX_SDR_SIZE - ibs->sdr.next_free) & 0xff); + IPMI_ADD_RSP_DATA(((MAX_SDR_SIZE - ibs->sdr.next_free) >> 8) & 0xff); + for (i = 0; i < 4; i++) { + IPMI_ADD_RSP_DATA(ibs->sdr.last_addition[i]); + } + for (i = 0; i < 4; i++) { + IPMI_ADD_RSP_DATA(ibs->sdr.last_clear[i]); + } + /* Only modal support, reserve supported */ + IPMI_ADD_RSP_DATA((ibs->sdr.overflow << 7) | 0x22); + out: + return; +} + +static void reserve_sdr_rep(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMI_ADD_RSP_DATA(ibs->sdr.reservation & 0xff); + IPMI_ADD_RSP_DATA((ibs->sdr.reservation >> 8) & 0xff); + out: + return; +} + +static void get_sdr(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + unsigned int pos; + uint16_t nextrec; + + IPMI_CHECK_CMD_LEN(8); + if (cmd[6]) { + IPMI_CHECK_RESERVATION(2, ibs->sdr.reservation); + } + pos = 0; + if (sdr_find_entry(&ibs->sdr, cmd[4] | (cmd[5] << 8), + &pos, &nextrec)) { + rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT; + goto out; + } + if (cmd[6] > (ibs->sdr.sdr[pos + 4])) { + rsp[2] = IPMI_CC_PARM_OUT_OF_RANGE; + goto out; + } + + IPMI_ADD_RSP_DATA(nextrec & 0xff); + IPMI_ADD_RSP_DATA((nextrec >> 8) & 0xff); + + if (cmd[7] == 0xff) { + cmd[7] = ibs->sdr.sdr[pos + 4] - cmd[6]; + } + + if ((cmd[7] + *rsp_len) > max_rsp_len) { + rsp[2] = IPMI_CC_CANNOT_RETURN_REQ_NUM_BYTES; + goto out; + } + memcpy(rsp + *rsp_len, ibs->sdr.sdr + pos + cmd[6], cmd[7]); + *rsp_len += cmd[7]; + out: + return; +} + +static void add_sdr(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + uint16_t recid; + + if (sdr_add_entry(ibs, cmd + 2, cmd_len - 2, &recid)) { + rsp[2] = IPMI_CC_INVALID_DATA_FIELD; + goto out; + } + IPMI_ADD_RSP_DATA(recid & 0xff); + IPMI_ADD_RSP_DATA((recid >> 8) & 0xff); + out: + return; +} + +static void clear_sdr_rep(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMI_CHECK_CMD_LEN(8); + IPMI_CHECK_RESERVATION(2, ibs->sdr.reservation); + if (cmd[4] != 'C' || cmd[5] != 'L' || cmd[6] != 'R') { + rsp[2] = IPMI_CC_INVALID_DATA_FIELD; + goto out; + } + if (cmd[7] == 0xaa) { + ibs->sdr.next_free = 0; + ibs->sdr.overflow = 0; + set_timestamp(ibs, ibs->sdr.last_clear); + IPMI_ADD_RSP_DATA(1); /* Erasure complete */ + sdr_inc_reservation(&ibs->sdr); + } else if (cmd[7] == 0) { + IPMI_ADD_RSP_DATA(1); /* Erasure complete */ + } else { + rsp[2] = IPMI_CC_INVALID_DATA_FIELD; + goto out; + } + out: + return; +} + +static void get_sel_info(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + unsigned int i, val; + + IPMI_ADD_RSP_DATA(0x51); /* Conform to IPMI 1.5 */ + IPMI_ADD_RSP_DATA(ibs->sel.next_free & 0xff); + IPMI_ADD_RSP_DATA((ibs->sel.next_free >> 8) & 0xff); + val = (MAX_SEL_SIZE - ibs->sel.next_free) * 16; + IPMI_ADD_RSP_DATA(val & 0xff); + IPMI_ADD_RSP_DATA((val >> 8) & 0xff); + for (i = 0; i < 4; i++) { + IPMI_ADD_RSP_DATA(ibs->sel.last_addition[i]); + } + for (i = 0; i < 4; i++) { + IPMI_ADD_RSP_DATA(ibs->sel.last_clear[i]); + } + /* Only support Reserve SEL */ + IPMI_ADD_RSP_DATA((ibs->sel.overflow << 7) | 0x02); + out: + return; +} + +static void reserve_sel(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMI_ADD_RSP_DATA(ibs->sel.reservation & 0xff); + IPMI_ADD_RSP_DATA((ibs->sel.reservation >> 8) & 0xff); + out: + return; +} + +static void get_sel_entry(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + unsigned int val; + + IPMI_CHECK_CMD_LEN(8); + if (cmd[6]) { + IPMI_CHECK_RESERVATION(2, ibs->sel.reservation); + } + if (ibs->sel.next_free == 0) { + rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT; + goto out; + } + if (cmd[6] > 15) { + rsp[2] = IPMI_CC_INVALID_DATA_FIELD; + goto out; + } + if (cmd[7] == 0xff) { + cmd[7] = 16; + } else if ((cmd[7] + cmd[6]) > 16) { + rsp[2] = IPMI_CC_INVALID_DATA_FIELD; + goto out; + } else { + cmd[7] += cmd[6]; + } + + val = cmd[4] | (cmd[5] << 8); + if (val == 0xffff) { + val = ibs->sel.next_free - 1; + } else if (val >= ibs->sel.next_free) { + rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT; + goto out; + } + if ((val + 1) == ibs->sel.next_free) { + IPMI_ADD_RSP_DATA(0xff); + IPMI_ADD_RSP_DATA(0xff); + } else { + IPMI_ADD_RSP_DATA((val + 1) & 0xff); + IPMI_ADD_RSP_DATA(((val + 1) >> 8) & 0xff); + } + for (; cmd[6] < cmd[7]; cmd[6]++) { + IPMI_ADD_RSP_DATA(ibs->sel.sel[val][cmd[6]]); + } + out: + return; +} + +static void add_sel_entry(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMI_CHECK_CMD_LEN(18); + if (sel_add_event(ibs, cmd + 2)) { + rsp[2] = IPMI_CC_OUT_OF_SPACE; + goto out; + } + /* sel_add_event fills in the record number. */ + IPMI_ADD_RSP_DATA(cmd[2]); + IPMI_ADD_RSP_DATA(cmd[3]); + out: + return; +} + +static void clear_sel(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMI_CHECK_CMD_LEN(8); + IPMI_CHECK_RESERVATION(2, ibs->sel.reservation); + if (cmd[4] != 'C' || cmd[5] != 'L' || cmd[6] != 'R') { + rsp[2] = IPMI_CC_INVALID_DATA_FIELD; + goto out; + } + if (cmd[7] == 0xaa) { + ibs->sel.next_free = 0; + ibs->sel.overflow = 0; + set_timestamp(ibs, ibs->sdr.last_clear); + IPMI_ADD_RSP_DATA(1); /* Erasure complete */ + sel_inc_reservation(&ibs->sel); + } else if (cmd[7] == 0) { + IPMI_ADD_RSP_DATA(1); /* Erasure complete */ + } else { + rsp[2] = IPMI_CC_INVALID_DATA_FIELD; + goto out; + } + out: + return; +} + +static void get_sel_time(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + uint32_t val; + struct ipmi_time now; + + ipmi_gettime(&now); + val = now.tv_sec + ibs->sel.time_offset; + IPMI_ADD_RSP_DATA(val & 0xff); + IPMI_ADD_RSP_DATA((val >> 8) & 0xff); + IPMI_ADD_RSP_DATA((val >> 16) & 0xff); + IPMI_ADD_RSP_DATA((val >> 24) & 0xff); + out: + return; +} + +static void set_sel_time(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + uint32_t val; + struct ipmi_time now; + + IPMI_CHECK_CMD_LEN(6); + val = cmd[2] | (cmd[3] << 8) | (cmd[4] << 16) | (cmd[5] << 24); + ipmi_gettime(&now); + ibs->sel.time_offset = now.tv_sec - ((long) val); + out: + return; +} + +static void set_sensor_evt_enable(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMISensor *sens; + + IPMI_CHECK_CMD_LEN(4); + if ((cmd[2] > MAX_SENSORS) || + !IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) { + rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT; + goto out; + } + sens = ibs->sensors + cmd[2]; + switch ((cmd[3] >> 4) & 0x3) { + case 0: /* Do not change */ + break; + case 1: /* Enable bits */ + if (cmd_len > 4) { + sens->assert_enable |= cmd[4]; + } + if (cmd_len > 5) { + sens->assert_enable |= cmd[5] << 8; + } + if (cmd_len > 6) { + sens->deassert_enable |= cmd[6]; + } + if (cmd_len > 7) { + sens->deassert_enable |= cmd[7] << 8; + } + break; + case 2: /* Disable bits */ + if (cmd_len > 4) { + sens->assert_enable &= ~cmd[4]; + } + if (cmd_len > 5) { + sens->assert_enable &= ~(cmd[5] << 8); + } + if (cmd_len > 6) { + sens->deassert_enable &= ~cmd[6]; + } + if (cmd_len > 7) { + sens->deassert_enable &= ~(cmd[7] << 8); + } + break; + case 3: + rsp[2] = IPMI_CC_INVALID_DATA_FIELD; + goto out; + } + IPMI_SENSOR_SET_RET_STATUS(sens, cmd[3]); + out: + return; +} + +static void get_sensor_evt_enable(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMISensor *sens; + + IPMI_CHECK_CMD_LEN(3); + if ((cmd[2] > MAX_SENSORS) || + !IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) { + rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT; + goto out; + } + sens = ibs->sensors + cmd[2]; + IPMI_ADD_RSP_DATA(IPMI_SENSOR_GET_RET_STATUS(sens)); + IPMI_ADD_RSP_DATA(sens->assert_enable & 0xff); + IPMI_ADD_RSP_DATA((sens->assert_enable >> 8) & 0xff); + IPMI_ADD_RSP_DATA(sens->deassert_enable & 0xff); + IPMI_ADD_RSP_DATA((sens->deassert_enable >> 8) & 0xff); + out: + return; +} + +static void rearm_sensor_evts(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMISensor *sens; + + IPMI_CHECK_CMD_LEN(4); + if ((cmd[2] > MAX_SENSORS) || + !IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) { + rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT; + goto out; + } + sens = ibs->sensors + cmd[2]; + + if ((cmd[3] & 0x80) == 0) { + /* Just clear everything */ + sens->states = 0; + goto out; + } + out: + return; +} + +static void get_sensor_evt_status(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMISensor *sens; + + IPMI_CHECK_CMD_LEN(3); + if ((cmd[2] > MAX_SENSORS) || + !IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) { + rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT; + goto out; + } + sens = ibs->sensors + cmd[2]; + IPMI_ADD_RSP_DATA(sens->reading); + IPMI_ADD_RSP_DATA(IPMI_SENSOR_GET_RET_STATUS(sens)); + IPMI_ADD_RSP_DATA(sens->assert_states & 0xff); + IPMI_ADD_RSP_DATA((sens->assert_states >> 8) & 0xff); + IPMI_ADD_RSP_DATA(sens->deassert_states & 0xff); + IPMI_ADD_RSP_DATA((sens->deassert_states >> 8) & 0xff); + out: + return; +} + +static void get_sensor_reading(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len, + unsigned int max_rsp_len) +{ + IPMISensor *sens; + + IPMI_CHECK_CMD_LEN(3); + if ((cmd[2] > MAX_SENSORS) || + !IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) { + rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT; + goto out; + } + sens = ibs->sensors + cmd[2]; + IPMI_ADD_RSP_DATA(sens->reading); + IPMI_ADD_RSP_DATA(IPMI_SENSOR_GET_RET_STATUS(sens)); + IPMI_ADD_RSP_DATA(sens->states & 0xff); + if (IPMI_SENSOR_IS_DISCRETE(sens)) { + IPMI_ADD_RSP_DATA((sens->states >> 8) & 0xff); + } + out: + return; +} + +static const IPMICmdHandler chassis_cmds[IPMI_NETFN_CHASSIS_MAXCMD] = { + [IPMI_CMD_GET_CHASSIS_CAPABILITIES] = chassis_capabilities, + [IPMI_CMD_GET_CHASSIS_STATUS] = chassis_status, + [IPMI_CMD_CHASSIS_CONTROL] = chassis_control +}; +static const IPMINetfn chassis_netfn = { + .cmd_nums = IPMI_NETFN_CHASSIS_MAXCMD, + .cmd_handlers = chassis_cmds +}; + +static const IPMICmdHandler +sensor_event_cmds[IPMI_NETFN_SENSOR_EVENT_MAXCMD] = { + [IPMI_CMD_SET_SENSOR_EVT_ENABLE] = set_sensor_evt_enable, + [IPMI_CMD_GET_SENSOR_EVT_ENABLE] = get_sensor_evt_enable, + [IPMI_CMD_REARM_SENSOR_EVTS] = rearm_sensor_evts, + [IPMI_CMD_GET_SENSOR_EVT_STATUS] = get_sensor_evt_status, + [IPMI_CMD_GET_SENSOR_READING] = get_sensor_reading +}; +static const IPMINetfn sensor_event_netfn = { + .cmd_nums = IPMI_NETFN_SENSOR_EVENT_MAXCMD, + .cmd_handlers = sensor_event_cmds +}; + +static const IPMICmdHandler app_cmds[IPMI_NETFN_APP_MAXCMD] = { + [IPMI_CMD_GET_DEVICE_ID] = get_device_id, + [IPMI_CMD_COLD_RESET] = cold_reset, + [IPMI_CMD_WARM_RESET] = warm_reset, + [IPMI_CMD_SET_BMC_GLOBAL_ENABLES] = set_bmc_global_enables, + [IPMI_CMD_GET_BMC_GLOBAL_ENABLES] = get_bmc_global_enables, + [IPMI_CMD_CLR_MSG_FLAGS] = clr_msg_flags, + [IPMI_CMD_GET_MSG_FLAGS] = get_msg_flags, + [IPMI_CMD_GET_MSG] = get_msg, + [IPMI_CMD_SEND_MSG] = send_msg, + [IPMI_CMD_READ_EVT_MSG_BUF] = read_evt_msg_buf, + [IPMI_CMD_RESET_WATCHDOG_TIMER] = reset_watchdog_timer, + [IPMI_CMD_SET_WATCHDOG_TIMER] = set_watchdog_timer, + [IPMI_CMD_GET_WATCHDOG_TIMER] = get_watchdog_timer, +}; +static const IPMINetfn app_netfn = { + .cmd_nums = IPMI_NETFN_APP_MAXCMD, + .cmd_handlers = app_cmds +}; + +static const IPMICmdHandler storage_cmds[IPMI_NETFN_STORAGE_MAXCMD] = { + [IPMI_CMD_GET_SDR_REP_INFO] = get_sdr_rep_info, + [IPMI_CMD_RESERVE_SDR_REP] = reserve_sdr_rep, + [IPMI_CMD_GET_SDR] = get_sdr, + [IPMI_CMD_ADD_SDR] = add_sdr, + [IPMI_CMD_CLEAR_SDR_REP] = clear_sdr_rep, + [IPMI_CMD_GET_SEL_INFO] = get_sel_info, + [IPMI_CMD_RESERVE_SEL] = reserve_sel, + [IPMI_CMD_GET_SEL_ENTRY] = get_sel_entry, + [IPMI_CMD_ADD_SEL_ENTRY] = add_sel_entry, + [IPMI_CMD_CLEAR_SEL] = clear_sel, + [IPMI_CMD_GET_SEL_TIME] = get_sel_time, + [IPMI_CMD_SET_SEL_TIME] = set_sel_time, +}; + +static const IPMINetfn storage_netfn = { + .cmd_nums = IPMI_NETFN_STORAGE_MAXCMD, + .cmd_handlers = storage_cmds +}; + +static void register_cmds(IPMIBmcSim *s) +{ + ipmi_register_netfn(s, IPMI_NETFN_CHASSIS, &chassis_netfn); + ipmi_register_netfn(s, IPMI_NETFN_SENSOR_EVENT, &sensor_event_netfn); + ipmi_register_netfn(s, IPMI_NETFN_APP, &app_netfn); + ipmi_register_netfn(s, IPMI_NETFN_STORAGE, &storage_netfn); +} + +static const uint8_t init_sdrs[] = { + /* Watchdog device */ + 0x00, 0x00, 0x51, 0x02, 35, 0x20, 0x00, 0x00, + 0x23, 0x01, 0x63, 0x00, 0x23, 0x6f, 0x0f, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, + 'W', 'a', 't', 'c', 'h', 'd', 'o', 'g', + /* End */ + 0xff, 0xff, 0x00, 0x00, 0x00 +}; + +static void ipmi_sim_init(Object *obj) +{ + IPMIBmc *b = IPMI_BMC(obj); + unsigned int i; + unsigned int recid; + IPMIBmcSim *ibs = IPMI_BMC_SIMULATOR(b); + + qemu_mutex_init(&ibs->lock); + QTAILQ_INIT(&ibs->rcvbufs); + + ibs->bmc_global_enables = (1 << IPMI_BMC_EVENT_LOG_BIT); + ibs->device_id = 0x20; + ibs->ipmi_version = 0x02; /* IPMI 2.0 */ + for (i = 0; i < 4; i++) { + ibs->sel.last_addition[i] = 0xff; + ibs->sel.last_clear[i] = 0xff; + ibs->sdr.last_addition[i] = 0xff; + ibs->sdr.last_clear[i] = 0xff; + } + + for (i = 0;;) { + int len; + if ((i + 5) > sizeof(init_sdrs)) { + error_report("Problem with recid 0x%4.4x: \n", i); + return; + } + len = init_sdrs[i + 4]; + recid = init_sdrs[i] | (init_sdrs[i + 1] << 8); + if (recid == 0xffff) { + break; + } + if ((i + len + 5) > sizeof(init_sdrs)) { + error_report("Problem with recid 0x%4.4x\n", i); + return; + } + sdr_add_entry(ibs, init_sdrs + i, len, NULL); + i += len + 5; + } + + ipmi_init_sensors_from_sdrs(ibs); + register_cmds(ibs); + + ibs->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, ipmi_timeout, ibs); +} + +static void ipmi_sim_class_init(ObjectClass *oc, void *data) +{ + IPMIBmcClass *bk = IPMI_BMC_CLASS(oc); + + bk->handle_command = ipmi_sim_handle_command; +} + +static const TypeInfo ipmi_sim_type = { + .name = TYPE_IPMI_BMC_SIMULATOR, + .parent = TYPE_IPMI_BMC, + .instance_size = sizeof(IPMIBmcSim), + .instance_init = ipmi_sim_init, + .class_init = ipmi_sim_class_init, +}; + +static void ipmi_sim_register_types(void) +{ + type_register_static(&ipmi_sim_type); +} + +type_init(ipmi_sim_register_types) From 67aa56fc03bea44ccf384ea400515a8a58844a50 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Thu, 17 Dec 2015 12:50:06 -0600 Subject: [PATCH 15/55] ipmi: Add an external connection simulation interface This adds an interface for IPMI that connects to a remote BMC over a chardev (generally a TCP socket). The OpenIPMI lanserv simulator describes this interface, see that for interface details. Signed-off-by: Corey Minyard Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- default-configs/i386-softmmu.mak | 1 + default-configs/x86_64-softmmu.mak | 1 + hw/ipmi/Makefile.objs | 1 + hw/ipmi/ipmi_bmc_extern.c | 478 +++++++++++++++++++++++++++++ 4 files changed, 481 insertions(+) create mode 100644 hw/ipmi/ipmi_bmc_extern.c diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak index 3f01ed1583..25d745859e 100644 --- a/default-configs/i386-softmmu.mak +++ b/default-configs/i386-softmmu.mak @@ -11,6 +11,7 @@ CONFIG_VIRTIO_VGA=y CONFIG_VMMOUSE=y CONFIG_IPMI=y CONFIG_IPMI_LOCAL=y +CONFIG_IPMI_EXTERN=y CONFIG_SERIAL=y CONFIG_PARALLEL=y CONFIG_I8254=y diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak index 40685e4aa8..5cff66d2a0 100644 --- a/default-configs/x86_64-softmmu.mak +++ b/default-configs/x86_64-softmmu.mak @@ -11,6 +11,7 @@ CONFIG_VIRTIO_VGA=y CONFIG_VMMOUSE=y CONFIG_IPMI=y CONFIG_IPMI_LOCAL=y +CONFIG_IPMI_EXTERN=y CONFIG_SERIAL=y CONFIG_PARALLEL=y CONFIG_I8254=y diff --git a/hw/ipmi/Makefile.objs b/hw/ipmi/Makefile.objs index 875271ca56..ddb15ecf7b 100644 --- a/hw/ipmi/Makefile.objs +++ b/hw/ipmi/Makefile.objs @@ -1,2 +1,3 @@ common-obj-$(CONFIG_IPMI) += ipmi.o common-obj-$(CONFIG_IPMI_LOCAL) += ipmi_bmc_sim.o +common-obj-$(CONFIG_IPMI_LOCAL) += ipmi_bmc_extern.o diff --git a/hw/ipmi/ipmi_bmc_extern.c b/hw/ipmi/ipmi_bmc_extern.c new file mode 100644 index 0000000000..d4669f072f --- /dev/null +++ b/hw/ipmi/ipmi_bmc_extern.c @@ -0,0 +1,478 @@ +/* + * IPMI BMC external connection + * + * Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * This is designed to connect with OpenIPMI's lanserv serial interface + * using the "VM" connection type. See that for details. + */ + +#include +#include "qemu/timer.h" +#include "sysemu/char.h" +#include "hw/ipmi/ipmi.h" + +#define VM_MSG_CHAR 0xA0 /* Marks end of message */ +#define VM_CMD_CHAR 0xA1 /* Marks end of a command */ +#define VM_ESCAPE_CHAR 0xAA /* Set bit 4 from the next byte to 0 */ + +#define VM_PROTOCOL_VERSION 1 +#define VM_CMD_VERSION 0xff /* A version number byte follows */ +#define VM_CMD_NOATTN 0x00 +#define VM_CMD_ATTN 0x01 +#define VM_CMD_ATTN_IRQ 0x02 +#define VM_CMD_POWEROFF 0x03 +#define VM_CMD_RESET 0x04 +#define VM_CMD_ENABLE_IRQ 0x05 /* Enable/disable the messaging irq */ +#define VM_CMD_DISABLE_IRQ 0x06 +#define VM_CMD_SEND_NMI 0x07 +#define VM_CMD_CAPABILITIES 0x08 +#define VM_CAPABILITIES_POWER 0x01 +#define VM_CAPABILITIES_RESET 0x02 +#define VM_CAPABILITIES_IRQ 0x04 +#define VM_CAPABILITIES_NMI 0x08 +#define VM_CAPABILITIES_ATTN 0x10 + +#define TYPE_IPMI_BMC_EXTERN "ipmi-bmc-extern" +#define IPMI_BMC_EXTERN(obj) OBJECT_CHECK(IPMIBmcExtern, (obj), \ + TYPE_IPMI_BMC_EXTERN) +typedef struct IPMIBmcExtern { + IPMIBmc parent; + + CharDriverState *chr; + + bool connected; + + unsigned char inbuf[MAX_IPMI_MSG_SIZE + 2]; + unsigned int inpos; + bool in_escape; + bool in_too_many; + bool waiting_rsp; + bool sending_cmd; + + unsigned char outbuf[(MAX_IPMI_MSG_SIZE + 2) * 2 + 1]; + unsigned int outpos; + unsigned int outlen; + + struct QEMUTimer *extern_timer; + + /* A reset event is pending to be sent upstream. */ + bool send_reset; +} IPMIBmcExtern; + +static int can_receive(void *opaque); +static void receive(void *opaque, const uint8_t *buf, int size); +static void chr_event(void *opaque, int event); + +static unsigned char +ipmb_checksum(const unsigned char *data, int size, unsigned char start) +{ + unsigned char csum = start; + + for (; size > 0; size--, data++) { + csum += *data; + } + return csum; +} + +static void continue_send(IPMIBmcExtern *ibe) +{ + if (ibe->outlen == 0) { + goto check_reset; + } + send: + ibe->outpos += qemu_chr_fe_write(ibe->chr, ibe->outbuf + ibe->outpos, + ibe->outlen - ibe->outpos); + if (ibe->outpos < ibe->outlen) { + /* Not fully transmitted, try again in a 10ms */ + timer_mod_ns(ibe->extern_timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 10000000); + } else { + /* Sent */ + ibe->outlen = 0; + ibe->outpos = 0; + if (!ibe->sending_cmd) { + ibe->waiting_rsp = true; + } else { + ibe->sending_cmd = false; + } + check_reset: + if (ibe->connected && ibe->send_reset) { + /* Send the reset */ + ibe->outbuf[0] = VM_CMD_RESET; + ibe->outbuf[1] = VM_CMD_CHAR; + ibe->outlen = 2; + ibe->outpos = 0; + ibe->send_reset = false; + ibe->sending_cmd = true; + goto send; + } + + if (ibe->waiting_rsp) { + /* Make sure we get a response within 4 seconds. */ + timer_mod_ns(ibe->extern_timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 4000000000ULL); + } + } + return; +} + +static void extern_timeout(void *opaque) +{ + IPMIBmcExtern *ibe = opaque; + IPMIInterface *s = ibe->parent.intf; + + if (ibe->connected) { + if (ibe->waiting_rsp && (ibe->outlen == 0)) { + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + /* The message response timed out, return an error. */ + ibe->waiting_rsp = false; + ibe->inbuf[1] = ibe->outbuf[1] | 0x04; + ibe->inbuf[2] = ibe->outbuf[2]; + ibe->inbuf[3] = IPMI_CC_TIMEOUT; + k->handle_rsp(s, ibe->outbuf[0], ibe->inbuf + 1, 3); + } else { + continue_send(ibe); + } + } +} + +static void addchar(IPMIBmcExtern *ibe, unsigned char ch) +{ + switch (ch) { + case VM_MSG_CHAR: + case VM_CMD_CHAR: + case VM_ESCAPE_CHAR: + ibe->outbuf[ibe->outlen] = VM_ESCAPE_CHAR; + ibe->outlen++; + ch |= 0x10; + /* No break */ + + default: + ibe->outbuf[ibe->outlen] = ch; + ibe->outlen++; + } +} + +static void ipmi_bmc_extern_handle_command(IPMIBmc *b, + uint8_t *cmd, unsigned int cmd_len, + unsigned int max_cmd_len, + uint8_t msg_id) +{ + IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(b); + IPMIInterface *s = ibe->parent.intf; + uint8_t err = 0, csum; + unsigned int i; + + if (ibe->outlen) { + /* We already have a command queued. Shouldn't ever happen. */ + fprintf(stderr, "IPMI KCS: Got command when not finished with the" + " previous commmand\n"); + abort(); + } + + /* If it's too short or it was truncated, return an error. */ + if (cmd_len < 2) { + err = IPMI_CC_REQUEST_DATA_LENGTH_INVALID; + } else if ((cmd_len > max_cmd_len) || (cmd_len > MAX_IPMI_MSG_SIZE)) { + err = IPMI_CC_REQUEST_DATA_TRUNCATED; + } else if (!ibe->connected) { + err = IPMI_CC_BMC_INIT_IN_PROGRESS; + } + if (err) { + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + unsigned char rsp[3]; + rsp[0] = cmd[0] | 0x04; + rsp[1] = cmd[1]; + rsp[2] = err; + ibe->waiting_rsp = false; + k->handle_rsp(s, msg_id, rsp, 3); + goto out; + } + + addchar(ibe, msg_id); + for (i = 0; i < cmd_len; i++) { + addchar(ibe, cmd[i]); + } + csum = ipmb_checksum(&msg_id, 1, 0); + addchar(ibe, -ipmb_checksum(cmd, cmd_len, csum)); + + ibe->outbuf[ibe->outlen] = VM_MSG_CHAR; + ibe->outlen++; + + /* Start the transmit */ + continue_send(ibe); + + out: + return; +} + +static void handle_hw_op(IPMIBmcExtern *ibe, unsigned char hw_op) +{ + IPMIInterface *s = ibe->parent.intf; + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + + switch (hw_op) { + case VM_CMD_VERSION: + /* We only support one version at this time. */ + break; + + case VM_CMD_NOATTN: + k->set_atn(s, 0, 0); + break; + + case VM_CMD_ATTN: + k->set_atn(s, 1, 0); + break; + + case VM_CMD_ATTN_IRQ: + k->set_atn(s, 1, 1); + break; + + case VM_CMD_POWEROFF: + k->do_hw_op(s, IPMI_POWEROFF_CHASSIS, 0); + break; + + case VM_CMD_RESET: + k->do_hw_op(s, IPMI_RESET_CHASSIS, 0); + break; + + case VM_CMD_ENABLE_IRQ: + k->set_irq_enable(s, 1); + break; + + case VM_CMD_DISABLE_IRQ: + k->set_irq_enable(s, 0); + break; + + case VM_CMD_SEND_NMI: + k->do_hw_op(s, IPMI_SEND_NMI, 0); + break; + } +} + +static void handle_msg(IPMIBmcExtern *ibe) +{ + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(ibe->parent.intf); + + if (ibe->in_escape) { + ipmi_debug("msg escape not ended\n"); + return; + } + if (ibe->inpos < 5) { + ipmi_debug("msg too short\n"); + return; + } + if (ibe->in_too_many) { + ibe->inbuf[3] = IPMI_CC_REQUEST_DATA_TRUNCATED; + ibe->inpos = 4; + } else if (ipmb_checksum(ibe->inbuf, ibe->inpos, 0) != 0) { + ipmi_debug("msg checksum failure\n"); + return; + } else { + ibe->inpos--; /* Remove checkum */ + } + + timer_del(ibe->extern_timer); + ibe->waiting_rsp = false; + k->handle_rsp(ibe->parent.intf, ibe->inbuf[0], ibe->inbuf + 1, ibe->inpos - 1); +} + +static int can_receive(void *opaque) +{ + return 1; +} + +static void receive(void *opaque, const uint8_t *buf, int size) +{ + IPMIBmcExtern *ibe = opaque; + int i; + unsigned char hw_op; + + for (i = 0; i < size; i++) { + unsigned char ch = buf[i]; + + switch (ch) { + case VM_MSG_CHAR: + handle_msg(ibe); + ibe->in_too_many = false; + ibe->inpos = 0; + break; + + case VM_CMD_CHAR: + if (ibe->in_too_many) { + ipmi_debug("cmd in too many\n"); + ibe->in_too_many = false; + ibe->inpos = 0; + break; + } + if (ibe->in_escape) { + ipmi_debug("cmd in escape\n"); + ibe->in_too_many = false; + ibe->inpos = 0; + ibe->in_escape = false; + break; + } + ibe->in_too_many = false; + if (ibe->inpos < 1) { + break; + } + hw_op = ibe->inbuf[0]; + ibe->inpos = 0; + goto out_hw_op; + break; + + case VM_ESCAPE_CHAR: + ibe->in_escape = true; + break; + + default: + if (ibe->in_escape) { + ch &= ~0x10; + ibe->in_escape = false; + } + if (ibe->in_too_many) { + break; + } + if (ibe->inpos >= sizeof(ibe->inbuf)) { + ibe->in_too_many = true; + break; + } + ibe->inbuf[ibe->inpos] = ch; + ibe->inpos++; + break; + } + } + return; + + out_hw_op: + handle_hw_op(ibe, hw_op); +} + +static void chr_event(void *opaque, int event) +{ + IPMIBmcExtern *ibe = opaque; + IPMIInterface *s = ibe->parent.intf; + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + unsigned char v; + + switch (event) { + case CHR_EVENT_OPENED: + ibe->connected = true; + ibe->outpos = 0; + ibe->outlen = 0; + addchar(ibe, VM_CMD_VERSION); + addchar(ibe, VM_PROTOCOL_VERSION); + ibe->outbuf[ibe->outlen] = VM_CMD_CHAR; + ibe->outlen++; + addchar(ibe, VM_CMD_CAPABILITIES); + v = VM_CAPABILITIES_IRQ | VM_CAPABILITIES_ATTN; + if (k->do_hw_op(ibe->parent.intf, IPMI_POWEROFF_CHASSIS, 1) == 0) { + v |= VM_CAPABILITIES_POWER; + } + if (k->do_hw_op(ibe->parent.intf, IPMI_RESET_CHASSIS, 1) == 0) { + v |= VM_CAPABILITIES_RESET; + } + if (k->do_hw_op(ibe->parent.intf, IPMI_SEND_NMI, 1) == 0) { + v |= VM_CAPABILITIES_NMI; + } + addchar(ibe, v); + ibe->outbuf[ibe->outlen] = VM_CMD_CHAR; + ibe->outlen++; + ibe->sending_cmd = false; + continue_send(ibe); + break; + + case CHR_EVENT_CLOSED: + if (!ibe->connected) { + return; + } + ibe->connected = false; + if (ibe->waiting_rsp) { + ibe->waiting_rsp = false; + ibe->inbuf[1] = ibe->outbuf[1] | 0x04; + ibe->inbuf[2] = ibe->outbuf[2]; + ibe->inbuf[3] = IPMI_CC_BMC_INIT_IN_PROGRESS; + k->handle_rsp(s, ibe->outbuf[0], ibe->inbuf + 1, 3); + } + break; + } +} + +static void ipmi_bmc_extern_handle_reset(IPMIBmc *b) +{ + IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(b); + + ibe->send_reset = true; + continue_send(ibe); +} + +static void ipmi_bmc_extern_realize(DeviceState *dev, Error **errp) +{ + IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(dev); + + if (!ibe->chr) { + error_setg(errp, "IPMI external bmc requires chardev attribute"); + return; + } + + qemu_chr_add_handlers(ibe->chr, can_receive, receive, chr_event, ibe); +} + +static void ipmi_bmc_extern_init(Object *obj) +{ + IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(obj); + + ibe->extern_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, extern_timeout, ibe); +} + +static Property ipmi_bmc_extern_properties[] = { + DEFINE_PROP_CHR("chardev", IPMIBmcExtern, chr), + DEFINE_PROP_END_OF_LIST(), +}; + +static void ipmi_bmc_extern_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + IPMIBmcClass *bk = IPMI_BMC_CLASS(oc); + + bk->handle_command = ipmi_bmc_extern_handle_command; + bk->handle_reset = ipmi_bmc_extern_handle_reset; + dc->realize = ipmi_bmc_extern_realize; + dc->props = ipmi_bmc_extern_properties; +} + +static const TypeInfo ipmi_bmc_extern_type = { + .name = TYPE_IPMI_BMC_EXTERN, + .parent = TYPE_IPMI_BMC, + .instance_size = sizeof(IPMIBmcExtern), + .instance_init = ipmi_bmc_extern_init, + .class_init = ipmi_bmc_extern_class_init, + }; + +static void ipmi_bmc_extern_register_types(void) +{ + type_register_static(&ipmi_bmc_extern_type); +} + +type_init(ipmi_bmc_extern_register_types) From 0719029c47b28404cf307f184934c8281a424918 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Thu, 17 Dec 2015 12:50:07 -0600 Subject: [PATCH 16/55] ipmi: Add an ISA KCS low-level interface This provides the simulation of the KCS hardware interface. Signed-off-by: Corey Minyard Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- default-configs/i386-softmmu.mak | 1 + default-configs/x86_64-softmmu.mak | 1 + hw/ipmi/Makefile.objs | 1 + hw/ipmi/isa_ipmi_kcs.c | 452 +++++++++++++++++++++++++++++ 4 files changed, 455 insertions(+) create mode 100644 hw/ipmi/isa_ipmi_kcs.c diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak index 25d745859e..a8d35d6a88 100644 --- a/default-configs/i386-softmmu.mak +++ b/default-configs/i386-softmmu.mak @@ -12,6 +12,7 @@ CONFIG_VMMOUSE=y CONFIG_IPMI=y CONFIG_IPMI_LOCAL=y CONFIG_IPMI_EXTERN=y +CONFIG_ISA_IPMI_KCS=y CONFIG_SERIAL=y CONFIG_PARALLEL=y CONFIG_I8254=y diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak index 5cff66d2a0..2999458ddf 100644 --- a/default-configs/x86_64-softmmu.mak +++ b/default-configs/x86_64-softmmu.mak @@ -12,6 +12,7 @@ CONFIG_VMMOUSE=y CONFIG_IPMI=y CONFIG_IPMI_LOCAL=y CONFIG_IPMI_EXTERN=y +CONFIG_ISA_IPMI_KCS=y CONFIG_SERIAL=y CONFIG_PARALLEL=y CONFIG_I8254=y diff --git a/hw/ipmi/Makefile.objs b/hw/ipmi/Makefile.objs index ddb15ecf7b..28618c54d2 100644 --- a/hw/ipmi/Makefile.objs +++ b/hw/ipmi/Makefile.objs @@ -1,3 +1,4 @@ common-obj-$(CONFIG_IPMI) += ipmi.o common-obj-$(CONFIG_IPMI_LOCAL) += ipmi_bmc_sim.o common-obj-$(CONFIG_IPMI_LOCAL) += ipmi_bmc_extern.o +common-obj-$(CONFIG_ISA_IPMI_KCS) += isa_ipmi_kcs.o diff --git a/hw/ipmi/isa_ipmi_kcs.c b/hw/ipmi/isa_ipmi_kcs.c new file mode 100644 index 0000000000..750a7251d2 --- /dev/null +++ b/hw/ipmi/isa_ipmi_kcs.c @@ -0,0 +1,452 @@ +/* + * QEMU ISA IPMI KCS emulation + * + * Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "hw/hw.h" +#include "hw/ipmi/ipmi.h" +#include "hw/isa/isa.h" +#include "hw/i386/pc.h" + +#define IPMI_KCS_OBF_BIT 0 +#define IPMI_KCS_IBF_BIT 1 +#define IPMI_KCS_SMS_ATN_BIT 2 +#define IPMI_KCS_CD_BIT 3 + +#define IPMI_KCS_OBF_MASK (1 << IPMI_KCS_OBF_BIT) +#define IPMI_KCS_GET_OBF(d) (((d) >> IPMI_KCS_OBF_BIT) & 0x1) +#define IPMI_KCS_SET_OBF(d, v) (d) = (((d) & ~IPMI_KCS_OBF_MASK) | \ + (((v) & 1) << IPMI_KCS_OBF_BIT)) +#define IPMI_KCS_IBF_MASK (1 << IPMI_KCS_IBF_BIT) +#define IPMI_KCS_GET_IBF(d) (((d) >> IPMI_KCS_IBF_BIT) & 0x1) +#define IPMI_KCS_SET_IBF(d, v) (d) = (((d) & ~IPMI_KCS_IBF_MASK) | \ + (((v) & 1) << IPMI_KCS_IBF_BIT)) +#define IPMI_KCS_SMS_ATN_MASK (1 << IPMI_KCS_SMS_ATN_BIT) +#define IPMI_KCS_GET_SMS_ATN(d) (((d) >> IPMI_KCS_SMS_ATN_BIT) & 0x1) +#define IPMI_KCS_SET_SMS_ATN(d, v) (d) = (((d) & ~IPMI_KCS_SMS_ATN_MASK) | \ + (((v) & 1) << IPMI_KCS_SMS_ATN_BIT)) +#define IPMI_KCS_CD_MASK (1 << IPMI_KCS_CD_BIT) +#define IPMI_KCS_GET_CD(d) (((d) >> IPMI_KCS_CD_BIT) & 0x1) +#define IPMI_KCS_SET_CD(d, v) (d) = (((d) & ~IPMI_KCS_CD_MASK) | \ + (((v) & 1) << IPMI_KCS_CD_BIT)) + +#define IPMI_KCS_IDLE_STATE 0 +#define IPMI_KCS_READ_STATE 1 +#define IPMI_KCS_WRITE_STATE 2 +#define IPMI_KCS_ERROR_STATE 3 + +#define IPMI_KCS_GET_STATE(d) (((d) >> 6) & 0x3) +#define IPMI_KCS_SET_STATE(d, v) ((d) = ((d) & ~0xc0) | (((v) & 0x3) << 6)) + +#define IPMI_KCS_ABORT_STATUS_CMD 0x60 +#define IPMI_KCS_WRITE_START_CMD 0x61 +#define IPMI_KCS_WRITE_END_CMD 0x62 +#define IPMI_KCS_READ_CMD 0x68 + +#define IPMI_KCS_STATUS_NO_ERR 0x00 +#define IPMI_KCS_STATUS_ABORTED_ERR 0x01 +#define IPMI_KCS_STATUS_BAD_CC_ERR 0x02 +#define IPMI_KCS_STATUS_LENGTH_ERR 0x06 + +typedef struct IPMIKCS { + IPMIBmc *bmc; + + bool do_wake; + + qemu_irq irq; + + uint32_t io_base; + unsigned long io_length; + MemoryRegion io; + + bool obf_irq_set; + bool atn_irq_set; + bool use_irq; + bool irqs_enabled; + + uint8_t outmsg[MAX_IPMI_MSG_SIZE]; + uint32_t outpos; + uint32_t outlen; + + uint8_t inmsg[MAX_IPMI_MSG_SIZE]; + uint32_t inlen; + bool write_end; + + uint8_t status_reg; + uint8_t data_out_reg; + + int16_t data_in_reg; /* -1 means not written */ + int16_t cmd_reg; + + /* + * This is a response number that we send with the command to make + * sure that the response matches the command. + */ + uint8_t waiting_rsp; +} IPMIKCS; + +#define SET_OBF() \ + do { \ + IPMI_KCS_SET_OBF(ik->status_reg, 1); \ + if (ik->use_irq && ik->irqs_enabled && !ik->obf_irq_set) { \ + ik->obf_irq_set = 1; \ + if (!ik->atn_irq_set) { \ + qemu_irq_raise(ik->irq); \ + } \ + } \ + } while (0) + +static void ipmi_kcs_signal(IPMIKCS *ik, IPMIInterface *ii) +{ + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + + ik->do_wake = 1; + while (ik->do_wake) { + ik->do_wake = 0; + iic->handle_if_event(ii); + } +} + +static void ipmi_kcs_handle_event(IPMIInterface *ii) +{ + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + IPMIKCS *ik = iic->get_backend_data(ii); + + if (ik->cmd_reg == IPMI_KCS_ABORT_STATUS_CMD) { + if (IPMI_KCS_GET_STATE(ik->status_reg) != IPMI_KCS_ERROR_STATE) { + ik->waiting_rsp++; /* Invalidate the message */ + ik->outmsg[0] = IPMI_KCS_STATUS_ABORTED_ERR; + ik->outlen = 1; + ik->outpos = 0; + IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_ERROR_STATE); + SET_OBF(); + } + goto out; + } + + switch (IPMI_KCS_GET_STATE(ik->status_reg)) { + case IPMI_KCS_IDLE_STATE: + if (ik->cmd_reg == IPMI_KCS_WRITE_START_CMD) { + IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_WRITE_STATE); + ik->cmd_reg = -1; + ik->write_end = 0; + ik->inlen = 0; + SET_OBF(); + } + break; + + case IPMI_KCS_READ_STATE: + handle_read: + if (ik->outpos >= ik->outlen) { + IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_IDLE_STATE); + SET_OBF(); + } else if (ik->data_in_reg == IPMI_KCS_READ_CMD) { + ik->data_out_reg = ik->outmsg[ik->outpos]; + ik->outpos++; + SET_OBF(); + } else { + ik->outmsg[0] = IPMI_KCS_STATUS_BAD_CC_ERR; + ik->outlen = 1; + ik->outpos = 0; + IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_ERROR_STATE); + SET_OBF(); + goto out; + } + break; + + case IPMI_KCS_WRITE_STATE: + if (ik->data_in_reg != -1) { + /* + * Don't worry about input overrun here, that will be + * handled in the BMC. + */ + if (ik->inlen < sizeof(ik->inmsg)) { + ik->inmsg[ik->inlen] = ik->data_in_reg; + } + ik->inlen++; + } + if (ik->write_end) { + IPMIBmcClass *bk = IPMI_BMC_GET_CLASS(ik->bmc); + ik->outlen = 0; + ik->write_end = 0; + ik->outpos = 0; + bk->handle_command(ik->bmc, ik->inmsg, ik->inlen, sizeof(ik->inmsg), + ik->waiting_rsp); + goto out_noibf; + } else if (ik->cmd_reg == IPMI_KCS_WRITE_END_CMD) { + ik->cmd_reg = -1; + ik->write_end = 1; + } + SET_OBF(); + break; + + case IPMI_KCS_ERROR_STATE: + if (ik->data_in_reg != -1) { + IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_READ_STATE); + ik->data_in_reg = IPMI_KCS_READ_CMD; + goto handle_read; + } + break; + } + + if (ik->cmd_reg != -1) { + /* Got an invalid command */ + ik->outmsg[0] = IPMI_KCS_STATUS_BAD_CC_ERR; + ik->outlen = 1; + ik->outpos = 0; + IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_ERROR_STATE); + } + + out: + ik->cmd_reg = -1; + ik->data_in_reg = -1; + IPMI_KCS_SET_IBF(ik->status_reg, 0); + out_noibf: + return; +} + +static void ipmi_kcs_handle_rsp(IPMIInterface *ii, uint8_t msg_id, + unsigned char *rsp, unsigned int rsp_len) +{ + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + IPMIKCS *ik = iic->get_backend_data(ii); + + if (ik->waiting_rsp == msg_id) { + ik->waiting_rsp++; + if (rsp_len > sizeof(ik->outmsg)) { + ik->outmsg[0] = rsp[0]; + ik->outmsg[1] = rsp[1]; + ik->outmsg[2] = IPMI_CC_CANNOT_RETURN_REQ_NUM_BYTES; + ik->outlen = 3; + } else { + memcpy(ik->outmsg, rsp, rsp_len); + ik->outlen = rsp_len; + } + IPMI_KCS_SET_STATE(ik->status_reg, IPMI_KCS_READ_STATE); + ik->data_in_reg = IPMI_KCS_READ_CMD; + ipmi_kcs_signal(ik, ii); + } +} + + +static uint64_t ipmi_kcs_ioport_read(void *opaque, hwaddr addr, unsigned size) +{ + IPMIInterface *ii = opaque; + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + IPMIKCS *ik = iic->get_backend_data(ii); + uint32_t ret; + + switch (addr & 1) { + case 0: + ret = ik->data_out_reg; + IPMI_KCS_SET_OBF(ik->status_reg, 0); + if (ik->obf_irq_set) { + ik->obf_irq_set = 0; + if (!ik->atn_irq_set) { + qemu_irq_lower(ik->irq); + } + } + break; + case 1: + ret = ik->status_reg; + if (ik->atn_irq_set) { + ik->atn_irq_set = 0; + if (!ik->obf_irq_set) { + qemu_irq_lower(ik->irq); + } + } + break; + } + return ret; +} + +static void ipmi_kcs_ioport_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + IPMIInterface *ii = opaque; + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + IPMIKCS *ik = iic->get_backend_data(ii); + + if (IPMI_KCS_GET_IBF(ik->status_reg)) { + return; + } + + switch (addr & 1) { + case 0: + ik->data_in_reg = val; + break; + + case 1: + ik->cmd_reg = val; + break; + } + IPMI_KCS_SET_IBF(ik->status_reg, 1); + ipmi_kcs_signal(ik, ii); +} + +const MemoryRegionOps ipmi_kcs_io_ops = { + .read = ipmi_kcs_ioport_read, + .write = ipmi_kcs_ioport_write, + .impl = { + .min_access_size = 1, + .max_access_size = 1, + }, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void ipmi_kcs_set_atn(IPMIInterface *ii, int val, int irq) +{ + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + IPMIKCS *ik = iic->get_backend_data(ii); + + IPMI_KCS_SET_SMS_ATN(ik->status_reg, val); + if (val) { + if (irq && !ik->atn_irq_set && ik->use_irq && ik->irqs_enabled) { + ik->atn_irq_set = 1; + if (!ik->obf_irq_set) { + qemu_irq_raise(ik->irq); + } + } + } else { + if (ik->atn_irq_set) { + ik->atn_irq_set = 0; + if (!ik->obf_irq_set) { + qemu_irq_lower(ik->irq); + } + } + } +} + +static void ipmi_kcs_set_irq_enable(IPMIInterface *ii, int val) +{ + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + IPMIKCS *ik = iic->get_backend_data(ii); + + ik->irqs_enabled = val; +} + +static void ipmi_kcs_init(IPMIInterface *ii, Error **errp) +{ + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + IPMIKCS *ik = iic->get_backend_data(ii); + + ik->io_length = 2; + memory_region_init_io(&ik->io, NULL, &ipmi_kcs_io_ops, ii, "ipmi-kcs", 2); +} + +static void ipmi_kcs_class_init(IPMIInterfaceClass *iic) +{ + iic->init = ipmi_kcs_init; + iic->set_atn = ipmi_kcs_set_atn; + iic->handle_rsp = ipmi_kcs_handle_rsp; + iic->handle_if_event = ipmi_kcs_handle_event; + iic->set_irq_enable = ipmi_kcs_set_irq_enable; +} + + +#define TYPE_ISA_IPMI_KCS "isa-ipmi-kcs" +#define ISA_IPMI_KCS(obj) OBJECT_CHECK(ISAIPMIKCSDevice, (obj), \ + TYPE_ISA_IPMI_KCS) + +typedef struct ISAIPMIKCSDevice { + ISADevice dev; + int32 isairq; + IPMIKCS kcs; +} ISAIPMIKCSDevice; + +static void ipmi_isa_realize(DeviceState *dev, Error **errp) +{ + ISADevice *isadev = ISA_DEVICE(dev); + ISAIPMIKCSDevice *iik = ISA_IPMI_KCS(dev); + IPMIInterface *ii = IPMI_INTERFACE(dev); + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + + if (!iik->kcs.bmc) { + error_setg(errp, "IPMI device requires a bmc attribute to be set"); + return; + } + + iik->kcs.bmc->intf = ii; + + iic->init(ii, errp); + if (*errp) + return; + + if (iik->isairq > 0) { + isa_init_irq(isadev, &iik->kcs.irq, iik->isairq); + iik->kcs.use_irq = 1; + } + + qdev_set_legacy_instance_id(dev, iik->kcs.io_base, iik->kcs.io_length); + + isa_register_ioport(isadev, &iik->kcs.io, iik->kcs.io_base); +} + +static void isa_ipmi_kcs_init(Object *obj) +{ + ISAIPMIKCSDevice *iik = ISA_IPMI_KCS(obj); + + ipmi_bmc_find_and_link(obj, (Object **) &iik->kcs.bmc); +} + +static void *isa_ipmi_kcs_get_backend_data(IPMIInterface *ii) +{ + ISAIPMIKCSDevice *iik = ISA_IPMI_KCS(ii); + + return &iik->kcs; +} + +static Property ipmi_isa_properties[] = { + DEFINE_PROP_UINT32("ioport", ISAIPMIKCSDevice, kcs.io_base, 0xca2), + DEFINE_PROP_INT32("irq", ISAIPMIKCSDevice, isairq, 5), + DEFINE_PROP_END_OF_LIST(), +}; + +static void isa_ipmi_kcs_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + IPMIInterfaceClass *iic = IPMI_INTERFACE_CLASS(oc); + + dc->realize = ipmi_isa_realize; + dc->props = ipmi_isa_properties; + + iic->get_backend_data = isa_ipmi_kcs_get_backend_data; + ipmi_kcs_class_init(iic); +} + +static const TypeInfo isa_ipmi_kcs_info = { + .name = TYPE_ISA_IPMI_KCS, + .parent = TYPE_ISA_DEVICE, + .instance_size = sizeof(ISAIPMIKCSDevice), + .instance_init = isa_ipmi_kcs_init, + .class_init = isa_ipmi_kcs_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_IPMI_INTERFACE }, + { } + } +}; + +static void ipmi_register_types(void) +{ + type_register_static(&isa_ipmi_kcs_info); +} + +type_init(ipmi_register_types) From a9b74079cbf68720bbe9b409775bed8e1002cf4f Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Thu, 17 Dec 2015 12:50:08 -0600 Subject: [PATCH 17/55] ipmi: Add a BT low-level interface This provides the simulation of the BT hardware interface for IPMI. Signed-off-by: Corey Minyard Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- default-configs/i386-softmmu.mak | 1 + default-configs/x86_64-softmmu.mak | 1 + hw/ipmi/Makefile.objs | 1 + hw/ipmi/isa_ipmi_bt.c | 489 +++++++++++++++++++++++++++++ 4 files changed, 492 insertions(+) create mode 100644 hw/ipmi/isa_ipmi_bt.c diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak index a8d35d6a88..fb1ad90a03 100644 --- a/default-configs/i386-softmmu.mak +++ b/default-configs/i386-softmmu.mak @@ -13,6 +13,7 @@ CONFIG_IPMI=y CONFIG_IPMI_LOCAL=y CONFIG_IPMI_EXTERN=y CONFIG_ISA_IPMI_KCS=y +CONFIG_ISA_IPMI_BT=y CONFIG_SERIAL=y CONFIG_PARALLEL=y CONFIG_I8254=y diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak index 2999458ddf..613154fda4 100644 --- a/default-configs/x86_64-softmmu.mak +++ b/default-configs/x86_64-softmmu.mak @@ -13,6 +13,7 @@ CONFIG_IPMI=y CONFIG_IPMI_LOCAL=y CONFIG_IPMI_EXTERN=y CONFIG_ISA_IPMI_KCS=y +CONFIG_ISA_IPMI_BT=y CONFIG_SERIAL=y CONFIG_PARALLEL=y CONFIG_I8254=y diff --git a/hw/ipmi/Makefile.objs b/hw/ipmi/Makefile.objs index 28618c54d2..a90318d5ba 100644 --- a/hw/ipmi/Makefile.objs +++ b/hw/ipmi/Makefile.objs @@ -2,3 +2,4 @@ common-obj-$(CONFIG_IPMI) += ipmi.o common-obj-$(CONFIG_IPMI_LOCAL) += ipmi_bmc_sim.o common-obj-$(CONFIG_IPMI_LOCAL) += ipmi_bmc_extern.o common-obj-$(CONFIG_ISA_IPMI_KCS) += isa_ipmi_kcs.o +common-obj-$(CONFIG_ISA_IPMI_BT) += isa_ipmi_bt.o diff --git a/hw/ipmi/isa_ipmi_bt.c b/hw/ipmi/isa_ipmi_bt.c new file mode 100644 index 0000000000..70e2c0d236 --- /dev/null +++ b/hw/ipmi/isa_ipmi_bt.c @@ -0,0 +1,489 @@ +/* + * QEMU ISA IPMI BT emulation + * + * Copyright (c) 2015 Corey Minyard, MontaVista Software, LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include "hw/hw.h" +#include "hw/ipmi/ipmi.h" +#include "hw/isa/isa.h" +#include "hw/i386/pc.h" + +/* Control register */ +#define IPMI_BT_CLR_WR_BIT 0 +#define IPMI_BT_CLR_RD_BIT 1 +#define IPMI_BT_H2B_ATN_BIT 2 +#define IPMI_BT_B2H_ATN_BIT 3 +#define IPMI_BT_SMS_ATN_BIT 4 +#define IPMI_BT_HBUSY_BIT 6 +#define IPMI_BT_BBUSY_BIT 7 + +#define IPMI_BT_CLR_WR_MASK (1 << IPMI_BT_CLR_WR_BIT) +#define IPMI_BT_GET_CLR_WR(d) (((d) >> IPMI_BT_CLR_WR_BIT) & 0x1) +#define IPMI_BT_SET_CLR_WR(d, v) (d) = (((d) & ~IPMI_BT_CLR_WR_MASK) | \ + (((v & 1) << IPMI_BT_CLR_WR_BIT))) + +#define IPMI_BT_CLR_RD_MASK (1 << IPMI_BT_CLR_RD_BIT) +#define IPMI_BT_GET_CLR_RD(d) (((d) >> IPMI_BT_CLR_RD_BIT) & 0x1) +#define IPMI_BT_SET_CLR_RD(d, v) (d) = (((d) & ~IPMI_BT_CLR_RD_MASK) | \ + (((v & 1) << IPMI_BT_CLR_RD_BIT))) + +#define IPMI_BT_H2B_ATN_MASK (1 << IPMI_BT_H2B_ATN_BIT) +#define IPMI_BT_GET_H2B_ATN(d) (((d) >> IPMI_BT_H2B_ATN_BIT) & 0x1) +#define IPMI_BT_SET_H2B_ATN(d, v) (d) = (((d) & ~IPMI_BT_H2B_ATN_MASK) | \ + (((v & 1) << IPMI_BT_H2B_ATN_BIT))) + +#define IPMI_BT_B2H_ATN_MASK (1 << IPMI_BT_B2H_ATN_BIT) +#define IPMI_BT_GET_B2H_ATN(d) (((d) >> IPMI_BT_B2H_ATN_BIT) & 0x1) +#define IPMI_BT_SET_B2H_ATN(d, v) (d) = (((d) & ~IPMI_BT_B2H_ATN_MASK) | \ + (((v & 1) << IPMI_BT_B2H_ATN_BIT))) + +#define IPMI_BT_SMS_ATN_MASK (1 << IPMI_BT_SMS_ATN_BIT) +#define IPMI_BT_GET_SMS_ATN(d) (((d) >> IPMI_BT_SMS_ATN_BIT) & 0x1) +#define IPMI_BT_SET_SMS_ATN(d, v) (d) = (((d) & ~IPMI_BT_SMS_ATN_MASK) | \ + (((v & 1) << IPMI_BT_SMS_ATN_BIT))) + +#define IPMI_BT_HBUSY_MASK (1 << IPMI_BT_HBUSY_BIT) +#define IPMI_BT_GET_HBUSY(d) (((d) >> IPMI_BT_HBUSY_BIT) & 0x1) +#define IPMI_BT_SET_HBUSY(d, v) (d) = (((d) & ~IPMI_BT_HBUSY_MASK) | \ + (((v & 1) << IPMI_BT_HBUSY_BIT))) + +#define IPMI_BT_BBUSY_MASK (1 << IPMI_BT_BBUSY_BIT) +#define IPMI_BT_GET_BBUSY(d) (((d) >> IPMI_BT_BBUSY_BIT) & 0x1) +#define IPMI_BT_SET_BBUSY(d, v) (d) = (((d) & ~IPMI_BT_BBUSY_MASK) | \ + (((v & 1) << IPMI_BT_BBUSY_BIT))) + + +/* Mask register */ +#define IPMI_BT_B2H_IRQ_EN_BIT 0 +#define IPMI_BT_B2H_IRQ_BIT 1 + +#define IPMI_BT_B2H_IRQ_EN_MASK (1 << IPMI_BT_B2H_IRQ_EN_BIT) +#define IPMI_BT_GET_B2H_IRQ_EN(d) (((d) >> IPMI_BT_B2H_IRQ_EN_BIT) & 0x1) +#define IPMI_BT_SET_B2H_IRQ_EN(d, v) (d) = (((d) & ~IPMI_BT_B2H_IRQ_EN_MASK) | \ + (((v & 1) << IPMI_BT_B2H_IRQ_EN_BIT))) + +#define IPMI_BT_B2H_IRQ_MASK (1 << IPMI_BT_B2H_IRQ_BIT) +#define IPMI_BT_GET_B2H_IRQ(d) (((d) >> IPMI_BT_B2H_IRQ_BIT) & 0x1) +#define IPMI_BT_SET_B2H_IRQ(d, v) (d) = (((d) & ~IPMI_BT_B2H_IRQ_MASK) | \ + (((v & 1) << IPMI_BT_B2H_IRQ_BIT))) + +typedef struct IPMIBT { + IPMIBmc *bmc; + + bool do_wake; + + qemu_irq irq; + + uint32_t io_base; + unsigned long io_length; + MemoryRegion io; + + bool obf_irq_set; + bool atn_irq_set; + bool use_irq; + bool irqs_enabled; + + uint8_t outmsg[MAX_IPMI_MSG_SIZE]; + uint32_t outpos; + uint32_t outlen; + + uint8_t inmsg[MAX_IPMI_MSG_SIZE]; + uint32_t inlen; + + uint8_t control_reg; + uint8_t mask_reg; + + /* + * This is a response number that we send with the command to make + * sure that the response matches the command. + */ + uint8_t waiting_rsp; + uint8_t waiting_seq; +} IPMIBT; + +#define IPMI_CMD_GET_BT_INTF_CAP 0x36 + +static void ipmi_bt_handle_event(IPMIInterface *ii) +{ + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + IPMIBT *ib = iic->get_backend_data(ii); + + if (ib->inlen < 4) { + goto out; + } + /* Note that overruns are handled by handle_command */ + if (ib->inmsg[0] != (ib->inlen - 1)) { + /* Length mismatch, just ignore. */ + IPMI_BT_SET_BBUSY(ib->control_reg, 1); + ib->inlen = 0; + goto out; + } + if ((ib->inmsg[1] == (IPMI_NETFN_APP << 2)) && + (ib->inmsg[3] == IPMI_CMD_GET_BT_INTF_CAP)) { + /* We handle this one ourselves. */ + ib->outmsg[0] = 9; + ib->outmsg[1] = ib->inmsg[1] | 0x04; + ib->outmsg[2] = ib->inmsg[2]; + ib->outmsg[3] = ib->inmsg[3]; + ib->outmsg[4] = 0; + ib->outmsg[5] = 1; /* Only support 1 outstanding request. */ + if (sizeof(ib->inmsg) > 0xff) { /* Input buffer size */ + ib->outmsg[6] = 0xff; + } else { + ib->outmsg[6] = (unsigned char) sizeof(ib->inmsg); + } + if (sizeof(ib->outmsg) > 0xff) { /* Output buffer size */ + ib->outmsg[7] = 0xff; + } else { + ib->outmsg[7] = (unsigned char) sizeof(ib->outmsg); + } + ib->outmsg[8] = 10; /* Max request to response time */ + ib->outmsg[9] = 0; /* Don't recommend retries */ + ib->outlen = 10; + IPMI_BT_SET_BBUSY(ib->control_reg, 0); + IPMI_BT_SET_B2H_ATN(ib->control_reg, 1); + if (ib->use_irq && ib->irqs_enabled && + !IPMI_BT_GET_B2H_IRQ(ib->mask_reg) && + IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) { + IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1); + qemu_irq_raise(ib->irq); + } + goto out; + } + ib->waiting_seq = ib->inmsg[2]; + ib->inmsg[2] = ib->inmsg[1]; + { + IPMIBmcClass *bk = IPMI_BMC_GET_CLASS(ib->bmc); + bk->handle_command(ib->bmc, ib->inmsg + 2, ib->inlen - 2, + sizeof(ib->inmsg), ib->waiting_rsp); + } + out: + return; +} + +static void ipmi_bt_handle_rsp(IPMIInterface *ii, uint8_t msg_id, + unsigned char *rsp, unsigned int rsp_len) +{ + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + IPMIBT *ib = iic->get_backend_data(ii); + + if (ib->waiting_rsp == msg_id) { + ib->waiting_rsp++; + if (rsp_len > (sizeof(ib->outmsg) - 2)) { + ib->outmsg[0] = 4; + ib->outmsg[1] = rsp[0]; + ib->outmsg[2] = ib->waiting_seq; + ib->outmsg[3] = rsp[1]; + ib->outmsg[4] = IPMI_CC_CANNOT_RETURN_REQ_NUM_BYTES; + ib->outlen = 5; + } else { + ib->outmsg[0] = rsp_len + 1; + ib->outmsg[1] = rsp[0]; + ib->outmsg[2] = ib->waiting_seq; + memcpy(ib->outmsg + 3, rsp + 1, rsp_len - 1); + ib->outlen = rsp_len + 2; + } + IPMI_BT_SET_BBUSY(ib->control_reg, 0); + IPMI_BT_SET_B2H_ATN(ib->control_reg, 1); + if (ib->use_irq && ib->irqs_enabled && + !IPMI_BT_GET_B2H_IRQ(ib->mask_reg) && + IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) { + IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1); + qemu_irq_raise(ib->irq); + } + } +} + + +static uint64_t ipmi_bt_ioport_read(void *opaque, hwaddr addr, unsigned size) +{ + IPMIInterface *ii = opaque; + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + IPMIBT *ib = iic->get_backend_data(ii); + uint32_t ret = 0xff; + + switch (addr & 3) { + case 0: + ret = ib->control_reg; + break; + case 1: + if (ib->outpos < ib->outlen) { + ret = ib->outmsg[ib->outpos]; + ib->outpos++; + if (ib->outpos == ib->outlen) { + ib->outpos = 0; + ib->outlen = 0; + } + } else { + ret = 0xff; + } + break; + case 2: + ret = ib->mask_reg; + break; + } + return ret; +} + +static void ipmi_bt_signal(IPMIBT *ib, IPMIInterface *ii) +{ + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + + ib->do_wake = 1; + while (ib->do_wake) { + ib->do_wake = 0; + iic->handle_if_event(ii); + } +} + +static void ipmi_bt_ioport_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + IPMIInterface *ii = opaque; + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + IPMIBT *ib = iic->get_backend_data(ii); + + switch (addr & 3) { + case 0: + if (IPMI_BT_GET_CLR_WR(val)) { + ib->inlen = 0; + } + if (IPMI_BT_GET_CLR_RD(val)) { + ib->outpos = 0; + } + if (IPMI_BT_GET_B2H_ATN(val)) { + IPMI_BT_SET_B2H_ATN(ib->control_reg, 0); + } + if (IPMI_BT_GET_SMS_ATN(val)) { + IPMI_BT_SET_SMS_ATN(ib->control_reg, 0); + } + if (IPMI_BT_GET_HBUSY(val)) { + /* Toggle */ + IPMI_BT_SET_HBUSY(ib->control_reg, + !IPMI_BT_GET_HBUSY(ib->control_reg)); + } + if (IPMI_BT_GET_H2B_ATN(val)) { + IPMI_BT_SET_BBUSY(ib->control_reg, 1); + ipmi_bt_signal(ib, ii); + } + break; + + case 1: + if (ib->inlen < sizeof(ib->inmsg)) { + ib->inmsg[ib->inlen] = val; + } + ib->inlen++; + break; + + case 2: + if (IPMI_BT_GET_B2H_IRQ_EN(val) != + IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) { + if (IPMI_BT_GET_B2H_IRQ_EN(val)) { + if (IPMI_BT_GET_B2H_ATN(ib->control_reg) || + IPMI_BT_GET_SMS_ATN(ib->control_reg)) { + IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1); + qemu_irq_raise(ib->irq); + } + IPMI_BT_SET_B2H_IRQ_EN(ib->mask_reg, 1); + } else { + if (IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) { + IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0); + qemu_irq_lower(ib->irq); + } + IPMI_BT_SET_B2H_IRQ_EN(ib->mask_reg, 0); + } + } + if (IPMI_BT_GET_B2H_IRQ(val) && IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) { + IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0); + qemu_irq_lower(ib->irq); + } + break; + } +} + +static const MemoryRegionOps ipmi_bt_io_ops = { + .read = ipmi_bt_ioport_read, + .write = ipmi_bt_ioport_write, + .impl = { + .min_access_size = 1, + .max_access_size = 1, + }, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void ipmi_bt_set_atn(IPMIInterface *ii, int val, int irq) +{ + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + IPMIBT *ib = iic->get_backend_data(ii); + + if (!!val == IPMI_BT_GET_SMS_ATN(ib->control_reg)) { + return; + } + + IPMI_BT_SET_SMS_ATN(ib->control_reg, val); + if (val) { + if (irq && ib->use_irq && ib->irqs_enabled && + !IPMI_BT_GET_B2H_ATN(ib->control_reg) && + IPMI_BT_GET_B2H_IRQ_EN(ib->mask_reg)) { + IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1); + qemu_irq_raise(ib->irq); + } + } else { + if (!IPMI_BT_GET_B2H_ATN(ib->control_reg) && + IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) { + IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0); + qemu_irq_lower(ib->irq); + } + } +} + +static void ipmi_bt_handle_reset(IPMIInterface *ii, bool is_cold) +{ + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + IPMIBT *ib = iic->get_backend_data(ii); + + if (is_cold) { + /* Disable the BT interrupt on reset */ + if (IPMI_BT_GET_B2H_IRQ(ib->mask_reg)) { + IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 0); + qemu_irq_lower(ib->irq); + } + IPMI_BT_SET_B2H_IRQ_EN(ib->mask_reg, 0); + } +} + +static void ipmi_bt_set_irq_enable(IPMIInterface *ii, int val) +{ + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + IPMIBT *ib = iic->get_backend_data(ii); + + ib->irqs_enabled = val; +} + +static void ipmi_bt_init(IPMIInterface *ii, Error **errp) +{ + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + IPMIBT *ib = iic->get_backend_data(ii); + + ib->io_length = 3; + + memory_region_init_io(&ib->io, NULL, &ipmi_bt_io_ops, ii, "ipmi-bt", 3); +} + +static void ipmi_bt_class_init(IPMIInterfaceClass *iic) +{ + iic->init = ipmi_bt_init; + iic->set_atn = ipmi_bt_set_atn; + iic->handle_rsp = ipmi_bt_handle_rsp; + iic->handle_if_event = ipmi_bt_handle_event; + iic->set_irq_enable = ipmi_bt_set_irq_enable; + iic->reset = ipmi_bt_handle_reset; +} + + +#define TYPE_ISA_IPMI_BT "isa-ipmi-bt" +#define ISA_IPMI_BT(obj) OBJECT_CHECK(ISAIPMIBTDevice, (obj), \ + TYPE_ISA_IPMI_BT) + +typedef struct ISAIPMIBTDevice { + ISADevice dev; + int32 isairq; + IPMIBT bt; +} ISAIPMIBTDevice; + +static void isa_ipmi_bt_realize(DeviceState *dev, Error **errp) +{ + ISADevice *isadev = ISA_DEVICE(dev); + ISAIPMIBTDevice *iib = ISA_IPMI_BT(dev); + IPMIInterface *ii = IPMI_INTERFACE(dev); + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + + if (!iib->bt.bmc) { + error_setg(errp, "IPMI device requires a bmc attribute to be set"); + return; + } + + iib->bt.bmc->intf = ii; + + iic->init(ii, errp); + if (*errp) + return; + + if (iib->isairq > 0) { + isa_init_irq(isadev, &iib->bt.irq, iib->isairq); + iib->bt.use_irq = 1; + } + + qdev_set_legacy_instance_id(dev, iib->bt.io_base, iib->bt.io_length); + + isa_register_ioport(isadev, &iib->bt.io, iib->bt.io_base); +} + +static void isa_ipmi_bt_init(Object *obj) +{ + ISAIPMIBTDevice *iib = ISA_IPMI_BT(obj); + + ipmi_bmc_find_and_link(obj, (Object **) &iib->bt.bmc); +} + +static void *isa_ipmi_bt_get_backend_data(IPMIInterface *ii) +{ + ISAIPMIBTDevice *iib = ISA_IPMI_BT(ii); + + return &iib->bt; +} + +static Property ipmi_isa_properties[] = { + DEFINE_PROP_UINT32("ioport", ISAIPMIBTDevice, bt.io_base, 0xe4), + DEFINE_PROP_INT32("irq", ISAIPMIBTDevice, isairq, 5), + DEFINE_PROP_END_OF_LIST(), +}; + +static void isa_ipmi_bt_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + IPMIInterfaceClass *iic = IPMI_INTERFACE_CLASS(oc); + + dc->realize = isa_ipmi_bt_realize; + dc->props = ipmi_isa_properties; + + iic->get_backend_data = isa_ipmi_bt_get_backend_data; + ipmi_bt_class_init(iic); +} + +static const TypeInfo isa_ipmi_bt_info = { + .name = TYPE_ISA_IPMI_BT, + .parent = TYPE_ISA_DEVICE, + .instance_size = sizeof(ISAIPMIBTDevice), + .instance_init = isa_ipmi_bt_init, + .class_init = isa_ipmi_bt_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_IPMI_INTERFACE }, + { } + } +}; + +static void ipmi_register_types(void) +{ + type_register_static(&isa_ipmi_bt_info); +} + +type_init(ipmi_register_types) From 24f976d30aa3f7a8912a3d5c0cfaabcc3be9f9a5 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Thu, 17 Dec 2015 12:50:09 -0600 Subject: [PATCH 18/55] ipmi: Add tests Test the KCS interface with a local BMC and a BT interface with an external BMC. Signed-off-by: Corey Minyard Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/Makefile | 4 + tests/ipmi-bt-test.c | 436 ++++++++++++++++++++++++++++++++++++++++++ tests/ipmi-kcs-test.c | 295 ++++++++++++++++++++++++++++ 3 files changed, 735 insertions(+) create mode 100644 tests/ipmi-bt-test.c create mode 100644 tests/ipmi-kcs-test.c diff --git a/tests/Makefile b/tests/Makefile index 7d2a0d36ee..b7352f1a35 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -174,6 +174,8 @@ gcov-files-i386-y += hw/block/hd-geometry.c check-qtest-i386-y += tests/boot-order-test$(EXESUF) check-qtest-i386-y += tests/bios-tables-test$(EXESUF) check-qtest-i386-y += tests/rtc-test$(EXESUF) +check-qtest-i386-y += tests/ipmi-kcs-test$(EXESUF) +check-qtest-i386-y += tests/ipmi-bt-test$(EXESUF) check-qtest-i386-y += tests/i440fx-test$(EXESUF) check-qtest-i386-y += tests/fw_cfg-test$(EXESUF) check-qtest-i386-y += tests/drive_del-test$(EXESUF) @@ -512,6 +514,8 @@ tests/spapr-phb-test$(EXESUF): tests/spapr-phb-test.o $(libqos-obj-y) tests/fdc-test$(EXESUF): tests/fdc-test.o tests/ide-test$(EXESUF): tests/ide-test.o $(libqos-pc-obj-y) tests/ahci-test$(EXESUF): tests/ahci-test.o $(libqos-pc-obj-y) +tests/ipmi-kcs-test$(EXESUF): tests/ipmi-kcs-test.o +tests/ipmi-bt-test$(EXESUF): tests/ipmi-bt-test.o tests/hd-geo-test$(EXESUF): tests/hd-geo-test.o tests/boot-order-test$(EXESUF): tests/boot-order-test.o $(libqos-obj-y) tests/bios-tables-test$(EXESUF): tests/bios-tables-test.o $(libqos-obj-y) diff --git a/tests/ipmi-bt-test.c b/tests/ipmi-bt-test.c new file mode 100644 index 0000000000..5b7eb73949 --- /dev/null +++ b/tests/ipmi-bt-test.c @@ -0,0 +1,436 @@ +/* + * IPMI BT test cases, using the external interface for checking + * + * Copyright (c) 2012 Corey Minyard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "libqtest.h" +#include "qemu-common.h" + +#define IPMI_IRQ 5 + +#define IPMI_BT_BASE 0xe4 + +#define IPMI_BT_CTLREG_CLR_WR_PTR 0 +#define IPMI_BT_CTLREG_CLR_RD_PTR 1 +#define IPMI_BT_CTLREG_H2B_ATN 2 +#define IPMI_BT_CTLREG_B2H_ATN 3 +#define IPMI_BT_CTLREG_SMS_ATN 4 +#define IPMI_BT_CTLREG_H_BUSY 6 +#define IPMI_BT_CTLREG_B_BUSY 7 + +#define IPMI_BT_CTLREG_GET(b) ((bt_get_ctrlreg() >> (b)) & 1) +#define IPMI_BT_CTLREG_GET_H2B_ATN() IPMI_BT_CTLREG_GET(IPMI_BT_CTLREG_H2B_ATN) +#define IPMI_BT_CTLREG_GET_B2H_ATN() IPMI_BT_CTLREG_GET(IPMI_BT_CTLREG_B2H_ATN) +#define IPMI_BT_CTLREG_GET_SMS_ATN() IPMI_BT_CTLREG_GET(IPMI_BT_CTLREG_SMS_ATN) +#define IPMI_BT_CTLREG_GET_H_BUSY() IPMI_BT_CTLREG_GET(IPMI_BT_CTLREG_H_BUSY) +#define IPMI_BT_CTLREG_GET_B_BUSY() IPMI_BT_CTLREG_GET(IPMI_BT_CTLREG_B_BUSY) + +#define IPMI_BT_CTLREG_SET(b) bt_write_ctrlreg(1 << (b)) +#define IPMI_BT_CTLREG_SET_CLR_WR_PTR() IPMI_BT_CTLREG_SET( \ + IPMI_BT_CTLREG_CLR_WR_PTR) +#define IPMI_BT_CTLREG_SET_CLR_RD_PTR() IPMI_BT_CTLREG_SET( \ + IPMI_BT_CTLREG_CLR_RD_PTR) +#define IPMI_BT_CTLREG_SET_H2B_ATN() IPMI_BT_CTLREG_SET(IPMI_BT_CTLREG_H2B_ATN) +#define IPMI_BT_CTLREG_SET_B2H_ATN() IPMI_BT_CTLREG_SET(IPMI_BT_CTLREG_B2H_ATN) +#define IPMI_BT_CTLREG_SET_SMS_ATN() IPMI_BT_CTLREG_SET(IPMI_BT_CTLREG_SMS_ATN) +#define IPMI_BT_CTLREG_SET_H_BUSY() IPMI_BT_CTLREG_SET(IPMI_BT_CTLREG_H_BUSY) + +static int bt_ints_enabled; + +static uint8_t bt_get_ctrlreg(void) +{ + return inb(IPMI_BT_BASE); +} + +static void bt_write_ctrlreg(uint8_t val) +{ + outb(IPMI_BT_BASE, val); +} + +static uint8_t bt_get_buf(void) +{ + return inb(IPMI_BT_BASE + 1); +} + +static void bt_write_buf(uint8_t val) +{ + outb(IPMI_BT_BASE + 1, val); +} + +static uint8_t bt_get_irqreg(void) +{ + return inb(IPMI_BT_BASE + 2); +} + +static void bt_write_irqreg(uint8_t val) +{ + outb(IPMI_BT_BASE + 2, val); +} + +static void bt_wait_b_busy(void) +{ + unsigned int count = 1000; + while (IPMI_BT_CTLREG_GET_B_BUSY() != 0) { + g_assert(--count != 0); + } +} + +static void bt_wait_b2h_atn(void) +{ + unsigned int count = 1000; + while (IPMI_BT_CTLREG_GET_B2H_ATN() == 0) { + g_assert(--count != 0); + } +} + + +static int emu_lfd; +static int emu_fd; +static in_port_t emu_port; +static uint8_t inbuf[100]; +static unsigned int inbuf_len; +static unsigned int inbuf_pos; +static int last_was_aa; + +static void read_emu_data(void) +{ + fd_set readfds; + int rv; + struct timeval tv; + + FD_ZERO(&readfds); + FD_SET(emu_fd, &readfds); + tv.tv_sec = 10; + tv.tv_usec = 0; + rv = select(emu_fd + 1, &readfds, NULL, NULL, &tv); + if (rv == -1) { + perror("select"); + } + g_assert(rv == 1); + rv = read(emu_fd, inbuf, sizeof(inbuf)); + if (rv == -1) { + perror("read"); + } + g_assert(rv > 0); + inbuf_len = rv; + inbuf_pos = 0; +} + +static void write_emu_msg(uint8_t *msg, unsigned int len) +{ + int rv; + +#ifdef DEBUG_TEST + { + unsigned int i; + printf("sending:"); + for (i = 0; i < len; i++) { + printf(" %2.2x", msg[i]); + } + printf("\n"); + } +#endif + rv = write(emu_fd, msg, len); + g_assert(rv == len); +} + +static void get_emu_msg(uint8_t *msg, unsigned int *len) +{ + unsigned int outpos = 0; + + for (;;) { + while (inbuf_pos < inbuf_len) { + uint8_t ch = inbuf[inbuf_pos++]; + + g_assert(outpos < *len); + if (last_was_aa) { + assert(ch & 0x10); + msg[outpos++] = ch & ~0x10; + last_was_aa = 0; + } else if (ch == 0xaa) { + last_was_aa = 1; + } else { + msg[outpos++] = ch; + if ((ch == 0xa0) || (ch == 0xa1)) { + /* Message complete */ + *len = outpos; + goto done; + } + } + } + read_emu_data(); + } + done: +#ifdef DEBUG_TEST + { + unsigned int i; + printf("Msg:"); + for (i = 0; i < outpos; i++) { + printf(" %2.2x", msg[i]); + } + printf("\n"); + } +#endif + return; +} + +static uint8_t +ipmb_checksum(const unsigned char *data, int size, unsigned char start) +{ + unsigned char csum = start; + + for (; size > 0; size--, data++) { + csum += *data; + } + return csum; +} + +static uint8_t get_dev_id_cmd[] = { 0x18, 0x01 }; +static uint8_t get_dev_id_rsp[] = { 0x1c, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x02, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static uint8_t set_bmc_globals_cmd[] = { 0x18, 0x2e, 0x0f }; +static uint8_t set_bmc_globals_rsp[] = { 0x1c, 0x2e, 0x00 }; +static uint8_t enable_irq_cmd[] = { 0x05, 0xa1 }; + +static void emu_msg_handler(void) +{ + uint8_t msg[100]; + unsigned int msg_len = sizeof(msg); + + get_emu_msg(msg, &msg_len); + g_assert(msg_len >= 5); + g_assert(msg[msg_len - 1] == 0xa0); + msg_len--; + g_assert(ipmb_checksum(msg, msg_len, 0) == 0); + msg_len--; + if ((msg[1] == get_dev_id_cmd[0]) && (msg[2] == get_dev_id_cmd[1])) { + memcpy(msg + 1, get_dev_id_rsp, sizeof(get_dev_id_rsp)); + msg_len = sizeof(get_dev_id_rsp) + 1; + msg[msg_len] = -ipmb_checksum(msg, msg_len, 0); + msg_len++; + msg[msg_len++] = 0xa0; + write_emu_msg(msg, msg_len); + } else if ((msg[1] == set_bmc_globals_cmd[0]) && + (msg[2] == set_bmc_globals_cmd[1])) { + memcpy(msg + 1, set_bmc_globals_rsp, sizeof(set_bmc_globals_rsp)); + msg_len = sizeof(set_bmc_globals_rsp) + 1; + msg[msg_len] = -ipmb_checksum(msg, msg_len, 0); + msg_len++; + msg[msg_len++] = 0xa0; + write_emu_msg(msg, msg_len); + write_emu_msg(enable_irq_cmd, sizeof(enable_irq_cmd)); + } else { + g_assert(0); + } +} + +static void bt_cmd(uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len) +{ + unsigned int i, len, j = 0; + uint8_t seq = 5; + + /* Should be idle */ + g_assert(bt_get_ctrlreg() == 0); + + bt_wait_b_busy(); + IPMI_BT_CTLREG_SET_CLR_WR_PTR(); + bt_write_buf(cmd_len + 1); + bt_write_buf(cmd[0]); + bt_write_buf(seq); + for (i = 1; i < cmd_len; i++) { + bt_write_buf(cmd[i]); + } + IPMI_BT_CTLREG_SET_H2B_ATN(); + + emu_msg_handler(); /* We should get a message on the socket here. */ + + bt_wait_b2h_atn(); + if (bt_ints_enabled) { + g_assert((bt_get_irqreg() & 0x02) == 0x02); + g_assert(get_irq(IPMI_IRQ)); + bt_write_irqreg(0x03); + } else { + g_assert(!get_irq(IPMI_IRQ)); + } + IPMI_BT_CTLREG_SET_H_BUSY(); + IPMI_BT_CTLREG_SET_B2H_ATN(); + IPMI_BT_CTLREG_SET_CLR_RD_PTR(); + len = bt_get_buf(); + g_assert(len >= 4); + rsp[0] = bt_get_buf(); + assert(bt_get_buf() == seq); + len--; + for (j = 1; j < len; j++) { + rsp[j] = bt_get_buf(); + } + IPMI_BT_CTLREG_SET_H_BUSY(); + *rsp_len = j; +} + + +/* + * We should get a connect request and a short message with capabilities. + */ +static void test_connect(void) +{ + fd_set readfds; + int rv; + int val; + struct timeval tv; + uint8_t msg[100]; + unsigned int msglen; + static uint8_t exp1[] = { 0xff, 0x01, 0xa1 }; /* A protocol version */ + static uint8_t exp2[] = { 0x08, 0x1f, 0xa1 }; /* A capabilities cmd */ + + FD_ZERO(&readfds); + FD_SET(emu_lfd, &readfds); + tv.tv_sec = 10; + tv.tv_usec = 0; + rv = select(emu_lfd + 1, &readfds, NULL, NULL, &tv); + g_assert(rv == 1); + emu_fd = accept(emu_lfd, NULL, 0); + if (emu_fd < 0) { + perror("accept"); + } + g_assert(emu_fd >= 0); + + val = 1; + rv = setsockopt(emu_fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)); + g_assert(rv != -1); + + /* Report our version */ + write_emu_msg(exp1, sizeof(exp1)); + + /* Validate that we get the info we expect. */ + msglen = sizeof(msg); + get_emu_msg(msg, &msglen); + g_assert(msglen == sizeof(exp1)); + g_assert(memcmp(msg, exp1, msglen) == 0); + msglen = sizeof(msg); + get_emu_msg(msg, &msglen); + g_assert(msglen == sizeof(exp2)); + g_assert(memcmp(msg, exp2, msglen) == 0); +} + +/* + * Send a get_device_id to do a basic test. + */ +static void test_bt_base(void) +{ + uint8_t rsp[20]; + unsigned int rsplen = sizeof(rsp); + + bt_cmd(get_dev_id_cmd, sizeof(get_dev_id_cmd), rsp, &rsplen); + g_assert(rsplen == sizeof(get_dev_id_rsp)); + g_assert(memcmp(get_dev_id_rsp, rsp, rsplen) == 0); +} + +/* + * Enable IRQs for the interface. + */ +static void test_enable_irq(void) +{ + uint8_t rsp[20]; + unsigned int rsplen = sizeof(rsp); + + bt_cmd(set_bmc_globals_cmd, sizeof(set_bmc_globals_cmd), rsp, &rsplen); + g_assert(rsplen == sizeof(set_bmc_globals_rsp)); + g_assert(memcmp(set_bmc_globals_rsp, rsp, rsplen) == 0); + bt_write_irqreg(0x01); + bt_ints_enabled = 1; +} + +/* + * Create a local TCP socket with any port, then save off the port we got. + */ +static void open_socket(void) +{ + struct sockaddr_in myaddr; + socklen_t addrlen; + + myaddr.sin_family = AF_INET; + myaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + myaddr.sin_port = 0; + emu_lfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (emu_lfd == -1) { + perror("socket"); + exit(1); + } + if (bind(emu_lfd, (struct sockaddr *) &myaddr, sizeof(myaddr)) == -1) { + perror("bind"); + exit(1); + } + addrlen = sizeof(myaddr); + if (getsockname(emu_lfd, (struct sockaddr *) &myaddr , &addrlen) == -1) { + perror("getsockname"); + exit(1); + } + emu_port = ntohs(myaddr.sin_port); + assert(listen(emu_lfd, 1) != -1); +} + +int main(int argc, char **argv) +{ + const char *arch = qtest_get_arch(); + char *cmdline; + int ret; + + /* Check architecture */ + if (strcmp(arch, "i386") && strcmp(arch, "x86_64")) { + g_test_message("Skipping test for non-x86\n"); + return 0; + } + + open_socket(); + + /* Run the tests */ + g_test_init(&argc, &argv, NULL); + + cmdline = g_strdup_printf("-vnc none" + " -chardev socket,id=ipmi0,host=localhost,port=%d,reconnect=10" + " -device ipmi-bmc-extern,chardev=ipmi0,id=bmc0" + " -device isa-ipmi-bt,bmc=bmc0", emu_port); + qtest_start(cmdline); + qtest_irq_intercept_in(global_qtest, "ioapic"); + qtest_add_func("/ipmi/extern/connect", test_connect); + qtest_add_func("/ipmi/extern/bt_base", test_bt_base); + qtest_add_func("/ipmi/extern/bt_enable_irq", test_enable_irq); + qtest_add_func("/ipmi/extern/bt_base_irq", test_bt_base); + ret = g_test_run(); + qtest_quit(global_qtest); + + return ret; +} diff --git a/tests/ipmi-kcs-test.c b/tests/ipmi-kcs-test.c new file mode 100644 index 0000000000..564c470f55 --- /dev/null +++ b/tests/ipmi-kcs-test.c @@ -0,0 +1,295 @@ +/* + * IPMI KCS test cases, using the local interface. + * + * Copyright (c) 2012 Corey Minyard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include + +#include + +#include "libqtest.h" + +#define IPMI_IRQ 5 + +#define IPMI_KCS_BASE 0xca2 + +#define IPMI_KCS_STATUS_ABORT 0x60 +#define IPMI_KCS_CMD_WRITE_START 0x61 +#define IPMI_KCS_CMD_WRITE_END 0x62 +#define IPMI_KCS_CMD_READ 0x68 + +#define IPMI_KCS_ABORTED_BY_CMD 0x01 + +#define IPMI_KCS_CMDREG_GET_STATE() ((kcs_get_cmdreg() >> 6) & 3) +#define IPMI_KCS_STATE_IDLE 0 +#define IPMI_KCS_STATE_READ 1 +#define IPMI_KCS_STATE_WRITE 2 +#define IPMI_KCS_STATE_ERROR 3 +#define IPMI_KCS_CMDREG_GET_CD() ((kcs_get_cmdreg() >> 3) & 1) +#define IPMI_KCS_CMDREG_GET_ATN() ((kcs_get_cmdreg() >> 2) & 1) +#define IPMI_KCS_CMDREG_GET_IBF() ((kcs_get_cmdreg() >> 1) & 1) +#define IPMI_KCS_CMDREG_GET_OBF() ((kcs_get_cmdreg() >> 0) & 1) + +static int kcs_ints_enabled; + +static uint8_t kcs_get_cmdreg(void) +{ + return inb(IPMI_KCS_BASE + 1); +} + +static void kcs_write_cmdreg(uint8_t val) +{ + outb(IPMI_KCS_BASE + 1, val); +} + +static uint8_t kcs_get_datareg(void) +{ + return inb(IPMI_KCS_BASE); +} + +static void kcs_write_datareg(uint8_t val) +{ + outb(IPMI_KCS_BASE, val); +} + +static void kcs_wait_ibf(void) +{ + unsigned int count = 1000; + while (IPMI_KCS_CMDREG_GET_IBF() != 0) { + g_assert(--count != 0); + } +} + +static void kcs_wait_obf(void) +{ + unsigned int count = 1000; + while (IPMI_KCS_CMDREG_GET_OBF() == 0) { + g_assert(--count != 0); + } +} + +static void kcs_clear_obf(void) +{ + if (kcs_ints_enabled) { + g_assert(get_irq(IPMI_IRQ)); + } else { + g_assert(!get_irq(IPMI_IRQ)); + } + g_assert(IPMI_KCS_CMDREG_GET_OBF() == 1); + kcs_get_datareg(); + g_assert(IPMI_KCS_CMDREG_GET_OBF() == 0); + g_assert(!get_irq(IPMI_IRQ)); +} + +static void kcs_check_state(uint8_t state) +{ + g_assert(IPMI_KCS_CMDREG_GET_STATE() == state); +} + +static void kcs_cmd(uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len) +{ + unsigned int i, j = 0; + + /* Should be idle */ + g_assert(kcs_get_cmdreg() == 0); + + kcs_write_cmdreg(IPMI_KCS_CMD_WRITE_START); + kcs_wait_ibf(); + kcs_check_state(IPMI_KCS_STATE_WRITE); + kcs_clear_obf(); + for (i = 0; i < cmd_len; i++) { + kcs_write_datareg(cmd[i]); + kcs_wait_ibf(); + kcs_check_state(IPMI_KCS_STATE_WRITE); + kcs_clear_obf(); + } + kcs_write_cmdreg(IPMI_KCS_CMD_WRITE_END); + kcs_wait_ibf(); + kcs_check_state(IPMI_KCS_STATE_WRITE); + kcs_clear_obf(); + kcs_write_datareg(0); + next_read_byte: + kcs_wait_ibf(); + switch (IPMI_KCS_CMDREG_GET_STATE()) { + case IPMI_KCS_STATE_READ: + kcs_wait_obf(); + g_assert(j < *rsp_len); + rsp[j++] = kcs_get_datareg(); + kcs_write_datareg(IPMI_KCS_CMD_READ); + goto next_read_byte; + break; + + case IPMI_KCS_STATE_IDLE: + kcs_wait_obf(); + kcs_get_datareg(); + break; + + default: + g_assert(0); + } + *rsp_len = j; +} + +static void kcs_abort(uint8_t *cmd, unsigned int cmd_len, + uint8_t *rsp, unsigned int *rsp_len) +{ + unsigned int i, j = 0; + unsigned int retries = 4; + + /* Should be idle */ + g_assert(kcs_get_cmdreg() == 0); + + kcs_write_cmdreg(IPMI_KCS_CMD_WRITE_START); + kcs_wait_ibf(); + kcs_check_state(IPMI_KCS_STATE_WRITE); + kcs_clear_obf(); + for (i = 0; i < cmd_len; i++) { + kcs_write_datareg(cmd[i]); + kcs_wait_ibf(); + kcs_check_state(IPMI_KCS_STATE_WRITE); + kcs_clear_obf(); + } + kcs_write_cmdreg(IPMI_KCS_CMD_WRITE_END); + kcs_wait_ibf(); + kcs_check_state(IPMI_KCS_STATE_WRITE); + kcs_clear_obf(); + kcs_write_datareg(0); + kcs_wait_ibf(); + switch (IPMI_KCS_CMDREG_GET_STATE()) { + case IPMI_KCS_STATE_READ: + kcs_wait_obf(); + g_assert(j < *rsp_len); + rsp[j++] = kcs_get_datareg(); + kcs_write_datareg(IPMI_KCS_CMD_READ); + break; + + default: + g_assert(0); + } + + /* Start the abort here */ + retry_abort: + g_assert(retries > 0); + + kcs_wait_ibf(); + kcs_write_cmdreg(IPMI_KCS_STATUS_ABORT); + kcs_wait_ibf(); + kcs_clear_obf(); + kcs_write_datareg(0); + kcs_wait_ibf(); + if (IPMI_KCS_CMDREG_GET_STATE() != IPMI_KCS_STATE_READ) { + retries--; + goto retry_abort; + } + kcs_wait_obf(); + rsp[0] = kcs_get_datareg(); + kcs_write_datareg(IPMI_KCS_CMD_READ); + kcs_wait_ibf(); + if (IPMI_KCS_CMDREG_GET_STATE() != IPMI_KCS_STATE_IDLE) { + retries--; + goto retry_abort; + } + kcs_wait_obf(); + kcs_clear_obf(); + + *rsp_len = j; +} + + +static uint8_t get_dev_id_cmd[] = { 0x18, 0x01 }; +static uint8_t get_dev_id_rsp[] = { 0x1c, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x02, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +/* + * Send a get_device_id to do a basic test. + */ +static void test_kcs_base(void) +{ + uint8_t rsp[20]; + unsigned int rsplen = sizeof(rsp); + + kcs_cmd(get_dev_id_cmd, sizeof(get_dev_id_cmd), rsp, &rsplen); + g_assert(rsplen == sizeof(get_dev_id_rsp)); + g_assert(memcmp(get_dev_id_rsp, rsp, rsplen) == 0); +} + +/* + * Abort a kcs operation while reading + */ +static void test_kcs_abort(void) +{ + uint8_t rsp[20]; + unsigned int rsplen = sizeof(rsp); + + kcs_abort(get_dev_id_cmd, sizeof(get_dev_id_cmd), rsp, &rsplen); + g_assert(rsp[0] == IPMI_KCS_ABORTED_BY_CMD); +} + +static uint8_t set_bmc_globals_cmd[] = { 0x18, 0x2e, 0x0f }; +static uint8_t set_bmc_globals_rsp[] = { 0x1c, 0x2e, 0x00 }; + +/* + * Enable interrupts + */ +static void test_enable_irq(void) +{ + uint8_t rsp[20]; + unsigned int rsplen = sizeof(rsp); + + kcs_cmd(set_bmc_globals_cmd, sizeof(set_bmc_globals_cmd), rsp, &rsplen); + g_assert(rsplen == sizeof(set_bmc_globals_rsp)); + g_assert(memcmp(set_bmc_globals_rsp, rsp, rsplen) == 0); + kcs_ints_enabled = 1; +} + +int main(int argc, char **argv) +{ + const char *arch = qtest_get_arch(); + char *cmdline; + int ret; + + /* Check architecture */ + if (strcmp(arch, "i386") && strcmp(arch, "x86_64")) { + g_test_message("Skipping test for non-x86\n"); + return 0; + } + + /* Run the tests */ + g_test_init(&argc, &argv, NULL); + + cmdline = g_strdup_printf("-vnc none -device ipmi-bmc-sim,id=bmc0" + " -device isa-ipmi-kcs,bmc=bmc0"); + qtest_start(cmdline); + qtest_irq_intercept_in(global_qtest, "ioapic"); + qtest_add_func("/ipmi/local/kcs_base", test_kcs_base); + qtest_add_func("/ipmi/local/kcs_abort", test_kcs_abort); + qtest_add_func("/ipmi/local/kcs_enable_irq", test_enable_irq); + qtest_add_func("/ipmi/local/kcs_base_irq", test_kcs_base); + qtest_add_func("/ipmi/local/kcs_abort_irq", test_kcs_abort); + ret = g_test_run(); + qtest_quit(global_qtest); + + return ret; +} From f8490451acbc45916357273e08a577a372fce1fb Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Thu, 17 Dec 2015 12:50:10 -0600 Subject: [PATCH 19/55] ipmi: Add documentation Add some basic documentation for the IPMI device. Signed-off-by: Corey Minyard Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- qemu-options.hx | 52 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/qemu-options.hx b/qemu-options.hx index 49afe6cd3b..94ab8806fb 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -382,6 +382,58 @@ Add device @var{driver}. @var{prop}=@var{value} sets driver properties. Valid properties depend on the driver. To get help on possible drivers and properties, use @code{-device help} and @code{-device @var{driver},help}. + +Some drivers are: +@item -device ipmi-bmc-sim,id=@var{id}[,slave_addr=@var{val}] + +Add an IPMI BMC. This is a simulation of a hardware management +interface processor that normally sits on a system. It provides +a watchdog and the ability to reset and power control the system. +You need to connect this to an IPMI interface to make it useful + +The IPMI slave address to use for the BMC. The default is 0x20. +This address is the BMC's address on the I2C network of management +controllers. If you don't know what this means, it is safe to ignore +it. + +@item -device ipmi-bmc-extern,id=@var{id},chardev=@var{id}[,slave_addr=@var{val}] + +Add a connection to an external IPMI BMC simulator. Instead of +locally emulating the BMC like the above item, instead connect +to an external entity that provides the IPMI services. + +A connection is made to an external BMC simulator. If you do this, it +is strongly recommended that you use the "reconnect=" chardev option +to reconnect to the simulator if the connection is lost. Note that if +this is not used carefully, it can be a security issue, as the +interface has the ability to send resets, NMIs, and power off the VM. +It's best if QEMU makes a connection to an external simulator running +on a secure port on localhost, so neither the simulator nor QEMU is +exposed to any outside network. + +See the "lanserv/README.vm" file in the OpenIPMI library for more +details on the external interface. + +@item -device isa-ipmi-kcs,bmc=@var{id}[,ioport=@var{val}][,irq=@var{val}] + +Add a KCS IPMI interafce on the ISA bus. This also adds a +corresponding ACPI and SMBIOS entries, if appropriate. + +@table @option +@item bmc=@var{id} +The BMC to connect to, one of ipmi-bmc-sim or ipmi-bmc-extern above. +@item ioport=@var{val} +Define the I/O address of the interface. The default is 0xca0 for KCS. +@item irq=@var{val} +Define the interrupt to use. The default is 5. To disable interrupts, +set this to 0. +@end table + +@item -device isa-ipmi-bt,bmc=@var{id}[,ioport=@var{val}][,irq=@var{val}] + +Like the KCS interface, but defines a BT interface. The default port is +0xe4 and the default interrupt is 5. + ETEXI DEF("name", HAS_ARG, QEMU_OPTION_name, From bd66bcfca571433e7913a4ec1cdd5029d9de0287 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Thu, 17 Dec 2015 12:50:11 -0600 Subject: [PATCH 20/55] ipmi: Add migration capability to the IPMI devices. Signed-off-by: Corey Minyard Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/ipmi/ipmi_bmc_extern.c | 34 ++++++++++++++++++++++++++++++++++ hw/ipmi/ipmi_bmc_sim.c | 30 ++++++++++++++++++++++++++++++ hw/ipmi/isa_ipmi_bt.c | 24 ++++++++++++++++++++++++ hw/ipmi/isa_ipmi_kcs.c | 26 ++++++++++++++++++++++++++ 4 files changed, 114 insertions(+) diff --git a/hw/ipmi/ipmi_bmc_extern.c b/hw/ipmi/ipmi_bmc_extern.c index d4669f072f..05b91212e1 100644 --- a/hw/ipmi/ipmi_bmc_extern.c +++ b/hw/ipmi/ipmi_bmc_extern.c @@ -439,11 +439,45 @@ static void ipmi_bmc_extern_realize(DeviceState *dev, Error **errp) qemu_chr_add_handlers(ibe->chr, can_receive, receive, chr_event, ibe); } +static int ipmi_bmc_extern_post_migrate(void *opaque, int version_id) +{ + IPMIBmcExtern *ibe = opaque; + + /* + * We don't directly restore waiting_rsp, Instead, we return an + * error on the interface if a response was being waited for. + */ + if (ibe->waiting_rsp) { + IPMIInterface *ii = ibe->parent.intf; + IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); + + ibe->waiting_rsp = false; + ibe->inbuf[1] = ibe->outbuf[1] | 0x04; + ibe->inbuf[2] = ibe->outbuf[2]; + ibe->inbuf[3] = IPMI_CC_BMC_INIT_IN_PROGRESS; + iic->handle_rsp(ii, ibe->outbuf[0], ibe->inbuf + 1, 3); + } + return 0; +} + +static const VMStateDescription vmstate_ipmi_bmc_extern = { + .name = TYPE_IPMI_BMC_EXTERN, + .version_id = 1, + .minimum_version_id = 1, + .post_load = ipmi_bmc_extern_post_migrate, + .fields = (VMStateField[]) { + VMSTATE_BOOL(send_reset, IPMIBmcExtern), + VMSTATE_BOOL(waiting_rsp, IPMIBmcExtern), + VMSTATE_END_OF_LIST() + } +}; + static void ipmi_bmc_extern_init(Object *obj) { IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(obj); ibe->extern_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, extern_timeout, ibe); + vmstate_register(NULL, 0, &vmstate_ipmi_bmc_extern, ibe); } static Property ipmi_bmc_extern_properties[] = { diff --git a/hw/ipmi/ipmi_bmc_sim.c b/hw/ipmi/ipmi_bmc_sim.c index af01e1a813..0a59e539f5 100644 --- a/hw/ipmi/ipmi_bmc_sim.c +++ b/hw/ipmi/ipmi_bmc_sim.c @@ -1658,6 +1658,34 @@ static const uint8_t init_sdrs[] = { 0xff, 0xff, 0x00, 0x00, 0x00 }; +static const VMStateDescription vmstate_ipmi_sim = { + .name = TYPE_IPMI_BMC_SIMULATOR, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(bmc_global_enables, IPMIBmcSim), + VMSTATE_UINT8(msg_flags, IPMIBmcSim), + VMSTATE_BOOL(watchdog_initialized, IPMIBmcSim), + VMSTATE_UINT8(watchdog_use, IPMIBmcSim), + VMSTATE_UINT8(watchdog_action, IPMIBmcSim), + VMSTATE_UINT8(watchdog_pretimeout, IPMIBmcSim), + VMSTATE_BOOL(watchdog_expired, IPMIBmcSim), + VMSTATE_UINT16(watchdog_timeout, IPMIBmcSim), + VMSTATE_BOOL(watchdog_running, IPMIBmcSim), + VMSTATE_BOOL(watchdog_preaction_ran, IPMIBmcSim), + VMSTATE_INT64(watchdog_expiry, IPMIBmcSim), + VMSTATE_UINT8_ARRAY(evtbuf, IPMIBmcSim, 16), + VMSTATE_UINT8(sensors[IPMI_WATCHDOG_SENSOR].status, IPMIBmcSim), + VMSTATE_UINT8(sensors[IPMI_WATCHDOG_SENSOR].reading, IPMIBmcSim), + VMSTATE_UINT16(sensors[IPMI_WATCHDOG_SENSOR].states, IPMIBmcSim), + VMSTATE_UINT16(sensors[IPMI_WATCHDOG_SENSOR].assert_states, IPMIBmcSim), + VMSTATE_UINT16(sensors[IPMI_WATCHDOG_SENSOR].deassert_states, + IPMIBmcSim), + VMSTATE_UINT16(sensors[IPMI_WATCHDOG_SENSOR].assert_enable, IPMIBmcSim), + VMSTATE_END_OF_LIST() + } +}; + static void ipmi_sim_init(Object *obj) { IPMIBmc *b = IPMI_BMC(obj); @@ -1701,6 +1729,8 @@ static void ipmi_sim_init(Object *obj) register_cmds(ibs); ibs->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, ipmi_timeout, ibs); + + vmstate_register(NULL, 0, &vmstate_ipmi_sim, ibs); } static void ipmi_sim_class_init(ObjectClass *oc, void *data) diff --git a/hw/ipmi/isa_ipmi_bt.c b/hw/ipmi/isa_ipmi_bt.c index 70e2c0d236..03eb9cc07a 100644 --- a/hw/ipmi/isa_ipmi_bt.c +++ b/hw/ipmi/isa_ipmi_bt.c @@ -437,11 +437,35 @@ static void isa_ipmi_bt_realize(DeviceState *dev, Error **errp) isa_register_ioport(isadev, &iib->bt.io, iib->bt.io_base); } +static const VMStateDescription vmstate_ISAIPMIBTDevice = { + .name = TYPE_IPMI_INTERFACE, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_BOOL(bt.obf_irq_set, ISAIPMIBTDevice), + VMSTATE_BOOL(bt.atn_irq_set, ISAIPMIBTDevice), + VMSTATE_BOOL(bt.use_irq, ISAIPMIBTDevice), + VMSTATE_BOOL(bt.irqs_enabled, ISAIPMIBTDevice), + VMSTATE_UINT32(bt.outpos, ISAIPMIBTDevice), + VMSTATE_VBUFFER_UINT32(bt.outmsg, ISAIPMIBTDevice, 1, NULL, 0, + bt.outlen), + VMSTATE_VBUFFER_UINT32(bt.inmsg, ISAIPMIBTDevice, 1, NULL, 0, + bt.inlen), + VMSTATE_UINT8(bt.control_reg, ISAIPMIBTDevice), + VMSTATE_UINT8(bt.mask_reg, ISAIPMIBTDevice), + VMSTATE_UINT8(bt.waiting_rsp, ISAIPMIBTDevice), + VMSTATE_UINT8(bt.waiting_seq, ISAIPMIBTDevice), + VMSTATE_END_OF_LIST() + } +}; + static void isa_ipmi_bt_init(Object *obj) { ISAIPMIBTDevice *iib = ISA_IPMI_BT(obj); ipmi_bmc_find_and_link(obj, (Object **) &iib->bt.bmc); + + vmstate_register(NULL, 0, &vmstate_ISAIPMIBTDevice, iib); } static void *isa_ipmi_bt_get_backend_data(IPMIInterface *ii) diff --git a/hw/ipmi/isa_ipmi_kcs.c b/hw/ipmi/isa_ipmi_kcs.c index 750a7251d2..b588711f8d 100644 --- a/hw/ipmi/isa_ipmi_kcs.c +++ b/hw/ipmi/isa_ipmi_kcs.c @@ -400,11 +400,37 @@ static void ipmi_isa_realize(DeviceState *dev, Error **errp) isa_register_ioport(isadev, &iik->kcs.io, iik->kcs.io_base); } +const VMStateDescription vmstate_ISAIPMIKCSDevice = { + .name = TYPE_IPMI_INTERFACE, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_BOOL(kcs.obf_irq_set, ISAIPMIKCSDevice), + VMSTATE_BOOL(kcs.atn_irq_set, ISAIPMIKCSDevice), + VMSTATE_BOOL(kcs.use_irq, ISAIPMIKCSDevice), + VMSTATE_BOOL(kcs.irqs_enabled, ISAIPMIKCSDevice), + VMSTATE_UINT32(kcs.outpos, ISAIPMIKCSDevice), + VMSTATE_VBUFFER_UINT32(kcs.outmsg, ISAIPMIKCSDevice, 1, NULL, 0, + kcs.outlen), + VMSTATE_VBUFFER_UINT32(kcs.inmsg, ISAIPMIKCSDevice, 1, NULL, 0, + kcs.inlen), + VMSTATE_BOOL(kcs.write_end, ISAIPMIKCSDevice), + VMSTATE_UINT8(kcs.status_reg, ISAIPMIKCSDevice), + VMSTATE_UINT8(kcs.data_out_reg, ISAIPMIKCSDevice), + VMSTATE_INT16(kcs.data_in_reg, ISAIPMIKCSDevice), + VMSTATE_INT16(kcs.cmd_reg, ISAIPMIKCSDevice), + VMSTATE_UINT8(kcs.waiting_rsp, ISAIPMIKCSDevice), + VMSTATE_END_OF_LIST() + } +}; + static void isa_ipmi_kcs_init(Object *obj) { ISAIPMIKCSDevice *iik = ISA_IPMI_KCS(obj); ipmi_bmc_find_and_link(obj, (Object **) &iik->kcs.bmc); + + vmstate_register(NULL, 0, &vmstate_ISAIPMIKCSDevice, iik); } static void *isa_ipmi_kcs_get_backend_data(IPMIInterface *ii) From 90b6180500a9a88f194dc58c70b87dd539405f94 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Thu, 17 Dec 2015 12:50:12 -0600 Subject: [PATCH 21/55] ipmi: Add a firmware configuration repository Add a way for IPMI devices to register their firmware information with the IPMI subsystem so that various firmware entities can pull that information later for adding to firmware tables. Signed-off-by: Corey Minyard Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/ipmi/ipmi.c | 27 +++++++++++++++++++++++++++ include/hw/ipmi/ipmi.h | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/hw/ipmi/ipmi.c b/hw/ipmi/ipmi.c index 7d174692aa..52aba1e20d 100644 --- a/hw/ipmi/ipmi.c +++ b/hw/ipmi/ipmi.c @@ -123,3 +123,30 @@ static void ipmi_register_types(void) } type_init(ipmi_register_types) + +static IPMIFwInfo *ipmi_fw_info; +static unsigned int ipmi_fw_info_len; + +static uint32_t current_uuid = 1; + +void ipmi_add_fwinfo(IPMIFwInfo *info, Error **errp) +{ + info->uuid = current_uuid++; + ipmi_fw_info = g_realloc(ipmi_fw_info, + sizeof(*ipmi_fw_info) * (ipmi_fw_info_len + 1)); + ipmi_fw_info[ipmi_fw_info_len] = *info; +} + +IPMIFwInfo *ipmi_first_fwinfo(void) +{ + return ipmi_fw_info; +} + +IPMIFwInfo *ipmi_next_fwinfo(IPMIFwInfo *current) +{ + current++; + if (current >= &ipmi_fw_info[ipmi_fw_info_len]) { + return NULL; + } + return current; +} diff --git a/include/hw/ipmi/ipmi.h b/include/hw/ipmi/ipmi.h index e4f773847d..32bac0fa88 100644 --- a/include/hw/ipmi/ipmi.h +++ b/include/hw/ipmi/ipmi.h @@ -168,6 +168,41 @@ typedef struct IPMIBmcClass { */ void ipmi_bmc_find_and_link(Object *obj, Object **bmc); +/* + * Used for transferring information to interfaces that add + * entries to firmware tables. + */ +typedef struct IPMIFwInfo { + const char *interface_name; + int interface_type; + uint8_t ipmi_spec_major_revision; + uint8_t ipmi_spec_minor_revision; + uint8_t i2c_slave_address; + uint32_t uuid; + + uint64_t base_address; + uint64_t register_length; + uint8_t register_spacing; + enum { + IPMI_MEMSPACE_IO, + IPMI_MEMSPACE_MEM32, + IPMI_MEMSPACE_MEM64, + IPMI_MEMSPACE_SMBUS + } memspace; + + int interrupt_number; + enum { + IPMI_LEVEL_IRQ, + IPMI_EDGE_IRQ + } irq_type; + + const char *acpi_parent; +} IPMIFwInfo; + +void ipmi_add_fwinfo(IPMIFwInfo *info, Error **errp); +IPMIFwInfo *ipmi_first_fwinfo(void); +IPMIFwInfo *ipmi_next_fwinfo(IPMIFwInfo *current); + #ifdef IPMI_DEBUG #define ipmi_debug(fs, ...) \ fprintf(stderr, "IPMI (%s): " fs, __func__, ##__VA_ARGS__) From 44d3db0d969ccfd70d81231ac531c724adf9e4ae Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Thu, 17 Dec 2015 12:50:13 -0600 Subject: [PATCH 22/55] ipmi: Add firmware registration to the ISA interface Signed-off-by: Corey Minyard Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/ipmi/isa_ipmi_bt.c | 15 +++++++++++++++ hw/ipmi/isa_ipmi_kcs.c | 15 +++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/hw/ipmi/isa_ipmi_bt.c b/hw/ipmi/isa_ipmi_bt.c index 03eb9cc07a..21fa4a7851 100644 --- a/hw/ipmi/isa_ipmi_bt.c +++ b/hw/ipmi/isa_ipmi_bt.c @@ -407,6 +407,7 @@ typedef struct ISAIPMIBTDevice { ISADevice dev; int32 isairq; IPMIBT bt; + IPMIFwInfo fwinfo; } ISAIPMIBTDevice; static void isa_ipmi_bt_realize(DeviceState *dev, Error **errp) @@ -435,6 +436,20 @@ static void isa_ipmi_bt_realize(DeviceState *dev, Error **errp) qdev_set_legacy_instance_id(dev, iib->bt.io_base, iib->bt.io_length); isa_register_ioport(isadev, &iib->bt.io, iib->bt.io_base); + + iib->fwinfo.interface_name = "bt"; + iib->fwinfo.interface_type = IPMI_SMBIOS_BT; + iib->fwinfo.ipmi_spec_major_revision = 2; + iib->fwinfo.ipmi_spec_minor_revision = 0; + iib->fwinfo.base_address = iib->bt.io_base; + iib->fwinfo.register_length = iib->bt.io_length; + iib->fwinfo.register_spacing = 1; + iib->fwinfo.memspace = IPMI_MEMSPACE_IO; + iib->fwinfo.irq_type = IPMI_LEVEL_IRQ; + iib->fwinfo.interrupt_number = iib->isairq; + iib->fwinfo.acpi_parent = "\\_SB.PCI0.ISA"; + iib->fwinfo.i2c_slave_address = iib->bt.bmc->slave_addr; + ipmi_add_fwinfo(&iib->fwinfo, errp); } static const VMStateDescription vmstate_ISAIPMIBTDevice = { diff --git a/hw/ipmi/isa_ipmi_kcs.c b/hw/ipmi/isa_ipmi_kcs.c index b588711f8d..c662aeec55 100644 --- a/hw/ipmi/isa_ipmi_kcs.c +++ b/hw/ipmi/isa_ipmi_kcs.c @@ -370,6 +370,7 @@ typedef struct ISAIPMIKCSDevice { ISADevice dev; int32 isairq; IPMIKCS kcs; + IPMIFwInfo fwinfo; } ISAIPMIKCSDevice; static void ipmi_isa_realize(DeviceState *dev, Error **errp) @@ -398,6 +399,20 @@ static void ipmi_isa_realize(DeviceState *dev, Error **errp) qdev_set_legacy_instance_id(dev, iik->kcs.io_base, iik->kcs.io_length); isa_register_ioport(isadev, &iik->kcs.io, iik->kcs.io_base); + + iik->fwinfo.interface_name = "kcs"; + iik->fwinfo.interface_type = IPMI_SMBIOS_KCS; + iik->fwinfo.ipmi_spec_major_revision = 2; + iik->fwinfo.ipmi_spec_minor_revision = 0; + iik->fwinfo.base_address = iik->kcs.io_base; + iik->fwinfo.i2c_slave_address = iik->kcs.bmc->slave_addr; + iik->fwinfo.register_length = iik->kcs.io_length; + iik->fwinfo.register_spacing = 1; + iik->fwinfo.memspace = IPMI_MEMSPACE_IO; + iik->fwinfo.irq_type = IPMI_LEVEL_IRQ; + iik->fwinfo.interrupt_number = iik->isairq; + iik->fwinfo.acpi_parent = "\\_SB.PCI0.ISA"; + ipmi_add_fwinfo(&iik->fwinfo, errp); } const VMStateDescription vmstate_ISAIPMIKCSDevice = { From ad2a8077188f05155b07489747ea682070eb135f Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Thu, 17 Dec 2015 12:50:19 -0600 Subject: [PATCH 23/55] ipmi: Add a force off function Allow the IPMI interface to request a forced power off. Signed-off-by: Corey Minyard Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/ipmi/ipmi_bmc_extern.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/ipmi/ipmi_bmc_extern.c b/hw/ipmi/ipmi_bmc_extern.c index 05b91212e1..56073b37ae 100644 --- a/hw/ipmi/ipmi_bmc_extern.c +++ b/hw/ipmi/ipmi_bmc_extern.c @@ -30,6 +30,7 @@ #include #include "qemu/timer.h" #include "sysemu/char.h" +#include "sysemu/sysemu.h" #include "hw/ipmi/ipmi.h" #define VM_MSG_CHAR 0xA0 /* Marks end of message */ @@ -52,6 +53,7 @@ #define VM_CAPABILITIES_IRQ 0x04 #define VM_CAPABILITIES_NMI 0x08 #define VM_CAPABILITIES_ATTN 0x10 +#define VM_CMD_FORCEOFF 0x09 #define TYPE_IPMI_BMC_EXTERN "ipmi-bmc-extern" #define IPMI_BMC_EXTERN(obj) OBJECT_CHECK(IPMIBmcExtern, (obj), \ @@ -268,6 +270,10 @@ static void handle_hw_op(IPMIBmcExtern *ibe, unsigned char hw_op) case VM_CMD_SEND_NMI: k->do_hw_op(s, IPMI_SEND_NMI, 0); break; + + case VM_CMD_FORCEOFF: + qemu_system_shutdown_request(); + break; } } From aa8abbed0028fc4a5a2a346eedaa7ab4f52c750a Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Thu, 17 Dec 2015 11:40:49 +0100 Subject: [PATCH 24/55] q35: skip q35-acpi-dsdt.aml load if not needed Only old machine types which don't use the acpi builder (qemu 1.7 + older) have to load that file for proper acpi support. Signed-off-by: Gerd Hoffmann Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/pc_q35.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 342e9cfca4..942ef7e478 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -117,7 +117,10 @@ static void pc_q35_init(MachineState *machine) } pc_cpus_init(pcms); - pc_acpi_init("q35-acpi-dsdt.aml"); + if (!pcmc->has_acpi_build) { + /* only machine types 1.7 & older need this */ + pc_acpi_init("q35-acpi-dsdt.aml"); + } kvmclock_create(); From 1e37b7149aa560959b7190924c87dad5d49d47a7 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Mon, 30 Nov 2015 12:56:35 -0200 Subject: [PATCH 25/55] pc: Remove redundant code from pc-*-2.3 machine classes Remove the redundant 'alias = NULL' and 'is_default = 0' lines from older machine-types. pc_*_2_4_machine_options() already clear those fields, so they don't need to be cleared by pc_*_2_3_machine_options(). Reviewed-by: Marcel Apfelbaum Signed-off-by: Eduardo Habkost Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Thomas Huth --- hw/i386/pc_piix.c | 2 -- hw/i386/pc_q35.c | 1 - 2 files changed, 3 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 9654628f09..d9b9ce823b 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -448,8 +448,6 @@ static void pc_i440fx_2_3_machine_options(MachineClass *m) { pc_i440fx_2_4_machine_options(m); m->hw_version = "2.3.0"; - m->alias = NULL; - m->is_default = 0; SET_MACHINE_COMPAT(m, PC_COMPAT_2_3); } diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 942ef7e478..f3a3595246 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -378,7 +378,6 @@ static void pc_q35_2_3_machine_options(MachineClass *m) m->hw_version = "2.3.0"; m->no_floppy = 0; m->no_tco = 1; - m->alias = NULL; SET_MACHINE_COMPAT(m, PC_COMPAT_2_3); } From 240240d5da54eb13745285d02fd215683c8f03c9 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Mon, 30 Nov 2015 12:56:36 -0200 Subject: [PATCH 26/55] pc: Add pc-*-2.6 machine classes Add pc-i440fx-2.6 and pc-q35-2.6 machine classes. Signed-off-by: Eduardo Habkost Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Marcel Apfelbaum Reviewed-by: Thomas Huth --- hw/i386/pc_piix.c | 16 +++++++++++++--- hw/i386/pc_q35.c | 13 +++++++++++-- include/hw/compat.h | 3 +++ include/hw/i386/pc.h | 4 ++++ 4 files changed, 31 insertions(+), 5 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index d9b9ce823b..438cdae99e 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -418,13 +418,25 @@ static void pc_i440fx_machine_options(MachineClass *m) m->default_display = "std"; } -static void pc_i440fx_2_5_machine_options(MachineClass *m) +static void pc_i440fx_2_6_machine_options(MachineClass *m) { pc_i440fx_machine_options(m); m->alias = "pc"; m->is_default = 1; } +DEFINE_I440FX_MACHINE(v2_6, "pc-i440fx-2.6", NULL, + pc_i440fx_2_6_machine_options); + + +static void pc_i440fx_2_5_machine_options(MachineClass *m) +{ + pc_i440fx_2_6_machine_options(m); + m->alias = NULL; + m->is_default = 0; + SET_MACHINE_COMPAT(m, PC_COMPAT_2_5); +} + DEFINE_I440FX_MACHINE(v2_5, "pc-i440fx-2.5", NULL, pc_i440fx_2_5_machine_options); @@ -434,8 +446,6 @@ static void pc_i440fx_2_4_machine_options(MachineClass *m) PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_i440fx_2_5_machine_options(m); m->hw_version = "2.4.0"; - m->alias = NULL; - m->is_default = 0; pcmc->broken_reserved_end = true; SET_MACHINE_COMPAT(m, PC_COMPAT_2_4); } diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index f3a3595246..412b3cd375 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -349,12 +349,22 @@ static void pc_q35_machine_options(MachineClass *m) m->no_tco = 0; } -static void pc_q35_2_5_machine_options(MachineClass *m) +static void pc_q35_2_6_machine_options(MachineClass *m) { pc_q35_machine_options(m); m->alias = "q35"; } +DEFINE_Q35_MACHINE(v2_6, "pc-q35-2.6", NULL, + pc_q35_2_6_machine_options); + +static void pc_q35_2_5_machine_options(MachineClass *m) +{ + pc_q35_2_6_machine_options(m); + m->alias = NULL; + SET_MACHINE_COMPAT(m, PC_COMPAT_2_5); +} + DEFINE_Q35_MACHINE(v2_5, "pc-q35-2.5", NULL, pc_q35_2_5_machine_options); @@ -363,7 +373,6 @@ static void pc_q35_2_4_machine_options(MachineClass *m) PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_q35_2_5_machine_options(m); m->hw_version = "2.4.0"; - m->alias = NULL; pcmc->broken_reserved_end = true; SET_MACHINE_COMPAT(m, PC_COMPAT_2_4); } diff --git a/include/hw/compat.h b/include/hw/compat.h index bcb36ef836..eaf5ab47ba 100644 --- a/include/hw/compat.h +++ b/include/hw/compat.h @@ -1,6 +1,9 @@ #ifndef HW_COMPAT_H #define HW_COMPAT_H +#define HW_COMPAT_2_5 \ + /* empty */ + #define HW_COMPAT_2_4 \ {\ .driver = "virtio-blk-device",\ diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index e5eb15c35e..e2a42db43b 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -350,7 +350,11 @@ int e820_add_entry(uint64_t, uint64_t, uint32_t); int e820_get_num_entries(void); bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *); +#define PC_COMPAT_2_5 \ + HW_COMPAT_2_5 + #define PC_COMPAT_2_4 \ + PC_COMPAT_2_5 \ HW_COMPAT_2_4 \ {\ .driver = "Haswell-" TYPE_X86_CPU,\ From 276a65ba4b96e757786ebfdfad1937be8a85a6ed Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Mon, 30 Nov 2015 12:56:37 -0200 Subject: [PATCH 27/55] pc: Change indentation of PC_COMPAT_* to 4 spaces Cosmetic change only. Signed-off-by: Eduardo Habkost Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Thomas Huth --- include/hw/i386/pc.h | 924 +++++++++++++++++++++---------------------- 1 file changed, 462 insertions(+), 462 deletions(-) diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index e2a42db43b..c6a777546a 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -354,485 +354,485 @@ bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *); HW_COMPAT_2_5 #define PC_COMPAT_2_4 \ - PC_COMPAT_2_5 \ - HW_COMPAT_2_4 \ - {\ - .driver = "Haswell-" TYPE_X86_CPU,\ - .property = "abm",\ - .value = "off",\ - },\ - {\ - .driver = "Haswell-noTSX-" TYPE_X86_CPU,\ - .property = "abm",\ - .value = "off",\ - },\ - {\ - .driver = "Broadwell-" TYPE_X86_CPU,\ - .property = "abm",\ - .value = "off",\ - },\ - {\ - .driver = "Broadwell-noTSX-" TYPE_X86_CPU,\ - .property = "abm",\ - .value = "off",\ - },\ - {\ - .driver = "host" "-" TYPE_X86_CPU,\ - .property = "host-cache-info",\ - .value = "on",\ - },\ - {\ - .driver = TYPE_X86_CPU,\ - .property = "check",\ - .value = "off",\ - },\ - {\ - .driver = "qemu64" "-" TYPE_X86_CPU,\ - .property = "sse4a",\ - .value = "on",\ - },\ - {\ - .driver = "qemu64" "-" TYPE_X86_CPU,\ - .property = "abm",\ - .value = "on",\ - },\ - {\ - .driver = "qemu64" "-" TYPE_X86_CPU,\ - .property = "popcnt",\ - .value = "on",\ - },\ - {\ - .driver = "qemu32" "-" TYPE_X86_CPU,\ - .property = "popcnt",\ - .value = "on",\ - },{\ - .driver = "Opteron_G2" "-" TYPE_X86_CPU,\ - .property = "rdtscp",\ - .value = "on",\ - },{\ - .driver = "Opteron_G3" "-" TYPE_X86_CPU,\ - .property = "rdtscp",\ - .value = "on",\ - },{\ - .driver = "Opteron_G4" "-" TYPE_X86_CPU,\ - .property = "rdtscp",\ - .value = "on",\ - },{\ - .driver = "Opteron_G5" "-" TYPE_X86_CPU,\ - .property = "rdtscp",\ - .value = "on",\ - }, + PC_COMPAT_2_5 \ + HW_COMPAT_2_4 \ + {\ + .driver = "Haswell-" TYPE_X86_CPU,\ + .property = "abm",\ + .value = "off",\ + },\ + {\ + .driver = "Haswell-noTSX-" TYPE_X86_CPU,\ + .property = "abm",\ + .value = "off",\ + },\ + {\ + .driver = "Broadwell-" TYPE_X86_CPU,\ + .property = "abm",\ + .value = "off",\ + },\ + {\ + .driver = "Broadwell-noTSX-" TYPE_X86_CPU,\ + .property = "abm",\ + .value = "off",\ + },\ + {\ + .driver = "host" "-" TYPE_X86_CPU,\ + .property = "host-cache-info",\ + .value = "on",\ + },\ + {\ + .driver = TYPE_X86_CPU,\ + .property = "check",\ + .value = "off",\ + },\ + {\ + .driver = "qemu64" "-" TYPE_X86_CPU,\ + .property = "sse4a",\ + .value = "on",\ + },\ + {\ + .driver = "qemu64" "-" TYPE_X86_CPU,\ + .property = "abm",\ + .value = "on",\ + },\ + {\ + .driver = "qemu64" "-" TYPE_X86_CPU,\ + .property = "popcnt",\ + .value = "on",\ + },\ + {\ + .driver = "qemu32" "-" TYPE_X86_CPU,\ + .property = "popcnt",\ + .value = "on",\ + },{\ + .driver = "Opteron_G2" "-" TYPE_X86_CPU,\ + .property = "rdtscp",\ + .value = "on",\ + },{\ + .driver = "Opteron_G3" "-" TYPE_X86_CPU,\ + .property = "rdtscp",\ + .value = "on",\ + },{\ + .driver = "Opteron_G4" "-" TYPE_X86_CPU,\ + .property = "rdtscp",\ + .value = "on",\ + },{\ + .driver = "Opteron_G5" "-" TYPE_X86_CPU,\ + .property = "rdtscp",\ + .value = "on",\ + }, #define PC_COMPAT_2_3 \ - PC_COMPAT_2_4 \ - HW_COMPAT_2_3 \ - {\ - .driver = TYPE_X86_CPU,\ - .property = "arat",\ - .value = "off",\ - },{\ - .driver = "qemu64" "-" TYPE_X86_CPU,\ - .property = "level",\ - .value = stringify(4),\ - },{\ - .driver = "kvm64" "-" TYPE_X86_CPU,\ - .property = "level",\ - .value = stringify(5),\ - },{\ - .driver = "pentium3" "-" TYPE_X86_CPU,\ - .property = "level",\ - .value = stringify(2),\ - },{\ - .driver = "n270" "-" TYPE_X86_CPU,\ - .property = "level",\ - .value = stringify(5),\ - },{\ - .driver = "Conroe" "-" TYPE_X86_CPU,\ - .property = "level",\ - .value = stringify(4),\ - },{\ - .driver = "Penryn" "-" TYPE_X86_CPU,\ - .property = "level",\ - .value = stringify(4),\ - },{\ - .driver = "Nehalem" "-" TYPE_X86_CPU,\ - .property = "level",\ - .value = stringify(4),\ - },{\ - .driver = "n270" "-" TYPE_X86_CPU,\ - .property = "xlevel",\ - .value = stringify(0x8000000a),\ - },{\ - .driver = "Penryn" "-" TYPE_X86_CPU,\ - .property = "xlevel",\ - .value = stringify(0x8000000a),\ - },{\ - .driver = "Conroe" "-" TYPE_X86_CPU,\ - .property = "xlevel",\ - .value = stringify(0x8000000a),\ - },{\ - .driver = "Nehalem" "-" TYPE_X86_CPU,\ - .property = "xlevel",\ - .value = stringify(0x8000000a),\ - },{\ - .driver = "Westmere" "-" TYPE_X86_CPU,\ - .property = "xlevel",\ - .value = stringify(0x8000000a),\ - },{\ - .driver = "SandyBridge" "-" TYPE_X86_CPU,\ - .property = "xlevel",\ - .value = stringify(0x8000000a),\ - },{\ - .driver = "IvyBridge" "-" TYPE_X86_CPU,\ - .property = "xlevel",\ - .value = stringify(0x8000000a),\ - },{\ - .driver = "Haswell" "-" TYPE_X86_CPU,\ - .property = "xlevel",\ - .value = stringify(0x8000000a),\ - },{\ - .driver = "Haswell-noTSX" "-" TYPE_X86_CPU,\ - .property = "xlevel",\ - .value = stringify(0x8000000a),\ - },{\ - .driver = "Broadwell" "-" TYPE_X86_CPU,\ - .property = "xlevel",\ - .value = stringify(0x8000000a),\ - },{\ - .driver = "Broadwell-noTSX" "-" TYPE_X86_CPU,\ - .property = "xlevel",\ - .value = stringify(0x8000000a),\ - }, + PC_COMPAT_2_4 \ + HW_COMPAT_2_3 \ + {\ + .driver = TYPE_X86_CPU,\ + .property = "arat",\ + .value = "off",\ + },{\ + .driver = "qemu64" "-" TYPE_X86_CPU,\ + .property = "level",\ + .value = stringify(4),\ + },{\ + .driver = "kvm64" "-" TYPE_X86_CPU,\ + .property = "level",\ + .value = stringify(5),\ + },{\ + .driver = "pentium3" "-" TYPE_X86_CPU,\ + .property = "level",\ + .value = stringify(2),\ + },{\ + .driver = "n270" "-" TYPE_X86_CPU,\ + .property = "level",\ + .value = stringify(5),\ + },{\ + .driver = "Conroe" "-" TYPE_X86_CPU,\ + .property = "level",\ + .value = stringify(4),\ + },{\ + .driver = "Penryn" "-" TYPE_X86_CPU,\ + .property = "level",\ + .value = stringify(4),\ + },{\ + .driver = "Nehalem" "-" TYPE_X86_CPU,\ + .property = "level",\ + .value = stringify(4),\ + },{\ + .driver = "n270" "-" TYPE_X86_CPU,\ + .property = "xlevel",\ + .value = stringify(0x8000000a),\ + },{\ + .driver = "Penryn" "-" TYPE_X86_CPU,\ + .property = "xlevel",\ + .value = stringify(0x8000000a),\ + },{\ + .driver = "Conroe" "-" TYPE_X86_CPU,\ + .property = "xlevel",\ + .value = stringify(0x8000000a),\ + },{\ + .driver = "Nehalem" "-" TYPE_X86_CPU,\ + .property = "xlevel",\ + .value = stringify(0x8000000a),\ + },{\ + .driver = "Westmere" "-" TYPE_X86_CPU,\ + .property = "xlevel",\ + .value = stringify(0x8000000a),\ + },{\ + .driver = "SandyBridge" "-" TYPE_X86_CPU,\ + .property = "xlevel",\ + .value = stringify(0x8000000a),\ + },{\ + .driver = "IvyBridge" "-" TYPE_X86_CPU,\ + .property = "xlevel",\ + .value = stringify(0x8000000a),\ + },{\ + .driver = "Haswell" "-" TYPE_X86_CPU,\ + .property = "xlevel",\ + .value = stringify(0x8000000a),\ + },{\ + .driver = "Haswell-noTSX" "-" TYPE_X86_CPU,\ + .property = "xlevel",\ + .value = stringify(0x8000000a),\ + },{\ + .driver = "Broadwell" "-" TYPE_X86_CPU,\ + .property = "xlevel",\ + .value = stringify(0x8000000a),\ + },{\ + .driver = "Broadwell-noTSX" "-" TYPE_X86_CPU,\ + .property = "xlevel",\ + .value = stringify(0x8000000a),\ + }, #define PC_COMPAT_2_2 \ - PC_COMPAT_2_3 \ - HW_COMPAT_2_2 \ - {\ - .driver = "kvm64" "-" TYPE_X86_CPU,\ - .property = "vme",\ - .value = "off",\ - },\ - {\ - .driver = "kvm32" "-" TYPE_X86_CPU,\ - .property = "vme",\ - .value = "off",\ - },\ - {\ - .driver = "Conroe" "-" TYPE_X86_CPU,\ - .property = "vme",\ - .value = "off",\ - },\ - {\ - .driver = "Penryn" "-" TYPE_X86_CPU,\ - .property = "vme",\ - .value = "off",\ - },\ - {\ - .driver = "Nehalem" "-" TYPE_X86_CPU,\ - .property = "vme",\ - .value = "off",\ - },\ - {\ - .driver = "Westmere" "-" TYPE_X86_CPU,\ - .property = "vme",\ - .value = "off",\ - },\ - {\ - .driver = "SandyBridge" "-" TYPE_X86_CPU,\ - .property = "vme",\ - .value = "off",\ - },\ - {\ - .driver = "Haswell" "-" TYPE_X86_CPU,\ - .property = "vme",\ - .value = "off",\ - },\ - {\ - .driver = "Broadwell" "-" TYPE_X86_CPU,\ - .property = "vme",\ - .value = "off",\ - },\ - {\ - .driver = "Opteron_G1" "-" TYPE_X86_CPU,\ - .property = "vme",\ - .value = "off",\ - },\ - {\ - .driver = "Opteron_G2" "-" TYPE_X86_CPU,\ - .property = "vme",\ - .value = "off",\ - },\ - {\ - .driver = "Opteron_G3" "-" TYPE_X86_CPU,\ - .property = "vme",\ - .value = "off",\ - },\ - {\ - .driver = "Opteron_G4" "-" TYPE_X86_CPU,\ - .property = "vme",\ - .value = "off",\ - },\ - {\ - .driver = "Opteron_G5" "-" TYPE_X86_CPU,\ - .property = "vme",\ - .value = "off",\ - },\ - {\ - .driver = "Haswell" "-" TYPE_X86_CPU,\ - .property = "f16c",\ - .value = "off",\ - },\ - {\ - .driver = "Haswell" "-" TYPE_X86_CPU,\ - .property = "rdrand",\ - .value = "off",\ - },\ - {\ - .driver = "Broadwell" "-" TYPE_X86_CPU,\ - .property = "f16c",\ - .value = "off",\ - },\ - {\ - .driver = "Broadwell" "-" TYPE_X86_CPU,\ - .property = "rdrand",\ - .value = "off",\ - }, + PC_COMPAT_2_3 \ + HW_COMPAT_2_2 \ + {\ + .driver = "kvm64" "-" TYPE_X86_CPU,\ + .property = "vme",\ + .value = "off",\ + },\ + {\ + .driver = "kvm32" "-" TYPE_X86_CPU,\ + .property = "vme",\ + .value = "off",\ + },\ + {\ + .driver = "Conroe" "-" TYPE_X86_CPU,\ + .property = "vme",\ + .value = "off",\ + },\ + {\ + .driver = "Penryn" "-" TYPE_X86_CPU,\ + .property = "vme",\ + .value = "off",\ + },\ + {\ + .driver = "Nehalem" "-" TYPE_X86_CPU,\ + .property = "vme",\ + .value = "off",\ + },\ + {\ + .driver = "Westmere" "-" TYPE_X86_CPU,\ + .property = "vme",\ + .value = "off",\ + },\ + {\ + .driver = "SandyBridge" "-" TYPE_X86_CPU,\ + .property = "vme",\ + .value = "off",\ + },\ + {\ + .driver = "Haswell" "-" TYPE_X86_CPU,\ + .property = "vme",\ + .value = "off",\ + },\ + {\ + .driver = "Broadwell" "-" TYPE_X86_CPU,\ + .property = "vme",\ + .value = "off",\ + },\ + {\ + .driver = "Opteron_G1" "-" TYPE_X86_CPU,\ + .property = "vme",\ + .value = "off",\ + },\ + {\ + .driver = "Opteron_G2" "-" TYPE_X86_CPU,\ + .property = "vme",\ + .value = "off",\ + },\ + {\ + .driver = "Opteron_G3" "-" TYPE_X86_CPU,\ + .property = "vme",\ + .value = "off",\ + },\ + {\ + .driver = "Opteron_G4" "-" TYPE_X86_CPU,\ + .property = "vme",\ + .value = "off",\ + },\ + {\ + .driver = "Opteron_G5" "-" TYPE_X86_CPU,\ + .property = "vme",\ + .value = "off",\ + },\ + {\ + .driver = "Haswell" "-" TYPE_X86_CPU,\ + .property = "f16c",\ + .value = "off",\ + },\ + {\ + .driver = "Haswell" "-" TYPE_X86_CPU,\ + .property = "rdrand",\ + .value = "off",\ + },\ + {\ + .driver = "Broadwell" "-" TYPE_X86_CPU,\ + .property = "f16c",\ + .value = "off",\ + },\ + {\ + .driver = "Broadwell" "-" TYPE_X86_CPU,\ + .property = "rdrand",\ + .value = "off",\ + }, #define PC_COMPAT_2_1 \ - PC_COMPAT_2_2 \ - HW_COMPAT_2_1 \ - {\ - .driver = "coreduo" "-" TYPE_X86_CPU,\ - .property = "vmx",\ - .value = "on",\ - },\ - {\ - .driver = "core2duo" "-" TYPE_X86_CPU,\ - .property = "vmx",\ - .value = "on",\ - }, + PC_COMPAT_2_2 \ + HW_COMPAT_2_1 \ + {\ + .driver = "coreduo" "-" TYPE_X86_CPU,\ + .property = "vmx",\ + .value = "on",\ + },\ + {\ + .driver = "core2duo" "-" TYPE_X86_CPU,\ + .property = "vmx",\ + .value = "on",\ + }, #define PC_COMPAT_2_0 \ - PC_COMPAT_2_1 \ - {\ - .driver = "virtio-scsi-pci",\ - .property = "any_layout",\ - .value = "off",\ - },{\ - .driver = "PIIX4_PM",\ - .property = "memory-hotplug-support",\ - .value = "off",\ - },\ - {\ - .driver = "apic",\ - .property = "version",\ - .value = stringify(0x11),\ - },\ - {\ - .driver = "nec-usb-xhci",\ - .property = "superspeed-ports-first",\ - .value = "off",\ - },\ - {\ - .driver = "nec-usb-xhci",\ - .property = "force-pcie-endcap",\ - .value = "on",\ - },\ - {\ - .driver = "pci-serial",\ - .property = "prog_if",\ - .value = stringify(0),\ - },\ - {\ - .driver = "pci-serial-2x",\ - .property = "prog_if",\ - .value = stringify(0),\ - },\ - {\ - .driver = "pci-serial-4x",\ - .property = "prog_if",\ - .value = stringify(0),\ - },\ - {\ - .driver = "virtio-net-pci",\ - .property = "guest_announce",\ - .value = "off",\ - },\ - {\ - .driver = "ICH9-LPC",\ - .property = "memory-hotplug-support",\ - .value = "off",\ - },{\ - .driver = "xio3130-downstream",\ - .property = COMPAT_PROP_PCP,\ - .value = "off",\ - },{\ - .driver = "ioh3420",\ - .property = COMPAT_PROP_PCP,\ - .value = "off",\ - }, + PC_COMPAT_2_1 \ + {\ + .driver = "virtio-scsi-pci",\ + .property = "any_layout",\ + .value = "off",\ + },{\ + .driver = "PIIX4_PM",\ + .property = "memory-hotplug-support",\ + .value = "off",\ + },\ + {\ + .driver = "apic",\ + .property = "version",\ + .value = stringify(0x11),\ + },\ + {\ + .driver = "nec-usb-xhci",\ + .property = "superspeed-ports-first",\ + .value = "off",\ + },\ + {\ + .driver = "nec-usb-xhci",\ + .property = "force-pcie-endcap",\ + .value = "on",\ + },\ + {\ + .driver = "pci-serial",\ + .property = "prog_if",\ + .value = stringify(0),\ + },\ + {\ + .driver = "pci-serial-2x",\ + .property = "prog_if",\ + .value = stringify(0),\ + },\ + {\ + .driver = "pci-serial-4x",\ + .property = "prog_if",\ + .value = stringify(0),\ + },\ + {\ + .driver = "virtio-net-pci",\ + .property = "guest_announce",\ + .value = "off",\ + },\ + {\ + .driver = "ICH9-LPC",\ + .property = "memory-hotplug-support",\ + .value = "off",\ + },{\ + .driver = "xio3130-downstream",\ + .property = COMPAT_PROP_PCP,\ + .value = "off",\ + },{\ + .driver = "ioh3420",\ + .property = COMPAT_PROP_PCP,\ + .value = "off",\ + }, #define PC_COMPAT_1_7 \ - PC_COMPAT_2_0 \ - {\ - .driver = TYPE_USB_DEVICE,\ - .property = "msos-desc",\ - .value = "no",\ - },\ - {\ - .driver = "PIIX4_PM",\ - .property = "acpi-pci-hotplug-with-bridge-support",\ - .value = "off",\ - },\ - {\ - .driver = "hpet",\ - .property = HPET_INTCAP,\ - .value = stringify(4),\ - }, + PC_COMPAT_2_0 \ + {\ + .driver = TYPE_USB_DEVICE,\ + .property = "msos-desc",\ + .value = "no",\ + },\ + {\ + .driver = "PIIX4_PM",\ + .property = "acpi-pci-hotplug-with-bridge-support",\ + .value = "off",\ + },\ + {\ + .driver = "hpet",\ + .property = HPET_INTCAP,\ + .value = stringify(4),\ + }, #define PC_COMPAT_1_6 \ - PC_COMPAT_1_7 \ - {\ - .driver = "e1000",\ - .property = "mitigation",\ - .value = "off",\ - },{\ - .driver = "qemu64-" TYPE_X86_CPU,\ - .property = "model",\ - .value = stringify(2),\ - },{\ - .driver = "qemu32-" TYPE_X86_CPU,\ - .property = "model",\ - .value = stringify(3),\ - },{\ - .driver = "i440FX-pcihost",\ - .property = "short_root_bus",\ - .value = stringify(1),\ - },{\ - .driver = "q35-pcihost",\ - .property = "short_root_bus",\ - .value = stringify(1),\ - }, + PC_COMPAT_1_7 \ + {\ + .driver = "e1000",\ + .property = "mitigation",\ + .value = "off",\ + },{\ + .driver = "qemu64-" TYPE_X86_CPU,\ + .property = "model",\ + .value = stringify(2),\ + },{\ + .driver = "qemu32-" TYPE_X86_CPU,\ + .property = "model",\ + .value = stringify(3),\ + },{\ + .driver = "i440FX-pcihost",\ + .property = "short_root_bus",\ + .value = stringify(1),\ + },{\ + .driver = "q35-pcihost",\ + .property = "short_root_bus",\ + .value = stringify(1),\ + }, #define PC_COMPAT_1_5 \ - PC_COMPAT_1_6 \ - {\ - .driver = "Conroe-" TYPE_X86_CPU,\ - .property = "model",\ - .value = stringify(2),\ - },{\ - .driver = "Conroe-" TYPE_X86_CPU,\ - .property = "level",\ - .value = stringify(2),\ - },{\ - .driver = "Penryn-" TYPE_X86_CPU,\ - .property = "model",\ - .value = stringify(2),\ - },{\ - .driver = "Penryn-" TYPE_X86_CPU,\ - .property = "level",\ - .value = stringify(2),\ - },{\ - .driver = "Nehalem-" TYPE_X86_CPU,\ - .property = "model",\ - .value = stringify(2),\ - },{\ - .driver = "Nehalem-" TYPE_X86_CPU,\ - .property = "level",\ - .value = stringify(2),\ - },{\ - .driver = "virtio-net-pci",\ - .property = "any_layout",\ - .value = "off",\ - },{\ - .driver = TYPE_X86_CPU,\ - .property = "pmu",\ - .value = "on",\ - },{\ - .driver = "i440FX-pcihost",\ - .property = "short_root_bus",\ - .value = stringify(0),\ - },{\ - .driver = "q35-pcihost",\ - .property = "short_root_bus",\ - .value = stringify(0),\ - }, + PC_COMPAT_1_6 \ + {\ + .driver = "Conroe-" TYPE_X86_CPU,\ + .property = "model",\ + .value = stringify(2),\ + },{\ + .driver = "Conroe-" TYPE_X86_CPU,\ + .property = "level",\ + .value = stringify(2),\ + },{\ + .driver = "Penryn-" TYPE_X86_CPU,\ + .property = "model",\ + .value = stringify(2),\ + },{\ + .driver = "Penryn-" TYPE_X86_CPU,\ + .property = "level",\ + .value = stringify(2),\ + },{\ + .driver = "Nehalem-" TYPE_X86_CPU,\ + .property = "model",\ + .value = stringify(2),\ + },{\ + .driver = "Nehalem-" TYPE_X86_CPU,\ + .property = "level",\ + .value = stringify(2),\ + },{\ + .driver = "virtio-net-pci",\ + .property = "any_layout",\ + .value = "off",\ + },{\ + .driver = TYPE_X86_CPU,\ + .property = "pmu",\ + .value = "on",\ + },{\ + .driver = "i440FX-pcihost",\ + .property = "short_root_bus",\ + .value = stringify(0),\ + },{\ + .driver = "q35-pcihost",\ + .property = "short_root_bus",\ + .value = stringify(0),\ + }, #define PC_COMPAT_1_4 \ - PC_COMPAT_1_5 \ - {\ - .driver = "scsi-hd",\ - .property = "discard_granularity",\ - .value = stringify(0),\ - },{\ - .driver = "scsi-cd",\ - .property = "discard_granularity",\ - .value = stringify(0),\ - },{\ - .driver = "scsi-disk",\ - .property = "discard_granularity",\ - .value = stringify(0),\ - },{\ - .driver = "ide-hd",\ - .property = "discard_granularity",\ - .value = stringify(0),\ - },{\ - .driver = "ide-cd",\ - .property = "discard_granularity",\ - .value = stringify(0),\ - },{\ - .driver = "ide-drive",\ - .property = "discard_granularity",\ - .value = stringify(0),\ - },{\ - .driver = "virtio-blk-pci",\ - .property = "discard_granularity",\ - .value = stringify(0),\ - },{\ - .driver = "virtio-serial-pci",\ - .property = "vectors",\ - /* DEV_NVECTORS_UNSPECIFIED as a uint32_t string */\ - .value = stringify(0xFFFFFFFF),\ - },{ \ - .driver = "virtio-net-pci", \ - .property = "ctrl_guest_offloads", \ - .value = "off", \ - },{\ - .driver = "e1000",\ - .property = "romfile",\ - .value = "pxe-e1000.rom",\ - },{\ - .driver = "ne2k_pci",\ - .property = "romfile",\ - .value = "pxe-ne2k_pci.rom",\ - },{\ - .driver = "pcnet",\ - .property = "romfile",\ - .value = "pxe-pcnet.rom",\ - },{\ - .driver = "rtl8139",\ - .property = "romfile",\ - .value = "pxe-rtl8139.rom",\ - },{\ - .driver = "virtio-net-pci",\ - .property = "romfile",\ - .value = "pxe-virtio.rom",\ - },{\ - .driver = "486-" TYPE_X86_CPU,\ - .property = "model",\ - .value = stringify(0),\ - },\ - {\ - .driver = "n270" "-" TYPE_X86_CPU,\ - .property = "movbe",\ - .value = "off",\ - },\ - {\ - .driver = "Westmere" "-" TYPE_X86_CPU,\ - .property = "pclmulqdq",\ - .value = "off",\ - }, + PC_COMPAT_1_5 \ + {\ + .driver = "scsi-hd",\ + .property = "discard_granularity",\ + .value = stringify(0),\ + },{\ + .driver = "scsi-cd",\ + .property = "discard_granularity",\ + .value = stringify(0),\ + },{\ + .driver = "scsi-disk",\ + .property = "discard_granularity",\ + .value = stringify(0),\ + },{\ + .driver = "ide-hd",\ + .property = "discard_granularity",\ + .value = stringify(0),\ + },{\ + .driver = "ide-cd",\ + .property = "discard_granularity",\ + .value = stringify(0),\ + },{\ + .driver = "ide-drive",\ + .property = "discard_granularity",\ + .value = stringify(0),\ + },{\ + .driver = "virtio-blk-pci",\ + .property = "discard_granularity",\ + .value = stringify(0),\ + },{\ + .driver = "virtio-serial-pci",\ + .property = "vectors",\ + /* DEV_NVECTORS_UNSPECIFIED as a uint32_t string */\ + .value = stringify(0xFFFFFFFF),\ + },{ \ + .driver = "virtio-net-pci", \ + .property = "ctrl_guest_offloads", \ + .value = "off", \ + },{\ + .driver = "e1000",\ + .property = "romfile",\ + .value = "pxe-e1000.rom",\ + },{\ + .driver = "ne2k_pci",\ + .property = "romfile",\ + .value = "pxe-ne2k_pci.rom",\ + },{\ + .driver = "pcnet",\ + .property = "romfile",\ + .value = "pxe-pcnet.rom",\ + },{\ + .driver = "rtl8139",\ + .property = "romfile",\ + .value = "pxe-rtl8139.rom",\ + },{\ + .driver = "virtio-net-pci",\ + .property = "romfile",\ + .value = "pxe-virtio.rom",\ + },{\ + .driver = "486-" TYPE_X86_CPU,\ + .property = "model",\ + .value = stringify(0),\ + },\ + {\ + .driver = "n270" "-" TYPE_X86_CPU,\ + .property = "movbe",\ + .value = "off",\ + },\ + {\ + .driver = "Westmere" "-" TYPE_X86_CPU,\ + .property = "pclmulqdq",\ + .value = "off",\ + }, #define DEFINE_PC_MACHINE(suffix, namestr, initfn, optsfn) \ static void pc_machine_##suffix##_class_init(ObjectClass *oc, void *data) \ From c9c0afbb1919f8df1e2c9f44b88b8a0dc275f61c Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Mon, 30 Nov 2015 12:56:38 -0200 Subject: [PATCH 28/55] hw/compat.h: Change indentation of HW_COMPAT_* to 4 spaces Cosmetic change only. Signed-off-by: Eduardo Habkost Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Thomas Huth --- include/hw/compat.h | 152 ++++++++++++++++++++++---------------------- 1 file changed, 76 insertions(+), 76 deletions(-) diff --git a/include/hw/compat.h b/include/hw/compat.h index eaf5ab47ba..3d8d2a9244 100644 --- a/include/hw/compat.h +++ b/include/hw/compat.h @@ -5,87 +5,87 @@ /* empty */ #define HW_COMPAT_2_4 \ - {\ - .driver = "virtio-blk-device",\ - .property = "scsi",\ - .value = "true",\ - },{\ - .driver = "pvscsi",\ - .property = "x-old-pci-configuration",\ - .value = "on",\ - },{\ - .driver = "pvscsi",\ - .property = "x-disable-pcie",\ - .value = "on",\ - },{\ - .driver = "e1000",\ - .property = "extra_mac_registers",\ - .value = "off",\ - },{\ - .driver = "virtio-pci",\ - .property = "x-disable-pcie",\ - .value = "on",\ - },{\ - .driver = "virtio-pci",\ - .property = "migrate-extra",\ - .value = "off",\ - }, + {\ + .driver = "virtio-blk-device",\ + .property = "scsi",\ + .value = "true",\ + },{\ + .driver = "pvscsi",\ + .property = "x-old-pci-configuration",\ + .value = "on",\ + },{\ + .driver = "pvscsi",\ + .property = "x-disable-pcie",\ + .value = "on",\ + },{\ + .driver = "e1000",\ + .property = "extra_mac_registers",\ + .value = "off",\ + },{\ + .driver = "virtio-pci",\ + .property = "x-disable-pcie",\ + .value = "on",\ + },{\ + .driver = "virtio-pci",\ + .property = "migrate-extra",\ + .value = "off",\ + }, #define HW_COMPAT_2_3 \ - {\ - .driver = "virtio-blk-pci",\ - .property = "any_layout",\ - .value = "off",\ - },{\ - .driver = "virtio-balloon-pci",\ - .property = "any_layout",\ - .value = "off",\ - },{\ - .driver = "virtio-serial-pci",\ - .property = "any_layout",\ - .value = "off",\ - },{\ - .driver = "virtio-9p-pci",\ - .property = "any_layout",\ - .value = "off",\ - },{\ - .driver = "virtio-rng-pci",\ - .property = "any_layout",\ - .value = "off",\ - }, + {\ + .driver = "virtio-blk-pci",\ + .property = "any_layout",\ + .value = "off",\ + },{\ + .driver = "virtio-balloon-pci",\ + .property = "any_layout",\ + .value = "off",\ + },{\ + .driver = "virtio-serial-pci",\ + .property = "any_layout",\ + .value = "off",\ + },{\ + .driver = "virtio-9p-pci",\ + .property = "any_layout",\ + .value = "off",\ + },{\ + .driver = "virtio-rng-pci",\ + .property = "any_layout",\ + .value = "off",\ + }, #define HW_COMPAT_2_2 \ - /* empty */ + /* empty */ #define HW_COMPAT_2_1 \ - {\ - .driver = "intel-hda",\ - .property = "old_msi_addr",\ - .value = "on",\ - },{\ - .driver = "VGA",\ - .property = "qemu-extended-regs",\ - .value = "off",\ - },{\ - .driver = "secondary-vga",\ - .property = "qemu-extended-regs",\ - .value = "off",\ - },{\ - .driver = "virtio-scsi-pci",\ - .property = "any_layout",\ - .value = "off",\ - },{\ - .driver = "usb-mouse",\ - .property = "usb_version",\ - .value = stringify(1),\ - },{\ - .driver = "usb-kbd",\ - .property = "usb_version",\ - .value = stringify(1),\ - },{\ - .driver = "virtio-pci",\ - .property = "virtio-pci-bus-master-bug-migration",\ - .value = "on",\ - }, + {\ + .driver = "intel-hda",\ + .property = "old_msi_addr",\ + .value = "on",\ + },{\ + .driver = "VGA",\ + .property = "qemu-extended-regs",\ + .value = "off",\ + },{\ + .driver = "secondary-vga",\ + .property = "qemu-extended-regs",\ + .value = "off",\ + },{\ + .driver = "virtio-scsi-pci",\ + .property = "any_layout",\ + .value = "off",\ + },{\ + .driver = "usb-mouse",\ + .property = "usb_version",\ + .value = stringify(1),\ + },{\ + .driver = "usb-kbd",\ + .property = "usb_version",\ + .value = stringify(1),\ + },{\ + .driver = "virtio-pci",\ + .property = "virtio-pci-bus-master-bug-migration",\ + .value = "on",\ + }, #endif /* HW_COMPAT_H */ From 40c520418b1172661f64f85d453edaf17b9a5317 Mon Sep 17 00:00:00 2001 From: Cao jin Date: Tue, 22 Dec 2015 20:00:43 +0800 Subject: [PATCH 29/55] docs/pci_expander_bridge: fix typo Signed-off-by: Cao jin Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Marcel Apfelbaum --- docs/pci_expander_bridge.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/pci_expander_bridge.txt b/docs/pci_expander_bridge.txt index d7913fb4ae..e7c8fe939f 100644 --- a/docs/pci_expander_bridge.txt +++ b/docs/pci_expander_bridge.txt @@ -23,9 +23,9 @@ A detailed command line would be: -m 2G -object memory-backend-ram,size=1024M,policy=bind,host-nodes=0,id=ram-node0 -numa node,nodeid=0,cpus=0,memdev=ram-node0 -object memory-backend-ram,size=1024M,policy=bind,host-nodes=1,id=ram-node1 -numa node,nodeid=1,cpus=1,memdev=ram-node1 --device pxb,id=bridge1,bus=pci.0,numa_node=1,bus_nr=4 -netdev user,id=nd-device e1000,bus=bridge1,addr=0x4,netdev=nd --device pxb,id=bridge2,bus=pci.0,numa_node=0,bus_nr=8,bus=pci.0 -device e1000,bus=bridge2,addr=0x3 --device pxb,id=bridge3,bus=pci.0,bus_nr=40,bus=pci.0 -drive if=none,id=drive0,file=[img] -device virtio-blk-pci,drive=drive0,scsi=off,bus=bridge3,addr=1 +-device pxb,id=bridge1,bus=pci.0,numa_node=1,bus_nr=4 -netdev user,id=nd -device e1000,bus=bridge1,addr=0x4,netdev=nd +-device pxb,id=bridge2,bus=pci.0,numa_node=0,bus_nr=8, -device e1000,bus=bridge2,addr=0x3 +-device pxb,id=bridge3,bus=pci.0,bus_nr=40, -drive if=none,id=drive0,file=[img] -device virtio-blk-pci,drive=drive0,scsi=off,bus=bridge3,addr=1 Here you have: - 2 NUMA nodes for the guest, 0 and 1. (both mapped to the same NUMA node in host, but you can and should put it in different host NUMA nodes) From 5c42eef24314fb51600b0e90fb69c60653ea05bb Mon Sep 17 00:00:00 2001 From: Xiao Guangrong Date: Wed, 2 Dec 2015 15:20:56 +0800 Subject: [PATCH 30/55] nvdimm: implement NVDIMM device abstract Introduce "nvdimm" device which is based on pc-dimm device type Currently, nothing is specific for nvdimm but hotplug is disabled Reviewed-by: Stefan Hajnoczi Signed-off-by: Xiao Guangrong Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- default-configs/i386-softmmu.mak | 1 + default-configs/x86_64-softmmu.mak | 1 + hw/acpi/memory_hotplug.c | 5 ++++ hw/mem/Makefile.objs | 1 + hw/mem/nvdimm.c | 46 ++++++++++++++++++++++++++++++ include/hw/mem/nvdimm.h | 29 +++++++++++++++++++ 6 files changed, 83 insertions(+) create mode 100644 hw/mem/nvdimm.c create mode 100644 include/hw/mem/nvdimm.h diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak index fb1ad90a03..b0355993e1 100644 --- a/default-configs/i386-softmmu.mak +++ b/default-configs/i386-softmmu.mak @@ -51,6 +51,7 @@ CONFIG_APIC=y CONFIG_IOAPIC=y CONFIG_PVPANIC=y CONFIG_MEM_HOTPLUG=y +CONFIG_NVDIMM=y CONFIG_XIO3130=y CONFIG_IOH3420=y CONFIG_I82801B11=y diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak index 613154fda4..27669bec58 100644 --- a/default-configs/x86_64-softmmu.mak +++ b/default-configs/x86_64-softmmu.mak @@ -51,6 +51,7 @@ CONFIG_APIC=y CONFIG_IOAPIC=y CONFIG_PVPANIC=y CONFIG_MEM_HOTPLUG=y +CONFIG_NVDIMM=y CONFIG_XIO3130=y CONFIG_IOH3420=y CONFIG_I82801B11=y diff --git a/hw/acpi/memory_hotplug.c b/hw/acpi/memory_hotplug.c index e4b9a01f80..298e8682af 100644 --- a/hw/acpi/memory_hotplug.c +++ b/hw/acpi/memory_hotplug.c @@ -231,6 +231,11 @@ void acpi_memory_plug_cb(ACPIREGS *ar, qemu_irq irq, MemHotplugState *mem_st, DeviceState *dev, Error **errp) { MemStatus *mdev; + DeviceClass *dc = DEVICE_GET_CLASS(dev); + + if (!dc->hotpluggable) { + return; + } mdev = acpi_memory_slot_status(mem_st, dev, errp); if (!mdev) { diff --git a/hw/mem/Makefile.objs b/hw/mem/Makefile.objs index b000fb42bf..f12f8b97a2 100644 --- a/hw/mem/Makefile.objs +++ b/hw/mem/Makefile.objs @@ -1 +1,2 @@ common-obj-$(CONFIG_MEM_HOTPLUG) += pc-dimm.o +common-obj-$(CONFIG_NVDIMM) += nvdimm.o diff --git a/hw/mem/nvdimm.c b/hw/mem/nvdimm.c new file mode 100644 index 0000000000..4fd397f015 --- /dev/null +++ b/hw/mem/nvdimm.c @@ -0,0 +1,46 @@ +/* + * Non-Volatile Dual In-line Memory Module Virtualization Implementation + * + * Copyright(C) 2015 Intel Corporation. + * + * Author: + * Xiao Guangrong + * + * Currently, it only supports PMEM Virtualization. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#include "hw/mem/nvdimm.h" + +static void nvdimm_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + /* nvdimm hotplug has not been supported yet. */ + dc->hotpluggable = false; +} + +static TypeInfo nvdimm_info = { + .name = TYPE_NVDIMM, + .parent = TYPE_PC_DIMM, + .class_init = nvdimm_class_init, +}; + +static void nvdimm_register_types(void) +{ + type_register_static(&nvdimm_info); +} + +type_init(nvdimm_register_types) diff --git a/include/hw/mem/nvdimm.h b/include/hw/mem/nvdimm.h new file mode 100644 index 0000000000..dbfa8d6bd4 --- /dev/null +++ b/include/hw/mem/nvdimm.h @@ -0,0 +1,29 @@ +/* + * Non-Volatile Dual In-line Memory Module Virtualization Implementation + * + * Copyright(C) 2015 Intel Corporation. + * + * Author: + * Xiao Guangrong + * + * NVDIMM specifications and some documents can be found at: + * NVDIMM ACPI device and NFIT are introduced in ACPI 6: + * http://www.uefi.org/sites/default/files/resources/ACPI_6.0.pdf + * NVDIMM Namespace specification: + * http://pmem.io/documents/NVDIMM_Namespace_Spec.pdf + * DSM Interface Example: + * http://pmem.io/documents/NVDIMM_DSM_Interface_Example.pdf + * Driver Writer's Guide: + * http://pmem.io/documents/NVDIMM_Driver_Writers_Guide.pdf + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef QEMU_NVDIMM_H +#define QEMU_NVDIMM_H + +#include "hw/mem/pc-dimm.h" + +#define TYPE_NVDIMM "nvdimm" +#endif From 8870ca0e94f2524644812dd759863c0851ffb870 Mon Sep 17 00:00:00 2001 From: Xiao Guangrong Date: Wed, 2 Dec 2015 15:20:57 +0800 Subject: [PATCH 31/55] acpi: support specified oem table id for build_header Let build_header() support specified OEM table id so that we can build multiple SSDT later If the oem table id is not specified (aka, NULL), we use the default id instead as the previous behavior Reviewed-by: Stefan Hajnoczi Signed-off-by: Xiao Guangrong Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/aml-build.c | 15 +++++++++++---- hw/arm/virt-acpi-build.c | 13 +++++++------ hw/i386/acpi-build.c | 20 ++++++++++---------- include/hw/acpi/aml-build.h | 3 ++- 4 files changed, 30 insertions(+), 21 deletions(-) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index 302cf01e5c..363e83b851 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -1242,14 +1242,21 @@ Aml *aml_unicode(const char *str) void build_header(GArray *linker, GArray *table_data, - AcpiTableHeader *h, const char *sig, int len, uint8_t rev) + AcpiTableHeader *h, const char *sig, int len, uint8_t rev, + const char *oem_table_id) { memcpy(&h->signature, sig, 4); h->length = cpu_to_le32(len); h->revision = rev; memcpy(h->oem_id, ACPI_BUILD_APPNAME6, 6); - memcpy(h->oem_table_id, ACPI_BUILD_APPNAME4, 4); - memcpy(h->oem_table_id + 4, sig, 4); + + if (oem_table_id) { + strncpy((char *)h->oem_table_id, oem_table_id, sizeof(h->oem_table_id)); + } else { + memcpy(h->oem_table_id, ACPI_BUILD_APPNAME4, 4); + memcpy(h->oem_table_id + 4, sig, 4); + } + h->oem_revision = cpu_to_le32(1); memcpy(h->asl_compiler_id, ACPI_BUILD_APPNAME4, 4); h->asl_compiler_revision = cpu_to_le32(1); @@ -1316,5 +1323,5 @@ build_rsdt(GArray *table_data, GArray *linker, GArray *table_offsets) sizeof(uint32_t)); } build_header(linker, table_data, - (void *)rsdt, "RSDT", rsdt_len, 1); + (void *)rsdt, "RSDT", rsdt_len, 1, NULL); } diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 32ec19e884..7aa0ab351d 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -423,7 +423,8 @@ build_spcr(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info) spcr->pci_device_id = 0xffff; /* PCI Device ID: not a PCI device */ spcr->pci_vendor_id = 0xffff; /* PCI Vendor ID: not a PCI device */ - build_header(linker, table_data, (void *)spcr, "SPCR", sizeof(*spcr), 2); + build_header(linker, table_data, (void *)spcr, "SPCR", sizeof(*spcr), 2, + NULL); } static void @@ -442,7 +443,7 @@ build_mcfg(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info) mcfg->allocation[0].end_bus_number = (memmap[VIRT_PCIE_ECAM].size / PCIE_MMCFG_SIZE_MIN) - 1; - build_header(linker, table_data, (void *)mcfg, "MCFG", len, 1); + build_header(linker, table_data, (void *)mcfg, "MCFG", len, 1, NULL); } /* GTDT */ @@ -468,7 +469,7 @@ build_gtdt(GArray *table_data, GArray *linker) build_header(linker, table_data, (void *)(table_data->data + gtdt_start), "GTDT", - table_data->len - gtdt_start, 2); + table_data->len - gtdt_start, 2, NULL); } /* MADT */ @@ -530,7 +531,7 @@ build_madt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info, build_header(linker, table_data, (void *)(table_data->data + madt_start), "APIC", - table_data->len - madt_start, 3); + table_data->len - madt_start, 3, NULL); } /* FADT */ @@ -555,7 +556,7 @@ build_fadt(GArray *table_data, GArray *linker, unsigned dsdt) sizeof fadt->dsdt); build_header(linker, table_data, - (void *)fadt, "FACP", sizeof(*fadt), 5); + (void *)fadt, "FACP", sizeof(*fadt), 5, NULL); } /* DSDT */ @@ -591,7 +592,7 @@ build_dsdt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info) g_array_append_vals(table_data, dsdt->buf->data, dsdt->buf->len); build_header(linker, table_data, (void *)(table_data->data + table_data->len - dsdt->buf->len), - "DSDT", dsdt->buf->len, 2); + "DSDT", dsdt->buf->len, 2, NULL); free_aml_allocator(); } diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index a60436b5bc..27d2caf144 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -361,7 +361,7 @@ build_fadt(GArray *table_data, GArray *linker, AcpiPmInfo *pm, fadt_setup(fadt, pm); build_header(linker, table_data, - (void *)fadt, "FACP", sizeof(*fadt), 1); + (void *)fadt, "FACP", sizeof(*fadt), 1, NULL); } static void @@ -431,7 +431,7 @@ build_madt(GArray *table_data, GArray *linker, AcpiCpuInfo *cpu, build_header(linker, table_data, (void *)(table_data->data + madt_start), "APIC", - table_data->len - madt_start, 1); + table_data->len - madt_start, 1, NULL); } /* Assign BSEL property to all buses. In the future, this can be changed @@ -1374,7 +1374,7 @@ build_ssdt(GArray *table_data, GArray *linker, g_array_append_vals(table_data, ssdt->buf->data, ssdt->buf->len); build_header(linker, table_data, (void *)(table_data->data + table_data->len - ssdt->buf->len), - "SSDT", ssdt->buf->len, 1); + "SSDT", ssdt->buf->len, 1, NULL); free_aml_allocator(); } @@ -1390,7 +1390,7 @@ build_hpet(GArray *table_data, GArray *linker) hpet->timer_block_id = cpu_to_le32(0x8086a201); hpet->addr.address = cpu_to_le64(HPET_BASE); build_header(linker, table_data, - (void *)hpet, "HPET", sizeof(*hpet), 1); + (void *)hpet, "HPET", sizeof(*hpet), 1, NULL); } static void @@ -1413,7 +1413,7 @@ build_tpm_tcpa(GArray *table_data, GArray *linker, GArray *tcpalog) sizeof(tcpa->log_area_start_address)); build_header(linker, table_data, - (void *)tcpa, "TCPA", sizeof(*tcpa), 2); + (void *)tcpa, "TCPA", sizeof(*tcpa), 2, NULL); acpi_data_push(tcpalog, TPM_LOG_AREA_MINIMUM_SIZE); } @@ -1430,7 +1430,7 @@ build_tpm2(GArray *table_data, GArray *linker) tpm2_ptr->start_method = cpu_to_le32(TPM2_START_METHOD_MMIO); build_header(linker, table_data, - (void *)tpm2_ptr, "TPM2", sizeof(*tpm2_ptr), 4); + (void *)tpm2_ptr, "TPM2", sizeof(*tpm2_ptr), 4, NULL); } typedef enum { @@ -1544,7 +1544,7 @@ build_srat(GArray *table_data, GArray *linker, PcGuestInfo *guest_info) build_header(linker, table_data, (void *)(table_data->data + srat_start), "SRAT", - table_data->len - srat_start, 1); + table_data->len - srat_start, 1, NULL); } static void @@ -1573,7 +1573,7 @@ build_mcfg_q35(GArray *table_data, GArray *linker, AcpiMcfgInfo *info) } else { sig = "MCFG"; } - build_header(linker, table_data, (void *)mcfg, sig, len, 1); + build_header(linker, table_data, (void *)mcfg, sig, len, 1, NULL); } static void @@ -1597,7 +1597,7 @@ build_dmar_q35(GArray *table_data, GArray *linker) drhd->address = cpu_to_le64(Q35_HOST_BRIDGE_IOMMU_ADDR); build_header(linker, table_data, (void *)(table_data->data + dmar_start), - "DMAR", table_data->len - dmar_start, 1); + "DMAR", table_data->len - dmar_start, 1, NULL); } static void @@ -1612,7 +1612,7 @@ build_dsdt(GArray *table_data, GArray *linker, AcpiMiscInfo *misc) memset(dsdt, 0, sizeof *dsdt); build_header(linker, table_data, dsdt, "DSDT", - misc->dsdt_size, 1); + misc->dsdt_size, 1, NULL); } static GArray * diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h index 0d3645322c..9fa61a3f6e 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -310,7 +310,8 @@ Aml *aml_unicode(const char *str); void build_header(GArray *linker, GArray *table_data, - AcpiTableHeader *h, const char *sig, int len, uint8_t rev); + AcpiTableHeader *h, const char *sig, int len, uint8_t rev, + const char *oem_table_id); void *acpi_data_push(GArray *table_data, unsigned size); unsigned acpi_data_len(GArray *table); void acpi_add_table(GArray *table_offsets, GArray *table_data); From 87252e1b61cb6e651da6cd3e9996ade8bd59a388 Mon Sep 17 00:00:00 2001 From: Xiao Guangrong Date: Wed, 2 Dec 2015 15:20:58 +0800 Subject: [PATCH 32/55] nvdimm acpi: build ACPI NFIT table NFIT is defined in ACPI 6.0: 5.2.25 NVDIMM Firmware Interface Table (NFIT) Currently, we only support PMEM mode. Each device has 3 structures: - SPA structure, defines the PMEM region info - MEM DEV structure, it has the @handle which is used to associate specified ACPI NVDIMM device we will introduce in later patch. Also we can happily ignored the memory device's interleave, the real nvdimm hardware access is hidden behind host - DCR structure, it defines vendor ID used to associate specified vendor nvdimm driver. Since we only implement PMEM mode this time, Command window and Data window are not needed The NVDIMM functionality is controlled by the parameter, 'nvdimm', which is introduced for the machine, there is a example to enable it: -machine pc,nvdimm -m 8G,maxmem=100G,slots=100 -object \ memory-backend-file,id=mem1,share,mem-path=/tmp/nvdimm1,size=10G -device \ nvdimm,memdev=mem1,id=nv1 It is disabled on default Reviewed-by: Stefan Hajnoczi Signed-off-by: Xiao Guangrong Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- default-configs/i386-softmmu.mak | 1 + default-configs/x86_64-softmmu.mak | 1 + hw/acpi/Makefile.objs | 1 + hw/acpi/nvdimm.c | 382 +++++++++++++++++++++++++++++ hw/i386/acpi-build.c | 12 + hw/i386/pc.c | 19 ++ include/hw/i386/pc.h | 2 + include/hw/mem/nvdimm.h | 3 + qemu-options.hx | 5 +- 9 files changed, 425 insertions(+), 1 deletion(-) create mode 100644 hw/acpi/nvdimm.c diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak index b0355993e1..b177e52104 100644 --- a/default-configs/i386-softmmu.mak +++ b/default-configs/i386-softmmu.mak @@ -52,6 +52,7 @@ CONFIG_IOAPIC=y CONFIG_PVPANIC=y CONFIG_MEM_HOTPLUG=y CONFIG_NVDIMM=y +CONFIG_ACPI_NVDIMM=y CONFIG_XIO3130=y CONFIG_IOH3420=y CONFIG_I82801B11=y diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak index 27669bec58..6e3b312c5f 100644 --- a/default-configs/x86_64-softmmu.mak +++ b/default-configs/x86_64-softmmu.mak @@ -52,6 +52,7 @@ CONFIG_IOAPIC=y CONFIG_PVPANIC=y CONFIG_MEM_HOTPLUG=y CONFIG_NVDIMM=y +CONFIG_ACPI_NVDIMM=y CONFIG_XIO3130=y CONFIG_IOH3420=y CONFIG_I82801B11=y diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs index 7d3230c2a5..095597f791 100644 --- a/hw/acpi/Makefile.objs +++ b/hw/acpi/Makefile.objs @@ -2,6 +2,7 @@ common-obj-$(CONFIG_ACPI_X86) += core.o piix4.o pcihp.o common-obj-$(CONFIG_ACPI_X86_ICH) += ich9.o tco.o common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu_hotplug.o common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o +common-obj-$(CONFIG_ACPI_NVDIMM) += nvdimm.o common-obj-$(CONFIG_ACPI) += acpi_interface.o common-obj-$(CONFIG_ACPI) += bios-linker-loader.o common-obj-$(CONFIG_ACPI) += aml-build.o diff --git a/hw/acpi/nvdimm.c b/hw/acpi/nvdimm.c new file mode 100644 index 0000000000..98c004d1a5 --- /dev/null +++ b/hw/acpi/nvdimm.c @@ -0,0 +1,382 @@ +/* + * NVDIMM ACPI Implementation + * + * Copyright(C) 2015 Intel Corporation. + * + * Author: + * Xiao Guangrong + * + * NFIT is defined in ACPI 6.0: 5.2.25 NVDIMM Firmware Interface Table (NFIT) + * and the DSM specification can be found at: + * http://pmem.io/documents/NVDIMM_DSM_Interface_Example.pdf + * + * Currently, it only supports PMEM Virtualization. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + */ + +#include "hw/acpi/acpi.h" +#include "hw/acpi/aml-build.h" +#include "hw/mem/nvdimm.h" + +static int nvdimm_plugged_device_list(Object *obj, void *opaque) +{ + GSList **list = opaque; + + if (object_dynamic_cast(obj, TYPE_NVDIMM)) { + DeviceState *dev = DEVICE(obj); + + if (dev->realized) { /* only realized NVDIMMs matter */ + *list = g_slist_append(*list, DEVICE(obj)); + } + } + + object_child_foreach(obj, nvdimm_plugged_device_list, opaque); + return 0; +} + +/* + * inquire plugged NVDIMM devices and link them into the list which is + * returned to the caller. + * + * Note: it is the caller's responsibility to free the list to avoid + * memory leak. + */ +static GSList *nvdimm_get_plugged_device_list(void) +{ + GSList *list = NULL; + + object_child_foreach(qdev_get_machine(), nvdimm_plugged_device_list, + &list); + return list; +} + +#define NVDIMM_UUID_LE(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7) \ + { (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \ + (b) & 0xff, ((b) >> 8) & 0xff, (c) & 0xff, ((c) >> 8) & 0xff, \ + (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) } + +/* + * define Byte Addressable Persistent Memory (PM) Region according to + * ACPI 6.0: 5.2.25.1 System Physical Address Range Structure. + */ +static const uint8_t nvdimm_nfit_spa_uuid[] = + NVDIMM_UUID_LE(0x66f0d379, 0xb4f3, 0x4074, 0xac, 0x43, 0x0d, 0x33, + 0x18, 0xb7, 0x8c, 0xdb); + +/* + * NVDIMM Firmware Interface Table + * @signature: "NFIT" + * + * It provides information that allows OSPM to enumerate NVDIMM present in + * the platform and associate system physical address ranges created by the + * NVDIMMs. + * + * It is defined in ACPI 6.0: 5.2.25 NVDIMM Firmware Interface Table (NFIT) + */ +struct NvdimmNfitHeader { + ACPI_TABLE_HEADER_DEF + uint32_t reserved; +} QEMU_PACKED; +typedef struct NvdimmNfitHeader NvdimmNfitHeader; + +/* + * define NFIT structures according to ACPI 6.0: 5.2.25 NVDIMM Firmware + * Interface Table (NFIT). + */ + +/* + * System Physical Address Range Structure + * + * It describes the system physical address ranges occupied by NVDIMMs and + * the types of the regions. + */ +struct NvdimmNfitSpa { + uint16_t type; + uint16_t length; + uint16_t spa_index; + uint16_t flags; + uint32_t reserved; + uint32_t proximity_domain; + uint8_t type_guid[16]; + uint64_t spa_base; + uint64_t spa_length; + uint64_t mem_attr; +} QEMU_PACKED; +typedef struct NvdimmNfitSpa NvdimmNfitSpa; + +/* + * Memory Device to System Physical Address Range Mapping Structure + * + * It enables identifying each NVDIMM region and the corresponding SPA + * describing the memory interleave + */ +struct NvdimmNfitMemDev { + uint16_t type; + uint16_t length; + uint32_t nfit_handle; + uint16_t phys_id; + uint16_t region_id; + uint16_t spa_index; + uint16_t dcr_index; + uint64_t region_len; + uint64_t region_offset; + uint64_t region_dpa; + uint16_t interleave_index; + uint16_t interleave_ways; + uint16_t flags; + uint16_t reserved; +} QEMU_PACKED; +typedef struct NvdimmNfitMemDev NvdimmNfitMemDev; + +/* + * NVDIMM Control Region Structure + * + * It describes the NVDIMM and if applicable, Block Control Window. + */ +struct NvdimmNfitControlRegion { + uint16_t type; + uint16_t length; + uint16_t dcr_index; + uint16_t vendor_id; + uint16_t device_id; + uint16_t revision_id; + uint16_t sub_vendor_id; + uint16_t sub_device_id; + uint16_t sub_revision_id; + uint8_t reserved[6]; + uint32_t serial_number; + uint16_t fic; + uint16_t num_bcw; + uint64_t bcw_size; + uint64_t cmd_offset; + uint64_t cmd_size; + uint64_t status_offset; + uint64_t status_size; + uint16_t flags; + uint8_t reserved2[6]; +} QEMU_PACKED; +typedef struct NvdimmNfitControlRegion NvdimmNfitControlRegion; + +/* + * Module serial number is a unique number for each device. We use the + * slot id of NVDIMM device to generate this number so that each device + * associates with a different number. + * + * 0x123456 is a magic number we arbitrarily chose. + */ +static uint32_t nvdimm_slot_to_sn(int slot) +{ + return 0x123456 + slot; +} + +/* + * handle is used to uniquely associate nfit_memdev structure with NVDIMM + * ACPI device - nfit_memdev.nfit_handle matches with the value returned + * by ACPI device _ADR method. + * + * We generate the handle with the slot id of NVDIMM device and reserve + * 0 for NVDIMM root device. + */ +static uint32_t nvdimm_slot_to_handle(int slot) +{ + return slot + 1; +} + +/* + * index uniquely identifies the structure, 0 is reserved which indicates + * that the structure is not valid or the associated structure is not + * present. + * + * Each NVDIMM device needs two indexes, one for nfit_spa and another for + * nfit_dc which are generated by the slot id of NVDIMM device. + */ +static uint16_t nvdimm_slot_to_spa_index(int slot) +{ + return (slot + 1) << 1; +} + +/* See the comments of nvdimm_slot_to_spa_index(). */ +static uint32_t nvdimm_slot_to_dcr_index(int slot) +{ + return nvdimm_slot_to_spa_index(slot) + 1; +} + +/* ACPI 6.0: 5.2.25.1 System Physical Address Range Structure */ +static void +nvdimm_build_structure_spa(GArray *structures, DeviceState *dev) +{ + NvdimmNfitSpa *nfit_spa; + uint64_t addr = object_property_get_int(OBJECT(dev), PC_DIMM_ADDR_PROP, + NULL); + uint64_t size = object_property_get_int(OBJECT(dev), PC_DIMM_SIZE_PROP, + NULL); + uint32_t node = object_property_get_int(OBJECT(dev), PC_DIMM_NODE_PROP, + NULL); + int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP, + NULL); + + nfit_spa = acpi_data_push(structures, sizeof(*nfit_spa)); + + nfit_spa->type = cpu_to_le16(0 /* System Physical Address Range + Structure */); + nfit_spa->length = cpu_to_le16(sizeof(*nfit_spa)); + nfit_spa->spa_index = cpu_to_le16(nvdimm_slot_to_spa_index(slot)); + + /* + * Control region is strict as all the device info, such as SN, index, + * is associated with slot id. + */ + nfit_spa->flags = cpu_to_le16(1 /* Control region is strictly for + management during hot add/online + operation */ | + 2 /* Data in Proximity Domain field is + valid*/); + + /* NUMA node. */ + nfit_spa->proximity_domain = cpu_to_le32(node); + /* the region reported as PMEM. */ + memcpy(nfit_spa->type_guid, nvdimm_nfit_spa_uuid, + sizeof(nvdimm_nfit_spa_uuid)); + + nfit_spa->spa_base = cpu_to_le64(addr); + nfit_spa->spa_length = cpu_to_le64(size); + + /* It is the PMEM and can be cached as writeback. */ + nfit_spa->mem_attr = cpu_to_le64(0x8ULL /* EFI_MEMORY_WB */ | + 0x8000ULL /* EFI_MEMORY_NV */); +} + +/* + * ACPI 6.0: 5.2.25.2 Memory Device to System Physical Address Range Mapping + * Structure + */ +static void +nvdimm_build_structure_memdev(GArray *structures, DeviceState *dev) +{ + NvdimmNfitMemDev *nfit_memdev; + uint64_t addr = object_property_get_int(OBJECT(dev), PC_DIMM_ADDR_PROP, + NULL); + uint64_t size = object_property_get_int(OBJECT(dev), PC_DIMM_SIZE_PROP, + NULL); + int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP, + NULL); + uint32_t handle = nvdimm_slot_to_handle(slot); + + nfit_memdev = acpi_data_push(structures, sizeof(*nfit_memdev)); + + nfit_memdev->type = cpu_to_le16(1 /* Memory Device to System Address + Range Map Structure*/); + nfit_memdev->length = cpu_to_le16(sizeof(*nfit_memdev)); + nfit_memdev->nfit_handle = cpu_to_le32(handle); + + /* + * associate memory device with System Physical Address Range + * Structure. + */ + nfit_memdev->spa_index = cpu_to_le16(nvdimm_slot_to_spa_index(slot)); + /* associate memory device with Control Region Structure. */ + nfit_memdev->dcr_index = cpu_to_le16(nvdimm_slot_to_dcr_index(slot)); + + /* The memory region on the device. */ + nfit_memdev->region_len = cpu_to_le64(size); + nfit_memdev->region_dpa = cpu_to_le64(addr); + + /* Only one interleave for PMEM. */ + nfit_memdev->interleave_ways = cpu_to_le16(1); +} + +/* + * ACPI 6.0: 5.2.25.5 NVDIMM Control Region Structure. + */ +static void nvdimm_build_structure_dcr(GArray *structures, DeviceState *dev) +{ + NvdimmNfitControlRegion *nfit_dcr; + int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP, + NULL); + uint32_t sn = nvdimm_slot_to_sn(slot); + + nfit_dcr = acpi_data_push(structures, sizeof(*nfit_dcr)); + + nfit_dcr->type = cpu_to_le16(4 /* NVDIMM Control Region Structure */); + nfit_dcr->length = cpu_to_le16(sizeof(*nfit_dcr)); + nfit_dcr->dcr_index = cpu_to_le16(nvdimm_slot_to_dcr_index(slot)); + + /* vendor: Intel. */ + nfit_dcr->vendor_id = cpu_to_le16(0x8086); + nfit_dcr->device_id = cpu_to_le16(1); + + /* The _DSM method is following Intel's DSM specification. */ + nfit_dcr->revision_id = cpu_to_le16(1 /* Current Revision supported + in ACPI 6.0 is 1. */); + nfit_dcr->serial_number = cpu_to_le32(sn); + nfit_dcr->fic = cpu_to_le16(0x201 /* Format Interface Code. See Chapter + 2: NVDIMM Device Specific Method + (DSM) in DSM Spec Rev1.*/); +} + +static GArray *nvdimm_build_device_structure(GSList *device_list) +{ + GArray *structures = g_array_new(false, true /* clear */, 1); + + for (; device_list; device_list = device_list->next) { + DeviceState *dev = device_list->data; + + /* build System Physical Address Range Structure. */ + nvdimm_build_structure_spa(structures, dev); + + /* + * build Memory Device to System Physical Address Range Mapping + * Structure. + */ + nvdimm_build_structure_memdev(structures, dev); + + /* build NVDIMM Control Region Structure. */ + nvdimm_build_structure_dcr(structures, dev); + } + + return structures; +} + +static void nvdimm_build_nfit(GSList *device_list, GArray *table_offsets, + GArray *table_data, GArray *linker) +{ + GArray *structures = nvdimm_build_device_structure(device_list); + void *header; + + acpi_add_table(table_offsets, table_data); + + /* NFIT header. */ + header = acpi_data_push(table_data, sizeof(NvdimmNfitHeader)); + /* NVDIMM device structures. */ + g_array_append_vals(table_data, structures->data, structures->len); + + build_header(linker, table_data, header, "NFIT", + sizeof(NvdimmNfitHeader) + structures->len, 1, NULL); + g_array_free(structures, true); +} + +void nvdimm_build_acpi(GArray *table_offsets, GArray *table_data, + GArray *linker) +{ + GSList *device_list; + + /* no NVDIMM device is plugged. */ + device_list = nvdimm_get_plugged_device_list(); + if (!device_list) { + return; + } + nvdimm_build_nfit(device_list, table_offsets, table_data, linker); + g_slist_free(device_list); +} diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 27d2caf144..479b11ec4d 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -39,6 +39,7 @@ #include "hw/loader.h" #include "hw/isa/isa.h" #include "hw/acpi/memory_hotplug.h" +#include "hw/mem/nvdimm.h" #include "sysemu/tpm.h" #include "hw/acpi/tpm.h" #include "sysemu/tpm_backend.h" @@ -1683,6 +1684,13 @@ static bool acpi_has_iommu(void) return intel_iommu && !ambiguous; } +static bool acpi_has_nvdimm(void) +{ + PCMachineState *pcms = PC_MACHINE(qdev_get_machine()); + + return pcms->nvdimm; +} + static void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables) { @@ -1767,6 +1775,10 @@ void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables) build_dmar_q35(tables_blob, tables->linker); } + if (acpi_has_nvdimm()) { + nvdimm_build_acpi(table_offsets, tables_blob, tables->linker); + } + /* Add tables supplied by user (if any) */ for (u = acpi_table_first(); u; u = acpi_table_next(u)) { unsigned len = acpi_table_len(u); diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 48aa37a138..459260b4af 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1865,6 +1865,20 @@ static void pc_machine_set_smm(Object *obj, Visitor *v, void *opaque, visit_type_OnOffAuto(v, &pcms->smm, name, errp); } +static bool pc_machine_get_nvdimm(Object *obj, Error **errp) +{ + PCMachineState *pcms = PC_MACHINE(obj); + + return pcms->nvdimm; +} + +static void pc_machine_set_nvdimm(Object *obj, bool value, Error **errp) +{ + PCMachineState *pcms = PC_MACHINE(obj); + + pcms->nvdimm = value; +} + static void pc_machine_initfn(Object *obj) { PCMachineState *pcms = PC_MACHINE(obj); @@ -1899,6 +1913,11 @@ static void pc_machine_initfn(Object *obj) object_property_set_description(obj, PC_MACHINE_VMPORT, "Enable vmport (pc & q35)", &error_abort); + + /* nvdimm is disabled on default. */ + pcms->nvdimm = false; + object_property_add_bool(obj, PC_MACHINE_NVDIMM, pc_machine_get_nvdimm, + pc_machine_set_nvdimm, &error_abort); } static void pc_machine_reset(void) diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index c6a777546a..b0d6283b25 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -55,6 +55,7 @@ struct PCMachineState { uint64_t max_ram_below_4g; OnOffAuto vmport; OnOffAuto smm; + bool nvdimm; /* RAM information (sizes, addresses, configuration): */ ram_addr_t below_4g_mem_size, above_4g_mem_size; @@ -65,6 +66,7 @@ struct PCMachineState { #define PC_MACHINE_MAX_RAM_BELOW_4G "max-ram-below-4g" #define PC_MACHINE_VMPORT "vmport" #define PC_MACHINE_SMM "smm" +#define PC_MACHINE_NVDIMM "nvdimm" /** * PCMachineClass: diff --git a/include/hw/mem/nvdimm.h b/include/hw/mem/nvdimm.h index dbfa8d6bd4..49183c126b 100644 --- a/include/hw/mem/nvdimm.h +++ b/include/hw/mem/nvdimm.h @@ -26,4 +26,7 @@ #include "hw/mem/pc-dimm.h" #define TYPE_NVDIMM "nvdimm" + +void nvdimm_build_acpi(GArray *table_offsets, GArray *table_data, + GArray *linker); #endif diff --git a/qemu-options.hx b/qemu-options.hx index 94ab8806fb..215d00ddd3 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -42,7 +42,8 @@ DEF("machine", HAS_ARG, QEMU_OPTION_machine, \ " igd-passthru=on|off controls IGD GFX passthrough support (default=off)\n" " aes-key-wrap=on|off controls support for AES key wrapping (default=on)\n" " dea-key-wrap=on|off controls support for DEA key wrapping (default=on)\n" - " suppress-vmdesc=on|off disables self-describing migration (default=off)\n", + " suppress-vmdesc=on|off disables self-describing migration (default=off)\n" + " nvdimm=on|off controls NVDIMM support (default=off)\n", QEMU_ARCH_ALL) STEXI @item -machine [type=]@var{name}[,prop=@var{value}[,...]] @@ -81,6 +82,8 @@ execution of AES cryptographic functions. The default is on. Enables or disables DEA key wrapping support on s390-ccw hosts. This feature controls whether DEA wrapping keys will be created to allow execution of DEA cryptographic functions. The default is on. +@item nvdimm=on|off +Enables or disables NVDIMM support. The default is off. @end table ETEXI From 772863954ca360cc67654ab781f20784bc299b95 Mon Sep 17 00:00:00 2001 From: Xiao Guangrong Date: Wed, 2 Dec 2015 15:20:59 +0800 Subject: [PATCH 33/55] nvdimm acpi: build ACPI nvdimm devices NVDIMM devices is defined in ACPI 6.0 9.20 NVDIMM Devices There is a root device under \_SB and specified NVDIMM devices are under the root device. Each NVDIMM device has _ADR which returns its handle used to associate MEMDEV structure in NFIT Currently, we do not support any function on _DSM, that means, NVDIMM label data has not been supported yet Reviewed-by: Stefan Hajnoczi Signed-off-by: Xiao Guangrong Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/nvdimm.c | 106 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/hw/acpi/nvdimm.c b/hw/acpi/nvdimm.c index 98c004d1a5..9534418824 100644 --- a/hw/acpi/nvdimm.c +++ b/hw/acpi/nvdimm.c @@ -367,6 +367,111 @@ static void nvdimm_build_nfit(GSList *device_list, GArray *table_offsets, g_array_free(structures, true); } +#define NVDIMM_COMMON_DSM "NCAL" + +static void nvdimm_build_common_dsm(Aml *dev) +{ + Aml *method, *ifctx, *function; + uint8_t byte_list[1]; + + method = aml_method(NVDIMM_COMMON_DSM, 4, AML_NOTSERIALIZED); + function = aml_arg(2); + + /* + * function 0 is called to inquire what functions are supported by + * OSPM + */ + ifctx = aml_if(aml_equal(function, aml_int(0))); + byte_list[0] = 0 /* No function Supported */; + aml_append(ifctx, aml_return(aml_buffer(1, byte_list))); + aml_append(method, ifctx); + + /* No function is supported yet. */ + byte_list[0] = 1 /* Not Supported */; + aml_append(method, aml_return(aml_buffer(1, byte_list))); + + aml_append(dev, method); +} + +static void nvdimm_build_device_dsm(Aml *dev) +{ + Aml *method; + + method = aml_method("_DSM", 4, AML_NOTSERIALIZED); + aml_append(method, aml_return(aml_call4(NVDIMM_COMMON_DSM, aml_arg(0), + aml_arg(1), aml_arg(2), aml_arg(3)))); + aml_append(dev, method); +} + +static void nvdimm_build_nvdimm_devices(GSList *device_list, Aml *root_dev) +{ + for (; device_list; device_list = device_list->next) { + DeviceState *dev = device_list->data; + int slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP, + NULL); + uint32_t handle = nvdimm_slot_to_handle(slot); + Aml *nvdimm_dev; + + nvdimm_dev = aml_device("NV%02X", slot); + + /* + * ACPI 6.0: 9.20 NVDIMM Devices: + * + * _ADR object that is used to supply OSPM with unique address + * of the NVDIMM device. This is done by returning the NFIT Device + * handle that is used to identify the associated entries in ACPI + * table NFIT or _FIT. + */ + aml_append(nvdimm_dev, aml_name_decl("_ADR", aml_int(handle))); + + nvdimm_build_device_dsm(nvdimm_dev); + aml_append(root_dev, nvdimm_dev); + } +} + +static void nvdimm_build_ssdt(GSList *device_list, GArray *table_offsets, + GArray *table_data, GArray *linker) +{ + Aml *ssdt, *sb_scope, *dev; + + acpi_add_table(table_offsets, table_data); + + ssdt = init_aml_allocator(); + acpi_data_push(ssdt->buf, sizeof(AcpiTableHeader)); + + sb_scope = aml_scope("\\_SB"); + + dev = aml_device("NVDR"); + + /* + * ACPI 6.0: 9.20 NVDIMM Devices: + * + * The ACPI Name Space device uses _HID of ACPI0012 to identify the root + * NVDIMM interface device. Platform firmware is required to contain one + * such device in _SB scope if NVDIMMs support is exposed by platform to + * OSPM. + * For each NVDIMM present or intended to be supported by platform, + * platform firmware also exposes an ACPI Namespace Device under the + * root device. + */ + aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0012"))); + + nvdimm_build_common_dsm(dev); + nvdimm_build_device_dsm(dev); + + nvdimm_build_nvdimm_devices(device_list, dev); + + aml_append(sb_scope, dev); + + aml_append(ssdt, sb_scope); + /* copy AML table into ACPI tables blob and patch header there */ + g_array_append_vals(table_data, ssdt->buf->data, ssdt->buf->len); + build_header(linker, table_data, + (void *)(table_data->data + table_data->len - ssdt->buf->len), + "SSDT", ssdt->buf->len, 1, "NVDIMM"); + free_aml_allocator(); +} + void nvdimm_build_acpi(GArray *table_offsets, GArray *table_data, GArray *linker) { @@ -378,5 +483,6 @@ void nvdimm_build_acpi(GArray *table_offsets, GArray *table_data, return; } nvdimm_build_nfit(device_list, table_offsets, table_data, linker); + nvdimm_build_ssdt(device_list, table_offsets, table_data, linker); g_slist_free(device_list); } From 70d1fb9c0b9e918f8f910cbfe07d983f4ef613d9 Mon Sep 17 00:00:00 2001 From: Xiao Guangrong Date: Wed, 2 Dec 2015 15:21:00 +0800 Subject: [PATCH 34/55] nvdimm: add maintain info Add NVDIMM maintainer Reviewed-by: Stefan Hajnoczi Signed-off-by: Xiao Guangrong Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 46bba68990..5a62ecda17 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -941,6 +941,13 @@ M: Jiri Pirko S: Maintained F: hw/net/rocker/ +NVDIMM +M: Xiao Guangrong +S: Maintained +F: hw/acpi/nvdimm.c +F: hw/mem/nvdimm.c +F: include/hw/mem/nvdimm.h + Subsystems ---------- Audio From 95cb06619092130eb41e68b2541e04aedb48b046 Mon Sep 17 00:00:00 2001 From: Xiao Guangrong Date: Thu, 10 Dec 2015 00:40:56 +0100 Subject: [PATCH 35/55] acpi: add aml_derefof Implement DeRefOf term which is used by NVDIMM _DSM method in later patch Reviewed-by: Igor Mammedov Signed-off-by: Xiao Guangrong Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/aml-build.c | 8 ++++++++ include/hw/acpi/aml-build.h | 1 + 2 files changed, 9 insertions(+) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index 363e83b851..501a98847e 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -1240,6 +1240,14 @@ Aml *aml_unicode(const char *str) return var; } +/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefDerefOf */ +Aml *aml_derefof(Aml *arg) +{ + Aml *var = aml_opcode(0x83 /* DerefOfOp */); + aml_append(var, arg); + return var; +} + void build_header(GArray *linker, GArray *table_data, AcpiTableHeader *h, const char *sig, int len, uint8_t rev, diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h index 9fa61a3f6e..89fb15e8e4 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -307,6 +307,7 @@ Aml *aml_create_dword_field(Aml *srcbuf, Aml *index, const char *name); Aml *aml_varpackage(uint32_t num_elements); Aml *aml_touuid(const char *uuid); Aml *aml_unicode(const char *str); +Aml *aml_derefof(Aml *arg); void build_header(GArray *linker, GArray *table_data, From 52483d147bfe49e98aa76230f5f90f9f246f44ed Mon Sep 17 00:00:00 2001 From: Xiao Guangrong Date: Thu, 10 Dec 2015 00:40:57 +0100 Subject: [PATCH 36/55] acpi: add aml_sizeof Implement SizeOf term which is used by NVDIMM _DSM method in later patch Reviewed-by: Igor Mammedov Signed-off-by: Xiao Guangrong Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/aml-build.c | 8 ++++++++ include/hw/acpi/aml-build.h | 1 + 2 files changed, 9 insertions(+) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index 501a98847e..867afbe688 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -1248,6 +1248,14 @@ Aml *aml_derefof(Aml *arg) return var; } +/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefSizeOf */ +Aml *aml_sizeof(Aml *arg) +{ + Aml *var = aml_opcode(0x87 /* SizeOfOp */); + aml_append(var, arg); + return var; +} + void build_header(GArray *linker, GArray *table_data, AcpiTableHeader *h, const char *sig, int len, uint8_t rev, diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h index 89fb15e8e4..b8a0ad2c46 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -308,6 +308,7 @@ Aml *aml_varpackage(uint32_t num_elements); Aml *aml_touuid(const char *uuid); Aml *aml_unicode(const char *str); Aml *aml_derefof(Aml *arg); +Aml *aml_sizeof(Aml *arg); void build_header(GArray *linker, GArray *table_data, From 2d3f667dc63f3bf96c5a8b47785f56c86f6dfd90 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 10 Dec 2015 00:40:58 +0100 Subject: [PATCH 37/55] acpi: add aml_lgreater_equal() Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Marcel Apfelbaum Reviewed-by: Shannon Zhao --- hw/acpi/aml-build.c | 11 +++++++++++ include/hw/acpi/aml-build.h | 1 + 2 files changed, 12 insertions(+) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index 867afbe688..60246eb9c2 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -764,6 +764,17 @@ Aml *aml_equal(Aml *arg1, Aml *arg2) return var; } +/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefLGreaterEqual */ +Aml *aml_lgreater_equal(Aml *arg1, Aml *arg2) +{ + /* LGreaterEqualOp := LNotOp LLessOp */ + Aml *var = aml_opcode(0x92 /* LNotOp */); + build_append_byte(var->buf, 0x95 /* LLessOp */); + aml_append(var, arg1); + aml_append(var, arg2); + return var; +} + /* ACPI 1.0b: 16.2.5.3 Type 1 Opcodes Encoding: DefIfElse */ Aml *aml_if(Aml *predicate) { diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h index b8a0ad2c46..fba8f2537d 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -262,6 +262,7 @@ Aml *aml_local(int num); Aml *aml_string(const char *name_format, ...) GCC_FMT_ATTR(1, 2); Aml *aml_lnot(Aml *arg); Aml *aml_equal(Aml *arg1, Aml *arg2); +Aml *aml_lgreater_equal(Aml *arg1, Aml *arg2); Aml *aml_processor(uint8_t proc_id, uint32_t pblk_addr, uint8_t pblk_len, const char *name_format, ...) GCC_FMT_ATTR(4, 5); Aml *aml_eisaid(const char *str); From 6e1db3f263c33a6f03584ef6463855ede4d6ce73 Mon Sep 17 00:00:00 2001 From: Xiao Guangrong Date: Thu, 10 Dec 2015 00:40:59 +0100 Subject: [PATCH 38/55] acpi: add aml_mutex(), aml_acquire(), aml_release() Signed-off-by: Xiao Guangrong Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/aml-build.c | 33 +++++++++++++++++++++++++++++++++ include/hw/acpi/aml-build.h | 3 +++ 2 files changed, 36 insertions(+) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index 60246eb9c2..c4120ef609 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -1267,6 +1267,39 @@ Aml *aml_sizeof(Aml *arg) return var; } +/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefMutex */ +Aml *aml_mutex(const char *name, uint8_t sync_level) +{ + Aml *var = aml_alloc(); + build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */ + build_append_byte(var->buf, 0x01); /* MutexOp */ + build_append_namestring(var->buf, "%s", name); + assert(!(sync_level & 0xF0)); + build_append_byte(var->buf, sync_level); + return var; +} + +/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefAcquire */ +Aml *aml_acquire(Aml *mutex, uint16_t timeout) +{ + Aml *var = aml_alloc(); + build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */ + build_append_byte(var->buf, 0x23); /* AcquireOp */ + aml_append(var, mutex); + build_append_int_noprefix(var->buf, timeout, sizeof(timeout)); + return var; +} + +/* ACPI 1.0b: 16.2.5.3 Type 1 Opcodes Encoding: DefRelease */ +Aml *aml_release(Aml *mutex) +{ + Aml *var = aml_alloc(); + build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */ + build_append_byte(var->buf, 0x27); /* ReleaseOp */ + aml_append(var, mutex); + return var; +} + void build_header(GArray *linker, GArray *table_data, AcpiTableHeader *h, const char *sig, int len, uint8_t rev, diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h index fba8f2537d..4831c5536f 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -304,6 +304,9 @@ Aml *aml_package(uint8_t num_elements); Aml *aml_buffer(int buffer_size, uint8_t *byte_list); Aml *aml_resource_template(void); Aml *aml_field(const char *name, AmlAccessType type, AmlUpdateRule rule); +Aml *aml_mutex(const char *name, uint8_t sync_level); +Aml *aml_acquire(Aml *mutex, uint16_t timeout); +Aml *aml_release(Aml *mutex); Aml *aml_create_dword_field(Aml *srcbuf, Aml *index, const char *name); Aml *aml_varpackage(uint32_t num_elements); Aml *aml_touuid(const char *uuid); From 7e192a383b941d1f422117dd0915bbeff5fee5bd Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 10 Dec 2015 00:41:00 +0100 Subject: [PATCH 39/55] acpi: add aml_create_qword_field() Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Marcel Apfelbaum --- hw/acpi/aml-build.c | 21 +++++++++++++++++---- include/hw/acpi/aml-build.h | 1 + 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index c4120ef609..95f9af9f94 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -910,17 +910,30 @@ Aml *aml_field(const char *name, AmlAccessType type, AmlUpdateRule rule) return var; } -/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefCreateDWordField */ -Aml *aml_create_dword_field(Aml *srcbuf, Aml *index, const char *name) +static +Aml *create_field_common(int opcode, Aml *srcbuf, Aml *index, const char *name) { - Aml *var = aml_alloc(); - build_append_byte(var->buf, 0x8A); /* CreateDWordFieldOp */ + Aml *var = aml_opcode(opcode); aml_append(var, srcbuf); aml_append(var, index); build_append_namestring(var->buf, "%s", name); return var; } +/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefCreateDWordField */ +Aml *aml_create_dword_field(Aml *srcbuf, Aml *index, const char *name) +{ + return create_field_common(0x8A /* CreateDWordFieldOp */, + srcbuf, index, name); +} + +/* ACPI 2.0a: 17.2.4.2 Named Objects Encoding: DefCreateQWordField */ +Aml *aml_create_qword_field(Aml *srcbuf, Aml *index, const char *name) +{ + return create_field_common(0x8F /* CreateQWordFieldOp */, + srcbuf, index, name); +} + /* ACPI 1.0b: 16.2.3 Data Objects Encoding: String */ Aml *aml_string(const char *name_format, ...) { diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h index 4831c5536f..4acc4d01a4 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -308,6 +308,7 @@ Aml *aml_mutex(const char *name, uint8_t sync_level); Aml *aml_acquire(Aml *mutex, uint16_t timeout); Aml *aml_release(Aml *mutex); Aml *aml_create_dword_field(Aml *srcbuf, Aml *index, const char *name); +Aml *aml_create_qword_field(Aml *srcbuf, Aml *index, const char *name); Aml *aml_varpackage(uint32_t num_elements); Aml *aml_touuid(const char *uuid); Aml *aml_unicode(const char *str); From 439e2a6e10ed7f5da819bf7dcaa54b8cfdbeab0d Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 10 Dec 2015 00:41:01 +0100 Subject: [PATCH 40/55] acpi: aml: add helper for Opcode Arg2 Arg2 [Dst] AML pattern Currently AML API doesn't compose terms in form of following pattern: Opcode Arg2 Arg2 [Dst] but ASL used in piix4/q35 DSDT ACPI tables uses that form, so for clean conversion of it, AML API should be able to handle an optional 'Dst' argumet used there. Since above pattern is used by arithmetic/bit ops, introduce helper that they could reuse. It reduces code duplication in existing 5 aml_foo() functions and also will prevent more duplication when exiting functions are extended to support optional 'Dst' argument. Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Marcel Apfelbaum Reviewed-by: Shannon Zhao --- hw/acpi/aml-build.c | 63 ++++++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index 95f9af9f94..c661d58718 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -436,44 +436,55 @@ Aml *aml_store(Aml *val, Aml *target) return var; } +/** + * build_opcode_2arg_dst: + * @op: 1-byte opcode + * @arg1: 1st operand + * @arg2: 2nd operand + * @dst: optional target to store to, set to NULL if it's not required + * + * An internal helper to compose AML terms that have + * "Op Operand Operand Target" + * pattern. + * + * Returns: The newly allocated and composed according to patter Aml object. + */ +static Aml * +build_opcode_2arg_dst(uint8_t op, Aml *arg1, Aml *arg2, Aml *dst) +{ + Aml *var = aml_opcode(op); + aml_append(var, arg1); + aml_append(var, arg2); + if (dst) { + aml_append(var, dst); + } else { + build_append_byte(var->buf, 0x00 /* NullNameOp */); + } + return var; +} + /* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefAnd */ Aml *aml_and(Aml *arg1, Aml *arg2) { - Aml *var = aml_opcode(0x7B /* AndOp */); - aml_append(var, arg1); - aml_append(var, arg2); - build_append_byte(var->buf, 0x00 /* NullNameOp */); - return var; + return build_opcode_2arg_dst(0x7B /* AndOp */, arg1, arg2, NULL); } /* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefOr */ Aml *aml_or(Aml *arg1, Aml *arg2) { - Aml *var = aml_opcode(0x7D /* OrOp */); - aml_append(var, arg1); - aml_append(var, arg2); - build_append_byte(var->buf, 0x00 /* NullNameOp */); - return var; + return build_opcode_2arg_dst(0x7D /* OrOp */, arg1, arg2, NULL); } /* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefShiftLeft */ Aml *aml_shiftleft(Aml *arg1, Aml *count) { - Aml *var = aml_opcode(0x79 /* ShiftLeftOp */); - aml_append(var, arg1); - aml_append(var, count); - build_append_byte(var->buf, 0x00); /* NullNameOp */ - return var; + return build_opcode_2arg_dst(0x79 /* ShiftLeftOp */, arg1, count, NULL); } /* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefShiftRight */ Aml *aml_shiftright(Aml *arg1, Aml *count) { - Aml *var = aml_opcode(0x7A /* ShiftRightOp */); - aml_append(var, arg1); - aml_append(var, count); - build_append_byte(var->buf, 0x00); /* NullNameOp */ - return var; + return build_opcode_2arg_dst(0x7A /* ShiftRightOp */, arg1, count, NULL); } /* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefLLess */ @@ -488,11 +499,7 @@ Aml *aml_lless(Aml *arg1, Aml *arg2) /* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefAdd */ Aml *aml_add(Aml *arg1, Aml *arg2) { - Aml *var = aml_opcode(0x72 /* AddOp */); - aml_append(var, arg1); - aml_append(var, arg2); - build_append_byte(var->buf, 0x00 /* NullNameOp */); - return var; + return build_opcode_2arg_dst(0x72 /* AddOp */, arg1, arg2, NULL); } /* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefIncrement */ @@ -506,11 +513,7 @@ Aml *aml_increment(Aml *arg) /* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefIndex */ Aml *aml_index(Aml *arg1, Aml *idx) { - Aml *var = aml_opcode(0x88 /* IndexOp */); - aml_append(var, arg1); - aml_append(var, idx); - build_append_byte(var->buf, 0x00 /* NullNameOp */); - return var; + return build_opcode_2arg_dst(0x88 /* IndexOp */, arg1, idx, NULL); } /* ACPI 1.0b: 16.2.5.3 Type 1 Opcodes Encoding: DefNotify */ From 20ca52088448252c392cde99a06bb9bf020413b4 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 10 Dec 2015 00:41:02 +0100 Subject: [PATCH 41/55] acpi: extend aml_add() to accept target argument it allows to express following ASL expression: Add(arg1, arg2, result) usecases that do not need to store result should pass NULL as 3rd arg that would express Add(arg1, arg2,) construct. Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Marcel Apfelbaum --- hw/acpi/aml-build.c | 4 ++-- hw/i386/acpi-build.c | 2 +- include/hw/acpi/aml-build.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index c661d58718..15bb5bdafe 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -497,9 +497,9 @@ Aml *aml_lless(Aml *arg1, Aml *arg2) } /* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefAdd */ -Aml *aml_add(Aml *arg1, Aml *arg2) +Aml *aml_add(Aml *arg1, Aml *arg2, Aml *dst) { - return build_opcode_2arg_dst(0x72 /* AddOp */, arg1, arg2, NULL); + return build_opcode_2arg_dst(0x72 /* AddOp */, arg1, arg2, dst); } /* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefIncrement */ diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 479b11ec4d..16fb168611 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -670,7 +670,7 @@ static Aml *build_prt(void) aml_store(aml_shiftright(pin, aml_int(2)), slot)); /* lnk_idx = (slot + pin) & 3 */ aml_append(while_ctx, - aml_store(aml_and(aml_add(pin, slot), aml_int(3)), lnk_idx)); + aml_store(aml_and(aml_add(pin, slot, NULL), aml_int(3)), lnk_idx)); /* route[2] = "LNK[D|A|B|C]", selection based on pin % 3 */ aml_append(while_ctx, initialize_route(route, "LNKD", lnk_idx, 0)); diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h index 4acc4d01a4..0769b35d91 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -230,7 +230,7 @@ Aml *aml_or(Aml *arg1, Aml *arg2); Aml *aml_shiftleft(Aml *arg1, Aml *count); Aml *aml_shiftright(Aml *arg1, Aml *count); Aml *aml_lless(Aml *arg1, Aml *arg2); -Aml *aml_add(Aml *arg1, Aml *arg2); +Aml *aml_add(Aml *arg1, Aml *arg2, Aml *dst); Aml *aml_increment(Aml *arg); Aml *aml_index(Aml *arg1, Aml *idx); Aml *aml_notify(Aml *arg1, Aml *arg2); From 7059eb4262d8a5575e5a81cdfac101d84a482c28 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 10 Dec 2015 00:41:03 +0100 Subject: [PATCH 42/55] acpi: add aml_decrement() and aml_subtract() Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Marcel Apfelbaum --- hw/acpi/aml-build.c | 14 ++++++++++++++ include/hw/acpi/aml-build.h | 2 ++ 2 files changed, 16 insertions(+) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index 15bb5bdafe..74316d111a 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -502,6 +502,12 @@ Aml *aml_add(Aml *arg1, Aml *arg2, Aml *dst) return build_opcode_2arg_dst(0x72 /* AddOp */, arg1, arg2, dst); } +/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefSubtract */ +Aml *aml_subtract(Aml *arg1, Aml *arg2, Aml *dst) +{ + return build_opcode_2arg_dst(0x74 /* SubtractOp */, arg1, arg2, dst); +} + /* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefIncrement */ Aml *aml_increment(Aml *arg) { @@ -510,6 +516,14 @@ Aml *aml_increment(Aml *arg) return var; } +/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefDecrement */ +Aml *aml_decrement(Aml *arg) +{ + Aml *var = aml_opcode(0x76 /* DecrementOp */); + aml_append(var, arg); + return var; +} + /* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefIndex */ Aml *aml_index(Aml *arg1, Aml *idx) { diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h index 0769b35d91..40a95d8f06 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -231,7 +231,9 @@ Aml *aml_shiftleft(Aml *arg1, Aml *count); Aml *aml_shiftright(Aml *arg1, Aml *count); Aml *aml_lless(Aml *arg1, Aml *arg2); Aml *aml_add(Aml *arg1, Aml *arg2, Aml *dst); +Aml *aml_subtract(Aml *arg1, Aml *arg2, Aml *dst); Aml *aml_increment(Aml *arg); +Aml *aml_decrement(Aml *arg); Aml *aml_index(Aml *arg1, Aml *idx); Aml *aml_notify(Aml *arg1, Aml *arg2); Aml *aml_call1(const char *method, Aml *arg1); From 7b38ba9cb6fd47d89ab2db5b92ea6e4e6cd046cd Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 10 Dec 2015 00:41:04 +0100 Subject: [PATCH 43/55] acpi: add aml_call0() helper it will help to call a method with 0 arguments Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Marcel Apfelbaum --- hw/acpi/aml-build.c | 8 ++++++++ include/hw/acpi/aml-build.h | 1 + 2 files changed, 9 insertions(+) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index 74316d111a..b4fff5f0a8 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -539,6 +539,14 @@ Aml *aml_notify(Aml *arg1, Aml *arg2) return var; } +/* helper to call method with 1 argument */ +Aml *aml_call0(const char *method) +{ + Aml *var = aml_alloc(); + build_append_namestring(var->buf, "%s", method); + return var; +} + /* helper to call method with 1 argument */ Aml *aml_call1(const char *method, Aml *arg1) { diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h index 40a95d8f06..81326bd9b7 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -236,6 +236,7 @@ Aml *aml_increment(Aml *arg); Aml *aml_decrement(Aml *arg); Aml *aml_index(Aml *arg1, Aml *idx); Aml *aml_notify(Aml *arg1, Aml *arg2); +Aml *aml_call0(const char *method); Aml *aml_call1(const char *method, Aml *arg1); Aml *aml_call2(const char *method, Aml *arg1, Aml *arg2); Aml *aml_call3(const char *method, Aml *arg1, Aml *arg2, Aml *arg3); From f411199de79bd3015975a31068bbd9d94adcfb38 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 10 Dec 2015 00:41:05 +0100 Subject: [PATCH 44/55] acpi: add aml_to_integer() Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Marcel Apfelbaum --- hw/acpi/aml-build.c | 9 +++++++++ include/hw/acpi/aml-build.h | 1 + 2 files changed, 10 insertions(+) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index b4fff5f0a8..f58eb0123a 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -427,6 +427,15 @@ Aml *aml_arg(int pos) return var; } +/* ACPI 2.0a: 17.2.4.4 Type 2 Opcodes Encoding: DefToInteger */ +Aml *aml_to_integer(Aml *arg) +{ + Aml *var = aml_opcode(0x99 /* ToIntegerOp */); + aml_append(var, arg); + build_append_byte(var->buf, 0x00 /* NullNameOp */); + return var; +} + /* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefStore */ Aml *aml_store(Aml *val, Aml *target) { diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h index 81326bd9b7..c3d3fd5c8f 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -224,6 +224,7 @@ Aml *aml_name_decl(const char *name, Aml *val); Aml *aml_return(Aml *val); Aml *aml_int(const uint64_t val); Aml *aml_arg(int pos); +Aml *aml_to_integer(Aml *arg); Aml *aml_store(Aml *val, Aml *target); Aml *aml_and(Aml *arg1, Aml *arg2); Aml *aml_or(Aml *arg1, Aml *arg2); From c360639aee914304c038399a11ab506dd72addb8 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 10 Dec 2015 00:41:06 +0100 Subject: [PATCH 45/55] acpi: extend aml_shiftright() to accept target argument it allows to express ShiftRight(A,B,C) syntax Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Marcel Apfelbaum --- hw/acpi/aml-build.c | 4 ++-- hw/i386/acpi-build.c | 2 +- include/hw/acpi/aml-build.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index f58eb0123a..9337b87528 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -491,9 +491,9 @@ Aml *aml_shiftleft(Aml *arg1, Aml *count) } /* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefShiftRight */ -Aml *aml_shiftright(Aml *arg1, Aml *count) +Aml *aml_shiftright(Aml *arg1, Aml *count, Aml *dst) { - return build_opcode_2arg_dst(0x7A /* ShiftRightOp */, arg1, count, NULL); + return build_opcode_2arg_dst(0x7A /* ShiftRightOp */, arg1, count, dst); } /* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefLLess */ diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 16fb168611..35ea92d022 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -667,7 +667,7 @@ static Aml *build_prt(void) /* slot = pin >> 2 */ aml_append(while_ctx, - aml_store(aml_shiftright(pin, aml_int(2)), slot)); + aml_store(aml_shiftright(pin, aml_int(2), NULL), slot)); /* lnk_idx = (slot + pin) & 3 */ aml_append(while_ctx, aml_store(aml_and(aml_add(pin, slot, NULL), aml_int(3)), lnk_idx)); diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h index c3d3fd5c8f..365e388fcb 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -229,7 +229,7 @@ Aml *aml_store(Aml *val, Aml *target); Aml *aml_and(Aml *arg1, Aml *arg2); Aml *aml_or(Aml *arg1, Aml *arg2); Aml *aml_shiftleft(Aml *arg1, Aml *count); -Aml *aml_shiftright(Aml *arg1, Aml *count); +Aml *aml_shiftright(Aml *arg1, Aml *count, Aml *dst); Aml *aml_lless(Aml *arg1, Aml *arg2); Aml *aml_add(Aml *arg1, Aml *arg2, Aml *dst); Aml *aml_subtract(Aml *arg1, Aml *arg2, Aml *dst); From 67a5c0faa68361949df80fc2830fe1ac228ed911 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 10 Dec 2015 00:41:07 +0100 Subject: [PATCH 46/55] acpi: add aml_alias() Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Marcel Apfelbaum --- hw/acpi/aml-build.c | 9 +++++++++ include/hw/acpi/aml-build.h | 1 + 2 files changed, 10 insertions(+) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index 9337b87528..210588a6df 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -1347,6 +1347,15 @@ Aml *aml_release(Aml *mutex) return var; } +/* ACPI 1.0b: 16.2.5.1 Name Space Modifier Objects Encoding: DefAlias */ +Aml *aml_alias(const char *source_object, const char *alias_object) +{ + Aml *var = aml_opcode(0x06 /* AliasOp */); + aml_append(var, aml_name("%s", source_object)); + aml_append(var, aml_name("%s", alias_object)); + return var; +} + void build_header(GArray *linker, GArray *table_data, AcpiTableHeader *h, const char *sig, int len, uint8_t rev, diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h index 365e388fcb..d9ec6d6a46 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -311,6 +311,7 @@ Aml *aml_field(const char *name, AmlAccessType type, AmlUpdateRule rule); Aml *aml_mutex(const char *name, uint8_t sync_level); Aml *aml_acquire(Aml *mutex, uint16_t timeout); Aml *aml_release(Aml *mutex); +Aml *aml_alias(const char *source_object, const char *alias_object); Aml *aml_create_dword_field(Aml *srcbuf, Aml *index, const char *name); Aml *aml_create_qword_field(Aml *srcbuf, Aml *index, const char *name); Aml *aml_varpackage(uint32_t num_elements); From 0073518dd7232fb4da6331ea298589383d2d16cb Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 10 Dec 2015 00:41:08 +0100 Subject: [PATCH 47/55] acpi: add aml_sleep() Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Marcel Apfelbaum --- hw/acpi/aml-build.c | 10 ++++++++++ include/hw/acpi/aml-build.h | 1 + 2 files changed, 11 insertions(+) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index 210588a6df..1d49c19798 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -1228,6 +1228,16 @@ Aml *aml_qword_memory(AmlDecode dec, AmlMinFixed min_fixed, addr_trans, len, flags); } +/* ACPI 1.0b: 16.2.5.3 Type 1 Opcodes Encoding: DefSleep */ +Aml *aml_sleep(uint64_t msec) +{ + Aml *var = aml_alloc(); + build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */ + build_append_byte(var->buf, 0x22); /* SleepOp */ + aml_append(var, aml_int(msec)); + return var; +} + static uint8_t Hex2Byte(const char *src) { int hi, lo; diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h index d9ec6d6a46..3525fe9a17 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -296,6 +296,7 @@ Aml *aml_qword_memory(AmlDecode dec, AmlMinFixed min_fixed, uint64_t addr_gran, uint64_t addr_min, uint64_t addr_max, uint64_t addr_trans, uint64_t len); +Aml *aml_sleep(uint64_t msec); /* Block AML object primitives */ Aml *aml_scope(const char *name_format, ...) GCC_FMT_ATTR(1, 2); From df241999b606079f01a6f25c54552f8d04058639 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 10 Dec 2015 00:41:10 +0100 Subject: [PATCH 48/55] acpi: add aml_lor() Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Marcel Apfelbaum --- hw/acpi/aml-build.c | 9 +++++++++ include/hw/acpi/aml-build.h | 1 + 2 files changed, 10 insertions(+) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index 1d49c19798..6234080fe9 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -484,6 +484,15 @@ Aml *aml_or(Aml *arg1, Aml *arg2) return build_opcode_2arg_dst(0x7D /* OrOp */, arg1, arg2, NULL); } +/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefLOr */ +Aml *aml_lor(Aml *arg1, Aml *arg2) +{ + Aml *var = aml_opcode(0x91 /* LOrOp */); + aml_append(var, arg1); + aml_append(var, arg2); + return var; +} + /* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefShiftLeft */ Aml *aml_shiftleft(Aml *arg1, Aml *count) { diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h index 3525fe9a17..67ab45fb3a 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -228,6 +228,7 @@ Aml *aml_to_integer(Aml *arg); Aml *aml_store(Aml *val, Aml *target); Aml *aml_and(Aml *arg1, Aml *arg2); Aml *aml_or(Aml *arg1, Aml *arg2); +Aml *aml_lor(Aml *arg1, Aml *arg2); Aml *aml_shiftleft(Aml *arg1, Aml *count); Aml *aml_shiftright(Aml *arg1, Aml *count, Aml *dst); Aml *aml_lless(Aml *arg1, Aml *arg2); From dabad78b0d2641a66d8192bd9df2930e4fec4ec7 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 10 Dec 2015 00:41:11 +0100 Subject: [PATCH 49/55] acpi: add aml_lgreater() Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Marcel Apfelbaum --- hw/acpi/aml-build.c | 9 +++++++++ include/hw/acpi/aml-build.h | 1 + 2 files changed, 10 insertions(+) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index 6234080fe9..fd512a5996 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -807,6 +807,15 @@ Aml *aml_equal(Aml *arg1, Aml *arg2) return var; } +/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefLGreater */ +Aml *aml_lgreater(Aml *arg1, Aml *arg2) +{ + Aml *var = aml_opcode(0x94 /* LGreaterOp */); + aml_append(var, arg1); + aml_append(var, arg2); + return var; +} + /* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefLGreaterEqual */ Aml *aml_lgreater_equal(Aml *arg1, Aml *arg2) { diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h index 67ab45fb3a..e90137121e 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -267,6 +267,7 @@ Aml *aml_local(int num); Aml *aml_string(const char *name_format, ...) GCC_FMT_ATTR(1, 2); Aml *aml_lnot(Aml *arg); Aml *aml_equal(Aml *arg1, Aml *arg2); +Aml *aml_lgreater(Aml *arg1, Aml *arg2); Aml *aml_lgreater_equal(Aml *arg1, Aml *arg2); Aml *aml_processor(uint8_t proc_id, uint32_t pblk_addr, uint8_t pblk_len, const char *name_format, ...) GCC_FMT_ATTR(4, 5); From 36de884a133f8cf1a18d55c1d76d783db9844abe Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 10 Dec 2015 00:41:12 +0100 Subject: [PATCH 50/55] acpi: extend aml_field() to support LockRule Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/aml-build.c | 5 ++++- hw/i386/acpi-build.c | 10 +++++----- include/hw/acpi/aml-build.h | 8 +++++++- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index fd512a5996..5dcaf3239e 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -952,11 +952,14 @@ Aml *aml_reserved_field(unsigned length) } /* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefField */ -Aml *aml_field(const char *name, AmlAccessType type, AmlUpdateRule rule) +Aml *aml_field(const char *name, AmlAccessType type, AmlLockRule lock, + AmlUpdateRule rule) { Aml *var = aml_bundle(0x81 /* FieldOp */, AML_EXT_PACKAGE); uint8_t flags = rule << 5 | type; + flags |= lock << 4; /* LockRule at 4 bit offset */ + build_append_namestring(var->buf, "%s", name); build_append_byte(var->buf, flags); return var; diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 35ea92d022..4e9855ad00 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -1130,7 +1130,7 @@ build_ssdt(GArray *table_data, GArray *linker, aml_append(dev, aml_operation_region("PEOR", AML_SYSTEM_IO, misc->pvpanic_port, 1)); - field = aml_field("PEOR", AML_BYTE_ACC, AML_PRESERVE); + field = aml_field("PEOR", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE); aml_append(field, aml_named_field("PEPT", 8)); aml_append(dev, field); @@ -1170,7 +1170,7 @@ build_ssdt(GArray *table_data, GArray *linker, /* declare CPU hotplug MMIO region and PRS field to access it */ aml_append(sb_scope, aml_operation_region( "PRST", AML_SYSTEM_IO, pm->cpu_hp_io_base, pm->cpu_hp_io_len)); - field = aml_field("PRST", AML_BYTE_ACC, AML_PRESERVE); + field = aml_field("PRST", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE); aml_append(field, aml_named_field("PRS", 256)); aml_append(sb_scope, field); @@ -1245,7 +1245,7 @@ build_ssdt(GArray *table_data, GArray *linker, ); field = aml_field(stringify(MEMORY_HOTPLUG_IO_REGION), AML_DWORD_ACC, - AML_PRESERVE); + AML_NOLOCK, AML_PRESERVE); aml_append(field, /* read only */ aml_named_field(stringify(MEMORY_SLOT_ADDR_LOW), 32)); aml_append(field, /* read only */ @@ -1259,7 +1259,7 @@ build_ssdt(GArray *table_data, GArray *linker, aml_append(scope, field); field = aml_field(stringify(MEMORY_HOTPLUG_IO_REGION), AML_BYTE_ACC, - AML_WRITE_AS_ZEROS); + AML_NOLOCK, AML_WRITE_AS_ZEROS); aml_append(field, aml_reserved_field(160 /* bits, Offset(20) */)); aml_append(field, /* 1 if enabled, read only */ aml_named_field(stringify(MEMORY_SLOT_ENABLED), 1)); @@ -1275,7 +1275,7 @@ build_ssdt(GArray *table_data, GArray *linker, aml_append(scope, field); field = aml_field(stringify(MEMORY_HOTPLUG_IO_REGION), AML_DWORD_ACC, - AML_PRESERVE); + AML_NOLOCK, AML_PRESERVE); aml_append(field, /* DIMM selector, write only */ aml_named_field(stringify(MEMORY_SLOT_SLECTOR), 32)); aml_append(field, /* _OST event code, write only */ diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h index e90137121e..289b4eb0f8 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -48,6 +48,11 @@ typedef enum { AML_BUFFER_ACC = 5, } AmlAccessType; +typedef enum { + AML_NOLOCK = 0, + AML_LOCK = 1, +} AmlLockRule; + typedef enum { AML_PRESERVE = 0, AML_WRITE_AS_ONES = 1, @@ -310,7 +315,8 @@ Aml *aml_while(Aml *predicate); Aml *aml_package(uint8_t num_elements); Aml *aml_buffer(int buffer_size, uint8_t *byte_list); Aml *aml_resource_template(void); -Aml *aml_field(const char *name, AmlAccessType type, AmlUpdateRule rule); +Aml *aml_field(const char *name, AmlAccessType type, AmlLockRule lock, + AmlUpdateRule rule); Aml *aml_mutex(const char *name, uint8_t sync_level); Aml *aml_acquire(Aml *mutex, uint16_t timeout); Aml *aml_release(Aml *mutex); From 6d5ea945ceeda364c18a28f7ef7bcbe3c7c94f67 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 10 Dec 2015 00:41:13 +0100 Subject: [PATCH 51/55] acpi: add aml_to_hexstring() Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Marcel Apfelbaum --- hw/acpi/aml-build.c | 13 +++++++++++++ include/hw/acpi/aml-build.h | 1 + 2 files changed, 14 insertions(+) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index 5dcaf3239e..40ea1284b7 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -436,6 +436,19 @@ Aml *aml_to_integer(Aml *arg) return var; } +/* ACPI 2.0a: 17.2.4.4 Type 2 Opcodes Encoding: DefToHexString */ +Aml *aml_to_hexstring(Aml *src, Aml *dst) +{ + Aml *var = aml_opcode(0x98 /* ToHexStringOp */); + aml_append(var, src); + if (dst) { + aml_append(var, dst); + } else { + build_append_byte(var->buf, 0x00 /* NullNameOp */); + } + return var; +} + /* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefStore */ Aml *aml_store(Aml *val, Aml *target) { diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h index 289b4eb0f8..7fafda8c24 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -230,6 +230,7 @@ Aml *aml_return(Aml *val); Aml *aml_int(const uint64_t val); Aml *aml_arg(int pos); Aml *aml_to_integer(Aml *arg); +Aml *aml_to_hexstring(Aml *src, Aml *dst); Aml *aml_store(Aml *val, Aml *target); Aml *aml_and(Aml *arg1, Aml *arg2); Aml *aml_or(Aml *arg1, Aml *arg2); From 25c1432ebeec3b0dee6d0fffa201e0dc8f6502f0 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 10 Dec 2015 00:41:14 +0100 Subject: [PATCH 52/55] acpi: add aml_to_buffer() Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Marcel Apfelbaum --- hw/acpi/aml-build.c | 13 +++++++++++++ include/hw/acpi/aml-build.h | 1 + 2 files changed, 14 insertions(+) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index 40ea1284b7..9222e8fd90 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -449,6 +449,19 @@ Aml *aml_to_hexstring(Aml *src, Aml *dst) return var; } +/* ACPI 2.0a: 17.2.4.4 Type 2 Opcodes Encoding: DefToBuffer */ +Aml *aml_to_buffer(Aml *src, Aml *dst) +{ + Aml *var = aml_opcode(0x96 /* ToBufferOp */); + aml_append(var, src); + if (dst) { + aml_append(var, dst); + } else { + build_append_byte(var->buf, 0x00 /* NullNameOp */); + } + return var; +} + /* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefStore */ Aml *aml_store(Aml *val, Aml *target) { diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h index 7fafda8c24..d906105a82 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -231,6 +231,7 @@ Aml *aml_int(const uint64_t val); Aml *aml_arg(int pos); Aml *aml_to_integer(Aml *arg); Aml *aml_to_hexstring(Aml *src, Aml *dst); +Aml *aml_to_buffer(Aml *src, Aml *dst); Aml *aml_store(Aml *val, Aml *target); Aml *aml_and(Aml *arg1, Aml *arg2); Aml *aml_or(Aml *arg1, Aml *arg2); From a23b887281a018495948b924182bd6767d013b47 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 10 Dec 2015 00:41:15 +0100 Subject: [PATCH 53/55] acpi add aml_dma() Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/aml-build.c | 14 ++++++++++++++ include/hw/acpi/aml-build.h | 20 ++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index 9222e8fd90..0ab808717f 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -1275,6 +1275,20 @@ Aml *aml_qword_memory(AmlDecode dec, AmlMinFixed min_fixed, addr_trans, len, flags); } +/* ACPI 1.0b: 6.4.2.2 DMA Format/6.4.2.2.1 ASL Macro for DMA Descriptor */ +Aml *aml_dma(AmlDmaType typ, AmlDmaBusMaster bm, AmlTransferSize sz, + uint8_t channel) +{ + Aml *var = aml_alloc(); + uint8_t flags = sz | bm << 2 | typ << 5; + + assert(channel < 8); + build_append_byte(var->buf, 0x2A); /* Byte 0: DMA Descriptor */ + build_append_byte(var->buf, 1U << channel); /* Byte 1: _DMA - DmaChannel */ + build_append_byte(var->buf, flags); /* Byte 2 */ + return var; +} + /* ACPI 1.0b: 16.2.5.3 Type 1 Opcodes Encoding: DefSleep */ Aml *aml_sleep(uint64_t msec) { diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h index d906105a82..dd3e1cacc8 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -34,6 +34,24 @@ struct Aml { }; typedef struct Aml Aml; +typedef enum { + AML_COMPATIBILITY = 0, + AML_TYPEA = 1, + AML_TYPEB = 2, + AML_TYPEF = 3, +} AmlDmaType; + +typedef enum { + AML_NOTBUSMASTER = 0, + AML_BUSMASTER = 1, +} AmlDmaBusMaster; + +typedef enum { + AML_TRANSFER8 = 0, + AML_TRANSFER8_16 = 1, + AML_TRANSFER16 = 2, +} AmlTransferSize; + typedef enum { AML_DECODE10 = 0, AML_DECODE16 = 1, @@ -305,6 +323,8 @@ Aml *aml_qword_memory(AmlDecode dec, AmlMinFixed min_fixed, uint64_t addr_gran, uint64_t addr_min, uint64_t addr_max, uint64_t addr_trans, uint64_t len); +Aml *aml_dma(AmlDmaType typ, AmlDmaBusMaster bm, AmlTransferSize sz, + uint8_t channel); Aml *aml_sleep(uint64_t msec); /* Block AML object primitives */ From ca3df95df86de9e261135db26a434ebf829999c6 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 10 Dec 2015 00:41:16 +0100 Subject: [PATCH 54/55] acpi: extend aml_or() to accept target argument Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Marcel Apfelbaum --- hw/acpi/aml-build.c | 4 ++-- hw/arm/virt-acpi-build.c | 6 +++--- hw/i386/acpi-build.c | 3 ++- include/hw/acpi/aml-build.h | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index 0ab808717f..1f5ed5bca9 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -505,9 +505,9 @@ Aml *aml_and(Aml *arg1, Aml *arg2) } /* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefOr */ -Aml *aml_or(Aml *arg1, Aml *arg2) +Aml *aml_or(Aml *arg1, Aml *arg2, Aml *dst) { - return build_opcode_2arg_dst(0x7D /* OrOp */, arg1, arg2, NULL); + return build_opcode_2arg_dst(0x7D /* OrOp */, arg1, arg2, dst); } /* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefLOr */ diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 7aa0ab351d..ff2fb7073c 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -278,12 +278,12 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, aml_name("CTRL"))); ifctx1 = aml_if(aml_lnot(aml_equal(aml_arg(1), aml_int(0x1)))); - aml_append(ifctx1, aml_store(aml_or(aml_name("CDW1"), aml_int(0x08)), + aml_append(ifctx1, aml_store(aml_or(aml_name("CDW1"), aml_int(0x08), NULL), aml_name("CDW1"))); aml_append(ifctx, ifctx1); ifctx1 = aml_if(aml_lnot(aml_equal(aml_name("CDW3"), aml_name("CTRL")))); - aml_append(ifctx1, aml_store(aml_or(aml_name("CDW1"), aml_int(0x10)), + aml_append(ifctx1, aml_store(aml_or(aml_name("CDW1"), aml_int(0x10), NULL), aml_name("CDW1"))); aml_append(ifctx, ifctx1); @@ -292,7 +292,7 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, aml_append(method, ifctx); elsectx = aml_else(); - aml_append(elsectx, aml_store(aml_or(aml_name("CDW1"), aml_int(4)), + aml_append(elsectx, aml_store(aml_or(aml_name("CDW1"), aml_int(4), NULL), aml_name("CDW1"))); aml_append(elsectx, aml_return(aml_arg(3))); aml_append(method, elsectx); diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 4e9855ad00..374644f630 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -680,7 +680,8 @@ static Aml *build_prt(void) /* route[0] = 0x[slot]FFFF */ aml_append(while_ctx, - aml_store(aml_or(aml_shiftleft(slot, aml_int(16)), aml_int(0xFFFF)), + aml_store(aml_or(aml_shiftleft(slot, aml_int(16)), aml_int(0xFFFF), + NULL), aml_index(route, aml_int(0)))); /* route[1] = pin & 3 */ aml_append(while_ctx, diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h index dd3e1cacc8..245c475ebc 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -252,7 +252,7 @@ Aml *aml_to_hexstring(Aml *src, Aml *dst); Aml *aml_to_buffer(Aml *src, Aml *dst); Aml *aml_store(Aml *val, Aml *target); Aml *aml_and(Aml *arg1, Aml *arg2); -Aml *aml_or(Aml *arg1, Aml *arg2); +Aml *aml_or(Aml *arg1, Aml *arg2, Aml *dst); Aml *aml_lor(Aml *arg1, Aml *arg2); Aml *aml_shiftleft(Aml *arg1, Aml *count); Aml *aml_shiftright(Aml *arg1, Aml *count, Aml *dst); From 5530427f0ca240b972f25ef0413fb966f96dfb05 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 10 Dec 2015 00:41:17 +0100 Subject: [PATCH 55/55] acpi: extend aml_and() to accept target argument Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/aml-build.c | 4 ++-- hw/arm/virt-acpi-build.c | 2 +- hw/i386/acpi-build.c | 8 +++++--- include/hw/acpi/aml-build.h | 2 +- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index 1f5ed5bca9..78e1290208 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -499,9 +499,9 @@ build_opcode_2arg_dst(uint8_t op, Aml *arg1, Aml *arg2, Aml *dst) } /* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefAnd */ -Aml *aml_and(Aml *arg1, Aml *arg2) +Aml *aml_and(Aml *arg1, Aml *arg2, Aml *dst) { - return build_opcode_2arg_dst(0x7B /* AndOp */, arg1, arg2, NULL); + return build_opcode_2arg_dst(0x7B /* AndOp */, arg1, arg2, dst); } /* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefOr */ diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index ff2fb7073c..0caf5ced69 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -274,7 +274,7 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, aml_create_dword_field(aml_arg(3), aml_int(8), "CDW3")); aml_append(ifctx, aml_store(aml_name("CDW2"), aml_name("SUPP"))); aml_append(ifctx, aml_store(aml_name("CDW3"), aml_name("CTRL"))); - aml_append(ifctx, aml_store(aml_and(aml_name("CTRL"), aml_int(0x1D)), + aml_append(ifctx, aml_store(aml_and(aml_name("CTRL"), aml_int(0x1D), NULL), aml_name("CTRL"))); ifctx1 = aml_if(aml_lnot(aml_equal(aml_arg(1), aml_int(0x1)))); diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 374644f630..4cc1440f77 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -470,7 +470,7 @@ static void build_append_pcihp_notify_entry(Aml *method, int slot) Aml *if_ctx; int32_t devfn = PCI_DEVFN(slot, 0); - if_ctx = aml_if(aml_and(aml_arg(0), aml_int(0x1U << slot))); + if_ctx = aml_if(aml_and(aml_arg(0), aml_int(0x1U << slot), NULL)); aml_append(if_ctx, aml_notify(aml_name("S%.02X", devfn), aml_arg(1))); aml_append(method, if_ctx); } @@ -670,7 +670,8 @@ static Aml *build_prt(void) aml_store(aml_shiftright(pin, aml_int(2), NULL), slot)); /* lnk_idx = (slot + pin) & 3 */ aml_append(while_ctx, - aml_store(aml_and(aml_add(pin, slot, NULL), aml_int(3)), lnk_idx)); + aml_store(aml_and(aml_add(pin, slot, NULL), aml_int(3), NULL), + lnk_idx)); /* route[2] = "LNK[D|A|B|C]", selection based on pin % 3 */ aml_append(while_ctx, initialize_route(route, "LNKD", lnk_idx, 0)); @@ -685,7 +686,8 @@ static Aml *build_prt(void) aml_index(route, aml_int(0)))); /* route[1] = pin & 3 */ aml_append(while_ctx, - aml_store(aml_and(pin, aml_int(3)), aml_index(route, aml_int(1)))); + aml_store(aml_and(pin, aml_int(3), NULL), + aml_index(route, aml_int(1)))); /* res[pin] = route */ aml_append(while_ctx, aml_store(route, aml_index(res, pin))); /* pin++ */ diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h index 245c475ebc..ef44d02285 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -251,7 +251,7 @@ Aml *aml_to_integer(Aml *arg); Aml *aml_to_hexstring(Aml *src, Aml *dst); Aml *aml_to_buffer(Aml *src, Aml *dst); Aml *aml_store(Aml *val, Aml *target); -Aml *aml_and(Aml *arg1, Aml *arg2); +Aml *aml_and(Aml *arg1, Aml *arg2, Aml *dst); Aml *aml_or(Aml *arg1, Aml *arg2, Aml *dst); Aml *aml_lor(Aml *arg1, Aml *arg2); Aml *aml_shiftleft(Aml *arg1, Aml *count);