Compare commits

...

18 commits
sfs ... master

Author SHA1 Message Date
Maxime
05c8516217
Update README.md 2020-01-28 04:59:04 +01:00
Maxime
ef72a8d37a
Update README.md 2020-01-27 23:59:30 +01:00
Robbe Van Herck
43bab5ff4f RobOS -> TABS 2020-01-27 23:25:31 +01:00
Robbe Van Herck
9a7992ae2e
Made exception handler describe flags 2020-01-10 14:44:09 +01:00
Robbe Van Herck
9536ddd3d3
Made getgdt more descriptive 2020-01-10 10:05:19 +01:00
Robbe Van Herck
95c0371065
Fix shift state and address BSS issue 2020-01-10 09:50:22 +01:00
Robbe Van Herck
3abf49f881
Read all ELF program headers 2020-01-10 09:28:31 +01:00
Robbe Van Herck
a0883e95d0
Add getgdt, struggle a bit, give up, cry... 2020-01-10 02:23:59 +01:00
Robbe Van Herck
2a82f01493
Add minimal shell, find out that stuff is broken :'( 2020-01-09 18:32:12 +01:00
Robbe Van Herck
41e3953d2c
Add newline and backspace 2020-01-09 16:53:06 +01:00
Robbe Van Herck
2f3b34475b
Add Exception handling 2020-01-09 16:27:19 +01:00
Robbe Van Herck
2bbd40acf6
More info in README.md 2020-01-09 12:23:01 +01:00
Robbe Van Herck
2177fa1bc9
Interrupt and keyboard support 2020-01-09 12:06:45 +01:00
Robbe Van Herck
1069a04ca3
so close yet so far 2020-01-08 22:36:49 +01:00
Robbe Van Herck
9ebbd0728b
Update README.md 2020-01-07 15:00:23 +01:00
Robbe Van Herck
6aba1fb4a8
The _most_ basic memory managment 2020-01-07 14:53:27 +01:00
Robbe Van Herck
b5678a0246
Merge branch 'sfs' 2020-01-07 14:08:24 +01:00
Robbe Van Herck
047f427581
Move terminal interactions to separate file 2020-01-07 14:08:12 +01:00
18 changed files with 1084 additions and 150 deletions

2
.gdbinit Normal file
View file

@ -0,0 +1,2 @@
target remote localhost:1234
file target/kernel/kernel.bin

View file

@ -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

View file

@ -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).

View file

@ -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

View file

@ -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.

View 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

View 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

View 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
View 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
View 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
View 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
View 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

View file

@ -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
View 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
View 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
View 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
View 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

View file

@ -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