mirror of
https://github.com/gbowne1/ClassicOS.git
synced 2024-11-21 05:46:52 -08:00
adding files as they are from deesix on my IRC via pastebin
This commit is contained in:
parent
f4f71fd3c2
commit
82e910ca86
45
boot.asm
45
boot.asm
@ -11,7 +11,7 @@ boot:
|
||||
mov [disk],dl
|
||||
|
||||
mov ah, 0x2 ;read sectors
|
||||
mov al, 6 ;sectors to read
|
||||
mov al, 64 ;sectors to read
|
||||
mov ch, 0 ;cylinder idx
|
||||
mov dh, 0 ;head idx
|
||||
mov cl, 2 ;sector idx
|
||||
@ -30,22 +30,37 @@ boot:
|
||||
mov gs, ax
|
||||
mov ss, ax
|
||||
jmp CODE_SEG:boot2
|
||||
|
||||
; access(8)
|
||||
; P:present(1) DPL:descriptor_privilege_level(2)
|
||||
; S:descriptor_type(1) E:executable(1) DC:direction/conforming(1)
|
||||
; RW:readable/writable(1) A:accessed(1)
|
||||
; flags(4)
|
||||
; G:granularity(1) DB:size(1) L:long_mode_code(1) Reserved(1)
|
||||
|
||||
gdt_start:
|
||||
dq 0x0
|
||||
dq 0x0000000000000000 ; null descriptor
|
||||
|
||||
gdt_code:
|
||||
dw 0xFFFF
|
||||
dw 0x0
|
||||
db 0x0
|
||||
db 10011010b
|
||||
db 11001111b
|
||||
db 0x0
|
||||
dw 0xFFFF ; limit_0_15(16)
|
||||
dw 0x0000 ; base_0_15(16)
|
||||
db 0x00 ; base_16_23(8)
|
||||
db 10011010b ; P(1)=1=present DPL(2)=00=ring0 S(1)=1=non_system E(1)=1=code
|
||||
; DC(1)=0=same_ring RW(1)=1=readable A(1)=0=not_accessed
|
||||
db 11001111b ; G(1)=1=page, DB(1)=1=32b, L(1)=0=non_64b Reserved(1)=0=_
|
||||
; limit_16_19(4)=1111
|
||||
db 0x00 ; base_24_31(8)
|
||||
|
||||
gdt_data:
|
||||
dw 0xFFFF
|
||||
dw 0x0
|
||||
db 0x0
|
||||
db 10010010b
|
||||
db 11001111b
|
||||
db 0x0
|
||||
dw 0xFFFF ; limit_0_15(16)
|
||||
dw 0x0000 ; base_0_15(16)
|
||||
db 0x00 ; base_16_23(8)
|
||||
db 10010010b ; P(1)=1=present DPL(2)=00=ring0 S(1)=1=non_system E(1)=0=data
|
||||
; DC(1)=0=grows_up RW(1)=1=rw A(1)=0=not_accessed
|
||||
db 11001111b ; G(1)=1=page DB(1)=1=32b_sp L(1)=0=_ Reserved(1)=0=_
|
||||
; limit_16_19(4)=1111
|
||||
db 0x00 ; base_24_31(8)
|
||||
|
||||
gdt_end:
|
||||
gdt_pointer:
|
||||
dw gdt_end - gdt_start
|
||||
@ -82,4 +97,4 @@ section .bss
|
||||
align 4
|
||||
kernel_stack_bottom: equ $
|
||||
resb 16384 ; 16 KB
|
||||
kernel_stack_top:
|
||||
kernel_stack_top:
|
29
do.sh
Normal file
29
do.sh
Normal file
@ -0,0 +1,29 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
# TODO: Check the compiler does x86
|
||||
|
||||
if [ "$1" = "build" ]; then
|
||||
shift
|
||||
|
||||
printf "Assembling bootloader...\n"
|
||||
nasm -f elf32 boot.asm -o boot.o
|
||||
|
||||
printf "Compiling...\n"
|
||||
gcc \
|
||||
-save-temps \
|
||||
-std=c99 -m32 \
|
||||
-fno-pic \
|
||||
-mgeneral-regs-only \
|
||||
-ffreestanding -nostdlib \
|
||||
-Wall -Wextra -Wpedantic \
|
||||
kernel.c boot.o \
|
||||
-o kernel.bin \
|
||||
-T linker.ld
|
||||
fi
|
||||
|
||||
if [ "$1" = "boot" ]; then
|
||||
printf "Booting...\n\n"
|
||||
MACHINE="-machine pc -cpu 486"
|
||||
qemu-system-i386 $MACHINE -net none -serial stdio -drive file=kernel.bin,index=0,if=floppy,format=raw
|
||||
fi
|
487
kernel.c
487
kernel.c
@ -1,13 +1,488 @@
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
bool terminate = false;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
static inline void outb(uint16_t port, uint8_t val)
|
||||
{
|
||||
__asm__ volatile ("outb %b0, %w1" : : "a"(val), "Nd"(port) : "memory");
|
||||
}
|
||||
|
||||
static inline uint8_t inb(uint16_t port)
|
||||
{
|
||||
uint8_t ret;
|
||||
__asm__ volatile ("inb %w1, %b0" : "=a"(ret) : "Nd"(port) : "memory");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void io_wait(void)
|
||||
{
|
||||
outb(0x80, 0);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define PORT 0x3f8 // COM1
|
||||
|
||||
void init_serial(void)
|
||||
{
|
||||
// Enable "data available" interrupt.
|
||||
outb(PORT + 1, 0x01);
|
||||
}
|
||||
|
||||
// Use only when there _is_ something to read.
|
||||
char read_serial()
|
||||
{
|
||||
return inb(PORT);
|
||||
}
|
||||
|
||||
int is_transmit_empty()
|
||||
{
|
||||
return inb(PORT + 5) & 0x20;
|
||||
}
|
||||
|
||||
void write_serial(char a)
|
||||
{
|
||||
while (is_transmit_empty() == 0);
|
||||
outb(PORT, a);
|
||||
}
|
||||
|
||||
#undef PORT
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
* Convert numbers to hexadecimal chars.
|
||||
*/
|
||||
|
||||
char nibble_to_hex(int n)
|
||||
{
|
||||
if (n>=0 && n<10) return (n+48);
|
||||
else if (n>=10 && n<16) return (n+55+32);
|
||||
else return '?';
|
||||
}
|
||||
|
||||
void int_to_hex(char *hex, uint64_t a, size_t size)
|
||||
{
|
||||
int i = 0;
|
||||
int divisor = size << 3;
|
||||
while (divisor != 0) {
|
||||
divisor -= 4;
|
||||
hex[i] = nibble_to_hex((a >> divisor) & 0xF);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
void u8_to_hex (char *s, uint8_t a) { int_to_hex(s, a, 1); }
|
||||
void u16_to_hex(char *s, uint16_t a) { int_to_hex(s, a, 2); }
|
||||
void u32_to_hex(char *s, uint32_t a) { int_to_hex(s, a, 4); }
|
||||
void u64_to_hex(char *s, uint64_t a) { int_to_hex(s, a, 8); }
|
||||
|
||||
/*
|
||||
* Logging functions.
|
||||
*/
|
||||
|
||||
// Use serial for log output.
|
||||
#define PUTCHAR write_serial
|
||||
|
||||
// Sends buffer of a given size to the log.
|
||||
void klog(char *buff, int size)
|
||||
{
|
||||
for (int i=0; i<size; ++i) PUTCHAR(buff[i]);
|
||||
}
|
||||
|
||||
// Like klog but also sends a newline character.
|
||||
void klogl(char *buff, int size)
|
||||
{
|
||||
klog(buff, size); PUTCHAR('\n');
|
||||
}
|
||||
|
||||
// Sends a NUL-terminated string to the log (not including the NUL).
|
||||
void klogs(const char *str)
|
||||
{
|
||||
while(*str) PUTCHAR(*str++);
|
||||
}
|
||||
|
||||
#undef PUTCHAR
|
||||
|
||||
// Functions that send integers to the log, in hex (without 0x prefix).
|
||||
#define B hexbuff
|
||||
#define D char B[16] = {0}
|
||||
void klog_u8 (uint8_t a) { D; u8_to_hex (B, a); klog(B, 2) ; }
|
||||
void klog_u16(uint16_t a) { D; u16_to_hex(B, a); klog(B, 4) ; }
|
||||
void klog_u32(uint32_t a) { D; u32_to_hex(B, a); klog(B, 8) ; }
|
||||
void klog_u64(uint64_t a) { D; u64_to_hex(B, a); klog(B, 16); }
|
||||
|
||||
// These send a newline character too.
|
||||
void klogl_u8 (uint8_t a) { D; u8_to_hex (B, a); klogl(B, 2) ; }
|
||||
void klogl_u16(uint16_t a) { D; u16_to_hex(B, a); klogl(B, 4) ; }
|
||||
void klogl_u32(uint32_t a) { D; u32_to_hex(B, a); klogl(B, 8) ; }
|
||||
void klogl_u64(uint64_t a) { D; u64_to_hex(B, a); klogl(B, 16); }
|
||||
#undef D
|
||||
#undef B
|
||||
|
||||
void demo_klog_functions(void)
|
||||
{
|
||||
klogs("\nCheck; one, two, one, two.\n");
|
||||
klogl_u8(0x01);
|
||||
klogl_u16(0x2345);
|
||||
klogl_u32(0x6789abcd);
|
||||
klogl_u64(0xef0123456789abcd);
|
||||
klog_u8(0x01);
|
||||
klog_u16(0x2345);
|
||||
klog_u32(0x6789abcd);
|
||||
klog_u64(0xef0123456789abcd);
|
||||
klogs("\n0123456789abcdef0123456789abcd\n");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define PIC1 0x20 /* IO base address for master PIC */
|
||||
#define PIC2 0xA0 /* IO base address for slave PIC */
|
||||
#define PIC1_COMMAND PIC1
|
||||
#define PIC1_DATA (PIC1+1)
|
||||
#define PIC2_COMMAND PIC2
|
||||
#define PIC2_DATA (PIC2+1)
|
||||
|
||||
#define ICW1_ICW4 0x01 /* Indicates that ICW4 will be present */
|
||||
#define ICW1_INIT 0x10 /* Initialization - required! */
|
||||
|
||||
#define ICW4_8086 0x01 /* 8086/88 (MCS-80/85) mode */
|
||||
|
||||
void pic_remap(int offset)
|
||||
{
|
||||
uint8_t a1, a2;
|
||||
// save masks
|
||||
a1 = inb(PIC1_DATA); a2 = inb(PIC2_DATA);
|
||||
// starts the initialization sequence (in cascade mode)
|
||||
outb(PIC1_COMMAND, ICW1_INIT | ICW1_ICW4); io_wait();
|
||||
outb(PIC2_COMMAND, ICW1_INIT | ICW1_ICW4); io_wait();
|
||||
// ICW2: Master PIC vector offset
|
||||
outb(PIC1_DATA, offset); io_wait();
|
||||
// ICW2: Slave PIC vector offset
|
||||
outb(PIC2_DATA, offset+8); io_wait();
|
||||
// ICW3: tell Master PIC that there is a slave PIC at IRQ2 (0000 0100)
|
||||
outb(PIC1_DATA, 4); io_wait();
|
||||
// ICW3: tell Slave PIC its cascade identity (0000 0010)
|
||||
outb(PIC2_DATA, 2); io_wait();
|
||||
// ICW4: have the PICs use 8086 mode (and not 8080 mode)
|
||||
outb(PIC1_DATA, ICW4_8086); io_wait();
|
||||
outb(PIC2_DATA, ICW4_8086); io_wait();
|
||||
// restore saved masks.
|
||||
outb(PIC1_DATA, a1); outb(PIC2_DATA, a2);
|
||||
}
|
||||
|
||||
void pic_end_of_irq(int irq)
|
||||
{
|
||||
if (irq >= 8) outb(PIC2_COMMAND, 0x20);
|
||||
outb(PIC1_COMMAND, 0x20);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct gdt_descriptor
|
||||
{
|
||||
uint16_t limit;
|
||||
uint32_t base;
|
||||
} __attribute__((packed));
|
||||
|
||||
void sgdt(struct gdt_descriptor *gdtd)
|
||||
{
|
||||
__asm__ volatile ("sgdt %0" : : "m"(*gdtd) : "memory");
|
||||
}
|
||||
|
||||
void klog_info_about_gdt(void)
|
||||
{
|
||||
struct gdt_descriptor gdtd;
|
||||
sgdt(&gdtd);
|
||||
|
||||
klogs("**\n GDT info:\n");
|
||||
klogl_u32(gdtd.base);
|
||||
klogl_u16(gdtd.limit);
|
||||
|
||||
// For each (8 bytes long) entry in the table...
|
||||
for (uint32_t b = gdtd.base; b < gdtd.base + gdtd.limit; b += 8) {
|
||||
// ... log each byte, in hex.
|
||||
for (uint32_t i = b; i < b + 8; ++i) {
|
||||
klog_u8(*(uint8_t*)i);
|
||||
}
|
||||
klogs("\n");
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct idt_gate_descriptor
|
||||
{
|
||||
uint16_t offset_1; // offset bits 0..15
|
||||
uint16_t selector; // a code segment selector in GDT or LDT
|
||||
uint8_t zero; // unused, set to 0
|
||||
uint8_t type_attributes; // gate type, dpl, and p fields
|
||||
uint16_t offset_2; // offset bits 16..31
|
||||
} __attribute__((packed));
|
||||
|
||||
#define PIC_REMAP_OFFSET 0x20
|
||||
|
||||
// The IDT begins with gates for the exceptions; we remap the PIC to start at
|
||||
// some point after them. For the PIC IRQs that's 16 gates more. If we define
|
||||
// our software interrupts... we'll have to make room.
|
||||
#define IDT_NUM_GATES (PIC_REMAP_OFFSET + 16)
|
||||
|
||||
__attribute__((aligned(0x10)))
|
||||
struct idt_gate_descriptor idt[IDT_NUM_GATES] = {0};
|
||||
|
||||
static inline void lidt(void)
|
||||
{
|
||||
struct {
|
||||
uint16_t limit;
|
||||
void *base;
|
||||
} __attribute__((packed)) src = { IDT_NUM_GATES * 8 - 1, idt };
|
||||
__asm__ ("lidt %0" : : "m"(src) );
|
||||
}
|
||||
|
||||
struct interrupt_frame; // Not defined yet, but we need a pointer to it.
|
||||
|
||||
#define GATE_F void (*f)(struct interrupt_frame *)
|
||||
#define GATE_WITH_ERR_F void (*f)(struct interrupt_frame *, uint32_t)
|
||||
#define FOR_INTR 0x8e /* Present, DPL 0, 32b interrupt gate */
|
||||
#define FOR_TRAP 0x8f /* Present, DPL 0, 32b trap gate */
|
||||
|
||||
struct idt_gate_descriptor gate(uint8_t type_attributes, uint32_t f)
|
||||
{
|
||||
return (struct idt_gate_descriptor){
|
||||
.type_attributes = type_attributes,
|
||||
.selector = 0x0008, // Our code segment in GDT, ring0 requested.
|
||||
.offset_1 = (uint16_t)(f & 0xffff),
|
||||
.offset_2 = (uint16_t)((f>>16) & 0xffff),
|
||||
.zero = 0x00
|
||||
};
|
||||
}
|
||||
|
||||
void klog_idt_gate_update(int n)
|
||||
{
|
||||
klogs(" "); klog_u8(n);
|
||||
}
|
||||
|
||||
void set_irq_handler(size_t irq, GATE_F)
|
||||
{
|
||||
idt[irq + PIC_REMAP_OFFSET] = gate(FOR_INTR, (uint32_t)f);
|
||||
klog_idt_gate_update(irq);
|
||||
}
|
||||
|
||||
void set_exception_with_err_handler(size_t number, GATE_WITH_ERR_F)
|
||||
{
|
||||
idt[number] = gate(FOR_TRAP, (uint32_t)f);
|
||||
klog_idt_gate_update(number);
|
||||
}
|
||||
|
||||
void set_exception_handler(size_t number, GATE_F)
|
||||
{
|
||||
idt[number] = gate(FOR_TRAP, (uint32_t)f);
|
||||
klog_idt_gate_update(number);
|
||||
}
|
||||
|
||||
#undef FOR_TRAP
|
||||
#undef FOR_INTR
|
||||
#undef GATE_WITH_ERR_F
|
||||
#undef GATE_F
|
||||
|
||||
void pic_clear_irq(uint8_t irq)
|
||||
{
|
||||
uint16_t port;
|
||||
uint8_t value;
|
||||
if(irq < 8) {
|
||||
port = PIC1_DATA;
|
||||
} else {
|
||||
port = PIC2_DATA; irq -= 8;
|
||||
}
|
||||
value = inb(port) & ~(1 << irq);
|
||||
outb(port, value);
|
||||
}
|
||||
|
||||
void pic_mask_all_irqs(void)
|
||||
{
|
||||
outb(PIC1_DATA, 0xff); outb(PIC2_DATA, 0xff);
|
||||
}
|
||||
|
||||
static inline void sti(void)
|
||||
{
|
||||
__asm__ volatile ("sti");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
void panic(void)
|
||||
{
|
||||
klogs("Panic: halt.");
|
||||
__asm__ volatile ("cli; hlt;");
|
||||
}
|
||||
|
||||
__attribute__((interrupt))
|
||||
void exception_with_err_handler(struct interrupt_frame *frame, uint32_t error_code)
|
||||
{
|
||||
(void)frame;
|
||||
klogs("\n(generic handler) Exception with err "); klogl_u32(error_code);
|
||||
panic();
|
||||
}
|
||||
|
||||
__attribute__((interrupt))
|
||||
void exception_handler(struct interrupt_frame *frame)
|
||||
{
|
||||
(void)frame;
|
||||
klogs("\n(generic handler) Exception\n");
|
||||
panic();
|
||||
}
|
||||
|
||||
__attribute__((interrupt))
|
||||
void exception_de(struct interrupt_frame *frame)
|
||||
{
|
||||
(void)frame;
|
||||
klogs("\nException: Divide Error.\n");
|
||||
panic();
|
||||
}
|
||||
|
||||
__attribute__((interrupt))
|
||||
void exception_gp(struct interrupt_frame *frame, uint32_t error_code)
|
||||
{
|
||||
(void)frame;
|
||||
klogs("\nException: General Protection; error code "); klogl_u32(error_code);
|
||||
panic();
|
||||
}
|
||||
|
||||
__attribute__((interrupt))
|
||||
void irq1_handler(struct interrupt_frame *frame)
|
||||
{
|
||||
(void)frame;
|
||||
uint8_t k = inb(0x60);
|
||||
klogs("Key: "); klogl_u8(k);
|
||||
if (k == /* q */ 0x90) terminate = true;
|
||||
pic_end_of_irq(1);
|
||||
}
|
||||
|
||||
__attribute__((interrupt))
|
||||
void irq4_handler(struct interrupt_frame *frame)
|
||||
{
|
||||
(void)frame;
|
||||
uint8_t c = read_serial();
|
||||
write_serial(c); // Echo.
|
||||
|
||||
// Testing stuff when receiving some chars.
|
||||
if (c == 'q') terminate = true;
|
||||
if (c == '0')
|
||||
__asm__ volatile ("mov $0, %bl; div %bl"); // Division by zero.
|
||||
if (c == 'f')
|
||||
__asm__ volatile ("int $13;"); // General Protection fault.
|
||||
|
||||
pic_end_of_irq(4);
|
||||
}
|
||||
|
||||
void init_interrupts(void)
|
||||
{
|
||||
klogs("**\n Init interrupts\n");
|
||||
|
||||
klogs("Preparing PIC.\n");
|
||||
pic_mask_all_irqs();
|
||||
pic_remap(PIC_REMAP_OFFSET);
|
||||
|
||||
klogs("Init exceptions.\n");
|
||||
#define A(n) set_exception_handler(n, &exception_handler)
|
||||
#define B(n) set_exception_with_err_handler(n, &exception_with_err_handler)
|
||||
set_exception_handler(0, &exception_de);
|
||||
A(1); A(2); A(3); A(4); A(5); A(6); A(7); B(8); A(9);
|
||||
B(10); B(11); B(12);
|
||||
set_exception_with_err_handler(13, &exception_gp);
|
||||
B(14); A(15); A(16); B(17); A(18); A(19);
|
||||
A(20); B(21); A(28); B(29); B(30);
|
||||
#undef B
|
||||
#undef A
|
||||
|
||||
klogs("\nInit IRQs.\n");
|
||||
|
||||
// Assign handlers to hardware interrupts.
|
||||
// Use the IRQ number here (the offset is applied elsewhere).
|
||||
set_irq_handler(1, &irq1_handler); // Keyboard.
|
||||
set_irq_handler(4, &irq4_handler); // Serial.
|
||||
klogs("\n");
|
||||
|
||||
// Make our IDT the active one.
|
||||
lidt();
|
||||
|
||||
// Unmask hardware interrupts that we're ready to handle.
|
||||
pic_clear_irq(1);
|
||||
pic_clear_irq(4);
|
||||
|
||||
// Start accepting IRQs (that is... from the PIC).
|
||||
sti();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
void kmain(void)
|
||||
{
|
||||
const uint16_t color = 0x0F00;
|
||||
const char *hello = "Hello C world!";
|
||||
volatile uint16_t *vga = (volatile uint16_t *)0xb8000;
|
||||
init_serial();
|
||||
klogs("Hello from kmain!\n");
|
||||
demo_klog_functions();
|
||||
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
init_interrupts();
|
||||
|
||||
const uint16_t color = 0x7e00;
|
||||
volatile uint16_t *vga = (volatile uint16_t *)0xb8000;
|
||||
const char *hello = "Hello! Please, see serial output.";
|
||||
|
||||
for (int i = 0; hello[i] != 0; ++i) {
|
||||
vga[i + 80] = color | (uint16_t)hello[i];
|
||||
}
|
||||
}
|
||||
|
||||
klog_info_about_gdt();
|
||||
|
||||
klogs("\n\n**\n Ready!\n");
|
||||
klogs(
|
||||
"(accepting input from keyboard)\n"
|
||||
" type q to return from kmain\n"
|
||||
" key info sent to serial\n"
|
||||
"(accepting input from serial, with echo)\n"
|
||||
" send q to return from kmain\n"
|
||||
" send f to invoke GPF\n"
|
||||
" send 0 to trigger divide by zero\n"
|
||||
);
|
||||
while (!terminate) {
|
||||
__asm__ volatile ("hlt");
|
||||
}
|
||||
|
||||
klogs("\nkmain returning now... o/\n");
|
||||
}
|
Loading…
Reference in New Issue
Block a user