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
|
// int 0x80 syscall gate + handlers. ring 3 entry itself lives in sched.zig
// see: https://wiki.osdev.org/System_Calls
const serial = @import("serial.zig");
const idt = @import("idt.zig");
const sched = @import("sched.zig");
const regs = @import("regs.zig");
const bridge = @import("bridge");
const Frame = regs.Frame;
// scratch for a filename copied out of the caller legit one static buffer is fine
var name_buf: [64]u8 = undefined;
fn userName(ptr: u64) []const u8 {
const p: [*]const u8 = @ptrFromInt(ptr);
var i: usize = 0;
while (i < name_buf.len - 1 and p[i] != 0) : (i += 1) name_buf[i] = p[i];
return name_buf[0..i];
}
pub const SyscallFrame = extern struct {
r15: u64,
r14: u64,
r13: u64,
r12: u64,
r11: u64,
r10: u64,
r9: u64,
r8: u64,
rbp: u64,
rdi: u64,
rsi: u64,
rdx: u64,
rcx: u64,
rbx: u64,
rax: u64,
rip: u64,
cs: u64,
rflags: u64,
rsp: u64,
ss: u64,
};
// install the int 0x80 gate at dpl 3 so ring3 code is allowed to invoke it
pub fn installSyscall() void {
idt.setGate(0x80, @intFromPtr(&syscallStub), 3, 1); // ist1: big stack for vm calls
}
// 1- save gp regs
// 2- hand the frame to the zig handler
// 3- restore
// 4- iretq
export fn syscallStub() callconv(.naked) void {
asm volatile (regs.PUSH_GPRS ++
\\
\\ mov %rsp, %rdi
\\ call syscallHandler
\\
++ regs.POP_GPRS ++
\\
\\ iretq
);
}
export fn syscallHandler(frame: *SyscallFrame) callconv(.c) void {
switch (frame.rax) {
0 => { // exit(code): code in rdi
sched.exitCurrent();
asm volatile ("sti");
while (true) asm volatile ("hlt");
},
1 => { // write(ptr, len): rdi = user pointer, rsi = length
const len = frame.rsi;
if (len > 0 and len <= 4096) {
const bytes: [*]const u8 = @ptrFromInt(frame.rdi);
serial.write(bytes[0..len]);
}
frame.rax = len; // syscall return value
},
2 => { // read(): return a buffered byte, else block for one
if (sched.readReady()) |byte| {
frame.rax = byte;
return;
}
const resume_frame: Frame = .{
.r15 = frame.r15,
.r14 = frame.r14,
.r13 = frame.r13,
.r12 = frame.r12,
.r11 = frame.r11,
.r10 = frame.r10,
.r9 = frame.r9,
.r8 = frame.r8,
.rbp = frame.rbp,
.rdi = frame.rdi,
.rsi = frame.rsi,
.rdx = frame.rdx,
.rcx = frame.rcx,
.rbx = frame.rbx,
.rax = frame.rax,
.vector = 0,
.error_code = 0,
.rip = frame.rip,
.cs = frame.cs,
.rflags = frame.rflags,
.rsp = frame.rsp,
.ss = frame.ss,
};
sched.blockCurrentOnRead(resume_frame);
asm volatile ("sti");
while (true) asm volatile ("hlt");
},
4 => { // fs_size(name_ptr): returns size (like obv), or -1
frame.rax = @bitCast(bridge.fsSize(userName(frame.rdi)));
},
5 => { // fs_read(name_ptr, offset, buf_ptr, len): returns bytes read
const len = @min(frame.rcx, 4096);
const name = userName(frame.rdi);
const dst: [*]u8 = @ptrFromInt(frame.rdx);
frame.rax = bridge.fsReadInto(name, frame.rsi, dst[0..len]);
},
6 => { // fs_write(name_ptr, buf_ptr, len): returns 1 ok / 0 fail
const len = @min(frame.rdx, 64 * 1024);
const name = userName(frame.rdi);
const src: [*]const u8 = @ptrFromInt(frame.rsi);
frame.rax = if (bridge.fsStore(name, src[0..len])) 1 else 0;
},
else => serial.write("[syscall] unknown\r\n"),
}
}
|