Compare commits
2 commits
0d81f79a96
...
887429636c
Author | SHA1 | Date | |
---|---|---|---|
vanten-s | 887429636c | ||
vanten-s | bb441eaeb6 |
6
Makefile
6
Makefile
|
@ -25,16 +25,18 @@ kernel/elf.o \
|
|||
kernel/syscall.o \
|
||||
kernel/io/keyboard.o \
|
||||
kernel/heap.o \
|
||||
kernel/task.o \
|
||||
|
||||
LIB_OBJS=\
|
||||
$(ARCHDIR)/bootstrap.o \
|
||||
$(ARCHDIR)/tty.o \
|
||||
$(ARCHDIR)/strlib.o \
|
||||
$(ARCHDIR)/paging.o \
|
||||
$(ARCHDIR)/gdt.o \
|
||||
$(ARCHDIR)/interrupts/idt.o \
|
||||
$(ARCHDIR)/interrupts/interrupt.o \
|
||||
$(ARCHDIR)/interrupts/irq0.o \
|
||||
$(ARCHDIR)/interrupts/syscall.o \
|
||||
$(ARCHDIR)/gdt.o \
|
||||
$(ARCHDIR)/userland.o \
|
||||
$(ARCHDIR)/interrupts/pic.o \
|
||||
|
||||
OBJS=$(KERNEL_OBJS) $(LIB_OBJS)
|
||||
|
|
0
arch/i686/ata.c
Normal file
0
arch/i686/ata.c
Normal file
|
@ -1,18 +1,12 @@
|
|||
/* Declare constants for the multiboot header. */
|
||||
.set ALIGN, 1<<0 /* align loaded modules on page boundaries */
|
||||
.set MEMINFO, 1<<1 /* provide memory map */
|
||||
.set FLAGS, ALIGN | MEMINFO /* this is the Multiboot 'flag' field */
|
||||
.set MAGIC, 0x1BADB002 /* 'magic number' lets bootloader find the header */
|
||||
.set CHECKSUM, -(MAGIC + FLAGS) /* checksum of above, to prove we are multiboot */
|
||||
# Declare constants for the multiboot header.
|
||||
.set ALIGN, 1<<0 # align loaded modules on page boundaries
|
||||
.set MEMINFO, 1<<1 # provide memory map
|
||||
.set FLAGS, ALIGN | MEMINFO # this is the Multiboot 'flag' field
|
||||
.set MAGIC, 0x1BADB002 # 'magic number' lets bootloader find the header
|
||||
.set CHECKSUM, -(MAGIC + FLAGS) # checksum of above, to prove we are multiboot
|
||||
|
||||
/*
|
||||
Declare a multiboot header that marks the program as a kernel. These are magic
|
||||
values that are documented in the multiboot standard. The bootloader will
|
||||
search for this signature in the first 8 KiB of the kernel file, aligned at a
|
||||
32-bit boundary. The signature is in its own section so the header can be
|
||||
forced to be within the first 8 KiB of the kernel file.
|
||||
*/
|
||||
.section .multiboot
|
||||
# Declare a multiboot header that marks the program as a kernel.
|
||||
.section .multiboot.data, "aw"
|
||||
.align 4
|
||||
.long MAGIC
|
||||
.long FLAGS
|
||||
|
@ -31,100 +25,109 @@ stack is properly aligned and failure to align the stack will result in
|
|||
undefined behavior.
|
||||
*/
|
||||
|
||||
.section .bss
|
||||
.section .bss, "aw", @nobits
|
||||
.align 16
|
||||
stack_bottom:
|
||||
.skip 16384 # 16 KiB
|
||||
stack_top:
|
||||
|
||||
.global boot_page_directory
|
||||
.align 4096
|
||||
boot_page_directory:
|
||||
.skip 4096
|
||||
boot_page_table1:
|
||||
.skip 4096
|
||||
|
||||
/*
|
||||
The linker script specifies _start as the entry point to the kernel and the
|
||||
bootloader will jump to this position once the kernel has been loaded. It
|
||||
doesn't make sense to return from this function as the bootloader is gone.
|
||||
*/
|
||||
.section .text
|
||||
.section .multiboot.text, "a"
|
||||
.global _start
|
||||
.type _start, @function
|
||||
_start:
|
||||
/*
|
||||
The bootloader has loaded us into 32-bit protected mode on a x86
|
||||
machine. Interrupts are disabled. Paging is disabled. The processor
|
||||
state is as defined in the multiboot standard. The kernel has full
|
||||
control of the CPU. The kernel can only make use of hardware features
|
||||
and any code it provides as part of itself. There's no printf
|
||||
function, unless the kernel provides its own <stdio.h> header and a
|
||||
printf implementation. There are no security restrictions, no
|
||||
safeguards, no debugging mechanisms, only what the kernel provides
|
||||
itself. It has absolute and complete power over the
|
||||
machine.
|
||||
*/
|
||||
# Physical address of boot_page_table1.
|
||||
# TODO: I recall seeing some assembly that used a macro to do the
|
||||
# conversions to and from physical. Maybe this should be done in this
|
||||
# code as well?
|
||||
movl $(boot_page_table1 - 0xC0000000), %edi
|
||||
# First address to map is address 0.
|
||||
# TODO: Start at the first kernel page instead. Alternatively map the first
|
||||
# 1 MiB as it can be generally useful, and there's no need to
|
||||
# specially map the VGA buffer.
|
||||
movl $0, %esi
|
||||
# Map 1023 pages. The 1024th will be the VGA text buffer.
|
||||
movl $1023, %ecx
|
||||
|
||||
/*
|
||||
To set up a stack, we set the esp register to point to the top of the
|
||||
stack (as it grows downwards on x86 systems). This is necessarily done
|
||||
in assembly as languages such as C cannot function without a stack.
|
||||
*/
|
||||
1:
|
||||
# Only map the kernel.
|
||||
cmpl $_kernel_start, %esi
|
||||
jl 2f
|
||||
cmpl $(_kernel_end - 0xC0000000), %esi
|
||||
jge 3f
|
||||
|
||||
# Map physical address as "present, writable". Note that this maps
|
||||
# .text and .rodata as writable. Mind security and map them as non-writable.
|
||||
movl %esi, %edx
|
||||
orl $0x003, %edx
|
||||
movl %edx, (%edi)
|
||||
|
||||
2:
|
||||
# Size of page is 4096 bytes.
|
||||
addl $4096, %esi
|
||||
# Size of entries in boot_page_table1 is 4 bytes.
|
||||
addl $4, %edi
|
||||
# Loop to the next entry if we haven't finished.
|
||||
loop 1b
|
||||
|
||||
3:
|
||||
# Map VGA video memory to 0xC03FF000 as "present, writable".
|
||||
movl $(0x000B8000 | 0x003), boot_page_table1 - 0xC0000000 + 1023 * 4
|
||||
|
||||
# The page table is used at both page directory entry 0 (virtually from 0x0
|
||||
# to 0x3FFFFF) (thus identity mapping the kernel) and page directory entry
|
||||
# 768 (virtually from 0xC0000000 to 0xC03FFFFF) (thus mapping it in the
|
||||
# higher half). The kernel is identity mapped because enabling paging does
|
||||
# not change the next instruction, which continues to be physical. The CPU
|
||||
# would instead page fault if there was no identity mapping.
|
||||
|
||||
# Map the page table to both virtual addresses 0x00000000 and 0xC0000000.
|
||||
movl $(boot_page_table1 - 0xC0000000 + 0x003), boot_page_directory - 0xC0000000 + 0
|
||||
movl $(boot_page_table1 - 0xC0000000 + 0x003), boot_page_directory - 0xC0000000 + 768 * 4
|
||||
|
||||
# Set cr3 to the address of the boot_page_directory.
|
||||
movl $(boot_page_directory - 0xC0000000), %ecx
|
||||
movl %ecx, %cr3
|
||||
|
||||
# Enable paging and the write-protect bit.
|
||||
movl %cr0, %ecx
|
||||
orl $0x80010000, %ecx
|
||||
movl %ecx, %cr0
|
||||
|
||||
# Jump to higher half with an absolute jump.
|
||||
lea 4f, %ecx
|
||||
jmp *%ecx
|
||||
|
||||
.section .text
|
||||
|
||||
4:
|
||||
# At this point, paging is fully set up and enabled.
|
||||
|
||||
# Unmap the identity mapping as it is now unnecessary.
|
||||
movl $0, boot_page_directory + 0
|
||||
|
||||
# Reload crc3 to force a TLB flush so the changes to take effect.
|
||||
movl %cr3, %ecx
|
||||
movl %ecx, %cr3
|
||||
|
||||
# Set up the stack.
|
||||
mov $stack_top, %esp
|
||||
|
||||
cli
|
||||
|
||||
call gdt_init
|
||||
|
||||
movw $0x10, %ax
|
||||
movw %ax, %ds
|
||||
movw %ax, %es
|
||||
movw %ax, %fs
|
||||
movw %ax, %gs
|
||||
movw %ax, %ss
|
||||
# jmp kernel
|
||||
ljmp $0x08, $kernel
|
||||
|
||||
kernel:
|
||||
/*
|
||||
This is a good place to initialize crucial processor state before the
|
||||
high-level kernel is entered. It's best to minimize the early
|
||||
environment where crucial features are offline. Note that the
|
||||
processor is not fully initialized yet: Features such as floating
|
||||
point instructions and instruction set extensions are not initialized
|
||||
yet. The GDT should be loaded here. Paging should be enabled here.
|
||||
C++ features such as global constructors and exceptions will require
|
||||
runtime support to work as well.
|
||||
*/
|
||||
|
||||
/*
|
||||
Enter the high-level kernel. The ABI requires the stack is 16-byte
|
||||
aligned at the time of the call instruction (which afterwards pushes
|
||||
the return pointer of size 4 bytes). The stack was originally 16-byte
|
||||
aligned above and we've pushed a multiple of 16 bytes to the
|
||||
stack since (pushed 0 bytes so far), so the alignment has thus been
|
||||
preserved and the call is well defined.
|
||||
*/
|
||||
|
||||
# Enter the high-level kernel.
|
||||
call kernel_main
|
||||
|
||||
.global keyboard_test
|
||||
keyboard_test:
|
||||
hlt
|
||||
|
||||
/*
|
||||
If the system has nothing more to do, put the computer into an
|
||||
infinite loop. To do that:
|
||||
1) Disable interrupts with cli (clear interrupt enable in eflags).
|
||||
They are already disabled by the bootloader, so this is not needed.
|
||||
Mind that you might later enable interrupts and return from
|
||||
kernel_main (which is sort of nonsensical to do).
|
||||
2) Wait for the next interrupt to arrive with hlt (halt instruction).
|
||||
Since they are disabled, this will lock up the computer.
|
||||
3) Jump to the hlt instruction if it ever wakes up due to a
|
||||
non-maskable interrupt occurring or due to system management mode.
|
||||
*/
|
||||
# Infinite loop if the system has nothing more to do.
|
||||
cli
|
||||
1: hlt
|
||||
jmp 1b
|
||||
|
||||
/*
|
||||
Set the size of the _start symbol to the current location '.' minus its start.
|
||||
This is useful when debugging or when you implement call tracing.
|
||||
*/
|
||||
|
||||
.size _start, . - _start
|
||||
|
|
|
@ -15,6 +15,15 @@
|
|||
#define IRQ6 (IRQ0 + 6)
|
||||
#define IRQ7 (IRQ0 + 7)
|
||||
|
||||
#define IRQ8 0x28
|
||||
#define IRQ9 (IRQ8 + 1)
|
||||
#define IRQ10 (IRQ8 + 2)
|
||||
#define IRQ11 (IRQ8 + 3)
|
||||
#define IRQ12 (IRQ8 + 4)
|
||||
#define IRQ13 (IRQ8 + 5)
|
||||
#define IRQ14 (IRQ8 + 6)
|
||||
#define IRQ15 (IRQ8 + 7)
|
||||
|
||||
#define NUM_ENTRIES 0x100
|
||||
|
||||
struct InterruptDescriptor {
|
||||
|
@ -97,21 +106,20 @@ static void load_idt()
|
|||
idt.base = entries;
|
||||
|
||||
asm ( "lidt %0" : : "m"(idt) );
|
||||
asm ( "sti" );
|
||||
}
|
||||
|
||||
struct InterruptDescriptor generate_entry(bool present, void isr(struct interrupt_frame*), uint8_t gate_type, uint8_t ring, uint8_t selector)
|
||||
static struct InterruptDescriptor generate_entry(bool present, void isr(struct interrupt_frame*), uint8_t gate_type, uint8_t ring, uint8_t selector)
|
||||
{
|
||||
struct InterruptDescriptor entry;
|
||||
entry.present = present;
|
||||
entry.offset = (uint32_t)isr;
|
||||
entry.offset = (size_t)isr;
|
||||
entry.gate_type = gate_type;
|
||||
entry.ring = ring;
|
||||
entry.selector = selector;
|
||||
return entry;
|
||||
}
|
||||
|
||||
void idt_init()
|
||||
static void add_exceptions()
|
||||
{
|
||||
struct InterruptDescriptor entry;
|
||||
|
||||
|
@ -130,23 +138,47 @@ void idt_init()
|
|||
entry = generate_entry(true, double_fault, 0xF, 0, 0x8);
|
||||
add_entry(entry, DoubleFault);
|
||||
|
||||
entry = generate_entry(true, irq, 0xE, 0, 0x8);
|
||||
}
|
||||
|
||||
static void add_irqs()
|
||||
{
|
||||
struct InterruptDescriptor entry;
|
||||
entry = generate_entry(true, irq_1, 0xE, 0, 0x8);
|
||||
for (int i = IRQ0; i <= IRQ7; i++)
|
||||
{
|
||||
add_entry(entry, i);
|
||||
}
|
||||
|
||||
entry = generate_entry(true, irq_2, 0xE, 0, 0x8);
|
||||
for (int i = IRQ8; i <= IRQ15; i++)
|
||||
{
|
||||
add_entry(entry, i);
|
||||
}
|
||||
|
||||
entry = generate_entry(true, irq0, 0xE, 0, 0x8);
|
||||
add_entry(entry, IRQ0);
|
||||
|
||||
entry = generate_entry(true, keyboard_interrupt, 0xE, 0, 0x8);
|
||||
add_entry(entry, IRQ1);
|
||||
}
|
||||
|
||||
static void add_syscall()
|
||||
{
|
||||
struct InterruptDescriptor syscall_entry;
|
||||
syscall_entry.present = true;
|
||||
syscall_entry.offset = (uint32_t)syscall;
|
||||
syscall_entry.offset = (size_t)syscall;
|
||||
syscall_entry.gate_type = 0xE;
|
||||
syscall_entry.ring = 3;
|
||||
syscall_entry.selector = 0x8;
|
||||
|
||||
add_entry(syscall_entry, 0x80);
|
||||
}
|
||||
|
||||
void idt_init()
|
||||
{
|
||||
add_exceptions();
|
||||
add_irqs();
|
||||
add_syscall();
|
||||
|
||||
load_idt();
|
||||
}
|
||||
|
|
|
@ -1,15 +1,11 @@
|
|||
#include <kernel/tty.h>
|
||||
#include <kernel/task.h>
|
||||
#include "interrupt.h"
|
||||
#include <stddef.h>
|
||||
#include <debugging.h>
|
||||
#include <kernel/io/keyboard.h>
|
||||
#include "../io.h"
|
||||
|
||||
struct interrupt_frame {
|
||||
uint32_t eflags;
|
||||
uint32_t cs;
|
||||
uint32_t eip;
|
||||
};
|
||||
|
||||
uint8_t test = 'a';
|
||||
|
||||
static void stack_trace()
|
||||
|
@ -25,40 +21,54 @@ static void stack_trace()
|
|||
}
|
||||
}
|
||||
|
||||
__attribute__((interrupt)) void divide_by_zero(void*)
|
||||
__attribute__((interrupt)) void divide_by_zero(struct interrupt_frame*)
|
||||
{
|
||||
terminal_writestring("Yo dude u cant divide by zero yao\n");
|
||||
while (1) { }
|
||||
}
|
||||
|
||||
__attribute__((interrupt)) void general_protection_fault(void*)
|
||||
__attribute__((interrupt)) void general_protection_fault(struct interrupt_frame*)
|
||||
{
|
||||
terminal_writestring("GPF handler called\n");
|
||||
while (1) { }
|
||||
while (1) {
|
||||
asm("cli");
|
||||
asm("hlt");
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((interrupt)) void double_fault(void*)
|
||||
__attribute__((interrupt)) void double_fault(struct interrupt_frame*)
|
||||
{
|
||||
stack_trace();
|
||||
terminal_writestring("2 Errors in a row, u better behave naughty naughty\n");
|
||||
while (1) { }
|
||||
while (1) {
|
||||
asm("cli");
|
||||
asm("hlt");
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((interrupt)) void exception(void*)
|
||||
__attribute__((interrupt)) void exception(struct interrupt_frame*)
|
||||
{
|
||||
terminal_writestring("Some weird error code stuff\n");
|
||||
while (1) { }
|
||||
}
|
||||
|
||||
__attribute__((interrupt)) void keyboard_interrupt(void*)
|
||||
__attribute__((interrupt)) void keyboard_interrupt(struct interrupt_frame*)
|
||||
{
|
||||
terminal_writestring("Keyboard Interrupt\n");
|
||||
uint8_t scancode = inb(0x60);
|
||||
handle_keyboard(scancode);
|
||||
outb(0x20, 0x20);
|
||||
}
|
||||
|
||||
__attribute__((interrupt)) void irq(void*)
|
||||
__attribute__((interrupt)) void irq_1(struct interrupt_frame*)
|
||||
{
|
||||
terminal_writestring("PIC 1\n");
|
||||
outb(0x20, 0x20);
|
||||
}
|
||||
|
||||
__attribute__((interrupt)) void irq_2(struct interrupt_frame*)
|
||||
{
|
||||
terminal_writestring("PIC 2\n");
|
||||
outb(0xA0, 0x20);
|
||||
outb(0x20, 0x20);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,12 @@
|
|||
struct interrupt_frame;
|
||||
#include <stddef.h>
|
||||
|
||||
struct interrupt_frame {
|
||||
size_t eip;
|
||||
size_t cs;
|
||||
size_t eflags;
|
||||
size_t ss;
|
||||
size_t esp;
|
||||
};
|
||||
|
||||
__attribute__((interrupt)) void divide_by_zero(struct interrupt_frame* frame);
|
||||
__attribute__((interrupt)) void general_protection_fault(struct interrupt_frame* frame);
|
||||
|
@ -6,7 +14,10 @@ __attribute__((interrupt)) void double_fault(struct interrupt_frame* frame);
|
|||
|
||||
__attribute__((interrupt)) void exception(struct interrupt_frame* frame);
|
||||
|
||||
__attribute__((interrupt)) void syscall(void*);
|
||||
__attribute__((interrupt)) void syscall(struct interrupt_frame* frame);
|
||||
|
||||
__attribute__((interrupt)) void keyboard_interrupt(struct interrupt_frame* frame);
|
||||
__attribute__((interrupt)) void irq(struct interrupt_frame* frame);
|
||||
__attribute__((interrupt)) void irq_1(struct interrupt_frame* frame);
|
||||
__attribute__((interrupt)) void irq_2(struct interrupt_frame* frame);
|
||||
|
||||
__attribute__((interrupt)) void irq0(struct interrupt_frame* frame);
|
||||
|
|
68
arch/i686/interrupts/irq0.s
Normal file
68
arch/i686/interrupts/irq0.s
Normal file
|
@ -0,0 +1,68 @@
|
|||
.global irq0
|
||||
.extern _switch_task
|
||||
|
||||
.global jump_kernel
|
||||
.global jump_user
|
||||
|
||||
.extern ss
|
||||
.extern esp
|
||||
.extern eflags
|
||||
.extern cs
|
||||
.extern eip
|
||||
|
||||
irq0:
|
||||
pusha
|
||||
|
||||
mov $0x10, %eax
|
||||
mov %ax, %ds
|
||||
mov %ax, %es
|
||||
mov %ax, %fs
|
||||
mov %ax, %gs
|
||||
|
||||
call _switch_task
|
||||
|
||||
iret
|
||||
|
||||
jump_kernel:
|
||||
mov $0x20, %eax
|
||||
outb %al, $0x20
|
||||
|
||||
mov (ss), %eax
|
||||
mov %ax, %ds
|
||||
mov %ax, %es
|
||||
mov %ax, %fs
|
||||
mov %ax, %gs
|
||||
|
||||
popa
|
||||
|
||||
mov (esp), %esp
|
||||
|
||||
push (ss)
|
||||
push (esp)
|
||||
push (eflags)
|
||||
push (cs)
|
||||
push (eip)
|
||||
|
||||
iret
|
||||
|
||||
jump_user:
|
||||
mov $0x20, %eax
|
||||
outb %al, $0x20
|
||||
|
||||
mov (ss), %eax
|
||||
mov %ax, %ds
|
||||
mov %ax, %es
|
||||
mov %ax, %fs
|
||||
mov %ax, %gs
|
||||
|
||||
popa
|
||||
|
||||
push (ss)
|
||||
push (esp)
|
||||
push (eflags)
|
||||
push (cs)
|
||||
push (eip)
|
||||
|
||||
iret
|
||||
|
||||
|
|
@ -21,17 +21,18 @@
|
|||
|
||||
void init_pic() {
|
||||
uint8_t a1;
|
||||
uint8_t a2;
|
||||
|
||||
a1 = inb(PIC1_DATA); // save masks
|
||||
inb(PIC2_DATA);
|
||||
|
||||
a2 = inb(PIC2_DATA);
|
||||
|
||||
outb(PIC1_COMMAND, ICW1_INIT | ICW1_ICW4); // starts the initialization sequence (in cascade mode)
|
||||
io_wait();
|
||||
outb(PIC2_COMMAND, ICW1_INIT | ICW1_ICW4);
|
||||
io_wait();
|
||||
outb(PIC1_DATA, 0x20); // ICW2: Master PIC vector offset
|
||||
io_wait();
|
||||
outb(PIC2_DATA, 0xFF); // ICW2: Slave PIC vector offset
|
||||
outb(PIC2_DATA, 0x28); // ICW2: Slave PIC vector offset
|
||||
io_wait();
|
||||
outb(PIC1_DATA, 4); // ICW3: tell Master PIC that there is a slave PIC at IRQ2 (0000 0100)
|
||||
io_wait();
|
||||
|
@ -45,5 +46,5 @@ void init_pic() {
|
|||
|
||||
outb(PIC1_DATA, a1); // restore saved masks.
|
||||
// outb(PIC2_DATA, a2);
|
||||
outb(PIC2_DATA, 0xFF);
|
||||
outb(PIC2_DATA, a2);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,20 @@
|
|||
.extern _syscall
|
||||
.global syscall
|
||||
syscall:
|
||||
sti
|
||||
mov $0x10, %edx
|
||||
mov %ax, %ds
|
||||
mov %ax, %es
|
||||
mov %ax, %fs
|
||||
mov %ax, %gs
|
||||
|
||||
mov %esp, %edx
|
||||
mov $0x00f00000, %esp
|
||||
push 0x10(%edx)
|
||||
push 0x0C(%edx)
|
||||
push 0x08(%edx)
|
||||
push 0x04(%edx)
|
||||
push 0x00(%edx)
|
||||
|
||||
push %edx
|
||||
push %ecx
|
||||
push %ebx
|
||||
push %eax
|
||||
|
@ -13,6 +24,5 @@ call _syscall
|
|||
pop %eax
|
||||
pop %ebx
|
||||
pop %ecx
|
||||
pop %edx
|
||||
iret
|
||||
|
||||
|
|
|
@ -6,47 +6,43 @@ ENTRY(_start)
|
|||
kernel image. */
|
||||
SECTIONS
|
||||
{
|
||||
/* It used to be universally recommended to use 1M as a start offset,
|
||||
as it was effectively guaranteed to be available under BIOS systems.
|
||||
However, UEFI has made things more complicated, and experimental data
|
||||
strongly suggests that 2M is a safer place to load. In 2016, a new
|
||||
feature was introduced to the multiboot2 spec to inform bootloaders
|
||||
that a kernel can be loaded anywhere within a range of addresses and
|
||||
will be able to relocate itself to run from such a loader-selected
|
||||
address, in order to give the loader freedom in selecting a span of
|
||||
memory which is verified to be available by the firmware, in order to
|
||||
work around this issue. This does not use that feature, so 2M was
|
||||
chosen as a safer option than the traditional 1M. */
|
||||
. = 2M;
|
||||
. = 1M;
|
||||
|
||||
_kernel_start = .;
|
||||
.multiboot.data :
|
||||
{
|
||||
*(.multiboot.data)
|
||||
}
|
||||
|
||||
/* First put the multiboot header, as it is required to be put very early
|
||||
in the image or the bootloader won't recognize the file format.
|
||||
Next we'll put the .text section. */
|
||||
.text BLOCK (4K) : ALIGN(4K)
|
||||
.multiboot.text :
|
||||
{
|
||||
*(.multiboot.text)
|
||||
}
|
||||
|
||||
. += 0xC0000000;
|
||||
|
||||
.text BLOCK (4K) : AT (ADDR (.text) - 0xC0000000)
|
||||
{
|
||||
*(.multiboot)
|
||||
*(.text)
|
||||
}
|
||||
|
||||
/* Read-only data. */
|
||||
.rodata BLOCK(4K) : ALIGN(4M)
|
||||
.rodata BLOCK(4K) : AT (ADDR (.rodata) - 0xC0000000)
|
||||
{
|
||||
*(.rodata)
|
||||
}
|
||||
|
||||
/* Read-write data (initialized) */
|
||||
.data BLOCK(4K) : ALIGN(4K)
|
||||
.data BLOCK(4K) : AT (ADDR (.data) - 0xC0000000)
|
||||
{
|
||||
*(.data)
|
||||
}
|
||||
|
||||
/* Read-write data (uninitialized) and stack */
|
||||
.bss BLOCK(4K) : ALIGN(4K)
|
||||
.bss BLOCK(4K) : AT (ADDR (.bss) - 0xC0000000)
|
||||
{
|
||||
*(COMMON)
|
||||
*(.bss)
|
||||
}
|
||||
|
||||
/* The compiler may produce other sections, by default it will put them in
|
||||
a segment with the same name. Simply add stuff here as needed. */
|
||||
_kernel_end = .;
|
||||
}
|
||||
|
|
26
arch/i686/paging.c
Normal file
26
arch/i686/paging.c
Normal file
|
@ -0,0 +1,26 @@
|
|||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <kernel/paging.h>
|
||||
#include <kernel/tty.h>
|
||||
|
||||
struct x86_Page_Directory {
|
||||
uint32_t address : 20;
|
||||
uint8_t available_1 : 4;
|
||||
bool page_Size: 1;
|
||||
bool available_2: 1;
|
||||
bool accesed: 1;
|
||||
bool cache_disable: 1;
|
||||
bool write_through: 1;
|
||||
bool user_supervisor: 1;
|
||||
bool read_write: 1;
|
||||
bool present: 1;
|
||||
|
||||
} __attribute__((packed));
|
||||
|
||||
struct x86_Page_Directory page_directory[1024] __attribute__((aligned(4096)));
|
||||
|
||||
void setup_paging() {
|
||||
size_t size = sizeof(page_directory[0]);
|
||||
}
|
|
@ -60,7 +60,7 @@ void terminal_initialize(void)
|
|||
terminal_row = 0;
|
||||
terminal_column = 0;
|
||||
terminal_color = vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK);
|
||||
terminal_buffer = (uint16_t*) 0xB8000;
|
||||
terminal_buffer = (uint16_t*) 0xC03FF000;
|
||||
for (size_t y = 0; y < VGA_HEIGHT; y++) {
|
||||
for (size_t x = 0; x < VGA_WIDTH; x++) {
|
||||
const size_t index = y * VGA_WIDTH + x;
|
||||
|
@ -117,11 +117,13 @@ void terminal_putchar(char c)
|
|||
return;
|
||||
}
|
||||
terminal_putentryat(c, terminal_color, terminal_column, terminal_row);
|
||||
if (++terminal_column == VGA_WIDTH) {
|
||||
if (++terminal_column >= VGA_WIDTH) {
|
||||
terminal_column = 0;
|
||||
if (++terminal_row == VGA_HEIGHT)
|
||||
terminal_scroll();
|
||||
terminal_row++;
|
||||
}
|
||||
|
||||
if (terminal_row >= VGA_HEIGHT)
|
||||
terminal_scroll();
|
||||
}
|
||||
|
||||
static void terminal_write(const char* data, size_t size)
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
.global jump_to_userspace
|
||||
jump_to_userspace:
|
||||
mov $0x20 | 0x3, %ax
|
||||
movw %ax, %ds
|
||||
movw %ax, %es
|
||||
movw %ax, %fs
|
||||
movw %ax, %gs
|
||||
pushl $0x20 | 0x3 /* SS*/
|
||||
pushl $0x00c00100 /* ESP */
|
||||
pushf /* EFLAGS */
|
||||
popl %eax
|
||||
orl $0x200, %eax
|
||||
pushl %eax /* Set interrupts */
|
||||
pushl $0x18 | 0x3 /* CS */
|
||||
pushl $0x00800000 /* EIP */
|
||||
iret
|
|
@ -1,5 +1,5 @@
|
|||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
void run_program(uint8_t* program, size_t length);
|
||||
void run_program(uint8_t* code, size_t code_length, uint8_t* data, size_t data_length);
|
||||
|
||||
|
|
0
include/kernel/io/ata.h
Normal file
0
include/kernel/io/ata.h
Normal file
11
include/kernel/paging.h
Normal file
11
include/kernel/paging.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
#include <stddef.h>
|
||||
|
||||
struct PageDirectory {
|
||||
size_t page_table;
|
||||
};
|
||||
|
||||
struct PageTable {
|
||||
|
||||
};
|
||||
|
||||
void setup_paging();
|
40
include/kernel/task.h
Normal file
40
include/kernel/task.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
struct CPUState {
|
||||
size_t eax;
|
||||
size_t ebx;
|
||||
size_t ecx;
|
||||
size_t edx;
|
||||
size_t eip;
|
||||
size_t esp;
|
||||
size_t ebp;
|
||||
size_t edi;
|
||||
size_t esi;
|
||||
size_t cs;
|
||||
size_t ds;
|
||||
size_t eflags;
|
||||
};
|
||||
|
||||
struct Task {
|
||||
struct CPUState state;
|
||||
};
|
||||
|
||||
void switch_task(
|
||||
size_t eax,
|
||||
size_t ebx,
|
||||
size_t ecx,
|
||||
size_t edx,
|
||||
|
||||
size_t ebp,
|
||||
|
||||
size_t esp_real,
|
||||
|
||||
size_t eip,
|
||||
size_t cs,
|
||||
size_t eflags,
|
||||
size_t esp_fake,
|
||||
size_t ds
|
||||
);
|
||||
void setup_tasks();
|
||||
void add_task(struct Task task);
|
29
kernel/elf.c
29
kernel/elf.c
|
@ -3,6 +3,7 @@
|
|||
#include <debugging.h>
|
||||
#include <kernel/tty.h>
|
||||
#include <kernel/gdt.h>
|
||||
#include <kernel/task.h>
|
||||
#include <debugging.h>
|
||||
|
||||
static void* memcpy(void* restrict dstptr, const void* restrict srcptr, size_t size) {
|
||||
|
@ -15,10 +16,30 @@ static void* memcpy(void* restrict dstptr, const void* restrict srcptr, size_t s
|
|||
|
||||
extern void jump_to_userspace();
|
||||
|
||||
void run_program(uint8_t* program, size_t length)
|
||||
void run_program(uint8_t* code, size_t code_length, uint8_t* data, size_t data_length)
|
||||
{
|
||||
uint8_t* userland_code = (uint8_t*) 0x00800000;
|
||||
memcpy(userland_code, program, length);
|
||||
uint8_t* userland_code = (uint8_t*) 0x00000000;
|
||||
uint8_t* userland_data = (uint8_t*) 0x00800000;
|
||||
|
||||
jump_to_userspace();
|
||||
memcpy(userland_code, code, code_length);
|
||||
memcpy(userland_data, data, data_length);
|
||||
|
||||
struct CPUState target = {
|
||||
.eip = (size_t) userland_code,
|
||||
.esp = 0x00000000,
|
||||
.eax = 0,
|
||||
.ebx = 0,
|
||||
.ecx = 0,
|
||||
.edx = 0,
|
||||
.cs = 0x18 | 0x3,
|
||||
.ds = 0x20 | 0x3,
|
||||
.eflags = 0x0200,
|
||||
.ebp = 0x00c00f00,
|
||||
};
|
||||
|
||||
struct Task task = {
|
||||
.state = target,
|
||||
};
|
||||
|
||||
add_task(task);
|
||||
}
|
||||
|
|
0
kernel/io/fat.c
Normal file
0
kernel/io/fat.c
Normal file
|
@ -3,6 +3,8 @@
|
|||
#include <kernel/elf.h>
|
||||
#include <kernel/pic.h>
|
||||
#include <kernel/heap.h>
|
||||
#include <kernel/task.h>
|
||||
#include <kernel/paging.h>
|
||||
|
||||
#include <debugging.h>
|
||||
|
||||
|
@ -12,23 +14,36 @@
|
|||
#endif
|
||||
|
||||
uint8_t program[] = {
|
||||
0xba, 0x01, 0x00, 0x00, 0x00, // mov 1, %edx
|
||||
0xb8, 0xff, 0x00, 0x80, 0x00, // mov $buffer, %eax
|
||||
0xcd, 0x80, // int 0x80
|
||||
0xba, 0x00, 0x00, 0x00, 0x00, // mov 0, %edx
|
||||
// 0xba, 0x01, 0x00, 0x00, 0x00, // mov 1, %edx
|
||||
0xb8, 0x00, 0x00, 0xC0, 0x00, // mov $buffer, %eax
|
||||
// 0xcd, 0x80, // int 0x80
|
||||
0xb9, 0x00, 0x00, 0x00, 0x00, // mov 0, %ecx
|
||||
0xcd, 0x80, // int 0x80
|
||||
0xb9, 0x00, 0x00, 0x80, 0x00, // mov $start, %ecx
|
||||
0xff, 0xe1, // jmp *%ecx
|
||||
};
|
||||
|
||||
uint8_t data[] = "Hello From Userspace\n";
|
||||
|
||||
|
||||
void kernel_main(void)
|
||||
{
|
||||
/* Initialize terminal interface */
|
||||
terminal_initialize();
|
||||
terminal_writestring("Hello from Kernelspace\n");
|
||||
|
||||
init_pic();
|
||||
idt_init();
|
||||
init_pic();
|
||||
heap_init();
|
||||
|
||||
run_program(program, sizeof(program));
|
||||
setup_tasks();
|
||||
setup_paging();
|
||||
|
||||
asm("sti");
|
||||
|
||||
run_program(program, sizeof(program), data, sizeof(data));
|
||||
|
||||
while (true) {
|
||||
terminal_writestring("Hello from Kernelspace\n");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ static void print(char* buffer)
|
|||
|
||||
static void input(char* buffer)
|
||||
{
|
||||
terminal_writestring("Input: ");
|
||||
while (true) {
|
||||
for (size_t i = 0; i < keyboard_buffer_top; i++) {
|
||||
buffer[i] = keyboard_buffer[i];
|
||||
|
@ -22,9 +23,9 @@ static void input(char* buffer)
|
|||
}
|
||||
}
|
||||
|
||||
void _syscall(uint32_t a, uint32_t b, uint32_t c, uint32_t d)
|
||||
void _syscall(uint32_t a, uint32_t b, uint32_t c)
|
||||
{
|
||||
switch (d) {
|
||||
switch (c) {
|
||||
case 0x00:
|
||||
print((char*) a);
|
||||
break;
|
||||
|
|
181
kernel/task.c
Normal file
181
kernel/task.c
Normal file
|
@ -0,0 +1,181 @@
|
|||
#include <kernel/task.h>
|
||||
#include <kernel/heap.h>
|
||||
#include <kernel/tty.h>
|
||||
|
||||
struct Task* tasks;
|
||||
size_t tasks_capacity;
|
||||
size_t num_tasks;
|
||||
|
||||
size_t current_task = 0;
|
||||
|
||||
void setup_tasks()
|
||||
{
|
||||
tasks = malloc(sizeof(*tasks));
|
||||
tasks_capacity = 1;
|
||||
num_tasks = 1;
|
||||
}
|
||||
|
||||
void add_task(struct Task task)
|
||||
{
|
||||
asm("cli");
|
||||
if (num_tasks == tasks_capacity) {
|
||||
struct Task* temp = malloc(sizeof(*tasks) * tasks_capacity * 2);
|
||||
|
||||
for (size_t i = 0; i < num_tasks; i++) {
|
||||
temp[i] = tasks[i];
|
||||
}
|
||||
|
||||
tasks_capacity *= 2;
|
||||
|
||||
free(tasks);
|
||||
tasks = temp;
|
||||
}
|
||||
|
||||
tasks[num_tasks] = task;
|
||||
num_tasks++;
|
||||
asm("sti");
|
||||
}
|
||||
|
||||
size_t ss;
|
||||
size_t esp;
|
||||
size_t eflags;
|
||||
size_t cs;
|
||||
size_t eip;
|
||||
|
||||
void jump_kernel(
|
||||
size_t edi,
|
||||
size_t esi,
|
||||
size_t ebp,
|
||||
size_t esp,
|
||||
size_t ebx,
|
||||
size_t edx,
|
||||
size_t ecx,
|
||||
size_t eax
|
||||
);
|
||||
void jump_user(
|
||||
size_t edi,
|
||||
size_t esi,
|
||||
size_t ebp,
|
||||
size_t esp,
|
||||
size_t ebx,
|
||||
size_t edx,
|
||||
size_t ecx,
|
||||
size_t eax
|
||||
);
|
||||
|
||||
static void save_state(
|
||||
size_t eax,
|
||||
size_t ebx,
|
||||
size_t ecx,
|
||||
size_t edx,
|
||||
|
||||
size_t esp,
|
||||
size_t ebp,
|
||||
|
||||
size_t esi,
|
||||
size_t edi,
|
||||
|
||||
size_t eip,
|
||||
|
||||
size_t cs,
|
||||
size_t ds,
|
||||
|
||||
size_t eflags
|
||||
) {
|
||||
struct CPUState state = {
|
||||
.eax = eax,
|
||||
.ebx = ebx,
|
||||
.ecx = ecx,
|
||||
.edx = edx,
|
||||
|
||||
.esp = esp,
|
||||
.ebp = ebp,
|
||||
|
||||
.eip = eip,
|
||||
|
||||
.esi = esi,
|
||||
.edi = edi,
|
||||
|
||||
.cs = cs,
|
||||
.ds = ds,
|
||||
|
||||
.eflags = eflags,
|
||||
};
|
||||
|
||||
tasks[current_task].state = state;
|
||||
|
||||
current_task++;
|
||||
current_task %= num_tasks;
|
||||
}
|
||||
|
||||
void next_task() {
|
||||
struct CPUState s = tasks[current_task].state;
|
||||
|
||||
ss = s.ds;
|
||||
esp = s.esp;
|
||||
eflags = s.eflags;
|
||||
cs = s.cs;
|
||||
eip = s.eip;
|
||||
|
||||
if ((cs & 3) == 0) {
|
||||
jump_kernel(
|
||||
s.edi,
|
||||
s.esi,
|
||||
s.ebp,
|
||||
s.esp,
|
||||
s.ebx,
|
||||
s.edx,
|
||||
s.ecx,
|
||||
s.eax
|
||||
);
|
||||
} else {
|
||||
jump_user(
|
||||
s.edi,
|
||||
s.esi,
|
||||
s.ebp,
|
||||
s.esp,
|
||||
s.ebx,
|
||||
s.edx,
|
||||
s.ecx,
|
||||
s.eax
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _switch_task(
|
||||
size_t edi,
|
||||
size_t esi,
|
||||
size_t ebp,
|
||||
size_t esp_current,
|
||||
size_t ebx,
|
||||
size_t edx,
|
||||
size_t ecx,
|
||||
size_t eax,
|
||||
|
||||
size_t eip_passed,
|
||||
size_t cs_passed,
|
||||
size_t eflags_passed,
|
||||
size_t esp_passed,
|
||||
size_t ds_passed
|
||||
)
|
||||
{
|
||||
terminal_writestring("Switching task\n");
|
||||
if (num_tasks == 0) {
|
||||
return;
|
||||
};
|
||||
|
||||
size_t ds;
|
||||
if ((cs & 3) == 0) {
|
||||
ds = 0x10;
|
||||
esp_current = esp_current + sizeof(size_t) * 3;
|
||||
} else {
|
||||
ds = ds_passed;
|
||||
esp_current = esp_passed;
|
||||
}
|
||||
|
||||
save_state(eax, ebx, ecx, edx, esp_current, ebp, esi, edi, eip_passed, cs_passed, ds, eflags_passed);
|
||||
|
||||
next_task();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in a new issue