2004-03-14 15:20:30 +03:00
|
|
|
/*
|
|
|
|
* QEMU PC System Emulator
|
2007-09-17 01:08:06 +04:00
|
|
|
*
|
2004-03-14 15:20:30 +03:00
|
|
|
* Copyright (c) 2003-2004 Fabrice Bellard
|
2007-09-17 01:08:06 +04:00
|
|
|
*
|
2004-03-14 15:20:30 +03:00
|
|
|
* 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.
|
|
|
|
*/
|
2013-02-04 18:40:22 +04:00
|
|
|
#include "hw/hw.h"
|
2013-02-05 20:06:20 +04:00
|
|
|
#include "hw/i386/pc.h"
|
|
|
|
#include "hw/char/serial.h"
|
|
|
|
#include "hw/i386/apic.h"
|
2014-12-19 04:43:35 +03:00
|
|
|
#include "hw/i386/topology.h"
|
|
|
|
#include "sysemu/cpus.h"
|
2013-02-05 20:06:20 +04:00
|
|
|
#include "hw/block/fdc.h"
|
2013-02-04 18:40:22 +04:00
|
|
|
#include "hw/ide.h"
|
|
|
|
#include "hw/pci/pci.h"
|
2015-06-02 14:23:07 +03:00
|
|
|
#include "hw/pci/pci_bus.h"
|
2013-02-05 20:06:20 +04:00
|
|
|
#include "hw/nvram/fw_cfg.h"
|
|
|
|
#include "hw/timer/hpet.h"
|
2015-08-12 05:08:20 +03:00
|
|
|
#include "hw/smbios/smbios.h"
|
2013-02-04 18:40:22 +04:00
|
|
|
#include "hw/loader.h"
|
2009-09-20 18:58:02 +04:00
|
|
|
#include "elf.h"
|
2013-03-18 20:36:02 +04:00
|
|
|
#include "multiboot.h"
|
2013-02-05 20:06:20 +04:00
|
|
|
#include "hw/timer/mc146818rtc.h"
|
|
|
|
#include "hw/timer/i8254.h"
|
|
|
|
#include "hw/audio/pcspk.h"
|
2013-02-04 18:40:22 +04:00
|
|
|
#include "hw/pci/msi.h"
|
|
|
|
#include "hw/sysbus.h"
|
2012-12-17 21:20:04 +04:00
|
|
|
#include "sysemu/sysemu.h"
|
2015-02-08 21:51:16 +03:00
|
|
|
#include "sysemu/numa.h"
|
2012-12-17 21:20:04 +04:00
|
|
|
#include "sysemu/kvm.h"
|
2015-03-14 16:24:37 +03:00
|
|
|
#include "sysemu/qtest.h"
|
2012-07-26 18:35:13 +04:00
|
|
|
#include "kvm_i386.h"
|
2013-02-05 20:06:20 +04:00
|
|
|
#include "hw/xen/xen.h"
|
2014-10-07 15:59:18 +04:00
|
|
|
#include "sysemu/block-backend.h"
|
2013-02-05 20:06:20 +04:00
|
|
|
#include "hw/block/block.h"
|
2010-04-27 13:50:11 +04:00
|
|
|
#include "ui/qemu-spice.h"
|
2012-12-17 21:19:49 +04:00
|
|
|
#include "exec/memory.h"
|
|
|
|
#include "exec/address-spaces.h"
|
2012-12-17 21:20:04 +04:00
|
|
|
#include "sysemu/arch_init.h"
|
2012-12-17 21:20:00 +04:00
|
|
|
#include "qemu/bitmap.h"
|
2013-03-21 03:23:17 +04:00
|
|
|
#include "qemu/config-file.h"
|
2015-03-17 20:29:20 +03:00
|
|
|
#include "qemu/error-report.h"
|
2013-04-15 10:19:22 +04:00
|
|
|
#include "hw/acpi/acpi.h"
|
2014-03-14 23:33:51 +04:00
|
|
|
#include "hw/acpi/cpu_hotplug.h"
|
2013-04-30 20:00:53 +04:00
|
|
|
#include "hw/boards.h"
|
2013-07-29 18:47:57 +04:00
|
|
|
#include "hw/pci/pci_host.h"
|
i386: ACPI table generation code from seabios
This adds C code for generating ACPI tables at runtime,
imported from seabios git tree
commit 51684b7ced75fb76776e8ee84833fcfb6ecf12dd
Although ACPI tables come from a system BIOS on real hw,
it makes sense that the ACPI tables are coupled with the
virtual machine, since they have to abstract the x86 machine to
the OS's.
This is widely desired as a way to avoid the churn
and proliferation of QEMU-specific interfaces
associated with ACPI tables in bios code.
Notes:
As BIOS can reprogram devices prior to loading
ACPI tables, we pre-format ACPI tables but defer loading
hardware configuration there until tables are loaded.
The code structure was intentionally kept as close
to the seabios original as possible, to simplify
comparison and making sure we didn't lose anything
in translation.
Minor code duplication results, to help ensure there are no functional
regressions, I think it's better to merge it like this and do more code
changes in follow-up patches.
Cross-version compatibility concerns have been addressed:
ACPI tables are exposed to guest as FW_CFG entries.
When running with -M 1.5 and older, this patch disables ACPI
table generation, and doesn't expose ACPI
tables to guest.
As table content is likely to change over time,
the following measures are taken to simplify
cross-version migration:
- All tables besides the RSDP are packed in a single FW CFG entry.
This entry size is currently 23K. We round it up to 64K
to avoid too much churn there.
- Tables are placed in special ROM blob (not mapped into guest memory)
which is automatically migrated together with the guest, same
as BIOS code.
- Offsets where hardware configuration is loaded in ACPI tables
are also migrated, this is in case future ACPI changes make us
rearrange the tables in memory.
This patch reuses some code from SeaBIOS, which was originally under
LGPLv2 and then relicensed to GPLv3 or LGPLv3, in QEMU under GPLv2+. This
relicensing has been acked by all contributors that had contributed to the
code since the v2->v3 relicense. ACKs approving the v2+ relicensing are
listed below. The list might include ACKs from people not holding
copyright on any parts of the reused code, but it's better to err on the
side of caution and include them.
Affected SeaBIOS files (GPLv2+ license headers added)
<http://thread.gmane.org/gmane.comp.bios.coreboot.seabios/5949>:
src/acpi-dsdt-cpu-hotplug.dsl
src/acpi-dsdt-dbug.dsl
src/acpi-dsdt-hpet.dsl
src/acpi-dsdt-isa.dsl
src/acpi-dsdt-pci-crs.dsl
src/acpi.c
src/acpi.h
src/ssdt-misc.dsl
src/ssdt-pcihp.dsl
src/ssdt-proc.dsl
tools/acpi_extract.py
tools/acpi_extract_preprocess.py
Each one of the listed people agreed to the following:
> If you allow the use of your contribution in QEMU under the
> terms of GPLv2 or later as proposed by this patch,
> please respond to this mail including the line:
>
> Acked-by: Name <email address>
Acked-by: Gerd Hoffmann <kraxel@redhat.com>
Acked-by: Jan Kiszka <jan.kiszka@siemens.com>
Acked-by: Jason Baron <jbaron@akamai.com>
Acked-by: David Woodhouse <David.Woodhouse@intel.com>
Acked-by: Gleb Natapov <gleb@redhat.com>
Acked-by: Marcelo Tosatti <mtosatti@redhat.com>
Acked-by: Dave Frodin <dave.frodin@se-eng.com>
Acked-by: Paolo Bonzini <pbonzini@redhat.com>
Acked-by: Kevin O'Connor <kevin@koconnor.net>
Acked-by: Laszlo Ersek <lersek@redhat.com>
Acked-by: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>
Acked-by: Isaku Yamahata <yamahata@valinux.co.jp>
Acked-by: Magnus Christensson <magnus.christensson@intel.com>
Acked-by: Hu Tao <hutao@cn.fujitsu.com>
Acked-by: Eduardo Habkost <ehabkost@redhat.com>
Reviewed-by: Gerd Hoffmann <kraxel@redhat.com>
Tested-by: Gerd Hoffmann <kraxel@redhat.com>
Reviewed-by: Igor Mammedov <imammedo@redhat.com>
Tested-by: Igor Mammedov <imammedo@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2013-07-24 19:56:14 +04:00
|
|
|
#include "acpi-build.h"
|
2014-06-02 17:25:12 +04:00
|
|
|
#include "hw/mem/pc-dimm.h"
|
2014-06-02 17:25:27 +04:00
|
|
|
#include "qapi/visitor.h"
|
2014-11-21 19:18:52 +03:00
|
|
|
#include "qapi-visit.h"
|
2015-12-17 19:16:08 +03:00
|
|
|
#include "qom/cpu.h"
|
2004-03-14 15:20:30 +03:00
|
|
|
|
2010-05-30 00:23:49 +04:00
|
|
|
/* debug PC/ISA interrupts */
|
|
|
|
//#define DEBUG_IRQ
|
|
|
|
|
|
|
|
#ifdef DEBUG_IRQ
|
|
|
|
#define DPRINTF(fmt, ...) \
|
|
|
|
do { printf("CPUIRQ: " fmt , ## __VA_ARGS__); } while (0)
|
|
|
|
#else
|
|
|
|
#define DPRINTF(fmt, ...)
|
|
|
|
#endif
|
|
|
|
|
2008-09-18 22:27:29 +04:00
|
|
|
#define BIOS_CFG_IOPORT 0x510
|
2009-02-27 23:12:36 +03:00
|
|
|
#define FW_CFG_ACPI_TABLES (FW_CFG_ARCH_LOCAL + 0)
|
qemu: Add support for SMBIOS command line otions (Alex Williamson)
Create a new -smbios option (x86-only) to allow binary SMBIOS entries
to be passed through to the BIOS or modify the default values of
individual fields of type 0 and 1 entries on the command line.
Binary SMBIOS entries can be generated as follows:
dmidecode -t 1 -u | grep $'^\t\t[^"]' | xargs -n1 | \
perl -lne 'printf "%c", hex($_)' > smbios_type_1.bin
These can then be passed to the BIOS using this switch:
-smbios file=smbios_type_1.bin
Command line generation supports the following syntax:
-smbios type=0[,vendor=str][,version=str][,date=str][,release=%d.%d]
-smbios type=1[,manufacturer=str][,product=str][,version=str][,serial=str]
[,uuid=$(uuidgen)][,sku=str][,family=str]
For instance, to add a serial number to the type 1 table:
-smbios type=1,serial=0123456789
Interface is extensible to support more fields/tables as needed.
aliguori: remove texi formatting from help output
Signed-off-by: Alex Williamson <alex.williamson@hp.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@7163 c046a42c-6fe2-441c-8c8c-71466251a162
2009-04-17 22:59:56 +04:00
|
|
|
#define FW_CFG_SMBIOS_ENTRIES (FW_CFG_ARCH_LOCAL + 1)
|
2009-08-06 18:25:50 +04:00
|
|
|
#define FW_CFG_IRQ0_OVERRIDE (FW_CFG_ARCH_LOCAL + 2)
|
2010-02-15 20:33:46 +03:00
|
|
|
#define FW_CFG_E820_TABLE (FW_CFG_ARCH_LOCAL + 3)
|
2010-06-14 12:29:28 +04:00
|
|
|
#define FW_CFG_HPET (FW_CFG_ARCH_LOCAL + 4)
|
2004-03-14 15:20:30 +03:00
|
|
|
|
2010-02-15 20:33:46 +03:00
|
|
|
#define E820_NR_ENTRIES 16
|
|
|
|
|
|
|
|
struct e820_entry {
|
|
|
|
uint64_t address;
|
|
|
|
uint64_t length;
|
|
|
|
uint32_t type;
|
2011-08-31 14:38:01 +04:00
|
|
|
} QEMU_PACKED __attribute((__aligned__(4)));
|
2010-02-15 20:33:46 +03:00
|
|
|
|
|
|
|
struct e820_table {
|
|
|
|
uint32_t count;
|
|
|
|
struct e820_entry entry[E820_NR_ENTRIES];
|
2011-08-31 14:38:01 +04:00
|
|
|
} QEMU_PACKED __attribute((__aligned__(4)));
|
2010-02-15 20:33:46 +03:00
|
|
|
|
2013-10-18 13:31:54 +04:00
|
|
|
static struct e820_table e820_reserve;
|
|
|
|
static struct e820_entry *e820_table;
|
|
|
|
static unsigned e820_entries;
|
2011-02-05 17:35:00 +03:00
|
|
|
struct hpet_fw_config hpet_cfg = {.count = UINT8_MAX};
|
2010-02-15 20:33:46 +03:00
|
|
|
|
2011-10-07 11:19:35 +04:00
|
|
|
void gsi_handler(void *opaque, int n, int level)
|
2009-08-09 20:44:55 +04:00
|
|
|
{
|
2011-10-07 11:19:35 +04:00
|
|
|
GSIState *s = opaque;
|
2009-08-09 20:44:55 +04:00
|
|
|
|
2011-10-07 11:19:35 +04:00
|
|
|
DPRINTF("pc: %s GSI %d\n", level ? "raising" : "lowering", n);
|
|
|
|
if (n < ISA_NUM_IRQS) {
|
|
|
|
qemu_set_irq(s->i8259_irq[n], level);
|
2009-08-09 20:44:56 +04:00
|
|
|
}
|
2011-10-07 11:19:35 +04:00
|
|
|
qemu_set_irq(s->ioapic_irq[n], level);
|
2011-10-07 11:19:34 +04:00
|
|
|
}
|
2009-08-09 20:44:55 +04:00
|
|
|
|
2012-09-19 15:50:08 +04:00
|
|
|
static void ioport80_write(void *opaque, hwaddr addr, uint64_t data,
|
|
|
|
unsigned size)
|
2004-03-14 15:20:30 +03:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2013-01-09 22:10:22 +04:00
|
|
|
static uint64_t ioport80_read(void *opaque, hwaddr addr, unsigned size)
|
|
|
|
{
|
2013-01-11 20:41:43 +04:00
|
|
|
return 0xffffffffffffffffULL;
|
2013-01-09 22:10:22 +04:00
|
|
|
}
|
|
|
|
|
2004-05-09 01:03:41 +04:00
|
|
|
/* MSDOS compatibility mode FPU exception support */
|
2007-04-07 22:14:41 +04:00
|
|
|
static qemu_irq ferr_irq;
|
2010-05-14 11:29:09 +04:00
|
|
|
|
|
|
|
void pc_register_ferr_irq(qemu_irq irq)
|
|
|
|
{
|
|
|
|
ferr_irq = irq;
|
|
|
|
}
|
|
|
|
|
2004-05-09 01:03:41 +04:00
|
|
|
/* XXX: add IGNNE support */
|
|
|
|
void cpu_set_ferr(CPUX86State *s)
|
|
|
|
{
|
2007-04-07 22:14:41 +04:00
|
|
|
qemu_irq_raise(ferr_irq);
|
2004-05-09 01:03:41 +04:00
|
|
|
}
|
|
|
|
|
2012-09-19 15:50:08 +04:00
|
|
|
static void ioportF0_write(void *opaque, hwaddr addr, uint64_t data,
|
|
|
|
unsigned size)
|
2004-05-09 01:03:41 +04:00
|
|
|
{
|
2007-04-07 22:14:41 +04:00
|
|
|
qemu_irq_lower(ferr_irq);
|
2004-05-09 01:03:41 +04:00
|
|
|
}
|
|
|
|
|
2013-01-09 22:10:22 +04:00
|
|
|
static uint64_t ioportF0_read(void *opaque, hwaddr addr, unsigned size)
|
|
|
|
{
|
2013-01-11 20:41:43 +04:00
|
|
|
return 0xffffffffffffffffULL;
|
2013-01-09 22:10:22 +04:00
|
|
|
}
|
|
|
|
|
2004-05-20 18:02:14 +04:00
|
|
|
/* TSC handling */
|
|
|
|
uint64_t cpu_get_tsc(CPUX86State *env)
|
|
|
|
{
|
2009-08-11 02:07:24 +04:00
|
|
|
return cpu_get_ticks();
|
2004-05-20 18:02:14 +04:00
|
|
|
}
|
|
|
|
|
2005-07-02 22:11:44 +04:00
|
|
|
/* IRQ handling */
|
2012-03-14 04:38:23 +04:00
|
|
|
int cpu_get_pic_interrupt(CPUX86State *env)
|
2005-07-02 22:11:44 +04:00
|
|
|
{
|
2013-12-23 13:04:02 +04:00
|
|
|
X86CPU *cpu = x86_env_get_cpu(env);
|
2005-07-02 22:11:44 +04:00
|
|
|
int intno;
|
|
|
|
|
2013-12-23 13:04:02 +04:00
|
|
|
intno = apic_get_interrupt(cpu->apic_state);
|
2005-07-02 22:11:44 +04:00
|
|
|
if (intno >= 0) {
|
|
|
|
return intno;
|
|
|
|
}
|
|
|
|
/* read the irq from the PIC */
|
2013-12-23 13:04:02 +04:00
|
|
|
if (!apic_accept_pic_intr(cpu->apic_state)) {
|
2007-10-09 07:08:56 +04:00
|
|
|
return -1;
|
2010-06-19 11:42:08 +04:00
|
|
|
}
|
2007-10-09 07:08:56 +04:00
|
|
|
|
2005-07-02 22:11:44 +04:00
|
|
|
intno = pic_read_irq(isa_pic);
|
|
|
|
return intno;
|
|
|
|
}
|
|
|
|
|
2007-04-07 22:14:41 +04:00
|
|
|
static void pic_irq_request(void *opaque, int irq, int level)
|
2005-07-02 22:11:44 +04:00
|
|
|
{
|
2013-05-30 00:29:20 +04:00
|
|
|
CPUState *cs = first_cpu;
|
|
|
|
X86CPU *cpu = X86_CPU(cs);
|
2008-04-13 20:08:30 +04:00
|
|
|
|
2010-05-30 00:23:49 +04:00
|
|
|
DPRINTF("pic_irqs: %s irq %d\n", level? "raise" : "lower", irq);
|
2013-12-23 13:04:02 +04:00
|
|
|
if (cpu->apic_state) {
|
2013-06-25 01:50:24 +04:00
|
|
|
CPU_FOREACH(cs) {
|
2013-05-30 00:29:20 +04:00
|
|
|
cpu = X86_CPU(cs);
|
2013-12-23 13:04:02 +04:00
|
|
|
if (apic_accept_pic_intr(cpu->apic_state)) {
|
|
|
|
apic_deliver_pic_intr(cpu->apic_state, level);
|
2010-06-19 11:42:08 +04:00
|
|
|
}
|
2008-08-19 16:55:20 +04:00
|
|
|
}
|
|
|
|
} else {
|
2013-01-18 01:30:20 +04:00
|
|
|
if (level) {
|
2013-01-18 18:03:43 +04:00
|
|
|
cpu_interrupt(cs, CPU_INTERRUPT_HARD);
|
2013-01-18 01:30:20 +04:00
|
|
|
} else {
|
|
|
|
cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
|
|
|
|
}
|
2008-04-13 20:08:30 +04:00
|
|
|
}
|
2005-07-02 22:11:44 +04:00
|
|
|
}
|
|
|
|
|
2004-03-31 22:58:38 +04:00
|
|
|
/* PC cmos mappings */
|
|
|
|
|
2004-03-14 15:20:30 +03:00
|
|
|
#define REG_EQUIPMENT_BYTE 0x14
|
|
|
|
|
2011-02-13 00:23:12 +03:00
|
|
|
static int cmos_get_fd_drive_type(FDriveType fd0)
|
2004-05-23 20:26:20 +04:00
|
|
|
{
|
|
|
|
int val;
|
|
|
|
|
|
|
|
switch (fd0) {
|
2011-02-13 00:23:12 +03:00
|
|
|
case FDRIVE_DRV_144:
|
2004-05-23 20:26:20 +04:00
|
|
|
/* 1.44 Mb 3"5 drive */
|
|
|
|
val = 4;
|
|
|
|
break;
|
2011-02-13 00:23:12 +03:00
|
|
|
case FDRIVE_DRV_288:
|
2004-05-23 20:26:20 +04:00
|
|
|
/* 2.88 Mb 3"5 drive */
|
|
|
|
val = 5;
|
|
|
|
break;
|
2011-02-13 00:23:12 +03:00
|
|
|
case FDRIVE_DRV_120:
|
2004-05-23 20:26:20 +04:00
|
|
|
/* 1.2 Mb 5"5 drive */
|
|
|
|
val = 2;
|
|
|
|
break;
|
2011-02-13 00:23:12 +03:00
|
|
|
case FDRIVE_DRV_NONE:
|
2004-05-23 20:26:20 +04:00
|
|
|
default:
|
|
|
|
val = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2012-07-10 13:12:38 +04:00
|
|
|
static void cmos_init_hd(ISADevice *s, int type_ofs, int info_ofs,
|
|
|
|
int16_t cylinders, int8_t heads, int8_t sectors)
|
2004-10-09 20:47:59 +04:00
|
|
|
{
|
|
|
|
rtc_set_memory(s, type_ofs, 47);
|
|
|
|
rtc_set_memory(s, info_ofs, cylinders);
|
|
|
|
rtc_set_memory(s, info_ofs + 1, cylinders >> 8);
|
|
|
|
rtc_set_memory(s, info_ofs + 2, heads);
|
|
|
|
rtc_set_memory(s, info_ofs + 3, 0xff);
|
|
|
|
rtc_set_memory(s, info_ofs + 4, 0xff);
|
|
|
|
rtc_set_memory(s, info_ofs + 5, 0xc0 | ((heads > 8) << 3));
|
|
|
|
rtc_set_memory(s, info_ofs + 6, cylinders);
|
|
|
|
rtc_set_memory(s, info_ofs + 7, cylinders >> 8);
|
|
|
|
rtc_set_memory(s, info_ofs + 8, sectors);
|
|
|
|
}
|
|
|
|
|
2007-10-31 04:54:04 +03:00
|
|
|
/* convert boot_device letter to something recognizable by the bios */
|
|
|
|
static int boot_device2nibble(char boot_device)
|
|
|
|
{
|
|
|
|
switch(boot_device) {
|
|
|
|
case 'a':
|
|
|
|
case 'b':
|
|
|
|
return 0x01; /* floppy boot */
|
|
|
|
case 'c':
|
|
|
|
return 0x02; /* hard drive boot */
|
|
|
|
case 'd':
|
|
|
|
return 0x03; /* CD-ROM boot */
|
|
|
|
case 'n':
|
|
|
|
return 0x04; /* Network boot */
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-12-03 22:04:02 +03:00
|
|
|
static void set_boot_dev(ISADevice *s, const char *boot_device, Error **errp)
|
2008-05-05 00:11:34 +04:00
|
|
|
{
|
|
|
|
#define PC_MAX_BOOT_DEVICES 3
|
|
|
|
int nbds, bds[3] = { 0, };
|
|
|
|
int i;
|
|
|
|
|
|
|
|
nbds = strlen(boot_device);
|
|
|
|
if (nbds > PC_MAX_BOOT_DEVICES) {
|
2014-12-03 22:04:02 +03:00
|
|
|
error_setg(errp, "Too many boot devices for PC");
|
|
|
|
return;
|
2008-05-05 00:11:34 +04:00
|
|
|
}
|
|
|
|
for (i = 0; i < nbds; i++) {
|
|
|
|
bds[i] = boot_device2nibble(boot_device[i]);
|
|
|
|
if (bds[i] == 0) {
|
2014-12-03 22:04:02 +03:00
|
|
|
error_setg(errp, "Invalid boot device for PC: '%c'",
|
|
|
|
boot_device[i]);
|
|
|
|
return;
|
2008-05-05 00:11:34 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
rtc_set_memory(s, 0x3d, (bds[1] << 4) | bds[0]);
|
2010-02-17 20:07:48 +03:00
|
|
|
rtc_set_memory(s, 0x38, (bds[2] << 4) | (fd_bootchk ? 0x0 : 0x1));
|
2008-05-05 00:11:34 +04:00
|
|
|
}
|
|
|
|
|
2014-12-03 22:04:02 +03:00
|
|
|
static void pc_boot_set(void *opaque, const char *boot_device, Error **errp)
|
2010-02-17 20:07:48 +03:00
|
|
|
{
|
2014-12-03 22:04:02 +03:00
|
|
|
set_boot_dev(opaque, boot_device, errp);
|
2010-02-17 20:07:48 +03:00
|
|
|
}
|
|
|
|
|
2015-06-25 16:35:05 +03:00
|
|
|
static void pc_cmos_init_floppy(ISADevice *rtc_state, ISADevice *floppy)
|
|
|
|
{
|
|
|
|
int val, nb, i;
|
|
|
|
FDriveType fd_type[2] = { FDRIVE_DRV_NONE, FDRIVE_DRV_NONE };
|
|
|
|
|
|
|
|
/* floppy type */
|
|
|
|
if (floppy) {
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
|
|
fd_type[i] = isa_fdc_get_drive_type(floppy, i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
val = (cmos_get_fd_drive_type(fd_type[0]) << 4) |
|
|
|
|
cmos_get_fd_drive_type(fd_type[1]);
|
|
|
|
rtc_set_memory(rtc_state, 0x10, val);
|
|
|
|
|
|
|
|
val = rtc_get_memory(rtc_state, REG_EQUIPMENT_BYTE);
|
|
|
|
nb = 0;
|
|
|
|
if (fd_type[0] < FDRIVE_DRV_NONE) {
|
|
|
|
nb++;
|
|
|
|
}
|
|
|
|
if (fd_type[1] < FDRIVE_DRV_NONE) {
|
|
|
|
nb++;
|
|
|
|
}
|
|
|
|
switch (nb) {
|
|
|
|
case 0:
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
val |= 0x01; /* 1 drive, ready for boot */
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
val |= 0x41; /* 2 drives, ready for boot */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
rtc_set_memory(rtc_state, REG_EQUIPMENT_BYTE, val);
|
|
|
|
}
|
|
|
|
|
2010-06-24 21:58:20 +04:00
|
|
|
typedef struct pc_cmos_init_late_arg {
|
|
|
|
ISADevice *rtc_state;
|
2012-07-10 13:12:38 +04:00
|
|
|
BusState *idebus[2];
|
2010-06-24 21:58:20 +04:00
|
|
|
} pc_cmos_init_late_arg;
|
|
|
|
|
hw/i386/pc: reflect any FDC @ ioport 0x3f0 in the CMOS
With the pc-q35-2.4 machine type, if the user creates an ISA FDC manually:
-device isa-fdc,driveA=drive-fdc0-0-0 \
-drive file=...,if=none,id=drive-fdc0-0-0,format=raw
then the board-default FDC will be skipped, and only the explicitly
requested FDC will exist. qtree-wise, this is correct; however such an FDC
is currently not registered in the CMOS, because that code is only reached
for the board-default FDC.
The pc_cmos_init_late() one-shot reset handler -- one-shot because the
CMOS is not reprogrammed during warm reset -- should search for any ISA
FDC devices, created implicitly (by board code) or explicitly, and set the
CMOS accordingly to the ISA FDC(s) with iobase=0x3f0:
- if there is no such FDC, report both drives absent,
- if there is exactly one such FDC, report its drives in the CMOS,
- if there are more than one such FDCs, then pick one (it is not specified
which one), and print a warning about the ambiguity.
Cc: Jan Tomko <jtomko@redhat.com>
Cc: John Snow <jsnow@redhat.com>
Cc: Markus Armbruster <armbru@redhat.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Reported-by: Jan Tomko <jtomko@redhat.com>
Suggested-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: John Snow <jsnow@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2015-06-25 16:35:06 +03:00
|
|
|
typedef struct check_fdc_state {
|
|
|
|
ISADevice *floppy;
|
|
|
|
bool multiple;
|
|
|
|
} CheckFdcState;
|
|
|
|
|
|
|
|
static int check_fdc(Object *obj, void *opaque)
|
|
|
|
{
|
|
|
|
CheckFdcState *state = opaque;
|
|
|
|
Object *fdc;
|
|
|
|
uint32_t iobase;
|
|
|
|
Error *local_err = NULL;
|
|
|
|
|
|
|
|
fdc = object_dynamic_cast(obj, TYPE_ISA_FDC);
|
|
|
|
if (!fdc) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
iobase = object_property_get_int(obj, "iobase", &local_err);
|
|
|
|
if (local_err || iobase != 0x3f0) {
|
|
|
|
error_free(local_err);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (state->floppy) {
|
|
|
|
state->multiple = true;
|
|
|
|
} else {
|
|
|
|
state->floppy = ISA_DEVICE(obj);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char * const fdc_container_path[] = {
|
|
|
|
"/unattached", "/peripheral", "/peripheral-anon"
|
|
|
|
};
|
|
|
|
|
2015-12-30 23:11:51 +03:00
|
|
|
/*
|
|
|
|
* Locate the FDC at IO address 0x3f0, in order to configure the CMOS registers
|
|
|
|
* and ACPI objects.
|
|
|
|
*/
|
|
|
|
ISADevice *pc_find_fdc0(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
Object *container;
|
|
|
|
CheckFdcState state = { 0 };
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(fdc_container_path); i++) {
|
|
|
|
container = container_get(qdev_get_machine(), fdc_container_path[i]);
|
|
|
|
object_child_foreach(container, check_fdc, &state);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (state.multiple) {
|
|
|
|
error_report("warning: multiple floppy disk controllers with "
|
2015-12-18 18:35:24 +03:00
|
|
|
"iobase=0x3f0 have been found");
|
|
|
|
error_printf("the one being picked for CMOS setup might not reflect "
|
2015-12-30 23:11:51 +03:00
|
|
|
"your intent");
|
|
|
|
}
|
|
|
|
|
|
|
|
return state.floppy;
|
|
|
|
}
|
|
|
|
|
2010-06-24 21:58:20 +04:00
|
|
|
static void pc_cmos_init_late(void *opaque)
|
|
|
|
{
|
|
|
|
pc_cmos_init_late_arg *arg = opaque;
|
|
|
|
ISADevice *s = arg->rtc_state;
|
2012-07-10 13:12:38 +04:00
|
|
|
int16_t cylinders;
|
|
|
|
int8_t heads, sectors;
|
2010-06-24 21:58:20 +04:00
|
|
|
int val;
|
2012-07-10 13:12:53 +04:00
|
|
|
int i, trans;
|
2010-06-24 21:58:20 +04:00
|
|
|
|
2012-07-10 13:12:38 +04:00
|
|
|
val = 0;
|
|
|
|
if (ide_get_geometry(arg->idebus[0], 0,
|
|
|
|
&cylinders, &heads, §ors) >= 0) {
|
|
|
|
cmos_init_hd(s, 0x19, 0x1b, cylinders, heads, sectors);
|
|
|
|
val |= 0xf0;
|
|
|
|
}
|
|
|
|
if (ide_get_geometry(arg->idebus[0], 1,
|
|
|
|
&cylinders, &heads, §ors) >= 0) {
|
|
|
|
cmos_init_hd(s, 0x1a, 0x24, cylinders, heads, sectors);
|
|
|
|
val |= 0x0f;
|
|
|
|
}
|
|
|
|
rtc_set_memory(s, 0x12, val);
|
2010-06-24 21:58:20 +04:00
|
|
|
|
|
|
|
val = 0;
|
|
|
|
for (i = 0; i < 4; i++) {
|
2012-07-10 13:12:38 +04:00
|
|
|
/* NOTE: ide_get_geometry() returns the physical
|
|
|
|
geometry. It is always such that: 1 <= sects <= 63, 1
|
|
|
|
<= heads <= 16, 1 <= cylinders <= 16383. The BIOS
|
|
|
|
geometry can be different if a translation is done. */
|
|
|
|
if (ide_get_geometry(arg->idebus[i / 2], i % 2,
|
|
|
|
&cylinders, &heads, §ors) >= 0) {
|
2012-07-10 13:12:53 +04:00
|
|
|
trans = ide_get_bios_chs_trans(arg->idebus[i / 2], i % 2) - 1;
|
|
|
|
assert((trans & ~3) == 0);
|
|
|
|
val |= trans << (i * 2);
|
2010-06-24 21:58:20 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
rtc_set_memory(s, 0x39, val);
|
|
|
|
|
2015-12-30 23:11:51 +03:00
|
|
|
pc_cmos_init_floppy(s, pc_find_fdc0());
|
hw/i386/pc: reflect any FDC @ ioport 0x3f0 in the CMOS
With the pc-q35-2.4 machine type, if the user creates an ISA FDC manually:
-device isa-fdc,driveA=drive-fdc0-0-0 \
-drive file=...,if=none,id=drive-fdc0-0-0,format=raw
then the board-default FDC will be skipped, and only the explicitly
requested FDC will exist. qtree-wise, this is correct; however such an FDC
is currently not registered in the CMOS, because that code is only reached
for the board-default FDC.
The pc_cmos_init_late() one-shot reset handler -- one-shot because the
CMOS is not reprogrammed during warm reset -- should search for any ISA
FDC devices, created implicitly (by board code) or explicitly, and set the
CMOS accordingly to the ISA FDC(s) with iobase=0x3f0:
- if there is no such FDC, report both drives absent,
- if there is exactly one such FDC, report its drives in the CMOS,
- if there are more than one such FDCs, then pick one (it is not specified
which one), and print a warning about the ambiguity.
Cc: Jan Tomko <jtomko@redhat.com>
Cc: John Snow <jsnow@redhat.com>
Cc: Markus Armbruster <armbru@redhat.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Reported-by: Jan Tomko <jtomko@redhat.com>
Suggested-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: John Snow <jsnow@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2015-06-25 16:35:06 +03:00
|
|
|
|
2010-06-24 21:58:20 +04:00
|
|
|
qemu_unregister_reset(pc_cmos_init_late, opaque);
|
|
|
|
}
|
|
|
|
|
2015-08-07 22:55:49 +03:00
|
|
|
void pc_cmos_init(PCMachineState *pcms,
|
2015-06-25 16:35:07 +03:00
|
|
|
BusState *idebus0, BusState *idebus1,
|
2011-02-05 19:32:23 +03:00
|
|
|
ISADevice *s)
|
2004-03-14 15:20:30 +03:00
|
|
|
{
|
2015-06-25 16:35:05 +03:00
|
|
|
int val;
|
2010-06-24 21:58:20 +04:00
|
|
|
static pc_cmos_init_late_arg arg;
|
2004-03-31 22:58:38 +04:00
|
|
|
|
|
|
|
/* various important CMOS locations needed by PC/Bochs bios */
|
2004-03-14 15:20:30 +03:00
|
|
|
|
|
|
|
/* memory size */
|
2012-08-15 15:12:20 +04:00
|
|
|
/* base memory (first MiB) */
|
2015-08-07 22:55:54 +03:00
|
|
|
val = MIN(pcms->below_4g_mem_size / 1024, 640);
|
2004-04-08 00:51:30 +04:00
|
|
|
rtc_set_memory(s, 0x15, val);
|
|
|
|
rtc_set_memory(s, 0x16, val >> 8);
|
2012-08-15 15:12:20 +04:00
|
|
|
/* extended memory (next 64MiB) */
|
2015-08-07 22:55:54 +03:00
|
|
|
if (pcms->below_4g_mem_size > 1024 * 1024) {
|
|
|
|
val = (pcms->below_4g_mem_size - 1024 * 1024) / 1024;
|
2012-08-15 15:12:20 +04:00
|
|
|
} else {
|
|
|
|
val = 0;
|
|
|
|
}
|
2004-03-14 15:20:30 +03:00
|
|
|
if (val > 65535)
|
|
|
|
val = 65535;
|
2004-03-31 22:58:38 +04:00
|
|
|
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);
|
2012-08-15 15:12:20 +04:00
|
|
|
/* memory between 16MiB and 4GiB */
|
2015-08-07 22:55:54 +03:00
|
|
|
if (pcms->below_4g_mem_size > 16 * 1024 * 1024) {
|
|
|
|
val = (pcms->below_4g_mem_size - 16 * 1024 * 1024) / 65536;
|
2012-08-15 15:12:20 +04:00
|
|
|
} else {
|
2004-06-26 19:53:17 +04:00
|
|
|
val = 0;
|
2012-08-15 15:12:20 +04:00
|
|
|
}
|
2004-03-14 15:20:30 +03:00
|
|
|
if (val > 65535)
|
|
|
|
val = 65535;
|
2004-03-31 22:58:38 +04:00
|
|
|
rtc_set_memory(s, 0x34, val);
|
|
|
|
rtc_set_memory(s, 0x35, val >> 8);
|
2012-08-15 15:12:20 +04:00
|
|
|
/* memory above 4GiB */
|
2015-08-07 22:55:54 +03:00
|
|
|
val = pcms->above_4g_mem_size / 65536;
|
2012-08-15 15:12:20 +04:00
|
|
|
rtc_set_memory(s, 0x5b, val);
|
|
|
|
rtc_set_memory(s, 0x5c, val >> 8);
|
|
|
|
rtc_set_memory(s, 0x5d, val >> 16);
|
2007-09-17 12:09:54 +04:00
|
|
|
|
2008-03-29 01:28:08 +03:00
|
|
|
/* set the number of CPU */
|
|
|
|
rtc_set_memory(s, 0x5f, smp_cpus - 1);
|
2014-10-22 07:24:29 +04:00
|
|
|
|
2015-08-07 22:55:49 +03:00
|
|
|
object_property_add_link(OBJECT(pcms), "rtc_state",
|
2014-10-22 07:24:29 +04:00
|
|
|
TYPE_ISA_DEVICE,
|
2015-08-07 22:55:45 +03:00
|
|
|
(Object **)&pcms->rtc,
|
2014-10-22 07:24:29 +04:00
|
|
|
object_property_allow_set_link,
|
|
|
|
OBJ_PROP_LINK_UNREF_ON_RELEASE, &error_abort);
|
2015-08-07 22:55:49 +03:00
|
|
|
object_property_set_link(OBJECT(pcms), OBJECT(s),
|
2014-10-22 07:24:29 +04:00
|
|
|
"rtc_state", &error_abort);
|
2008-03-29 01:28:08 +03:00
|
|
|
|
2015-09-11 16:04:45 +03:00
|
|
|
set_boot_dev(s, MACHINE(pcms)->boot_order, &error_fatal);
|
2004-03-14 15:20:30 +03:00
|
|
|
|
2004-03-31 22:58:38 +04:00
|
|
|
val = 0;
|
|
|
|
val |= 0x02; /* FPU is there */
|
|
|
|
val |= 0x04; /* PS/2 mouse installed */
|
|
|
|
rtc_set_memory(s, REG_EQUIPMENT_BYTE, val);
|
|
|
|
|
hw/i386/pc: reflect any FDC @ ioport 0x3f0 in the CMOS
With the pc-q35-2.4 machine type, if the user creates an ISA FDC manually:
-device isa-fdc,driveA=drive-fdc0-0-0 \
-drive file=...,if=none,id=drive-fdc0-0-0,format=raw
then the board-default FDC will be skipped, and only the explicitly
requested FDC will exist. qtree-wise, this is correct; however such an FDC
is currently not registered in the CMOS, because that code is only reached
for the board-default FDC.
The pc_cmos_init_late() one-shot reset handler -- one-shot because the
CMOS is not reprogrammed during warm reset -- should search for any ISA
FDC devices, created implicitly (by board code) or explicitly, and set the
CMOS accordingly to the ISA FDC(s) with iobase=0x3f0:
- if there is no such FDC, report both drives absent,
- if there is exactly one such FDC, report its drives in the CMOS,
- if there are more than one such FDCs, then pick one (it is not specified
which one), and print a warning about the ambiguity.
Cc: Jan Tomko <jtomko@redhat.com>
Cc: John Snow <jsnow@redhat.com>
Cc: Markus Armbruster <armbru@redhat.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Reported-by: Jan Tomko <jtomko@redhat.com>
Suggested-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: John Snow <jsnow@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2015-06-25 16:35:06 +03:00
|
|
|
/* hard drives and FDC */
|
2010-06-24 21:58:20 +04:00
|
|
|
arg.rtc_state = s;
|
2012-07-10 13:12:38 +04:00
|
|
|
arg.idebus[0] = idebus0;
|
|
|
|
arg.idebus[1] = idebus1;
|
2010-06-24 21:58:20 +04:00
|
|
|
qemu_register_reset(pc_cmos_init_late, &arg);
|
2004-03-14 15:20:30 +03:00
|
|
|
}
|
|
|
|
|
2013-04-28 00:18:46 +04:00
|
|
|
#define TYPE_PORT92 "port92"
|
|
|
|
#define PORT92(obj) OBJECT_CHECK(Port92State, (obj), TYPE_PORT92)
|
|
|
|
|
2011-01-06 21:24:35 +03:00
|
|
|
/* port 92 stuff: could be split off */
|
|
|
|
typedef struct Port92State {
|
2013-04-28 00:18:46 +04:00
|
|
|
ISADevice parent_obj;
|
|
|
|
|
2011-08-16 19:32:44 +04:00
|
|
|
MemoryRegion io;
|
2011-01-06 21:24:35 +03:00
|
|
|
uint8_t outport;
|
|
|
|
qemu_irq *a20_out;
|
|
|
|
} Port92State;
|
|
|
|
|
2012-10-08 15:24:52 +04:00
|
|
|
static void port92_write(void *opaque, hwaddr addr, uint64_t val,
|
|
|
|
unsigned size)
|
2011-01-06 21:24:35 +03:00
|
|
|
{
|
|
|
|
Port92State *s = opaque;
|
2013-03-05 18:04:36 +04:00
|
|
|
int oldval = s->outport;
|
2011-01-06 21:24:35 +03:00
|
|
|
|
Fix debug print warning
Steps:
1.enable qemu debug print, using simply scprit as below:
grep "//#define DEBUG" * -rl | xargs sed -i "s/\/\/#define DEBUG/#define DEBUG/g"
2. make -j
3. get some warning:
hw/i2c/pm_smbus.c: In function 'smb_ioport_writeb':
hw/i2c/pm_smbus.c:142: warning: format '%04x' expects type 'unsigned int', but argument 2 has type 'hwaddr'
hw/i2c/pm_smbus.c:142: warning: format '%02x' expects type 'unsigned int', but argument 3 has type 'uint64_t'
hw/i2c/pm_smbus.c: In function 'smb_ioport_readb':
hw/i2c/pm_smbus.c:209: warning: format '%04x' expects type 'unsigned int', but argument 2 has type 'hwaddr'
hw/intc/i8259.c: In function 'pic_ioport_read':
hw/intc/i8259.c:373: warning: format '%02x' expects type 'unsigned int', but argument 2 has type 'hwaddr'
hw/input/pckbd.c: In function 'kbd_write_command':
hw/input/pckbd.c:232: warning: format '%02x' expects type 'unsigned int', but argument 2 has type 'uint64_t'
hw/input/pckbd.c: In function 'kbd_write_data':
hw/input/pckbd.c:333: warning: format '%02x' expects type 'unsigned int', but argument 2 has type 'uint64_t'
hw/isa/apm.c: In function 'apm_ioport_writeb':
hw/isa/apm.c:44: warning: format '%x' expects type 'unsigned int', but argument 2 has type 'hwaddr'
hw/isa/apm.c:44: warning: format '%02x' expects type 'unsigned int', but argument 3 has type 'uint64_t'
hw/isa/apm.c: In function 'apm_ioport_readb':
hw/isa/apm.c:67: warning: format '%x' expects type 'unsigned int', but argument 2 has type 'hwaddr'
hw/timer/mc146818rtc.c: In function 'cmos_ioport_write':
hw/timer/mc146818rtc.c:394: warning: format '%02x' expects type 'unsigned int', but argument 3 has type 'uint64_t'
hw/i386/pc.c: In function 'port92_write':
hw/i386/pc.c:479: warning: format '%02x' expects type 'unsigned int', but argument 2 has type 'uint64_t'
Fix them.
Cc: qemu-trivial@nongnu.org
Signed-off-by: Gonglei <arei.gonglei@huawei.com>
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
2014-08-25 06:01:27 +04:00
|
|
|
DPRINTF("port92: write 0x%02" PRIx64 "\n", val);
|
2011-01-06 21:24:35 +03:00
|
|
|
s->outport = val;
|
|
|
|
qemu_set_irq(*s->a20_out, (val >> 1) & 1);
|
2013-03-05 18:04:36 +04:00
|
|
|
if ((val & 1) && !(oldval & 1)) {
|
2011-01-06 21:24:35 +03:00
|
|
|
qemu_system_reset_request();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-08 15:24:52 +04:00
|
|
|
static uint64_t port92_read(void *opaque, hwaddr addr,
|
|
|
|
unsigned size)
|
2011-01-06 21:24:35 +03:00
|
|
|
{
|
|
|
|
Port92State *s = opaque;
|
|
|
|
uint32_t ret;
|
|
|
|
|
|
|
|
ret = s->outport;
|
|
|
|
DPRINTF("port92: read 0x%02x\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void port92_init(ISADevice *dev, qemu_irq *a20_out)
|
|
|
|
{
|
2013-04-28 00:18:46 +04:00
|
|
|
Port92State *s = PORT92(dev);
|
2011-01-06 21:24:35 +03:00
|
|
|
|
|
|
|
s->a20_out = a20_out;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const VMStateDescription vmstate_port92_isa = {
|
|
|
|
.name = "port92",
|
|
|
|
.version_id = 1,
|
|
|
|
.minimum_version_id = 1,
|
2014-04-16 17:32:32 +04:00
|
|
|
.fields = (VMStateField[]) {
|
2011-01-06 21:24:35 +03:00
|
|
|
VMSTATE_UINT8(outport, Port92State),
|
|
|
|
VMSTATE_END_OF_LIST()
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
static void port92_reset(DeviceState *d)
|
|
|
|
{
|
2013-04-28 00:18:46 +04:00
|
|
|
Port92State *s = PORT92(d);
|
2011-01-06 21:24:35 +03:00
|
|
|
|
|
|
|
s->outport &= ~1;
|
|
|
|
}
|
|
|
|
|
2011-08-16 19:32:44 +04:00
|
|
|
static const MemoryRegionOps port92_ops = {
|
2012-10-08 15:24:52 +04:00
|
|
|
.read = port92_read,
|
|
|
|
.write = port92_write,
|
|
|
|
.impl = {
|
|
|
|
.min_access_size = 1,
|
|
|
|
.max_access_size = 1,
|
|
|
|
},
|
|
|
|
.endianness = DEVICE_LITTLE_ENDIAN,
|
2011-08-16 19:32:44 +04:00
|
|
|
};
|
|
|
|
|
2012-11-25 05:37:14 +04:00
|
|
|
static void port92_initfn(Object *obj)
|
2011-01-06 21:24:35 +03:00
|
|
|
{
|
2012-11-25 05:37:14 +04:00
|
|
|
Port92State *s = PORT92(obj);
|
2011-01-06 21:24:35 +03:00
|
|
|
|
2013-06-07 05:25:08 +04:00
|
|
|
memory_region_init_io(&s->io, OBJECT(s), &port92_ops, s, "port92", 1);
|
2011-08-16 19:32:44 +04:00
|
|
|
|
2011-01-06 21:24:35 +03:00
|
|
|
s->outport = 0;
|
2012-11-25 05:37:14 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void port92_realizefn(DeviceState *dev, Error **errp)
|
|
|
|
{
|
|
|
|
ISADevice *isadev = ISA_DEVICE(dev);
|
|
|
|
Port92State *s = PORT92(dev);
|
|
|
|
|
|
|
|
isa_register_ioport(isadev, &s->io, 0x92);
|
2011-01-06 21:24:35 +03:00
|
|
|
}
|
|
|
|
|
2011-12-04 21:52:49 +04:00
|
|
|
static void port92_class_initfn(ObjectClass *klass, void *data)
|
|
|
|
{
|
2011-12-08 07:34:16 +04:00
|
|
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
2012-11-25 05:37:14 +04:00
|
|
|
|
|
|
|
dc->realize = port92_realizefn;
|
2011-12-08 07:34:16 +04:00
|
|
|
dc->reset = port92_reset;
|
|
|
|
dc->vmsd = &vmstate_port92_isa;
|
2013-11-28 20:27:02 +04:00
|
|
|
/*
|
|
|
|
* Reason: unlike ordinary ISA devices, this one needs additional
|
|
|
|
* wiring: its A20 output line needs to be wired up by
|
|
|
|
* port92_init().
|
|
|
|
*/
|
|
|
|
dc->cannot_instantiate_with_device_add_yet = true;
|
2011-12-04 21:52:49 +04:00
|
|
|
}
|
|
|
|
|
2013-01-10 19:19:07 +04:00
|
|
|
static const TypeInfo port92_info = {
|
2013-04-28 00:18:46 +04:00
|
|
|
.name = TYPE_PORT92,
|
2011-12-08 07:34:16 +04:00
|
|
|
.parent = TYPE_ISA_DEVICE,
|
|
|
|
.instance_size = sizeof(Port92State),
|
2012-11-25 05:37:14 +04:00
|
|
|
.instance_init = port92_initfn,
|
2011-12-08 07:34:16 +04:00
|
|
|
.class_init = port92_class_initfn,
|
2011-01-06 21:24:35 +03:00
|
|
|
};
|
|
|
|
|
2012-02-09 18:20:55 +04:00
|
|
|
static void port92_register_types(void)
|
2011-01-06 21:24:35 +03:00
|
|
|
{
|
2011-12-08 07:34:16 +04:00
|
|
|
type_register_static(&port92_info);
|
2011-01-06 21:24:35 +03:00
|
|
|
}
|
2012-02-09 18:20:55 +04:00
|
|
|
|
|
|
|
type_init(port92_register_types)
|
2011-01-06 21:24:35 +03:00
|
|
|
|
2010-05-22 11:59:01 +04:00
|
|
|
static void handle_a20_line_change(void *opaque, int irq, int level)
|
2005-11-22 02:34:32 +03:00
|
|
|
{
|
2013-01-18 18:19:06 +04:00
|
|
|
X86CPU *cpu = opaque;
|
2004-04-06 00:26:03 +04:00
|
|
|
|
2010-05-22 11:59:01 +04:00
|
|
|
/* XXX: send to all CPUs ? */
|
2011-01-06 21:24:35 +03:00
|
|
|
/* XXX: add logic to handle multiple A20 line sources */
|
2013-01-18 18:19:06 +04:00
|
|
|
x86_cpu_set_a20(cpu, level);
|
2004-04-06 00:26:03 +04:00
|
|
|
}
|
|
|
|
|
2010-02-15 20:33:46 +03:00
|
|
|
int e820_add_entry(uint64_t address, uint64_t length, uint32_t type)
|
|
|
|
{
|
2013-10-18 13:31:54 +04:00
|
|
|
int index = le32_to_cpu(e820_reserve.count);
|
2010-02-15 20:33:46 +03:00
|
|
|
struct e820_entry *entry;
|
|
|
|
|
2013-10-18 13:31:54 +04:00
|
|
|
if (type != E820_RAM) {
|
|
|
|
/* old FW_CFG_E820_TABLE entry -- reservations only */
|
|
|
|
if (index >= E820_NR_ENTRIES) {
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
entry = &e820_reserve.entry[index++];
|
|
|
|
|
|
|
|
entry->address = cpu_to_le64(address);
|
|
|
|
entry->length = cpu_to_le64(length);
|
|
|
|
entry->type = cpu_to_le32(type);
|
|
|
|
|
|
|
|
e820_reserve.count = cpu_to_le32(index);
|
|
|
|
}
|
2010-02-15 20:33:46 +03:00
|
|
|
|
2013-10-18 13:31:54 +04:00
|
|
|
/* new "etc/e820" file -- include ram too */
|
2014-12-04 16:46:45 +03:00
|
|
|
e820_table = g_renew(struct e820_entry, e820_table, e820_entries + 1);
|
2013-10-18 13:31:54 +04:00
|
|
|
e820_table[e820_entries].address = cpu_to_le64(address);
|
|
|
|
e820_table[e820_entries].length = cpu_to_le64(length);
|
|
|
|
e820_table[e820_entries].type = cpu_to_le32(type);
|
|
|
|
e820_entries++;
|
2010-02-15 20:33:46 +03:00
|
|
|
|
2013-10-18 13:31:54 +04:00
|
|
|
return e820_entries;
|
2010-02-15 20:33:46 +03:00
|
|
|
}
|
|
|
|
|
2014-04-23 17:42:36 +04:00
|
|
|
int e820_get_num_entries(void)
|
|
|
|
{
|
|
|
|
return e820_entries;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool e820_get_entry(int idx, uint32_t type, uint64_t *address, uint64_t *length)
|
|
|
|
{
|
|
|
|
if (idx < e820_entries && e820_table[idx].type == cpu_to_le32(type)) {
|
|
|
|
*address = le64_to_cpu(e820_table[idx].address);
|
|
|
|
*length = le64_to_cpu(e820_table[idx].length);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-12-19 04:43:35 +03:00
|
|
|
/* Enables contiguous-apic-ID mode, for compatibility */
|
|
|
|
static bool compat_apic_id_mode;
|
|
|
|
|
|
|
|
void enable_compat_apic_id_mode(void)
|
|
|
|
{
|
|
|
|
compat_apic_id_mode = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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(unsigned int cpu_index)
|
|
|
|
{
|
|
|
|
uint32_t correct_id;
|
|
|
|
static bool warned;
|
|
|
|
|
|
|
|
correct_id = x86_apicid_from_cpu_idx(smp_cores, smp_threads, cpu_index);
|
|
|
|
if (compat_apic_id_mode) {
|
2015-03-14 16:24:37 +03:00
|
|
|
if (cpu_index != correct_id && !warned && !qtest_enabled()) {
|
2014-12-19 04:43:35 +03:00
|
|
|
error_report("APIC IDs set in compatibility mode, "
|
|
|
|
"CPU topology won't match the configuration");
|
|
|
|
warned = true;
|
|
|
|
}
|
|
|
|
return cpu_index;
|
|
|
|
} else {
|
|
|
|
return correct_id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-01-23 21:51:18 +04:00
|
|
|
/* Calculates the limit to CPU APIC ID values
|
|
|
|
*
|
|
|
|
* This function returns the limit for the APIC ID value, so that all
|
|
|
|
* CPU APIC IDs are < pc_apic_id_limit().
|
|
|
|
*
|
|
|
|
* This is used for FW_CFG_MAX_CPUS. See comments on bochs_bios_init().
|
|
|
|
*/
|
|
|
|
static unsigned int pc_apic_id_limit(unsigned int max_cpus)
|
|
|
|
{
|
|
|
|
return x86_cpu_apic_id_from_index(max_cpus - 1) + 1;
|
|
|
|
}
|
|
|
|
|
2015-08-12 05:08:18 +03:00
|
|
|
static void pc_build_smbios(FWCfgState *fw_cfg)
|
2004-03-14 15:20:30 +03:00
|
|
|
{
|
2014-04-23 17:42:42 +04:00
|
|
|
uint8_t *smbios_tables, *smbios_anchor;
|
|
|
|
size_t smbios_tables_len, smbios_anchor_len;
|
2015-08-12 05:08:19 +03:00
|
|
|
struct smbios_phys_mem_area *mem_array;
|
|
|
|
unsigned i, array_count;
|
2015-08-12 05:08:18 +03:00
|
|
|
|
|
|
|
smbios_tables = smbios_get_table_legacy(&smbios_tables_len);
|
|
|
|
if (smbios_tables) {
|
|
|
|
fw_cfg_add_bytes(fw_cfg, FW_CFG_SMBIOS_ENTRIES,
|
|
|
|
smbios_tables, smbios_tables_len);
|
|
|
|
}
|
|
|
|
|
2015-08-12 05:08:19 +03:00
|
|
|
/* build the array of physical mem area from e820 table */
|
|
|
|
mem_array = g_malloc0(sizeof(*mem_array) * e820_get_num_entries());
|
|
|
|
for (i = 0, array_count = 0; i < e820_get_num_entries(); i++) {
|
|
|
|
uint64_t addr, len;
|
|
|
|
|
|
|
|
if (e820_get_entry(i, E820_RAM, &addr, &len)) {
|
|
|
|
mem_array[array_count].address = addr;
|
|
|
|
mem_array[array_count].length = len;
|
|
|
|
array_count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
smbios_get_tables(mem_array, array_count,
|
|
|
|
&smbios_tables, &smbios_tables_len,
|
2015-08-12 05:08:18 +03:00
|
|
|
&smbios_anchor, &smbios_anchor_len);
|
2015-08-12 05:08:19 +03:00
|
|
|
g_free(mem_array);
|
|
|
|
|
2015-08-12 05:08:18 +03:00
|
|
|
if (smbios_anchor) {
|
|
|
|
fw_cfg_add_file(fw_cfg, "etc/smbios/smbios-tables",
|
|
|
|
smbios_tables, smbios_tables_len);
|
|
|
|
fw_cfg_add_file(fw_cfg, "etc/smbios/smbios-anchor",
|
|
|
|
smbios_anchor, smbios_anchor_len);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-08 18:02:57 +03:00
|
|
|
static FWCfgState *bochs_bios_init(AddressSpace *as)
|
2015-08-12 05:08:18 +03:00
|
|
|
{
|
|
|
|
FWCfgState *fw_cfg;
|
2009-04-22 02:31:41 +04:00
|
|
|
uint64_t *numa_fw_cfg;
|
|
|
|
int i, j;
|
2013-01-23 21:51:18 +04:00
|
|
|
unsigned int apic_id_limit = pc_apic_id_limit(max_cpus);
|
2008-09-18 22:27:29 +04:00
|
|
|
|
2015-10-08 18:02:57 +03:00
|
|
|
fw_cfg = fw_cfg_init_io_dma(BIOS_CFG_IOPORT, BIOS_CFG_IOPORT + 4, as);
|
|
|
|
|
2013-01-23 21:51:18 +04:00
|
|
|
/* FW_CFG_MAX_CPUS is a bit confusing/problematic on x86:
|
|
|
|
*
|
|
|
|
* SeaBIOS needs FW_CFG_MAX_CPUS for CPU hotplug, but the CPU hotplug
|
|
|
|
* QEMU<->SeaBIOS interface is not based on the "CPU index", but on the APIC
|
|
|
|
* ID of hotplugged CPUs[1]. This means that FW_CFG_MAX_CPUS is not the
|
|
|
|
* "maximum number of CPUs", but the "limit to the APIC ID values SeaBIOS
|
|
|
|
* may see".
|
|
|
|
*
|
|
|
|
* So, this means we must not use max_cpus, here, but the maximum possible
|
|
|
|
* APIC ID value, plus one.
|
|
|
|
*
|
|
|
|
* [1] The only kind of "CPU identifier" used between SeaBIOS and QEMU is
|
|
|
|
* the APIC ID, not the "CPU index"
|
|
|
|
*/
|
|
|
|
fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)apic_id_limit);
|
2008-09-18 22:33:18 +04:00
|
|
|
fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size);
|
2013-01-16 17:50:28 +04:00
|
|
|
fw_cfg_add_bytes(fw_cfg, FW_CFG_ACPI_TABLES,
|
|
|
|
acpi_tables, acpi_tables_len);
|
2011-10-15 16:08:26 +04:00
|
|
|
fw_cfg_add_i32(fw_cfg, FW_CFG_IRQ0_OVERRIDE, kvm_allows_irq0_override());
|
qemu: Add support for SMBIOS command line otions (Alex Williamson)
Create a new -smbios option (x86-only) to allow binary SMBIOS entries
to be passed through to the BIOS or modify the default values of
individual fields of type 0 and 1 entries on the command line.
Binary SMBIOS entries can be generated as follows:
dmidecode -t 1 -u | grep $'^\t\t[^"]' | xargs -n1 | \
perl -lne 'printf "%c", hex($_)' > smbios_type_1.bin
These can then be passed to the BIOS using this switch:
-smbios file=smbios_type_1.bin
Command line generation supports the following syntax:
-smbios type=0[,vendor=str][,version=str][,date=str][,release=%d.%d]
-smbios type=1[,manufacturer=str][,product=str][,version=str][,serial=str]
[,uuid=$(uuidgen)][,sku=str][,family=str]
For instance, to add a serial number to the type 1 table:
-smbios type=1,serial=0123456789
Interface is extensible to support more fields/tables as needed.
aliguori: remove texi formatting from help output
Signed-off-by: Alex Williamson <alex.williamson@hp.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@7163 c046a42c-6fe2-441c-8c8c-71466251a162
2009-04-17 22:59:56 +04:00
|
|
|
|
2015-08-12 05:08:18 +03:00
|
|
|
pc_build_smbios(fw_cfg);
|
2014-04-23 17:42:42 +04:00
|
|
|
|
2013-01-16 17:50:28 +04:00
|
|
|
fw_cfg_add_bytes(fw_cfg, FW_CFG_E820_TABLE,
|
2013-10-18 13:31:54 +04:00
|
|
|
&e820_reserve, sizeof(e820_reserve));
|
|
|
|
fw_cfg_add_file(fw_cfg, "etc/e820", e820_table,
|
|
|
|
sizeof(struct e820_entry) * e820_entries);
|
2009-04-22 02:31:41 +04:00
|
|
|
|
2013-01-16 17:50:28 +04:00
|
|
|
fw_cfg_add_bytes(fw_cfg, FW_CFG_HPET, &hpet_cfg, sizeof(hpet_cfg));
|
2009-04-22 02:31:41 +04:00
|
|
|
/* allocate memory for the NUMA channel: one (64bit) word for the number
|
|
|
|
* of nodes, one word for each VCPU->node and one word for each node to
|
|
|
|
* hold the amount of memory.
|
|
|
|
*/
|
2013-01-23 21:51:18 +04:00
|
|
|
numa_fw_cfg = g_new0(uint64_t, 1 + apic_id_limit + nb_numa_nodes);
|
2009-04-22 02:31:41 +04:00
|
|
|
numa_fw_cfg[0] = cpu_to_le64(nb_numa_nodes);
|
2011-10-26 16:19:00 +04:00
|
|
|
for (i = 0; i < max_cpus; i++) {
|
2013-01-23 21:51:18 +04:00
|
|
|
unsigned int apic_id = x86_cpu_apic_id_from_index(i);
|
|
|
|
assert(apic_id < apic_id_limit);
|
2009-04-22 02:31:41 +04:00
|
|
|
for (j = 0; j < nb_numa_nodes; j++) {
|
2014-05-14 13:43:07 +04:00
|
|
|
if (test_bit(i, numa_info[j].node_cpu)) {
|
2013-01-23 21:51:18 +04:00
|
|
|
numa_fw_cfg[apic_id + 1] = cpu_to_le64(j);
|
2009-04-22 02:31:41 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (i = 0; i < nb_numa_nodes; i++) {
|
2014-05-14 13:43:07 +04:00
|
|
|
numa_fw_cfg[apic_id_limit + 1 + i] = cpu_to_le64(numa_info[i].node_mem);
|
2009-04-22 02:31:41 +04:00
|
|
|
}
|
2013-01-16 17:50:28 +04:00
|
|
|
fw_cfg_add_bytes(fw_cfg, FW_CFG_NUMA, numa_fw_cfg,
|
2013-01-23 21:51:18 +04:00
|
|
|
(1 + apic_id_limit + nb_numa_nodes) *
|
|
|
|
sizeof(*numa_fw_cfg));
|
2009-06-29 17:37:38 +04:00
|
|
|
|
|
|
|
return fw_cfg;
|
2004-03-14 15:20:30 +03:00
|
|
|
}
|
|
|
|
|
2007-05-20 01:04:38 +04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-08-07 22:55:53 +03:00
|
|
|
static void load_linux(PCMachineState *pcms,
|
|
|
|
FWCfgState *fw_cfg)
|
2007-05-20 01:04:38 +04:00
|
|
|
{
|
|
|
|
uint16_t protocol;
|
2009-05-30 03:52:44 +04:00
|
|
|
int setup_size, kernel_size, initrd_size = 0, cmdline_size;
|
2007-05-20 01:04:38 +04:00
|
|
|
uint32_t initrd_max;
|
2009-11-12 23:53:14 +03:00
|
|
|
uint8_t header[8192], *setup, *kernel, *initrd_data;
|
2012-10-23 14:30:10 +04:00
|
|
|
hwaddr real_addr, prot_addr, cmdline_addr, initrd_addr = 0;
|
2009-10-01 18:42:33 +04:00
|
|
|
FILE *f;
|
2009-07-13 19:46:42 +04:00
|
|
|
char *vmode;
|
2015-08-07 22:55:53 +03:00
|
|
|
MachineState *machine = MACHINE(pcms);
|
2015-12-02 01:58:05 +03:00
|
|
|
PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
|
2015-08-07 22:55:53 +03:00
|
|
|
const char *kernel_filename = machine->kernel_filename;
|
|
|
|
const char *initrd_filename = machine->initrd_filename;
|
|
|
|
const char *kernel_cmdline = machine->kernel_cmdline;
|
2007-05-20 01:04:38 +04:00
|
|
|
|
|
|
|
/* 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 || !(kernel_size = get_file_size(f)) ||
|
2013-03-26 12:43:19 +04:00
|
|
|
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);
|
2007-05-20 01:04:38 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* kernel protocol version */
|
2007-11-07 19:54:42 +03:00
|
|
|
#if 0
|
2007-05-20 01:04:38 +04:00
|
|
|
fprintf(stderr, "header magic: %#x\n", ldl_p(header+0x202));
|
2007-11-07 19:54:42 +03:00
|
|
|
#endif
|
2013-03-26 12:43:19 +04:00
|
|
|
if (ldl_p(header+0x202) == 0x53726448) {
|
|
|
|
protocol = lduw_p(header+0x206);
|
|
|
|
} else {
|
|
|
|
/* This looks like a multiboot kernel. If it is, let's stop
|
|
|
|
treating it like a Linux kernel. */
|
2009-12-26 16:13:46 +03:00
|
|
|
if (load_multiboot(fw_cfg, f, kernel_filename, initrd_filename,
|
2013-03-26 12:43:19 +04:00
|
|
|
kernel_cmdline, kernel_size, header)) {
|
2009-09-06 20:31:58 +04:00
|
|
|
return;
|
2013-03-26 12:43:19 +04:00
|
|
|
}
|
|
|
|
protocol = 0;
|
2009-06-29 17:37:39 +04:00
|
|
|
}
|
2007-05-20 01:04:38 +04:00
|
|
|
|
|
|
|
if (protocol < 0x200 || !(header[0x211] & 0x01)) {
|
2013-03-26 12:43:19 +04:00
|
|
|
/* Low kernel */
|
|
|
|
real_addr = 0x90000;
|
|
|
|
cmdline_addr = 0x9a000 - cmdline_size;
|
|
|
|
prot_addr = 0x10000;
|
2007-05-20 01:04:38 +04:00
|
|
|
} else if (protocol < 0x202) {
|
2013-03-26 12:43:19 +04:00
|
|
|
/* High but ancient kernel */
|
|
|
|
real_addr = 0x90000;
|
|
|
|
cmdline_addr = 0x9a000 - cmdline_size;
|
|
|
|
prot_addr = 0x100000;
|
2007-05-20 01:04:38 +04:00
|
|
|
} else {
|
2013-03-26 12:43:19 +04:00
|
|
|
/* High and recent kernel */
|
|
|
|
real_addr = 0x10000;
|
|
|
|
cmdline_addr = 0x20000;
|
|
|
|
prot_addr = 0x100000;
|
2007-05-20 01:04:38 +04:00
|
|
|
}
|
|
|
|
|
2007-11-07 19:54:42 +03:00
|
|
|
#if 0
|
2007-05-20 01:04:38 +04:00
|
|
|
fprintf(stderr,
|
2013-03-26 12:43:19 +04:00
|
|
|
"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);
|
2007-11-07 19:54:42 +03:00
|
|
|
#endif
|
2007-05-20 01:04:38 +04:00
|
|
|
|
|
|
|
/* highest address for loading the initrd */
|
2013-03-26 12:43:19 +04:00
|
|
|
if (protocol >= 0x203) {
|
|
|
|
initrd_max = ldl_p(header+0x22c);
|
|
|
|
} else {
|
|
|
|
initrd_max = 0x37ffffff;
|
|
|
|
}
|
2007-05-20 01:04:38 +04:00
|
|
|
|
2015-12-02 01:58:05 +03:00
|
|
|
if (initrd_max >= pcms->below_4g_mem_size - pcmc->acpi_data_size) {
|
|
|
|
initrd_max = pcms->below_4g_mem_size - pcmc->acpi_data_size - 1;
|
2014-08-20 23:58:12 +04:00
|
|
|
}
|
2007-05-20 01:04:38 +04:00
|
|
|
|
2009-11-12 23:53:14 +03:00
|
|
|
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);
|
2013-01-16 17:50:25 +04:00
|
|
|
fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline);
|
2007-05-20 01:04:38 +04:00
|
|
|
|
|
|
|
if (protocol >= 0x202) {
|
2013-03-26 12:43:19 +04:00
|
|
|
stl_p(header+0x228, cmdline_addr);
|
2007-05-20 01:04:38 +04:00
|
|
|
} else {
|
2013-03-26 12:43:19 +04:00
|
|
|
stw_p(header+0x20, 0xA33F);
|
|
|
|
stw_p(header+0x22, cmdline_addr-real_addr);
|
2007-05-20 01:04:38 +04:00
|
|
|
}
|
|
|
|
|
2009-07-13 19:46:42 +04:00
|
|
|
/* handle vga= parameter */
|
|
|
|
vmode = strstr(kernel_cmdline, "vga=");
|
|
|
|
if (vmode) {
|
|
|
|
unsigned int video_mode;
|
|
|
|
/* 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 {
|
|
|
|
video_mode = strtol(vmode, NULL, 0);
|
|
|
|
}
|
|
|
|
stw_p(header+0x1fa, video_mode);
|
|
|
|
}
|
|
|
|
|
2007-05-20 01:04:38 +04:00
|
|
|
/* loader type */
|
2012-04-07 11:23:39 +04:00
|
|
|
/* High nybble = B reserved for QEMU; low nybble is revision number.
|
2007-05-20 01:04:38 +04:00
|
|
|
If this code is substantially changed, you may want to consider
|
|
|
|
incrementing the revision. */
|
2013-03-26 12:43:19 +04:00
|
|
|
if (protocol >= 0x200) {
|
|
|
|
header[0x210] = 0xB0;
|
|
|
|
}
|
2007-05-20 01:04:38 +04:00
|
|
|
/* heap */
|
|
|
|
if (protocol >= 0x201) {
|
2013-03-26 12:43:19 +04:00
|
|
|
header[0x211] |= 0x80; /* CAN_USE_HEAP */
|
|
|
|
stw_p(header+0x224, cmdline_addr-real_addr-0x200);
|
2007-05-20 01:04:38 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* load initrd */
|
|
|
|
if (initrd_filename) {
|
2013-03-26 12:43:19 +04:00
|
|
|
if (protocol < 0x200) {
|
|
|
|
fprintf(stderr, "qemu: linux kernel too old to load a ram disk\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
2007-05-20 01:04:38 +04:00
|
|
|
|
2013-03-26 12:43:19 +04:00
|
|
|
initrd_size = get_image_size(initrd_filename);
|
2010-04-12 08:31:33 +04:00
|
|
|
if (initrd_size < 0) {
|
2014-02-03 00:45:28 +04:00
|
|
|
fprintf(stderr, "qemu: error reading initrd %s: %s\n",
|
|
|
|
initrd_filename, strerror(errno));
|
2010-04-12 08:31:33 +04:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2009-10-01 18:42:33 +04:00
|
|
|
initrd_addr = (initrd_max-initrd_size) & ~4095;
|
2009-11-12 23:53:14 +03:00
|
|
|
|
2011-08-21 07:09:37 +04:00
|
|
|
initrd_data = g_malloc(initrd_size);
|
2009-11-12 23:53:14 +03:00
|
|
|
load_image(initrd_filename, initrd_data);
|
|
|
|
|
|
|
|
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);
|
2007-05-20 01:04:38 +04:00
|
|
|
|
2013-03-26 12:43:19 +04:00
|
|
|
stl_p(header+0x218, initrd_addr);
|
|
|
|
stl_p(header+0x21c, initrd_size);
|
2007-05-20 01:04:38 +04:00
|
|
|
}
|
|
|
|
|
2009-10-01 18:42:33 +04:00
|
|
|
/* load kernel and setup */
|
2007-05-20 01:04:38 +04:00
|
|
|
setup_size = header[0x1f1];
|
2013-03-26 12:43:19 +04:00
|
|
|
if (setup_size == 0) {
|
|
|
|
setup_size = 4;
|
|
|
|
}
|
2007-05-20 01:04:38 +04:00
|
|
|
setup_size = (setup_size+1)*512;
|
2015-09-14 13:07:22 +03:00
|
|
|
if (setup_size > kernel_size) {
|
|
|
|
fprintf(stderr, "qemu: invalid kernel header\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
2009-10-01 18:42:33 +04:00
|
|
|
kernel_size -= setup_size;
|
2007-05-20 01:04:38 +04:00
|
|
|
|
2011-08-21 07:09:37 +04:00
|
|
|
setup = g_malloc(setup_size);
|
|
|
|
kernel = g_malloc(kernel_size);
|
2009-10-01 18:42:33 +04:00
|
|
|
fseek(f, 0, SEEK_SET);
|
2009-12-25 21:19:17 +03:00
|
|
|
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);
|
|
|
|
}
|
2007-05-20 01:04:38 +04:00
|
|
|
fclose(f);
|
2009-10-01 18:42:33 +04:00
|
|
|
memcpy(setup, header, MIN(sizeof(header), setup_size));
|
2009-11-12 23:53:14 +03:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2010-12-08 14:35:07 +03:00
|
|
|
option_rom[nb_option_roms].name = "linuxboot.bin";
|
|
|
|
option_rom[nb_option_roms].bootindex = 0;
|
2009-11-12 23:53:14 +03:00
|
|
|
nb_option_roms++;
|
2007-05-20 01:04:38 +04:00
|
|
|
}
|
|
|
|
|
2004-03-15 00:46:48 +03:00
|
|
|
#define NE2000_NB_MAX 6
|
|
|
|
|
2009-09-13 12:32:37 +04:00
|
|
|
static const int ne2000_io[NE2000_NB_MAX] = { 0x300, 0x320, 0x340, 0x360,
|
|
|
|
0x280, 0x380 };
|
|
|
|
static const int ne2000_irq[NE2000_NB_MAX] = { 9, 10, 11, 3, 4, 5 };
|
2004-03-15 00:46:48 +03:00
|
|
|
|
2011-12-16 01:09:51 +04:00
|
|
|
void pc_init_ne2k_isa(ISABus *bus, NICInfo *nd)
|
2006-02-05 07:14:41 +03:00
|
|
|
{
|
|
|
|
static int nb_ne2k = 0;
|
|
|
|
|
|
|
|
if (nb_ne2k == NE2000_NB_MAX)
|
|
|
|
return;
|
2011-12-16 01:09:51 +04:00
|
|
|
isa_ne2000_init(bus, ne2000_io[nb_ne2k],
|
2009-09-10 13:43:33 +04:00
|
|
|
ne2000_irq[nb_ne2k], nd);
|
2006-02-05 07:14:41 +03:00
|
|
|
nb_ne2k++;
|
|
|
|
}
|
|
|
|
|
2010-06-19 11:47:42 +04:00
|
|
|
DeviceState *cpu_get_current_apic(void)
|
2010-06-19 11:42:34 +04:00
|
|
|
{
|
2013-05-27 07:17:50 +04:00
|
|
|
if (current_cpu) {
|
|
|
|
X86CPU *cpu = X86_CPU(current_cpu);
|
2013-12-23 13:04:02 +04:00
|
|
|
return cpu->apic_state;
|
2010-06-19 11:42:34 +04:00
|
|
|
} else {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-05-14 11:29:15 +04:00
|
|
|
void pc_acpi_smi_interrupt(void *opaque, int irq, int level)
|
2010-03-29 23:23:52 +04:00
|
|
|
{
|
2013-01-18 18:03:43 +04:00
|
|
|
X86CPU *cpu = opaque;
|
2010-03-29 23:23:52 +04:00
|
|
|
|
|
|
|
if (level) {
|
2013-01-18 18:03:43 +04:00
|
|
|
cpu_interrupt(CPU(cpu), CPU_INTERRUPT_SMI);
|
2010-03-29 23:23:52 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-29 20:54:13 +04:00
|
|
|
static X86CPU *pc_new_cpu(const char *cpu_model, int64_t apic_id,
|
2015-09-16 12:19:14 +03:00
|
|
|
Error **errp)
|
2013-04-25 18:05:26 +04:00
|
|
|
{
|
2015-03-05 20:26:51 +03:00
|
|
|
X86CPU *cpu = NULL;
|
2013-04-25 18:05:26 +04:00
|
|
|
Error *local_err = NULL;
|
|
|
|
|
2015-03-05 20:26:51 +03:00
|
|
|
cpu = cpu_x86_create(cpu_model, &local_err);
|
2013-08-02 20:56:05 +04:00
|
|
|
if (local_err != NULL) {
|
2015-03-05 20:26:51 +03:00
|
|
|
goto out;
|
2013-04-25 18:05:26 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
object_property_set_int(OBJECT(cpu), apic_id, "apic-id", &local_err);
|
|
|
|
object_property_set_bool(OBJECT(cpu), true, "realized", &local_err);
|
|
|
|
|
2015-03-05 20:26:51 +03:00
|
|
|
out:
|
2013-04-25 18:05:26 +04:00
|
|
|
if (local_err) {
|
|
|
|
error_propagate(errp, local_err);
|
2013-08-02 20:56:05 +04:00
|
|
|
object_unref(OBJECT(cpu));
|
|
|
|
cpu = NULL;
|
2013-04-25 18:05:26 +04:00
|
|
|
}
|
|
|
|
return cpu;
|
|
|
|
}
|
|
|
|
|
2013-04-30 20:00:53 +04:00
|
|
|
void pc_hot_add_cpu(const int64_t id, Error **errp)
|
|
|
|
{
|
2015-03-17 19:46:36 +03:00
|
|
|
X86CPU *cpu;
|
2015-10-15 06:12:12 +03:00
|
|
|
MachineState *machine = MACHINE(qdev_get_machine());
|
2013-04-30 20:00:53 +04:00
|
|
|
int64_t apic_id = x86_cpu_apic_id_from_index(id);
|
2015-03-17 19:46:36 +03:00
|
|
|
Error *local_err = NULL;
|
2013-04-30 20:00:53 +04:00
|
|
|
|
2013-05-30 19:09:34 +04:00
|
|
|
if (id < 0) {
|
|
|
|
error_setg(errp, "Invalid CPU id: %" PRIi64, id);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-04-30 20:00:53 +04:00
|
|
|
if (cpu_exists(apic_id)) {
|
|
|
|
error_setg(errp, "Unable to add CPU: %" PRIi64
|
|
|
|
", it already exists", id);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (id >= max_cpus) {
|
|
|
|
error_setg(errp, "Unable to add CPU: %" PRIi64
|
|
|
|
", max allowed: %d", id, max_cpus - 1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-03-14 23:33:51 +04:00
|
|
|
if (apic_id >= ACPI_CPU_HOTPLUG_ID_LIMIT) {
|
|
|
|
error_setg(errp, "Unable to add CPU: %" PRIi64
|
|
|
|
", resulting APIC ID (%" PRIi64 ") is too large",
|
|
|
|
id, apic_id);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-10-15 06:12:12 +03:00
|
|
|
cpu = pc_new_cpu(machine->cpu_model, apic_id, &local_err);
|
2015-03-17 19:46:36 +03:00
|
|
|
if (local_err) {
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
object_unref(OBJECT(cpu));
|
2013-04-30 20:00:53 +04:00
|
|
|
}
|
|
|
|
|
2015-10-15 06:12:12 +03:00
|
|
|
void pc_cpus_init(PCMachineState *pcms)
|
2010-05-14 11:29:10 +04:00
|
|
|
{
|
|
|
|
int i;
|
2013-04-29 21:03:01 +04:00
|
|
|
X86CPU *cpu = NULL;
|
2015-10-15 06:12:12 +03:00
|
|
|
MachineState *machine = MACHINE(pcms);
|
2014-03-14 23:33:54 +04:00
|
|
|
unsigned long apic_id_limit;
|
2010-05-14 11:29:10 +04:00
|
|
|
|
|
|
|
/* init CPUs */
|
2015-10-15 06:12:12 +03:00
|
|
|
if (machine->cpu_model == NULL) {
|
2010-05-14 11:29:10 +04:00
|
|
|
#ifdef TARGET_X86_64
|
2015-10-15 06:12:12 +03:00
|
|
|
machine->cpu_model = "qemu64";
|
2010-05-14 11:29:10 +04:00
|
|
|
#else
|
2015-10-15 06:12:12 +03:00
|
|
|
machine->cpu_model = "qemu32";
|
2010-05-14 11:29:10 +04:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2014-03-14 23:33:54 +04:00
|
|
|
apic_id_limit = pc_apic_id_limit(max_cpus);
|
|
|
|
if (apic_id_limit > ACPI_CPU_HOTPLUG_ID_LIMIT) {
|
|
|
|
error_report("max_cpus is too large. APIC ID of last CPU is %lu",
|
|
|
|
apic_id_limit - 1);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2012-10-14 00:35:39 +04:00
|
|
|
for (i = 0; i < smp_cpus; i++) {
|
2015-10-15 06:12:12 +03:00
|
|
|
cpu = pc_new_cpu(machine->cpu_model, x86_cpu_apic_id_from_index(i),
|
2015-09-11 16:04:45 +03:00
|
|
|
&error_fatal);
|
2015-03-17 19:46:36 +03:00
|
|
|
object_unref(OBJECT(cpu));
|
2010-05-14 11:29:10 +04:00
|
|
|
}
|
2013-04-29 21:03:01 +04:00
|
|
|
|
2014-04-23 17:42:42 +04:00
|
|
|
/* tell smbios about cpuid version and features */
|
|
|
|
smbios_set_cpuid(cpu->env.cpuid_version, cpu->env.features[FEAT_1_EDX]);
|
2010-05-14 11:29:10 +04:00
|
|
|
}
|
|
|
|
|
2013-05-13 21:00:23 +04:00
|
|
|
/* pci-info ROM file. Little endian format */
|
|
|
|
typedef struct PcRomPciInfo {
|
|
|
|
uint64_t w32_min;
|
|
|
|
uint64_t w32_max;
|
|
|
|
uint64_t w64_min;
|
|
|
|
uint64_t w64_max;
|
|
|
|
} PcRomPciInfo;
|
|
|
|
|
2013-05-30 13:57:26 +04:00
|
|
|
typedef struct PcGuestInfoState {
|
|
|
|
PcGuestInfo info;
|
|
|
|
Notifier machine_done;
|
|
|
|
} PcGuestInfoState;
|
|
|
|
|
|
|
|
static
|
|
|
|
void pc_guest_info_machine_done(Notifier *notifier, void *data)
|
|
|
|
{
|
|
|
|
PcGuestInfoState *guest_info_state = container_of(notifier,
|
|
|
|
PcGuestInfoState,
|
|
|
|
machine_done);
|
2015-11-26 19:00:28 +03:00
|
|
|
PCIBus *bus = PC_MACHINE(qdev_get_machine())->bus;
|
2015-06-02 14:23:07 +03:00
|
|
|
|
|
|
|
if (bus) {
|
|
|
|
int extra_hosts = 0;
|
|
|
|
|
|
|
|
QLIST_FOREACH(bus, &bus->child, sibling) {
|
|
|
|
/* look for expander root buses */
|
|
|
|
if (pci_bus_is_root(bus)) {
|
|
|
|
extra_hosts++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (extra_hosts && guest_info_state->info.fw_cfg) {
|
|
|
|
uint64_t *val = g_malloc(sizeof(*val));
|
|
|
|
*val = cpu_to_le64(extra_hosts);
|
|
|
|
fw_cfg_add_file(guest_info_state->info.fw_cfg,
|
|
|
|
"etc/extra-pci-roots", val, sizeof(*val));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
i386: ACPI table generation code from seabios
This adds C code for generating ACPI tables at runtime,
imported from seabios git tree
commit 51684b7ced75fb76776e8ee84833fcfb6ecf12dd
Although ACPI tables come from a system BIOS on real hw,
it makes sense that the ACPI tables are coupled with the
virtual machine, since they have to abstract the x86 machine to
the OS's.
This is widely desired as a way to avoid the churn
and proliferation of QEMU-specific interfaces
associated with ACPI tables in bios code.
Notes:
As BIOS can reprogram devices prior to loading
ACPI tables, we pre-format ACPI tables but defer loading
hardware configuration there until tables are loaded.
The code structure was intentionally kept as close
to the seabios original as possible, to simplify
comparison and making sure we didn't lose anything
in translation.
Minor code duplication results, to help ensure there are no functional
regressions, I think it's better to merge it like this and do more code
changes in follow-up patches.
Cross-version compatibility concerns have been addressed:
ACPI tables are exposed to guest as FW_CFG entries.
When running with -M 1.5 and older, this patch disables ACPI
table generation, and doesn't expose ACPI
tables to guest.
As table content is likely to change over time,
the following measures are taken to simplify
cross-version migration:
- All tables besides the RSDP are packed in a single FW CFG entry.
This entry size is currently 23K. We round it up to 64K
to avoid too much churn there.
- Tables are placed in special ROM blob (not mapped into guest memory)
which is automatically migrated together with the guest, same
as BIOS code.
- Offsets where hardware configuration is loaded in ACPI tables
are also migrated, this is in case future ACPI changes make us
rearrange the tables in memory.
This patch reuses some code from SeaBIOS, which was originally under
LGPLv2 and then relicensed to GPLv3 or LGPLv3, in QEMU under GPLv2+. This
relicensing has been acked by all contributors that had contributed to the
code since the v2->v3 relicense. ACKs approving the v2+ relicensing are
listed below. The list might include ACKs from people not holding
copyright on any parts of the reused code, but it's better to err on the
side of caution and include them.
Affected SeaBIOS files (GPLv2+ license headers added)
<http://thread.gmane.org/gmane.comp.bios.coreboot.seabios/5949>:
src/acpi-dsdt-cpu-hotplug.dsl
src/acpi-dsdt-dbug.dsl
src/acpi-dsdt-hpet.dsl
src/acpi-dsdt-isa.dsl
src/acpi-dsdt-pci-crs.dsl
src/acpi.c
src/acpi.h
src/ssdt-misc.dsl
src/ssdt-pcihp.dsl
src/ssdt-proc.dsl
tools/acpi_extract.py
tools/acpi_extract_preprocess.py
Each one of the listed people agreed to the following:
> If you allow the use of your contribution in QEMU under the
> terms of GPLv2 or later as proposed by this patch,
> please respond to this mail including the line:
>
> Acked-by: Name <email address>
Acked-by: Gerd Hoffmann <kraxel@redhat.com>
Acked-by: Jan Kiszka <jan.kiszka@siemens.com>
Acked-by: Jason Baron <jbaron@akamai.com>
Acked-by: David Woodhouse <David.Woodhouse@intel.com>
Acked-by: Gleb Natapov <gleb@redhat.com>
Acked-by: Marcelo Tosatti <mtosatti@redhat.com>
Acked-by: Dave Frodin <dave.frodin@se-eng.com>
Acked-by: Paolo Bonzini <pbonzini@redhat.com>
Acked-by: Kevin O'Connor <kevin@koconnor.net>
Acked-by: Laszlo Ersek <lersek@redhat.com>
Acked-by: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>
Acked-by: Isaku Yamahata <yamahata@valinux.co.jp>
Acked-by: Magnus Christensson <magnus.christensson@intel.com>
Acked-by: Hu Tao <hutao@cn.fujitsu.com>
Acked-by: Eduardo Habkost <ehabkost@redhat.com>
Reviewed-by: Gerd Hoffmann <kraxel@redhat.com>
Tested-by: Gerd Hoffmann <kraxel@redhat.com>
Reviewed-by: Igor Mammedov <imammedo@redhat.com>
Tested-by: Igor Mammedov <imammedo@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2013-07-24 19:56:14 +04:00
|
|
|
acpi_setup(&guest_info_state->info);
|
2013-05-30 13:57:26 +04:00
|
|
|
}
|
|
|
|
|
2015-08-07 22:55:52 +03:00
|
|
|
PcGuestInfo *pc_guest_info_init(PCMachineState *pcms)
|
2013-05-30 13:57:26 +04:00
|
|
|
{
|
|
|
|
PcGuestInfoState *guest_info_state = g_malloc0(sizeof *guest_info_state);
|
|
|
|
PcGuestInfo *guest_info = &guest_info_state->info;
|
2013-07-24 19:56:09 +04:00
|
|
|
int i, j;
|
|
|
|
|
2015-08-07 22:55:52 +03:00
|
|
|
guest_info->ram_size_below_4g = pcms->below_4g_mem_size;
|
|
|
|
guest_info->ram_size = pcms->below_4g_mem_size + pcms->above_4g_mem_size;
|
2013-07-24 19:56:09 +04:00
|
|
|
guest_info->apic_id_limit = pc_apic_id_limit(max_cpus);
|
|
|
|
guest_info->apic_xrupt_override = kvm_allows_irq0_override();
|
|
|
|
guest_info->numa_nodes = nb_numa_nodes;
|
2014-05-14 13:43:07 +04:00
|
|
|
guest_info->node_mem = g_malloc0(guest_info->numa_nodes *
|
2013-07-24 19:56:09 +04:00
|
|
|
sizeof *guest_info->node_mem);
|
2014-05-14 13:43:07 +04:00
|
|
|
for (i = 0; i < nb_numa_nodes; i++) {
|
|
|
|
guest_info->node_mem[i] = numa_info[i].node_mem;
|
|
|
|
}
|
|
|
|
|
2013-07-24 19:56:09 +04:00
|
|
|
guest_info->node_cpu = g_malloc0(guest_info->apic_id_limit *
|
|
|
|
sizeof *guest_info->node_cpu);
|
|
|
|
|
|
|
|
for (i = 0; i < max_cpus; i++) {
|
|
|
|
unsigned int apic_id = x86_cpu_apic_id_from_index(i);
|
|
|
|
assert(apic_id < guest_info->apic_id_limit);
|
|
|
|
for (j = 0; j < nb_numa_nodes; j++) {
|
2014-05-14 13:43:07 +04:00
|
|
|
if (test_bit(i, numa_info[j].node_cpu)) {
|
2013-07-24 19:56:09 +04:00
|
|
|
guest_info->node_cpu[apic_id] = j;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-05-30 13:57:26 +04:00
|
|
|
|
|
|
|
guest_info_state->machine_done.notify = pc_guest_info_machine_done;
|
|
|
|
qemu_add_machine_init_done_notifier(&guest_info_state->machine_done);
|
|
|
|
return guest_info;
|
|
|
|
}
|
|
|
|
|
2013-10-29 16:57:34 +04:00
|
|
|
/* setup pci memory address space mapping into system address space */
|
|
|
|
void pc_pci_as_mapping_init(Object *owner, MemoryRegion *system_memory,
|
|
|
|
MemoryRegion *pci_address_space)
|
2013-07-29 18:47:57 +04:00
|
|
|
{
|
2013-10-29 16:57:34 +04:00
|
|
|
/* Set to lower priority than RAM */
|
|
|
|
memory_region_add_subregion_overlap(system_memory, 0x0,
|
|
|
|
pci_address_space, -1);
|
2013-07-29 18:47:57 +04:00
|
|
|
}
|
|
|
|
|
2012-12-03 13:47:27 +04:00
|
|
|
void pc_acpi_init(const char *default_dsdt)
|
|
|
|
{
|
2013-03-21 03:23:22 +04:00
|
|
|
char *filename;
|
2012-12-03 13:47:27 +04:00
|
|
|
|
|
|
|
if (acpi_tables != NULL) {
|
|
|
|
/* manually set via -acpitable, leave it alone */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, default_dsdt);
|
|
|
|
if (filename == NULL) {
|
|
|
|
fprintf(stderr, "WARNING: failed to find %s\n", default_dsdt);
|
2013-03-21 03:23:22 +04:00
|
|
|
} else {
|
2015-02-13 17:46:36 +03:00
|
|
|
QemuOpts *opts = qemu_opts_create(qemu_find_opts("acpi"), NULL, 0,
|
|
|
|
&error_abort);
|
2013-03-21 03:23:22 +04:00
|
|
|
Error *err = NULL;
|
2012-12-03 13:47:27 +04:00
|
|
|
|
2015-02-13 17:46:36 +03:00
|
|
|
qemu_opt_set(opts, "file", filename, &error_abort);
|
2013-03-21 03:23:17 +04:00
|
|
|
|
2013-10-07 23:12:00 +04:00
|
|
|
acpi_table_add_builtin(opts, &err);
|
2013-03-21 03:23:22 +04:00
|
|
|
if (err) {
|
2015-12-18 18:35:14 +03:00
|
|
|
error_reportf_err(err, "WARNING: failed to load %s: ",
|
|
|
|
filename);
|
2013-03-21 03:23:22 +04:00
|
|
|
}
|
|
|
|
g_free(filename);
|
2012-12-03 13:47:27 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-07 22:55:53 +03:00
|
|
|
FWCfgState *xen_load_linux(PCMachineState *pcms,
|
2014-07-07 10:34:35 +04:00
|
|
|
PcGuestInfo *guest_info)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
FWCfgState *fw_cfg;
|
|
|
|
|
2015-08-07 22:55:53 +03:00
|
|
|
assert(MACHINE(pcms)->kernel_filename != NULL);
|
2014-07-07 10:34:35 +04:00
|
|
|
|
2014-12-22 15:11:36 +03:00
|
|
|
fw_cfg = fw_cfg_init_io(BIOS_CFG_IOPORT);
|
2014-07-07 10:34:35 +04:00
|
|
|
rom_set_fw(fw_cfg);
|
|
|
|
|
2015-08-07 22:55:53 +03:00
|
|
|
load_linux(pcms, fw_cfg);
|
2014-07-07 10:34:35 +04:00
|
|
|
for (i = 0; i < nb_option_roms; i++) {
|
|
|
|
assert(!strcmp(option_rom[i].name, "linuxboot.bin") ||
|
|
|
|
!strcmp(option_rom[i].name, "multiboot.bin"));
|
|
|
|
rom_add_option(option_rom[i].name, option_rom[i].bootindex);
|
|
|
|
}
|
|
|
|
guest_info->fw_cfg = fw_cfg;
|
|
|
|
return fw_cfg;
|
|
|
|
}
|
|
|
|
|
2015-08-07 22:55:50 +03:00
|
|
|
FWCfgState *pc_memory_init(PCMachineState *pcms,
|
2014-06-10 15:15:17 +04:00
|
|
|
MemoryRegion *system_memory,
|
2013-04-16 04:24:08 +04:00
|
|
|
MemoryRegion *rom_memory,
|
2013-05-30 13:57:26 +04:00
|
|
|
MemoryRegion **ram_memory,
|
|
|
|
PcGuestInfo *guest_info)
|
2004-03-14 15:20:30 +03:00
|
|
|
{
|
2012-02-22 11:18:51 +04:00
|
|
|
int linux_boot, i;
|
|
|
|
MemoryRegion *ram, *option_rom_mr;
|
2011-07-26 15:26:17 +04:00
|
|
|
MemoryRegion *ram_below_4g, *ram_above_4g;
|
2013-04-16 04:24:08 +04:00
|
|
|
FWCfgState *fw_cfg;
|
2015-08-07 22:55:50 +03:00
|
|
|
MachineState *machine = MACHINE(pcms);
|
2015-12-02 01:58:06 +03:00
|
|
|
PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
|
2005-07-23 23:05:37 +04:00
|
|
|
|
2015-08-07 22:55:55 +03:00
|
|
|
assert(machine->ram_size == pcms->below_4g_mem_size +
|
|
|
|
pcms->above_4g_mem_size);
|
2014-06-10 15:15:17 +04:00
|
|
|
|
|
|
|
linux_boot = (machine->kernel_filename != NULL);
|
2004-03-14 15:20:30 +03:00
|
|
|
|
2011-07-26 15:26:17 +04:00
|
|
|
/* Allocate RAM. We allocate it as a single memory region and use
|
2011-11-29 12:52:39 +04:00
|
|
|
* aliases to address portions of it, mostly for backwards compatibility
|
2011-07-26 15:26:17 +04:00
|
|
|
* with older qemus that used qemu_ram_alloc().
|
|
|
|
*/
|
2011-08-21 07:09:37 +04:00
|
|
|
ram = g_malloc(sizeof(*ram));
|
2014-06-10 15:15:17 +04:00
|
|
|
memory_region_allocate_system_memory(ram, NULL, "pc.ram",
|
|
|
|
machine->ram_size);
|
2011-08-15 18:17:38 +04:00
|
|
|
*ram_memory = ram;
|
2011-08-21 07:09:37 +04:00
|
|
|
ram_below_4g = g_malloc(sizeof(*ram_below_4g));
|
2013-06-06 13:41:28 +04:00
|
|
|
memory_region_init_alias(ram_below_4g, NULL, "ram-below-4g", ram,
|
2015-08-07 22:55:55 +03:00
|
|
|
0, pcms->below_4g_mem_size);
|
2011-07-26 15:26:17 +04:00
|
|
|
memory_region_add_subregion(system_memory, 0, ram_below_4g);
|
2015-08-07 22:55:55 +03:00
|
|
|
e820_add_entry(0, pcms->below_4g_mem_size, E820_RAM);
|
|
|
|
if (pcms->above_4g_mem_size > 0) {
|
2011-08-21 07:09:37 +04:00
|
|
|
ram_above_4g = g_malloc(sizeof(*ram_above_4g));
|
2013-06-06 13:41:28 +04:00
|
|
|
memory_region_init_alias(ram_above_4g, NULL, "ram-above-4g", ram,
|
2015-08-07 22:55:55 +03:00
|
|
|
pcms->below_4g_mem_size,
|
|
|
|
pcms->above_4g_mem_size);
|
2011-07-26 15:26:17 +04:00
|
|
|
memory_region_add_subregion(system_memory, 0x100000000ULL,
|
|
|
|
ram_above_4g);
|
2015-08-07 22:55:55 +03:00
|
|
|
e820_add_entry(0x100000000ULL, pcms->above_4g_mem_size, E820_RAM);
|
2010-07-06 20:37:17 +04:00
|
|
|
}
|
2008-09-15 20:01:01 +04:00
|
|
|
|
2014-06-02 17:25:11 +04:00
|
|
|
if (!guest_info->has_reserved_memory &&
|
|
|
|
(machine->ram_slots ||
|
2014-06-10 15:15:17 +04:00
|
|
|
(machine->maxram_size > machine->ram_size))) {
|
2014-06-02 17:25:11 +04:00
|
|
|
MachineClass *mc = MACHINE_GET_CLASS(machine);
|
|
|
|
|
|
|
|
error_report("\"-memory 'slots|maxmem'\" is not supported by: %s",
|
|
|
|
mc->name);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
2014-06-02 17:25:08 +04:00
|
|
|
/* initialize hotplug memory address space */
|
2014-06-02 17:25:10 +04:00
|
|
|
if (guest_info->has_reserved_memory &&
|
2014-06-10 15:15:17 +04:00
|
|
|
(machine->ram_size < machine->maxram_size)) {
|
2014-06-02 17:25:08 +04:00
|
|
|
ram_addr_t hotplug_mem_size =
|
2014-06-10 15:15:17 +04:00
|
|
|
machine->maxram_size - machine->ram_size;
|
2014-06-02 17:25:08 +04:00
|
|
|
|
2014-06-02 17:25:09 +04:00
|
|
|
if (machine->ram_slots > ACPI_MAX_RAM_SLOTS) {
|
|
|
|
error_report("unsupported amount of memory slots: %"PRIu64,
|
|
|
|
machine->ram_slots);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
2015-01-29 16:48:41 +03:00
|
|
|
if (QEMU_ALIGN_UP(machine->maxram_size,
|
|
|
|
TARGET_PAGE_SIZE) != machine->maxram_size) {
|
|
|
|
error_report("maximum memory size must by aligned to multiple of "
|
|
|
|
"%d bytes", TARGET_PAGE_SIZE);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
2015-06-29 11:20:22 +03:00
|
|
|
pcms->hotplug_memory.base =
|
2015-08-07 22:55:55 +03:00
|
|
|
ROUND_UP(0x100000000ULL + pcms->above_4g_mem_size, 1ULL << 30);
|
2014-06-02 17:25:08 +04:00
|
|
|
|
2015-12-02 01:58:06 +03:00
|
|
|
if (pcmc->enforce_aligned_dimm) {
|
pc: count in 1Gb hugepage alignment when sizing hotplug-memory container
if DIMMs with different size/alignment are interleaved
in creation order, it could lead to hotplug-memory
container fragmentation and following inability to use
all RAM upto maxmem.
For example:
-m 4G,slots=3,maxmem=7G
-object memory-backend-file,id=mem-1,size=256M,mem-path=/pagesize-2MB
-device pc-dimm,id=mem1,memdev=mem-1
-object memory-backend-file,id=mem-2,size=1G,mem-path=/pagesize-1GB
-device pc-dimm,id=mem2,memdev=mem-2
-object memory-backend-file,id=mem-3,size=256M,mem-path=/pagesize-2MB
-device pc-dimm,id=mem3,memdev=mem-3
fragments hotplug-memory container and doesn't allow
to use 1GB hugepage backend to consume remainig 1Gb.
To ease managment factor count in max 1Gb alignment for
each memory slot when sizing hotplug-memory region so
that regadless of fragmentaion it would be possible to
add max aligned DIMM.
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2014-10-31 19:38:42 +03:00
|
|
|
/* size hotplug region assuming 1G page max alignment per slot */
|
|
|
|
hotplug_mem_size += (1ULL << 30) * machine->ram_slots;
|
|
|
|
}
|
|
|
|
|
2015-06-29 11:20:22 +03:00
|
|
|
if ((pcms->hotplug_memory.base + hotplug_mem_size) <
|
2014-06-02 17:25:08 +04:00
|
|
|
hotplug_mem_size) {
|
|
|
|
error_report("unsupported amount of maximum memory: " RAM_ADDR_FMT,
|
|
|
|
machine->maxram_size);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
2015-06-29 11:20:22 +03:00
|
|
|
memory_region_init(&pcms->hotplug_memory.mr, OBJECT(pcms),
|
2014-06-02 17:25:08 +04:00
|
|
|
"hotplug-memory", hotplug_mem_size);
|
2015-06-29 11:20:22 +03:00
|
|
|
memory_region_add_subregion(system_memory, pcms->hotplug_memory.base,
|
|
|
|
&pcms->hotplug_memory.mr);
|
2014-06-02 17:25:08 +04:00
|
|
|
}
|
2012-02-22 11:18:51 +04:00
|
|
|
|
|
|
|
/* Initialize PC system firmware */
|
2013-08-09 21:35:02 +04:00
|
|
|
pc_system_firmware_init(rom_memory, guest_info->isapc_ram_fw);
|
2011-07-26 15:26:17 +04:00
|
|
|
|
2011-08-21 07:09:37 +04:00
|
|
|
option_rom_mr = g_malloc(sizeof(*option_rom_mr));
|
2014-09-09 09:27:55 +04:00
|
|
|
memory_region_init_ram(option_rom_mr, NULL, "pc.rom", PC_ROM_SIZE,
|
Fix bad error handling after memory_region_init_ram()
Symptom:
$ qemu-system-x86_64 -m 10000000
Unexpected error in ram_block_add() at /work/armbru/qemu/exec.c:1456:
upstream-qemu: cannot set up guest memory 'pc.ram': Cannot allocate memory
Aborted (core dumped)
Root cause: commit ef701d7 screwed up handling of out-of-memory
conditions. Before the commit, we report the error and exit(1), in
one place, ram_block_add(). The commit lifts the error handling up
the call chain some, to three places. Fine. Except it uses
&error_abort in these places, changing the behavior from exit(1) to
abort(), and thus undoing the work of commit 3922825 "exec: Don't
abort when we can't allocate guest memory".
The three places are:
* memory_region_init_ram()
Commit 4994653 (right after commit ef701d7) lifted the error
handling further, through memory_region_init_ram(), multiplying the
incorrect use of &error_abort. Later on, imitation of existing
(bad) code may have created more.
* memory_region_init_ram_ptr()
The &error_abort is still there.
* memory_region_init_rom_device()
Doesn't need fixing, because commit 33e0eb5 (soon after commit
ef701d7) lifted the error handling further, and in the process
changed it from &error_abort to passing it up the call chain.
Correct, because the callers are realize() methods.
Fix the error handling after memory_region_init_ram() with a
Coccinelle semantic patch:
@r@
expression mr, owner, name, size, err;
position p;
@@
memory_region_init_ram(mr, owner, name, size,
(
- &error_abort
+ &error_fatal
|
err@p
)
);
@script:python@
p << r.p;
@@
print "%s:%s:%s" % (p[0].file, p[0].line, p[0].column)
When the last argument is &error_abort, it gets replaced by
&error_fatal. This is the fix.
If the last argument is anything else, its position is reported. This
lets us check the fix is complete. Four positions get reported:
* ram_backend_memory_alloc()
Error is passed up the call chain, ultimately through
user_creatable_complete(). As far as I can tell, it's callers all
handle the error sanely.
* fsl_imx25_realize(), fsl_imx31_realize(), dp8393x_realize()
DeviceClass.realize() methods, errors handled sanely further up the
call chain.
We're good. Test case again behaves:
$ qemu-system-x86_64 -m 10000000
qemu-system-x86_64: cannot set up guest memory 'pc.ram': Cannot allocate memory
[Exit 1 ]
The next commits will repair the rest of commit ef701d7's damage.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <1441983105-26376-3-git-send-email-armbru@redhat.com>
Reviewed-by: Peter Crosthwaite <crosthwaite.peter@gmail.com>
2015-09-11 17:51:43 +03:00
|
|
|
&error_fatal);
|
2011-12-20 17:59:12 +04:00
|
|
|
vmstate_register_ram_global(option_rom_mr);
|
2011-09-21 22:49:29 +04:00
|
|
|
memory_region_add_subregion_overlap(rom_memory,
|
2011-07-26 15:26:17 +04:00
|
|
|
PC_ROM_MIN_VGA,
|
|
|
|
option_rom_mr,
|
|
|
|
1);
|
2009-04-10 00:59:05 +04:00
|
|
|
|
2015-10-08 18:02:57 +03:00
|
|
|
fw_cfg = bochs_bios_init(&address_space_memory);
|
|
|
|
|
2010-01-08 17:25:40 +03:00
|
|
|
rom_set_fw(fw_cfg);
|
2009-06-29 17:37:37 +04:00
|
|
|
|
2015-06-29 11:20:22 +03:00
|
|
|
if (guest_info->has_reserved_memory && pcms->hotplug_memory.base) {
|
2014-06-02 17:25:10 +04:00
|
|
|
uint64_t *val = g_malloc(sizeof(*val));
|
2015-09-07 14:55:32 +03:00
|
|
|
PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
|
|
|
|
uint64_t res_mem_end = pcms->hotplug_memory.base;
|
|
|
|
|
|
|
|
if (!pcmc->broken_reserved_end) {
|
|
|
|
res_mem_end += memory_region_size(&pcms->hotplug_memory.mr);
|
|
|
|
}
|
2015-09-07 14:55:31 +03:00
|
|
|
*val = cpu_to_le64(ROUND_UP(res_mem_end, 0x1ULL << 30));
|
2014-06-02 17:25:10 +04:00
|
|
|
fw_cfg_add_file(fw_cfg, "etc/reserved-memory-end", val, sizeof(*val));
|
|
|
|
}
|
|
|
|
|
2009-04-10 00:59:05 +04:00
|
|
|
if (linux_boot) {
|
2015-08-07 22:55:53 +03:00
|
|
|
load_linux(pcms, fw_cfg);
|
2009-04-10 00:59:05 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < nb_option_roms; i++) {
|
2010-12-08 14:35:07 +03:00
|
|
|
rom_add_option(option_rom[i].name, option_rom[i].bootindex);
|
2009-06-17 17:05:30 +04:00
|
|
|
}
|
2013-05-30 13:57:26 +04:00
|
|
|
guest_info->fw_cfg = fw_cfg;
|
2012-06-04 15:31:55 +04:00
|
|
|
return fw_cfg;
|
2010-05-14 11:29:11 +04:00
|
|
|
}
|
|
|
|
|
2015-05-29 08:26:59 +03:00
|
|
|
qemu_irq pc_allocate_cpu_irq(void)
|
2010-05-14 11:29:15 +04:00
|
|
|
{
|
2015-05-29 08:26:59 +03:00
|
|
|
return qemu_allocate_irq(pic_irq_request, NULL, 0);
|
2010-05-14 11:29:15 +04:00
|
|
|
}
|
|
|
|
|
2011-12-16 01:09:51 +04:00
|
|
|
DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus)
|
2010-05-14 11:29:12 +04:00
|
|
|
{
|
2011-12-13 00:29:41 +04:00
|
|
|
DeviceState *dev = NULL;
|
|
|
|
|
2012-09-08 14:47:45 +04:00
|
|
|
if (pci_bus) {
|
|
|
|
PCIDevice *pcidev = pci_vga_init(pci_bus);
|
|
|
|
dev = pcidev ? &pcidev->qdev : NULL;
|
|
|
|
} else if (isa_bus) {
|
|
|
|
ISADevice *isadev = isa_vga_init(isa_bus);
|
2013-06-07 15:49:13 +04:00
|
|
|
dev = isadev ? DEVICE(isadev) : NULL;
|
2010-05-14 11:29:12 +04:00
|
|
|
}
|
2011-12-13 00:29:41 +04:00
|
|
|
return dev;
|
2010-05-14 11:29:12 +04:00
|
|
|
}
|
|
|
|
|
2012-09-19 15:50:08 +04:00
|
|
|
static const MemoryRegionOps ioport80_io_ops = {
|
|
|
|
.write = ioport80_write,
|
2013-01-09 22:10:22 +04:00
|
|
|
.read = ioport80_read,
|
2012-09-19 15:50:08 +04:00
|
|
|
.endianness = DEVICE_NATIVE_ENDIAN,
|
|
|
|
.impl = {
|
|
|
|
.min_access_size = 1,
|
|
|
|
.max_access_size = 1,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static const MemoryRegionOps ioportF0_io_ops = {
|
|
|
|
.write = ioportF0_write,
|
2013-01-09 22:10:22 +04:00
|
|
|
.read = ioportF0_read,
|
2012-09-19 15:50:08 +04:00
|
|
|
.endianness = DEVICE_NATIVE_ENDIAN,
|
|
|
|
.impl = {
|
|
|
|
.min_access_size = 1,
|
|
|
|
.max_access_size = 1,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2011-12-16 01:09:51 +04:00
|
|
|
void pc_basic_device_init(ISABus *isa_bus, qemu_irq *gsi,
|
2011-05-03 20:06:54 +04:00
|
|
|
ISADevice **rtc_state,
|
2015-05-28 23:04:08 +03:00
|
|
|
bool create_fdctrl,
|
2013-12-08 13:38:17 +04:00
|
|
|
bool no_vmport,
|
|
|
|
uint32 hpet_irqs)
|
2010-05-14 11:29:13 +04:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
DriveInfo *fd[MAX_FD];
|
2012-02-01 23:31:41 +04:00
|
|
|
DeviceState *hpet = NULL;
|
|
|
|
int pit_isa_irq = 0;
|
|
|
|
qemu_irq pit_alt_irq = NULL;
|
2010-06-13 16:15:40 +04:00
|
|
|
qemu_irq rtc_irq = NULL;
|
2010-05-22 11:59:01 +04:00
|
|
|
qemu_irq *a20_line;
|
2011-11-14 19:07:01 +04:00
|
|
|
ISADevice *i8042, *port92, *vmmouse, *pit = NULL;
|
2012-09-19 15:50:08 +04:00
|
|
|
MemoryRegion *ioport80_io = g_new(MemoryRegion, 1);
|
|
|
|
MemoryRegion *ioportF0_io = g_new(MemoryRegion, 1);
|
2010-05-14 11:29:13 +04:00
|
|
|
|
2013-06-06 13:41:28 +04:00
|
|
|
memory_region_init_io(ioport80_io, NULL, &ioport80_io_ops, NULL, "ioport80", 1);
|
2012-09-19 15:50:08 +04:00
|
|
|
memory_region_add_subregion(isa_bus->address_space_io, 0x80, ioport80_io);
|
2010-05-14 11:29:13 +04:00
|
|
|
|
2013-06-06 13:41:28 +04:00
|
|
|
memory_region_init_io(ioportF0_io, NULL, &ioportF0_io_ops, NULL, "ioportF0", 1);
|
2012-09-19 15:50:08 +04:00
|
|
|
memory_region_add_subregion(isa_bus->address_space_io, 0xf0, ioportF0_io);
|
2010-05-14 11:29:13 +04:00
|
|
|
|
2012-03-02 23:28:49 +04:00
|
|
|
/*
|
|
|
|
* Check if an HPET shall be created.
|
|
|
|
*
|
|
|
|
* Without KVM_CAP_PIT_STATE2, we cannot switch off the in-kernel PIT
|
|
|
|
* when the HPET wants to take over. Thus we have to disable the latter.
|
|
|
|
*/
|
|
|
|
if (!no_hpet && (!kvm_irqchip_in_kernel() || kvm_has_pit_state2())) {
|
2013-12-08 13:38:17 +04:00
|
|
|
/* In order to set property, here not using sysbus_try_create_simple */
|
2013-12-11 04:48:49 +04:00
|
|
|
hpet = qdev_try_create(NULL, TYPE_HPET);
|
2011-02-05 17:35:00 +03:00
|
|
|
if (hpet) {
|
2013-12-08 13:38:17 +04:00
|
|
|
/* For pc-piix-*, hpet's intcap is always IRQ2. For pc-q35-1.7
|
|
|
|
* and earlier, use IRQ2 for compat. Otherwise, use IRQ16~23,
|
|
|
|
* IRQ8 and IRQ2.
|
|
|
|
*/
|
|
|
|
uint8_t compat = object_property_get_int(OBJECT(hpet),
|
|
|
|
HPET_INTCAP, NULL);
|
|
|
|
if (!compat) {
|
|
|
|
qdev_prop_set_uint32(hpet, HPET_INTCAP, hpet_irqs);
|
|
|
|
}
|
|
|
|
qdev_init_nofail(hpet);
|
|
|
|
sysbus_mmio_map(SYS_BUS_DEVICE(hpet), 0, HPET_BASE);
|
|
|
|
|
2011-10-07 11:19:35 +04:00
|
|
|
for (i = 0; i < GSI_NUM_PINS; i++) {
|
2013-01-20 05:47:33 +04:00
|
|
|
sysbus_connect_irq(SYS_BUS_DEVICE(hpet), i, gsi[i]);
|
2011-02-05 17:35:00 +03:00
|
|
|
}
|
2012-02-01 23:31:41 +04:00
|
|
|
pit_isa_irq = -1;
|
|
|
|
pit_alt_irq = qdev_get_gpio_in(hpet, HPET_LEGACY_PIT_INT);
|
|
|
|
rtc_irq = qdev_get_gpio_in(hpet, HPET_LEGACY_RTC_INT);
|
2010-06-13 16:15:38 +04:00
|
|
|
}
|
2010-05-14 11:29:13 +04:00
|
|
|
}
|
2011-12-16 01:09:51 +04:00
|
|
|
*rtc_state = rtc_init(isa_bus, 2000, rtc_irq);
|
2010-06-13 16:15:40 +04:00
|
|
|
|
|
|
|
qemu_register_boot_set(pc_boot_set, *rtc_state);
|
|
|
|
|
2011-11-14 19:07:01 +04:00
|
|
|
if (!xen_enabled()) {
|
2015-12-17 19:16:08 +03:00
|
|
|
if (kvm_pit_in_kernel()) {
|
2011-11-14 19:07:01 +04:00
|
|
|
pit = kvm_pit_init(isa_bus, 0x40);
|
|
|
|
} else {
|
|
|
|
pit = pit_init(isa_bus, 0x40, pit_isa_irq, pit_alt_irq);
|
|
|
|
}
|
|
|
|
if (hpet) {
|
|
|
|
/* connect PIT to output control line of the HPET */
|
2013-06-07 15:49:13 +04:00
|
|
|
qdev_connect_gpio_out(hpet, 0, qdev_get_gpio_in(DEVICE(pit), 0));
|
2011-11-14 19:07:01 +04:00
|
|
|
}
|
|
|
|
pcspk_init(isa_bus, pit);
|
2012-02-01 23:31:41 +04:00
|
|
|
}
|
2010-05-14 11:29:13 +04:00
|
|
|
|
2015-02-04 20:33:05 +03:00
|
|
|
serial_hds_isa_init(isa_bus, MAX_SERIAL_PORTS);
|
2015-02-04 20:33:07 +03:00
|
|
|
parallel_hds_isa_init(isa_bus, MAX_PARALLEL_PORTS);
|
2010-05-14 11:29:13 +04:00
|
|
|
|
2013-05-30 00:29:20 +04:00
|
|
|
a20_line = qemu_allocate_irqs(handle_a20_line_change, first_cpu, 2);
|
2011-12-16 01:09:51 +04:00
|
|
|
i8042 = isa_create_simple(isa_bus, "i8042");
|
2011-01-06 21:24:35 +03:00
|
|
|
i8042_setup_a20_line(i8042, &a20_line[0]);
|
2011-05-03 20:06:54 +04:00
|
|
|
if (!no_vmport) {
|
2011-12-16 01:09:51 +04:00
|
|
|
vmport_init(isa_bus);
|
|
|
|
vmmouse = isa_try_create(isa_bus, "vmmouse");
|
2011-05-03 20:06:54 +04:00
|
|
|
} else {
|
|
|
|
vmmouse = NULL;
|
|
|
|
}
|
2011-02-05 17:34:52 +03:00
|
|
|
if (vmmouse) {
|
2013-06-07 15:49:13 +04:00
|
|
|
DeviceState *dev = DEVICE(vmmouse);
|
|
|
|
qdev_prop_set_ptr(dev, "ps2_mouse", i8042);
|
|
|
|
qdev_init_nofail(dev);
|
2011-02-05 17:34:52 +03:00
|
|
|
}
|
2011-12-16 01:09:51 +04:00
|
|
|
port92 = isa_create_simple(isa_bus, "port92");
|
2011-01-06 21:24:35 +03:00
|
|
|
port92_init(port92, &a20_line[1]);
|
2010-05-22 11:59:01 +04:00
|
|
|
|
2015-02-16 16:13:11 +03:00
|
|
|
DMA_init(0);
|
2010-05-14 11:29:13 +04:00
|
|
|
|
|
|
|
for(i = 0; i < MAX_FD; i++) {
|
|
|
|
fd[i] = drive_get(IF_FLOPPY, 0, i);
|
2015-05-28 23:04:09 +03:00
|
|
|
create_fdctrl |= !!fd[i];
|
2010-05-14 11:29:13 +04:00
|
|
|
}
|
2015-06-25 16:35:07 +03:00
|
|
|
if (create_fdctrl) {
|
|
|
|
fdctrl_init_isa(isa_bus, fd);
|
|
|
|
}
|
2010-05-14 11:29:13 +04:00
|
|
|
}
|
|
|
|
|
2012-11-15 00:54:01 +04:00
|
|
|
void pc_nic_init(ISABus *isa_bus, PCIBus *pci_bus)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < nb_nics; i++) {
|
|
|
|
NICInfo *nd = &nd_table[i];
|
|
|
|
|
|
|
|
if (!pci_bus || (nd->model && strcmp(nd->model, "ne2k_isa") == 0)) {
|
|
|
|
pc_init_ne2k_isa(isa_bus, nd);
|
|
|
|
} else {
|
2013-06-06 12:48:51 +04:00
|
|
|
pci_nic_init_nofail(nd, pci_bus, "e1000", NULL);
|
2012-11-15 00:54:01 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-05-14 11:29:15 +04:00
|
|
|
void pc_pci_device_init(PCIBus *pci_bus)
|
2010-05-14 11:29:14 +04:00
|
|
|
{
|
|
|
|
int max_bus;
|
|
|
|
int bus;
|
|
|
|
|
|
|
|
max_bus = drive_get_max_bus(IF_SCSI);
|
|
|
|
for (bus = 0; bus <= max_bus; bus++) {
|
|
|
|
pci_create_simple(pci_bus, -1, "lsi53c895a");
|
|
|
|
}
|
|
|
|
}
|
2012-11-15 00:54:01 +04:00
|
|
|
|
|
|
|
void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name)
|
|
|
|
{
|
|
|
|
DeviceState *dev;
|
|
|
|
SysBusDevice *d;
|
|
|
|
unsigned int i;
|
|
|
|
|
2015-12-17 19:16:08 +03:00
|
|
|
if (kvm_ioapic_in_kernel()) {
|
2012-11-15 00:54:01 +04:00
|
|
|
dev = qdev_create(NULL, "kvm-ioapic");
|
|
|
|
} else {
|
|
|
|
dev = qdev_create(NULL, "ioapic");
|
|
|
|
}
|
|
|
|
if (parent_name) {
|
|
|
|
object_property_add_child(object_resolve_path(parent_name, NULL),
|
|
|
|
"ioapic", OBJECT(dev), NULL);
|
|
|
|
}
|
|
|
|
qdev_init_nofail(dev);
|
2013-01-20 05:47:33 +04:00
|
|
|
d = SYS_BUS_DEVICE(dev);
|
2013-03-21 03:23:21 +04:00
|
|
|
sysbus_mmio_map(d, 0, IO_APIC_DEFAULT_ADDRESS);
|
2012-11-15 00:54:01 +04:00
|
|
|
|
|
|
|
for (i = 0; i < IOAPIC_NUM_PINS; i++) {
|
|
|
|
gsi_state->ioapic_irq[i] = qdev_get_gpio_in(dev, i);
|
|
|
|
}
|
|
|
|
}
|
2014-06-02 17:24:57 +04:00
|
|
|
|
2014-06-02 17:25:12 +04:00
|
|
|
static void pc_dimm_plug(HotplugHandler *hotplug_dev,
|
|
|
|
DeviceState *dev, Error **errp)
|
|
|
|
{
|
2014-06-02 17:25:25 +04:00
|
|
|
HotplugHandlerClass *hhc;
|
2014-06-02 17:25:12 +04:00
|
|
|
Error *local_err = NULL;
|
|
|
|
PCMachineState *pcms = PC_MACHINE(hotplug_dev);
|
2015-12-02 01:58:06 +03:00
|
|
|
PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
|
2014-06-02 17:25:12 +04:00
|
|
|
PCDIMMDevice *dimm = PC_DIMM(dev);
|
|
|
|
PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm);
|
|
|
|
MemoryRegion *mr = ddc->get_memory_region(dimm);
|
2014-10-31 19:38:36 +03:00
|
|
|
uint64_t align = TARGET_PAGE_SIZE;
|
2014-06-02 17:25:12 +04:00
|
|
|
|
2015-12-02 01:58:06 +03:00
|
|
|
if (memory_region_get_alignment(mr) && pcmc->enforce_aligned_dimm) {
|
2014-10-31 19:38:39 +03:00
|
|
|
align = memory_region_get_alignment(mr);
|
|
|
|
}
|
|
|
|
|
2014-06-02 17:25:25 +04:00
|
|
|
if (!pcms->acpi_dev) {
|
|
|
|
error_setg(&local_err,
|
|
|
|
"memory hotplug is not enabled: missing acpi device");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2015-10-28 19:55:06 +03:00
|
|
|
pc_dimm_memory_plug(dev, &pcms->hotplug_memory, mr, align, &local_err);
|
2015-06-29 11:20:23 +03:00
|
|
|
if (local_err) {
|
2014-10-31 19:38:32 +03:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2014-06-02 17:25:25 +04:00
|
|
|
hhc = HOTPLUG_HANDLER_GET_CLASS(pcms->acpi_dev);
|
2015-06-29 11:20:24 +03:00
|
|
|
hhc->plug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &error_abort);
|
2014-06-02 17:25:12 +04:00
|
|
|
out:
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
}
|
|
|
|
|
2015-04-27 11:47:17 +03:00
|
|
|
static void pc_dimm_unplug_request(HotplugHandler *hotplug_dev,
|
|
|
|
DeviceState *dev, Error **errp)
|
|
|
|
{
|
|
|
|
HotplugHandlerClass *hhc;
|
|
|
|
Error *local_err = NULL;
|
|
|
|
PCMachineState *pcms = PC_MACHINE(hotplug_dev);
|
|
|
|
|
|
|
|
if (!pcms->acpi_dev) {
|
|
|
|
error_setg(&local_err,
|
|
|
|
"memory hotplug is not enabled: missing acpi device");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
hhc = HOTPLUG_HANDLER_GET_CLASS(pcms->acpi_dev);
|
|
|
|
hhc->unplug_request(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err);
|
|
|
|
|
|
|
|
out:
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
}
|
|
|
|
|
2015-04-27 11:47:18 +03:00
|
|
|
static void pc_dimm_unplug(HotplugHandler *hotplug_dev,
|
|
|
|
DeviceState *dev, Error **errp)
|
|
|
|
{
|
|
|
|
PCMachineState *pcms = PC_MACHINE(hotplug_dev);
|
|
|
|
PCDIMMDevice *dimm = PC_DIMM(dev);
|
|
|
|
PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm);
|
|
|
|
MemoryRegion *mr = ddc->get_memory_region(dimm);
|
|
|
|
HotplugHandlerClass *hhc;
|
|
|
|
Error *local_err = NULL;
|
|
|
|
|
|
|
|
hhc = HOTPLUG_HANDLER_GET_CLASS(pcms->acpi_dev);
|
|
|
|
hhc->unplug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err);
|
|
|
|
|
|
|
|
if (local_err) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2015-06-29 11:20:23 +03:00
|
|
|
pc_dimm_memory_unplug(dev, &pcms->hotplug_memory, mr);
|
2015-04-27 11:47:18 +03:00
|
|
|
object_unparent(OBJECT(dev));
|
|
|
|
|
|
|
|
out:
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
}
|
|
|
|
|
2014-10-22 07:24:28 +04:00
|
|
|
static void pc_cpu_plug(HotplugHandler *hotplug_dev,
|
|
|
|
DeviceState *dev, Error **errp)
|
|
|
|
{
|
|
|
|
HotplugHandlerClass *hhc;
|
|
|
|
Error *local_err = NULL;
|
|
|
|
PCMachineState *pcms = PC_MACHINE(hotplug_dev);
|
|
|
|
|
|
|
|
if (!dev->hotplugged) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pcms->acpi_dev) {
|
|
|
|
error_setg(&local_err,
|
|
|
|
"cpu hotplug is not enabled: missing acpi device");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
hhc = HOTPLUG_HANDLER_GET_CLASS(pcms->acpi_dev);
|
|
|
|
hhc->plug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err);
|
2014-10-22 07:24:29 +04:00
|
|
|
if (local_err) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* increment the number of CPUs */
|
|
|
|
rtc_set_memory(pcms->rtc, 0x5f, rtc_get_memory(pcms->rtc, 0x5f) + 1);
|
2014-10-22 07:24:28 +04:00
|
|
|
out:
|
|
|
|
error_propagate(errp, local_err);
|
|
|
|
}
|
|
|
|
|
2014-06-02 17:25:12 +04:00
|
|
|
static void pc_machine_device_plug_cb(HotplugHandler *hotplug_dev,
|
|
|
|
DeviceState *dev, Error **errp)
|
|
|
|
{
|
|
|
|
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
|
|
|
|
pc_dimm_plug(hotplug_dev, dev, errp);
|
2014-10-22 07:24:28 +04:00
|
|
|
} else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
|
|
|
|
pc_cpu_plug(hotplug_dev, dev, errp);
|
2014-06-02 17:25:12 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-28 10:45:37 +03:00
|
|
|
static void pc_machine_device_unplug_request_cb(HotplugHandler *hotplug_dev,
|
|
|
|
DeviceState *dev, Error **errp)
|
|
|
|
{
|
2015-04-27 11:47:17 +03:00
|
|
|
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
|
|
|
|
pc_dimm_unplug_request(hotplug_dev, dev, errp);
|
|
|
|
} else {
|
|
|
|
error_setg(errp, "acpi: device unplug request for not supported device"
|
|
|
|
" type: %s", object_get_typename(OBJECT(dev)));
|
|
|
|
}
|
2015-01-28 10:45:37 +03:00
|
|
|
}
|
|
|
|
|
2015-01-28 10:45:39 +03:00
|
|
|
static void pc_machine_device_unplug_cb(HotplugHandler *hotplug_dev,
|
|
|
|
DeviceState *dev, Error **errp)
|
|
|
|
{
|
2015-04-27 11:47:18 +03:00
|
|
|
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
|
|
|
|
pc_dimm_unplug(hotplug_dev, dev, errp);
|
|
|
|
} else {
|
|
|
|
error_setg(errp, "acpi: device unplug for not supported device"
|
|
|
|
" type: %s", object_get_typename(OBJECT(dev)));
|
|
|
|
}
|
2015-01-28 10:45:39 +03:00
|
|
|
}
|
|
|
|
|
2014-06-02 17:25:12 +04:00
|
|
|
static HotplugHandler *pc_get_hotpug_handler(MachineState *machine,
|
|
|
|
DeviceState *dev)
|
|
|
|
{
|
|
|
|
PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(machine);
|
|
|
|
|
2014-10-22 07:24:28 +04:00
|
|
|
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) ||
|
|
|
|
object_dynamic_cast(OBJECT(dev), TYPE_CPU)) {
|
2014-06-02 17:25:12 +04:00
|
|
|
return HOTPLUG_HANDLER(machine);
|
|
|
|
}
|
|
|
|
|
|
|
|
return pcmc->get_hotplug_handler ?
|
|
|
|
pcmc->get_hotplug_handler(machine, dev) : NULL;
|
|
|
|
}
|
|
|
|
|
2014-06-02 17:25:27 +04:00
|
|
|
static void
|
|
|
|
pc_machine_get_hotplug_memory_region_size(Object *obj, Visitor *v, void *opaque,
|
|
|
|
const char *name, Error **errp)
|
|
|
|
{
|
|
|
|
PCMachineState *pcms = PC_MACHINE(obj);
|
2015-06-29 11:20:22 +03:00
|
|
|
int64_t value = memory_region_size(&pcms->hotplug_memory.mr);
|
2014-06-02 17:25:27 +04:00
|
|
|
|
|
|
|
visit_type_int(v, &value, name, errp);
|
|
|
|
}
|
|
|
|
|
2014-06-20 05:40:25 +04:00
|
|
|
static void pc_machine_get_max_ram_below_4g(Object *obj, Visitor *v,
|
|
|
|
void *opaque, const char *name,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
PCMachineState *pcms = PC_MACHINE(obj);
|
|
|
|
uint64_t value = pcms->max_ram_below_4g;
|
|
|
|
|
|
|
|
visit_type_size(v, &value, name, errp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pc_machine_set_max_ram_below_4g(Object *obj, Visitor *v,
|
|
|
|
void *opaque, const char *name,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
PCMachineState *pcms = PC_MACHINE(obj);
|
|
|
|
Error *error = NULL;
|
|
|
|
uint64_t value;
|
|
|
|
|
|
|
|
visit_type_size(v, &value, name, &error);
|
|
|
|
if (error) {
|
|
|
|
error_propagate(errp, error);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (value > (1ULL << 32)) {
|
2015-11-11 09:51:20 +03:00
|
|
|
error_setg(&error,
|
|
|
|
"Machine option 'max-ram-below-4g=%"PRIu64
|
|
|
|
"' expects size less than or equal to 4G", value);
|
2014-06-20 05:40:25 +04:00
|
|
|
error_propagate(errp, error);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (value < (1ULL << 20)) {
|
|
|
|
error_report("Warning: small max_ram_below_4g(%"PRIu64
|
|
|
|
") less than 1M. BIOS may not work..",
|
|
|
|
value);
|
|
|
|
}
|
|
|
|
|
|
|
|
pcms->max_ram_below_4g = value;
|
|
|
|
}
|
|
|
|
|
2014-11-21 19:18:52 +03:00
|
|
|
static void pc_machine_get_vmport(Object *obj, Visitor *v, void *opaque,
|
|
|
|
const char *name, Error **errp)
|
2014-10-04 01:33:37 +04:00
|
|
|
{
|
|
|
|
PCMachineState *pcms = PC_MACHINE(obj);
|
2014-11-21 19:18:52 +03:00
|
|
|
OnOffAuto vmport = pcms->vmport;
|
2014-10-04 01:33:37 +04:00
|
|
|
|
2014-11-21 19:18:52 +03:00
|
|
|
visit_type_OnOffAuto(v, &vmport, name, errp);
|
2014-10-04 01:33:37 +04:00
|
|
|
}
|
|
|
|
|
2014-11-21 19:18:52 +03:00
|
|
|
static void pc_machine_set_vmport(Object *obj, Visitor *v, void *opaque,
|
|
|
|
const char *name, Error **errp)
|
2014-10-04 01:33:37 +04:00
|
|
|
{
|
|
|
|
PCMachineState *pcms = PC_MACHINE(obj);
|
|
|
|
|
2014-11-21 19:18:52 +03:00
|
|
|
visit_type_OnOffAuto(v, &pcms->vmport, name, errp);
|
2014-10-04 01:33:37 +04:00
|
|
|
}
|
|
|
|
|
2015-06-18 19:30:52 +03:00
|
|
|
bool pc_machine_is_smm_enabled(PCMachineState *pcms)
|
|
|
|
{
|
|
|
|
bool smm_available = false;
|
|
|
|
|
|
|
|
if (pcms->smm == ON_OFF_AUTO_OFF) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tcg_enabled() || qtest_enabled()) {
|
|
|
|
smm_available = true;
|
|
|
|
} else if (kvm_enabled()) {
|
|
|
|
smm_available = kvm_has_smm();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (smm_available) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pcms->smm == ON_OFF_AUTO_ON) {
|
|
|
|
error_report("System Management Mode not supported by this hypervisor.");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pc_machine_get_smm(Object *obj, Visitor *v, void *opaque,
|
|
|
|
const char *name, Error **errp)
|
|
|
|
{
|
|
|
|
PCMachineState *pcms = PC_MACHINE(obj);
|
|
|
|
OnOffAuto smm = pcms->smm;
|
|
|
|
|
|
|
|
visit_type_OnOffAuto(v, &smm, name, errp);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pc_machine_set_smm(Object *obj, Visitor *v, void *opaque,
|
|
|
|
const char *name, Error **errp)
|
|
|
|
{
|
|
|
|
PCMachineState *pcms = PC_MACHINE(obj);
|
|
|
|
|
|
|
|
visit_type_OnOffAuto(v, &pcms->smm, name, errp);
|
|
|
|
}
|
|
|
|
|
nvdimm acpi: build ACPI NFIT table
NFIT is defined in ACPI 6.0: 5.2.25 NVDIMM Firmware Interface Table (NFIT)
Currently, we only support PMEM mode. Each device has 3 structures:
- SPA structure, defines the PMEM region info
- MEM DEV structure, it has the @handle which is used to associate specified
ACPI NVDIMM device we will introduce in later patch.
Also we can happily ignored the memory device's interleave, the real
nvdimm hardware access is hidden behind host
- DCR structure, it defines vendor ID used to associate specified vendor
nvdimm driver. Since we only implement PMEM mode this time, Command
window and Data window are not needed
The NVDIMM functionality is controlled by the parameter, 'nvdimm', which
is introduced for the machine, there is a example to enable it:
-machine pc,nvdimm -m 8G,maxmem=100G,slots=100 -object \
memory-backend-file,id=mem1,share,mem-path=/tmp/nvdimm1,size=10G -device \
nvdimm,memdev=mem1,id=nv1
It is disabled on default
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Xiao Guangrong <guangrong.xiao@linux.intel.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2015-12-02 10:20:58 +03:00
|
|
|
static bool pc_machine_get_nvdimm(Object *obj, Error **errp)
|
|
|
|
{
|
|
|
|
PCMachineState *pcms = PC_MACHINE(obj);
|
|
|
|
|
|
|
|
return pcms->nvdimm;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pc_machine_set_nvdimm(Object *obj, bool value, Error **errp)
|
|
|
|
{
|
|
|
|
PCMachineState *pcms = PC_MACHINE(obj);
|
|
|
|
|
|
|
|
pcms->nvdimm = value;
|
|
|
|
}
|
|
|
|
|
2014-06-02 17:25:27 +04:00
|
|
|
static void pc_machine_initfn(Object *obj)
|
|
|
|
{
|
2014-06-20 05:40:25 +04:00
|
|
|
PCMachineState *pcms = PC_MACHINE(obj);
|
|
|
|
|
2014-06-02 17:25:27 +04:00
|
|
|
object_property_add(obj, PC_MACHINE_MEMHP_REGION_SIZE, "int",
|
|
|
|
pc_machine_get_hotplug_memory_region_size,
|
2015-08-07 22:55:44 +03:00
|
|
|
NULL, NULL, NULL, &error_abort);
|
2014-12-16 19:58:05 +03:00
|
|
|
|
2014-06-20 05:40:25 +04:00
|
|
|
pcms->max_ram_below_4g = 1ULL << 32; /* 4G */
|
|
|
|
object_property_add(obj, PC_MACHINE_MAX_RAM_BELOW_4G, "size",
|
|
|
|
pc_machine_get_max_ram_below_4g,
|
|
|
|
pc_machine_set_max_ram_below_4g,
|
2015-08-07 22:55:44 +03:00
|
|
|
NULL, NULL, &error_abort);
|
2014-12-16 19:58:05 +03:00
|
|
|
object_property_set_description(obj, PC_MACHINE_MAX_RAM_BELOW_4G,
|
|
|
|
"Maximum ram below the 4G boundary (32bit boundary)",
|
2015-08-07 22:55:44 +03:00
|
|
|
&error_abort);
|
2014-10-31 19:38:39 +03:00
|
|
|
|
2015-06-18 19:30:52 +03:00
|
|
|
pcms->smm = ON_OFF_AUTO_AUTO;
|
|
|
|
object_property_add(obj, PC_MACHINE_SMM, "OnOffAuto",
|
|
|
|
pc_machine_get_smm,
|
|
|
|
pc_machine_set_smm,
|
2015-08-07 22:55:44 +03:00
|
|
|
NULL, NULL, &error_abort);
|
2015-06-18 19:30:52 +03:00
|
|
|
object_property_set_description(obj, PC_MACHINE_SMM,
|
|
|
|
"Enable SMM (pc & q35)",
|
2015-08-07 22:55:44 +03:00
|
|
|
&error_abort);
|
2015-06-18 19:30:52 +03:00
|
|
|
|
2014-11-21 19:18:52 +03:00
|
|
|
pcms->vmport = ON_OFF_AUTO_AUTO;
|
|
|
|
object_property_add(obj, PC_MACHINE_VMPORT, "OnOffAuto",
|
|
|
|
pc_machine_get_vmport,
|
|
|
|
pc_machine_set_vmport,
|
2015-08-07 22:55:44 +03:00
|
|
|
NULL, NULL, &error_abort);
|
2014-12-16 19:58:05 +03:00
|
|
|
object_property_set_description(obj, PC_MACHINE_VMPORT,
|
|
|
|
"Enable vmport (pc & q35)",
|
2015-08-07 22:55:44 +03:00
|
|
|
&error_abort);
|
nvdimm acpi: build ACPI NFIT table
NFIT is defined in ACPI 6.0: 5.2.25 NVDIMM Firmware Interface Table (NFIT)
Currently, we only support PMEM mode. Each device has 3 structures:
- SPA structure, defines the PMEM region info
- MEM DEV structure, it has the @handle which is used to associate specified
ACPI NVDIMM device we will introduce in later patch.
Also we can happily ignored the memory device's interleave, the real
nvdimm hardware access is hidden behind host
- DCR structure, it defines vendor ID used to associate specified vendor
nvdimm driver. Since we only implement PMEM mode this time, Command
window and Data window are not needed
The NVDIMM functionality is controlled by the parameter, 'nvdimm', which
is introduced for the machine, there is a example to enable it:
-machine pc,nvdimm -m 8G,maxmem=100G,slots=100 -object \
memory-backend-file,id=mem1,share,mem-path=/tmp/nvdimm1,size=10G -device \
nvdimm,memdev=mem1,id=nv1
It is disabled on default
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Xiao Guangrong <guangrong.xiao@linux.intel.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2015-12-02 10:20:58 +03:00
|
|
|
|
|
|
|
/* nvdimm is disabled on default. */
|
|
|
|
pcms->nvdimm = false;
|
|
|
|
object_property_add_bool(obj, PC_MACHINE_NVDIMM, pc_machine_get_nvdimm,
|
|
|
|
pc_machine_set_nvdimm, &error_abort);
|
2014-06-02 17:25:27 +04:00
|
|
|
}
|
|
|
|
|
2015-09-16 12:19:13 +03:00
|
|
|
static void pc_machine_reset(void)
|
|
|
|
{
|
|
|
|
CPUState *cs;
|
|
|
|
X86CPU *cpu;
|
|
|
|
|
|
|
|
qemu_devices_reset();
|
|
|
|
|
|
|
|
/* Reset APIC after devices have been reset to cancel
|
|
|
|
* any changes that qemu_devices_reset() might have done.
|
|
|
|
*/
|
|
|
|
CPU_FOREACH(cs) {
|
|
|
|
cpu = X86_CPU(cs);
|
|
|
|
|
|
|
|
if (cpu->apic_state) {
|
|
|
|
device_reset(cpu->apic_state);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
pc: fix default VCPU to NUMA node mapping
Since commit
dd0247e0 pc: acpi: mark all possible CPUs as enabled in SRAT
Linux kernel actually tries to use CPU to Node mapping from
QEMU provided SRAT table instead of discarding it, and that
in some cases breaks build_sched_domains() which expects
sane mapping where cores/threads belonging to the same socket
are on the same NUMA node.
With current default round-robin mapping of VCPUs to nodes
guest ends-up with cores/threads belonging to the same socket
being on different NUMA nodes.
For example with following CLI:
qemu-system-x86_64 -m 4G \
-cpu Opteron_G3,vendor=AuthenticAMD \
-smp 5,sockets=1,cores=4,threads=1,maxcpus=8 \
-numa node,nodeid=0 -numa node,nodeid=1
2.6.32 based kernels will hang on boot due to incorrectly built
sched_group-s list in update_sd_lb_stats()
Replacing default mapping with a manual, where VCPUs belonging to
the same socket are on the same NUMA node, fixes the issue for
guests which can't handle nonsense topology i.e. changing CLI to:
-numa node,nodeid=0,cpus=0-3 -numa node,nodeid=1,cpus=4-7
So instead of simply scattering VCPUs around nodes, provide
callback to map the same socket VCPUs to the same NUMA node,
which is what guests would expect from a sane hardware/BIOS.
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
Reviewed-by: Andreas Färber <afaerber@suse.de>
Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
2015-03-19 20:09:22 +03:00
|
|
|
static unsigned pc_cpu_index_to_socket_id(unsigned cpu_index)
|
|
|
|
{
|
2015-08-21 12:34:45 +03:00
|
|
|
X86CPUTopoInfo topo;
|
pc: fix default VCPU to NUMA node mapping
Since commit
dd0247e0 pc: acpi: mark all possible CPUs as enabled in SRAT
Linux kernel actually tries to use CPU to Node mapping from
QEMU provided SRAT table instead of discarding it, and that
in some cases breaks build_sched_domains() which expects
sane mapping where cores/threads belonging to the same socket
are on the same NUMA node.
With current default round-robin mapping of VCPUs to nodes
guest ends-up with cores/threads belonging to the same socket
being on different NUMA nodes.
For example with following CLI:
qemu-system-x86_64 -m 4G \
-cpu Opteron_G3,vendor=AuthenticAMD \
-smp 5,sockets=1,cores=4,threads=1,maxcpus=8 \
-numa node,nodeid=0 -numa node,nodeid=1
2.6.32 based kernels will hang on boot due to incorrectly built
sched_group-s list in update_sd_lb_stats()
Replacing default mapping with a manual, where VCPUs belonging to
the same socket are on the same NUMA node, fixes the issue for
guests which can't handle nonsense topology i.e. changing CLI to:
-numa node,nodeid=0,cpus=0-3 -numa node,nodeid=1,cpus=4-7
So instead of simply scattering VCPUs around nodes, provide
callback to map the same socket VCPUs to the same NUMA node,
which is what guests would expect from a sane hardware/BIOS.
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
Reviewed-by: Andreas Färber <afaerber@suse.de>
Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
2015-03-19 20:09:22 +03:00
|
|
|
x86_topo_ids_from_idx(smp_cores, smp_threads, cpu_index,
|
2015-08-21 12:34:45 +03:00
|
|
|
&topo);
|
|
|
|
return topo.pkg_id;
|
pc: fix default VCPU to NUMA node mapping
Since commit
dd0247e0 pc: acpi: mark all possible CPUs as enabled in SRAT
Linux kernel actually tries to use CPU to Node mapping from
QEMU provided SRAT table instead of discarding it, and that
in some cases breaks build_sched_domains() which expects
sane mapping where cores/threads belonging to the same socket
are on the same NUMA node.
With current default round-robin mapping of VCPUs to nodes
guest ends-up with cores/threads belonging to the same socket
being on different NUMA nodes.
For example with following CLI:
qemu-system-x86_64 -m 4G \
-cpu Opteron_G3,vendor=AuthenticAMD \
-smp 5,sockets=1,cores=4,threads=1,maxcpus=8 \
-numa node,nodeid=0 -numa node,nodeid=1
2.6.32 based kernels will hang on boot due to incorrectly built
sched_group-s list in update_sd_lb_stats()
Replacing default mapping with a manual, where VCPUs belonging to
the same socket are on the same NUMA node, fixes the issue for
guests which can't handle nonsense topology i.e. changing CLI to:
-numa node,nodeid=0,cpus=0-3 -numa node,nodeid=1,cpus=4-7
So instead of simply scattering VCPUs around nodes, provide
callback to map the same socket VCPUs to the same NUMA node,
which is what guests would expect from a sane hardware/BIOS.
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
Reviewed-by: Andreas Färber <afaerber@suse.de>
Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
2015-03-19 20:09:22 +03:00
|
|
|
}
|
|
|
|
|
2014-06-02 17:25:12 +04:00
|
|
|
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);
|
|
|
|
|
|
|
|
pcmc->get_hotplug_handler = mc->get_hotplug_handler;
|
2015-12-02 01:58:03 +03:00
|
|
|
pcmc->pci_enabled = true;
|
|
|
|
pcmc->has_acpi_build = true;
|
|
|
|
pcmc->rsdp_in_ram = true;
|
|
|
|
pcmc->smbios_defaults = true;
|
|
|
|
pcmc->smbios_uuid_encoded = true;
|
|
|
|
pcmc->gigabyte_align = true;
|
|
|
|
pcmc->has_reserved_memory = true;
|
|
|
|
pcmc->kvmclock_enabled = true;
|
2015-12-02 01:58:06 +03:00
|
|
|
pcmc->enforce_aligned_dimm = true;
|
2015-12-02 01:58:05 +03:00
|
|
|
/* BIOS ACPI tables: 128K. Other BIOS datastructures: less than 4K reported
|
|
|
|
* to be used at the moment, 32K should be enough for a while. */
|
|
|
|
pcmc->acpi_data_size = 0x20000 + 0x8000;
|
2014-06-02 17:25:12 +04:00
|
|
|
mc->get_hotplug_handler = pc_get_hotpug_handler;
|
pc: fix default VCPU to NUMA node mapping
Since commit
dd0247e0 pc: acpi: mark all possible CPUs as enabled in SRAT
Linux kernel actually tries to use CPU to Node mapping from
QEMU provided SRAT table instead of discarding it, and that
in some cases breaks build_sched_domains() which expects
sane mapping where cores/threads belonging to the same socket
are on the same NUMA node.
With current default round-robin mapping of VCPUs to nodes
guest ends-up with cores/threads belonging to the same socket
being on different NUMA nodes.
For example with following CLI:
qemu-system-x86_64 -m 4G \
-cpu Opteron_G3,vendor=AuthenticAMD \
-smp 5,sockets=1,cores=4,threads=1,maxcpus=8 \
-numa node,nodeid=0 -numa node,nodeid=1
2.6.32 based kernels will hang on boot due to incorrectly built
sched_group-s list in update_sd_lb_stats()
Replacing default mapping with a manual, where VCPUs belonging to
the same socket are on the same NUMA node, fixes the issue for
guests which can't handle nonsense topology i.e. changing CLI to:
-numa node,nodeid=0,cpus=0-3 -numa node,nodeid=1,cpus=4-7
So instead of simply scattering VCPUs around nodes, provide
callback to map the same socket VCPUs to the same NUMA node,
which is what guests would expect from a sane hardware/BIOS.
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
Reviewed-by: Andreas Färber <afaerber@suse.de>
Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
2015-03-19 20:09:22 +03:00
|
|
|
mc->cpu_index_to_socket_id = pc_cpu_index_to_socket_id;
|
2015-08-07 22:55:47 +03:00
|
|
|
mc->default_boot_order = "cad";
|
2015-08-07 22:55:48 +03:00
|
|
|
mc->hot_add_cpu = pc_hot_add_cpu;
|
|
|
|
mc->max_cpus = 255;
|
2015-09-16 12:19:13 +03:00
|
|
|
mc->reset = pc_machine_reset;
|
2014-06-02 17:25:12 +04:00
|
|
|
hc->plug = pc_machine_device_plug_cb;
|
2015-01-28 10:45:37 +03:00
|
|
|
hc->unplug_request = pc_machine_device_unplug_request_cb;
|
2015-01-28 10:45:39 +03:00
|
|
|
hc->unplug = pc_machine_device_unplug_cb;
|
2014-06-02 17:25:12 +04:00
|
|
|
}
|
|
|
|
|
2014-06-02 17:24:57 +04:00
|
|
|
static const TypeInfo pc_machine_info = {
|
|
|
|
.name = TYPE_PC_MACHINE,
|
|
|
|
.parent = TYPE_MACHINE,
|
|
|
|
.abstract = true,
|
|
|
|
.instance_size = sizeof(PCMachineState),
|
2014-06-02 17:25:27 +04:00
|
|
|
.instance_init = pc_machine_initfn,
|
2014-06-02 17:24:57 +04:00
|
|
|
.class_size = sizeof(PCMachineClass),
|
2014-06-02 17:25:12 +04:00
|
|
|
.class_init = pc_machine_class_init,
|
|
|
|
.interfaces = (InterfaceInfo[]) {
|
|
|
|
{ TYPE_HOTPLUG_HANDLER },
|
|
|
|
{ }
|
|
|
|
},
|
2014-06-02 17:24:57 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
static void pc_machine_register_types(void)
|
|
|
|
{
|
|
|
|
type_register_static(&pc_machine_info);
|
|
|
|
}
|
|
|
|
|
|
|
|
type_init(pc_machine_register_types)
|