mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-08 10:13:56 -06:00
virtio,pc,pci: features, cleanups, fixes
vhost-user-snd support x2APIC mode with TCG support CXL update to r3.1 fixes, cleanups all over the place. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> -----BEGIN PGP SIGNATURE----- iQFDBAABCAAtFiEEXQn9CHHI+FuUyooNKB8NuNKNVGkFAmXMoXUPHG1zdEByZWRo YXQuY29tAAoJECgfDbjSjVRpFtMIAKUKD0hzJrwOyPo4xsRUMbsB3ehIsJsMKfOK w+JWzTaojAG8ENPelWBdL2sEIs5U73VOchjLqHbH2m5sz6GJ13214amvdU/fYc8+ /dU2ZKoAmaR5L1ovKO/fq07y/J6DrITZ5tosy2i84Xa8EnsL4j3wEPNVWsDi7dna mvXUICSOOoJQ4O2YhSruKCQ8qIgF1/0Oi3u/rcrW3alSs8VQlrtQXxl6k+LbYqek +Fytco3jMRHPvQ+GYUIwGuHjN15ghArcvbsV0GIa+24BPY5h7YbDYGbfasePT5OK zDz51jitkoyDrQr+OzwOEe/X5+dVGhayRXfMtU5Qm53IE3y61qc= =K4b1 -----END PGP SIGNATURE----- Merge tag 'for_upstream' of https://git.kernel.org/pub/scm/virt/kvm/mst/qemu into staging virtio,pc,pci: features, cleanups, fixes vhost-user-snd support x2APIC mode with TCG support CXL update to r3.1 fixes, cleanups all over the place. Signed-off-by: Michael S. Tsirkin <mst@redhat.com> # -----BEGIN PGP SIGNATURE----- # # iQFDBAABCAAtFiEEXQn9CHHI+FuUyooNKB8NuNKNVGkFAmXMoXUPHG1zdEByZWRo # YXQuY29tAAoJECgfDbjSjVRpFtMIAKUKD0hzJrwOyPo4xsRUMbsB3ehIsJsMKfOK # w+JWzTaojAG8ENPelWBdL2sEIs5U73VOchjLqHbH2m5sz6GJ13214amvdU/fYc8+ # /dU2ZKoAmaR5L1ovKO/fq07y/J6DrITZ5tosy2i84Xa8EnsL4j3wEPNVWsDi7dna # mvXUICSOOoJQ4O2YhSruKCQ8qIgF1/0Oi3u/rcrW3alSs8VQlrtQXxl6k+LbYqek # +Fytco3jMRHPvQ+GYUIwGuHjN15ghArcvbsV0GIa+24BPY5h7YbDYGbfasePT5OK # zDz51jitkoyDrQr+OzwOEe/X5+dVGhayRXfMtU5Qm53IE3y61qc= # =K4b1 # -----END PGP SIGNATURE----- # gpg: Signature made Wed 14 Feb 2024 11:18:13 GMT # gpg: using RSA key 5D09FD0871C8F85B94CA8A0D281F0DB8D28D5469 # gpg: issuer "mst@redhat.com" # gpg: Good signature from "Michael S. Tsirkin <mst@kernel.org>" [full] # gpg: aka "Michael S. Tsirkin <mst@redhat.com>" [full] # Primary key fingerprint: 0270 606B 6F3C DF3D 0B17 0970 C350 3912 AFBE 8E67 # Subkey fingerprint: 5D09 FD08 71C8 F85B 94CA 8A0D 281F 0DB8 D28D 5469 * tag 'for_upstream' of https://git.kernel.org/pub/scm/virt/kvm/mst/qemu: (60 commits) MAINTAINERS: Switch to my Enfabrica email virtio-gpu-rutabaga.c: override resource_destroy method virtio-gpu.c: add resource_destroy class method hw/display/virtio-gpu.c: use reset_bh class method hw/smbios: Fix port connector option validation hw/smbios: Fix OEM strings table option validation virtio-gpu: Correct virgl_renderer_resource_get_info() error check hw/cxl: Standardize all references on CXL r3.1 and minor updates hw/cxl: Update mailbox status registers. hw/cxl: Update RAS Capability Definitions for version 3. hw/cxl: Update link register definitions. hw/cxl: Update HDM Decoder capability to version 3 tests/acpi: Update DSDT.cxl to reflect change _STA return value. hw/i386: Fix _STA return value for ACPI0017 tests/acpi: Allow update of DSDT.cxl hw/mem/cxl_type3: Fix potential divide by zero reported by coverity hw/cxl: Pass NULL for a NULL MemoryRegionOps hw/cxl: Pass CXLComponentState to cache_mem_ops hw/cxl/device: read from register values in mdev_reg_read() hw/cxl/mbox: Remove dead code ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
5767815218
84 changed files with 2004 additions and 1910 deletions
473
hw/intc/apic.c
473
hw/intc/apic.c
|
@ -32,14 +32,13 @@
|
|||
#include "qapi/error.h"
|
||||
#include "qom/object.h"
|
||||
|
||||
#define MAX_APICS 255
|
||||
#define MAX_APIC_WORDS 8
|
||||
|
||||
#define SYNC_FROM_VAPIC 0x1
|
||||
#define SYNC_TO_VAPIC 0x2
|
||||
#define SYNC_ISR_IRR_TO_VAPIC 0x4
|
||||
|
||||
static APICCommonState *local_apics[MAX_APICS + 1];
|
||||
static APICCommonState **local_apics;
|
||||
static uint32_t max_apics;
|
||||
static uint32_t max_apic_words;
|
||||
|
||||
#define TYPE_APIC "apic"
|
||||
/*This is reusing the APICCommonState typedef from APIC_COMMON */
|
||||
|
@ -49,7 +48,19 @@ DECLARE_INSTANCE_CHECKER(APICCommonState, APIC,
|
|||
static void apic_set_irq(APICCommonState *s, int vector_num, int trigger_mode);
|
||||
static void apic_update_irq(APICCommonState *s);
|
||||
static void apic_get_delivery_bitmask(uint32_t *deliver_bitmask,
|
||||
uint8_t dest, uint8_t dest_mode);
|
||||
uint32_t dest, uint8_t dest_mode);
|
||||
|
||||
void apic_set_max_apic_id(uint32_t max_apic_id)
|
||||
{
|
||||
int word_size = 32;
|
||||
|
||||
/* round up the max apic id to next multiple of words */
|
||||
max_apics = (max_apic_id + word_size - 1) & ~(word_size - 1);
|
||||
|
||||
local_apics = g_malloc0(sizeof(*local_apics) * max_apics);
|
||||
max_apic_words = max_apics >> 5;
|
||||
}
|
||||
|
||||
|
||||
/* Find first bit starting from msb */
|
||||
static int apic_fls_bit(uint32_t value)
|
||||
|
@ -199,10 +210,10 @@ static void apic_external_nmi(APICCommonState *s)
|
|||
#define foreach_apic(apic, deliver_bitmask, code) \
|
||||
{\
|
||||
int __i, __j;\
|
||||
for(__i = 0; __i < MAX_APIC_WORDS; __i++) {\
|
||||
for (__i = 0; __i < max_apic_words; __i++) {\
|
||||
uint32_t __mask = deliver_bitmask[__i];\
|
||||
if (__mask) {\
|
||||
for(__j = 0; __j < 32; __j++) {\
|
||||
for (__j = 0; __j < 32; __j++) {\
|
||||
if (__mask & (1U << __j)) {\
|
||||
apic = local_apics[__i * 32 + __j];\
|
||||
if (apic) {\
|
||||
|
@ -226,7 +237,7 @@ static void apic_bus_deliver(const uint32_t *deliver_bitmask,
|
|||
{
|
||||
int i, d;
|
||||
d = -1;
|
||||
for(i = 0; i < MAX_APIC_WORDS; i++) {
|
||||
for (i = 0; i < max_apic_words; i++) {
|
||||
if (deliver_bitmask[i]) {
|
||||
d = i * 32 + apic_ffs_bit(deliver_bitmask[i]);
|
||||
break;
|
||||
|
@ -276,20 +287,70 @@ static void apic_bus_deliver(const uint32_t *deliver_bitmask,
|
|||
apic_set_irq(apic_iter, vector_num, trigger_mode) );
|
||||
}
|
||||
|
||||
void apic_deliver_irq(uint8_t dest, uint8_t dest_mode, uint8_t delivery_mode,
|
||||
uint8_t vector_num, uint8_t trigger_mode)
|
||||
static void apic_deliver_irq(uint32_t dest, uint8_t dest_mode,
|
||||
uint8_t delivery_mode, uint8_t vector_num,
|
||||
uint8_t trigger_mode)
|
||||
{
|
||||
uint32_t deliver_bitmask[MAX_APIC_WORDS];
|
||||
uint32_t *deliver_bitmask = g_malloc(max_apic_words * sizeof(uint32_t));
|
||||
|
||||
trace_apic_deliver_irq(dest, dest_mode, delivery_mode, vector_num,
|
||||
trigger_mode);
|
||||
|
||||
apic_get_delivery_bitmask(deliver_bitmask, dest, dest_mode);
|
||||
apic_bus_deliver(deliver_bitmask, delivery_mode, vector_num, trigger_mode);
|
||||
g_free(deliver_bitmask);
|
||||
}
|
||||
|
||||
static void apic_set_base(APICCommonState *s, uint64_t val)
|
||||
bool is_x2apic_mode(DeviceState *dev)
|
||||
{
|
||||
APICCommonState *s = APIC(dev);
|
||||
|
||||
return s->apicbase & MSR_IA32_APICBASE_EXTD;
|
||||
}
|
||||
|
||||
static int apic_set_base_check(APICCommonState *s, uint64_t val)
|
||||
{
|
||||
/* Enable x2apic when x2apic is not supported by CPU */
|
||||
if (!cpu_has_x2apic_feature(&s->cpu->env) &&
|
||||
val & MSR_IA32_APICBASE_EXTD) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Transition into invalid state
|
||||
* (s->apicbase & MSR_IA32_APICBASE_ENABLE == 0) &&
|
||||
* (s->apicbase & MSR_IA32_APICBASE_EXTD) == 1
|
||||
*/
|
||||
if (!(val & MSR_IA32_APICBASE_ENABLE) &&
|
||||
(val & MSR_IA32_APICBASE_EXTD)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Invalid transition from disabled mode to x2APIC */
|
||||
if (!(s->apicbase & MSR_IA32_APICBASE_ENABLE) &&
|
||||
!(s->apicbase & MSR_IA32_APICBASE_EXTD) &&
|
||||
(val & MSR_IA32_APICBASE_ENABLE) &&
|
||||
(val & MSR_IA32_APICBASE_EXTD)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Invalid transition from x2APIC to xAPIC */
|
||||
if ((s->apicbase & MSR_IA32_APICBASE_ENABLE) &&
|
||||
(s->apicbase & MSR_IA32_APICBASE_EXTD) &&
|
||||
(val & MSR_IA32_APICBASE_ENABLE) &&
|
||||
!(val & MSR_IA32_APICBASE_EXTD)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int apic_set_base(APICCommonState *s, uint64_t val)
|
||||
{
|
||||
if (apic_set_base_check(s, val) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
s->apicbase = (val & 0xfffff000) |
|
||||
(s->apicbase & (MSR_IA32_APICBASE_BSP | MSR_IA32_APICBASE_ENABLE));
|
||||
/* if disabled, cannot be enabled again */
|
||||
|
@ -298,6 +359,25 @@ static void apic_set_base(APICCommonState *s, uint64_t val)
|
|||
cpu_clear_apic_feature(&s->cpu->env);
|
||||
s->spurious_vec &= ~APIC_SV_ENABLE;
|
||||
}
|
||||
|
||||
/* Transition from disabled mode to xAPIC */
|
||||
if (!(s->apicbase & MSR_IA32_APICBASE_ENABLE) &&
|
||||
(val & MSR_IA32_APICBASE_ENABLE)) {
|
||||
s->apicbase |= MSR_IA32_APICBASE_ENABLE;
|
||||
cpu_set_apic_feature(&s->cpu->env);
|
||||
}
|
||||
|
||||
/* Transition from xAPIC to x2APIC */
|
||||
if (cpu_has_x2apic_feature(&s->cpu->env) &&
|
||||
!(s->apicbase & MSR_IA32_APICBASE_EXTD) &&
|
||||
(val & MSR_IA32_APICBASE_EXTD)) {
|
||||
s->apicbase |= MSR_IA32_APICBASE_EXTD;
|
||||
|
||||
s->log_dest = ((s->initial_apic_id & 0xffff0) << 16) |
|
||||
(1 << (s->initial_apic_id & 0xf));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void apic_set_tpr(APICCommonState *s, uint8_t val)
|
||||
|
@ -435,57 +515,123 @@ static void apic_eoi(APICCommonState *s)
|
|||
apic_update_irq(s);
|
||||
}
|
||||
|
||||
static int apic_find_dest(uint8_t dest)
|
||||
static bool apic_match_dest(APICCommonState *apic, uint32_t dest)
|
||||
{
|
||||
APICCommonState *apic = local_apics[dest];
|
||||
if (is_x2apic_mode(&apic->parent_obj)) {
|
||||
return apic->initial_apic_id == dest;
|
||||
} else {
|
||||
return apic->id == (uint8_t)dest;
|
||||
}
|
||||
}
|
||||
|
||||
static void apic_find_dest(uint32_t *deliver_bitmask, uint32_t dest)
|
||||
{
|
||||
APICCommonState *apic = NULL;
|
||||
int i;
|
||||
|
||||
if (apic && apic->id == dest)
|
||||
return dest; /* shortcut in case apic->id == local_apics[dest]->id */
|
||||
|
||||
for (i = 0; i < MAX_APICS; i++) {
|
||||
for (i = 0; i < max_apics; i++) {
|
||||
apic = local_apics[i];
|
||||
if (apic && apic->id == dest)
|
||||
return i;
|
||||
if (!apic)
|
||||
break;
|
||||
if (apic && apic_match_dest(apic, dest)) {
|
||||
apic_set_bit(deliver_bitmask, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
/*
|
||||
* Deliver interrupt to x2APIC CPUs if it is x2APIC broadcast.
|
||||
* Otherwise, deliver interrupt to xAPIC CPUs if it is xAPIC
|
||||
* broadcast.
|
||||
*/
|
||||
static void apic_get_broadcast_bitmask(uint32_t *deliver_bitmask,
|
||||
bool is_x2apic_broadcast)
|
||||
{
|
||||
int i;
|
||||
APICCommonState *apic_iter;
|
||||
|
||||
for (i = 0; i < max_apics; i++) {
|
||||
apic_iter = local_apics[i];
|
||||
if (apic_iter) {
|
||||
bool apic_in_x2apic = is_x2apic_mode(&apic_iter->parent_obj);
|
||||
|
||||
if (is_x2apic_broadcast && apic_in_x2apic) {
|
||||
apic_set_bit(deliver_bitmask, i);
|
||||
} else if (!is_x2apic_broadcast && !apic_in_x2apic) {
|
||||
apic_set_bit(deliver_bitmask, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void apic_get_delivery_bitmask(uint32_t *deliver_bitmask,
|
||||
uint8_t dest, uint8_t dest_mode)
|
||||
uint32_t dest, uint8_t dest_mode)
|
||||
{
|
||||
APICCommonState *apic_iter;
|
||||
APICCommonState *apic;
|
||||
int i;
|
||||
|
||||
if (dest_mode == 0) {
|
||||
if (dest == 0xff) {
|
||||
memset(deliver_bitmask, 0xff, MAX_APIC_WORDS * sizeof(uint32_t));
|
||||
memset(deliver_bitmask, 0x00, max_apic_words * sizeof(uint32_t));
|
||||
|
||||
/*
|
||||
* x2APIC broadcast is delivered to all x2APIC CPUs regardless of
|
||||
* destination mode. In case the destination mode is physical, it is
|
||||
* broadcasted to all xAPIC CPUs too. Otherwise, if the destination
|
||||
* mode is logical, we need to continue checking if xAPIC CPUs accepts
|
||||
* the interrupt.
|
||||
*/
|
||||
if (dest == 0xffffffff) {
|
||||
if (dest_mode == APIC_DESTMODE_PHYSICAL) {
|
||||
memset(deliver_bitmask, 0xff, max_apic_words * sizeof(uint32_t));
|
||||
return;
|
||||
} else {
|
||||
int idx = apic_find_dest(dest);
|
||||
memset(deliver_bitmask, 0x00, MAX_APIC_WORDS * sizeof(uint32_t));
|
||||
if (idx >= 0)
|
||||
apic_set_bit(deliver_bitmask, idx);
|
||||
apic_get_broadcast_bitmask(deliver_bitmask, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (dest_mode == APIC_DESTMODE_PHYSICAL) {
|
||||
apic_find_dest(deliver_bitmask, dest);
|
||||
/* Any APIC in xAPIC mode will interpret 0xFF as broadcast */
|
||||
if (dest == 0xff) {
|
||||
apic_get_broadcast_bitmask(deliver_bitmask, false);
|
||||
}
|
||||
} else {
|
||||
/* XXX: cluster mode */
|
||||
memset(deliver_bitmask, 0x00, MAX_APIC_WORDS * sizeof(uint32_t));
|
||||
for(i = 0; i < MAX_APICS; i++) {
|
||||
apic_iter = local_apics[i];
|
||||
if (apic_iter) {
|
||||
if (apic_iter->dest_mode == 0xf) {
|
||||
if (dest & apic_iter->log_dest)
|
||||
apic_set_bit(deliver_bitmask, i);
|
||||
} else if (apic_iter->dest_mode == 0x0) {
|
||||
if ((dest & 0xf0) == (apic_iter->log_dest & 0xf0) &&
|
||||
(dest & apic_iter->log_dest & 0x0f)) {
|
||||
/* XXX: logical mode */
|
||||
for (i = 0; i < max_apics; i++) {
|
||||
apic = local_apics[i];
|
||||
if (apic) {
|
||||
/* x2APIC logical mode */
|
||||
if (apic->apicbase & MSR_IA32_APICBASE_EXTD) {
|
||||
if ((dest >> 16) == (apic->extended_log_dest >> 16) &&
|
||||
(dest & apic->extended_log_dest & 0xffff)) {
|
||||
apic_set_bit(deliver_bitmask, i);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
|
||||
/* xAPIC logical mode */
|
||||
dest = (uint8_t)dest;
|
||||
if (apic->dest_mode == APIC_DESTMODE_LOGICAL_FLAT) {
|
||||
if (dest & apic->log_dest) {
|
||||
apic_set_bit(deliver_bitmask, i);
|
||||
}
|
||||
} else if (apic->dest_mode == APIC_DESTMODE_LOGICAL_CLUSTER) {
|
||||
/*
|
||||
* In cluster model of xAPIC logical mode IPI, 4 higher
|
||||
* bits are used as cluster address, 4 lower bits are
|
||||
* the bitmask for local APICs in the cluster. The IPI
|
||||
* is delivered to an APIC if the cluster address
|
||||
* matches and the APIC's address bit in the cluster is
|
||||
* set in bitmask of destination ID in IPI.
|
||||
*
|
||||
* The cluster address ranges from 0 - 14, the cluster
|
||||
* address 15 (0xf) is the broadcast address to all
|
||||
* clusters.
|
||||
*/
|
||||
if ((dest & 0xf0) == 0xf0 ||
|
||||
(dest & 0xf0) == (apic->log_dest & 0xf0)) {
|
||||
if (dest & apic->log_dest & 0x0f) {
|
||||
apic_set_bit(deliver_bitmask, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -509,29 +655,36 @@ void apic_sipi(DeviceState *dev)
|
|||
s->wait_for_sipi = 0;
|
||||
}
|
||||
|
||||
static void apic_deliver(DeviceState *dev, uint8_t dest, uint8_t dest_mode,
|
||||
static void apic_deliver(DeviceState *dev, uint32_t dest, uint8_t dest_mode,
|
||||
uint8_t delivery_mode, uint8_t vector_num,
|
||||
uint8_t trigger_mode)
|
||||
uint8_t trigger_mode, uint8_t dest_shorthand)
|
||||
{
|
||||
APICCommonState *s = APIC(dev);
|
||||
uint32_t deliver_bitmask[MAX_APIC_WORDS];
|
||||
int dest_shorthand = (s->icr[0] >> 18) & 3;
|
||||
APICCommonState *apic_iter;
|
||||
uint32_t deliver_bitmask_size = max_apic_words * sizeof(uint32_t);
|
||||
uint32_t *deliver_bitmask = g_malloc(deliver_bitmask_size);
|
||||
uint32_t current_apic_id;
|
||||
|
||||
if (is_x2apic_mode(dev)) {
|
||||
current_apic_id = s->initial_apic_id;
|
||||
} else {
|
||||
current_apic_id = s->id;
|
||||
}
|
||||
|
||||
switch (dest_shorthand) {
|
||||
case 0:
|
||||
apic_get_delivery_bitmask(deliver_bitmask, dest, dest_mode);
|
||||
break;
|
||||
case 1:
|
||||
memset(deliver_bitmask, 0x00, sizeof(deliver_bitmask));
|
||||
apic_set_bit(deliver_bitmask, s->id);
|
||||
memset(deliver_bitmask, 0x00, deliver_bitmask_size);
|
||||
apic_set_bit(deliver_bitmask, current_apic_id);
|
||||
break;
|
||||
case 2:
|
||||
memset(deliver_bitmask, 0xff, sizeof(deliver_bitmask));
|
||||
memset(deliver_bitmask, 0xff, deliver_bitmask_size);
|
||||
break;
|
||||
case 3:
|
||||
memset(deliver_bitmask, 0xff, sizeof(deliver_bitmask));
|
||||
apic_reset_bit(deliver_bitmask, s->id);
|
||||
memset(deliver_bitmask, 0xff, deliver_bitmask_size);
|
||||
apic_reset_bit(deliver_bitmask, current_apic_id);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -555,6 +708,7 @@ static void apic_deliver(DeviceState *dev, uint8_t dest, uint8_t dest_mode,
|
|||
}
|
||||
|
||||
apic_bus_deliver(deliver_bitmask, delivery_mode, vector_num, trigger_mode);
|
||||
g_free(deliver_bitmask);
|
||||
}
|
||||
|
||||
static bool apic_check_pic(APICCommonState *s)
|
||||
|
@ -636,27 +790,26 @@ static void apic_timer(void *opaque)
|
|||
apic_timer_update(s, s->next_time);
|
||||
}
|
||||
|
||||
static uint64_t apic_mem_read(void *opaque, hwaddr addr, unsigned size)
|
||||
static int apic_register_read(int index, uint64_t *value)
|
||||
{
|
||||
DeviceState *dev;
|
||||
APICCommonState *s;
|
||||
uint32_t val;
|
||||
int index;
|
||||
|
||||
if (size < 4) {
|
||||
return 0;
|
||||
}
|
||||
int ret = 0;
|
||||
|
||||
dev = cpu_get_current_apic();
|
||||
if (!dev) {
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
s = APIC(dev);
|
||||
|
||||
index = (addr >> 4) & 0xff;
|
||||
switch(index) {
|
||||
case 0x02: /* id */
|
||||
val = s->id << 24;
|
||||
if (is_x2apic_mode(dev)) {
|
||||
val = s->initial_apic_id;
|
||||
} else {
|
||||
val = s->id << 24;
|
||||
}
|
||||
break;
|
||||
case 0x03: /* version */
|
||||
val = s->version | ((APIC_LVT_NB - 1) << 16);
|
||||
|
@ -679,10 +832,19 @@ static uint64_t apic_mem_read(void *opaque, hwaddr addr, unsigned size)
|
|||
val = 0;
|
||||
break;
|
||||
case 0x0d:
|
||||
val = s->log_dest << 24;
|
||||
if (is_x2apic_mode(dev)) {
|
||||
val = s->extended_log_dest;
|
||||
} else {
|
||||
val = s->log_dest << 24;
|
||||
}
|
||||
break;
|
||||
case 0x0e:
|
||||
val = (s->dest_mode << 28) | 0xfffffff;
|
||||
if (is_x2apic_mode(dev)) {
|
||||
val = 0;
|
||||
ret = -1;
|
||||
} else {
|
||||
val = (s->dest_mode << 28) | 0xfffffff;
|
||||
}
|
||||
break;
|
||||
case 0x0f:
|
||||
val = s->spurious_vec;
|
||||
|
@ -718,17 +880,56 @@ static uint64_t apic_mem_read(void *opaque, hwaddr addr, unsigned size)
|
|||
default:
|
||||
s->esr |= APIC_ESR_ILLEGAL_ADDRESS;
|
||||
val = 0;
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
trace_apic_mem_readl(addr, val);
|
||||
|
||||
trace_apic_register_read(index, val);
|
||||
*value = val;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint64_t apic_mem_read(void *opaque, hwaddr addr, unsigned size)
|
||||
{
|
||||
uint64_t val;
|
||||
int index;
|
||||
|
||||
if (size < 4) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
index = (addr >> 4) & 0xff;
|
||||
apic_register_read(index, &val);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
int apic_msr_read(int index, uint64_t *val)
|
||||
{
|
||||
DeviceState *dev;
|
||||
|
||||
dev = cpu_get_current_apic();
|
||||
if (!dev) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!is_x2apic_mode(dev)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return apic_register_read(index, val);
|
||||
}
|
||||
|
||||
static void apic_send_msi(MSIMessage *msi)
|
||||
{
|
||||
uint64_t addr = msi->address;
|
||||
uint32_t data = msi->data;
|
||||
uint8_t dest = (addr & MSI_ADDR_DEST_ID_MASK) >> MSI_ADDR_DEST_ID_SHIFT;
|
||||
uint32_t dest = (addr & MSI_ADDR_DEST_ID_MASK) >> MSI_ADDR_DEST_ID_SHIFT;
|
||||
/*
|
||||
* The higher 3 bytes of destination id is stored in higher word of
|
||||
* msi address. See x86_iommu_irq_to_msi_message()
|
||||
*/
|
||||
dest = dest | (addr >> 32);
|
||||
uint8_t vector = (data & MSI_DATA_VECTOR_MASK) >> MSI_DATA_VECTOR_SHIFT;
|
||||
uint8_t dest_mode = (addr >> MSI_ADDR_DEST_MODE_SHIFT) & 0x1;
|
||||
uint8_t trigger_mode = (data >> MSI_DATA_TRIGGER_SHIFT) & 0x1;
|
||||
|
@ -737,38 +938,25 @@ static void apic_send_msi(MSIMessage *msi)
|
|||
apic_deliver_irq(dest, dest_mode, delivery, vector, trigger_mode);
|
||||
}
|
||||
|
||||
static void apic_mem_write(void *opaque, hwaddr addr, uint64_t val,
|
||||
unsigned size)
|
||||
static int apic_register_write(int index, uint64_t val)
|
||||
{
|
||||
DeviceState *dev;
|
||||
APICCommonState *s;
|
||||
int index = (addr >> 4) & 0xff;
|
||||
|
||||
if (size < 4) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (addr > 0xfff || !index) {
|
||||
/* MSI and MMIO APIC are at the same memory location,
|
||||
* but actually not on the global bus: MSI is on PCI bus
|
||||
* APIC is connected directly to the CPU.
|
||||
* Mapping them on the global bus happens to work because
|
||||
* MSI registers are reserved in APIC MMIO and vice versa. */
|
||||
MSIMessage msi = { .address = addr, .data = val };
|
||||
apic_send_msi(&msi);
|
||||
return;
|
||||
}
|
||||
|
||||
dev = cpu_get_current_apic();
|
||||
if (!dev) {
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
s = APIC(dev);
|
||||
|
||||
trace_apic_mem_writel(addr, val);
|
||||
trace_apic_register_write(index, val);
|
||||
|
||||
switch(index) {
|
||||
case 0x02:
|
||||
if (is_x2apic_mode(dev)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
s->id = (val >> 24);
|
||||
break;
|
||||
case 0x03:
|
||||
|
@ -788,9 +976,17 @@ static void apic_mem_write(void *opaque, hwaddr addr, uint64_t val,
|
|||
apic_eoi(s);
|
||||
break;
|
||||
case 0x0d:
|
||||
if (is_x2apic_mode(dev)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
s->log_dest = val >> 24;
|
||||
break;
|
||||
case 0x0e:
|
||||
if (is_x2apic_mode(dev)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
s->dest_mode = val >> 28;
|
||||
break;
|
||||
case 0x0f:
|
||||
|
@ -802,13 +998,27 @@ static void apic_mem_write(void *opaque, hwaddr addr, uint64_t val,
|
|||
case 0x20 ... 0x27:
|
||||
case 0x28:
|
||||
break;
|
||||
case 0x30:
|
||||
case 0x30: {
|
||||
uint32_t dest;
|
||||
|
||||
s->icr[0] = val;
|
||||
apic_deliver(dev, (s->icr[1] >> 24) & 0xff, (s->icr[0] >> 11) & 1,
|
||||
if (is_x2apic_mode(dev)) {
|
||||
s->icr[1] = val >> 32;
|
||||
dest = s->icr[1];
|
||||
} else {
|
||||
dest = (s->icr[1] >> 24) & 0xff;
|
||||
}
|
||||
|
||||
apic_deliver(dev, dest, (s->icr[0] >> 11) & 1,
|
||||
(s->icr[0] >> 8) & 7, (s->icr[0] & 0xff),
|
||||
(s->icr[0] >> 15) & 1);
|
||||
(s->icr[0] >> 15) & 1, (s->icr[0] >> 18) & 3);
|
||||
break;
|
||||
}
|
||||
case 0x31:
|
||||
if (is_x2apic_mode(dev)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
s->icr[1] = val;
|
||||
break;
|
||||
case 0x32 ... 0x37:
|
||||
|
@ -837,10 +1047,70 @@ static void apic_mem_write(void *opaque, hwaddr addr, uint64_t val,
|
|||
s->count_shift = (v + 1) & 7;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
s->esr |= APIC_ESR_ILLEGAL_ADDRESS;
|
||||
case 0x3f: {
|
||||
int vector = val & 0xff;
|
||||
|
||||
if (!is_x2apic_mode(dev)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Self IPI is identical to IPI with
|
||||
* - Destination shorthand: 1 (Self)
|
||||
* - Trigger mode: 0 (Edge)
|
||||
* - Delivery mode: 0 (Fixed)
|
||||
*/
|
||||
apic_deliver(dev, 0, 0, APIC_DM_FIXED, vector, 0, 1);
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
s->esr |= APIC_ESR_ILLEGAL_ADDRESS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void apic_mem_write(void *opaque, hwaddr addr, uint64_t val,
|
||||
unsigned size)
|
||||
{
|
||||
int index = (addr >> 4) & 0xff;
|
||||
|
||||
if (size < 4) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (addr > 0xfff || !index) {
|
||||
/*
|
||||
* MSI and MMIO APIC are at the same memory location,
|
||||
* but actually not on the global bus: MSI is on PCI bus
|
||||
* APIC is connected directly to the CPU.
|
||||
* Mapping them on the global bus happens to work because
|
||||
* MSI registers are reserved in APIC MMIO and vice versa.
|
||||
*/
|
||||
MSIMessage msi = { .address = addr, .data = val };
|
||||
apic_send_msi(&msi);
|
||||
return;
|
||||
}
|
||||
|
||||
apic_register_write(index, val);
|
||||
}
|
||||
|
||||
int apic_msr_write(int index, uint64_t val)
|
||||
{
|
||||
DeviceState *dev;
|
||||
|
||||
dev = cpu_get_current_apic();
|
||||
if (!dev) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!is_x2apic_mode(dev)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return apic_register_write(index, val);
|
||||
}
|
||||
|
||||
static void apic_pre_save(APICCommonState *s)
|
||||
|
@ -871,12 +1141,6 @@ static void apic_realize(DeviceState *dev, Error **errp)
|
|||
{
|
||||
APICCommonState *s = APIC(dev);
|
||||
|
||||
if (s->id >= MAX_APICS) {
|
||||
error_setg(errp, "%s initialization failed. APIC ID %d is invalid",
|
||||
object_get_typename(OBJECT(dev)), s->id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (kvm_enabled()) {
|
||||
warn_report("Userspace local APIC is deprecated for KVM.");
|
||||
warn_report("Do not use kernel-irqchip except for the -M isapc machine type.");
|
||||
|
@ -893,7 +1157,16 @@ static void apic_realize(DeviceState *dev, Error **errp)
|
|||
s->io_memory.disable_reentrancy_guard = true;
|
||||
|
||||
s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, apic_timer, s);
|
||||
local_apics[s->id] = s;
|
||||
|
||||
/*
|
||||
* The --machine none does not call apic_set_max_apic_id before creating
|
||||
* apic, so we need to call it here and set it to 1 which is the max cpus
|
||||
* in machine none.
|
||||
*/
|
||||
if (!local_apics) {
|
||||
apic_set_max_apic_id(1);
|
||||
}
|
||||
local_apics[s->initial_apic_id] = s;
|
||||
|
||||
msi_nonbroken = true;
|
||||
}
|
||||
|
@ -903,7 +1176,7 @@ static void apic_unrealize(DeviceState *dev)
|
|||
APICCommonState *s = APIC(dev);
|
||||
|
||||
timer_free(s->timer);
|
||||
local_apics[s->id] = NULL;
|
||||
local_apics[s->initial_apic_id] = NULL;
|
||||
}
|
||||
|
||||
static void apic_class_init(ObjectClass *klass, void *data)
|
||||
|
|
|
@ -35,20 +35,19 @@
|
|||
|
||||
bool apic_report_tpr_access;
|
||||
|
||||
void cpu_set_apic_base(DeviceState *dev, uint64_t val)
|
||||
int cpu_set_apic_base(DeviceState *dev, uint64_t val)
|
||||
{
|
||||
trace_cpu_set_apic_base(val);
|
||||
|
||||
if (dev) {
|
||||
APICCommonState *s = APIC_COMMON(dev);
|
||||
APICCommonClass *info = APIC_COMMON_GET_CLASS(s);
|
||||
/* switching to x2APIC, reset possibly modified xAPIC ID */
|
||||
if (!(s->apicbase & MSR_IA32_APICBASE_EXTD) &&
|
||||
(val & MSR_IA32_APICBASE_EXTD)) {
|
||||
s->id = s->initial_apic_id;
|
||||
}
|
||||
info->set_base(s, val);
|
||||
/* Reset possibly modified xAPIC ID */
|
||||
s->id = s->initial_apic_id;
|
||||
return info->set_base(s, val);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t cpu_get_apic_base(DeviceState *dev)
|
||||
|
@ -63,6 +62,19 @@ uint64_t cpu_get_apic_base(DeviceState *dev)
|
|||
}
|
||||
}
|
||||
|
||||
bool cpu_is_apic_enabled(DeviceState *dev)
|
||||
{
|
||||
APICCommonState *s;
|
||||
|
||||
if (!dev) {
|
||||
return false;
|
||||
}
|
||||
|
||||
s = APIC_COMMON(dev);
|
||||
|
||||
return s->apicbase & MSR_IA32_APICBASE_ENABLE;
|
||||
}
|
||||
|
||||
void cpu_set_apic_tpr(DeviceState *dev, uint8_t val)
|
||||
{
|
||||
APICCommonState *s;
|
||||
|
@ -287,6 +299,10 @@ static void apic_common_realize(DeviceState *dev, Error **errp)
|
|||
}
|
||||
vmstate_register_with_alias_id(NULL, instance_id, &vmstate_apic_common,
|
||||
s, -1, 0, NULL);
|
||||
|
||||
/* APIC LDR in x2APIC mode */
|
||||
s->extended_log_dest = ((s->initial_apic_id >> 4) << 16) |
|
||||
(1 << (s->initial_apic_id & 0xf));
|
||||
}
|
||||
|
||||
static void apic_common_unrealize(DeviceState *dev)
|
||||
|
@ -427,6 +443,11 @@ static void apic_common_set_id(Object *obj, Visitor *v, const char *name,
|
|||
return;
|
||||
}
|
||||
|
||||
if (value >= 255 && !cpu_has_x2apic_feature(&s->cpu->env)) {
|
||||
error_setg(errp, "APIC ID %d requires x2APIC feature in CPU", value);
|
||||
return;
|
||||
}
|
||||
|
||||
s->initial_apic_id = value;
|
||||
s->id = (uint8_t)value;
|
||||
}
|
||||
|
|
|
@ -14,8 +14,8 @@ cpu_get_apic_base(uint64_t val) "0x%016"PRIx64
|
|||
# apic.c
|
||||
apic_local_deliver(int vector, uint32_t lvt) "vector %d delivery mode %d"
|
||||
apic_deliver_irq(uint8_t dest, uint8_t dest_mode, uint8_t delivery_mode, uint8_t vector_num, uint8_t trigger_mode) "dest %d dest_mode %d delivery_mode %d vector %d trigger_mode %d"
|
||||
apic_mem_readl(uint64_t addr, uint32_t val) "0x%"PRIx64" = 0x%08x"
|
||||
apic_mem_writel(uint64_t addr, uint32_t val) "0x%"PRIx64" = 0x%08x"
|
||||
apic_register_read(uint8_t reg, uint64_t val) "register 0x%02x = 0x%"PRIx64
|
||||
apic_register_write(uint8_t reg, uint64_t val) "register 0x%02x = 0x%"PRIx64
|
||||
|
||||
# ioapic.c
|
||||
ioapic_set_remote_irr(int n) "set remote irr for pin %d"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue