Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 20 additions & 7 deletions docker/qemu/run-aarch64-interactive.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,25 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BREENIX_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"

# Build the kernel if needed
KERNEL="$BREENIX_ROOT/target/aarch64-unknown-none/release/kernel"
KERNEL="$BREENIX_ROOT/target/aarch64-unknown-none/release/kernel-aarch64"
if [ ! -f "$KERNEL" ]; then
echo "Building ARM64 kernel..."
cd "$BREENIX_ROOT/kernel"
cargo build --release --target aarch64-unknown-none
cd "$BREENIX_ROOT"
cargo build --release --target aarch64-unknown-none -p kernel --bin kernel-aarch64
fi

if [ ! -f "$KERNEL" ]; then
echo "Error: ARM64 kernel not found at $KERNEL"
echo "Try building with:"
echo " cd kernel && cargo build --release --target aarch64-unknown-none"
echo " cargo build --release --target aarch64-unknown-none -p kernel --bin kernel-aarch64"
exit 1
fi

# Check for ext2 disk image
EXT2_DISK="$BREENIX_ROOT/target/ext2-aarch64.img"
if [ -f "$EXT2_DISK" ]; then
echo "Found ext2 disk: $EXT2_DISK"
fi

# Build Docker image if needed
IMAGE_NAME="breenix-qemu-aarch64"
if ! docker image inspect "$IMAGE_NAME" &>/dev/null; then
Expand Down Expand Up @@ -57,10 +61,20 @@ echo ""

# Run QEMU with VNC display in Docker
# Port 5901 to avoid conflict with x86_64 on 5900

# Build disk options
DISK_VOLUME=""
DISK_OPTS="-device virtio-blk-device,drive=hd0 -drive if=none,id=hd0,format=raw,file=/dev/null"
if [ -f "$EXT2_DISK" ]; then
DISK_VOLUME="-v $EXT2_DISK:/breenix/ext2.img:ro"
DISK_OPTS="-device virtio-blk-device,drive=ext2disk -drive if=none,id=ext2disk,format=raw,readonly=on,file=/breenix/ext2.img"
fi

docker run --rm \
-p 5901:5900 \
-v "$KERNEL:/breenix/kernel:ro" \
-v "$OUTPUT_DIR:/output" \
$DISK_VOLUME \
"$IMAGE_NAME" \
qemu-system-aarch64 \
-M virt \
Expand All @@ -70,8 +84,7 @@ docker run --rm \
-device virtio-gpu-device \
-vnc :0 \
-device virtio-keyboard-device \
-device virtio-blk-device,drive=hd0 \
-drive if=none,id=hd0,format=raw,file=/dev/null \
$DISK_OPTS \
-device virtio-net-device,netdev=net0 \
-netdev user,id=net0 \
-serial file:/output/serial.txt \
Expand Down
4 changes: 2 additions & 2 deletions docker/qemu/run-aarch64-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BREENIX_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"

# Find the ARM64 kernel
KERNEL="$BREENIX_ROOT/target/aarch64-unknown-none/release/kernel-aarch64"
KERNEL="$BREENIX_ROOT/target/aarch64-breenix/release/kernel-aarch64"
if [ ! -f "$KERNEL" ]; then
echo "Error: No ARM64 kernel found. Build with:"
echo " cargo build --release --target aarch64-unknown-none -p kernel --features aarch64-qemu --bin kernel-aarch64"
echo " cargo build --release --target aarch64-breenix.json -p kernel --features aarch64-qemu --bin kernel-aarch64"
exit 1
fi

Expand Down
73 changes: 27 additions & 46 deletions docker/qemu/run-aarch64-userspace.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,63 +8,44 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BREENIX_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"

# Find the ARM64 kernel
KERNEL="$BREENIX_ROOT/target/aarch64-unknown-none/release/kernel-aarch64"
KERNEL="$BREENIX_ROOT/target/aarch64-breenix/release/kernel-aarch64"
if [ ! -f "$KERNEL" ]; then
echo "Error: No ARM64 kernel found. Build with:"
echo " cargo build --release --target aarch64-unknown-none -p kernel --features aarch64-qemu --bin kernel-aarch64"
echo " cargo build --release --target aarch64-breenix.json -p kernel --features aarch64-qemu --bin kernel-aarch64"
exit 1
fi

# Find or create ARM64 test disk
TEST_DISK="$BREENIX_ROOT/target/aarch64_test_binaries.img"
if [ ! -f "$TEST_DISK" ]; then
echo "Creating ARM64 test disk image..."

