From 3e16d14fd93ca6059134ba6b4f65c1c3e4cd3a18 Mon Sep 17 00:00:00 2001 From: Laszlo Ersek Date: Tue, 17 Dec 2013 01:37:06 +0100 Subject: Python-lang gdb script to extract x86_64 guest vmcore from qemu coredump When qemu dies unexpectedly, for example in response to an explicit abort() call, or (more importantly) when an external signal is delivered to it that results in a coredump, sometimes it is useful to extract the guest vmcore from the qemu process' memory image. The guest vmcore might help understand an emulation problem in qemu, or help debug the guest. This script reimplements (and cuts many features of) the qmp_dump_guest_memory() command in gdb/Python, https://sourceware.org/gdb/current/onlinedocs/gdb/Python-API.html working off the saved memory image of the qemu process. The docstring in the patch (serving as gdb help text) describes the limitations relative to the QMP command. Dependencies of qmp_dump_guest_memory() have been reimplemented as needed. I sought to follow the general structure, sticking to original function names where possible. However, keeping it simple prevailed in some places. The patch has been tested with a 4 VCPU, 768 MB, RHEL-6.4 (2.6.32-358.el6.x86_64) guest: - The script printed > guest RAM blocks: > target_start target_end host_addr message count > ---------------- ---------------- ---------------- ------- ----- > 0000000000000000 00000000000a0000 00007f95d0000000 added 1 > 00000000000a0000 00000000000b0000 00007f960ac00000 added 2 > 00000000000c0000 00000000000ca000 00007f95d00c0000 added 3 > 00000000000ca000 00000000000cd000 00007f95d00ca000 joined 3 > 00000000000cd000 00000000000d0000 00007f95d00cd000 joined 3 > 00000000000d0000 00000000000f0000 00007f95d00d0000 joined 3 > 00000000000f0000 0000000000100000 00007f95d00f0000 joined 3 > 0000000000100000 0000000030000000 00007f95d0100000 joined 3 > 00000000fc000000 00000000fc800000 00007f960ac00000 added 4 > 00000000fffe0000 0000000100000000 00007f9618800000 added 5 > dumping range at 00007f95d0000000 for length 00000000000a0000 > dumping range at 00007f960ac00000 for length 0000000000010000 > dumping range at 00007f95d00c0000 for length 000000002ff40000 > dumping range at 00007f960ac00000 for length 0000000000800000 > dumping range at 00007f9618800000 for length 0000000000020000 - The vmcore was checked with "readelf", comparing the results against a vmcore written by qmp_dump_guest_memory(): > --- theirs 2013-09-12 17:38:59.797289404 +0200 > +++ mine 2013-09-12 17:39:03.820289404 +0200 > @@ -27,16 +27,16 @@ > Type Offset VirtAddr PhysAddr > FileSiz MemSiz Flags Align > NOTE 0x0000000000000190 0x0000000000000000 0x0000000000000000 > - 0x0000000000000ca0 0x0000000000000ca0 0 > - LOAD 0x0000000000000e30 0x0000000000000000 0x0000000000000000 > + 0x000000000000001c 0x000000000000001c 0 > + LOAD 0x00000000000001ac 0x0000000000000000 0x0000000000000000 > 0x00000000000a0000 0x00000000000a0000 0 > - LOAD 0x00000000000a0e30 0x0000000000000000 0x00000000000a0000 > + LOAD 0x00000000000a01ac 0x0000000000000000 0x00000000000a0000 > 0x0000000000010000 0x0000000000010000 0 > - LOAD 0x00000000000b0e30 0x0000000000000000 0x00000000000c0000 > + LOAD 0x00000000000b01ac 0x0000000000000000 0x00000000000c0000 > 0x000000002ff40000 0x000000002ff40000 0 > - LOAD 0x000000002fff0e30 0x0000000000000000 0x00000000fc000000 > + LOAD 0x000000002fff01ac 0x0000000000000000 0x00000000fc000000 > 0x0000000000800000 0x0000000000800000 0 > - LOAD 0x00000000307f0e30 0x0000000000000000 0x00000000fffe0000 > + LOAD 0x00000000307f01ac 0x0000000000000000 0x00000000fffe0000 > 0x0000000000020000 0x0000000000020000 0 > > There is no dynamic section in this file. > @@ -47,13 +47,6 @@ > > No version information found in this file. > > -Notes at offset 0x00000190 with length 0x00000ca0: > +Notes at offset 0x00000190 with length 0x0000001c: > Owner Data size Description > - CORE 0x00000150 NT_PRSTATUS (prstatus structure) > - CORE 0x00000150 NT_PRSTATUS (prstatus structure) > - CORE 0x00000150 NT_PRSTATUS (prstatus structure) > - CORE 0x00000150 NT_PRSTATUS (prstatus structure) > - QEMU 0x000001b0 Unknown note type: (0x00000000) > - QEMU 0x000001b0 Unknown note type: (0x00000000) > - QEMU 0x000001b0 Unknown note type: (0x00000000) > - QEMU 0x000001b0 Unknown note type: (0x00000000) > + NONE 0x00000005 Unknown note type: (0x00000000) - The vmcore was checked with "crash" too, again comparing the results against a vmcore written by qmp_dump_guest_memory(): > --- guest.vmcore.log2 2013-09-12 17:52:27.074289201 +0200 > +++ example.dump.log2 2013-09-12 17:52:15.904289203 +0200 > @@ -22,11 +22,11 @@ > This GDB was configured as "x86_64-unknown-linux-gnu"... > > KERNEL: /usr/lib/debug/lib/modules/2.6.32-358.el6.x86_64/vmlinux > - DUMPFILE: /home/lacos/tmp/guest.vmcore > + DUMPFILE: /home/lacos/tmp/example.dump > CPUS: 4 > - DATE: Thu Sep 12 17:16:11 2013 > - UPTIME: 00:01:09 > -LOAD AVERAGE: 0.07, 0.03, 0.00 > + DATE: Thu Sep 12 17:17:41 2013 > + UPTIME: 00:00:38 > +LOAD AVERAGE: 0.18, 0.05, 0.01 > TASKS: 130 > NODENAME: localhost.localdomain > RELEASE: 2.6.32-358.el6.x86_64 > @@ -38,12 +38,12 @@ > COMMAND: "swapper" > TASK: ffffffff81a8d020 (1 of 4) [THREAD_INFO: ffffffff81a00000] > CPU: 0 > - STATE: TASK_RUNNING (PANIC) > + STATE: TASK_RUNNING (ACTIVE) > + WARNING: panic task not found > > crash> bt > PID: 0 TASK: ffffffff81a8d020 CPU: 0 COMMAND: "swapper" > - #0 [ffffffff81a01ed0] default_idle at ffffffff8101495d > - #1 [ffffffff81a01ef0] cpu_idle at ffffffff81009fc6 > + #0 [ffffffff81a01ef0] cpu_idle at ffffffff81009fc6 > crash> task ffffffff81a8d020 > PID: 0 TASK: ffffffff81a8d020 CPU: 0 COMMAND: "swapper" > struct task_struct { > @@ -75,7 +75,7 @@ > prev = 0xffffffff81a8d080 > }, > on_rq = 0, > - exec_start = 8618466836, > + exec_start = 7469214014, > sum_exec_runtime = 0, > vruntime = 0, > prev_sum_exec_runtime = 0, > @@ -149,7 +149,7 @@ > }, > tasks = { > next = 0xffff88002d621948, > - prev = 0xffff880029618f28 > + prev = 0xffff880023b74488 > }, > pushable_tasks = { > prio = 140, > @@ -165,7 +165,7 @@ > } > }, > mm = 0x0, > - active_mm = 0xffff88002929b780, > + active_mm = 0xffff8800297eb980, > exit_state = 0, > exit_code = 0, > exit_signal = 0, > @@ -177,7 +177,7 @@ > sched_reset_on_fork = 0, > pid = 0, > tgid = 0, > - stack_canary = 2483693585637059287, > + stack_canary = 7266362296181431986, > real_parent = 0xffffffff81a8d020, > parent = 0xffffffff81a8d020, > children = { > @@ -224,14 +224,14 @@ > set_child_tid = 0x0, > clear_child_tid = 0x0, > utime = 0, > - stime = 3, > + stime = 2, > utimescaled = 0, > - stimescaled = 3, > + stimescaled = 2, > gtime = 0, > prev_utime = 0, > prev_stime = 0, > nvcsw = 0, > - nivcsw = 1000, > + nivcsw = 1764, > start_time = { > tv_sec = 0, > tv_nsec = 0 - I asked for Dave Anderson's help with verifying the extracted vmcore, and his comments make me think I should post this. Signed-off-by: Laszlo Ersek Signed-off-by: Michael S. Tsirkin --- scripts/dump-guest-memory.py | 339 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 339 insertions(+) create mode 100644 scripts/dump-guest-memory.py diff --git a/scripts/dump-guest-memory.py b/scripts/dump-guest-memory.py new file mode 100644 index 0000000000..1ed8b67883 --- /dev/null +++ b/scripts/dump-guest-memory.py @@ -0,0 +1,339 @@ +# This python script adds a new gdb command, "dump-guest-memory". It +# should be loaded with "source dump-guest-memory.py" at the (gdb) +# prompt. +# +# Copyright (C) 2013, Red Hat, Inc. +# +# Authors: +# Laszlo Ersek +# +# This work is licensed under the terms of the GNU GPL, version 2 or later. See +# the COPYING file in the top-level directory. +# +# The leading docstring doesn't have idiomatic Python formatting. It is +# printed by gdb's "help" command (the first line is printed in the +# "help data" summary), and it should match how other help texts look in +# gdb. + +import struct + +class DumpGuestMemory(gdb.Command): + """Extract guest vmcore from qemu process coredump. + +The sole argument is FILE, identifying the target file to write the +guest vmcore to. + +This GDB command reimplements the dump-guest-memory QMP command in +python, using the representation of guest memory as captured in the qemu +coredump. The qemu process that has been dumped must have had the +command line option "-machine dump-guest-core=on". + +For simplicity, the "paging", "begin" and "end" parameters of the QMP +command are not supported -- no attempt is made to get the guest's +internal paging structures (ie. paging=false is hard-wired), and guest +memory is always fully dumped. + +Only x86_64 guests are supported. + +The CORE/NT_PRSTATUS and QEMU notes (that is, the VCPUs' statuses) are +not written to the vmcore. Preparing these would require context that is +only present in the KVM host kernel module when the guest is alive. A +fake ELF note is written instead, only to keep the ELF parser of "crash" +happy. + +Dependent on how busted the qemu process was at the time of the +coredump, this command might produce unpredictable results. If qemu +deliberately called abort(), or it was dumped in response to a signal at +a halfway fortunate point, then its coredump should be in reasonable +shape and this command should mostly work.""" + + TARGET_PAGE_SIZE = 0x1000 + TARGET_PAGE_MASK = 0xFFFFFFFFFFFFF000 + + # Various ELF constants + EM_X86_64 = 62 # AMD x86-64 target machine + ELFDATA2LSB = 1 # little endian + ELFCLASS64 = 2 + ELFMAG = "\x7FELF" + EV_CURRENT = 1 + ET_CORE = 4 + PT_LOAD = 1 + PT_NOTE = 4 + + # Special value for e_phnum. This indicates that the real number of + # program headers is too large to fit into e_phnum. Instead the real + # value is in the field sh_info of section 0. + PN_XNUM = 0xFFFF + + # Format strings for packing and header size calculation. + ELF64_EHDR = ("4s" # e_ident/magic + "B" # e_ident/class + "B" # e_ident/data + "B" # e_ident/version + "B" # e_ident/osabi + "8s" # e_ident/pad + "H" # e_type + "H" # e_machine + "I" # e_version + "Q" # e_entry + "Q" # e_phoff + "Q" # e_shoff + "I" # e_flags + "H" # e_ehsize + "H" # e_phentsize + "H" # e_phnum + "H" # e_shentsize + "H" # e_shnum + "H" # e_shstrndx + ) + ELF64_PHDR = ("I" # p_type + "I" # p_flags + "Q" # p_offset + "Q" # p_vaddr + "Q" # p_paddr + "Q" # p_filesz + "Q" # p_memsz + "Q" # p_align + ) + + def __init__(self): + super(DumpGuestMemory, self).__init__("dump-guest-memory", + gdb.COMMAND_DATA, + gdb.COMPLETE_FILENAME) + self.uintptr_t = gdb.lookup_type("uintptr_t") + self.elf64_ehdr_le = struct.Struct("<%s" % self.ELF64_EHDR) + self.elf64_phdr_le = struct.Struct("<%s" % self.ELF64_PHDR) + + def int128_get64(self, val): + assert (val["hi"] == 0) + return val["lo"] + + def qtailq_foreach(self, head, field_str): + var_p = head["tqh_first"] + while (var_p != 0): + var = var_p.dereference() + yield var + var_p = var[field_str]["tqe_next"] + + def qemu_get_ram_block(self, ram_addr): + ram_blocks = gdb.parse_and_eval("ram_list.blocks") + for block in self.qtailq_foreach(ram_blocks, "next"): + if (ram_addr - block["offset"] < block["length"]): + return block + raise gdb.GdbError("Bad ram offset %x" % ram_addr) + + def qemu_get_ram_ptr(self, ram_addr): + block = self.qemu_get_ram_block(ram_addr) + return block["host"] + (ram_addr - block["offset"]) + + def memory_region_get_ram_ptr(self, mr): + if (mr["alias"] != 0): + return (self.memory_region_get_ram_ptr(mr["alias"].dereference()) + + mr["alias_offset"]) + return self.qemu_get_ram_ptr(mr["ram_addr"] & self.TARGET_PAGE_MASK) + + def guest_phys_blocks_init(self): + self.guest_phys_blocks = [] + + def guest_phys_blocks_append(self): + print "guest RAM blocks:" + print ("target_start target_end host_addr message " + "count") + print ("---------------- ---------------- ---------------- ------- " + "-----") + + current_map_p = gdb.parse_and_eval("address_space_memory.current_map") + current_map = current_map_p.dereference() + for cur in range(current_map["nr"]): + flat_range = (current_map["ranges"] + cur).dereference() + mr = flat_range["mr"].dereference() + + # we only care about RAM + if (not mr["ram"]): + continue + + section_size = self.int128_get64(flat_range["addr"]["size"]) + target_start = self.int128_get64(flat_range["addr"]["start"]) + target_end = target_start + section_size + host_addr = (self.memory_region_get_ram_ptr(mr) + + flat_range["offset_in_region"]) + predecessor = None + + # find continuity in guest physical address space + if (len(self.guest_phys_blocks) > 0): + predecessor = self.guest_phys_blocks[-1] + predecessor_size = (predecessor["target_end"] - + predecessor["target_start"]) + + # the memory API guarantees monotonically increasing + # traversal + assert (predecessor["target_end"] <= target_start) + + # we want continuity in both guest-physical and + # host-virtual memory + if (predecessor["target_end"] < target_start or + predecessor["host_addr"] + predecessor_size != host_addr): + predecessor = None + + if (predecessor is None): + # isolated mapping, add it to the list + self.guest_phys_blocks.append({"target_start": target_start, + "target_end" : target_end, + "host_addr" : host_addr}) + message = "added" + else: + # expand predecessor until @target_end; predecessor's + # start doesn't change + predecessor["target_end"] = target_end + message = "joined" + + print ("%016x %016x %016x %-7s %5u" % + (target_start, target_end, host_addr.cast(self.uintptr_t), + message, len(self.guest_phys_blocks))) + + def cpu_get_dump_info(self): + # We can't synchronize the registers with KVM post-mortem, and + # the bits in (first_x86_cpu->env.hflags) seem to be stale; they + # may not reflect long mode for example. Hence just assume the + # most common values. This also means that instruction pointer + # etc. will be bogus in the dump, but at least the RAM contents + # should be valid. + self.dump_info = {"d_machine": self.EM_X86_64, + "d_endian" : self.ELFDATA2LSB, + "d_class" : self.ELFCLASS64} + + def encode_elf64_ehdr_le(self): + return self.elf64_ehdr_le.pack( + self.ELFMAG, # e_ident/magic + self.dump_info["d_class"], # e_ident/class + self.dump_info["d_endian"], # e_ident/data + self.EV_CURRENT, # e_ident/version + 0, # e_ident/osabi + "", # e_ident/pad + self.ET_CORE, # e_type + self.dump_info["d_machine"], # e_machine + self.EV_CURRENT, # e_version + 0, # e_entry + self.elf64_ehdr_le.size, # e_phoff + 0, # e_shoff + 0, # e_flags + self.elf64_ehdr_le.size, # e_ehsize + self.elf64_phdr_le.size, # e_phentsize + self.phdr_num, # e_phnum + 0, # e_shentsize + 0, # e_shnum + 0 # e_shstrndx + ) + + def encode_elf64_note_le(self): + return self.elf64_phdr_le.pack(self.PT_NOTE, # p_type + 0, # p_flags + (self.memory_offset - + len(self.note)), # p_offset + 0, # p_vaddr + 0, # p_paddr + len(self.note), # p_filesz + len(self.note), # p_memsz + 0 # p_align + ) + + def encode_elf64_load_le(self, offset, start_hwaddr, range_size): + return self.elf64_phdr_le.pack(self.PT_LOAD, # p_type + 0, # p_flags + offset, # p_offset + 0, # p_vaddr + start_hwaddr, # p_paddr + range_size, # p_filesz + range_size, # p_memsz + 0 # p_align + ) + + def note_init(self, name, desc, type): + # name must include a trailing NUL + namesz = (len(name) + 1 + 3) / 4 * 4 + descsz = (len(desc) + 3) / 4 * 4 + fmt = ("<" # little endian + "I" # n_namesz + "I" # n_descsz + "I" # n_type + "%us" # name + "%us" # desc + % (namesz, descsz)) + self.note = struct.pack(fmt, + len(name) + 1, len(desc), type, name, desc) + + def dump_init(self): + self.guest_phys_blocks_init() + self.guest_phys_blocks_append() + self.cpu_get_dump_info() + # we have no way to retrieve the VCPU status from KVM + # post-mortem + self.note_init("NONE", "EMPTY", 0) + + # Account for PT_NOTE. + self.phdr_num = 1 + + # We should never reach PN_XNUM for paging=false dumps: there's + # just a handful of discontiguous ranges after merging. + self.phdr_num += len(self.guest_phys_blocks) + assert (self.phdr_num < self.PN_XNUM) + + # Calculate the ELF file offset where the memory dump commences: + # + # ELF header + # PT_NOTE + # PT_LOAD: 1 + # PT_LOAD: 2 + # ... + # PT_LOAD: len(self.guest_phys_blocks) + # ELF note + # memory dump + self.memory_offset = (self.elf64_ehdr_le.size + + self.elf64_phdr_le.size * self.phdr_num + + len(self.note)) + + def dump_begin(self, vmcore): + vmcore.write(self.encode_elf64_ehdr_le()) + vmcore.write(self.encode_elf64_note_le()) + running = self.memory_offset + for block in self.guest_phys_blocks: + range_size = block["target_end"] - block["target_start"] + vmcore.write(self.encode_elf64_load_le(running, + block["target_start"], + range_size)) + running += range_size + vmcore.write(self.note) + + def dump_iterate(self, vmcore): + qemu_core = gdb.inferiors()[0] + for block in self.guest_phys_blocks: + cur = block["host_addr"] + left = block["target_end"] - block["target_start"] + print ("dumping range at %016x for length %016x" % + (cur.cast(self.uintptr_t), left)) + while (left > 0): + chunk_size = min(self.TARGET_PAGE_SIZE, left) + chunk = qemu_core.read_memory(cur, chunk_size) + vmcore.write(chunk) + cur += chunk_size + left -= chunk_size + + def create_vmcore(self, filename): + vmcore = open(filename, "wb") + self.dump_begin(vmcore) + self.dump_iterate(vmcore) + vmcore.close() + + def invoke(self, args, from_tty): + # Unwittingly pressing the Enter key after the command should + # not dump the same multi-gig coredump to the same file. + self.dont_repeat() + + argv = gdb.string_to_argv(args) + if (len(argv) != 1): + raise gdb.GdbError("usage: dump-guest-memory FILE") + + self.dump_init() + self.create_vmcore(argv[0]) + +DumpGuestMemory() -- cgit v1.2.3 From 15bce1b7c55c69f47e13c9eb2a4b80f41da26581 Mon Sep 17 00:00:00 2001 From: "Gabriel L. Somlo" Date: Sun, 22 Dec 2013 10:34:56 -0500 Subject: Add DSDT node for AppleSMC AppleSMC (-device isa-applesmc) is required to boot OS X guests. OS X expects a SMC node to be present in the ACPI DSDT. This patch adds a SMC node to the DSDT, and dynamically patches the return value of SMC._STA to either 0x0B if the chip is present, or otherwise to 0x00, before booting the guest. Signed-off-by: Gabriel Somlo Signed-off-by: Michael S. Tsirkin --- hw/i386/acpi-build.c | 9 +++++++++ hw/i386/acpi-dsdt-isa.dsl | 11 +++++++++++ hw/i386/acpi-dsdt.dsl | 1 + hw/i386/q35-acpi-dsdt.dsl | 1 + hw/misc/applesmc.c | 1 - include/hw/isa/isa.h | 7 +++++++ 6 files changed, 29 insertions(+), 1 deletion(-) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 48312f5a83..30bfcd2b4a 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -36,6 +36,7 @@ #include "hw/nvram/fw_cfg.h" #include "bios-linker-loader.h" #include "hw/loader.h" +#include "hw/isa/isa.h" /* Supported chipsets: */ #include "hw/acpi/piix4.h" @@ -80,6 +81,7 @@ typedef struct AcpiMiscInfo { static void acpi_get_dsdt(AcpiMiscInfo *info) { + unsigned short applesmc_sta_val, *applesmc_sta_off; Object *piix = piix4_pm_find(); Object *lpc = ich9_lpc_find(); assert(!!piix != !!lpc); @@ -87,11 +89,18 @@ static void acpi_get_dsdt(AcpiMiscInfo *info) if (piix) { info->dsdt_code = AcpiDsdtAmlCode; info->dsdt_size = sizeof AcpiDsdtAmlCode; + applesmc_sta_off = piix_dsdt_applesmc_sta; } if (lpc) { info->dsdt_code = Q35AcpiDsdtAmlCode; info->dsdt_size = sizeof Q35AcpiDsdtAmlCode; + applesmc_sta_off = q35_dsdt_applesmc_sta; } + + /* Patch in appropriate value for AppleSMC _STA */ + applesmc_sta_val = applesmc_find() ? 0x0b : 0x00; + *(uint16_t *)(info->dsdt_code + *applesmc_sta_off) = + cpu_to_le16(applesmc_sta_val); } static diff --git a/hw/i386/acpi-dsdt-isa.dsl b/hw/i386/acpi-dsdt-isa.dsl index 89caa1649d..46942c12aa 100644 --- a/hw/i386/acpi-dsdt-isa.dsl +++ b/hw/i386/acpi-dsdt-isa.dsl @@ -16,6 +16,17 @@ /* Common legacy ISA style devices. */ Scope(\_SB.PCI0.ISA) { + Device (SMC) { + Name(_HID, EisaId("APP0001")) + /* _STA will be patched to 0x0B if AppleSMC is present */ + ACPI_EXTRACT_NAME_WORD_CONST DSDT_APPLESMC_STA + Name(_STA, 0xFF00) + Name(_CRS, ResourceTemplate () { + IO (Decode16, 0x0300, 0x0300, 0x01, 0x20) + IRQNoFlags() { 6 } + }) + } + Device(RTC) { Name(_HID, EisaId("PNP0B00")) Name(_CRS, ResourceTemplate() { diff --git a/hw/i386/acpi-dsdt.dsl b/hw/i386/acpi-dsdt.dsl index a377424f39..b87c6e0ae8 100644 --- a/hw/i386/acpi-dsdt.dsl +++ b/hw/i386/acpi-dsdt.dsl @@ -114,6 +114,7 @@ DefinitionBlock ( } } +#define DSDT_APPLESMC_STA piix_dsdt_applesmc_sta #include "acpi-dsdt-isa.dsl" diff --git a/hw/i386/q35-acpi-dsdt.dsl b/hw/i386/q35-acpi-dsdt.dsl index 7934a9ddfb..ee38fd6580 100644 --- a/hw/i386/q35-acpi-dsdt.dsl +++ b/hw/i386/q35-acpi-dsdt.dsl @@ -171,6 +171,7 @@ DefinitionBlock ( } } +#define DSDT_APPLESMC_STA q35_dsdt_applesmc_sta #include "acpi-dsdt-isa.dsl" diff --git a/hw/misc/applesmc.c b/hw/misc/applesmc.c index 1e8d183e7f..627adb97c9 100644 --- a/hw/misc/applesmc.c +++ b/hw/misc/applesmc.c @@ -66,7 +66,6 @@ struct AppleSMCData { QLIST_ENTRY(AppleSMCData) node; }; -#define TYPE_APPLE_SMC "isa-applesmc" #define APPLE_SMC(obj) OBJECT_CHECK(AppleSMCState, (obj), TYPE_APPLE_SMC) typedef struct AppleSMCState AppleSMCState; diff --git a/include/hw/isa/isa.h b/include/hw/isa/isa.h index fa45a5b094..e0c749f9e9 100644 --- a/include/hw/isa/isa.h +++ b/include/hw/isa/isa.h @@ -20,6 +20,13 @@ #define TYPE_ISA_BUS "ISA" #define ISA_BUS(obj) OBJECT_CHECK(ISABus, (obj), TYPE_ISA_BUS) +#define TYPE_APPLE_SMC "isa-applesmc" + +static inline bool applesmc_find(void) +{ + return object_resolve_path_type("", TYPE_APPLE_SMC, NULL); +} + typedef struct ISADeviceClass { DeviceClass parent_class; } ISADeviceClass; -- cgit v1.2.3 From 8977557ae418f9b9ff5c35ee39161a33f2368e6a Mon Sep 17 00:00:00 2001 From: "Gabriel L. Somlo" Date: Mon, 13 Jan 2014 15:27:13 -0500 Subject: ACPI: Fix AppleSMC _STA size Minimize the storage used for AppleSMC's _STA (8bit), relying on ASL to implicitly convert it to the officially specified 32bit value. Signed-off-by: Gabriel Somlo Signed-off-by: Michael S. Tsirkin --- hw/i386/acpi-build.c | 11 +++++------ hw/i386/acpi-dsdt-isa.dsl | 4 ++-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 30bfcd2b4a..1d62866ec4 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -81,7 +81,7 @@ typedef struct AcpiMiscInfo { static void acpi_get_dsdt(AcpiMiscInfo *info) { - unsigned short applesmc_sta_val, *applesmc_sta_off; + uint16_t *applesmc_sta; Object *piix = piix4_pm_find(); Object *lpc = ich9_lpc_find(); assert(!!piix != !!lpc); @@ -89,18 +89,17 @@ static void acpi_get_dsdt(AcpiMiscInfo *info) if (piix) { info->dsdt_code = AcpiDsdtAmlCode; info->dsdt_size = sizeof AcpiDsdtAmlCode; - applesmc_sta_off = piix_dsdt_applesmc_sta; + applesmc_sta = piix_dsdt_applesmc_sta; } if (lpc) { info->dsdt_code = Q35AcpiDsdtAmlCode; info->dsdt_size = sizeof Q35AcpiDsdtAmlCode; - applesmc_sta_off = q35_dsdt_applesmc_sta; + applesmc_sta = q35_dsdt_applesmc_sta; } /* Patch in appropriate value for AppleSMC _STA */ - applesmc_sta_val = applesmc_find() ? 0x0b : 0x00; - *(uint16_t *)(info->dsdt_code + *applesmc_sta_off) = - cpu_to_le16(applesmc_sta_val); + *(uint8_t *)(info->dsdt_code + *applesmc_sta) = + applesmc_find() ? 0x0b : 0x00; } static diff --git a/hw/i386/acpi-dsdt-isa.dsl b/hw/i386/acpi-dsdt-isa.dsl index 46942c12aa..deb37de92e 100644 --- a/hw/i386/acpi-dsdt-isa.dsl +++ b/hw/i386/acpi-dsdt-isa.dsl @@ -19,8 +19,8 @@ Scope(\_SB.PCI0.ISA) { Device (SMC) { Name(_HID, EisaId("APP0001")) /* _STA will be patched to 0x0B if AppleSMC is present */ - ACPI_EXTRACT_NAME_WORD_CONST DSDT_APPLESMC_STA - Name(_STA, 0xFF00) + ACPI_EXTRACT_NAME_BYTE_CONST DSDT_APPLESMC_STA + Name(_STA, 0xF0) Name(_CRS, ResourceTemplate () { IO (Decode16, 0x0300, 0x0300, 0x01, 0x20) IRQNoFlags() { 6 } -- cgit v1.2.3 From 4dd72e04c2c4759782d0eb3e3aff6a36889c1e18 Mon Sep 17 00:00:00 2001 From: Stefan Weil Date: Sun, 22 Dec 2013 15:51:22 +0100 Subject: virtio: Fix return value for dummy function vhost_net_virtqueue_pending cgcc complains that -ENOSYS is not a good value for 'bool'. A dummy virtio will never have pending queue entries, so let us return false. Signed-off-by: Stefan Weil Signed-off-by: Michael S. Tsirkin --- hw/net/vhost_net.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index 006576db31..854997d9ba 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -321,7 +321,7 @@ void vhost_net_ack_features(struct vhost_net *net, unsigned features) bool vhost_net_virtqueue_pending(VHostNetState *net, int idx) { - return -ENOSYS; + return false; } void vhost_net_virtqueue_mask(VHostNetState *net, VirtIODevice *dev, -- cgit v1.2.3 From d259793808052dbef6d2d90cdb09150d03b8413b Mon Sep 17 00:00:00 2001 From: Marcel Apfelbaum Date: Thu, 26 Dec 2013 16:54:19 +0200 Subject: acpi unit-test: add test files Added unit-test's expected aml files to be compared with the actual ACPI tables. Signed-off-by: Marcel Apfelbaum Signed-off-by: Michael S. Tsirkin --- tests/acpi-test-data/pc/APIC | Bin 0 -> 120 bytes tests/acpi-test-data/pc/DSDT | Bin 0 -> 4407 bytes tests/acpi-test-data/pc/FACP | Bin 0 -> 116 bytes tests/acpi-test-data/pc/FACS | Bin 0 -> 64 bytes tests/acpi-test-data/pc/HPET | Bin 0 -> 56 bytes tests/acpi-test-data/pc/SSDT | Bin 0 -> 2104 bytes tests/acpi-test-data/q35/APIC | Bin 0 -> 120 bytes tests/acpi-test-data/q35/DSDT | Bin 0 -> 7344 bytes tests/acpi-test-data/q35/FACP | Bin 0 -> 116 bytes tests/acpi-test-data/q35/FACS | Bin 0 -> 64 bytes tests/acpi-test-data/q35/HPET | Bin 0 -> 56 bytes tests/acpi-test-data/q35/MCFG | Bin 0 -> 60 bytes tests/acpi-test-data/q35/SSDT | Bin 0 -> 2104 bytes 13 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/acpi-test-data/pc/APIC create mode 100644 tests/acpi-test-data/pc/DSDT create mode 100644 tests/acpi-test-data/pc/FACP create mode 100644 tests/acpi-test-data/pc/FACS create mode 100644 tests/acpi-test-data/pc/HPET create mode 100644 tests/acpi-test-data/pc/SSDT create mode 100644 tests/acpi-test-data/q35/APIC create mode 100644 tests/acpi-test-data/q35/DSDT create mode 100644 tests/acpi-test-data/q35/FACP create mode 100644 tests/acpi-test-data/q35/FACS create mode 100644 tests/acpi-test-data/q35/HPET create mode 100644 tests/acpi-test-data/q35/MCFG create mode 100644 tests/acpi-test-data/q35/SSDT diff --git a/tests/acpi-test-data/pc/APIC b/tests/acpi-test-data/pc/APIC new file mode 100644 index 0000000000..84509e0ae4 Binary files /dev/null and b/tests/acpi-test-data/pc/APIC differ diff --git a/tests/acpi-test-data/pc/DSDT b/tests/acpi-test-data/pc/DSDT new file mode 100644 index 0000000000..b12e5eb85b Binary files /dev/null and b/tests/acpi-test-data/pc/DSDT differ diff --git a/tests/acpi-test-data/pc/FACP b/tests/acpi-test-data/pc/FACP new file mode 100644 index 0000000000..0639999ed1 Binary files /dev/null and b/tests/acpi-test-data/pc/FACP differ diff --git a/tests/acpi-test-data/pc/FACS b/tests/acpi-test-data/pc/FACS new file mode 100644 index 0000000000..fc67ecc407 Binary files /dev/null and b/tests/acpi-test-data/pc/FACS differ diff --git a/tests/acpi-test-data/pc/HPET b/tests/acpi-test-data/pc/HPET new file mode 100644 index 0000000000..df689b8f99 Binary files /dev/null and b/tests/acpi-test-data/pc/HPET differ diff --git a/tests/acpi-test-data/pc/SSDT b/tests/acpi-test-data/pc/SSDT new file mode 100644 index 0000000000..5ab1b96af0 Binary files /dev/null and b/tests/acpi-test-data/pc/SSDT differ diff --git a/tests/acpi-test-data/q35/APIC b/tests/acpi-test-data/q35/APIC new file mode 100644 index 0000000000..84509e0ae4 Binary files /dev/null and b/tests/acpi-test-data/q35/APIC differ diff --git a/tests/acpi-test-data/q35/DSDT b/tests/acpi-test-data/q35/DSDT new file mode 100644 index 0000000000..59243b952d Binary files /dev/null and b/tests/acpi-test-data/q35/DSDT differ diff --git a/tests/acpi-test-data/q35/FACP b/tests/acpi-test-data/q35/FACP new file mode 100644 index 0000000000..19f3ac3ce6 Binary files /dev/null and b/tests/acpi-test-data/q35/FACP differ diff --git a/tests/acpi-test-data/q35/FACS b/tests/acpi-test-data/q35/FACS new file mode 100644 index 0000000000..fc67ecc407 Binary files /dev/null and b/tests/acpi-test-data/q35/FACS differ diff --git a/tests/acpi-test-data/q35/HPET b/tests/acpi-test-data/q35/HPET new file mode 100644 index 0000000000..df689b8f99 Binary files /dev/null and b/tests/acpi-test-data/q35/HPET differ diff --git a/tests/acpi-test-data/q35/MCFG b/tests/acpi-test-data/q35/MCFG new file mode 100644 index 0000000000..79ceb27a03 Binary files /dev/null and b/tests/acpi-test-data/q35/MCFG differ diff --git a/tests/acpi-test-data/q35/SSDT b/tests/acpi-test-data/q35/SSDT new file mode 100644 index 0000000000..186e748e6c Binary files /dev/null and b/tests/acpi-test-data/q35/SSDT differ -- cgit v1.2.3 From c2304b52f1b20fa79d299177d1fe75df2d265bfa Mon Sep 17 00:00:00 2001 From: Marcel Apfelbaum Date: Thu, 26 Dec 2013 16:54:20 +0200 Subject: configure: added acpi unit-test files Ensure configure will set-up links for the files if the build is created in other directory. Signed-off-by: Marcel Apfelbaum Signed-off-by: Michael S. Tsirkin --- configure | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/configure b/configure index b472694cb2..236764a3bd 100755 --- a/configure +++ b/configure @@ -4774,6 +4774,10 @@ for bios_file in \ do FILES="$FILES pc-bios/`basename $bios_file`" done +for test_file in `find $source_path/tests/acpi-test-data -type f` +do + FILES="$FILES tests/acpi-test-data`echo $test_file | sed -e 's/.*acpi-test-data//'`" +done mkdir -p $DIRS for f in $FILES ; do if [ -e "$source_path/$f" ] && [ "$source_path" != `pwd` ]; then -- cgit v1.2.3 From 9e8458c0232356a0c0eebb4fa4928fd02250c5f4 Mon Sep 17 00:00:00 2001 From: Marcel Apfelbaum Date: Thu, 26 Dec 2013 16:54:21 +0200 Subject: acpi unit-test: compare DSDT and SSDT tables against expected values This test will run only if iasl is installed on the host machine. The test plan: 1. Dumps the ACPI tables as AML on the disk. 2. Runs iasl to disassembly the tables into ASL files. 3. Runs iasl to disassembly the offline AML files into ASL files. 4. Compares the ASL files. The test runs for both default machine and q35. In case the test fails, it can be easily tweaked to show the differences between the ASL files and understand the issue. Signed-off-by: Marcel Apfelbaum Signed-off-by: Michael S. Tsirkin --- tests/acpi-test.c | 261 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 240 insertions(+), 21 deletions(-) diff --git a/tests/acpi-test.c b/tests/acpi-test.c index df1af83158..c3fc6033f1 100644 --- a/tests/acpi-test.c +++ b/tests/acpi-test.c @@ -18,14 +18,22 @@ #include "qemu/compiler.h" #include "hw/i386/acpi-defs.h" +#define MACHINE_PC "pc" +#define MACHINE_Q35 "q35" + /* DSDT and SSDTs format */ typedef struct { AcpiTableHeader header; - uint8_t *aml; - int aml_len; -} AcpiSdtTable; + gchar *aml; /* aml bytecode from guest */ + gsize aml_len; + gchar *aml_file; + gchar *asl; /* asl code generated from aml */ + gsize asl_len; + gchar *asl_file; +} QEMU_PACKED AcpiSdtTable; typedef struct { + const char *machine; uint32_t rsdp_addr; AcpiRsdpDescriptor rsdp_table; AcpiRsdtDescriptorRev1 rsdt_table; @@ -33,8 +41,7 @@ typedef struct { AcpiFacsDescriptorRev1 facs_table; uint32_t *rsdt_tables_addr; int rsdt_tables_nr; - AcpiSdtTable dsdt_table; - GArray *ssdt_tables; + GArray *ssdt_tables; /* first is DSDT */ } test_data; #define LOW(x) ((x) & 0xff) @@ -91,8 +98,10 @@ typedef struct { /* Boot sector code: write SIGNATURE into memory, * then halt. + * Q35 machine requires a minimum 0x7e000 bytes disk. + * (bug or feature?) */ -static uint8_t boot_sector[0x200] = { +static uint8_t boot_sector[0x7e000] = { /* 7c00: mov $0xdead,%ax */ [0x00] = 0xb8, [0x01] = LOW(SIGNATURE), @@ -117,17 +126,40 @@ static uint8_t boot_sector[0x200] = { }; static const char *disk = "tests/acpi-test-disk.raw"; +static const char *data_dir = "tests/acpi-test-data"; static void free_test_data(test_data *data) { + AcpiSdtTable *temp; int i; - g_free(data->rsdt_tables_addr); + if (data->rsdt_tables_addr) { + g_free(data->rsdt_tables_addr); + } + for (i = 0; i < data->ssdt_tables->len; ++i) { - g_free(g_array_index(data->ssdt_tables, AcpiSdtTable, i).aml); + temp = &g_array_index(data->ssdt_tables, AcpiSdtTable, i); + if (temp->aml) { + g_free(temp->aml); + } + if (temp->aml_file) { + if (g_strstr_len(temp->aml_file, -1, "aml-")) { + unlink(temp->aml_file); + } + g_free(temp->aml_file); + } + if (temp->asl) { + g_free(temp->asl); + } + if (temp->asl_file) { + if (g_strstr_len(temp->asl_file, -1, "asl-")) { + unlink(temp->asl_file); + } + g_free(temp->asl_file); + } } + g_array_free(data->ssdt_tables, false); - g_free(data->dsdt_table.aml); } static uint8_t acpi_checksum(const uint8_t *data, int len) @@ -292,34 +324,207 @@ static void test_dst_table(AcpiSdtTable *sdt_table, uint32_t addr) ACPI_READ_ARRAY_PTR(sdt_table->aml, sdt_table->aml_len, addr); checksum = acpi_checksum((uint8_t *)sdt_table, sizeof(AcpiTableHeader)) + - acpi_checksum(sdt_table->aml, sdt_table->aml_len); + acpi_checksum((uint8_t *)sdt_table->aml, sdt_table->aml_len); g_assert(!checksum); } static void test_acpi_dsdt_table(test_data *data) { - AcpiSdtTable *dsdt_table = &data->dsdt_table; + AcpiSdtTable dsdt_table; uint32_t addr = data->fadt_table.dsdt; - test_dst_table(dsdt_table, addr); - g_assert_cmphex(dsdt_table->header.signature, ==, ACPI_DSDT_SIGNATURE); + memset(&dsdt_table, 0, sizeof(dsdt_table)); + data->ssdt_tables = g_array_new(false, true, sizeof(AcpiSdtTable)); + + test_dst_table(&dsdt_table, addr); + g_assert_cmphex(dsdt_table.header.signature, ==, ACPI_DSDT_SIGNATURE); + + /* Place DSDT first */ + g_array_append_val(data->ssdt_tables, dsdt_table); } static void test_acpi_ssdt_tables(test_data *data) { - GArray *ssdt_tables; int ssdt_tables_nr = data->rsdt_tables_nr - 1; /* fadt is first */ int i; - ssdt_tables = g_array_sized_new(false, true, sizeof(AcpiSdtTable), - ssdt_tables_nr); for (i = 0; i < ssdt_tables_nr; i++) { AcpiSdtTable ssdt_table; + + memset(&ssdt_table, 0 , sizeof(ssdt_table)); uint32_t addr = data->rsdt_tables_addr[i + 1]; /* fadt is first */ test_dst_table(&ssdt_table, addr); - g_array_append_val(ssdt_tables, ssdt_table); + g_array_append_val(data->ssdt_tables, ssdt_table); } - data->ssdt_tables = ssdt_tables; +} + +static bool iasl_installed(void) +{ + gchar *out = NULL, *out_err = NULL; + bool ret; + + /* pass 'out' and 'out_err' in order to be redirected */ + ret = g_spawn_command_line_sync("iasl", &out, &out_err, NULL, NULL); + + if (out_err) { + ret = ret && (out_err[0] == '\0'); + g_free(out_err); + } + + if (out) { + g_free(out); + } + + return ret; +} + +static void dump_aml_files(test_data *data) +{ + AcpiSdtTable *sdt; + GError *error = NULL; + gint fd; + ssize_t ret; + int i; + + for (i = 0; i < data->ssdt_tables->len; ++i) { + sdt = &g_array_index(data->ssdt_tables, AcpiSdtTable, i); + g_assert(sdt->aml); + + fd = g_file_open_tmp("aml-XXXXXX", &sdt->aml_file, &error); + g_assert_no_error(error); + + ret = qemu_write_full(fd, sdt, sizeof(AcpiTableHeader)); + g_assert(ret == sizeof(AcpiTableHeader)); + ret = qemu_write_full(fd, sdt->aml, sdt->aml_len); + g_assert(ret == sdt->aml_len); + + close(fd); + } +} + +static void load_asl(GArray *sdts, AcpiSdtTable *sdt) +{ + AcpiSdtTable *temp; + GError *error = NULL; + GString *command_line = g_string_new("'iasl' "); + gint fd; + gchar *out, *out_err; + gboolean ret; + int i; + + fd = g_file_open_tmp("asl-XXXXXX.dsl", &sdt->asl_file, &error); + g_assert_no_error(error); + close(fd); + + /* build command line */ + g_string_append_printf(command_line, "-p %s ", sdt->asl_file); + for (i = 0; i < 2; ++i) { /* reference DSDT and SSDT */ + temp = &g_array_index(sdts, AcpiSdtTable, i); + g_string_append_printf(command_line, "-e %s ", temp->aml_file); + } + g_string_append_printf(command_line, "-d %s", sdt->aml_file); + + /* pass 'out' and 'out_err' in order to be redirected */ + g_spawn_command_line_sync(command_line->str, &out, &out_err, NULL, &error); + g_assert_no_error(error); + + ret = g_file_get_contents(sdt->asl_file, (gchar **)&sdt->asl, + &sdt->asl_len, &error); + g_assert(ret); + g_assert_no_error(error); + g_assert(sdt->asl_len); + + g_free(out); + g_free(out_err); + g_string_free(command_line, true); +} + +#define COMMENT_END "*/" +#define DEF_BLOCK "DefinitionBlock (" +#define BLOCK_NAME_END ".aml" + +static GString *normalize_asl(gchar *asl_code) +{ + GString *asl = g_string_new(asl_code); + gchar *comment, *block_name; + + /* strip comments (different generation days) */ + comment = g_strstr_len(asl->str, asl->len, COMMENT_END); + if (comment) { + asl = g_string_erase(asl, 0, comment + sizeof(COMMENT_END) - asl->str); + } + + /* strip def block name (it has file path in it) */ + if (g_str_has_prefix(asl->str, DEF_BLOCK)) { + block_name = g_strstr_len(asl->str, asl->len, BLOCK_NAME_END); + g_assert(block_name); + asl = g_string_erase(asl, 0, + block_name + sizeof(BLOCK_NAME_END) - asl->str); + } + + return asl; +} + +static GArray *load_expected_aml(test_data *data) +{ + int i; + AcpiSdtTable *sdt; + gchar *aml_file; + GError *error = NULL; + gboolean ret; + + GArray *exp_ssdt_tables = g_array_new(false, true, sizeof(AcpiSdtTable)); + for (i = 0; i < data->ssdt_tables->len; ++i) { + AcpiSdtTable exp_sdt; + sdt = &g_array_index(data->ssdt_tables, AcpiSdtTable, i); + + memset(&exp_sdt, 0, sizeof(exp_sdt)); + exp_sdt.header.signature = sdt->header.signature; + + aml_file = g_strdup_printf("%s/%s/%.4s", data_dir, data->machine, + (gchar *)&exp_sdt.header.signature); + exp_sdt.aml_file = aml_file; + g_assert(g_file_test(aml_file, G_FILE_TEST_EXISTS)); + ret = g_file_get_contents(aml_file, &exp_sdt.aml, + &exp_sdt.aml_len, &error); + g_assert(ret); + g_assert_no_error(error); + g_assert(exp_sdt.aml); + g_assert(exp_sdt.aml_len); + + g_array_append_val(exp_ssdt_tables, exp_sdt); + } + + return exp_ssdt_tables; +} + +static void test_acpi_asl(test_data *data) +{ + int i; + AcpiSdtTable *sdt, *exp_sdt; + test_data exp_data; + + memset(&exp_data, 0, sizeof(exp_data)); + exp_data.ssdt_tables = load_expected_aml(data); + dump_aml_files(data); + for (i = 0; i < data->ssdt_tables->len; ++i) { + GString *asl, *exp_asl; + + sdt = &g_array_index(data->ssdt_tables, AcpiSdtTable, i); + exp_sdt = &g_array_index(exp_data.ssdt_tables, AcpiSdtTable, i); + + load_asl(data->ssdt_tables, sdt); + asl = normalize_asl(sdt->asl); + + load_asl(exp_data.ssdt_tables, exp_sdt); + exp_asl = normalize_asl(exp_sdt->asl); + + g_assert(!g_strcmp0(asl->str, exp_asl->str)); + g_string_free(asl, true); + g_string_free(exp_asl, true); + } + + free_test_data(&exp_data); } static void test_acpi_one(const char *params, test_data *data) @@ -329,10 +534,14 @@ static void test_acpi_one(const char *params, test_data *data) uint8_t signature_high; uint16_t signature; int i; + const char *device = ""; - memset(data, 0, sizeof(*data)); - args = g_strdup_printf("-net none -display none %s %s", - params ? params : "", disk); + if (!g_strcmp0(data->machine, MACHINE_Q35)) { + device = ",id=hd -device ide-hd,drive=hd"; + } + + args = g_strdup_printf("-net none -display none %s -drive file=%s%s,", + params ? params : "", disk, device); qtest_start(args); /* Wait at most 1 minute */ @@ -362,6 +571,10 @@ static void test_acpi_one(const char *params, test_data *data) test_acpi_dsdt_table(data); test_acpi_ssdt_tables(data); + if (iasl_installed()) { + test_acpi_asl(data); + } + qtest_quit(global_qtest); g_free(args); } @@ -373,8 +586,14 @@ static void test_acpi_tcg(void) /* Supplying -machine accel argument overrides the default (qtest). * This is to make guest actually run. */ + memset(&data, 0, sizeof(data)); + data.machine = MACHINE_PC; test_acpi_one("-machine accel=tcg", &data); + free_test_data(&data); + memset(&data, 0, sizeof(data)); + data.machine = MACHINE_Q35; + test_acpi_one("-machine q35,accel=tcg", &data); free_test_data(&data); } -- cgit v1.2.3 From 9dd6cabdd3bfa49d4f594956e8ee93f8996460c7 Mon Sep 17 00:00:00 2001 From: Marcel Apfelbaum Date: Thu, 26 Dec 2013 16:54:22 +0200 Subject: configure: add CONFIG_IASL to config-host.h Acpi unit-tests will extract iasl executable from CONFIG_IASL define. Signed-off-by: Marcel Apfelbaum Signed-off-by: Michael S. Tsirkin --- scripts/create_config | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/create_config b/scripts/create_config index b1adbf5897..06f5316d9d 100755 --- a/scripts/create_config +++ b/scripts/create_config @@ -26,6 +26,10 @@ case $line in # save for the next definitions prefix=${line#*=} ;; + IASL=*) # iasl executable + value=${line#*=} + echo "#define CONFIG_IASL $value" + ;; CONFIG_AUDIO_DRIVERS=*) drivers=${line#*=} echo "#define CONFIG_AUDIO_DRIVERS \\" -- cgit v1.2.3 From cc8fa0e80836c51ba644d910cd89540a5bc83fc2 Mon Sep 17 00:00:00 2001 From: Marcel Apfelbaum Date: Thu, 26 Dec 2013 16:54:23 +0200 Subject: acpi unit-test: extract iasl executable from configuration The test checked if iasl is installed by running "iasl" and checking the error output. It is better to use the iasl executable as appears in configuration. Signed-off-by: Marcel Apfelbaum Signed-off-by: Michael S. Tsirkin --- tests/acpi-test.c | 31 ++++++++----------------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/tests/acpi-test.c b/tests/acpi-test.c index c3fc6033f1..83e5aed06d 100644 --- a/tests/acpi-test.c +++ b/tests/acpi-test.c @@ -127,6 +127,11 @@ static uint8_t boot_sector[0x7e000] = { static const char *disk = "tests/acpi-test-disk.raw"; static const char *data_dir = "tests/acpi-test-data"; +#ifdef CONFIG_IASL +static const char *iasl = stringify(CONFIG_IASL); +#else +static const char *iasl; +#endif static void free_test_data(test_data *data) { @@ -358,26 +363,6 @@ static void test_acpi_ssdt_tables(test_data *data) } } -static bool iasl_installed(void) -{ - gchar *out = NULL, *out_err = NULL; - bool ret; - - /* pass 'out' and 'out_err' in order to be redirected */ - ret = g_spawn_command_line_sync("iasl", &out, &out_err, NULL, NULL); - - if (out_err) { - ret = ret && (out_err[0] == '\0'); - g_free(out_err); - } - - if (out) { - g_free(out); - } - - return ret; -} - static void dump_aml_files(test_data *data) { AcpiSdtTable *sdt; @@ -406,7 +391,7 @@ static void load_asl(GArray *sdts, AcpiSdtTable *sdt) { AcpiSdtTable *temp; GError *error = NULL; - GString *command_line = g_string_new("'iasl' "); + GString *command_line = g_string_new(iasl); gint fd; gchar *out, *out_err; gboolean ret; @@ -417,7 +402,7 @@ static void load_asl(GArray *sdts, AcpiSdtTable *sdt) close(fd); /* build command line */ - g_string_append_printf(command_line, "-p %s ", sdt->asl_file); + g_string_append_printf(command_line, " -p %s ", sdt->asl_file); for (i = 0; i < 2; ++i) { /* reference DSDT and SSDT */ temp = &g_array_index(sdts, AcpiSdtTable, i); g_string_append_printf(command_line, "-e %s ", temp->aml_file); @@ -571,7 +556,7 @@ static void test_acpi_one(const char *params, test_data *data) test_acpi_dsdt_table(data); test_acpi_ssdt_tables(data); - if (iasl_installed()) { + if (iasl) { test_acpi_asl(data); } -- cgit v1.2.3 From cebc92a2131be21cc4dc5f72f1e243061cd96b1f Mon Sep 17 00:00:00 2001 From: Marcel Apfelbaum Date: Thu, 26 Dec 2013 16:54:24 +0200 Subject: acpi unit-test: added script to rebuild the expected aml files Acpi unit-test will fail every time the acpi tables change. This script rebuild the expected aml files, so the test will pass. It also validates the modifications. Signed-off-by: Marcel Apfelbaum Signed-off-by: Michael S. Tsirkin --- tests/acpi-test-data/rebuild-expected-aml.sh | 36 ++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100755 tests/acpi-test-data/rebuild-expected-aml.sh diff --git a/tests/acpi-test-data/rebuild-expected-aml.sh b/tests/acpi-test-data/rebuild-expected-aml.sh new file mode 100755 index 0000000000..ab98498884 --- /dev/null +++ b/tests/acpi-test-data/rebuild-expected-aml.sh @@ -0,0 +1,36 @@ +#! /bin/bash + +# +# Rebuild expected AML files for acpi unit-test +# +# Copyright (c) 2013 Red Hat Inc. +# +# Authors: +# Marcel Apfelbaum +# +# This work is licensed under the terms of the GNU GPLv2. +# See the COPYING.LIB file in the top-level directory. + +qemu= + +if [ -e x86_64-softmmu/qemu-system-x86_64 ]; then + qemu="x86_64-softmmu/qemu-system-x86_64" +elif [ -e i386-softmmu/qemu-system-i386 ]; then + qemu="i386-softmmu/qemu-system-i386" +else + echo "Run 'make' to build the qemu exectutable!" + echo "Run this script from the build directory." + exit 1; +fi + +if [ ! -e "tests/acpi-test" ]; then + echo "Test: acpi-test is required! Run make check before this script." + echo "Run this script from the build directory." + exit 1; +fi + +TEST_ACPI_REBUILD_AML=y QTEST_QEMU_BINARY=$qemu tests/acpi-test + +echo "The files were rebuilt and can be added to git." +echo "However, if new files were created, please copy them manually" \ + "to tests/acpi-test-data/pc/ or tests/acpi-test-data/q35/ ." -- cgit v1.2.3 From 4500bc98a6aab1734d865afaeade3509eb65b560 Mon Sep 17 00:00:00 2001 From: Marcel Apfelbaum Date: Thu, 26 Dec 2013 16:54:25 +0200 Subject: acpi unit-test: hook to rebuild expected aml files When running the test with TEST_ACPI_REBUILD_AML=y environment variable, the test will rebuild and validate the expected aml files. Signed-off-by: Marcel Apfelbaum Signed-off-by: Michael S. Tsirkin --- tests/acpi-test.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/tests/acpi-test.c b/tests/acpi-test.c index 83e5aed06d..89c4b81fdb 100644 --- a/tests/acpi-test.c +++ b/tests/acpi-test.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "qemu-common.h" #include "libqtest.h" #include "qemu/compiler.h" @@ -21,6 +22,8 @@ #define MACHINE_PC "pc" #define MACHINE_Q35 "q35" +#define ACPI_REBUILD_EXPECTED_AML "TEST_ACPI_REBUILD_AML" + /* DSDT and SSDTs format */ typedef struct { AcpiTableHeader header; @@ -363,10 +366,11 @@ static void test_acpi_ssdt_tables(test_data *data) } } -static void dump_aml_files(test_data *data) +static void dump_aml_files(test_data *data, bool rebuild) { AcpiSdtTable *sdt; GError *error = NULL; + gchar *aml_file = NULL; gint fd; ssize_t ret; int i; @@ -375,8 +379,16 @@ static void dump_aml_files(test_data *data) sdt = &g_array_index(data->ssdt_tables, AcpiSdtTable, i); g_assert(sdt->aml); - fd = g_file_open_tmp("aml-XXXXXX", &sdt->aml_file, &error); - g_assert_no_error(error); + if (rebuild) { + aml_file = g_strdup_printf("%s/%s/%.4s", data_dir, data->machine, + (gchar *)&sdt->header.signature); + fd = g_open(aml_file, O_WRONLY|O_TRUNC|O_CREAT, + S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH); + } else { + fd = g_file_open_tmp("aml-XXXXXX", &sdt->aml_file, &error); + g_assert_no_error(error); + } + g_assert(fd >= 0); ret = qemu_write_full(fd, sdt, sizeof(AcpiTableHeader)); g_assert(ret == sizeof(AcpiTableHeader)); @@ -384,6 +396,10 @@ static void dump_aml_files(test_data *data) g_assert(ret == sdt->aml_len); close(fd); + + if (aml_file) { + g_free(aml_file); + } } } @@ -491,7 +507,7 @@ static void test_acpi_asl(test_data *data) memset(&exp_data, 0, sizeof(exp_data)); exp_data.ssdt_tables = load_expected_aml(data); - dump_aml_files(data); + dump_aml_files(data, false); for (i = 0; i < data->ssdt_tables->len; ++i) { GString *asl, *exp_asl; @@ -557,7 +573,11 @@ static void test_acpi_one(const char *params, test_data *data) test_acpi_ssdt_tables(data); if (iasl) { - test_acpi_asl(data); + if (getenv(ACPI_REBUILD_EXPECTED_AML)) { + dump_aml_files(data, true); + } else { + test_acpi_asl(data); + } } qtest_quit(global_qtest); -- cgit v1.2.3 From 084137ddbbb30f5f85298711dc2d501a625d37ab Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Mon, 13 Jan 2014 18:33:53 +1100 Subject: tests: fix acpi to work on bigendian host Double endianness convertion make this test failing on POWERPC machine running in big-endian. This fixes the test to success on big-endian host. Signed-off-by: Alexey Kardashevskiy Signed-off-by: Michael S. Tsirkin --- tests/acpi-test.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/acpi-test.c b/tests/acpi-test.c index 89c4b81fdb..b5ab70a378 100644 --- a/tests/acpi-test.c +++ b/tests/acpi-test.c @@ -61,13 +61,13 @@ typedef struct { field = readb(addr); \ break; \ case 2: \ - field = le16_to_cpu(readw(addr)); \ + field = readw(addr); \ break; \ case 4: \ - field = le32_to_cpu(readl(addr)); \ + field = readl(addr); \ break; \ case 8: \ - field = le64_to_cpu(readq(addr)); \ + field = readq(addr); \ break; \ default: \ g_assert(false); \ -- cgit v1.2.3 From a3a74ab90ea81cafaf4d03824d47a6d028ea996d Mon Sep 17 00:00:00 2001 From: Marcel Apfelbaum Date: Thu, 16 Jan 2014 17:50:46 +0200 Subject: acpi unit-test: renamed ssdt_tables to tables Just a refactoring, ssdt_tables name was confusing as it included other tables as well. Signed-off-by: Marcel Apfelbaum Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/acpi-test.c | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/tests/acpi-test.c b/tests/acpi-test.c index b5ab70a378..a3d2b34473 100644 --- a/tests/acpi-test.c +++ b/tests/acpi-test.c @@ -44,7 +44,7 @@ typedef struct { AcpiFacsDescriptorRev1 facs_table; uint32_t *rsdt_tables_addr; int rsdt_tables_nr; - GArray *ssdt_tables; /* first is DSDT */ + GArray *tables; } test_data; #define LOW(x) ((x) & 0xff) @@ -145,8 +145,8 @@ static void free_test_data(test_data *data) g_free(data->rsdt_tables_addr); } - for (i = 0; i < data->ssdt_tables->len; ++i) { - temp = &g_array_index(data->ssdt_tables, AcpiSdtTable, i); + for (i = 0; i < data->tables->len; ++i) { + temp = &g_array_index(data->tables, AcpiSdtTable, i); if (temp->aml) { g_free(temp->aml); } @@ -167,7 +167,7 @@ static void free_test_data(test_data *data) } } - g_array_free(data->ssdt_tables, false); + g_array_free(data->tables, false); } static uint8_t acpi_checksum(const uint8_t *data, int len) @@ -342,27 +342,27 @@ static void test_acpi_dsdt_table(test_data *data) uint32_t addr = data->fadt_table.dsdt; memset(&dsdt_table, 0, sizeof(dsdt_table)); - data->ssdt_tables = g_array_new(false, true, sizeof(AcpiSdtTable)); + data->tables = g_array_new(false, true, sizeof(AcpiSdtTable)); test_dst_table(&dsdt_table, addr); g_assert_cmphex(dsdt_table.header.signature, ==, ACPI_DSDT_SIGNATURE); /* Place DSDT first */ - g_array_append_val(data->ssdt_tables, dsdt_table); + g_array_append_val(data->tables, dsdt_table); } -static void test_acpi_ssdt_tables(test_data *data) +static void test_acpi_tables(test_data *data) { - int ssdt_tables_nr = data->rsdt_tables_nr - 1; /* fadt is first */ + int tables_nr = data->rsdt_tables_nr - 1; /* fadt is first */ int i; - for (i = 0; i < ssdt_tables_nr; i++) { + for (i = 0; i < tables_nr; i++) { AcpiSdtTable ssdt_table; memset(&ssdt_table, 0 , sizeof(ssdt_table)); uint32_t addr = data->rsdt_tables_addr[i + 1]; /* fadt is first */ test_dst_table(&ssdt_table, addr); - g_array_append_val(data->ssdt_tables, ssdt_table); + g_array_append_val(data->tables, ssdt_table); } } @@ -375,8 +375,8 @@ static void dump_aml_files(test_data *data, bool rebuild) ssize_t ret; int i; - for (i = 0; i < data->ssdt_tables->len; ++i) { - sdt = &g_array_index(data->ssdt_tables, AcpiSdtTable, i); + for (i = 0; i < data->tables->len; ++i) { + sdt = &g_array_index(data->tables, AcpiSdtTable, i); g_assert(sdt->aml); if (rebuild) { @@ -474,10 +474,10 @@ static GArray *load_expected_aml(test_data *data) GError *error = NULL; gboolean ret; - GArray *exp_ssdt_tables = g_array_new(false, true, sizeof(AcpiSdtTable)); - for (i = 0; i < data->ssdt_tables->len; ++i) { + GArray *exp_tables = g_array_new(false, true, sizeof(AcpiSdtTable)); + for (i = 0; i < data->tables->len; ++i) { AcpiSdtTable exp_sdt; - sdt = &g_array_index(data->ssdt_tables, AcpiSdtTable, i); + sdt = &g_array_index(data->tables, AcpiSdtTable, i); memset(&exp_sdt, 0, sizeof(exp_sdt)); exp_sdt.header.signature = sdt->header.signature; @@ -493,10 +493,10 @@ static GArray *load_expected_aml(test_data *data) g_assert(exp_sdt.aml); g_assert(exp_sdt.aml_len); - g_array_append_val(exp_ssdt_tables, exp_sdt); + g_array_append_val(exp_tables, exp_sdt); } - return exp_ssdt_tables; + return exp_tables; } static void test_acpi_asl(test_data *data) @@ -506,18 +506,18 @@ static void test_acpi_asl(test_data *data) test_data exp_data; memset(&exp_data, 0, sizeof(exp_data)); - exp_data.ssdt_tables = load_expected_aml(data); + exp_data.tables = load_expected_aml(data); dump_aml_files(data, false); - for (i = 0; i < data->ssdt_tables->len; ++i) { + for (i = 0; i < data->tables->len; ++i) { GString *asl, *exp_asl; - sdt = &g_array_index(data->ssdt_tables, AcpiSdtTable, i); - exp_sdt = &g_array_index(exp_data.ssdt_tables, AcpiSdtTable, i); + sdt = &g_array_index(data->tables, AcpiSdtTable, i); + exp_sdt = &g_array_index(exp_data.tables, AcpiSdtTable, i); - load_asl(data->ssdt_tables, sdt); + load_asl(data->tables, sdt); asl = normalize_asl(sdt->asl); - load_asl(exp_data.ssdt_tables, exp_sdt); + load_asl(exp_data.tables, exp_sdt); exp_asl = normalize_asl(exp_sdt->asl); g_assert(!g_strcmp0(asl->str, exp_asl->str)); @@ -570,7 +570,7 @@ static void test_acpi_one(const char *params, test_data *data) test_acpi_fadt_table(data); test_acpi_facs_table(data); test_acpi_dsdt_table(data); - test_acpi_ssdt_tables(data); + test_acpi_tables(data); if (iasl) { if (getenv(ACPI_REBUILD_EXPECTED_AML)) { -- cgit v1.2.3 From 69d09245d19765fd461cc3d3a7d79686007c4474 Mon Sep 17 00:00:00 2001 From: Marcel Apfelbaum Date: Thu, 16 Jan 2014 17:50:47 +0200 Subject: acpi unit-test: resolved iasl crash It seems that iasl has an issue when disassembles some ACPI tables using the command line: iasl -e DSDT -e SSDT -d HPET Modified the iasl command line to "iasl -d HPET" until the problem is solved. The command line remained the same for DSDT and SSDT tables. Reported-by: Michael S. Tsirkin Signed-off-by: Marcel Apfelbaum Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/acpi-test.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/tests/acpi-test.c b/tests/acpi-test.c index a3d2b34473..6095d07d04 100644 --- a/tests/acpi-test.c +++ b/tests/acpi-test.c @@ -23,6 +23,7 @@ #define MACHINE_Q35 "q35" #define ACPI_REBUILD_EXPECTED_AML "TEST_ACPI_REBUILD_AML" +#define ACPI_SSDT_SIGNATURE 0x54445353 /* SSDT */ /* DSDT and SSDTs format */ typedef struct { @@ -403,6 +404,11 @@ static void dump_aml_files(test_data *data, bool rebuild) } } +static bool compare_signature(AcpiSdtTable *sdt, uint32_t signature) +{ + return sdt->header.signature == signature; +} + static void load_asl(GArray *sdts, AcpiSdtTable *sdt) { AcpiSdtTable *temp; @@ -419,9 +425,15 @@ static void load_asl(GArray *sdts, AcpiSdtTable *sdt) /* build command line */ g_string_append_printf(command_line, " -p %s ", sdt->asl_file); - for (i = 0; i < 2; ++i) { /* reference DSDT and SSDT */ - temp = &g_array_index(sdts, AcpiSdtTable, i); - g_string_append_printf(command_line, "-e %s ", temp->aml_file); + if (compare_signature(sdt, ACPI_DSDT_SIGNATURE) || + compare_signature(sdt, ACPI_SSDT_SIGNATURE)) { + for (i = 0; i < sdts->len; ++i) { + temp = &g_array_index(sdts, AcpiSdtTable, i); + if (compare_signature(temp, ACPI_DSDT_SIGNATURE) || + compare_signature(temp, ACPI_SSDT_SIGNATURE)) { + g_string_append_printf(command_line, "-e %s ", temp->aml_file); + } + } } g_string_append_printf(command_line, "-d %s", sdt->aml_file); -- cgit v1.2.3 From 0651596cbed8c0806aa2d7e436201f28b2fe179a Mon Sep 17 00:00:00 2001 From: Marcel Apfelbaum Date: Thu, 16 Jan 2014 17:50:48 +0200 Subject: acpi unit-test: do not fail on asl mismatch The asl comparison will break every time the ACPI tables are updated. This may break the git bisect. Instead of failing print a warning on stderr including the retained asl files, so they can be compared offline. Signed-off-by: Marcel Apfelbaum Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/acpi-test.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tests/acpi-test.c b/tests/acpi-test.c index 6095d07d04..31f5359787 100644 --- a/tests/acpi-test.c +++ b/tests/acpi-test.c @@ -34,6 +34,7 @@ typedef struct { gchar *asl; /* asl code generated from aml */ gsize asl_len; gchar *asl_file; + bool asl_file_retain; /* do not delete the temp asl */ } QEMU_PACKED AcpiSdtTable; typedef struct { @@ -161,7 +162,7 @@ static void free_test_data(test_data *data) g_free(temp->asl); } if (temp->asl_file) { - if (g_strstr_len(temp->asl_file, -1, "asl-")) { + if (!temp->asl_file_retain) { unlink(temp->asl_file); } g_free(temp->asl_file); @@ -532,7 +533,15 @@ static void test_acpi_asl(test_data *data) load_asl(exp_data.tables, exp_sdt); exp_asl = normalize_asl(exp_sdt->asl); - g_assert(!g_strcmp0(asl->str, exp_asl->str)); + if (g_strcmp0(asl->str, exp_asl->str)) { + sdt->asl_file_retain = true; + exp_sdt->asl_file_retain = true; + fprintf(stderr, + "acpi-test: Warning! %.4s mismatch. " + "Orig asl: %s, expected asl %s.\n", + (gchar *)&exp_sdt->header.signature, + sdt->asl_file, exp_sdt->asl_file); + } g_string_free(asl, true); g_string_free(exp_asl, true); } -- cgit v1.2.3 From 4d25299cb2b57a16fd3194e938eeb78d219400ba Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 9 Jan 2014 17:36:33 +0100 Subject: pc: make: fix dependencies: rebuild when included file is changed some *.dsl files include another *.dsl files but there weren't any dependicies and when included file changed target table wasn't rebuild. Fix this by using the same auto dependency generation as for C files. Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/Makefile.objs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/i386/Makefile.objs b/hw/i386/Makefile.objs index 09ac433cf9..d58a103efe 100644 --- a/hw/i386/Makefile.objs +++ b/hw/i386/Makefile.objs @@ -17,7 +17,7 @@ iasl-option=$(shell if test -z "`$(1) $(2) 2>&1 > /dev/null`" \ ifdef IASL #IASL Present. Generate hex files from .dsl hw/i386/%.hex: $(SRC_PATH)/hw/i386/%.dsl $(SRC_PATH)/scripts/acpi_extract_preprocess.py $(SRC_PATH)/scripts/acpi_extract.py - $(call quiet-command, cpp -P $< -o $*.dsl.i.orig, " CPP $(TARGET_DIR)$*.dsl.i.orig") + $(call quiet-command, cpp -P $(QEMU_DGFLAGS) $< -o $*.dsl.i.orig, " CPP $(TARGET_DIR)$*.dsl.i.orig") $(call quiet-command, $(PYTHON) $(SRC_PATH)/scripts/acpi_extract_preprocess.py $*.dsl.i.orig > $*.dsl.i, " ACPI_PREPROCESS $(TARGET_DIR)$*.dsl.i") $(call quiet-command, $(IASL) $(call iasl-option,$(IASL),-Pn,) -vs -l -tc -p $* $*.dsl.i $(if $(V), , > /dev/null) 2>&1 ," IASL $(TARGET_DIR)$*.dsl.i") $(call quiet-command, $(PYTHON) $(SRC_PATH)/scripts/acpi_extract.py $*.lst > $*.off, " ACPI_EXTRACT $(TARGET_DIR)$*.off") -- cgit v1.2.3 From eb0acfdde604930688c47fe1ba99bec2bd84b7ad Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 14 Oct 2013 18:01:07 +0300 Subject: pci: add pci_for_each_bus_depth_first Useful for ACPI hotplug. Signed-off-by: Michael S. Tsirkin --- hw/pci/pci.c | 28 ++++++++++++++++++++++++++++ include/hw/pci/pci.h | 14 ++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index aa2a395499..2aca8a4d7e 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -1704,6 +1704,34 @@ static PCIBus *pci_find_bus_nr(PCIBus *bus, int bus_num) return NULL; } +void pci_for_each_bus_depth_first(PCIBus *bus, + void *(*begin)(PCIBus *bus, void *parent_state), + void (*end)(PCIBus *bus, void *state), + void *parent_state) +{ + PCIBus *sec; + void *state; + + if (!bus) { + return; + } + + if (begin) { + state = begin(bus, parent_state); + } else { + state = parent_state; + } + + QLIST_FOREACH(sec, &bus->child, sibling) { + pci_for_each_bus_depth_first(sec, begin, end, state); + } + + if (end) { + end(bus, state); + } +} + + PCIDevice *pci_find_device(PCIBus *bus, int bus_num, uint8_t devfn) { bus = pci_find_bus_nr(bus, bus_num); diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index 754b82de81..52523467b6 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -387,6 +387,20 @@ int pci_bus_num(PCIBus *s); void pci_for_each_device(PCIBus *bus, int bus_num, void (*fn)(PCIBus *bus, PCIDevice *d, void *opaque), void *opaque); +void pci_for_each_bus_depth_first(PCIBus *bus, + void *(*begin)(PCIBus *bus, void *parent_state), + void (*end)(PCIBus *bus, void *state), + void *parent_state); + +/* Use this wrapper when specific scan order is not required. */ +static inline +void pci_for_each_bus(PCIBus *bus, + void (*fn)(PCIBus *bus, void *opaque), + void *opaque) +{ + pci_for_each_bus_depth_first(bus, NULL, fn, opaque); +} + PCIBus *pci_find_primary_bus(void); PCIBus *pci_device_root_bus(const PCIDevice *d); const char *pci_root_bus_path(PCIDevice *dev); -- cgit v1.2.3 From db4728e6fec0364b866d3106125974eedc00e091 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 14 Oct 2013 18:01:11 +0300 Subject: pcihp: generalization of piix4 acpi Add ACPI based PCI hotplug library with bridge hotplug support. Design - each bus gets assigned "bsel" property. - ACPI code writes this number to a new BNUM register, then uses existing UP/DOWN registers to probe slot status; to eject, write number to BNUM register, then slot into existing EJ. The interface is actually backwards-compatible with existing PIIX4 ACPI (though not migration compatible). This is split out from PIIX4 codebase so we can reuse it for Q35 as well. Signed-off-by: Michael S. Tsirkin --- hw/acpi/Makefile.objs | 3 +- hw/acpi/pcihp.c | 316 ++++++++++++++++++++++++++++++++++++++++++++++++ include/hw/acpi/pcihp.h | 72 +++++++++++ 3 files changed, 389 insertions(+), 2 deletions(-) create mode 100644 hw/acpi/pcihp.c create mode 100644 include/hw/acpi/pcihp.h diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs index a0b63b5626..841eca2e7c 100644 --- a/hw/acpi/Makefile.objs +++ b/hw/acpi/Makefile.objs @@ -1,2 +1 @@ -common-obj-$(CONFIG_ACPI) += core.o piix4.o ich9.o - +common-obj-$(CONFIG_ACPI) += core.o piix4.o ich9.o pcihp.o diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c new file mode 100644 index 0000000000..3fa3d7c290 --- /dev/null +++ b/hw/acpi/pcihp.c @@ -0,0 +1,316 @@ +/* + * QEMU<->ACPI BIOS PCI hotplug interface + * + * QEMU supports PCI hotplug via ACPI. This module + * implements the interface between QEMU and the ACPI BIOS. + * Interface specification - see docs/specs/acpi_pci_hotplug.txt + * + * Copyright (c) 2013, Red Hat Inc, Michael S. Tsirkin (mst@redhat.com) + * Copyright (c) 2006 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#include "hw/acpi/pcihp.h" + +#include "hw/hw.h" +#include "hw/i386/pc.h" +#include "hw/pci/pci.h" +#include "hw/acpi/acpi.h" +#include "sysemu/sysemu.h" +#include "qemu/range.h" +#include "exec/ioport.h" +#include "exec/address-spaces.h" +#include "hw/pci/pci_bus.h" +#include "qom/qom-qobject.h" +#include "qapi/qmp/qint.h" + +//#define DEBUG + +#ifdef DEBUG +# define ACPI_PCIHP_DPRINTF(format, ...) printf(format, ## __VA_ARGS__) +#else +# define ACPI_PCIHP_DPRINTF(format, ...) do { } while (0) +#endif + +#define PCI_HOTPLUG_ADDR 0xae00 +#define PCI_HOTPLUG_SIZE 0x0014 +#define PCI_UP_BASE 0xae00 +#define PCI_DOWN_BASE 0xae04 +#define PCI_EJ_BASE 0xae08 +#define PCI_RMV_BASE 0xae0c +#define PCI_SEL_BASE 0xae10 + +typedef struct AcpiPciHpFind { + int bsel; + PCIBus *bus; +} AcpiPciHpFind; + +static int acpi_pcihp_get_bsel(PCIBus *bus) +{ + QObject *o = object_property_get_qobject(OBJECT(bus), + ACPI_PCIHP_PROP_BSEL, NULL); + int64_t bsel = -1; + if (o) { + bsel = qint_get_int(qobject_to_qint(o)); + } + if (bsel < 0) { + return -1; + } + return bsel; +} + +static void acpi_pcihp_test_hotplug_bus(PCIBus *bus, void *opaque) +{ + AcpiPciHpFind *find = opaque; + if (find->bsel == acpi_pcihp_get_bsel(bus)) { + find->bus = bus; + } +} + +static PCIBus *acpi_pcihp_find_hotplug_bus(AcpiPciHpState *s, int bsel) +{ + AcpiPciHpFind find = { .bsel = bsel, .bus = NULL }; + + if (bsel < 0) { + return NULL; + } + + pci_for_each_bus(s->root, acpi_pcihp_test_hotplug_bus, &find); + + /* Make bsel 0 eject root bus if bsel property is not set, + * for compatibility with non acpi setups. + * TODO: really needed? + */ + if (!bsel && !find.bus) { + find.bus = s->root; + } + return find.bus; +} + +static bool acpi_pcihp_pc_no_hotplug(AcpiPciHpState *s, PCIDevice *dev) +{ + PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev); + /* + * ACPI doesn't allow hotplug of bridge devices. Don't allow + * hot-unplug of bridge devices unless they were added by hotplug + * (and so, not described by acpi). + */ + return (pc->is_bridge && !dev->qdev.hotplugged) || pc->no_hotplug; +} + +static void acpi_pcihp_eject_slot(AcpiPciHpState *s, unsigned bsel, unsigned slots) +{ + BusChild *kid, *next; + int slot = ffs(slots) - 1; + bool slot_free = true; + PCIBus *bus = acpi_pcihp_find_hotplug_bus(s, bsel); + + if (!bus) { + return; + } + + /* Mark request as complete */ + s->acpi_pcihp_pci_status[bsel].down &= ~(1U << slot); + + QTAILQ_FOREACH_SAFE(kid, &bus->qbus.children, sibling, next) { + DeviceState *qdev = kid->child; + PCIDevice *dev = PCI_DEVICE(qdev); + if (PCI_SLOT(dev->devfn) == slot) { + if (acpi_pcihp_pc_no_hotplug(s, dev)) { + slot_free = false; + } else { + object_unparent(OBJECT(qdev)); + } + } + } + if (slot_free) { + s->acpi_pcihp_pci_status[bsel].device_present &= ~(1U << slot); + } +} + +static void acpi_pcihp_update_hotplug_bus(AcpiPciHpState *s, int bsel) +{ + BusChild *kid, *next; + PCIBus *bus = acpi_pcihp_find_hotplug_bus(s, bsel); + + /* Execute any pending removes during reset */ + while (s->acpi_pcihp_pci_status[bsel].down) { + acpi_pcihp_eject_slot(s, bsel, s->acpi_pcihp_pci_status[bsel].down); + } + + s->acpi_pcihp_pci_status[bsel].hotplug_enable = ~0; + s->acpi_pcihp_pci_status[bsel].device_present = 0; + + if (!bus) { + return; + } + QTAILQ_FOREACH_SAFE(kid, &bus->qbus.children, sibling, next) { + DeviceState *qdev = kid->child; + PCIDevice *pdev = PCI_DEVICE(qdev); + int slot = PCI_SLOT(pdev->devfn); + + if (acpi_pcihp_pc_no_hotplug(s, pdev)) { + s->acpi_pcihp_pci_status[bsel].hotplug_enable &= ~(1U << slot); + } + + s->acpi_pcihp_pci_status[bsel].device_present |= (1U << slot); + } +} + +static void acpi_pcihp_update(AcpiPciHpState *s) +{ + int i; + + for (i = 0; i < ACPI_PCIHP_MAX_HOTPLUG_BUS; ++i) { + acpi_pcihp_update_hotplug_bus(s, i); + } +} + +void acpi_pcihp_reset(AcpiPciHpState *s) +{ + acpi_pcihp_update(s); +} + +static void enable_device(AcpiPciHpState *s, unsigned bsel, int slot) +{ + s->acpi_pcihp_pci_status[bsel].device_present |= (1U << slot); +} + +static void disable_device(AcpiPciHpState *s, unsigned bsel, int slot) +{ + s->acpi_pcihp_pci_status[bsel].down |= (1U << slot); +} + +int acpi_pcihp_device_hotplug(AcpiPciHpState *s, PCIDevice *dev, + PCIHotplugState state) +{ + int slot = PCI_SLOT(dev->devfn); + int bsel = acpi_pcihp_get_bsel(dev->bus); + if (bsel < 0) { + return -1; + } + + /* Don't send event when device is enabled during qemu machine creation: + * it is present on boot, no hotplug event is necessary. We do send an + * event when the device is disabled later. */ + if (state == PCI_COLDPLUG_ENABLED) { + s->acpi_pcihp_pci_status[bsel].device_present |= (1U << slot); + return 0; + } + + if (state == PCI_HOTPLUG_ENABLED) { + enable_device(s, bsel, slot); + } else { + disable_device(s, bsel, slot); + } + + return 0; +} + +static uint64_t pci_read(void *opaque, hwaddr addr, unsigned int size) +{ + AcpiPciHpState *s = opaque; + uint32_t val = 0; + int bsel = s->hotplug_select; + + if (bsel < 0 || bsel > ACPI_PCIHP_MAX_HOTPLUG_BUS) { + return 0; + } + + switch (addr) { + case PCI_UP_BASE - PCI_HOTPLUG_ADDR: + /* Manufacture an "up" value to cause a device check on any hotplug + * slot with a device. Extra device checks are harmless. */ + val = s->acpi_pcihp_pci_status[bsel].device_present & + s->acpi_pcihp_pci_status[bsel].hotplug_enable; + ACPI_PCIHP_DPRINTF("pci_up_read %" PRIu32 "\n", val); + break; + case PCI_DOWN_BASE - PCI_HOTPLUG_ADDR: + val = s->acpi_pcihp_pci_status[bsel].down; + ACPI_PCIHP_DPRINTF("pci_down_read %" PRIu32 "\n", val); + break; + case PCI_EJ_BASE - PCI_HOTPLUG_ADDR: + /* No feature defined yet */ + ACPI_PCIHP_DPRINTF("pci_features_read %" PRIu32 "\n", val); + break; + case PCI_RMV_BASE - PCI_HOTPLUG_ADDR: + val = s->acpi_pcihp_pci_status[bsel].hotplug_enable; + ACPI_PCIHP_DPRINTF("pci_rmv_read %" PRIu32 "\n", val); + break; + case PCI_SEL_BASE - PCI_HOTPLUG_ADDR: + val = s->hotplug_select; + ACPI_PCIHP_DPRINTF("pci_sel_read %" PRIu32 "\n", val); + default: + break; + } + + return val; +} + +static void pci_write(void *opaque, hwaddr addr, uint64_t data, + unsigned int size) +{ + AcpiPciHpState *s = opaque; + switch (addr) { + case PCI_EJ_BASE - PCI_HOTPLUG_ADDR: + if (s->hotplug_select >= ACPI_PCIHP_MAX_HOTPLUG_BUS) { + break; + } + acpi_pcihp_eject_slot(s, s->hotplug_select, data); + ACPI_PCIHP_DPRINTF("pciej write %" HWADDR_PRIx " <== %" PRIu64 "\n", + addr, data); + break; + case PCI_SEL_BASE - PCI_HOTPLUG_ADDR: + s->hotplug_select = data; + ACPI_PCIHP_DPRINTF("pcisel write %" HWADDR_PRIx " <== %" PRIu64 "\n", + addr, data); + default: + break; + } +} + +static const MemoryRegionOps acpi_pcihp_io_ops = { + .read = pci_read, + .write = pci_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +void acpi_pcihp_init(AcpiPciHpState *s, PCIBus *root_bus, + MemoryRegion *address_space_io) +{ + s->root= root_bus; + memory_region_init_io(&s->io, NULL, &acpi_pcihp_io_ops, s, + "acpi-pci-hotplug", + PCI_HOTPLUG_SIZE); + memory_region_add_subregion(address_space_io, PCI_HOTPLUG_ADDR, &s->io); +} + +const VMStateDescription vmstate_acpi_pcihp_pci_status = { + .name = "acpi_pcihp_pci_status", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField []) { + VMSTATE_UINT32(up, AcpiPciHpPciStatus), + VMSTATE_UINT32(down, AcpiPciHpPciStatus), + VMSTATE_END_OF_LIST() + } +}; diff --git a/include/hw/acpi/pcihp.h b/include/hw/acpi/pcihp.h new file mode 100644 index 0000000000..6230e60954 --- /dev/null +++ b/include/hw/acpi/pcihp.h @@ -0,0 +1,72 @@ +/* + * QEMU<->ACPI BIOS PCI hotplug interface + * + * QEMU supports PCI hotplug via ACPI. This module + * implements the interface between QEMU and the ACPI BIOS. + * Interface specification - see docs/specs/acpi_pci_hotplug.txt + * + * Copyright (c) 2013, Red Hat Inc, Michael S. Tsirkin (mst@redhat.com) + * Copyright (c) 2006 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#ifndef HW_ACPI_PCIHP_H +#define HW_ACPI_PCIHP_H + +#include +#include +#include "hw/pci/pci.h" /* for PCIHotplugState */ + +typedef struct AcpiPciHpPciStatus { + uint32_t up; /* deprecated, maintained for migration compatibility */ + uint32_t down; + uint32_t hotplug_enable; + uint32_t device_present; +} AcpiPciHpPciStatus; + +#define ACPI_PCIHP_PROP_BSEL "acpi-pcihp-bsel" +#define ACPI_PCIHP_MAX_HOTPLUG_BUS 256 + +typedef struct AcpiPciHpState { + AcpiPciHpPciStatus acpi_pcihp_pci_status[ACPI_PCIHP_MAX_HOTPLUG_BUS]; + uint32_t hotplug_select; + PCIBus *root; + MemoryRegion io; +} AcpiPciHpState; + +void acpi_pcihp_init(AcpiPciHpState *, PCIBus *root, + MemoryRegion *address_space_io); + +/* Invoke on device hotplug */ +int acpi_pcihp_device_hotplug(AcpiPciHpState *, PCIDevice *, + PCIHotplugState state); + +/* Called on reset */ +void acpi_pcihp_reset(AcpiPciHpState *s); + +extern const VMStateDescription vmstate_acpi_pcihp_pci_status; + +#define VMSTATE_PCI_HOTPLUG(pcihp, state, test_pcihp) \ + VMSTATE_UINT32_TEST(pcihp.hotplug_select, state, \ + test_pcihp), \ + VMSTATE_STRUCT_ARRAY_TEST(pcihp.acpi_pcihp_pci_status, state, \ + ACPI_PCIHP_MAX_HOTPLUG_BUS, \ + test_pcihp, 1, \ + vmstate_acpi_pcihp_pci_status, \ + AcpiPciHpPciStatus) + +#endif -- cgit v1.2.3 From 9e047b982452c633882b486682966c1d97097015 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 14 Oct 2013 18:01:20 +0300 Subject: piix4: add acpi pci hotplug support Add support for acpi pci hotplug using the new infrastructure. PIIX4 legacy interface is maintained as is for machine types 1.7 and older. Signed-off-by: Michael S. Tsirkin --- hw/acpi/piix4.c | 75 +++++++++++++++++++++++++++++++++++++++++++++------- include/hw/i386/pc.h | 5 ++++ 2 files changed, 70 insertions(+), 10 deletions(-) diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index 20353b983e..1aa35bcb1b 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -30,6 +30,7 @@ #include "hw/nvram/fw_cfg.h" #include "exec/address-spaces.h" #include "hw/acpi/piix4.h" +#include "hw/acpi/pcihp.h" //#define DEBUG @@ -73,7 +74,6 @@ typedef struct PIIX4PMState { uint32_t io_base; MemoryRegion io_gpe; - MemoryRegion io_pci; MemoryRegion io_cpu; ACPIREGS ar; @@ -88,11 +88,16 @@ typedef struct PIIX4PMState { Notifier machine_ready; Notifier powerdown_notifier; - /* for pci hotplug */ + /* for legacy pci hotplug (compatible with qemu 1.6 and older) */ + MemoryRegion io_pci; struct pci_status pci0_status; uint32_t pci0_hotplug_enable; uint32_t pci0_slot_device_present; + /* for new pci hotplug (with PCI2PCI bridge support) */ + AcpiPciHpState acpi_pci_hotplug; + bool use_acpi_pci_hotplug; + uint8_t disable_s3; uint8_t disable_s4; uint8_t s4_val; @@ -263,6 +268,18 @@ static int acpi_load_old(QEMUFile *f, void *opaque, int version_id) return ret; } +static bool vmstate_test_use_acpi_pci_hotplug(void *opaque, int version_id) +{ + PIIX4PMState *s = opaque; + return s->use_acpi_pci_hotplug; +} + +static bool vmstate_test_no_use_acpi_pci_hotplug(void *opaque, int version_id) +{ + PIIX4PMState *s = opaque; + return !s->use_acpi_pci_hotplug; +} + /* qemu-kvm 1.2 uses version 3 but advertised as 2 * To support incoming qemu-kvm 1.2 migration, change version_id * and minimum_version_id to 2 below (which breaks migration from @@ -285,8 +302,12 @@ static const VMStateDescription vmstate_acpi = { VMSTATE_TIMER(ar.tmr.timer, PIIX4PMState), VMSTATE_INT64(ar.tmr.overflow_time, PIIX4PMState), VMSTATE_STRUCT(ar.gpe, PIIX4PMState, 2, vmstate_gpe, ACPIGPE), - VMSTATE_STRUCT(pci0_status, PIIX4PMState, 2, vmstate_pci_status, - struct pci_status), + VMSTATE_STRUCT_TEST(pci0_status, PIIX4PMState, + vmstate_test_no_use_acpi_pci_hotplug, + 2, vmstate_pci_status, + struct pci_status), + VMSTATE_PCI_HOTPLUG(acpi_pci_hotplug, PIIX4PMState, + vmstate_test_use_acpi_pci_hotplug), VMSTATE_END_OF_LIST() } }; @@ -364,7 +385,11 @@ static void piix4_reset(void *opaque) pci_conf[0x5B] = 0x02; } pm_io_space_update(s); - piix4_update_hotplug(s); + if (s->use_acpi_pci_hotplug) { + acpi_pcihp_reset(&s->acpi_pci_hotplug); + } else { + piix4_update_hotplug(s); + } } static void piix4_pm_powerdown_req(Notifier *n, void *opaque) @@ -375,6 +400,26 @@ static void piix4_pm_powerdown_req(Notifier *n, void *opaque) acpi_pm1_evt_power_down(&s->ar); } +static int piix4_acpi_pci_hotplug(DeviceState *qdev, PCIDevice *dev, + PCIHotplugState state) +{ + PIIX4PMState *s = PIIX4_PM(qdev); + int ret = acpi_pcihp_device_hotplug(&s->acpi_pci_hotplug, dev, state); + if (ret < 0) { + return ret; + } + s->ar.gpe.sts[0] |= PIIX4_PCI_HOTPLUG_STATUS; + + acpi_update_sci(&s->ar, s->irq); + return 0; +} + +static void piix4_update_bus_hotplug(PCIBus *bus, void *opaque) +{ + PIIX4PMState *s = opaque; + pci_bus_hotplug(bus, piix4_acpi_pci_hotplug, DEVICE(s)); +} + static void piix4_pm_machine_ready(Notifier *n, void *opaque) { PIIX4PMState *s = container_of(n, PIIX4PMState, machine_ready); @@ -388,6 +433,10 @@ static void piix4_pm_machine_ready(Notifier *n, void *opaque) pci_conf[0x63] = 0x60; pci_conf[0x67] = (memory_region_present(io_as, 0x3f8) ? 0x08 : 0) | (memory_region_present(io_as, 0x2f8) ? 0x90 : 0); + + if (s->use_acpi_pci_hotplug) { + pci_for_each_bus(d->bus, piix4_update_bus_hotplug, s); + } } static void piix4_pm_add_propeties(PIIX4PMState *s) @@ -509,6 +558,8 @@ static Property piix4_pm_properties[] = { DEFINE_PROP_UINT8(ACPI_PM_PROP_S3_DISABLED, PIIX4PMState, disable_s3, 0), DEFINE_PROP_UINT8(ACPI_PM_PROP_S4_DISABLED, PIIX4PMState, disable_s4, 0), DEFINE_PROP_UINT8(ACPI_PM_PROP_S4_VAL, PIIX4PMState, s4_val, 2), + DEFINE_PROP_BOOL("acpi-pci-hotplug-with-bridge-support", PIIX4PMState, + use_acpi_pci_hotplug, true), DEFINE_PROP_END_OF_LIST(), }; @@ -701,11 +752,15 @@ static void piix4_acpi_system_hot_add_init(MemoryRegion *parent, "acpi-gpe0", GPE_LEN); memory_region_add_subregion(parent, GPE_BASE, &s->io_gpe); - memory_region_init_io(&s->io_pci, OBJECT(s), &piix4_pci_ops, s, - "acpi-pci-hotplug", PCI_HOTPLUG_SIZE); - memory_region_add_subregion(parent, PCI_HOTPLUG_ADDR, - &s->io_pci); - pci_bus_hotplug(bus, piix4_device_hotplug, DEVICE(s)); + if (s->use_acpi_pci_hotplug) { + acpi_pcihp_init(&s->acpi_pci_hotplug, bus, parent); + } else { + memory_region_init_io(&s->io_pci, OBJECT(s), &piix4_pci_ops, s, + "acpi-pci-hotplug", PCI_HOTPLUG_SIZE); + memory_region_add_subregion(parent, PCI_HOTPLUG_ADDR, + &s->io_pci); + pci_bus_hotplug(bus, piix4_device_hotplug, DEVICE(s)); + } CPU_FOREACH(cpu) { CPUClass *cc = CPU_GET_CLASS(cpu); diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 7fe2bd17f6..92eabebb2c 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -265,6 +265,11 @@ int e820_add_entry(uint64_t, uint64_t, uint32_t); .driver = TYPE_USB_DEVICE,\ .property = "msos-desc",\ .value = "no",\ + },\ + {\ + .driver = "PIIX4_PM",\ + .property = "acpi-pci-hotplug-with-bridge-support",\ + .value = "off",\ } #define PC_COMPAT_1_6 \ -- cgit v1.2.3 From 99fd437dee468609de8218f0eb3b16621fb6a9c9 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 14 Oct 2013 18:01:29 +0300 Subject: acpi-build: enable hotplug for PCI bridges This enables support for device hotplug behind pci bridges. Bridge devices themselves need to be pre-configured on qemu command line. Design: - at machine init time, assign "bsel" property to bridges with hotplug support - dynamically (At ACPI table read) generate ACPI code to handle hotplug events for each bridge with "bsel" property Note: ACPI doesn't support adding or removing bridges by hotplug. We detect and prevent removal of bridges by hotplug, unless they were added by hotplug previously (and so, are not described by ACPI). Signed-off-by: Michael S. Tsirkin --- hw/i386/acpi-build.c | 346 +++++++++++++++++++++++++++++++++++++++---------- hw/i386/acpi-dsdt.dsl | 34 +++-- hw/i386/ssdt-pcihp.dsl | 11 +- 3 files changed, 297 insertions(+), 94 deletions(-) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 1d62866ec4..9cd3d0e698 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -40,6 +40,7 @@ /* Supported chipsets: */ #include "hw/acpi/piix4.h" +#include "hw/acpi/pcihp.h" #include "hw/i386/ich9.h" #include "hw/pci/pci_bus.h" #include "hw/pci-host/q35.h" @@ -79,6 +80,12 @@ typedef struct AcpiMiscInfo { uint16_t pvpanic_port; } AcpiMiscInfo; +typedef struct AcpiBuildPciBusHotplugState { + GArray *device_table; + GArray *notify_table; + struct AcpiBuildPciBusHotplugState *parent; +} AcpiBuildPciBusHotplugState; + static void acpi_get_dsdt(AcpiMiscInfo *info) { uint16_t *applesmc_sta; @@ -179,38 +186,6 @@ static void acpi_get_pm_info(AcpiPmInfo *pm) NULL); } -static void acpi_get_hotplug_info(AcpiMiscInfo *misc) -{ - int i; - PCIBus *bus = find_i440fx(); - - if (!bus) { - /* Only PIIX supports ACPI hotplug */ - memset(misc->slot_hotplug_enable, 0, sizeof misc->slot_hotplug_enable); - return; - } - - memset(misc->slot_hotplug_enable, 0xff, - DIV_ROUND_UP(PCI_SLOT_MAX, BITS_PER_BYTE)); - - for (i = 0; i < ARRAY_SIZE(bus->devices); ++i) { - PCIDeviceClass *pc; - PCIDevice *pdev = bus->devices[i]; - - if (!pdev) { - continue; - } - - pc = PCI_DEVICE_GET_CLASS(pdev); - - if (pc->no_hotplug) { - int slot = PCI_SLOT(i); - - clear_bit(slot, misc->slot_hotplug_enable); - } - } -} - static void acpi_get_misc_info(AcpiMiscInfo *info) { info->has_hpet = hpet_find(); @@ -376,6 +351,12 @@ static void build_package(GArray *package, uint8_t op, unsigned min_bytes) build_prepend_byte(package, op); } +static void build_extop_package(GArray *package, uint8_t op) +{ + build_package(package, op, 1); + build_prepend_byte(package, 0x5B); /* ExtOpPrefix */ +} + static void build_append_value(GArray *table, uint32_t value, int size) { uint8_t prefix; @@ -402,8 +383,44 @@ static void build_append_value(GArray *table, uint32_t value, int size) } } -static void build_append_notify_target(GArray *method, GArray *target_name, - uint32_t value, int size) +static void build_append_int(GArray *table, uint32_t value) +{ + if (value == 0x00) { + build_append_byte(table, 0x00); /* ZeroOp */ + } else if (value == 0x01) { + build_append_byte(table, 0x01); /* OneOp */ + } else if (value <= 0xFF) { + build_append_value(table, value, 1); + } else if (value <= 0xFFFFF) { + build_append_value(table, value, 2); + } else { + build_append_value(table, value, 4); + } +} + +static GArray *build_alloc_method(const char *name, uint8_t arg_count) +{ + GArray *method = build_alloc_array(); + + build_append_nameseg(method, "%s", name); + build_append_byte(method, arg_count); /* MethodFlags: ArgCount */ + + return method; +} + +static void build_append_and_cleanup_method(GArray *device, GArray *method) +{ + uint8_t op = 0x14; /* MethodOp */ + + build_package(method, op, 0); + + build_append_array(device, method); + build_free_array(method); +} + +static void build_append_notify_target_ifequal(GArray *method, + GArray *target_name, + uint32_t value, int size) { GArray *notify = build_alloc_array(); uint8_t op = 0xA0; /* IfOp */ @@ -423,6 +440,7 @@ static void build_append_notify_target(GArray *method, GArray *target_name, build_free_array(notify); } +/* End here */ #define ACPI_PORT_SMI_CMD 0x00b2 /* TODO: this is APM_CNT_IOPORT */ static inline void *acpi_data_push(GArray *table_data, unsigned size) @@ -632,44 +650,236 @@ static inline char acpi_get_hex(uint32_t val) #include "hw/i386/ssdt-pcihp.hex" static void -build_append_notify(GArray *device, const char *name, - const char *format, int skip, int count) +build_append_notify_method(GArray *device, const char *name, + const char *format, int count) { int i; - GArray *method = build_alloc_array(); - uint8_t op = 0x14; /* MethodOp */ + GArray *method = build_alloc_method(name, 2); - build_append_nameseg(method, "%s", name); - build_append_byte(method, 0x02); /* MethodFlags: ArgCount */ - for (i = skip; i < count; i++) { + for (i = 0; i < count; i++) { GArray *target = build_alloc_array(); build_append_nameseg(target, format, i); assert(i < 256); /* Fits in 1 byte */ - build_append_notify_target(method, target, i, 1); + build_append_notify_target_ifequal(method, target, i, 1); build_free_array(target); } - build_package(method, op, 2); - build_append_array(device, method); - build_free_array(method); + build_append_and_cleanup_method(device, method); } -static void patch_pcihp(int slot, uint8_t *ssdt_ptr, uint32_t eject) +static void patch_pcihp(int slot, uint8_t *ssdt_ptr) { - ssdt_ptr[ACPI_PCIHP_OFFSET_HEX] = acpi_get_hex(slot >> 4); - ssdt_ptr[ACPI_PCIHP_OFFSET_HEX + 1] = acpi_get_hex(slot); + unsigned devfn = PCI_DEVFN(slot, 0); + + ssdt_ptr[ACPI_PCIHP_OFFSET_HEX] = acpi_get_hex(devfn >> 4); + ssdt_ptr[ACPI_PCIHP_OFFSET_HEX + 1] = acpi_get_hex(devfn); ssdt_ptr[ACPI_PCIHP_OFFSET_ID] = slot; ssdt_ptr[ACPI_PCIHP_OFFSET_ADR + 2] = slot; +} + +/* Assign BSEL property to all buses. In the future, this can be changed + * to only assign to buses that support hotplug. + */ +static void *acpi_set_bsel(PCIBus *bus, void *opaque) +{ + unsigned *bsel_alloc = opaque; + unsigned *bus_bsel; + + if (bus->qbus.allow_hotplug) { + bus_bsel = g_malloc(sizeof *bus_bsel); + + *bus_bsel = (*bsel_alloc)++; + object_property_add_uint32_ptr(OBJECT(bus), ACPI_PCIHP_PROP_BSEL, + bus_bsel, NULL); + } + + return bsel_alloc; +} + +static void acpi_set_pci_info(void) +{ + PCIBus *bus = find_i440fx(); /* TODO: Q35 support */ + unsigned bsel_alloc = 0; + + if (bus) { + /* Scan all PCI buses. Set property to enable acpi based hotplug. */ + pci_for_each_bus_depth_first(bus, acpi_set_bsel, NULL, &bsel_alloc); + } +} + +static void build_pci_bus_state_init(AcpiBuildPciBusHotplugState *state, + AcpiBuildPciBusHotplugState *parent) +{ + state->parent = parent; + state->device_table = build_alloc_array(); + state->notify_table = build_alloc_array(); +} + +static void build_pci_bus_state_cleanup(AcpiBuildPciBusHotplugState *state) +{ + build_free_array(state->device_table); + build_free_array(state->notify_table); +} + +static void *build_pci_bus_begin(PCIBus *bus, void *parent_state) +{ + AcpiBuildPciBusHotplugState *parent = parent_state; + AcpiBuildPciBusHotplugState *child = g_malloc(sizeof *child); + + build_pci_bus_state_init(child, parent); + + return child; +} + +static void build_pci_bus_end(PCIBus *bus, void *bus_state) +{ + AcpiBuildPciBusHotplugState *child = bus_state; + AcpiBuildPciBusHotplugState *parent = child->parent; + GArray *bus_table = build_alloc_array(); + DECLARE_BITMAP(slot_hotplug_enable, PCI_SLOT_MAX); + uint8_t op; + int i; + QObject *bsel; + GArray *method; + bool bus_hotplug_support = false; + + if (bus->parent_dev) { + op = 0x82; /* DeviceOp */ + build_append_nameseg(bus_table, "S%.02X_", + bus->parent_dev->devfn); + build_append_byte(bus_table, 0x08); /* NameOp */ + build_append_nameseg(bus_table, "_SUN"); + build_append_value(bus_table, PCI_SLOT(bus->parent_dev->devfn), 1); + build_append_byte(bus_table, 0x08); /* NameOp */ + build_append_nameseg(bus_table, "_ADR"); + build_append_value(bus_table, (PCI_SLOT(bus->parent_dev->devfn) << 16) | + PCI_FUNC(bus->parent_dev->devfn), 4); + } else { + op = 0x10; /* ScopeOp */; + build_append_nameseg(bus_table, "PCI0"); + } - /* Runtime patching of ACPI_EJ0: to disable hotplug for a slot, - * replace the method name: _EJ0 by ACPI_EJ0_. + bsel = object_property_get_qobject(OBJECT(bus), ACPI_PCIHP_PROP_BSEL, NULL); + if (bsel) { + build_append_byte(bus_table, 0x08); /* NameOp */ + build_append_nameseg(bus_table, "BSEL"); + build_append_int(bus_table, qint_get_int(qobject_to_qint(bsel))); + + memset(slot_hotplug_enable, 0xff, sizeof slot_hotplug_enable); + + for (i = 0; i < ARRAY_SIZE(bus->devices); ++i) { + PCIDeviceClass *pc; + PCIDevice *pdev = bus->devices[i]; + + if (!pdev) { + continue; + } + + pc = PCI_DEVICE_GET_CLASS(pdev); + + if (pc->no_hotplug || pc->is_bridge) { + int slot = PCI_SLOT(i); + + clear_bit(slot, slot_hotplug_enable); + } + } + + /* Append Device object for each slot which supports eject */ + for (i = 0; i < PCI_SLOT_MAX; i++) { + bool can_eject = test_bit(i, slot_hotplug_enable); + if (can_eject) { + void *pcihp = acpi_data_push(bus_table, + ACPI_PCIHP_SIZEOF); + memcpy(pcihp, ACPI_PCIHP_AML, ACPI_PCIHP_SIZEOF); + patch_pcihp(i, pcihp); + bus_hotplug_support = true; + } + } + + method = build_alloc_method("DVNT", 2); + + for (i = 0; i < PCI_SLOT_MAX; i++) { + GArray *notify; + uint8_t op; + + if (!test_bit(i, slot_hotplug_enable)) { + continue; + } + + notify = build_alloc_array(); + op = 0xA0; /* IfOp */ + + build_append_byte(notify, 0x7B); /* AndOp */ + build_append_byte(notify, 0x68); /* Arg0Op */ + build_append_int(notify, 0x1 << i); + build_append_byte(notify, 0x00); /* NullName */ + build_append_byte(notify, 0x86); /* NotifyOp */ + build_append_nameseg(notify, "S%.02X_", PCI_DEVFN(i, 0)); + build_append_byte(notify, 0x69); /* Arg1Op */ + + /* Pack it up */ + build_package(notify, op, 0); + + build_append_array(method, notify); + + build_free_array(notify); + } + + build_append_and_cleanup_method(bus_table, method); + } + + /* Append PCNT method to notify about events on local and child buses. + * Add unconditionally for root since DSDT expects it. */ - /* Sanity check */ - assert(!memcmp(ssdt_ptr + ACPI_PCIHP_OFFSET_EJ0, "_EJ0", 4)); + if (bus_hotplug_support || child->notify_table->len || !bus->parent_dev) { + method = build_alloc_method("PCNT", 0); + + /* If bus supports hotplug select it and notify about local events */ + if (bsel) { + build_append_byte(method, 0x70); /* StoreOp */ + build_append_int(method, qint_get_int(qobject_to_qint(bsel))); + build_append_nameseg(method, "BNUM"); + build_append_nameseg(method, "DVNT"); + build_append_nameseg(method, "PCIU"); + build_append_int(method, 1); /* Device Check */ + build_append_nameseg(method, "DVNT"); + build_append_nameseg(method, "PCID"); + build_append_int(method, 3); /* Eject Request */ + } + + /* Notify about child bus events in any case */ + build_append_array(method, child->notify_table); + + build_append_and_cleanup_method(bus_table, method); + + /* Append description of child buses */ + build_append_array(bus_table, child->device_table); + + /* Pack it up */ + if (bus->parent_dev) { + build_extop_package(bus_table, op); + } else { + build_package(bus_table, op, 0); + } - if (!eject) { - memcpy(ssdt_ptr + ACPI_PCIHP_OFFSET_EJ0, "EJ0_", 4); + /* Append our bus description to parent table */ + build_append_array(parent->device_table, bus_table); + + /* Also tell parent how to notify us, invoking PCNT method. + * At the moment this is not needed for root as we have a single root. + */ + if (bus->parent_dev) { + build_append_byte(parent->notify_table, '^'); /* ParentPrefixChar */ + build_append_byte(parent->notify_table, 0x2E); /* DualNamePrefix */ + build_append_nameseg(parent->notify_table, "S%.02X_", + bus->parent_dev->devfn); + build_append_nameseg(parent->notify_table, "PCNT"); + } } + + build_free_array(bus_table); + build_pci_bus_state_cleanup(child); + g_free(child); } static void patch_pci_windows(PcPciInfo *pci, uint8_t *start, unsigned size) @@ -741,7 +951,7 @@ build_ssdt(GArray *table_data, GArray *linker, * Method(NTFY, 2) {If (LEqual(Arg0, 0x00)) {Notify(CP00, Arg1)} ...} */ /* Arg0 = Processor ID = APIC ID */ - build_append_notify(sb_scope, "NTFY", "CP%0.02X", 0, acpi_cpus); + build_append_notify_method(sb_scope, "NTFY", "CP%0.02X", acpi_cpus); /* build "Name(CPON, Package() { One, One, ..., Zero, Zero, ... })" */ build_append_byte(sb_scope, 0x08); /* NameOp */ @@ -763,24 +973,19 @@ build_ssdt(GArray *table_data, GArray *linker, } { - GArray *pci0 = build_alloc_array(); - uint8_t op = 0x10; /* ScopeOp */; + AcpiBuildPciBusHotplugState hotplug_state; + PCIBus *bus = find_i440fx(); /* TODO: Q35 support */ - build_append_nameseg(pci0, "PCI0"); + build_pci_bus_state_init(&hotplug_state, NULL); - /* build Device object for each slot */ - for (i = 1; i < PCI_SLOT_MAX; i++) { - bool eject = test_bit(i, misc->slot_hotplug_enable); - void *pcihp = acpi_data_push(pci0, ACPI_PCIHP_SIZEOF); - - memcpy(pcihp, ACPI_PCIHP_AML, ACPI_PCIHP_SIZEOF); - patch_pcihp(i, pcihp, eject); + if (bus) { + /* Scan all PCI buses. Generate tables to support hotplug. */ + pci_for_each_bus_depth_first(bus, build_pci_bus_begin, + build_pci_bus_end, &hotplug_state); } - build_append_notify(pci0, "PCNT", "S%0.02X_", 1, PCI_SLOT_MAX); - build_package(pci0, op, 3); - build_append_array(sb_scope, pci0); - build_free_array(pci0); + build_append_array(sb_scope, hotplug_state.device_table); + build_pci_bus_state_cleanup(&hotplug_state); } build_package(sb_scope, op, 3); @@ -1063,7 +1268,6 @@ void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables) acpi_get_cpu_info(&cpu); acpi_get_pm_info(&pm); acpi_get_dsdt(&misc); - acpi_get_hotplug_info(&misc); acpi_get_misc_info(&misc); acpi_get_pci_info(&pci); @@ -1208,6 +1412,8 @@ void acpi_setup(PcGuestInfo *guest_info) build_state->guest_info = guest_info; + acpi_set_pci_info(); + acpi_build_tables_init(&tables); acpi_build(build_state->guest_info, &tables); diff --git a/hw/i386/acpi-dsdt.dsl b/hw/i386/acpi-dsdt.dsl index b87c6e0ae8..6d76f30398 100644 --- a/hw/i386/acpi-dsdt.dsl +++ b/hw/i386/acpi-dsdt.dsl @@ -134,32 +134,28 @@ DefinitionBlock ( B0EJ, 32, } + OperationRegion(BNMR, SystemIO, 0xae10, 0x04) + Field(BNMR, DWordAcc, NoLock, WriteAsZeros) { + BNUM, 32, + } + + /* Lock to protect access to fields above. */ + Mutex(BLCK, 0) + /* Methods called by bulk generated PCI devices below */ /* Methods called by hotplug devices */ - Method(PCEJ, 1, NotSerialized) { + Method(PCEJ, 2, NotSerialized) { // _EJ0 method - eject callback - Store(ShiftLeft(1, Arg0), B0EJ) + Acquire(BLCK, 0xFFFF) + Store(Arg0, BNUM) + Store(ShiftLeft(1, Arg1), B0EJ) + Release(BLCK) Return (0x0) } /* Hotplug notification method supplied by SSDT */ External(\_SB.PCI0.PCNT, MethodObj) - - /* PCI hotplug notify method */ - Method(PCNF, 0) { - // Local0 = iterator - Store(Zero, Local0) - While (LLess(Local0, 31)) { - Increment(Local0) - If (And(PCIU, ShiftLeft(1, Local0))) { - PCNT(Local0, 1) - } - If (And(PCID, ShiftLeft(1, Local0))) { - PCNT(Local0, 3) - } - } - } } @@ -308,7 +304,9 @@ DefinitionBlock ( } Method(_E01) { // PCI hotplug event - \_SB.PCI0.PCNF() + Acquire(\_SB.PCI0.BLCK, 0xFFFF) + \_SB.PCI0.PCNT() + Release(\_SB.PCI0.BLCK) } Method(_E02) { // CPU hotplug event diff --git a/hw/i386/ssdt-pcihp.dsl b/hw/i386/ssdt-pcihp.dsl index d29a5b95d2..cc245c3e7c 100644 --- a/hw/i386/ssdt-pcihp.dsl +++ b/hw/i386/ssdt-pcihp.dsl @@ -25,6 +25,7 @@ DefinitionBlock ("ssdt-pcihp.aml", "SSDT", 0x01, "BXPC", "BXSSDTPCIHP", 0x1) /* Objects supplied by DSDT */ External(\_SB.PCI0, DeviceObj) External(\_SB.PCI0.PCEJ, MethodObj) + External(BSEL, IntObj) Scope(\_SB.PCI0) { @@ -33,19 +34,17 @@ DefinitionBlock ("ssdt-pcihp.aml", "SSDT", 0x01, "BXPC", "BXSSDTPCIHP", 0x1) ACPI_EXTRACT_DEVICE_END ssdt_pcihp_end ACPI_EXTRACT_DEVICE_STRING ssdt_pcihp_name - // Method _EJ0 can be patched by BIOS to EJ0_ - // at runtime, if the slot is detected to not support hotplug. - // Extract the offset of the address dword and the - // _EJ0 name to allow this patching. + // Extract the offsets of the device name, address dword and the slot + // name byte - we fill them in for each device. Device(SAA) { ACPI_EXTRACT_NAME_BYTE_CONST ssdt_pcihp_id Name(_SUN, 0xAA) ACPI_EXTRACT_NAME_DWORD_CONST ssdt_pcihp_adr Name(_ADR, 0xAA0000) - ACPI_EXTRACT_METHOD_STRING ssdt_pcihp_ej0 Method(_EJ0, 1) { - Return (PCEJ(_SUN)) + PCEJ(BSEL, _SUN) } } + } } -- cgit v1.2.3 From 81cea5e7f2aa61c6c5d33d28bceb81276603d3c9 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 9 Jan 2014 17:36:31 +0100 Subject: acpi: factor out common cpu hotplug code for PIIX4/Q35 .. so it could be used for adding CPU hotplug to Q35 machine Add an additional header with that will be shared between C and ASL code: include/hw/acpi/cpu_hotplug_defs.h Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/Makefile.objs | 2 +- hw/acpi/cpu_hotplug.c | 64 +++++++++++++++++++++++++++++++ hw/acpi/piix4.c | 77 +++----------------------------------- include/hw/acpi/cpu_hotplug.h | 27 +++++++++++++ include/hw/acpi/cpu_hotplug_defs.h | 22 +++++++++++ 5 files changed, 120 insertions(+), 72 deletions(-) create mode 100644 hw/acpi/cpu_hotplug.c create mode 100644 include/hw/acpi/cpu_hotplug.h create mode 100644 include/hw/acpi/cpu_hotplug_defs.h diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs index 841eca2e7c..397d32babd 100644 --- a/hw/acpi/Makefile.objs +++ b/hw/acpi/Makefile.objs @@ -1 +1 @@ -common-obj-$(CONFIG_ACPI) += core.o piix4.o ich9.o pcihp.o +common-obj-$(CONFIG_ACPI) += core.o piix4.o ich9.o pcihp.o cpu_hotplug.o diff --git a/hw/acpi/cpu_hotplug.c b/hw/acpi/cpu_hotplug.c new file mode 100644 index 0000000000..48928dc0ea --- /dev/null +++ b/hw/acpi/cpu_hotplug.c @@ -0,0 +1,64 @@ +/* + * QEMU ACPI hotplug utilities + * + * Copyright (C) 2013 Red Hat Inc + * + * Authors: + * Igor Mammedov + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#include "hw/hw.h" +#include "hw/acpi/cpu_hotplug.h" + +static uint64_t cpu_status_read(void *opaque, hwaddr addr, unsigned int size) +{ + AcpiCpuHotplug *cpus = opaque; + uint64_t val = cpus->sts[addr]; + + return val; +} + +static void cpu_status_write(void *opaque, hwaddr addr, uint64_t data, + unsigned int size) +{ + /* TODO: implement VCPU removal on guest signal that CPU can be removed */ +} + +static const MemoryRegionOps AcpiCpuHotplug_ops = { + .read = cpu_status_read, + .write = cpu_status_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 1, + }, +}; + +void AcpiCpuHotplug_add(ACPIGPE *gpe, AcpiCpuHotplug *g, CPUState *cpu) +{ + CPUClass *k = CPU_GET_CLASS(cpu); + int64_t cpu_id; + + *gpe->sts = *gpe->sts | ACPI_CPU_HOTPLUG_STATUS; + cpu_id = k->get_arch_id(CPU(cpu)); + g->sts[cpu_id / 8] |= (1 << (cpu_id % 8)); +} + +void AcpiCpuHotplug_init(MemoryRegion *parent, Object *owner, + AcpiCpuHotplug *gpe_cpu, uint16_t base) +{ + CPUState *cpu; + + CPU_FOREACH(cpu) { + CPUClass *cc = CPU_GET_CLASS(cpu); + int64_t id = cc->get_arch_id(cpu); + + g_assert((id / 8) < ACPI_GPE_PROC_LEN); + gpe_cpu->sts[id / 8] |= (1 << (id % 8)); + } + memory_region_init_io(&gpe_cpu->io, owner, &AcpiCpuHotplug_ops, + gpe_cpu, "acpi-cpu-hotplug", ACPI_GPE_PROC_LEN); + memory_region_add_subregion(parent, base, &gpe_cpu->io); +} diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index 1aa35bcb1b..12f8dde178 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -31,6 +31,7 @@ #include "exec/address-spaces.h" #include "hw/acpi/piix4.h" #include "hw/acpi/pcihp.h" +#include "hw/acpi/cpu_hotplug.h" //#define DEBUG @@ -51,20 +52,14 @@ #define PCI_RMV_BASE 0xae0c #define PIIX4_PROC_BASE 0xaf00 -#define PIIX4_PROC_LEN 32 #define PIIX4_PCI_HOTPLUG_STATUS 2 -#define PIIX4_CPU_HOTPLUG_STATUS 4 struct pci_status { uint32_t up; /* deprecated, maintained for migration compatibility */ uint32_t down; }; -typedef struct CPUStatus { - uint8_t sts[PIIX4_PROC_LEN]; -} CPUStatus; - typedef struct PIIX4PMState { /*< private >*/ PCIDevice parent_obj; @@ -74,7 +69,6 @@ typedef struct PIIX4PMState { uint32_t io_base; MemoryRegion io_gpe; - MemoryRegion io_cpu; ACPIREGS ar; APMState apm; @@ -102,7 +96,7 @@ typedef struct PIIX4PMState { uint8_t disable_s4; uint8_t s4_val; - CPUStatus gpe_cpu; + AcpiCpuHotplug gpe_cpu; Notifier cpu_added_notifier; } PIIX4PMState; @@ -683,61 +677,13 @@ static const MemoryRegionOps piix4_pci_ops = { }, }; -static uint64_t cpu_status_read(void *opaque, hwaddr addr, unsigned int size) -{ - PIIX4PMState *s = opaque; - CPUStatus *cpus = &s->gpe_cpu; - uint64_t val = cpus->sts[addr]; - - return val; -} - -static void cpu_status_write(void *opaque, hwaddr addr, uint64_t data, - unsigned int size) -{ - /* TODO: implement VCPU removal on guest signal that CPU can be removed */ -} - -static const MemoryRegionOps cpu_hotplug_ops = { - .read = cpu_status_read, - .write = cpu_status_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 1, - }, -}; - -typedef enum { - PLUG, - UNPLUG, -} HotplugEventType; - -static void piix4_cpu_hotplug_req(PIIX4PMState *s, CPUState *cpu, - HotplugEventType action) -{ - CPUStatus *g = &s->gpe_cpu; - ACPIGPE *gpe = &s->ar.gpe; - CPUClass *k = CPU_GET_CLASS(cpu); - int64_t cpu_id; - - assert(s != NULL); - - *gpe->sts = *gpe->sts | PIIX4_CPU_HOTPLUG_STATUS; - cpu_id = k->get_arch_id(CPU(cpu)); - if (action == PLUG) { - g->sts[cpu_id / 8] |= (1 << (cpu_id % 8)); - } else { - g->sts[cpu_id / 8] &= ~(1 << (cpu_id % 8)); - } - acpi_update_sci(&s->ar, s->irq); -} - static void piix4_cpu_added_req(Notifier *n, void *opaque) { PIIX4PMState *s = container_of(n, PIIX4PMState, cpu_added_notifier); - piix4_cpu_hotplug_req(s, CPU(opaque), PLUG); + assert(s != NULL); + AcpiCpuHotplug_add(&s->ar.gpe, &s->gpe_cpu, CPU(opaque)); + acpi_update_sci(&s->ar, s->irq); } static int piix4_device_hotplug(DeviceState *qdev, PCIDevice *dev, @@ -746,8 +692,6 @@ static int piix4_device_hotplug(DeviceState *qdev, PCIDevice *dev, static void piix4_acpi_system_hot_add_init(MemoryRegion *parent, PCIBus *bus, PIIX4PMState *s) { - CPUState *cpu; - memory_region_init_io(&s->io_gpe, OBJECT(s), &piix4_gpe_ops, s, "acpi-gpe0", GPE_LEN); memory_region_add_subregion(parent, GPE_BASE, &s->io_gpe); @@ -762,16 +706,7 @@ static void piix4_acpi_system_hot_add_init(MemoryRegion *parent, pci_bus_hotplug(bus, piix4_device_hotplug, DEVICE(s)); } - CPU_FOREACH(cpu) { - CPUClass *cc = CPU_GET_CLASS(cpu); - int64_t id = cc->get_arch_id(cpu); - - g_assert((id / 8) < PIIX4_PROC_LEN); - s->gpe_cpu.sts[id / 8] |= (1 << (id % 8)); - } - memory_region_init_io(&s->io_cpu, OBJECT(s), &cpu_hotplug_ops, s, - "acpi-cpu-hotplug", PIIX4_PROC_LEN); - memory_region_add_subregion(parent, PIIX4_PROC_BASE, &s->io_cpu); + AcpiCpuHotplug_init(parent, OBJECT(s), &s->gpe_cpu, PIIX4_PROC_BASE); s->cpu_added_notifier.notify = piix4_cpu_added_req; qemu_register_cpu_added_notifier(&s->cpu_added_notifier); } diff --git a/include/hw/acpi/cpu_hotplug.h b/include/hw/acpi/cpu_hotplug.h new file mode 100644 index 0000000000..4576400fd7 --- /dev/null +++ b/include/hw/acpi/cpu_hotplug.h @@ -0,0 +1,27 @@ +/* + * QEMU ACPI hotplug utilities + * + * Copyright (C) 2013 Red Hat Inc + * + * Authors: + * Igor Mammedov + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#ifndef ACPI_HOTPLUG_H +#define ACPI_HOTPLUG_H + +#include "hw/acpi/acpi.h" +#include "hw/acpi/cpu_hotplug_defs.h" + +typedef struct AcpiCpuHotplug { + MemoryRegion io; + uint8_t sts[ACPI_GPE_PROC_LEN]; +} AcpiCpuHotplug; + +void AcpiCpuHotplug_add(ACPIGPE *gpe, AcpiCpuHotplug *g, CPUState *cpu); + +void AcpiCpuHotplug_init(MemoryRegion *parent, Object *owner, + AcpiCpuHotplug *gpe_cpu, uint16_t base); +#endif diff --git a/include/hw/acpi/cpu_hotplug_defs.h b/include/hw/acpi/cpu_hotplug_defs.h new file mode 100644 index 0000000000..c6c4f78413 --- /dev/null +++ b/include/hw/acpi/cpu_hotplug_defs.h @@ -0,0 +1,22 @@ +/* + * QEMU ACPI hotplug utilities shared defines + * + * Copyright (C) 2013 Red Hat Inc + * + * Authors: + * Igor Mammedov + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#ifndef ACPI_HOTPLUG_DEFS_H +#define ACPI_HOTPLUG_DEFS_H + +/* + * ONLY DEFINEs are permited in this file since it's shared + * between C and ASL code. + */ +#define ACPI_CPU_HOTPLUG_STATUS 4 +#define ACPI_GPE_PROC_LEN 32 + +#endif -- cgit v1.2.3 From d6610bc2adc19a632cb14fc094378cbf5cd60868 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 9 Jan 2014 17:36:32 +0100 Subject: acpi: ich9: add CPU hotplug handling to Q35 machine .. use IO port 0cd8-0xcf7 range for CPU present bitmap Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- docs/specs/acpi_cpu_hotplug.txt | 4 +++- hw/acpi/ich9.c | 14 ++++++++++++++ include/hw/acpi/cpu_hotplug_defs.h | 1 + include/hw/acpi/ich9.h | 4 ++++ 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/docs/specs/acpi_cpu_hotplug.txt b/docs/specs/acpi_cpu_hotplug.txt index f6f577457d..340b751a95 100644 --- a/docs/specs/acpi_cpu_hotplug.txt +++ b/docs/specs/acpi_cpu_hotplug.txt @@ -10,7 +10,9 @@ ACPI GPE block (IO ports 0xafe0-0xafe3, byte access): Generic ACPI GPE block. Bit 2 (GPE.2) used to notify CPU hot-add/remove event to ACPI BIOS, via SCI interrupt. -CPU present bitmap (IO port 0xaf00-0xaf1f, 1-byte access): +CPU present bitmap for: + ICH9-LPC (IO port 0x0cd8-0xcf7, 1-byte access) + PIIX-PM (IO port 0xaf00-0xaf1f, 1-byte access) --------------------------------------------------------------- One bit per CPU. Bit position reflects corresponding CPU APIC ID. Read-only. diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c index 30f0df8713..0afac425ec 100644 --- a/hw/acpi/ich9.c +++ b/hw/acpi/ich9.c @@ -185,6 +185,15 @@ static void pm_powerdown_req(Notifier *n, void *opaque) acpi_pm1_evt_power_down(&pm->acpi_regs); } +static void ich9_cpu_added_req(Notifier *n, void *opaque) +{ + ICH9LPCPMRegs *pm = container_of(n, ICH9LPCPMRegs, cpu_added_notifier); + + assert(pm != NULL); + AcpiCpuHotplug_add(&pm->acpi_regs.gpe, &pm->gpe_cpu, CPU(opaque)); + acpi_update_sci(&pm->acpi_regs, pm->irq); +} + void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, qemu_irq sci_irq) { @@ -210,6 +219,11 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, qemu_register_reset(pm_reset, pm); pm->powerdown_notifier.notify = pm_powerdown_req; qemu_register_powerdown_notifier(&pm->powerdown_notifier); + + AcpiCpuHotplug_init(pci_address_space_io(lpc_pci), OBJECT(lpc_pci), + &pm->gpe_cpu, ICH9_CPU_HOTPLUG_IO_BASE); + pm->cpu_added_notifier.notify = ich9_cpu_added_req; + qemu_register_cpu_added_notifier(&pm->cpu_added_notifier); } static void ich9_pm_get_gpe0_blk(Object *obj, Visitor *v, diff --git a/include/hw/acpi/cpu_hotplug_defs.h b/include/hw/acpi/cpu_hotplug_defs.h index c6c4f78413..47a35e3373 100644 --- a/include/hw/acpi/cpu_hotplug_defs.h +++ b/include/hw/acpi/cpu_hotplug_defs.h @@ -18,5 +18,6 @@ */ #define ACPI_CPU_HOTPLUG_STATUS 4 #define ACPI_GPE_PROC_LEN 32 +#define ICH9_CPU_HOTPLUG_IO_BASE 0x0CD8 #endif diff --git a/include/hw/acpi/ich9.h b/include/hw/acpi/ich9.h index 82fcf9f2eb..104f419852 100644 --- a/include/hw/acpi/ich9.h +++ b/include/hw/acpi/ich9.h @@ -22,6 +22,7 @@ #define HW_ACPI_ICH9_H #include "hw/acpi/acpi.h" +#include "hw/acpi/cpu_hotplug.h" typedef struct ICH9LPCPMRegs { /* @@ -42,6 +43,9 @@ typedef struct ICH9LPCPMRegs { uint32_t pm_io_base; Notifier powerdown_notifier; + + AcpiCpuHotplug gpe_cpu; + Notifier cpu_added_notifier; } ICH9LPCPMRegs; void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, -- cgit v1.2.3 From 4782434f3cc016fbab16228e92e90265cf6db908 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 9 Jan 2014 17:36:34 +0100 Subject: pc: set PRST base in DSDT depending on chipset Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/acpi-dsdt-cpu-hotplug.dsl | 2 +- hw/i386/acpi-dsdt.dsl | 1 + hw/i386/q35-acpi-dsdt.dsl | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/i386/acpi-dsdt-cpu-hotplug.dsl b/hw/i386/acpi-dsdt-cpu-hotplug.dsl index 995b415bae..1dfbb4f086 100644 --- a/hw/i386/acpi-dsdt-cpu-hotplug.dsl +++ b/hw/i386/acpi-dsdt-cpu-hotplug.dsl @@ -52,7 +52,7 @@ Scope(\_SB) { Sleep(200) } - OperationRegion(PRST, SystemIO, 0xaf00, 32) + OperationRegion(PRST, SystemIO, CPU_STATUS_BASE, 32) Field(PRST, ByteAcc, NoLock, Preserve) { PRS, 256 } diff --git a/hw/i386/acpi-dsdt.dsl b/hw/i386/acpi-dsdt.dsl index 6d76f30398..3dc4789e50 100644 --- a/hw/i386/acpi-dsdt.dsl +++ b/hw/i386/acpi-dsdt.dsl @@ -290,6 +290,7 @@ DefinitionBlock ( } } +#define CPU_STATUS_BASE 0xaf00 #include "acpi-dsdt-cpu-hotplug.dsl" diff --git a/hw/i386/q35-acpi-dsdt.dsl b/hw/i386/q35-acpi-dsdt.dsl index ee38fd6580..9a43947651 100644 --- a/hw/i386/q35-acpi-dsdt.dsl +++ b/hw/i386/q35-acpi-dsdt.dsl @@ -405,6 +405,7 @@ DefinitionBlock ( define_gsi_link(GSIH, 0, 0x17) } +#define CPU_STATUS_BASE 0x0CD8 #include "acpi-dsdt-cpu-hotplug.dsl" -- cgit v1.2.3 From 1aa149b479a479323121251f1e8e676765cb354d Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 9 Jan 2014 17:36:35 +0100 Subject: pc: PIIX DSDT: exclude CPU/PCI hotplug & GPE0 IO range from PCI bus resources .. so that they might not be used by PCI devices. Note: Resort to concatenating templates with preprocessor help, because 1.0b spec isn't supporting ConcatenateResTemplate, as result Windows XP fails to execute PCI0._CRS method if ConcatenateResTemplate() is used. Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/acpi-dsdt-pci-crs.dsl | 8 +------- hw/i386/acpi-dsdt.dsl | 32 ++++++++++++++++++++++++++++++++ hw/i386/q35-acpi-dsdt.dsl | 8 ++++++++ 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/hw/i386/acpi-dsdt-pci-crs.dsl b/hw/i386/acpi-dsdt-pci-crs.dsl index b375a19cf6..8b631d1a0c 100644 --- a/hw/i386/acpi-dsdt-pci-crs.dsl +++ b/hw/i386/acpi-dsdt-pci-crs.dsl @@ -37,13 +37,7 @@ Scope(\_SB.PCI0) { 0x0000, // Address Translation Offset 0x0CF8, // Address Length ,, , TypeStatic) - WordIO(ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, - 0x0000, // Address Space Granularity - 0x0D00, // Address Range Minimum - 0xFFFF, // Address Range Maximum - 0x0000, // Address Translation Offset - 0xF300, // Address Length - ,, , TypeStatic) + BOARD_SPECIFIC_PCI_RESOURSES DWordMemory(ResourceProducer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite, 0x00000000, // Address Space Granularity 0x000A0000, // Address Range Minimum diff --git a/hw/i386/acpi-dsdt.dsl b/hw/i386/acpi-dsdt.dsl index 3dc4789e50..f501c8dc00 100644 --- a/hw/i386/acpi-dsdt.dsl +++ b/hw/i386/acpi-dsdt.dsl @@ -35,6 +35,38 @@ DefinitionBlock ( /**************************************************************** * PCI Bus definition ****************************************************************/ +#define BOARD_SPECIFIC_PCI_RESOURSES \ + WordIO(ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, \ + 0x0000, \ + 0x0D00, \ + 0xADFF, \ + 0x0000, \ + 0xA100, \ + ,, , TypeStatic) \ + /* 0xae00-0xae0e hole for PCI hotplug, hw/acpi/piix4.c:PCI_HOTPLUG_ADDR */ \ + WordIO(ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, \ + 0x0000, \ + 0xAE0F, \ + 0xAEFF, \ + 0x0000, \ + 0x00F1, \ + ,, , TypeStatic) \ + /* 0xaf00-0xaf1f hole for CPU hotplug, hw/acpi/piix4.c:PIIX4_PROC_BASE */ \ + WordIO(ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, \ + 0x0000, \ + 0xAF20, \ + 0xAFDF, \ + 0x0000, \ + 0x00C0, \ + ,, , TypeStatic) \ + /* 0xafe0-0xafe3 hole for ACPI.GPE0, hw/acpi/piix4.c:GPE_BASE */ \ + WordIO(ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, \ + 0x0000, \ + 0xAFE4, \ + 0xFFFF, \ + 0x0000, \ + 0x501C, \ + ,, , TypeStatic) Scope(\_SB) { Device(PCI0) { diff --git a/hw/i386/q35-acpi-dsdt.dsl b/hw/i386/q35-acpi-dsdt.dsl index 9a43947651..f3e5921079 100644 --- a/hw/i386/q35-acpi-dsdt.dsl +++ b/hw/i386/q35-acpi-dsdt.dsl @@ -48,6 +48,14 @@ DefinitionBlock ( /**************************************************************** * PCI Bus definition ****************************************************************/ +#define BOARD_SPECIFIC_PCI_RESOURSES \ + WordIO(ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, \ + 0x0000, \ + 0x0D00, \ + 0xFFFF, \ + 0x0000, \ + 0xF300, \ + ,, , TypeStatic) Scope(\_SB) { Device(PCI0) { -- cgit v1.2.3 From 562e56a9f8e627b2a4ef60037507361ce3cb4e6d Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 9 Jan 2014 17:36:36 +0100 Subject: pc: Q35 DSDT: exclude CPU hotplug IO range from PCI bus resources ... for range defined at hw/acpi/ich9.c:ICH9_PROC_BASE Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/acpi-dsdt-pci-crs.dsl | 7 ------- hw/i386/acpi-dsdt.dsl | 7 +++++++ hw/i386/q35-acpi-dsdt.dsl | 8 ++++++++ 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/hw/i386/acpi-dsdt-pci-crs.dsl b/hw/i386/acpi-dsdt-pci-crs.dsl index 8b631d1a0c..4648e90366 100644 --- a/hw/i386/acpi-dsdt-pci-crs.dsl +++ b/hw/i386/acpi-dsdt-pci-crs.dsl @@ -30,13 +30,6 @@ Scope(\_SB.PCI0) { 0x01, // Address Alignment 0x08, // Address Length ) - WordIO(ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, - 0x0000, // Address Space Granularity - 0x0000, // Address Range Minimum - 0x0CF7, // Address Range Maximum - 0x0000, // Address Translation Offset - 0x0CF8, // Address Length - ,, , TypeStatic) BOARD_SPECIFIC_PCI_RESOURSES DWordMemory(ResourceProducer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite, 0x00000000, // Address Space Granularity diff --git a/hw/i386/acpi-dsdt.dsl b/hw/i386/acpi-dsdt.dsl index f501c8dc00..d89dcda90c 100644 --- a/hw/i386/acpi-dsdt.dsl +++ b/hw/i386/acpi-dsdt.dsl @@ -36,6 +36,13 @@ DefinitionBlock ( * PCI Bus definition ****************************************************************/ #define BOARD_SPECIFIC_PCI_RESOURSES \ + WordIO(ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, \ + 0x0000, \ + 0x0000, \ + 0x0CF7, \ + 0x0000, \ + 0x0CF8, \ + ,, , TypeStatic) \ WordIO(ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, \ 0x0000, \ 0x0D00, \ diff --git a/hw/i386/q35-acpi-dsdt.dsl b/hw/i386/q35-acpi-dsdt.dsl index f3e5921079..9e070eeb6c 100644 --- a/hw/i386/q35-acpi-dsdt.dsl +++ b/hw/i386/q35-acpi-dsdt.dsl @@ -49,6 +49,14 @@ DefinitionBlock ( * PCI Bus definition ****************************************************************/ #define BOARD_SPECIFIC_PCI_RESOURSES \ + WordIO(ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, \ + 0x0000, \ + 0x0000, \ + 0x0CD7, \ + 0x0000, \ + 0x0CD8, \ + ,, , TypeStatic) \ + /* 0xcd8-0xcf7 hole for CPU hotplug, hw/acpi/ich9.c:ICH9_PROC_BASE */ \ WordIO(ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, \ 0x0000, \ 0x0D00, \ -- cgit v1.2.3 From 61a3f63560ccd2b5e8c9134e9213a1cff36f26bf Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 9 Jan 2014 17:36:37 +0100 Subject: pc: ACPI: expose PRST IO range via _CRS .. so OSPM could notice resource conflict if there is any. Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/acpi-dsdt-cpu-hotplug.dsl | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/hw/i386/acpi-dsdt-cpu-hotplug.dsl b/hw/i386/acpi-dsdt-cpu-hotplug.dsl index 1dfbb4f086..f91eafd008 100644 --- a/hw/i386/acpi-dsdt-cpu-hotplug.dsl +++ b/hw/i386/acpi-dsdt-cpu-hotplug.dsl @@ -16,6 +16,7 @@ /**************************************************************** * CPU hotplug ****************************************************************/ +#define CPU_HOTPLUG_RESOURCE_DEVICE PRES Scope(\_SB) { /* Objects filled in by run-time generated SSDT */ @@ -52,7 +53,8 @@ Scope(\_SB) { Sleep(200) } - OperationRegion(PRST, SystemIO, CPU_STATUS_BASE, 32) +#define CPU_STATUS_LEN 32 + OperationRegion(PRST, SystemIO, CPU_STATUS_BASE, CPU_STATUS_LEN) Field(PRST, ByteAcc, NoLock, Preserve) { PRS, 256 } @@ -89,4 +91,14 @@ Scope(\_SB) { Increment(Local0) } } + + Device(CPU_HOTPLUG_RESOURCE_DEVICE) { + Name(_HID, "ACPI0004") + + Name(_CRS, ResourceTemplate() { + IO(Decode16, CPU_STATUS_BASE, CPU_STATUS_BASE, 0, CPU_STATUS_LEN) + }) + + Name(_STA, 0xB) /* present, functioning, decoding, not shown in UI */ + } } -- cgit v1.2.3 From e4cf8ed08a1b8b996f9939de0d8eda4ed8a3d25e Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 9 Jan 2014 17:36:38 +0100 Subject: pc: ACPI: unify source of CPU hotplug IO base/len use C headers defines as source of IO base/len for respective values in ASL code. Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/piix4.c | 5 ++--- hw/i386/Makefile.objs | 2 +- hw/i386/acpi-dsdt-cpu-hotplug.dsl | 2 +- hw/i386/acpi-dsdt.dsl | 3 ++- hw/i386/q35-acpi-dsdt.dsl | 3 ++- include/hw/acpi/cpu_hotplug_defs.h | 1 + 6 files changed, 9 insertions(+), 7 deletions(-) diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index 12f8dde178..5d55a3c222 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -51,8 +51,6 @@ #define PCI_EJ_BASE 0xae08 #define PCI_RMV_BASE 0xae0c -#define PIIX4_PROC_BASE 0xaf00 - #define PIIX4_PCI_HOTPLUG_STATUS 2 struct pci_status { @@ -706,7 +704,8 @@ static void piix4_acpi_system_hot_add_init(MemoryRegion *parent, pci_bus_hotplug(bus, piix4_device_hotplug, DEVICE(s)); } - AcpiCpuHotplug_init(parent, OBJECT(s), &s->gpe_cpu, PIIX4_PROC_BASE); + AcpiCpuHotplug_init(parent, OBJECT(s), &s->gpe_cpu, + PIIX4_CPU_HOTPLUG_IO_BASE); s->cpu_added_notifier.notify = piix4_cpu_added_req; qemu_register_cpu_added_notifier(&s->cpu_added_notifier); } diff --git a/hw/i386/Makefile.objs b/hw/i386/Makefile.objs index d58a103efe..3df1612651 100644 --- a/hw/i386/Makefile.objs +++ b/hw/i386/Makefile.objs @@ -17,7 +17,7 @@ iasl-option=$(shell if test -z "`$(1) $(2) 2>&1 > /dev/null`" \ ifdef IASL #IASL Present. Generate hex files from .dsl hw/i386/%.hex: $(SRC_PATH)/hw/i386/%.dsl $(SRC_PATH)/scripts/acpi_extract_preprocess.py $(SRC_PATH)/scripts/acpi_extract.py - $(call quiet-command, cpp -P $(QEMU_DGFLAGS) $< -o $*.dsl.i.orig, " CPP $(TARGET_DIR)$*.dsl.i.orig") + $(call quiet-command, cpp -P $(QEMU_DGFLAGS) $(QEMU_INCLUDES) $< -o $*.dsl.i.orig, " CPP $(TARGET_DIR)$*.dsl.i.orig") $(call quiet-command, $(PYTHON) $(SRC_PATH)/scripts/acpi_extract_preprocess.py $*.dsl.i.orig > $*.dsl.i, " ACPI_PREPROCESS $(TARGET_DIR)$*.dsl.i") $(call quiet-command, $(IASL) $(call iasl-option,$(IASL),-Pn,) -vs -l -tc -p $* $*.dsl.i $(if $(V), , > /dev/null) 2>&1 ," IASL $(TARGET_DIR)$*.dsl.i") $(call quiet-command, $(PYTHON) $(SRC_PATH)/scripts/acpi_extract.py $*.lst > $*.off, " ACPI_EXTRACT $(TARGET_DIR)$*.off") diff --git a/hw/i386/acpi-dsdt-cpu-hotplug.dsl b/hw/i386/acpi-dsdt-cpu-hotplug.dsl index f91eafd008..dee4843cde 100644 --- a/hw/i386/acpi-dsdt-cpu-hotplug.dsl +++ b/hw/i386/acpi-dsdt-cpu-hotplug.dsl @@ -53,7 +53,7 @@ Scope(\_SB) { Sleep(200) } -#define CPU_STATUS_LEN 32 +#define CPU_STATUS_LEN ACPI_GPE_PROC_LEN OperationRegion(PRST, SystemIO, CPU_STATUS_BASE, CPU_STATUS_LEN) Field(PRST, ByteAcc, NoLock, Preserve) { PRS, 256 diff --git a/hw/i386/acpi-dsdt.dsl b/hw/i386/acpi-dsdt.dsl index d89dcda90c..b23d5e0eac 100644 --- a/hw/i386/acpi-dsdt.dsl +++ b/hw/i386/acpi-dsdt.dsl @@ -329,7 +329,8 @@ DefinitionBlock ( } } -#define CPU_STATUS_BASE 0xaf00 +#include "hw/acpi/cpu_hotplug_defs.h" +#define CPU_STATUS_BASE PIIX4_CPU_HOTPLUG_IO_BASE #include "acpi-dsdt-cpu-hotplug.dsl" diff --git a/hw/i386/q35-acpi-dsdt.dsl b/hw/i386/q35-acpi-dsdt.dsl index 9e070eeb6c..d618e9e2d2 100644 --- a/hw/i386/q35-acpi-dsdt.dsl +++ b/hw/i386/q35-acpi-dsdt.dsl @@ -421,7 +421,8 @@ DefinitionBlock ( define_gsi_link(GSIH, 0, 0x17) } -#define CPU_STATUS_BASE 0x0CD8 +#include "hw/acpi/cpu_hotplug_defs.h" +#define CPU_STATUS_BASE ICH9_CPU_HOTPLUG_IO_BASE #include "acpi-dsdt-cpu-hotplug.dsl" diff --git a/include/hw/acpi/cpu_hotplug_defs.h b/include/hw/acpi/cpu_hotplug_defs.h index 47a35e3373..2725b50aac 100644 --- a/include/hw/acpi/cpu_hotplug_defs.h +++ b/include/hw/acpi/cpu_hotplug_defs.h @@ -19,5 +19,6 @@ #define ACPI_CPU_HOTPLUG_STATUS 4 #define ACPI_GPE_PROC_LEN 32 #define ICH9_CPU_HOTPLUG_IO_BASE 0x0CD8 +#define PIIX4_CPU_HOTPLUG_IO_BASE 0xaf00 #endif -- cgit v1.2.3 From 2867534f3d1daa970eba2afdb9b401388bd75b56 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 9 Jan 2014 17:36:39 +0100 Subject: pc: ACPI: update acpi-dsdt.hex.generated q35-acpi-dsdt.hex.generated Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/acpi-dsdt.hex.generated | 217 +++++++++++++++++++++++++----------- hw/i386/q35-acpi-dsdt.hex.generated | 74 ++++++++++-- hw/i386/ssdt-pcihp.hex.generated | 20 ++-- hw/i386/ssdt-proc.hex.generated | 6 +- 4 files changed, 225 insertions(+), 92 deletions(-) diff --git a/hw/i386/acpi-dsdt.hex.generated b/hw/i386/acpi-dsdt.hex.generated index f8bd4ea1b5..1e58801b2a 100644 --- a/hw/i386/acpi-dsdt.hex.generated +++ b/hw/i386/acpi-dsdt.hex.generated @@ -3,12 +3,12 @@ static unsigned char AcpiDsdtAmlCode[] = { 0x53, 0x44, 0x54, -0x37, +0x87, 0x11, 0x0, 0x0, 0x1, -0xd8, +0xb8, 0x42, 0x58, 0x50, @@ -860,8 +860,8 @@ static unsigned char AcpiDsdtAmlCode[] = { 0x4e, 0x1, 0x10, -0x4c, -0x1b, +0x4b, +0x1e, 0x2f, 0x3, 0x5f, @@ -879,6 +879,53 @@ static unsigned char AcpiDsdtAmlCode[] = { 0x5b, 0x82, 0x2d, +0x53, +0x4d, +0x43, +0x5f, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x6, +0x10, +0x0, +0x1, +0x8, +0x5f, +0x53, +0x54, +0x41, +0xb, +0x0, +0xff, +0x8, +0x5f, +0x43, +0x52, +0x53, +0x11, +0x10, +0xa, +0xd, +0x47, +0x1, +0x0, +0x3, +0x0, +0x3, +0x1, +0x20, +0x22, +0x40, +0x0, +0x79, +0x0, +0x5b, +0x82, +0x2d, 0x52, 0x54, 0x43, @@ -1305,7 +1352,7 @@ static unsigned char AcpiDsdtAmlCode[] = { 0x79, 0x0, 0x10, -0x4b, +0x48, 0x8, 0x2e, 0x5f, @@ -1371,79 +1418,76 @@ static unsigned char AcpiDsdtAmlCode[] = { 0x45, 0x4a, 0x20, +0x5b, +0x80, +0x42, +0x4e, +0x4d, +0x52, +0x1, +0xb, +0x10, +0xae, +0xa, +0x4, +0x5b, +0x81, +0xb, +0x42, +0x4e, +0x4d, +0x52, +0x43, +0x42, +0x4e, +0x55, +0x4d, +0x20, +0x5b, +0x1, +0x42, +0x4c, +0x43, +0x4b, +0x0, 0x14, -0x11, +0x25, 0x50, 0x43, 0x45, 0x4a, -0x1, +0x2, +0x5b, +0x23, +0x42, +0x4c, +0x43, +0x4b, +0xff, +0xff, 0x70, -0x79, -0x1, 0x68, -0x0, 0x42, -0x30, -0x45, -0x4a, -0xa4, -0x0, -0x14, -0x36, -0x50, -0x43, 0x4e, -0x46, -0x0, -0x70, -0x0, -0x60, -0xa2, -0x2c, -0x95, -0x60, -0xa, -0x1f, -0x75, -0x60, -0xa0, -0x11, -0x7b, -0x50, -0x43, -0x49, 0x55, +0x4d, +0x70, 0x79, 0x1, -0x60, +0x69, 0x0, -0x0, -0x50, -0x43, -0x4e, -0x54, -0x60, -0x1, -0xa0, -0x12, -0x7b, -0x50, +0x42, +0x30, +0x45, +0x4a, +0x5b, +0x27, +0x42, +0x4c, 0x43, -0x49, -0x44, -0x79, -0x1, -0x60, -0x0, +0x4b, +0xa4, 0x0, -0x50, -0x43, -0x4e, -0x54, -0x60, -0xa, -0x3, 0x10, 0x4a, 0xa0, @@ -4248,8 +4292,8 @@ static unsigned char AcpiDsdtAmlCode[] = { 0x75, 0x60, 0x10, -0x4e, -0x9, +0x42, +0xc, 0x5f, 0x47, 0x50, @@ -4277,12 +4321,31 @@ static unsigned char AcpiDsdtAmlCode[] = { 0x30, 0x0, 0x14, -0x15, +0x39, 0x5f, 0x45, 0x30, 0x31, 0x0, +0x5b, +0x23, +0x5c, +0x2f, +0x3, +0x5f, +0x53, +0x42, +0x5f, +0x50, +0x43, +0x49, +0x30, +0x42, +0x4c, +0x43, +0x4b, +0xff, +0xff, 0x5c, 0x2f, 0x3, @@ -4297,7 +4360,24 @@ static unsigned char AcpiDsdtAmlCode[] = { 0x50, 0x43, 0x4e, -0x46, +0x54, +0x5b, +0x27, +0x5c, +0x2f, +0x3, +0x5f, +0x53, +0x42, +0x5f, +0x50, +0x43, +0x49, +0x30, +0x42, +0x4c, +0x43, +0x4b, 0x14, 0x10, 0x5f, @@ -4407,3 +4487,6 @@ static unsigned char AcpiDsdtAmlCode[] = { 0x46, 0x0 }; +static unsigned short piix_dsdt_applesmc_sta[] = { +0x384 +}; diff --git a/hw/i386/q35-acpi-dsdt.hex.generated b/hw/i386/q35-acpi-dsdt.hex.generated index 111ad3e9c2..6d885a9055 100644 --- a/hw/i386/q35-acpi-dsdt.hex.generated +++ b/hw/i386/q35-acpi-dsdt.hex.generated @@ -3,12 +3,12 @@ static unsigned char Q35AcpiDsdtAmlCode[] = { 0x53, 0x44, 0x54, -0xb0, +0xdf, 0x1c, 0x0, 0x0, 0x1, -0xfe, +0xff, 0x42, 0x58, 0x50, @@ -1033,8 +1033,8 @@ static unsigned char Q35AcpiDsdtAmlCode[] = { 0x4e, 0x1, 0x10, -0x4c, -0x1b, +0x4b, +0x1e, 0x2f, 0x3, 0x5f, @@ -1052,6 +1052,53 @@ static unsigned char Q35AcpiDsdtAmlCode[] = { 0x5b, 0x82, 0x2d, +0x53, +0x4d, +0x43, +0x5f, +0x8, +0x5f, +0x48, +0x49, +0x44, +0xc, +0x6, +0x10, +0x0, +0x1, +0x8, +0x5f, +0x53, +0x54, +0x41, +0xb, +0x0, +0xff, +0x8, +0x5f, +0x43, +0x52, +0x53, +0x11, +0x10, +0xa, +0xd, +0x47, +0x1, +0x0, +0x3, +0x0, +0x3, +0x1, +0x20, +0x22, +0x40, +0x0, +0x79, +0x0, +0x5b, +0x82, +0x2d, 0x52, 0x54, 0x43, @@ -7229,12 +7276,19 @@ static unsigned char Q35AcpiDsdtAmlCode[] = { 0x30, 0x0, 0x14, -0x10, +0x6, 0x5f, 0x4c, 0x30, 0x31, 0x0, +0x14, +0x10, +0x5f, +0x45, +0x30, +0x32, +0x0, 0x5c, 0x2e, 0x5f, @@ -7250,13 +7304,6 @@ static unsigned char Q35AcpiDsdtAmlCode[] = { 0x5f, 0x4c, 0x30, -0x32, -0x0, -0x14, -0x6, -0x5f, -0x4c, -0x30, 0x33, 0x0, 0x14, @@ -7344,3 +7391,6 @@ static unsigned char Q35AcpiDsdtAmlCode[] = { 0x46, 0x0 }; +static unsigned short q35_dsdt_applesmc_sta[] = { +0x431 +}; diff --git a/hw/i386/ssdt-pcihp.hex.generated b/hw/i386/ssdt-pcihp.hex.generated index b3c2cd5cf9..610a631fd1 100644 --- a/hw/i386/ssdt-pcihp.hex.generated +++ b/hw/i386/ssdt-pcihp.hex.generated @@ -5,19 +5,19 @@ static unsigned char ssdt_pcihp_adr[] = { 0x44 }; static unsigned char ssdt_pcihp_end[] = { -0x58 +0x5b }; static unsigned char ssdp_pcihp_aml[] = { 0x53, 0x53, 0x44, 0x54, -0x58, +0x5b, 0x0, 0x0, 0x0, 0x1, -0x76, +0xe8, 0x42, 0x58, 0x50, @@ -45,7 +45,7 @@ static unsigned char ssdp_pcihp_aml[] = { 0x13, 0x20, 0x10, -0x33, +0x36, 0x5c, 0x2e, 0x5f, @@ -58,7 +58,7 @@ static unsigned char ssdp_pcihp_aml[] = { 0x30, 0x5b, 0x82, -0x26, +0x29, 0x53, 0x41, 0x41, @@ -81,17 +81,20 @@ static unsigned char ssdp_pcihp_aml[] = { 0xaa, 0x0, 0x14, -0xf, +0x12, 0x5f, 0x45, 0x4a, 0x30, 0x1, -0xa4, 0x50, 0x43, 0x45, 0x4a, +0x42, +0x53, +0x45, +0x4c, 0x5f, 0x53, 0x55, @@ -103,6 +106,3 @@ static unsigned char ssdt_pcihp_start[] = { static unsigned char ssdt_pcihp_id[] = { 0x3d }; -static unsigned char ssdt_pcihp_ej0[] = { -0x4a -}; diff --git a/hw/i386/ssdt-proc.hex.generated b/hw/i386/ssdt-proc.hex.generated index bb9920d3c9..97e28d4820 100644 --- a/hw/i386/ssdt-proc.hex.generated +++ b/hw/i386/ssdt-proc.hex.generated @@ -11,7 +11,7 @@ static unsigned char ssdp_proc_aml[] = { 0x0, 0x0, 0x1, -0xb8, +0x78, 0x42, 0x58, 0x50, @@ -47,8 +47,8 @@ static unsigned char ssdp_proc_aml[] = { 0x41, 0x41, 0xaa, -0x10, -0xb0, +0x0, +0x0, 0x0, 0x0, 0x0, -- cgit v1.2.3 From b8124cecb55b3bc4e7dee0ff34b294942044d74a Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 20 Jan 2014 14:00:12 +0200 Subject: acpi-test: update expected AML since recent changes Signed-off-by: Michael S. Tsirkin --- tests/acpi-test-data/pc/DSDT | Bin 4407 -> 4582 bytes tests/acpi-test-data/pc/SSDT | Bin 2104 -> 2200 bytes tests/acpi-test-data/q35/DSDT | Bin 7344 -> 7438 bytes tests/acpi-test-data/q35/SSDT | Bin 2104 -> 475 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/tests/acpi-test-data/pc/DSDT b/tests/acpi-test-data/pc/DSDT index b12e5eb85b..fbf1c3e6e8 100644 Binary files a/tests/acpi-test-data/pc/DSDT and b/tests/acpi-test-data/pc/DSDT differ diff --git a/tests/acpi-test-data/pc/SSDT b/tests/acpi-test-data/pc/SSDT index 5ab1b96af0..a51c68e21b 100644 Binary files a/tests/acpi-test-data/pc/SSDT and b/tests/acpi-test-data/pc/SSDT differ diff --git a/tests/acpi-test-data/q35/DSDT b/tests/acpi-test-data/q35/DSDT index 59243b952d..5086b839a6 100644 Binary files a/tests/acpi-test-data/q35/DSDT and b/tests/acpi-test-data/q35/DSDT differ diff --git a/tests/acpi-test-data/q35/SSDT b/tests/acpi-test-data/q35/SSDT index 186e748e6c..9c6cad8b0b 100644 Binary files a/tests/acpi-test-data/q35/SSDT and b/tests/acpi-test-data/q35/SSDT differ -- cgit v1.2.3 From 306077640a652e090779498aadbeb0c605feaacd Mon Sep 17 00:00:00 2001 From: Marcel Apfelbaum Date: Tue, 21 Jan 2014 18:37:51 +0200 Subject: hw/pci: fix error flow in pci multifunction init Scenario: - There is a non multifunction pci device A on 00:0X.0. - Hot-plug another multifunction pci device B at 00:0X.1. - The operation will fail of course. - Try to hot-plug the B device 2-3 more times, qemu will crash. Reason: The error flow leaves the B's address space into global address spaces list, but the device object is freed. Fixed that. Signed-off-by: Marcel Apfelbaum Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pci.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 2aca8a4d7e..1221f32847 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -793,6 +793,15 @@ static void pci_config_free(PCIDevice *pci_dev) g_free(pci_dev->used); } +static void do_pci_unregister_device(PCIDevice *pci_dev) +{ + pci_dev->bus->devices[pci_dev->devfn] = NULL; + pci_config_free(pci_dev); + + address_space_destroy(&pci_dev->bus_master_as); + memory_region_destroy(&pci_dev->bus_master_enable_region); +} + /* -1 for devfn means auto assign */ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus, const char *name, int devfn) @@ -858,7 +867,7 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus, pci_init_mask_bridge(pci_dev); } if (pci_init_multifunction(bus, pci_dev)) { - pci_config_free(pci_dev); + do_pci_unregister_device(pci_dev); return NULL; } @@ -873,15 +882,6 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus, return pci_dev; } -static void do_pci_unregister_device(PCIDevice *pci_dev) -{ - pci_dev->bus->devices[pci_dev->devfn] = NULL; - pci_config_free(pci_dev); - - address_space_destroy(&pci_dev->bus_master_as); - memory_region_destroy(&pci_dev->bus_master_enable_region); -} - static void pci_unregister_io_regions(PCIDevice *pci_dev) { PCIIORegion *r; -- cgit v1.2.3 From f30ee8a9682be4abfcb05c6389894f8cfc35c3f0 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Thu, 9 Jan 2014 17:12:42 -0200 Subject: pc: Save size of RAM below 4GB The ram_below_4g value will be useful in other places, such as the ACPI table code, and other code that currently requires passing below_4g_mem_size around in function arguments. Signed-off-by: Eduardo Habkost Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/pc.c | 1 + include/hw/i386/pc.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 6f0be37d8b..348b15f267 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1072,6 +1072,7 @@ PcGuestInfo *pc_guest_info_init(ram_addr_t below_4g_mem_size, PcGuestInfo *guest_info = &guest_info_state->info; int i, j; + guest_info->ram_size_below_4g = below_4g_mem_size; guest_info->ram_size = below_4g_mem_size + above_4g_mem_size; guest_info->apic_id_limit = pc_apic_id_limit(max_cpus); guest_info->apic_xrupt_override = kvm_allows_irq0_override(); diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 92eabebb2c..3e1e81b27b 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -35,7 +35,7 @@ typedef struct PcPciInfo { struct PcGuestInfo { bool has_pci_info; bool isapc_ram_fw; - hwaddr ram_size; + hwaddr ram_size, ram_size_below_4g; unsigned apic_id_limit; bool apic_xrupt_override; uint64_t numa_nodes; -- cgit v1.2.3 From 4c8a949b2416158a311abefc09a20153aa107e93 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Thu, 9 Jan 2014 17:12:43 -0200 Subject: acpi: Fix PCI hole handling on build_srat() The original SeaBIOS code used the RamSize variable, that was used by SeaBIOS for the size of RAM below 4GB, not for all RAM. When copied to QEMU, the code was changed to use the full RAM size, and this broke the build_srat() code that handles the PCI hole. Change build_srat() to use ram_size_below_4g instead of ram_size, to restore the original behavior from SeaBIOS. Signed-off-by: Eduardo Habkost Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/acpi-build.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 9cd3d0e698..50e83f3b46 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -1080,16 +1080,16 @@ build_srat(GArray *table_data, GArray *linker, next_base = mem_base + mem_len; /* Cut out the ACPI_PCI hole */ - if (mem_base <= guest_info->ram_size && - next_base > guest_info->ram_size) { - mem_len -= next_base - guest_info->ram_size; + if (mem_base <= guest_info->ram_size_below_4g && + next_base > guest_info->ram_size_below_4g) { + mem_len -= next_base - guest_info->ram_size_below_4g; if (mem_len > 0) { numamem = acpi_data_push(table_data, sizeof *numamem); acpi_build_srat_memory(numamem, mem_base, mem_len, i-1, 1); } mem_base = 1ULL << 32; - mem_len = next_base - guest_info->ram_size; - next_base += (1ULL << 32) - guest_info->ram_size; + mem_len = next_base - guest_info->ram_size_below_4g; + next_base += (1ULL << 32) - guest_info->ram_size_below_4g; } numamem = acpi_data_push(table_data, sizeof *numamem); acpi_build_srat_memory(numamem, mem_base, mem_len, i - 1, 1); -- cgit v1.2.3 From 9a305c8fc7bb567e9f754ddfe5fb102aaeeb770a Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 16 Dec 2013 10:11:28 +0100 Subject: q35: gigabyte alignment for ram Map 2G (q35) of memory below 4G, so the RAM pieces are nicely aligned to gigabyte borders. Keep old memory layout for (a) old machine types and (b) in case all memory fits below 4G and thus we don't have to split RAM into pieces in the first place. The later makes sure this change doesn't take away memory from 32bit guests. Signed-off-by: Gerd Hoffmann Signed-off-by: Michael S. Tsirkin --- hw/i386/pc_q35.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 07f38ff704..710464598e 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -51,6 +51,7 @@ static bool has_pci_info; static bool has_acpi_build = true; static bool smbios_type1_defaults = true; +static bool gigabyte_align = true; /* PC hardware initialisation */ static void pc_q35_init(QEMUMachineInitArgs *args) @@ -93,8 +94,9 @@ static void pc_q35_init(QEMUMachineInitArgs *args) kvmclock_create(); if (args->ram_size >= 0xb0000000) { - above_4g_mem_size = args->ram_size - 0xb0000000; - below_4g_mem_size = 0xb0000000; + ram_addr_t lowmem = gigabyte_align ? 0x80000000 : 0xb0000000; + above_4g_mem_size = args->ram_size - lowmem; + below_4g_mem_size = lowmem; } else { above_4g_mem_size = 0; below_4g_mem_size = args->ram_size; @@ -228,6 +230,7 @@ static void pc_q35_init(QEMUMachineInitArgs *args) static void pc_compat_1_7(QEMUMachineInitArgs *args) { smbios_type1_defaults = false; + gigabyte_align = false; } static void pc_compat_1_6(QEMUMachineInitArgs *args) -- cgit v1.2.3 From 4e17997d49d6e3a426fd465dfbdf5a4dc04639f4 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 16 Dec 2013 13:55:06 +0200 Subject: q35: document gigabyte_align Document the logic behind the below/above 4G split. Signed-off-by: Michael S. Tsirkin --- hw/i386/pc_q35.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 710464598e..a7f626096a 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -51,6 +51,10 @@ static bool has_pci_info; static bool has_acpi_build = true; static bool smbios_type1_defaults = true; +/* Make sure that guest addresses aligned at 1Gbyte boundaries get mapped to + * host addresses aligned at 1Gbyte boundaries. This way we can use 1GByte + * pages in the host. + */ static bool gigabyte_align = true; /* PC hardware initialisation */ @@ -93,6 +97,15 @@ static void pc_q35_init(QEMUMachineInitArgs *args) kvmclock_create(); + /* Check whether RAM fits below 4G (leaving 1/2 GByte for IO memory + * and 256 Mbytes for PCI Express Enhanced Configuration Access Mapping + * also known as MMCFG). + * If it doesn't, we need to split it in chunks below and above 4G. + * In any case, try to make sure that guest addresses aligned at + * 1G boundaries get mapped to host addresses aligned at 1G boundaries. + * For old machine types, use whatever split we used historically to avoid + * breaking migration. + */ if (args->ram_size >= 0xb0000000) { ram_addr_t lowmem = gigabyte_align ? 0x80000000 : 0xb0000000; above_4g_mem_size = args->ram_size - lowmem; -- cgit v1.2.3 From a75143eda2ddf581b51e96c000974bcdfe2cbd10 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Sun, 26 Jan 2014 12:39:55 +0200 Subject: MAINTAINERS: add self as virtio co-maintainer This will help make sure I get Cc'd on patches. Signed-off-by: Michael S. Tsirkin --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index fb5324285c..adc59735a9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -610,6 +610,7 @@ F: hw/*/*vhost* virtio M: Anthony Liguori +M: Michael S. Tsirkin S: Supported F: hw/*/virtio* -- cgit v1.2.3