+16
-2
components/root_server/src/main.zig
+16
-2
components/root_server/src/main.zig
···
2
2
const os = @import("os.zig");
3
3
4
4
export fn _start() callconv(.c) noreturn {
5
-
_ = os.syscall1(SYS_poke, 0xB16B00B5BADBABE);
6
-
_ = os.syscall1(SYS_exit, 0x69696969);
5
+
// _ = os.syscall1(SYS_poke, 0xB16B00B5BADBABE);
6
+
// _ = os.syscall1(SYS_exit, 0x69696969);
7
+
asm volatile (
8
+
\\ mov $0x69696969, %%rdi
9
+
\\ xor %%rsi, %%rsi
10
+
\\ xor %%rbx, %%rbx
11
+
\\ mainloop:
12
+
\\ xor %%rax, %%rax
13
+
\\ delayloop:
14
+
\\ inc %%rax
15
+
\\ cmp $0x4000000, %%rax
16
+
\\ jnz delayloop
17
+
\\ inc %%rbx
18
+
\\ syscall
19
+
\\ jmp mainloop
20
+
);
7
21
8
22
die();
9
23
}
+31
-50
components/ukernel/arch/amd64/boot.zig
+31
-50
components/ukernel/arch/amd64/boot.zig
···
5
5
const console = @import("console");
6
6
const flanterm = @import("flanterm");
7
7
const log = std.log.scoped(.amd64_init);
8
-
const Idt = arch.structures.Idt;
9
8
const StandardGdt = arch.structures.gdt.StandardGdt;
10
9
const Tss = arch.structures.tss.Tss;
11
10
···
77
76
arch.per_cpu_init_data.init(limine_requests.mp.response.?.cpu_count);
78
77
79
78
// Install the IDT
80
-
initIdt();
79
+
arch.interrupts.idt.init();
81
80
82
81
// Set up our own GDT and TSS
83
82
const gdt = &arch.per_cpu_init_data.gdt_buf[0];
···
85
84
const tss = &arch.per_cpu_init_data.tss_buf[0];
86
85
// TSS rsp 0x3800
87
86
tss.* = .{
88
-
.rsp0 = 0x3800,
89
-
.rsp1 = 0x3800,
90
-
.rsp2 = 0x3800,
87
+
.rsp0 = 0x7ffe_0000_8000,
88
+
.rsp1 = 0x7ffe_0000_8000,
89
+
.rsp2 = 0x7ffe_0000_8000,
91
90
};
92
91
93
92
gdt.tss_desc.set_tss_addr(tss);
···
117
116
118
117
// Allocate a stack (0x3000 - 0x4000)
119
118
common.mm.paging.map(.{
120
-
.vaddr = 0x3000,
121
-
.size = 0x1000,
119
+
.vaddr = 0x7ffe_0000_0000,
120
+
.size = 65536,
122
121
.memory_type = .MemoryWriteBack,
123
122
.perms = .{
124
123
.x = false,
···
136
135
137
136
init_syscalls();
138
137
139
-
enter_userspace(entry, 0x69, 0x4000);
138
+
enter_userspace(entry, 0x69, 0x7ffe_0001_0000);
140
139
}
141
140
142
141
// Get ready for system calls (set MSRs)
···
311
310
}
312
311
}
313
312
314
-
pub fn initIdt() void {
315
-
const idt_addr: usize = @intFromPtr(arch.per_cpu_init_data.idt);
316
-
317
-
// Install the known exception handlers
318
-
arch.per_cpu_init_data.idt.breakpoint.installHandler(breakpoint_handler);
319
-
arch.per_cpu_init_data.idt.double_fault.installHandler(double_fault);
320
-
arch.per_cpu_init_data.idt.general_protection_fault.installHandler(gpf);
321
-
arch.per_cpu_init_data.idt.page_fault.installHandler(page_fault);
322
-
arch.per_cpu_init_data.idt.interrupts[0xFF - 32].installHandler(arch.interrupts.apic.spurious_interrupt_handler);
323
-
arch.per_cpu_init_data.idt.interrupts[48 - 32].installHandler(arch.interrupts.apic.periodic_handler);
324
-
325
-
// Load the Idt Register
326
-
const reg: Idt.Idtr = .{ .addr = idt_addr, .limit = @sizeOf(Idt) - 1 };
327
-
reg.load();
328
-
}
329
-
330
313
// TODO: update the type reflection thing to make a custom
331
314
// function type for the ISR
332
315
pub const PageFaultErrorCode = packed struct(u64) {
···
345
328
return @bitCast(self.*);
346
329
}
347
330
};
348
-
pub fn page_fault(stack_frame: *arch.structures.Idt.InterruptStackFrame, err_code_u64: u64) callconv(.{ .x86_64_interrupt = .{} }) void {
349
-
const err_code: PageFaultErrorCode = @bitCast(err_code_u64);
350
-
log.err("PAGE FAULT @ 0x{x:0>16}, code 0x{x}!!!!!!!!!!!", .{ stack_frame.instruction_pointer, err_code.val() });
351
-
const cr2 = arch.registers.ControlRegisters.Cr2.read();
352
-
switch (err_code.write) {
353
-
true => log.err("Tried to write to vaddr 0x{x:0>16}", .{cr2}),
354
-
false => log.err("Tried to read from vaddr 0x{x:0>16}", .{cr2}),
355
-
}
356
-
log.err("dying...", .{});
357
-
arch.instructions.die();
358
-
}
331
+
// pub fn page_fault(stack_frame: *arch.structures.Idt.InterruptStackFrame, err_code_u64: u64) callconv(.{ .x86_64_interrupt = .{} }) void {
332
+
// const err_code: PageFaultErrorCode = @bitCast(err_code_u64);
333
+
// log.err("PAGE FAULT @ 0x{x:0>16}, code 0x{x}!!!!!!!!!!!", .{ stack_frame.instruction_pointer, err_code.val() });
334
+
// const cr2 = arch.registers.ControlRegisters.Cr2.read();
335
+
// switch (err_code.write) {
336
+
// true => log.err("Tried to write to vaddr 0x{x:0>16}", .{cr2}),
337
+
// false => log.err("Tried to read from vaddr 0x{x:0>16}", .{cr2}),
338
+
// }
339
+
// log.err("dying...", .{});
340
+
// arch.instructions.die();
341
+
// }
359
342
360
-
pub fn breakpoint_handler(stack_frame: *Idt.InterruptStackFrame) callconv(.{ .x86_64_interrupt = .{} }) void {
361
-
log.warn("Breakpoint @ 0x{x:0>16}, returning execution...", .{stack_frame.instruction_pointer});
362
-
}
343
+
// pub fn breakpoint_handler(stack_frame: *Idt.InterruptStackFrame) callconv(.{ .x86_64_interrupt = .{} }) void {
344
+
// log.warn("Breakpoint @ 0x{x:0>16}, returning execution...", .{stack_frame.instruction_pointer});
345
+
// }
363
346
364
-
pub fn gpf(stack_frame: *Idt.InterruptStackFrame, err_code: u64) callconv(.{ .x86_64_interrupt = .{} }) void {
365
-
log.warn("gpf @ 0x{x:0>16} ERR CODE {}, returning execution...", .{ stack_frame.instruction_pointer, err_code });
366
-
arch.instructions.die();
367
-
}
347
+
// pub fn gpf(stack_frame: *Idt.InterruptStackFrame, err_code: u64) callconv(.{ .x86_64_interrupt = .{} }) void {
348
+
// log.warn("gpf @ 0x{x:0>16} ERR CODE {}, returning execution...", .{ stack_frame.instruction_pointer, err_code });
349
+
// arch.instructions.die();
350
+
// }
368
351
369
-
pub fn double_fault(stack_frame: *Idt.InterruptStackFrame, err_code: u64) callconv(.{ .x86_64_interrupt = .{} }) noreturn {
370
-
log.err("FATAL DOUBLE FAULT @ 0x{x:0>16}, code 0x{x}!!!!!!!!!!!", .{ stack_frame.instruction_pointer, err_code });
371
-
log.err("dying...", .{});
372
-
arch.instructions.die();
373
-
}
352
+
// pub fn double_fault(stack_frame: *Idt.InterruptStackFrame, err_code: u64) callconv(.{ .x86_64_interrupt = .{} }) noreturn {
353
+
// log.err("FATAL DOUBLE FAULT @ 0x{x:0>16}, code 0x{x}!!!!!!!!!!!", .{ stack_frame.instruction_pointer, err_code });
354
+
// log.err("dying...", .{});
355
+
// arch.instructions.die();
356
+
// }
374
357
375
358
fn bootstrapAPs() void {
376
359
log.info("Bootstrapping APs...", .{});
···
382
365
383
366
fn ap_init(mp_info: *limine.SmpMpFeature.MpInfo) callconv(.c) noreturn {
384
367
// Set up the IDT
385
-
const idt_addr: usize = @intFromPtr(arch.per_cpu_init_data.idt);
386
-
const reg: Idt.Idtr = .{ .addr = idt_addr, .limit = @sizeOf(Idt) - 1 };
387
-
reg.load();
368
+
arch.interrupts.idt.load();
388
369
389
370
// Set up our GDT and TSS
390
371
const gdt = &arch.per_cpu_init_data.gdt_buf[mp_info.processor_id];
+6
-3
components/ukernel/arch/amd64/interrupts/apic.zig
+6
-3
components/ukernel/arch/amd64/interrupts/apic.zig
···
234
234
// .priority_class = 0,
235
235
// .priority_sub_class = 0,
236
236
// });
237
+
arch.interrupts.idt.add_handler(.{ .interrupt = 0xFF }, spurious_interrupt_handler, 3, 0);
238
+
arch.interrupts.idt.add_handler(.{ .interrupt = 48 }, periodic_handler, 3, 0);
237
239
}
238
240
239
241
pub fn calibrateTimer() void {
···
291
293
}
292
294
};
293
295
294
-
pub fn spurious_interrupt_handler(_: *arch.structures.Idt.InterruptStackFrame) callconv(.{ .x86_64_interrupt = .{} }) void {
296
+
pub fn spurious_interrupt_handler(_: *arch.interrupts.idt.InterruptFrame(u64)) callconv(.{ .x86_64_sysv = .{} }) void {
295
297
log.warn("Got a spurious interrupt!", .{});
296
298
}
297
299
298
-
pub fn periodic_handler(_: *arch.structures.Idt.InterruptStackFrame) callconv(.{ .x86_64_interrupt = .{} }) void {
299
-
log.warn("Got an APIC timer interrupt!", .{});
300
+
pub fn periodic_handler(stack_trace: *arch.interrupts.idt.InterruptFrame(u64)) callconv(.{ .x86_64_sysv = .{} }) void {
301
+
log.warn("Got an APIC timer interrupt, incrementing user's %rsi...", .{});
302
+
stack_trace.regs.rsi += 1;
300
303
singleton.setRegister(.eoi, 0);
301
304
}
+310
components/ukernel/arch/amd64/interrupts/idt.zig
+310
components/ukernel/arch/amd64/interrupts/idt.zig
···
1
+
const arch = @import("../root.zig");
2
+
const std = @import("std");
3
+
const interrupts = arch.interrupts;
4
+
const StandardGdt = arch.structures.gdt.StandardGdt;
5
+
6
+
// The actual IDT memory
7
+
const entry_count = 256;
8
+
export var interrupt_descriptor_table: [entry_count]Entry = undefined;
9
+
10
+
// Pointers to the actual ISRs which the global interrupt handler call
11
+
// Each IDT entry pushes the interrupt number then calls the global
12
+
// handler, which pushes more information then calls the user
13
+
// defined handler. Use common sense and don't return from an exception
14
+
// which shouldn't be returned from.
15
+
const DefinedHandler = *const fn (*InterruptFrame(u64)) callconv(.{ .x86_64_sysv = .{} }) void;
16
+
pub export var defined_handlers: [entry_count]DefinedHandler = undefined;
17
+
18
+
// The actual handlers with addresses in the IDT.
19
+
const ActualHandler = *const fn () callconv(.naked) void;
20
+
const actual_handlers: [entry_count]ActualHandler = blk: {
21
+
@setEvalBranchQuota(100000000);
22
+
var ret: [entry_count]ActualHandler = undefined;
23
+
for (0..256) |i| {
24
+
ret[i] = make_actual_handler(.{ .interrupt = i });
25
+
}
26
+
break :blk ret;
27
+
};
28
+
29
+
fn make_actual_handler(comptime interrupt: Interrupt) ActualHandler {
30
+
var asm_code: []const u8 = "";
31
+
// Make the stack consistent
32
+
if (!interrupt.has_error_code()) {
33
+
asm_code = asm_code ++ "pushq $0\n";
34
+
}
35
+
// Push the interrupt number
36
+
asm_code = asm_code ++ std.fmt.comptimePrint("pushq ${}\n", .{interrupt.interrupt});
37
+
// Jump to the common interrupt handler code
38
+
asm_code = asm_code ++ "jmp _int_handler_common";
39
+
const code = asm_code;
40
+
41
+
const tmp_struct = struct {
42
+
fn actual_handler() callconv(.naked) void {
43
+
asm volatile (code);
44
+
}
45
+
};
46
+
return tmp_struct.actual_handler;
47
+
}
48
+
49
+
// The global assembly for the common interrupt handler
50
+
comptime {
51
+
// Construct the push and pop instructions from SavedRegisters
52
+
var push_instrs: []const u8 = "\n";
53
+
var pop_instrs: []const u8 = "\n";
54
+
55
+
const saved_regs = @typeInfo(SavedRegisters).@"struct".fields;
56
+
for (saved_regs) |saved_reg| {
57
+
// We must prepend to push because pushes are basically
58
+
// building the struct in reverse order, so reverse the effects
59
+
push_instrs = "\n pushq %" ++ saved_reg.name ++ push_instrs;
60
+
// Of course, pop in the opposite order as push
61
+
pop_instrs = pop_instrs ++ "popq %" ++ saved_reg.name ++ "\n";
62
+
}
63
+
64
+
asm (
65
+
\\ .global _int_handler_common
66
+
\\ .type _int_handler_common, @function
67
+
\\ _int_handler_common:
68
+
69
+
// Push the general purpose registers and then CR3
70
+
++ push_instrs ++
71
+
\\ mov %cr3, %rax
72
+
\\ pushq %rax
73
+
74
+
// Now, rsp points to the start of InterruptFrame. Read int_num and call into an offset of
75
+
// the defined handlers list after setting the first arg to the created structure.
76
+
++ std.fmt.comptimePrint("\nmov {}(%rsp), %rcx\n", .{@offsetOf(InterruptFrame(u64), "int_num")}) ++
77
+
\\ mov %rsp, %rdi
78
+
\\ callq *defined_handlers(, %rcx, 8)
79
+
80
+
// Skip the stack all the way down to the general purpose
81
+
// registers, then pop all of them
82
+
// TODO: restore CR3??
83
+
++ std.fmt.comptimePrint("\nadd ${}, %rsp\n", .{@offsetOf(InterruptFrame(u64), "regs")}) ++ pop_instrs ++
84
+
// Skip the interrupt number and error code and iretq
85
+
\\ add $16, %rsp
86
+
\\ iretq
87
+
);
88
+
}
89
+
90
+
/// IDT Register
91
+
const Idtr = packed struct(u80) {
92
+
limit: u16,
93
+
addr: u64,
94
+
95
+
/// Load the IDT Register
96
+
pub fn load(self: *const Idtr) void {
97
+
asm volatile ("lidt (%[idtr_addr])"
98
+
:
99
+
: [idtr_addr] "r" (self),
100
+
);
101
+
}
102
+
};
103
+
104
+
// A raw IDT entry
105
+
const Entry = extern struct {
106
+
func_low: u16,
107
+
gdt_selector: u16,
108
+
options: Options,
109
+
func_mid: u16,
110
+
func_high: u32,
111
+
_reserved0: u32 = 0,
112
+
113
+
pub const Options = packed struct(u16) {
114
+
ist_index: u3,
115
+
_reserved0: u5 = 0,
116
+
disable_interrupts: bool = true,
117
+
// type: Type,
118
+
must_be_one: u3 = 0b111,
119
+
must_be_zero: u1 = 0,
120
+
dpl: u2,
121
+
present: bool,
122
+
};
123
+
124
+
pub const Owner = enum {
125
+
kernel,
126
+
user,
127
+
};
128
+
129
+
const Self = @This();
130
+
pub fn init(func_ptr: usize, dpl: u2, ist: u3) Entry {
131
+
// _ = typ;
132
+
return .{
133
+
.func_low = @truncate(func_ptr),
134
+
.gdt_selector = StandardGdt.selectors.kernel_code,
135
+
.options = .{
136
+
.ist_index = ist,
137
+
.dpl = dpl,
138
+
.present = true,
139
+
},
140
+
.func_mid = @truncate(func_ptr >> 16),
141
+
.func_high = @truncate(func_ptr >> 32),
142
+
};
143
+
}
144
+
145
+
// Changes the address without changing anything else
146
+
pub fn set_func(self: *Self, ptr: usize) void {
147
+
self.func_low = @truncate(ptr);
148
+
self.func_mid = @truncate(ptr >> 16);
149
+
self.func_high = @truncate(ptr >> 32);
150
+
}
151
+
};
152
+
153
+
/// A selector error code indexing into the GDT, IDT, or LDT.
154
+
/// Used in a general protection fault for example.
155
+
pub const SelectorErrorCode = packed struct(u64) {
156
+
external: bool,
157
+
interrupt: bool,
158
+
// Only valid if not interrupt
159
+
type: enum(u1) {
160
+
gdt = 0,
161
+
ldt = 1,
162
+
},
163
+
idx: u13,
164
+
_reserved0: u48 = 0,
165
+
166
+
pub const Target = union(enum) {
167
+
interrupt: Interrupt,
168
+
gdt_sel: u16,
169
+
ldt_sel: u13,
170
+
};
171
+
172
+
const Self = @This();
173
+
pub fn parse(self: Self) Target {
174
+
return switch (self.interrupt) {
175
+
true => .{ .interrupt = @enumFromInt(self.idx) },
176
+
false => switch (self.type) {
177
+
.gdt => .{ .gdt_sel = self.idx },
178
+
.ldt => .{ .ldt_sel = self.idx },
179
+
},
180
+
};
181
+
}
182
+
};
183
+
184
+
/// List of the general built in exceptions
185
+
pub const Exception = enum(u8) {
186
+
divide_error = 0x00,
187
+
debug_exeption = 0x01,
188
+
non_maskable_interrupt = 0x02,
189
+
breakpoint = 0x03,
190
+
overflow = 0x04,
191
+
bound_range_exceeded = 0x05,
192
+
invalid_opcode = 0x06,
193
+
device_not_available = 0x07,
194
+
double_fault = 0x08,
195
+
// _coprocessor_segment_overrun = 0x09,
196
+
invalid_tss = 0x0a,
197
+
segment_not_present = 0x0b,
198
+
stack_segment_fault = 0x0c,
199
+
general_protection_fault = 0x0d,
200
+
page_fault = 0x0e,
201
+
// _reserved0 = 0x0f,
202
+
x87_floating_point = 0x10,
203
+
alignment_check = 0x11,
204
+
machine_check = 0x12,
205
+
simd_floating_point = 0x13,
206
+
virtualization = 0x14,
207
+
control_protection = 0x15,
208
+
hypervisor = 0x1c,
209
+
vmm = 0x1d,
210
+
security_fault = 0x1e,
211
+
_,
212
+
213
+
fn has_error_code(self: Exception) bool {
214
+
return switch (self) {
215
+
.double_fault, .invalid_tss, .segment_not_present, .general_protection_fault, .page_fault, .security_fault => true,
216
+
else => false,
217
+
};
218
+
}
219
+
};
220
+
221
+
pub const Interrupt = packed union {
222
+
exception: Exception,
223
+
interrupt: u8,
224
+
225
+
pub fn has_error_code(self: Interrupt) bool {
226
+
return self.exception.has_error_code();
227
+
}
228
+
};
229
+
230
+
/// Basically all the general purpose registers except rsp,
231
+
/// because RSP is already in the InterruptFrame. Ordered
232
+
/// in the order of the X.Reg from the instruction encoding lol
233
+
pub const SavedRegisters = extern struct {
234
+
rax: u64,
235
+
rcx: u64,
236
+
rdx: u64,
237
+
rbx: u64,
238
+
// rsp: u64,
239
+
rbp: u64,
240
+
rsi: u64,
241
+
rdi: u64,
242
+
r8: u64,
243
+
r9: u64,
244
+
r10: u64,
245
+
r11: u64,
246
+
r12: u64,
247
+
r13: u64,
248
+
r14: u64,
249
+
r15: u64,
250
+
};
251
+
252
+
/// The Interrupt frame which we help generate
253
+
pub fn InterruptFrame(comptime ErrorCode: type) type {
254
+
if (@bitSizeOf(ErrorCode) != 64) {
255
+
@compileError("ErrorCode for InterruptFrame must be exactly 64 bits!");
256
+
}
257
+
return extern struct {
258
+
// CR3
259
+
cr3: u64,
260
+
// All the general purpose registers
261
+
regs: SavedRegisters align(8),
262
+
// The interrupt number
263
+
int_num: Interrupt align(8),
264
+
// Pushed by the CPU (error_code may be pushed by us)
265
+
error_code: ErrorCode,
266
+
rip: u64,
267
+
cs: u16 align(8),
268
+
eflags: u64,
269
+
rsp: u64,
270
+
ss: u16 align(8),
271
+
};
272
+
}
273
+
274
+
// Initialize the IDT with the default unhandled exception
275
+
pub fn init() void {
276
+
// Set every IDT entry to the corresponding ActualHandler
277
+
for (0..entry_count) |i| {
278
+
const actual_handler = @intFromPtr(actual_handlers[i]);
279
+
interrupt_descriptor_table[i] = Entry.init(actual_handler, 3, 0);
280
+
}
281
+
// Now, set every defined handler to the default one
282
+
@memset(&defined_handlers, arch.interrupts.unhandled_interrupt);
283
+
284
+
// Finally, load the idt
285
+
load();
286
+
287
+
add_handler(.{ .exception = .breakpoint }, arch.interrupts.breakpoint, 3, 0);
288
+
add_handler(.{ .exception = .double_fault }, arch.interrupts.double_fault, 3, 0);
289
+
add_handler(.{ .exception = .general_protection_fault }, arch.interrupts.general_protection_fault, 3, 0);
290
+
add_handler(.{ .exception = .page_fault }, arch.mm.paging.page_fault_handler, 3, 0);
291
+
}
292
+
293
+
pub fn load() void {
294
+
const idtr: Idtr = .{
295
+
.addr = @intFromPtr(&interrupt_descriptor_table),
296
+
.limit = 0xFFF,
297
+
};
298
+
idtr.load();
299
+
}
300
+
301
+
pub fn add_handler(interrupt: Interrupt, handler: anytype, dpl: u2, ist: u3) void {
302
+
// Modify the type, dpl, and ist in place
303
+
var tmp = interrupt_descriptor_table[interrupt.interrupt];
304
+
tmp.options.dpl = dpl;
305
+
tmp.options.ist_index = ist;
306
+
interrupt_descriptor_table[interrupt.interrupt] = tmp;
307
+
308
+
// Add the DefinedHandler
309
+
defined_handlers[interrupt.interrupt] = @ptrCast(&handler);
310
+
}
+9
components/ukernel/arch/amd64/interrupts/pic.zig
+9
components/ukernel/arch/amd64/interrupts/pic.zig
···
1
1
/// Remap the 8259 PIC to an interrupt base of 0x32
2
2
const arch = @import("../root.zig");
3
+
const std = @import("std");
4
+
const log = std.log.scoped(.pic);
3
5
const out = arch.port.out;
4
6
const in = arch.port.in;
5
7
···
48
50
wait();
49
51
out(u8, PIC_TWO_DATA_PORT, 0b1111_1111);
50
52
wait();
53
+
54
+
// Set up a spurious IRQ7 handler
55
+
arch.interrupts.idt.add_handler(.{ .interrupt = 32 + 7 }, spurious_handler, 3, 0);
51
56
}
52
57
53
58
inline fn wait() void {
···
58
63
pub fn end_of_timer_interrupt() void {
59
64
out(u8, PIC_ONE_CMD_PORT, CMD_EOI);
60
65
}
66
+
67
+
pub fn spurious_handler(_: *arch.interrupts.idt.InterruptFrame(u64)) callconv(.{ .x86_64_sysv = .{} }) void {
68
+
std.log.warn("Got a spurious IRQ7 (8259)", .{});
69
+
}
+22
components/ukernel/arch/amd64/interrupts/root.zig
+22
components/ukernel/arch/amd64/interrupts/root.zig
···
1
1
pub const apic = @import("apic.zig");
2
2
pub const pic = @import("pic.zig");
3
3
pub const pit = @import("pit.zig");
4
+
pub const idt = @import("idt.zig");
5
+
const std = @import("std");
6
+
const arch = @import("../root.zig");
4
7
5
8
pub inline fn enable() void {
6
9
asm volatile ("sti");
···
8
11
pub inline fn disable() void {
9
12
asm volatile ("cli");
10
13
}
14
+
15
+
pub fn unhandled_interrupt(stack_frame: *idt.InterruptFrame(u64)) callconv(.{ .x86_64_sysv = .{} }) void {
16
+
std.log.err("Unhandled interrupt (0x{x})!!! rip = 0x{x}", .{ stack_frame.int_num.interrupt, stack_frame.rip });
17
+
arch.instructions.die();
18
+
}
19
+
20
+
pub fn breakpoint(stack_frame: *idt.InterruptFrame(u64)) callconv(.{ .x86_64_sysv = .{} }) void {
21
+
std.log.warn("Breakpoint @ 0x{x}, returning execution...", .{stack_frame.rip});
22
+
}
23
+
24
+
pub fn double_fault(stack_frame: *idt.InterruptFrame(u64)) callconv(.{ .x86_64_sysv = .{} }) void {
25
+
std.log.err("Double fault @ 0x{x}, dying!!!", .{stack_frame.rip});
26
+
arch.instructions.die();
27
+
}
28
+
29
+
pub fn general_protection_fault(stack_frame: *idt.InterruptFrame(idt.SelectorErrorCode)) callconv(.{ .x86_64_sysv = .{} }) void {
30
+
std.log.warn("General Protection Fault @ 0x{x} (Error Code {}), returning execution...", .{ stack_frame.rip, stack_frame.error_code });
31
+
arch.instructions.die();
32
+
}
+6
components/ukernel/arch/amd64/mm/paging.zig
+6
components/ukernel/arch/amd64/mm/paging.zig
···
3
3
const std = @import("std");
4
4
const Cr3 = arch.registers.ControlRegisters.Cr3;
5
5
const Cr4 = arch.registers.ControlRegisters.Cr4;
6
+
const idt = arch.interrupts.idt;
6
7
const Perms = common.mm.paging.Perms;
7
8
8
9
pub const PageTable = extern struct {
···
329
330
len: usize,
330
331
ptr: usize,
331
332
};
333
+
334
+
pub fn page_fault_handler(stack_frame: *idt.InterruptFrame(u64)) callconv(.{ .x86_64_sysv = .{} }) void {
335
+
std.log.err("Page Fault @ 0x{x}, dying...", .{stack_frame.rip});
336
+
arch.instructions.die();
337
+
}
+2
-11
components/ukernel/arch/amd64/root.zig
+2
-11
components/ukernel/arch/amd64/root.zig
···
26
26
const PerCpuInitData = struct {
27
27
const StandardGdt = structures.gdt.StandardGdt;
28
28
const Tss = structures.tss.Tss;
29
-
const Idt = structures.Idt;
30
29
31
30
gdt_buf: []StandardGdt = undefined,
32
31
tss_buf: []Tss = undefined,
33
-
idt: *Idt = undefined,
34
32
35
33
const Self = @This();
36
34
pub fn init(self: *Self, cpu_count: u64) void {
37
-
// 1. Allocate an IDT
38
-
const idt_addr = common.init_data.bootmem.allocMem(@sizeOf(Idt)) catch |err| {
39
-
std.log.err("init PerCpuInitData: IDT alloc failed: {}", .{err});
40
-
@panic("rip bozo");
41
-
};
42
-
self.idt = @ptrFromInt(idt_addr);
43
-
44
-
// 2. Allocate space for GDT and TSS data
35
+
// 1. Allocate space for GDT and TSS data
45
36
const gdt_size = @sizeOf(StandardGdt);
46
37
const tss_size = @sizeOf(Tss);
47
38
···
51
42
@panic("rip bozo");
52
43
});
53
44
54
-
// 3. Transmute and fill out the structure
45
+
// 2. Transmute and fill out the structure
55
46
const gdt_buf: [*]StandardGdt = @ptrCast(@alignCast(buf[0 .. gdt_size * cpu_count]));
56
47
const tss_buf: [*]Tss = @ptrCast(@alignCast(buf[gdt_size * cpu_count ..][0 .. tss_size * cpu_count]));
57
48
self.gdt_buf = gdt_buf[0..cpu_count];
-170
components/ukernel/arch/amd64/structures/Idt.zig
-170
components/ukernel/arch/amd64/structures/Idt.zig
···
1
-
//! The entire Interrupt Descriptor Table (IDT) structure for AMD64,
2
-
//! including all the necessary ISR entries. Each of the defined
3
-
//! ISRs is meant for a specific type of exception, while the
4
-
//! array at the end of the IDT can be used for whatever is necessary.
5
-
const std = @import("std");
6
-
const arch = @import("../root.zig");
7
-
const StandardGdt = arch.structures.gdt.StandardGdt;
8
-
9
-
/// Faulty division (mostly divide by zero)
10
-
divide_error: Entry(.handler),
11
-
/// AMD64 Debug Exception, either a fault or a trap
12
-
debug_exception: Entry(.handler),
13
-
/// Non Maskable Interrupt
14
-
non_maskable_interrupt: Entry(.handler),
15
-
/// Breakpoint (int3) trap
16
-
breakpoint: Entry(.handler),
17
-
/// Overflow trap (INTO instruction)
18
-
overflow: Entry(.handler),
19
-
/// Bound Range Exception (BOUND instruction)
20
-
bound_range_exceeded: Entry(.handler),
21
-
/// Invalid Opcode Exception
22
-
invalid_opcode: Entry(.handler),
23
-
/// Device Not Available (FPU instructions when FPU disabled)
24
-
device_not_available: Entry(.handler),
25
-
/// Double Fault Exception
26
-
double_fault: Entry(.abort_with_err_code),
27
-
_coprocessor_segment_overrun: Entry(.handler),
28
-
/// Invalid TSS: bad segment selector
29
-
invalid_tss: Entry(.handler_with_err_code),
30
-
/// Segment Not Present
31
-
segment_not_present: Entry(.handler_with_err_code),
32
-
/// Stack Segment Fault
33
-
stack_segment_fault: Entry(.handler_with_err_code),
34
-
/// General Protection Fault
35
-
general_protection_fault: Entry(.handler_with_err_code),
36
-
/// Page Fault
37
-
page_fault: Entry(.handler_with_err_code),
38
-
39
-
_reserved1: Entry(.handler),
40
-
/// x87 Floating Point Exception
41
-
x87_floating_point: Entry(.handler),
42
-
/// Alignment Check Exception
43
-
alignment_check: Entry(.handler_with_err_code),
44
-
/// Machine Check Exception (MCE)
45
-
machine_check: Entry(.abort),
46
-
/// SIMD Floating Point Exception
47
-
simd_floating_point: Entry(.handler),
48
-
/// Virtualization Exception
49
-
virtualization: Entry(.handler),
50
-
/// Control Protection Exception
51
-
control_protection: Entry(.handler_with_err_code),
52
-
_reserved2: [10]Entry(.handler),
53
-
/// User Accessible Interrupts
54
-
interrupts: [256 - 32]Entry(.handler),
55
-
56
-
/// An ISR Entry in the IDT
57
-
pub const EntryType = union(enum) {
58
-
abort: void,
59
-
abort_with_err_code: void,
60
-
handler: void,
61
-
handler_with_err_code: void,
62
-
handler_with_custom_err_code: type,
63
-
};
64
-
pub fn Entry(comptime entry_type: EntryType) type {
65
-
const return_type = switch (entry_type) {
66
-
.abort, .abort_with_err_code => noreturn,
67
-
.handler, .handler_with_err_code, .handler_with_custom_err_code => void,
68
-
};
69
-
const params: []const std.builtin.Type.Fn.Param = switch (entry_type) {
70
-
.handler, .abort => &.{
71
-
// Interrupt stack frame
72
-
.{ .is_generic = false, .is_noalias = false, .type = *InterruptStackFrame },
73
-
},
74
-
.handler_with_err_code, .abort_with_err_code => &.{
75
-
// Interrupt stack frame
76
-
.{ .is_generic = false, .is_noalias = false, .type = *InterruptStackFrame },
77
-
// Error code
78
-
.{ .is_generic = false, .is_noalias = false, .type = u64 },
79
-
},
80
-
.handler_with_custom_err_code => |err_code_type| &.{
81
-
// Interrupt stack frame
82
-
.{ .is_generic = false, .is_noalias = false, .type = *InterruptStackFrame },
83
-
// Custom Error code
84
-
.{ .is_generic = false, .is_noalias = false, .type = err_code_type },
85
-
},
86
-
};
87
-
const FunctionTypeInfo: std.builtin.Type = .{
88
-
.@"fn" = .{
89
-
.calling_convention = .{ .x86_64_interrupt = .{} },
90
-
.is_generic = false,
91
-
.is_var_args = false,
92
-
.return_type = return_type,
93
-
.params = params,
94
-
},
95
-
};
96
-
97
-
// The actual IDT entry structure
98
-
return extern struct {
99
-
func_low: u16,
100
-
gdt_selector: u16,
101
-
options: Options,
102
-
func_mid: u16,
103
-
func_high: u32,
104
-
_reserved: u32 = 0,
105
-
106
-
const FuncType = @Type(FunctionTypeInfo);
107
-
108
-
pub const Options = packed struct(u16) {
109
-
/// Interrupt Stack Table Index
110
-
ist_index: u3,
111
-
_reserved: u5 = 0,
112
-
disable_interrupts: bool,
113
-
must_be_one: u3 = 0b111,
114
-
must_be_zero: u1 = 0,
115
-
/// Descriptor Privilege Level
116
-
dpl: u2,
117
-
present: bool,
118
-
};
119
-
120
-
const Self = @This();
121
-
122
-
pub fn installHandler(self: *Self, func: *const FuncType) void {
123
-
// Fetch the Code Segment
124
-
const func_ptr = @intFromPtr(func);
125
-
self.* = .{
126
-
// Set the function pointer
127
-
.func_low = @truncate(func_ptr & 0xFFFF),
128
-
.func_mid = @truncate((func_ptr >> 16) & 0xFFFF),
129
-
.func_high = @truncate((func_ptr >> 32) & 0xFFFF_FFFF),
130
-
.gdt_selector = StandardGdt.selectors.kernel_code,
131
-
.options = .{
132
-
// No Interrupt Stack Table yet
133
-
.ist_index = 0,
134
-
// Mask interrupts while running ISR handler
135
-
.disable_interrupts = true,
136
-
// Ring 3 Minimum privilege level
137
-
.dpl = 3,
138
-
// Mark as present
139
-
.present = true,
140
-
},
141
-
};
142
-
}
143
-
};
144
-
}
145
-
146
-
/// IDT Register
147
-
pub const Idtr = packed struct(u80) {
148
-
limit: u16,
149
-
addr: u64,
150
-
151
-
/// Load the IDT Register
152
-
pub fn load(self: *const Idtr) void {
153
-
asm volatile ("lidt (%[idtr_addr])"
154
-
:
155
-
: [idtr_addr] "r" (self),
156
-
);
157
-
}
158
-
};
159
-
160
-
/// Interrupt Stack Frame
161
-
/// TODO: maybe move this somewhere else
162
-
pub const InterruptStackFrame = extern struct {
163
-
instruction_pointer: u64,
164
-
code_segment: u16,
165
-
_reserved1: [6]u8,
166
-
cpu_flags: u64,
167
-
stack_pointer: u64,
168
-
stack_segment: u16,
169
-
_reserved2: [6]u8,
170
-
};
-1
components/ukernel/arch/amd64/structures/root.zig
-1
components/ukernel/arch/amd64/structures/root.zig