mirror of
https://github.com/encounter/osdev.git
synced 2026-07-10 12:18:43 -07:00
Initial work on paging & higher-half kernel
This commit is contained in:
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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) {
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define VIDEO_ADDRESS ((uint16_t *) 0xC00B8000)
|
||||
|
||||
/**********************************************************
|
||||
* Private kernel functions *
|
||||
**********************************************************/
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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()*/;
|
||||
}
|
||||
@@ -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
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user