sylveos

Toy Operating System
Log | Files | Refs

commit 0fffffe53076e11126f574b594a71db62d144b2a
parent f0032e962f4cd48af67f1d78ca7f37c7c82f740e
Author: Sylvia Ivory <git@sivory.net>
Date:   Sun, 15 Mar 2026 18:04:41 -0700

Hello World from Lua

Diffstat:
Mpi/faults.zig | 4+++-
Asylveos/kuser.zig | 140+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msylveos/loader.zig | 36++++++++++++++++++++++++++++++------
Msylveos/root.zig | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Msylveos/syscall.zig | 319+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
5 files changed, 534 insertions(+), 29 deletions(-)

diff --git a/pi/faults.zig b/pi/faults.zig @@ -1,4 +1,5 @@ pub const FaultKindZero = enum(u4) { + _NoFunction0 = 0b0000, AlignmentFault = 0b0001, InstructionDebugEventFault = 0b0010, AccessBitFaultOnSection = 0b0011, @@ -8,6 +9,7 @@ pub const FaultKindZero = enum(u4) { TranslationPageFault = 0b0111, PreciseExternalAbort = 0b1000, DomainSectionFault = 0b1001, + _NoFunction10 = 0b1010, DomainPageFault = 0b1011, ExternalAbortOnTranslationFirstLevel = 0b1100, PermissionSectionFault = 0b1101, @@ -56,7 +58,7 @@ pub const DFSR = packed struct(u32) { pub const IFSR = packed struct(u32) { status: FaultKindZero, - _reserved_4_9: u8, + _reserved_4_9: u6, status_kind: u1, _reserved_11: u1, axi_source: AxiSource, diff --git a/sylveos/kuser.zig b/sylveos/kuser.zig @@ -0,0 +1,140 @@ +// https://docs.kernel.org/6.2/arm/kernel_user_helpers.html + +fn mk_fn(name: []const u8, body: []const u8) []const u8 { + return "" ++ + ".global " ++ name ++ "\n" ++ + ".type " ++ name ++ ", %function\n" ++ + name ++ ":\n" ++ body ++ "\n" ++ + name ++ "_end:"; +} + +fn get_range(start_p: *const u32, end_p: *const u32) []const u8 { + const start = @intFromPtr(start_p); + const end = @intFromPtr(end_p); + const ptr: [*]const u8 = @ptrCast(start_p); + + return ptr[0..(end - start)]; +} + +comptime { + asm (mk_fn("__kuser_get_tls", + \\ mcr p15, 0, r0, c13, c0, 3 + \\ bx lr + )); + + asm (mk_fn("__kuser_cmpxchg", + \\ ldr pc, .Lcmpxchg_impl + \\ .Lcmpxchg_impl: .word __kuser_cmpxchg_impl + )); + + asm (mk_fn("__kuser_cmpxchg_impl", + \\ stmfd sp!, {r4, lr} + \\ .Lcmpxchg_retry: + \\ ldr r4, [r2] + \\ cmp r4, r0 + \\ bne .Lcmpxchg_fail + \\ swp r3, r1, [r2] + \\ cmp r3, r0 + \\ bne .Lcmpxchg_retry + \\ mov r0, #0 + \\ ldmfd sp!, {r4, pc} + \\ .Lcmpxchg_fail: + \\ mov r0, #-1 + \\ ldmfd sp!, {r4, pc} + )); + + asm (mk_fn("__kuser_memory_barrier", + \\ mcr p15, 0, r0, c7, c10, 4 + \\ mcr p15, 0, r0, c7, c5, 4 + \\ bx lr + )); + + asm (mk_fn("__kuser_cmpxchg64", + \\ ldr pc, .Lcmpxchg64_impl + \\ .Lcmpxchg64_impl: .word __kuser_cmpxchg64_impl + )); + + asm (mk_fn("__kuser_cmpxchg64_impl", + \\ stmfd sp!, {r4, r5, r6, r7, lr} + \\ mov r7, r0 + \\ ldmia r1, {r4, r5} + \\ mov r6, r2 + \\ .Lcmpxchg64_retry: + \\ ldrexd r0, r1, [r6] + \\ ldmia r7, {r2, r3} + \\ teq r0, r2 + \\ teqeq r1, r3 + \\ bne .Lcmpxchg64_fail + \\ strexd r0, r4, r5, [r6] + \\ cmp r0, #0 + \\ bne .Lcmpxchg64_retry + \\ mov r0, #0 + \\ ldmfd sp!, {r4, r5, r6, r7, pc} + \\ .Lcmpxchg64_fail: + \\ mov r0, #-1 + \\ ldmfd sp!, {r4, r5, r6, r7, pc} + )); +} + +// 0xFFFF_0FFC +pub const helper_version_offset = 0x0FFC; +pub const helper_version: i32 = 2; + +// 0xFFFF_0FE0 +pub const get_tls_offset = 0x0FE0; +extern const __kuser_get_tls: u32; +extern const __kuser_get_tls_end: u32; +pub fn get_tls() []const u8 { + const slice = get_range(&__kuser_get_tls, &__kuser_get_tls_end); + if (slice.len > 28) @panic("too large get_tls"); + return slice; +} + +// 0xFFFF_0FC0 +pub const cmpxchg_offset = 0x0FC0; +pub extern const __kuser_cmpxchg: u32; +pub extern const __kuser_cmpxchg_end: u32; +pub fn cmpxchg() []const u8 { + const slice = get_range(&__kuser_cmpxchg, &__kuser_cmpxchg_end); + if (slice.len > 32) @panic("too large cmpxchg"); + return slice; +} + +// 0xFFFF_0FA0 +pub const memory_barrier_offset = 0x0FA0; +pub extern const __kuser_memory_barrier: u32; +pub extern const __kuser_memory_barrier_end: u32; +pub fn memory_barrier() []const u8 { + const slice = get_range(&__kuser_memory_barrier, &__kuser_memory_barrier_end); + if (slice.len > 32) @panic("too large memory_barrier"); + return slice; +} + +// 0xFFFF_0F60 +pub const cmpxchg64_offset = 0x0F60; +pub extern const __kuser_cmpxchg64: u32; +pub extern const __kuser_cmpxchg64_end: u32; +pub fn cmpxchg64() []const u8 { + const slice = get_range(&__kuser_cmpxchg64, &__kuser_cmpxchg64_end); + if (slice.len > 64) @panic("too large cmpxchg64"); + return slice; +} + +pub fn load(page: []u8) void { + @memset(page, 0); + + const version_ptr: *i32 = @ptrCast(@alignCast(page.ptr + helper_version_offset)); + version_ptr.* = helper_version; + + const tls_slice = get_tls(); + @memcpy(page[get_tls_offset..][0..tls_slice.len], tls_slice); + + const cmpxchg_slice = cmpxchg(); + @memcpy(page[cmpxchg_offset..][0..cmpxchg_slice.len], cmpxchg_slice); + + const membar_slice = memory_barrier(); + @memcpy(page[memory_barrier_offset..][0..membar_slice.len], membar_slice); + + const cmpxchg64_slice = cmpxchg64(); + @memcpy(page[cmpxchg64_offset..][0..cmpxchg64_slice.len], cmpxchg64_slice); +} diff --git a/sylveos/loader.zig b/sylveos/loader.zig @@ -3,6 +3,7 @@ const pi = @import("pi"); const process = @import("./process.zig"); const memory = @import("./memory.zig"); +const kuser = @import("./kuser.zig"); const Pages = @import("./pages.zig"); const Page = process.Page; @@ -19,6 +20,8 @@ pub const Program = struct { stack_pointer: u32, elf_header: std.elf.Header, header_location: u32, + heap_start: u32, + heap_current: u32, }; pub fn init(pt_alloc: std.mem.Allocator, heap_alloc: std.mem.Allocator, root: []mmu.FirstLevelDescriptor, elf: []const u8) !Program { @@ -40,17 +43,22 @@ pub fn init(pt_alloc: std.mem.Allocator, heap_alloc: std.mem.Allocator, root: [] }; var header_location: ?u32 = null; + var heap_start: u32 = 0; while (try it.next()) |program_header| { if (program_header.p_type != std.elf.PT_LOAD) continue; - var va = std.mem.alignBackward(u32, @intCast(program_header.p_vaddr), memory.KB4); + const alignment = @max(@as(u32, @truncate(program_header.p_align)), memory.KB4); + + var va = std.mem.alignBackward(u32, @intCast(program_header.p_vaddr), alignment); const end = std.mem.alignForward(u32, @intCast(program_header.p_vaddr + program_header.p_memsz), memory.KB4); + heap_start = @max(heap_start, end); + while (va < end) : (va += memory.KB4) { const pa = try memory.request_4kb(heap_alloc); - try pages.map_4kb(va, pa, .{}); + try pages.map_4kb(va, pa, .{ .encoding = .{ .preset = .OuterInnerWriteBackAllocOnWrite } }); // Zero page const dst: [*]u8 = @ptrFromInt(va); @@ -87,6 +95,7 @@ pub fn init(pt_alloc: std.mem.Allocator, heap_alloc: std.mem.Allocator, root: [] if (header_location == null) { // TODO; store header in VM + return error.NoHeader; } // Configure stack @@ -98,21 +107,26 @@ pub fn init(pt_alloc: std.mem.Allocator, heap_alloc: std.mem.Allocator, root: [] const sp = stack_top - stack_size; const pa = try memory.request_64kb(heap_alloc); - try pages.map_64kb(sp, pa, .{}); + try pages.map_64kb(sp, pa, .{ .encoding = .{ .preset = .OuterInnerWriteBackAllocOnWrite } }); const dst: [*]u8 = @ptrFromInt(sp); @memset(dst[0..memory.KB4], 0); } // kuser + { + const va = 0xFFFF_0000; const pa = try memory.request_4kb(heap_alloc); // TODO; read only - try pages.map_4kb(0xFFFF_0000, pa, .{}); - pi.mem.put_u32(@ptrFromInt(0xFFFF_0FFC), 2); + try pages.map_4kb(va, pa, .{ .encoding = .{ .preset = .OuterInnerWriteBackAllocOnWrite } }); + + kuser.load(@as([*]u8, @ptrFromInt(va))[0..memory.KB4]); } + heap_start = std.mem.alignForward(u32, heap_start, memory.KB4); + return .{ .heap_alloc = heap_alloc, .pt_alloc = pt_alloc, @@ -121,9 +135,14 @@ pub fn init(pt_alloc: std.mem.Allocator, heap_alloc: std.mem.Allocator, root: [] .stack_pointer = stack_top, .elf_header = header, .header_location = header_location orelse unreachable, + .heap_start = heap_start, + .heap_current = heap_start, }; } +// TODO; proper process handling +pub var current_program: *Program = undefined; + pub fn execute(self: *Program, pid: u24, args: []const []const u8, env: []const []const u8) !noreturn { // TODO; args, env self.pages.switch_into(pid); @@ -242,12 +261,17 @@ pub fn execute(self: *Program, pid: u24, args: []const []const u8, env: []const psr.i = false; const registers: pi.interrupts.Registers = .{ .gp = .{0} ** 13, - .lr = self.entrypoint, + .lr = 0, .pc = self.entrypoint, .sp = sp, .psr = psr, }; + current_program = self; + + // pi.debug.enable_monitor_mode(); + // pi.debug.set_breakpoint(0, 0, .IMVAMismatch); + pi.switching.restore_state_privileged(&registers); } diff --git a/sylveos/root.zig b/sylveos/root.zig @@ -4,7 +4,7 @@ const pi = @import("pi"); const memory = @import("./memory.zig"); const loader = @import("./loader.zig"); const syscall = @import("./syscall.zig"); -const hello_binary = @embedFile("./hello"); +const lua_binary = @embedFile("./lua"); const uart = pi.devices.mini_uart; const interrupts = pi.interrupts; @@ -49,11 +49,62 @@ fn panic_handler(msg: []const u8, trace_addr: ?usize) noreturn { pub const panic = std.debug.FullPanic(panic_handler); // figure out MMU issues -fn abort_handler(regs: interrupts.Registers, _: interrupts.ExceptionVector) void { +fn abort_handler(regs: interrupts.Registers, ev: interrupts.ExceptionVector) void { const far = pi.faults.FAR.get(); + const ifsr = pi.faults.IFSR.get(); + const dfsr = pi.faults.DFSR.get(); + + // pi.debug.set_breakpoint(0, regs.pc, .IMVAMismatch); + + if (ev == .PrefetchAbort and ifsr.status == .InstructionDebugEventFault) { + uart.print("[breakpoint] PC=0x{X:0>8}\n", .{regs.pc}); + } else if (ev == .PrefetchAbort) { + uart.print("[{s}] [{s}] got fault on 0x{X:0>8} from 0x{X:0>8}\n", .{ + @tagName(ev), + @tagName(ifsr.status), + far, + regs.pc, + }); + interrupts.dump_registers(&regs); + pi.reboot(); + } else if (ev == .DataAbort) { + const status = if (dfsr.status_kind == 0) @tagName(dfsr.status.@"0") else @tagName(dfsr.status.@"1"); + + uart.print("[{s}] [{s}] got fault on 0x{X:0>8} from 0x{X:0>8} (source: {s})\n", .{ + @tagName(ev), + status, + far, + regs.pc, + @tagName(dfsr.abort_source), + }); + interrupts.dump_registers(&regs); + pi.reboot(); + } - uart.print("got fault on 0x{X:0>8} from 0x{X:0>8}\n", .{ far, regs.pc }); - pi.reboot(); + if (regs.pc == 0x0006E674) { + uart.print("0x86034: {X}\n", .{pi.mem.get_u32(@ptrFromInt(0x86034))}); + interrupts.dump_registers(&regs); + } +} + +fn undef(regs: interrupts.Registers, _: interrupts.ExceptionVector) void { + if (regs.pc == 0x629c8) { + uart.print("\n=== DEBUGGING UDF ===\n", .{}); + uart.print("lr = 0x{X}\n", .{regs.lr}); + + const mem_88028: *const u32 = @ptrFromInt(0x88028); + uart.print("Memory at [0x88028] = 0x{X:0>8}\n", .{mem_88028.*}); + + const mem_88010: *const [8]u32 = @ptrFromInt(0x88010); + uart.print("Memory at [0x88010]:\n", .{}); + for (mem_88010.*, 0..) |val, i| { + uart.print(" [+0x{X}] = 0x{X:0>8}\n", .{ i * 4, val }); + } + + // Check if 0x88028 is executable + const translated = memory.translate(0x88028) catch 0; + uart.print("0x88028 translates to PA: 0x{X}\n", .{translated}); + } } // Kinda just a repeat of boot... @@ -100,6 +151,7 @@ export fn abort() noreturn { fn main() !void { interrupts.set_exception_handler(.SoftwareInterrupt, syscall.syscall_handler); + interrupts.set_exception_handler(.UndefinedInstruction, undef); var heap_fba = memory.get_allocator(); const heap_alloc = heap_fba.allocator(); @@ -109,11 +161,11 @@ fn main() !void { const pt = memory.get_page_table(); - var empty = try loader.init(pt_alloc, heap_alloc, pt, hello_binary); + var empty = try loader.init(pt_alloc, heap_alloc, pt, lua_binary); uart.print( \\Stack Pointer: {X:0>8} \\ Entrypoint: {X:0>8} \\ , .{ empty.stack_pointer, empty.entrypoint }); - try loader.execute(&empty, 0, &[_][]const u8{"hello"}, &[_][]u8{}); + try loader.execute(&empty, 0, &[_][]const u8{ "lua", "hello.lua" }, &[_][]u8{}); } diff --git a/sylveos/syscall.zig b/sylveos/syscall.zig @@ -1,50 +1,337 @@ +const std = @import("std"); const pi = @import("pi"); +const loader = @import("./loader.zig"); +const memory = @import("./memory.zig"); + +const switching = pi.switching; const interrupts = pi.interrupts; const uart = pi.devices.mini_uart; +var last_registers: interrupts.Registers = undefined; + +const Timespec = extern struct { + tv_sec: i64, + tv_nsec: u32, +}; + +const IoVec = extern struct { + iov_base: [*]u8, + iov_len: u32, +}; + +fn sbrk(addr: u32) u32 { + const current = loader.current_program.heap_current; + + if (addr == 0) { + return current; + } + + if (addr <= current) { + return current; // Already have enough + } + + // Align to page boundary + const old_end = std.mem.alignForward(u32, current, memory.KB4); + const new_end = std.mem.alignForward(u32, addr, memory.KB4); + + var va = old_end; + while (va < new_end) : (va += memory.KB4) { + const pa = memory.request_4kb(loader.current_program.heap_alloc) catch { + // Partial allocation - update to what we got + if (va > old_end) { + loader.current_program.heap_current = va; + } + return loader.current_program.heap_current; + }; + + loader.current_program.pages.map_4kb(va, pa, .{ + .encoding = .{ .preset = .OuterInnerWriteBackAllocOnWrite }, + }) catch { + if (va > old_end) { + loader.current_program.heap_current = va; + } + return loader.current_program.heap_current; + }; + + // Zero the new page + const page_ptr: [*]u8 = @ptrFromInt(va); + @memset(page_ptr[0..memory.KB4], 0); + } + + // Success - update heap end + loader.current_program.heap_current = new_end; + return new_end; +} + pub fn syscall_handler(registers: interrupts.Registers, _: interrupts.ExceptionVector) void { - const n = registers.gp[7]; + var regs = registers; + const n = regs.gp[7]; switch (n) { // restart_syscall - 0 => {}, + // 0 => {}, // exit - 1 => { - const error_code = registers.gp[0]; + 1, 248 => { + const error_code = regs.gp[0]; uart.print("[syscall] exit({d})\n", .{error_code}); pi.reboot(); }, // fork - 2 => {}, + // 2 => {}, // read - 3 => {}, + 3 => { + const fd = regs.gp[0]; + const buffer: [*]u8 = @ptrFromInt(regs.gp[1]); + const count = regs.gp[2]; + + uart.print("[syscall] read({d}, 0x{X}, {d})", .{ fd, @intFromPtr(buffer), count }); + + if (fd == 0) { + // stdin + for (0..count) |i| { + const byte = uart.read_byte_sync(); + buffer[i] = byte; + } + regs.gp[0] = @bitCast(@as(i32, @bitCast(count))); + } else if (fd == 3) { + // temporary buffer + const code = "print('hello world')"; + + @memcpy(buffer, code); + + regs.gp[0] = @bitCast(@as(i32, @bitCast(code.len))); + } + }, // write 4 => { - const fd = registers.gp[0]; - const buffer: [*]u8 = @ptrFromInt(registers.gp[1]); - const count = registers.gp[2]; + const fd = regs.gp[0]; + const buffer: [*]u8 = @ptrFromInt(regs.gp[1]); + const count = regs.gp[2]; - uart.print("[syscall] write({d}, 0x{X}, {d})\n", .{ fd, @intFromPtr(buffer), count }); + uart.print("[syscall] write({d}, 0x{X}, {d})", .{ fd, @intFromPtr(buffer), count }); // stdout if (fd == 1) { uart.print("{s}", .{buffer[0..count]}); + regs.gp[0] = @bitCast(@as(i32, @bitCast(count))); + } else { + regs.gp[0] = @bitCast(@as(i32, -9)); } }, // open - 5 => {}, + 5 => { + const path: [*:0]u8 = @ptrFromInt(regs.gp[0]); + const flags = regs.gp[1]; + + uart.print("[syscall] open({s}, 0x{X})", .{ path, flags }); + + regs.gp[0] = @bitCast(@as(i32, 3)); + }, // close - 6 => {}, + 6 => { + const fd = regs.gp[0]; + + uart.print("[syscall] close({d})", .{fd}); + + regs.gp[0] = 0; + }, + // brk + 45 => { + uart.print("[syscall] brk(0x{X})", .{regs.gp[0]}); + regs.gp[0] = sbrk(regs.gp[0]); + }, + // clone + 120 => { + uart.print("[syscall] clone()", .{}); + regs.gp[0] = @bitCast(@as(i32, -38)); + }, + // mprotect + 125 => { + // Trust me, totally protected + uart.print("[syscall] mprotect(0x{X})", .{regs.gp[0]}); + regs.gp[0] = 0; + }, + // readv + 145 => { + const fd = regs.gp[0]; + const iov_ptr: [*]IoVec = @ptrFromInt(regs.gp[1]); + const count = regs.gp[2]; + + uart.print("[syscall] readv({d}, 0x{X}, {d})", .{ fd, @intFromPtr(iov_ptr), count }); + + const iov = iov_ptr[0..count]; + + if (fd == 0) { + var total: u32 = 0; + for (iov) |vec| { + if (vec.iov_len == 0) continue; + + uart.print("{s}", .{vec.iov_base[0..vec.iov_len]}); + total += vec.iov_len; + } + regs.gp[0] = total; + } else if (fd == 3) { + // done by first read + regs.gp[0] = 0; + } else { + regs.gp[0] = @bitCast(@as(i32, -1)); + } + }, + // writev + 146 => { + const fd = regs.gp[0]; + const iov_ptr: [*]const IoVec = @ptrFromInt(regs.gp[1]); + const count = regs.gp[2]; + + uart.print("[syscall] writev({d}, 0x{X}, {d})", .{ fd, @intFromPtr(iov_ptr), count }); + + const iov = iov_ptr[0..count]; + + if (fd == 1 or fd == 2) { + var total: u32 = 0; + for (iov) |vec| { + if (vec.iov_len == 0) continue; + + uart.print("{s}", .{vec.iov_base[0..vec.iov_len]}); + total += vec.iov_len; + } + regs.gp[0] = total; + } else { + regs.gp[0] = @bitCast(@as(i32, -1)); + } + }, + // mmap2 + 192 => { + const addr = regs.gp[0]; + const length = regs.gp[1]; + const prot = regs.gp[2]; + const flags = regs.gp[3]; + const fd = regs.gp[4]; + const offset = regs.gp[5]; + + uart.print("[syscall] mmap2(0x{X}, {d}, 0x{X}, 0x{X}, {d}, {d})", .{ + addr, + length, + prot, + flags, + fd, + offset, + }); + + var translated: u32 = 0; + + if ((flags & 0x20) == 0) { + regs.gp[0] = @bitCast(@as(i32, -38)); + } else { + const aligned_length = std.mem.alignForward(u32, length, memory.KB4); + var map_addr: usize = undefined; + + if ((flags & 0x10) != 0) { + if (addr == 0 or !std.mem.isAligned(addr, memory.KB4)) { + regs.gp[0] = @bitCast(@as(i32, -22)); + } else { + translated = memory.translate(addr) catch 0; + + if (translated == 0) { + map_addr = addr; + } else { + // change permissions + // but we don't :3 + regs.gp[0] = addr; + } + } + } else { + map_addr = loader.current_program.heap_current; + } + + if (regs.gp[0] >= 0x80000000) {} else if (translated != 0 and (flags & 0x10) != 0) {} else { + var va = map_addr; + const end = map_addr + aligned_length; + + while (va < end) : (va += memory.KB4) { + const pa = memory.request_4kb(loader.current_program.heap_alloc) catch { + regs.gp[0] = @bitCast(@as(i32, -12)); + break; + }; + + loader.current_program.pages.map_4kb(va, pa, .{ .encoding = .{ .preset = .OuterInnerWriteBackAllocOnWrite } }) catch { + regs.gp[0] = @bitCast(@as(i32, -12)); + break; + }; + + if (prot != 0) { + const page_ptr: [*]u8 = @ptrFromInt(va); + @memset(page_ptr[0..memory.KB4], 0); + } + } + + if (va == end) { + if ((flags & 0x10) == 0) { + loader.current_program.heap_current = end; + } + regs.gp[0] = map_addr; + } + } + } + }, // exit_group - 248 => {}, + // 248 => {}, // set_tid_address - 256 => {}, + 256 => { + uart.print("[syscall] set_tid_address(0x{X})", .{regs.gp[0]}); + + const addr: ?*i32 = @ptrFromInt(regs.gp[0]); + if (addr) |ptr| { + ptr.* = 1; + } + regs.gp[0] = 1; + }, + // clock_gettime64 + 403 => { + uart.print("[syscall] clock_gettime64(0x{X})", .{regs.gp[0]}); + + const time = pi.devices.clock.current_count(); + const seconds = time / std.time.us_per_s; + const ns = (time % 1000) * 1000; + + const addr: ?*Timespec = @ptrFromInt(regs.gp[0]); + if (addr) |ptr| { + ptr.* = .{ .tv_nsec = @truncate(ns), .tv_sec = @intCast(seconds) }; + } + regs.gp[0] = 0; + }, // ARM_set_tls - 983045 => {}, + 983045 => { + const addr = regs.gp[0]; + + uart.print("[syscall] ARM_set_tls(0x{X})", .{addr}); + + asm volatile ("mcr p15, 0, %[value], c13, c0, 3" + : + : [value] "r" (addr), + ); + + regs.gp[0] = 0; + }, + // ARM_get_tls + 983046 => { + uart.print("[syscall] ARM_get_tls()", .{}); + + const result = asm volatile ("mrc p15, 0, %[result], c13, c0, 3" + : [result] "=r" (-> usize), + ); + regs.gp[0] = result; + }, else => { - uart.print("Unhandled syscall: {d}\n", .{n}); + uart.print("[syscall]: UNHANDLED({d})", .{n}); + regs.gp[0] = @bitCast(@as(i32, -1)); }, } + + uart.print(" = 0x{X}\n", .{regs.gp[0]}); + regs.pc += 4; + last_registers = regs; + switching.restore_state(&last_registers); }