282 lines
7.8 KiB
Zig
282 lines
7.8 KiB
Zig
const cpu = @import("cpu");
|
|
const lib = @import("lib");
|
|
const log = lib.log;
|
|
const privileged = @import("privileged");
|
|
const birth = @import("birth");
|
|
|
|
const assert = lib.assert;
|
|
|
|
const cr3 = privileged.arch.x86_64.registers.cr3;
|
|
|
|
const cr3_user_page_table_mask = 1 << @bitOffsetOf(cr3, "address");
|
|
const cr3_user_page_table_and_pcid_mask = cr3_user_page_table_mask | pcid_mask;
|
|
const pcid_bit = 11;
|
|
const pcid_mask = 1 << pcid_bit;
|
|
|
|
/// SYSCALL documentation
|
|
/// ABI:
|
|
/// - RAX: System call options (number for Linux)
|
|
/// - RCX: Return address
|
|
/// - R11: Saved rflags
|
|
/// - RDI: argument 0
|
|
/// - RSI: argument 1
|
|
/// - RDX: argument 2
|
|
/// - R10: argument 3
|
|
/// - R8: argument 4
|
|
/// - R9: argument 5
|
|
fn birthSyscall(comptime Syscall: type, raw_arguments: birth.syscall.Arguments) Syscall.ErrorSet.Error!Syscall.Result {
|
|
cpu.syscall_count += 1;
|
|
comptime assert(Syscall == birth.capabilities.Syscall(Syscall.capability, Syscall.command));
|
|
const capability: birth.capabilities.Type = Syscall.capability;
|
|
const command: birth.capabilities.Command(capability) = Syscall.command;
|
|
const arguments = try Syscall.toArguments(raw_arguments);
|
|
|
|
return if (cpu.user_scheduler.capability_root_node.hasPermissions(capability, command)) switch (capability) {
|
|
.io => switch (command) {
|
|
.copy, .mint, .retype, .delete, .revoke, .create => unreachable,
|
|
.log => blk: {
|
|
const message = arguments;
|
|
cpu.writer.writeAll(message) catch unreachable;
|
|
comptime assert(Syscall.Result == usize);
|
|
break :blk message.len;
|
|
},
|
|
},
|
|
.cpu => switch (command) {
|
|
.copy, .mint, .retype, .delete, .revoke, .create => unreachable,
|
|
.get_core_id => cpu.core_id,
|
|
.shutdown => cpu.shutdown(.success),
|
|
.get_command_buffer => {
|
|
const command_buffer = arguments;
|
|
_ = command_buffer;
|
|
@panic("TODO: get_command_buffer");
|
|
},
|
|
},
|
|
.cpu_memory => switch (command) {
|
|
.allocate => blk: {
|
|
comptime assert(@TypeOf(arguments) == usize);
|
|
const size = arguments;
|
|
const physical_region = try cpu.user_scheduler.capability_root_node.allocatePages(size);
|
|
try cpu.user_scheduler.capability_root_node.allocateCPUMemory(physical_region, .{ .privileged = false });
|
|
break :blk physical_region.address;
|
|
},
|
|
else => @panic(@tagName(command)),
|
|
},
|
|
.ram => unreachable,
|
|
.boot => switch (command) {
|
|
.get_bundle_size => cpu.bundle.len,
|
|
.get_bundle_file_list_size => cpu.bundle_files.len,
|
|
else => @panic(@tagName(command)),
|
|
},
|
|
.process => switch (command) {
|
|
.exit => switch (arguments) {
|
|
true => cpu.shutdown(.success),
|
|
false => cpu.panic("User process panicked", .{}),
|
|
},
|
|
else => @panic(@tagName(command)),
|
|
},
|
|
.page_table => @panic("TODO: page_table"),
|
|
} else error.forbidden;
|
|
}
|
|
|
|
export fn syscall(registers: *const Registers) callconv(.C) birth.syscall.Result {
|
|
const options = @as(birth.syscall.Options, @bitCast(registers.syscall_number));
|
|
const arguments = birth.syscall.Arguments{ registers.rdi, registers.rsi, registers.rdx, registers.r10, registers.r8, registers.r9 };
|
|
|
|
return switch (options.general.convention) {
|
|
.birth => switch (options.birth.type) {
|
|
inline else => |capability| switch (@as(birth.capabilities.Command(capability), @enumFromInt(options.birth.command))) {
|
|
inline else => |command| blk: {
|
|
const Syscall = birth.capabilities.Syscall(capability, command);
|
|
const result: Syscall.Result = birthSyscall(Syscall, arguments) catch |err| break :blk Syscall.errorToRaw(err);
|
|
break :blk Syscall.resultToRaw(result);
|
|
},
|
|
},
|
|
},
|
|
.linux => @panic("linux syscall"),
|
|
};
|
|
}
|
|
|
|
/// SYSCALL documentation
|
|
/// ABI:
|
|
/// - RAX: System call number
|
|
/// - RCX: Return address
|
|
/// - R11: Saved rflags
|
|
/// - RDI: argument 0
|
|
/// - RSI: argument 1
|
|
/// - RDX: argument 2
|
|
/// - R10: argument 3
|
|
/// - R8: argument 4
|
|
/// - R9: argument 5
|
|
pub fn entryPoint() callconv(.Naked) void {
|
|
asm volatile (
|
|
\\endbr64
|
|
\\swapgs
|
|
\\movq %rsp, user_stack(%rip)
|
|
);
|
|
|
|
if (cpu.arch.x86_64.kpti) {
|
|
asm volatile (
|
|
\\mov %cr3, %rsp
|
|
::: "memory");
|
|
|
|
if (cpu.arch.pcid) {
|
|
@compileError("pcid support not yet implemented");
|
|
}
|
|
|
|
asm volatile (
|
|
\\andq %[mask], %rsp
|
|
\\mov %rsp, %cr3
|
|
:
|
|
: [mask] "i" (~@as(u64, cr3_user_page_table_and_pcid_mask)),
|
|
: "memory"
|
|
);
|
|
}
|
|
|
|
// Safe stack
|
|
asm volatile ("movabsq %[capability_address_space_stack_top], %rsp"
|
|
:
|
|
: [capability_address_space_stack_top] "i" (cpu.arch.x86_64.capability_address_space_stack_top),
|
|
: "memory", "rsp"
|
|
);
|
|
|
|
asm volatile (
|
|
\\pushq %[user_ds]
|
|
\\pushq (user_stack)
|
|
\\pushq %r11
|
|
\\pushq %[user_cs]
|
|
\\pushq %rcx
|
|
\\pushq %rax
|
|
:
|
|
: [user_ds] "i" (cpu.arch.x86_64.user_data_selector),
|
|
[user_cs] "i" (cpu.arch.x86_64.user_code_selector),
|
|
: "memory"
|
|
);
|
|
|
|
// Push and clear registers
|
|
asm volatile (
|
|
// Push
|
|
\\pushq %rdi
|
|
\\pushq %rsi
|
|
\\pushq %rdx
|
|
\\pushq %rcx
|
|
\\pushq %rax
|
|
\\pushq %r8
|
|
\\pushq %r9
|
|
\\pushq %r10
|
|
\\pushq %r11
|
|
\\pushq %rbx
|
|
\\pushq %rbp
|
|
\\pushq %r12
|
|
\\pushq %r13
|
|
\\pushq %r14
|
|
\\pushq %r15
|
|
// Clear
|
|
\\xorl %esi, %esi
|
|
\\xorl %edx, %edx
|
|
\\xorl %ecx, %ecx
|
|
\\xorl %r8d, %r8d
|
|
\\xorl %r9d, %r9d
|
|
\\xorl %r10d, %r10d
|
|
\\xorl %r11d, %r11d
|
|
\\xorl %ebx, %ebx
|
|
\\xorl %ebp, %ebp
|
|
\\xorl %r12d, %r12d
|
|
\\xorl %r13d, %r13d
|
|
\\xorl %r14d, %r14d
|
|
\\xorl %r15d, %r15d
|
|
::: "memory");
|
|
|
|
// Pass arguments
|
|
asm volatile (
|
|
\\mov %rsp, %rdi
|
|
\\mov %rax, %rsi
|
|
::: "memory");
|
|
|
|
// TODO: more security stuff
|
|
asm volatile (
|
|
\\call syscall
|
|
::: "memory");
|
|
|
|
// TODO: more security stuff
|
|
|
|
// Pop registers
|
|
asm volatile (
|
|
\\popq %r15
|
|
\\popq %r14
|
|
\\popq %r13
|
|
\\popq %r12
|
|
\\popq %rbp
|
|
\\popq %rbx
|
|
\\popq %r11
|
|
\\popq %r10
|
|
\\popq %r9
|
|
\\popq %r8
|
|
\\popq %rcx
|
|
// RAX
|
|
\\popq %rcx
|
|
// RDX
|
|
\\popq %rsi
|
|
\\popq %rsi
|
|
\\popq %rdi
|
|
::: "memory");
|
|
|
|
if (cpu.arch.x86_64.kpti) {
|
|
// Restore CR3
|
|
asm volatile (
|
|
\\mov %cr3, %rsp
|
|
::: "memory");
|
|
|
|
if (cpu.arch.x86_64.pcid) {
|
|
@compileError("PCID not supported yet");
|
|
}
|
|
|
|
asm volatile (
|
|
\\orq %[user_cr3_mask], %rsp
|
|
\\mov %rsp, %cr3
|
|
:
|
|
: [user_cr3_mask] "i" (cr3_user_page_table_mask),
|
|
: "memory"
|
|
);
|
|
}
|
|
|
|
// Restore RSP
|
|
asm volatile (
|
|
\\mov user_stack(%rip), %rsp
|
|
::: "memory");
|
|
|
|
asm volatile (
|
|
\\swapgs
|
|
\\sysretq
|
|
::: "memory");
|
|
|
|
asm volatile (
|
|
\\int3
|
|
::: "memory");
|
|
|
|
unreachable;
|
|
}
|
|
|
|
pub const Registers = extern struct {
|
|
r15: u64,
|
|
r14: u64,
|
|
r13: u64,
|
|
r12: u64,
|
|
rbp: u64,
|
|
rbx: u64,
|
|
r11: u64,
|
|
r10: u64,
|
|
r9: u64,
|
|
r8: u64,
|
|
rax: u64,
|
|
rcx: u64,
|
|
rdx: u64,
|
|
rsi: u64,
|
|
rdi: u64,
|
|
syscall_number: u64,
|
|
rip: u64,
|
|
cs: u64,
|
|
rflags: u64,
|
|
rsp: u64,
|
|
ss: u64,
|
|
};
|