diff options
28 files changed, 567 insertions, 160 deletions
diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index a991ea2964..e3b5750c3b 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -80,9 +80,14 @@ QEMU_BUILD_BUG_ON(sizeof(target_ulong) > sizeof(run_on_cpu_data)); QEMU_BUILD_BUG_ON(NB_MMU_MODES > 16); #define ALL_MMUIDX_BITS ((1 << NB_MMU_MODES) - 1) -static inline size_t sizeof_tlb(CPUArchState *env, uintptr_t mmu_idx) +static inline size_t tlb_n_entries(CPUTLBDescFast *fast) { - return env_tlb(env)->f[mmu_idx].mask + (1 << CPU_TLB_ENTRY_BITS); + return (fast->mask >> CPU_TLB_ENTRY_BITS) + 1; +} + +static inline size_t sizeof_tlb(CPUTLBDescFast *fast) +{ + return fast->mask + (1 << CPU_TLB_ENTRY_BITS); } static void tlb_window_reset(CPUTLBDesc *desc, int64_t ns, @@ -92,26 +97,10 @@ static void tlb_window_reset(CPUTLBDesc *desc, int64_t ns, desc->window_max_entries = max_entries; } -static void tlb_dyn_init(CPUArchState *env) -{ - int i; - - for (i = 0; i < NB_MMU_MODES; i++) { - CPUTLBDesc *desc = &env_tlb(env)->d[i]; - size_t n_entries = 1 << CPU_TLB_DYN_DEFAULT_BITS; - - tlb_window_reset(desc, get_clock_realtime(), 0); - desc->n_used_entries = 0; - env_tlb(env)->f[i].mask = (n_entries - 1) << CPU_TLB_ENTRY_BITS; - env_tlb(env)->f[i].table = g_new(CPUTLBEntry, n_entries); - env_tlb(env)->d[i].iotlb = g_new(CPUIOTLBEntry, n_entries); - } -} - /** * tlb_mmu_resize_locked() - perform TLB resize bookkeeping; resize if necessary - * @env: CPU that owns the TLB - * @mmu_idx: MMU index of the TLB + * @desc: The CPUTLBDesc portion of the TLB + * @fast: The CPUTLBDescFast portion of the same TLB * * Called with tlb_lock_held. * @@ -148,13 +137,12 @@ static void tlb_dyn_init(CPUArchState *env) * high), since otherwise we are likely to have a significant amount of * conflict misses. */ -static void tlb_mmu_resize_locked(CPUArchState *env, int mmu_idx) +static void tlb_mmu_resize_locked(CPUTLBDesc *desc, CPUTLBDescFast *fast, + int64_t now) { - CPUTLBDesc *desc = &env_tlb(env)->d[mmu_idx]; - size_t old_size = tlb_n_entries(env, mmu_idx); + size_t old_size = tlb_n_entries(fast); size_t rate; size_t new_size = old_size; - int64_t now = get_clock_realtime(); int64_t window_len_ms = 100; int64_t window_len_ns = window_len_ms * 1000 * 1000; bool window_expired = now > desc->window_begin_ns + window_len_ns; @@ -193,14 +181,15 @@ static void tlb_mmu_resize_locked(CPUArchState *env, int mmu_idx) return; } - g_free(env_tlb(env)->f[mmu_idx].table); - g_free(env_tlb(env)->d[mmu_idx].iotlb); + g_free(fast->table); + g_free(desc->iotlb); tlb_window_reset(desc, now, 0); /* desc->n_used_entries is cleared by the caller */ - env_tlb(env)->f[mmu_idx].mask = (new_size - 1) << CPU_TLB_ENTRY_BITS; - env_tlb(env)->f[mmu_idx].table = g_try_new(CPUTLBEntry, new_size); - env_tlb(env)->d[mmu_idx].iotlb = g_try_new(CPUIOTLBEntry, new_size); + fast->mask = (new_size - 1) << CPU_TLB_ENTRY_BITS; + fast->table = g_try_new(CPUTLBEntry, new_size); + desc->iotlb = g_try_new(CPUIOTLBEntry, new_size); + /* * If the allocations fail, try smaller sizes. We just freed some * memory, so going back to half of new_size has a good chance of working. @@ -208,27 +197,51 @@ static void tlb_mmu_resize_locked(CPUArchState *env, int mmu_idx) * allocations to fail though, so we progressively reduce the allocation * size, aborting if we cannot even allocate the smallest TLB we support. */ - while (env_tlb(env)->f[mmu_idx].table == NULL || - env_tlb(env)->d[mmu_idx].iotlb == NULL) { + while (fast->table == NULL || desc->iotlb == NULL) { if (new_size == (1 << CPU_TLB_DYN_MIN_BITS)) { error_report("%s: %s", __func__, strerror(errno)); abort(); } new_size = MAX(new_size >> 1, 1 << CPU_TLB_DYN_MIN_BITS); - env_tlb(env)->f[mmu_idx].mask = (new_size - 1) << CPU_TLB_ENTRY_BITS; + fast->mask = (new_size - 1) << CPU_TLB_ENTRY_BITS; - g_free(env_tlb(env)->f[mmu_idx].table); - g_free(env_tlb(env)->d[mmu_idx].iotlb); - env_tlb(env)->f[mmu_idx].table = g_try_new(CPUTLBEntry, new_size); - env_tlb(env)->d[mmu_idx].iotlb = g_try_new(CPUIOTLBEntry, new_size); + g_free(fast->table); + g_free(desc->iotlb); + fast->table = g_try_new(CPUTLBEntry, new_size); + desc->iotlb = g_try_new(CPUIOTLBEntry, new_size); } } -static inline void tlb_table_flush_by_mmuidx(CPUArchState *env, int mmu_idx) +static void tlb_mmu_flush_locked(CPUTLBDesc *desc, CPUTLBDescFast *fast) { - tlb_mmu_resize_locked(env, mmu_idx); - memset(env_tlb(env)->f[mmu_idx].table, -1, sizeof_tlb(env, mmu_idx)); - env_tlb(env)->d[mmu_idx].n_used_entries = 0; + desc->n_used_entries = 0; + desc->large_page_addr = -1; + desc->large_page_mask = -1; + desc->vindex = 0; + memset(fast->table, -1, sizeof_tlb(fast)); + memset(desc->vtable, -1, sizeof(desc->vtable)); +} + +static void tlb_flush_one_mmuidx_locked(CPUArchState *env, int mmu_idx, + int64_t now) +{ + CPUTLBDesc *desc = &env_tlb(env)->d[mmu_idx]; + CPUTLBDescFast *fast = &env_tlb(env)->f[mmu_idx]; + + tlb_mmu_resize_locked(desc, fast, now); + tlb_mmu_flush_locked(desc, fast); +} + +static void tlb_mmu_init(CPUTLBDesc *desc, CPUTLBDescFast *fast, int64_t now) +{ + size_t n_entries = 1 << CPU_TLB_DYN_DEFAULT_BITS; + + tlb_window_reset(desc, now, 0); + desc->n_used_entries = 0; + fast->mask = (n_entries - 1) << CPU_TLB_ENTRY_BITS; + fast->table = g_new(CPUTLBEntry, n_entries); + desc->iotlb = g_new(CPUIOTLBEntry, n_entries); + tlb_mmu_flush_locked(desc, fast); } static inline void tlb_n_used_entries_inc(CPUArchState *env, uintptr_t mmu_idx) @@ -244,13 +257,17 @@ static inline void tlb_n_used_entries_dec(CPUArchState *env, uintptr_t mmu_idx) void tlb_init(CPUState *cpu) { CPUArchState *env = cpu->env_ptr; + int64_t now = get_clock_realtime(); + int i; qemu_spin_init(&env_tlb(env)->c.lock); - /* Ensure that cpu_reset performs a full flush. */ - env_tlb(env)->c.dirty = ALL_MMUIDX_BITS; + /* All tlbs are initialized flushed. */ + env_tlb(env)->c.dirty = 0; - tlb_dyn_init(env); + for (i = 0; i < NB_MMU_MODES; i++) { + tlb_mmu_init(&env_tlb(env)->d[i], &env_tlb(env)->f[i], now); + } } /* flush_all_helper: run fn across all cpus @@ -289,21 +306,12 @@ void tlb_flush_counts(size_t *pfull, size_t *ppart, size_t *pelide) *pelide = elide; } -static void tlb_flush_one_mmuidx_locked(CPUArchState *env, int mmu_idx) -{ - tlb_table_flush_by_mmuidx(env, mmu_idx); - env_tlb(env)->d[mmu_idx].large_page_addr = -1; - env_tlb(env)->d[mmu_idx].large_page_mask = -1; - env_tlb(env)->d[mmu_idx].vindex = 0; - memset(env_tlb(env)->d[mmu_idx].vtable, -1, - sizeof(env_tlb(env)->d[0].vtable)); -} - static void tlb_flush_by_mmuidx_async_work(CPUState *cpu, run_on_cpu_data data) { CPUArchState *env = cpu->env_ptr; uint16_t asked = data.host_int; uint16_t all_dirty, work, to_clean; + int64_t now = get_clock_realtime(); assert_cpu_is_self(cpu); @@ -318,7 +326,7 @@ static void tlb_flush_by_mmuidx_async_work(CPUState *cpu, run_on_cpu_data data) for (work = to_clean; work != 0; work &= work - 1) { int mmu_idx = ctz32(work); - tlb_flush_one_mmuidx_locked(env, mmu_idx); + tlb_flush_one_mmuidx_locked(env, mmu_idx, now); } qemu_spin_unlock(&env_tlb(env)->c.lock); @@ -440,7 +448,7 @@ static void tlb_flush_page_locked(CPUArchState *env, int midx, tlb_debug("forcing full flush midx %d (" TARGET_FMT_lx "/" TARGET_FMT_lx ")\n", midx, lp_addr, lp_mask); - tlb_flush_one_mmuidx_locked(env, midx); + tlb_flush_one_mmuidx_locked(env, midx, get_clock_realtime()); } else { if (tlb_flush_entry_locked(tlb_entry(env, midx, page), page)) { tlb_n_used_entries_dec(env, midx); @@ -449,28 +457,29 @@ static void tlb_flush_page_locked(CPUArchState *env, int midx, } } -/* As we are going to hijack the bottom bits of the page address for a - * mmuidx bit mask we need to fail to build if we can't do that +/** + * tlb_flush_page_by_mmuidx_async_0: + * @cpu: cpu on which to flush + * @addr: page of virtual address to flush + * @idxmap: set of mmu_idx to flush + * + * Helper for tlb_flush_page_by_mmuidx and friends, flush one page + * at @addr from the tlbs indicated by @idxmap from @cpu. */ -QEMU_BUILD_BUG_ON(NB_MMU_MODES > TARGET_PAGE_BITS_MIN); - -static void tlb_flush_page_by_mmuidx_async_work(CPUState *cpu, - run_on_cpu_data data) +static void tlb_flush_page_by_mmuidx_async_0(CPUState *cpu, + target_ulong addr, + uint16_t idxmap) { CPUArchState *env = cpu->env_ptr; - target_ulong addr_and_mmuidx = (target_ulong) data.target_ptr; - target_ulong addr = addr_and_mmuidx & TARGET_PAGE_MASK; - unsigned long mmu_idx_bitmap = addr_and_mmuidx & ALL_MMUIDX_BITS; int mmu_idx; assert_cpu_is_self(cpu); - tlb_debug("page addr:" TARGET_FMT_lx " mmu_map:0x%lx\n", - addr, mmu_idx_bitmap); + tlb_debug("page addr:" TARGET_FMT_lx " mmu_map:0x%x\n", addr, idxmap); qemu_spin_lock(&env_tlb(env)->c.lock); for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { - if (test_bit(mmu_idx, &mmu_idx_bitmap)) { + if ((idxmap >> mmu_idx) & 1) { tlb_flush_page_locked(env, mmu_idx, addr); } } @@ -479,22 +488,75 @@ static void tlb_flush_page_by_mmuidx_async_work(CPUState *cpu, tb_flush_jmp_cache(cpu, addr); } -void tlb_flush_page_by_mmuidx(CPUState *cpu, target_ulong addr, uint16_t idxmap) +/** + * tlb_flush_page_by_mmuidx_async_1: + * @cpu: cpu on which to flush + * @data: encoded addr + idxmap + * + * Helper for tlb_flush_page_by_mmuidx and friends, called through + * async_run_on_cpu. The idxmap parameter is encoded in the page + * offset of the target_ptr field. This limits the set of mmu_idx + * that can be passed via this method. + */ +static void tlb_flush_page_by_mmuidx_async_1(CPUState *cpu, + run_on_cpu_data data) +{ + target_ulong addr_and_idxmap = (target_ulong) data.target_ptr; + target_ulong addr = addr_and_idxmap & TARGET_PAGE_MASK; + uint16_t idxmap = addr_and_idxmap & ~TARGET_PAGE_MASK; + + tlb_flush_page_by_mmuidx_async_0(cpu, addr, idxmap); +} + +typedef struct { + target_ulong addr; + uint16_t idxmap; +} TLBFlushPageByMMUIdxData; + +/** + * tlb_flush_page_by_mmuidx_async_2: + * @cpu: cpu on which to flush + * @data: allocated addr + idxmap + * + * Helper for tlb_flush_page_by_mmuidx and friends, called through + * async_run_on_cpu. The addr+idxmap parameters are stored in a + * TLBFlushPageByMMUIdxData structure that has been allocated + * specifically for this helper. Free the structure when done. + */ +static void tlb_flush_page_by_mmuidx_async_2(CPUState *cpu, + run_on_cpu_data data) { - target_ulong addr_and_mmu_idx; + TLBFlushPageByMMUIdxData *d = data.host_ptr; + + tlb_flush_page_by_mmuidx_async_0(cpu, d->addr, d->idxmap); + g_free(d); +} +void tlb_flush_page_by_mmuidx(CPUState *cpu, target_ulong addr, uint16_t idxmap) +{ tlb_debug("addr: "TARGET_FMT_lx" mmu_idx:%" PRIx16 "\n", addr, idxmap); /* This should already be page aligned */ - addr_and_mmu_idx = addr & TARGET_PAGE_MASK; - addr_and_mmu_idx |= idxmap; + addr &= TARGET_PAGE_MASK; - if (!qemu_cpu_is_self(cpu)) { - async_run_on_cpu(cpu, tlb_flush_page_by_mmuidx_async_work, - RUN_ON_CPU_TARGET_PTR(addr_and_mmu_idx)); + if (qemu_cpu_is_self(cpu)) { + tlb_flush_page_by_mmuidx_async_0(cpu, addr, idxmap); + } else if (idxmap < TARGET_PAGE_SIZE) { + /* + * Most targets have only a few mmu_idx. In the case where + * we can stuff idxmap into the low TARGET_PAGE_BITS, avoid + * allocating memory for this operation. + */ + async_run_on_cpu(cpu, tlb_flush_page_by_mmuidx_async_1, + RUN_ON_CPU_TARGET_PTR(addr | idxmap)); } else { - tlb_flush_page_by_mmuidx_async_work( - cpu, RUN_ON_CPU_TARGET_PTR(addr_and_mmu_idx)); + TLBFlushPageByMMUIdxData *d = g_new(TLBFlushPageByMMUIdxData, 1); + + /* Otherwise allocate a structure, freed by the worker. */ + d->addr = addr; + d->idxmap = idxmap; + async_run_on_cpu(cpu, tlb_flush_page_by_mmuidx_async_2, + RUN_ON_CPU_HOST_PTR(d)); } } @@ -506,17 +568,36 @@ void tlb_flush_page(CPUState *cpu, target_ulong addr) void tlb_flush_page_by_mmuidx_all_cpus(CPUState *src_cpu, target_ulong addr, uint16_t idxmap) { - const run_on_cpu_func fn = tlb_flush_page_by_mmuidx_async_work; - target_ulong addr_and_mmu_idx; - tlb_debug("addr: "TARGET_FMT_lx" mmu_idx:%"PRIx16"\n", addr, idxmap); /* This should already be page aligned */ - addr_and_mmu_idx = addr & TARGET_PAGE_MASK; - addr_and_mmu_idx |= idxmap; + addr &= TARGET_PAGE_MASK; + + /* + * Allocate memory to hold addr+idxmap only when needed. + * See tlb_flush_page_by_mmuidx for details. + */ + if (idxmap < TARGET_PAGE_SIZE) { + flush_all_helper(src_cpu, tlb_flush_page_by_mmuidx_async_1, + RUN_ON_CPU_TARGET_PTR(addr | idxmap)); + } else { + CPUState *dst_cpu; + + /* Allocate a separate data block for each destination cpu. */ + CPU_FOREACH(dst_cpu) { + if (dst_cpu != src_cpu) { + TLBFlushPageByMMUIdxData *d + = g_new(TLBFlushPageByMMUIdxData, 1); + + d->addr = addr; + d->idxmap = idxmap; + async_run_on_cpu(dst_cpu, tlb_flush_page_by_mmuidx_async_2, + RUN_ON_CPU_HOST_PTR(d)); + } + } + } - flush_all_helper(src_cpu, fn, RUN_ON_CPU_TARGET_PTR(addr_and_mmu_idx)); - fn(src_cpu, RUN_ON_CPU_TARGET_PTR(addr_and_mmu_idx)); + tlb_flush_page_by_mmuidx_async_0(src_cpu, addr, idxmap); } void tlb_flush_page_all_cpus(CPUState *src, target_ulong addr) @@ -528,17 +609,41 @@ void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *src_cpu, target_ulong addr, uint16_t idxmap) { - const run_on_cpu_func fn = tlb_flush_page_by_mmuidx_async_work; - target_ulong addr_and_mmu_idx; - tlb_debug("addr: "TARGET_FMT_lx" mmu_idx:%"PRIx16"\n", addr, idxmap); /* This should already be page aligned */ - addr_and_mmu_idx = addr & TARGET_PAGE_MASK; - addr_and_mmu_idx |= idxmap; + addr &= TARGET_PAGE_MASK; + + /* + * Allocate memory to hold addr+idxmap only when needed. + * See tlb_flush_page_by_mmuidx for details. + */ + if (idxmap < TARGET_PAGE_SIZE) { + flush_all_helper(src_cpu, tlb_flush_page_by_mmuidx_async_1, + RUN_ON_CPU_TARGET_PTR(addr | idxmap)); + async_safe_run_on_cpu(src_cpu, tlb_flush_page_by_mmuidx_async_1, + RUN_ON_CPU_TARGET_PTR(addr | idxmap)); + } else { + CPUState *dst_cpu; + TLBFlushPageByMMUIdxData *d; + + /* Allocate a separate data block for each destination cpu. */ + CPU_FOREACH(dst_cpu) { + if (dst_cpu != src_cpu) { + d = g_new(TLBFlushPageByMMUIdxData, 1); + d->addr = addr; + d->idxmap = idxmap; + async_run_on_cpu(dst_cpu, tlb_flush_page_by_mmuidx_async_2, + RUN_ON_CPU_HOST_PTR(d)); + } + } - flush_all_helper(src_cpu, fn, RUN_ON_CPU_TARGET_PTR(addr_and_mmu_idx)); - async_safe_run_on_cpu(src_cpu, fn, RUN_ON_CPU_TARGET_PTR(addr_and_mmu_idx)); + d = g_new(TLBFlushPageByMMUIdxData, 1); + d->addr = addr; + d->idxmap = idxmap; + async_safe_run_on_cpu(src_cpu, tlb_flush_page_by_mmuidx_async_2, + RUN_ON_CPU_HOST_PTR(d)); + } } void tlb_flush_page_all_cpus_synced(CPUState *src, target_ulong addr) @@ -622,7 +727,7 @@ void tlb_reset_dirty(CPUState *cpu, ram_addr_t start1, ram_addr_t length) qemu_spin_lock(&env_tlb(env)->c.lock); for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { unsigned int i; - unsigned int n = tlb_n_entries(env, mmu_idx); + unsigned int n = tlb_n_entries(&env_tlb(env)->f[mmu_idx]); for (i = 0; i < n; i++) { tlb_reset_dirty_range_locked(&env_tlb(env)->f[mmu_idx].table[i], diff --git a/docs/specs/acpi_cpu_hotplug.txt b/docs/specs/acpi_cpu_hotplug.txt index ee219c8358..a8ce5e7402 100644 --- a/docs/specs/acpi_cpu_hotplug.txt +++ b/docs/specs/acpi_cpu_hotplug.txt @@ -15,14 +15,14 @@ CPU present bitmap for: PIIX-PM (IO port 0xaf00-0xaf1f, 1-byte access) One bit per CPU. Bit position reflects corresponding CPU APIC ID. Read-only. The first DWORD in bitmap is used in write mode to switch from legacy - to new CPU hotplug interface, write 0 into it to do switch. + to modern CPU hotplug interface, write 0 into it to do switch. --------------------------------------------------------------- QEMU sets corresponding CPU bit on hot-add event and issues SCI with GPE.2 event set. CPU present map is read by ACPI BIOS GPE.2 handler to notify OS about CPU hot-add events. CPU hot-remove isn't supported. ===================================== -ACPI CPU hotplug interface registers: +Modern ACPI CPU hotplug interface registers: ------------------------------------- Register block base address: ICH9-LPC IO port 0x0cd8 @@ -30,9 +30,25 @@ Register block base address: Register block size: ACPI_CPU_HOTPLUG_REG_LEN = 12 +All accesses to registers described below, imply little-endian byte order. + +Reserved resisters behavior: + - write accesses are ignored + - read accesses return all bits set to 0. + +The last stored value in 'CPU selector' must refer to a possible CPU, otherwise + - reads from any register return 0 + - writes to any other register are ignored until valid value is stored into it +On QEMU start, 'CPU selector' is initialized to a valid value, on reset it +keeps the current value. + read access: offset: - [0x0-0x3] reserved + [0x0-0x3] Command data 2: (DWORD access) + if value last stored in 'Command field': + 0: reads as 0x0 + 3: upper 32 bits of architecture specific CPU ID value + other values: reserved [0x4] CPU device status fields: (1 byte access) bits: 0: Device is enabled and may be used by guest @@ -44,15 +60,17 @@ read access: 3-7: reserved and should be ignored by OSPM [0x5-0x7] reserved [0x8] Command data: (DWORD access) - in case of error or unsupported command reads is 0xFFFFFFFF - current 'Command field' value: - 0: returns PXM value corresponding to device + contains 0 unless value last stored in 'Command field' is one of: + 0: contains 'CPU selector' value of a CPU with pending event[s] + 3: lower 32 bits of architecture specific CPU ID value + (in x86 case: APIC ID) write access: offset: [0x0-0x3] CPU selector: (DWORD access) selects active CPU device. All following accesses to other registers will read/store data from/to selected CPU. + Valid values: [0 .. max_cpus) [0x4] CPU device control fields: (1 byte access) bits: 0: reserved, OSPM must clear it before writing to register. @@ -69,9 +87,9 @@ write access: value: 0: selects a CPU device with inserting/removing events and following reads from 'Command data' register return - selected CPU (CPU selector value). If no CPU with events - found, the current CPU selector doesn't change and - corresponding insert/remove event flags are not set. + selected CPU ('CPU selector' value). + If no CPU with events found, the current 'CPU selector' doesn't + change and corresponding insert/remove event flags are not modified. 1: following writes to 'Command data' register set OST event register in QEMU 2: following writes to 'Command data' register set OST status @@ -79,16 +97,53 @@ write access: other values: reserved [0x6-0x7] reserved [0x8] Command data: (DWORD access) - current 'Command field' value: - 0: OSPM reads value of CPU selector + if last stored 'Command field' value: 1: stores value into OST event register 2: stores value into OST status register, triggers ACPI_DEVICE_OST QMP event from QEMU to external applications with current values of OST event and status registers. - other values: reserved + other values: reserved + +Typical usecases: + - (x86) Detecting and enabling modern CPU hotplug interface. + QEMU starts with legacy CPU hotplug interface enabled. Detecting and + switching to modern interface is based on the 2 legacy CPU hotplug features: + 1. Writes into CPU bitmap are ignored. + 2. CPU bitmap always has bit#0 set, corresponding to boot CPU. + + Use following steps to detect and enable modern CPU hotplug interface: + 1. Store 0x0 to the 'CPU selector' register, + attempting to switch to modern mode + 2. Store 0x0 to the 'CPU selector' register, + to ensure valid selector value + 3. Store 0x0 to the 'Command field' register, + 4. Read the 'Command data 2' register. + If read value is 0x0, the modern interface is enabled. + Otherwise legacy or no CPU hotplug interface available + + - Get a cpu with pending event + 1. Store 0x0 to the 'CPU selector' register. + 2. Store 0x0 to the 'Command field' register. + 3. Read the 'CPU device status fields' register. + 4. If both bit#1 and bit#2 are clear in the value read, there is no CPU + with a pending event and selected CPU remains unchanged. + 5. Otherwise, read the 'Command data' register. The value read is the + selector of the CPU with the pending event (which is already + selected). -Selecting CPU device beyond possible range has no effect on platform: - - write accesses to CPU hot-plug registers not documented above are - ignored - - read accesses to CPU hot-plug registers not documented above return - all bits set to 0. + - Enumerate CPUs present/non present CPUs + 01. Set the present CPU count to 0. + 02. Set the iterator to 0. + 03. Store 0x0 to the 'CPU selector' register, to ensure that it's in + a valid state and that access to other registers won't be ignored. + 04. Store 0x0 to the 'Command field' register to make 'Command data' + register return 'CPU selector' value of selected CPU + 05. Read the 'CPU device status fields' register. + 06. If bit#0 is set, increment the present CPU count. + 07. Increment the iterator. + 08. Store the iterator to the 'CPU selector' register. + 09. Read the 'Command data' register. + 10. If the value read is not zero, goto 05. + 11. Otherwise store 0x0 to the 'CPU selector' register, to put it + into a valid state and exit. + The iterator at this point equals "max_cpus". diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c index 991e175c82..1d1c50409c 100644 --- a/hw/9pfs/virtio-9p-device.c +++ b/hw/9pfs/virtio-9p-device.c @@ -218,6 +218,7 @@ static void virtio_9p_device_unrealize(DeviceState *dev, Error **errp) V9fsVirtioState *v = VIRTIO_9P(dev); V9fsState *s = &v->state; + virtio_delete_queue(v->vq); virtio_cleanup(vdev); v9fs_device_unrealize_common(s, errp); } diff --git a/hw/acpi/cpu.c b/hw/acpi/cpu.c index 87f30a31d7..e2c957ce00 100644 --- a/hw/acpi/cpu.c +++ b/hw/acpi/cpu.c @@ -12,11 +12,13 @@ #define ACPI_CPU_FLAGS_OFFSET_RW 4 #define ACPI_CPU_CMD_OFFSET_WR 5 #define ACPI_CPU_CMD_DATA_OFFSET_RW 8 +#define ACPI_CPU_CMD_DATA2_OFFSET_R 0 enum { CPHP_GET_NEXT_CPU_WITH_EVENT_CMD = 0, CPHP_OST_EVENT_CMD = 1, CPHP_OST_STATUS_CMD = 2, + CPHP_GET_CPU_ID_CMD = 3, CPHP_CMD_MAX }; @@ -74,11 +76,27 @@ static uint64_t cpu_hotplug_rd(void *opaque, hwaddr addr, unsigned size) case CPHP_GET_NEXT_CPU_WITH_EVENT_CMD: val = cpu_st->selector; break; + case CPHP_GET_CPU_ID_CMD: + val = cdev->arch_id & 0xFFFFFFFF; + break; default: break; } trace_cpuhp_acpi_read_cmd_data(cpu_st->selector, val); break; + case ACPI_CPU_CMD_DATA2_OFFSET_R: + switch (cpu_st->command) { + case CPHP_GET_NEXT_CPU_WITH_EVENT_CMD: + val = 0; + break; + case CPHP_GET_CPU_ID_CMD: + val = cdev->arch_id >> 32; + break; + default: + break; + } + trace_cpuhp_acpi_read_cmd_data2(cpu_st->selector, val); + break; default: break; } diff --git a/hw/acpi/trace-events b/hw/acpi/trace-events index 96b8273297..afbc77de1c 100644 --- a/hw/acpi/trace-events +++ b/hw/acpi/trace-events @@ -23,6 +23,7 @@ cpuhp_acpi_read_flags(uint32_t idx, uint8_t flags) "idx[0x%"PRIx32"] flags: 0x%" cpuhp_acpi_write_idx(uint32_t idx) "set active cpu idx: 0x%"PRIx32 cpuhp_acpi_write_cmd(uint32_t idx, uint8_t cmd) "idx[0x%"PRIx32"] cmd: 0x%"PRIx8 cpuhp_acpi_read_cmd_data(uint32_t idx, uint32_t data) "idx[0x%"PRIx32"] data: 0x%"PRIx32 +cpuhp_acpi_read_cmd_data2(uint32_t idx, uint32_t data) "idx[0x%"PRIx32"] data: 0x%"PRIx32 cpuhp_acpi_cpu_has_events(uint32_t idx, bool ins, bool rm) "idx[0x%"PRIx32"] inserting: %d, removing: %d" cpuhp_acpi_clear_inserting_evt(uint32_t idx) "idx[0x%"PRIx32"]" cpuhp_acpi_clear_remove_evt(uint32_t idx) "idx[0x%"PRIx32"]" diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index e25df838f0..9c4e46fa74 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -1816,7 +1816,6 @@ static void build_smb0(Aml *table, I2CBus *smbus, int devnr, int func) Aml *scope = aml_scope("_SB.PCI0"); Aml *dev = aml_device("SMB0"); - aml_append(dev, aml_name_decl("_HID", aml_eisaid("APP0005"))); aml_append(dev, aml_name_decl("_ADR", aml_int(devnr << 16 | func))); build_acpi_ipmi_devices(dev, BUS(smbus), "\\_SB.PCI0.SMB0"); aml_append(scope, dev); diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 8054bc4147..a6302a772d 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -93,7 +93,9 @@ #include "fw_cfg.h" #include "trace.h" -GlobalProperty pc_compat_4_2[] = {}; +GlobalProperty pc_compat_4_2[] = { + { "mch", "smbase-smram", "off" }, +}; const size_t pc_compat_4_2_len = G_N_ELEMENTS(pc_compat_4_2); GlobalProperty pc_compat_4_1[] = {}; diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c index 158d270b9f..6342f73b9f 100644 --- a/hw/pci-host/q35.c +++ b/hw/pci-host/q35.c @@ -275,20 +275,20 @@ static const TypeInfo q35_host_info = { * MCH D0:F0 */ -static uint64_t tseg_blackhole_read(void *ptr, hwaddr reg, unsigned size) +static uint64_t blackhole_read(void *ptr, hwaddr reg, unsigned size) { return 0xffffffff; } -static void tseg_blackhole_write(void *opaque, hwaddr addr, uint64_t val, - unsigned width) +static void blackhole_write(void *opaque, hwaddr addr, uint64_t val, + unsigned width) { /* nothing */ } -static const MemoryRegionOps tseg_blackhole_ops = { - .read = tseg_blackhole_read, - .write = tseg_blackhole_write, +static const MemoryRegionOps blackhole_ops = { + .read = blackhole_read, + .write = blackhole_write, .endianness = DEVICE_NATIVE_ENDIAN, .valid.min_access_size = 1, .valid.max_access_size = 4, @@ -430,6 +430,46 @@ static void mch_update_ext_tseg_mbytes(MCHPCIState *mch) } } +static void mch_update_smbase_smram(MCHPCIState *mch) +{ + PCIDevice *pd = PCI_DEVICE(mch); + uint8_t *reg = pd->config + MCH_HOST_BRIDGE_F_SMBASE; + bool lck; + + if (!mch->has_smram_at_smbase) { + return; + } + + if (*reg == MCH_HOST_BRIDGE_F_SMBASE_QUERY) { + pd->wmask[MCH_HOST_BRIDGE_F_SMBASE] = + MCH_HOST_BRIDGE_F_SMBASE_LCK; + *reg = MCH_HOST_BRIDGE_F_SMBASE_IN_RAM; + return; + } + + /* + * default/reset state, discard written value + * which will disable SMRAM balackhole at SMBASE + */ + if (pd->wmask[MCH_HOST_BRIDGE_F_SMBASE] == 0xff) { + *reg = 0x00; + } + + memory_region_transaction_begin(); + if (*reg & MCH_HOST_BRIDGE_F_SMBASE_LCK) { + /* disable all writes */ + pd->wmask[MCH_HOST_BRIDGE_F_SMBASE] &= + ~MCH_HOST_BRIDGE_F_SMBASE_LCK; + *reg = MCH_HOST_BRIDGE_F_SMBASE_LCK; + lck = true; + } else { + lck = false; + } + memory_region_set_enabled(&mch->smbase_blackhole, lck); + memory_region_set_enabled(&mch->smbase_window, lck); + memory_region_transaction_commit(); +} + static void mch_write_config(PCIDevice *d, uint32_t address, uint32_t val, int len) { @@ -456,6 +496,10 @@ static void mch_write_config(PCIDevice *d, MCH_HOST_BRIDGE_EXT_TSEG_MBYTES_SIZE)) { mch_update_ext_tseg_mbytes(mch); } + + if (ranges_overlap(address, len, MCH_HOST_BRIDGE_F_SMBASE, 1)) { + mch_update_smbase_smram(mch); + } } static void mch_update(MCHPCIState *mch) @@ -464,6 +508,7 @@ static void mch_update(MCHPCIState *mch) mch_update_pam(mch); mch_update_smram(mch); mch_update_ext_tseg_mbytes(mch); + mch_update_smbase_smram(mch); /* * pci hole goes from end-of-low-ram to io-apic. @@ -514,6 +559,9 @@ static void mch_reset(DeviceState *qdev) MCH_HOST_BRIDGE_EXT_TSEG_MBYTES_QUERY); } + d->config[MCH_HOST_BRIDGE_F_SMBASE] = 0; + d->wmask[MCH_HOST_BRIDGE_F_SMBASE] = 0xff; + mch_update(mch); } @@ -563,7 +611,7 @@ static void mch_realize(PCIDevice *d, Error **errp) memory_region_add_subregion(&mch->smram, 0xfeda0000, &mch->high_smram); memory_region_init_io(&mch->tseg_blackhole, OBJECT(mch), - &tseg_blackhole_ops, NULL, + &blackhole_ops, NULL, "tseg-blackhole", 0); memory_region_set_enabled(&mch->tseg_blackhole, false); memory_region_add_subregion_overlap(mch->system_memory, @@ -575,6 +623,27 @@ static void mch_realize(PCIDevice *d, Error **errp) memory_region_set_enabled(&mch->tseg_window, false); memory_region_add_subregion(&mch->smram, mch->below_4g_mem_size, &mch->tseg_window); + + /* + * This is not what hardware does, so it's QEMU specific hack. + * See commit message for details. + */ + memory_region_init_io(&mch->smbase_blackhole, OBJECT(mch), &blackhole_ops, + NULL, "smbase-blackhole", + MCH_HOST_BRIDGE_SMBASE_SIZE); + memory_region_set_enabled(&mch->smbase_blackhole, false); + memory_region_add_subregion_overlap(mch->system_memory, + MCH_HOST_BRIDGE_SMBASE_ADDR, + &mch->smbase_blackhole, 1); + + memory_region_init_alias(&mch->smbase_window, OBJECT(mch), + "smbase-window", mch->ram_memory, + MCH_HOST_BRIDGE_SMBASE_ADDR, + MCH_HOST_BRIDGE_SMBASE_SIZE); + memory_region_set_enabled(&mch->smbase_window, false); + memory_region_add_subregion(&mch->smram, MCH_HOST_BRIDGE_SMBASE_ADDR, + &mch->smbase_window); + object_property_add_const_link(qdev_get_machine(), "smram", OBJECT(&mch->smram), &error_abort); @@ -601,6 +670,7 @@ uint64_t mch_mcfg_base(void) static Property mch_props[] = { DEFINE_PROP_UINT16("extended-tseg-mbytes", MCHPCIState, ext_tseg_mbytes, 16), + DEFINE_PROP_BOOL("smbase-smram", MCHPCIState, has_smram_at_smbase, true), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index 4bc73a370e..d3af42ef92 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -943,7 +943,13 @@ void virtio_scsi_common_unrealize(DeviceState *dev) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(dev); + int i; + virtio_delete_queue(vs->ctrl_vq); + virtio_delete_queue(vs->event_vq); + for (i = 0; i < vs->conf.num_queues; i++) { + virtio_delete_queue(vs->cmd_vqs[i]); + } g_free(vs->cmd_vqs); virtio_cleanup(vdev); } diff --git a/hw/virtio/vhost-vsock.c b/hw/virtio/vhost-vsock.c index f5744363a8..b6cee479bb 100644 --- a/hw/virtio/vhost-vsock.c +++ b/hw/virtio/vhost-vsock.c @@ -335,8 +335,10 @@ static void vhost_vsock_device_realize(DeviceState *dev, Error **errp) sizeof(struct virtio_vsock_config)); /* Receive and transmit queues belong to vhost */ - virtio_add_queue(vdev, VHOST_VSOCK_QUEUE_SIZE, vhost_vsock_handle_output); - virtio_add_queue(vdev, VHOST_VSOCK_QUEUE_SIZE, vhost_vsock_handle_output); + vsock->recv_vq = virtio_add_queue(vdev, VHOST_VSOCK_QUEUE_SIZE, + vhost_vsock_handle_output); + vsock->trans_vq = virtio_add_queue(vdev, VHOST_VSOCK_QUEUE_SIZE, + vhost_vsock_handle_output); /* The event queue belongs to QEMU */ vsock->event_vq = virtio_add_queue(vdev, VHOST_VSOCK_QUEUE_SIZE, @@ -363,6 +365,9 @@ static void vhost_vsock_device_realize(DeviceState *dev, Error **errp) err_vhost_dev: vhost_dev_cleanup(&vsock->vhost_dev); err_virtio: + virtio_delete_queue(vsock->recv_vq); + virtio_delete_queue(vsock->trans_vq); + virtio_delete_queue(vsock->event_vq); virtio_cleanup(vdev); close(vhostfd); return; @@ -379,6 +384,9 @@ static void vhost_vsock_device_unrealize(DeviceState *dev, Error **errp) vhost_vsock_set_status(vdev, 0); vhost_dev_cleanup(&vsock->vhost_dev); + virtio_delete_queue(vsock->recv_vq); + virtio_delete_queue(vsock->trans_vq); + virtio_delete_queue(vsock->event_vq); virtio_cleanup(vdev); } diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index 4da0d5a6c5..9edfadc81d 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -547,26 +547,28 @@ static void vhost_region_add_section(struct vhost_dev *dev, uintptr_t mrs_host = (uintptr_t)memory_region_get_ram_ptr(section->mr) + section->offset_within_region; RAMBlock *mrs_rb = section->mr->ram_block; - size_t mrs_page = qemu_ram_pagesize(mrs_rb); trace_vhost_region_add_section(section->mr->name, mrs_gpa, mrs_size, mrs_host); - /* Round the section to it's page size */ - /* First align the start down to a page boundary */ - uint64_t alignage = mrs_host & (mrs_page - 1); - if (alignage) { - mrs_host -= alignage; - mrs_size += alignage; - mrs_gpa -= alignage; - } - /* Now align the size up to a page boundary */ - alignage = mrs_size & (mrs_page - 1); - if (alignage) { - mrs_size += mrs_page - alignage; + if (dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER) { + /* Round the section to it's page size */ + /* First align the start down to a page boundary */ + size_t mrs_page = qemu_ram_pagesize(mrs_rb); + uint64_t alignage = mrs_host & (mrs_page - 1); + if (alignage) { + mrs_host -= alignage; + mrs_size += alignage; + mrs_gpa -= alignage; + } + /* Now align the size up to a page boundary */ + alignage = mrs_size & (mrs_page - 1); + if (alignage) { + mrs_size += mrs_page - alignage; + } + trace_vhost_region_add_section_aligned(section->mr->name, mrs_gpa, + mrs_size, mrs_host); } - trace_vhost_region_add_section_aligned(section->mr->name, mrs_gpa, mrs_size, - mrs_host); if (dev->n_tmp_sections) { /* Since we already have at least one section, lets see if @@ -590,9 +592,10 @@ static void vhost_region_add_section(struct vhost_dev *dev, * match up in the same RAMBlock if they do. */ if (mrs_gpa < prev_gpa_start) { - error_report("%s:Section rounded to %"PRIx64 - " prior to previous %"PRIx64, - __func__, mrs_gpa, prev_gpa_start); + error_report("%s:Section '%s' rounded to %"PRIx64 + " prior to previous '%s' %"PRIx64, + __func__, section->mr->name, mrs_gpa, + prev_sec->mr->name, prev_gpa_start); /* A way to cleanly fail here would be better */ return; } diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h index a46116167c..53de19753a 100644 --- a/include/exec/cpu_ldst.h +++ b/include/exec/cpu_ldst.h @@ -234,11 +234,6 @@ static inline uintptr_t tlb_index(CPUArchState *env, uintptr_t mmu_idx, return (addr >> TARGET_PAGE_BITS) & size_mask; } -static inline size_t tlb_n_entries(CPUArchState *env, uintptr_t mmu_idx) -{ - return (env_tlb(env)->f[mmu_idx].mask >> CPU_TLB_ENTRY_BITS) + 1; -} - /* Find the TLB entry corresponding to the mmu_idx + address pair. */ static inline CPUTLBEntry *tlb_entry(CPUArchState *env, uintptr_t mmu_idx, target_ulong addr) diff --git a/include/hw/pci-host/q35.h b/include/hw/pci-host/q35.h index b3bcf2e632..976fbae599 100644 --- a/include/hw/pci-host/q35.h +++ b/include/hw/pci-host/q35.h @@ -32,6 +32,7 @@ #include "hw/acpi/ich9.h" #include "hw/pci-host/pam.h" #include "hw/i386/intel_iommu.h" +#include "qemu/units.h" #define TYPE_Q35_HOST_DEVICE "q35-pcihost" #define Q35_HOST_DEVICE(obj) \ @@ -54,6 +55,8 @@ typedef struct MCHPCIState { MemoryRegion smram_region, open_high_smram; MemoryRegion smram, low_smram, high_smram; MemoryRegion tseg_blackhole, tseg_window; + MemoryRegion smbase_blackhole, smbase_window; + bool has_smram_at_smbase; Range pci_hole; uint64_t below_4g_mem_size; uint64_t above_4g_mem_size; @@ -97,6 +100,13 @@ typedef struct Q35PCIHost { #define MCH_HOST_BRIDGE_EXT_TSEG_MBYTES_QUERY 0xffff #define MCH_HOST_BRIDGE_EXT_TSEG_MBYTES_MAX 0xfff +#define MCH_HOST_BRIDGE_SMBASE_SIZE (128 * KiB) +#define MCH_HOST_BRIDGE_SMBASE_ADDR 0x30000 +#define MCH_HOST_BRIDGE_F_SMBASE 0x9c +#define MCH_HOST_BRIDGE_F_SMBASE_QUERY 0xff +#define MCH_HOST_BRIDGE_F_SMBASE_IN_RAM 0x01 +#define MCH_HOST_BRIDGE_F_SMBASE_LCK 0x02 + #define MCH_HOST_BRIDGE_PCIEXBAR 0x60 /* 64bit register */ #define MCH_HOST_BRIDGE_PCIEXBAR_SIZE 8 /* 64bit register */ #define MCH_HOST_BRIDGE_PCIEXBAR_DEFAULT 0xb0000000 diff --git a/include/hw/virtio/vhost-vsock.h b/include/hw/virtio/vhost-vsock.h index d509d67c4a..bc5a988ee5 100644 --- a/include/hw/virtio/vhost-vsock.h +++ b/include/hw/virtio/vhost-vsock.h @@ -33,6 +33,8 @@ typedef struct { struct vhost_virtqueue vhost_vqs[2]; struct vhost_dev vhost_dev; VirtQueue *event_vq; + VirtQueue *recv_vq; + VirtQueue *trans_vq; QEMUTimer *post_load_timer; /*< public >*/ diff --git a/scripts/git.orderfile b/scripts/git.orderfile index e89790941c..1f747b583a 100644 --- a/scripts/git.orderfile +++ b/scripts/git.orderfile @@ -25,5 +25,8 @@ qga/*.json # headers *.h +# decoding tree specification +*.decode + # code *.c diff --git a/tests/data/acpi/q35/DSDT b/tests/data/acpi/q35/DSDT Binary files differindex 77ea60ffed..1f91888d7a 100644 --- a/tests/data/acpi/q35/DSDT +++ b/tests/data/acpi/q35/DSDT diff --git a/tests/data/acpi/q35/DSDT.acpihmat b/tests/data/acpi/q35/DSDT.acpihmat Binary files differindex 30e3717b5b..3586f6368a 100644 --- a/tests/data/acpi/q35/DSDT.acpihmat +++ b/tests/data/acpi/q35/DSDT.acpihmat diff --git a/tests/data/acpi/q35/DSDT.bridge b/tests/data/acpi/q35/DSDT.bridge Binary files differindex fbc2d40000..eae3a2a865 100644 --- a/tests/data/acpi/q35/DSDT.bridge +++ b/tests/data/acpi/q35/DSDT.bridge diff --git a/tests/data/acpi/q35/DSDT.cphp b/tests/data/acpi/q35/DSDT.cphp Binary files differindex 6a896cb214..53d735a4de 100644 --- a/tests/data/acpi/q35/DSDT.cphp +++ b/tests/data/acpi/q35/DSDT.cphp diff --git a/tests/data/acpi/q35/DSDT.dimmpxm b/tests/data/acpi/q35/DSDT.dimmpxm Binary files differindex 23fdf5e60a..02ccdd5f38 100644 --- a/tests/data/acpi/q35/DSDT.dimmpxm +++ b/tests/data/acpi/q35/DSDT.dimmpxm diff --git a/tests/data/acpi/q35/DSDT.ipmibt b/tests/data/acpi/q35/DSDT.ipmibt Binary files differindex c3fca0a71e..9e2d4f785c 100644 --- a/tests/data/acpi/q35/DSDT.ipmibt +++ b/tests/data/acpi/q35/DSDT.ipmibt diff --git a/tests/data/acpi/q35/DSDT.memhp b/tests/data/acpi/q35/DSDT.memhp Binary files differindex 2abd0e36cd..baefa611ac 100644 --- a/tests/data/acpi/q35/DSDT.memhp +++ b/tests/data/acpi/q35/DSDT.memhp diff --git a/tests/data/acpi/q35/DSDT.mmio64 b/tests/data/acpi/q35/DSDT.mmio64 Binary files differindex b32034a11c..aae0ea2110 100644 --- a/tests/data/acpi/q35/DSDT.mmio64 +++ b/tests/data/acpi/q35/DSDT.mmio64 diff --git a/tests/data/acpi/q35/DSDT.numamem b/tests/data/acpi/q35/DSDT.numamem Binary files differindex d8b2b47f8b..859a2e0871 100644 --- a/tests/data/acpi/q35/DSDT.numamem +++ b/tests/data/acpi/q35/DSDT.numamem diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index f1ac2d7e96..3ab4872bd7 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -16,7 +16,10 @@ * 1. add empty files for new tables, if any, under tests/data/acpi * 2. list any changed files in tests/bios-tables-test-allowed-diff.h * 3. commit the above *before* making changes that affect the tables - * Maintainer: + * + * Contributor or ACPI Maintainer (steps 4-7 need to be redone to resolve conflicts + * in binary commit created in step 6): + * * After 1-3 above tests will pass but ignore differences with the expected files. * You will also notice that tests/bios-tables-test-allowed-diff.h lists * a bunch of files. This is your hint that you need to do the below: @@ -28,13 +31,23 @@ * output. If not - disassemble them yourself in any way you like. * Look at the differences - make sure they make sense and match what the * changes you are merging are supposed to do. + * Save the changes, preferably in form of ASL diff for the commit log in + * step 6. * * 5. From build directory, run: * $(SRC_PATH)/tests/data/acpi/rebuild-expected-aml.sh - * 6. Now commit any changes. - * 7. Before doing a pull request, make sure tests/bios-tables-test-allowed-diff.h - * is empty - this will ensure following changes to ACPI tables will - * be noticed. + * 6. Now commit any changes to the expected binary, include diff from step 4 + * in commit log. + * 7. Before sending patches to the list (Contributor) + * or before doing a pull request (Maintainer), make sure + * tests/bios-tables-test-allowed-diff.h is empty - this will ensure + * following changes to ACPI tables will be noticed. + * + * The resulting patchset/pull request then looks like this: + * - patch 1: list changed files in tests/bios-tables-test-allowed-diff.h. + * - patches 2 - n: real changes, may contain multiple patches. + * - patch n + 1: update golden master binaries and empty + * tests/bios-tables-test-allowed-diff.h */ #include "qemu/osdep.h" diff --git a/tests/qtest/q35-test.c b/tests/qtest/q35-test.c index a68183d513..c922d81bc0 100644 --- a/tests/qtest/q35-test.c +++ b/tests/qtest/q35-test.c @@ -186,6 +186,109 @@ static void test_tseg_size(const void *data) qtest_quit(qts); } +#define SMBASE 0x30000 +#define SMRAM_TEST_PATTERN 0x32 +#define SMRAM_TEST_RESET_PATTERN 0x23 + +static void test_smram_smbase_lock(void) +{ + QPCIBus *pcibus; + QPCIDevice *pcidev; + QDict *response; + QTestState *qts; + int i; + + qts = qtest_init("-M q35"); + + pcibus = qpci_new_pc(qts, NULL); + g_assert(pcibus != NULL); + + pcidev = qpci_device_find(pcibus, 0); + g_assert(pcidev != NULL); + + /* check that SMRAM is not enabled by default */ + g_assert(qpci_config_readb(pcidev, MCH_HOST_BRIDGE_F_SMBASE) == 0); + qtest_writeb(qts, SMBASE, SMRAM_TEST_PATTERN); + g_assert_cmpint(qtest_readb(qts, SMBASE), ==, SMRAM_TEST_PATTERN); + + /* check that writing junk to 0x9c before before negotiating is ignored */ + for (i = 0; i < 0xff; i++) { + qpci_config_writeb(pcidev, MCH_HOST_BRIDGE_F_SMBASE, i); + g_assert(qpci_config_readb(pcidev, MCH_HOST_BRIDGE_F_SMBASE) == 0); + } + + /* enable SMRAM at SMBASE */ + qpci_config_writeb(pcidev, MCH_HOST_BRIDGE_F_SMBASE, 0xff); + g_assert(qpci_config_readb(pcidev, MCH_HOST_BRIDGE_F_SMBASE) == 0x01); + /* lock SMRAM at SMBASE */ + qpci_config_writeb(pcidev, MCH_HOST_BRIDGE_F_SMBASE, 0x02); + g_assert(qpci_config_readb(pcidev, MCH_HOST_BRIDGE_F_SMBASE) == 0x02); + + /* check that SMRAM at SMBASE is locked and can't be unlocked */ + g_assert_cmpint(qtest_readb(qts, SMBASE), ==, 0xff); + for (i = 0; i <= 0xff; i++) { + /* make sure register is immutable */ + qpci_config_writeb(pcidev, MCH_HOST_BRIDGE_F_SMBASE, i); + g_assert(qpci_config_readb(pcidev, MCH_HOST_BRIDGE_F_SMBASE) == 0x02); + + /* RAM access should go into black hole */ + qtest_writeb(qts, SMBASE, SMRAM_TEST_PATTERN); + g_assert_cmpint(qtest_readb(qts, SMBASE), ==, 0xff); + } + + /* reset */ + response = qtest_qmp(qts, "{'execute': 'system_reset', 'arguments': {} }"); + g_assert(response); + g_assert(!qdict_haskey(response, "error")); + qobject_unref(response); + + /* check RAM at SMBASE is available after reset */ + g_assert_cmpint(qtest_readb(qts, SMBASE), ==, SMRAM_TEST_PATTERN); + g_assert(qpci_config_readb(pcidev, MCH_HOST_BRIDGE_F_SMBASE) == 0); + qtest_writeb(qts, SMBASE, SMRAM_TEST_RESET_PATTERN); + g_assert_cmpint(qtest_readb(qts, SMBASE), ==, SMRAM_TEST_RESET_PATTERN); + + g_free(pcidev); + qpci_free_pc(pcibus); + + qtest_quit(qts); +} + +static void test_without_smram_base(void) +{ + QPCIBus *pcibus; + QPCIDevice *pcidev; + QTestState *qts; + int i; + + qts = qtest_init("-M pc-q35-4.1"); + + pcibus = qpci_new_pc(qts, NULL); + g_assert(pcibus != NULL); + + pcidev = qpci_device_find(pcibus, 0); + g_assert(pcidev != NULL); + + /* check that RAM is accessible */ + qtest_writeb(qts, SMBASE, SMRAM_TEST_PATTERN); + g_assert_cmpint(qtest_readb(qts, SMBASE), ==, SMRAM_TEST_PATTERN); + + /* check that writing to 0x9c succeeds */ + for (i = 0; i <= 0xff; i++) { + qpci_config_writeb(pcidev, MCH_HOST_BRIDGE_F_SMBASE, i); + g_assert(qpci_config_readb(pcidev, MCH_HOST_BRIDGE_F_SMBASE) == i); + } + + /* check that RAM is still accessible */ + qtest_writeb(qts, SMBASE, SMRAM_TEST_PATTERN + 1); + g_assert_cmpint(qtest_readb(qts, SMBASE), ==, (SMRAM_TEST_PATTERN + 1)); + + g_free(pcidev); + qpci_free_pc(pcibus); + + qtest_quit(qts); +} + int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); @@ -197,5 +300,7 @@ int main(int argc, char **argv) qtest_add_data_func("/q35/tseg-size/8mb", &tseg_8mb, test_tseg_size); qtest_add_data_func("/q35/tseg-size/ext/16mb", &tseg_ext_16mb, test_tseg_size); + qtest_add_func("/q35/smram/smbase_lock", test_smram_smbase_lock); + qtest_add_func("/q35/smram/legacy_smbase", test_without_smram_base); return g_test_run(); } diff --git a/util/cacheinfo.c b/util/cacheinfo.c index ea6f3e99bf..d94dc6adc8 100644 --- a/util/cacheinfo.c +++ b/util/cacheinfo.c @@ -93,10 +93,16 @@ static void sys_cache_info(int *isize, int *dsize) static void sys_cache_info(int *isize, int *dsize) { # ifdef _SC_LEVEL1_ICACHE_LINESIZE - *isize = sysconf(_SC_LEVEL1_ICACHE_LINESIZE); + int tmp_isize = (int) sysconf(_SC_LEVEL1_ICACHE_LINESIZE); + if (tmp_isize > 0) { + *isize = tmp_isize; + } # endif # ifdef _SC_LEVEL1_DCACHE_LINESIZE - *dsize = sysconf(_SC_LEVEL1_DCACHE_LINESIZE); + int tmp_dsize = (int) sysconf(_SC_LEVEL1_DCACHE_LINESIZE); + if (tmp_dsize > 0) { + *dsize = tmp_dsize; + } # endif } #endif /* sys_cache_info */ @@ -2755,8 +2755,6 @@ static int do_configure_accelerator(void *opaque, QemuOpts *opts, Error **errp) static void configure_accelerators(const char *progname) { const char *accel; - char **accel_list, **tmp; - bool accel_initialised = false; bool init_failed = false; qemu_opts_foreach(qemu_find_opts("icount"), @@ -2764,26 +2762,33 @@ static void configure_accelerators(const char *progname) accel = qemu_opt_get(qemu_get_machine_opts(), "accel"); if (QTAILQ_EMPTY(&qemu_accel_opts.head)) { + char **accel_list, **tmp; + if (accel == NULL) { /* Select the default accelerator */ - if (!accel_find("tcg") && !accel_find("kvm")) { - error_report("No accelerator selected and" - " no default accelerator available"); - exit(1); - } else { - int pnlen = strlen(progname); - if (pnlen >= 3 && g_str_equal(&progname[pnlen - 3], "kvm")) { + bool have_tcg = accel_find("tcg"); + bool have_kvm = accel_find("kvm"); + + if (have_tcg && have_kvm) { + if (g_str_has_suffix(progname, "kvm")) { /* If the program name ends with "kvm", we prefer KVM */ accel = "kvm:tcg"; } else { accel = "tcg:kvm"; } + } else if (have_kvm) { + accel = "kvm"; + } else if (have_tcg) { + accel = "tcg"; + } else { + error_report("No accelerator selected and" + " no default accelerator available"); + exit(1); } } - accel_list = g_strsplit(accel, ":", 0); - for (tmp = accel_list; !accel_initialised && tmp && *tmp; tmp++) { + for (tmp = accel_list; *tmp; tmp++) { /* * Filter invalid accelerators here, to prevent obscenities * such as "-machine accel=tcg,,thread=single". |