2022-12-07 10:40:02 +00:00
|
|
|
#include "arch/CPU.h"
|
2022-11-30 16:16:36 +00:00
|
|
|
#include "Log.h"
|
2022-11-19 19:01:01 +00:00
|
|
|
#include "arch/Timer.h"
|
2022-12-07 11:26:09 +00:00
|
|
|
#include "arch/x86_64/CPU.h"
|
2022-12-26 18:59:18 +00:00
|
|
|
#include "arch/x86_64/IO.h"
|
2022-12-07 16:39:59 +00:00
|
|
|
#include "memory/MemoryManager.h"
|
2023-01-05 21:39:56 +00:00
|
|
|
#include "sys/Syscall.h"
|
2022-12-07 14:04:02 +00:00
|
|
|
#include "thread/Scheduler.h"
|
2022-11-15 18:10:32 +00:00
|
|
|
#include <cpuid.h>
|
2022-12-16 19:40:04 +00:00
|
|
|
#include <luna/CString.h>
|
2022-12-26 18:59:18 +00:00
|
|
|
#include <luna/CircularQueue.h>
|
2022-12-07 11:26:09 +00:00
|
|
|
#include <luna/Result.h>
|
2022-12-04 11:42:43 +00:00
|
|
|
#include <luna/SystemError.h>
|
|
|
|
#include <luna/Types.h>
|
2022-11-15 18:10:32 +00:00
|
|
|
|
|
|
|
extern "C" void enable_sse();
|
|
|
|
extern "C" void enable_write_protect();
|
2022-11-15 19:41:59 +00:00
|
|
|
extern "C" void enable_nx();
|
|
|
|
|
2022-12-17 14:45:06 +00:00
|
|
|
extern void setup_gdt();
|
|
|
|
extern void remap_pic();
|
|
|
|
extern void pic_eoi(unsigned char irq);
|
|
|
|
extern void pic_eoi(Registers* regs);
|
|
|
|
extern void setup_idt();
|
2022-11-15 19:41:59 +00:00
|
|
|
|
2022-11-16 16:37:18 +00:00
|
|
|
// Interrupt handling
|
|
|
|
|
2022-11-18 19:51:25 +00:00
|
|
|
#define FIXME_UNHANDLED_INTERRUPT(name) \
|
2022-11-30 16:16:36 +00:00
|
|
|
kerrorln("FIXME(interrupt): %s", name); \
|
2022-11-18 19:51:25 +00:00
|
|
|
CPU::efficient_halt();
|
|
|
|
|
2023-01-06 16:31:22 +00:00
|
|
|
#define PF_PRESENT 1 << 0
|
|
|
|
#define PF_WRITE 1 << 1
|
|
|
|
#define PF_USER 1 << 2
|
|
|
|
#define PF_RESERVED 1 << 3
|
|
|
|
#define PF_NX_VIOLATION 1 << 4
|
|
|
|
|
|
|
|
void decode_page_fault_error_code(u64 code)
|
|
|
|
{
|
|
|
|
kwarnln("Fault details: %s | %s | %s%s%s", (code & PF_PRESENT) ? "Present" : "Not present",
|
|
|
|
(code & PF_WRITE) ? "Write access" : "Read access", (code & PF_USER) ? "User mode" : "Kernel mode",
|
|
|
|
(code & PF_RESERVED) ? " | Reserved bits set" : "", (code & PF_NX_VIOLATION) ? " | NX violation" : "");
|
|
|
|
}
|
|
|
|
|
2022-12-05 20:02:21 +00:00
|
|
|
[[noreturn]] void handle_page_fault(Registers* regs)
|
|
|
|
{
|
2022-12-24 10:49:47 +00:00
|
|
|
CPU::disable_interrupts();
|
|
|
|
|
2022-12-05 20:02:21 +00:00
|
|
|
u64 cr2;
|
|
|
|
asm volatile("mov %%cr2, %0" : "=r"(cr2));
|
|
|
|
kerrorln("Page fault at RIP %lx while accessing %lx!", regs->rip, cr2);
|
2022-12-07 16:39:59 +00:00
|
|
|
|
2023-01-06 16:31:22 +00:00
|
|
|
decode_page_fault_error_code(regs->error);
|
|
|
|
|
2022-12-07 17:11:24 +00:00
|
|
|
CPU::print_stack_trace_at(regs);
|
2022-12-07 16:39:59 +00:00
|
|
|
|
2022-12-05 20:02:21 +00:00
|
|
|
CPU::efficient_halt();
|
|
|
|
}
|
|
|
|
|
2022-12-24 10:49:47 +00:00
|
|
|
[[noreturn]] void handle_general_protection_fault(Registers* regs)
|
|
|
|
{
|
|
|
|
CPU::disable_interrupts();
|
|
|
|
|
|
|
|
kerrorln("General protection fault at RIP %lx, error code %lx!", regs->rip, regs->error);
|
|
|
|
|
|
|
|
CPU::print_stack_trace_at(regs);
|
|
|
|
|
|
|
|
CPU::efficient_halt();
|
|
|
|
}
|
|
|
|
|
2022-12-05 20:02:21 +00:00
|
|
|
extern "C" void handle_x86_exception(Registers* regs)
|
2022-11-16 16:37:18 +00:00
|
|
|
{
|
2022-11-18 19:51:25 +00:00
|
|
|
switch (regs->isr)
|
2022-11-16 16:37:18 +00:00
|
|
|
{
|
2022-11-18 19:51:25 +00:00
|
|
|
case 0: FIXME_UNHANDLED_INTERRUPT("Division by zero");
|
|
|
|
case 1: FIXME_UNHANDLED_INTERRUPT("Debug interrupt");
|
|
|
|
case 2: FIXME_UNHANDLED_INTERRUPT("NMI (Non-maskable interrupt)");
|
|
|
|
case 3: FIXME_UNHANDLED_INTERRUPT("Breakpoint");
|
|
|
|
case 4: FIXME_UNHANDLED_INTERRUPT("Overflow");
|
|
|
|
case 5: FIXME_UNHANDLED_INTERRUPT("Bound range exceeded");
|
|
|
|
case 6: FIXME_UNHANDLED_INTERRUPT("Invalid opcode");
|
|
|
|
case 7: FIXME_UNHANDLED_INTERRUPT("Device not available");
|
|
|
|
case 10: FIXME_UNHANDLED_INTERRUPT("Invalid TSS");
|
|
|
|
case 11: FIXME_UNHANDLED_INTERRUPT("Segment not present");
|
|
|
|
case 12: FIXME_UNHANDLED_INTERRUPT("Stack-segment fault");
|
2022-12-24 10:49:47 +00:00
|
|
|
case 13: handle_general_protection_fault(regs);
|
2022-12-05 20:02:21 +00:00
|
|
|
case 14: handle_page_fault(regs);
|
2022-11-18 19:51:25 +00:00
|
|
|
case 16: FIXME_UNHANDLED_INTERRUPT("x87 floating-point exception");
|
|
|
|
case 17: FIXME_UNHANDLED_INTERRUPT("Alignment check");
|
|
|
|
case 19: FIXME_UNHANDLED_INTERRUPT("SIMD floating-point exception");
|
|
|
|
case 20: FIXME_UNHANDLED_INTERRUPT("Virtualization exception");
|
|
|
|
case 21: FIXME_UNHANDLED_INTERRUPT("Control-protection exception");
|
|
|
|
default: FIXME_UNHANDLED_INTERRUPT("Reserved exception or #DF/#MC, which shouldn't call handle_x86_exception");
|
2022-11-16 16:37:18 +00:00
|
|
|
}
|
2022-11-18 19:51:25 +00:00
|
|
|
}
|
|
|
|
|
2022-12-26 18:59:18 +00:00
|
|
|
CircularQueue<u8, 60> scancode_queue;
|
|
|
|
|
|
|
|
void io_thread()
|
|
|
|
{
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
u8 scancode;
|
|
|
|
while (!scancode_queue.try_pop(scancode)) { kernel_sleep(10); }
|
|
|
|
|
|
|
|
kinfoln("Read scancode: %#hhx", scancode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-18 19:51:25 +00:00
|
|
|
// Called from _asm_interrupt_entry
|
|
|
|
extern "C" void arch_interrupt_entry(Registers* regs)
|
|
|
|
{
|
|
|
|
if (regs->isr < 32) handle_x86_exception(regs);
|
2022-12-26 18:59:18 +00:00
|
|
|
else if (regs->isr == 32) // Timer interrupt
|
2022-11-19 19:01:01 +00:00
|
|
|
{
|
|
|
|
Timer::tick();
|
2022-12-07 14:04:02 +00:00
|
|
|
if (should_invoke_scheduler()) Scheduler::invoke(regs);
|
2022-11-19 19:01:01 +00:00
|
|
|
pic_eoi(regs);
|
|
|
|
}
|
2022-12-26 18:59:18 +00:00
|
|
|
else if (regs->isr == 33) // Keyboard interrupt
|
|
|
|
{
|
|
|
|
u8 scancode = IO::inb(0x60);
|
|
|
|
scancode_queue.try_push(scancode);
|
|
|
|
pic_eoi(regs);
|
|
|
|
}
|
2023-01-05 21:39:56 +00:00
|
|
|
else if (regs->isr == 66) // System call
|
2023-01-05 20:53:48 +00:00
|
|
|
{
|
2023-01-05 21:39:56 +00:00
|
|
|
SyscallArgs args = { regs->rdi, regs->rsi, regs->rdx, regs->r10, regs->r8, regs->r9 };
|
|
|
|
regs->rax = (u64)invoke_syscall(regs, args, regs->rax);
|
2023-01-05 20:53:48 +00:00
|
|
|
}
|
2022-11-16 16:37:18 +00:00
|
|
|
else
|
|
|
|
{
|
2022-11-30 16:16:36 +00:00
|
|
|
kwarnln("IRQ catched! Halting.");
|
2022-11-16 16:37:18 +00:00
|
|
|
CPU::efficient_halt();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-18 19:51:25 +00:00
|
|
|
extern "C" [[noreturn]] void arch_double_fault()
|
|
|
|
{
|
2022-11-30 16:16:36 +00:00
|
|
|
kerrorln("ERROR: Catched double fault");
|
2022-11-18 19:51:25 +00:00
|
|
|
CPU::efficient_halt();
|
|
|
|
}
|
|
|
|
|
|
|
|
extern "C" [[noreturn]] void arch_machine_check()
|
|
|
|
{
|
2022-11-30 16:16:36 +00:00
|
|
|
kerrorln("ERROR: Machine check failed");
|
2022-11-18 19:51:25 +00:00
|
|
|
CPU::efficient_halt();
|
|
|
|
}
|
|
|
|
|
2022-11-15 19:41:59 +00:00
|
|
|
// Generic CPU code
|
|
|
|
|
|
|
|
static bool test_nx()
|
|
|
|
{
|
|
|
|
u32 __unused, edx = 0;
|
|
|
|
if (!__get_cpuid(0x80000001, &__unused, &__unused, &__unused, &edx)) return 0;
|
|
|
|
return edx & (1 << 20);
|
|
|
|
}
|
2022-11-15 18:10:32 +00:00
|
|
|
|
|
|
|
namespace CPU
|
|
|
|
{
|
|
|
|
Result<const char*> identify()
|
|
|
|
{
|
|
|
|
static char brand_string[49];
|
|
|
|
|
|
|
|
u32 buf[4];
|
2022-11-30 16:13:59 +00:00
|
|
|
if (!__get_cpuid(0x80000002, &buf[0], &buf[1], &buf[2], &buf[3])) return err(ENOTSUP);
|
2022-11-15 18:10:32 +00:00
|
|
|
memcpy(brand_string, buf, 16);
|
2022-11-30 16:13:59 +00:00
|
|
|
if (!__get_cpuid(0x80000003, &buf[0], &buf[1], &buf[2], &buf[3])) return err(ENOTSUP);
|
2022-11-15 18:10:32 +00:00
|
|
|
memcpy(&brand_string[16], buf, 16);
|
2022-11-30 16:13:59 +00:00
|
|
|
if (!__get_cpuid(0x80000004, &buf[0], &buf[1], &buf[2], &buf[3])) return err(ENOTSUP);
|
2022-11-15 18:10:32 +00:00
|
|
|
memcpy(&brand_string[32], buf, 16);
|
|
|
|
|
|
|
|
brand_string[48] = 0; // null-terminate it :)
|
|
|
|
|
|
|
|
return brand_string;
|
|
|
|
}
|
|
|
|
|
2022-11-18 20:04:53 +00:00
|
|
|
const char* platform_string()
|
|
|
|
{
|
|
|
|
return "x86_64";
|
|
|
|
}
|
|
|
|
|
2022-11-15 18:10:32 +00:00
|
|
|
void platform_init()
|
|
|
|
{
|
|
|
|
enable_sse();
|
2023-01-23 19:06:45 +00:00
|
|
|
// enable_write_protect();
|
2022-11-15 19:41:59 +00:00
|
|
|
if (test_nx()) enable_nx();
|
2022-12-26 11:11:10 +00:00
|
|
|
else
|
|
|
|
kwarnln("not setting the NX bit as it is unsupported");
|
2022-11-15 19:41:59 +00:00
|
|
|
setup_gdt();
|
|
|
|
setup_idt();
|
2022-11-15 18:10:32 +00:00
|
|
|
}
|
|
|
|
|
2022-11-19 19:01:01 +00:00
|
|
|
void platform_finish_init()
|
|
|
|
{
|
2022-12-26 18:59:18 +00:00
|
|
|
Scheduler::new_kernel_thread(io_thread).expect_value("Could not create the IO background thread!");
|
|
|
|
|
2022-11-19 19:01:01 +00:00
|
|
|
remap_pic();
|
|
|
|
}
|
|
|
|
|
|
|
|
void enable_interrupts()
|
|
|
|
{
|
|
|
|
asm volatile("sti");
|
|
|
|
}
|
|
|
|
|
|
|
|
void disable_interrupts()
|
|
|
|
{
|
|
|
|
asm volatile("cli");
|
|
|
|
}
|
|
|
|
|
|
|
|
void wait_for_interrupt()
|
|
|
|
{
|
|
|
|
asm volatile("hlt");
|
|
|
|
}
|
|
|
|
|
2022-11-15 18:10:32 +00:00
|
|
|
[[noreturn]] void efficient_halt() // Halt the CPU, using the lowest power possible. On x86-64 we do this using the
|
|
|
|
// "hlt" instruction, which puts the CPU into a low-power idle state until the
|
|
|
|
// next interrupt arrives... and we disable interrupts beforehand.
|
|
|
|
{
|
|
|
|
asm volatile("cli"); // Disable interrupts
|
|
|
|
loop:
|
|
|
|
asm volatile("hlt"); // Let the cpu rest and pause until the next interrupt arrives... which in this case should
|
|
|
|
// be never (unless an NMI arrives) :)
|
|
|
|
goto loop; // Safeguard: if we ever wake up, start our low-power rest again
|
|
|
|
}
|
2022-11-15 19:41:59 +00:00
|
|
|
|
2022-12-07 13:46:56 +00:00
|
|
|
[[noreturn]] void idle_loop()
|
2022-12-07 11:26:09 +00:00
|
|
|
{
|
|
|
|
asm volatile("sti");
|
|
|
|
loop:
|
|
|
|
asm volatile("hlt");
|
|
|
|
goto loop;
|
|
|
|
}
|
|
|
|
|
2022-11-15 19:41:59 +00:00
|
|
|
void switch_kernel_stack(u64 top)
|
|
|
|
{
|
|
|
|
task_state_segment.rsp[0] = top;
|
|
|
|
}
|
2022-12-07 16:39:59 +00:00
|
|
|
|
|
|
|
struct StackFrame
|
|
|
|
{
|
|
|
|
StackFrame* next;
|
|
|
|
u64 instruction;
|
|
|
|
};
|
|
|
|
|
2022-12-21 16:21:01 +00:00
|
|
|
static void backtrace_impl(u64 base_pointer, void (*callback)(u64, void*), void* arg)
|
2022-12-07 16:39:59 +00:00
|
|
|
{
|
2022-12-21 16:21:01 +00:00
|
|
|
StackFrame* current_frame = (StackFrame*)base_pointer;
|
2022-12-07 16:39:59 +00:00
|
|
|
// FIXME: Validate that the frame itself is readable, might span across multiple pages
|
2023-01-16 19:44:45 +00:00
|
|
|
while (current_frame && MemoryManager::validate_readable_page((u64)current_frame) && current_frame->instruction)
|
2022-12-07 16:39:59 +00:00
|
|
|
{
|
|
|
|
callback(current_frame->instruction, arg);
|
|
|
|
current_frame = current_frame->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-21 16:21:01 +00:00
|
|
|
void get_stack_trace(void (*callback)(u64, void*), void* arg)
|
|
|
|
{
|
|
|
|
u64 rbp;
|
|
|
|
asm volatile("mov %%rbp, %0" : "=r"(rbp));
|
|
|
|
return backtrace_impl(rbp, callback, arg);
|
|
|
|
}
|
|
|
|
|
2022-12-07 17:11:24 +00:00
|
|
|
void print_stack_trace()
|
|
|
|
{
|
|
|
|
u64 rbp;
|
|
|
|
int frame_index = 0;
|
|
|
|
asm volatile("mov %%rbp, %0" : "=r"(rbp));
|
2022-12-21 16:21:01 +00:00
|
|
|
return backtrace_impl(
|
|
|
|
rbp,
|
|
|
|
[](u64 instruction, void* arg) {
|
|
|
|
int* ptr = (int*)arg;
|
|
|
|
kinfoln("#%d at %p", *ptr, (void*)instruction);
|
|
|
|
(*ptr)++;
|
|
|
|
},
|
|
|
|
&frame_index);
|
2022-12-07 17:11:24 +00:00
|
|
|
}
|
|
|
|
|
2022-12-07 16:39:59 +00:00
|
|
|
void get_stack_trace_at(Registers* regs, void (*callback)(u64, void*), void* arg)
|
|
|
|
{
|
|
|
|
callback(regs->rip, arg);
|
2022-12-21 16:21:01 +00:00
|
|
|
return backtrace_impl(regs->rbp, callback, arg);
|
2022-12-07 16:39:59 +00:00
|
|
|
}
|
2022-12-07 17:11:24 +00:00
|
|
|
|
|
|
|
void print_stack_trace_at(Registers* regs)
|
|
|
|
{
|
|
|
|
int frame_index = 0;
|
|
|
|
get_stack_trace_at(
|
|
|
|
regs,
|
|
|
|
[](u64 instruction, void* arg) {
|
|
|
|
int* ptr = (int*)arg;
|
|
|
|
kinfoln("#%d at %p", *ptr, (void*)instruction);
|
|
|
|
(*ptr)++;
|
|
|
|
},
|
|
|
|
&frame_index);
|
|
|
|
}
|
2022-12-17 09:45:55 +00:00
|
|
|
|
|
|
|
void pause()
|
|
|
|
{
|
|
|
|
asm volatile("pause");
|
|
|
|
}
|
2022-12-07 14:04:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// called by kernel_yield
|
|
|
|
extern "C" void switch_task(Registers* regs)
|
|
|
|
{
|
|
|
|
Scheduler::switch_task(regs);
|
2023-01-02 12:07:29 +00:00
|
|
|
}
|