fix: let nested-KVM vmlaunch succeed with Virtual NMIs
Self-test and Linux guest were failing VM-instruction error 7. Share a KVM-style control adjust, request PIN_VIRTUAL_NMIS, and stop intercepting guest #PF. Land isolated XNU/Python/KDE scaffolding off the boot path. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
#ifndef UTSM_IPC_NATIVE_H
|
||||
#define UTSM_IPC_NATIVE_H
|
||||
|
||||
#include <utsm/types.h>
|
||||
|
||||
/*
|
||||
* Experimental native Deshab capability IPC ABI.
|
||||
*
|
||||
* This is intentionally distinct from CODE/utsm-ipc/ipc_proto.h, which is a
|
||||
* UTSM <-> Linux guest transport. No native queue implementation or syscall
|
||||
* binding exists yet. All ABI fields use bounded fixed-width Deshab types;
|
||||
* pointers and implementation-owned kernel addresses never appear on wire.
|
||||
*/
|
||||
|
||||
#define UTSM_IPC_NATIVE_MAGIC 0x44534950u /* "DSIP" */
|
||||
#define UTSM_IPC_NATIVE_ABI_MAJOR 1u
|
||||
#define UTSM_IPC_NATIVE_ABI_MINOR 0u
|
||||
|
||||
#define UTSM_IPC_NATIVE_MESSAGE_BYTES 256u
|
||||
#define UTSM_IPC_NATIVE_PAYLOAD_BYTES 144u
|
||||
#define UTSM_IPC_NATIVE_MAX_CAPS 4u
|
||||
#define UTSM_IPC_NATIVE_MIN_DEPTH 2u
|
||||
#define UTSM_IPC_NATIVE_MAX_DEPTH 4096u
|
||||
#define UTSM_IPC_NATIVE_DEADLINE_NONE (~0ULL)
|
||||
|
||||
/*
|
||||
* The rights field requests or describes attenuation. Authority comes from
|
||||
* the calling task's kernel capability table; copying or editing this value
|
||||
* must never create authority.
|
||||
*/
|
||||
typedef struct utsm_ipc_native_cap {
|
||||
u64 object_id;
|
||||
u32 generation;
|
||||
u32 rights;
|
||||
} utsm_ipc_native_cap;
|
||||
|
||||
typedef enum utsm_ipc_native_right {
|
||||
UTSM_IPC_RIGHT_SEND = (1u << 0),
|
||||
UTSM_IPC_RIGHT_RECEIVE = (1u << 1),
|
||||
UTSM_IPC_RIGHT_CALL = (1u << 2),
|
||||
UTSM_IPC_RIGHT_REPLY = (1u << 3),
|
||||
UTSM_IPC_RIGHT_TRANSFER = (1u << 4),
|
||||
UTSM_IPC_RIGHT_DUPLICATE = (1u << 5),
|
||||
UTSM_IPC_RIGHT_MANAGE = (1u << 6),
|
||||
UTSM_IPC_RIGHT_WAIT = (1u << 7),
|
||||
UTSM_IPC_RIGHT_SIGNAL = (1u << 8)
|
||||
} utsm_ipc_native_right;
|
||||
|
||||
#define UTSM_IPC_RIGHT_ALL ((u32)((1u << 9) - 1u))
|
||||
|
||||
/*
|
||||
* Queue operations are carried in a u16 ABI field. CALL creates a transaction
|
||||
* ID; REPLY must name that transaction. WAIT/SIGNAL are queue-state events,
|
||||
* not substitutes for SEND/RECEIVE.
|
||||
*/
|
||||
typedef enum utsm_ipc_native_queue_op {
|
||||
UTSM_IPC_QUEUE_CREATE = 1,
|
||||
UTSM_IPC_QUEUE_DESTROY = 2,
|
||||
UTSM_IPC_QUEUE_SEND = 3,
|
||||
UTSM_IPC_QUEUE_RECEIVE = 4,
|
||||
UTSM_IPC_QUEUE_CALL = 5,
|
||||
UTSM_IPC_QUEUE_REPLY = 6,
|
||||
UTSM_IPC_QUEUE_WAIT = 7,
|
||||
UTSM_IPC_QUEUE_SIGNAL = 8,
|
||||
UTSM_IPC_QUEUE_QUERY = 9
|
||||
} utsm_ipc_native_queue_op;
|
||||
|
||||
#define UTSM_IPC_MSG_F_NONBLOCK (1u << 0)
|
||||
#define UTSM_IPC_MSG_F_MOVE_CAPS (1u << 1)
|
||||
#define UTSM_IPC_MSG_F_EXPECT_REPLY (1u << 2)
|
||||
#define UTSM_IPC_MSG_F_CANCEL (1u << 3)
|
||||
|
||||
typedef enum utsm_ipc_native_result {
|
||||
UTSM_IPC_NATIVE_OK = 0,
|
||||
UTSM_IPC_NATIVE_ERR_INVALID = -1,
|
||||
UTSM_IPC_NATIVE_ERR_ACCESS = -2,
|
||||
UTSM_IPC_NATIVE_ERR_STALE_CAP = -3,
|
||||
UTSM_IPC_NATIVE_ERR_FULL = -4,
|
||||
UTSM_IPC_NATIVE_ERR_EMPTY = -5,
|
||||
UTSM_IPC_NATIVE_ERR_CLOSED = -6,
|
||||
UTSM_IPC_NATIVE_ERR_TIMEOUT = -7,
|
||||
UTSM_IPC_NATIVE_ERR_CANCELLED = -8,
|
||||
UTSM_IPC_NATIVE_ERR_UNSUPPORTED = -38
|
||||
} utsm_ipc_native_result;
|
||||
|
||||
/*
|
||||
* Fixed 256-byte message. message_size must equal sizeof(this structure),
|
||||
* payload_length is bounded by PAYLOAD_BYTES, and capability_count is bounded
|
||||
* by MAX_CAPS. Receivers must reject nonzero reserved fields.
|
||||
*/
|
||||
typedef struct utsm_ipc_native_message {
|
||||
u32 magic;
|
||||
u16 abi_major;
|
||||
u16 abi_minor;
|
||||
|
||||
u16 operation; /* utsm_ipc_native_queue_op */
|
||||
u16 flags;
|
||||
u32 message_size;
|
||||
u64 transaction_id;
|
||||
|
||||
utsm_ipc_native_cap endpoint;
|
||||
u32 payload_length;
|
||||
u16 capability_count;
|
||||
u16 reserved0;
|
||||
|
||||
utsm_ipc_native_cap capabilities[UTSM_IPC_NATIVE_MAX_CAPS];
|
||||
u8 payload[UTSM_IPC_NATIVE_PAYLOAD_BYTES];
|
||||
} utsm_ipc_native_message;
|
||||
|
||||
/*
|
||||
* Fixed queue-control request for a future syscall dispatcher. depth is valid
|
||||
* only for CREATE and must stay within MIN_DEPTH..MAX_DEPTH. deadline_ns is
|
||||
* an absolute monotonic deadline or UTSM_IPC_NATIVE_DEADLINE_NONE.
|
||||
*/
|
||||
typedef struct utsm_ipc_native_queue_request {
|
||||
u16 abi_major;
|
||||
u16 abi_minor;
|
||||
u16 operation; /* utsm_ipc_native_queue_op */
|
||||
u16 flags;
|
||||
|
||||
u32 depth;
|
||||
u32 message_size;
|
||||
utsm_ipc_native_cap queue;
|
||||
u64 deadline_ns;
|
||||
} utsm_ipc_native_queue_request;
|
||||
|
||||
_Static_assert(sizeof(utsm_ipc_native_cap) == 16u,
|
||||
"native IPC capability ABI changed");
|
||||
_Static_assert(sizeof(utsm_ipc_native_message) ==
|
||||
UTSM_IPC_NATIVE_MESSAGE_BYTES,
|
||||
"native IPC message ABI changed");
|
||||
_Static_assert(sizeof(utsm_ipc_native_queue_request) == 40u,
|
||||
"native IPC queue request ABI changed");
|
||||
|
||||
#endif /* UTSM_IPC_NATIVE_H */
|
||||
@@ -0,0 +1,130 @@
|
||||
#ifndef UTSM_SCHED_H
|
||||
#define UTSM_SCHED_H
|
||||
|
||||
#include <utsm/types.h>
|
||||
|
||||
/*
|
||||
* Experimental SAS-R0-PCQ scheduler contract.
|
||||
*
|
||||
* This header reserves the first ABI shape for a future per-CPU O(1)
|
||||
* priority-bitmap scheduler. It is not connected to boot or context-switch
|
||||
* code. Structures begin with version/size where they may cross component
|
||||
* boundaries; existing fields must not be reordered and extensions append.
|
||||
*/
|
||||
|
||||
#define UTSM_SCHED_ABI_VERSION 1u
|
||||
#define UTSM_SCHED_PRIORITY_COUNT 64u
|
||||
#define UTSM_SCHED_INVALID_SLOT 0xffffffffu
|
||||
#define UTSM_SCHED_PRIORITY_HIGHEST 0u
|
||||
#define UTSM_SCHED_PRIORITY_LOWEST (UTSM_SCHED_PRIORITY_COUNT - 1u)
|
||||
|
||||
/* Explicit API results. The isolated stub returns UNSUPPORTED for every call. */
|
||||
typedef enum utsm_sched_result {
|
||||
UTSM_SCHED_OK = 0,
|
||||
UTSM_SCHED_ERR_INVALID = -1,
|
||||
UTSM_SCHED_ERR_STATE = -2,
|
||||
UTSM_SCHED_ERR_NO_TASK = -3,
|
||||
UTSM_SCHED_ERR_UNSUPPORTED = -38
|
||||
} utsm_sched_result;
|
||||
|
||||
/* Values are stored in u32 fields so the TCB layout does not depend on enum size. */
|
||||
typedef enum utsm_sched_task_state {
|
||||
UTSM_SCHED_TASK_UNUSED = 0,
|
||||
UTSM_SCHED_TASK_NEW,
|
||||
UTSM_SCHED_TASK_READY,
|
||||
UTSM_SCHED_TASK_RUNNING,
|
||||
UTSM_SCHED_TASK_BLOCKED,
|
||||
UTSM_SCHED_TASK_SLEEPING,
|
||||
UTSM_SCHED_TASK_RECOVERING,
|
||||
UTSM_SCHED_TASK_FAULTED,
|
||||
UTSM_SCHED_TASK_ZOMBIE
|
||||
} utsm_sched_task_state;
|
||||
|
||||
#define UTSM_SCHED_TASK_F_KERNEL (1u << 0)
|
||||
#define UTSM_SCHED_TASK_F_IDLE (1u << 1)
|
||||
#define UTSM_SCHED_TASK_F_NO_MIGRATE (1u << 2)
|
||||
#define UTSM_SCHED_TASK_F_RECOVERY (1u << 3)
|
||||
|
||||
/*
|
||||
* Minimum x86_64 resume context. Volatile registers belong to the eventual
|
||||
* interrupt/syscall frame and are deliberately not duplicated here.
|
||||
*/
|
||||
typedef struct utsm_sched_arch_context {
|
||||
u64 rsp;
|
||||
u64 rip;
|
||||
u64 rflags;
|
||||
u64 rbx;
|
||||
u64 rbp;
|
||||
u64 r12;
|
||||
u64 r13;
|
||||
u64 r14;
|
||||
u64 r15;
|
||||
} utsm_sched_arch_context;
|
||||
|
||||
/*
|
||||
* Task control block prefix.
|
||||
*
|
||||
* runq_prev/runq_next are task-table slots, not pointers, so queues remain
|
||||
* relocatable. priority 0 is highest. crypto_context is loaded as a pointer
|
||||
* during a future switch; switch code must not scan capabilities or perform
|
||||
* cryptography/checkpoint work.
|
||||
*/
|
||||
typedef struct utsm_sched_tcb {
|
||||
u32 abi_version;
|
||||
u32 struct_size;
|
||||
|
||||
u64 task_id;
|
||||
u32 process_slot;
|
||||
u32 flags;
|
||||
|
||||
u32 state; /* utsm_sched_task_state */
|
||||
u32 priority;
|
||||
u32 assigned_cpu;
|
||||
u32 last_cpu;
|
||||
u64 affinity_mask;
|
||||
|
||||
u64 timeslice_ns;
|
||||
u64 wake_deadline_ns;
|
||||
void *crypto_context;
|
||||
|
||||
u32 runq_prev;
|
||||
u32 runq_next;
|
||||
utsm_sched_arch_context arch;
|
||||
|
||||
u64 reserved[4];
|
||||
} utsm_sched_tcb;
|
||||
|
||||
/*
|
||||
* One ready bitmap and one intrusive FIFO per priority form the planned O(1)
|
||||
* run queue. A set bit means that head[priority] is valid. Synchronization is
|
||||
* an implementation concern and is intentionally not implied by this layout.
|
||||
*/
|
||||
typedef struct utsm_sched_run_queue {
|
||||
u32 abi_version;
|
||||
u32 struct_size;
|
||||
u32 cpu_id;
|
||||
u32 flags;
|
||||
|
||||
u64 ready_bitmap;
|
||||
u32 head[UTSM_SCHED_PRIORITY_COUNT];
|
||||
u32 tail[UTSM_SCHED_PRIORITY_COUNT];
|
||||
|
||||
u32 current_task_slot;
|
||||
u32 idle_task_slot;
|
||||
u32 ready_count;
|
||||
u32 reserved0;
|
||||
} utsm_sched_run_queue;
|
||||
|
||||
int utsm_sched_init(void);
|
||||
int utsm_sched_cpu_init(u32 cpu_id, utsm_sched_run_queue *queue);
|
||||
int utsm_sched_task_init(utsm_sched_tcb *task, u64 task_id,
|
||||
u32 process_slot, u32 priority, u64 affinity_mask);
|
||||
int utsm_sched_enqueue(u32 cpu_id, utsm_sched_tcb *task);
|
||||
int utsm_sched_dequeue(utsm_sched_tcb *task);
|
||||
int utsm_sched_block(utsm_sched_tcb *task, u32 blocked_state);
|
||||
int utsm_sched_wake(utsm_sched_tcb *task);
|
||||
int utsm_sched_pick_next(u32 cpu_id, utsm_sched_tcb **out_task);
|
||||
int utsm_sched_tick(u32 cpu_id, u64 elapsed_ns,
|
||||
utsm_sched_tcb **out_preempted_task);
|
||||
|
||||
#endif /* UTSM_SCHED_H */
|
||||
@@ -88,5 +88,7 @@ int vmm_self_test(void);
|
||||
/* VMM 状态查询 */
|
||||
int vmm_is_ready(void);
|
||||
u64 vmm_get_eptp(void);
|
||||
/* P8.5: VMCS 物理地址(linux_launch 前 VMCLEAR+VMPTRLD 用) */
|
||||
u64 vmm_get_vmcs_phys(void);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#define IA32_VMX_PROCBASED_CTLS 0x482
|
||||
#define IA32_VMX_EXIT_CTLS 0x483
|
||||
#define IA32_VMX_ENTRY_CTLS 0x484
|
||||
#define IA32_VMX_MISC 0x485
|
||||
#define IA32_VMX_PROCBASED_CTLS2 0x48B
|
||||
#define IA32_VMX_EPT_VPID_CAP 0x48C
|
||||
#define IA32_VMX_TRUE_PINBASED_CTLS 0x48D
|
||||
@@ -37,15 +38,9 @@
|
||||
|
||||
/* Control fields */
|
||||
#define VMCS_VPID 0x0000
|
||||
#define VMCS_GUEST_ES_ADDR 0x0006
|
||||
#define VMCS_GUEST_CS_ADDR 0x0008
|
||||
#define VMCS_GUEST_SS_ADDR 0x000A
|
||||
#define VMCS_GUEST_DS_ADDR 0x000C
|
||||
#define VMCS_GUEST_FS_ADDR 0x000E
|
||||
#define VMCS_GUEST_GS_ADDR 0x0010
|
||||
#define VMCS_GUEST_LDTR_ADDR 0x0012
|
||||
#define VMCS_GUEST_TR_ADDR 0x0014
|
||||
#define VMCS_GUEST_INTR_STATUS 0x0009
|
||||
/* P8.5: guest interrupt status 真实编码为 0x0810(16-bit guest-state),
|
||||
* 旧值 0x0009 是 posted-interrupt 描述符区域,属于错误编码。 */
|
||||
#define VMCS_GUEST_INTR_STATUS 0x0810
|
||||
#define VMCS_IO_BITMAP_A 0x2000
|
||||
#define VMCS_IO_BITMAP_B 0x2002
|
||||
#define VMCS_MSR_BITMAP 0x2004
|
||||
@@ -54,15 +49,22 @@
|
||||
|
||||
/* Read-only data fields */
|
||||
#define VMCS_GUEST_PHYSICAL_ADDR 0x2400
|
||||
#define VMCS_EXIT_INTR_INFO 0x4404
|
||||
#define VMCS_EXIT_INTR_ERROR_CODE 0x4406
|
||||
#define VMCS_EXIT_REASON 0x4402
|
||||
#define VMCS_EXIT_QUALIFICATION 0x6400
|
||||
#define VMCS_IO_RCX 0x6404
|
||||
#define VMCS_IO_RSI 0x6406
|
||||
#define VMCS_IO_RDI 0x6408
|
||||
#define VMCS_IO_RIP 0x640A
|
||||
/* P8.5: IO RCX/RSI/RDI/RIP 真实编码为 0x6402/0x6404/0x6406/0x6408,
|
||||
* 旧值整体右移了一格。guest-linear-address = 0x640A 不变。 */
|
||||
#define VMCS_IO_RCX 0x6402
|
||||
#define VMCS_IO_RSI 0x6404
|
||||
#define VMCS_IO_RDI 0x6406
|
||||
#define VMCS_IO_RIP 0x6408
|
||||
#define VMCS_GUEST_LINEAR_ADDR 0x640A
|
||||
#define VMCS_INSTRUCTION_LENGTH 0x440C
|
||||
#define VMCS_VMX_INSTRUCTION_ERROR 0x4406
|
||||
/* P8.5 关键修复:VM-instruction error 真实编码是 0x4400。
|
||||
* 旧值 0x4406 是 "VM-exit interruption error code",
|
||||
* 导致 vmlaunch 失败后错误码永远读出 0。 */
|
||||
#define VMCS_VMX_INSTRUCTION_ERROR 0x4400
|
||||
|
||||
/* Guest-state fields */
|
||||
#define VMCS_GUEST_ES_SELECTOR 0x0800
|
||||
@@ -82,8 +84,8 @@
|
||||
#define VMCS_GUEST_LDTR_LIMIT 0x480C
|
||||
#define VMCS_GUEST_TR_LIMIT 0x480E
|
||||
#define VMCS_GUEST_CS_ACCESS 0x4816
|
||||
#define VMCS_GUEST_SS_ACCESS 0x481A
|
||||
#define VMCS_GUEST_DS_ACCESS 0x4818
|
||||
#define VMCS_GUEST_SS_ACCESS 0x4818
|
||||
#define VMCS_GUEST_DS_ACCESS 0x481A
|
||||
#define VMCS_GUEST_ES_ACCESS 0x4814
|
||||
#define VMCS_GUEST_FS_ACCESS 0x481C
|
||||
#define VMCS_GUEST_GS_ACCESS 0x481E
|
||||
@@ -165,11 +167,13 @@
|
||||
#define VMCS_VM_EXIT_CONTROLS 0x400C
|
||||
#define VMCS_VM_ENTRY_CONTROLS 0x4012
|
||||
#define VMCS_VM_ENTRY_INTERRUPT_INFO 0x4016
|
||||
#define VMCS_VM_ENTRY_EXCEPTION_ERROR 0x4018
|
||||
#define VMCS_SECONDARY_VM_EXEC_CONTROL 0x401E
|
||||
|
||||
/* ===== Pin-based controls ===== */
|
||||
#define PIN_EXT_INTERRUPT_EXITING (1ULL << 0)
|
||||
#define PIN_NMI_EXITING (1ULL << 3)
|
||||
#define PIN_VIRTUAL_NMIS (1ULL << 5)
|
||||
#define PIN_VMX_PREEMPTION_TIMER (1ULL << 6)
|
||||
|
||||
/* ===== Primary processor-based controls ===== */
|
||||
@@ -269,11 +273,23 @@ int vmx_vmcs_load(u64 phys);
|
||||
int vmx_vmcs_clear(u64 phys); /* P8.4: VMCLEAR before VMLAUNCH */
|
||||
u64 vmx_vmcs_read(u64 field);
|
||||
void vmx_vmcs_write(u64 field, u64 value);
|
||||
/* P8.5: 带状态返回的 VMREAD。
|
||||
* 返回 0 成功(CF=0 且 ZF=0);-1 = VMfailValid/VMfailInvalid
|
||||
* (此时 *value 未更新,多为 current-VMCS 无效)。 */
|
||||
int vmx_vmcs_read_checked(u64 field, u64 *value);
|
||||
int vmx_vmlaunch(void);
|
||||
int vmx_vmresume(void);
|
||||
|
||||
u64 vmx_read_msr(u32 msr);
|
||||
/* Capability polarity used by KVM and verified under nested KVM:
|
||||
* low bit=1 => must be 1; high bit=0 => must be 0.
|
||||
* Formula: (desired | allowed0) & allowed1. */
|
||||
u64 vmx_adjust_control(u64 value, u32 msr);
|
||||
u64 vmx_get_host_cr3(void);
|
||||
/* Convert a pointer inside the linked UTSM kernel image to its physical
|
||||
* address using Limine's kernel-address response. Kernel-image mappings are
|
||||
* distinct from the HHDM and must never be translated by subtracting HHDM. */
|
||||
u64 vmx_kernel_virt_to_phys(const void *address);
|
||||
|
||||
/* ===== 虚拟中断注入(vmexit.c 实现) ===== */
|
||||
/* 向 guest 队列注入一个 ISA IRQ 向量(legacy PIC:vector = 0x30 + irq)。
|
||||
@@ -281,9 +297,9 @@ u64 vmx_get_host_cr3(void);
|
||||
* 否则 arm interrupt-window exiting,待 guest 可接收时注入。 */
|
||||
void vmx_guest_queue_irq(u32 vector);
|
||||
|
||||
/* VMX preemption timer:返回 1ms 对应的 timer 计数值(按 IA32_VMX_BASIC
|
||||
* bits[55:48] 的 scale 换算 TSC 频率)。 */
|
||||
u64 vmx_preemption_quantum_1ms(void);
|
||||
/* VMX preemption timer:返回 host polling 周期对应的 timer 计数值。
|
||||
* 当前周期为 10ms,避免 nested KVM 在 1ms VM-exit 下失去前进性。 */
|
||||
u64 vmx_preemption_quantum(void);
|
||||
/* 检查 CPU 是否支持 VMX preemption timer。 */
|
||||
int vmx_preemption_timer_supported(void);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user