2026-07-28 12:00:02 +08:00
|
|
|
/* ipc_shm.c — UTSM-side IPC shared memory manager.
|
|
|
|
|
*
|
|
|
|
|
* Allocates a 1MB contiguous physical region, initializes the shared
|
|
|
|
|
* utsm_ipc_shm structure (header + two ring buffers + payload pool),
|
|
|
|
|
* and EPT-maps it at LINUX_GUEST_IPC_SHM_GPA (0x04000000) so the Linux
|
|
|
|
|
* guest can access it after querying the GPA via UTSM_HCALL_SHM_INFO.
|
|
|
|
|
*
|
|
|
|
|
* Ring buffer discipline (single-producer, single-consumer):
|
|
|
|
|
* utsm_to_linux: UTSM produces via ipc_shm_send, Linux consumes
|
|
|
|
|
* linux_to_utsm: Linux produces, UTSM consumes via ipc_shm_recv
|
|
|
|
|
*
|
|
|
|
|
* Memory barriers ensure correct visibility across the two kernels
|
|
|
|
|
* (which share the same physical memory via EPT).
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <utsm/ipc_shm.h>
|
2026-10-03 21:26:04 +08:00
|
|
|
#include <utsm/panic.h>
|
2026-07-28 12:00:02 +08:00
|
|
|
#include <utsm/vmx.h>
|
|
|
|
|
#include <utsm/ept.h>
|
|
|
|
|
#include <utsm/log.h>
|
|
|
|
|
#include <utsm/types.h>
|
|
|
|
|
#include <utsm/dma.h>
|
|
|
|
|
#include <utsm/linux_loader.h>
|
|
|
|
|
#include "../arch/x86_64/limine.h"
|
|
|
|
|
|
|
|
|
|
extern volatile struct limine_hhdm_request g_hhdm_request;
|
|
|
|
|
|
|
|
|
|
/* ===== Internal state ===== */
|
|
|
|
|
|
|
|
|
|
static dkm_dma_buffer g_shm_buf; /* allocated DMA buffer for the SHM region */
|
|
|
|
|
static struct utsm_ipc_shm *g_shm; /* virtual address (HHDM) of the SHM region */
|
|
|
|
|
static int g_shm_ready;
|
|
|
|
|
static u32 g_send_seq; /* monotonic sequence for UTSM→Linux messages */
|
|
|
|
|
static u64 g_send_count;
|
|
|
|
|
static u64 g_recv_count;
|
|
|
|
|
|
|
|
|
|
/* ===== Initialization ===== */
|
|
|
|
|
|
|
|
|
|
int ipc_shm_init(void) {
|
|
|
|
|
if (g_shm_ready) {
|
|
|
|
|
log_warn("[IPC] shm already initialized");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log_info("[IPC] shm init begin");
|
|
|
|
|
|
|
|
|
|
/* 1. Allocate 1MB contiguous physical memory for the shared region.
|
|
|
|
|
* Alignment: 4KB (page-aligned). Max phys: none (EPT maps any HPA). */
|
|
|
|
|
u64 page_count = UTSM_IPC_SHM_SIZE / 4096;
|
|
|
|
|
if (dma_alloc_pages(page_count, 4096, 0, &g_shm_buf) != 0) {
|
|
|
|
|
log_error("[IPC] failed to alloc shared memory (1MB)");
|
2026-10-03 21:26:04 +08:00
|
|
|
panic_full("IPC-E01 ALLOC SHARED MEMORY FAILED", "dma_alloc_pages failed for the 1mb ipc shared memory region", 0);
|
2026-07-28 12:00:02 +08:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
log_hex64("[IPC] shm HPA=", g_shm_buf.phys);
|
|
|
|
|
log_hex64("[IPC] shm virt=", (u64)g_shm_buf.virt);
|
|
|
|
|
log_hex64("[IPC] shm size=", g_shm_buf.size);
|
|
|
|
|
|
|
|
|
|
/* 2. Zero the entire region */
|
|
|
|
|
u8 *p = (u8 *)g_shm_buf.virt;
|
|
|
|
|
for (u64 i = 0; i < g_shm_buf.size; i++) {
|
|
|
|
|
p[i] = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 3. Initialize the shared memory structure */
|
|
|
|
|
g_shm = (struct utsm_ipc_shm *)g_shm_buf.virt;
|
|
|
|
|
|
|
|
|
|
/* Header */
|
|
|
|
|
g_shm->header.magic = UTSM_IPC_MAGIC;
|
|
|
|
|
g_shm->header.version = UTSM_IPC_VERSION;
|
|
|
|
|
g_shm->header.header_size = sizeof(struct utsm_ipc_shm_header);
|
|
|
|
|
g_shm->header.shm_phys = LINUX_GUEST_IPC_SHM_GPA; /* GPA as seen by Linux */
|
|
|
|
|
g_shm->header.shm_size = UTSM_IPC_SHM_SIZE;
|
|
|
|
|
g_shm->header.utsm_ready = 1; /* UTSM is ready now */
|
|
|
|
|
g_shm->header.linux_ready = 0; /* Linux will set this when it connects */
|
|
|
|
|
|
|
|
|
|
/* Ring buffers */
|
|
|
|
|
utsm_ipc_ring_init(&g_shm->utsm_to_linux);
|
|
|
|
|
utsm_ipc_ring_init(&g_shm->linux_to_utsm);
|
|
|
|
|
|
|
|
|
|
/* Memory barrier: ensure all writes are visible before EPT mapping */
|
|
|
|
|
utsm_ipc_mb();
|
|
|
|
|
|
|
|
|
|
/* 4. EPT-map the shared memory at the guest-visible GPA.
|
|
|
|
|
* Both kernels need read/write access. Execute is not needed. */
|
|
|
|
|
if (ept_map_range(LINUX_GUEST_IPC_SHM_GPA, g_shm_buf.phys,
|
|
|
|
|
UTSM_IPC_SHM_SIZE, EPT_READ | EPT_WRITE) != 0) {
|
|
|
|
|
log_error("[IPC] EPT map shared memory failed");
|
2026-10-03 21:26:04 +08:00
|
|
|
panic_full("IPC-E02 EPT MAP SHARED MEMORY FAILED", "ept_map_range failed for the ipc shared memory region", 0);
|
2026-07-28 12:00:02 +08:00
|
|
|
return -2;
|
|
|
|
|
}
|
|
|
|
|
log_hex64("[IPC] shm mapped GPA=", LINUX_GUEST_IPC_SHM_GPA);
|
|
|
|
|
|
|
|
|
|
g_shm_ready = 1;
|
|
|
|
|
g_send_seq = 0;
|
|
|
|
|
g_send_count = 0;
|
|
|
|
|
g_recv_count = 0;
|
|
|
|
|
|
|
|
|
|
log_info("[IPC] shm init ok");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ipc_shm_shutdown(void) {
|
|
|
|
|
if (!g_shm_ready) return;
|
|
|
|
|
g_shm->header.utsm_ready = 0;
|
|
|
|
|
utsm_ipc_mb();
|
|
|
|
|
g_shm_ready = 0;
|
|
|
|
|
log_info("[IPC] shm shutdown");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int ipc_shm_is_ready(void) {
|
|
|
|
|
return g_shm_ready;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
u64 ipc_shm_get_hpa(void) {
|
|
|
|
|
return g_shm_ready ? g_shm_buf.phys : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
u64 ipc_shm_get_gpa(void) {
|
|
|
|
|
return LINUX_GUEST_IPC_SHM_GPA;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
u64 ipc_shm_get_size(void) {
|
|
|
|
|
return UTSM_IPC_SHM_SIZE;
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-31 13:53:49 +08:00
|
|
|
/* ===== Payload pool access ===== */
|
|
|
|
|
|
|
|
|
|
void *ipc_shm_payload_ptr(u32 offset) {
|
|
|
|
|
if (!g_shm_ready || !g_shm) return (void *)0;
|
|
|
|
|
if (offset >= UTSM_IPC_PAYLOAD_SIZE) return (void *)0;
|
|
|
|
|
return (void *)(g_shm->payload_pool + offset);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
u32 ipc_shm_payload_capacity(void) {
|
|
|
|
|
return UTSM_IPC_PAYLOAD_SIZE;
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-28 12:00:02 +08:00
|
|
|
/* ===== Message API (UTSM-side) ===== */
|
|
|
|
|
|
|
|
|
|
int ipc_shm_send(u32 msg_type, const void *data, u32 data_len) {
|
|
|
|
|
if (!g_shm_ready || !g_shm) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Check if Linux is ready to receive */
|
|
|
|
|
if (!g_shm->header.linux_ready) {
|
|
|
|
|
log_warn("[IPC] send: Linux not ready, dropping message");
|
2026-10-03 21:26:04 +08:00
|
|
|
panic_full("IPC-E03 SEND WITH LINUX NOT READY", "message dropped because the linux side never became ready", 0);
|
2026-07-28 12:00:02 +08:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Cap data length to inline buffer size */
|
|
|
|
|
if (data_len > UTSM_IPC_MSG_DATA_SIZE) {
|
|
|
|
|
data_len = UTSM_IPC_MSG_DATA_SIZE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Build the message */
|
|
|
|
|
struct utsm_ipc_msg msg;
|
|
|
|
|
msg.type = msg_type;
|
|
|
|
|
msg.seq = g_send_seq++;
|
|
|
|
|
msg.data_len = data_len;
|
|
|
|
|
msg.flags = 0;
|
|
|
|
|
|
|
|
|
|
/* Copy data if present */
|
|
|
|
|
if (data && data_len > 0) {
|
|
|
|
|
const u8 *src = (const u8 *)data;
|
|
|
|
|
for (u32 i = 0; i < data_len; i++) {
|
|
|
|
|
msg.data[i] = src[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Enqueue into utsm_to_linux ring */
|
|
|
|
|
if (utsm_ipc_ring_push(&g_shm->utsm_to_linux, &msg) != 0) {
|
|
|
|
|
log_warn("[IPC] send: ring full, dropping message");
|
2026-10-03 21:26:04 +08:00
|
|
|
panic_full("IPC-E04 SEND RING FULL", "message dropped because the utsm to linux ring is full", 0);
|
2026-07-28 12:00:02 +08:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_send_count++;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int ipc_shm_recv(struct utsm_ipc_msg *msg_out) {
|
|
|
|
|
if (!g_shm_ready || !g_shm || !msg_out) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (utsm_ipc_ring_pop(&g_shm->linux_to_utsm, msg_out) != 0) {
|
|
|
|
|
return -1; /* empty */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_recv_count++;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int ipc_shm_has_pending(void) {
|
|
|
|
|
if (!g_shm_ready || !g_shm) return 0;
|
|
|
|
|
return !utsm_ipc_ring_empty(&g_shm->linux_to_utsm);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ipc_shm_notify_linux(void) {
|
|
|
|
|
/* Phase 1.3: log only.
|
|
|
|
|
* Phase 1.4+: inject a guest IRQ (via VMCS VM-entry interruption info)
|
|
|
|
|
* to notify Linux that a message is waiting in the utsm_to_linux ring. */
|
|
|
|
|
log_info("[IPC] notify_linux (IPI injection not yet implemented)");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ===== Debug ===== */
|
|
|
|
|
|
|
|
|
|
void ipc_shm_dump_stats(void) {
|
|
|
|
|
if (!g_shm_ready) {
|
|
|
|
|
log_info("[IPC] not initialized");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log_hex64("[IPC] utsm_ready=", g_shm->header.utsm_ready);
|
|
|
|
|
log_hex64("[IPC] linux_ready=", g_shm->header.linux_ready);
|
|
|
|
|
log_hex64("[IPC] utsm_to_linux head=", g_shm->utsm_to_linux.head);
|
|
|
|
|
log_hex64("[IPC] utsm_to_linux tail=", g_shm->utsm_to_linux.tail);
|
|
|
|
|
log_hex64("[IPC] linux_to_utsm head=", g_shm->linux_to_utsm.head);
|
|
|
|
|
log_hex64("[IPC] linux_to_utsm tail=", g_shm->linux_to_utsm.tail);
|
|
|
|
|
log_hex64("[IPC] send_count=", g_send_count);
|
|
|
|
|
log_hex64("[IPC] recv_count=", g_recv_count);
|
|
|
|
|
}
|