Initial work on paging & higher-half kernel

This commit is contained in:
Luke Street
2018-09-28 16:10:20 -04:00
parent eaaf014454
commit 4baf81d91c
17 changed files with 147 additions and 446 deletions
-8
View File
@@ -1,8 +0,0 @@
cmake_minimum_required(VERSION 3.2)
project(boot NONE)
set(CMAKE_ASM_NASM_OBJECT_FORMAT bin)
set(CMAKE_ASM_NASM_LINK_EXECUTABLE "cp <OBJECTS> <TARGET>") # hacky hack
enable_language(ASM_NASM)
add_executable(boot.bin main.asm)
-42
View File
@@ -1,42 +0,0 @@
; load 'dh' sectors from drive 'dl' into ES:BX
disk_load:
pusha
; reading from disk requires setting specific values in all registers
; so we will overwrite our input parameters from 'dx'. Let's save it
; to the stack for later use.
push dx
mov ah, 0x02 ; ah <- int 0x13 function. 0x02 = 'read'
mov al, dh ; al <- number of sectors to read (0x01 .. 0x80)
mov cl, 0x02 ; cl <- sector (0x01 .. 0x11)
; 0x01 is our boot sector, 0x02 is the first 'available' sector
mov ch, 0x00 ; ch <- cylinder (0x0 .. 0x3FF, upper 2 bits in 'cl')
; dl <- drive number. Our caller sets it as a parameter and gets it from BIOS
; (0 = floppy, 1 = floppy2, 0x80 = hdd, 0x81 = hdd2)
mov dh, 0x00 ; dh <- head number (0x0 .. 0xF)
; [es:bx] <- pointer to buffer where the data will be stored
; caller sets it up for us, and it is actually the standard location for int 13h
int 0x13 ; BIOS interrupt
jc disk_error ; if error (stored in the carry bit)
pop dx
cmp al, dh ; BIOS also sets 'al' to the # of sectors read. Compare it.
jne sectors_error
popa
ret
disk_error:
mov bx, DISK_ERROR
call print_str
mov dh, ah ; ah = error code, dl = disk drive that dropped the error
mov bx, dx
call print_hex
jmp $
sectors_error:
mov dx, SECTORS_ERROR
call print_str
DISK_ERROR: db "Disk read error", 0Dh, 0Ah, 0
SECTORS_ERROR: db "Incorrect number of sectors read", 0Dh, 0Ah, 0
-35
View File
@@ -1,35 +0,0 @@
gdt_start: ; don't remove the labels, they're needed to compute sizes and jumps
; the GDT starts with a null 8-byte
dd 0x0 ; 4 byte
dd 0x0 ; 4 byte
; GDT for code segment. base = 0x00000000, length = 0xfffff
; for flags, refer to os-dev.pdf document, page 36
gdt_code:
dw 0xffff ; segment length, bits 0-15
dw 0x0 ; segment base, bits 0-15
db 0x0 ; segment base, bits 16-23
db 10011010b ; flags (8 bits)
db 11001111b ; flags (4 bits) + segment length, bits 16-19
db 0x0 ; segment base, bits 24-31
; GDT for data segment. base and length identical to code segment
; some flags changed, again, refer to os-dev.pdf
gdt_data:
dw 0xffff
dw 0x0
db 0x0
db 10010010b
db 11001111b
db 0x0
gdt_end:
; GDT descriptor
gdt_descriptor:
dw gdt_end - gdt_start - 1 ; size (16 bit), always one less of its true size
dd gdt_start ; address (32 bit)
; define some constants for later use
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start
-98
View File
@@ -1,98 +0,0 @@
[bits 16]
[org 0x7C00]
STACK_BASE equ 0x9000
KERNEL_OFFSET equ 0x1000
start:
mov bp, STACK_BASE ; position our stack pointer
mov sp, bp
mov ax, 0x0003 ; "Set Video Mode" (mode 03h)
int 10h
mov bx, STARTING_UP
call print_str
mov bx, KERNEL_OFFSET
mov dh, 33
; mov dl, [BOOT_DRIVE]
call disk_load
mov ax, 0x0003 ; "Set Video Mode" (mode 03h)
int 10h
call switch_to_pm
jmp $ ; never called
; Prints null-terminated string referenced by `bx`
print_str:
mov ah, 0Eh ; "Write Character in TTY Mode"
.inner:
mov al, [bx]
test al, al
je .end
int 10h
add bx, 1
jmp .inner
.end:
ret
; Prints hex string of 16-bit value stored in `bx`
print_hex:
mov ah, 0Eh ; "Write Character in TTY Mode"
mov al, '0'
int 10h
mov al, 'x'
int 10h
mov al, bh
shr al, 4
call print_hex_char
mov al, bh
and al, 0Fh
call print_hex_char
mov al, bl
shr al, 4
call print_hex_char
mov al, bl
and al, 0Fh
call print_hex_char
ret
; Prints hex representation of the value stored in `al`
print_hex_char:
mov ah, 0Eh ; "Write Character in TTY Mode"
cmp al, 9
jle .inner
add al, 7
.inner:
add al, 30h
int 10h
ret
%include "disk.asm"
%include "pm.asm" ; start of 32-bit PM code
%include "gdt.asm"
%include "print.asm"
begin_pm:
call KERNEL_OFFSET
jmp $ ; spin forever
; datas
ENDL: db 0Dh, 0Ah, 0
STARTING_UP: db 'Reading boot sector...', 0Dh, 0Ah, 0
PROT_MODE db "Loaded 32-bit protected mode", 0
; Fill rest of the bin with nops
times 510-($-$$) nop
; Bootloader magic!
dw 0xAA55
; my awesome kernel
; incbin "kernel/main.bin"
; incbin "boot/zero.bin" ; some padding
-22
View File
@@ -1,22 +0,0 @@
[bits 16]
switch_to_pm:
cli ; 1. disable interrupts
lgdt [gdt_descriptor] ; 2. load the GDT descriptor
mov eax, cr0
or eax, 0x1 ; 3. set 32-bit mode bit in cr0
mov cr0, eax
jmp CODE_SEG:init_pm ; 4. far jump by using a different segment
[bits 32]
init_pm: ; we are now using 32-bit instructions
mov ax, DATA_SEG ; 5. update the segment registers
mov ds, ax
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ebp, 0x90000 ; 6. update the stack right at the top of the free space
mov esp, ebp
call begin_pm ; 7. Call a well-known label with useful code
-23
View File
@@ -1,23 +0,0 @@
VIDEO_MEMORY equ 0xb8000
WHITE_ON_BLACK equ 0x0f ; the color byte for each character
print_string_pm:
pusha
mov edx, VIDEO_MEMORY
print_string_pm_loop:
mov al, [ebx] ; [ebx] is the address of our character
mov ah, WHITE_ON_BLACK
test al, al ; check if end of string
je print_string_pm_done
mov [edx], ax ; store character + attribute in video memory
add ebx, 1 ; next char
add edx, 2 ; next video memory position
jmp print_string_pm_loop
print_string_pm_done:
popa
ret
-8
View File
@@ -92,7 +92,6 @@ void ide_read_buffer(uint8_t channel, uint8_t reg, uint32_t *buffer, uint32_t qu
*/
if (reg > 0x07 && reg < 0x0C)
ide_write(channel, ATA_REG_CONTROL, (uint8_t) (0x80 | channels[channel].nIEN));
__asm__("pushw %es; movw %ds, %ax; movw %ax, %es");
if (reg < 0x08)
insl((uint16_t) (channels[channel].base + reg - 0x00), buffer, quads);
else if (reg < 0x0C)
@@ -101,7 +100,6 @@ void ide_read_buffer(uint8_t channel, uint8_t reg, uint32_t *buffer, uint32_t qu
insl((uint16_t) (channels[channel].ctrl + reg - 0x0A), buffer, quads);
else if (reg < 0x16)
insl((uint16_t) (channels[channel].bm_ide + reg - 0x0E), buffer, quads);
__asm__("popw %es;");
if (reg > 0x07 && reg < 0x0C)
ide_write(channel, ATA_REG_CONTROL, channels[channel].nIEN);
}
@@ -401,20 +399,14 @@ uint8_t ide_ata_access(uint8_t direction, uint8_t drive, uint32_t lba,
for (i = 0; i < numsects; i++) {
if ((err = ide_polling(channel, 1)))
return err; // Polling, set error and exit if there is.
__asm__("pushw %es");
__asm__("mov %%ax, %%es" : : "a"(selector));
__asm__("rep insw" : : "c"(words), "d"(bus), "D"(edi)); // Receive Data.
__asm__("popw %es");
edi += (words * 2);
}
else {
// PIO Write.
for (i = 0; i < numsects; i++) {
ide_polling(channel, 0); // Polling.
__asm__("pushw %ds");
__asm__("mov %%ax, %%ds"::"a"(selector));
__asm__("rep outsw"::"c"(words), "d"(bus), "S"(edi)); // Send Data
__asm__("popw %ds");
edi += (words * 2);
}
ide_write(channel, ATA_REG_COMMAND,
+18 -18
View File
@@ -59,24 +59,24 @@ bool pci_check_function(uint32_t id) {
uint16_t class = pci_read_class(id);
uint16_t revision = pci_read_revision(id);
uint8_t header_type = pci_read_header_type(id);
vc_vector_push_back(pci_devices, &(pci_device_t) {
.loc = {
.bus = PCI_ID_BUS(id),
.device = PCI_ID_DEV(id),
.function = PCI_ID_FUNC(id)
},
.class = class,
.vendor_id = vendor_id,
.device_id = device_id,
.revision_id = (uint8_t) (revision & 0xFF),
.prog_if = (uint8_t) (revision >> 8 & 0xFF),
.bar0 = pci_config_read_long(id, PCI_HEADER_BAR0_ADDR),
.bar1 = pci_config_read_long(id, PCI_HEADER_BAR1_ADDR),
.bar2 = header_type == 0x00 ? pci_config_read_long(id, PCI_HEADER_BAR2_ADDR) : 0,
.bar3 = header_type == 0x00 ? pci_config_read_long(id, PCI_HEADER_BAR3_ADDR) : 0,
.bar4 = header_type == 0x00 ? pci_config_read_long(id, PCI_HEADER_BAR4_ADDR) : 0,
.bar5 = header_type == 0x00 ? pci_config_read_long(id, PCI_HEADER_BAR5_ADDR) : 0,
});
// vc_vector_push_back(pci_devices, &(pci_device_t) {
// .loc = {
// .bus = PCI_ID_BUS(id),
// .device = PCI_ID_DEV(id),
// .function = PCI_ID_FUNC(id)
// },
// .class = class,
// .vendor_id = vendor_id,
// .device_id = device_id,
// .revision_id = (uint8_t) (revision & 0xFF),
// .prog_if = (uint8_t) (revision >> 8 & 0xFF),
// .bar0 = pci_config_read_long(id, PCI_HEADER_BAR0_ADDR),
// .bar1 = pci_config_read_long(id, PCI_HEADER_BAR1_ADDR),
// .bar2 = header_type == 0x00 ? pci_config_read_long(id, PCI_HEADER_BAR2_ADDR) : 0,
// .bar3 = header_type == 0x00 ? pci_config_read_long(id, PCI_HEADER_BAR3_ADDR) : 0,
// .bar4 = header_type == 0x00 ? pci_config_read_long(id, PCI_HEADER_BAR4_ADDR) : 0,
// .bar5 = header_type == 0x00 ? pci_config_read_long(id, PCI_HEADER_BAR5_ADDR) : 0,
// });
// PCI-to-PCI bridge
if (class == 0x0604) {
+2
View File
@@ -3,6 +3,8 @@
#include <string.h>
#define VIDEO_ADDRESS ((uint16_t *) 0xC00B8000)
/**********************************************************
* Private kernel functions *
**********************************************************/
-1
View File
@@ -2,7 +2,6 @@
#include "../../libc/common.h"
#define VIDEO_ADDRESS ((volatile uint16_t *) 0xb8000)
#define MAX_ROWS 25
#define MAX_COLS 80
+4 -1
View File
@@ -12,9 +12,12 @@ _unused
void isr_handler(registers_t regs) {
kprint("Received interrupt: ");
kprint_uint32(regs.int_no);
kprint(" @ ");
kprint(" (err: ");
kprint_uint32(regs.err_code);
kprint(") @ ");
kprint_uint32(regs.eip);
kprint_char('\n');
panic(NULL);
}
_unused
+3
View File
@@ -12,6 +12,8 @@
// #define KDEBUG
extern bool vc_vector_run_tests();
_noreturn _unused
void kernel_main(uint32_t multiboot_magic, void *multiboot_info) {
serial_init();
@@ -22,6 +24,7 @@ void kernel_main(uint32_t multiboot_magic, void *multiboot_info) {
ata_init();
clear_screen();
vc_vector_run_tests();
#ifdef KDEBUG
kprint("Initializing timer...\n");
+18 -75
View File
@@ -1,6 +1,9 @@
#include "multiboot.h"
#include "console.h"
#define PAGE_OFFSET 0xC0000000
#define KERNEL_OFFSET 0x100000 // FIXME
extern void *malloc_memory_start;
extern void *malloc_memory_end;
@@ -16,13 +19,14 @@ void multiboot_init(uint32_t magic, void *info_ptr) {
panic("multiboot_magic: Invalid magic.\n");
}
struct multiboot_info *info = (struct multiboot_info *) info_ptr;
kprint("flags = "); kprint_uint32(info->flags); kprint_char('\n');
struct multiboot_info *info = (struct multiboot_info *) (info_ptr + PAGE_OFFSET);
kprint("multiboot_info = "); kprint_uint32((uintptr_t) info);
kprint("\nflags = "); kprint_uint32(info->flags); kprint_char('\n');
if (CHECK_FLAG(info->flags, MULTIBOOT_INFO_MEMORY)) {
malloc_memory_start = (void *) 0x100000;
malloc_memory_start = (void *) 0x100000 + PAGE_OFFSET + KERNEL_OFFSET;
// 1 MiB + (info->mem_upper * 1 KiB)
malloc_memory_end = malloc_memory_start + (info->mem_upper * 0x400);
malloc_memory_end = malloc_memory_start + (info->mem_upper * 0x400) - KERNEL_OFFSET;
kprint("upper_memory_end = "); kprint_uint32((uintptr_t) malloc_memory_end);
kprint_char('\n');
} else {
@@ -35,7 +39,7 @@ void multiboot_init(uint32_t magic, void *info_ptr) {
}
if (CHECK_FLAG(info->flags, MULTIBOOT_INFO_CMDLINE)) {
kprint("cmdline = "); kprint((char *) info->cmdline);
kprint("cmdline = "); kprint((char *) (info->cmdline + PAGE_OFFSET));
kprint_char('\n');
}
@@ -46,12 +50,12 @@ void multiboot_init(uint32_t magic, void *info_ptr) {
kprint("mods_count = "); kprint_uint32(info->mods_count);
kprint(", mods_addr = "); kprint_uint32(info->mods_addr);
kprint_char('\n');
for (i = 0, mod = (struct multiboot_mod_list *) info->mods_addr;
for (i = 0, mod = (struct multiboot_mod_list *) (info->mods_addr + PAGE_OFFSET);
i < info->mods_count;
i++, mod++) {
kprint(" mod_start = "); kprint_uint32(mod->mod_start);
kprint(", mod_end = "); kprint_uint32(mod->mod_end);
kprint(", cmdline = "); kprint((char *) mod->cmdline);
kprint(", cmdline = "); kprint((char *) (mod->cmdline + PAGE_OFFSET));
kprint_char('\n');
}
}
@@ -92,8 +96,8 @@ void multiboot_init(uint32_t magic, void *info_ptr) {
kprint_char('\n');
struct multiboot_mmap_entry *largest_available_entry = NULL;
for (mmap = (struct multiboot_mmap_entry *) info->mmap_addr;
(unsigned long) mmap < info->mmap_addr + info->mmap_length;
for (mmap = (struct multiboot_mmap_entry *) (info->mmap_addr + PAGE_OFFSET);
(unsigned long) mmap < info->mmap_addr + PAGE_OFFSET + info->mmap_length;
mmap = (struct multiboot_mmap_entry *)
((unsigned long) mmap + mmap->size + sizeof(mmap->size))) {
if (mmap->type == MULTIBOOT_MEMORY_AVAILABLE &&
@@ -109,93 +113,32 @@ void multiboot_init(uint32_t magic, void *info_ptr) {
}
if (largest_available_entry != NULL) {
malloc_memory_start = (void *) (uint32_t) largest_available_entry->addr;
malloc_memory_end = malloc_memory_start + largest_available_entry->len;
// malloc_memory_start = (void *) (uint32_t) largest_available_entry->addr + PAGE_OFFSET + KERNEL_OFFSET;
// malloc_memory_end = malloc_memory_start + largest_available_entry->len - KERNEL_OFFSET;
kprint("malloc_memory_start = "); kprint_uint32((uintptr_t) malloc_memory_start);
kprint(", end = "); kprint_uint32((uintptr_t) malloc_memory_end);
kprint_char('\n');
}
}
/* Draw diagonal blue line. */
/* Check VGA framebuffer. */
if (CHECK_FLAG (info->flags, MULTIBOOT_INFO_FRAMEBUFFER_INFO)) {
uint32_t color;
unsigned i;
void *fb = (void *) (unsigned long) info->framebuffer_addr;
kprint("framebuffer addr = "); kprint_uint64(info->framebuffer_addr);
kprint(", type = "); kprint_uint8(info->framebuffer_type);
kprint(", bpp = "); kprint_uint8(info->framebuffer_bpp);
kprint_char('\n');
switch (info->framebuffer_type) {
case MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED: {
unsigned best_distance, distance;
struct multiboot_color *palette;
palette = (struct multiboot_color *) info->framebuffer_palette_addr;
color = 0;
best_distance = 4 * 256 * 256;
for (i = 0; i < info->framebuffer_palette_num_colors; i++) {
distance = (0xff - palette[i].blue) * (0xff - palette[i].blue)
+ palette[i].red * palette[i].red
+ palette[i].green * palette[i].green;
if (distance < best_distance) {
color = i;
best_distance = distance;
}
}
}
break;
case MULTIBOOT_FRAMEBUFFER_TYPE_RGB:
color = ((1 << info->framebuffer_blue_mask_size) - 1)
<< info->framebuffer_blue_field_position;
break;
case MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT:
console_set_vga_enabled(true);
color = '\\' | 0x0100;
break;
default:
color = 0xffffffff;
break;
}
for (i = 0; i < info->framebuffer_width
&& i < info->framebuffer_height; i++) {
switch (info->framebuffer_bpp) {
case 8: {
uint8_t *pixel = fb + info->framebuffer_pitch * i + i;
*pixel = (uint8_t) color;
}
break;
case 15:
case 16: {
uint16_t *pixel
= fb + info->framebuffer_pitch * i + 2 * i;
*pixel = (uint16_t) color;
}
break;
case 24: {
uint32_t *pixel
= fb + info->framebuffer_pitch * i + 3 * i;
*pixel = (color & 0xffffff) | (*pixel & 0xff000000);
}
break;
case 32: {
uint32_t *pixel
= fb + info->framebuffer_pitch * i + 4 * i;
*pixel = color;
}
break;
}
}
} else {
console_set_vga_enabled(true); // FIXME
}
kprint("Multiboot info loaded.\n");
}
+2 -2
View File
@@ -23,7 +23,7 @@ static size_t key_buffer_printed;
static vc_vector *shell_history;
static size_t shell_history_offset = 0;
static void command_lspci() {
void command_lspci() {
vc_vector *pci_devices = pci_get_devices();
for (pci_device_t *device = vc_vector_begin(pci_devices);
device != vc_vector_end(pci_devices);
@@ -84,7 +84,7 @@ static void command_lspci() {
kprint_char('\n');
}
static void command_lsata() {
void command_lsata() {
for (uint8_t i = 0; i < 4; i++) {
if (ide_devices[i].reserved == 1) {
kprint_uint8(i);
+8 -2
View File
@@ -23,11 +23,15 @@
// ----------------------------------------------------------------------------
bool test_vc_vector_create() {
kprint("starting vector create\n");
const size_t size_of_type = sizeof(int);
kprint("fetching default\n");
const size_t default_count_of_elements = vc_vector_get_default_count_of_elements();
// Creating vector with default count of elements
kprint("creating vector w/ "); kprint_uint32(size_of_type); kprint_char('\n');
vc_vector *vector = vc_vector_create(0, size_of_type, NULL);
kprint("vector ptr = "); kprint_uint32((uintptr_t) vector); kprint_char('\n');
ASSERT_NE(NULL, vector);
ASSERT_EQ(0, vc_vector_count(vector));
ASSERT_EQ(0, vc_vector_size(vector));
@@ -319,10 +323,12 @@ bool test_vc_vector_with_strfreefunc() {
}
bool vc_vector_run_tests() {
return test_vc_vector_create() &&
kprint("from "); kprint_uint32(*(uint32_t *) 0x8000000); kprint_char('\n');
kprint("starting vector tests "); kprint_uint32((uintptr_t) test_vc_vector_create); kprint_char('\n');
return test_vc_vector_create() /*&&
test_vc_vector_element_access() &&
test_vc_vector_iterators() &&
test_vc_vector_capacity() &&
test_vc_vector_modifiers() &&
test_vc_vector_with_strfreefunc();
test_vc_vector_with_strfreefunc()*/;
}
+12 -29
View File
@@ -1,56 +1,39 @@
/* The bootloader will look at this image and start execution at the symbol
designated as the entry point. */
ENTRY(_start)
OUTPUT_FORMAT(elf32-i386)
/* Tell where the various sections of the object files will be put in the final
kernel image. */
SECTIONS
{
/* Begin putting sections at 1 MiB, a conventional place for kernels to be
loaded at by the bootloader. */
. = 1M;
SECTIONS {
/* The kernel will live at 3GB + 1MB in the virtual
address space, which will be mapped to 1MB in the
physical address space. */
. = 0xC0100000;
/* First put the multiboot header, as it is required to be put very early
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)
.text : AT(ADDR(.text) - 0xC0000000)
{
*(.multiboot)
*(.text)
}
/* Make sure the GNU notes information is placed after .text. Failure to
to do so may push the GRUB multiboot information beyond the first 8k
and GRUB will not identify this kernel as multiboot capable.
Alternative to this is to compile the final binary with this linker
option to exclude this unqiue header:
-Wl,--build-id=none */
.note.gnu.build-id BLOCK(4K) : ALIGN(4K)
{
*(.note.gnu.build-id)
}
/* Read-only data. */
.rodata BLOCK(4K) : ALIGN(4K)
.rodata ALIGN(4K) : AT(ADDR(.rodata) - 0xC0000000)
{
*(.rodata)
}
/* Read-write data (initialized) */
.data BLOCK(4K) : ALIGN(4K)
.data ALIGN(4K) : AT(ADDR(.data) - 0xC0000000)
{
*(.data)
}
/* Read-write data (uninitialized) and stack */
.bss BLOCK(4K) : ALIGN(4K)
.bss ALIGN(4K) : AT(ADDR(.bss) - 0xC0000000)
{
_sbss = .;
*(COMMON)
*(.bss)
*(.bootstrap_stack)
_ebss = .;
}
/* 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. */
}
+80 -82
View File
@@ -6,6 +6,30 @@ FLAGS equ FMBALIGN | FMEMINFO | FVIDMODE
MAGIC equ 0x1BADB002
CHECKSUM equ -(MAGIC + FLAGS)
; This is the virtual base address of kernel space. It must be used to convert virtual
; addresses into physical addresses until paging is enabled.
KERNEL_VIRTUAL_BASE equ 0xC0000000 ; 3GB
KERNEL_PAGE_NUMBER equ (KERNEL_VIRTUAL_BASE >> 22) ; 768 -- Page directory index of kernel's 4MB PTE.
section .data
align 0x1000
boot_page_directory:
; This page directory entry identity-maps the first 4MB of the 32-bit physical address space.
; All bits are clear except the following:
; bit 7: PS The kernel page is 4MB.
; bit 1: RW The kernel page is read/write.
; bit 0: P The kernel page is present.
; This entry must be here -- otherwise the kernel will crash immediately after paging is
; enabled because it can't fetch the next instruction! It's ok to unmap this page later.
dd 0x00000083
times (KERNEL_PAGE_NUMBER - 1) dd 0 ; Pages before kernel space.
; This page directory entry defines a 4MB page containing the kernel.
dd 0x00000083
dd 0x00000083
times (1024 - KERNEL_PAGE_NUMBER - 2) dd 0 ; Pages after the kernel image.
; 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
@@ -14,39 +38,30 @@ CHECKSUM equ -(MAGIC + FLAGS)
section .multiboot
align 4
; header
dd MAGIC
dd FLAGS
dd CHECKSUM
dd MAGIC
dd FLAGS
dd CHECKSUM
; address tag
dd 0 ; header_addr
dd 0 ; load_addr
dd 0 ; load_end_addr
dd 0 ; bss_end_addr
dd 0 ; entry_addr
; address tag
dd 0 ; header_addr
dd 0 ; load_addr
dd 0 ; load_end_addr
dd 0 ; bss_end_addr
dd 0 ; entry_addr
; graphics tag
dd 0 ; mode_type
dd 800 ; width
dd 600 ; height
dd 32 ; depth
; graphics tag
dd 1 ; mode_type
dd 800 ; width
dd 600 ; height
dd 32 ; depth
; The multiboot standard does not define the value of the stack pointer register
; (esp) and it is up to the kernel to provide a stack. This allocates room for a
; small stack by creating a symbol at the bottom of it, then allocating 16384
; bytes for it, and finally creating a symbol at the top. The stack grows
; downwards on x86. The stack is in its own section so it can be marked nobits,
; which means the kernel file is smaller because it does not contain an
; uninitialized stack. The stack on x86 must be 16-byte aligned according to the
; System V ABI standard and de-facto extensions. The compiler will assume the
; stack is properly aligned and failure to align the stack will result in
; undefined behavior.
; Create bootstrap stack
section .bss
align 16
stack_bottom:
resb 16384 ; 16 KiB
resb 0x4000 ; 16 KiB
stack_top:
; 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.
@@ -54,74 +69,57 @@ stack_top:
section .text
global _start:function (_start.end - _start)
_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.
mov ecx, (boot_page_directory - KERNEL_VIRTUAL_BASE)
mov cr3, ecx ; Load Page Directory Base Register.
; To set up a stack, we set the esp register to point to the top of our
; stack (as it grows downwards on x86 systems). This is necessarily done
; in assembly as languages such as C cannot function without a stack.
mov esp, stack_top
mov ecx, cr4
or ecx, (1 << 4) ; Set PSE bit in CR4 to enable 4MB pages.
mov cr4, ecx
mov ecx, cr0
or ecx, (1 << 31) ; Set PG bit in CR0 to enable paging.
mov cr0, ecx
lea ecx, [.start_higher_half] ; far jump
jmp ecx
.start_higher_half:
; Unmap the identity-mapped first 4MB of physical address space.
; It should not be needed anymore.
mov dword [boot_page_directory], 0
invlpg [0]
; Set up stack
mov esp, stack_top
; Save multiboot information
push ebx ; multiboot_info
push eax ; multiboot_magic
; 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.
push ebx ; multiboot_info
push eax ; multiboot_magic
; -- START SSE
mov eax, cr0
and ax, 0xFFFB ;clear coprocessor emulation CR0.EM
or ax, 0x2 ;set coprocessor monitoring CR0.MP
; Enable SSE
mov eax, cr0
and ax, 0xFFFB ; clear coprocessor emulation CR0.EM
or ax, (1 << 2) ; set coprocessor monitoring CR0.MP
mov cr0, eax
mov eax, cr4
or ax, 3 << 9 ;set CR4.OSFXSR and CR4.OSXMMEXCPT at the same time
or ax, (3 << 9) ; set CR4.OSFXSR and CR4.OSXMMEXCPT at the same time
mov cr4, eax
; -- END SSE
; -- START GDT/IDT
; Initialize GDT & IDT
extern init_descriptor_tables
call init_descriptor_tables
; -- END GDT/IDT
; 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 since pushed a multiple of 16 bytes to the
; stack since (pushed 0 bytes so far) and the alignment is thus
; preserved and the call is well defined.
; note, that if you are building on Windows, C functions may have "_" prefix in assembly: _kernel_main
extern kernel_main
call kernel_main
; 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 since pushed a multiple of 16 bytes to the
; stack since (pushed 0 bytes so far) and the alignment is thus
; preserved and the call is well defined.
extern kernel_main
call kernel_main
; 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
.hang:
hlt
jmp .hang
jmp .hang
.end: