commit 107906135356aa51bdcfa7cb0b55e1d4dfdca706 Author: g1n Date: Wed Feb 2 10:22:28 2022 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d11847b --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*~ +src/build diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4d1842a --- /dev/null +++ b/LICENSE @@ -0,0 +1,10 @@ +MIT License + +Copyright (c) 2021-2022 GRU + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/README.org b/README.org new file mode 100644 index 0000000..393d2e7 --- /dev/null +++ b/README.org @@ -0,0 +1,14 @@ +#+TITLE: Orion OS + +Orion is simple OS on C (and a bit assembler). + +Some code can be taken from OSDev wiki and James Molloy tutorial (I am trying to use it as less as possible) +This is rewrite of old version of Orion that is not maintained now to better understand how it works. + +** Build + +To build your need crosscompiler for i686 (check OSDev wiki for instructions) + +- First you need to ~cd src~ +- Then ~make~ +- And run it: if you want to run without grub - ~make run~, if with grub - ~make run-grub~ diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..0a6b83e --- /dev/null +++ b/src/Makefile @@ -0,0 +1,64 @@ +CC= i686-elf-gcc +ASMFILES = arch/i386/boot.asm +CFILES = kernel.c vga.c + +INCLUDEFLAGS=-Iinclude -Ilibk/include +DEBUGFLAGS= +CFLAGS=-std=c99 -ffreestanding -fstack-protector-all $(INCLUDEFLAGS) $(DEBUGFLAGS) -Wall -Wextra + +CRTI_OBJ = build/crti.o +CRTBEGIN_OBJ:=$(shell $(CC) $(CFLAGS) -print-file-name=crtbegin.o) +CRTEND_OBJ:=$(shell $(CC) $(CFLAGS) -print-file-name=crtend.o) +CRTN_OBJ = build/crtn.o + +LIBKOBJFILES = build/libk/string.o build/libk/stdio.o build/libk/stdlib.o build/libk/stack_protector.o build/libk/asm.o + +OBJFILES = \ +$(CRTI_OBJ) \ +$(CRTBEGIN_OBJ) \ +build/boot.o \ +$(LIBKOBJFILES) \ +build/early_kernel.o \ +build/kernel.o \ +build/vga.o \ +build/idt.o \ +$(CRTEND_OBJ) $(CRTN_OBJ) + +KERNELFILE = build/orion.bin + +all: + mkdir -p build + mkdir -p build/libk + i686-elf-gcc -c libk/string.c -o build/libk/string.o $(CFLAGS) + i686-elf-gcc -c libk/stdio.c -o build/libk/stdio.o $(CFLAGS) + i686-elf-gcc -c libk/stdlib.c -o build/libk/stdlib.o $(CFLAGS) + i686-elf-gcc -c libk/stack_protector.c -o build/libk/stack_protector.o $(CFLAGS) + i686-elf-gcc -c libk/asm.c -o build/libk/asm.o $(CFLAGS) + + nasm arch/i386/boot.asm -f elf32 -o build/boot.o + nasm arch/i386/crti.asm -f elf32 -o build/crti.o + nasm arch/i386/crtn.asm -f elf32 -o build/crtn.o + + i686-elf-gcc -c vga.c -o build/vga.o $(CFLAGS) + i686-elf-gcc -c idt.c -o build/idt.o $(CFLAGS) + i686-elf-gcc -c kernel.c -o build/kernel.o $(CFLAGS) + i686-elf-gcc -c early_kernel.c -o build/early_kernel.o $(CFLAGS) + + i686-elf-gcc -T linker.ld -o $(KERNELFILE) $(CFLAGS) -nostdlib $(OBJFILES) -lgcc +run: + qemu-system-i386 -kernel $(KERNELFILE) -serial stdio -S -s + +run-grub: + mkdir -p build/isodir + mkdir -p build/isodir/boot + mkdir -p build/isodir/boot/grub + + cp build/orion.bin build/isodir/boot/orion.kernel + + echo 'menuentry "orion" { ' > build/isodir/boot/grub/grub.cfg + echo ' multiboot /boot/orion.kernel' >> build/isodir/boot/grub/grub.cfg + echo '}' >> build/isodir/boot/grub/grub.cfg + + grub-mkrescue -o build/orion.iso build/isodir + qemu-system-i386 -cdrom build/orion.iso -serial stdio -S -s + diff --git a/src/arch/i386/boot.asm b/src/arch/i386/boot.asm new file mode 100644 index 0000000..3559c79 --- /dev/null +++ b/src/arch/i386/boot.asm @@ -0,0 +1,228 @@ +MBALIGN equ 1 << 0 +MEMINFO equ 1 << 1 +FLAGS equ MBALIGN | MEMINFO +MAGIC equ 0x1BADB002 +CHECKSUM equ -(MAGIC + FLAGS) + +section .multiboot +align 4 + dd MAGIC + dd FLAGS + dd CHECKSUM + +section .text +global boot +boot: + cli + lgdt [gdt_pointer] + + mov eax, cr0 + or eax,0x1 ; set protected mode bit + mov cr0, eax + + mov ax, DATA_SEG + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + jmp CODE_SEG:_start + +gdt_start: + dq 0x0 +gdt_code: + dw 0xFFFF + dw 0x0 + db 0x0 + db 10011010b + db 11001111b + db 0x0 +gdt_data: + dw 0xFFFF + dw 0x0 + db 0x0 + db 10010010b + db 11001111b + db 0x0 +gdt_end: + +gdt_pointer: + dw gdt_end - gdt_start + dd gdt_start + +CODE_SEG equ gdt_code - gdt_start +DATA_SEG equ gdt_data - gdt_start + +times 510 - ($-$$) db 0 +dw 0xaa55 + +section .text +global _start +_start: + mov esp, stack_top + + extern early_kernel_main + call early_kernel_main + + extern _init + call _init + + extern kernel_main + call kernel_main + + cli + +hang: hlt + jmp hang + +global idt_flush +idt_flush: + lidt [eax] + ret + +times 1024-($-$$) db 0 + +%macro isr_err_stub 1 +isr_stub_%+%1: + cli + push %1 + jmp isr_wrapper +%endmacro + +%macro isr_no_err_stub 1 +isr_stub_%+%1: + cli + push 0x0 + push %1 + jmp isr_wrapper +%endmacro + +%macro irq 2 +global irq%1 +irq%1: + push 0x0 + push %2 + jmp irq_wrapper +%endmacro + +extern isr_handler +isr_no_err_stub 0 +isr_no_err_stub 1 +isr_no_err_stub 2 +isr_no_err_stub 3 +isr_no_err_stub 4 +isr_no_err_stub 5 +isr_no_err_stub 6 +isr_no_err_stub 7 +isr_err_stub 8 +isr_no_err_stub 9 +isr_err_stub 10 +isr_err_stub 11 +isr_err_stub 12 +isr_err_stub 13 +isr_err_stub 14 +isr_no_err_stub 15 +isr_no_err_stub 16 +isr_err_stub 17 +isr_no_err_stub 18 +isr_no_err_stub 19 +isr_no_err_stub 20 +isr_no_err_stub 21 +isr_no_err_stub 22 +isr_no_err_stub 23 +isr_no_err_stub 24 +isr_no_err_stub 25 +isr_no_err_stub 26 +isr_no_err_stub 27 +isr_no_err_stub 28 +isr_no_err_stub 29 +isr_err_stub 30 +isr_no_err_stub 31 + +global isr_stub_table +isr_stub_table: +%assign i 0 +%rep 32 + dd isr_stub_%+i ; use DQ instead if targeting 64-bit +%assign i i+1 +%endrep + +extern irq_handler +irq 0, 32 +irq 1, 33 +irq 2, 34 +irq 3, 35 +irq 4, 36 +irq 5, 37 +irq 6, 38 +irq 7, 39 +irq 8, 40 +irq 9, 41 +irq 10, 42 +irq 11, 43 +irq 12, 44 +irq 13, 45 +irq 14, 46 +irq 15, 47 + +global irq_stub_table +irq_stub_table: +%assign i 0 +%rep 15 + dd irq%+i ; use DQ instead if targeting 64-bit +%assign i i+1 +%endrep + +global isr_wrapper +isr_wrapper: + pusha + mov ax, ds + push eax + mov ax, 0x10 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + call isr_handler + + pop eax + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + popa + add esp, 0x8 + sti + iret + +global irq_wrapper +irq_wrapper: + pusha + + mov ax, ds + push eax + + mov ax, 0x10 + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + call irq_handler + + pop ebx + mov ds, bx + mov es, bx + mov fs, bx + mov gs, bx + + popa + add esp, 0x8 + iret + +section .bss +align 16 +stack_bottom: +resb 16384 ; 16 KiB +stack_top: diff --git a/src/arch/i386/crti.asm b/src/arch/i386/crti.asm new file mode 100644 index 0000000..42df125 --- /dev/null +++ b/src/arch/i386/crti.asm @@ -0,0 +1,11 @@ +section .init +global _init +_init: + push ebp + mov ebp, esp + +section .fini +global _fini +_fini: + push ebp + mov ebp, esp diff --git a/src/arch/i386/crtn.asm b/src/arch/i386/crtn.asm new file mode 100644 index 0000000..a593ab0 --- /dev/null +++ b/src/arch/i386/crtn.asm @@ -0,0 +1,7 @@ +section .init + pop ebp + ret + +section .fini + pop ebp + ret diff --git a/src/early_kernel.c b/src/early_kernel.c new file mode 100644 index 0000000..5335954 --- /dev/null +++ b/src/early_kernel.c @@ -0,0 +1,9 @@ +#include +#include +#include +#include + +void early_kernel_main() { + terminal_initialize(); + idt_init(); +} diff --git a/src/idt.c b/src/idt.c new file mode 100644 index 0000000..243ee5b --- /dev/null +++ b/src/idt.c @@ -0,0 +1,79 @@ +#include +#include +#include + +static idt_entry_t idt[256]; // Create an array of IDT entries; aligned for performance +static idtr_t idtr; +isr_t interrupt_handlers[256]; + +void isr_handler(registers_t regs) { + if (interrupt_handlers[regs.int_no] != 0) { + isr_t handler = interrupt_handlers[regs.int_no]; + handler(regs); + } else { + kprintf("Unhandled interrupt: %d\n", regs.int_no); + } +} + +void irq_handler(registers_t regs) { + if (regs.int_no >= 40) { + outb(0xA0, 0x20); + } + + outb(0x20, 0x20); + + if (interrupt_handlers[regs.int_no] != 0) { + isr_t handler = interrupt_handlers[regs.int_no]; + handler(regs); + } else { + kprintf("Unhandled IRQ interrupt: %d\n", regs.int_no); + } +} + +void register_interrupt_handler(uint8_t n, isr_t handler) { + interrupt_handlers[n] = handler; +} + +void idt_set_gate(uint8_t num, uint32_t base, uint8_t flags) { + idt[num].isr_low = base & 0xFFFF; + idt[num].isr_high = (base >> 16) & 0xFFFF; + + idt[num].kernel_cs = 0x08; + idt[num].reserved = 0; + + idt[num].attributes = flags; +} + +extern void* isr_stub_table[]; +extern void* irq_stub_table[]; + +void idt_init() { + idtr.base = (uintptr_t)&idt; + idtr.limit = (uint16_t)sizeof(idt_entry_t) * 256 - 1; + + for (uint8_t vector = 0; vector < 32; vector++) { + idt_set_gate(vector, (uint32_t)isr_stub_table[vector], 0x8E); + } + + extern void idt_flush(uint32_t); + idt_flush((uint32_t)&idtr); + + // Remap the irq table. + outb(0x20, 0x11); + outb(0xA0, 0x11); + outb(0x21, 0x20); + outb(0xA1, 0x28); + outb(0x21, 0x04); + outb(0xA1, 0x02); + outb(0x21, 0x01); + outb(0xA1, 0x01); + outb(0x21, 0x0); + outb(0xA1, 0x0); + + for (uint8_t vector = 32; vector < 48; vector++) { + idt_set_gate(vector, (uint32_t)irq_stub_table[vector], 0x8E); + } + outb(0x21,0xfc); + outb(0xa1,0xff); + __asm__("sti"); +} diff --git a/src/include/idt.h b/src/include/idt.h new file mode 100644 index 0000000..62ab177 --- /dev/null +++ b/src/include/idt.h @@ -0,0 +1,50 @@ +#ifndef IDT_H +#define IDT_H + +#include + +#define IRQ0 32 +#define IRQ1 33 +#define IRQ2 34 +#define IRQ3 35 +#define IRQ4 36 +#define IRQ5 37 +#define IRQ6 38 +#define IRQ7 39 +#define IRQ8 40 +#define IRQ9 41 +#define IRQ10 42 +#define IRQ11 43 +#define IRQ12 44 +#define IRQ13 45 +#define IRQ14 46 +#define IRQ15 47 + +typedef struct { + uint16_t isr_low; // The lower 16 bits of the ISR's address + uint16_t kernel_cs; // The GDT segment selector that the CPU will load into CS before calling the ISR + uint8_t reserved; // Set to zero + uint8_t attributes; // Type and attributes; see the IDT page + uint16_t isr_high; // The higher 16 bits of the ISR's address +} __attribute__((packed)) idt_entry_t; + +__attribute__((aligned(0x10))) + +typedef struct { + uint16_t limit; + uint32_t base; +} __attribute__((packed)) idtr_t; + +typedef struct registers { + uint32_t ds; // Data segment selector + uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; // Pushed by pusha. + uint32_t int_no, err_code; // Interrupt number and error code (if applicable) + uint32_t eip, cs, eflags, useresp, ss; // Pushed by the processor automatically. +} registers_t; + +typedef void (*isr_t)(registers_t); + +void register_interrupt_handler(uint8_t n, isr_t handler); +void idt_init(); + +#endif diff --git a/src/include/vga.h b/src/include/vga.h new file mode 100644 index 0000000..a0ba4d9 --- /dev/null +++ b/src/include/vga.h @@ -0,0 +1,38 @@ +#ifndef VGA_H +#define VGA_H + +#include +#include +#include + +/* 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, +}; + +#define VGA_WIDTH 80 +#define VGA_HEIGHT 25 + +void terminal_initialize(void); +void terminal_setcolor(uint8_t color); +void terminal_putentryat(char c, uint8_t color, size_t x, size_t y); +void terminal_putchar(char c); +void terminal_write(const char* data, size_t size); +void terminal_writestring(const char* data); + +#endif diff --git a/src/kernel.c b/src/kernel.c new file mode 100644 index 0000000..6211dd8 --- /dev/null +++ b/src/kernel.c @@ -0,0 +1,7 @@ +#include +#include +#include + +void kernel_main() { + kprintf("Hello, World!\n"); +} diff --git a/src/libk/asm.c b/src/libk/asm.c new file mode 100644 index 0000000..8e19673 --- /dev/null +++ b/src/libk/asm.c @@ -0,0 +1,13 @@ +#include + +inline void outb(uint16_t port, uint8_t val) { + __asm__ volatile ("outb %0, %1" : : "a"(val), "Nd"(port)); +} + +inline int inb(int port) { + uint8_t ret; + __asm__ volatile ( "inb %1, %0" + : "=a"(ret) + : "Nd"(port) ); + return ret; +} diff --git a/src/libk/include/asm.h b/src/libk/include/asm.h new file mode 100644 index 0000000..a6dcc1a --- /dev/null +++ b/src/libk/include/asm.h @@ -0,0 +1,9 @@ +#ifndef _ASM_H +#define _ASM_H + +#include + +void outb(uint16_t port, uint8_t val); +int inb(int port); + +#endif diff --git a/src/libk/include/stdio.h b/src/libk/include/stdio.h new file mode 100644 index 0000000..fbc709e --- /dev/null +++ b/src/libk/include/stdio.h @@ -0,0 +1,17 @@ +#ifndef _STDIO_H +#define _STDIO_H + +#include +#include + +int kputchar(int c); +int kputs(const char *s); + +int kvsnprintf(char *restrict s, size_t n, const char *restrict format, va_list ap); +int ksnprintf(char *restrict s, size_t n, const char *restrict format, ...); +int kvsprintf(char *restrict s, const char *restrict format, va_list ap); +int ksprintf(char *restrict s, const char *restrict format, ...); +int kvprintf(const char *restrict format, va_list ap); +int kprintf(const char *restrict format, ...); + +#endif diff --git a/src/libk/include/stdlib.h b/src/libk/include/stdlib.h new file mode 100644 index 0000000..81de07b --- /dev/null +++ b/src/libk/include/stdlib.h @@ -0,0 +1,6 @@ +#ifndef _STDLIB_H +#define _STDLIB_H + +void abort(void); + +#endif diff --git a/src/libk/include/string.h b/src/libk/include/string.h new file mode 100644 index 0000000..e4a2b2a --- /dev/null +++ b/src/libk/include/string.h @@ -0,0 +1,12 @@ +#ifndef _STRING_H +#define _STRING_H + +#include + +size_t strlen(const char *s); +void *memmove(void *dest, const void *src, size_t n); +int memcmp(const void *s1, const void *s2, size_t n); +void *memset(void *s, int c, size_t n); +void *memcpy(void *restrict dest, const void *restrict src, size_t n); + +#endif diff --git a/src/libk/stack_protector.c b/src/libk/stack_protector.c new file mode 100644 index 0000000..e3ad75e --- /dev/null +++ b/src/libk/stack_protector.c @@ -0,0 +1,20 @@ +#include +#include + +#if UINT32_MAX == UINTPTR_MAX +#define STACK_CHK_GUARD 0xe2dee396 +#else +#define STACK_CHK_GUARD 0x595e9fbd94fda766 +#endif + +uintptr_t __stack_chk_guard = STACK_CHK_GUARD; + +// __attribute__((noreturn)) // FIXME +void __stack_chk_fail(void) +{ +#if __STDC_HOSTED__ + abort(); +#elif __is_myos_kernel + panic("Stack smashing detected"); +#endif +} diff --git a/src/libk/stdio.c b/src/libk/stdio.c new file mode 100644 index 0000000..5bfcbad --- /dev/null +++ b/src/libk/stdio.c @@ -0,0 +1,156 @@ +#include +#include + +int kputchar(int c) { + terminal_putchar(c); + return (unsigned char)c; +} + +int kputs(const char *s) { + int i = 0; + while(s[i] != '\0') { + kputchar(s[i]); + i++; + } + kputchar('\n'); + return 1; // FIXME +} + +int kvsnprintf(char *restrict s, size_t n, const char *restrict format, va_list ap) { + char *str; + int value; + size_t size = 0; + for (size_t i = 0; i < strlen(format) && size < n-1; i++) { + if (format[i] == '%') { + i++; + switch(format[i]) { + case 'c': + s[size] = (char) va_arg(ap, int); + size++; + break; + case 's': + str = va_arg(ap, char *); + for (size_t j = 0; j < strlen(str); j++) { + s[size] = str[j]; + size++; + } + break; + case 'd': + value = va_arg(ap, int); + if (value == 0) { + s[size] = '0'; + size++; + } if (value < 0) { + s[size] = '-'; + value *= (-1); + size++; + } + + long long power = 1; + + while (value > power) + power *= 10; + + if (power >= 10) + power /= 10; + + while (value != 0) { + int digit = (int)(value / power); + s[size] = '0' + digit; + size++; + if (digit != 0) + value = value - digit * power; + if (power != 1) + power /= 10; + } + break; + case 'x': + value = va_arg(ap, int); + if (value == 0) { + s[size] = '0'; + size++; + } + + power = 1; + + while (value > power) + power *= 16; + + if (power >= 16) + power /= 16; + + while (value != 0) { + int digit = (int)(value / power); + if (digit < 10) + s[size] = '0' + digit; + else if (digit == 10) + s[size] = 'a'; + else if (digit == 11) + s[size] = 'b'; + else if (digit == 12) + s[size] = 'c'; + else if (digit == 13) + s[size] = 'd'; + else if (digit == 14) + s[size] = 'e'; + else if (digit == 15) + s[size] = 'f'; + size++; + if (digit != 0) + value = value - digit * power; + if (power != 1) + power /= 16; + } + break; + } + } else { + s[size] = format[i]; + size++; + } + } + s[size] = '\0'; + return size; +} + +int ksnprintf(char *restrict s, size_t n, const char *restrict format, ...) { + int size; + va_list ap; + va_start(ap, format); + size = kvsnprintf(s, n, format, ap); + va_end(ap); + return size; +} + +int kvsprintf(char *restrict s, const char *restrict format, va_list ap) { + size_t n = strlen(s); + return kvsnprintf(s, n, format, ap); +} + +int ksprintf(char *restrict s, const char *restrict format, ...) { + int size; + va_list ap; + va_start(ap, format); + size = kvsprintf(s, format, ap); + va_end(ap); + return size; +} + +int kvprintf(const char *restrict format, va_list ap) { + char s[1024] = ""; // FIXME + int size = kvsprintf(s, format, ap); + int i = 0; + while (i < size) { + kputchar(s[i]); + i++; + } + return size; +} + +int kprintf(const char *restrict format, ...) { + int size; + va_list ap; + va_start(ap, format); + size = kvprintf(format, ap); + va_end(ap); + return size; +} diff --git a/src/libk/stdlib.c b/src/libk/stdlib.c new file mode 100644 index 0000000..ab4784b --- /dev/null +++ b/src/libk/stdlib.c @@ -0,0 +1,15 @@ +#include +#include + +__attribute__((__noreturn__)) +void abort(void) { +#if defined(__is_libk) + // TODO: Add proper kernel panic. + kprintf("kernel: panic: abort()\n"); +#else + // TODO: Abnormally terminate the process as if by SIGABRT. + kprintf("abort()\n"); +#endif + while (1) { } + __builtin_unreachable(); +} diff --git a/src/libk/string.c b/src/libk/string.c new file mode 100644 index 0000000..44aa2aa --- /dev/null +++ b/src/libk/string.c @@ -0,0 +1,64 @@ +#include + +size_t strlen(const char *s) { + int i = 0; + while(s[i] != '\0') { + i++; + } + return i; +} + +void *memmove(void *dest, const void *src, size_t n) { + unsigned char* chardest = dest; + const unsigned char* charsrc = src; + + if (chardest == charsrc) { + return (void*)chardest; + } else if (chardest > charsrc) { + for (size_t i = n; i > sizeof(charsrc); i--) + chardest[i] = charsrc[i]; + } else { + for (int i = 0; i < chardest - charsrc; i++) + chardest[i] = charsrc[i]; + } + + memcpy(chardest, charsrc, n); + + return (void*)chardest; +} + +int memcmp(const void *s1, const void *s2, size_t n) { + int status = sizeof(s1) - sizeof(s2); + if (status > 0 || status < 0) { + return status; + } else { + const unsigned char* first = s1; + const unsigned char* second = s2; + for (size_t i = 0; i < n; i++) { + if (first[i] != second[i]) + return first[i] - second[i]; + } + return 0; + } +} + +void *memset(void *s, int c, size_t n) { + unsigned char uc = c; + unsigned char* ss = s; + for (size_t i = 0; i < n; i++) + ss[i] = uc; + return (void*)ss; +} + +void *memcpy(void *restrict dest, const void *restrict src, size_t n) { + size_t i; + unsigned char* chardest = dest; + const unsigned char* charsrc = src; + for (i = 0; i < n && charsrc[i] != '\0'; i++) { + chardest[i] = charsrc[i]; + } + for (; i < n; i++) { + chardest[i] = '\0'; + } + return (void*)chardest; +} diff --git a/src/linker.ld b/src/linker.ld new file mode 100644 index 0000000..7ab94d3 --- /dev/null +++ b/src/linker.ld @@ -0,0 +1,43 @@ +/* The bootloader will look at this image and start execution at the symbol + designated as the entry point. */ +ENTRY(_start) + +/* 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; + + /* 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) + { + *(.multiboot) + *(.text) + } + + /* Read-only data. */ + .rodata BLOCK(4K) : ALIGN(4K) + { + *(.rodata) + } + + /* Read-write data (initialized) */ + .data BLOCK(4K) : ALIGN(4K) + { + *(.data) + } + + /* Read-write data (uninitialized) and stack */ + .bss BLOCK(4K) : ALIGN(4K) + { + *(COMMON) + *(.bss) + } + + /* 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. */ +} diff --git a/src/vga.c b/src/vga.c new file mode 100644 index 0000000..d6bbd18 --- /dev/null +++ b/src/vga.c @@ -0,0 +1,96 @@ +/* + From OSDev Wiki: + + IMPORTANT NOTE: the VGA text mode (as well as the BIOS) is deprecated + on newer machines, and UEFI only supports pixel buffers. For forward + compatibility you might want to start with that. Ask GRUB to set up a + framebuffer using appropriate Multiboot flags or call VESA VBE yourself. Unlike + VGA text mode, a framebuffer has pixels, so you have to draw each glyph + yourself. +*/ + +#include +#include + +size_t terminal_row; +size_t terminal_column; +uint8_t terminal_color; +uint16_t* terminal_buffer; + +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, int color) { + return (uint16_t) uc | (uint16_t) color << 8; +} + +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_scroll() { + if (terminal_row >= 25) { + int i; + for (i = 0; i < 24*80; i++) + terminal_buffer[i] = terminal_buffer[i+80]; + for (i = 24*80; i < 25*80; i++) + terminal_buffer[i] = ' '; + terminal_row = 24; + } +} + +void terminal_delete_last_line() { + int x, *ptr; + + for(x = 0; x < VGA_WIDTH * 2; x++) { + ptr = (int*)0xB8000 + (VGA_WIDTH * 2) * (VGA_HEIGHT - 1) + x; + *ptr = 0; + } +} + +void terminal_putchar(char c) { + terminal_putentryat(c, terminal_color, terminal_column, terminal_row); + if (c != '\n') { + if (++terminal_column == VGA_WIDTH) { + terminal_column = 0; + if (++terminal_row == VGA_HEIGHT) { + for(int line = 1; line <= VGA_HEIGHT - 1; line++) + terminal_scroll(line); + terminal_delete_last_line(); + terminal_row = VGA_HEIGHT - 1; + } + } + } else { + terminal_row += 1; + terminal_column = 0; + } + terminal_scroll(); +} + +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)); +}