mirror of
https://github.com/qemu/qemu.git
synced 2026-07-08 17:46:10 +00:00
Merge tag 'for-upstream' of https://gitlab.com/bonzini/qemu into staging
* target/i386/mshv: CPU model support * target/i386/mshv: first part of migration support * target/i386/mshv: faster register access for MMIO exits * target/i386/tdx: add support for AMX alias bits in CPUID and AVX10 * Deprecate memory-encryption in favor of confidential-guest-support # -----BEGIN PGP SIGNATURE----- # # iQFIBAABCgAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmo9sDwUHHBib256aW5p # QHJlZGhhdC5jb20ACgkQv/vSX3jHroOHBwf8Dx4gkbzOFxmCNX3EaW+ROYwlyAC7 # ADo9LFloDHXforRYTm4mBXNUVNF1/KFA6Tf92rzBlUZgp9KuMy/KhWZ1GbNsE+9b # k5/1RF9/IxRHy6GL69apdHEKY2OYzXl76or2HF3wMd6Mu77qD8Onthko81VaLWox # 5ZOBz6NaSnykzs9RimkVLtD9HswtFile2NWTPSliUV874lEJioNi9RcdhnQvJCnX # WqGWViC0THucIGCm+NVhSEmvRnAFbPgUBPvQuy7skLu+R7Ryy7GAWmE/gFlSrYy2 # 4c4zt4SB0tFYJlT9db5ZdaUSgCs52CFawQ9uTSEjNSmEEuQFzXGo6BeY4w== # =4V0w # -----END PGP SIGNATURE----- # gpg: Signature made Thu 25 Jun 2026 18:48:28 EDT # gpg: using RSA key F13338574B662389866C7682BFFBD25F78C7AE83 # gpg: issuer "pbonzini@redhat.com" # gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" [full] # gpg: aka "Paolo Bonzini <pbonzini@redhat.com>" [full] # Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4 E2F7 7E15 100C CD36 69B1 # Subkey fingerprint: F133 3857 4B66 2389 866C 7682 BFFB D25F 78C7 AE83 * tag 'for-upstream' of https://gitlab.com/bonzini/qemu: (45 commits) i386/tdx: Add CPUID_24_0_EBX_AVX10_VL_MASK as supported i386/tdx: Make AMX alias bits supported i386/tdx: Use .has_gpa field to check if the gpa is valid machine: Deprecate memory-encryption qemu-options: Add description of tdx-guest object qemu-options: Add confidential-guest-support to machine options qemu-options: Change memory-encryption to confidential-guest-support in the example i386/sev: Remove the example that references memory-encryption target/i386/mshv: use the register page to set registers target/i386/mshv: use the register page to get registers target/i386/mshv: hv_vp_register_page setup for the vcpu include/hw/hyperv: add hv_vp_register_page struct definition accel: remove unnecessary #ifdefs target/i386/mshv: migrate CET/SS MSRs target/i386/mshv: migrate MTRR MSRs target/i386/mshv: migrate MSRs target/i386/mshv: reconstruct hflags after load target/i386/mshv: migrate XSAVE state target/i386/mshv: migrate pending ints/excs target/i386/mshv: move msr code to arch ... Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
@@ -10,19 +10,18 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/pci/msi.h"
|
||||
|
||||
#include "system/kvm.h"
|
||||
#include "system/mshv.h"
|
||||
#include "system/accel-irq.h"
|
||||
|
||||
int accel_irqchip_add_msi_route(KVMRouteChange *c, int vector, PCIDevice *dev)
|
||||
int accel_irqchip_add_msi_route(AccelRouteChange *c, int vector, PCIDevice *dev)
|
||||
{
|
||||
#ifdef CONFIG_MSHV_IS_POSSIBLE
|
||||
if (mshv_msi_via_irqfd_enabled()) {
|
||||
return mshv_irqchip_add_msi_route(vector, dev);
|
||||
return mshv_irqchip_add_msi_route(c, vector, dev);
|
||||
}
|
||||
#endif
|
||||
if (kvm_enabled()) {
|
||||
return kvm_irqchip_add_msi_route(c, vector, dev);
|
||||
}
|
||||
@@ -31,36 +30,28 @@ int accel_irqchip_add_msi_route(KVMRouteChange *c, int vector, PCIDevice *dev)
|
||||
|
||||
int accel_irqchip_update_msi_route(int vector, MSIMessage msg, PCIDevice *dev)
|
||||
{
|
||||
#ifdef CONFIG_MSHV_IS_POSSIBLE
|
||||
if (mshv_msi_via_irqfd_enabled()) {
|
||||
return mshv_irqchip_update_msi_route(vector, msg, dev);
|
||||
}
|
||||
#endif
|
||||
if (kvm_enabled()) {
|
||||
return kvm_irqchip_update_msi_route(kvm_state, vector, msg, dev);
|
||||
}
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
void accel_irqchip_commit_route_changes(KVMRouteChange *c)
|
||||
void accel_irqchip_commit_route_changes(AccelRouteChange *c)
|
||||
{
|
||||
#ifdef CONFIG_MSHV_IS_POSSIBLE
|
||||
if (mshv_msi_via_irqfd_enabled()) {
|
||||
mshv_irqchip_commit_routes();
|
||||
}
|
||||
#endif
|
||||
if (kvm_enabled()) {
|
||||
kvm_irqchip_commit_route_changes(c);
|
||||
if (c->changes) {
|
||||
accel_irqchip_commit_routes();
|
||||
c->changes = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void accel_irqchip_commit_routes(void)
|
||||
{
|
||||
#ifdef CONFIG_MSHV_IS_POSSIBLE
|
||||
if (mshv_msi_via_irqfd_enabled()) {
|
||||
mshv_irqchip_commit_routes();
|
||||
mshv_irqchip_commit_routes(mshv_state);
|
||||
}
|
||||
#endif
|
||||
if (kvm_enabled()) {
|
||||
kvm_irqchip_commit_routes(kvm_state);
|
||||
}
|
||||
@@ -68,11 +59,9 @@ void accel_irqchip_commit_routes(void)
|
||||
|
||||
void accel_irqchip_release_virq(int virq)
|
||||
{
|
||||
#ifdef CONFIG_MSHV_IS_POSSIBLE
|
||||
if (mshv_msi_via_irqfd_enabled()) {
|
||||
mshv_irqchip_release_virq(virq);
|
||||
mshv_irqchip_release_virq(mshv_state, virq);
|
||||
}
|
||||
#endif
|
||||
if (kvm_enabled()) {
|
||||
kvm_irqchip_release_virq(kvm_state, virq);
|
||||
}
|
||||
@@ -81,11 +70,9 @@ void accel_irqchip_release_virq(int virq)
|
||||
int accel_irqchip_add_irqfd_notifier_gsi(EventNotifier *n, EventNotifier *rn,
|
||||
int virq)
|
||||
{
|
||||
#ifdef CONFIG_MSHV_IS_POSSIBLE
|
||||
if (mshv_msi_via_irqfd_enabled()) {
|
||||
return mshv_irqchip_add_irqfd_notifier_gsi(n, rn, virq);
|
||||
}
|
||||
#endif
|
||||
if (kvm_enabled()) {
|
||||
return kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, n, rn, virq);
|
||||
}
|
||||
@@ -94,13 +81,29 @@ int accel_irqchip_add_irqfd_notifier_gsi(EventNotifier *n, EventNotifier *rn,
|
||||
|
||||
int accel_irqchip_remove_irqfd_notifier_gsi(EventNotifier *n, int virq)
|
||||
{
|
||||
#ifdef CONFIG_MSHV_IS_POSSIBLE
|
||||
if (mshv_msi_via_irqfd_enabled()) {
|
||||
return mshv_irqchip_remove_irqfd_notifier_gsi(n, virq);
|
||||
}
|
||||
#endif
|
||||
if (kvm_enabled()) {
|
||||
return kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, n, virq);
|
||||
}
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
inline AccelRouteChange accel_irqchip_begin_route_changes(void)
|
||||
{
|
||||
if (mshv_msi_via_irqfd_enabled()) {
|
||||
return (AccelRouteChange) {
|
||||
.accel = ACCEL(mshv_state),
|
||||
.changes = 0,
|
||||
};
|
||||
}
|
||||
if (kvm_enabled()) {
|
||||
return (AccelRouteChange) {
|
||||
.accel = ACCEL(kvm_state),
|
||||
.changes = 0,
|
||||
};
|
||||
}
|
||||
error_report("can't initiate route change, no accel irqchip available");
|
||||
abort();
|
||||
}
|
||||
|
||||
@@ -2359,11 +2359,11 @@ int kvm_irqchip_send_msi(KVMState *s, MSIMessage msg)
|
||||
return kvm_vm_ioctl(s, KVM_SIGNAL_MSI, &msi);
|
||||
}
|
||||
|
||||
int kvm_irqchip_add_msi_route(KVMRouteChange *c, int vector, PCIDevice *dev)
|
||||
int kvm_irqchip_add_msi_route(AccelRouteChange *c, int vector, PCIDevice *dev)
|
||||
{
|
||||
struct kvm_irq_routing_entry kroute = {};
|
||||
int virq;
|
||||
KVMState *s = c->s;
|
||||
KVMState *s = KVM_STATE(c->accel);
|
||||
MSIMessage msg = {0, 0};
|
||||
|
||||
if (pci_available && dev) {
|
||||
@@ -2506,7 +2506,7 @@ int kvm_irqchip_send_msi(KVMState *s, MSIMessage msg)
|
||||
abort();
|
||||
}
|
||||
|
||||
int kvm_irqchip_add_msi_route(KVMRouteChange *c, int vector, PCIDevice *dev)
|
||||
int kvm_irqchip_add_msi_route(AccelRouteChange *c, int vector, PCIDevice *dev)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
362
accel/mshv/irq.c
362
accel/mshv/irq.c
@@ -25,177 +25,6 @@
|
||||
#define MSHV_IRQFD_RESAMPLE_FLAG (1 << MSHV_IRQFD_BIT_RESAMPLE)
|
||||
#define MSHV_IRQFD_BIT_DEASSIGN_FLAG (1 << MSHV_IRQFD_BIT_DEASSIGN)
|
||||
|
||||
static MshvMsiControl *msi_control;
|
||||
static QemuMutex msi_control_mutex;
|
||||
|
||||
void mshv_init_msicontrol(void)
|
||||
{
|
||||
qemu_mutex_init(&msi_control_mutex);
|
||||
msi_control = g_new0(MshvMsiControl, 1);
|
||||
msi_control->gsi_routes = g_hash_table_new(g_direct_hash, g_direct_equal);
|
||||
msi_control->updated = false;
|
||||
}
|
||||
|
||||
static int set_msi_routing(uint32_t gsi, uint64_t addr, uint32_t data)
|
||||
{
|
||||
struct mshv_user_irq_entry *entry;
|
||||
uint32_t high_addr = addr >> 32;
|
||||
uint32_t low_addr = addr & 0xFFFFFFFF;
|
||||
GHashTable *gsi_routes;
|
||||
|
||||
trace_mshv_set_msi_routing(gsi, addr, data);
|
||||
|
||||
if (gsi >= MSHV_MAX_MSI_ROUTES) {
|
||||
error_report("gsi >= MSHV_MAX_MSI_ROUTES");
|
||||
return -1;
|
||||
}
|
||||
|
||||
assert(msi_control);
|
||||
|
||||
WITH_QEMU_LOCK_GUARD(&msi_control_mutex) {
|
||||
gsi_routes = msi_control->gsi_routes;
|
||||
entry = g_hash_table_lookup(gsi_routes, GINT_TO_POINTER(gsi));
|
||||
|
||||
if (entry
|
||||
&& entry->address_hi == high_addr
|
||||
&& entry->address_lo == low_addr
|
||||
&& entry->data == data)
|
||||
{
|
||||
/* nothing to update */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* free old entry */
|
||||
g_free(entry);
|
||||
|
||||
/* create new entry */
|
||||
entry = g_new0(struct mshv_user_irq_entry, 1);
|
||||
entry->gsi = gsi;
|
||||
entry->address_hi = high_addr;
|
||||
entry->address_lo = low_addr;
|
||||
entry->data = data;
|
||||
|
||||
g_hash_table_insert(gsi_routes, GINT_TO_POINTER(gsi), entry);
|
||||
msi_control->updated = true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_msi_routing(uint64_t addr, uint32_t data)
|
||||
{
|
||||
struct mshv_user_irq_entry *route_entry;
|
||||
uint32_t high_addr = addr >> 32;
|
||||
uint32_t low_addr = addr & 0xFFFFFFFF;
|
||||
int gsi;
|
||||
GHashTable *gsi_routes;
|
||||
|
||||
trace_mshv_add_msi_routing(addr, data);
|
||||
|
||||
assert(msi_control);
|
||||
|
||||
WITH_QEMU_LOCK_GUARD(&msi_control_mutex) {
|
||||
/* find an empty slot */
|
||||
gsi = 0;
|
||||
gsi_routes = msi_control->gsi_routes;
|
||||
while (gsi < MSHV_MAX_MSI_ROUTES) {
|
||||
route_entry = g_hash_table_lookup(gsi_routes, GINT_TO_POINTER(gsi));
|
||||
if (!route_entry) {
|
||||
break;
|
||||
}
|
||||
gsi++;
|
||||
}
|
||||
if (gsi >= MSHV_MAX_MSI_ROUTES) {
|
||||
error_report("No empty gsi slot available");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* create new entry */
|
||||
route_entry = g_new0(struct mshv_user_irq_entry, 1);
|
||||
route_entry->gsi = gsi;
|
||||
route_entry->address_hi = high_addr;
|
||||
route_entry->address_lo = low_addr;
|
||||
route_entry->data = data;
|
||||
|
||||
g_hash_table_insert(gsi_routes, GINT_TO_POINTER(gsi), route_entry);
|
||||
msi_control->updated = true;
|
||||
}
|
||||
|
||||
return gsi;
|
||||
}
|
||||
|
||||
static int commit_msi_routing_table(int vm_fd)
|
||||
{
|
||||
guint len;
|
||||
int i, ret;
|
||||
size_t table_size;
|
||||
struct mshv_user_irq_table *table;
|
||||
GHashTableIter iter;
|
||||
gpointer key, value;
|
||||
|
||||
assert(msi_control);
|
||||
|
||||
WITH_QEMU_LOCK_GUARD(&msi_control_mutex) {
|
||||
if (!msi_control->updated) {
|
||||
/* nothing to update */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Calculate the size of the table */
|
||||
len = g_hash_table_size(msi_control->gsi_routes);
|
||||
table_size = sizeof(struct mshv_user_irq_table)
|
||||
+ len * sizeof(struct mshv_user_irq_entry);
|
||||
table = g_malloc0(table_size);
|
||||
|
||||
g_hash_table_iter_init(&iter, msi_control->gsi_routes);
|
||||
i = 0;
|
||||
while (g_hash_table_iter_next(&iter, &key, &value)) {
|
||||
struct mshv_user_irq_entry *entry = value;
|
||||
table->entries[i] = *entry;
|
||||
i++;
|
||||
}
|
||||
table->nr = i;
|
||||
|
||||
trace_mshv_commit_msi_routing_table(vm_fd, len);
|
||||
|
||||
ret = ioctl(vm_fd, MSHV_SET_MSI_ROUTING, table);
|
||||
g_free(table);
|
||||
if (ret < 0) {
|
||||
error_report("Failed to commit msi routing table");
|
||||
return -1;
|
||||
}
|
||||
msi_control->updated = false;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int remove_msi_routing(uint32_t gsi)
|
||||
{
|
||||
struct mshv_user_irq_entry *route_entry;
|
||||
GHashTable *gsi_routes;
|
||||
|
||||
trace_mshv_remove_msi_routing(gsi);
|
||||
|
||||
if (gsi >= MSHV_MAX_MSI_ROUTES) {
|
||||
error_report("Invalid GSI: %u", gsi);
|
||||
return -1;
|
||||
}
|
||||
|
||||
assert(msi_control);
|
||||
|
||||
WITH_QEMU_LOCK_GUARD(&msi_control_mutex) {
|
||||
gsi_routes = msi_control->gsi_routes;
|
||||
route_entry = g_hash_table_lookup(gsi_routes, GINT_TO_POINTER(gsi));
|
||||
if (route_entry) {
|
||||
g_hash_table_remove(gsi_routes, GINT_TO_POINTER(gsi));
|
||||
g_free(route_entry);
|
||||
msi_control->updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Pass an eventfd which is to be used for injecting interrupts from userland */
|
||||
static int irqfd(int vm_fd, int fd, int resample_fd, uint32_t gsi,
|
||||
uint32_t flags)
|
||||
@@ -278,35 +107,155 @@ static int irqchip_update_irqfd_notifier_gsi(const EventNotifier *event,
|
||||
return register_irqfd(vm_fd, fd, virq);
|
||||
}
|
||||
|
||||
|
||||
int mshv_irqchip_add_msi_route(int vector, PCIDevice *dev)
|
||||
static int irqchip_allocate_gsi(MshvState *s, int *gsi)
|
||||
{
|
||||
MSIMessage msg = { 0, 0 };
|
||||
int virq = 0;
|
||||
int next_gsi;
|
||||
|
||||
if (pci_available && dev) {
|
||||
msg = pci_get_msi_message(dev, vector);
|
||||
virq = add_msi_routing(msg.address, le32_to_cpu(msg.data));
|
||||
/* Return the lowest unused GSI in the bitmap */
|
||||
next_gsi = find_first_zero_bit(s->used_gsi_bitmap, s->gsi_count);
|
||||
if (next_gsi >= s->gsi_count) {
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
return virq;
|
||||
*gsi = next_gsi;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mshv_irqchip_release_virq(int virq)
|
||||
static void irqchip_release_gsi(MshvState *s, int gsi)
|
||||
{
|
||||
remove_msi_routing(virq);
|
||||
clear_bit(gsi, s->used_gsi_bitmap);
|
||||
}
|
||||
|
||||
static void add_routing_entry(MshvState *s, struct mshv_user_irq_entry *entry)
|
||||
{
|
||||
struct mshv_user_irq_entry *new;
|
||||
int n, size;
|
||||
|
||||
if (s->irq_routes->nr == s->nr_allocated_irq_routes) {
|
||||
n = s->nr_allocated_irq_routes * 2;
|
||||
if (n < MSHV_MIN_ALLOCATED_MSI_ROUTES) {
|
||||
n = MSHV_MIN_ALLOCATED_MSI_ROUTES;
|
||||
}
|
||||
size = sizeof(struct mshv_user_irq_table);
|
||||
size += n * sizeof(*new);
|
||||
s->irq_routes = g_realloc(s->irq_routes, size);
|
||||
s->nr_allocated_irq_routes = n;
|
||||
}
|
||||
|
||||
n = s->irq_routes->nr;
|
||||
s->irq_routes->nr++;
|
||||
new = &s->irq_routes->entries[n];
|
||||
|
||||
*new = *entry;
|
||||
|
||||
set_bit(entry->gsi, s->used_gsi_bitmap);
|
||||
|
||||
trace_mshv_add_msi_routing(entry->address_lo | entry->address_hi,
|
||||
entry->data);
|
||||
}
|
||||
|
||||
int mshv_irqchip_add_msi_route(AccelRouteChange *c, int vector, PCIDevice *dev)
|
||||
{
|
||||
struct mshv_user_irq_entry entry = { 0 };
|
||||
MSIMessage msg = { 0 };
|
||||
uint32_t data, high_addr, low_addr;
|
||||
int gsi, ret;
|
||||
MshvState *s = MSHV_STATE(c->accel);
|
||||
|
||||
if (!pci_available || !dev) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
msg = pci_get_msi_message(dev, vector);
|
||||
|
||||
ret = irqchip_allocate_gsi(mshv_state, &gsi);
|
||||
if (ret < 0) {
|
||||
error_report("Could not allocate GSI for MSI route");
|
||||
return -1;
|
||||
}
|
||||
high_addr = msg.address >> 32;
|
||||
low_addr = msg.address & 0xFFFFFFFF;
|
||||
data = le32_to_cpu(msg.data);
|
||||
|
||||
entry.gsi = gsi;
|
||||
entry.address_hi = high_addr;
|
||||
entry.address_lo = low_addr;
|
||||
entry.data = data;
|
||||
|
||||
if (s->irq_routes->nr < s->gsi_count) {
|
||||
add_routing_entry(s, &entry);
|
||||
c->changes++;
|
||||
} else {
|
||||
irqchip_release_gsi(s, gsi);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
return gsi;
|
||||
}
|
||||
|
||||
void mshv_irqchip_release_virq(MshvState *s, int virq)
|
||||
{
|
||||
struct mshv_user_irq_entry *e;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s->irq_routes->nr; i++) {
|
||||
e = &s->irq_routes->entries[i];
|
||||
if (e->gsi == virq) {
|
||||
s->irq_routes->nr--;
|
||||
*e = s->irq_routes->entries[s->irq_routes->nr];
|
||||
}
|
||||
}
|
||||
irqchip_release_gsi(s, virq);
|
||||
|
||||
trace_mshv_remove_msi_routing(virq);
|
||||
}
|
||||
|
||||
static int update_routing_entry(MshvState *s,
|
||||
struct mshv_user_irq_entry *new_entry)
|
||||
{
|
||||
struct mshv_user_irq_entry *entry;
|
||||
int n;
|
||||
|
||||
for (n = 0; n < s->irq_routes->nr; n++) {
|
||||
entry = &s->irq_routes->entries[n];
|
||||
if (entry->gsi != new_entry->gsi) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!memcmp(entry, new_entry, sizeof *entry)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
*entry = *new_entry;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ESRCH;
|
||||
}
|
||||
|
||||
int mshv_irqchip_update_msi_route(int virq, MSIMessage msg, PCIDevice *dev)
|
||||
{
|
||||
uint32_t addr_hi = msg.address >> 32;
|
||||
uint32_t addr_lo = msg.address & 0xFFFFFFFF;
|
||||
uint32_t data = le32_to_cpu(msg.data);
|
||||
struct mshv_user_irq_entry entry = {
|
||||
.gsi = virq,
|
||||
.address_hi = addr_hi,
|
||||
.address_lo = addr_lo,
|
||||
.data = data,
|
||||
};
|
||||
int ret;
|
||||
|
||||
ret = set_msi_routing(virq, msg.address, le32_to_cpu(msg.data));
|
||||
ret = update_routing_entry(mshv_state, &entry);
|
||||
if (ret < 0) {
|
||||
error_report("Failed to set msi routing");
|
||||
return -1;
|
||||
error_report("Failed to set msi routing for gsi %d", virq);
|
||||
abort();
|
||||
}
|
||||
|
||||
trace_mshv_set_msi_routing(virq, msg.address, data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -347,16 +296,17 @@ int mshv_request_interrupt(MshvState *mshv_state, uint32_t interrupt_type, uint3
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mshv_irqchip_commit_routes(void)
|
||||
void mshv_irqchip_commit_routes(MshvState *s)
|
||||
{
|
||||
int ret;
|
||||
int vm_fd = mshv_state->vm;
|
||||
int vm_fd = s->vm;
|
||||
|
||||
ret = commit_msi_routing_table(vm_fd);
|
||||
ret = ioctl(vm_fd, MSHV_SET_MSI_ROUTING, s->irq_routes);
|
||||
if (ret < 0) {
|
||||
error_report("Failed to commit msi routing table");
|
||||
abort();
|
||||
}
|
||||
trace_mshv_commit_msi_routing_table(vm_fd, s->irq_routes->nr);
|
||||
}
|
||||
|
||||
int mshv_irqchip_add_irqfd_notifier_gsi(const EventNotifier *event,
|
||||
@@ -372,27 +322,45 @@ int mshv_irqchip_remove_irqfd_notifier_gsi(const EventNotifier *event,
|
||||
return irqchip_update_irqfd_notifier_gsi(event, NULL, virq, false);
|
||||
}
|
||||
|
||||
int mshv_reserve_ioapic_msi_routes(int vm_fd)
|
||||
static int mshv_reserve_ioapic_msi_routes(MshvState *s)
|
||||
{
|
||||
int ret, gsi;
|
||||
int ret, i;
|
||||
int gsi = 0;
|
||||
struct mshv_user_irq_entry blank_entry = { 0 };
|
||||
|
||||
/*
|
||||
* Reserve GSI 0-23 for IOAPIC pins, to avoid conflicts of legacy
|
||||
* peripherals with MSI-X devices
|
||||
*/
|
||||
for (gsi = 0; gsi < IOAPIC_NUM_PINS; gsi++) {
|
||||
ret = add_msi_routing(0, 0);
|
||||
for (i = 0; i < IOAPIC_NUM_PINS; i++) {
|
||||
/* ret = add_msi_routing(0, 0); */
|
||||
ret = irqchip_allocate_gsi(s, &gsi);
|
||||
if (ret < 0) {
|
||||
error_report("Failed to reserve GSI %d", gsi);
|
||||
error_report("Failed to reserve GSI %d: %s", gsi, strerror(-ret));
|
||||
return -1;
|
||||
}
|
||||
blank_entry.gsi = gsi;
|
||||
add_routing_entry(s, &blank_entry);
|
||||
}
|
||||
|
||||
ret = commit_msi_routing_table(vm_fd);
|
||||
if (ret < 0) {
|
||||
error_report("Failed to commit reserved IOAPIC MSI routes");
|
||||
return -1;
|
||||
}
|
||||
mshv_irqchip_commit_routes(s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mshv_init_irq_routing(MshvState *s)
|
||||
{
|
||||
int ret;
|
||||
int gsi_count = MSHV_MAX_MSI_ROUTES;
|
||||
|
||||
s->irq_routes = g_malloc0(sizeof(*s->irq_routes));
|
||||
s->nr_allocated_irq_routes = 0;
|
||||
s->gsi_count = gsi_count;
|
||||
s->used_gsi_bitmap = bitmap_new(gsi_count);
|
||||
|
||||
ret = mshv_reserve_ioapic_msi_routes(s);
|
||||
if (ret < 0) {
|
||||
error_report("Failed to reserve IOAPIC MSI routes");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
211
accel/mshv/mem.c
211
accel/mshv/mem.c
@@ -12,10 +12,13 @@
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qapi/error.h"
|
||||
#include "linux/mshv.h"
|
||||
#include "system/address-spaces.h"
|
||||
#include "system/mshv.h"
|
||||
#include "system/mshv_int.h"
|
||||
#include "hw/hyperv/hvhdk_mini.h"
|
||||
#include "system/physmem.h"
|
||||
#include "exec/memattrs.h"
|
||||
#include <sys/ioctl.h>
|
||||
#include "trace.h"
|
||||
@@ -211,3 +214,211 @@ void mshv_set_phys_mem(MshvMemoryListener *mml, MemoryRegionSection *section,
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static int enable_dirty_page_tracking(int vm_fd)
|
||||
{
|
||||
int ret;
|
||||
struct hv_input_set_partition_property in = {0};
|
||||
struct mshv_root_hvcall args = {0};
|
||||
|
||||
in.property_code = HV_PARTITION_PROPERTY_GPA_PAGE_ACCESS_TRACKING;
|
||||
in.property_value = 1;
|
||||
|
||||
args.code = HVCALL_SET_PARTITION_PROPERTY;
|
||||
args.in_sz = sizeof(in);
|
||||
args.in_ptr = (uint64_t)∈
|
||||
|
||||
ret = mshv_hvcall(vm_fd, &args);
|
||||
if (ret < 0) {
|
||||
error_report("Failed to enable dirty page tracking: %s",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieve dirty page bitmap for a GPA range, clearing the dirty bits
|
||||
* atomically. Large ranges are handled in batches.
|
||||
*/
|
||||
static int get_dirty_log(int vm_fd, uint64_t base_pfn, uint64_t page_count,
|
||||
unsigned long *bitmap, size_t bitmap_size)
|
||||
{
|
||||
uint64_t batch, bitmap_offset, completed = 0;
|
||||
struct mshv_gpap_access_bitmap args = {0};
|
||||
int ret;
|
||||
|
||||
QEMU_BUILD_BUG_ON(MSHV_DIRTY_PAGES_BATCH_SIZE % BITS_PER_LONG != 0);
|
||||
assert(bitmap_size >= ROUND_UP(page_count, BITS_PER_LONG) / 8);
|
||||
|
||||
while (completed < page_count) {
|
||||
batch = MIN(MSHV_DIRTY_PAGES_BATCH_SIZE, page_count - completed);
|
||||
bitmap_offset = completed / BITS_PER_LONG;
|
||||
|
||||
args.access_type = MSHV_GPAP_ACCESS_TYPE_DIRTY;
|
||||
args.access_op = MSHV_GPAP_ACCESS_OP_CLEAR;
|
||||
args.page_count = batch;
|
||||
args.gpap_base = base_pfn + completed;
|
||||
args.bitmap_ptr = (uint64_t)(bitmap + bitmap_offset);
|
||||
|
||||
ret = ioctl(vm_fd, MSHV_GET_GPAP_ACCESS_BITMAP, &args);
|
||||
if (ret < 0) {
|
||||
error_report("Failed to get dirty log (base_pfn=0x%" PRIx64
|
||||
" batch=%" PRIu64 "): %s",
|
||||
base_pfn + completed, batch, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
completed += batch;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool mshv_log_global_start(MemoryListener *listener, Error **errp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = enable_dirty_page_tracking(mshv_state->vm);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Failed to enable dirty page tracking");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int disable_dirty_page_tracking(int vm_fd)
|
||||
{
|
||||
int ret;
|
||||
struct hv_input_set_partition_property in = {0};
|
||||
struct mshv_root_hvcall args = {0};
|
||||
|
||||
in.property_code = HV_PARTITION_PROPERTY_GPA_PAGE_ACCESS_TRACKING;
|
||||
in.property_value = 0;
|
||||
|
||||
args.code = HVCALL_SET_PARTITION_PROPERTY;
|
||||
args.in_sz = sizeof(in);
|
||||
args.in_ptr = (uint64_t)∈
|
||||
|
||||
ret = mshv_hvcall(vm_fd, &args);
|
||||
if (ret < 0) {
|
||||
error_report("Failed to disable dirty page tracking: %s",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_dirty_pages(int vm_fd, uint64_t base_pfn, uint64_t page_count)
|
||||
{
|
||||
uint64_t batch, completed = 0;
|
||||
unsigned long bitmap[MSHV_DIRTY_PAGES_BATCH_SIZE / BITS_PER_LONG];
|
||||
struct mshv_gpap_access_bitmap args = {0};
|
||||
int ret;
|
||||
|
||||
while (completed < page_count) {
|
||||
batch = MIN(MSHV_DIRTY_PAGES_BATCH_SIZE, page_count - completed);
|
||||
|
||||
args.access_type = MSHV_GPAP_ACCESS_TYPE_DIRTY;
|
||||
args.access_op = MSHV_GPAP_ACCESS_OP_SET;
|
||||
args.page_count = batch;
|
||||
args.gpap_base = base_pfn + completed;
|
||||
args.bitmap_ptr = (uint64_t)bitmap;
|
||||
|
||||
ret = ioctl(vm_fd, MSHV_GET_GPAP_ACCESS_BITMAP, &args);
|
||||
if (ret < 0) {
|
||||
error_report("Failed to set dirty pages (base_pfn=0x%" PRIx64
|
||||
" batch=%" PRIu64 "): %s",
|
||||
base_pfn + completed, batch, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
completed += batch;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool set_dirty_bits_cb(Int128 start, Int128 len, const MemoryRegion *mr,
|
||||
hwaddr offset_in_region, void *opaque)
|
||||
{
|
||||
int ret, *errp = opaque;
|
||||
hwaddr gpa, size;
|
||||
uint64_t page_count, base_pfn;
|
||||
|
||||
gpa = int128_get64(start);
|
||||
size = int128_get64(len);
|
||||
page_count = size >> MSHV_PAGE_SHIFT;
|
||||
base_pfn = gpa >> MSHV_PAGE_SHIFT;
|
||||
|
||||
if (!mr->ram || mr->readonly) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (page_count == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = set_dirty_pages(mshv_state->vm, base_pfn, page_count);
|
||||
|
||||
/* true aborts the iteration, which is what we want if there's an error */
|
||||
if (ret < 0) {
|
||||
*errp = ret;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void mshv_log_global_stop(MemoryListener *listener)
|
||||
{
|
||||
int err = 0;
|
||||
/* MSHV requires all dirty bits to be set before disabling tracking. */
|
||||
FlatView *fv = address_space_to_flatview(&address_space_memory);
|
||||
flatview_for_each_range(fv, set_dirty_bits_cb, &err);
|
||||
|
||||
if (err < 0) {
|
||||
error_report("Failed to set dirty bits before disabling tracking");
|
||||
}
|
||||
|
||||
disable_dirty_page_tracking(mshv_state->vm);
|
||||
}
|
||||
|
||||
void mshv_log_sync(MemoryListener *listener, MemoryRegionSection *section)
|
||||
{
|
||||
hwaddr size, start_addr, mr_offset;
|
||||
uint64_t page_count, base_pfn;
|
||||
size_t bitmap_size;
|
||||
unsigned long *bitmap;
|
||||
ram_addr_t ram_addr;
|
||||
int ret;
|
||||
MemoryRegion *mr = section->mr;
|
||||
|
||||
if (!memory_region_is_ram(mr) || memory_region_is_rom(mr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
size = align_section(section, &start_addr);
|
||||
if (!size) {
|
||||
return;
|
||||
}
|
||||
|
||||
page_count = size >> MSHV_PAGE_SHIFT;
|
||||
base_pfn = start_addr >> MSHV_PAGE_SHIFT;
|
||||
bitmap_size = ROUND_UP(page_count, BITS_PER_LONG) / 8;
|
||||
bitmap = g_malloc0(bitmap_size);
|
||||
|
||||
ret = get_dirty_log(mshv_state->vm, base_pfn, page_count, bitmap,
|
||||
bitmap_size);
|
||||
if (ret < 0) {
|
||||
g_free(bitmap);
|
||||
return;
|
||||
}
|
||||
|
||||
mr_offset = section->offset_within_region + start_addr -
|
||||
section->offset_within_address_space;
|
||||
ram_addr = memory_region_get_ram_addr(mr) + mr_offset;
|
||||
|
||||
physical_memory_set_dirty_lebitmap(bitmap, ram_addr, page_count);
|
||||
g_free(bitmap);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
system_ss.add(when: 'CONFIG_MSHV', if_true: files(
|
||||
'irq.c',
|
||||
'mem.c',
|
||||
'msr.c',
|
||||
'mshv-all.c'
|
||||
))
|
||||
|
||||
@@ -43,10 +43,6 @@
|
||||
#include <err.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#define TYPE_MSHV_ACCEL ACCEL_CLASS_NAME("mshv")
|
||||
|
||||
DECLARE_INSTANCE_CHECKER(MshvState, MSHV_STATE, TYPE_MSHV_ACCEL)
|
||||
|
||||
bool mshv_allowed;
|
||||
|
||||
MshvState *mshv_state;
|
||||
@@ -110,21 +106,136 @@ static int resume_vm(int vm_fd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_host_partition_property(int mshv_fd, uint32_t property_code,
|
||||
uint64_t *value)
|
||||
{
|
||||
int ret;
|
||||
struct hv_input_get_partition_property in = {0};
|
||||
struct hv_output_get_partition_property out = {0};
|
||||
struct mshv_root_hvcall args = {0};
|
||||
|
||||
in.property_code = property_code;
|
||||
|
||||
args.code = HVCALL_GET_PARTITION_PROPERTY;
|
||||
args.in_sz = sizeof(in);
|
||||
args.in_ptr = (uint64_t)∈
|
||||
args.out_sz = sizeof(out);
|
||||
args.out_ptr = (uint64_t)&out;
|
||||
|
||||
ret = ioctl(mshv_fd, MSHV_ROOT_HVCALL, &args);
|
||||
if (ret < 0) {
|
||||
error_report("Failed to get host partition property bank: %s",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
*value = out.property_value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_partition_property(int vm_fd, uint32_t feature_bank,
|
||||
uint64_t *value)
|
||||
{
|
||||
struct hv_input_get_partition_property in = {0};
|
||||
struct hv_output_get_partition_property out = {0};
|
||||
struct mshv_root_hvcall args = {0};
|
||||
int ret;
|
||||
|
||||
in.property_code = feature_bank;
|
||||
|
||||
args.code = HVCALL_GET_PARTITION_PROPERTY;
|
||||
args.in_sz = sizeof(in);
|
||||
args.in_ptr = (uint64_t)∈
|
||||
args.out_sz = sizeof(out);
|
||||
args.out_ptr = (uint64_t)&out;
|
||||
|
||||
ret = ioctl(vm_fd, MSHV_ROOT_HVCALL, &args);
|
||||
if (ret < 0) {
|
||||
error_report("Failed to get guest partition property bank: %s",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
*value = out.property_value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_proc_features(int vm_fd,
|
||||
union hv_partition_processor_features *features)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = get_partition_property(vm_fd,
|
||||
HV_PARTITION_PROPERTY_PROCESSOR_FEATURES0,
|
||||
features[0].as_uint64);
|
||||
if (ret < 0) {
|
||||
error_report("Failed to get processor features bank 0");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = get_partition_property(vm_fd,
|
||||
HV_PARTITION_PROPERTY_PROCESSOR_FEATURES1,
|
||||
features[1].as_uint64);
|
||||
if (ret < 0) {
|
||||
error_report("Failed to get processor features bank 1");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int create_partition(int mshv_fd, int *vm_fd)
|
||||
{
|
||||
int ret;
|
||||
struct mshv_create_partition args = {0};
|
||||
uint64_t pt_flags, host_proc_features;
|
||||
union hv_partition_processor_xsave_features disabled_xsave_features;
|
||||
union hv_partition_processor_features disabled_partition_features = {0};
|
||||
|
||||
struct mshv_create_partition_v2 args = {0};
|
||||
|
||||
QEMU_BUILD_BUG_ON(MSHV_NUM_CPU_FEATURES_BANKS != 2);
|
||||
|
||||
/* Initialize pt_flags with the desired features */
|
||||
uint64_t pt_flags = (1ULL << MSHV_PT_BIT_LAPIC) |
|
||||
(1ULL << MSHV_PT_BIT_X2APIC) |
|
||||
(1ULL << MSHV_PT_BIT_GPA_SUPER_PAGES);
|
||||
pt_flags = (1ULL << MSHV_PT_BIT_LAPIC) |
|
||||
(1ULL << MSHV_PT_BIT_X2APIC) |
|
||||
(1ULL << MSHV_PT_BIT_GPA_SUPER_PAGES) |
|
||||
(1ULL << MSHV_PT_BIT_CPU_AND_XSAVE_FEATURES);
|
||||
|
||||
/* Set default isolation type */
|
||||
uint64_t pt_isolation = MSHV_PT_ISOLATION_NONE;
|
||||
/* enable all */
|
||||
disabled_xsave_features.as_uint64 = 0;
|
||||
|
||||
/*
|
||||
* query host for supported processor features and disable unsupported
|
||||
* features: (0 means supported, 1 means disabled, hence the negation)
|
||||
*/
|
||||
ret = get_host_partition_property(mshv_fd,
|
||||
HV_PARTITION_PROPERTY_PROCESSOR_FEATURES0,
|
||||
&host_proc_features);
|
||||
if (ret < 0) {
|
||||
error_report("Failed to get host processor feature bank 0");
|
||||
return -1;
|
||||
}
|
||||
args.pt_cpu_fbanks[0] = ~host_proc_features;
|
||||
|
||||
ret = get_host_partition_property(mshv_fd,
|
||||
HV_PARTITION_PROPERTY_PROCESSOR_FEATURES1,
|
||||
&host_proc_features);
|
||||
if (ret < 0) {
|
||||
error_report("Failed to get host processor feature bank 1");
|
||||
return -1;
|
||||
}
|
||||
args.pt_cpu_fbanks[1] = ~host_proc_features;
|
||||
|
||||
/* arch-specific features we disable regardless of host support */
|
||||
mshv_arch_disable_partition_proc_features(&disabled_partition_features);
|
||||
args.pt_cpu_fbanks[0] |= disabled_partition_features.as_uint64[0];
|
||||
args.pt_cpu_fbanks[1] |= disabled_partition_features.as_uint64[1];
|
||||
|
||||
/* populate args structure */
|
||||
args.pt_flags = pt_flags;
|
||||
args.pt_isolation = pt_isolation;
|
||||
args.pt_isolation = MSHV_PT_ISOLATION_NONE;
|
||||
args.pt_disabled_xsave = disabled_xsave_features.as_uint64;
|
||||
args.pt_num_cpu_fbanks = MSHV_NUM_CPU_FEATURES_BANKS;
|
||||
|
||||
ret = ioctl(mshv_fd, MSHV_CREATE_PARTITION, &args);
|
||||
if (ret < 0) {
|
||||
@@ -204,11 +315,6 @@ static int create_vm(int mshv_fd, int *vm_fd)
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = mshv_reserve_ioapic_msi_routes(*vm_fd);
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = mshv_arch_post_init_vm(*vm_fd);
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
@@ -371,6 +477,9 @@ static MemoryListener mshv_memory_listener = {
|
||||
.region_del = mem_region_del,
|
||||
.eventfd_add = mem_ioeventfd_add,
|
||||
.eventfd_del = mem_ioeventfd_del,
|
||||
.log_sync = mshv_log_sync,
|
||||
.log_global_start = mshv_log_global_start,
|
||||
.log_global_stop = mshv_log_global_stop,
|
||||
};
|
||||
|
||||
static MemoryListener mshv_io_listener = {
|
||||
@@ -415,13 +524,13 @@ static int mshv_init_vcpu(CPUState *cpu)
|
||||
int ret;
|
||||
|
||||
cpu->accel = g_new0(AccelCPUState, 1);
|
||||
mshv_arch_init_vcpu(cpu);
|
||||
|
||||
ret = mshv_create_vcpu(vm_fd, vp_index, &cpu->accel->cpufd);
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
mshv_arch_init_vcpu(cpu);
|
||||
cpu->accel->dirty = true;
|
||||
|
||||
return 0;
|
||||
@@ -450,8 +559,6 @@ static int mshv_init(AccelState *as, MachineState *ms)
|
||||
|
||||
mshv_init_mmio_emu();
|
||||
|
||||
mshv_init_msicontrol();
|
||||
|
||||
ret = create_vm(mshv_fd, &vm_fd);
|
||||
if (ret < 0) {
|
||||
close(mshv_fd);
|
||||
@@ -467,11 +574,19 @@ static int mshv_init(AccelState *as, MachineState *ms)
|
||||
|
||||
s->vm = vm_fd;
|
||||
s->fd = mshv_fd;
|
||||
|
||||
ret = get_proc_features(vm_fd, &s->processor_features);
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
s->nr_as = 1;
|
||||
s->as = g_new0(MshvAddressSpace, s->nr_as);
|
||||
|
||||
mshv_state = s;
|
||||
|
||||
mshv_init_irq_routing(s);
|
||||
|
||||
register_mshv_memory_listener(s, &s->memory_listener, &address_space_memory,
|
||||
0, "mshv-memory");
|
||||
memory_listener_register(&mshv_io_listener, &address_space_io);
|
||||
@@ -503,7 +618,7 @@ static int mshv_cpu_exec(CPUState *cpu)
|
||||
|
||||
do {
|
||||
if (cpu->accel->dirty) {
|
||||
ret = mshv_arch_put_registers(cpu);
|
||||
ret = mshv_arch_store_vcpu_state(cpu);
|
||||
if (ret) {
|
||||
error_report("Failed to put registers after init: %s",
|
||||
strerror(-ret));
|
||||
@@ -625,7 +740,7 @@ static void mshv_start_vcpu_thread(CPUState *cpu)
|
||||
static void do_mshv_cpu_synchronize_post_init(CPUState *cpu,
|
||||
run_on_cpu_data arg)
|
||||
{
|
||||
int ret = mshv_arch_put_registers(cpu);
|
||||
int ret = mshv_arch_store_vcpu_state(cpu);
|
||||
if (ret < 0) {
|
||||
error_report("Failed to put registers after init: %s", strerror(-ret));
|
||||
abort();
|
||||
@@ -641,7 +756,7 @@ static void mshv_cpu_synchronize_post_init(CPUState *cpu)
|
||||
|
||||
static void mshv_cpu_synchronize_post_reset(CPUState *cpu)
|
||||
{
|
||||
int ret = mshv_arch_put_registers(cpu);
|
||||
int ret = mshv_arch_store_vcpu_state(cpu);
|
||||
if (ret) {
|
||||
error_report("Failed to put registers after reset: %s",
|
||||
strerror(-ret));
|
||||
@@ -665,7 +780,7 @@ static void mshv_cpu_synchronize_pre_loadvm(CPUState *cpu)
|
||||
static void do_mshv_cpu_synchronize(CPUState *cpu, run_on_cpu_data arg)
|
||||
{
|
||||
if (!cpu->accel->dirty) {
|
||||
int ret = mshv_load_regs(cpu);
|
||||
int ret = mshv_arch_load_vcpu_state(cpu);
|
||||
if (ret < 0) {
|
||||
error_report("Failed to load registers for vcpu %d",
|
||||
cpu->cpu_index);
|
||||
|
||||
375
accel/mshv/msr.c
375
accel/mshv/msr.c
@@ -1,375 +0,0 @@
|
||||
/*
|
||||
* QEMU MSHV support
|
||||
*
|
||||
* Copyright Microsoft, Corp. 2025
|
||||
*
|
||||
* Authors: Magnus Kulke <magnuskulke@microsoft.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "system/mshv.h"
|
||||
#include "system/mshv_int.h"
|
||||
#include "hw/hyperv/hvgdk_mini.h"
|
||||
#include "linux/mshv.h"
|
||||
#include "qemu/error-report.h"
|
||||
|
||||
static uint32_t supported_msrs[64] = {
|
||||
IA32_MSR_TSC,
|
||||
IA32_MSR_EFER,
|
||||
IA32_MSR_KERNEL_GS_BASE,
|
||||
IA32_MSR_APIC_BASE,
|
||||
IA32_MSR_PAT,
|
||||
IA32_MSR_SYSENTER_CS,
|
||||
IA32_MSR_SYSENTER_ESP,
|
||||
IA32_MSR_SYSENTER_EIP,
|
||||
IA32_MSR_STAR,
|
||||
IA32_MSR_LSTAR,
|
||||
IA32_MSR_CSTAR,
|
||||
IA32_MSR_SFMASK,
|
||||
IA32_MSR_MTRR_DEF_TYPE,
|
||||
IA32_MSR_MTRR_PHYSBASE0,
|
||||
IA32_MSR_MTRR_PHYSMASK0,
|
||||
IA32_MSR_MTRR_PHYSBASE1,
|
||||
IA32_MSR_MTRR_PHYSMASK1,
|
||||
IA32_MSR_MTRR_PHYSBASE2,
|
||||
IA32_MSR_MTRR_PHYSMASK2,
|
||||
IA32_MSR_MTRR_PHYSBASE3,
|
||||
IA32_MSR_MTRR_PHYSMASK3,
|
||||
IA32_MSR_MTRR_PHYSBASE4,
|
||||
IA32_MSR_MTRR_PHYSMASK4,
|
||||
IA32_MSR_MTRR_PHYSBASE5,
|
||||
IA32_MSR_MTRR_PHYSMASK5,
|
||||
IA32_MSR_MTRR_PHYSBASE6,
|
||||
IA32_MSR_MTRR_PHYSMASK6,
|
||||
IA32_MSR_MTRR_PHYSBASE7,
|
||||
IA32_MSR_MTRR_PHYSMASK7,
|
||||
IA32_MSR_MTRR_FIX64K_00000,
|
||||
IA32_MSR_MTRR_FIX16K_80000,
|
||||
IA32_MSR_MTRR_FIX16K_A0000,
|
||||
IA32_MSR_MTRR_FIX4K_C0000,
|
||||
IA32_MSR_MTRR_FIX4K_C8000,
|
||||
IA32_MSR_MTRR_FIX4K_D0000,
|
||||
IA32_MSR_MTRR_FIX4K_D8000,
|
||||
IA32_MSR_MTRR_FIX4K_E0000,
|
||||
IA32_MSR_MTRR_FIX4K_E8000,
|
||||
IA32_MSR_MTRR_FIX4K_F0000,
|
||||
IA32_MSR_MTRR_FIX4K_F8000,
|
||||
IA32_MSR_TSC_AUX,
|
||||
IA32_MSR_DEBUG_CTL,
|
||||
HV_X64_MSR_GUEST_OS_ID,
|
||||
HV_X64_MSR_SINT0,
|
||||
HV_X64_MSR_SINT1,
|
||||
HV_X64_MSR_SINT2,
|
||||
HV_X64_MSR_SINT3,
|
||||
HV_X64_MSR_SINT4,
|
||||
HV_X64_MSR_SINT5,
|
||||
HV_X64_MSR_SINT6,
|
||||
HV_X64_MSR_SINT7,
|
||||
HV_X64_MSR_SINT8,
|
||||
HV_X64_MSR_SINT9,
|
||||
HV_X64_MSR_SINT10,
|
||||
HV_X64_MSR_SINT11,
|
||||
HV_X64_MSR_SINT12,
|
||||
HV_X64_MSR_SINT13,
|
||||
HV_X64_MSR_SINT14,
|
||||
HV_X64_MSR_SINT15,
|
||||
HV_X64_MSR_SCONTROL,
|
||||
HV_X64_MSR_SIEFP,
|
||||
HV_X64_MSR_SIMP,
|
||||
HV_X64_MSR_REFERENCE_TSC,
|
||||
HV_X64_MSR_EOM,
|
||||
};
|
||||
static const size_t msr_count = ARRAY_SIZE(supported_msrs);
|
||||
|
||||
static int compare_msr_index(const void *a, const void *b)
|
||||
{
|
||||
return *(uint32_t *)a - *(uint32_t *)b;
|
||||
}
|
||||
|
||||
__attribute__((constructor))
|
||||
static void init_sorted_msr_map(void)
|
||||
{
|
||||
qsort(supported_msrs, msr_count, sizeof(uint32_t), compare_msr_index);
|
||||
}
|
||||
|
||||
static int mshv_is_supported_msr(uint32_t msr)
|
||||
{
|
||||
return bsearch(&msr, supported_msrs, msr_count, sizeof(uint32_t),
|
||||
compare_msr_index) != NULL;
|
||||
}
|
||||
|
||||
static int mshv_msr_to_hv_reg_name(uint32_t msr, uint32_t *hv_reg)
|
||||
{
|
||||
switch (msr) {
|
||||
case IA32_MSR_TSC:
|
||||
*hv_reg = HV_X64_REGISTER_TSC;
|
||||
return 0;
|
||||
case IA32_MSR_EFER:
|
||||
*hv_reg = HV_X64_REGISTER_EFER;
|
||||
return 0;
|
||||
case IA32_MSR_KERNEL_GS_BASE:
|
||||
*hv_reg = HV_X64_REGISTER_KERNEL_GS_BASE;
|
||||
return 0;
|
||||
case IA32_MSR_APIC_BASE:
|
||||
*hv_reg = HV_X64_REGISTER_APIC_BASE;
|
||||
return 0;
|
||||
case IA32_MSR_PAT:
|
||||
*hv_reg = HV_X64_REGISTER_PAT;
|
||||
return 0;
|
||||
case IA32_MSR_SYSENTER_CS:
|
||||
*hv_reg = HV_X64_REGISTER_SYSENTER_CS;
|
||||
return 0;
|
||||
case IA32_MSR_SYSENTER_ESP:
|
||||
*hv_reg = HV_X64_REGISTER_SYSENTER_ESP;
|
||||
return 0;
|
||||
case IA32_MSR_SYSENTER_EIP:
|
||||
*hv_reg = HV_X64_REGISTER_SYSENTER_EIP;
|
||||
return 0;
|
||||
case IA32_MSR_STAR:
|
||||
*hv_reg = HV_X64_REGISTER_STAR;
|
||||
return 0;
|
||||
case IA32_MSR_LSTAR:
|
||||
*hv_reg = HV_X64_REGISTER_LSTAR;
|
||||
return 0;
|
||||
case IA32_MSR_CSTAR:
|
||||
*hv_reg = HV_X64_REGISTER_CSTAR;
|
||||
return 0;
|
||||
case IA32_MSR_SFMASK:
|
||||
*hv_reg = HV_X64_REGISTER_SFMASK;
|
||||
return 0;
|
||||
case IA32_MSR_MTRR_CAP:
|
||||
*hv_reg = HV_X64_REGISTER_MSR_MTRR_CAP;
|
||||
return 0;
|
||||
case IA32_MSR_MTRR_DEF_TYPE:
|
||||
*hv_reg = HV_X64_REGISTER_MSR_MTRR_DEF_TYPE;
|
||||
return 0;
|
||||
case IA32_MSR_MTRR_PHYSBASE0:
|
||||
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE0;
|
||||
return 0;
|
||||
case IA32_MSR_MTRR_PHYSMASK0:
|
||||
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK0;
|
||||
return 0;
|
||||
case IA32_MSR_MTRR_PHYSBASE1:
|
||||
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE1;
|
||||
return 0;
|
||||
case IA32_MSR_MTRR_PHYSMASK1:
|
||||
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK1;
|
||||
return 0;
|
||||
case IA32_MSR_MTRR_PHYSBASE2:
|
||||
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE2;
|
||||
return 0;
|
||||
case IA32_MSR_MTRR_PHYSMASK2:
|
||||
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK2;
|
||||
return 0;
|
||||
case IA32_MSR_MTRR_PHYSBASE3:
|
||||
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE3;
|
||||
return 0;
|
||||
case IA32_MSR_MTRR_PHYSMASK3:
|
||||
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK3;
|
||||
return 0;
|
||||
case IA32_MSR_MTRR_PHYSBASE4:
|
||||
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE4;
|
||||
return 0;
|
||||
case IA32_MSR_MTRR_PHYSMASK4:
|
||||
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK4;
|
||||
return 0;
|
||||
case IA32_MSR_MTRR_PHYSBASE5:
|
||||
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE5;
|
||||
return 0;
|
||||
case IA32_MSR_MTRR_PHYSMASK5:
|
||||
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK5;
|
||||
return 0;
|
||||
case IA32_MSR_MTRR_PHYSBASE6:
|
||||
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE6;
|
||||
return 0;
|
||||
case IA32_MSR_MTRR_PHYSMASK6:
|
||||
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK6;
|
||||
return 0;
|
||||
case IA32_MSR_MTRR_PHYSBASE7:
|
||||
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_BASE7;
|
||||
return 0;
|
||||
case IA32_MSR_MTRR_PHYSMASK7:
|
||||
*hv_reg = HV_X64_REGISTER_MSR_MTRR_PHYS_MASK7;
|
||||
return 0;
|
||||
case IA32_MSR_MTRR_FIX64K_00000:
|
||||
*hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX64K00000;
|
||||
return 0;
|
||||
case IA32_MSR_MTRR_FIX16K_80000:
|
||||
*hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX16K80000;
|
||||
return 0;
|
||||
case IA32_MSR_MTRR_FIX16K_A0000:
|
||||
*hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX16KA0000;
|
||||
return 0;
|
||||
case IA32_MSR_MTRR_FIX4K_C0000:
|
||||
*hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KC0000;
|
||||
return 0;
|
||||
case IA32_MSR_MTRR_FIX4K_C8000:
|
||||
*hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KC8000;
|
||||
return 0;
|
||||
case IA32_MSR_MTRR_FIX4K_D0000:
|
||||
*hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KD0000;
|
||||
return 0;
|
||||
case IA32_MSR_MTRR_FIX4K_D8000:
|
||||
*hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KD8000;
|
||||
return 0;
|
||||
case IA32_MSR_MTRR_FIX4K_E0000:
|
||||
*hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KE0000;
|
||||
return 0;
|
||||
case IA32_MSR_MTRR_FIX4K_E8000:
|
||||
*hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KE8000;
|
||||
return 0;
|
||||
case IA32_MSR_MTRR_FIX4K_F0000:
|
||||
*hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KF0000;
|
||||
return 0;
|
||||
case IA32_MSR_MTRR_FIX4K_F8000:
|
||||
*hv_reg = HV_X64_REGISTER_MSR_MTRR_FIX4KF8000;
|
||||
return 0;
|
||||
case IA32_MSR_TSC_AUX:
|
||||
*hv_reg = HV_X64_REGISTER_TSC_AUX;
|
||||
return 0;
|
||||
case IA32_MSR_BNDCFGS:
|
||||
*hv_reg = HV_X64_REGISTER_BNDCFGS;
|
||||
return 0;
|
||||
case IA32_MSR_DEBUG_CTL:
|
||||
*hv_reg = HV_X64_REGISTER_DEBUG_CTL;
|
||||
return 0;
|
||||
case IA32_MSR_TSC_ADJUST:
|
||||
*hv_reg = HV_X64_REGISTER_TSC_ADJUST;
|
||||
return 0;
|
||||
case IA32_MSR_SPEC_CTRL:
|
||||
*hv_reg = HV_X64_REGISTER_SPEC_CTRL;
|
||||
return 0;
|
||||
case HV_X64_MSR_GUEST_OS_ID:
|
||||
*hv_reg = HV_REGISTER_GUEST_OS_ID;
|
||||
return 0;
|
||||
case HV_X64_MSR_SINT0:
|
||||
*hv_reg = HV_REGISTER_SINT0;
|
||||
return 0;
|
||||
case HV_X64_MSR_SINT1:
|
||||
*hv_reg = HV_REGISTER_SINT1;
|
||||
return 0;
|
||||
case HV_X64_MSR_SINT2:
|
||||
*hv_reg = HV_REGISTER_SINT2;
|
||||
return 0;
|
||||
case HV_X64_MSR_SINT3:
|
||||
*hv_reg = HV_REGISTER_SINT3;
|
||||
return 0;
|
||||
case HV_X64_MSR_SINT4:
|
||||
*hv_reg = HV_REGISTER_SINT4;
|
||||
return 0;
|
||||
case HV_X64_MSR_SINT5:
|
||||
*hv_reg = HV_REGISTER_SINT5;
|
||||
return 0;
|
||||
case HV_X64_MSR_SINT6:
|
||||
*hv_reg = HV_REGISTER_SINT6;
|
||||
return 0;
|
||||
case HV_X64_MSR_SINT7:
|
||||
*hv_reg = HV_REGISTER_SINT7;
|
||||
return 0;
|
||||
case HV_X64_MSR_SINT8:
|
||||
*hv_reg = HV_REGISTER_SINT8;
|
||||
return 0;
|
||||
case HV_X64_MSR_SINT9:
|
||||
*hv_reg = HV_REGISTER_SINT9;
|
||||
return 0;
|
||||
case HV_X64_MSR_SINT10:
|
||||
*hv_reg = HV_REGISTER_SINT10;
|
||||
return 0;
|
||||
case HV_X64_MSR_SINT11:
|
||||
*hv_reg = HV_REGISTER_SINT11;
|
||||
return 0;
|
||||
case HV_X64_MSR_SINT12:
|
||||
*hv_reg = HV_REGISTER_SINT12;
|
||||
return 0;
|
||||
case HV_X64_MSR_SINT13:
|
||||
*hv_reg = HV_REGISTER_SINT13;
|
||||
return 0;
|
||||
case HV_X64_MSR_SINT14:
|
||||
*hv_reg = HV_REGISTER_SINT14;
|
||||
return 0;
|
||||
case HV_X64_MSR_SINT15:
|
||||
*hv_reg = HV_REGISTER_SINT15;
|
||||
return 0;
|
||||
case IA32_MSR_MISC_ENABLE:
|
||||
*hv_reg = HV_X64_REGISTER_MSR_IA32_MISC_ENABLE;
|
||||
return 0;
|
||||
case HV_X64_MSR_SCONTROL:
|
||||
*hv_reg = HV_REGISTER_SCONTROL;
|
||||
return 0;
|
||||
case HV_X64_MSR_SIEFP:
|
||||
*hv_reg = HV_REGISTER_SIEFP;
|
||||
return 0;
|
||||
case HV_X64_MSR_SIMP:
|
||||
*hv_reg = HV_REGISTER_SIMP;
|
||||
return 0;
|
||||
case HV_X64_MSR_REFERENCE_TSC:
|
||||
*hv_reg = HV_REGISTER_REFERENCE_TSC;
|
||||
return 0;
|
||||
case HV_X64_MSR_EOM:
|
||||
*hv_reg = HV_REGISTER_EOM;
|
||||
return 0;
|
||||
default:
|
||||
error_report("failed to map MSR %u to HV register name", msr);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int set_msrs(const CPUState *cpu, GList *msrs)
|
||||
{
|
||||
size_t n_msrs;
|
||||
GList *entries;
|
||||
MshvMsrEntry *entry;
|
||||
enum hv_register_name name;
|
||||
struct hv_register_assoc *assoc;
|
||||
int ret;
|
||||
size_t i = 0;
|
||||
|
||||
n_msrs = g_list_length(msrs);
|
||||
hv_register_assoc *assocs = g_new0(hv_register_assoc, n_msrs);
|
||||
|
||||
entries = msrs;
|
||||
for (const GList *elem = entries; elem != NULL; elem = elem->next) {
|
||||
entry = elem->data;
|
||||
ret = mshv_msr_to_hv_reg_name(entry->index, &name);
|
||||
if (ret < 0) {
|
||||
g_free(assocs);
|
||||
return ret;
|
||||
}
|
||||
assoc = &assocs[i];
|
||||
assoc->name = name;
|
||||
/* the union has been initialized to 0 */
|
||||
assoc->value.reg64 = entry->data;
|
||||
i++;
|
||||
}
|
||||
ret = mshv_set_generic_regs(cpu, assocs, n_msrs);
|
||||
g_free(assocs);
|
||||
if (ret < 0) {
|
||||
error_report("failed to set msrs");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int mshv_configure_msr(const CPUState *cpu, const MshvMsrEntry *msrs,
|
||||
size_t n_msrs)
|
||||
{
|
||||
GList *valid_msrs = NULL;
|
||||
uint32_t msr_index;
|
||||
int ret;
|
||||
|
||||
for (size_t i = 0; i < n_msrs; i++) {
|
||||
msr_index = msrs[i].index;
|
||||
/* check whether index of msrs is in SUPPORTED_MSRS */
|
||||
if (mshv_is_supported_msr(msr_index)) {
|
||||
valid_msrs = g_list_append(valid_msrs, (void *) &msrs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
ret = set_msrs(cpu, valid_msrs);
|
||||
g_list_free(valid_msrs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -44,7 +44,7 @@ int kvm_on_sigbus(int code, void *addr)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int kvm_irqchip_add_msi_route(KVMRouteChange *c, int vector, PCIDevice *dev)
|
||||
int kvm_irqchip_add_msi_route(AccelRouteChange *c, int vector, PCIDevice *dev)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
@@ -13,13 +13,14 @@
|
||||
#include "system/mshv.h"
|
||||
|
||||
bool mshv_allowed;
|
||||
MshvState *mshv_state;
|
||||
|
||||
int mshv_irqchip_add_msi_route(int vector, PCIDevice *dev)
|
||||
int mshv_irqchip_add_msi_route(AccelRouteChange *c, int vector, PCIDevice *dev)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
void mshv_irqchip_release_virq(int virq)
|
||||
void mshv_irqchip_release_virq(MshvState *s, int virq)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -28,7 +29,7 @@ int mshv_irqchip_update_msi_route(int virq, MSIMessage msg, PCIDevice *dev)
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
void mshv_irqchip_commit_routes(void)
|
||||
void mshv_irqchip_commit_routes(MshvState *s)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user