From 73284563dce268bade300370a8b20728d3866afe Mon Sep 17 00:00:00 2001 From: Mario Smarduch Date: Wed, 9 Oct 2019 09:44:59 -0700 Subject: [PATCH 01/34] target/i386: log MCE guest and host addresses Patch logs MCE AO, AR messages injected to guest or taken by QEMU itself. We print the QEMU address for guest MCEs, helps on hypervisors that have another source of MCE logging like mce log, and when they go missing. For example we found these QEMU logs: September 26th 2019, 17:36:02.309 Droplet-153258224: Guest MCE Memory Error at qemu addr 0x7f8ce14f5000 and guest 3d6f5000 addr of type BUS_MCEERR_AR injected qemu-system-x86_64 amsN ams3nodeNNNN September 27th 2019, 06:25:03.234 Droplet-153258224: Guest MCE Memory Error at qemu addr 0x7f8ce14f5000 and guest 3d6f5000 addr of type BUS_MCEERR_AR injected qemu-system-x86_64 amsN ams3nodeNNNN The first log had a corresponding mce log entry, the second didnt (logging thresholds) we can infer from second entry same PA and mce type. Signed-off-by: Mario Smarduch Message-Id: <20191009164459.8209-3-msmarduch@digitalocean.com> Signed-off-by: Paolo Bonzini --- target/i386/kvm.c | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/target/i386/kvm.c b/target/i386/kvm.c index 8c73438c67..0e3da998d1 100644 --- a/target/i386/kvm.c +++ b/target/i386/kvm.c @@ -592,9 +592,9 @@ static void kvm_mce_inject(X86CPU *cpu, hwaddr paddr, int code) (MCM_ADDR_PHYS << 6) | 0xc, flags); } -static void hardware_memory_error(void) +static void hardware_memory_error(void *host_addr) { - fprintf(stderr, "Hardware memory error!\n"); + error_report("QEMU got Hardware memory error at addr %p", host_addr); exit(1); } @@ -618,15 +618,34 @@ void kvm_arch_on_sigbus_vcpu(CPUState *c, int code, void *addr) kvm_physical_memory_addr_from_host(c->kvm_state, addr, &paddr)) { kvm_hwpoison_page_add(ram_addr); kvm_mce_inject(cpu, paddr, code); + + /* + * Use different logging severity based on error type. + * If there is additional MCE reporting on the hypervisor, QEMU VA + * could be another source to identify the PA and MCE details. + */ + if (code == BUS_MCEERR_AR) { + error_report("Guest MCE Memory Error at QEMU addr %p and " + "GUEST addr 0x%" HWADDR_PRIx " of type %s injected", + addr, paddr, "BUS_MCEERR_AR"); + } else { + warn_report("Guest MCE Memory Error at QEMU addr %p and " + "GUEST addr 0x%" HWADDR_PRIx " of type %s injected", + addr, paddr, "BUS_MCEERR_AO"); + } + return; } - fprintf(stderr, "Hardware memory error for memory used by " - "QEMU itself instead of guest system!\n"); + if (code == BUS_MCEERR_AO) { + warn_report("Hardware memory error at addr %p of type %s " + "for memory used by QEMU itself instead of guest system!", + addr, "BUS_MCEERR_AO"); + } } if (code == BUS_MCEERR_AR) { - hardware_memory_error(); + hardware_memory_error(addr); } /* Hope we are lucky for AO MCE */ From b429de730174b388ea5760e3debb0d542ea3c261 Mon Sep 17 00:00:00 2001 From: Marcelo Tosatti Date: Thu, 10 Oct 2019 09:30:08 -0300 Subject: [PATCH 02/34] mc146818rtc: fix timer interrupt reinjection commit 369b41359af46bded5799c9ef8be2b641d92e043 broke timer interrupt reinjection when there is no period change by the guest. In that case, old_period is 0, which ends up zeroing irq_coalesced (counter of reinjected interrupts). The consequence is Windows 7 is unable to synchronize time via NTP. Easily reproducible by playing a fullscreen video with cirrus and VNC. Fix by not updating s->irq_coalesced when old_period is 0. V2: reorganize code (Paolo Bonzini) Signed-off-by: Marcelo Tosatti Message-Id: <20191010123008.GA19158@amt.cnet> Signed-off-by: Paolo Bonzini --- hw/timer/mc146818rtc.c | 53 +++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c index 6cb378751b..0e7cf97042 100644 --- a/hw/timer/mc146818rtc.c +++ b/hw/timer/mc146818rtc.c @@ -203,24 +203,28 @@ periodic_timer_update(RTCState *s, int64_t current_time, uint32_t old_period) period = rtc_periodic_clock_ticks(s); - if (period) { - /* compute 32 khz clock */ - cur_clock = - muldiv64(current_time, RTC_CLOCK_RATE, NANOSECONDS_PER_SECOND); + if (!period) { + s->irq_coalesced = 0; + timer_del(s->periodic_timer); + return; + } - /* - * if the periodic timer's update is due to period re-configuration, - * we should count the clock since last interrupt. - */ - if (old_period) { - int64_t last_periodic_clock, next_periodic_clock; + /* compute 32 khz clock */ + cur_clock = + muldiv64(current_time, RTC_CLOCK_RATE, NANOSECONDS_PER_SECOND); - next_periodic_clock = muldiv64(s->next_periodic_time, - RTC_CLOCK_RATE, NANOSECONDS_PER_SECOND); - last_periodic_clock = next_periodic_clock - old_period; - lost_clock = cur_clock - last_periodic_clock; - assert(lost_clock >= 0); - } + /* + * if the periodic timer's update is due to period re-configuration, + * we should count the clock since last interrupt. + */ + if (old_period) { + int64_t last_periodic_clock, next_periodic_clock; + + next_periodic_clock = muldiv64(s->next_periodic_time, + RTC_CLOCK_RATE, NANOSECONDS_PER_SECOND); + last_periodic_clock = next_periodic_clock - old_period; + lost_clock = cur_clock - last_periodic_clock; + assert(lost_clock >= 0); /* * s->irq_coalesced can change for two reasons: @@ -251,22 +255,19 @@ periodic_timer_update(RTCState *s, int64_t current_time, uint32_t old_period) rtc_coalesced_timer_update(s); } } else { - /* + /* * no way to compensate the interrupt if LOST_TICK_POLICY_SLEW * is not used, we should make the time progress anyway. */ lost_clock = MIN(lost_clock, period); } - - assert(lost_clock >= 0 && lost_clock <= period); - - next_irq_clock = cur_clock + period - lost_clock; - s->next_periodic_time = periodic_clock_to_ns(next_irq_clock) + 1; - timer_mod(s->periodic_timer, s->next_periodic_time); - } else { - s->irq_coalesced = 0; - timer_del(s->periodic_timer); } + + assert(lost_clock >= 0 && lost_clock <= period); + + next_irq_clock = cur_clock + period - lost_clock; + s->next_periodic_time = periodic_clock_to_ns(next_irq_clock) + 1; + timer_mod(s->periodic_timer, s->next_periodic_time); } static void rtc_periodic_timer(void *opaque) From f1733749709b2f6935493e8183afe1ce6c80d52d Mon Sep 17 00:00:00 2001 From: Alexander Shopov Date: Sat, 19 Oct 2019 14:05:34 +0200 Subject: [PATCH 03/34] Updated Bulgarian translation (19) - 4.1.0 Signed-off-by: Alexander Shopov Message-Id: <20191019120534.27479-2-ash@kambanaria.org> Signed-off-by: Paolo Bonzini --- po/bg.po | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/po/bg.po b/po/bg.po index 3d8c353372..98c57e5b22 100644 --- a/po/bg.po +++ b/po/bg.po @@ -1,14 +1,14 @@ # Bulgarian translation of qemu po-file. -# Copyright (C) 2016 Alexander Shopov +# Copyright (C) 2016, 2019 Alexander Shopov # This file is distributed under the same license as the qemu package. -# Alexander Shopov , 2016. +# Alexander Shopov , 2016, 2019. # msgid "" msgstr "" -"Project-Id-Version: QEMU 2.6.50\n" +"Project-Id-Version: QEMU 4.1.0\n" "Report-Msgid-Bugs-To: qemu-devel@nongnu.org\n" "POT-Creation-Date: 2018-07-18 07:56+0200\n" -"PO-Revision-Date: 2016-06-09 15:54+0300\n" +"PO-Revision-Date: 2019-10-19 13:14+0200\n" "Last-Translator: Alexander Shopov \n" "Language-Team: Bulgarian \n" "Language: bg\n" @@ -66,7 +66,7 @@ msgid "Detach Tab" msgstr "Към самостоятелен подпрозорец" msgid "Show Menubar" -msgstr "" +msgstr "Лента за менюто" msgid "_Machine" msgstr "_Машина" From 30d6ff662d0411fcb98a577e58582690fc2c513c Mon Sep 17 00:00:00 2001 From: Vitaly Kuznetsov Date: Fri, 18 Oct 2019 18:39:08 +0200 Subject: [PATCH 04/34] i386/kvm: add NoNonArchitecturalCoreSharing Hyper-V enlightenment Hyper-V TLFS specifies this enlightenment as: "NoNonArchitecturalCoreSharing - Indicates that a virtual processor will never share a physical core with another virtual processor, except for virtual processors that are reported as sibling SMT threads. This can be used as an optimization to avoid the performance overhead of STIBP". However, STIBP is not the only implication. It was found that Hyper-V on KVM doesn't pass MD_CLEAR bit to its guests if it doesn't see NoNonArchitecturalCoreSharing bit. KVM reports NoNonArchitecturalCoreSharing in KVM_GET_SUPPORTED_HV_CPUID to indicate that SMT on the host is impossible (not supported of forcefully disabled). Implement NoNonArchitecturalCoreSharing support in QEMU as tristate: 'off' - the feature is disabled (default) 'on' - the feature is enabled. This is only safe if vCPUS are properly pinned and correct topology is exposed. As CPU pinning is done outside of QEMU the enablement decision will be made on a higher level. 'auto' - copy KVM setting. As during live migration SMT settings on the source and destination host may differ this requires us to add a migration blocker. Signed-off-by: Vitaly Kuznetsov Message-Id: <20191018163908.10246-1-vkuznets@redhat.com> Signed-off-by: Paolo Bonzini --- docs/hyperv.txt | 13 +++++++++++++ target/i386/cpu.c | 2 ++ target/i386/cpu.h | 2 ++ target/i386/hyperv-proto.h | 1 + target/i386/kvm.c | 26 ++++++++++++++++++++++++++ 5 files changed, 44 insertions(+) diff --git a/docs/hyperv.txt b/docs/hyperv.txt index 8fdf25c829..6518b716a9 100644 --- a/docs/hyperv.txt +++ b/docs/hyperv.txt @@ -184,6 +184,19 @@ enabled. Requires: hv-vpindex, hv-synic, hv-time, hv-stimer +3.17. hv-no-nonarch-coresharing=on/off/auto +=========================================== +This enlightenment tells guest OS that virtual processors will never share a +physical core unless they are reported as sibling SMT threads. This information +is required by Windows and Hyper-V guests to properly mitigate SMT related CPU +vulnerabilities. +When the option is set to 'auto' QEMU will enable the feature only when KVM +reports that non-architectural coresharing is impossible, this means that +hyper-threading is not supported or completely disabled on the host. This +setting also prevents migration as SMT settings on the destination may differ. +When the option is set to 'on' QEMU will always enable the feature, regardless +of host setup. To keep guests secure, this can only be used in conjunction with +exposing correct vCPU topology and vCPU pinning. 4. Development features ======================== diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 47200b40c1..6c87c99487 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -6174,6 +6174,8 @@ static Property x86_cpu_properties[] = { HYPERV_FEAT_IPI, 0), DEFINE_PROP_BIT64("hv-stimer-direct", X86CPU, hyperv_features, HYPERV_FEAT_STIMER_DIRECT, 0), + DEFINE_PROP_ON_OFF_AUTO("hv-no-nonarch-coresharing", X86CPU, + hyperv_no_nonarch_cs, ON_OFF_AUTO_OFF), DEFINE_PROP_BOOL("hv-passthrough", X86CPU, hyperv_passthrough, false), DEFINE_PROP_BOOL("check", X86CPU, check_cpuid, true), diff --git a/target/i386/cpu.h b/target/i386/cpu.h index cedb5bc205..c50a9c7257 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -24,6 +24,7 @@ #include "cpu-qom.h" #include "hyperv-proto.h" #include "exec/cpu-defs.h" +#include "qapi/qapi-types-common.h" /* The x86 has a strong memory model with some store-after-load re-ordering */ #define TCG_GUEST_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD) @@ -1614,6 +1615,7 @@ struct X86CPU { bool hyperv_synic_kvm_only; uint64_t hyperv_features; bool hyperv_passthrough; + OnOffAuto hyperv_no_nonarch_cs; bool check_cpuid; bool enforce_cpuid; diff --git a/target/i386/hyperv-proto.h b/target/i386/hyperv-proto.h index cffac10b45..056a305be3 100644 --- a/target/i386/hyperv-proto.h +++ b/target/i386/hyperv-proto.h @@ -63,6 +63,7 @@ #define HV_CLUSTER_IPI_RECOMMENDED (1u << 10) #define HV_EX_PROCESSOR_MASKS_RECOMMENDED (1u << 11) #define HV_ENLIGHTENED_VMCS_RECOMMENDED (1u << 14) +#define HV_NO_NONARCH_CORESHARING (1u << 18) /* * Basic virtualized MSRs diff --git a/target/i386/kvm.c b/target/i386/kvm.c index 0e3da998d1..49a301190a 100644 --- a/target/i386/kvm.c +++ b/target/i386/kvm.c @@ -1227,6 +1227,16 @@ static int hyperv_handle_properties(CPUState *cs, } } + if (cpu->hyperv_no_nonarch_cs == ON_OFF_AUTO_ON) { + env->features[FEAT_HV_RECOMM_EAX] |= HV_NO_NONARCH_CORESHARING; + } else if (cpu->hyperv_no_nonarch_cs == ON_OFF_AUTO_AUTO) { + c = cpuid_find_entry(cpuid, HV_CPUID_ENLIGHTMENT_INFO, 0); + if (c) { + env->features[FEAT_HV_RECOMM_EAX] |= + c->eax & HV_NO_NONARCH_CORESHARING; + } + } + /* Features */ r = hv_cpuid_check_and_set(cs, cpuid, HYPERV_FEAT_RELAXED); r |= hv_cpuid_check_and_set(cs, cpuid, HYPERV_FEAT_VAPIC); @@ -1340,6 +1350,7 @@ free: } static Error *hv_passthrough_mig_blocker; +static Error *hv_no_nonarch_cs_mig_blocker; static int hyperv_init_vcpu(X86CPU *cpu) { @@ -1359,6 +1370,21 @@ static int hyperv_init_vcpu(X86CPU *cpu) } } + if (cpu->hyperv_no_nonarch_cs == ON_OFF_AUTO_AUTO && + hv_no_nonarch_cs_mig_blocker == NULL) { + error_setg(&hv_no_nonarch_cs_mig_blocker, + "'hv-no-nonarch-coresharing=auto' CPU flag prevents migration" + " use explicit 'hv-no-nonarch-coresharing=on' instead (but" + " make sure SMT is disabled and/or that vCPUs are properly" + " pinned)"); + ret = migrate_add_blocker(hv_no_nonarch_cs_mig_blocker, &local_err); + if (local_err) { + error_report_err(local_err); + error_free(hv_no_nonarch_cs_mig_blocker); + return ret; + } + } + if (hyperv_feat_enabled(cpu, HYPERV_FEAT_VPINDEX) && !hv_vpindex_settable) { /* * the kernel doesn't support setting vp_index; assert that its value From 372a87a1d9f611cedeff2f679fe8da219f0b938f Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 18 Oct 2019 15:07:16 +0200 Subject: [PATCH 05/34] Do not use %m in common code to print error messages The %m format specifier is an extension from glibc - and when compiling QEMU for NetBSD, the compiler correctly complains, e.g.: /home/qemu/qemu-test.ELjfrQ/src/util/main-loop.c: In function 'sigfd_handler': /home/qemu/qemu-test.ELjfrQ/src/util/main-loop.c:64:13: warning: %m is only allowed in syslog(3) like functions [-Wformat=] printf("read from sigfd returned %zd: %m\n", len); ^ Let's use g_strerror() here instead, which is an easy-to-use wrapper around the thread-safe strerror_r() function. While we're at it, also convert the "printf()" in main-loop.c into the preferred "error_report()". Signed-off-by: Thomas Huth Message-Id: <20191018130716.25438-1-thuth@redhat.com> Signed-off-by: Paolo Bonzini --- hw/misc/tmp421.c | 4 ++-- util/main-loop.c | 3 ++- util/systemd.c | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/hw/misc/tmp421.c b/hw/misc/tmp421.c index 9f044705fa..c0bc150bca 100644 --- a/hw/misc/tmp421.c +++ b/hw/misc/tmp421.c @@ -120,7 +120,7 @@ static void tmp421_get_temperature(Object *obj, Visitor *v, const char *name, int tempid; if (sscanf(name, "temperature%d", &tempid) != 1) { - error_setg(errp, "error reading %s: %m", name); + error_setg(errp, "error reading %s: %s", name, g_strerror(errno)); return; } @@ -160,7 +160,7 @@ static void tmp421_set_temperature(Object *obj, Visitor *v, const char *name, } if (sscanf(name, "temperature%d", &tempid) != 1) { - error_setg(errp, "error reading %s: %m", name); + error_setg(errp, "error reading %s: %s", name, g_strerror(errno)); return; } diff --git a/util/main-loop.c b/util/main-loop.c index e3eaa55866..eda63fe4e0 100644 --- a/util/main-loop.c +++ b/util/main-loop.c @@ -61,7 +61,8 @@ static void sigfd_handler(void *opaque) } if (len != sizeof(info)) { - printf("read from sigfd returned %zd: %m\n", len); + error_report("read from sigfd returned %zd: %s", len, + g_strerror(errno)); return; } diff --git a/util/systemd.c b/util/systemd.c index d22e86c707..1dd0367d9a 100644 --- a/util/systemd.c +++ b/util/systemd.c @@ -60,8 +60,8 @@ unsigned int check_socket_activation(void) * and we should exit. */ error_report("Socket activation failed: " - "invalid file descriptor fd = %d: %m", - fd); + "invalid file descriptor fd = %d: %s", + fd, g_strerror(errno)); exit(EXIT_FAILURE); } } From 7320c8b4ae610ad0b2faa3b049d8312b26f31518 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 21 Oct 2019 19:11:09 +0200 Subject: [PATCH 06/34] memory-device: simplify Makefile.objs conditions hw/mem/ is only included if CONFIG_MEM_DEVICE is true, so we need not specify the condition again in hw/mem/Makefile.objs. Signed-off-by: Paolo Bonzini --- hw/mem/Makefile.objs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/mem/Makefile.objs b/hw/mem/Makefile.objs index 3e2f7c5ca2..56345befd0 100644 --- a/hw/mem/Makefile.objs +++ b/hw/mem/Makefile.objs @@ -1,3 +1,3 @@ common-obj-$(CONFIG_DIMM) += pc-dimm.o -common-obj-$(CONFIG_MEM_DEVICE) += memory-device.o +common-obj-y += memory-device.o common-obj-$(CONFIG_NVDIMM) += nvdimm.o From bca964bcea65e69895ff7200ca0b5ddd01b87d24 Mon Sep 17 00:00:00 2001 From: Sergio Lopez Date: Thu, 26 Sep 2019 15:02:22 +0200 Subject: [PATCH 07/34] hw/virtio: Factorize virtio-mmio headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Put QOM and main struct definition in a separate header file, so it can be accessed from other components. Signed-off-by: Sergio Lopez Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Reviewed-by: Michael S. Tsirkin --- hw/virtio/virtio-mmio.c | 48 +--------------------- include/hw/virtio/virtio-mmio.h | 73 +++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 47 deletions(-) create mode 100644 include/hw/virtio/virtio-mmio.h diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c index 3d5ca0f667..94d934c44b 100644 --- a/hw/virtio/virtio-mmio.c +++ b/hw/virtio/virtio-mmio.c @@ -29,57 +29,11 @@ #include "qemu/host-utils.h" #include "qemu/module.h" #include "sysemu/kvm.h" -#include "hw/virtio/virtio-bus.h" +#include "hw/virtio/virtio-mmio.h" #include "qemu/error-report.h" #include "qemu/log.h" #include "trace.h" -/* QOM macros */ -/* virtio-mmio-bus */ -#define TYPE_VIRTIO_MMIO_BUS "virtio-mmio-bus" -#define VIRTIO_MMIO_BUS(obj) \ - OBJECT_CHECK(VirtioBusState, (obj), TYPE_VIRTIO_MMIO_BUS) -#define VIRTIO_MMIO_BUS_GET_CLASS(obj) \ - OBJECT_GET_CLASS(VirtioBusClass, (obj), TYPE_VIRTIO_MMIO_BUS) -#define VIRTIO_MMIO_BUS_CLASS(klass) \ - OBJECT_CLASS_CHECK(VirtioBusClass, (klass), TYPE_VIRTIO_MMIO_BUS) - -/* virtio-mmio */ -#define TYPE_VIRTIO_MMIO "virtio-mmio" -#define VIRTIO_MMIO(obj) \ - OBJECT_CHECK(VirtIOMMIOProxy, (obj), TYPE_VIRTIO_MMIO) - -#define VIRT_MAGIC 0x74726976 /* 'virt' */ -#define VIRT_VERSION 2 -#define VIRT_VERSION_LEGACY 1 -#define VIRT_VENDOR 0x554D4551 /* 'QEMU' */ - -typedef struct VirtIOMMIOQueue { - uint16_t num; - bool enabled; - uint32_t desc[2]; - uint32_t avail[2]; - uint32_t used[2]; -} VirtIOMMIOQueue; - -typedef struct { - /* Generic */ - SysBusDevice parent_obj; - MemoryRegion iomem; - qemu_irq irq; - bool legacy; - /* Guest accessible state needing migration and reset */ - uint32_t host_features_sel; - uint32_t guest_features_sel; - uint32_t guest_page_shift; - /* virtio-bus */ - VirtioBusState bus; - bool format_transport_address; - /* Fields only used for non-legacy (v2) devices */ - uint32_t guest_features[2]; - VirtIOMMIOQueue vqs[VIRTIO_QUEUE_MAX]; -} VirtIOMMIOProxy; - static bool virtio_mmio_ioeventfd_enabled(DeviceState *d) { return kvm_eventfds_enabled(); diff --git a/include/hw/virtio/virtio-mmio.h b/include/hw/virtio/virtio-mmio.h new file mode 100644 index 0000000000..7dbfd03dcf --- /dev/null +++ b/include/hw/virtio/virtio-mmio.h @@ -0,0 +1,73 @@ +/* + * Virtio MMIO bindings + * + * Copyright (c) 2011 Linaro Limited + * + * Author: + * Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#ifndef HW_VIRTIO_MMIO_H +#define HW_VIRTIO_MMIO_H + +#include "hw/virtio/virtio-bus.h" + +/* QOM macros */ +/* virtio-mmio-bus */ +#define TYPE_VIRTIO_MMIO_BUS "virtio-mmio-bus" +#define VIRTIO_MMIO_BUS(obj) \ + OBJECT_CHECK(VirtioBusState, (obj), TYPE_VIRTIO_MMIO_BUS) +#define VIRTIO_MMIO_BUS_GET_CLASS(obj) \ + OBJECT_GET_CLASS(VirtioBusClass, (obj), TYPE_VIRTIO_MMIO_BUS) +#define VIRTIO_MMIO_BUS_CLASS(klass) \ + OBJECT_CLASS_CHECK(VirtioBusClass, (klass), TYPE_VIRTIO_MMIO_BUS) + +/* virtio-mmio */ +#define TYPE_VIRTIO_MMIO "virtio-mmio" +#define VIRTIO_MMIO(obj) \ + OBJECT_CHECK(VirtIOMMIOProxy, (obj), TYPE_VIRTIO_MMIO) + +#define VIRT_MAGIC 0x74726976 /* 'virt' */ +#define VIRT_VERSION 2 +#define VIRT_VERSION_LEGACY 1 +#define VIRT_VENDOR 0x554D4551 /* 'QEMU' */ + +typedef struct VirtIOMMIOQueue { + uint16_t num; + bool enabled; + uint32_t desc[2]; + uint32_t avail[2]; + uint32_t used[2]; +} VirtIOMMIOQueue; + +typedef struct { + /* Generic */ + SysBusDevice parent_obj; + MemoryRegion iomem; + qemu_irq irq; + bool legacy; + /* Guest accessible state needing migration and reset */ + uint32_t host_features_sel; + uint32_t guest_features_sel; + uint32_t guest_page_shift; + /* virtio-bus */ + VirtioBusState bus; + bool format_transport_address; + /* Fields only used for non-legacy (v2) devices */ + uint32_t guest_features[2]; + VirtIOMMIOQueue vqs[VIRTIO_QUEUE_MAX]; +} VirtIOMMIOProxy; + +#endif From 81ef68e44c84377fe66e0b27626f2f562f558a6d Mon Sep 17 00:00:00 2001 From: Sergio Lopez Date: Thu, 26 Sep 2019 15:26:16 +0200 Subject: [PATCH 08/34] hw/i386/pc: rename functions shared with non-PC machines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The following functions are named *pc* but are not PC-machine specific but generic to the X86 architecture, rename them: load_linux -> x86_load_linux pc_new_cpu -> x86_new_cpu pc_cpus_init -> x86_cpus_init pc_cpu_index_to_props -> x86_cpu_index_to_props pc_get_default_cpu_node_id -> x86_get_default_cpu_node_id pc_possible_cpu_arch_ids -> x86_possible_cpu_arch_ids old_pc_system_rom_init -> x86_system_rom_init Signed-off-by: Sergio Lopez Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Reviewed-by: Stefano Garzarella Reviewed-by: Michael S. Tsirkin --- hw/i386/pc.c | 28 ++++++++++++++-------------- hw/i386/pc_piix.c | 2 +- hw/i386/pc_q35.c | 2 +- hw/i386/pc_sysfw.c | 6 +++--- include/hw/i386/pc.h | 2 +- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 4b1904237e..1c27223774 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1019,8 +1019,8 @@ static bool load_elfboot(const char *kernel_filename, return true; } -static void load_linux(PCMachineState *pcms, - FWCfgState *fw_cfg) +static void x86_load_linux(PCMachineState *pcms, + FWCfgState *fw_cfg) { uint16_t protocol; int setup_size, kernel_size, cmdline_size; @@ -1374,7 +1374,7 @@ void pc_acpi_smi_interrupt(void *opaque, int irq, int level) } } -static void pc_new_cpu(PCMachineState *pcms, int64_t apic_id, Error **errp) +static void x86_cpu_new(PCMachineState *pcms, int64_t apic_id, Error **errp) { Object *cpu = NULL; Error *local_err = NULL; @@ -1490,14 +1490,14 @@ void pc_hot_add_cpu(MachineState *ms, const int64_t id, Error **errp) return; } - pc_new_cpu(PC_MACHINE(ms), apic_id, &local_err); + x86_cpu_new(PC_MACHINE(ms), apic_id, &local_err); if (local_err) { error_propagate(errp, local_err); return; } } -void pc_cpus_init(PCMachineState *pcms) +void x86_cpus_init(PCMachineState *pcms) { int i; const CPUArchIdList *possible_cpus; @@ -1518,7 +1518,7 @@ void pc_cpus_init(PCMachineState *pcms) ms->smp.max_cpus - 1) + 1; possible_cpus = mc->possible_cpu_arch_ids(ms); for (i = 0; i < ms->smp.cpus; i++) { - pc_new_cpu(pcms, possible_cpus->cpus[i].arch_id, &error_fatal); + x86_cpu_new(pcms, possible_cpus->cpus[i].arch_id, &error_fatal); } } @@ -1621,7 +1621,7 @@ void xen_load_linux(PCMachineState *pcms) fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, pcms->boot_cpus); rom_set_fw(fw_cfg); - load_linux(pcms, fw_cfg); + x86_load_linux(pcms, fw_cfg); for (i = 0; i < nb_option_roms; i++) { assert(!strcmp(option_rom[i].name, "linuxboot.bin") || !strcmp(option_rom[i].name, "linuxboot_dma.bin") || @@ -1756,7 +1756,7 @@ void pc_memory_init(PCMachineState *pcms, } if (linux_boot) { - load_linux(pcms, fw_cfg); + x86_load_linux(pcms, fw_cfg); } for (i = 0; i < nb_option_roms; i++) { @@ -2681,7 +2681,7 @@ static void pc_machine_wakeup(MachineState *machine) } static CpuInstanceProperties -pc_cpu_index_to_props(MachineState *ms, unsigned cpu_index) +x86_cpu_index_to_props(MachineState *ms, unsigned cpu_index) { MachineClass *mc = MACHINE_GET_CLASS(ms); const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(ms); @@ -2690,7 +2690,7 @@ pc_cpu_index_to_props(MachineState *ms, unsigned cpu_index) return possible_cpus->cpus[cpu_index].props; } -static int64_t pc_get_default_cpu_node_id(const MachineState *ms, int idx) +static int64_t x86_get_default_cpu_node_id(const MachineState *ms, int idx) { X86CPUTopoInfo topo; PCMachineState *pcms = PC_MACHINE(ms); @@ -2702,7 +2702,7 @@ static int64_t pc_get_default_cpu_node_id(const MachineState *ms, int idx) return topo.pkg_id % ms->numa_state->num_nodes; } -static const CPUArchIdList *pc_possible_cpu_arch_ids(MachineState *ms) +static const CPUArchIdList *x86_possible_cpu_arch_ids(MachineState *ms) { PCMachineState *pcms = PC_MACHINE(ms); int i; @@ -2804,9 +2804,9 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) assert(!mc->get_hotplug_handler); mc->get_hotplug_handler = pc_get_hotplug_handler; mc->hotplug_allowed = pc_hotplug_allowed; - mc->cpu_index_to_instance_props = pc_cpu_index_to_props; - mc->get_default_cpu_node_id = pc_get_default_cpu_node_id; - mc->possible_cpu_arch_ids = pc_possible_cpu_arch_ids; + mc->cpu_index_to_instance_props = x86_cpu_index_to_props; + mc->get_default_cpu_node_id = x86_get_default_cpu_node_id; + mc->possible_cpu_arch_ids = x86_possible_cpu_arch_ids; mc->auto_enable_numa_with_memhp = true; mc->has_hotpluggable_cpus = true; mc->default_boot_order = "cad"; diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 6824b72124..de09e076cd 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -152,7 +152,7 @@ static void pc_init1(MachineState *machine, } } - pc_cpus_init(pcms); + x86_cpus_init(pcms); if (kvm_enabled() && pcmc->kvmclock_enabled) { kvmclock_create(); diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 8fad20f314..894989b64e 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -179,7 +179,7 @@ static void pc_q35_init(MachineState *machine) xen_hvm_init(pcms, &ram_memory); } - pc_cpus_init(pcms); + x86_cpus_init(pcms); kvmclock_create(); diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c index a9983f0bfb..28cb1f63c9 100644 --- a/hw/i386/pc_sysfw.c +++ b/hw/i386/pc_sysfw.c @@ -211,7 +211,7 @@ static void pc_system_flash_map(PCMachineState *pcms, } } -static void old_pc_system_rom_init(MemoryRegion *rom_memory, bool isapc_ram_fw) +static void x86_bios_rom_init(MemoryRegion *rom_memory, bool isapc_ram_fw) { char *filename; MemoryRegion *bios, *isa_bios; @@ -272,7 +272,7 @@ void pc_system_firmware_init(PCMachineState *pcms, BlockBackend *pflash_blk[ARRAY_SIZE(pcms->flash)]; if (!pcmc->pci_enabled) { - old_pc_system_rom_init(rom_memory, true); + x86_bios_rom_init(rom_memory, true); return; } @@ -293,7 +293,7 @@ void pc_system_firmware_init(PCMachineState *pcms, if (!pflash_blk[0]) { /* Machine property pflash0 not set, use ROM mode */ - old_pc_system_rom_init(rom_memory, false); + x86_bios_rom_init(rom_memory, false); } else { if (kvm_enabled() && !kvm_readonly_mem_enabled()) { /* diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 37bfd95113..7082b06c51 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -198,7 +198,7 @@ bool pc_machine_is_smm_enabled(PCMachineState *pcms); void pc_register_ferr_irq(qemu_irq irq); void pc_acpi_smi_interrupt(void *opaque, int irq, int level); -void pc_cpus_init(PCMachineState *pcms); +void x86_cpus_init(PCMachineState *pcms); void pc_hot_add_cpu(MachineState *ms, const int64_t id, Error **errp); void pc_smp_parse(MachineState *ms, QemuOpts *opts); From 62a188546ff9dec8020776c04a728ff62d2b4aeb Mon Sep 17 00:00:00 2001 From: Sergio Lopez Date: Thu, 10 Oct 2019 15:45:34 +0200 Subject: [PATCH 09/34] hw/i386/pc: fix code style issues on functions that will be moved out MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix code style issues detected by checkpatch.pl on functions that will be moved out to x86.c. Signed-off-by: Sergio Lopez Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau --- hw/i386/pc.c | 53 ++++++++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 1c27223774..75a97c2157 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -866,7 +866,8 @@ static void handle_a20_line_change(void *opaque, int irq, int level) x86_cpu_set_a20(cpu, level); } -/* Calculates initial APIC ID for a specific CPU index +/* + * Calculates initial APIC ID for a specific CPU index * * Currently we need to be able to calculate the APIC ID from the CPU index * alone (without requiring a CPU object), as the QEMU<->Seabios interfaces have @@ -1039,7 +1040,7 @@ static void x86_load_linux(PCMachineState *pcms, const char *kernel_cmdline = machine->kernel_cmdline; /* Align to 16 bytes as a paranoia measure */ - cmdline_size = (strlen(kernel_cmdline)+16) & ~15; + cmdline_size = (strlen(kernel_cmdline) + 16) & ~15; /* load the kernel header */ f = fopen(kernel_filename, "rb"); @@ -1055,8 +1056,8 @@ static void x86_load_linux(PCMachineState *pcms, #if 0 fprintf(stderr, "header magic: %#x\n", ldl_p(header+0x202)); #endif - if (ldl_p(header+0x202) == 0x53726448) { - protocol = lduw_p(header+0x206); + if (ldl_p(header + 0x202) == 0x53726448) { + protocol = lduw_p(header + 0x206); } else { /* * This could be a multiboot kernel. If it is, let's stop treating it @@ -1158,7 +1159,7 @@ static void x86_load_linux(PCMachineState *pcms, /* highest address for loading the initrd */ if (protocol >= 0x20c && - lduw_p(header+0x236) & XLF_CAN_BE_LOADED_ABOVE_4G) { + lduw_p(header + 0x236) & XLF_CAN_BE_LOADED_ABOVE_4G) { /* * Linux has supported initrd up to 4 GB for a very long time (2007, * long before XLF_CAN_BE_LOADED_ABOVE_4G which was added in 2013), @@ -1177,7 +1178,7 @@ static void x86_load_linux(PCMachineState *pcms, */ initrd_max = UINT32_MAX; } else if (protocol >= 0x203) { - initrd_max = ldl_p(header+0x22c); + initrd_max = ldl_p(header + 0x22c); } else { initrd_max = 0x37ffffff; } @@ -1187,14 +1188,14 @@ static void x86_load_linux(PCMachineState *pcms, } fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_ADDR, cmdline_addr); - fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, strlen(kernel_cmdline)+1); + fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, strlen(kernel_cmdline) + 1); fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline); if (protocol >= 0x202) { - stl_p(header+0x228, cmdline_addr); + stl_p(header + 0x228, cmdline_addr); } else { - stw_p(header+0x20, 0xA33F); - stw_p(header+0x22, cmdline_addr-real_addr); + stw_p(header + 0x20, 0xA33F); + stw_p(header + 0x22, cmdline_addr - real_addr); } /* handle vga= parameter */ @@ -1212,20 +1213,22 @@ static void x86_load_linux(PCMachineState *pcms, } else { video_mode = strtol(vmode, NULL, 0); } - stw_p(header+0x1fa, video_mode); + stw_p(header + 0x1fa, video_mode); } /* loader type */ - /* High nybble = B reserved for QEMU; low nybble is revision number. - If this code is substantially changed, you may want to consider - incrementing the revision. */ + /* + * High nybble = B reserved for QEMU; low nybble is revision number. + * If this code is substantially changed, you may want to consider + * incrementing the revision. + */ if (protocol >= 0x200) { header[0x210] = 0xB0; } /* heap */ if (protocol >= 0x201) { - header[0x211] |= 0x80; /* CAN_USE_HEAP */ - stw_p(header+0x224, cmdline_addr-real_addr-0x200); + header[0x211] |= 0x80; /* CAN_USE_HEAP */ + stw_p(header + 0x224, cmdline_addr - real_addr - 0x200); } /* load initrd */ @@ -1257,14 +1260,14 @@ static void x86_load_linux(PCMachineState *pcms, exit(1); } - initrd_addr = (initrd_max-initrd_size) & ~4095; + initrd_addr = (initrd_max - initrd_size) & ~4095; fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_addr); fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size); fw_cfg_add_bytes(fw_cfg, FW_CFG_INITRD_DATA, initrd_data, initrd_size); - stl_p(header+0x218, initrd_addr); - stl_p(header+0x21c, initrd_size); + stl_p(header + 0x218, initrd_addr); + stl_p(header + 0x21c, initrd_size); } /* load kernel and setup */ @@ -1272,7 +1275,7 @@ static void x86_load_linux(PCMachineState *pcms, if (setup_size == 0) { setup_size = 4; } - setup_size = (setup_size+1)*512; + setup_size = (setup_size + 1) * 512; if (setup_size > kernel_size) { fprintf(stderr, "qemu: invalid kernel header\n"); exit(1); @@ -1310,7 +1313,7 @@ static void x86_load_linux(PCMachineState *pcms, kernel_size = setup_data_offset + sizeof(struct setup_data) + dtb_size; kernel = g_realloc(kernel, kernel_size); - stq_p(header+0x250, prot_addr + setup_data_offset); + stq_p(header + 0x250, prot_addr + setup_data_offset); setup_data = (struct setup_data *)(kernel + setup_data_offset); setup_data->next = 0; @@ -1507,7 +1510,8 @@ void x86_cpus_init(PCMachineState *pcms) x86_cpu_set_default_version(pcmc->default_cpu_version); - /* Calculates the limit to CPU APIC ID values + /* + * Calculates the limit to CPU APIC ID values * * Limit for the APIC ID value, so that all * CPU APIC IDs are < pcms->apic_id_limit. @@ -2712,7 +2716,7 @@ static const CPUArchIdList *x86_possible_cpu_arch_ids(MachineState *ms) /* * make sure that max_cpus hasn't changed since the first use, i.e. * -smp hasn't been parsed after it - */ + */ assert(ms->possible_cpus->len == max_cpus); return ms->possible_cpus; } @@ -2725,7 +2729,8 @@ static const CPUArchIdList *x86_possible_cpu_arch_ids(MachineState *ms) ms->possible_cpus->cpus[i].type = ms->cpu_type; ms->possible_cpus->cpus[i].vcpus_count = 1; - ms->possible_cpus->cpus[i].arch_id = x86_cpu_apic_id_from_index(pcms, i); + ms->possible_cpus->cpus[i].arch_id = + x86_cpu_apic_id_from_index(pcms, i); x86_topo_ids_from_apicid(ms->possible_cpus->cpus[i].arch_id, pcms->smp_dies, ms->smp.cores, ms->smp.threads, &topo); From 133ef074bd88957c0ed5b38d4489111889acf915 Mon Sep 17 00:00:00 2001 From: Sergio Lopez Date: Thu, 10 Oct 2019 15:49:39 +0200 Subject: [PATCH 10/34] hw/i386/pc: replace use of strtol with qemu_strtoui in x86_load_linux() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow checkpatch.pl recommendation and replace the use of strtol with qemu_strtoui in x86_load_linux(). Signed-off-by: Sergio Lopez Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau --- hw/i386/pc.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 75a97c2157..b5b660f941 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -68,6 +68,7 @@ #include "qemu/config-file.h" #include "qemu/error-report.h" #include "qemu/option.h" +#include "qemu/cutils.h" #include "hw/acpi/acpi.h" #include "hw/acpi/cpu_hotplug.h" #include "hw/boards.h" @@ -1202,6 +1203,7 @@ static void x86_load_linux(PCMachineState *pcms, vmode = strstr(kernel_cmdline, "vga="); if (vmode) { unsigned int video_mode; + int ret; /* skip "vga=" */ vmode += 4; if (!strncmp(vmode, "normal", 6)) { @@ -1211,7 +1213,12 @@ static void x86_load_linux(PCMachineState *pcms, } else if (!strncmp(vmode, "ask", 3)) { video_mode = 0xfffd; } else { - video_mode = strtol(vmode, NULL, 0); + ret = qemu_strtoui(vmode, NULL, 0, &video_mode); + if (ret != 0) { + fprintf(stderr, "qemu: can't parse 'vga' parameter: %s\n", + strerror(-ret)); + exit(1); + } } stw_p(header + 0x1fa, video_mode); } From 350f5233d755cb00e62ccadc7635435ac3238aa6 Mon Sep 17 00:00:00 2001 From: Sergio Lopez Date: Thu, 10 Oct 2019 15:57:17 +0200 Subject: [PATCH 11/34] hw/i386/pc: avoid an assignment in if condition in x86_load_linux() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow checkpatch.pl recommendation and avoid an assignment in if condition in x86_load_linux(). Signed-off-by: Sergio Lopez Reviewed-by: Philippe Mathieu-Daudé --- hw/i386/pc.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index b5b660f941..77198d5f71 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1045,7 +1045,14 @@ static void x86_load_linux(PCMachineState *pcms, /* load the kernel header */ f = fopen(kernel_filename, "rb"); - if (!f || !(kernel_size = get_file_size(f)) || + if (!f) { + fprintf(stderr, "qemu: could not open kernel file '%s': %s\n", + kernel_filename, strerror(errno)); + exit(1); + } + + kernel_size = get_file_size(f); + if (!kernel_size || fread(header, 1, MIN(ARRAY_SIZE(header), kernel_size), f) != MIN(ARRAY_SIZE(header), kernel_size)) { fprintf(stderr, "qemu: could not load kernel '%s': %s\n", From 86a9a7916b6b69e429f078dd9c89c2907ff43308 Mon Sep 17 00:00:00 2001 From: Sergio Lopez Date: Thu, 10 Oct 2019 16:00:43 +0200 Subject: [PATCH 12/34] hw/i386/pc: remove commented out code from x86_load_linux() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow checkpatch.pl recommendation and remove commented out code from x86_load_linux(). Signed-off-by: Sergio Lopez Reviewed-by: Philippe Mathieu-Daudé --- hw/i386/pc.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 77198d5f71..903bc05f8f 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1061,9 +1061,6 @@ static void x86_load_linux(PCMachineState *pcms, } /* kernel protocol version */ -#if 0 - fprintf(stderr, "header magic: %#x\n", ldl_p(header+0x202)); -#endif if (ldl_p(header + 0x202) == 0x53726448) { protocol = lduw_p(header + 0x206); } else { @@ -1155,16 +1152,6 @@ static void x86_load_linux(PCMachineState *pcms, prot_addr = 0x100000; } -#if 0 - fprintf(stderr, - "qemu: real_addr = 0x" TARGET_FMT_plx "\n" - "qemu: cmdline_addr = 0x" TARGET_FMT_plx "\n" - "qemu: prot_addr = 0x" TARGET_FMT_plx "\n", - real_addr, - cmdline_addr, - prot_addr); -#endif - /* highest address for loading the initrd */ if (protocol >= 0x20c && lduw_p(header + 0x236) & XLF_CAN_BE_LOADED_ABOVE_4G) { From 549e984e67d8b3ea868be4ba935cecb9c1e753dc Mon Sep 17 00:00:00 2001 From: Sergio Lopez Date: Tue, 8 Oct 2019 11:56:49 +0200 Subject: [PATCH 13/34] hw/i386/pc: move shared x86 functions to x86.c and export them MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move x86 functions that will be shared between PC and non-PC machine types to x86.c, along with their helpers. Signed-off-by: Sergio Lopez Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Reviewed-by: Stefano Garzarella Reviewed-by: Michael S. Tsirkin --- hw/i386/Makefile.objs | 1 + hw/i386/pc.c | 587 +---------------------------------- hw/i386/pc_piix.c | 1 + hw/i386/pc_q35.c | 1 + hw/i386/pc_sysfw.c | 56 +--- hw/i386/x86.c | 690 ++++++++++++++++++++++++++++++++++++++++++ include/hw/i386/pc.h | 1 - include/hw/i386/x86.h | 35 +++ 8 files changed, 730 insertions(+), 642 deletions(-) create mode 100644 hw/i386/x86.c create mode 100644 include/hw/i386/x86.h diff --git a/hw/i386/Makefile.objs b/hw/i386/Makefile.objs index d3374e0831..7ed80a4853 100644 --- a/hw/i386/Makefile.objs +++ b/hw/i386/Makefile.objs @@ -1,5 +1,6 @@ obj-$(CONFIG_KVM) += kvm/ obj-y += e820_memory_layout.o multiboot.o +obj-y += x86.o obj-y += pc.o obj-$(CONFIG_I440FX) += pc_piix.o obj-$(CONFIG_Q35) += pc_q35.o diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 903bc05f8f..4a1bc95ccb 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -24,6 +24,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" +#include "hw/i386/x86.h" #include "hw/i386/pc.h" #include "hw/char/serial.h" #include "hw/char/parallel.h" @@ -103,9 +104,6 @@ struct hpet_fw_config hpet_cfg = {.count = UINT8_MAX}; -/* Physical Address of PVH entry point read from kernel ELF NOTE */ -static size_t pvh_start_addr; - GlobalProperty pc_compat_4_1[] = {}; const size_t pc_compat_4_1_len = G_N_ELEMENTS(pc_compat_4_1); @@ -867,481 +865,6 @@ static void handle_a20_line_change(void *opaque, int irq, int level) x86_cpu_set_a20(cpu, level); } -/* - * Calculates initial APIC ID for a specific CPU index - * - * Currently we need to be able to calculate the APIC ID from the CPU index - * alone (without requiring a CPU object), as the QEMU<->Seabios interfaces have - * no concept of "CPU index", and the NUMA tables on fw_cfg need the APIC ID of - * all CPUs up to max_cpus. - */ -static uint32_t x86_cpu_apic_id_from_index(PCMachineState *pcms, - unsigned int cpu_index) -{ - MachineState *ms = MACHINE(pcms); - PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); - uint32_t correct_id; - static bool warned; - - correct_id = x86_apicid_from_cpu_idx(pcms->smp_dies, ms->smp.cores, - ms->smp.threads, cpu_index); - if (pcmc->compat_apic_id_mode) { - if (cpu_index != correct_id && !warned && !qtest_enabled()) { - error_report("APIC IDs set in compatibility mode, " - "CPU topology won't match the configuration"); - warned = true; - } - return cpu_index; - } else { - return correct_id; - } -} - -static long get_file_size(FILE *f) -{ - long where, size; - - /* XXX: on Unix systems, using fstat() probably makes more sense */ - - where = ftell(f); - fseek(f, 0, SEEK_END); - size = ftell(f); - fseek(f, where, SEEK_SET); - - return size; -} - -struct setup_data { - uint64_t next; - uint32_t type; - uint32_t len; - uint8_t data[0]; -} __attribute__((packed)); - - -/* - * The entry point into the kernel for PVH boot is different from - * the native entry point. The PVH entry is defined by the x86/HVM - * direct boot ABI and is available in an ELFNOTE in the kernel binary. - * - * This function is passed to load_elf() when it is called from - * load_elfboot() which then additionally checks for an ELF Note of - * type XEN_ELFNOTE_PHYS32_ENTRY and passes it to this function to - * parse the PVH entry address from the ELF Note. - * - * Due to trickery in elf_opts.h, load_elf() is actually available as - * load_elf32() or load_elf64() and this routine needs to be able - * to deal with being called as 32 or 64 bit. - * - * The address of the PVH entry point is saved to the 'pvh_start_addr' - * global variable. (although the entry point is 32-bit, the kernel - * binary can be either 32-bit or 64-bit). - */ -static uint64_t read_pvh_start_addr(void *arg1, void *arg2, bool is64) -{ - size_t *elf_note_data_addr; - - /* Check if ELF Note header passed in is valid */ - if (arg1 == NULL) { - return 0; - } - - if (is64) { - struct elf64_note *nhdr64 = (struct elf64_note *)arg1; - uint64_t nhdr_size64 = sizeof(struct elf64_note); - uint64_t phdr_align = *(uint64_t *)arg2; - uint64_t nhdr_namesz = nhdr64->n_namesz; - - elf_note_data_addr = - ((void *)nhdr64) + nhdr_size64 + - QEMU_ALIGN_UP(nhdr_namesz, phdr_align); - } else { - struct elf32_note *nhdr32 = (struct elf32_note *)arg1; - uint32_t nhdr_size32 = sizeof(struct elf32_note); - uint32_t phdr_align = *(uint32_t *)arg2; - uint32_t nhdr_namesz = nhdr32->n_namesz; - - elf_note_data_addr = - ((void *)nhdr32) + nhdr_size32 + - QEMU_ALIGN_UP(nhdr_namesz, phdr_align); - } - - pvh_start_addr = *elf_note_data_addr; - - return pvh_start_addr; -} - -static bool load_elfboot(const char *kernel_filename, - int kernel_file_size, - uint8_t *header, - size_t pvh_xen_start_addr, - FWCfgState *fw_cfg) -{ - uint32_t flags = 0; - uint32_t mh_load_addr = 0; - uint32_t elf_kernel_size = 0; - uint64_t elf_entry; - uint64_t elf_low, elf_high; - int kernel_size; - - if (ldl_p(header) != 0x464c457f) { - return false; /* no elfboot */ - } - - bool elf_is64 = header[EI_CLASS] == ELFCLASS64; - flags = elf_is64 ? - ((Elf64_Ehdr *)header)->e_flags : ((Elf32_Ehdr *)header)->e_flags; - - if (flags & 0x00010004) { /* LOAD_ELF_HEADER_HAS_ADDR */ - error_report("elfboot unsupported flags = %x", flags); - exit(1); - } - - uint64_t elf_note_type = XEN_ELFNOTE_PHYS32_ENTRY; - kernel_size = load_elf(kernel_filename, read_pvh_start_addr, - NULL, &elf_note_type, &elf_entry, - &elf_low, &elf_high, 0, I386_ELF_MACHINE, - 0, 0); - - if (kernel_size < 0) { - error_report("Error while loading elf kernel"); - exit(1); - } - mh_load_addr = elf_low; - elf_kernel_size = elf_high - elf_low; - - if (pvh_start_addr == 0) { - error_report("Error loading uncompressed kernel without PVH ELF Note"); - exit(1); - } - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ENTRY, pvh_start_addr); - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, mh_load_addr); - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, elf_kernel_size); - - return true; -} - -static void x86_load_linux(PCMachineState *pcms, - FWCfgState *fw_cfg) -{ - uint16_t protocol; - int setup_size, kernel_size, cmdline_size; - int dtb_size, setup_data_offset; - uint32_t initrd_max; - uint8_t header[8192], *setup, *kernel; - hwaddr real_addr, prot_addr, cmdline_addr, initrd_addr = 0; - FILE *f; - char *vmode; - MachineState *machine = MACHINE(pcms); - PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); - struct setup_data *setup_data; - const char *kernel_filename = machine->kernel_filename; - const char *initrd_filename = machine->initrd_filename; - const char *dtb_filename = machine->dtb; - const char *kernel_cmdline = machine->kernel_cmdline; - - /* Align to 16 bytes as a paranoia measure */ - cmdline_size = (strlen(kernel_cmdline) + 16) & ~15; - - /* load the kernel header */ - f = fopen(kernel_filename, "rb"); - if (!f) { - fprintf(stderr, "qemu: could not open kernel file '%s': %s\n", - kernel_filename, strerror(errno)); - exit(1); - } - - kernel_size = get_file_size(f); - if (!kernel_size || - fread(header, 1, MIN(ARRAY_SIZE(header), kernel_size), f) != - MIN(ARRAY_SIZE(header), kernel_size)) { - fprintf(stderr, "qemu: could not load kernel '%s': %s\n", - kernel_filename, strerror(errno)); - exit(1); - } - - /* kernel protocol version */ - if (ldl_p(header + 0x202) == 0x53726448) { - protocol = lduw_p(header + 0x206); - } else { - /* - * This could be a multiboot kernel. If it is, let's stop treating it - * like a Linux kernel. - * Note: some multiboot images could be in the ELF format (the same of - * PVH), so we try multiboot first since we check the multiboot magic - * header before to load it. - */ - if (load_multiboot(fw_cfg, f, kernel_filename, initrd_filename, - kernel_cmdline, kernel_size, header)) { - return; - } - /* - * Check if the file is an uncompressed kernel file (ELF) and load it, - * saving the PVH entry point used by the x86/HVM direct boot ABI. - * If load_elfboot() is successful, populate the fw_cfg info. - */ - if (pcmc->pvh_enabled && - load_elfboot(kernel_filename, kernel_size, - header, pvh_start_addr, fw_cfg)) { - fclose(f); - - fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, - strlen(kernel_cmdline) + 1); - fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline); - - fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_SIZE, sizeof(header)); - fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA, - header, sizeof(header)); - - /* load initrd */ - if (initrd_filename) { - GMappedFile *mapped_file; - gsize initrd_size; - gchar *initrd_data; - GError *gerr = NULL; - - mapped_file = g_mapped_file_new(initrd_filename, false, &gerr); - if (!mapped_file) { - fprintf(stderr, "qemu: error reading initrd %s: %s\n", - initrd_filename, gerr->message); - exit(1); - } - pcms->initrd_mapped_file = mapped_file; - - initrd_data = g_mapped_file_get_contents(mapped_file); - initrd_size = g_mapped_file_get_length(mapped_file); - initrd_max = pcms->below_4g_mem_size - pcmc->acpi_data_size - 1; - if (initrd_size >= initrd_max) { - fprintf(stderr, "qemu: initrd is too large, cannot support." - "(max: %"PRIu32", need %"PRId64")\n", - initrd_max, (uint64_t)initrd_size); - exit(1); - } - - initrd_addr = (initrd_max - initrd_size) & ~4095; - - fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_addr); - fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size); - fw_cfg_add_bytes(fw_cfg, FW_CFG_INITRD_DATA, initrd_data, - initrd_size); - } - - option_rom[nb_option_roms].bootindex = 0; - option_rom[nb_option_roms].name = "pvh.bin"; - nb_option_roms++; - - return; - } - protocol = 0; - } - - if (protocol < 0x200 || !(header[0x211] & 0x01)) { - /* Low kernel */ - real_addr = 0x90000; - cmdline_addr = 0x9a000 - cmdline_size; - prot_addr = 0x10000; - } else if (protocol < 0x202) { - /* High but ancient kernel */ - real_addr = 0x90000; - cmdline_addr = 0x9a000 - cmdline_size; - prot_addr = 0x100000; - } else { - /* High and recent kernel */ - real_addr = 0x10000; - cmdline_addr = 0x20000; - prot_addr = 0x100000; - } - - /* highest address for loading the initrd */ - if (protocol >= 0x20c && - lduw_p(header + 0x236) & XLF_CAN_BE_LOADED_ABOVE_4G) { - /* - * Linux has supported initrd up to 4 GB for a very long time (2007, - * long before XLF_CAN_BE_LOADED_ABOVE_4G which was added in 2013), - * though it only sets initrd_max to 2 GB to "work around bootloader - * bugs". Luckily, QEMU firmware(which does something like bootloader) - * has supported this. - * - * It's believed that if XLF_CAN_BE_LOADED_ABOVE_4G is set, initrd can - * be loaded into any address. - * - * In addition, initrd_max is uint32_t simply because QEMU doesn't - * support the 64-bit boot protocol (specifically the ext_ramdisk_image - * field). - * - * Therefore here just limit initrd_max to UINT32_MAX simply as well. - */ - initrd_max = UINT32_MAX; - } else if (protocol >= 0x203) { - initrd_max = ldl_p(header + 0x22c); - } else { - initrd_max = 0x37ffffff; - } - - 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); - fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, strlen(kernel_cmdline) + 1); - fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline); - - if (protocol >= 0x202) { - stl_p(header + 0x228, cmdline_addr); - } else { - stw_p(header + 0x20, 0xA33F); - stw_p(header + 0x22, cmdline_addr - real_addr); - } - - /* handle vga= parameter */ - vmode = strstr(kernel_cmdline, "vga="); - if (vmode) { - unsigned int video_mode; - int ret; - /* skip "vga=" */ - vmode += 4; - if (!strncmp(vmode, "normal", 6)) { - video_mode = 0xffff; - } else if (!strncmp(vmode, "ext", 3)) { - video_mode = 0xfffe; - } else if (!strncmp(vmode, "ask", 3)) { - video_mode = 0xfffd; - } else { - ret = qemu_strtoui(vmode, NULL, 0, &video_mode); - if (ret != 0) { - fprintf(stderr, "qemu: can't parse 'vga' parameter: %s\n", - strerror(-ret)); - exit(1); - } - } - stw_p(header + 0x1fa, video_mode); - } - - /* loader type */ - /* - * High nybble = B reserved for QEMU; low nybble is revision number. - * If this code is substantially changed, you may want to consider - * incrementing the revision. - */ - if (protocol >= 0x200) { - header[0x210] = 0xB0; - } - /* heap */ - if (protocol >= 0x201) { - header[0x211] |= 0x80; /* CAN_USE_HEAP */ - stw_p(header + 0x224, cmdline_addr - real_addr - 0x200); - } - - /* load initrd */ - if (initrd_filename) { - GMappedFile *mapped_file; - gsize initrd_size; - gchar *initrd_data; - GError *gerr = NULL; - - if (protocol < 0x200) { - fprintf(stderr, "qemu: linux kernel too old to load a ram disk\n"); - exit(1); - } - - mapped_file = g_mapped_file_new(initrd_filename, false, &gerr); - if (!mapped_file) { - fprintf(stderr, "qemu: error reading initrd %s: %s\n", - initrd_filename, gerr->message); - exit(1); - } - pcms->initrd_mapped_file = mapped_file; - - initrd_data = g_mapped_file_get_contents(mapped_file); - initrd_size = g_mapped_file_get_length(mapped_file); - if (initrd_size >= initrd_max) { - fprintf(stderr, "qemu: initrd is too large, cannot support." - "(max: %"PRIu32", need %"PRId64")\n", - initrd_max, (uint64_t)initrd_size); - exit(1); - } - - initrd_addr = (initrd_max - initrd_size) & ~4095; - - fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_addr); - fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size); - fw_cfg_add_bytes(fw_cfg, FW_CFG_INITRD_DATA, initrd_data, initrd_size); - - stl_p(header + 0x218, initrd_addr); - stl_p(header + 0x21c, initrd_size); - } - - /* load kernel and setup */ - setup_size = header[0x1f1]; - if (setup_size == 0) { - setup_size = 4; - } - setup_size = (setup_size + 1) * 512; - if (setup_size > kernel_size) { - fprintf(stderr, "qemu: invalid kernel header\n"); - exit(1); - } - kernel_size -= setup_size; - - setup = g_malloc(setup_size); - kernel = g_malloc(kernel_size); - fseek(f, 0, SEEK_SET); - if (fread(setup, 1, setup_size, f) != setup_size) { - fprintf(stderr, "fread() failed\n"); - exit(1); - } - if (fread(kernel, 1, kernel_size, f) != kernel_size) { - fprintf(stderr, "fread() failed\n"); - exit(1); - } - fclose(f); - - /* append dtb to kernel */ - if (dtb_filename) { - if (protocol < 0x209) { - fprintf(stderr, "qemu: Linux kernel too old to load a dtb\n"); - exit(1); - } - - dtb_size = get_image_size(dtb_filename); - if (dtb_size <= 0) { - fprintf(stderr, "qemu: error reading dtb %s: %s\n", - dtb_filename, strerror(errno)); - exit(1); - } - - setup_data_offset = QEMU_ALIGN_UP(kernel_size, 16); - kernel_size = setup_data_offset + sizeof(struct setup_data) + dtb_size; - kernel = g_realloc(kernel, kernel_size); - - stq_p(header + 0x250, prot_addr + setup_data_offset); - - setup_data = (struct setup_data *)(kernel + setup_data_offset); - setup_data->next = 0; - setup_data->type = cpu_to_le32(SETUP_DTB); - setup_data->len = cpu_to_le32(dtb_size); - - load_image_size(dtb_filename, setup_data->data, dtb_size); - } - - memcpy(setup, header, MIN(sizeof(header), setup_size)); - - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, prot_addr); - fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size); - fw_cfg_add_bytes(fw_cfg, FW_CFG_KERNEL_DATA, kernel, kernel_size); - - fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_ADDR, real_addr); - fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_SIZE, setup_size); - fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA, setup, setup_size); - - option_rom[nb_option_roms].bootindex = 0; - option_rom[nb_option_roms].name = "linuxboot.bin"; - if (pcmc->linuxboot_dma_enabled && fw_cfg_dma_enabled(fw_cfg)) { - option_rom[nb_option_roms].name = "linuxboot_dma.bin"; - } - nb_option_roms++; -} - #define NE2000_NB_MAX 6 static const int ne2000_io[NE2000_NB_MAX] = { 0x300, 0x320, 0x340, 0x360, @@ -1378,24 +901,6 @@ void pc_acpi_smi_interrupt(void *opaque, int irq, int level) } } -static void x86_cpu_new(PCMachineState *pcms, int64_t apic_id, Error **errp) -{ - Object *cpu = NULL; - Error *local_err = NULL; - CPUX86State *env = NULL; - - cpu = object_new(MACHINE(pcms)->cpu_type); - - env = &X86_CPU(cpu)->env; - env->nr_dies = pcms->smp_dies; - - object_property_set_uint(cpu, apic_id, "apic-id", &local_err); - object_property_set_bool(cpu, true, "realized", &local_err); - - object_unref(cpu); - error_propagate(errp, local_err); -} - /* * This function is very similar to smp_parse() * in hw/core/machine.c but includes CPU die support. @@ -1501,32 +1006,6 @@ void pc_hot_add_cpu(MachineState *ms, const int64_t id, Error **errp) } } -void x86_cpus_init(PCMachineState *pcms) -{ - int i; - const CPUArchIdList *possible_cpus; - MachineState *ms = MACHINE(pcms); - MachineClass *mc = MACHINE_GET_CLASS(pcms); - PCMachineClass *pcmc = PC_MACHINE_CLASS(mc); - - x86_cpu_set_default_version(pcmc->default_cpu_version); - - /* - * Calculates the limit to CPU APIC ID values - * - * Limit for the APIC ID value, so that all - * CPU APIC IDs are < pcms->apic_id_limit. - * - * This is used for FW_CFG_MAX_CPUS. See comments on fw_cfg_arch_create(). - */ - pcms->apic_id_limit = x86_cpu_apic_id_from_index(pcms, - ms->smp.max_cpus - 1) + 1; - possible_cpus = mc->possible_cpu_arch_ids(ms); - for (i = 0; i < ms->smp.cpus; i++) { - x86_cpu_new(pcms, possible_cpus->cpus[i].arch_id, &error_fatal); - } -} - static void rtc_set_cpus_count(ISADevice *rtc, uint16_t cpus_count) { if (cpus_count > 0xff) { @@ -2685,70 +2164,6 @@ static void pc_machine_wakeup(MachineState *machine) cpu_synchronize_all_post_reset(); } -static CpuInstanceProperties -x86_cpu_index_to_props(MachineState *ms, unsigned cpu_index) -{ - MachineClass *mc = MACHINE_GET_CLASS(ms); - const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(ms); - - assert(cpu_index < possible_cpus->len); - return possible_cpus->cpus[cpu_index].props; -} - -static int64_t x86_get_default_cpu_node_id(const MachineState *ms, int idx) -{ - X86CPUTopoInfo topo; - PCMachineState *pcms = PC_MACHINE(ms); - - assert(idx < ms->possible_cpus->len); - x86_topo_ids_from_apicid(ms->possible_cpus->cpus[idx].arch_id, - pcms->smp_dies, ms->smp.cores, - ms->smp.threads, &topo); - return topo.pkg_id % ms->numa_state->num_nodes; -} - -static const CPUArchIdList *x86_possible_cpu_arch_ids(MachineState *ms) -{ - PCMachineState *pcms = PC_MACHINE(ms); - int i; - unsigned int max_cpus = ms->smp.max_cpus; - - if (ms->possible_cpus) { - /* - * make sure that max_cpus hasn't changed since the first use, i.e. - * -smp hasn't been parsed after it - */ - assert(ms->possible_cpus->len == max_cpus); - return ms->possible_cpus; - } - - ms->possible_cpus = g_malloc0(sizeof(CPUArchIdList) + - sizeof(CPUArchId) * max_cpus); - ms->possible_cpus->len = max_cpus; - for (i = 0; i < ms->possible_cpus->len; i++) { - X86CPUTopoInfo topo; - - ms->possible_cpus->cpus[i].type = ms->cpu_type; - ms->possible_cpus->cpus[i].vcpus_count = 1; - ms->possible_cpus->cpus[i].arch_id = - x86_cpu_apic_id_from_index(pcms, i); - x86_topo_ids_from_apicid(ms->possible_cpus->cpus[i].arch_id, - pcms->smp_dies, ms->smp.cores, - ms->smp.threads, &topo); - ms->possible_cpus->cpus[i].props.has_socket_id = true; - ms->possible_cpus->cpus[i].props.socket_id = topo.pkg_id; - if (pcms->smp_dies > 1) { - ms->possible_cpus->cpus[i].props.has_die_id = true; - ms->possible_cpus->cpus[i].props.die_id = topo.die_id; - } - ms->possible_cpus->cpus[i].props.has_core_id = true; - ms->possible_cpus->cpus[i].props.core_id = topo.core_id; - ms->possible_cpus->cpus[i].props.has_thread_id = true; - ms->possible_cpus->cpus[i].props.thread_id = topo.smt_id; - } - return ms->possible_cpus; -} - static void x86_nmi(NMIState *n, int cpu_index, Error **errp) { /* cpu index isn't used */ diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index de09e076cd..1396451abf 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -27,6 +27,7 @@ #include "qemu/units.h" #include "hw/loader.h" +#include "hw/i386/x86.h" #include "hw/i386/pc.h" #include "hw/i386/apic.h" #include "hw/display/ramfb.h" diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 894989b64e..8920bd8978 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -41,6 +41,7 @@ #include "hw/pci-host/q35.h" #include "hw/qdev-properties.h" #include "exec/address-spaces.h" +#include "hw/i386/x86.h" #include "hw/i386/pc.h" #include "hw/i386/ich9.h" #include "hw/i386/amd_iommu.h" diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c index 28cb1f63c9..f5f3f466b0 100644 --- a/hw/i386/pc_sysfw.c +++ b/hw/i386/pc_sysfw.c @@ -31,6 +31,7 @@ #include "qemu/option.h" #include "qemu/units.h" #include "hw/sysbus.h" +#include "hw/i386/x86.h" #include "hw/i386/pc.h" #include "hw/loader.h" #include "hw/qdev-properties.h" @@ -38,8 +39,6 @@ #include "hw/block/flash.h" #include "sysemu/kvm.h" -#define BIOS_FILENAME "bios.bin" - /* * We don't have a theoretically justifiable exact lower bound on the base * address of any flash mapping. In practice, the IO-APIC MMIO range is @@ -211,59 +210,6 @@ static void pc_system_flash_map(PCMachineState *pcms, } } -static void x86_bios_rom_init(MemoryRegion *rom_memory, bool isapc_ram_fw) -{ - char *filename; - MemoryRegion *bios, *isa_bios; - int bios_size, isa_bios_size; - int ret; - - /* BIOS load */ - if (bios_name == NULL) { - bios_name = BIOS_FILENAME; - } - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - if (filename) { - bios_size = get_image_size(filename); - } else { - bios_size = -1; - } - if (bios_size <= 0 || - (bios_size % 65536) != 0) { - goto bios_error; - } - bios = g_malloc(sizeof(*bios)); - memory_region_init_ram(bios, NULL, "pc.bios", bios_size, &error_fatal); - if (!isapc_ram_fw) { - memory_region_set_readonly(bios, true); - } - ret = rom_add_file_fixed(bios_name, (uint32_t)(-bios_size), -1); - if (ret != 0) { - bios_error: - fprintf(stderr, "qemu: could not load PC BIOS '%s'\n", bios_name); - exit(1); - } - g_free(filename); - - /* map the last 128KB of the BIOS in ISA space */ - isa_bios_size = MIN(bios_size, 128 * KiB); - isa_bios = g_malloc(sizeof(*isa_bios)); - memory_region_init_alias(isa_bios, NULL, "isa-bios", bios, - bios_size - isa_bios_size, isa_bios_size); - memory_region_add_subregion_overlap(rom_memory, - 0x100000 - isa_bios_size, - isa_bios, - 1); - if (!isapc_ram_fw) { - memory_region_set_readonly(isa_bios, true); - } - - /* map all the bios at the top of memory */ - memory_region_add_subregion(rom_memory, - (uint32_t)(-bios_size), - bios); -} - void pc_system_firmware_init(PCMachineState *pcms, MemoryRegion *rom_memory) { diff --git a/hw/i386/x86.c b/hw/i386/x86.c new file mode 100644 index 0000000000..71fb093983 --- /dev/null +++ b/hw/i386/x86.c @@ -0,0 +1,690 @@ +/* + * Copyright (c) 2003-2004 Fabrice Bellard + * Copyright (c) 2019 Red Hat, Inc. + * + * 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 "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qemu/option.h" +#include "qemu/cutils.h" +#include "qemu/units.h" +#include "qemu-common.h" +#include "qapi/error.h" +#include "qapi/qmp/qerror.h" +#include "qapi/qapi-visit-common.h" +#include "qapi/visitor.h" +#include "sysemu/qtest.h" +#include "sysemu/numa.h" +#include "sysemu/replay.h" +#include "sysemu/sysemu.h" + +#include "hw/i386/x86.h" +#include "hw/i386/pc.h" +#include "target/i386/cpu.h" +#include "hw/i386/topology.h" +#include "hw/i386/fw_cfg.h" + +#include "hw/acpi/cpu_hotplug.h" +#include "hw/nmi.h" +#include "hw/loader.h" +#include "multiboot.h" +#include "elf.h" +#include "standard-headers/asm-x86/bootparam.h" + +#define BIOS_FILENAME "bios.bin" + +/* Physical Address of PVH entry point read from kernel ELF NOTE */ +static size_t pvh_start_addr; + +/* + * Calculates initial APIC ID for a specific CPU index + * + * Currently we need to be able to calculate the APIC ID from the CPU index + * alone (without requiring a CPU object), as the QEMU<->Seabios interfaces have + * no concept of "CPU index", and the NUMA tables on fw_cfg need the APIC ID of + * all CPUs up to max_cpus. + */ +uint32_t x86_cpu_apic_id_from_index(PCMachineState *pcms, + unsigned int cpu_index) +{ + MachineState *ms = MACHINE(pcms); + PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); + uint32_t correct_id; + static bool warned; + + correct_id = x86_apicid_from_cpu_idx(pcms->smp_dies, ms->smp.cores, + ms->smp.threads, cpu_index); + if (pcmc->compat_apic_id_mode) { + if (cpu_index != correct_id && !warned && !qtest_enabled()) { + error_report("APIC IDs set in compatibility mode, " + "CPU topology won't match the configuration"); + warned = true; + } + return cpu_index; + } else { + return correct_id; + } +} + +void x86_cpu_new(PCMachineState *pcms, int64_t apic_id, Error **errp) +{ + Object *cpu = NULL; + Error *local_err = NULL; + CPUX86State *env = NULL; + + cpu = object_new(MACHINE(pcms)->cpu_type); + + env = &X86_CPU(cpu)->env; + env->nr_dies = pcms->smp_dies; + + object_property_set_uint(cpu, apic_id, "apic-id", &local_err); + object_property_set_bool(cpu, true, "realized", &local_err); + + object_unref(cpu); + error_propagate(errp, local_err); +} + +void x86_cpus_init(PCMachineState *pcms) +{ + int i; + const CPUArchIdList *possible_cpus; + MachineState *ms = MACHINE(pcms); + MachineClass *mc = MACHINE_GET_CLASS(pcms); + PCMachineClass *pcmc = PC_MACHINE_CLASS(mc); + + x86_cpu_set_default_version(pcmc->default_cpu_version); + + /* + * Calculates the limit to CPU APIC ID values + * + * Limit for the APIC ID value, so that all + * CPU APIC IDs are < pcms->apic_id_limit. + * + * This is used for FW_CFG_MAX_CPUS. See comments on fw_cfg_arch_create(). + */ + pcms->apic_id_limit = x86_cpu_apic_id_from_index(pcms, + ms->smp.max_cpus - 1) + 1; + possible_cpus = mc->possible_cpu_arch_ids(ms); + for (i = 0; i < ms->smp.cpus; i++) { + x86_cpu_new(pcms, possible_cpus->cpus[i].arch_id, &error_fatal); + } +} + +CpuInstanceProperties +x86_cpu_index_to_props(MachineState *ms, unsigned cpu_index) +{ + MachineClass *mc = MACHINE_GET_CLASS(ms); + const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(ms); + + assert(cpu_index < possible_cpus->len); + return possible_cpus->cpus[cpu_index].props; +} + +int64_t x86_get_default_cpu_node_id(const MachineState *ms, int idx) +{ + X86CPUTopoInfo topo; + PCMachineState *pcms = PC_MACHINE(ms); + + assert(idx < ms->possible_cpus->len); + x86_topo_ids_from_apicid(ms->possible_cpus->cpus[idx].arch_id, + pcms->smp_dies, ms->smp.cores, + ms->smp.threads, &topo); + return topo.pkg_id % ms->numa_state->num_nodes; +} + +const CPUArchIdList *x86_possible_cpu_arch_ids(MachineState *ms) +{ + PCMachineState *pcms = PC_MACHINE(ms); + int i; + unsigned int max_cpus = ms->smp.max_cpus; + + if (ms->possible_cpus) { + /* + * make sure that max_cpus hasn't changed since the first use, i.e. + * -smp hasn't been parsed after it + */ + assert(ms->possible_cpus->len == max_cpus); + return ms->possible_cpus; + } + + ms->possible_cpus = g_malloc0(sizeof(CPUArchIdList) + + sizeof(CPUArchId) * max_cpus); + ms->possible_cpus->len = max_cpus; + for (i = 0; i < ms->possible_cpus->len; i++) { + X86CPUTopoInfo topo; + + ms->possible_cpus->cpus[i].type = ms->cpu_type; + ms->possible_cpus->cpus[i].vcpus_count = 1; + ms->possible_cpus->cpus[i].arch_id = + x86_cpu_apic_id_from_index(pcms, i); + x86_topo_ids_from_apicid(ms->possible_cpus->cpus[i].arch_id, + pcms->smp_dies, ms->smp.cores, + ms->smp.threads, &topo); + ms->possible_cpus->cpus[i].props.has_socket_id = true; + ms->possible_cpus->cpus[i].props.socket_id = topo.pkg_id; + if (pcms->smp_dies > 1) { + ms->possible_cpus->cpus[i].props.has_die_id = true; + ms->possible_cpus->cpus[i].props.die_id = topo.die_id; + } + ms->possible_cpus->cpus[i].props.has_core_id = true; + ms->possible_cpus->cpus[i].props.core_id = topo.core_id; + ms->possible_cpus->cpus[i].props.has_thread_id = true; + ms->possible_cpus->cpus[i].props.thread_id = topo.smt_id; + } + return ms->possible_cpus; +} + +static long get_file_size(FILE *f) +{ + long where, size; + + /* XXX: on Unix systems, using fstat() probably makes more sense */ + + where = ftell(f); + fseek(f, 0, SEEK_END); + size = ftell(f); + fseek(f, where, SEEK_SET); + + return size; +} + +struct setup_data { + uint64_t next; + uint32_t type; + uint32_t len; + uint8_t data[0]; +} __attribute__((packed)); + + +/* + * The entry point into the kernel for PVH boot is different from + * the native entry point. The PVH entry is defined by the x86/HVM + * direct boot ABI and is available in an ELFNOTE in the kernel binary. + * + * This function is passed to load_elf() when it is called from + * load_elfboot() which then additionally checks for an ELF Note of + * type XEN_ELFNOTE_PHYS32_ENTRY and passes it to this function to + * parse the PVH entry address from the ELF Note. + * + * Due to trickery in elf_opts.h, load_elf() is actually available as + * load_elf32() or load_elf64() and this routine needs to be able + * to deal with being called as 32 or 64 bit. + * + * The address of the PVH entry point is saved to the 'pvh_start_addr' + * global variable. (although the entry point is 32-bit, the kernel + * binary can be either 32-bit or 64-bit). + */ +static uint64_t read_pvh_start_addr(void *arg1, void *arg2, bool is64) +{ + size_t *elf_note_data_addr; + + /* Check if ELF Note header passed in is valid */ + if (arg1 == NULL) { + return 0; + } + + if (is64) { + struct elf64_note *nhdr64 = (struct elf64_note *)arg1; + uint64_t nhdr_size64 = sizeof(struct elf64_note); + uint64_t phdr_align = *(uint64_t *)arg2; + uint64_t nhdr_namesz = nhdr64->n_namesz; + + elf_note_data_addr = + ((void *)nhdr64) + nhdr_size64 + + QEMU_ALIGN_UP(nhdr_namesz, phdr_align); + } else { + struct elf32_note *nhdr32 = (struct elf32_note *)arg1; + uint32_t nhdr_size32 = sizeof(struct elf32_note); + uint32_t phdr_align = *(uint32_t *)arg2; + uint32_t nhdr_namesz = nhdr32->n_namesz; + + elf_note_data_addr = + ((void *)nhdr32) + nhdr_size32 + + QEMU_ALIGN_UP(nhdr_namesz, phdr_align); + } + + pvh_start_addr = *elf_note_data_addr; + + return pvh_start_addr; +} + +static bool load_elfboot(const char *kernel_filename, + int kernel_file_size, + uint8_t *header, + size_t pvh_xen_start_addr, + FWCfgState *fw_cfg) +{ + uint32_t flags = 0; + uint32_t mh_load_addr = 0; + uint32_t elf_kernel_size = 0; + uint64_t elf_entry; + uint64_t elf_low, elf_high; + int kernel_size; + + if (ldl_p(header) != 0x464c457f) { + return false; /* no elfboot */ + } + + bool elf_is64 = header[EI_CLASS] == ELFCLASS64; + flags = elf_is64 ? + ((Elf64_Ehdr *)header)->e_flags : ((Elf32_Ehdr *)header)->e_flags; + + if (flags & 0x00010004) { /* LOAD_ELF_HEADER_HAS_ADDR */ + error_report("elfboot unsupported flags = %x", flags); + exit(1); + } + + uint64_t elf_note_type = XEN_ELFNOTE_PHYS32_ENTRY; + kernel_size = load_elf(kernel_filename, read_pvh_start_addr, + NULL, &elf_note_type, &elf_entry, + &elf_low, &elf_high, 0, I386_ELF_MACHINE, + 0, 0); + + if (kernel_size < 0) { + error_report("Error while loading elf kernel"); + exit(1); + } + mh_load_addr = elf_low; + elf_kernel_size = elf_high - elf_low; + + if (pvh_start_addr == 0) { + error_report("Error loading uncompressed kernel without PVH ELF Note"); + exit(1); + } + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ENTRY, pvh_start_addr); + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, mh_load_addr); + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, elf_kernel_size); + + return true; +} + +void x86_load_linux(PCMachineState *pcms, + FWCfgState *fw_cfg) +{ + uint16_t protocol; + int setup_size, kernel_size, cmdline_size; + int dtb_size, setup_data_offset; + uint32_t initrd_max; + uint8_t header[8192], *setup, *kernel; + hwaddr real_addr, prot_addr, cmdline_addr, initrd_addr = 0; + FILE *f; + char *vmode; + MachineState *machine = MACHINE(pcms); + PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); + struct setup_data *setup_data; + const char *kernel_filename = machine->kernel_filename; + const char *initrd_filename = machine->initrd_filename; + const char *dtb_filename = machine->dtb; + const char *kernel_cmdline = machine->kernel_cmdline; + + /* Align to 16 bytes as a paranoia measure */ + cmdline_size = (strlen(kernel_cmdline) + 16) & ~15; + + /* load the kernel header */ + f = fopen(kernel_filename, "rb"); + if (!f) { + fprintf(stderr, "qemu: could not open kernel file '%s': %s\n", + kernel_filename, strerror(errno)); + exit(1); + } + + kernel_size = get_file_size(f); + if (!kernel_size || + fread(header, 1, MIN(ARRAY_SIZE(header), kernel_size), f) != + MIN(ARRAY_SIZE(header), kernel_size)) { + fprintf(stderr, "qemu: could not load kernel '%s': %s\n", + kernel_filename, strerror(errno)); + exit(1); + } + + /* kernel protocol version */ + if (ldl_p(header + 0x202) == 0x53726448) { + protocol = lduw_p(header + 0x206); + } else { + /* + * This could be a multiboot kernel. If it is, let's stop treating it + * like a Linux kernel. + * Note: some multiboot images could be in the ELF format (the same of + * PVH), so we try multiboot first since we check the multiboot magic + * header before to load it. + */ + if (load_multiboot(fw_cfg, f, kernel_filename, initrd_filename, + kernel_cmdline, kernel_size, header)) { + return; + } + /* + * Check if the file is an uncompressed kernel file (ELF) and load it, + * saving the PVH entry point used by the x86/HVM direct boot ABI. + * If load_elfboot() is successful, populate the fw_cfg info. + */ + if (pcmc->pvh_enabled && + load_elfboot(kernel_filename, kernel_size, + header, pvh_start_addr, fw_cfg)) { + fclose(f); + + fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, + strlen(kernel_cmdline) + 1); + fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline); + + fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_SIZE, sizeof(header)); + fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA, + header, sizeof(header)); + + /* load initrd */ + if (initrd_filename) { + GMappedFile *mapped_file; + gsize initrd_size; + gchar *initrd_data; + GError *gerr = NULL; + + mapped_file = g_mapped_file_new(initrd_filename, false, &gerr); + if (!mapped_file) { + fprintf(stderr, "qemu: error reading initrd %s: %s\n", + initrd_filename, gerr->message); + exit(1); + } + pcms->initrd_mapped_file = mapped_file; + + initrd_data = g_mapped_file_get_contents(mapped_file); + initrd_size = g_mapped_file_get_length(mapped_file); + initrd_max = pcms->below_4g_mem_size - pcmc->acpi_data_size - 1; + if (initrd_size >= initrd_max) { + fprintf(stderr, "qemu: initrd is too large, cannot support." + "(max: %"PRIu32", need %"PRId64")\n", + initrd_max, (uint64_t)initrd_size); + exit(1); + } + + initrd_addr = (initrd_max - initrd_size) & ~4095; + + fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_addr); + fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size); + fw_cfg_add_bytes(fw_cfg, FW_CFG_INITRD_DATA, initrd_data, + initrd_size); + } + + option_rom[nb_option_roms].bootindex = 0; + option_rom[nb_option_roms].name = "pvh.bin"; + nb_option_roms++; + + return; + } + protocol = 0; + } + + if (protocol < 0x200 || !(header[0x211] & 0x01)) { + /* Low kernel */ + real_addr = 0x90000; + cmdline_addr = 0x9a000 - cmdline_size; + prot_addr = 0x10000; + } else if (protocol < 0x202) { + /* High but ancient kernel */ + real_addr = 0x90000; + cmdline_addr = 0x9a000 - cmdline_size; + prot_addr = 0x100000; + } else { + /* High and recent kernel */ + real_addr = 0x10000; + cmdline_addr = 0x20000; + prot_addr = 0x100000; + } + + /* highest address for loading the initrd */ + if (protocol >= 0x20c && + lduw_p(header + 0x236) & XLF_CAN_BE_LOADED_ABOVE_4G) { + /* + * Linux has supported initrd up to 4 GB for a very long time (2007, + * long before XLF_CAN_BE_LOADED_ABOVE_4G which was added in 2013), + * though it only sets initrd_max to 2 GB to "work around bootloader + * bugs". Luckily, QEMU firmware(which does something like bootloader) + * has supported this. + * + * It's believed that if XLF_CAN_BE_LOADED_ABOVE_4G is set, initrd can + * be loaded into any address. + * + * In addition, initrd_max is uint32_t simply because QEMU doesn't + * support the 64-bit boot protocol (specifically the ext_ramdisk_image + * field). + * + * Therefore here just limit initrd_max to UINT32_MAX simply as well. + */ + initrd_max = UINT32_MAX; + } else if (protocol >= 0x203) { + initrd_max = ldl_p(header + 0x22c); + } else { + initrd_max = 0x37ffffff; + } + + 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); + fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, strlen(kernel_cmdline) + 1); + fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline); + + if (protocol >= 0x202) { + stl_p(header + 0x228, cmdline_addr); + } else { + stw_p(header + 0x20, 0xA33F); + stw_p(header + 0x22, cmdline_addr - real_addr); + } + + /* handle vga= parameter */ + vmode = strstr(kernel_cmdline, "vga="); + if (vmode) { + unsigned int video_mode; + int ret; + /* skip "vga=" */ + vmode += 4; + if (!strncmp(vmode, "normal", 6)) { + video_mode = 0xffff; + } else if (!strncmp(vmode, "ext", 3)) { + video_mode = 0xfffe; + } else if (!strncmp(vmode, "ask", 3)) { + video_mode = 0xfffd; + } else { + ret = qemu_strtoui(vmode, NULL, 0, &video_mode); + if (ret != 0) { + fprintf(stderr, "qemu: can't parse 'vga' parameter: %s\n", + strerror(-ret)); + exit(1); + } + } + stw_p(header + 0x1fa, video_mode); + } + + /* loader type */ + /* + * High nybble = B reserved for QEMU; low nybble is revision number. + * If this code is substantially changed, you may want to consider + * incrementing the revision. + */ + if (protocol >= 0x200) { + header[0x210] = 0xB0; + } + /* heap */ + if (protocol >= 0x201) { + header[0x211] |= 0x80; /* CAN_USE_HEAP */ + stw_p(header + 0x224, cmdline_addr - real_addr - 0x200); + } + + /* load initrd */ + if (initrd_filename) { + GMappedFile *mapped_file; + gsize initrd_size; + gchar *initrd_data; + GError *gerr = NULL; + + if (protocol < 0x200) { + fprintf(stderr, "qemu: linux kernel too old to load a ram disk\n"); + exit(1); + } + + mapped_file = g_mapped_file_new(initrd_filename, false, &gerr); + if (!mapped_file) { + fprintf(stderr, "qemu: error reading initrd %s: %s\n", + initrd_filename, gerr->message); + exit(1); + } + pcms->initrd_mapped_file = mapped_file; + + initrd_data = g_mapped_file_get_contents(mapped_file); + initrd_size = g_mapped_file_get_length(mapped_file); + if (initrd_size >= initrd_max) { + fprintf(stderr, "qemu: initrd is too large, cannot support." + "(max: %"PRIu32", need %"PRId64")\n", + initrd_max, (uint64_t)initrd_size); + exit(1); + } + + initrd_addr = (initrd_max - initrd_size) & ~4095; + + fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_addr); + fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size); + fw_cfg_add_bytes(fw_cfg, FW_CFG_INITRD_DATA, initrd_data, initrd_size); + + stl_p(header + 0x218, initrd_addr); + stl_p(header + 0x21c, initrd_size); + } + + /* load kernel and setup */ + setup_size = header[0x1f1]; + if (setup_size == 0) { + setup_size = 4; + } + setup_size = (setup_size + 1) * 512; + if (setup_size > kernel_size) { + fprintf(stderr, "qemu: invalid kernel header\n"); + exit(1); + } + kernel_size -= setup_size; + + setup = g_malloc(setup_size); + kernel = g_malloc(kernel_size); + fseek(f, 0, SEEK_SET); + if (fread(setup, 1, setup_size, f) != setup_size) { + fprintf(stderr, "fread() failed\n"); + exit(1); + } + if (fread(kernel, 1, kernel_size, f) != kernel_size) { + fprintf(stderr, "fread() failed\n"); + exit(1); + } + fclose(f); + + /* append dtb to kernel */ + if (dtb_filename) { + if (protocol < 0x209) { + fprintf(stderr, "qemu: Linux kernel too old to load a dtb\n"); + exit(1); + } + + dtb_size = get_image_size(dtb_filename); + if (dtb_size <= 0) { + fprintf(stderr, "qemu: error reading dtb %s: %s\n", + dtb_filename, strerror(errno)); + exit(1); + } + + setup_data_offset = QEMU_ALIGN_UP(kernel_size, 16); + kernel_size = setup_data_offset + sizeof(struct setup_data) + dtb_size; + kernel = g_realloc(kernel, kernel_size); + + stq_p(header + 0x250, prot_addr + setup_data_offset); + + setup_data = (struct setup_data *)(kernel + setup_data_offset); + setup_data->next = 0; + setup_data->type = cpu_to_le32(SETUP_DTB); + setup_data->len = cpu_to_le32(dtb_size); + + load_image_size(dtb_filename, setup_data->data, dtb_size); + } + + memcpy(setup, header, MIN(sizeof(header), setup_size)); + + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, prot_addr); + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size); + fw_cfg_add_bytes(fw_cfg, FW_CFG_KERNEL_DATA, kernel, kernel_size); + + fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_ADDR, real_addr); + fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_SIZE, setup_size); + fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA, setup, setup_size); + + option_rom[nb_option_roms].bootindex = 0; + option_rom[nb_option_roms].name = "linuxboot.bin"; + if (pcmc->linuxboot_dma_enabled && fw_cfg_dma_enabled(fw_cfg)) { + option_rom[nb_option_roms].name = "linuxboot_dma.bin"; + } + nb_option_roms++; +} + +void x86_bios_rom_init(MemoryRegion *rom_memory, bool isapc_ram_fw) +{ + char *filename; + MemoryRegion *bios, *isa_bios; + int bios_size, isa_bios_size; + int ret; + + /* BIOS load */ + if (bios_name == NULL) { + bios_name = BIOS_FILENAME; + } + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); + if (filename) { + bios_size = get_image_size(filename); + } else { + bios_size = -1; + } + if (bios_size <= 0 || + (bios_size % 65536) != 0) { + goto bios_error; + } + bios = g_malloc(sizeof(*bios)); + memory_region_init_ram(bios, NULL, "pc.bios", bios_size, &error_fatal); + if (!isapc_ram_fw) { + memory_region_set_readonly(bios, true); + } + ret = rom_add_file_fixed(bios_name, (uint32_t)(-bios_size), -1); + if (ret != 0) { + bios_error: + fprintf(stderr, "qemu: could not load PC BIOS '%s'\n", bios_name); + exit(1); + } + g_free(filename); + + /* map the last 128KB of the BIOS in ISA space */ + isa_bios_size = MIN(bios_size, 128 * KiB); + isa_bios = g_malloc(sizeof(*isa_bios)); + memory_region_init_alias(isa_bios, NULL, "isa-bios", bios, + bios_size - isa_bios_size, isa_bios_size); + memory_region_add_subregion_overlap(rom_memory, + 0x100000 - isa_bios_size, + isa_bios, + 1); + if (!isapc_ram_fw) { + memory_region_set_readonly(isa_bios, true); + } + + /* map all the bios at the top of memory */ + memory_region_add_subregion(rom_memory, + (uint32_t)(-bios_size), + bios); +} diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 7082b06c51..f135ae8c07 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -198,7 +198,6 @@ bool pc_machine_is_smm_enabled(PCMachineState *pcms); void pc_register_ferr_irq(qemu_irq irq); void pc_acpi_smi_interrupt(void *opaque, int irq, int level); -void x86_cpus_init(PCMachineState *pcms); void pc_hot_add_cpu(MachineState *ms, const int64_t id, Error **errp); void pc_smp_parse(MachineState *ms, QemuOpts *opts); diff --git a/include/hw/i386/x86.h b/include/hw/i386/x86.h new file mode 100644 index 0000000000..71e2b6985d --- /dev/null +++ b/include/hw/i386/x86.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2019 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef HW_I386_X86_H +#define HW_I386_X86_H + +#include "hw/boards.h" + +uint32_t x86_cpu_apic_id_from_index(PCMachineState *pcms, + unsigned int cpu_index); +void x86_cpu_new(PCMachineState *pcms, int64_t apic_id, Error **errp); +void x86_cpus_init(PCMachineState *pcms); +CpuInstanceProperties x86_cpu_index_to_props(MachineState *ms, + unsigned cpu_index); +int64_t x86_get_default_cpu_node_id(const MachineState *ms, int idx); +const CPUArchIdList *x86_possible_cpu_arch_ids(MachineState *ms); + +void x86_bios_rom_init(MemoryRegion *rom_memory, bool isapc_ram_fw); + +void x86_load_linux(PCMachineState *x86ms, FWCfgState *fw_cfg); + +#endif From f0bb276bf8d5b3df57697357b802ca76e4cdf05f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 22 Oct 2019 09:39:50 +0200 Subject: [PATCH 14/34] hw/i386: split PCMachineState deriving X86MachineState from it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split up PCMachineState and PCMachineClass and derive X86MachineState and X86MachineClass from them. This allows sharing code with non-PC x86 machine types. Signed-off-by: Sergio Lopez Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Reviewed-by: Michael S. Tsirkin --- hw/acpi/cpu_hotplug.c | 10 +-- hw/i386/acpi-build.c | 29 ++++--- hw/i386/amd_iommu.c | 3 +- hw/i386/intel_iommu.c | 3 +- hw/i386/pc.c | 178 ++++++++++++++---------------------------- hw/i386/pc_piix.c | 43 +++++----- hw/i386/pc_q35.c | 35 +++++---- hw/i386/x86.c | 140 +++++++++++++++++++++++++++++---- hw/i386/xen/xen-hvm.c | 28 ++++--- hw/intc/ioapic.c | 2 +- include/hw/i386/pc.h | 27 +------ include/hw/i386/x86.h | 58 +++++++++++++- 12 files changed, 326 insertions(+), 230 deletions(-) diff --git a/hw/acpi/cpu_hotplug.c b/hw/acpi/cpu_hotplug.c index 6e8293aac9..3ac2045a95 100644 --- a/hw/acpi/cpu_hotplug.c +++ b/hw/acpi/cpu_hotplug.c @@ -128,7 +128,7 @@ void build_legacy_cpu_hotplug_aml(Aml *ctx, MachineState *machine, Aml *one = aml_int(1); MachineClass *mc = MACHINE_GET_CLASS(machine); const CPUArchIdList *apic_ids = mc->possible_cpu_arch_ids(machine); - PCMachineState *pcms = PC_MACHINE(machine); + X86MachineState *x86ms = X86_MACHINE(machine); /* * _MAT method - creates an madt apic buffer @@ -236,9 +236,9 @@ void build_legacy_cpu_hotplug_aml(Aml *ctx, MachineState *machine, /* The current AML generator can cover the APIC ID range [0..255], * inclusive, for VCPU hotplug. */ QEMU_BUILD_BUG_ON(ACPI_CPU_HOTPLUG_ID_LIMIT > 256); - if (pcms->apic_id_limit > ACPI_CPU_HOTPLUG_ID_LIMIT) { + if (x86ms->apic_id_limit > ACPI_CPU_HOTPLUG_ID_LIMIT) { error_report("max_cpus is too large. APIC ID of last CPU is %u", - pcms->apic_id_limit - 1); + x86ms->apic_id_limit - 1); exit(1); } @@ -315,8 +315,8 @@ void build_legacy_cpu_hotplug_aml(Aml *ctx, MachineState *machine, * ith up to 255 elements. Windows guests up to win2k8 fail when * VarPackageOp is used. */ - pkg = pcms->apic_id_limit <= 255 ? aml_package(pcms->apic_id_limit) : - aml_varpackage(pcms->apic_id_limit); + pkg = x86ms->apic_id_limit <= 255 ? aml_package(x86ms->apic_id_limit) : + aml_varpackage(x86ms->apic_id_limit); for (i = 0, apic_idx = 0; i < apic_ids->len; i++) { int apic_id = apic_ids->cpus[i].arch_id; diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 1d077a7cb7..bc0256e1e0 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -361,6 +361,7 @@ static void build_madt(GArray *table_data, BIOSLinker *linker, PCMachineState *pcms) { MachineClass *mc = MACHINE_GET_CLASS(pcms); + X86MachineState *x86ms = X86_MACHINE(pcms); const CPUArchIdList *apic_ids = mc->possible_cpu_arch_ids(MACHINE(pcms)); int madt_start = table_data->len; AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_GET_CLASS(pcms->acpi_dev); @@ -390,7 +391,7 @@ build_madt(GArray *table_data, BIOSLinker *linker, PCMachineState *pcms) io_apic->address = cpu_to_le32(IO_APIC_DEFAULT_ADDRESS); io_apic->interrupt = cpu_to_le32(0); - if (pcms->apic_xrupt_override) { + if (x86ms->apic_xrupt_override) { intsrcovr = acpi_data_push(table_data, sizeof *intsrcovr); intsrcovr->type = ACPI_APIC_XRUPT_OVERRIDE; intsrcovr->length = sizeof(*intsrcovr); @@ -1831,6 +1832,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, CrsRangeSet crs_range_set; PCMachineState *pcms = PC_MACHINE(machine); PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(machine); + X86MachineState *x86ms = X86_MACHINE(machine); AcpiMcfgInfo mcfg; uint32_t nr_mem = machine->ram_slots; int root_bus_limit = 0xFF; @@ -2103,7 +2105,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, * with half of the 16-bit control register. Hence, the total size * of the i/o region used is FW_CFG_CTL_SIZE; when using DMA, the * DMA control register is located at FW_CFG_DMA_IO_BASE + 4 */ - uint8_t io_size = object_property_get_bool(OBJECT(pcms->fw_cfg), + uint8_t io_size = object_property_get_bool(OBJECT(x86ms->fw_cfg), "dma_enabled", NULL) ? ROUND_UP(FW_CFG_CTL_SIZE, 4) + sizeof(dma_addr_t) : FW_CFG_CTL_SIZE; @@ -2336,6 +2338,7 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) int srat_start, numa_start, slots; uint64_t mem_len, mem_base, next_base; MachineClass *mc = MACHINE_GET_CLASS(machine); + X86MachineState *x86ms = X86_MACHINE(machine); const CPUArchIdList *apic_ids = mc->possible_cpu_arch_ids(machine); PCMachineState *pcms = PC_MACHINE(machine); ram_addr_t hotplugabble_address_space_size = @@ -2406,16 +2409,16 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) } /* Cut out the ACPI_PCI hole */ - if (mem_base <= pcms->below_4g_mem_size && - next_base > pcms->below_4g_mem_size) { - mem_len -= next_base - pcms->below_4g_mem_size; + if (mem_base <= x86ms->below_4g_mem_size && + next_base > x86ms->below_4g_mem_size) { + mem_len -= next_base - x86ms->below_4g_mem_size; if (mem_len > 0) { numamem = acpi_data_push(table_data, sizeof *numamem); build_srat_memory(numamem, mem_base, mem_len, i - 1, MEM_AFFINITY_ENABLED); } mem_base = 1ULL << 32; - mem_len = next_base - pcms->below_4g_mem_size; + mem_len = next_base - x86ms->below_4g_mem_size; next_base = mem_base + mem_len; } @@ -2634,6 +2637,7 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine) { PCMachineState *pcms = PC_MACHINE(machine); PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); + X86MachineState *x86ms = X86_MACHINE(machine); GArray *table_offsets; unsigned facs, dsdt, rsdt, fadt; AcpiPmInfo pm; @@ -2795,7 +2799,7 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine) */ int legacy_aml_len = pcmc->legacy_acpi_table_size + - ACPI_BUILD_LEGACY_CPU_AML_SIZE * pcms->apic_id_limit; + ACPI_BUILD_LEGACY_CPU_AML_SIZE * x86ms->apic_id_limit; int legacy_table_size = ROUND_UP(tables_blob->len - aml_len + legacy_aml_len, ACPI_BUILD_ALIGN_SIZE); @@ -2885,13 +2889,14 @@ void acpi_setup(void) { PCMachineState *pcms = PC_MACHINE(qdev_get_machine()); PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); + X86MachineState *x86ms = X86_MACHINE(pcms); AcpiBuildTables tables; AcpiBuildState *build_state; Object *vmgenid_dev; TPMIf *tpm; static FwCfgTPMConfig tpm_config; - if (!pcms->fw_cfg) { + if (!x86ms->fw_cfg) { ACPI_BUILD_DPRINTF("No fw cfg. Bailing out.\n"); return; } @@ -2922,7 +2927,7 @@ void acpi_setup(void) acpi_add_rom_blob(acpi_build_update, build_state, tables.linker->cmd_blob, "etc/table-loader", 0); - fw_cfg_add_file(pcms->fw_cfg, ACPI_BUILD_TPMLOG_FILE, + fw_cfg_add_file(x86ms->fw_cfg, ACPI_BUILD_TPMLOG_FILE, tables.tcpalog->data, acpi_data_len(tables.tcpalog)); tpm = tpm_find(); @@ -2932,13 +2937,13 @@ void acpi_setup(void) .tpm_version = tpm_get_version(tpm), .tpmppi_version = TPM_PPI_VERSION_1_30 }; - fw_cfg_add_file(pcms->fw_cfg, "etc/tpm/config", + fw_cfg_add_file(x86ms->fw_cfg, "etc/tpm/config", &tpm_config, sizeof tpm_config); } vmgenid_dev = find_vmgenid_dev(); if (vmgenid_dev) { - vmgenid_add_fw_cfg(VMGENID(vmgenid_dev), pcms->fw_cfg, + vmgenid_add_fw_cfg(VMGENID(vmgenid_dev), x86ms->fw_cfg, tables.vmgenid); } @@ -2951,7 +2956,7 @@ void acpi_setup(void) uint32_t rsdp_size = acpi_data_len(tables.rsdp); build_state->rsdp = g_memdup(tables.rsdp->data, rsdp_size); - fw_cfg_add_file_callback(pcms->fw_cfg, ACPI_BUILD_RSDP_FILE, + fw_cfg_add_file_callback(x86ms->fw_cfg, ACPI_BUILD_RSDP_FILE, acpi_build_update, NULL, build_state, build_state->rsdp, rsdp_size, true); build_state->rsdp_mr = NULL; diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index d3726361dd..d55dbf07fc 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -1540,6 +1540,7 @@ static void amdvi_realize(DeviceState *dev, Error **err) X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(dev); MachineState *ms = MACHINE(qdev_get_machine()); PCMachineState *pcms = PC_MACHINE(ms); + X86MachineState *x86ms = X86_MACHINE(ms); PCIBus *bus = pcms->bus; s->iotlb = g_hash_table_new_full(amdvi_uint64_hash, @@ -1568,7 +1569,7 @@ static void amdvi_realize(DeviceState *dev, Error **err) } /* Pseudo address space under root PCI bus. */ - pcms->ioapic_as = amdvi_host_dma_iommu(bus, s, AMDVI_IOAPIC_SB_DEVID); + x86ms->ioapic_as = amdvi_host_dma_iommu(bus, s, AMDVI_IOAPIC_SB_DEVID); /* set up MMIO */ memory_region_init_io(&s->mmio, OBJECT(s), &mmio_mem_ops, s, "amdvi-mmio", diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 771bed25c9..14e4e6ad62 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -3733,6 +3733,7 @@ static void vtd_realize(DeviceState *dev, Error **errp) { MachineState *ms = MACHINE(qdev_get_machine()); PCMachineState *pcms = PC_MACHINE(ms); + X86MachineState *x86ms = X86_MACHINE(ms); PCIBus *bus = pcms->bus; IntelIOMMUState *s = INTEL_IOMMU_DEVICE(dev); X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(dev); @@ -3773,7 +3774,7 @@ static void vtd_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(s), 0, Q35_HOST_BRIDGE_IOMMU_ADDR); pci_setup_iommu(bus, vtd_host_dma_iommu, dev); /* Pseudo address space under root PCI bus. */ - pcms->ioapic_as = vtd_host_dma_iommu(bus, s, Q35_PSEUDO_DEVFN_IOAPIC); + x86ms->ioapic_as = vtd_host_dma_iommu(bus, s, Q35_PSEUDO_DEVFN_IOAPIC); qemu_add_machine_init_done_notifier(&vtd_machine_done_notify); } diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 4a1bc95ccb..e6ad1f1f36 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -79,7 +79,6 @@ #include "qapi/qapi-visit-common.h" #include "qapi/visitor.h" #include "hw/core/cpu.h" -#include "hw/nmi.h" #include "hw/usb.h" #include "hw/i386/intel_iommu.h" #include "hw/net/ne2000-isa.h" @@ -680,17 +679,18 @@ void pc_cmos_init(PCMachineState *pcms, { int val; static pc_cmos_init_late_arg arg; + X86MachineState *x86ms = X86_MACHINE(pcms); /* various important CMOS locations needed by PC/Bochs bios */ /* memory size */ /* base memory (first MiB) */ - val = MIN(pcms->below_4g_mem_size / KiB, 640); + val = MIN(x86ms->below_4g_mem_size / KiB, 640); rtc_set_memory(s, 0x15, val); rtc_set_memory(s, 0x16, val >> 8); /* extended memory (next 64MiB) */ - if (pcms->below_4g_mem_size > 1 * MiB) { - val = (pcms->below_4g_mem_size - 1 * MiB) / KiB; + if (x86ms->below_4g_mem_size > 1 * MiB) { + val = (x86ms->below_4g_mem_size - 1 * MiB) / KiB; } else { val = 0; } @@ -701,8 +701,8 @@ void pc_cmos_init(PCMachineState *pcms, rtc_set_memory(s, 0x30, val); rtc_set_memory(s, 0x31, val >> 8); /* memory between 16MiB and 4GiB */ - if (pcms->below_4g_mem_size > 16 * MiB) { - val = (pcms->below_4g_mem_size - 16 * MiB) / (64 * KiB); + if (x86ms->below_4g_mem_size > 16 * MiB) { + val = (x86ms->below_4g_mem_size - 16 * MiB) / (64 * KiB); } else { val = 0; } @@ -711,14 +711,14 @@ void pc_cmos_init(PCMachineState *pcms, rtc_set_memory(s, 0x34, val); rtc_set_memory(s, 0x35, val >> 8); /* memory above 4GiB */ - val = pcms->above_4g_mem_size / 65536; + val = x86ms->above_4g_mem_size / 65536; rtc_set_memory(s, 0x5b, val); rtc_set_memory(s, 0x5c, val >> 8); rtc_set_memory(s, 0x5d, val >> 16); object_property_add_link(OBJECT(pcms), "rtc_state", TYPE_ISA_DEVICE, - (Object **)&pcms->rtc, + (Object **)&x86ms->rtc, object_property_allow_set_link, OBJ_PROP_LINK_STRONG, &error_abort); object_property_set_link(OBJECT(pcms), OBJECT(s), @@ -907,7 +907,7 @@ void pc_acpi_smi_interrupt(void *opaque, int irq, int level) */ void pc_smp_parse(MachineState *ms, QemuOpts *opts) { - PCMachineState *pcms = PC_MACHINE(ms); + X86MachineState *x86ms = X86_MACHINE(ms); if (opts) { unsigned cpus = qemu_opt_get_number(opts, "cpus", 0); @@ -971,7 +971,7 @@ void pc_smp_parse(MachineState *ms, QemuOpts *opts) ms->smp.cpus = cpus; ms->smp.cores = cores; ms->smp.threads = threads; - pcms->smp_dies = dies; + x86ms->smp_dies = dies; } if (ms->smp.cpus > 1) { @@ -1024,10 +1024,11 @@ void pc_machine_done(Notifier *notifier, void *data) { PCMachineState *pcms = container_of(notifier, PCMachineState, machine_done); + X86MachineState *x86ms = X86_MACHINE(pcms); PCIBus *bus = pcms->bus; /* set the number of CPUs */ - rtc_set_cpus_count(pcms->rtc, pcms->boot_cpus); + rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus); if (bus) { int extra_hosts = 0; @@ -1038,23 +1039,23 @@ void pc_machine_done(Notifier *notifier, void *data) extra_hosts++; } } - if (extra_hosts && pcms->fw_cfg) { + if (extra_hosts && x86ms->fw_cfg) { uint64_t *val = g_malloc(sizeof(*val)); *val = cpu_to_le64(extra_hosts); - fw_cfg_add_file(pcms->fw_cfg, + fw_cfg_add_file(x86ms->fw_cfg, "etc/extra-pci-roots", val, sizeof(*val)); } } acpi_setup(); - if (pcms->fw_cfg) { - fw_cfg_build_smbios(MACHINE(pcms), pcms->fw_cfg); - fw_cfg_build_feature_control(MACHINE(pcms), pcms->fw_cfg); + if (x86ms->fw_cfg) { + fw_cfg_build_smbios(MACHINE(pcms), x86ms->fw_cfg); + fw_cfg_build_feature_control(MACHINE(pcms), x86ms->fw_cfg); /* update FW_CFG_NB_CPUS to account for -device added CPUs */ - fw_cfg_modify_i16(pcms->fw_cfg, FW_CFG_NB_CPUS, pcms->boot_cpus); + fw_cfg_modify_i16(x86ms->fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus); } - if (pcms->apic_id_limit > 255 && !xen_enabled()) { + if (x86ms->apic_id_limit > 255 && !xen_enabled()) { IntelIOMMUState *iommu = INTEL_IOMMU_DEVICE(x86_iommu_get_default()); if (!iommu || !x86_iommu_ir_supported(X86_IOMMU_DEVICE(iommu)) || @@ -1072,8 +1073,9 @@ void pc_guest_info_init(PCMachineState *pcms) { int i; MachineState *ms = MACHINE(pcms); + X86MachineState *x86ms = X86_MACHINE(pcms); - pcms->apic_xrupt_override = kvm_allows_irq0_override(); + x86ms->apic_xrupt_override = kvm_allows_irq0_override(); pcms->numa_nodes = ms->numa_state->num_nodes; pcms->node_mem = g_malloc0(pcms->numa_nodes * sizeof *pcms->node_mem); @@ -1098,11 +1100,12 @@ void xen_load_linux(PCMachineState *pcms) { int i; FWCfgState *fw_cfg; + X86MachineState *x86ms = X86_MACHINE(pcms); assert(MACHINE(pcms)->kernel_filename != NULL); fw_cfg = fw_cfg_init_io(FW_CFG_IO_BASE); - fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, pcms->boot_cpus); + fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus); rom_set_fw(fw_cfg); x86_load_linux(pcms, fw_cfg); @@ -1113,7 +1116,7 @@ void xen_load_linux(PCMachineState *pcms) !strcmp(option_rom[i].name, "multiboot.bin")); rom_add_option(option_rom[i].name, option_rom[i].bootindex); } - pcms->fw_cfg = fw_cfg; + x86ms->fw_cfg = fw_cfg; } void pc_memory_init(PCMachineState *pcms, @@ -1128,9 +1131,10 @@ void pc_memory_init(PCMachineState *pcms, MachineState *machine = MACHINE(pcms); MachineClass *mc = MACHINE_GET_CLASS(machine); PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); + X86MachineState *x86ms = X86_MACHINE(pcms); - assert(machine->ram_size == pcms->below_4g_mem_size + - pcms->above_4g_mem_size); + assert(machine->ram_size == x86ms->below_4g_mem_size + + x86ms->above_4g_mem_size); linux_boot = (machine->kernel_filename != NULL); @@ -1144,17 +1148,17 @@ void pc_memory_init(PCMachineState *pcms, *ram_memory = ram; ram_below_4g = g_malloc(sizeof(*ram_below_4g)); memory_region_init_alias(ram_below_4g, NULL, "ram-below-4g", ram, - 0, pcms->below_4g_mem_size); + 0, x86ms->below_4g_mem_size); memory_region_add_subregion(system_memory, 0, ram_below_4g); - e820_add_entry(0, pcms->below_4g_mem_size, E820_RAM); - if (pcms->above_4g_mem_size > 0) { + e820_add_entry(0, x86ms->below_4g_mem_size, E820_RAM); + if (x86ms->above_4g_mem_size > 0) { ram_above_4g = g_malloc(sizeof(*ram_above_4g)); memory_region_init_alias(ram_above_4g, NULL, "ram-above-4g", ram, - pcms->below_4g_mem_size, - pcms->above_4g_mem_size); + x86ms->below_4g_mem_size, + x86ms->above_4g_mem_size); memory_region_add_subregion(system_memory, 0x100000000ULL, ram_above_4g); - e820_add_entry(0x100000000ULL, pcms->above_4g_mem_size, E820_RAM); + e820_add_entry(0x100000000ULL, x86ms->above_4g_mem_size, E820_RAM); } if (!pcmc->has_reserved_memory && @@ -1188,7 +1192,7 @@ void pc_memory_init(PCMachineState *pcms, } machine->device_memory->base = - ROUND_UP(0x100000000ULL + pcms->above_4g_mem_size, 1 * GiB); + ROUND_UP(0x100000000ULL + x86ms->above_4g_mem_size, 1 * GiB); if (pcmc->enforce_aligned_dimm) { /* size device region assuming 1G page max alignment per slot */ @@ -1223,7 +1227,7 @@ void pc_memory_init(PCMachineState *pcms, 1); fw_cfg = fw_cfg_arch_create(machine, - pcms->boot_cpus, pcms->apic_id_limit); + x86ms->boot_cpus, x86ms->apic_id_limit); rom_set_fw(fw_cfg); @@ -1246,10 +1250,10 @@ void pc_memory_init(PCMachineState *pcms, for (i = 0; i < nb_option_roms; i++) { rom_add_option(option_rom[i].name, option_rom[i].bootindex); } - pcms->fw_cfg = fw_cfg; + x86ms->fw_cfg = fw_cfg; /* Init default IOAPIC address space */ - pcms->ioapic_as = &address_space_memory; + x86ms->ioapic_as = &address_space_memory; /* Init ACPI memory hotplug IO base address */ pcms->memhp_io_base = ACPI_MEMORY_HOTPLUG_BASE; @@ -1264,6 +1268,7 @@ uint64_t pc_pci_hole64_start(void) PCMachineState *pcms = PC_MACHINE(qdev_get_machine()); PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); MachineState *ms = MACHINE(pcms); + X86MachineState *x86ms = X86_MACHINE(pcms); uint64_t hole64_start = 0; if (pcmc->has_reserved_memory && ms->device_memory->base) { @@ -1272,7 +1277,7 @@ uint64_t pc_pci_hole64_start(void) hole64_start += memory_region_size(&ms->device_memory->mr); } } else { - hole64_start = 0x100000000ULL + pcms->above_4g_mem_size; + hole64_start = 0x100000000ULL + x86ms->above_4g_mem_size; } return ROUND_UP(hole64_start, 1 * GiB); @@ -1611,6 +1616,7 @@ static void pc_cpu_plug(HotplugHandler *hotplug_dev, Error *local_err = NULL; X86CPU *cpu = X86_CPU(dev); PCMachineState *pcms = PC_MACHINE(hotplug_dev); + X86MachineState *x86ms = X86_MACHINE(pcms); if (pcms->acpi_dev) { hotplug_handler_plug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err); @@ -1620,12 +1626,12 @@ static void pc_cpu_plug(HotplugHandler *hotplug_dev, } /* increment the number of CPUs */ - pcms->boot_cpus++; - if (pcms->rtc) { - rtc_set_cpus_count(pcms->rtc, pcms->boot_cpus); + x86ms->boot_cpus++; + if (x86ms->rtc) { + rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus); } - if (pcms->fw_cfg) { - fw_cfg_modify_i16(pcms->fw_cfg, FW_CFG_NB_CPUS, pcms->boot_cpus); + if (x86ms->fw_cfg) { + fw_cfg_modify_i16(x86ms->fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus); } found_cpu = pc_find_cpu_slot(MACHINE(pcms), cpu->apic_id, NULL); @@ -1671,6 +1677,7 @@ static void pc_cpu_unplug_cb(HotplugHandler *hotplug_dev, Error *local_err = NULL; X86CPU *cpu = X86_CPU(dev); PCMachineState *pcms = PC_MACHINE(hotplug_dev); + X86MachineState *x86ms = X86_MACHINE(pcms); hotplug_handler_unplug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err); if (local_err) { @@ -1682,10 +1689,10 @@ static void pc_cpu_unplug_cb(HotplugHandler *hotplug_dev, object_property_set_bool(OBJECT(dev), false, "realized", NULL); /* decrement the number of CPUs */ - pcms->boot_cpus--; + x86ms->boot_cpus--; /* Update the number of CPUs in CMOS */ - rtc_set_cpus_count(pcms->rtc, pcms->boot_cpus); - fw_cfg_modify_i16(pcms->fw_cfg, FW_CFG_NB_CPUS, pcms->boot_cpus); + rtc_set_cpus_count(x86ms->rtc, x86ms->boot_cpus); + fw_cfg_modify_i16(x86ms->fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus); out: error_propagate(errp, local_err); } @@ -1701,6 +1708,7 @@ static void pc_cpu_pre_plug(HotplugHandler *hotplug_dev, CPUX86State *env = &cpu->env; MachineState *ms = MACHINE(hotplug_dev); PCMachineState *pcms = PC_MACHINE(hotplug_dev); + X86MachineState *x86ms = X86_MACHINE(pcms); unsigned int smp_cores = ms->smp.cores; unsigned int smp_threads = ms->smp.threads; @@ -1710,7 +1718,7 @@ static void pc_cpu_pre_plug(HotplugHandler *hotplug_dev, return; } - env->nr_dies = pcms->smp_dies; + env->nr_dies = x86ms->smp_dies; /* * If APIC ID is not set, @@ -1718,13 +1726,13 @@ static void pc_cpu_pre_plug(HotplugHandler *hotplug_dev, */ if (cpu->apic_id == UNASSIGNED_APIC_ID) { int max_socket = (ms->smp.max_cpus - 1) / - smp_threads / smp_cores / pcms->smp_dies; + smp_threads / smp_cores / x86ms->smp_dies; /* * die-id was optional in QEMU 4.0 and older, so keep it optional * if there's only one die per socket. */ - if (cpu->die_id < 0 && pcms->smp_dies == 1) { + if (cpu->die_id < 0 && x86ms->smp_dies == 1) { cpu->die_id = 0; } @@ -1739,9 +1747,9 @@ static void pc_cpu_pre_plug(HotplugHandler *hotplug_dev, if (cpu->die_id < 0) { error_setg(errp, "CPU die-id is not set"); return; - } else if (cpu->die_id > pcms->smp_dies - 1) { + } else if (cpu->die_id > x86ms->smp_dies - 1) { error_setg(errp, "Invalid CPU die-id: %u must be in range 0:%u", - cpu->die_id, pcms->smp_dies - 1); + cpu->die_id, x86ms->smp_dies - 1); return; } if (cpu->core_id < 0) { @@ -1765,7 +1773,7 @@ static void pc_cpu_pre_plug(HotplugHandler *hotplug_dev, topo.die_id = cpu->die_id; topo.core_id = cpu->core_id; topo.smt_id = cpu->thread_id; - cpu->apic_id = apicid_from_topo_ids(pcms->smp_dies, smp_cores, + cpu->apic_id = apicid_from_topo_ids(x86ms->smp_dies, smp_cores, smp_threads, &topo); } @@ -1773,7 +1781,7 @@ static void pc_cpu_pre_plug(HotplugHandler *hotplug_dev, if (!cpu_slot) { MachineState *ms = MACHINE(pcms); - x86_topo_ids_from_apicid(cpu->apic_id, pcms->smp_dies, + x86_topo_ids_from_apicid(cpu->apic_id, x86ms->smp_dies, smp_cores, smp_threads, &topo); error_setg(errp, "Invalid CPU [socket: %u, die: %u, core: %u, thread: %u] with" @@ -1795,7 +1803,7 @@ static void pc_cpu_pre_plug(HotplugHandler *hotplug_dev, /* TODO: move socket_id/core_id/thread_id checks into x86_cpu_realizefn() * once -smp refactoring is complete and there will be CPU private * CPUState::nr_cores and CPUState::nr_threads fields instead of globals */ - x86_topo_ids_from_apicid(cpu->apic_id, pcms->smp_dies, + x86_topo_ids_from_apicid(cpu->apic_id, x86ms->smp_dies, smp_cores, smp_threads, &topo); if (cpu->socket_id != -1 && cpu->socket_id != topo.pkg_id) { error_setg(errp, "property socket-id: %u doesn't match set apic-id:" @@ -1977,45 +1985,6 @@ pc_machine_get_device_memory_region_size(Object *obj, Visitor *v, visit_type_int(v, name, &value, errp); } -static void pc_machine_get_max_ram_below_4g(Object *obj, Visitor *v, - const char *name, void *opaque, - Error **errp) -{ - PCMachineState *pcms = PC_MACHINE(obj); - uint64_t value = pcms->max_ram_below_4g; - - visit_type_size(v, name, &value, errp); -} - -static void pc_machine_set_max_ram_below_4g(Object *obj, Visitor *v, - const char *name, void *opaque, - Error **errp) -{ - PCMachineState *pcms = PC_MACHINE(obj); - Error *error = NULL; - uint64_t value; - - visit_type_size(v, name, &value, &error); - if (error) { - error_propagate(errp, error); - return; - } - if (value > 4 * GiB) { - error_setg(&error, - "Machine option 'max-ram-below-4g=%"PRIu64 - "' expects size less than or equal to 4G", value); - error_propagate(errp, error); - return; - } - - if (value < 1 * MiB) { - warn_report("Only %" PRIu64 " bytes of RAM below the 4GiB boundary," - "BIOS may not work with less than 1MiB", value); - } - - pcms->max_ram_below_4g = value; -} - static void pc_machine_get_vmport(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { @@ -2121,7 +2090,6 @@ static void pc_machine_initfn(Object *obj) { PCMachineState *pcms = PC_MACHINE(obj); - pcms->max_ram_below_4g = 0; /* use default */ pcms->smm = ON_OFF_AUTO_AUTO; #ifdef CONFIG_VMPORT pcms->vmport = ON_OFF_AUTO_AUTO; @@ -2133,7 +2101,6 @@ static void pc_machine_initfn(Object *obj) pcms->smbus_enabled = true; pcms->sata_enabled = true; pcms->pit_enabled = true; - pcms->smp_dies = 1; pc_system_flash_create(pcms); } @@ -2164,23 +2131,6 @@ static void pc_machine_wakeup(MachineState *machine) cpu_synchronize_all_post_reset(); } -static void x86_nmi(NMIState *n, int cpu_index, Error **errp) -{ - /* cpu index isn't used */ - CPUState *cs; - - CPU_FOREACH(cs) { - X86CPU *cpu = X86_CPU(cs); - - if (!cpu->apic_state) { - cpu_interrupt(cs, CPU_INTERRUPT_NMI); - } else { - apic_deliver_nmi(cpu->apic_state); - } - } -} - - static bool pc_hotplug_allowed(MachineState *ms, DeviceState *dev, Error **errp) { X86IOMMUState *iommu = x86_iommu_get_default(); @@ -2205,7 +2155,6 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) MachineClass *mc = MACHINE_CLASS(oc); PCMachineClass *pcmc = PC_MACHINE_CLASS(oc); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); - NMIClass *nc = NMI_CLASS(oc); pcmc->pci_enabled = true; pcmc->has_acpi_build = true; @@ -2241,7 +2190,6 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) hc->plug = pc_machine_device_plug_cb; hc->unplug_request = pc_machine_device_unplug_request_cb; hc->unplug = pc_machine_device_unplug_cb; - nc->nmi_monitor_handler = x86_nmi; mc->default_cpu_type = TARGET_DEFAULT_CPU_TYPE; mc->nvdimm_supported = true; mc->numa_mem_supported = true; @@ -2250,13 +2198,6 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) pc_machine_get_device_memory_region_size, NULL, NULL, NULL, &error_abort); - object_class_property_add(oc, PC_MACHINE_MAX_RAM_BELOW_4G, "size", - pc_machine_get_max_ram_below_4g, pc_machine_set_max_ram_below_4g, - NULL, NULL, &error_abort); - - object_class_property_set_description(oc, PC_MACHINE_MAX_RAM_BELOW_4G, - "Maximum ram below the 4G boundary (32bit boundary)", &error_abort); - object_class_property_add(oc, PC_MACHINE_SMM, "OnOffAuto", pc_machine_get_smm, pc_machine_set_smm, NULL, NULL, &error_abort); @@ -2281,7 +2222,7 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) static const TypeInfo pc_machine_info = { .name = TYPE_PC_MACHINE, - .parent = TYPE_MACHINE, + .parent = TYPE_X86_MACHINE, .abstract = true, .instance_size = sizeof(PCMachineState), .instance_init = pc_machine_initfn, @@ -2289,7 +2230,6 @@ static const TypeInfo pc_machine_info = { .class_init = pc_machine_class_init, .interfaces = (InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, - { TYPE_NMI }, { } }, }; diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 1396451abf..0afa8fe6ea 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -74,6 +74,7 @@ static void pc_init1(MachineState *machine, { PCMachineState *pcms = PC_MACHINE(machine); PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); + X86MachineState *x86ms = X86_MACHINE(machine); MemoryRegion *system_memory = get_system_memory(); MemoryRegion *system_io = get_system_io(); int i; @@ -126,11 +127,11 @@ static void pc_init1(MachineState *machine, if (xen_enabled()) { xen_hvm_init(pcms, &ram_memory); } else { - if (!pcms->max_ram_below_4g) { - pcms->max_ram_below_4g = 0xe0000000; /* default: 3.5G */ + if (!x86ms->max_ram_below_4g) { + x86ms->max_ram_below_4g = 0xe0000000; /* default: 3.5G */ } - lowmem = pcms->max_ram_below_4g; - if (machine->ram_size >= pcms->max_ram_below_4g) { + lowmem = x86ms->max_ram_below_4g; + if (machine->ram_size >= x86ms->max_ram_below_4g) { if (pcmc->gigabyte_align) { if (lowmem > 0xc0000000) { lowmem = 0xc0000000; @@ -139,17 +140,17 @@ static void pc_init1(MachineState *machine, warn_report("Large machine and max_ram_below_4g " "(%" PRIu64 ") not a multiple of 1G; " "possible bad performance.", - pcms->max_ram_below_4g); + x86ms->max_ram_below_4g); } } } if (machine->ram_size >= lowmem) { - pcms->above_4g_mem_size = machine->ram_size - lowmem; - pcms->below_4g_mem_size = lowmem; + x86ms->above_4g_mem_size = machine->ram_size - lowmem; + x86ms->below_4g_mem_size = lowmem; } else { - pcms->above_4g_mem_size = 0; - pcms->below_4g_mem_size = machine->ram_size; + x86ms->above_4g_mem_size = 0; + x86ms->below_4g_mem_size = machine->ram_size; } } @@ -191,19 +192,19 @@ static void pc_init1(MachineState *machine, gsi_state = g_malloc0(sizeof(*gsi_state)); if (kvm_ioapic_in_kernel()) { kvm_pc_setup_irq_routing(pcmc->pci_enabled); - pcms->gsi = qemu_allocate_irqs(kvm_pc_gsi_handler, gsi_state, + x86ms->gsi = qemu_allocate_irqs(kvm_pc_gsi_handler, gsi_state, GSI_NUM_PINS); } else { - pcms->gsi = qemu_allocate_irqs(gsi_handler, gsi_state, GSI_NUM_PINS); + x86ms->gsi = qemu_allocate_irqs(gsi_handler, gsi_state, GSI_NUM_PINS); } if (pcmc->pci_enabled) { pci_bus = i440fx_init(host_type, pci_type, - &i440fx_state, &piix3_devfn, &isa_bus, pcms->gsi, + &i440fx_state, &piix3_devfn, &isa_bus, x86ms->gsi, system_memory, system_io, machine->ram_size, - pcms->below_4g_mem_size, - pcms->above_4g_mem_size, + x86ms->below_4g_mem_size, + x86ms->above_4g_mem_size, pci_memory, ram_memory); pcms->bus = pci_bus; } else { @@ -213,7 +214,7 @@ static void pc_init1(MachineState *machine, &error_abort); no_hpet = 1; } - isa_bus_irqs(isa_bus, pcms->gsi); + isa_bus_irqs(isa_bus, x86ms->gsi); if (kvm_pic_in_kernel()) { i8259 = kvm_i8259_init(isa_bus); @@ -231,7 +232,7 @@ static void pc_init1(MachineState *machine, ioapic_init_gsi(gsi_state, "i440fx"); } - pc_register_ferr_irq(pcms->gsi[13]); + pc_register_ferr_irq(x86ms->gsi[13]); pc_vga_init(isa_bus, pcmc->pci_enabled ? pci_bus : NULL); @@ -241,7 +242,7 @@ static void pc_init1(MachineState *machine, } /* init basic PC hardware */ - pc_basic_device_init(isa_bus, pcms->gsi, &rtc_state, true, + pc_basic_device_init(isa_bus, x86ms->gsi, &rtc_state, true, (pcms->vmport != ON_OFF_AUTO_ON), pcms->pit_enabled, 0x4); @@ -288,7 +289,7 @@ else { smi_irq = qemu_allocate_irq(pc_acpi_smi_interrupt, first_cpu, 0); /* TODO: Populate SPD eeprom data. */ pcms->smbus = piix4_pm_init(pci_bus, piix3_devfn + 3, 0xb100, - pcms->gsi[9], smi_irq, + x86ms->gsi[9], smi_irq, pc_machine_is_smm_enabled(pcms), &piix4_pm); smbus_eeprom_init(pcms->smbus, 8, NULL, 0); @@ -304,7 +305,7 @@ else { if (machine->nvdimms_state->is_enabled) { nvdimm_init_acpi_state(machine->nvdimms_state, system_io, - pcms->fw_cfg, OBJECT(pcms)); + x86ms->fw_cfg, OBJECT(pcms)); } } @@ -729,7 +730,7 @@ DEFINE_I440FX_MACHINE(v1_4, "pc-i440fx-1.4", pc_compat_1_4_fn, static void pc_i440fx_1_3_machine_options(MachineClass *m) { - PCMachineClass *pcmc = PC_MACHINE_CLASS(m); + X86MachineClass *x86mc = X86_MACHINE_CLASS(m); static GlobalProperty compat[] = { PC_CPU_MODEL_IDS("1.3.0") { "usb-tablet", "usb_version", "1" }, @@ -740,7 +741,7 @@ static void pc_i440fx_1_3_machine_options(MachineClass *m) pc_i440fx_1_4_machine_options(m); m->hw_version = "1.3.0"; - pcmc->compat_apic_id_mode = true; + x86mc->compat_apic_id_mode = true; compat_props_add(m->compat_props, compat, G_N_ELEMENTS(compat)); } diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 8920bd8978..374ac6c068 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -116,6 +116,7 @@ static void pc_q35_init(MachineState *machine) { PCMachineState *pcms = PC_MACHINE(machine); PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); + X86MachineState *x86ms = X86_MACHINE(machine); Q35PCIHost *q35_host; PCIHostState *phb; PCIBus *host_bus; @@ -153,27 +154,27 @@ static void pc_q35_init(MachineState *machine) /* Handle the machine opt max-ram-below-4g. It is basically doing * min(qemu limit, user limit). */ - if (!pcms->max_ram_below_4g) { - pcms->max_ram_below_4g = 1ULL << 32; /* default: 4G */; + if (!x86ms->max_ram_below_4g) { + x86ms->max_ram_below_4g = 4 * GiB; } - if (lowmem > pcms->max_ram_below_4g) { - lowmem = pcms->max_ram_below_4g; + if (lowmem > x86ms->max_ram_below_4g) { + lowmem = x86ms->max_ram_below_4g; if (machine->ram_size - lowmem > lowmem && lowmem & (1 * GiB - 1)) { warn_report("There is possibly poor performance as the ram size " " (0x%" PRIx64 ") is more then twice the size of" " max-ram-below-4g (%"PRIu64") and" " max-ram-below-4g is not a multiple of 1G.", - (uint64_t)machine->ram_size, pcms->max_ram_below_4g); + (uint64_t)machine->ram_size, x86ms->max_ram_below_4g); } } if (machine->ram_size >= lowmem) { - pcms->above_4g_mem_size = machine->ram_size - lowmem; - pcms->below_4g_mem_size = lowmem; + x86ms->above_4g_mem_size = machine->ram_size - lowmem; + x86ms->below_4g_mem_size = lowmem; } else { - pcms->above_4g_mem_size = 0; - pcms->below_4g_mem_size = machine->ram_size; + x86ms->above_4g_mem_size = 0; + x86ms->below_4g_mem_size = machine->ram_size; } if (xen_enabled()) { @@ -214,10 +215,10 @@ static void pc_q35_init(MachineState *machine) gsi_state = g_malloc0(sizeof(*gsi_state)); if (kvm_ioapic_in_kernel()) { kvm_pc_setup_irq_routing(pcmc->pci_enabled); - pcms->gsi = qemu_allocate_irqs(kvm_pc_gsi_handler, gsi_state, + x86ms->gsi = qemu_allocate_irqs(kvm_pc_gsi_handler, gsi_state, GSI_NUM_PINS); } else { - pcms->gsi = qemu_allocate_irqs(gsi_handler, gsi_state, GSI_NUM_PINS); + x86ms->gsi = qemu_allocate_irqs(gsi_handler, gsi_state, GSI_NUM_PINS); } /* create pci host bus */ @@ -232,9 +233,9 @@ static void pc_q35_init(MachineState *machine) MCH_HOST_PROP_SYSTEM_MEM, NULL); object_property_set_link(OBJECT(q35_host), OBJECT(system_io), MCH_HOST_PROP_IO_MEM, NULL); - object_property_set_int(OBJECT(q35_host), pcms->below_4g_mem_size, + object_property_set_int(OBJECT(q35_host), x86ms->below_4g_mem_size, PCI_HOST_BELOW_4G_MEM_SIZE, NULL); - object_property_set_int(OBJECT(q35_host), pcms->above_4g_mem_size, + object_property_set_int(OBJECT(q35_host), x86ms->above_4g_mem_size, PCI_HOST_ABOVE_4G_MEM_SIZE, NULL); /* pci */ qdev_init_nofail(DEVICE(q35_host)); @@ -256,7 +257,7 @@ static void pc_q35_init(MachineState *machine) ich9_lpc = ICH9_LPC_DEVICE(lpc); lpc_dev = DEVICE(lpc); for (i = 0; i < GSI_NUM_PINS; i++) { - qdev_connect_gpio_out_named(lpc_dev, ICH9_GPIO_GSI, i, pcms->gsi[i]); + qdev_connect_gpio_out_named(lpc_dev, ICH9_GPIO_GSI, i, x86ms->gsi[i]); } pci_bus_irqs(host_bus, ich9_lpc_set_irq, ich9_lpc_map_irq, ich9_lpc, ICH9_LPC_NB_PIRQS); @@ -280,7 +281,7 @@ static void pc_q35_init(MachineState *machine) ioapic_init_gsi(gsi_state, "q35"); } - pc_register_ferr_irq(pcms->gsi[13]); + pc_register_ferr_irq(x86ms->gsi[13]); assert(pcms->vmport != ON_OFF_AUTO__MAX); if (pcms->vmport == ON_OFF_AUTO_AUTO) { @@ -288,7 +289,7 @@ static void pc_q35_init(MachineState *machine) } /* init basic PC hardware */ - pc_basic_device_init(isa_bus, pcms->gsi, &rtc_state, !mc->no_floppy, + pc_basic_device_init(isa_bus, x86ms->gsi, &rtc_state, !mc->no_floppy, (pcms->vmport != ON_OFF_AUTO_ON), pcms->pit_enabled, 0xff0104); @@ -331,7 +332,7 @@ static void pc_q35_init(MachineState *machine) if (machine->nvdimms_state->is_enabled) { nvdimm_init_acpi_state(machine->nvdimms_state, system_io, - pcms->fw_cfg, OBJECT(pcms)); + x86ms->fw_cfg, OBJECT(pcms)); } } diff --git a/hw/i386/x86.c b/hw/i386/x86.c index 71fb093983..de4fed0164 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -65,13 +65,14 @@ uint32_t x86_cpu_apic_id_from_index(PCMachineState *pcms, unsigned int cpu_index) { MachineState *ms = MACHINE(pcms); - PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); + X86MachineState *x86ms = X86_MACHINE(pcms); + X86MachineClass *x86mc = X86_MACHINE_GET_CLASS(x86ms); uint32_t correct_id; static bool warned; - correct_id = x86_apicid_from_cpu_idx(pcms->smp_dies, ms->smp.cores, + correct_id = x86_apicid_from_cpu_idx(x86ms->smp_dies, ms->smp.cores, ms->smp.threads, cpu_index); - if (pcmc->compat_apic_id_mode) { + if (x86mc->compat_apic_id_mode) { if (cpu_index != correct_id && !warned && !qtest_enabled()) { error_report("APIC IDs set in compatibility mode, " "CPU topology won't match the configuration"); @@ -88,11 +89,12 @@ void x86_cpu_new(PCMachineState *pcms, int64_t apic_id, Error **errp) Object *cpu = NULL; Error *local_err = NULL; CPUX86State *env = NULL; + X86MachineState *x86ms = X86_MACHINE(pcms); cpu = object_new(MACHINE(pcms)->cpu_type); env = &X86_CPU(cpu)->env; - env->nr_dies = pcms->smp_dies; + env->nr_dies = x86ms->smp_dies; object_property_set_uint(cpu, apic_id, "apic-id", &local_err); object_property_set_bool(cpu, true, "realized", &local_err); @@ -108,6 +110,7 @@ void x86_cpus_init(PCMachineState *pcms) MachineState *ms = MACHINE(pcms); MachineClass *mc = MACHINE_GET_CLASS(pcms); PCMachineClass *pcmc = PC_MACHINE_CLASS(mc); + X86MachineState *x86ms = X86_MACHINE(pcms); x86_cpu_set_default_version(pcmc->default_cpu_version); @@ -119,8 +122,8 @@ void x86_cpus_init(PCMachineState *pcms) * * This is used for FW_CFG_MAX_CPUS. See comments on fw_cfg_arch_create(). */ - pcms->apic_id_limit = x86_cpu_apic_id_from_index(pcms, - ms->smp.max_cpus - 1) + 1; + x86ms->apic_id_limit = x86_cpu_apic_id_from_index(pcms, + ms->smp.max_cpus - 1) + 1; possible_cpus = mc->possible_cpu_arch_ids(ms); for (i = 0; i < ms->smp.cpus; i++) { x86_cpu_new(pcms, possible_cpus->cpus[i].arch_id, &error_fatal); @@ -140,11 +143,11 @@ x86_cpu_index_to_props(MachineState *ms, unsigned cpu_index) int64_t x86_get_default_cpu_node_id(const MachineState *ms, int idx) { X86CPUTopoInfo topo; - PCMachineState *pcms = PC_MACHINE(ms); + X86MachineState *x86ms = X86_MACHINE(ms); assert(idx < ms->possible_cpus->len); x86_topo_ids_from_apicid(ms->possible_cpus->cpus[idx].arch_id, - pcms->smp_dies, ms->smp.cores, + x86ms->smp_dies, ms->smp.cores, ms->smp.threads, &topo); return topo.pkg_id % ms->numa_state->num_nodes; } @@ -152,6 +155,7 @@ int64_t x86_get_default_cpu_node_id(const MachineState *ms, int idx) const CPUArchIdList *x86_possible_cpu_arch_ids(MachineState *ms) { PCMachineState *pcms = PC_MACHINE(ms); + X86MachineState *x86ms = X86_MACHINE(ms); int i; unsigned int max_cpus = ms->smp.max_cpus; @@ -175,11 +179,11 @@ const CPUArchIdList *x86_possible_cpu_arch_ids(MachineState *ms) ms->possible_cpus->cpus[i].arch_id = x86_cpu_apic_id_from_index(pcms, i); x86_topo_ids_from_apicid(ms->possible_cpus->cpus[i].arch_id, - pcms->smp_dies, ms->smp.cores, + x86ms->smp_dies, ms->smp.cores, ms->smp.threads, &topo); ms->possible_cpus->cpus[i].props.has_socket_id = true; ms->possible_cpus->cpus[i].props.socket_id = topo.pkg_id; - if (pcms->smp_dies > 1) { + if (x86ms->smp_dies > 1) { ms->possible_cpus->cpus[i].props.has_die_id = true; ms->possible_cpus->cpus[i].props.die_id = topo.die_id; } @@ -191,6 +195,22 @@ const CPUArchIdList *x86_possible_cpu_arch_ids(MachineState *ms) return ms->possible_cpus; } +static void x86_nmi(NMIState *n, int cpu_index, Error **errp) +{ + /* cpu index isn't used */ + CPUState *cs; + + CPU_FOREACH(cs) { + X86CPU *cpu = X86_CPU(cs); + + if (!cpu->apic_state) { + cpu_interrupt(cs, CPU_INTERRUPT_NMI); + } else { + apic_deliver_nmi(cpu->apic_state); + } + } +} + static long get_file_size(FILE *f) { long where, size; @@ -328,6 +348,7 @@ void x86_load_linux(PCMachineState *pcms, char *vmode; MachineState *machine = MACHINE(pcms); PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); + X86MachineState *x86ms = X86_MACHINE(pcms); struct setup_data *setup_data; const char *kernel_filename = machine->kernel_filename; const char *initrd_filename = machine->initrd_filename; @@ -400,11 +421,12 @@ void x86_load_linux(PCMachineState *pcms, initrd_filename, gerr->message); exit(1); } - pcms->initrd_mapped_file = mapped_file; + x86ms->initrd_mapped_file = mapped_file; initrd_data = g_mapped_file_get_contents(mapped_file); initrd_size = g_mapped_file_get_length(mapped_file); - initrd_max = pcms->below_4g_mem_size - pcmc->acpi_data_size - 1; + initrd_max = + x86ms->below_4g_mem_size - pcmc->acpi_data_size - 1; if (initrd_size >= initrd_max) { fprintf(stderr, "qemu: initrd is too large, cannot support." "(max: %"PRIu32", need %"PRId64")\n", @@ -472,8 +494,8 @@ void x86_load_linux(PCMachineState *pcms, initrd_max = 0x37ffffff; } - if (initrd_max >= pcms->below_4g_mem_size - pcmc->acpi_data_size) { - initrd_max = pcms->below_4g_mem_size - pcmc->acpi_data_size - 1; + if (initrd_max >= x86ms->below_4g_mem_size - pcmc->acpi_data_size) { + initrd_max = x86ms->below_4g_mem_size - pcmc->acpi_data_size - 1; } fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_ADDR, cmdline_addr); @@ -544,7 +566,7 @@ void x86_load_linux(PCMachineState *pcms, initrd_filename, gerr->message); exit(1); } - pcms->initrd_mapped_file = mapped_file; + x86ms->initrd_mapped_file = mapped_file; initrd_data = g_mapped_file_get_contents(mapped_file); initrd_size = g_mapped_file_get_length(mapped_file); @@ -688,3 +710,91 @@ void x86_bios_rom_init(MemoryRegion *rom_memory, bool isapc_ram_fw) (uint32_t)(-bios_size), bios); } + +static void x86_machine_get_max_ram_below_4g(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + X86MachineState *x86ms = X86_MACHINE(obj); + uint64_t value = x86ms->max_ram_below_4g; + + visit_type_size(v, name, &value, errp); +} + +static void x86_machine_set_max_ram_below_4g(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + X86MachineState *x86ms = X86_MACHINE(obj); + Error *error = NULL; + uint64_t value; + + visit_type_size(v, name, &value, &error); + if (error) { + error_propagate(errp, error); + return; + } + if (value > 4 * GiB) { + error_setg(&error, + "Machine option 'max-ram-below-4g=%"PRIu64 + "' expects size less than or equal to 4G", value); + error_propagate(errp, error); + return; + } + + if (value < 1 * MiB) { + warn_report("Only %" PRIu64 " bytes of RAM below the 4GiB boundary," + "BIOS may not work with less than 1MiB", value); + } + + x86ms->max_ram_below_4g = value; +} + +static void x86_machine_initfn(Object *obj) +{ + X86MachineState *x86ms = X86_MACHINE(obj); + + x86ms->max_ram_below_4g = 0; /* use default */ + x86ms->smp_dies = 1; +} + +static void x86_machine_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + X86MachineClass *x86mc = X86_MACHINE_CLASS(oc); + NMIClass *nc = NMI_CLASS(oc); + + mc->cpu_index_to_instance_props = x86_cpu_index_to_props; + mc->get_default_cpu_node_id = x86_get_default_cpu_node_id; + mc->possible_cpu_arch_ids = x86_possible_cpu_arch_ids; + x86mc->compat_apic_id_mode = false; + nc->nmi_monitor_handler = x86_nmi; + + object_class_property_add(oc, X86_MACHINE_MAX_RAM_BELOW_4G, "size", + x86_machine_get_max_ram_below_4g, x86_machine_set_max_ram_below_4g, + NULL, NULL, &error_abort); + + object_class_property_set_description(oc, X86_MACHINE_MAX_RAM_BELOW_4G, + "Maximum ram below the 4G boundary (32bit boundary)", &error_abort); +} + +static const TypeInfo x86_machine_info = { + .name = TYPE_X86_MACHINE, + .parent = TYPE_MACHINE, + .abstract = true, + .instance_size = sizeof(X86MachineState), + .instance_init = x86_machine_initfn, + .class_size = sizeof(X86MachineClass), + .class_init = x86_machine_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_NMI }, + { } + }, +}; + +static void x86_machine_register_types(void) +{ + type_register_static(&x86_machine_info); +} + +type_init(x86_machine_register_types) diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c index 6b5e5bb7f5..95f23a263c 100644 --- a/hw/i386/xen/xen-hvm.c +++ b/hw/i386/xen/xen-hvm.c @@ -197,11 +197,13 @@ qemu_irq *xen_interrupt_controller_init(void) static void xen_ram_init(PCMachineState *pcms, ram_addr_t ram_size, MemoryRegion **ram_memory_p) { + X86MachineState *x86ms = X86_MACHINE(pcms); MemoryRegion *sysmem = get_system_memory(); ram_addr_t block_len; - uint64_t user_lowmem = object_property_get_uint(qdev_get_machine(), - PC_MACHINE_MAX_RAM_BELOW_4G, - &error_abort); + uint64_t user_lowmem = + object_property_get_uint(qdev_get_machine(), + X86_MACHINE_MAX_RAM_BELOW_4G, + &error_abort); /* Handle the machine opt max-ram-below-4g. It is basically doing * min(xen limit, user limit). @@ -214,20 +216,20 @@ static void xen_ram_init(PCMachineState *pcms, } if (ram_size >= user_lowmem) { - pcms->above_4g_mem_size = ram_size - user_lowmem; - pcms->below_4g_mem_size = user_lowmem; + x86ms->above_4g_mem_size = ram_size - user_lowmem; + x86ms->below_4g_mem_size = user_lowmem; } else { - pcms->above_4g_mem_size = 0; - pcms->below_4g_mem_size = ram_size; + x86ms->above_4g_mem_size = 0; + x86ms->below_4g_mem_size = ram_size; } - if (!pcms->above_4g_mem_size) { + if (!x86ms->above_4g_mem_size) { block_len = ram_size; } else { /* * Xen does not allocate the memory continuously, it keeps a * hole of the size computed above or passed in. */ - block_len = (1ULL << 32) + pcms->above_4g_mem_size; + block_len = (1ULL << 32) + x86ms->above_4g_mem_size; } memory_region_init_ram(&ram_memory, NULL, "xen.ram", block_len, &error_fatal); @@ -244,12 +246,12 @@ static void xen_ram_init(PCMachineState *pcms, */ memory_region_init_alias(&ram_lo, NULL, "xen.ram.lo", &ram_memory, 0xc0000, - pcms->below_4g_mem_size - 0xc0000); + x86ms->below_4g_mem_size - 0xc0000); memory_region_add_subregion(sysmem, 0xc0000, &ram_lo); - if (pcms->above_4g_mem_size > 0) { + if (x86ms->above_4g_mem_size > 0) { memory_region_init_alias(&ram_hi, NULL, "xen.ram.hi", &ram_memory, 0x100000000ULL, - pcms->above_4g_mem_size); + x86ms->above_4g_mem_size); memory_region_add_subregion(sysmem, 0x100000000ULL, &ram_hi); } } @@ -265,7 +267,7 @@ void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size, MemoryRegion *mr, /* RAM already populated in Xen */ fprintf(stderr, "%s: do not alloc "RAM_ADDR_FMT " bytes of ram at "RAM_ADDR_FMT" when runstate is INMIGRATE\n", - __func__, size, ram_addr); + __func__, size, ram_addr); return; } diff --git a/hw/intc/ioapic.c b/hw/intc/ioapic.c index 1ede055387..ead14e1888 100644 --- a/hw/intc/ioapic.c +++ b/hw/intc/ioapic.c @@ -89,7 +89,7 @@ static void ioapic_entry_parse(uint64_t entry, struct ioapic_entry_info *info) static void ioapic_service(IOAPICCommonState *s) { - AddressSpace *ioapic_as = PC_MACHINE(qdev_get_machine())->ioapic_as; + AddressSpace *ioapic_as = X86_MACHINE(qdev_get_machine())->ioapic_as; struct ioapic_entry_info info; uint8_t i; uint32_t mask; diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index f135ae8c07..13c4eac0f6 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -8,6 +8,7 @@ #include "hw/block/flash.h" #include "net/net.h" #include "hw/i386/ioapic.h" +#include "hw/i386/x86.h" #include "qemu/range.h" #include "qemu/bitmap.h" @@ -27,7 +28,7 @@ */ struct PCMachineState { /*< private >*/ - MachineState parent_obj; + X86MachineState parent_obj; /* */ @@ -36,16 +37,11 @@ struct PCMachineState { /* Pointers to devices and objects: */ HotplugHandler *acpi_dev; - ISADevice *rtc; PCIBus *bus; I2CBus *smbus; - FWCfgState *fw_cfg; - qemu_irq *gsi; PFlashCFI01 *flash[2]; - GMappedFile *initrd_mapped_file; /* Configuration options: */ - uint64_t max_ram_below_4g; OnOffAuto vmport; OnOffAuto smm; @@ -54,30 +50,16 @@ struct PCMachineState { bool sata_enabled; bool pit_enabled; - /* RAM information (sizes, addresses, configuration): */ - ram_addr_t below_4g_mem_size, above_4g_mem_size; - - /* CPU and apic information: */ - bool apic_xrupt_override; - unsigned apic_id_limit; - uint16_t boot_cpus; - unsigned smp_dies; - /* NUMA information: */ uint64_t numa_nodes; uint64_t *node_mem; - /* Address space used by IOAPIC device. All IOAPIC interrupts - * will be translated to MSI messages in the address space. */ - AddressSpace *ioapic_as; - /* ACPI Memory hotplug IO base address */ hwaddr memhp_io_base; }; #define PC_MACHINE_ACPI_DEVICE_PROP "acpi-device" #define PC_MACHINE_DEVMEM_REGION_SIZE "device-memory-region-size" -#define PC_MACHINE_MAX_RAM_BELOW_4G "max-ram-below-4g" #define PC_MACHINE_VMPORT "vmport" #define PC_MACHINE_SMM "smm" #define PC_MACHINE_SMBUS "smbus" @@ -102,7 +84,7 @@ struct PCMachineState { */ typedef struct PCMachineClass { /*< private >*/ - MachineClass parent_class; + X86MachineClass parent_class; /*< public >*/ @@ -144,9 +126,6 @@ typedef struct PCMachineClass { /* use PVH to load kernels that support this feature */ bool pvh_enabled; - - /* Enables contiguous-apic-ID mode */ - bool compat_apic_id_mode; } PCMachineClass; #define TYPE_PC_MACHINE "generic-pc-machine" diff --git a/include/hw/i386/x86.h b/include/hw/i386/x86.h index 71e2b6985d..d15713e92e 100644 --- a/include/hw/i386/x86.h +++ b/include/hw/i386/x86.h @@ -17,7 +17,63 @@ #ifndef HW_I386_X86_H #define HW_I386_X86_H +#include "qemu-common.h" +#include "exec/hwaddr.h" +#include "qemu/notify.h" + #include "hw/boards.h" +#include "hw/nmi.h" + +typedef struct { + /*< private >*/ + MachineClass parent; + + /*< public >*/ + + /* Enables contiguous-apic-ID mode */ + bool compat_apic_id_mode; +} X86MachineClass; + +typedef struct { + /*< private >*/ + MachineState parent; + + /*< public >*/ + + /* Pointers to devices and objects: */ + ISADevice *rtc; + FWCfgState *fw_cfg; + qemu_irq *gsi; + GMappedFile *initrd_mapped_file; + + /* Configuration options: */ + uint64_t max_ram_below_4g; + + /* RAM information (sizes, addresses, configuration): */ + ram_addr_t below_4g_mem_size, above_4g_mem_size; + + /* CPU and apic information: */ + bool apic_xrupt_override; + unsigned apic_id_limit; + uint16_t boot_cpus; + unsigned smp_dies; + + /* + * Address space used by IOAPIC device. All IOAPIC interrupts + * will be translated to MSI messages in the address space. + */ + AddressSpace *ioapic_as; +} X86MachineState; + +#define X86_MACHINE_MAX_RAM_BELOW_4G "max-ram-below-4g" + +#define TYPE_X86_MACHINE MACHINE_TYPE_NAME("x86") +#define X86_MACHINE(obj) \ + OBJECT_CHECK(X86MachineState, (obj), TYPE_X86_MACHINE) +#define X86_MACHINE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(X86MachineClass, obj, TYPE_X86_MACHINE) +#define X86_MACHINE_CLASS(class) \ + OBJECT_CLASS_CHECK(X86MachineClass, class, TYPE_X86_MACHINE) uint32_t x86_cpu_apic_id_from_index(PCMachineState *pcms, unsigned int cpu_index); @@ -30,6 +86,6 @@ const CPUArchIdList *x86_possible_cpu_arch_ids(MachineState *ms); void x86_bios_rom_init(MemoryRegion *rom_memory, bool isapc_ram_fw); -void x86_load_linux(PCMachineState *x86ms, FWCfgState *fw_cfg); +void x86_load_linux(PCMachineState *pcms, FWCfgState *fw_cfg); #endif From 703a548aa9f148186ed8647092e8645e5a85f373 Mon Sep 17 00:00:00 2001 From: Sergio Lopez Date: Mon, 30 Sep 2019 17:26:29 +0200 Subject: [PATCH 15/34] hw/i386: make x86.c independent from PCMachineState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As a last step into splitting PCMachineState and deriving X86MachineState from it, make the functions previously extracted from pc.c to x86.c independent from PCMachineState, using X86MachineState instead. Signed-off-by: Sergio Lopez Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Reviewed-by: Michael S. Tsirkin --- hw/i386/pc.c | 14 ++++++++---- hw/i386/pc_piix.c | 2 +- hw/i386/pc_q35.c | 2 +- hw/i386/x86.c | 53 ++++++++++++++++++++----------------------- include/hw/i386/x86.h | 13 +++++++---- 5 files changed, 44 insertions(+), 40 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index e6ad1f1f36..a8888dd622 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -983,8 +983,8 @@ void pc_smp_parse(MachineState *ms, QemuOpts *opts) void pc_hot_add_cpu(MachineState *ms, const int64_t id, Error **errp) { - PCMachineState *pcms = PC_MACHINE(ms); - int64_t apic_id = x86_cpu_apic_id_from_index(pcms, id); + X86MachineState *x86ms = X86_MACHINE(ms); + int64_t apic_id = x86_cpu_apic_id_from_index(x86ms, id); Error *local_err = NULL; if (id < 0) { @@ -999,7 +999,8 @@ void pc_hot_add_cpu(MachineState *ms, const int64_t id, Error **errp) return; } - x86_cpu_new(PC_MACHINE(ms), apic_id, &local_err); + + x86_cpu_new(X86_MACHINE(ms), apic_id, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -1100,6 +1101,7 @@ void xen_load_linux(PCMachineState *pcms) { int i; FWCfgState *fw_cfg; + PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); X86MachineState *x86ms = X86_MACHINE(pcms); assert(MACHINE(pcms)->kernel_filename != NULL); @@ -1108,7 +1110,8 @@ void xen_load_linux(PCMachineState *pcms) fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus); rom_set_fw(fw_cfg); - x86_load_linux(pcms, fw_cfg); + x86_load_linux(x86ms, fw_cfg, pcmc->acpi_data_size, + pcmc->pvh_enabled, pcmc->linuxboot_dma_enabled); for (i = 0; i < nb_option_roms; i++) { assert(!strcmp(option_rom[i].name, "linuxboot.bin") || !strcmp(option_rom[i].name, "linuxboot_dma.bin") || @@ -1244,7 +1247,8 @@ void pc_memory_init(PCMachineState *pcms, } if (linux_boot) { - x86_load_linux(pcms, fw_cfg); + x86_load_linux(x86ms, fw_cfg, pcmc->acpi_data_size, + pcmc->pvh_enabled, pcmc->linuxboot_dma_enabled); } for (i = 0; i < nb_option_roms; i++) { diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 0afa8fe6ea..a86317cdff 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -154,7 +154,7 @@ static void pc_init1(MachineState *machine, } } - x86_cpus_init(pcms); + x86_cpus_init(x86ms, pcmc->default_cpu_version); if (kvm_enabled() && pcmc->kvmclock_enabled) { kvmclock_create(); diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 374ac6c068..75c8caf7c2 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -181,7 +181,7 @@ static void pc_q35_init(MachineState *machine) xen_hvm_init(pcms, &ram_memory); } - x86_cpus_init(pcms); + x86_cpus_init(x86ms, pcmc->default_cpu_version); kvmclock_create(); diff --git a/hw/i386/x86.c b/hw/i386/x86.c index de4fed0164..fd84b23124 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -36,7 +36,6 @@ #include "sysemu/sysemu.h" #include "hw/i386/x86.h" -#include "hw/i386/pc.h" #include "target/i386/cpu.h" #include "hw/i386/topology.h" #include "hw/i386/fw_cfg.h" @@ -61,11 +60,10 @@ static size_t pvh_start_addr; * no concept of "CPU index", and the NUMA tables on fw_cfg need the APIC ID of * all CPUs up to max_cpus. */ -uint32_t x86_cpu_apic_id_from_index(PCMachineState *pcms, +uint32_t x86_cpu_apic_id_from_index(X86MachineState *x86ms, unsigned int cpu_index) { - MachineState *ms = MACHINE(pcms); - X86MachineState *x86ms = X86_MACHINE(pcms); + MachineState *ms = MACHINE(x86ms); X86MachineClass *x86mc = X86_MACHINE_GET_CLASS(x86ms); uint32_t correct_id; static bool warned; @@ -84,14 +82,14 @@ uint32_t x86_cpu_apic_id_from_index(PCMachineState *pcms, } } -void x86_cpu_new(PCMachineState *pcms, int64_t apic_id, Error **errp) + +void x86_cpu_new(X86MachineState *x86ms, int64_t apic_id, Error **errp) { Object *cpu = NULL; Error *local_err = NULL; CPUX86State *env = NULL; - X86MachineState *x86ms = X86_MACHINE(pcms); - cpu = object_new(MACHINE(pcms)->cpu_type); + cpu = object_new(MACHINE(x86ms)->cpu_type); env = &X86_CPU(cpu)->env; env->nr_dies = x86ms->smp_dies; @@ -103,30 +101,28 @@ void x86_cpu_new(PCMachineState *pcms, int64_t apic_id, Error **errp) error_propagate(errp, local_err); } -void x86_cpus_init(PCMachineState *pcms) +void x86_cpus_init(X86MachineState *x86ms, int default_cpu_version) { int i; const CPUArchIdList *possible_cpus; - MachineState *ms = MACHINE(pcms); - MachineClass *mc = MACHINE_GET_CLASS(pcms); - PCMachineClass *pcmc = PC_MACHINE_CLASS(mc); - X86MachineState *x86ms = X86_MACHINE(pcms); + MachineState *ms = MACHINE(x86ms); + MachineClass *mc = MACHINE_GET_CLASS(x86ms); - x86_cpu_set_default_version(pcmc->default_cpu_version); + x86_cpu_set_default_version(default_cpu_version); /* * Calculates the limit to CPU APIC ID values * * Limit for the APIC ID value, so that all - * CPU APIC IDs are < pcms->apic_id_limit. + * CPU APIC IDs are < x86ms->apic_id_limit. * * This is used for FW_CFG_MAX_CPUS. See comments on fw_cfg_arch_create(). */ - x86ms->apic_id_limit = x86_cpu_apic_id_from_index(pcms, + x86ms->apic_id_limit = x86_cpu_apic_id_from_index(x86ms, ms->smp.max_cpus - 1) + 1; possible_cpus = mc->possible_cpu_arch_ids(ms); for (i = 0; i < ms->smp.cpus; i++) { - x86_cpu_new(pcms, possible_cpus->cpus[i].arch_id, &error_fatal); + x86_cpu_new(x86ms, possible_cpus->cpus[i].arch_id, &error_fatal); } } @@ -154,7 +150,6 @@ int64_t x86_get_default_cpu_node_id(const MachineState *ms, int idx) const CPUArchIdList *x86_possible_cpu_arch_ids(MachineState *ms) { - PCMachineState *pcms = PC_MACHINE(ms); X86MachineState *x86ms = X86_MACHINE(ms); int i; unsigned int max_cpus = ms->smp.max_cpus; @@ -177,7 +172,7 @@ const CPUArchIdList *x86_possible_cpu_arch_ids(MachineState *ms) ms->possible_cpus->cpus[i].type = ms->cpu_type; ms->possible_cpus->cpus[i].vcpus_count = 1; ms->possible_cpus->cpus[i].arch_id = - x86_cpu_apic_id_from_index(pcms, i); + x86_cpu_apic_id_from_index(x86ms, i); x86_topo_ids_from_apicid(ms->possible_cpus->cpus[i].arch_id, x86ms->smp_dies, ms->smp.cores, ms->smp.threads, &topo); @@ -335,8 +330,11 @@ static bool load_elfboot(const char *kernel_filename, return true; } -void x86_load_linux(PCMachineState *pcms, - FWCfgState *fw_cfg) +void x86_load_linux(X86MachineState *x86ms, + FWCfgState *fw_cfg, + int acpi_data_size, + bool pvh_enabled, + bool linuxboot_dma_enabled) { uint16_t protocol; int setup_size, kernel_size, cmdline_size; @@ -346,9 +344,7 @@ void x86_load_linux(PCMachineState *pcms, hwaddr real_addr, prot_addr, cmdline_addr, initrd_addr = 0; FILE *f; char *vmode; - MachineState *machine = MACHINE(pcms); - PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); - X86MachineState *x86ms = X86_MACHINE(pcms); + MachineState *machine = MACHINE(x86ms); struct setup_data *setup_data; const char *kernel_filename = machine->kernel_filename; const char *initrd_filename = machine->initrd_filename; @@ -395,7 +391,7 @@ void x86_load_linux(PCMachineState *pcms, * saving the PVH entry point used by the x86/HVM direct boot ABI. * If load_elfboot() is successful, populate the fw_cfg info. */ - if (pcmc->pvh_enabled && + if (pvh_enabled && load_elfboot(kernel_filename, kernel_size, header, pvh_start_addr, fw_cfg)) { fclose(f); @@ -425,8 +421,7 @@ void x86_load_linux(PCMachineState *pcms, initrd_data = g_mapped_file_get_contents(mapped_file); initrd_size = g_mapped_file_get_length(mapped_file); - initrd_max = - x86ms->below_4g_mem_size - pcmc->acpi_data_size - 1; + initrd_max = x86ms->below_4g_mem_size - acpi_data_size - 1; if (initrd_size >= initrd_max) { fprintf(stderr, "qemu: initrd is too large, cannot support." "(max: %"PRIu32", need %"PRId64")\n", @@ -494,8 +489,8 @@ void x86_load_linux(PCMachineState *pcms, initrd_max = 0x37ffffff; } - if (initrd_max >= x86ms->below_4g_mem_size - pcmc->acpi_data_size) { - initrd_max = x86ms->below_4g_mem_size - pcmc->acpi_data_size - 1; + if (initrd_max >= x86ms->below_4g_mem_size - acpi_data_size) { + initrd_max = x86ms->below_4g_mem_size - acpi_data_size - 1; } fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_ADDR, cmdline_addr); @@ -652,7 +647,7 @@ void x86_load_linux(PCMachineState *pcms, option_rom[nb_option_roms].bootindex = 0; option_rom[nb_option_roms].name = "linuxboot.bin"; - if (pcmc->linuxboot_dma_enabled && fw_cfg_dma_enabled(fw_cfg)) { + if (linuxboot_dma_enabled && fw_cfg_dma_enabled(fw_cfg)) { option_rom[nb_option_roms].name = "linuxboot_dma.bin"; } nb_option_roms++; diff --git a/include/hw/i386/x86.h b/include/hw/i386/x86.h index d15713e92e..82d09fd7d0 100644 --- a/include/hw/i386/x86.h +++ b/include/hw/i386/x86.h @@ -75,10 +75,11 @@ typedef struct { #define X86_MACHINE_CLASS(class) \ OBJECT_CLASS_CHECK(X86MachineClass, class, TYPE_X86_MACHINE) -uint32_t x86_cpu_apic_id_from_index(PCMachineState *pcms, +uint32_t x86_cpu_apic_id_from_index(X86MachineState *pcms, unsigned int cpu_index); -void x86_cpu_new(PCMachineState *pcms, int64_t apic_id, Error **errp); -void x86_cpus_init(PCMachineState *pcms); + +void x86_cpu_new(X86MachineState *pcms, int64_t apic_id, Error **errp); +void x86_cpus_init(X86MachineState *pcms, int default_cpu_version); CpuInstanceProperties x86_cpu_index_to_props(MachineState *ms, unsigned cpu_index); int64_t x86_get_default_cpu_node_id(const MachineState *ms, int idx); @@ -86,6 +87,10 @@ const CPUArchIdList *x86_possible_cpu_arch_ids(MachineState *ms); void x86_bios_rom_init(MemoryRegion *rom_memory, bool isapc_ram_fw); -void x86_load_linux(PCMachineState *pcms, FWCfgState *fw_cfg); +void x86_load_linux(X86MachineState *x86ms, + FWCfgState *fw_cfg, + int acpi_data_size, + bool pvh_enabled, + bool linuxboot_dma_enabled); #endif From e5f6aa319f7636168469b2c797249680fa5e0413 Mon Sep 17 00:00:00 2001 From: Sergio Lopez Date: Tue, 24 Sep 2019 11:38:18 +0200 Subject: [PATCH 16/34] fw_cfg: add "modify" functions for all types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows to alter the contents of an already added item. Signed-off-by: Sergio Lopez Reviewed-by: Michael S. Tsirkin Reviewed-by: Marc-André Lureau --- hw/nvram/fw_cfg.c | 29 +++++++++++++++++++++++++++ include/hw/nvram/fw_cfg.h | 42 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index 7dc3ac378e..aef1727250 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -690,6 +690,15 @@ void fw_cfg_add_string(FWCfgState *s, uint16_t key, const char *value) fw_cfg_add_bytes(s, key, g_memdup(value, sz), sz); } +void fw_cfg_modify_string(FWCfgState *s, uint16_t key, const char *value) +{ + size_t sz = strlen(value) + 1; + char *old; + + old = fw_cfg_modify_bytes_read(s, key, g_memdup(value, sz), sz); + g_free(old); +} + void fw_cfg_add_i16(FWCfgState *s, uint16_t key, uint16_t value) { uint16_t *copy; @@ -720,6 +729,16 @@ void fw_cfg_add_i32(FWCfgState *s, uint16_t key, uint32_t value) fw_cfg_add_bytes(s, key, copy, sizeof(value)); } +void fw_cfg_modify_i32(FWCfgState *s, uint16_t key, uint32_t value) +{ + uint32_t *copy, *old; + + copy = g_malloc(sizeof(value)); + *copy = cpu_to_le32(value); + old = fw_cfg_modify_bytes_read(s, key, copy, sizeof(value)); + g_free(old); +} + void fw_cfg_add_i64(FWCfgState *s, uint16_t key, uint64_t value) { uint64_t *copy; @@ -730,6 +749,16 @@ void fw_cfg_add_i64(FWCfgState *s, uint16_t key, uint64_t value) fw_cfg_add_bytes(s, key, copy, sizeof(value)); } +void fw_cfg_modify_i64(FWCfgState *s, uint16_t key, uint64_t value) +{ + uint64_t *copy, *old; + + copy = g_malloc(sizeof(value)); + *copy = cpu_to_le64(value); + old = fw_cfg_modify_bytes_read(s, key, copy, sizeof(value)); + g_free(old); +} + void fw_cfg_set_order_override(FWCfgState *s, int order) { assert(s->fw_cfg_order_override == 0); diff --git a/include/hw/nvram/fw_cfg.h b/include/hw/nvram/fw_cfg.h index 80e435d303..b5291eefad 100644 --- a/include/hw/nvram/fw_cfg.h +++ b/include/hw/nvram/fw_cfg.h @@ -98,6 +98,20 @@ void fw_cfg_add_bytes(FWCfgState *s, uint16_t key, void *data, size_t len); */ void fw_cfg_add_string(FWCfgState *s, uint16_t key, const char *value); +/** + * fw_cfg_modify_string: + * @s: fw_cfg device being modified + * @key: selector key value for new fw_cfg item + * @value: NUL-terminated ascii string + * + * Replace the fw_cfg item available by selecting the given key. The new + * data will consist of a dynamically allocated copy of the provided string, + * including its NUL terminator. The data being replaced, assumed to have + * been dynamically allocated during an earlier call to either + * fw_cfg_add_string() or fw_cfg_modify_string(), is freed before returning. + */ +void fw_cfg_modify_string(FWCfgState *s, uint16_t key, const char *value); + /** * fw_cfg_add_i16: * @s: fw_cfg device being modified @@ -136,6 +150,20 @@ void fw_cfg_modify_i16(FWCfgState *s, uint16_t key, uint16_t value); */ void fw_cfg_add_i32(FWCfgState *s, uint16_t key, uint32_t value); +/** + * fw_cfg_modify_i32: + * @s: fw_cfg device being modified + * @key: selector key value for new fw_cfg item + * @value: 32-bit integer + * + * Replace the fw_cfg item available by selecting the given key. The new + * data will consist of a dynamically allocated copy of the given 32-bit + * value, converted to little-endian representation. The data being replaced, + * assumed to have been dynamically allocated during an earlier call to + * either fw_cfg_add_i32() or fw_cfg_modify_i32(), is freed before returning. + */ +void fw_cfg_modify_i32(FWCfgState *s, uint16_t key, uint32_t value); + /** * fw_cfg_add_i64: * @s: fw_cfg device being modified @@ -148,6 +176,20 @@ void fw_cfg_add_i32(FWCfgState *s, uint16_t key, uint32_t value); */ void fw_cfg_add_i64(FWCfgState *s, uint16_t key, uint64_t value); +/** + * fw_cfg_modify_i64: + * @s: fw_cfg device being modified + * @key: selector key value for new fw_cfg item + * @value: 64-bit integer + * + * Replace the fw_cfg item available by selecting the given key. The new + * data will consist of a dynamically allocated copy of the given 64-bit + * value, converted to little-endian representation. The data being replaced, + * assumed to have been dynamically allocated during an earlier call to + * either fw_cfg_add_i64() or fw_cfg_modify_i64(), is freed before returning. + */ +void fw_cfg_modify_i64(FWCfgState *s, uint16_t key, uint64_t value); + /** * fw_cfg_add_file: * @s: fw_cfg device being modified From 78cafff81088492fe70e12b3bded219096031f7c Mon Sep 17 00:00:00 2001 From: Sergio Lopez Date: Thu, 26 Sep 2019 14:48:00 +0200 Subject: [PATCH 17/34] hw/intc/apic: reject pic ints if isa_pic == NULL In apic_accept_pic_intr(), reject PIC interruptions if a i8259 PIC has not been instantiated (isa_pic == NULL). Suggested-by: Paolo Bonzini Signed-off-by: Sergio Lopez Reviewed-by: Michael S. Tsirkin --- hw/intc/apic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/intc/apic.c b/hw/intc/apic.c index bce89911dc..2a74f7b4bf 100644 --- a/hw/intc/apic.c +++ b/hw/intc/apic.c @@ -610,7 +610,7 @@ int apic_accept_pic_intr(DeviceState *dev) if ((s->apicbase & MSR_IA32_APICBASE_ENABLE) == 0 || (lvt0 & APIC_LVT_MASKED) == 0) - return 1; + return isa_pic != NULL; return 0; } From 0d5fae3e52e8a6b1068e53ebb87e96702246529e Mon Sep 17 00:00:00 2001 From: Sergio Lopez Date: Mon, 30 Sep 2019 17:36:58 +0200 Subject: [PATCH 18/34] roms: add microvm-bios (qboot) as binary and git submodule qboot is a minimalist x86 firmware for booting Linux kernels. It does the mininum amount of work required for the task, and it's able to boot both PVH images and bzImages without relying on option roms. This characteristics make it an ideal companion for the microvm machine type. Signed-off-by: Sergio Lopez Reviewed-by: Stefano Garzarella Reviewed-by: Michael S. Tsirkin --- .gitmodules | 3 +++ pc-bios/bios-microvm.bin | Bin 0 -> 65536 bytes roms/Makefile | 6 ++++++ roms/qboot | 1 + 4 files changed, 10 insertions(+) create mode 100644 pc-bios/bios-microvm.bin create mode 160000 roms/qboot diff --git a/.gitmodules b/.gitmodules index c5c474169d..19792c9a11 100644 --- a/.gitmodules +++ b/.gitmodules @@ -58,3 +58,6 @@ [submodule "roms/opensbi"] path = roms/opensbi url = https://git.qemu.org/git/opensbi.git +[submodule "roms/qboot"] + path = roms/qboot + url = https://github.com/bonzini/qboot diff --git a/pc-bios/bios-microvm.bin b/pc-bios/bios-microvm.bin new file mode 100644 index 0000000000000000000000000000000000000000..45eabc516692e2d134bbb630d133c7c2dcc9a9b6 GIT binary patch literal 65536 zcmeI2eS8zwneS)hF_vth5y2#;brv;O^x_6mtAUMO%tn5}IEf)jY``G|XbMBqa_^-@ zBBUXSyplo3+VMx9Ci}VFwz~@rx!t?By&v!0UdNb=VGyaU`%sJ0_&U3!!Ij`fT?33lo8bTirhX$S6(dmK^Nu2qLAPZJO zd+n=&@XLYwWFdS~Zh2c2gidFUEU-}gkOUzx)FqJ6 z;W433mmmmAY+Noh;tibd?18@VxCH}v4GeX#`0DNc_4sRW0TC1A zmJsrGlX|tHBmSvHZ7h?b^=>;mu1$tb(P>mvG{5}(=7p;C?X{{UgF+=c4I zP{`<|^mTPhV|UNEdEHc{)HAxSYaiW>P|0;&C$X5aR9UVpQyP@Vl`hhv$gdx{Z+-NR zwvW~;(eFuX@wtUs8?fTZ0 z-^y7ZnXZ(N2F|Wml7g>taRZ)SYa#R~3)d_2XS+8|g~IPiN|W-WvPxO4Jf$R*7zq(M zVPZ7&u2-7NT$&*G^Vf&!N<}4sxYR-&zO@e~z~qj%l+aa&|1SJK8kh_(z79nsokwAD^1AC7pf@Oj~FIF9# zF9b$C2U-gqk-~z?@R7iuo?L~z4ZVWkIiSQ^i}NGJ*2?fn#8cjeR;%3om9j(r$>}0* zmHG01U~>3C;Jl}&R|!j%@?|5s#Espmjtu)+#k%inqSKe2u4Y^$dyZBy&S@?QTm*4Jv!DYIMrLsd*G z=`T-i{>XEL^*_04^-~G9FB2d;TMqbVppb+**NV)Wh3cyEi~fAMBS-GYFX{6SqmnR3 zi7j8o-YehtBPZl-;$fEDRenBjZPn_8`k0QW%duAboe{h9;n1k=PmPBITKXgysm0cG zff9plWlq19^_3qFT=eu943;%0F}d3r9Ci6~gQK=UjyF9V9G&A|6db+Rf1l@a_=w-< zIgp;B+L?Gjt)J5Gg)}0q>Wcp0HQVM-TQ2)8SKeoRFckXbZl8$MyIG&-ayp6njK|qn zUpF;;x*hu7L6uw4aN!=m!{{NE=USzLa8KY0G!Yj<9~vxX3HENZ(Onw#yXUrCeraCo zVgU^`gK6GqgVb`wZ;#Gbmy2v_MAjdX^lEFX6)k+K<#J$AXn(Om8@eKmZkcJ?-+r#^ zCA}?|Tk-m02jZiTNML7==D)CNvx3sYjI-Lq-*XZa2UU;-+c$WsKoy6%2oD<>eine z!#Ejf&|7)}XW?0sdUNKePg&pstJ#T?3+&za*%{)yhd+P##k*mz+)*)!?YnZKwSKA| zu}@-GBM5lQwXLUnbA-^KRrkB=>1G$AHSS_ujsr9%IKnN3NV_f2z%xyG&<)WQp>tn@_V;6awYf z{q=0PWP~N+=^0|;@HO^_x)<+6Z;;S0r?eL5MGEsG)4dPD(&64oU$Ar(mKJI91WQp* z*fzBkMi!_#i^;Q2JgahPiGQ8gvStZuJR~ zt#rF1Q*=b?$N+zvO5*<=;%c=B9T?OE0Z$h_QD(6#I65=Xr9N+IZ4eRkS9#6`N53nF zgGh%{`-7vUa`;ocs8#P&SmkZ6Ac(#qhoeSdgWU2t0t?xQx ztJi2*MGFO0nhpzHsS48`Vp6I$SzypCIQ|r)66h+xi_VhantBb5r^GsquKknX=-R+M zR5)+3-109(YL#R<<}5hotld~R3DG;%8uwi7=x5}ePWAz;ei|y+wczL$|D53HM*s5Q z=%zrc`r^c_nOvX1R?2lfbszGepq#~lx-UxZrlnOz{2p{&Q(UKz(M1ePy8b6tOokmV zLn9Qdcp}|RixRp+gLWhp+axvgJlad?eTee+@?{jXu zfYy+<4a9q#+Xg%bFc#Lkt?+6)8^}a<6=7{Pl(xY4C3hw+G;lp|9;H5+Abs%xXQ(ef zlf)pDipQPXQqUx2%7Egdw^JoenzQOp1R=J}G z#oOYxtAp`@B3qnI9t8DHEcgY=VF>p@4)U7qZI&7f#y_FB9^0F2j)oj89X~b6STSQI zJGB67=8qA3IhV9q{PWleZJ*%`FMcgLjZ#$m)L7J#t+PdmS4q~;K5zNqKq{1(6=GPR zzt1jQ+&}|ddcP9G9Na5+7s=gK*0Mxk2BxTKvC!2A{4?b4l@75|?ykwFgh^OnXr7ZL z33i+&JdDZR31b~%GO@GXorc9VZcG&~Hp$(SA~o)u=mIh;l(c%zVi2>ct4HK+8P1VY z))%OjyZbRz-UVHukq7*D$=!{UL`<^vIo7g+DDMc$J5q8GgR;CZl=RNu;Fbd2d(lC4 zriJ#~jnN$*YE7SvVGH7y;{%g&a;dneK=#pEW<1I(by1@!Ly6_^4fgx76!~?plm%+n z5{9EYS7Y6gk?*3`y|@8xwK@?azsl16k9t%NY`T@Nl08`i3bYI6;DEBwPKPHJZrYud zoU9dPO@-cD*-GuwJh;JyN!V~#+6S;vWoVD#t|#DT!#BC>`HZ{PyBj;gRfXAAGOT^wY+@zX`N53g(TAO^u5eMPrzo_quzVwk?s9XOZ#u`Wf%1iwZW^pj&5Bay_-h4&?!svD7EzF;^s5-#C%kdOM#Y? za@Yk<+8fAV&^B6`nhQ&_8YH*uMTN7?oyxijCbmWhc*lVK22!2VV6WNICN4;=#ENPf zBHT7SIiG;A!)*l2Phb~}x8987b92)D_ZSoaU%3bdVOzo&{{{O9OoL)L>`+Wn!p z#QN3ZzZ5U3#V4ZW(Pt!9hI5eUbJplHcDBXJzGsg<=VV))p?D$OxjYd#!KTeJY?z&~ zfz2yL=s!A+^!Xn!dcxQkGNBI)$4@(L=h^RU*E49yoIC59*PBRXEDujoIhOBniwe>5W9{HXg z<;*qm=Ha%JZHcy1ZRxh7>{uw_YVawZlM2o+JVz9du6WKKYVQ0xe6?=vj~e8b9^|0; z>oFzzQ}W>GJQ}vt7j=f?u;y#JC~w~hkuG6}O}N)r6#8fwn`OJZ868i5U~}$ndO*%% zk;7}@H#p&{dGJ+*`D(ib^ude<8&a@8wFyz53;WeD?Nv(-XAWBru?UW11Qzq+SMG>S z!-IrAfROsI82`}5>YUNK;*!G{16h+D4z&?}B(;kxjNw3u(fZ?Za31oBflXhq)U<5d zBe2r2$2211gXny_dvgwI!eaG!>kZDuZ#(1#i@a?qz=4cjF9Ph?ZS^EoxCBfo5*W#(xICY8?vDl{f1nn!9n&<6< zv)Q`-&R=6X(U$xP-rTDlqDLY$5G>h`BRqR_UB9~Sk~xlJb^H+<*4~9gJe}wNn3fjt zQ+evv;tKY{TPzW+-X9vV_&3Sh$rrFY+}BzyMHU{zc}#VmcJ7j){|BT$9=eW`@gUrc zjfQmRrrgYU6@1~Cg)KfNho>M(n*|6s7?b|57cL%Md<Hx1Orywv z+FD&f1wCJ;ZtBB^;IC0uw6KNpv88?ForP4^u=dP&0>xg3o#JS6;Q*dOa^c7HkUJJ0 zQ(qXsF=PDVu4tgoqlPrUhKrUNj>c(e|dX3&vva7^I^n zyjNWyk<4}Dm#!2Y1EC)tN$=8zFo?l_XiTBCzjdad>-cnmo~(X9uX_Z?_c7Kt^E2eK z9iNQfAiFRmcN}g$_!*Mg?>|>r{&wI4Hgbam8<9KuGcu*;GX0*)*i9Nc4Lt{k_K*&C z;_QNL!3XY;!)~+@f;}aE8%`qWSX7CCc@AEV8=c6--h<0+c`uzQUa^%_Ra-Y{0eBLs$Cy9f*$ zF<;E}3$dX^Nk)L!Fb%aVFvyJ6GHAZHi%``W%O+xxPYC)mi8iR&{RFC+8ZrEb9rW}V z`@)ojTG4rQ!=CZxJu$S~Xx@X=ykl)j`_Jw*Iy!5}-1betc|rhY(S zEcgzZA){m@O+SYpJ1m=y!_Q)>y%^exYRcjNftpg#Du;iCAd)5A_Ee71uBZn#EmVHz zFd@7!gcwXdM$9EzT^ccl6_D9DCIiZbjNg<&y$azA&W-*&Jf{3G(A<2;n>Bs6fUbel zN7_t~`36STrwo}Uwmbz>;5)&sZhRJwtiW3=gtoF$gO#IE<(5ivO04wmgUnZPw7v&5 zp<4fvTn2~5#cUpVP3ztNgx{9S=Ey8})VrtW;6#u0!Br+3b$GLj+#%CogsVDY_W^%Gq$AZJ!d^I0O%5r!e zeD2vz%~!3)+jhJ)U$r3|crd!LIO>(gCbkRB9~pC5zcH7Y2C?DLfMv2VSnX5J%CGmZ zYHR6PR?vh~yymN-p>hZ8SDUXdp_yOY{7~uH;M>itAib#hs@+KcHPV}}PNpK~Pbe?i zP<)F5qt(_fDsCwKQ&O~z6@(s{Et*ccpM3QW&>K|NMI}^Ahde1Z#9k%5OU&HOhHlFOKF}Se7{^O zi2VzjKd02d`B}O7cy<-y$N1ntyWxxfE165FzUsxk-rPG8l9IVacsy41=%Y9XK&a8> za&@{8=>8ZB=iL~_@LwDkUrXlBMVufET<^qD$Bm~s6Hc$jcCsG~GMxVu87JfNFKt(_ zTa()!L^E~!pvj}tbOTK^xL9$fKf>N+e~hWi29vcT6rHRkrxki@2ZczL*jr^O!~!qe z2siJo$`RR*CH8%*y5y|qi05QF7j>UfDIP8VUEs2~(k*#Iy@lnHJMb^~?qrQhZnZcV zKV?z(RG?t>Uu*CiO4KXKJW)t4vOLsK=~9x5Cb?Yrgo?vy!(beIOLDiRN{pBPK5!W$ zEdDW}cao)5a(g3^J5nnu$y!UHX#96b&vkW~80xilqHg$>o6f^HlF9pC;Ib;om<^>) z3z&cMtgSN4u^euyssPTN%+kJ2}SK9=+xQA5M^<~khAvsVdWq>ur#;6Bu5%IZ0t+rzO;df!Z8#MHfrhlo# zz%(cID>xx+@Ac+c(!V8i$q;FhpvkBxhJ%-FMginQoz!|0I6=S3DH)<uM-rRoLC+t2igf`Y|;Ad({_=sc^6xzT-L>nK(g_#I(CG(V;*TE}#I08Gt9D6>K&20HSUL!oPU`wj5~y-m zTP#%$`}UkFhjW`$2!a=KVaU-3KeIl|jf#+tm6^0H z2;Yt~=^)O>pyO^lO&u@=ynJ{q!+jiS#y=s!j+^RT?I?W#Zc}+fRci_?>u!!k+d<#o z;I8;U*Z&nyD(@CLy_SHZuEtSc>(G-gGq8I9Ap;)-*VLYqH@b4qsz7jQEH1voDW3 zR#rNis?e6y2D?zMHlXV+goSE{bCILC(&1{PrVwW1-k(Wtpmf+3veY_m*RI+AWB*Xs zDL-8<+|++QLmAYzrjBZK{Dkmx-I7DQCL{eAeWfovymzF*9JY+zQzy2HS5DRs+s~MszfA78 zkjwfj`3d>!F2qe9>x)&iRW`00>oga!RHyKuu0Kr@x8h=1al=Suj!BIWZ%4ilh{dh) zegDScy}BSr7H^ECu565%yYTd$({(!DG27i3zcF8gB${z2%O{UNQgZe>fg z!3qy_x650R$s4<<>(#fn-?h&F-EXcd`&Ow?x<#O{|2t1_ST|?GfBVkbbw3gwZ>Vwk z8XtE-7r!_GPJk2O1ULasfD_;ZH~~(86W|0m0ZxDu-~>1UPJk2O1ULasfD_;ZH~~(8 z6W|0m0ZxDu-~>1UPJk2O1ULasfD_;ZH~~(86W|0m0ZxDu-~>1UPJk2O1ULasfD_;Z zH~~(86W|0m0ZxDu-~>1UPJk2O1ULasfD_;ZH~~(86W|0m0ZxDu-~>1UPJk2O1ULas zfD_;ZH~~(86W|0m0ZxDu-~>1UPJk2O1ULasfD_;ZH~~(86W|0m0ZxDu-~>1UPJk2O z1ULasfD_;ZH~~(86W|0m0ZxDu-~>1UPJk2O1ULasfD_;ZH~~(86W|0m0ZxDu-~>1U zPJk2O1ULasfD_;ZH~~(86W|0m0ZxDu-~>1UPJk2O1ULasfD_;ZH~~(86W|0m0ZxDu z-~>1UPJk2O1ULasfD_;ZH~~(86W|0m0ZxDu-~>1UPJk2O1ULasfD_;ZH~~(86W|0m z0Z!od1Ux-$$J=_^2HLc?{{JUzm0dl`OkLOi(JspO^xUV&;+-j7IrD2oSp}ujDU3^> o5d@0NUb>FZ&)-2Do-dnE`R8)xT^9bc5QmajO4XIv_+RY*0|l%Fi~s-t literal 0 HcmV?d00001 diff --git a/roms/Makefile b/roms/Makefile index da4efa47a9..c150170519 100644 --- a/roms/Makefile +++ b/roms/Makefile @@ -67,6 +67,7 @@ default help: @echo " opensbi32-virt -- update OpenSBI for 32-bit virt machine" @echo " opensbi64-virt -- update OpenSBI for 64-bit virt machine" @echo " opensbi64-sifive_u -- update OpenSBI for 64-bit sifive_u machine" + @echo " bios-microvm -- update bios-microvm.bin (qboot)" @echo " clean -- delete the files generated by the previous" \ "build targets" @@ -186,6 +187,10 @@ opensbi64-sifive_u: PLATFORM="sifive/fu540" cp opensbi/build/platform/sifive/fu540/firmware/fw_jump.bin ../pc-bios/opensbi-riscv64-sifive_u-fw_jump.bin +bios-microvm: + $(MAKE) -C qboot + cp qboot/bios.bin ../pc-bios/bios-microvm.bin + clean: rm -rf seabios/.config seabios/out seabios/builds $(MAKE) -C sgabios clean @@ -198,3 +203,4 @@ clean: $(MAKE) -C skiboot clean $(MAKE) -f Makefile.edk2 clean $(MAKE) -C opensbi clean + $(MAKE) -C qboot clean diff --git a/roms/qboot b/roms/qboot new file mode 160000 index 0000000000..cb1c49e0cf --- /dev/null +++ b/roms/qboot @@ -0,0 +1 @@ +Subproject commit cb1c49e0cfac99b9961d136ac0194da62c28cf64 From e00803aab46c6018b695ee63eef41263c15376d9 Mon Sep 17 00:00:00 2001 From: Sergio Lopez Date: Wed, 2 Oct 2019 08:01:59 +0200 Subject: [PATCH 19/34] docs/microvm.rst: document the new microvm machine type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Document the new microvm machine type. Signed-off-by: Sergio Lopez Reviewed-by: Michael S. Tsirkin Reviewed-by: Marc-André Lureau --- docs/microvm.rst | 108 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 docs/microvm.rst diff --git a/docs/microvm.rst b/docs/microvm.rst new file mode 100644 index 0000000000..aae811a922 --- /dev/null +++ b/docs/microvm.rst @@ -0,0 +1,108 @@ +==================== +microvm Machine Type +==================== + +``microvm`` is a machine type inspired by ``Firecracker`` and +constructed after its machine model. + +It's a minimalist machine type without ``PCI`` nor ``ACPI`` support, +designed for short-lived guests. microvm also establishes a baseline +for benchmarking and optimizing both QEMU and guest operating systems, +since it is optimized for both boot time and footprint. + + +Supported devices +----------------- + +The microvm machine type supports the following devices: + +- ISA bus +- i8259 PIC (optional) +- i8254 PIT (optional) +- MC146818 RTC (optional) +- One ISA serial port (optional) +- LAPIC +- IOAPIC (with kernel-irqchip=split by default) +- kvmclock (if using KVM) +- fw_cfg +- Up to eight virtio-mmio devices (configured by the user) + + +Limitations +----------- + +Currently, microvm does *not* support the following features: + + - PCI-only devices. + - Hotplug of any kind. + - Live migration across QEMU versions. + + +Using the microvm machine type +------------------------------ + +Machine-specific options +~~~~~~~~~~~~~~~~~~~~~~~~ + +It supports the following machine-specific options: + +- microvm.x-option-roms=bool (Set off to disable loading option ROMs) +- microvm.pit=OnOffAuto (Enable i8254 PIT) +- microvm.isa-serial=bool (Set off to disable the instantiation an ISA serial port) +- microvm.pic=OnOffAuto (Enable i8259 PIC) +- microvm.rtc=OnOffAuto (Enable MC146818 RTC) +- microvm.auto-kernel-cmdline=bool (Set off to disable adding virtio-mmio devices to the kernel cmdline) + + +Boot options +~~~~~~~~~~~~ + +By default, microvm uses ``qboot`` as its BIOS, to obtain better boot +times, but it's also compatible with ``SeaBIOS``. + +As no current FW is able to boot from a block device using +``virtio-mmio`` as its transport, a microvm-based VM needs to be run +using a host-side kernel and, optionally, an initrd image. + + +Running a microvm-based VM +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +By default, microvm aims for maximum compatibility, enabling both +legacy and non-legacy devices. In this example, a VM is created +without passing any additional machine-specific option, using the +legacy ``ISA serial`` device as console:: + + $ qemu-system-x86_64 -M microvm \ + -enable-kvm -cpu host -m 512m -smp 2 \ + -kernel vmlinux -append "earlyprintk=ttyS0 console=ttyS0 root=/dev/vda" \ + -nodefaults -no-user-config -nographic \ + -serial stdio \ + -drive id=test,file=test.img,format=raw,if=none \ + -device virtio-blk-device,drive=test \ + -netdev tap,id=tap0,script=no,downscript=no \ + -device virtio-net-device,netdev=tap0 + +While the example above works, you might be interested in reducing the +footprint further by disabling some legacy devices. If you're using +``KVM``, you can disable the ``RTC``, making the Guest rely on +``kvmclock`` exclusively. Additionally, if your host's CPUs have the +``TSC_DEADLINE`` feature, you can also disable both the i8259 PIC and +the i8254 PIT (make sure you're also emulating a CPU with such feature +in the guest). + +This is an example of a VM with all optional legacy features +disabled:: + + $ qemu-system-x86_64 \ + -M microvm,x-option-roms=off,pit=off,pic=off,isa-serial=off,rtc=off \ + -enable-kvm -cpu host -m 512m -smp 2 \ + -kernel vmlinux -append "console=hvc0 root=/dev/vda" \ + -nodefaults -no-user-config -nographic \ + -chardev stdio,id=virtiocon0 \ + -device virtio-serial-device \ + -device virtconsole,chardev=virtiocon0 \ + -drive id=test,file=test.img,format=raw,if=none \ + -device virtio-blk-device,drive=test \ + -netdev tap,id=tap0,script=no,downscript=no \ + -device virtio-net-device,netdev=tap0 From 0ebf007ddacf15a4483a4e740bf6c9dd688b5b5a Mon Sep 17 00:00:00 2001 From: Sergio Lopez Date: Wed, 2 Oct 2019 08:03:19 +0200 Subject: [PATCH 20/34] hw/i386: Introduce the microvm machine type microvm is a machine type inspired by Firecracker and constructed after its machine model. It's a minimalist machine type without PCI nor ACPI support, designed for short-lived guests. microvm also establishes a baseline for benchmarking and optimizing both QEMU and guest operating systems, since it is optimized for both boot time and footprint. Signed-off-by: Sergio Lopez Reviewed-by: Michael S. Tsirkin --- default-configs/i386-softmmu.mak | 1 + hw/i386/Kconfig | 10 + hw/i386/Makefile.objs | 1 + hw/i386/microvm.c | 572 +++++++++++++++++++++++++++++++ include/hw/i386/microvm.h | 71 ++++ 5 files changed, 655 insertions(+) create mode 100644 hw/i386/microvm.c create mode 100644 include/hw/i386/microvm.h diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak index 4229900f57..4cc64dafa2 100644 --- a/default-configs/i386-softmmu.mak +++ b/default-configs/i386-softmmu.mak @@ -28,3 +28,4 @@ CONFIG_ISAPC=y CONFIG_I440FX=y CONFIG_Q35=y +CONFIG_MICROVM=y diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig index c5c9d4900e..b25bb6d78a 100644 --- a/hw/i386/Kconfig +++ b/hw/i386/Kconfig @@ -92,6 +92,16 @@ config Q35 select SMBIOS select FW_CFG_DMA +config MICROVM + bool + imply SERIAL_ISA + select ISA_BUS + select APIC + select IOAPIC + select I8259 + select MC146818RTC + select VIRTIO_MMIO + config VTD bool diff --git a/hw/i386/Makefile.objs b/hw/i386/Makefile.objs index 7ed80a4853..0d195b5210 100644 --- a/hw/i386/Makefile.objs +++ b/hw/i386/Makefile.objs @@ -4,6 +4,7 @@ obj-y += x86.o obj-y += pc.o obj-$(CONFIG_I440FX) += pc_piix.o obj-$(CONFIG_Q35) += pc_q35.o +obj-$(CONFIG_MICROVM) += microvm.o obj-y += fw_cfg.o pc_sysfw.o obj-y += x86-iommu.o obj-$(CONFIG_VTD) += intel_iommu.o diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c new file mode 100644 index 0000000000..20d2189ea8 --- /dev/null +++ b/hw/i386/microvm.c @@ -0,0 +1,572 @@ +/* + * Copyright (c) 2018 Intel Corporation + * Copyright (c) 2019 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qemu/cutils.h" +#include "qemu/units.h" +#include "qapi/error.h" +#include "qapi/visitor.h" +#include "qapi/qapi-visit-common.h" +#include "sysemu/sysemu.h" +#include "sysemu/cpus.h" +#include "sysemu/numa.h" +#include "sysemu/reset.h" + +#include "hw/loader.h" +#include "hw/irq.h" +#include "hw/kvm/clock.h" +#include "hw/i386/microvm.h" +#include "hw/i386/x86.h" +#include "hw/i386/pc.h" +#include "target/i386/cpu.h" +#include "hw/timer/i8254.h" +#include "hw/timer/mc146818rtc.h" +#include "hw/char/serial.h" +#include "hw/i386/topology.h" +#include "hw/i386/e820_memory_layout.h" +#include "hw/i386/fw_cfg.h" +#include "hw/virtio/virtio-mmio.h" + +#include "cpu.h" +#include "elf.h" +#include "kvm_i386.h" +#include "hw/xen/start_info.h" + +#define MICROVM_BIOS_FILENAME "bios-microvm.bin" + +static void microvm_set_rtc(MicrovmMachineState *mms, ISADevice *s) +{ + X86MachineState *x86ms = X86_MACHINE(mms); + int val; + + val = MIN(x86ms->below_4g_mem_size / KiB, 640); + rtc_set_memory(s, 0x15, val); + rtc_set_memory(s, 0x16, val >> 8); + /* extended memory (next 64MiB) */ + if (x86ms->below_4g_mem_size > 1 * MiB) { + val = (x86ms->below_4g_mem_size - 1 * MiB) / KiB; + } else { + val = 0; + } + if (val > 65535) { + val = 65535; + } + rtc_set_memory(s, 0x17, val); + rtc_set_memory(s, 0x18, val >> 8); + rtc_set_memory(s, 0x30, val); + rtc_set_memory(s, 0x31, val >> 8); + /* memory between 16MiB and 4GiB */ + if (x86ms->below_4g_mem_size > 16 * MiB) { + val = (x86ms->below_4g_mem_size - 16 * MiB) / (64 * KiB); + } else { + val = 0; + } + if (val > 65535) { + val = 65535; + } + rtc_set_memory(s, 0x34, val); + rtc_set_memory(s, 0x35, val >> 8); + /* memory above 4GiB */ + val = x86ms->above_4g_mem_size / 65536; + rtc_set_memory(s, 0x5b, val); + rtc_set_memory(s, 0x5c, val >> 8); + rtc_set_memory(s, 0x5d, val >> 16); +} + +static void microvm_gsi_handler(void *opaque, int n, int level) +{ + GSIState *s = opaque; + + qemu_set_irq(s->ioapic_irq[n], level); +} + +static void microvm_devices_init(MicrovmMachineState *mms) +{ + X86MachineState *x86ms = X86_MACHINE(mms); + ISABus *isa_bus; + ISADevice *rtc_state; + GSIState *gsi_state; + int i; + + /* Core components */ + + gsi_state = g_malloc0(sizeof(*gsi_state)); + if (mms->pic == ON_OFF_AUTO_ON || mms->pic == ON_OFF_AUTO_AUTO) { + x86ms->gsi = qemu_allocate_irqs(gsi_handler, gsi_state, GSI_NUM_PINS); + } else { + x86ms->gsi = qemu_allocate_irqs(microvm_gsi_handler, + gsi_state, GSI_NUM_PINS); + } + + isa_bus = isa_bus_new(NULL, get_system_memory(), get_system_io(), + &error_abort); + isa_bus_irqs(isa_bus, x86ms->gsi); + + ioapic_init_gsi(gsi_state, "machine"); + + kvmclock_create(); + + for (i = 0; i < VIRTIO_NUM_TRANSPORTS; i++) { + sysbus_create_simple("virtio-mmio", + VIRTIO_MMIO_BASE + i * 512, + x86ms->gsi[VIRTIO_IRQ_BASE + i]); + } + + /* Optional and legacy devices */ + + if (mms->pic == ON_OFF_AUTO_ON || mms->pic == ON_OFF_AUTO_AUTO) { + qemu_irq *i8259; + + i8259 = i8259_init(isa_bus, pc_allocate_cpu_irq()); + for (i = 0; i < ISA_NUM_IRQS; i++) { + gsi_state->i8259_irq[i] = i8259[i]; + } + g_free(i8259); + } + + if (mms->pit == ON_OFF_AUTO_ON || mms->pit == ON_OFF_AUTO_AUTO) { + if (kvm_pit_in_kernel()) { + kvm_pit_init(isa_bus, 0x40); + } else { + i8254_pit_init(isa_bus, 0x40, 0, NULL); + } + } + + if (mms->rtc == ON_OFF_AUTO_ON || + (mms->rtc == ON_OFF_AUTO_AUTO && !kvm_enabled())) { + rtc_state = mc146818_rtc_init(isa_bus, 2000, NULL); + microvm_set_rtc(mms, rtc_state); + } + + if (mms->isa_serial) { + serial_hds_isa_init(isa_bus, 0, 1); + } + + if (bios_name == NULL) { + bios_name = MICROVM_BIOS_FILENAME; + } + x86_bios_rom_init(get_system_memory(), true); +} + +static void microvm_memory_init(MicrovmMachineState *mms) +{ + MachineState *machine = MACHINE(mms); + X86MachineState *x86ms = X86_MACHINE(mms); + MemoryRegion *ram, *ram_below_4g, *ram_above_4g; + MemoryRegion *system_memory = get_system_memory(); + FWCfgState *fw_cfg; + ram_addr_t lowmem; + int i; + + /* + * Check whether RAM fits below 4G (leaving 1/2 GByte for IO memory + * and 256 Mbytes for PCI Express Enhanced Configuration Access Mapping + * also known as MMCFG). + * If it doesn't, we need to split it in chunks below and above 4G. + * In any case, try to make sure that guest addresses aligned at + * 1G boundaries get mapped to host addresses aligned at 1G boundaries. + */ + if (machine->ram_size >= 0xb0000000) { + lowmem = 0x80000000; + } else { + lowmem = 0xb0000000; + } + + /* + * Handle the machine opt max-ram-below-4g. It is basically doing + * min(qemu limit, user limit). + */ + if (!x86ms->max_ram_below_4g) { + x86ms->max_ram_below_4g = 4 * GiB; + } + if (lowmem > x86ms->max_ram_below_4g) { + lowmem = x86ms->max_ram_below_4g; + if (machine->ram_size - lowmem > lowmem && + lowmem & (1 * GiB - 1)) { + warn_report("There is possibly poor performance as the ram size " + " (0x%" PRIx64 ") is more then twice the size of" + " max-ram-below-4g (%"PRIu64") and" + " max-ram-below-4g is not a multiple of 1G.", + (uint64_t)machine->ram_size, x86ms->max_ram_below_4g); + } + } + + if (machine->ram_size > lowmem) { + x86ms->above_4g_mem_size = machine->ram_size - lowmem; + x86ms->below_4g_mem_size = lowmem; + } else { + x86ms->above_4g_mem_size = 0; + x86ms->below_4g_mem_size = machine->ram_size; + } + + ram = g_malloc(sizeof(*ram)); + memory_region_allocate_system_memory(ram, NULL, "microvm.ram", + machine->ram_size); + + ram_below_4g = g_malloc(sizeof(*ram_below_4g)); + memory_region_init_alias(ram_below_4g, NULL, "ram-below-4g", ram, + 0, x86ms->below_4g_mem_size); + memory_region_add_subregion(system_memory, 0, ram_below_4g); + + e820_add_entry(0, x86ms->below_4g_mem_size, E820_RAM); + + if (x86ms->above_4g_mem_size > 0) { + ram_above_4g = g_malloc(sizeof(*ram_above_4g)); + memory_region_init_alias(ram_above_4g, NULL, "ram-above-4g", ram, + x86ms->below_4g_mem_size, + x86ms->above_4g_mem_size); + memory_region_add_subregion(system_memory, 0x100000000ULL, + ram_above_4g); + e820_add_entry(0x100000000ULL, x86ms->above_4g_mem_size, E820_RAM); + } + + fw_cfg = fw_cfg_init_io_dma(FW_CFG_IO_BASE, FW_CFG_IO_BASE + 4, + &address_space_memory); + + fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, machine->smp.cpus); + fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, machine->smp.max_cpus); + fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)machine->ram_size); + fw_cfg_add_i32(fw_cfg, FW_CFG_IRQ0_OVERRIDE, kvm_allows_irq0_override()); + fw_cfg_add_bytes(fw_cfg, FW_CFG_E820_TABLE, + &e820_reserve, sizeof(e820_reserve)); + fw_cfg_add_file(fw_cfg, "etc/e820", e820_table, + sizeof(struct e820_entry) * e820_get_num_entries()); + + rom_set_fw(fw_cfg); + + if (machine->kernel_filename != NULL) { + x86_load_linux(x86ms, fw_cfg, 0, true, true); + } + + if (mms->option_roms) { + for (i = 0; i < nb_option_roms; i++) { + rom_add_option(option_rom[i].name, option_rom[i].bootindex); + } + } + + x86ms->fw_cfg = fw_cfg; + x86ms->ioapic_as = &address_space_memory; +} + +static gchar *microvm_get_mmio_cmdline(gchar *name) +{ + gchar *cmdline; + gchar *separator; + long int index; + int ret; + + separator = g_strrstr(name, "."); + if (!separator) { + return NULL; + } + + if (qemu_strtol(separator + 1, NULL, 10, &index) != 0) { + return NULL; + } + + cmdline = g_malloc0(VIRTIO_CMDLINE_MAXLEN); + ret = g_snprintf(cmdline, VIRTIO_CMDLINE_MAXLEN, + " virtio_mmio.device=512@0x%lx:%ld", + VIRTIO_MMIO_BASE + index * 512, + VIRTIO_IRQ_BASE + index); + if (ret < 0 || ret >= VIRTIO_CMDLINE_MAXLEN) { + g_free(cmdline); + return NULL; + } + + return cmdline; +} + +static void microvm_fix_kernel_cmdline(MachineState *machine) +{ + X86MachineState *x86ms = X86_MACHINE(machine); + BusState *bus; + BusChild *kid; + char *cmdline; + + /* + * Find MMIO transports with attached devices, and add them to the kernel + * command line. + * + * Yes, this is a hack, but one that heavily improves the UX without + * introducing any significant issues. + */ + cmdline = g_strdup(machine->kernel_cmdline); + bus = sysbus_get_default(); + QTAILQ_FOREACH(kid, &bus->children, sibling) { + DeviceState *dev = kid->child; + ObjectClass *class = object_get_class(OBJECT(dev)); + + if (class == object_class_by_name(TYPE_VIRTIO_MMIO)) { + VirtIOMMIOProxy *mmio = VIRTIO_MMIO(OBJECT(dev)); + VirtioBusState *mmio_virtio_bus = &mmio->bus; + BusState *mmio_bus = &mmio_virtio_bus->parent_obj; + + if (!QTAILQ_EMPTY(&mmio_bus->children)) { + gchar *mmio_cmdline = microvm_get_mmio_cmdline(mmio_bus->name); + if (mmio_cmdline) { + char *newcmd = g_strjoin(NULL, cmdline, mmio_cmdline, NULL); + g_free(mmio_cmdline); + g_free(cmdline); + cmdline = newcmd; + } + } + } + } + + fw_cfg_modify_i32(x86ms->fw_cfg, FW_CFG_CMDLINE_SIZE, strlen(cmdline) + 1); + fw_cfg_modify_string(x86ms->fw_cfg, FW_CFG_CMDLINE_DATA, cmdline); +} + +static void microvm_machine_state_init(MachineState *machine) +{ + MicrovmMachineState *mms = MICROVM_MACHINE(machine); + X86MachineState *x86ms = X86_MACHINE(machine); + Error *local_err = NULL; + + microvm_memory_init(mms); + + x86_cpus_init(x86ms, CPU_VERSION_LATEST); + if (local_err) { + error_report_err(local_err); + exit(1); + } + + microvm_devices_init(mms); +} + +static void microvm_machine_reset(MachineState *machine) +{ + MicrovmMachineState *mms = MICROVM_MACHINE(machine); + CPUState *cs; + X86CPU *cpu; + + if (machine->kernel_filename != NULL && + mms->auto_kernel_cmdline && !mms->kernel_cmdline_fixed) { + microvm_fix_kernel_cmdline(machine); + mms->kernel_cmdline_fixed = true; + } + + qemu_devices_reset(); + + CPU_FOREACH(cs) { + cpu = X86_CPU(cs); + + if (cpu->apic_state) { + device_reset(cpu->apic_state); + } + } +} + +static void microvm_machine_get_pic(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + MicrovmMachineState *mms = MICROVM_MACHINE(obj); + OnOffAuto pic = mms->pic; + + visit_type_OnOffAuto(v, name, &pic, errp); +} + +static void microvm_machine_set_pic(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + MicrovmMachineState *mms = MICROVM_MACHINE(obj); + + visit_type_OnOffAuto(v, name, &mms->pic, errp); +} + +static void microvm_machine_get_pit(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + MicrovmMachineState *mms = MICROVM_MACHINE(obj); + OnOffAuto pit = mms->pit; + + visit_type_OnOffAuto(v, name, &pit, errp); +} + +static void microvm_machine_set_pit(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + MicrovmMachineState *mms = MICROVM_MACHINE(obj); + + visit_type_OnOffAuto(v, name, &mms->pit, errp); +} + +static void microvm_machine_get_rtc(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + MicrovmMachineState *mms = MICROVM_MACHINE(obj); + OnOffAuto rtc = mms->rtc; + + visit_type_OnOffAuto(v, name, &rtc, errp); +} + +static void microvm_machine_set_rtc(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + MicrovmMachineState *mms = MICROVM_MACHINE(obj); + + visit_type_OnOffAuto(v, name, &mms->rtc, errp); +} + +static bool microvm_machine_get_isa_serial(Object *obj, Error **errp) +{ + MicrovmMachineState *mms = MICROVM_MACHINE(obj); + + return mms->isa_serial; +} + +static void microvm_machine_set_isa_serial(Object *obj, bool value, + Error **errp) +{ + MicrovmMachineState *mms = MICROVM_MACHINE(obj); + + mms->isa_serial = value; +} + +static bool microvm_machine_get_option_roms(Object *obj, Error **errp) +{ + MicrovmMachineState *mms = MICROVM_MACHINE(obj); + + return mms->option_roms; +} + +static void microvm_machine_set_option_roms(Object *obj, bool value, + Error **errp) +{ + MicrovmMachineState *mms = MICROVM_MACHINE(obj); + + mms->option_roms = value; +} + +static bool microvm_machine_get_auto_kernel_cmdline(Object *obj, Error **errp) +{ + MicrovmMachineState *mms = MICROVM_MACHINE(obj); + + return mms->auto_kernel_cmdline; +} + +static void microvm_machine_set_auto_kernel_cmdline(Object *obj, bool value, + Error **errp) +{ + MicrovmMachineState *mms = MICROVM_MACHINE(obj); + + mms->auto_kernel_cmdline = value; +} + +static void microvm_machine_initfn(Object *obj) +{ + MicrovmMachineState *mms = MICROVM_MACHINE(obj); + + /* Configuration */ + mms->pic = ON_OFF_AUTO_AUTO; + mms->pit = ON_OFF_AUTO_AUTO; + mms->rtc = ON_OFF_AUTO_AUTO; + mms->isa_serial = true; + mms->option_roms = true; + mms->auto_kernel_cmdline = true; + + /* State */ + mms->kernel_cmdline_fixed = false; +} + +static void microvm_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->init = microvm_machine_state_init; + + mc->family = "microvm_i386"; + mc->desc = "microvm (i386)"; + mc->units_per_default_bus = 1; + mc->no_floppy = 1; + mc->max_cpus = 288; + mc->has_hotpluggable_cpus = false; + mc->auto_enable_numa_with_memhp = false; + mc->default_cpu_type = TARGET_DEFAULT_CPU_TYPE; + mc->nvdimm_supported = false; + + /* Avoid relying too much on kernel components */ + mc->default_kernel_irqchip_split = true; + + /* Machine class handlers */ + mc->reset = microvm_machine_reset; + + object_class_property_add(oc, MICROVM_MACHINE_PIC, "OnOffAuto", + microvm_machine_get_pic, + microvm_machine_set_pic, + NULL, NULL, &error_abort); + object_class_property_set_description(oc, MICROVM_MACHINE_PIC, + "Enable i8259 PIC", &error_abort); + + object_class_property_add(oc, MICROVM_MACHINE_PIT, "OnOffAuto", + microvm_machine_get_pit, + microvm_machine_set_pit, + NULL, NULL, &error_abort); + object_class_property_set_description(oc, MICROVM_MACHINE_PIT, + "Enable i8254 PIT", &error_abort); + + object_class_property_add(oc, MICROVM_MACHINE_RTC, "OnOffAuto", + microvm_machine_get_rtc, + microvm_machine_set_rtc, + NULL, NULL, &error_abort); + object_class_property_set_description(oc, MICROVM_MACHINE_RTC, + "Enable MC146818 RTC", &error_abort); + + object_class_property_add_bool(oc, MICROVM_MACHINE_ISA_SERIAL, + microvm_machine_get_isa_serial, + microvm_machine_set_isa_serial, + &error_abort); + object_class_property_set_description(oc, MICROVM_MACHINE_ISA_SERIAL, + "Set off to disable the instantiation an ISA serial port", + &error_abort); + + object_class_property_add_bool(oc, MICROVM_MACHINE_OPTION_ROMS, + microvm_machine_get_option_roms, + microvm_machine_set_option_roms, + &error_abort); + object_class_property_set_description(oc, MICROVM_MACHINE_OPTION_ROMS, + "Set off to disable loading option ROMs", &error_abort); + + object_class_property_add_bool(oc, MICROVM_MACHINE_AUTO_KERNEL_CMDLINE, + microvm_machine_get_auto_kernel_cmdline, + microvm_machine_set_auto_kernel_cmdline, + &error_abort); + object_class_property_set_description(oc, + MICROVM_MACHINE_AUTO_KERNEL_CMDLINE, + "Set off to disable adding virtio-mmio devices to the kernel cmdline", + &error_abort); +} + +static const TypeInfo microvm_machine_info = { + .name = TYPE_MICROVM_MACHINE, + .parent = TYPE_X86_MACHINE, + .instance_size = sizeof(MicrovmMachineState), + .instance_init = microvm_machine_initfn, + .class_size = sizeof(MicrovmMachineClass), + .class_init = microvm_class_init, + .interfaces = (InterfaceInfo[]) { + { } + }, +}; + +static void microvm_machine_init(void) +{ + type_register_static(µvm_machine_info); +} +type_init(microvm_machine_init); diff --git a/include/hw/i386/microvm.h b/include/hw/i386/microvm.h new file mode 100644 index 0000000000..ba68d1f22b --- /dev/null +++ b/include/hw/i386/microvm.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2018 Intel Corporation + * Copyright (c) 2019 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef HW_I386_MICROVM_H +#define HW_I386_MICROVM_H + +#include "qemu-common.h" +#include "exec/hwaddr.h" +#include "qemu/notify.h" + +#include "hw/boards.h" +#include "hw/i386/x86.h" + +/* Platform virtio definitions */ +#define VIRTIO_MMIO_BASE 0xc0000000 +#define VIRTIO_IRQ_BASE 5 +#define VIRTIO_NUM_TRANSPORTS 8 +#define VIRTIO_CMDLINE_MAXLEN 64 + +/* Machine type options */ +#define MICROVM_MACHINE_PIT "pit" +#define MICROVM_MACHINE_PIC "pic" +#define MICROVM_MACHINE_RTC "rtc" +#define MICROVM_MACHINE_ISA_SERIAL "isa-serial" +#define MICROVM_MACHINE_OPTION_ROMS "x-option-roms" +#define MICROVM_MACHINE_AUTO_KERNEL_CMDLINE "auto-kernel-cmdline" + +typedef struct { + X86MachineClass parent; + HotplugHandler *(*orig_hotplug_handler)(MachineState *machine, + DeviceState *dev); +} MicrovmMachineClass; + +typedef struct { + X86MachineState parent; + + /* Machine type options */ + OnOffAuto pic; + OnOffAuto pit; + OnOffAuto rtc; + bool isa_serial; + bool option_roms; + bool auto_kernel_cmdline; + + /* Machine state */ + bool kernel_cmdline_fixed; +} MicrovmMachineState; + +#define TYPE_MICROVM_MACHINE MACHINE_TYPE_NAME("microvm") +#define MICROVM_MACHINE(obj) \ + OBJECT_CHECK(MicrovmMachineState, (obj), TYPE_MICROVM_MACHINE) +#define MICROVM_MACHINE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(MicrovmMachineClass, obj, TYPE_MICROVM_MACHINE) +#define MICROVM_MACHINE_CLASS(class) \ + OBJECT_CLASS_CHECK(MicrovmMachineClass, class, TYPE_MICROVM_MACHINE) + +#endif From 5cb48f05dd90d7a11d2bfb6f368da74622c218b4 Mon Sep 17 00:00:00 2001 From: Sergio Lopez Date: Tue, 8 Oct 2019 12:42:28 +0200 Subject: [PATCH 21/34] MAINTAINERS: add microvm related files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new "microvm" section under "X86 Machines" with the new files related to this machine type. Signed-off-by: Sergio Lopez Reviewed-by: Michael S. Tsirkin Reviewed-by: Philippe Mathieu-Daudé --- MAINTAINERS | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 250ce8e7a1..72ab731987 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1275,6 +1275,15 @@ F: include/hw/timer/hpet.h F: include/hw/timer/i8254* F: include/hw/timer/mc146818rtc* +microvm +M: Sergio Lopez +M: Paolo Bonzini +S: Maintained +F: docs/microvm.rst +F: hw/i386/microvm.c +F: include/hw/i386/microvm.h +F: pc-bios/bios-microvm.bin + Machine core M: Eduardo Habkost M: Marcel Apfelbaum From e31db8aabece49b815c394a3a0ddb6bb1624a07a Mon Sep 17 00:00:00 2001 From: Wei Yang Date: Thu, 17 Oct 2019 08:46:33 +0800 Subject: [PATCH 22/34] checkpatch: suggest qemu_real_host_page_size instead of getpagesize() or sysconf(_SC_PAGESIZE) Signed-off-by: Wei Yang CC: Richard Henderson CC: Stefan Hajnoczi Reviewed-by: Richard Henderson Reviewed-by: Stefan Hajnoczi Message-Id: <20191017004633.13229-1-richardw.yang@linux.intel.com> Signed-off-by: Paolo Bonzini --- scripts/checkpatch.pl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index aa9a354a0e..ab68a16fd2 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -2915,6 +2915,12 @@ sub process { if ($line =~ /\bbzero\(/) { ERROR("use memset() instead of bzero()\n" . $herecurr); } + if ($line =~ /\bgetpagesize\(\)/) { + ERROR("use qemu_real_host_page_size instead of getpagesize()\n" . $herecurr); + } + if ($line =~ /\bsysconf\(_SC_PAGESIZE\)/) { + ERROR("use qemu_real_host_page_size instead of sysconf(_SC_PAGESIZE)\n" . $herecurr); + } my $non_exit_glib_asserts = qr{g_assert_cmpstr| g_assert_cmpint| g_assert_cmpuint| From 1bd71dce4bf2d24d003780ef01eb5fc274d66703 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Thu, 17 Oct 2019 12:18:06 +0200 Subject: [PATCH 23/34] runstate: ignore exit request in finish migrate state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Trying to reboot a VM while a migration is running can move to the prelaunch state (because of the reset) while the runstate is in finish migrate state. As the logical step after the finish migrate is postmigrate, this can create an invalid state transition from prelaunch state to postmigrate state and this raises an error and aborts: invalid runstate transition: 'prelaunch' -> 'postmigrate' As we are not able to manage reset in finish migrate state the best we can do is to ignore any changes and delay them until the next state which should be postmigrate and which should allow this kind of transition. Reported-by: Lukáš Doktor Suggested-by: Dr. David Alan Gilbert Signed-off-by: Laurent Vivier Message-Id: <20191017101806.3644-1-lvivier@redhat.com> Signed-off-by: Paolo Bonzini --- vl.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/vl.c b/vl.c index 4489cfb2bb..4a7d011661 100644 --- a/vl.c +++ b/vl.c @@ -1744,6 +1744,9 @@ static bool main_loop_should_exit(void) RunState r; ShutdownCause request; + if (runstate_check(RUN_STATE_FINISH_MIGRATE)) { + return false; + } if (preconfig_exit_requested) { if (runstate_check(RUN_STATE_PRECONFIG)) { runstate_set(RUN_STATE_PRELAUNCH); From 3c13c4be221a83f80431c228bbaf7f8a064897dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 17 Oct 2019 18:26:14 +0200 Subject: [PATCH 24/34] hw/timer/mc146818rtc: Only include qapi-commands-misc on I386 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit a6c7040fb09 restricted the rtc-reset-reinjection command to the I386 target. Restrict the "qapi/qapi-commands-misc-target.h" header to it too. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20191017162614.21327-1-philmd@redhat.com> Signed-off-by: Paolo Bonzini --- hw/timer/mc146818rtc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c index 0e7cf97042..c979db0992 100644 --- a/hw/timer/mc146818rtc.c +++ b/hw/timer/mc146818rtc.c @@ -37,12 +37,12 @@ #include "hw/timer/mc146818rtc.h" #include "migration/vmstate.h" #include "qapi/error.h" -#include "qapi/qapi-commands-misc-target.h" #include "qapi/qapi-events-misc-target.h" #include "qapi/visitor.h" #include "exec/address-spaces.h" #ifdef TARGET_I386 +#include "qapi/qapi-commands-misc-target.h" #include "hw/i386/apic.h" #endif From 67192a298f5bf98f96e5516c3b6474c49e4853cd Mon Sep 17 00:00:00 2001 From: Tao Xu Date: Fri, 11 Oct 2019 15:41:02 +0800 Subject: [PATCH 25/34] x86/cpu: Add support for UMONITOR/UMWAIT/TPAUSE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit UMONITOR, UMWAIT and TPAUSE are a set of user wait instructions. This patch adds support for user wait instructions in KVM. Availability of the user wait instructions is indicated by the presence of the CPUID feature flag WAITPKG CPUID.0x07.0x0:ECX[5]. User wait instructions may be executed at any privilege level, and use IA32_UMWAIT_CONTROL MSR to set the maximum time. The patch enable the umonitor, umwait and tpause features in KVM. Because umwait and tpause can put a (psysical) CPU into a power saving state, by default we dont't expose it to kvm and enable it only when guest CPUID has it. And use QEMU command-line "-overcommit cpu-pm=on" (enable_cpu_pm is enabled), a VM can use UMONITOR, UMWAIT and TPAUSE instructions. If the instruction causes a delay, the amount of time delayed is called here the physical delay. The physical delay is first computed by determining the virtual delay (the time to delay relative to the VM’s timestamp counter). Otherwise, UMONITOR, UMWAIT and TPAUSE cause an invalid-opcode exception(#UD). The release document ref below link: https://software.intel.com/sites/default/files/\ managed/39/c5/325462-sdm-vol-1-2abcd-3abcd.pdf Co-developed-by: Jingqi Liu Signed-off-by: Jingqi Liu Signed-off-by: Tao Xu Message-Id: <20191011074103.30393-2-tao3.xu@intel.com> Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 2 +- target/i386/cpu.h | 2 ++ target/i386/kvm.c | 6 ++++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 6c87c99487..b635302859 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -1058,7 +1058,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { .type = CPUID_FEATURE_WORD, .feat_names = { NULL, "avx512vbmi", "umip", "pku", - NULL /* ospke */, NULL, "avx512vbmi2", NULL, + NULL /* ospke */, "waitpkg", "avx512vbmi2", NULL, "gfni", "vaes", "vpclmulqdq", "avx512vnni", "avx512bitalg", NULL, "avx512-vpopcntdq", NULL, "la57", NULL, NULL, NULL, diff --git a/target/i386/cpu.h b/target/i386/cpu.h index c50a9c7257..112f8670d9 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -731,6 +731,8 @@ typedef uint64_t FeatureWordArray[FEATURE_WORDS]; #define CPUID_7_0_ECX_PKU (1U << 3) /* OS Enable Protection Keys */ #define CPUID_7_0_ECX_OSPKE (1U << 4) +/* UMONITOR/UMWAIT/TPAUSE Instructions */ +#define CPUID_7_0_ECX_WAITPKG (1U << 5) /* Additional AVX-512 Vector Byte Manipulation Instruction */ #define CPUID_7_0_ECX_AVX512_VBMI2 (1U << 6) /* Galois Field New Instructions */ diff --git a/target/i386/kvm.c b/target/i386/kvm.c index 49a301190a..94c233943e 100644 --- a/target/i386/kvm.c +++ b/target/i386/kvm.c @@ -401,6 +401,12 @@ uint32_t kvm_arch_get_supported_cpuid(KVMState *s, uint32_t function, if (host_tsx_blacklisted()) { ret &= ~(CPUID_7_0_EBX_RTM | CPUID_7_0_EBX_HLE); } + } else if (function == 7 && index == 0 && reg == R_ECX) { + if (enable_cpu_pm) { + ret |= CPUID_7_0_ECX_WAITPKG; + } else { + ret &= ~CPUID_7_0_ECX_WAITPKG; + } } else if (function == 7 && index == 0 && reg == R_EDX) { /* * Linux v4.17-v4.20 incorrectly return ARCH_CAPABILITIES on SVM hosts. From 6508799707bbf018df82d354c388820217757f21 Mon Sep 17 00:00:00 2001 From: Tao Xu Date: Fri, 11 Oct 2019 15:41:03 +0800 Subject: [PATCH 26/34] target/i386: Add support for save/load IA32_UMWAIT_CONTROL MSR UMWAIT and TPAUSE instructions use 32bits IA32_UMWAIT_CONTROL at MSR index E1H to determines the maximum time in TSC-quanta that the processor can reside in either C0.1 or C0.2. This patch is to Add support for save/load IA32_UMWAIT_CONTROL MSR in guest. Co-developed-by: Jingqi Liu Signed-off-by: Jingqi Liu Signed-off-by: Tao Xu Message-Id: <20191011074103.30393-3-tao3.xu@intel.com> Signed-off-by: Paolo Bonzini --- target/i386/cpu.h | 2 ++ target/i386/kvm.c | 13 +++++++++++++ target/i386/machine.c | 20 ++++++++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 112f8670d9..b772e82476 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -452,6 +452,7 @@ typedef enum X86Seg { #define MSR_IA32_BNDCFGS 0x00000d90 #define MSR_IA32_XSS 0x00000da0 +#define MSR_IA32_UMWAIT_CONTROL 0xe1 #define MSR_IA32_VMX_BASIC 0x00000480 #define MSR_IA32_VMX_PINBASED_CTLS 0x00000481 @@ -1587,6 +1588,7 @@ typedef struct CPUX86State { uint16_t fpregs_format_vmstate; uint64_t xss; + uint32_t umwait; TPRAccess tpr_access_type; diff --git a/target/i386/kvm.c b/target/i386/kvm.c index 94c233943e..bfd09bd441 100644 --- a/target/i386/kvm.c +++ b/target/i386/kvm.c @@ -95,6 +95,7 @@ static bool has_msr_hv_stimer; static bool has_msr_hv_frequencies; static bool has_msr_hv_reenlightenment; static bool has_msr_xss; +static bool has_msr_umwait; static bool has_msr_spec_ctrl; static bool has_msr_virt_ssbd; static bool has_msr_smi_count; @@ -2005,6 +2006,9 @@ static int kvm_get_supported_msrs(KVMState *s) case MSR_IA32_XSS: has_msr_xss = true; break; + case MSR_IA32_UMWAIT_CONTROL: + has_msr_umwait = true; + break; case HV_X64_MSR_CRASH_CTL: has_msr_hv_crash = true; break; @@ -2684,6 +2688,9 @@ static int kvm_put_msrs(X86CPU *cpu, int level) if (has_msr_xss) { kvm_msr_entry_add(cpu, MSR_IA32_XSS, env->xss); } + if (has_msr_umwait) { + kvm_msr_entry_add(cpu, MSR_IA32_UMWAIT_CONTROL, env->umwait); + } if (has_msr_spec_ctrl) { kvm_msr_entry_add(cpu, MSR_IA32_SPEC_CTRL, env->spec_ctrl); } @@ -3097,6 +3104,9 @@ static int kvm_get_msrs(X86CPU *cpu) if (has_msr_xss) { kvm_msr_entry_add(cpu, MSR_IA32_XSS, 0); } + if (has_msr_umwait) { + kvm_msr_entry_add(cpu, MSR_IA32_UMWAIT_CONTROL, 0); + } if (has_msr_spec_ctrl) { kvm_msr_entry_add(cpu, MSR_IA32_SPEC_CTRL, 0); } @@ -3349,6 +3359,9 @@ static int kvm_get_msrs(X86CPU *cpu) case MSR_IA32_XSS: env->xss = msrs[i].data; break; + case MSR_IA32_UMWAIT_CONTROL: + env->umwait = msrs[i].data; + break; default: if (msrs[i].index >= MSR_MC0_CTL && msrs[i].index < MSR_MC0_CTL + (env->mcg_cap & 0xff) * 4) { diff --git a/target/i386/machine.c b/target/i386/machine.c index 2767b3096d..6481f846f6 100644 --- a/target/i386/machine.c +++ b/target/i386/machine.c @@ -943,6 +943,25 @@ static const VMStateDescription vmstate_xss = { } }; +static bool umwait_needed(void *opaque) +{ + X86CPU *cpu = opaque; + CPUX86State *env = &cpu->env; + + return env->umwait != 0; +} + +static const VMStateDescription vmstate_umwait = { + .name = "cpu/umwait", + .version_id = 1, + .minimum_version_id = 1, + .needed = umwait_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT32(env.umwait, X86CPU), + VMSTATE_END_OF_LIST() + } +}; + #ifdef TARGET_X86_64 static bool pkru_needed(void *opaque) { @@ -1391,6 +1410,7 @@ VMStateDescription vmstate_x86_cpu = { &vmstate_msr_hyperv_reenlightenment, &vmstate_avx512, &vmstate_xss, + &vmstate_umwait, &vmstate_tsc_khz, &vmstate_msr_smi_count, #ifdef TARGET_X86_64 From 417258f139e61899511d6a99c11b276f12bbbd86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 18 Oct 2019 15:59:06 +0200 Subject: [PATCH 27/34] hw/i386/pc: Extract pc_gsi_create() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The GSI creation code is common to all PC machines, extract the common code. Reviewed-by: Aleksandar Markovic Reviewed-by: Thomas Huth Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20191018135910.24286-2-philmd@redhat.com> Signed-off-by: Paolo Bonzini --- hw/i386/pc.c | 15 +++++++++++++++ hw/i386/pc_piix.c | 9 +-------- hw/i386/pc_q35.c | 9 +-------- include/hw/i386/pc.h | 2 ++ 4 files changed, 19 insertions(+), 16 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index a8888dd622..e8a54acc38 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -355,6 +355,21 @@ void gsi_handler(void *opaque, int n, int level) qemu_set_irq(s->ioapic_irq[n], level); } +GSIState *pc_gsi_create(qemu_irq **irqs, bool pci_enabled) +{ + GSIState *s; + + s = g_new0(GSIState, 1); + if (kvm_ioapic_in_kernel()) { + kvm_pc_setup_irq_routing(pci_enabled); + *irqs = qemu_allocate_irqs(kvm_pc_gsi_handler, s, GSI_NUM_PINS); + } else { + *irqs = qemu_allocate_irqs(gsi_handler, s, GSI_NUM_PINS); + } + + return s; +} + static void ioport80_write(void *opaque, hwaddr addr, uint64_t data, unsigned size) { diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index a86317cdff..0cc951a0b5 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -189,14 +189,7 @@ static void pc_init1(MachineState *machine, xen_load_linux(pcms); } - gsi_state = g_malloc0(sizeof(*gsi_state)); - if (kvm_ioapic_in_kernel()) { - kvm_pc_setup_irq_routing(pcmc->pci_enabled); - x86ms->gsi = qemu_allocate_irqs(kvm_pc_gsi_handler, gsi_state, - GSI_NUM_PINS); - } else { - x86ms->gsi = qemu_allocate_irqs(gsi_handler, gsi_state, GSI_NUM_PINS); - } + gsi_state = pc_gsi_create(&x86ms->gsi, pcmc->pci_enabled); if (pcmc->pci_enabled) { pci_bus = i440fx_init(host_type, diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 75c8caf7c2..255c803688 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -212,14 +212,7 @@ static void pc_q35_init(MachineState *machine) } /* irq lines */ - gsi_state = g_malloc0(sizeof(*gsi_state)); - if (kvm_ioapic_in_kernel()) { - kvm_pc_setup_irq_routing(pcmc->pci_enabled); - x86ms->gsi = qemu_allocate_irqs(kvm_pc_gsi_handler, gsi_state, - GSI_NUM_PINS); - } else { - x86ms->gsi = qemu_allocate_irqs(gsi_handler, gsi_state, GSI_NUM_PINS); - } + gsi_state = pc_gsi_create(&x86ms->gsi, pcmc->pci_enabled); /* create pci host bus */ q35_host = Q35_HOST_DEVICE(qdev_create(NULL, TYPE_Q35_HOST_DEVICE)); diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 13c4eac0f6..8c5dc39d84 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -157,6 +157,8 @@ typedef struct GSIState { void gsi_handler(void *opaque, int n, int level); +GSIState *pc_gsi_create(qemu_irq **irqs, bool pci_enabled); + /* vmport.c */ #define TYPE_VMPORT "vmport" typedef uint32_t (VMPortReadFunc)(void *opaque, uint32_t address); From b00c6f18a618f42f90e8608fbc4dbc6c133ad0e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 18 Oct 2019 15:59:08 +0200 Subject: [PATCH 28/34] hw/i386/pc: Move gsi_state creation code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The code block related to IRQ starts few lines later. Move the comment and the pc_gsi_create() invocation where we start to use the IRQs. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20191018135910.24286-4-philmd@redhat.com> Signed-off-by: Paolo Bonzini --- hw/i386/pc_q35.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 255c803688..6cf12b792b 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -211,9 +211,6 @@ static void pc_q35_init(MachineState *machine) rom_memory, &ram_memory); } - /* irq lines */ - gsi_state = pc_gsi_create(&x86ms->gsi, pcmc->pci_enabled); - /* create pci host bus */ q35_host = Q35_HOST_DEVICE(qdev_create(NULL, TYPE_Q35_HOST_DEVICE)); @@ -247,6 +244,9 @@ static void pc_q35_init(MachineState *machine) object_property_set_link(OBJECT(machine), OBJECT(lpc), PC_MACHINE_ACPI_DEVICE_PROP, &error_abort); + /* irq lines */ + gsi_state = pc_gsi_create(&x86ms->gsi, pcmc->pci_enabled); + ich9_lpc = ICH9_LPC_DEVICE(lpc); lpc_dev = DEVICE(lpc); for (i = 0; i < GSI_NUM_PINS; i++) { From 4501d317b50e52de090192aab6244060fbe42da0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 18 Oct 2019 15:59:09 +0200 Subject: [PATCH 29/34] hw/i386/pc: Extract pc_i8259_create() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The i8259 creation code is common to all PC machines, extract the common code. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20191018135910.24286-5-philmd@redhat.com> Signed-off-by: Paolo Bonzini --- hw/i386/pc.c | 19 +++++++++++++++++++ hw/i386/pc_piix.c | 13 +------------ hw/i386/pc_q35.c | 14 +------------- include/hw/i386/pc.h | 1 + 4 files changed, 22 insertions(+), 25 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index e8a54acc38..5fce60c856 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1474,6 +1474,25 @@ void pc_nic_init(PCMachineClass *pcmc, ISABus *isa_bus, PCIBus *pci_bus) rom_reset_order_override(); } +void pc_i8259_create(ISABus *isa_bus, qemu_irq *i8259_irqs) +{ + qemu_irq *i8259; + + if (kvm_pic_in_kernel()) { + i8259 = kvm_i8259_init(isa_bus); + } else if (xen_enabled()) { + i8259 = xen_interrupt_controller_init(); + } else { + i8259 = i8259_init(isa_bus, pc_allocate_cpu_irq()); + } + + for (size_t i = 0; i < ISA_NUM_IRQS; i++) { + i8259_irqs[i] = i8259[i]; + } + + g_free(i8259); +} + void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name) { DeviceState *dev; diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 0cc951a0b5..648dc9ab2d 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -82,7 +82,6 @@ static void pc_init1(MachineState *machine, ISABus *isa_bus; PCII440FXState *i440fx_state; int piix3_devfn = -1; - qemu_irq *i8259; qemu_irq smi_irq; GSIState *gsi_state; DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; @@ -209,18 +208,8 @@ static void pc_init1(MachineState *machine, } isa_bus_irqs(isa_bus, x86ms->gsi); - if (kvm_pic_in_kernel()) { - i8259 = kvm_i8259_init(isa_bus); - } else if (xen_enabled()) { - i8259 = xen_interrupt_controller_init(); - } else { - i8259 = i8259_init(isa_bus, pc_allocate_cpu_irq()); - } + pc_i8259_create(isa_bus, gsi_state->i8259_irq); - for (i = 0; i < ISA_NUM_IRQS; i++) { - gsi_state->i8259_irq[i] = i8259[i]; - } - g_free(i8259); if (pcmc->pci_enabled) { ioapic_init_gsi(gsi_state, "i440fx"); } diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 6cf12b792b..157d1daa60 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -130,7 +130,6 @@ static void pc_q35_init(MachineState *machine) MemoryRegion *ram_memory; GSIState *gsi_state; ISABus *isa_bus; - qemu_irq *i8259; int i; ICH9LPCState *ich9_lpc; PCIDevice *ahci; @@ -257,18 +256,7 @@ static void pc_q35_init(MachineState *machine) pci_bus_set_route_irq_fn(host_bus, ich9_route_intx_pin_to_irq); isa_bus = ich9_lpc->isa_bus; - if (kvm_pic_in_kernel()) { - i8259 = kvm_i8259_init(isa_bus); - } else if (xen_enabled()) { - i8259 = xen_interrupt_controller_init(); - } else { - i8259 = i8259_init(isa_bus, pc_allocate_cpu_irq()); - } - - for (i = 0; i < ISA_NUM_IRQS; i++) { - gsi_state->i8259_irq[i] = i8259[i]; - } - g_free(i8259); + pc_i8259_create(isa_bus, gsi_state->i8259_irq); if (pcmc->pci_enabled) { ioapic_init_gsi(gsi_state, "q35"); diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 8c5dc39d84..5923318ea5 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -219,6 +219,7 @@ void pc_pci_device_init(PCIBus *pci_bus); typedef void (*cpu_set_smm_t)(int smm, void *arg); +void pc_i8259_create(ISABus *isa_bus, qemu_irq *i8259_irqs); void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name); ISADevice *pc_find_fdc0(void); From b4de58a4e1b3d1cfe7ea1a3fc1c75e032c13ffa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 15 Oct 2019 18:26:57 +0200 Subject: [PATCH 30/34] hw/i386/pc: Remove kvm_i386.h include MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By extracting pc_gsi_create() and pc_i8259_create() we removed the access to "kvm_i386.h" from the machine code. We can now remove it. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20191015162705.28087-25-philmd@redhat.com> Signed-off-by: Paolo Bonzini --- hw/i386/pc_piix.c | 1 - hw/i386/pc_q35.c | 1 - 2 files changed, 2 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 648dc9ab2d..3a4a64a38d 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -57,7 +57,6 @@ #endif #include "migration/global_state.h" #include "migration/misc.h" -#include "kvm_i386.h" #include "sysemu/numa.h" #define MAX_IDE_BUS 2 diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 157d1daa60..def3bc2d7b 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -36,7 +36,6 @@ #include "hw/timer/mc146818rtc.h" #include "hw/xen/xen.h" #include "sysemu/kvm.h" -#include "kvm_i386.h" #include "hw/kvm/clock.h" #include "hw/pci-host/q35.h" #include "hw/qdev-properties.h" From 53e4b8018e600c256bd60e5898f6747eddbc2131 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herv=C3=A9=20Poussineau?= Date: Fri, 18 Oct 2019 15:35:44 +0200 Subject: [PATCH 31/34] mc146818rtc: move structure to header file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We are now able to embed a timer in another object. Acked-by: Michael S. Tsirkin Acked-by: Paolo Bonzini Signed-off-by: Hervé Poussineau Message-Id: <20171216090228.28505-4-hpoussin@reactos.org> Reviewed-by: Aleksandar Markovic Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20191018133547.10936-2-philmd@redhat.com> Signed-off-by: Paolo Bonzini --- hw/timer/mc146818rtc.c | 30 ------------------------------ include/hw/timer/mc146818rtc.h | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 30 deletions(-) diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c index c979db0992..09edb934e5 100644 --- a/hw/timer/mc146818rtc.c +++ b/hw/timer/mc146818rtc.c @@ -71,36 +71,6 @@ #define RTC_CLOCK_RATE 32768 #define UIP_HOLD_LENGTH (8 * NANOSECONDS_PER_SECOND / 32768) -#define MC146818_RTC(obj) OBJECT_CHECK(RTCState, (obj), TYPE_MC146818_RTC) - -typedef struct RTCState { - ISADevice parent_obj; - - MemoryRegion io; - MemoryRegion coalesced_io; - uint8_t cmos_data[128]; - uint8_t cmos_index; - int32_t base_year; - uint64_t base_rtc; - uint64_t last_update; - int64_t offset; - qemu_irq irq; - int it_shift; - /* periodic timer */ - QEMUTimer *periodic_timer; - int64_t next_periodic_time; - /* update-ended timer */ - QEMUTimer *update_timer; - uint64_t next_alarm_time; - uint16_t irq_reinject_on_ack_count; - uint32_t irq_coalesced; - uint32_t period; - QEMUTimer *coalesced_timer; - LostTickPolicy lost_tick_policy; - Notifier suspend_notifier; - QLIST_ENTRY(RTCState) link; -} RTCState; - static void rtc_set_time(RTCState *s); static void rtc_update_time(RTCState *s); static void rtc_set_cmos(RTCState *s, const struct tm *tm); diff --git a/include/hw/timer/mc146818rtc.h b/include/hw/timer/mc146818rtc.h index fe6ed63f71..0f1c886e5b 100644 --- a/include/hw/timer/mc146818rtc.h +++ b/include/hw/timer/mc146818rtc.h @@ -1,10 +1,43 @@ #ifndef MC146818RTC_H #define MC146818RTC_H +#include "qapi/qapi-types-misc.h" +#include "qemu/queue.h" +#include "qemu/timer.h" #include "hw/isa/isa.h" #include "hw/timer/mc146818rtc_regs.h" #define TYPE_MC146818_RTC "mc146818rtc" +#define MC146818_RTC(obj) OBJECT_CHECK(RTCState, (obj), TYPE_MC146818_RTC) + +typedef struct RTCState { + ISADevice parent_obj; + + MemoryRegion io; + MemoryRegion coalesced_io; + uint8_t cmos_data[128]; + uint8_t cmos_index; + int32_t base_year; + uint64_t base_rtc; + uint64_t last_update; + int64_t offset; + qemu_irq irq; + int it_shift; + /* periodic timer */ + QEMUTimer *periodic_timer; + int64_t next_periodic_time; + /* update-ended timer */ + QEMUTimer *update_timer; + uint64_t next_alarm_time; + uint16_t irq_reinject_on_ack_count; + uint32_t irq_coalesced; + uint32_t period; + QEMUTimer *coalesced_timer; + Notifier clock_reset_notifier; + LostTickPolicy lost_tick_policy; + Notifier suspend_notifier; + QLIST_ENTRY(RTCState) link; +} RTCState; ISADevice *mc146818_rtc_init(ISABus *bus, int base_year, qemu_irq intercept_irq); From d578f7dfb4e4b911fd7c6418b9344dc3dccad7f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 18 Oct 2019 15:35:45 +0200 Subject: [PATCH 32/34] mc146818rtc: Move RTC_ISA_IRQ definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ISA default number for the RTC devices is not related to its registers neither. Move this definition to "hw/timer/mc146818rtc.h". Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20191018133547.10936-3-philmd@redhat.com> Reviewed-by: Thomas Huth Signed-off-by: Paolo Bonzini Signed-off-by: Philippe Mathieu-Daudé --- include/hw/timer/mc146818rtc.h | 2 ++ include/hw/timer/mc146818rtc_regs.h | 2 -- tests/rtc-test.c | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/include/hw/timer/mc146818rtc.h b/include/hw/timer/mc146818rtc.h index 0f1c886e5b..17761cf6d9 100644 --- a/include/hw/timer/mc146818rtc.h +++ b/include/hw/timer/mc146818rtc.h @@ -39,6 +39,8 @@ typedef struct RTCState { QLIST_ENTRY(RTCState) link; } RTCState; +#define RTC_ISA_IRQ 8 + ISADevice *mc146818_rtc_init(ISABus *bus, int base_year, qemu_irq intercept_irq); void rtc_set_memory(ISADevice *dev, int addr, int val); diff --git a/include/hw/timer/mc146818rtc_regs.h b/include/hw/timer/mc146818rtc_regs.h index bfbb57e570..631f71cfd9 100644 --- a/include/hw/timer/mc146818rtc_regs.h +++ b/include/hw/timer/mc146818rtc_regs.h @@ -27,8 +27,6 @@ #include "qemu/timer.h" -#define RTC_ISA_IRQ 8 - #define RTC_SECONDS 0 #define RTC_SECONDS_ALARM 1 #define RTC_MINUTES 2 diff --git a/tests/rtc-test.c b/tests/rtc-test.c index 6309b0ef6c..18f895690f 100644 --- a/tests/rtc-test.c +++ b/tests/rtc-test.c @@ -15,6 +15,7 @@ #include "libqtest-single.h" #include "qemu/timer.h" +#include "hw/timer/mc146818rtc.h" #include "hw/timer/mc146818rtc_regs.h" #define UIP_HOLD_LENGTH (8 * NANOSECONDS_PER_SECOND / 32768) From d93884e89bb6bf26ebffdee5887907cf940b3090 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 18 Oct 2019 15:35:46 +0200 Subject: [PATCH 33/34] mc146818rtc: Include mc146818rtc_regs.h directly in mc146818rtc.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Devices/boards wanting to use the MC146818 RTC don't need the knowledge its internal registers. Move the "mc146818rtc_regs.h" inclusion to mc146818rtc.c where it is required. We can not move this file from include/hw/timer/ to hw/timer/ for local inclusion because the ACPI FADT table use the RTC_CENTURY register address. Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20191018133547.10936-4-philmd@redhat.com> Signed-off-by: Paolo Bonzini Signed-off-by: Philippe Mathieu-Daudé --- hw/timer/mc146818rtc.c | 1 + include/hw/timer/mc146818rtc.h | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c index 09edb934e5..dce4dae483 100644 --- a/hw/timer/mc146818rtc.c +++ b/hw/timer/mc146818rtc.c @@ -40,6 +40,7 @@ #include "qapi/qapi-events-misc-target.h" #include "qapi/visitor.h" #include "exec/address-spaces.h" +#include "hw/timer/mc146818rtc_regs.h" #ifdef TARGET_I386 #include "qapi/qapi-commands-misc-target.h" diff --git a/include/hw/timer/mc146818rtc.h b/include/hw/timer/mc146818rtc.h index 17761cf6d9..a857dcdc69 100644 --- a/include/hw/timer/mc146818rtc.h +++ b/include/hw/timer/mc146818rtc.h @@ -5,7 +5,6 @@ #include "qemu/queue.h" #include "qemu/timer.h" #include "hw/isa/isa.h" -#include "hw/timer/mc146818rtc_regs.h" #define TYPE_MC146818_RTC "mc146818rtc" #define MC146818_RTC(obj) OBJECT_CHECK(RTCState, (obj), TYPE_MC146818_RTC) From df84f17d1beabbb2aca39be18b21afc277cd6754 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herv=C3=A9=20Poussineau?= Date: Fri, 18 Oct 2019 15:35:47 +0200 Subject: [PATCH 34/34] mc146818rtc: always register rtc to rtc list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We are not required anymore to use rtc_init() function. Acked-by: Michael S. Tsirkin Acked-by: Paolo Bonzini Signed-off-by: Hervé Poussineau Message-Id: <20171216090228.28505-5-hpoussin@reactos.org> [PMD: rebased, fix OBJECT() value] Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20191018133547.10936-5-philmd@redhat.com> Signed-off-by: Paolo Bonzini --- hw/timer/mc146818rtc.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c index dce4dae483..0d7784b104 100644 --- a/hw/timer/mc146818rtc.c +++ b/hw/timer/mc146818rtc.c @@ -964,17 +964,16 @@ static void rtc_realizefn(DeviceState *dev, Error **errp) object_property_add_tm(OBJECT(s), "date", rtc_get_date, NULL); qdev_init_gpio_out(dev, &s->irq, 1); + QLIST_INSERT_HEAD(&rtc_devices, s, link); } ISADevice *mc146818_rtc_init(ISABus *bus, int base_year, qemu_irq intercept_irq) { DeviceState *dev; ISADevice *isadev; - RTCState *s; isadev = isa_create(bus, TYPE_MC146818_RTC); dev = DEVICE(isadev); - s = MC146818_RTC(isadev); qdev_prop_set_int32(dev, "base_year", base_year); qdev_init_nofail(dev); if (intercept_irq) { @@ -982,9 +981,8 @@ ISADevice *mc146818_rtc_init(ISABus *bus, int base_year, qemu_irq intercept_irq) } else { isa_connect_gpio_out(isadev, 0, RTC_ISA_IRQ); } - QLIST_INSERT_HEAD(&rtc_devices, s, link); - object_property_add_alias(qdev_get_machine(), "rtc-time", OBJECT(s), + object_property_add_alias(qdev_get_machine(), "rtc-time", OBJECT(isadev), "date", NULL); return isadev; @@ -1016,8 +1014,6 @@ static void rtc_class_initfn(ObjectClass *klass, void *data) dc->reset = rtc_resetdev; dc->vmsd = &vmstate_rtc; dc->props = mc146818rtc_properties; - /* Reason: needs to be wired up by rtc_init() */ - dc->user_creatable = false; } static const TypeInfo mc146818rtc_info = {