feat: setup gdt and idt and handle basic ISRs

This commit is contained in:
2023-10-01 19:49:56 +02:00
parent f79e6e10ac
commit 295c11e107
11 changed files with 511 additions and 3 deletions

View File

@@ -75,12 +75,26 @@ if (NOT IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/${CMAKE_SYSTEM_PRO
message(FATAL_ERROR "Unknown architecture ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/${CMAKE_SYSTEM_PROCESSOR}")
endif ()
file(GLOB PLATFORM_SPECIFIC_SOURCES src/platform/${CMAKE_SYSTEM_PROCESSOR}/*.c)
file(GLOB PLATFORM_SPECIFIC_ASM_SOURCES src/platform/${CMAKE_SYSTEM_PROCESSOR}/*.asm)
file(GLOB_RECURSE PLATFORM_SPECIFIC_SOURCES src/platform/${CMAKE_SYSTEM_PROCESSOR}/*.c)
file(GLOB_RECURSE PLATFORM_SPECIFIC_ASM_SOURCES src/platform/${CMAKE_SYSTEM_PROCESSOR}/*.asm)
file(GLOB CRTI src/platform/generic/crt/crti.c)
file(GLOB CRTN src/platform/generic/crt/crtn.c)
file(GLOB_RECURSE PYTHON_GENERATORS src/**/*.generator.py)
foreach (GENERATOR IN LISTS PYTHON_GENERATORS)
get_filename_component(GENERATED_BARE ${GENERATOR} NAME_WE)
add_custom_command(
COMMAND python3 ${GENERATOR}
OUTPUT ${GENERATED_BARE}.c
COMMENT "Generates ${GENERATED_BARE}.c"
)
list(APPEND GENERATED_SOURCES ${GENERATED_BARE}.c)
endforeach ()
# Define aggregate sources
set(KERNEL_SOURCE_FILES
${KERNEL_SOURCES}
@@ -89,6 +103,7 @@ set(KERNEL_SOURCE_FILES
${PLATFORM_GENERIC_SOURCES}
${PLATFORM_SPECIFIC_SOURCES}
${PLATFORM_SPECIFIC_ASM_SOURCES}
${GENERATED_SOURCES}
)
# Define executable

View File

@@ -0,0 +1,12 @@
//
// Created by rick on 30-9-23.
//
#ifndef YAK_CPU_H
#define YAK_CPU_H
void gdt_init();
void idt_init();
#endif //YAK_CPU_H

View File

@@ -0,0 +1,60 @@
//
// Created by rick on 22-3-23.
//
#ifndef YAK_GDT_H
#define YAK_GDT_H
#include <stdint.h>
typedef struct {
uint64_t base;
uint32_t limit;
uint8_t access;
uint8_t flags;
} gdt_segment_c_t;
typedef struct gdt_segment {
uint16_t limit1;
uint16_t base1;
uint8_t base2;
uint8_t access_byte;
uint8_t limit2: 4;
uint8_t flags: 4;
uint8_t base3;
uint32_t base4;
uint32_t: 32; // reserved
} __attribute__((packed)) gdt_segment_t;
_Static_assert(sizeof(gdt_segment_t) == 16, "GDT must be 128 bits/16 bytes");
typedef struct gdtr {
uint16_t limit;
uint64_t pointer;
} __attribute__((packed)) gdtr_t;
#define ACCESS_BYTE_PRESENT (0b1<<7)
#define ACCESS_BYTE_DPL_0 (0b00 << 5)
#define ACCESS_BYTE_DPL_1 (0b01 << 5)
#define ACCESS_BYTE_DPL_2 (0b10 << 5)
#define ACCESS_BYTE_DPL_3 (0b11 << 5)
#define ACCESS_BYTE_CODE_DATA (0b1 << 4)
#define ACCESS_BYTE_EXECUTABLE (0b1 << 3)
#define ACCESS_BYTE_DC (0b1 << 2)
#define ACCESS_BYTE_RW (0b1 << 1)
#define ACCESS_BYTE_ACCESSED (0b1 << 0)
#define GDT_FLAG_GRANULARITY (1 << 3)
#define GDT_FLAG_SIZE_32 (1 << 2)
#define GDT_FLAG_LONG (1 << 1)
#define GDT_FLAG_RESERVED (1 << 0)
#define GDT_OFFSET_NULL 0x00
#define GDT_OFFSET_KERNEL_CODE 0x10
#define GDT_OFFSET_KERNEL_DATA 0x20
#define GDT_OFFSET_USER_32_CODE 0x30
#define GDT_OFFSET_USER_32_DATA 0x40
#define GDT_OFFSET_USER_64_CODE 0x50
#define GDT_OFFSET_USER_64_DATA 0x60
#define GDT_OFFSET_TSS 0x38
#endif //YAK_GDT_H

View File

@@ -0,0 +1,25 @@
#include <stdint.h>
#define IDT_MAX_DESCRIPTORS 256
typedef struct {
uint16_t isr_low; // The lower 16 bits of the ISR's address
uint16_t kernel_cs; // The GDT segment selector that the CPU will load into CS before calling the ISR
uint8_t ist; // The IST in the TSS that the CPU will load into RSP; set to zero for now
uint8_t attributes; // Type and attributes; see the IDT page
uint16_t isr_mid; // The higher 16 bits of the lower 32 bits of the ISR's address
uint32_t isr_high; // The higher 32 bits of the ISR's address
uint32_t reserved; // Set to zero
} __attribute__((packed)) idt_entry_t;
typedef struct {
uint16_t limit;
uint64_t base;
} __attribute((packed)) idtr_t;
typedef struct {
uint64_t ds; // pushed in common handler
uint64_t r15, r14, r13, r12, r11, r10, r9, r8, rdi, rsi, rbx, rdx, rcx, rax; // pusha/popa
uint64_t int_no, err_code; // pushed by handlers
uint64_t rip, cs, rflags, userrsp, ss; // isr/iret
} isr_registers_t;

View File

@@ -0,0 +1,24 @@
.code64
.global gdt_reload_segments
gdt_reload_segments:
pushq %rbp
movq %rsp, %rbp
# Reload data segment registers
movw $0x20,%ax # 0x10 is a stand-in for your data segment
movw %ax,%ds
movw %ax,%es
movw %ax,%fs
movw %ax,%gs
movw %ax,%ss
# Reload CS register:
push $0x10 # Push code segment to stack, 0x08 is a stand-in for your code segment
leaq .reload_CS, %rax
pushq %rax
lretq
.reload_CS:
popq %rbp
ret

View File

@@ -0,0 +1,92 @@
//
// Created by rick on 22-3-23.
//
#include <yak/platform/x86_64/cpu/cpu.h>
#include <yak/platform/x86_64/cpu/gdt.h>
#define NO_GDT_ENTRIES 8
gdt_segment_t gdt_table[NO_GDT_ENTRIES] = {
{0}, // 0x0
{ // 0x8
.base1 = 0,
.base2 = 0,
.base3 = 0,
.base4 = 0,
.limit1 = 0x0,
.limit2 = 0x0,
.flags = GDT_FLAG_LONG,
.access_byte = ACCESS_BYTE_PRESENT | ACCESS_BYTE_DPL_0 | ACCESS_BYTE_CODE_DATA |
ACCESS_BYTE_EXECUTABLE | ACCESS_BYTE_RW | ACCESS_BYTE_ACCESSED,
},
{ // 0x10
.base1 = 0,
.base2 = 0,
.base3 = 0,
.base4 = 0,
.limit1 = 0x0,
.limit2 = 0x0000,
.flags = 0,
.access_byte = ACCESS_BYTE_PRESENT | ACCESS_BYTE_DPL_0 | ACCESS_BYTE_CODE_DATA | ACCESS_BYTE_RW |
ACCESS_BYTE_ACCESSED,
},
{ // 0x18
.base1 = 0,
.base2 = 0,
.base3 = 0,
.base4 = 0,
.limit1 = 0xFFFF,
.limit2 = 0xF,
.flags = GDT_FLAG_SIZE_32 | GDT_FLAG_GRANULARITY,
.access_byte = ACCESS_BYTE_PRESENT | ACCESS_BYTE_DPL_0 | ACCESS_BYTE_CODE_DATA |
ACCESS_BYTE_EXECUTABLE | ACCESS_BYTE_RW | ACCESS_BYTE_ACCESSED,
},
{ // 0x20
.base1 = 0,
.base2 = 0,
.base3 = 0,
.base4 = 0,
.limit1 = 0xFFFF,
.limit2 = 0xF,
.flags = GDT_FLAG_SIZE_32 | GDT_FLAG_GRANULARITY,
.access_byte = ACCESS_BYTE_PRESENT | ACCESS_BYTE_DPL_0 | ACCESS_BYTE_CODE_DATA | ACCESS_BYTE_RW |
ACCESS_BYTE_ACCESSED,
},
{ // 0x28
.base1 = 0,
.base2 = 0,
.base3 = 0,
.base4 = 0,
.limit1 = 0x0,
.limit2 = 0x0,
.flags = GDT_FLAG_LONG,
.access_byte = ACCESS_BYTE_PRESENT | ACCESS_BYTE_DPL_0 | ACCESS_BYTE_CODE_DATA |
ACCESS_BYTE_EXECUTABLE | ACCESS_BYTE_RW | ACCESS_BYTE_ACCESSED,
},
{ // 0x30
.base1 = 0,
.base2 = 0,
.base3 = 0,
.base4 = 0,
.limit1 = 0x0,
.limit2 = 0x0,
.flags = GDT_FLAG_LONG,
.access_byte = ACCESS_BYTE_PRESENT | ACCESS_BYTE_DPL_0 | ACCESS_BYTE_CODE_DATA | ACCESS_BYTE_RW |
ACCESS_BYTE_ACCESSED,
},
{0}
// todo tss
};
gdtr_t gdtr = {
.limit = sizeof(gdt_table) - 1,
.pointer = (uintptr_t) gdt_table,
};
extern void gdt_reload_segments();
void gdt_init() {
__asm__ volatile ("lgdt %0" : : "m"(gdtr));
gdt_reload_segments();
// todo map virtual address to physical
}

View File

@@ -0,0 +1,156 @@
.code64
.extern isr_handler
# TODO SSE(2), AVX, FP, ...
.macro save_registers_stack
pushq %rax
pushq %rbx
pushq %rcx
pushq %rdx
pushq %rsi
pushq %rdi
# skip RSP and RBP
pushq %r8
pushq %r9
pushq %r10
pushq %r11
pushq %r12
pushq %r13
pushq %r14
pushq %r15
.endm
.macro restore_registers_stack
popq %r15
popq %r14
popq %r13
popq %r12
popq %r11
popq %r10
popq %r9
popq %r8
# skip RSP and RBP
popq %rdi
popq %rsi
popq %rdx
popq %rcx
popq %rbx
popq %rax
.endm
.macro isr_common_stub
save_registers_stack
# Push DS
xor %rax, %rax
movw %ds, %ax
pushq %rax
# switch to kernel DS
movw $0x20,%ax
movw %ax,%ds
movw %ax,%es
movw %ax,%fs
movw %ax,%gs
call isr_handler
popq %rax
movw %ax,%ds
movw %ax,%es
movw %ax,%fs
movw %ax,%gs
restore_registers_stack
# Removes Error Code and ISR number
addq $8,%rsp
sti
iretq
.endm
.macro isr_err_stub isr_num
.global isr_stub_\isr_num
isr_stub_\isr_num:
cli
push $\isr_num
isr_common_stub
.endm
.macro isr_no_err_stub isr_num
.global isr_stub_\isr_num
isr_stub_\isr_num:
cli
pushq $0
push $\isr_num
isr_common_stub
.endm
isr_no_err_stub 0
isr_no_err_stub 1
isr_no_err_stub 2
isr_no_err_stub 3
isr_no_err_stub 4
isr_no_err_stub 5
isr_no_err_stub 6
isr_no_err_stub 7
isr_err_stub 8
isr_no_err_stub 9
isr_err_stub 10
isr_err_stub 11
isr_err_stub 12
isr_err_stub 13
isr_err_stub 14
isr_no_err_stub 15
isr_no_err_stub 16
isr_err_stub 17
isr_no_err_stub 18
isr_no_err_stub 19
isr_no_err_stub 20
isr_err_stub 21
isr_no_err_stub 22
isr_no_err_stub 23
isr_no_err_stub 24
isr_no_err_stub 25
isr_no_err_stub 26
isr_no_err_stub 27
isr_no_err_stub 28
isr_err_stub 29
isr_err_stub 30
isr_no_err_stub 31
# python3 -c "print('\n'.join(f'.quad isr_stub_{i}' for i in range(32)))"
.global isr_stub_table
isr_stub_table:
.quad isr_stub_0
.quad isr_stub_1
.quad isr_stub_2
.quad isr_stub_3
.quad isr_stub_4
.quad isr_stub_5
.quad isr_stub_6
.quad isr_stub_7
.quad isr_stub_8
.quad isr_stub_9
.quad isr_stub_10
.quad isr_stub_11
.quad isr_stub_12
.quad isr_stub_13
.quad isr_stub_14
.quad isr_stub_15
.quad isr_stub_16
.quad isr_stub_17
.quad isr_stub_18
.quad isr_stub_19
.quad isr_stub_20
.quad isr_stub_21
.quad isr_stub_22
.quad isr_stub_23
.quad isr_stub_24
.quad isr_stub_25
.quad isr_stub_26
.quad isr_stub_27
.quad isr_stub_28
.quad isr_stub_29
.quad isr_stub_30
.quad isr_stub_31

View File

@@ -0,0 +1,94 @@
//
// Created by rick on 29-9-23.
//
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <yak/rt/panic.h>
#include <yak/platform/x86_64/cpu/cpu.h>
#include <yak/platform/x86_64/cpu/gdt.h>
#include <yak/platform/x86_64/cpu/idt.h>
#define NO_ISR 256
extern void* isr_entries[];
__attribute((aligned(0x10))) static idt_entry_t idt[NO_ISR];
static idtr_t idtr;
void *isr_vectors[NO_ISR] = {0};
char *exception_messages[] = {
"Division By Zero",
"Debug",
"Non Maskable Interrupt",
"Breakpoint",
"Into Detected Overflow",
"Out of Bounds",
"Invalid Opcode",
"No Coprocessor",
"Double Fault",
"Coprocessor Segment Overrun",
"Bad TSS",
"Segment Not Present",
"Stack Fault",
"General Protection Fault",
"Page Fault",
"Reserved",
"Coprocessor Fault",
"Alignment Check",
"Machine Check",
"SIMD Floating Point Exception",
"Virtualization Exception",
"Control Protection Exception",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Hypervisor Injection Exception",
"VMM Communication Exception",
"Security Exception",
"Reserved",
};
__attribute__((unused)) void isr_handler(isr_registers_t r) {
if (r.int_no <= 31) {
char buffer[256];
snprintf(buffer, 256, "ISR Received: %s af 0x%16lx\n", exception_messages[r.int_no], r.rip);
panic(buffer);
}
panic("ISR Received");
}
void idt_set_descriptor(uint8_t vector, void *isr, uint8_t flags) {
idt_entry_t *descriptor = &idt[vector];
descriptor->isr_low = (uint64_t) isr & 0xFFFF;
descriptor->kernel_cs = GDT_OFFSET_KERNEL_CODE;
descriptor->ist = 0;
descriptor->attributes = flags;
descriptor->isr_mid = ((uint64_t) isr >> 16) & 0xFFFF;
descriptor->isr_high = ((uint64_t) isr >> 32) & 0xFFFFFFFF;
descriptor->reserved = 0;
}
void idt_init() {
idtr.base = (uint64_t) idt;
idtr.limit = sizeof(idt_entry_t) * IDT_MAX_DESCRIPTORS - 1;
memset(idt, 0, sizeof(idt_entry_t) * NO_ISR);
// register
for (int i = 0; i < 32; ++i) {
idt_set_descriptor(i, isr_entries[i], 0x8E);
isr_vectors[i] = NULL;
}
__asm__ volatile ("lidt %0" : : "m"(idtr));
__asm__ volatile ("sti");
}

View File

@@ -0,0 +1,21 @@
NO_ISRS = 32
def main():
with open('idt_stub.c', 'w') as f:
for stub in range(NO_ISRS):
f.write(f"extern void isr_stub_{stub}();\n")
f.write("\n")
f.write("void* isr_entries[] = {\n")
for stub in range(NO_ISRS):
f.write(f" isr_stub_{stub},\n")
f.write("};\n")
pass
if __name__ == '__main__':
main()

View File

@@ -3,9 +3,13 @@
//
#include <stdio.h>
#include <yak/platform/generic/platform.h>
#include <yak/platform/x86_64/cpu/cpu.h>
void platform_init() {
printf("Init X86_64\n");
__asm__("cli");
gdt_init();
idt_init();
}
void __attribute__((noreturn)) halt_forever() {