Changess
This commit is contained in:
parent
bb441eaeb6
commit
887429636c
4
Makefile
4
Makefile
|
@ -31,12 +31,12 @@ LIB_OBJS=\
|
||||||
$(ARCHDIR)/bootstrap.o \
|
$(ARCHDIR)/bootstrap.o \
|
||||||
$(ARCHDIR)/tty.o \
|
$(ARCHDIR)/tty.o \
|
||||||
$(ARCHDIR)/strlib.o \
|
$(ARCHDIR)/strlib.o \
|
||||||
|
$(ARCHDIR)/paging.o \
|
||||||
|
$(ARCHDIR)/gdt.o \
|
||||||
$(ARCHDIR)/interrupts/idt.o \
|
$(ARCHDIR)/interrupts/idt.o \
|
||||||
$(ARCHDIR)/interrupts/interrupt.o \
|
$(ARCHDIR)/interrupts/interrupt.o \
|
||||||
$(ARCHDIR)/interrupts/irq0.o \
|
$(ARCHDIR)/interrupts/irq0.o \
|
||||||
$(ARCHDIR)/interrupts/syscall.o \
|
$(ARCHDIR)/interrupts/syscall.o \
|
||||||
$(ARCHDIR)/gdt.o \
|
|
||||||
$(ARCHDIR)/userland.o \
|
|
||||||
$(ARCHDIR)/interrupts/pic.o \
|
$(ARCHDIR)/interrupts/pic.o \
|
||||||
|
|
||||||
OBJS=$(KERNEL_OBJS) $(LIB_OBJS)
|
OBJS=$(KERNEL_OBJS) $(LIB_OBJS)
|
||||||
|
|
|
@ -1,18 +1,12 @@
|
||||||
/* Declare constants for the multiboot header. */
|
# Declare constants for the multiboot header.
|
||||||
.set ALIGN, 1<<0 /* align loaded modules on page boundaries */
|
.set ALIGN, 1<<0 # align loaded modules on page boundaries
|
||||||
.set MEMINFO, 1<<1 /* provide memory map */
|
.set MEMINFO, 1<<1 # provide memory map
|
||||||
.set FLAGS, ALIGN | MEMINFO /* this is the Multiboot 'flag' field */
|
.set FLAGS, ALIGN | MEMINFO # this is the Multiboot 'flag' field
|
||||||
.set MAGIC, 0x1BADB002 /* 'magic number' lets bootloader find the header */
|
.set MAGIC, 0x1BADB002 # 'magic number' lets bootloader find the header
|
||||||
.set CHECKSUM, -(MAGIC + FLAGS) /* checksum of above, to prove we are multiboot */
|
.set CHECKSUM, -(MAGIC + FLAGS) # checksum of above, to prove we are multiboot
|
||||||
|
|
||||||
/*
|
# Declare a multiboot header that marks the program as a kernel.
|
||||||
Declare a multiboot header that marks the program as a kernel. These are magic
|
.section .multiboot.data, "aw"
|
||||||
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
|
|
||||||
.align 4
|
.align 4
|
||||||
.long MAGIC
|
.long MAGIC
|
||||||
.long FLAGS
|
.long FLAGS
|
||||||
|
@ -31,100 +25,109 @@ stack is properly aligned and failure to align the stack will result in
|
||||||
undefined behavior.
|
undefined behavior.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.section .bss
|
.section .bss, "aw", @nobits
|
||||||
.align 16
|
.align 16
|
||||||
stack_bottom:
|
stack_bottom:
|
||||||
.skip 16384 # 16 KiB
|
.skip 16384 # 16 KiB
|
||||||
stack_top:
|
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
|
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
|
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.
|
doesn't make sense to return from this function as the bootloader is gone.
|
||||||
*/
|
*/
|
||||||
.section .text
|
.section .multiboot.text, "a"
|
||||||
.global _start
|
.global _start
|
||||||
.type _start, @function
|
.type _start, @function
|
||||||
_start:
|
_start:
|
||||||
/*
|
# Physical address of boot_page_table1.
|
||||||
The bootloader has loaded us into 32-bit protected mode on a x86
|
# TODO: I recall seeing some assembly that used a macro to do the
|
||||||
machine. Interrupts are disabled. Paging is disabled. The processor
|
# conversions to and from physical. Maybe this should be done in this
|
||||||
state is as defined in the multiboot standard. The kernel has full
|
# code as well?
|
||||||
control of the CPU. The kernel can only make use of hardware features
|
movl $(boot_page_table1 - 0xC0000000), %edi
|
||||||
and any code it provides as part of itself. There's no printf
|
# First address to map is address 0.
|
||||||
function, unless the kernel provides its own <stdio.h> header and a
|
# TODO: Start at the first kernel page instead. Alternatively map the first
|
||||||
printf implementation. There are no security restrictions, no
|
# 1 MiB as it can be generally useful, and there's no need to
|
||||||
safeguards, no debugging mechanisms, only what the kernel provides
|
# specially map the VGA buffer.
|
||||||
itself. It has absolute and complete power over the
|
movl $0, %esi
|
||||||
machine.
|
# Map 1023 pages. The 1024th will be the VGA text buffer.
|
||||||
*/
|
movl $1023, %ecx
|
||||||
|
|
||||||
/*
|
1:
|
||||||
To set up a stack, we set the esp register to point to the top of the
|
# Only map the kernel.
|
||||||
stack (as it grows downwards on x86 systems). This is necessarily done
|
cmpl $_kernel_start, %esi
|
||||||
in assembly as languages such as C cannot function without a stack.
|
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
|
mov $stack_top, %esp
|
||||||
|
|
||||||
cli
|
# Enter the high-level kernel.
|
||||||
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
call kernel_main
|
call kernel_main
|
||||||
|
|
||||||
.global keyboard_test
|
# Infinite loop if the system has nothing more to do.
|
||||||
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.
|
|
||||||
*/
|
|
||||||
cli
|
cli
|
||||||
1: hlt
|
1: hlt
|
||||||
jmp 1b
|
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
|
|
||||||
|
|
|
@ -30,14 +30,20 @@ __attribute__((interrupt)) void divide_by_zero(struct interrupt_frame*)
|
||||||
__attribute__((interrupt)) void general_protection_fault(struct interrupt_frame*)
|
__attribute__((interrupt)) void general_protection_fault(struct interrupt_frame*)
|
||||||
{
|
{
|
||||||
terminal_writestring("GPF handler called\n");
|
terminal_writestring("GPF handler called\n");
|
||||||
while (1) { }
|
while (1) {
|
||||||
|
asm("cli");
|
||||||
|
asm("hlt");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((interrupt)) void double_fault(struct interrupt_frame*)
|
__attribute__((interrupt)) void double_fault(struct interrupt_frame*)
|
||||||
{
|
{
|
||||||
stack_trace();
|
stack_trace();
|
||||||
terminal_writestring("2 Errors in a row, u better behave naughty naughty\n");
|
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(struct interrupt_frame*)
|
__attribute__((interrupt)) void exception(struct interrupt_frame*)
|
||||||
|
@ -48,6 +54,7 @@ __attribute__((interrupt)) void exception(struct interrupt_frame*)
|
||||||
|
|
||||||
__attribute__((interrupt)) void keyboard_interrupt(struct interrupt_frame*)
|
__attribute__((interrupt)) void keyboard_interrupt(struct interrupt_frame*)
|
||||||
{
|
{
|
||||||
|
terminal_writestring("Keyboard Interrupt\n");
|
||||||
uint8_t scancode = inb(0x60);
|
uint8_t scancode = inb(0x60);
|
||||||
handle_keyboard(scancode);
|
handle_keyboard(scancode);
|
||||||
outb(0x20, 0x20);
|
outb(0x20, 0x20);
|
||||||
|
@ -55,11 +62,13 @@ __attribute__((interrupt)) void keyboard_interrupt(struct interrupt_frame*)
|
||||||
|
|
||||||
__attribute__((interrupt)) void irq_1(struct interrupt_frame*)
|
__attribute__((interrupt)) void irq_1(struct interrupt_frame*)
|
||||||
{
|
{
|
||||||
|
terminal_writestring("PIC 1\n");
|
||||||
outb(0x20, 0x20);
|
outb(0x20, 0x20);
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((interrupt)) void irq_2(struct interrupt_frame*)
|
__attribute__((interrupt)) void irq_2(struct interrupt_frame*)
|
||||||
{
|
{
|
||||||
|
terminal_writestring("PIC 2\n");
|
||||||
outb(0xA0, 0x20);
|
outb(0xA0, 0x20);
|
||||||
outb(0x20, 0x20);
|
outb(0x20, 0x20);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,30 +11,34 @@
|
||||||
.extern eip
|
.extern eip
|
||||||
|
|
||||||
irq0:
|
irq0:
|
||||||
push %esp
|
pusha
|
||||||
|
|
||||||
push %ebp
|
mov $0x10, %eax
|
||||||
|
mov %ax, %ds
|
||||||
push %edx
|
mov %ax, %es
|
||||||
push %ecx
|
mov %ax, %fs
|
||||||
push %ebx
|
mov %ax, %gs
|
||||||
push %eax
|
|
||||||
|
|
||||||
call _switch_task
|
call _switch_task
|
||||||
|
|
||||||
iret
|
iret
|
||||||
|
|
||||||
jump_kernel:
|
jump_kernel:
|
||||||
pop %eax /* pop return instruction */
|
mov $0x20, %eax
|
||||||
|
outb %al, $0x20
|
||||||
|
|
||||||
pop %eax
|
mov (ss), %eax
|
||||||
pop %ebx
|
mov %ax, %ds
|
||||||
pop %ecx
|
mov %ax, %es
|
||||||
pop %edx
|
mov %ax, %fs
|
||||||
|
mov %ax, %gs
|
||||||
|
|
||||||
pop %ebp
|
popa
|
||||||
pop %esp
|
|
||||||
|
|
||||||
|
mov (esp), %esp
|
||||||
|
|
||||||
|
push (ss)
|
||||||
|
push (esp)
|
||||||
push (eflags)
|
push (eflags)
|
||||||
push (cs)
|
push (cs)
|
||||||
push (eip)
|
push (eip)
|
||||||
|
@ -42,14 +46,16 @@ push (eip)
|
||||||
iret
|
iret
|
||||||
|
|
||||||
jump_user:
|
jump_user:
|
||||||
pop %eax /* pop return instruction */
|
mov $0x20, %eax
|
||||||
|
outb %al, $0x20
|
||||||
|
|
||||||
pop %eax
|
mov (ss), %eax
|
||||||
pop %ebx
|
mov %ax, %ds
|
||||||
pop %ecx
|
mov %ax, %es
|
||||||
pop %edx
|
mov %ax, %fs
|
||||||
|
mov %ax, %gs
|
||||||
|
|
||||||
pop %ebp
|
popa
|
||||||
|
|
||||||
push (ss)
|
push (ss)
|
||||||
push (esp)
|
push (esp)
|
||||||
|
|
|
@ -1,9 +1,20 @@
|
||||||
.extern _syscall
|
.extern _syscall
|
||||||
.global syscall
|
.global syscall
|
||||||
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 %ecx
|
||||||
push %ebx
|
push %ebx
|
||||||
push %eax
|
push %eax
|
||||||
|
@ -13,6 +24,5 @@ call _syscall
|
||||||
pop %eax
|
pop %eax
|
||||||
pop %ebx
|
pop %ebx
|
||||||
pop %ecx
|
pop %ecx
|
||||||
pop %edx
|
|
||||||
iret
|
iret
|
||||||
|
|
||||||
|
|
|
@ -6,47 +6,43 @@ ENTRY(_start)
|
||||||
kernel image. */
|
kernel image. */
|
||||||
SECTIONS
|
SECTIONS
|
||||||
{
|
{
|
||||||
/* It used to be universally recommended to use 1M as a start offset,
|
. = 1M;
|
||||||
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;
|
|
||||||
|
|
||||||
/* First put the multiboot header, as it is required to be put very early
|
_kernel_start = .;
|
||||||
in the image or the bootloader won't recognize the file format.
|
.multiboot.data :
|
||||||
Next we'll put the .text section. */
|
{
|
||||||
.text BLOCK (4K) : ALIGN(4K)
|
*(.multiboot.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
.multiboot.text :
|
||||||
|
{
|
||||||
|
*(.multiboot.text)
|
||||||
|
}
|
||||||
|
|
||||||
|
. += 0xC0000000;
|
||||||
|
|
||||||
|
.text BLOCK (4K) : AT (ADDR (.text) - 0xC0000000)
|
||||||
{
|
{
|
||||||
*(.multiboot)
|
|
||||||
*(.text)
|
*(.text)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read-only data. */
|
/* Read-only data. */
|
||||||
.rodata BLOCK(4K) : ALIGN(4M)
|
.rodata BLOCK(4K) : AT (ADDR (.rodata) - 0xC0000000)
|
||||||
{
|
{
|
||||||
*(.rodata)
|
*(.rodata)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read-write data (initialized) */
|
/* Read-write data (initialized) */
|
||||||
.data BLOCK(4K) : ALIGN(4K)
|
.data BLOCK(4K) : AT (ADDR (.data) - 0xC0000000)
|
||||||
{
|
{
|
||||||
*(.data)
|
*(.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read-write data (uninitialized) and stack */
|
/* Read-write data (uninitialized) and stack */
|
||||||
.bss BLOCK(4K) : ALIGN(4K)
|
.bss BLOCK(4K) : AT (ADDR (.bss) - 0xC0000000)
|
||||||
{
|
{
|
||||||
*(COMMON)
|
*(COMMON)
|
||||||
*(.bss)
|
*(.bss)
|
||||||
}
|
}
|
||||||
|
_kernel_end = .;
|
||||||
/* 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. */
|
|
||||||
}
|
}
|
||||||
|
|
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_row = 0;
|
||||||
terminal_column = 0;
|
terminal_column = 0;
|
||||||
terminal_color = vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK);
|
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 y = 0; y < VGA_HEIGHT; y++) {
|
||||||
for (size_t x = 0; x < VGA_WIDTH; x++) {
|
for (size_t x = 0; x < VGA_WIDTH; x++) {
|
||||||
const size_t index = y * VGA_WIDTH + x;
|
const size_t index = y * VGA_WIDTH + x;
|
||||||
|
@ -117,11 +117,13 @@ void terminal_putchar(char c)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
terminal_putentryat(c, terminal_color, terminal_column, terminal_row);
|
terminal_putentryat(c, terminal_color, terminal_column, terminal_row);
|
||||||
if (++terminal_column == VGA_WIDTH) {
|
if (++terminal_column >= VGA_WIDTH) {
|
||||||
terminal_column = 0;
|
terminal_column = 0;
|
||||||
if (++terminal_row == VGA_HEIGHT)
|
terminal_row++;
|
||||||
terminal_scroll();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (terminal_row >= VGA_HEIGHT)
|
||||||
|
terminal_scroll();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void terminal_write(const char* data, size_t size)
|
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 <stdint.h>
|
||||||
#include <stddef.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);
|
||||||
|
|
||||||
|
|
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();
|
|
@ -9,6 +9,8 @@ struct CPUState {
|
||||||
size_t eip;
|
size_t eip;
|
||||||
size_t esp;
|
size_t esp;
|
||||||
size_t ebp;
|
size_t ebp;
|
||||||
|
size_t edi;
|
||||||
|
size_t esi;
|
||||||
size_t cs;
|
size_t cs;
|
||||||
size_t ds;
|
size_t ds;
|
||||||
size_t eflags;
|
size_t eflags;
|
||||||
|
|
13
kernel/elf.c
13
kernel/elf.c
|
@ -16,14 +16,17 @@ static void* memcpy(void* restrict dstptr, const void* restrict srcptr, size_t s
|
||||||
|
|
||||||
extern void jump_to_userspace();
|
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;
|
uint8_t* userland_code = (uint8_t*) 0x00000000;
|
||||||
memcpy(userland_code, program, length);
|
uint8_t* userland_data = (uint8_t*) 0x00800000;
|
||||||
|
|
||||||
|
memcpy(userland_code, code, code_length);
|
||||||
|
memcpy(userland_data, data, data_length);
|
||||||
|
|
||||||
struct CPUState target = {
|
struct CPUState target = {
|
||||||
.eip = (size_t) userland_code,
|
.eip = (size_t) userland_code,
|
||||||
.esp = 0x00c00100,
|
.esp = 0x00000000,
|
||||||
.eax = 0,
|
.eax = 0,
|
||||||
.ebx = 0,
|
.ebx = 0,
|
||||||
.ecx = 0,
|
.ecx = 0,
|
||||||
|
@ -31,7 +34,7 @@ void run_program(uint8_t* program, size_t length)
|
||||||
.cs = 0x18 | 0x3,
|
.cs = 0x18 | 0x3,
|
||||||
.ds = 0x20 | 0x3,
|
.ds = 0x20 | 0x3,
|
||||||
.eflags = 0x0200,
|
.eflags = 0x0200,
|
||||||
.ebp = 0x00c00100,
|
.ebp = 0x00c00f00,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Task task = {
|
struct Task task = {
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include <kernel/pic.h>
|
#include <kernel/pic.h>
|
||||||
#include <kernel/heap.h>
|
#include <kernel/heap.h>
|
||||||
#include <kernel/task.h>
|
#include <kernel/task.h>
|
||||||
|
#include <kernel/paging.h>
|
||||||
|
|
||||||
#include <debugging.h>
|
#include <debugging.h>
|
||||||
|
|
||||||
|
@ -13,33 +14,36 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
uint8_t program[] = {
|
uint8_t program[] = {
|
||||||
0xba, 0x01, 0x00, 0x00, 0x00, // mov 1, %edx
|
// 0xba, 0x01, 0x00, 0x00, 0x00, // mov 1, %edx
|
||||||
0xb8, 0xff, 0x00, 0x80, 0x00, // mov $buffer, %eax
|
0xb8, 0x00, 0x00, 0xC0, 0x00, // mov $buffer, %eax
|
||||||
|
// 0xcd, 0x80, // int 0x80
|
||||||
|
0xb9, 0x00, 0x00, 0x00, 0x00, // mov 0, %ecx
|
||||||
0xcd, 0x80, // int 0x80
|
0xcd, 0x80, // int 0x80
|
||||||
0xba, 0x00, 0x00, 0x00, 0x00, // mov 0, %edx
|
|
||||||
0xcd, 0x80, // int 0x80
|
|
||||||
0xcd, 0x20, // int 0x20
|
|
||||||
0xb9, 0x00, 0x00, 0x80, 0x00, // mov $start, %ecx
|
0xb9, 0x00, 0x00, 0x80, 0x00, // mov $start, %ecx
|
||||||
0xff, 0xe1, // jmp *%ecx
|
0xff, 0xe1, // jmp *%ecx
|
||||||
};
|
};
|
||||||
|
|
||||||
|
uint8_t data[] = "Hello From Userspace\n";
|
||||||
|
|
||||||
|
|
||||||
void kernel_main(void)
|
void kernel_main(void)
|
||||||
{
|
{
|
||||||
/* Initialize terminal interface */
|
/* Initialize terminal interface */
|
||||||
terminal_initialize();
|
terminal_initialize();
|
||||||
|
terminal_writestring("Hello from Kernelspace\n");
|
||||||
|
|
||||||
idt_init();
|
idt_init();
|
||||||
init_pic();
|
init_pic();
|
||||||
heap_init();
|
heap_init();
|
||||||
|
|
||||||
setup_tasks();
|
setup_tasks();
|
||||||
|
setup_paging();
|
||||||
|
|
||||||
asm("sti");
|
asm("sti");
|
||||||
|
|
||||||
run_program(program, sizeof(program));
|
run_program(program, sizeof(program), data, sizeof(data));
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
asm("hlt");
|
terminal_writestring("Hello from Kernelspace\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ static void print(char* buffer)
|
||||||
|
|
||||||
static void input(char* buffer)
|
static void input(char* buffer)
|
||||||
{
|
{
|
||||||
|
terminal_writestring("Input: ");
|
||||||
while (true) {
|
while (true) {
|
||||||
for (size_t i = 0; i < keyboard_buffer_top; i++) {
|
for (size_t i = 0; i < keyboard_buffer_top; i++) {
|
||||||
buffer[i] = keyboard_buffer[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:
|
case 0x00:
|
||||||
print((char*) a);
|
print((char*) a);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include <kernel/task.h>
|
#include <kernel/task.h>
|
||||||
#include <kernel/heap.h>
|
#include <kernel/heap.h>
|
||||||
|
#include <kernel/tty.h>
|
||||||
|
|
||||||
struct Task* tasks;
|
struct Task* tasks;
|
||||||
size_t tasks_capacity;
|
size_t tasks_capacity;
|
||||||
|
@ -16,6 +17,7 @@ void setup_tasks()
|
||||||
|
|
||||||
void add_task(struct Task task)
|
void add_task(struct Task task)
|
||||||
{
|
{
|
||||||
|
asm("cli");
|
||||||
if (num_tasks == tasks_capacity) {
|
if (num_tasks == tasks_capacity) {
|
||||||
struct Task* temp = malloc(sizeof(*tasks) * tasks_capacity * 2);
|
struct Task* temp = malloc(sizeof(*tasks) * tasks_capacity * 2);
|
||||||
|
|
||||||
|
@ -31,6 +33,7 @@ void add_task(struct Task task)
|
||||||
|
|
||||||
tasks[num_tasks] = task;
|
tasks[num_tasks] = task;
|
||||||
num_tasks++;
|
num_tasks++;
|
||||||
|
asm("sti");
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t ss;
|
size_t ss;
|
||||||
|
@ -39,8 +42,26 @@ size_t eflags;
|
||||||
size_t cs;
|
size_t cs;
|
||||||
size_t eip;
|
size_t eip;
|
||||||
|
|
||||||
void jump_kernel(size_t eax, size_t ebx, size_t ecx, size_t edx, size_t ebp, size_t esp);
|
void jump_kernel(
|
||||||
void jump_user(size_t eax, size_t ebx, size_t ecx, size_t edx, size_t ebp);
|
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(
|
static void save_state(
|
||||||
size_t eax,
|
size_t eax,
|
||||||
|
@ -51,6 +72,9 @@ static void save_state(
|
||||||
size_t esp,
|
size_t esp,
|
||||||
size_t ebp,
|
size_t ebp,
|
||||||
|
|
||||||
|
size_t esi,
|
||||||
|
size_t edi,
|
||||||
|
|
||||||
size_t eip,
|
size_t eip,
|
||||||
|
|
||||||
size_t cs,
|
size_t cs,
|
||||||
|
@ -69,6 +93,9 @@ static void save_state(
|
||||||
|
|
||||||
.eip = eip,
|
.eip = eip,
|
||||||
|
|
||||||
|
.esi = esi,
|
||||||
|
.edi = edi,
|
||||||
|
|
||||||
.cs = cs,
|
.cs = cs,
|
||||||
.ds = ds,
|
.ds = ds,
|
||||||
|
|
||||||
|
@ -91,21 +118,39 @@ void next_task() {
|
||||||
eip = s.eip;
|
eip = s.eip;
|
||||||
|
|
||||||
if ((cs & 3) == 0) {
|
if ((cs & 3) == 0) {
|
||||||
jump_kernel(s.eax, s.ebx, s.ecx, s.edx, s.ebp, s.esp);
|
jump_kernel(
|
||||||
|
s.edi,
|
||||||
|
s.esi,
|
||||||
|
s.ebp,
|
||||||
|
s.esp,
|
||||||
|
s.ebx,
|
||||||
|
s.edx,
|
||||||
|
s.ecx,
|
||||||
|
s.eax
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
jump_user(s.eax, s.ebx, s.ecx, s.edx, s.ebp);
|
jump_user(
|
||||||
|
s.edi,
|
||||||
|
s.esi,
|
||||||
|
s.ebp,
|
||||||
|
s.esp,
|
||||||
|
s.ebx,
|
||||||
|
s.edx,
|
||||||
|
s.ecx,
|
||||||
|
s.eax
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _switch_task(
|
void _switch_task(
|
||||||
size_t eax,
|
size_t edi,
|
||||||
size_t ebx,
|
size_t esi,
|
||||||
size_t ecx,
|
|
||||||
size_t edx,
|
|
||||||
|
|
||||||
size_t ebp,
|
size_t ebp,
|
||||||
|
|
||||||
size_t esp_current,
|
size_t esp_current,
|
||||||
|
size_t ebx,
|
||||||
|
size_t edx,
|
||||||
|
size_t ecx,
|
||||||
|
size_t eax,
|
||||||
|
|
||||||
size_t eip_passed,
|
size_t eip_passed,
|
||||||
size_t cs_passed,
|
size_t cs_passed,
|
||||||
|
@ -114,6 +159,7 @@ void _switch_task(
|
||||||
size_t ds_passed
|
size_t ds_passed
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
terminal_writestring("Switching task\n");
|
||||||
if (num_tasks == 0) {
|
if (num_tasks == 0) {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
@ -127,13 +173,7 @@ void _switch_task(
|
||||||
esp_current = esp_passed;
|
esp_current = esp_passed;
|
||||||
}
|
}
|
||||||
|
|
||||||
ss = ds_passed;
|
save_state(eax, ebx, ecx, edx, esp_current, ebp, esi, edi, eip_passed, cs_passed, ds, eflags_passed);
|
||||||
esp = esp_passed;
|
|
||||||
eflags = eflags_passed;
|
|
||||||
cs = cs_passed;
|
|
||||||
eip = eip_passed;
|
|
||||||
|
|
||||||
save_state(eax, ebx, ecx, edx, esp, ebp, eip_passed, cs_passed, ds, eflags_passed);
|
|
||||||
|
|
||||||
next_task();
|
next_task();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue