X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/8f242cb724cad4a3996e4634e55b7c03ed508a69..4f67d30b5e74e060b8dbe10528829b47345cd6e8:/hw/isa/lpc_ich9.c diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c index 10d1ee8b93..f85b484eac 100644 --- a/hw/isa/lpc_ich9.c +++ b/hw/isa/lpc_ich9.c @@ -27,27 +27,30 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ + #include "qemu/osdep.h" -#include "qemu-common.h" #include "cpu.h" -#include "hw/hw.h" #include "qapi/visitor.h" #include "qemu/range.h" #include "hw/isa/isa.h" #include "hw/sysbus.h" -#include "hw/i386/pc.h" +#include "migration/vmstate.h" +#include "hw/irq.h" #include "hw/isa/apm.h" #include "hw/i386/ioapic.h" #include "hw/pci/pci.h" -#include "hw/pci/pcie_host.h" #include "hw/pci/pci_bridge.h" #include "hw/i386/ich9.h" #include "hw/acpi/acpi.h" #include "hw/acpi/ich9.h" #include "hw/pci/pci_bus.h" +#include "hw/qdev-properties.h" #include "exec/address-spaces.h" +#include "sysemu/runstate.h" #include "sysemu/sysemu.h" -#include "qom/cpu.h" +#include "hw/core/cpu.h" +#include "hw/nvram/fw_cfg.h" +#include "qemu/cutils.h" /*****************************************************************************/ /* ICH9 LPC PCI to ISA bridge */ @@ -160,7 +163,7 @@ static void ich9_cc_write(void *opaque, hwaddr addr, ich9_cc_addr_len(&addr, &len); memcpy(lpc->chip_config + addr, &val, len); - pci_bus_fire_intx_routing_notifier(lpc->d.bus); + pci_bus_fire_intx_routing_notifier(pci_get_bus(&lpc->d)); ich9_cc_update(lpc); } @@ -216,7 +219,7 @@ static void ich9_lpc_update_pic(ICH9LPCState *lpc, int gsi) int tmp_dis; ich9_lpc_pic_irq(lpc, i, &tmp_irq, &tmp_dis); if (!tmp_dis && tmp_irq == gsi) { - pic_level |= pci_bus_get_irq_level(lpc->d.bus, i); + pic_level |= pci_bus_get_irq_level(pci_get_bus(&lpc->d), i); } } if (gsi == lpc->sci_gsi) { @@ -244,7 +247,7 @@ static void ich9_lpc_update_apic(ICH9LPCState *lpc, int gsi) assert(gsi >= ICH9_LPC_PIC_NUM_PINS); - level |= pci_bus_get_irq_level(lpc->d.bus, ich9_gsi_to_pirq(gsi)); + level |= pci_bus_get_irq_level(pci_get_bus(&lpc->d), ich9_gsi_to_pirq(gsi)); if (gsi == lpc->sci_gsi) { level |= lpc->sci_level; } @@ -310,11 +313,6 @@ void ich9_generate_smi(void) cpu_interrupt(first_cpu, CPU_INTERRUPT_SMI); } -void ich9_generate_nmi(void) -{ - cpu_interrupt(first_cpu, CPU_INTERRUPT_NMI); -} - static int ich9_lpc_sci_irq(ICH9LPCState *lpc) { switch (lpc->d.config[ICH9_LPC_ACPI_CTRL] & @@ -360,14 +358,63 @@ static void ich9_set_sci(void *opaque, int irq_num, int level) } } +static void smi_features_ok_callback(void *opaque) +{ + ICH9LPCState *lpc = opaque; + uint64_t guest_features; + + if (lpc->smi_features_ok) { + /* negotiation already complete, features locked */ + return; + } + + memcpy(&guest_features, lpc->smi_guest_features_le, sizeof guest_features); + le64_to_cpus(&guest_features); + if (guest_features & ~lpc->smi_host_features) { + /* guest requests invalid features, leave @features_ok at zero */ + return; + } + + /* valid feature subset requested, lock it down, report success */ + lpc->smi_negotiated_features = guest_features; + lpc->smi_features_ok = 1; +} + void ich9_lpc_pm_init(PCIDevice *lpc_pci, bool smm_enabled) { ICH9LPCState *lpc = ICH9_LPC_DEVICE(lpc_pci); qemu_irq sci_irq; + FWCfgState *fw_cfg = fw_cfg_find(); sci_irq = qemu_allocate_irq(ich9_set_sci, lpc, 0); ich9_pm_init(lpc_pci, &lpc->pm, smm_enabled, sci_irq); - ich9_lpc_reset(&lpc->d.qdev); + + if (lpc->smi_host_features && fw_cfg) { + uint64_t host_features_le; + + host_features_le = cpu_to_le64(lpc->smi_host_features); + memcpy(lpc->smi_host_features_le, &host_features_le, + sizeof host_features_le); + fw_cfg_add_file(fw_cfg, "etc/smi/supported-features", + lpc->smi_host_features_le, + sizeof lpc->smi_host_features_le); + + /* The other two guest-visible fields are cleared on device reset, we + * just link them into fw_cfg here. + */ + fw_cfg_add_file_callback(fw_cfg, "etc/smi/requested-features", + NULL, NULL, NULL, + lpc->smi_guest_features_le, + sizeof lpc->smi_guest_features_le, + false); + fw_cfg_add_file_callback(fw_cfg, "etc/smi/features-ok", + smi_features_ok_callback, NULL, lpc, + &lpc->smi_features_ok, + sizeof lpc->smi_features_ok, + true); + } + + ich9_lpc_reset(DEVICE(lpc)); } /* APM */ @@ -386,7 +433,15 @@ static void ich9_apm_ctrl_changed(uint32_t val, void *arg) /* SMI_EN = PMBASE + 30. SMI control and enable register */ if (lpc->pm.smi_en & ICH9_PMIO_SMI_EN_APMC_EN) { - cpu_interrupt(current_cpu, CPU_INTERRUPT_SMI); + if (lpc->smi_negotiated_features & + (UINT64_C(1) << ICH9_LPC_SMI_F_BROADCAST_BIT)) { + CPUState *cs; + CPU_FOREACH(cs) { + cpu_interrupt(cs, CPU_INTERRUPT_SMI); + } + } else { + cpu_interrupt(current_cpu, CPU_INTERRUPT_SMI); + } } } @@ -470,10 +525,10 @@ static void ich9_lpc_config_write(PCIDevice *d, ich9_lpc_rcba_update(lpc, rcba_old); } if (ranges_overlap(addr, len, ICH9_LPC_PIRQA_ROUT, 4)) { - pci_bus_fire_intx_routing_notifier(lpc->d.bus); + pci_bus_fire_intx_routing_notifier(pci_get_bus(&lpc->d)); } if (ranges_overlap(addr, len, ICH9_LPC_PIRQE_ROUT, 4)) { - pci_bus_fire_intx_routing_notifier(lpc->d.bus); + pci_bus_fire_intx_routing_notifier(pci_get_bus(&lpc->d)); } if (ranges_overlap(addr, len, ICH9_LPC_GEN_PMCON_1, 8)) { ich9_lpc_pmcon_update(lpc); @@ -507,6 +562,10 @@ static void ich9_lpc_reset(DeviceState *qdev) lpc->sci_level = 0; lpc->rst_cnt = 0; + + memset(lpc->smi_guest_features_le, 0, sizeof lpc->smi_guest_features_le); + lpc->smi_features_ok = 0; + lpc->smi_negotiated_features = 0; } /* root complex register block is mapped into memory space */ @@ -548,7 +607,7 @@ static void ich9_rst_cnt_write(void *opaque, hwaddr addr, uint64_t val, ICH9LPCState *lpc = opaque; if (val & 4) { - qemu_system_reset_request(); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); return; } lpc->rst_cnt = val & 0xA; /* keep FULL_RST (bit 3) and SYS_RST (bit 1) */ @@ -567,17 +626,6 @@ static const MemoryRegionOps ich9_rst_cnt_ops = { .endianness = DEVICE_LITTLE_ENDIAN }; -Object *ich9_lpc_find(void) -{ - bool ambig; - Object *o = object_resolve_path_type("", TYPE_ICH9_LPC_DEVICE, &ambig); - - if (ambig) { - return NULL; - } - return o; -} - static void ich9_lpc_get_sci_int(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { @@ -668,6 +716,29 @@ static const VMStateDescription vmstate_ich9_rst_cnt = { } }; +static bool ich9_smi_feat_needed(void *opaque) +{ + ICH9LPCState *lpc = opaque; + + return !buffer_is_zero(lpc->smi_guest_features_le, + sizeof lpc->smi_guest_features_le) || + lpc->smi_features_ok; +} + +static const VMStateDescription vmstate_ich9_smi_feat = { + .name = "ICH9LPC/smi_feat", + .version_id = 1, + .minimum_version_id = 1, + .needed = ich9_smi_feat_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT8_ARRAY(smi_guest_features_le, ICH9LPCState, + sizeof(uint64_t)), + VMSTATE_UINT8(smi_features_ok, ICH9LPCState), + VMSTATE_UINT64(smi_negotiated_features, ICH9LPCState), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_ich9_lpc = { .name = "ICH9LPC", .version_id = 1, @@ -683,12 +754,15 @@ static const VMStateDescription vmstate_ich9_lpc = { }, .subsections = (const VMStateDescription*[]) { &vmstate_ich9_rst_cnt, + &vmstate_ich9_smi_feat, NULL } }; static Property ich9_lpc_properties[] = { DEFINE_PROP_BOOL("noreboot", ICH9LPCState, pin_strap.spkr_hi, true), + DEFINE_PROP_BIT64("x-smi-broadcast", ICH9LPCState, smi_host_features, + ICH9_LPC_SMI_F_BROADCAST_BIT, true), DEFINE_PROP_END_OF_LIST(), }; @@ -710,7 +784,7 @@ static void ich9_lpc_class_init(ObjectClass *klass, void *data) dc->reset = ich9_lpc_reset; k->realize = ich9_lpc_realize; dc->vmsd = &vmstate_ich9_lpc; - dc->props = ich9_lpc_properties; + device_class_set_props(dc, ich9_lpc_properties); k->config_write = ich9_lpc_config_write; dc->desc = "ICH9 LPC bridge"; k->vendor_id = PCI_VENDOR_ID_INTEL; @@ -721,7 +795,8 @@ static void ich9_lpc_class_init(ObjectClass *klass, void *data) * Reason: part of ICH9 southbridge, needs to be wired up by * pc_q35_init() */ - dc->cannot_instantiate_with_device_add_yet = true; + dc->user_creatable = false; + hc->pre_plug = ich9_pm_device_pre_plug_cb; hc->plug = ich9_pm_device_plug_cb; hc->unplug_request = ich9_pm_device_unplug_request_cb; hc->unplug = ich9_pm_device_unplug_cb; @@ -739,6 +814,7 @@ static const TypeInfo ich9_lpc_info = { .interfaces = (InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { TYPE_ACPI_DEVICE_IF }, + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { } } };