Compare commits
18 commits
Author | SHA1 | Date | |
---|---|---|---|
|
05c8516217 | ||
|
ef72a8d37a | ||
|
43bab5ff4f | ||
|
9a7992ae2e | ||
|
9536ddd3d3 | ||
|
95c0371065 | ||
|
3abf49f881 | ||
|
a0883e95d0 | ||
|
2a82f01493 | ||
|
41e3953d2c | ||
|
2f3b34475b | ||
|
2bbd40acf6 | ||
|
2177fa1bc9 | ||
|
1069a04ca3 | ||
|
9ebbd0728b | ||
|
6aba1fb4a8 | ||
|
b5678a0246 | ||
|
047f427581 |
18 changed files with 1084 additions and 150 deletions
2
.gdbinit
Normal file
2
.gdbinit
Normal file
|
@ -0,0 +1,2 @@
|
|||
target remote localhost:1234
|
||||
file target/kernel/kernel.bin
|
10
Makefile
10
Makefile
|
@ -1,6 +1,12 @@
|
|||
run: bin
|
||||
qemu-system-i386 -drive format=raw,file=target/boot.bin -monitor stdio
|
||||
|
||||
run_kernelonly: compile_kernel
|
||||
qemu-system-i386 -kernel target/kernel/kernel.bin -monitor stdio
|
||||
|
||||
debug_kernel: compile_kernel
|
||||
qemu-system-i386 -s -S -kernel target/kernel/kernel.bin
|
||||
|
||||
clean:
|
||||
rm -r ./target/
|
||||
mkdir -p ./target/helpers
|
||||
|
@ -19,5 +25,5 @@ compile_bootloader:
|
|||
|
||||
compile_kernel:
|
||||
nasm -felf32 kernel/wrapper.asm -o target/kernel/wrapper.o
|
||||
i686-elf-gcc -c kernel/kernel.c -o target/kernel/kernel.o -std=gnu99 -ffreestanding -O2 -Wall -Wextra
|
||||
i686-elf-gcc -T kernel/linker.ld -o target/kernel/kernel.bin -ffreestanding -O2 -nostdlib target/kernel/wrapper.o target/kernel/kernel.o -lgcc
|
||||
i686-elf-gcc -g -c kernel/kernel.c -o target/kernel/kernel.o -std=gnu99 -ffreestanding -Og -Wall -Wextra -mno-80387 -mgeneral-regs-only -mno-red-zone
|
||||
i686-elf-gcc -g -T kernel/linker.ld -o target/kernel/kernel.bin -ffreestanding -Og -nostdlib target/kernel/wrapper.o target/kernel/kernel.o -lgcc
|
||||
|
|
42
README.md
42
README.md
|
@ -1,6 +1,23 @@
|
|||
# RoBoot
|
||||
# TABS
|
||||
|
||||
My attempt at making a bootloader and kernel.
|
||||
Totaal Arbitrair BesturingsSysteem, Zeus maakt een besturingssysteem!
|
||||
|
||||
## How to use
|
||||
|
||||
To compile, you need:
|
||||
|
||||
- `i686-elf-gcc` for cross-compiling the kernel
|
||||
- A system `gcc` for compiling the host-helpers
|
||||
- `nasm` for compiling the bootloader
|
||||
- `qemu-system-i386` for emulating (`qemu-system-x86_64` will probably work too, but that is not directly supported)
|
||||
|
||||
More information about installing a cross compiler can be found [here](https://wiki.osdev.org/GCC_Cross-Compiler#The_Build).
|
||||
|
||||
If you run `make bin`, it will generate `target/boot.bin`, this is a binary file layed out as described in [the docs](docs/bootloader.md). To burn it on a USB drive, simply `dd if=target/boot.bin of=/dev/sdb` and the drive is bootable and contains an SFS filesystem with the files in `filesystem/`.
|
||||
|
||||
In case there are errors in the bootloader you can use `make compile_kernel` to only compile the kernel.
|
||||
|
||||
To run TABS in the qemu simulator run `make run`.
|
||||
|
||||
## Bootloader
|
||||
|
||||
|
@ -10,6 +27,23 @@ More info in [the docs](./docs/bootloader.md)
|
|||
|
||||
## Kernel
|
||||
|
||||
The kernel is basically [the bare bones kernel from the OSDev wiki](https://wiki.osdev.org/Bare_Bones). I have implemented newlines, but otherwise it's still exactly the same...
|
||||
The kernel is based on [the bare bones kernel from the OSDev wiki](https://wiki.osdev.org/Bare_Bones).
|
||||
|
||||
As a more "functional" example, I've implemented day 1 of [advent of code](https://adventofcode.com/) on the [AoC branch](https://github.com/Robbe7730/RoBoot/tree/AoC).
|
||||
### Features
|
||||
|
||||
- [x] Terminal output (with newlines!)
|
||||
- [x] _Very_ basic (and probably broken) memory management
|
||||
- [x] Interrupt handling
|
||||
- [x] Keyboard input
|
||||
- [x] Exception handling
|
||||
- [x] Minimal shell
|
||||
- [ ] Filesystem interaction
|
||||
- [ ] Show files in directory
|
||||
- [ ] Read files in directory
|
||||
- [ ] Write files in directory
|
||||
- [ ] Tests
|
||||
- [ ] Running executables from filesystem
|
||||
- [ ] Better memory management
|
||||
- [ ] Better shell
|
||||
|
||||
As a test, I've implemented day 1 of [advent of code](https://adventofcode.com/) on the [AoC branch](https://github.com/Robbe7730/RoBoot/tree/AoC).
|
||||
|
|
|
@ -136,9 +136,6 @@ mov ch, 0b00100000
|
|||
mov cl, 0b00000000
|
||||
int 0x10
|
||||
|
||||
mov bl, dl
|
||||
call puthex
|
||||
|
||||
; reset disk system
|
||||
mov ah, 0x00
|
||||
; mov dl, DISK_ID
|
||||
|
@ -213,52 +210,60 @@ or eax, 1
|
|||
mov cr0, eax
|
||||
|
||||
; jump to 32 bit code
|
||||
jmp 0x08:.clear_pipe
|
||||
jmp 0x08:.clear_cs
|
||||
|
||||
[BITS 32]
|
||||
.clear_pipe:
|
||||
.clear_cs:
|
||||
|
||||
; set Data Segment and Stack segment
|
||||
mov ax, 10h
|
||||
; set Data Segment, Stack Segment and other segments
|
||||
mov ax, 0x10
|
||||
mov ds, ax
|
||||
mov ss, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
|
||||
; set up stack
|
||||
mov esp, 0x090000
|
||||
|
||||
; parse elf file at ELF_START to KERNEL_START
|
||||
|
||||
; verify ELF header
|
||||
; verify ELF header (scrapped for memory)
|
||||
mov esi, ELF_START
|
||||
cmp dword [esi], 464C457Fh ; ELF magic
|
||||
jne .invalid_elf
|
||||
cmp word [esi+4], 0101h ; lsb 32 bit, little endian
|
||||
jne .invalid_elf
|
||||
cmp word [esi+18], 03 ; x86 architecture
|
||||
jne .invalid_elf
|
||||
; cmp dword [esi], 464C457Fh ; ELF magic
|
||||
; jne .invalid_elf
|
||||
; cmp word [esi+4], 0101h ; lsb 32 bit, little endian
|
||||
; jne .invalid_elf
|
||||
; cmp word [esi+18], 03 ; x86 architecture
|
||||
; jne .invalid_elf
|
||||
|
||||
; read the entrypoint and store it
|
||||
mov eax, dword [esi+0x18] ; program entry position
|
||||
mov dword [.entrypoint], eax
|
||||
|
||||
mov cx, word [esi+0x2C] ; read phnum (number of program headers)
|
||||
mov eax, dword [esi+0x1C] ; read phoff (offset of program header)
|
||||
mov ax, word [esi+0x2C] ; read phnum (number of program headers)
|
||||
|
||||
; ebx is now ELF_START, esi jumps to the start of the program header
|
||||
mov ebx, esi
|
||||
add esi, eax
|
||||
; move esi to the start of the program header
|
||||
add esi, dword [esi+0x1C]
|
||||
|
||||
; set up for loop
|
||||
sub esi, 0x20
|
||||
inc cx
|
||||
inc ax
|
||||
mov dword [.edi_backup], esi
|
||||
|
||||
.elf_ph_loop:
|
||||
mov ebx, ELF_START
|
||||
mov esi, dword [.edi_backup]
|
||||
add esi, 0x20
|
||||
dec cx
|
||||
jz .invalid_elf ; there is no valid code block
|
||||
mov dword [.edi_backup], esi
|
||||
dec ax
|
||||
jz .start_kernel ; there is no valid code block
|
||||
cmp word [esi], 1 ; check if p_type is loadable
|
||||
jne .elf_ph_loop
|
||||
|
||||
; set destination
|
||||
mov edi, dword [esi+0x08]
|
||||
|
||||
; add offset to ebx (ebx = pointer to code)
|
||||
add ebx, dword [esi+0x04]
|
||||
|
||||
|
@ -276,19 +281,26 @@ jz .invalid_elf
|
|||
; set source
|
||||
mov esi, ebx
|
||||
|
||||
; set destination
|
||||
mov edi, KERNEL_START
|
||||
|
||||
; repeat ecx/4 times (because it moves 4 bytes at a time)
|
||||
shr ecx, 2
|
||||
|
||||
; copy
|
||||
repnz movsd
|
||||
|
||||
or ax, ax
|
||||
jnz .elf_ph_loop
|
||||
|
||||
.start_kernel
|
||||
|
||||
cmp edi, KERNEL_START
|
||||
je .invalid_elf
|
||||
|
||||
; jump to start of kernel
|
||||
jmp [.entrypoint]
|
||||
|
||||
.end:
|
||||
cli
|
||||
hlt
|
||||
jmp .end
|
||||
|
||||
.invalid_elf:
|
||||
|
@ -309,22 +321,22 @@ times 66 - (.file_end - .data) db 0
|
|||
.data:
|
||||
|
||||
.gdt:
|
||||
dd 0
|
||||
dd 0
|
||||
dd 0x00000000
|
||||
dd 0x00000000
|
||||
|
||||
dw 0FFFFh
|
||||
dw 0
|
||||
db 0
|
||||
db 10011010b
|
||||
db 11001111b
|
||||
db 0
|
||||
dw 0xffff
|
||||
dw 0x0000
|
||||
db 0x00
|
||||
db 0b10011110
|
||||
db 0b11001111
|
||||
db 0x00
|
||||
|
||||
dw 0FFFFh
|
||||
dw 0
|
||||
db 0
|
||||
db 10010010b
|
||||
db 11001111b
|
||||
db 0
|
||||
dw 0xffff
|
||||
dw 0x0000
|
||||
db 0x00
|
||||
db 0b10010110
|
||||
db 0b11001111
|
||||
db 0x00
|
||||
.gdt_end:
|
||||
|
||||
.gdt_desc:
|
||||
|
@ -332,6 +344,7 @@ dw .gdt_end - .gdt - 1
|
|||
dd .gdt
|
||||
|
||||
.entrypoint: dd 0
|
||||
.edi_backup: dd 0
|
||||
|
||||
; magic string
|
||||
dw 0xAA55
|
||||
|
|
|
@ -10,10 +10,17 @@
|
|||
| 0x07c00 | 0x07dff | Bootloader |
|
||||
| 0x07e00 | 0x07fff | _free space_ |
|
||||
| 0x08000 | 0x0ffff | **ELF file** (64 sectors) |
|
||||
| 0x10000 | 0x7ffff | **kernel space** |
|
||||
| 0x00000 | 0x7ffff | _free space_ |
|
||||
| 0x80000 | 0x9ffff | EDBA, partially usable |
|
||||
| 0xa0000 | 0xfffff | unusable |
|
||||
|
||||
## Higher Memory
|
||||
|
||||
| start | end | use |
|
||||
|----------|----------|---------------------------|
|
||||
| 0x00000 | 0xfffff | Low memory |
|
||||
| 0x100000 | 0x200000 | **Kernel space** |
|
||||
|
||||
## Bootsector layout
|
||||
|
||||
(all addresses are offsets from 0x7C00)
|
||||
|
@ -38,6 +45,14 @@
|
|||
| ??? | 0x00ffffff | SFS index area |
|
||||
| 0x01000000 | end | Unused (Disk size is set to 16MiB) |
|
||||
|
||||
## GDT
|
||||
|
||||
| Entry | Base | Limit | Flags | Access | Purpose |
|
||||
|-------|-------------|---------|--------|------------|--------------|
|
||||
| 0x0 | 0x00000000 | 0x00000 | 0b0000 | 0b00000000 | Null Segment |
|
||||
| 0x8 | 0x00000000 | 0xfffff | 0b1100 | 0b10011110 | Code Segment |
|
||||
| 0x10 | 0x00000000 | 0xfffff | 0b1100 | 0b10010110 | Data Segment |
|
||||
|
||||
## Known issues
|
||||
|
||||
### Stack setup
|
||||
|
@ -47,3 +62,7 @@ As I don't quite get how segments work in real mode, there are most likely error
|
|||
### Kernel is not a file
|
||||
|
||||
I reserve 32KiB of SFS reserved area for the ELF file of the kernel. This is currently for "historic reasons" (a.k.a. I'm too lazy to load the filesystem in the bootloader). The kernel then handles the filesystem.
|
||||
|
||||
### BSS does not get cleared
|
||||
|
||||
I do calculate the size of the BSS, so either I intialize every variable in a function or things like global variables that are false by default don't work. It does work in emulators because they initialize all memory to 0, but real computerss don't always do that.
|
||||
|
|
86
kernel/drivers/keyboard/keyboard.c
Normal file
86
kernel/drivers/keyboard/keyboard.c
Normal file
|
@ -0,0 +1,86 @@
|
|||
#ifndef DRIVERS_KEYBOARD_C
|
||||
#define DRIVERS_KEYBOARD_C
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "keycodes.c"
|
||||
#include "../../util/fifo.c"
|
||||
#include "keymaps/DUMMY_azerty.c"
|
||||
|
||||
#define KEY_QUEUE_EMPTY FIFO_QUEUE_EMPTY
|
||||
|
||||
bool keyboard_shift_state;
|
||||
bool keyboard_caps_state;
|
||||
|
||||
fifo* key_queue = NULL;
|
||||
|
||||
int scancode_to_char(unsigned char scancode) {
|
||||
keycode keycode = get_keycode(scancode);
|
||||
|
||||
if (keycode == KEYCODE_UNKNOWN) {
|
||||
return -1;
|
||||
} else if (keycode == KEYCODE_SHIFT_DOWN) {
|
||||
keyboard_shift_state = true;
|
||||
return -1;
|
||||
} else if (keycode == KEYCODE_SHIFT_UP) {
|
||||
keyboard_shift_state = false;
|
||||
return -1;
|
||||
} else if (keycode == KEYCODE_CAPS_UP) {
|
||||
keyboard_caps_state = !keyboard_caps_state;
|
||||
return -1;
|
||||
} else if (keycode == KEYCODE_CAPS_DOWN) {
|
||||
return -1;
|
||||
} else if (keycode >= KEYCODE_A && keycode <= KEYCODE_Z) {
|
||||
if (keyboard_shift_state ^ keyboard_caps_state) {
|
||||
return (char) keycode; // Upper case
|
||||
} else {
|
||||
return ((char) keycode) + 32; // Lower case
|
||||
}
|
||||
} else {
|
||||
return (char) keycode;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void handle_scancode(unsigned char scancode) {
|
||||
int keycode = scancode_to_char(scancode);
|
||||
if (keycode == -1) {
|
||||
return;
|
||||
} else {
|
||||
fifo_enqueue(key_queue, (char) keycode);
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((interrupt)) void keyboard_handler(struct interrupt_frame* frame) {
|
||||
unsigned char scan_code = inb(0x60);
|
||||
outb(0x20, 0x20);
|
||||
|
||||
handle_scancode(scan_code);
|
||||
}
|
||||
|
||||
int getchar_nonblocking() {
|
||||
return fifo_dequeue(key_queue);
|
||||
}
|
||||
|
||||
char getchar() {
|
||||
int curr = KEY_QUEUE_EMPTY;
|
||||
while ((curr = getchar_nonblocking()) == KEY_QUEUE_EMPTY) {
|
||||
asm("hlt"); // And don't catch fire
|
||||
}
|
||||
return (char) curr;
|
||||
}
|
||||
|
||||
void keyboard_init() {
|
||||
keyboard_shift_state = false;
|
||||
keyboard_caps_state = false;
|
||||
key_queue = fifo_new();
|
||||
|
||||
uint8_t mask = inb(0x21);
|
||||
|
||||
mask = mask & ~(1 << 1);
|
||||
|
||||
outb(0x21 , mask);
|
||||
}
|
||||
|
||||
#endif //DRIVERS_KEYBOARD_C
|
45
kernel/drivers/keyboard/keycodes.c
Normal file
45
kernel/drivers/keyboard/keycodes.c
Normal file
|
@ -0,0 +1,45 @@
|
|||
#ifndef DRIVERS_KEYBOARD_KEYCODES_C
|
||||
#define DRIVERS_KEYBOARD_KEYCODES_C
|
||||
|
||||
typedef enum keycode_enum {
|
||||
KEYCODE_A = 'A',
|
||||
KEYCODE_B = 'B',
|
||||
KEYCODE_C = 'C',
|
||||
KEYCODE_D = 'D',
|
||||
KEYCODE_E = 'E',
|
||||
KEYCODE_F = 'F',
|
||||
KEYCODE_G = 'G',
|
||||
KEYCODE_H = 'H',
|
||||
KEYCODE_I = 'I',
|
||||
KEYCODE_J = 'J',
|
||||
KEYCODE_K = 'K',
|
||||
KEYCODE_L = 'L',
|
||||
KEYCODE_M = 'M',
|
||||
KEYCODE_N = 'N',
|
||||
KEYCODE_O = 'O',
|
||||
KEYCODE_P = 'P',
|
||||
KEYCODE_Q = 'Q',
|
||||
KEYCODE_R = 'R',
|
||||
KEYCODE_S = 'S',
|
||||
KEYCODE_T = 'T',
|
||||
KEYCODE_U = 'U',
|
||||
KEYCODE_V = 'V',
|
||||
KEYCODE_W = 'W',
|
||||
KEYCODE_X = 'X',
|
||||
KEYCODE_Y = 'Y',
|
||||
KEYCODE_Z = 'Z',
|
||||
|
||||
KEYCODE_SPACE = ' ',
|
||||
|
||||
KEYCODE_BACKSPACE = 0x08,
|
||||
KEYCODE_NEWLINE = '\n',
|
||||
|
||||
KEYCODE_SHIFT_UP,
|
||||
KEYCODE_SHIFT_DOWN,
|
||||
KEYCODE_CAPS_UP,
|
||||
KEYCODE_CAPS_DOWN,
|
||||
|
||||
KEYCODE_UNKNOWN
|
||||
} keycode;
|
||||
|
||||
#endif //DRIVERS_KEYBOARD_KEYCODES_C
|
84
kernel/drivers/keyboard/keymaps/DUMMY_azerty.c
Normal file
84
kernel/drivers/keyboard/keymaps/DUMMY_azerty.c
Normal file
|
@ -0,0 +1,84 @@
|
|||
#ifndef DRIVERS_KEYBOARD_KEYMAPS_DUMMY_AZERTY_C
|
||||
#define DRIVERS_KEYBOARD_KEYMAPS_DUMMY_AZERTY_C
|
||||
|
||||
#include "../keycodes.c"
|
||||
|
||||
keycode get_keycode(unsigned char scancode) {
|
||||
switch (scancode)
|
||||
{
|
||||
case 0x10:
|
||||
return KEYCODE_A;
|
||||
case 0x30:
|
||||
return KEYCODE_B;
|
||||
case 0x2e:
|
||||
return KEYCODE_C;
|
||||
case 0x20:
|
||||
return KEYCODE_D;
|
||||
case 0x12:
|
||||
return KEYCODE_E;
|
||||
case 0x21:
|
||||
return KEYCODE_F;
|
||||
case 0x22:
|
||||
return KEYCODE_G;
|
||||
case 0x23:
|
||||
return KEYCODE_H;
|
||||
case 0x17:
|
||||
return KEYCODE_I;
|
||||
case 0x24:
|
||||
return KEYCODE_J;
|
||||
case 0x25:
|
||||
return KEYCODE_K;
|
||||
case 0x26:
|
||||
return KEYCODE_L;
|
||||
case 0x27:
|
||||
return KEYCODE_M;
|
||||
case 0x31:
|
||||
return KEYCODE_N;
|
||||
case 0x18:
|
||||
return KEYCODE_O;
|
||||
case 0x19:
|
||||
return KEYCODE_P;
|
||||
case 0x1e:
|
||||
return KEYCODE_Q;
|
||||
case 0x13:
|
||||
return KEYCODE_R;
|
||||
case 0x1f:
|
||||
return KEYCODE_S;
|
||||
case 0x14:
|
||||
return KEYCODE_T;
|
||||
case 0x16:
|
||||
return KEYCODE_U;
|
||||
case 0x2f:
|
||||
return KEYCODE_V;
|
||||
case 0x2c:
|
||||
return KEYCODE_W;
|
||||
case 0x2d:
|
||||
return KEYCODE_X;
|
||||
case 0x15:
|
||||
return KEYCODE_Y;
|
||||
case 0x11:
|
||||
return KEYCODE_Z;
|
||||
case 0x39:
|
||||
return KEYCODE_SPACE;
|
||||
case 0x2a:
|
||||
return KEYCODE_SHIFT_DOWN;
|
||||
case 0xaa:
|
||||
return KEYCODE_SHIFT_UP;
|
||||
case 0x36:
|
||||
return KEYCODE_SHIFT_DOWN;
|
||||
case 0xb6:
|
||||
return KEYCODE_SHIFT_UP;
|
||||
case 0x3a:
|
||||
return KEYCODE_CAPS_DOWN;
|
||||
case 0xba:
|
||||
return KEYCODE_CAPS_UP;
|
||||
case 0x0e:
|
||||
return KEYCODE_BACKSPACE;
|
||||
case 0x1c:
|
||||
return KEYCODE_NEWLINE;
|
||||
default:
|
||||
return KEYCODE_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
#endif //DRIVERS_KEYBOARD_KEYMAPS_DUMMY_AZERTY_C
|
107
kernel/exception.c
Normal file
107
kernel/exception.c
Normal file
|
@ -0,0 +1,107 @@
|
|||
#ifndef EXCEPTION_C
|
||||
#define EXCEPTION_C
|
||||
|
||||
#include "terminal.c"
|
||||
|
||||
typedef struct interrupt_frame_struct {
|
||||
uint32_t eip;
|
||||
uint32_t cs;
|
||||
uint32_t eflags;
|
||||
} interrupt_frame;
|
||||
|
||||
#define EXCEPTION_HANDLER_NO_ERR(NAME, STR) \
|
||||
__attribute__((interrupt)) void NAME (interrupt_frame* frame) { \
|
||||
outb(0x20, 0x20); \
|
||||
bsod(frame, STR, -1); \
|
||||
}
|
||||
|
||||
#define EXCEPTION_HANDLER_ERR(NAME, STR) \
|
||||
__attribute__((interrupt)) void NAME (interrupt_frame* frame, uint32_t err_code) { \
|
||||
outb(0x20, 0x20); \
|
||||
bsod(frame, STR, err_code); \
|
||||
}
|
||||
|
||||
void bsod(interrupt_frame* frame, char* err_msg, int32_t err_code) {
|
||||
|
||||
terminal_initialize();
|
||||
terminal_writestring("An exception occured: ");
|
||||
terminal_writestring(err_msg);
|
||||
if(err_code != -1) {
|
||||
terminal_writestring(" (error code = 0x");
|
||||
terminal_writeint(err_code, 16);
|
||||
terminal_writestring(")");
|
||||
}
|
||||
terminal_writestring("\nHere's what we know:\n");
|
||||
terminal_writestring("eip = 0x");
|
||||
terminal_writeint(frame->eip, 16);
|
||||
terminal_writestring("\ncs = 0x");
|
||||
terminal_writeint(frame->cs, 16);
|
||||
terminal_writestring("\neflags = 0b");
|
||||
terminal_writeint(frame->eflags, 2);
|
||||
terminal_writestring("\n(");
|
||||
|
||||
terminal_writestring((0x0001 & frame->eflags) != 0 ? "NC,": "CY,");
|
||||
terminal_writestring((0x0004 & frame->eflags) != 0 ? "PE,": "PO,");
|
||||
terminal_writestring((0x0010 & frame->eflags) != 0 ? "AC,": "NA,");
|
||||
terminal_writestring((0x0040 & frame->eflags) != 0 ? "ZR,": "NZ,");
|
||||
terminal_writestring((0x0080 & frame->eflags) != 0 ? "NG,": "PL,");
|
||||
terminal_writestring("TF:");
|
||||
terminal_writeint((0x0100 & frame->eflags) >> 8, 2);
|
||||
terminal_putchar(',');
|
||||
terminal_writestring((0x0200 & frame->eflags) != 0 ? "EI,": "DI,");
|
||||
terminal_writestring((0x0400 & frame->eflags) != 0 ? "DN,": "UP,");
|
||||
terminal_writestring((0x0800 & frame->eflags) != 0 ? "OV,": "NV,");
|
||||
terminal_writestring("IOPL:");
|
||||
terminal_writeint((0x3000 & frame->eflags) >> 12, 10);
|
||||
terminal_putchar(',');
|
||||
terminal_writestring("NT:");
|
||||
terminal_writeint((0x4000 & frame->eflags) >> 14, 2);
|
||||
terminal_putchar(',');
|
||||
terminal_writestring("RF:");
|
||||
terminal_writeint((0x0001000 & frame->eflags) >> 16, 2);
|
||||
terminal_putchar(',');
|
||||
terminal_writestring("VM:");
|
||||
terminal_writeint((0x0002000 & frame->eflags) >> 17, 2);
|
||||
terminal_putchar(',');
|
||||
terminal_writestring("AC:");
|
||||
terminal_writeint((0x0004000 & frame->eflags) >> 18, 2);
|
||||
terminal_putchar(',');
|
||||
terminal_writestring("VIF:");
|
||||
terminal_writeint((0x0008000 & frame->eflags) >> 19, 2);
|
||||
terminal_putchar(',');
|
||||
terminal_writestring("VIP:");
|
||||
terminal_writeint((0x0010000 & frame->eflags) >> 20, 2);
|
||||
terminal_putchar(',');
|
||||
terminal_writestring("ID:");
|
||||
terminal_writeint((0x0020000 & frame->eflags) >> 21, 2);
|
||||
terminal_putchar(')');
|
||||
|
||||
for(;;) {
|
||||
asm("hlt");
|
||||
}
|
||||
}
|
||||
|
||||
EXCEPTION_HANDLER_NO_ERR(divide_by_zero_handler, "Divide by zero")
|
||||
EXCEPTION_HANDLER_NO_ERR(debug_handler, "debug")
|
||||
EXCEPTION_HANDLER_NO_ERR(non_maskable_interrupt_handler, "Non maskable interrupt")
|
||||
EXCEPTION_HANDLER_NO_ERR(breakpoint_handler, "Breakpoint")
|
||||
EXCEPTION_HANDLER_NO_ERR(overflow_handler, "Overflow")
|
||||
EXCEPTION_HANDLER_NO_ERR(bound_range_exceeded_handler, "Bound range exceeded")
|
||||
EXCEPTION_HANDLER_NO_ERR(invalid_opcode_handler, "Invalid opcode")
|
||||
EXCEPTION_HANDLER_NO_ERR(device_not_available_handler, "Device not available")
|
||||
EXCEPTION_HANDLER_ERR(double_fault_handler, "Double fault")
|
||||
EXCEPTION_HANDLER_NO_ERR(coprocessor_seg_overrun_handler, "Coprocessor seg overrun")
|
||||
EXCEPTION_HANDLER_ERR(invalid_tss_handler, "Invalid tss")
|
||||
EXCEPTION_HANDLER_ERR(segment_not_present_handler, "Segment not present")
|
||||
EXCEPTION_HANDLER_ERR(stack_segment_fault_handler, "Stack segment fault")
|
||||
EXCEPTION_HANDLER_ERR(general_protection_fault_handler, "General protection fault")
|
||||
EXCEPTION_HANDLER_ERR(page_fault_handler, "Page fault")
|
||||
EXCEPTION_HANDLER_NO_ERR(reserved_handler, "Reserved")
|
||||
EXCEPTION_HANDLER_NO_ERR(x87_fp_exception_handler, "x87 floating point exception")
|
||||
EXCEPTION_HANDLER_NO_ERR(alignment_check_handler, "Alignment check");
|
||||
EXCEPTION_HANDLER_NO_ERR(machine_check_handler, "Machine check");
|
||||
EXCEPTION_HANDLER_NO_ERR(simd_fp_exception_handler, "SIMD floating point exception");
|
||||
EXCEPTION_HANDLER_NO_ERR(virtualization_exception_handler, "Virtualization exception");
|
||||
EXCEPTION_HANDLER_NO_ERR(security_exception_handler, "Security exception");
|
||||
|
||||
#endif //EXCEPTION_C
|
34
kernel/exception_list.c
Normal file
34
kernel/exception_list.c
Normal file
|
@ -0,0 +1,34 @@
|
|||
typedef enum exception_enum {
|
||||
DIVIDE_BY_ZERO = 0,
|
||||
DEBUG = 1,
|
||||
NON_MASKABLE_INTERRUPT = 2,
|
||||
BREAKPOINT = 3,
|
||||
OVERFLOW = 4,
|
||||
BOUND_RANGE_EXCEEDED = 5,
|
||||
INVALID_OPCODE = 6,
|
||||
DEVICE_NOT_AVAILABLE = 7,
|
||||
DOUBLE_FAULT = 8, // Error code = 0
|
||||
COPROCESSOR_SEG_OVERRUN = 9,
|
||||
INVALID_TSS = 10, // Error code = (invalid selector index)
|
||||
SEGMENT_NOT_PRESENT = 11, // Error code = (segment selector index)
|
||||
STACK_SEGMENT_FAULT = 12, // Error code = (segment selector index) if invalid ssi, otherwise 0
|
||||
GENERAL_PROTECTION_FAULT = 13, // Error code = (segment selector index) if invalid ssi, otherwise 0
|
||||
PAGE_FAULT = 14, // Error code = bitmap, see https://wiki.osdev.org/Exceptions#Page_Fault
|
||||
RESERVED_15 = 15,
|
||||
X87_FP_EXCEPTION = 16,
|
||||
ALIGNMENT_CHECK = 17,
|
||||
MACHINE_CHECK = 18,
|
||||
SIMD_FP_EXCEPTION = 19,
|
||||
VIRTUALIZATION_EXCEPTION = 20,
|
||||
RESERVED_21 = 21,
|
||||
RESERVED_22 = 22,
|
||||
RESERVED_23 = 23,
|
||||
RESERVED_24 = 24,
|
||||
RESERVED_25 = 25,
|
||||
RESERVED_26 = 26,
|
||||
RESERVED_27 = 27,
|
||||
RESERVED_28 = 28,
|
||||
RESERVED_29 = 29,
|
||||
SECURITY_EXCEPTION = 30,
|
||||
RESERVED_31 = 31
|
||||
} exception;
|
64
kernel/inline_asm.c
Normal file
64
kernel/inline_asm.c
Normal file
|
@ -0,0 +1,64 @@
|
|||
#ifndef INLINE_ASM_C
|
||||
#define INLINE_ASM_C
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "memory.c"
|
||||
|
||||
typedef struct {
|
||||
uint16_t limit;
|
||||
uint32_t base;
|
||||
}__attribute__((packed)) gdt_desc;
|
||||
|
||||
typedef struct {
|
||||
uint16_t limit_lower;
|
||||
uint16_t base_lower;
|
||||
uint8_t base_middle;
|
||||
uint8_t access_byte;
|
||||
uint8_t flags_limit_higher;
|
||||
uint8_t base_higher;
|
||||
}__attribute__((packed)) gdt_entry;
|
||||
|
||||
static inline void outb(uint16_t port, uint8_t val) {
|
||||
asm volatile ( "outb %0, %1" : : "a"(val), "Nd"(port) );
|
||||
}
|
||||
|
||||
static inline uint8_t inb(uint16_t port) {
|
||||
uint8_t ret;
|
||||
asm volatile ( "inb %1, %0"
|
||||
: "=a"(ret)
|
||||
: "Nd"(port) );
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void lidt(void* base, uint16_t size)
|
||||
{ // This function works in 32 and 64bit mode
|
||||
struct {
|
||||
uint16_t length;
|
||||
void* base;
|
||||
} __attribute__((packed)) IDTR = { size, base };
|
||||
|
||||
asm ( "lidt %0" : : "m"(IDTR) ); // let the compiler choose an addressing mode
|
||||
}
|
||||
|
||||
/** issue a single request to CPUID. Fits 'intel features', for instance
|
||||
* note that even if only "eax" and "edx" are of interest, other registers
|
||||
* will be modified by the operation, so we need to tell the compiler about it.
|
||||
*/
|
||||
static inline void cpuid(int code, uint32_t *a, uint32_t *d) {
|
||||
asm volatile("cpuid":"=a"(*a),"=d"(*d):"a"(code):"ecx","ebx");
|
||||
}
|
||||
|
||||
/** issue a complete request, storing general registers output as a string
|
||||
*/
|
||||
static inline int cpuid_string(int code, uint32_t where[4]) {
|
||||
asm volatile("cpuid":"=a"(*where),"=b"(*(where+1)),
|
||||
"=c"(*(where+2)),"=d"(*(where+3)):"a"(code));
|
||||
return (int)where[0];
|
||||
}
|
||||
|
||||
static inline void sgdt(gdt_desc* ret) {
|
||||
asm volatile ("sgdt %0" : : "m"(*ret) : "memory");
|
||||
}
|
||||
|
||||
#endif //INLINE ASM_C
|
97
kernel/interrupts.c
Normal file
97
kernel/interrupts.c
Normal file
|
@ -0,0 +1,97 @@
|
|||
#ifndef INTERRUPTS_C
|
||||
#define INTERRUPTS_C
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "memory.c"
|
||||
#include "terminal.c"
|
||||
#include "inline_asm.c"
|
||||
#include "exception.c"
|
||||
#include "exception_list.c"
|
||||
#include "drivers/keyboard/keyboard.c"
|
||||
|
||||
typedef struct idt_entry_struct {
|
||||
uint16_t offset_1; // offset bits 0..15
|
||||
uint16_t selector; // a code segment selector in GDT or LDT
|
||||
uint8_t zero; // unused, set to 0
|
||||
uint8_t type_attr; // type and attributes, see below
|
||||
uint16_t offset_2; // offset bits 16..31
|
||||
} idt_entry;
|
||||
|
||||
idt_entry IDT[256];
|
||||
|
||||
void interrupt_new_handler(int intnum, void (*handler)(interrupt_frame*)) {
|
||||
uint32_t handler_address = (uint32_t) handler;
|
||||
IDT[intnum].offset_1 = (uint16_t) (handler_address & 0xffff);
|
||||
IDT[intnum].selector = 0x08;
|
||||
IDT[intnum].zero = 0;
|
||||
IDT[intnum].type_attr = 0b10001110; // Active, privilege level 00, storage segment = 0; 32 bit interrupt gate
|
||||
IDT[intnum].offset_2 = (uint16_t) ((handler_address & 0xffff0000) >> 16);
|
||||
}
|
||||
|
||||
void interrupt_init() {
|
||||
/* ICW1 - begin initialization */
|
||||
outb(0x20, 0x11);
|
||||
outb(0xA0, 0x11);
|
||||
|
||||
/* ICW2 - remap offset address of IDT */
|
||||
outb(0x21, 0x20);
|
||||
outb(0xA1, 0x82);
|
||||
|
||||
/* ICW3 - setup cascading */
|
||||
outb(0x21, 0x00);
|
||||
outb(0xA1, 0x00);
|
||||
|
||||
/* ICW4 - environment info */
|
||||
outb(0x21, 0x01);
|
||||
outb(0xA1, 0x01);
|
||||
|
||||
/* mask interrupts */
|
||||
outb(0x21 , 0xff);
|
||||
outb(0xA1 , 0xff);
|
||||
|
||||
// Exceptions
|
||||
interrupt_new_handler(DIVIDE_BY_ZERO, divide_by_zero_handler);
|
||||
interrupt_new_handler(DEBUG, debug_handler);
|
||||
interrupt_new_handler(NON_MASKABLE_INTERRUPT, non_maskable_interrupt_handler);
|
||||
interrupt_new_handler(BREAKPOINT, breakpoint_handler);
|
||||
interrupt_new_handler(OVERFLOW, overflow_handler);
|
||||
interrupt_new_handler(BOUND_RANGE_EXCEEDED, bound_range_exceeded_handler);
|
||||
interrupt_new_handler(INVALID_OPCODE, invalid_opcode_handler);
|
||||
interrupt_new_handler(DEVICE_NOT_AVAILABLE, device_not_available_handler);
|
||||
interrupt_new_handler(DOUBLE_FAULT, double_fault_handler);
|
||||
interrupt_new_handler(COPROCESSOR_SEG_OVERRUN, coprocessor_seg_overrun_handler);
|
||||
interrupt_new_handler(INVALID_TSS, invalid_tss_handler);
|
||||
interrupt_new_handler(SEGMENT_NOT_PRESENT, segment_not_present_handler);
|
||||
interrupt_new_handler(STACK_SEGMENT_FAULT, stack_segment_fault_handler);
|
||||
interrupt_new_handler(GENERAL_PROTECTION_FAULT, general_protection_fault_handler);
|
||||
interrupt_new_handler(PAGE_FAULT, page_fault_handler);
|
||||
interrupt_new_handler(RESERVED_15, reserved_handler);
|
||||
interrupt_new_handler(X87_FP_EXCEPTION, x87_fp_exception_handler);
|
||||
interrupt_new_handler(ALIGNMENT_CHECK, alignment_check_handler);
|
||||
interrupt_new_handler(MACHINE_CHECK, machine_check_handler);
|
||||
interrupt_new_handler(SIMD_FP_EXCEPTION, simd_fp_exception_handler);
|
||||
interrupt_new_handler(VIRTUALIZATION_EXCEPTION, virtualization_exception_handler);
|
||||
interrupt_new_handler(RESERVED_21, reserved_handler);
|
||||
interrupt_new_handler(RESERVED_22, reserved_handler);
|
||||
interrupt_new_handler(RESERVED_23, reserved_handler);
|
||||
interrupt_new_handler(RESERVED_24, reserved_handler);
|
||||
interrupt_new_handler(RESERVED_25, reserved_handler);
|
||||
interrupt_new_handler(RESERVED_26, reserved_handler);
|
||||
interrupt_new_handler(RESERVED_27, reserved_handler);
|
||||
interrupt_new_handler(RESERVED_28, reserved_handler);
|
||||
interrupt_new_handler(RESERVED_29, reserved_handler);
|
||||
interrupt_new_handler(SECURITY_EXCEPTION, security_exception_handler);
|
||||
interrupt_new_handler(RESERVED_31, reserved_handler);
|
||||
|
||||
interrupt_new_handler(0x21, keyboard_handler);
|
||||
|
||||
uint16_t size = (sizeof(idt_entry) * 256);
|
||||
|
||||
lidt(IDT, size);
|
||||
|
||||
keyboard_init();
|
||||
}
|
||||
|
||||
#endif //INTERRUPTS_C
|
145
kernel/kernel.c
145
kernel/kernel.c
|
@ -1,7 +1,3 @@
|
|||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* Check if the compiler thinks you are targeting the wrong operating system. */
|
||||
#if defined(__linux__)
|
||||
#error "You are not using a cross-compiler, you will most certainly run into trouble"
|
||||
|
@ -9,107 +5,26 @@
|
|||
|
||||
/* This tutorial will only work for the 32-bit ix86 targets. */
|
||||
#if !defined(__i386__)
|
||||
#error "This tutorial needs to be compiled with a ix86-elf compiler"
|
||||
#error "This kernel needs to be compiled with a ix86-elf compiler"
|
||||
#endif
|
||||
|
||||
/* Hardware text mode color constants. */
|
||||
enum vga_color {
|
||||
VGA_COLOR_BLACK = 0,
|
||||
VGA_COLOR_BLUE = 1,
|
||||
VGA_COLOR_GREEN = 2,
|
||||
VGA_COLOR_CYAN = 3,
|
||||
VGA_COLOR_RED = 4,
|
||||
VGA_COLOR_MAGENTA = 5,
|
||||
VGA_COLOR_BROWN = 6,
|
||||
VGA_COLOR_LIGHT_GREY = 7,
|
||||
VGA_COLOR_DARK_GREY = 8,
|
||||
VGA_COLOR_LIGHT_BLUE = 9,
|
||||
VGA_COLOR_LIGHT_GREEN = 10,
|
||||
VGA_COLOR_LIGHT_CYAN = 11,
|
||||
VGA_COLOR_LIGHT_RED = 12,
|
||||
VGA_COLOR_LIGHT_MAGENTA = 13,
|
||||
VGA_COLOR_LIGHT_BROWN = 14,
|
||||
VGA_COLOR_WHITE = 15,
|
||||
};
|
||||
|
||||
static inline uint8_t vga_entry_color(enum vga_color fg, enum vga_color bg)
|
||||
{
|
||||
return fg | bg << 4;
|
||||
}
|
||||
|
||||
static inline uint16_t vga_entry(unsigned char uc, uint8_t color)
|
||||
{
|
||||
return (uint16_t) uc | (uint16_t) color << 8;
|
||||
}
|
||||
|
||||
size_t strlen(const char* str)
|
||||
{
|
||||
size_t len = 0;
|
||||
while (str[len])
|
||||
len++;
|
||||
return len;
|
||||
}
|
||||
|
||||
static const size_t VGA_WIDTH = 80;
|
||||
static const size_t VGA_HEIGHT = 25;
|
||||
|
||||
size_t terminal_row;
|
||||
size_t terminal_column;
|
||||
uint8_t terminal_color;
|
||||
uint16_t* terminal_buffer;
|
||||
|
||||
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;
|
||||
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;
|
||||
terminal_buffer[index] = vga_entry(' ', terminal_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void terminal_setcolor(uint8_t color)
|
||||
{
|
||||
terminal_color = color;
|
||||
}
|
||||
|
||||
void terminal_putentryat(char c, uint8_t color, size_t x, size_t y)
|
||||
{
|
||||
const size_t index = y * VGA_WIDTH + x;
|
||||
terminal_buffer[index] = vga_entry(c, color);
|
||||
}
|
||||
|
||||
void terminal_putchar(char c)
|
||||
{
|
||||
if (c == '\n') {
|
||||
terminal_column = 0;
|
||||
terminal_row++;
|
||||
return;
|
||||
}
|
||||
|
||||
terminal_putentryat(c, terminal_color, terminal_column, terminal_row);
|
||||
if (++terminal_column == VGA_WIDTH) {
|
||||
terminal_column = 0;
|
||||
if (++terminal_row == VGA_HEIGHT)
|
||||
terminal_row = 0;
|
||||
}
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "terminal.c"
|
||||
#include "memory.c"
|
||||
#include "interrupts.c"
|
||||
#include "shell.c"
|
||||
|
||||
static inline bool are_interrupts_enabled() {
|
||||
unsigned long flags;
|
||||
asm volatile ( "pushf\n\t"
|
||||
"pop %0"
|
||||
: "=g"(flags) );
|
||||
return flags & (1 << 9);
|
||||
}
|
||||
|
||||
void terminal_write(const char* data, size_t size)
|
||||
{
|
||||
for (size_t i = 0; i < size; i++)
|
||||
terminal_putchar(data[i]);
|
||||
}
|
||||
|
||||
void terminal_writestring(const char* data)
|
||||
{
|
||||
terminal_write(data, strlen(data));
|
||||
}
|
||||
|
||||
|
||||
void kernel_main(void)
|
||||
{
|
||||
/* Initialize terminal interface */
|
||||
|
@ -121,10 +36,32 @@ void kernel_main(void)
|
|||
terminal_putchar('l');
|
||||
terminal_putchar('o');
|
||||
|
||||
/* Newline support is left as an exercise. */
|
||||
terminal_setcolor(vga_entry_color(VGA_COLOR_GREEN, VGA_COLOR_BLACK));
|
||||
terminal_writestring(" kernel");
|
||||
terminal_setcolor(vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK));
|
||||
terminal_writestring(" World!\n");
|
||||
terminal_writestring("Newlines!");
|
||||
terminal_writestring("Newlines!\n");
|
||||
|
||||
char* memory_str = alloc(sizeof(char) * 7);
|
||||
for (int i = 0; i < 6; i++) {
|
||||
memory_str[i] = "Memory"[i];
|
||||
}
|
||||
memory_str[6] = 0;
|
||||
|
||||
char* management_str = alloc(sizeof(char) * 13);
|
||||
for (int i = 0; i < 13; i++) {
|
||||
management_str[i] = " management!\n"[i];
|
||||
}
|
||||
management_str[13] = 0;
|
||||
|
||||
terminal_writestring(memory_str);
|
||||
terminal_writestring(management_str);
|
||||
|
||||
terminal_writestring((are_interrupts_enabled())? "Interrupts!\n": "No interrupts :(\n");
|
||||
|
||||
interrupt_init();
|
||||
|
||||
for(;;) {
|
||||
shell_step();
|
||||
}
|
||||
}
|
13
kernel/memory.c
Normal file
13
kernel/memory.c
Normal file
|
@ -0,0 +1,13 @@
|
|||
#ifndef MEMORY_C
|
||||
#define MEMORY_C
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
void* _curr_end = (void*) 0x200000;
|
||||
|
||||
void* alloc(size_t size) {
|
||||
void* thanks_jp = _curr_end;
|
||||
_curr_end += size;
|
||||
return thanks_jp;
|
||||
}
|
||||
#endif // MEMORY_C
|
194
kernel/shell.c
Normal file
194
kernel/shell.c
Normal file
|
@ -0,0 +1,194 @@
|
|||
#ifndef SHELL_C
|
||||
#define SHELL_C
|
||||
|
||||
#define SHELL_CMD_BUFFER_SIZE 128
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "terminal.c"
|
||||
#include "inline_asm.c"
|
||||
#include "drivers/keyboard/keyboard.c"
|
||||
|
||||
char buffer[SHELL_CMD_BUFFER_SIZE];
|
||||
int buffer_idx = 0;
|
||||
|
||||
int echo(char* input) {
|
||||
terminal_writestring(input);
|
||||
terminal_putchar('\n');
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hello(char* unused) {
|
||||
terminal_writestring("Hello, world!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cls(char* unused) {
|
||||
terminal_initialize();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_gdt(char* unused) {
|
||||
gdt_desc desc = {2,2};
|
||||
sgdt(&desc);
|
||||
terminal_writestring("limit = ");
|
||||
terminal_writeint(desc.limit, 10);
|
||||
terminal_writestring("\nbase = 0x");
|
||||
terminal_writeint(desc.base, 16);
|
||||
terminal_putchar('\n');
|
||||
|
||||
gdt_entry* entries = (gdt_entry*) desc.base;
|
||||
int num_entries = (desc.limit+1) / 8;
|
||||
for (int entry_num = 0; entry_num < num_entries; entry_num++) {
|
||||
gdt_entry entry = entries[entry_num];
|
||||
uint32_t base = entry.base_lower | entry.base_middle << 16 | entry.base_higher << 24;
|
||||
uint32_t limit = entry.limit_lower | (entry.flags_limit_higher & 0x0f) << 16;
|
||||
uint8_t flags = (entry.flags_limit_higher >> 4);
|
||||
bool is_data = ((entry.access_byte & 0b00001000) >> 3) == 0;
|
||||
|
||||
//terminal_writestring("\nEntry ");
|
||||
//terminal_writeint(entry_num, 10);
|
||||
terminal_writestring("base = 0x");
|
||||
terminal_writeint(base, 16);
|
||||
terminal_writestring("\nlimit = 0x");
|
||||
terminal_writeint(limit, 16);
|
||||
terminal_writestring("\nflags = 0b");
|
||||
terminal_writeint((entry.flags_limit_higher >> 4), 2);
|
||||
|
||||
if ((flags & 0b1000) == 0) {
|
||||
terminal_writestring(" (byte granularity");
|
||||
} else {
|
||||
terminal_writestring(" (page granularity");
|
||||
}
|
||||
|
||||
if ((flags & 0b0100) == 0) {
|
||||
terminal_writestring(", 16 bit)");
|
||||
} else {
|
||||
terminal_writestring(", 32 bit)");
|
||||
}
|
||||
|
||||
terminal_writestring("\naccess = 0b");
|
||||
terminal_writeint(entry.access_byte, 2);
|
||||
terminal_writestring(" (ring ");
|
||||
terminal_writeint((entry.access_byte & 0b01100000) >> 5, 10);
|
||||
if ((entry.access_byte & 0b00010000) == 0) {
|
||||
terminal_writestring(", System");
|
||||
}
|
||||
if (is_data) {
|
||||
terminal_writestring(", Data");
|
||||
|
||||
if((entry.access_byte & 0b00000100) == 0) {
|
||||
terminal_writestring(" (growing up, ");
|
||||
} else {
|
||||
terminal_writestring(" (growing down, ");
|
||||
}
|
||||
|
||||
if((entry.access_byte & 0b00000010) == 0) {
|
||||
terminal_writestring("r--)");
|
||||
} else {
|
||||
terminal_writestring("rw-)");
|
||||
}
|
||||
} else {
|
||||
terminal_writestring(", Code");
|
||||
|
||||
if((entry.access_byte & 0b00000100) == 0) {
|
||||
terminal_writestring(" (non-conforming, ");
|
||||
} else {
|
||||
terminal_writestring(" (conforming, ");
|
||||
}
|
||||
|
||||
if((entry.access_byte & 0b00000010) == 0) {
|
||||
terminal_writestring("--x)");
|
||||
} else {
|
||||
terminal_writestring("r-x)");
|
||||
}
|
||||
}
|
||||
|
||||
terminal_writestring(")\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ree(char* unused) {
|
||||
terminal_initialize();
|
||||
terminal_putchar('R');
|
||||
for (int i = 1; i < VGA_WIDTH * VGA_HEIGHT; i++) {
|
||||
terminal_putchar('e');
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO This is ugly, fix this
|
||||
const char* shell_commands_strings[] = {
|
||||
"echo",
|
||||
"hello",
|
||||
"cls",
|
||||
"ree",
|
||||
"getgdt",
|
||||
NULL
|
||||
};
|
||||
|
||||
int (*shell_commands_functions[]) (char*) = {
|
||||
echo,
|
||||
hello,
|
||||
cls,
|
||||
ree,
|
||||
get_gdt
|
||||
};
|
||||
|
||||
int run_command(char* buffer) {
|
||||
|
||||
if(buffer[0] == 0) {
|
||||
return 0;
|
||||
}
|
||||
char command[SHELL_CMD_BUFFER_SIZE] = {0};
|
||||
int i = 0;
|
||||
while (buffer[i] != 0 && buffer[i] != ' ') {
|
||||
command[i] = buffer[i];
|
||||
i++;
|
||||
}
|
||||
|
||||
int command_idx = 0;
|
||||
|
||||
while(shell_commands_strings[command_idx] != NULL) {
|
||||
int check_idx = 0;
|
||||
while(command[check_idx] != 0 && shell_commands_strings[command_idx][check_idx] == command[check_idx]) {
|
||||
check_idx++;
|
||||
}
|
||||
if (command[check_idx] == 0 && shell_commands_strings[command_idx][check_idx] == 0) {
|
||||
return (shell_commands_functions[command_idx])(buffer + i + 1);
|
||||
}
|
||||
command_idx++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void shell_step() {
|
||||
char curr_char = getchar();
|
||||
|
||||
if (curr_char == '\n') {
|
||||
terminal_putchar(curr_char);
|
||||
buffer[buffer_idx] = 0;
|
||||
int result = run_command(buffer);
|
||||
for (int i = 0; i < SHELL_CMD_BUFFER_SIZE; i++) {
|
||||
buffer[i] = 0;
|
||||
}
|
||||
buffer_idx = 0;
|
||||
|
||||
if (result == -1) {
|
||||
terminal_writestring("No such command\n");
|
||||
}
|
||||
} else if (curr_char == 0x08) {
|
||||
if (buffer_idx != 0) {
|
||||
buffer_idx--;
|
||||
buffer[buffer_idx] = 0;
|
||||
terminal_putchar(curr_char);
|
||||
}
|
||||
} else {
|
||||
buffer[buffer_idx] = curr_char;
|
||||
buffer_idx++;
|
||||
terminal_putchar(curr_char);
|
||||
}
|
||||
}
|
||||
|
||||
#endif //SHELL_C
|
138
kernel/terminal.c
Normal file
138
kernel/terminal.c
Normal file
|
@ -0,0 +1,138 @@
|
|||
#ifndef TERMINAL_C
|
||||
#define TERMINAL_C
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
static const size_t VGA_WIDTH = 80;
|
||||
static const size_t VGA_HEIGHT = 25;
|
||||
|
||||
/* Hardware text mode color constants. */
|
||||
enum vga_color {
|
||||
VGA_COLOR_BLACK = 0,
|
||||
VGA_COLOR_BLUE = 1,
|
||||
VGA_COLOR_GREEN = 2,
|
||||
VGA_COLOR_CYAN = 3,
|
||||
VGA_COLOR_RED = 4,
|
||||
VGA_COLOR_MAGENTA = 5,
|
||||
VGA_COLOR_BROWN = 6,
|
||||
VGA_COLOR_LIGHT_GREY = 7,
|
||||
VGA_COLOR_DARK_GREY = 8,
|
||||
VGA_COLOR_LIGHT_BLUE = 9,
|
||||
VGA_COLOR_LIGHT_GREEN = 10,
|
||||
VGA_COLOR_LIGHT_CYAN = 11,
|
||||
VGA_COLOR_LIGHT_RED = 12,
|
||||
VGA_COLOR_LIGHT_MAGENTA = 13,
|
||||
VGA_COLOR_LIGHT_BROWN = 14,
|
||||
VGA_COLOR_WHITE = 15,
|
||||
};
|
||||
|
||||
size_t terminal_row;
|
||||
size_t terminal_column;
|
||||
uint8_t terminal_color;
|
||||
uint16_t* terminal_buffer;
|
||||
|
||||
uint8_t vga_entry_color(enum vga_color fg, enum vga_color bg) {
|
||||
return fg | bg << 4;
|
||||
}
|
||||
|
||||
uint16_t vga_entry(unsigned char uc, uint8_t color) {
|
||||
return (uint16_t) uc | (uint16_t) color << 8;
|
||||
}
|
||||
|
||||
size_t strlen(const char* str) {
|
||||
size_t len = 0;
|
||||
while (str[len])
|
||||
len++;
|
||||
return len;
|
||||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
terminal_buffer[index] = vga_entry(' ', terminal_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void terminal_setcolor(uint8_t color) {
|
||||
terminal_color = color;
|
||||
}
|
||||
|
||||
void terminal_putentryat(char c, uint8_t color, size_t x, size_t y) {
|
||||
const size_t index = y * VGA_WIDTH + x;
|
||||
terminal_buffer[index] = vga_entry(c, color);
|
||||
}
|
||||
|
||||
void terminal_putchar(char c) {
|
||||
if (c == '\n') {
|
||||
terminal_column = 0;
|
||||
terminal_row++;
|
||||
return;
|
||||
}
|
||||
if (c == 0x08) {
|
||||
terminal_putentryat(' ', terminal_color, terminal_column, terminal_row);
|
||||
if (terminal_column == 0) {
|
||||
if(terminal_row != 0) {
|
||||
terminal_row--;
|
||||
terminal_column = VGA_WIDTH - 1;
|
||||
}
|
||||
} else {
|
||||
terminal_column--;
|
||||
}
|
||||
terminal_putentryat(' ', terminal_color, terminal_column, terminal_row);
|
||||
return;
|
||||
}
|
||||
|
||||
terminal_putentryat(c, terminal_color, terminal_column, terminal_row);
|
||||
if (++terminal_column == VGA_WIDTH) {
|
||||
terminal_column = 0;
|
||||
if (++terminal_row == VGA_HEIGHT)
|
||||
terminal_row = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void terminal_write(const char* data, size_t size) {
|
||||
for (size_t i = 0; i < size; i++)
|
||||
terminal_putchar(data[i]);
|
||||
}
|
||||
|
||||
void terminal_writestring(const char* data) {
|
||||
terminal_write(data, strlen(data));
|
||||
}
|
||||
|
||||
char* itoa(unsigned int value, char* result, int base) {
|
||||
// check that the base if valid
|
||||
if (base < 2 || base > 36) { *result = '\0'; return result; }
|
||||
|
||||
char* ptr = result, *ptr1 = result, tmp_char;
|
||||
int tmp_value;
|
||||
|
||||
do {
|
||||
tmp_value = value;
|
||||
value /= base;
|
||||
*ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" [35 + (tmp_value - value * base)];
|
||||
} while ( value );
|
||||
|
||||
*ptr-- = '\0';
|
||||
while(ptr1 < ptr) {
|
||||
tmp_char = *ptr;
|
||||
*ptr--= *ptr1;
|
||||
*ptr1++ = tmp_char;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void terminal_writeint(int number, int base) {
|
||||
char* result = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
|
||||
itoa(number, result, base);
|
||||
terminal_writestring(result);
|
||||
}
|
||||
|
||||
#endif //TERMINAL_C
|
60
kernel/util/fifo.c
Normal file
60
kernel/util/fifo.c
Normal file
|
@ -0,0 +1,60 @@
|
|||
#ifndef UTIL_FIFO_C
|
||||
#define UTIL_FIFO_C
|
||||
|
||||
#define FIFO_INITIAL_SIZE 128
|
||||
#define FIFO_QUEUE_EMPTY -1
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "../memory.c"
|
||||
|
||||
typedef struct fifo_struct {
|
||||
int num_elems;
|
||||
int max_elems;
|
||||
uint8_t* head;
|
||||
uint8_t* curr_elem;
|
||||
} fifo;
|
||||
|
||||
fifo* fifo_new() {
|
||||
fifo* ret = alloc(sizeof(fifo));
|
||||
void* head = alloc(sizeof(uint8_t) * FIFO_INITIAL_SIZE);
|
||||
ret->num_elems = 0;
|
||||
ret->max_elems = FIFO_INITIAL_SIZE;
|
||||
ret->head = head;
|
||||
ret->curr_elem = head;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void fifo_optimize(fifo* queue) {
|
||||
if (queue->head == queue->curr_elem) {
|
||||
// resize, not implemented yet (blocked by reallocarray)
|
||||
} else {
|
||||
// move all items to the beginning of the list
|
||||
for(int i = 0; i < queue->num_elems; i++) {
|
||||
queue->head[i] = queue->curr_elem[i];
|
||||
}
|
||||
queue->curr_elem = queue->head;
|
||||
}
|
||||
}
|
||||
|
||||
void fifo_enqueue(fifo* queue, uint8_t value) {
|
||||
if (queue->num_elems == queue->max_elems) {
|
||||
fifo_optimize(queue);
|
||||
}
|
||||
queue->curr_elem[queue->num_elems] = value;
|
||||
queue->num_elems++;
|
||||
}
|
||||
|
||||
int16_t fifo_dequeue(fifo* queue) {
|
||||
if (queue->num_elems <= 0) {
|
||||
return FIFO_QUEUE_EMPTY;
|
||||
}
|
||||
uint8_t ret = queue->curr_elem[0];
|
||||
queue->curr_elem++;
|
||||
queue->num_elems--;
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif //UTIL_FIFO_C
|
|
@ -71,6 +71,7 @@ _start:
|
|||
; 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
|
||||
sti
|
||||
extern kernel_main
|
||||
call kernel_main
|
||||
|
||||
|
|
Loading…
Reference in a new issue