From a2a4a155c259d24101435771607b81f167b62cf3 Mon Sep 17 00:00:00 2001 From: Simponic Date: Sat, 20 Feb 2021 23:24:25 -0700 Subject: [PATCH] Changed to nasm --- boot.o | Bin 0 -> 976 bytes gdt.o | Bin 0 -> 952 bytes include/gdt.h | 17 ++++++++ include/print.h | 19 +++++++++ include/types.h | 16 ++++++++ linker.ld | 43 ++++++++++++++++++++ makefile | 27 ++++++++++++ print.o | Bin 0 -> 1704 bytes src/boot.s | 106 ++++++++++++++++++++++++++++++++++++++++++++++++ src/gdt.c | 36 ++++++++++++++++ src/kernel.c | 22 ++++++++++ src/print.c | 73 +++++++++++++++++++++++++++++++++ 12 files changed, 359 insertions(+) create mode 100644 boot.o create mode 100644 gdt.o create mode 100644 include/gdt.h create mode 100644 include/print.h create mode 100644 include/types.h create mode 100644 linker.ld create mode 100644 makefile create mode 100644 print.o create mode 100644 src/boot.s create mode 100644 src/gdt.c create mode 100644 src/kernel.c create mode 100644 src/print.c diff --git a/boot.o b/boot.o new file mode 100644 index 0000000000000000000000000000000000000000..68fc8c6f4678786454a7ee3a8dcb3d6a4a81b644 GIT binary patch literal 976 zcmb7DO-~b16umQ3Nr^_mC@jzfKNhfH(1e`{X)SGO>_8&AV$rm8D1^2{<~Qx`xXI1A=f3++@4LNoR&CZC#}R9ec#<=bk~vt( za6yVvu%*{RYSLYE>mA6gF$Js2ToLY>v-iAIG6XPX!f7CnCJblYGZPbR-7I3O#rzl4 zy2 zF6y^|*0LQ^@y;@*>njOfmYa?Gh6L51u~FNUTC-f=lAv5~RHU+At-RWL8;H{For6{< z(mEO#qN79Ub#*K%)b*~G7HVQG2Vp!6`>jEHe`r$vUVGTHaX9Rvk(+P_%Q9o|p}{9a M43hJ5`7s##2W@n0GXMYp literal 0 HcmV?d00001 diff --git a/gdt.o b/gdt.o new file mode 100644 index 0000000000000000000000000000000000000000..6034c8b0d106cabdbbfd99761b2ed536acb7b87a GIT binary patch literal 952 zcma)5&r2Io5T5;^uDYyS@gV3SLZBAPL%~a13f)a?M5G6sTCh-!$*QGEgk)P(3Pl9_ zVlMsxo;`cCCtU*8KSSsxMJR|Dp@;3v?tATe5PdN3n{VFtX6DT%Ungf?DvAPfDWJiB z29OtIA`*;3FBtH4?sYStwwu}XKyxNN_#E)YXS>nb`Wf~CZ2h6#caLA-6|Oc;gWpEu ziq9)S+ulC!>%Qi0C!bauXF=QGu108cR}X#-^69SKY%lgV|-Yg)d(HS_@+9RxuZFjy{)T@ zJuS<0PA$XvVdG~rt-Y5szlt48O!;y#y3i2IU40OvN;vqIl{Zfo(Ksw?N(BvD?jf%`h zPQ1zY63Uu%V&bJe#2z%tL-}kp%Ad@)kA!N-CG$xxF&Wq4GL&(`oBtBx$xetTD-xGe nh_h(_zPjWc&=tGUZ=jzhyHfI55xi<~t>ophc{)y^saWbSrL$`0 literal 0 HcmV?d00001 diff --git a/include/gdt.h b/include/gdt.h new file mode 100644 index 0000000..8b239dd --- /dev/null +++ b/include/gdt.h @@ -0,0 +1,17 @@ +#ifndef GDT_H +#define GDT_H + +#include "types.h" + +struct GDT { + uint32_t limit; + uint32_t base; + uint8_t type; +} __attribute((packed)); + +struct GDT_ptr { + uint16_t limit; + uint32_t base; +} __attribute((packed)); + +#endif //GDT_H diff --git a/include/print.h b/include/print.h new file mode 100644 index 0000000..c46843c --- /dev/null +++ b/include/print.h @@ -0,0 +1,19 @@ +#ifndef PRINT_H +#define PRINT_H + +#include "types.h" + +typedef struct TextOutput { + int terminal_row; + int terminal_column; + int max_row; + int max_column; + uint16_t* vid_mem; +}__attribute__((packed)) TextOutput; + +TextOutput createOutput(const int max_row, const int max_column, uint16_t* vid_mem); +void scrollText(TextOutput* textOutput); +void putChar(uint8_t character, uint8_t background, uint8_t foreground, TextOutput* textOutput); +void print(char* string, uint8_t background, uint8_t foreground, TextOutput* textOutput); + +#endif // PRINT_H diff --git a/include/types.h b/include/types.h new file mode 100644 index 0000000..a4795c6 --- /dev/null +++ b/include/types.h @@ -0,0 +1,16 @@ +#ifndef TYPES_H +#define TYPES_H + +typedef char int8_t; +typedef unsigned char uint8_t; + +typedef short int16_t; +typedef unsigned short uint16_t; + + +typedef int int32_t; +typedef unsigned int uint32_t; + +typedef long long int int64_t; +typedef unsigned long long int uint64_t; +#endif diff --git a/linker.ld b/linker.ld new file mode 100644 index 0000000..38ddd94 --- /dev/null +++ b/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/makefile b/makefile new file mode 100644 index 0000000..acda2c3 --- /dev/null +++ b/makefile @@ -0,0 +1,27 @@ +# vim: noexpandtab tabstop=4 shiftwidth=4 +CXX = i386-elf-gcc +CFLAGS = -std=gnu99 -ffreestanding -O2 -Wall -Wextra -nostdlib -Iinclude +ASM = nasm + +OBJECTS = gdt.o boot.o print.o kernel.o + +%.o : src/%.c + $(CXX) $(CFLAGS) -o $@ -c $< + +%.o : src/%.s + $(ASM) -felf32 $< -o $@ + +os.bin : $(OBJECTS) + $(CXX) -T linker.ld -o os.bin $(CFLAGS) $(OBJECTS) -lgcc + +os.iso : os.bin + mkdir -p isodir/boot/grub + mv os.bin isodir/boot/os.bin + echo "menuentry 'os' {" >> grub.cfg + echo " multiboot /boot/os.bin" >> grub.cfg + echo "}" >> grub.cfg + mv grub.cfg isodir/boot/grub/grub.cfg + grub-mkrescue -o os.iso isodir + +clean : + rm -rf *.o *.iso *.bin *.cfg isodir/ diff --git a/print.o b/print.o new file mode 100644 index 0000000000000000000000000000000000000000..1e31a59deb5edc6cedb59f3630f51e99eeedcc84 GIT binary patch literal 1704 zcma)6U1%It6uz@F)k!kWOv50>MqP1~4Tdp+eQ;GY!(`23qghPaf?CUV-6bKrN!ZM4 zqqQw;q8Ts2eHI^l^g+m5K%uZ}s%zhBKwtV&M5?hq7>k7l>H3}7xpdWtUO4-m^WF1v z&$)N@TzcX;Ns^dY5|h~-i?L7S&Q3Cf1k+d#<72kc3hmaYLdd6WHOQ&}Ey!vBGsv1> zC~W-=cJ%nMsoF0lKV#c`Tn)x-b+vJJk{{RBm3~;-$dwCJt{x28NUNv^CNQd|N5ji#+KTgQPc_x?H`%L8XHmT(eUXwt8->ZPYHBue^5h#8$mi&%zlJ z@EYc?+R69F?4jCKV@ef4t^0{a$xNU6wD(y;_q#fzDbW{2ry#@zs%~)!udUG_8 zTV6O~5e+6B*vO@w>2|wae?Iu;`Uf~ifT2(Q^am&|zf3o^d8Js#6=lQFH0@xND~;;)ut7HNc~8a@<1Zc^j3=FxGr}zNM@xF^ zB_^FwrNf${tle_pz2Ev zo9>rzm<$C`aJl0pZ3Ij5GE zSV%b4`O@rMp|s4L{6e{0n1>5F7iLZtE4gxk&hkIkgYctq-4*%Mcl$FD(Fz|E^uv%I zK>|-BfbOstLhj_JLLQ0i=x=~M5ChS*MC!-Dk^g`B(R=7V-@oO1Hu58XTIAQ?QNv%# zK{64cIMy(fxHuH{M86s6-Tkhi-#9qx_il*j_aJo915}oUag3B`iPVImCT*=9V5|Y` zgU}o(AI+0$cIV5W?&ILZ68VHJu$x!dZd>q#fBlChySoC}qU%nS%y77_C5Ii~A!NK3 zF`Gfq{g~{%K=Iv@{Sa*NEs{-s;yWRmEmmgdeJ9WIl|s%hOjiBHs?V14m4#Ain%+E2 hZzfmi_%Ze&LSiWTEg>!;j(jl;l-?`hVuU|L_$OfAFQxzh literal 0 HcmV?d00001 diff --git a/src/boot.s b/src/boot.s new file mode 100644 index 0000000..2472103 --- /dev/null +++ b/src/boot.s @@ -0,0 +1,106 @@ +; Declare constants for the multiboot header. +MBALIGN equ 1 << 0 ; align loaded modules on page boundaries +MEMINFO equ 1 << 1 ; provide memory map +FLAGS equ MBALIGN | MEMINFO ; this is the Multiboot 'flag' field +MAGIC equ 0x1BADB002 ; 'magic number' lets bootloader find the header +CHECKSUM equ -(MAGIC + FLAGS) ; checksum of above, to prove we are multiboot + +; Declare a multiboot header that marks the program as a kernel. These are magic +; values that are documented in the multiboot standard. The bootloader will +; search for this signature in the first 8 KiB of the kernel file, aligned at a +; 32-bit boundary. The signature is in its own section so the header can be +; forced to be within the first 8 KiB of the kernel file. +section .multiboot +align 4 + dd MAGIC + dd FLAGS + dd CHECKSUM + +; The multiboot standard does not define the value of the stack pointer register +; (esp) and it is up to the kernel to provide a stack. This allocates room for a +; small stack by creating a symbol at the bottom of it, then allocating 16384 +; bytes for it, and finally creating a symbol at the top. The stack grows +; downwards on x86. The stack is in its own section so it can be marked nobits, +; which means the kernel file is smaller because it does not contain an +; uninitialized stack. The stack on x86 must be 16-byte aligned according to the +; System V ABI standard and de-facto extensions. The compiler will assume the +; stack is properly aligned and failure to align the stack will result in +; undefined behavior. +section .bss +align 16 +stack_bottom: +resb 16384 ; 16 KiB +stack_top: + +; The linker script specifies _start as the entry point to the kernel and the +; bootloader will jump to this position once the kernel has been loaded. It +; doesn't make sense to return from this function as the bootloader is gone. +; Declare _start as a function symbol with the given symbol size. +section .text + +global _gdt_flush ; Allows the C code to link to this +extern _gp ; Says that '_gp' is in another file +_gdt_flush: + lgdt [_gp] ; Load the GDT with our '_gp' which is a special pointer + mov ax, 0x10 ; 0x10 is the offset in the GDT to our data segment + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + jmp 0x08:flush2 ; 0x08 is the offset to our code segment: Far jump! +flush2: + ret ; Returns back to the C code! + +global _start:function (_start.end - _start) +_start: + ; The bootloader has loaded us into 32-bit protected mode on a x86 + ; machine. Interrupts are disabled. Paging is disabled. The processor + ; state is as defined in the multiboot standard. The kernel has full + ; control of the CPU. The kernel can only make use of hardware features + ; and any code it provides as part of itself. There's no printf + ; function, unless the kernel provides its own header and a + ; printf implementation. There are no security restrictions, no + ; safeguards, no debugging mechanisms, only what the kernel provides + ; itself. It has absolute and complete power over the + ; machine. + + ; To set up a stack, we set the esp register to point to the top of our + ; stack (as it grows downwards on x86 systems). This is necessarily done + ; in assembly as languages such as C cannot function without a stack. + mov esp, stack_top + + ; This is a good place to initialize crucial processor state before the + ; high-level kernel is entered. It's best to minimize the early + ; environment where crucial features are offline. Note that the + ; processor is not fully initialized yet: Features such as floating + ; point instructions and instruction set extensions are not initialized + ; yet. The GDT should be loaded here. Paging should be enabled here. + ; C++ features such as global constructors and exceptions will require + ; runtime support to work as well. + + ; Enter the high-level kernel. The ABI requires the stack is 16-byte + ; aligned at the time of the call instruction (which afterwards pushes + ; the return pointer of size 4 bytes). The stack was originally 16-byte + ; aligned above and we've since pushed a multiple of 16 bytes to the + ; stack since (pushed 0 bytes so far) and the alignment is thus + ; preserved and the call is well defined. + ; note, that if you are building on Windows, C functions may have "_" prefix in assembly: _kernel_main + extern kernel_main + call kernel_main + + ; If the system has nothing more to do, put the computer into an + ; infinite loop. To do that: + ; 1) Disable interrupts with cli (clear interrupt enable in eflags). + ; They are already disabled by the bootloader, so this is not needed. + ; Mind that you might later enable interrupts and return from + ; kernel_main (which is sort of nonsensical to do). + ; 2) Wait for the next interrupt to arrive with hlt (halt instruction). + ; Since they are disabled, this will lock up the computer. + ; 3) Jump to the hlt instruction if it ever wakes up due to a + ; non-maskable interrupt occurring or due to system management mode. + cli + +.hang: hlt + jmp .hang +.end: diff --git a/src/gdt.c b/src/gdt.c new file mode 100644 index 0000000..a9808aa --- /dev/null +++ b/src/gdt.c @@ -0,0 +1,36 @@ +#include "gdt.h" + +void encodeGDT(uint8_t* gdtEntry, struct GDT source) { + if ((source.limit > 65536) && ((source.limit & 0xFFF) == 0xFFF)) { + // Set the GDT to use paging + // To do this we need to make sure the limit is aligned to 4KiB + source.limit = source.limit >> 12; + // Granularity: 1 (use paging with 4KiB segments) + // Size: 1 (32 bit protected mode) + gdtEntry[6] = 0xC0; + } + else { + // Granularity: 0 (1 byte segments) + // Size: 1 + gdtEntry[6] = 0x40; + } + + // Here we are encoding the limit + + // Bits 0-15 encode the bottom 16 bits of the limit + gdtEntry[0] = source.limit & 0xFF; + gdtEntry[1] = (source.limit >> 8) & 0xFF; + // Bits 48-51 encode the last 4 bits of the limit + gdtEntry[6] |= (source.limit >> 16) & 0xF; + + // Bits 16-39 encode the bottom 24 bits of the base + gdtEntry[2] = source.base & 0xFF; + gdtEntry[3] = (source.base >> 8) & 0xFF; + gdtEntry[4] = (source.base >> 16) & 0xFF; + // Bits 56-64 encode the last byte of the base + gdtEntry[7] = (source.base >> 24) & 0xFF; + + // Bits 40-47 set the access byte, which is documented at https://wiki.osdev.org/GDT, + // where most of the ideas for this function are taken from shamelessly + gdtEntry[5] = source.type; +} diff --git a/src/kernel.c b/src/kernel.c new file mode 100644 index 0000000..d24c24e --- /dev/null +++ b/src/kernel.c @@ -0,0 +1,22 @@ +#include "gdt.h" +#include "types.h" +#include "print.h" + +#define FOREGROUND 0x0 +#define BACKGROUND 0xF + +void PrintWithScreenFill(char* string, TextOutput* output_stream) { + // Print a string and fill the screen + print(string, BACKGROUND, FOREGROUND, output_stream); + int row = output_stream->terminal_row; + while (output_stream->terminal_row < output_stream->max_row) { + putChar('\n', BACKGROUND, FOREGROUND, output_stream); + } + output_stream->terminal_row = row; +} + +void kernel_main(void) { + TextOutput output_stream = createOutput(25,80,(uint16_t*)0xB8000); + PrintWithScreenFill("Hello, Logan World!\n", &output_stream); +} + diff --git a/src/print.c b/src/print.c new file mode 100644 index 0000000..0a0220c --- /dev/null +++ b/src/print.c @@ -0,0 +1,73 @@ +#include "print.h" + +TextOutput createOutput(const int max_row, const int max_column, uint16_t* vid_mem) { + // Create a new TextOutput interface + TextOutput output; + output.max_row = max_row; + output.max_column = max_column; + output.vid_mem = vid_mem; + output.terminal_row = 0; + output.terminal_column = 0; + return output; +} + +void scrollText(TextOutput* textOutput) { + // Move each character up one row + for (int y = 0; y < textOutput->max_row; y++) { + for (int x = 0; x < textOutput->max_column; x++) { + *(textOutput->vid_mem + textOutput->max_column*y + x) = + *(textOutput->vid_mem + textOutput->max_column*(y+1) + x); + } + } + textOutput->terminal_row--; +} + +void putChar(uint8_t character, uint8_t background, uint8_t foreground, + TextOutput* textOutput) { + foreground = foreground & 0xF; background = background & 0xF; + + // Handle putting a character to the screen + if (textOutput->terminal_row == textOutput->max_row) { + scrollText(textOutput); + } + + if (character == '\r') { + // Delete the character before this \r + if (textOutput->terminal_column == 0) { + textOutput->terminal_row--; + textOutput->terminal_column = 80; + } + textOutput->terminal_column--; + *(textOutput->vid_mem + textOutput->terminal_row*textOutput->max_column + + textOutput->terminal_column) = background << 12; + return; + } + + else if (character == '\n' || textOutput->terminal_column == textOutput->max_column) { + // Make a new line + for (int i = textOutput->terminal_column; i < textOutput->max_column; i++) { + *(textOutput->vid_mem + textOutput->terminal_row*textOutput->max_column + + textOutput->terminal_column) = background << 12; + textOutput->terminal_column++; + } + + textOutput->terminal_row++; + textOutput->terminal_column = 0; + } + + if (character != '\n' && character != '\r') { + // Write character to video memory + uint16_t entry = (background << 12) | (foreground << 8) | character; + *(textOutput->vid_mem + textOutput->terminal_row*textOutput->max_column + + textOutput->terminal_column) = entry; + textOutput->terminal_column++; + } +} + +void print(char* string, uint8_t background, uint8_t foreground, TextOutput* textOutput) { + // Print a string + for (string; *string; string++) { + putChar(*string, background, foreground, textOutput); + } +} +