guests: add linux-aarch64 guest images and scripts
- initramfs.img, rootfs.img for AArch64 Linux guest - mkdisk.py for disk image creation - initramfs and rootfs directories
This commit is contained in:
parent
4960a14096
commit
5ab1b438b4
7 changed files with 426 additions and 0 deletions
BIN
guests/linux-aarch64/initramfs.img
Normal file
BIN
guests/linux-aarch64/initramfs.img
Normal file
Binary file not shown.
62
guests/linux-aarch64/initramfs/build.sh
Normal file
62
guests/linux-aarch64/initramfs/build.sh
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
#!/bin/bash
|
||||
# Build a minimal initramfs for UniversalisOS Linux guest.
|
||||
# Cross-compiles init.c for AArch64 and creates a cpio archive.
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
OUTPUT_DIR="$SCRIPT_DIR/.."
|
||||
INITRAMFS_DIR="$SCRIPT_DIR/rootfs"
|
||||
|
||||
# Cross-compiler
|
||||
CROSS=aarch64-linux-gnu
|
||||
CC=${CROSS}-gcc
|
||||
OBJCOPY=${CROSS}-objcopy
|
||||
STRIP=${CROSS}-strip
|
||||
|
||||
echo "=== Building minimal initramfs ==="
|
||||
|
||||
# Create rootfs directory structure
|
||||
rm -rf "$INITRAMFS_DIR"
|
||||
mkdir -p "$INITRAMFS_DIR"/{bin,dev,proc,sys,etc,sbin}
|
||||
|
||||
# Compile init.c as static binary
|
||||
echo "Compiling init.c..."
|
||||
${CC} -static -O2 -o "$INITRAMFS_DIR/init" "$SCRIPT_DIR/init.c"
|
||||
${STRIP} "$INITRAMFS_DIR/init"
|
||||
echo "init binary: $(file "$INITRAMFS_DIR/init")"
|
||||
|
||||
# Create a minimal /etc/init.d/rcS
|
||||
cat > "$INITRAMFS_DIR/etc/init.d/rcS" << 'EOF'
|
||||
#!/bin/sh
|
||||
echo "Running rcS..."
|
||||
mount -t proc proc /proc
|
||||
mount -t sysfs sysfs /sys
|
||||
mount -t tmpfs tmpfs /dev
|
||||
exec /init
|
||||
EOF
|
||||
chmod +x "$INITRAMFS_DIR/etc/init.d/rcS"
|
||||
|
||||
# Create a minimal /etc/fstab
|
||||
cat > "$INITRAMFS_DIR/etc/fstab" << 'EOF'
|
||||
# <filesystem> <mount point> <type> <options> <dump> <pass>
|
||||
proc /proc proc defaults 0 0
|
||||
sysfs /sys sysfs defaults 0 0
|
||||
tmpfs /dev tmpfs defaults 0 0
|
||||
EOF
|
||||
|
||||
# Create cpio archive
|
||||
echo "Creating cpio archive..."
|
||||
cd "$INITRAMFS_DIR"
|
||||
find . -print0 | cpio --null -o --format=newc > "$OUTPUT_DIR/initramfs.cpio"
|
||||
cd "$SCRIPT_DIR"
|
||||
|
||||
# Create gzip-compressed version
|
||||
echo "Compressing..."
|
||||
gzip -9 -f "$OUTPUT_DIR/initramfs.cpio"
|
||||
mv "$OUTPUT_DIR/initramfs.cpio.gz" "$OUTPUT_DIR/initramfs.img"
|
||||
|
||||
echo "=== Initramfs built ==="
|
||||
echo "Output: $OUTPUT_DIR/initramfs.img"
|
||||
echo "Size: $(du -h "$OUTPUT_DIR/initramfs.img" | cut -f1)"
|
||||
ls -la "$OUTPUT_DIR/initramfs.img"
|
||||
144
guests/linux-aarch64/initramfs/init.c
Normal file
144
guests/linux-aarch64/initramfs/init.c
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* Minimal /init for UniversalisOS initramfs.
|
||||
*
|
||||
* This is the first userspace program executed by Linux after mounting
|
||||
* the rootfs. It demonstrates that the hypervisor can boot a real Linux
|
||||
* kernel to userspace via virtio-blk.
|
||||
*
|
||||
* Features:
|
||||
* - Mounts /proc, /sys, /dev (tmpfs)
|
||||
* - Prints system information from /proc/cpuinfo
|
||||
* - Runs a minimal shell loop (echo + wait)
|
||||
* - Demonstrates the full hypervisor -> Linux -> userspace path
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#include <sys/reboot.h>
|
||||
#include <linux/reboot.h>
|
||||
|
||||
#define BANNER \
|
||||
"\r\n" \
|
||||
"============================================\r\n" \
|
||||
" UniversalisOS Linux Guest Boot Successful!\r\n" \
|
||||
"============================================\r\n" \
|
||||
"\r\n"
|
||||
|
||||
static void mount_proc(void) {
|
||||
mkdir("/proc", 0755);
|
||||
if (mount("proc", "/proc", "proc", 0, NULL) == 0) {
|
||||
printf("[init] /proc mounted\r\n");
|
||||
} else {
|
||||
printf("[init] /proc mount failed\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void mount_sys(void) {
|
||||
mkdir("/sys", 0755);
|
||||
if (mount("sysfs", "/sys", "sysfs", 0, NULL) == 0) {
|
||||
printf("[init] /sys mounted\r\n");
|
||||
} else {
|
||||
printf("[init] /sys mount failed\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void mount_dev(void) {
|
||||
mkdir("/dev", 0755);
|
||||
if (mount("tmpfs", "/dev", "tmpfs", 0, "size=64M") == 0) {
|
||||
printf("[init] /dev mounted (tmpfs)\r\n");
|
||||
} else {
|
||||
printf("[init] /dev mount failed\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void print_cpuinfo(void) {
|
||||
FILE *f = fopen("/proc/cpuinfo", "r");
|
||||
if (f) {
|
||||
char line[256];
|
||||
printf("[init] CPU info:\r\n");
|
||||
while (fgets(line, sizeof(line), f)) {
|
||||
if (strncmp(line, "Processor", 9) == 0 ||
|
||||
strncmp(line, "model name", 10) == 0 ||
|
||||
strncmp(line, "CPU implementer", 14) == 0 ||
|
||||
strncmp(line, "CPU variant", 10) == 0 ||
|
||||
strncmp(line, "BogoMIPS", 8) == 0) {
|
||||
printf(" %s", line);
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
static void print_uptime(void) {
|
||||
FILE *f = fopen("/proc/uptime", "r");
|
||||
if (f) {
|
||||
double uptime;
|
||||
if (fscanf(f, "%lf", &uptime) == 1) {
|
||||
printf("[init] System uptime: %.1f seconds\r\n", uptime);
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
static void print_memory(void) {
|
||||
FILE *f = fopen("/proc/meminfo", "r");
|
||||
if (f) {
|
||||
char line[256];
|
||||
printf("[init] Memory info:\r\n");
|
||||
while (fgets(line, sizeof(line), f)) {
|
||||
if (strncmp(line, "MemTotal", 8) == 0 ||
|
||||
strncmp(line, "MemFree", 7) == 0 ||
|
||||
strncmp(line, "MemAvailable", 12) == 0) {
|
||||
printf(" %s", line);
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
printf(BANNER);
|
||||
printf("[init] UniversalisOS Linux Guest Init\r\n");
|
||||
printf("[init] PID=%d\r\n", getpid());
|
||||
|
||||
/* Mount essential filesystems */
|
||||
mount_dev();
|
||||
mount_proc();
|
||||
mount_sys();
|
||||
|
||||
/* Create basic device nodes */
|
||||
mknod("/dev/console", 0600 | S_IFCHR, makedev(5, 1));
|
||||
mknod("/dev/null", 0666 | S_IFCHR, makedev(1, 3));
|
||||
mknod("/dev/zero", 0666 | S_IFCHR, makedev(1, 5));
|
||||
|
||||
/* Print system information */
|
||||
print_cpuinfo();
|
||||
print_uptime();
|
||||
print_memory();
|
||||
|
||||
/* Print mount info */
|
||||
FILE *f = fopen("/proc/mounts", "r");
|
||||
if (f) {
|
||||
char line[256];
|
||||
printf("[init] Mounts:\r\n");
|
||||
while (fgets(line, sizeof(line), f)) {
|
||||
printf(" %s", line);
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
printf("\r\n[init] System ready. Entering idle loop.\r\n");
|
||||
printf("[init] To shutdown: echo 1 > /proc/sys/kernel/sysrq && echo o > /proc/sysrq-trigger\r\n");
|
||||
|
||||
/* Idle loop — in a real system this would run a shell or service manager */
|
||||
while (1) {
|
||||
sleep(10);
|
||||
printf("[init] heartbeat\r\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
BIN
guests/linux-aarch64/initramfs/rootfs/init
Executable file
BIN
guests/linux-aarch64/initramfs/rootfs/init
Executable file
Binary file not shown.
80
guests/linux-aarch64/mkdisk.py
Normal file
80
guests/linux-aarch64/mkdisk.py
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Generate a virtio-blk disk image for UniversalisOS Linux guest.
|
||||
|
||||
Creates a raw disk image with the UOSVBLK1 header format that
|
||||
virtio-blk can detect and serve to the guest.
|
||||
|
||||
Usage:
|
||||
python3 mkdisk.py <input_file> <output.img> [--size=SIZE]
|
||||
|
||||
The output image format:
|
||||
Offset 0x000: "UOSVBLK1" magic (8 bytes)
|
||||
Offset 0x008: data size in bytes (8 bytes, little-endian)
|
||||
Offset 0x010: raw sector data (512-byte aligned)
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
import struct
|
||||
|
||||
MAGIC = b"UOSVBLK1"
|
||||
SECTOR_SIZE = 512
|
||||
|
||||
def create_disk_image(input_path, output_path, target_size=None):
|
||||
"""Create a virtio-blk disk image from an input file."""
|
||||
|
||||
# Read input file
|
||||
with open(input_path, 'rb') as f:
|
||||
data = f.read()
|
||||
|
||||
# Pad to sector boundary
|
||||
if len(data) % SECTOR_SIZE != 0:
|
||||
data += b'\x00' * (SECTOR_SIZE - (len(data) % SECTOR_SIZE))
|
||||
|
||||
# Pad to target size if specified
|
||||
if target_size and target_size > len(data):
|
||||
data += b'\x00' * (target_size - len(data))
|
||||
|
||||
# Create image with header
|
||||
header = MAGIC + struct.pack('<Q', len(data)) # magic + size (LE 64-bit)
|
||||
|
||||
# Pad header to sector boundary
|
||||
if len(header) % SECTOR_SIZE != 0:
|
||||
header += b'\x00' * (SECTOR_SIZE - len(header))
|
||||
|
||||
image = header + data
|
||||
|
||||
# Write output
|
||||
with open(output_path, 'wb') as f:
|
||||
f.write(image)
|
||||
|
||||
print(f"Disk image created: {output_path}")
|
||||
print(f" Input: {input_path} ({len(data)} bytes)")
|
||||
print(f" Header: {len(header)} bytes")
|
||||
print(f" Total: {len(image)} bytes ({len(image) // 1024} KB)")
|
||||
print(f" Sectors: {len(image) // SECTOR_SIZE}")
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 3:
|
||||
print(f"Usage: {sys.argv[0]} <input> <output.img> [--size=SIZE]")
|
||||
print(f" input: input file (initramfs, rootfs, etc.)")
|
||||
print(f" output: output disk image")
|
||||
print(f" --size: target size in bytes (optional, pads with zeros)")
|
||||
sys.exit(1)
|
||||
|
||||
input_path = sys.argv[1]
|
||||
output_path = sys.argv[2]
|
||||
target_size = None
|
||||
|
||||
for arg in sys.argv[3:]:
|
||||
if arg.startswith('--size='):
|
||||
target_size = int(arg.split('=')[1])
|
||||
|
||||
if not os.path.exists(input_path):
|
||||
print(f"Error: input file not found: {input_path}")
|
||||
sys.exit(1)
|
||||
|
||||
create_disk_image(input_path, output_path, target_size)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
BIN
guests/linux-aarch64/rootfs.img
Normal file
BIN
guests/linux-aarch64/rootfs.img
Normal file
Binary file not shown.
140
guests/linux-aarch64/rootfs/Makefile
Normal file
140
guests/linux-aarch64/rootfs/Makefile
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
# UniversalisOS Linux Guest Rootfs Generation
|
||||
#
|
||||
# Targets:
|
||||
# make minimal - Build minimal initramfs (init only, ~50KB)
|
||||
# make busybox - Build busybox-based initramfs (~1MB)
|
||||
# make full - Build full rootfs from yocto/buildroot (~50MB)
|
||||
# make disk - Create virtio-blk disk image
|
||||
# make all - Build minimal + disk image
|
||||
# make clean - Remove all build artifacts
|
||||
#
|
||||
# Prerequisites:
|
||||
# - aarch64-linux-gnu-gcc (cross-compiler)
|
||||
# - cpio, gzip (for initramfs)
|
||||
# - python3 (for mkdisk.py)
|
||||
|
||||
# Configuration
|
||||
CROSS_COMPILE ?= aarch64-linux-gnu-
|
||||
CC = $(CROSS_COMPILE)gcc
|
||||
STRIP = $(CROSS_COMPILE)strip
|
||||
|
||||
# Directories
|
||||
ROOTFS_DIR = $(CURDIR)
|
||||
OUTPUT_DIR = $(ROOTFS_DIR)/output
|
||||
BUILD_DIR = $(ROOTFS_DIR)/build
|
||||
INITRAMFS_DIR = $(BUILD_DIR)/initramfs
|
||||
|
||||
# Output files
|
||||
INITRAMFS_IMG = $(OUTPUT_DIR)/initramfs.img
|
||||
DISK_IMG = $(OUTPUT_DIR)/rootfs.img
|
||||
|
||||
# Disk image size (256 MiB for full rootfs)
|
||||
DISK_SIZE = 268435456
|
||||
|
||||
# Busybox configuration
|
||||
BUSYBOX_VERSION = 1.36.1
|
||||
BUSYBOX_URL = https://busybox.net/downloads/busybox-$(BUSYBOX_VERSION).tar.bz2
|
||||
|
||||
# ===== Minimal initramfs (init only) =====
|
||||
|
||||
.PHONY: minimal
|
||||
minimal: $(INITRAMFS_IMG)
|
||||
|
||||
$(OUTPUT_DIR):
|
||||
mkdir -p $(OUTPUT_DIR)
|
||||
|
||||
$(BUILD_DIR):
|
||||
mkdir -p $(BUILD_DIR)
|
||||
|
||||
$(INITRAMFS_DIR): $(BUILD_DIR)
|
||||
mkdir -p $(INITRAMFS_DIR)/{bin,dev,proc,sys,etc,sbin}
|
||||
|
||||
$(INITRAMFS_DIR)/init: $(INITRAMFS_DIR) $(ROOTFS_DIR)/initramfs/init.c
|
||||
$(CC) -static -O2 -o $@ $(ROOTFS_DIR)/initramfs/init.c
|
||||
$(STRIP) $@
|
||||
|
||||
$(INITRAMFS_DIR)/etc/init.d/rcS: $(INITRAMFS_DIR)
|
||||
mkdir -p $(INITRAMFS_DIR)/etc/init.d
|
||||
@echo '#!/bin/sh' > $@
|
||||
@echo 'mount -t proc proc /proc' >> $@
|
||||
@echo 'mount -t sysfs sysfs /sys' >> $@
|
||||
@echo 'mount -t tmpfs tmpfs /dev' >> $@
|
||||
@chmod +x $@
|
||||
|
||||
$(INITRAMFS_DIR)/etc/fstab: $(INITRAMFS_DIR)
|
||||
@echo '# <filesystem> <mount> <type> <opts> <dump> <pass>' > $@
|
||||
@echo 'proc /proc proc defaults 0 0' >> $@
|
||||
@echo 'sysfs /sys sysfs defaults 0 0' >> $@
|
||||
@echo 'tmpfs /dev tmpfs defaults 0 0' >> $@
|
||||
|
||||
$(INITRAMFS_IMG): $(INITRAMFS_DIR)/init $(INITRAMFS_DIR)/etc/init.d/rcS $(INITRAMFS_DIR)/etc/fstab
|
||||
cd $(INITRAMFS_DIR) && find . -print0 | cpio --null -o --format=newc 2>/dev/null | gzip -9 > $@
|
||||
@echo "Initramfs: $$(du -h $@ | cut -f1)"
|
||||
|
||||
# ===== Busybox initramfs =====
|
||||
|
||||
.PHONY: busybox
|
||||
busybox: $(OUTPUT_DIR)/busybox-initramfs.img
|
||||
|
||||
$(BUILD_DIR)/busybox-$(BUSYBOX_VERSION): $(BUILD_DIR)
|
||||
cd $(BUILD_DIR) && curl -L $(BUSYBOX_URL) | tar xj
|
||||
cd $(BUILD_DIR)/busybox-$(BUSYBOX_VERSION) && make ARCH=aarch64 CROSS_COMPILE=$(CROSS_COMPILE) defconfig
|
||||
cd $(BUILD_DIR)/busybox-$(BUSYBOX_VERSION) && sed -i 's/# CONFIG_STATIC is not set/CONFIG_STATIC=y/' .config
|
||||
cd $(BUILD_DIR)/busybox-$(BUSYBOX_VERSION) && make ARCH=aarch64 CROSS_COMPILE=$(CROSS_COMPILE) -j$$(nproc)
|
||||
|
||||
$(BUILD_DIR)/busybox-rootfs: $(BUILD_DIR)/busybox-$(BUSYBOX_VERSION)
|
||||
mkdir -p $@/{bin,dev,proc,sys,etc,sbin,tmp,root}
|
||||
cp $(BUILD_DIR)/busybox-$(BUSYBOX_VERSION)/busybox $@/bin/busybox
|
||||
cd $@/bin && for cmd in sh ls cat echo mount mkdir mknod sleep reboot; do ln -sf busybox $$cmd; done
|
||||
cd $@/sbin && ln -sf ../bin/busybox init
|
||||
@echo '#!/bin/sh' > $@/etc/init.d/rcS
|
||||
@echo 'mount -t proc proc /proc' >> $@/etc/init.d/rcS
|
||||
@echo 'mount -t sysfs sysfs /sys' >> $@/etc/init.d/rcS
|
||||
@echo 'mount -t tmpfs tmpfs /dev' >> $@/etc/init.d/rcS
|
||||
@echo 'mount -t devpts devpts /dev/pts' >> $@/etc/init.d/rcS
|
||||
@chmod +x $@/etc/init.d/rcS
|
||||
|
||||
$(OUTPUT_DIR)/busybox-initramfs.img: $(BUILD_DIR)/busybox-rootfs $(OUTPUT_DIR)
|
||||
cd $(BUILD_DIR)/busybox-rootfs && find . -print0 | cpio --null -o --format=newc 2>/dev/null | gzip -9 > $@
|
||||
@echo "Busybox initramfs: $$(du -h $@ | cut -f1)"
|
||||
|
||||
# ===== Full rootfs (yocto/buildroot placeholder) =====
|
||||
|
||||
.PHONY: full
|
||||
full: $(OUTPUT_DIR)/full-rootfs.img
|
||||
|
||||
$(OUTPUT_DIR)/full-rootfs.img: $(OUTPUT_DIR)
|
||||
@echo "=== Full rootfs generation ==="
|
||||
@echo "This target requires yocto or buildroot to be configured."
|
||||
@echo "To use:"
|
||||
@echo " 1. Configure yocto/buildroot for aarch64"
|
||||
@echo " 2. Build the rootfs"
|
||||
@echo " 3. Copy the output to $(OUTPUT_DIR)/full-rootfs/"
|
||||
@echo " 4. Run: make disk INPUT=$(OUTPUT_DIR)/full-rootfs/"
|
||||
@echo ""
|
||||
@echo "For now, using minimal initramfs as fallback."
|
||||
$(MAKE) minimal
|
||||
cp $(INITRAMFS_IMG) $@
|
||||
|
||||
# ===== Disk image generation =====
|
||||
|
||||
.PHONY: disk
|
||||
disk: $(DISK_IMG)
|
||||
|
||||
$(DISK_IMG): $(INITRAMFS_IMG)
|
||||
python3 ../mkdisk.py $(INITRAMFS_IMG) $@ --size=$(DISK_SIZE)
|
||||
@echo "Disk image: $$(du -h $@ | cut -f1)"
|
||||
|
||||
# ===== All =====
|
||||
|
||||
.PHONY: all
|
||||
all: minimal disk
|
||||
@echo "=== Build complete ==="
|
||||
@echo "Initramfs: $(INITRAMFS_IMG)"
|
||||
@echo "Disk image: $(DISK_IMG)"
|
||||
|
||||
# ===== Clean =====
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -rf $(BUILD_DIR) $(OUTPUT_DIR)
|
||||
Loading…
Reference in a new issue