This commit is contained in:
xyou
2026-07-28 12:00:02 +08:00
parent efa626ed17
commit 17a577f2ed
51 changed files with 8446 additions and 787 deletions
+114
View File
@@ -0,0 +1,114 @@
# Linux 6.6 LTS for UTSM Dual-Kernel Architecture
## Overview
This directory contains the Linux 6.6 LTS configuration and build scripts for the
UTSM + Linux dual-kernel architecture. Linux runs as a VMX non-root guest under
the UTSM hypervisor (VMX root).
## Prerequisites
- **WSL** (Windows Subsystem for Linux) with Ubuntu 22.04+ installed
```powersShell
wsl --install -d Ubuntu-22.04
```
- Build dependencies (inside WSL):
```bash
sudo apt update
sudo apt install -y build-essential bc flex bison libelf-dev libssl-dev cpio gzip git
```
- ~3GB free disk space
## Building Linux
### 1. From WSL
```bash
# Navigate to the project (adjust path for your WSL mount)
cd /mnt/d/Code/Deshab/CODE/linux
# Make the build script executable
chmod +x build.sh
# Run the build (clones Linux 6.6 LTS, configures, builds)
./build.sh
```
The build will:
1. Clone Linux 6.6 LTS source to `CODE/linux/src/`
2. Apply UTSM patches from `CODE/linux/patches/` (if any)
3. Configure with `configs/utsm_x86_64_defconfig`
4. Build `bzImage`
5. Copy to `SYSTEM/boot/linux-bzImage`
6. Build initramfs if `CODE/linux/initramfs/` exists
### 2. Enable in Limine
After building, uncomment the Linux module entries in `SYSTEM/limine/limine.conf`:
```
module_path: boot():/boot/linux-bzImage
module_cmdline: linux:bzImage
module_path: boot():/boot/linux-initrd.img
module_cmdline: linux:initrd
```
### 3. Rebuild the disk image
```PowerShell
cd d:\Code\Deshab
.\build.bat
```
### 4. Test with QEMU
**Note:** VMX requires hardware virtualization support. QEMU's TCG (software
emulation) does NOT support VMX instructions. To test the VMX path:
- **Real hardware**: Boot on an Intel machine with VT-x enabled in BIOS
- **QEMU with WHPX**: `qemu-system-x86_64 -accel whpx -cpu max` (may not expose
nested VMX to the guest)
- **QEMU with KVM** (Linux host only): `qemu-system-x86_64 -enable-kvm -cpu host`
Without VMX, UTSM gracefully falls back to the normal DSK boot flow.
## Configuration Details
The `utsm_x86_64_defconfig` enables:
- 64-bit kernel with SMP support
- Serial console (8250/16550 at 0x3F8, 115200 baud)
- virtio: console, block, net, fs (for UTSM host backends)
- ext4, FAT, tmpfs, proc, sysfs filesystems
- No module loading (all drivers built-in for Phase 1)
- No EFI stub (UTSM uses custom VMX boot protocol)
- Debug info and printk timestamps
## File Structure
```
CODE/linux/
├── configs/
│ └── utsm_x86_64_defconfig # Kernel configuration
├── patches/ # UTSM-specific kernel patches
│ ├── utsm-hcall.patch # /dev/utsm hypercall driver (Phase 1.3)
│ ├── utsm-ipc.patch # Shared memory IPC driver (Phase 1.3)
│ └── utsm-virtio.patch # virtio enhancements (Phase 1.4+)
├── initramfs/ # Minimal initramfs contents (optional)
├── build.sh # Build script (run in WSL/Linux)
└── README.md # This file
```
## Boot Flow
```
Limine (UEFI)
→ utsm.elf (VMX root)
→ vmm_init() + vmm_self_test()
→ linux_loader_init() (parse bzImage, set up boot_params, EPT)
→ linux_launch() (VMCS config + vmlaunch)
→ Linux 6.6 startup_64 (VMX non-root)
→ Serial console output
→ virtio-console ↔ UTSM
→ virtio-fs mount /utsm
→ User space (init/shell)
```
+134
View File
@@ -0,0 +1,134 @@
#!/bin/bash
#
# Build Linux 6.6 LTS bzImage for UTSM dual-kernel guest.
#
# Prerequisites:
# - Linux build environment (WSL, native Linux, or Docker)
# - gcc, make, bc, flex, bison, libelf-dev, libssl-dev
# - ~3GB free disk space
#
# Usage:
# cd CODE/linux
# ./build.sh
#
# Output:
# - SYSTEM/boot/linux-bzImage (kernel image)
# - SYSTEM/boot/linux-initrd.img (minimal initramfs, if initramfs/ exists)
set -e
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
LINUX_SRC="${LINUX_SRC:-$SCRIPT_DIR/src}"
OUTPUT_DIR="$PROJECT_ROOT/SYSTEM/boot"
CONFIG_FILE="$SCRIPT_DIR/configs/utsm_x86_64_defconfig"
INITRAMFS_DIR="$SCRIPT_DIR/initramfs"
echo "[build] Linux kernel build for UTSM"
echo "[build] Project root: $PROJECT_ROOT"
echo "[build] Linux source: $LINUX_SRC"
# 1. Clone Linux 6.6 LTS if not present
if [ ! -d "$LINUX_SRC" ]; then
echo "[build] Cloning Linux 6.6 LTS..."
git clone --depth 1 --branch v6.6 \
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git \
"$LINUX_SRC"
fi
cd "$LINUX_SRC"
# 2. Apply UTSM patches if any
PATCH_DIR="$SCRIPT_DIR/patches"
if [ -d "$PATCH_DIR" ] && [ "$(ls -A $PATCH_DIR 2>/dev/null)" ]; then
echo "[build] Applying UTSM patches..."
# 2a. Apply .patch files (if any)
for patch in "$PATCH_DIR"/*.patch; do
if [ -f "$patch" ]; then
echo "[build] applying $(basename $patch)"
git apply --check "$patch" 2>/dev/null && git apply "$patch" || \
echo "[build] WARNING: $patch already applied or failed"
fi
done
# 2b. Install UTSM driver source files into drivers/utsm/
UTSM_DRV_DIR="$LINUX_SRC/drivers/utsm"
echo "[build] installing UTSM driver into drivers/utsm/"
mkdir -p "$UTSM_DRV_DIR/include"
# Copy driver source, Kconfig, and Makefile
cp "$PATCH_DIR/utsm_hcall.c" "$UTSM_DRV_DIR/utsm_hcall.c"
cp "$PATCH_DIR/utsm_Kconfig" "$UTSM_DRV_DIR/Kconfig"
cp "$PATCH_DIR/utsm_Makefile" "$UTSM_DRV_DIR/Makefile"
# Copy shared IPC protocol header (included by both UTSM and Linux)
cp "$PROJECT_ROOT/CODE/utsm-ipc/ipc_proto.h" "$UTSM_DRV_DIR/include/ipc_proto.h"
# Add UTSM to drivers/Kconfig (if not already present)
if ! grep -q "drivers/utsm/Kconfig" "$LINUX_SRC/drivers/Kconfig"; then
echo '[build] adding source "drivers/utsm/Kconfig" to drivers/Kconfig'
echo 'source "drivers/utsm/Kconfig"' >> "$LINUX_SRC/drivers/Kconfig"
fi
# Add UTSM to drivers/Makefile (if not already present)
if ! grep -q "CONFIG_UTSM_HCALL" "$LINUX_SRC/drivers/Makefile"; then
echo '[build] adding obj-$(CONFIG_UTSM_HCALL) += utsm/ to drivers/Makefile'
echo 'obj-$(CONFIG_UTSM_HCALL) += utsm/' >> "$LINUX_SRC/drivers/Makefile"
fi
echo "[build] UTSM driver installed:"
echo "[build] $UTSM_DRV_DIR/utsm_hcall.c"
echo "[build] $UTSM_DRV_DIR/Kconfig"
echo "[build] $UTSM_DRV_DIR/Makefile"
echo "[build] $UTSM_DRV_DIR/include/ipc_proto.h"
fi
# 3. Configure
echo "[build] Configuring kernel..."
cp "$CONFIG_FILE" .config
make olddefconfig
# 4. Build bzImage
echo "[build] Building bzImage (this may take 10-30 minutes)..."
make -j"$(nproc)" bzImage
# 5. Copy bzImage to SYSTEM/boot/
mkdir -p "$OUTPUT_DIR"
cp arch/x86/boot/bzImage "$OUTPUT_DIR/linux-bzImage"
echo "[build] bzImage: $OUTPUT_DIR/linux-bzImage ($(du -h $OUTPUT_DIR/linux-bzImage | cut -f1))"
# 6. Build initramfs if initramfs/ directory exists
if [ -d "$INITRAMFS_DIR" ]; then
echo "[build] Building initramfs..."
# Create a minimal initramfs with busybox or a simple init
INITRAMFS_TMP="$SCRIPT_DIR/initramfs.tmp"
rm -rf "$INITRAMFS_TMP"
mkdir -p "$INITRAMFS_TMP"
# Copy initramfs contents
cp -a "$INITRAMFS_DIR"/* "$INITRAMFS_TMP"/
# Create init script if not present
if [ ! -f "$INITRAMFS_TMP/init" ]; then
cat > "$INITRAMFS_TMP/init" <<'INITEOF'
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs none /dev
echo "[utsm-linux] Boot complete"
echo "[utsm-linux] Console ready on /dev/hvc0 (virtio-console)"
exec /bin/sh
INITEOF
chmod +x "$INITRAMFS_TMP/init"
fi
# Pack initramfs
cd "$INITRAMFS_TMP"
find . | cpio -H newc -o | gzip -9 > "$OUTPUT_DIR/linux-initrd.img"
echo "[build] initrd: $OUTPUT_DIR/linux-initrd.img ($(du -h $OUTPUT_DIR/linux-initrd.img | cut -f1))"
cd "$LINUX_SRC"
fi
echo "[build] Done. Copy linux-bzImage (and linux-initrd.img) to your FAT32 image."
echo "[build] Limine will load them as boot modules for UTSM."
+231
View File
@@ -0,0 +1,231 @@
#
# Linux 6.6 LTS minimal config for UTSM dual-kernel guest
#
# Build: make utsm_x86_64_defconfig && make -j$(nproc) bzImage
#
# Key design choices:
# - 64-bit only (CONFIG_64BIT=y)
# - Serial console (8250/16550 at 0x3F8)
# - virtio: console, block, net, fs (for UTSM host backends)
# - EFI stub disabled (UTSM uses custom VMX boot, not EFI)
# - Built-in drivers (no module loading needed for Phase 1)
# - No SMP (single CPU for Phase 1 simplicity)
# - printk to serial for debugging
CONFIG_64BIT=y
CONFIG_X86_64=y
CONFIG_X86=y
CONFIG_INSTRUCTION_DECODER=y
CONFIG_OUTPUT_FORMAT="elf64-x86-64"
CONFIG_ARCH_MMAP_RND_BITS_MIN=28
CONFIG_ARCH_MMAP_RND_BITS_MAX=32
CONFIG_GENERIC_ISA_DMA=y
CONFIG_GENERIC_BUG=y
CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y
CONFIG_GENERIC_HWEIGHT=y
CONFIG_ARCH_MMAP_RND_BITS=28
CONFIG_SMP=y
CONFIG_NR_CPUS=4
CONFIG_X86_64_SMP=y
CONFIG_SCHED_MC=y
CONFIG_SCHED_SMT=y
# Processor type
CONFIG_MK8=y
CONFIG_X86_CPU=y
CONFIG_X86_L1_CACHE_SHIFT=6
CONFIG_X86_TSC=y
CONFIG_X86_CMPXCHG64=y
CONFIG_X86_CMOV=y
CONFIG_X86_MINIMUM_CPU_FAMILY=64
CONFIG_PROCESSOR_SELECT=y
CONFIG_CPU_SUP_INTEL=y
CONFIG_CPU_SUP_AMD=y
CONFIG_CPU_SUP_CENTAUR=y
CONFIG_HPET_TIMER=y
CONFIG_HPET_EMULATE_RTC=y
CONFIG_DMI=y
# Memory
CONFIG_PHYS_ADDR_T_64BIT=y
CONFIG_ARCH_PHYS_ADDR_T_64BIT=y
CONFIG_ARCH_DMA_ADDR_T_64BIT=y
CONFIG_DIRECT_GBPAGES=y
CONFIG_ARCH_SPARSEMEM_ENABLE=y
CONFIG_ARCH_SPARSEMEM_DEFAULT=y
CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000
CONFIG_X86_PMEM_LEGACY_DEVICE=y
CONFIG_X86_PMEM_LEGACY=y
CONFIG_X86_CHECK_BIOS_CORRUPTION=y
CONFIG_X86_BOOTPARAM_MEMORY_CORRUPTION_CHECK=y
CONFIG_X86_RESERVE_LOW=64
CONFIG_MTRR=y
CONFIG_MTRR_SANITIZER=y
CONFIG_MTRR_SANITIZER_ENABLE_DEFAULT=0
CONFIG_X86_PAT=y
CONFIG_ARCH_USES_PG_UNCACHED=y
CONFIG_ARCH_RANDOM=y
CONFIG_X86_SMAP=y
CONFIG_X86_INTEL_UMIP=y
CONFIG_X86_UMIP=y
# Power management (minimal)
CONFIG_PM=y
CONFIG_PM_CLK=y
# Bus options
CONFIG_PCI=y
CONFIG_PCI_DIRECT=y
CONFIG_PCI_MMCONFIG=y
CONFIG_PCI_DOMAINS=y
CONFIG_PCI_MSI=y
CONFIG_PCI_MSI_IRQ_DOMAIN=y
CONFIG_PCI_LABEL=y
CONFIG_ISA_DMA_API=y
CONFIG_AMD_NB=y
#Executable file formats
CONFIG_BINFMT_ELF=y
CONFIG_BINFMT_SCRIPT=y
CONFIG_BINFMT_MISC=y
# Networking
CONFIG_NET=y
CONFIG_PACKET=y
CONFIG_UNIX=y
CONFIG_INET=y
CONFIG_IP_MULTICAST=y
CONFIG_IP_ADVANCED_ROUTER=y
CONFIG_IP_PNP=y
CONFIG_IP_PNP_DHCP=y
CONFIG_SYN_COOKIES=y
CONFIG_TCP_CONG_CUBIC=y
# Device Drivers
# Serial console (critical: UTSM captures serial output)
CONFIG_SERIAL_8250=y
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_8250_PCI=y
CONFIG_SERIAL_8250_NR_UARTS=4
CONFIG_SERIAL_8250_RUNTIME_UARTS=4
CONFIG_SERIAL_CORE=y
CONFIG_SERIAL_CORE_CONSOLE=y
CONFIG_EARLY_PRINTK=y
CONFIG_EARLY_PRINTK_SERIAL=y
# PCI host bridge
CONFIG_PCI_HOST_GENERIC=y
# virtio (critical: UTSM provides virtio backends)
CONFIG_VIRTIO=y
CONFIG_VIRTIO_PCI=y
CONFIG_VIRTIO_PCI_LEGACY=y
CONFIG_VIRTIO_BALLOON=y
CONFIG_VIRTIO_INPUT=y
CONFIG_VIRTIO_MMIO=y
CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES=y
# virtio console (Phase 1.4: UTSM↔Linux console)
CONFIG_VIRTIO_CONSOLE=y
CONFIG_HW_RANDOM=y
CONFIG_HW_RANDOM_VIRTIO=y
# UTSM dual-kernel hypercall and IPC driver (Phase 1.3)
CONFIG_UTSM_HCALL=y
# virtio block (Phase 2: block device)
CONFIG_VIRTIO_BLK=y
CONFIG_VIRTIO_BLK_SCSI=y
# virtio net (Phase 2: network)
CONFIG_VIRTIO_NET=y
# virtio-fs (Phase 1.5: shared filesystem)
CONFIG_VIRTIO_FS=y
CONFIG_FUSE_FS=y
# Filesystems
CONFIG_EXT4_FS=y
CONFIG_EXT4_USE_FOR_EXT2=y
CONFIG_FAT_FS=y
CONFIG_VFAT_FS=y
CONFIG_FAT_DEFAULT_CODEPAGE=437
CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
CONFIG_TMPFS=y
CONFIG_PROC_FS=y
CONFIG_PROC_SYSCTL=y
CONFIG_SYSFS=y
CONFIG_DEVTMPFS=y
CONFIG_DEVTMPFS_MOUNT=y
# Block devices
CONFIG_BLK_DEV=y
CONFIG_BLK_DEV_LOOP=y
# Input (minimal)
CONFIG_INPUT=y
CONFIG_INPUT_KEYBOARD=y
# RTC
CONFIG_RTC_LIB=y
CONFIG_RTC_CLASS=y
CONFIG_RTC_HCTOSYS=y
CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
# PNP/ACPI (minimal for QEMU)
CONFIG_ACPI=y
CONFIG_ACPI_AC=y
CONFIG_ACPI_BUTTON=y
CONFIG_ACPI_FAN=y
CONFIG_ACPI_PROCESSOR=y
CONFIG_ACPI_THERMAL=y
# File systems
CONFIG_FHANDLE=y
# Kernel hacking
CONFIG_PRINTK=y
CONFIG_PRINTK_TIME=y
CONFIG_CONSOLE_LOGLEVEL_DEFAULT=7
CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y
CONFIG_FRAME_WARN=2048
CONFIG_MAGIC_SYSRQ=y
CONFIG_DEBUG_KERNEL=y
CONFIG_DEBUG_RODATA=y
CONFIG_DEBUG_SET_MODULE_RONX=y
CONFIG_DEBUG_WX=y
# Security (minimal)
CONFIG_SECURITY=y
CONFIG_SECURITYFS=y
# Crypto (minimal)
CONFIG_CRYPTO=y
CONFIG_CRYPTO_CRC32C=y
CONFIG_CRYPTO_SHA256=y
# Library routines
CONFIG_CRC16=y
CONFIG_CRC32=y
CONFIG_ZLIB_INFLATE=y
CONFIG_ZLIB_DEFLATE=y
CONFIG_LZO_COMPRESS=y
CONFIG_LZO_DECOMPRESS=y
CONFIG_XZ_DEC=y
CONFIG_XZ_DEC_X86=y
# Initramfs
CONFIG_BLK_DEV_INITRD=y
CONFIG_INITRAMFS_SOURCE=""
# No module support (build everything in for Phase 1)
# CONFIG_MODULES is not set
# No EFI stub (UTSM uses custom VMX boot)
# CONFIG_EFI_STUB is not set
# CONFIG_EFI is not set
# Boot protocol
CONFIG_EARLY_PRINTK_SERIAL=y
+33
View File
@@ -0,0 +1,33 @@
# SPDX-License-Identifier: GPL-2.0
#
# UTSM dual-kernel hypercall driver configuration
#
config UTSM_HCALL
tristate "UTSM dual-kernel hypercall and IPC driver"
default y
help
This driver enables communication between the Linux guest and
the UTSM monitor kernel in the Deshab dual-kernel architecture.
When loaded, it:
- Probes for the UTSM monitor via a VMCALL PING hypercall
- Maps the IPC shared memory ring buffer
- Registers /dev/utsm for userspace IPC message passing
If running on bare metal or a non-UTSM hypervisor, the driver
loads in passive mode (no /dev/utsm registered).
If unsure, say Y for UTSM guest builds, N otherwise.
config UTSM_EARLY_CONSOLE
bool "Redirect kernel printk to UTSM serial console"
depends on UTSM_HCALL
default n
help
Redirects Linux kernel console output to the UTSM monitor's
serial port via UTSM_HCALL_CONSOLE_WRITE hypercalls. Useful
for early boot debugging when the guest's own serial is not
configured.
If unsure, say N.
+10
View File
@@ -0,0 +1,10 @@
# SPDX-License-Identifier: GPL-2.0
#
# UTSM dual-kernel driver Makefile
#
obj-$(CONFIG_UTSM_HCALL) += utsm_hcall.o
# Include the shared IPC protocol header from CODE/utsm-ipc/
# This path is set up by build.sh when copying files into the source tree
ccflags-y += -I$(src)/include
+369
View File
@@ -0,0 +1,369 @@
// SPDX-License-Identifier: GPL-2.0
/*
* UTSM hypercall driver — Linux guest side.
*
* Provides:
* 1. VMCALL hypercall interface to communicate with the UTSM monitor kernel
* 2. /dev/utsm character device for userspace IPC message passing
* 3. Shared memory ring buffer mapping (queried via UTSM_HCALL_SHM_INFO)
*
* The driver is platform-agnostic: it probes for UTSM by issuing a PING
* hypercall at init. If UTSM is not present (e.g. running on bare metal
* or a different hypervisor), the driver exits gracefully.
*
* Build: configured via CONFIG_UTSM_HCALL (built-in or module).
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/mm.h>
/* Shared IPC protocol header (included via build.sh -I flag) */
#include <ipc_proto.h>
#define UTSM_DEV_NAME "utsm"
/* ===== Hypercall ABI =====
*
* Linux calls VMCALL with:
* RAX = UTSM_HCALL_MAGIC | op
* RDI = arg0, RSI = arg1, RDX = arg2
*
* UTSM handles the VMCALL VM-Exit and returns:
* RAX = return value
*
* Since we run in the guest, the VMCALL instruction triggers a VM-Exit
* to UTSM. The inline assembly must use the same register conventions.
*/
static inline long utsm_hcall(u64 op, u64 a0, u64 a1, u64 a2)
{
long ret;
register u64 rax asm("rax") = UTSM_HCALL_MAGIC | op;
register u64 rdi asm("rdi") = a0;
register u64 rsi asm("rsi") = a1;
register u64 rdx asm("rdx") = a2;
asm volatile("vmcall"
: "+r"(rax)
: "r"(rdi), "r"(rsi), "r"(rdx)
: "memory");
return (long)rax;
}
static inline long utsm_hcall_ping(void)
{
return utsm_hcall(UTSM_HCALL_PING, 0, 0, 0);
}
static inline long utsm_hcall_hello(void)
{
return utsm_hcall(UTSM_HCALL_HELLO, 0, 0, 0);
}
static inline long utsm_hcall_shm_info(u64 *gpa_out, u64 *size_out)
{
u64 gpa = 0, size = 0;
long ret;
/* Pass stack addresses (GVA=GPA due to identity mapping in early boot).
* UTSM translates GPA→HPA via EPT walk and writes the values there. */
ret = utsm_hcall(UTSM_HCALL_SHM_INFO, (u64)&gpa, (u64)&size, 0);
if (ret == 0) {
*gpa_out = gpa;
*size_out = size;
}
return ret;
}
static inline long utsm_hcall_console_write(const char *buf, u64 len)
{
return utsm_hcall(UTSM_HCALL_CONSOLE_WRITE, (u64)buf, len, 0);
}
/* ===== Shared memory state ===== */
static struct utsm_ipc_shm *g_shm; /* kernel virtual address of SHM */
static u64 g_shm_gpa; /* guest physical address of SHM */
static u64 g_shm_size; /* size of SHM region */
static int g_utsm_present; /* 1 = UTSM detected */
static int g_shm_mapped;
/* ===== /dev/utsm character device ===== */
static ssize_t utsm_dev_write(struct file *f, const char __user *buf,
size_t len, loff_t *off)
{
struct utsm_ipc_msg msg;
size_t copy_len;
ssize_t ret;
if (!g_shm_mapped)
return -ENODEV;
if (len == 0)
return 0;
/* Cap data to inline message size */
copy_len = min_t(size_t, len, UTSM_IPC_MSG_DATA_SIZE);
memset(&msg, 0, sizeof(msg));
msg.type = UTSM_MSG_DATA;
msg.data_len = (u32)copy_len;
if (copy_from_user(msg.data, buf, copy_len)) {
pr_err("[utsm] copy_from_user failed\n");
return -EFAULT;
}
/* Enqueue into linux_to_utsm ring (Linux produces, UTSM consumes) */
if (utsm_ipc_ring_push(&g_shm->linux_to_utsm, &msg) != 0) {
pr_warn("[utsm] linux_to_utsm ring full\n");
return -EAGAIN;
}
ret = (ssize_t)copy_len;
/* Also relay the data to the UTSM serial console via hypercall.
* This gives immediate visibility into Linux userspace output. */
if (copy_len > 0) {
char kbuf[UTSM_IPC_MSG_DATA_SIZE + 1];
memcpy(kbuf, msg.data, copy_len);
kbuf[copy_len] = '\0';
utsm_hcall_console_write(kbuf, copy_len);
}
return ret;
}
static ssize_t utsm_dev_read(struct file *f, char __user *buf,
size_t len, loff_t *off)
{
struct utsm_ipc_msg msg;
if (!g_shm_mapped)
return -ENODEV;
/* Dequeue from utsm_to_linux ring (UTSM produces, Linux consumes) */
if (utsm_ipc_ring_pop(&g_shm->utsm_to_linux, &msg) != 0) {
/* No message available */
return -EAGAIN; /* Non-blocking; userspace can poll */
}
if (msg.data_len == 0)
return 0;
if (len < msg.data_len)
return -EINVAL;
if (copy_to_user(buf, msg.data, msg.data_len))
return -EFAULT;
return (ssize_t)msg.data_len;
}
static const struct file_operations utsm_fops = {
.owner = THIS_MODULE,
.write = utsm_dev_write,
.read = utsm_dev_read,
};
static struct miscdevice utsm_miscdev = {
.minor = MISC_DYNAMIC_MINOR,
.name = UTSM_DEV_NAME,
.fops = &utsm_fops,
.mode = 0666,
};
/* ===== Early console redirect (optional) =====
*
* Redirect kernel printk output to UTSM serial console via hypercall.
* This is useful for debugging when the guest's own serial is not set up.
* Currently disabled; can be enabled via CONFIG_UTSM_EARLY_CONSOLE.
*/
#ifdef CONFIG_UTSM_EARLY_CONSOLE
static void utsm_console_write(struct console *con, const char *s, unsigned int n)
{
if (g_utsm_present) {
char buf[256];
unsigned int chunk;
while (n > 0) {
chunk = min_t(unsigned int, n, sizeof(buf) - 1);
memcpy(buf, s, chunk);
buf[chunk] = '\0';
utsm_hcall_console_write(buf, chunk);
s += chunk;
n -= chunk;
}
}
}
static struct console utsm_console = {
.name = "utsmcon",
.write = utsm_console_write,
.flags = CON_PRINTBUFFER | CON_ENABLED,
.index = -1,
};
#endif
/* ===== Initialization ===== */
/* Forward declaration: used by init before /dev/utsm is registered */
static inline int ipc_shm_send_to_utsm(u32 type, const void *data, u32 len);
static int __init utsm_hcall_init(void)
{
long ping_ret;
u64 shm_gpa = 0, shm_size = 0;
int ret;
pr_info("[utsm] probing for UTSM monitor...\n");
/* 1. Ping UTSM to verify it's present */
ping_ret = utsm_hcall_ping();
if (ping_ret != 0x504F4E47) { /* "PONG" */
pr_info("[utsm] UTSM not detected (ping returned 0x%lx)\n", ping_ret);
pr_info("[utsm] Driver loaded in passive mode (no /dev/utsm)\n");
return 0; /* Not an error: we might be running on bare metal */
}
g_utsm_present = 1;
pr_info("[utsm] UTSM monitor detected (PONG=0x%lx)\n", ping_ret);
/* 2. Say hello */
utsm_hcall_hello();
/* 3. Query shared memory info */
ret = utsm_hcall_shm_info(&shm_gpa, &shm_size);
if (ret != 0) {
pr_err("[utsm] SHM_INFO hypercall failed: %ld\n", ret);
goto err_no_shm;
}
pr_info("[utsm] shared memory: gpa=0x%llx size=%llu KB\n",
shm_gpa, shm_size / 1024);
if (shm_gpa == 0 || shm_size == 0) {
pr_err("[utsm] invalid SHM info\n");
goto err_no_shm;
}
/* 4. Map the shared memory region.
* The GPA is a physical address from Linux's perspective.
* Use ioremap_cache for cacheable memory (the SHM region is
* configured as WB in EPT). */
g_shm = (struct utsm_ipc_shm *)ioremap_cache(shm_gpa, shm_size);
if (!g_shm) {
pr_err("[utsm] failed to ioremap SHM at 0x%llx\n", shm_gpa);
goto err_ioremap;
}
g_shm_gpa = shm_gpa;
g_shm_size = shm_size;
g_shm_mapped = 1;
/* 5. Verify SHM header */
if (g_shm->header.magic != UTSM_IPC_MAGIC) {
pr_err("[utsm] SHM magic mismatch: 0x%llx\n", g_shm->header.magic);
goto err_bad_magic;
}
pr_info("[utsm] SHM header: magic=OK version=%u utsm_ready=%u\n",
g_shm->header.version, g_shm->header.utsm_ready);
/* 6. Set linux_ready flag */
g_shm->header.linux_ready = 1;
mb(); /* Ensure visible to UTSM */
/* 7. Register /dev/utsm */
ret = misc_register(&utsm_miscdev);
if (ret) {
pr_err("[utsm] misc_register failed: %d\n", ret);
goto err_misc;
}
#ifdef CONFIG_UTSM_EARLY_CONSOLE
register_console(&utsm_console);
#endif
pr_info("[utsm] /dev/%s registered (IPC ready)\n", UTSM_DEV_NAME);
/* 8. Send a HELLO message via the ring buffer */
{
const char *hello_msg = "Linux booted OK";
ipc_shm_send_to_utsm(UTSM_MSG_DATA, hello_msg, 15);
}
return 0;
err_misc:
g_shm->header.linux_ready = 0;
err_bad_magic:
iounmap(g_shm);
g_shm = NULL;
g_shm_mapped = 0;
err_ioremap:
err_no_shm:
g_utsm_present = 0;
return -ENODEV;
}
/* Helper to send a message (used by init before /dev/utsm is available) */
static inline int ipc_shm_send_to_utsm(u32 type, const void *data, u32 len)
{
struct utsm_ipc_msg msg;
if (!g_shm_mapped || !g_shm)
return -1;
if (len > UTSM_IPC_MSG_DATA_SIZE)
len = UTSM_IPC_MSG_DATA_SIZE;
memset(&msg, 0, sizeof(msg));
msg.type = type;
msg.data_len = len;
if (data && len > 0)
memcpy(msg.data, data, len);
return utsm_ipc_ring_push(&g_shm->linux_to_utsm, &msg);
}
static void __exit utsm_hcall_exit(void)
{
if (g_shm_mapped) {
/* Clear linux_ready flag */
g_shm->header.linux_ready = 0;
mb();
/* Send shutdown notification */
ipc_shm_send_to_utsm(UTSM_MSG_SHUTDOWN, NULL, 0);
misc_deregister(&utsm_miscdev);
#ifdef CONFIG_UTSM_EARLY_CONSOLE
unregister_console(&utsm_console);
#endif
iounmap(g_shm);
g_shm = NULL;
g_shm_mapped = 0;
}
pr_info("[utsm] driver unloaded\n");
}
module_init(utsm_hcall_init);
module_exit(utsm_hcall_exit);
MODULE_AUTHOR("Deshab Project");
MODULE_DESCRIPTION("UTSM dual-kernel hypercall and IPC driver");
MODULE_LICENSE("GPL");