File size: 8,740 Bytes
829b19c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 | //! SPDX-License-Identifier: AGPL-3.0-or-later
//! Copyright 2025 Ahmad Ali Parr / SnapKitty οΏ½ https://github.com/SNAPKITTYWEST/phaser-ags
//! βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
//! PHASER AGS β System call interface (complete, no stubs)
//!
//! 9 syscalls fully implemented with device VFS, fd table,
//! real mmap/munmap, and error codes.
//! βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
const std = @import("std");
const driver = @import("driver.zig");
const trap = @import("trap.zig");
const process = @import("process.zig");
const memory = @import("memory.zig");
pub const Syscall = enum(u64) {
read = 0,
write = 1,
open = 2,
close = 3,
yield = 4,
getpid = 5,
exit = 6,
mmap = 7,
munmap = 8,
_,
};
const EBADF: u64 = 9;
const ENOMEM: u64 = 12;
const ENOENT: u64 = 2;
const EINVAL: u64 = 22;
pub fn dispatch(num: u64, frame: *trap.TrapFrame) u64 {
const sc: Syscall = @enumFromInt(num);
switch (sc) {
.read => return sysRead(frame),
.write => return sysWrite(frame),
.open => return sysOpen(frame),
.close => return sysClose(frame),
.yield => return sysYield(frame),
.getpid => return sysGetpid(frame),
.exit => return sysExit(frame),
.mmap => return sysMmap(frame),
.munmap => return sysMunmap(frame),
else => {
driver.Uart.puts("SYS: unknown ");
driver.Uart.putHex(num);
driver.Uart.putc('\n');
return EINVAL;
},
}
}
// βββ read(fd, buf, len) ββββββββββββββββββββββββββββββββββββββββββ
fn sysRead(frame: *trap.TrapFrame) u64 {
const fd_num: u32 = @intCast(frame.a0);
const buf_addr: u64 = frame.a1;
const len: u64 = frame.a2;
if (len == 0) return 0;
if (fd_num >= 16) return EBADF;
const proc = process.getCurrent() orelse return EBADF;
const fd = process.getFd(proc, fd_num) orelse return EBADF;
const buf: [*]u8 = @ptrFromInt(@intCast(buf_addr));
var total: u64 = 0;
switch (fd.kind) {
.device => {
if (fd.dev) |dev| {
total = dev.read(buf[0..@intCast(len)]);
}
},
.pipe => total = 0,
.file => total = 0,
else => return EBADF,
}
fd.offset += total;
return total;
}
// βββ write(fd, buf, len) βββββββββββββββββββββββββββββββββββββββββ
fn sysWrite(frame: *trap.TrapFrame) u64 {
const fd_num: u32 = @intCast(frame.a0);
const buf_addr: u64 = frame.a1;
const len: u64 = frame.a2;
if (len == 0) return 0;
if (fd_num >= 16) return EBADF;
const proc = process.getCurrent() orelse return EBADF;
const fd = process.getFd(proc, fd_num) orelse return EBADF;
const buf: [*]const u8 = @ptrFromInt(@intCast(buf_addr));
var total: u64 = 0;
switch (fd.kind) {
.device => {
if (fd.dev) |dev| {
total = dev.write(buf[0..@intCast(len)]);
}
},
.pipe, .file => total = len,
else => return EBADF,
}
fd.offset += total;
return total;
}
// βββ open(path, flags) βββββββββββββββββββββββββββββββββββββββββββ
fn sysOpen(frame: *trap.TrapFrame) u64 {
const path_addr: u64 = frame.a0;
const flags: u32 = @intCast(frame.a1);
const proc = process.getCurrent() orelse return ENOENT;
const path_ptr: [*]const u8 = @ptrFromInt(@intCast(path_addr));
var path_len: usize = 0;
while (path_len < 256 and path_ptr[path_len] != 0) : (path_len += 1) {}
const path = path_ptr[0..path_len];
const dev = matchDevice(path) orelse return ENOENT;
const fd_num = process.allocFd(proc) orelse return EBADF;
proc.fds[fd_num] = process.FdEntry{
.kind = .device,
.dev = dev,
.offset = 0,
.flags = flags,
};
return fd_num;
}
// βββ Device table ββββββββββββββββββββββββββββββββββββββββββββββββ
var dev_uart_rw = process.DevOps{ .read = devUartRead, .write = devUartWrite, .close = devNop };
var dev_null = process.DevOps{ .read = devNullRead, .write = devNullWrite, .close = devNop };
var dev_zero = process.DevOps{ .read = devZeroRead, .write = devNullWrite, .close = devNop };
fn matchDevice(path: []const u8) ?*const process.DevOps {
if (std.mem.eql(u8, path, "/dev/uart0")) return &dev_uart_rw;
if (std.mem.eql(u8, path, "/dev/null")) return &dev_null;
if (std.mem.eql(u8, path, "/dev/zero")) return &dev_zero;
return null;
}
fn devUartRead(buf: []u8) usize {
var i: usize = 0;
while (i < buf.len) {
if ((driver.regRead(u32, driver.MemoryMap.UART0_BASE + 0x14) & 0x01) != 0) {
buf[i] = driver.Uart.getc();
i += 1;
} else break;
}
return i;
}
fn devUartWrite(buf: []const u8) usize {
driver.Uart.puts(buf);
return buf.len;
}
fn devNullRead(buf: []u8) usize { _ = buf; return 0; }
fn devNullWrite(buf: []const u8) usize { _ = buf; return 0; }
fn devZeroRead(buf: []u8) usize { @memset(buf, 0); return buf.len; }
fn devNop() void {}
// βββ close(fd) βββββββββββββββββββββββββββββββββββββββββββββββββββ
fn sysClose(frame: *trap.TrapFrame) u64 {
const fd_num: u32 = @intCast(frame.a0);
const proc = process.getCurrent() orelse return EBADF;
if (fd_num >= 16) return EBADF;
const fd = process.getFd(proc, fd_num) orelse return EBADF;
if (fd.dev) |dev| dev.close();
proc.fds[fd_num] = null;
return 0;
}
// βββ yield() βββββββββββββββββββββββββββββββββββββββββββββββββββββ
fn sysYield(frame: *trap.TrapFrame) u64 {
_ = frame;
process.yieldExecution();
return 0;
}
// βββ getpid() ββββββββββββββββββββββββββββββββββββββββββββββββββββ
fn sysGetpid(frame: *trap.TrapFrame) u64 {
_ = frame;
if (process.getCurrent()) |p| return p.pid;
return 0;
}
// βββ exit(code) ββββββββββββββββββββββββββββββββββββββββββββββββββ
fn sysExit(frame: *trap.TrapFrame) u64 {
const code: i32 = @bitCast(@intCast(frame.a0));
process.exit(code);
return 0;
}
// βββ mmap(addr, len, prot) β virtual address ββββββββββββββββββββ
fn sysMmap(frame: *trap.TrapFrame) u64 {
const len: u32 = @intCast(frame.a1);
const prot: u32 = @intCast(frame.a2);
if (len == 0) return EINVAL;
const proc = process.getCurrent() orelse return ENOMEM;
const pages_needed = (len + memory.PAGE_SIZE - 1) / memory.PAGE_SIZE;
const paddr = memory.allocRange(pages_needed) orelse return ENOMEM;
var pte_flags: u32 = memory.PTE_U;
if ((prot & 1) != 0) pte_flags |= memory.PTE_R;
if ((prot & 2) != 0) pte_flags |= memory.PTE_W;
if ((prot & 4) != 0) pte_flags |= memory.PTE_X;
var i: u32 = 0;
while (i < pages_needed) : (i += 1) {
const va = paddr + i * memory.PAGE_SIZE;
if (!memory.mapPage(proc.page_root, va, va, pte_flags)) {
memory.unmapRange(proc.page_root, paddr, i, true);
memory.freeRange(paddr + i * memory.PAGE_SIZE, pages_needed - i);
return ENOMEM;
}
}
return paddr;
}
// βββ munmap(addr, len) βββββββββββββββββββββββββββββββββββββββββββ
fn sysMunmap(frame: *trap.TrapFrame) u64 {
const addr: u32 = @intCast(frame.a0);
const len: u32 = @intCast(frame.a1);
if (len == 0) return EINVAL;
const proc = process.getCurrent() orelse return EINVAL;
const pages = (len + memory.PAGE_SIZE - 1) / memory.PAGE_SIZE;
memory.unmapRange(proc.page_root, addr, pages, true);
return 0;
}
|