# Create a disk image with userspace binaries
# Using a simple raw format - kernel will need to parse this
TEMP_DIR=$(mktemp -d)

# Copy ARM64 binaries to temp dir
if [ -d "$BREENIX_ROOT/userspace/tests/aarch64" ]; then
cp "$BREENIX_ROOT/userspace/tests/aarch64/"*.elf "$TEMP_DIR/" 2>/dev/null || true
fi

# Create a simple FAT disk image
# 4MB should be plenty for test binaries
dd if=/dev/zero of="$TEST_DISK" bs=1M count=4

# Format as FAT16
if command -v mkfs.fat &>/dev/null; then
mkfs.fat -F 16 "$TEST_DISK"
# Mount and copy files
MOUNT_DIR=$(mktemp -d)
if mount -o loop "$TEST_DISK" "$MOUNT_DIR" 2>/dev/null; then
cp "$TEMP_DIR"/*.elf "$MOUNT_DIR/" 2>/dev/null || true
umount "$MOUNT_DIR"
else
echo "Note: Could not mount disk image to copy files"
echo " (This is expected on macOS - using mtools instead)"
fi
rmdir "$MOUNT_DIR"
# Find or create ARM64 ext2 disk
EXT2_DISK="$BREENIX_ROOT/target/ext2-aarch64.img"
EXT2_SIZE_BYTES=$((8 * 1024 * 1024))
EXT2_SIZE_ACTUAL=0
if [ -f "$EXT2_DISK" ]; then
if stat -f%z "$EXT2_DISK" >/dev/null 2>&1; then
EXT2_SIZE_ACTUAL=$(stat -f%z "$EXT2_DISK")
else
EXT2_SIZE_ACTUAL=$(stat -c %s "$EXT2_DISK")
fi
fi

# On macOS, use mtools if available
if command -v mtools &>/dev/null || [ -f /opt/homebrew/bin/mformat ]; then
# Try to use mtools
mformat -i "$TEST_DISK" -F :: 2>/dev/null || true
for f in "$TEMP_DIR"/*.elf; do
[ -f "$f" ] && mcopy -i "$TEST_DISK" "$f" :: 2>/dev/null || true
done
if [ ! -f "$EXT2_DISK" ] || [ "$EXT2_SIZE_ACTUAL" -ne "$EXT2_SIZE_BYTES" ]; then
if [ -f "$EXT2_DISK" ]; then
echo "Recreating ARM64 ext2 disk (size mismatch: $EXT2_SIZE_ACTUAL bytes)"
rm -f "$EXT2_DISK"
else
echo "Creating ARM64 ext2 disk image..."
fi

rm -rf "$TEMP_DIR"
"$BREENIX_ROOT/scripts/create_ext2_disk.sh" --arch aarch64 --size 8

echo "Created: $TEST_DISK"
if [ ! -f "$EXT2_DISK" ]; then
echo "Error: Failed to create ext2 disk image at $EXT2_DISK"
exit 1
fi
fi

echo "Running ARM64 kernel with userspace..."
echo "Kernel: $KERNEL"
echo "Test disk: $TEST_DISK"
echo "Ext2 disk: $EXT2_DISK"

# Create output directory
OUTPUT_DIR="/tmp/breenix_aarch64_1"
Expand All @@ -86,16 +67,16 @@ echo "Starting QEMU ARM64 with VirtIO devices..."
# etc.
docker run --rm \
-v "$KERNEL:/breenix/kernel:ro" \
-v "$TEST_DISK:/breenix/test_disk.img:ro" \
-v "$EXT2_DISK:/breenix/ext2.img:ro" \
-v "$OUTPUT_DIR:/output" \
breenix-qemu-aarch64 \
qemu-system-aarch64 \
-M virt \
-cpu cortex-a72 \
-m 512 \
-kernel /breenix/kernel \
-drive if=none,id=hd0,format=raw,readonly=on,file=/breenix/test_disk.img \
-device virtio-blk-device,drive=hd0 \
-drive if=none,id=ext2disk,format=raw,readonly=on,file=/breenix/ext2.img \
-device virtio-blk-device,drive=ext2disk \
-display none \
-no-reboot \
-serial file:/output/serial.txt \
Expand Down
8 changes: 1 addition & 7 deletions kernel/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,6 @@ fn main() {
println!("cargo:rustc-link-arg={}/breakpoint_entry.o", out_dir);
}

// For aarch64, use our custom linker script
if target.contains("aarch64") {
// Use ARM64-specific linker script
println!("cargo:rustc-link-arg=-T{}/src/arch_impl/aarch64/linker.ld", manifest_dir);
}

// Use our custom linker script for x86_64
// Temporarily disabled to test with bootloader's default
// println!("cargo:rustc-link-arg=-Tkernel/linker.ld");
Expand Down Expand Up @@ -130,4 +124,4 @@ fn main() {
} else {
println!("cargo:warning=Userspace test directory not found at {:?}", userspace_test_dir);
}
}
}
7 changes: 5 additions & 2 deletions kernel/src/arch_impl/aarch64/boot.S
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,14 @@
.equ DESC_TABLE, (1 << 0) | (1 << 1) // 0b11
.equ DESC_AF, (1 << 10)
.equ DESC_SH_INNER, (0b11 << 8)
.equ DESC_SH_NONE, (0b00 << 8)
.equ DESC_ATTR_DEVICE, (0 << 2)
.equ DESC_ATTR_NORMAL, (1 << 2)
.equ DESC_AP_KERNEL, (0 << 6) // EL1 RW, EL0 no access
.equ DESC_PXN, (1 << 53)
.equ DESC_UXN, (1 << 54)

.equ BLOCK_FLAGS_DEVICE, (DESC_BLOCK | DESC_AF | DESC_SH_INNER | DESC_AP_KERNEL | DESC_ATTR_DEVICE | DESC_PXN | DESC_UXN)
.equ BLOCK_FLAGS_DEVICE, (DESC_BLOCK | DESC_AF | DESC_SH_NONE | DESC_AP_KERNEL | DESC_ATTR_DEVICE | DESC_PXN | DESC_UXN)
.equ BLOCK_FLAGS_NORMAL, (DESC_BLOCK | DESC_AF | DESC_SH_INNER | DESC_AP_KERNEL | DESC_ATTR_NORMAL)

// MAIR attributes
Expand Down Expand Up @@ -245,6 +246,7 @@ lower_el_aarch32_serror:
.section .text.boot

setup_mmu:
str x30, [sp, #-16]! // Save link register
// Zero page tables
ldr x0, =ttbr0_l0
bl zero_table
Expand Down Expand Up @@ -280,7 +282,7 @@ setup_mmu:
orr x1, x1, #DESC_TABLE
str x1, [x0]

// TTBR1 L1[0] = device (high-half direct map)
// TTBR1 L1[0] = device (high-half direct map, includes PL011 @ 0x0900_0000)
ldr x0, =ttbr1_l1
ldr x1, =0x00000000
ldr x2, =BLOCK_FLAGS_DEVICE
Expand Down Expand Up @@ -330,6 +332,7 @@ setup_mmu:
msr sctlr_el1, x0
isb

ldr x30, [sp], #16 // Restore link register
ret

// Zero a 4KB table at x0
Expand Down
1 change: 1 addition & 0 deletions kernel/src/arch_impl/aarch64/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ pub use crate::task::thread::CpuContext;
// 120 elr_el1 (exception return address)
// 128 spsr_el1 (saved program status)
core::arch::global_asm!(r#"
.section .text
.global switch_context
.type switch_context, @function
switch_context:
Expand Down
55 changes: 55 additions & 0 deletions kernel/src/arch_impl/aarch64/context_switch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,10 @@ fn setup_kernel_thread_return_arm64(thread_id: u64, frame: &mut Aarch64Exception

/// Restore userspace context for a thread.
fn restore_userspace_context_arm64(thread_id: u64, frame: &mut Aarch64ExceptionFrame) {
crate::serial_println!(
"ARM64: enter restore_userspace_context_arm64 thread={}",
thread_id
);
log::trace!("restore_userspace_context_arm64: thread {}", thread_id);

// Check if this thread has ever run
Expand Down Expand Up @@ -398,6 +402,9 @@ fn restore_userspace_context_arm64(thread_id: u64, frame: &mut Aarch64ExceptionF
}
});

// Set TTBR0 target for this thread's process address space
set_next_ttbr0_for_thread(thread_id);

// Switch TTBR0 if needed for different address space
switch_ttbr0_if_needed(thread_id);
}
Expand Down Expand Up @@ -462,6 +469,9 @@ fn setup_first_userspace_entry_arm64(thread_id: u64, frame: &mut Aarch64Exceptio
);
});

// Set TTBR0 target for this thread's process address space
set_next_ttbr0_for_thread(thread_id);

// Switch TTBR0 for this thread's address space
switch_ttbr0_if_needed(thread_id);

Expand Down Expand Up @@ -492,6 +502,12 @@ fn switch_ttbr0_if_needed(thread_id: u64) {
}

if current_ttbr0 != next_ttbr0 {
crate::serial_println!(
"ARM64: TTBR0 switch thread={} {:#x} -> {:#x}",
thread_id,
current_ttbr0,
next_ttbr0
);
log::trace!(
"TTBR0 switch: {:#x} -> {:#x} for thread {}",
current_ttbr0,
Expand Down Expand Up @@ -529,6 +545,45 @@ fn switch_ttbr0_if_needed(thread_id: u64) {
}
}

/// Determine and set the next TTBR0 value for a userspace thread.
fn set_next_ttbr0_for_thread(thread_id: u64) {
let next_ttbr0 = {
let manager_guard = crate::process::manager();
if let Some(ref manager) = *manager_guard {
if let Some((_pid, process)) = manager.find_process_by_thread(thread_id) {
process
.page_table
.as_ref()
.map(|pt| pt.level_4_frame().start_address().as_u64())
} else {
None
}
} else {
None
}
};

if let Some(ttbr0) = next_ttbr0 {
crate::serial_println!(
"ARM64: set_next_ttbr0_for_thread thread={} ttbr0={:#x}",
thread_id,
ttbr0
);
unsafe {
Aarch64PerCpu::set_next_cr3(ttbr0);
}
} else {
crate::serial_println!(
"ARM64: set_next_ttbr0_for_thread thread={} ttbr0=NONE",
thread_id
);
log::error!(
"ARM64: Failed to determine TTBR0 for thread {} (process/page table missing)",
thread_id
);
}
}

/// ARM64 idle loop - wait for interrupts.
///
/// This function runs when no other threads are ready. It uses WFI
Expand Down
15 changes: 10 additions & 5 deletions kernel/src/arch_impl/aarch64/linker.ld
Original file line number Diff line number Diff line change
Expand Up @@ -54,30 +54,35 @@ SECTIONS
}

/* Main code section */
.text : ALIGN(4K) AT(ADDR(.text) - VMLINUX_OFFSET) {
. = ALIGN(4K);
.text : AT(ADDR(.text) - VMLINUX_OFFSET) {
*(.text .text.*)
}

/* Read-only data */
.rodata : ALIGN(4K) AT(ADDR(.rodata) - VMLINUX_OFFSET) {
. = ALIGN(4K);
.rodata : AT(ADDR(.rodata) - VMLINUX_OFFSET) {
*(.rodata .rodata.*)
}

/* Initialized data */
.data : ALIGN(4K) AT(ADDR(.data) - VMLINUX_OFFSET) {
. = ALIGN(4K);
.data : AT(ADDR(.data) - VMLINUX_OFFSET) {
*(.data .data.*)
}

/* BSS - zero initialized */
.bss (NOLOAD) : ALIGN(4K) AT(ADDR(.bss) - VMLINUX_OFFSET) {
. = ALIGN(4K);
.bss (NOLOAD) : AT(ADDR(.bss) - VMLINUX_OFFSET) {
__bss_start = .;
*(.bss .bss.*)
*(COMMON)
__bss_end = .;
}

/* Stack section - 64KB stack */
.bss.stack (NOLOAD) : ALIGN(16) AT(ADDR(.bss.stack) - VMLINUX_OFFSET) {
. = ALIGN(16);
.bss.stack (NOLOAD) : AT(ADDR(.bss.stack) - VMLINUX_OFFSET) {
__stack_bottom = .;
. = . + 65536; /* Reserve 64KB for stack */
__stack_top = .;
Expand Down
2 changes: 1 addition & 1 deletion kernel/src/arch_impl/aarch64/mmu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ fn l2_block_desc_user(base: u64, attr: u64) -> u64 {
/// - 0x4100_0000 - 0x8000_0000: User (2MB blocks, AP=1, EL0 exec only due to implicit PXN)
pub fn init() {
// If MMU is already enabled, do not reprogram page tables.
let mut sctlr: u64 = 0;
let mut sctlr: u64;
unsafe {
core::arch::asm!("mrs {0}, sctlr_el1", out(reg) sctlr, options(nomem, nostack));
}
Expand Down
Loading
Loading