diff --git a/gems-kernel.git/build.sh b/gems-kernel.git/build.sh index 0f064bf99..f384fdf3a 100755 --- a/gems-kernel.git/build.sh +++ b/gems-kernel.git/build.sh @@ -7,12 +7,12 @@ mkdir builds/iso mkdir builds/iso/boot mkdir builds/iso/boot/grub echo "Building bootloader" -nasm -f elf32 source/bootloader.asm -o builds/blocks/bl.o +nasm -f elf32 source/bootloader.asm -o builds/blocks/bootloader.o echo "Building basic keyboard support" nasm -f elf32 source/detect-kbinput.asm -o builds/blocks/detectkeys.o echo "Building OS" set disassembly-flavor intel -opt/cross/bin/i686-elf-gcc builds/blocks/bl.o -ffreestanding -nostdlib builds/blocks/detectkeys.o source/os.c -w -g -m32 -o builds/iso/gems.elf -I"/usr/include" -I"source/THIRDPARTY/lwext4-master/include/" -I"source/THIRDPARTY/linux-old/include/linux" -I"source/THIRDPARTY/linux-old/include/asm" +opt/cross/bin/i686-elf-gcc builds/blocks/bootloader.o -ffreestanding -nostdlib builds/blocks/detectkeys.o source/os.c -w -g -m32 -o builds/iso/gems.elf -I"/usr/include" -I"source/THIRDPARTY/lwext4-master/include/" -I"source/THIRDPARTY/linux-old/include/linux" -I"source/THIRDPARTY/linux-old/include/asm" echo "Creating GRUB config" echo "set default=0" > builds/iso/boot/grub/grub.cfg echo "set timeout=60" >> builds/iso/boot/grub/grub.cfg diff --git a/gems-kernel.git/builds/blocks/bootloader.o b/gems-kernel.git/builds/blocks/bootloader.o new file mode 100644 index 000000000..0df3ae802 Binary files /dev/null and b/gems-kernel.git/builds/blocks/bootloader.o differ diff --git a/gems-kernel.git/builds/gems.iso b/gems-kernel.git/builds/gems.iso index fbfa4d00d..0b728422d 100644 Binary files a/gems-kernel.git/builds/gems.iso and b/gems-kernel.git/builds/gems.iso differ diff --git a/gems-kernel.git/builds/iso/gems.elf b/gems-kernel.git/builds/iso/gems.elf index 9714a3448..9f288c868 100755 Binary files a/gems-kernel.git/builds/iso/gems.elf and b/gems-kernel.git/builds/iso/gems.elf differ diff --git a/gems-kernel.git/linker.ld b/gems-kernel.git/linker.ld new file mode 100644 index 000000000..269cda129 --- /dev/null +++ b/gems-kernel.git/linker.ld @@ -0,0 +1,52 @@ +/* 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 +{ + /* It used to be universally recommended to use 1M as a start offset, + as it was effectively guaranteed to be available under BIOS systems. + However, UEFI has made things more complicated, and experimental data + strongly suggests that 2M is a safer place to load. In 2016, a new + feature was introduced to the multiboot2 spec to inform bootloaders + that a kernel can be loaded anywhere within a range of addresses and + will be able to relocate itself to run from such a loader-selected + address, in order to give the loader freedom in selecting a span of + memory which is verified to be available by the firmware, in order to + work around this issue. This does not use that feature, so 2M was + chosen as a safer option than the traditional 1M. */ + . = 2M; + + /* First put the multiboot header, as it is required to be put very 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. */ +} \ No newline at end of file diff --git a/gems-kernel.git/quick-test.sh b/gems-kernel.git/quick-test.sh new file mode 100755 index 000000000..8ba98ac84 --- /dev/null +++ b/gems-kernel.git/quick-test.sh @@ -0,0 +1,5 @@ +if grub-file --is-x86-multiboot builds/iso/gems.elf; then + echo multiboot confirmed +else + echo the file is not multiboot +fi diff --git a/gems-kernel.git/source/bootloader.asm b/gems-kernel.git/source/bootloader.asm index 3a85769c8..2e6577e15 100644 --- a/gems-kernel.git/source/bootloader.asm +++ b/gems-kernel.git/source/bootloader.asm @@ -1,16 +1,90 @@ -SECTION .multiboot -ALIGN 4 -extern kern -mboot: - mov ax, 9ch - mov ss, ax ;cannot be written directly - mov sp, 4094d - mov ax, 7c0h - mov ds, ax ;cannot be written directly - call kern -quit: - hlt - jmp quit - jmp $ - times 510-($-$$) db 0 - dw 0xAA55 \ No newline at end of file +; Declare constants for the multiboot header. +MBALIGN equ 1 << 0 ; align loaded modules on page boundaries +MEMINFO equ 1 << 1 ; provide memory map +MBFLAGS equ MBALIGN | MEMINFO ; this is the Multiboot 'flag' field +MAGIC equ 0x1BADB002 ; 'magic number' lets bootloader find the header +CHECKSUM equ -(MAGIC + MBFLAGS) ; 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 MBFLAGS + 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 _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: \ No newline at end of file diff --git a/gems-kernel.git/source/debug.h b/gems-kernel.git/source/debug.h index 6ec512aa4..9771da5d8 100644 --- a/gems-kernel.git/source/debug.h +++ b/gems-kernel.git/source/debug.h @@ -78,5 +78,5 @@ void panic(char deets[128]) { print("\nAre colors working? (Order: Blue,Green,Lightblue,Red,Pink,Orange,White.)\n"); showColors(); print("Regular\n"); - halt(); + halt(1); } diff --git a/gems-kernel.git/source/linker.ld b/gems-kernel.git/source/linker.ld index ff2879da5..269cda129 100644 --- a/gems-kernel.git/source/linker.ld +++ b/gems-kernel.git/source/linker.ld @@ -1,41 +1,52 @@ +/* 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 { - .text.start (_KERNEL_BASE_) : { -/* link multiboot struct */ -. = ALIGN(8); -KEEP(*(.multiboot)) -/* keep this */ - startup.o( .text ) - } - - .text : ALIGN(0x1000) { -/* link multiboot struct */ -. = ALIGN(8); -KEEP(*(.multiboot)) -/* keep this */ -_TEXT_START_ = .; - *(.text) -_TEXT_END_ = .; - } - - .data : ALIGN(0x1000) { -/* link multiboot struct */ -. = ALIGN(8); -KEEP(*(.multiboot)) -/* keep this */ -_DATA_START_ = .; - *(.data) -_DATA_END_ = .; - } - - .bss : ALIGN(0x1000) { -/* link multiboot struct */ -. = ALIGN(8); -KEEP(*(.multiboot)) -/* keep this */ -_BSS_START_ = .; - *(.bss) -_BSS_END_ = .; - } -} - + /* It used to be universally recommended to use 1M as a start offset, + as it was effectively guaranteed to be available under BIOS systems. + However, UEFI has made things more complicated, and experimental data + strongly suggests that 2M is a safer place to load. In 2016, a new + feature was introduced to the multiboot2 spec to inform bootloaders + that a kernel can be loaded anywhere within a range of addresses and + will be able to relocate itself to run from such a loader-selected + address, in order to give the loader freedom in selecting a span of + memory which is verified to be available by the firmware, in order to + work around this issue. This does not use that feature, so 2M was + chosen as a safer option than the traditional 1M. */ + . = 2M; + + /* First put the multiboot header, as it is required to be put very 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. */ +} \ No newline at end of file diff --git a/gems-kernel.git/source/os.c b/gems-kernel.git/source/os.c index 7fc2736f0..f91d78bff 100644 --- a/gems-kernel.git/source/os.c +++ b/gems-kernel.git/source/os.c @@ -63,15 +63,15 @@ void os() { while ( 1 == 1 ) { rloadstring("basickeys"); } - panic("RUSHELL-LEVEL CRASH"); } void kern() { //extern bootloader(); //bootloader(); + haltLoop(); clear(lastVGATextColor()); print("GEMS OK, WAITING A FEW TICKS TO TEST TIME... \n"); - wait(100000000); //ok? ok. + wait(10); //ok? ok. beep(); print("Getting frequency of A2 \n"); print("(might be garbled until fixed) \n"); @@ -84,10 +84,6 @@ void kern() { waitSecs(5); clear(); os(); - while (1 == 1) - { - panic("KERNEL-LEVEL CRASH"); - } } int kernel_main(struct multiboot_info* mbd, unsigned int magic) { diff --git a/gems-kernel.git/source/start.s b/gems-kernel.git/source/start.s index 0eb1538b8..269cda129 100644 --- a/gems-kernel.git/source/start.s +++ b/gems-kernel.git/source/start.s @@ -1,53 +1,52 @@ -// We declare the 'kernel_main' label as being external to this file. -// That's because it's the name of the main C function in 'kernel.c'. -.extern kernel_main +/* The bootloader will look at this image and start execution at the symbol + designated as the entry point. */ +ENTRY(_start) -// We declare the 'start' label as global (accessible from outside this file), since the linker will need to know where it is. -// In a bit, we'll actually take a look at the code that defines this label. -.global start +/* Tell where the various sections of the object files will be put in the final + kernel image. */ +SECTIONS +{ + /* It used to be universally recommended to use 1M as a start offset, + as it was effectively guaranteed to be available under BIOS systems. + However, UEFI has made things more complicated, and experimental data + strongly suggests that 2M is a safer place to load. In 2016, a new + feature was introduced to the multiboot2 spec to inform bootloaders + that a kernel can be loaded anywhere within a range of addresses and + will be able to relocate itself to run from such a loader-selected + address, in order to give the loader freedom in selecting a span of + memory which is verified to be available by the firmware, in order to + work around this issue. This does not use that feature, so 2M was + chosen as a safer option than the traditional 1M. */ + . = 2M; -// Our bootloader, GRUB, needs to know some basic information about our kernel before it can boot it. -// We give GRUB this information using a standard known as 'Multiboot'. -// To define a valid 'Multiboot header' that will be recognised by GRUB, we need to hard code some -// constants into the executable. The following code calculates those constants. -.set MB_MAGIC, 0x1BADB002 // This is a 'magic' constant that GRUB will use to detect our kernel's location. -.set MB_FLAGS, (1 << 0) | (1 << 1) // This tells GRUB to 1: load modules on page boundaries and 2: provide a memory map (this is useful later in development) -// Finally, we calculate a checksum that includes all the previous values -.set MB_CHECKSUM, (0 - (MB_MAGIC + MB_FLAGS)) + /* First put the multiboot header, as it is required to be put very 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) + } -// We now start the section of the executable that will contain our Multiboot header -.section .multiboot - .align 4 // Make sure the following data is aligned on a multiple of 4 bytes - // Use the previously calculated constants in executable code - .long MB_MAGIC - .long MB_FLAGS - // Use the checksum we calculated earlier - .long MB_CHECKSUM + /* Read-only data. */ + .rodata BLOCK(4K) : ALIGN(4K) + { + *(.rodata) + } -// This section contains data initialised to zeroes when the kernel is loaded -.section .bss - // Our C code will need a stack to run. Here, we allocate 4096 bytes (or 4 Kilobytes) for our stack. - // We can expand this later if we want a larger stack. For now, it will be perfectly adequate. - .align 16 - stack_bottom: - .skip 4096 // Reserve a 4096-byte (4K) stack - stack_top: + /* Read-write data (initialized) */ + .data BLOCK(4K) : ALIGN(4K) + { + *(.data) + } -// This section contains our actual assembly code to be run when our kernel loads -.section .text - // Here is the 'start' label we mentioned before. This is the first code that gets run in our kernel. - start: - // First thing's first: we want to set up an environment that's ready to run C code. - // C is very relaxed in its requirements: All we need to do is to set up the stack. - // Please note that on x86, the stack grows DOWNWARD. This is why we start at the top. - mov $stack_top, %esp // Set the stack pointer to the top of the stack + /* Read-write data (uninitialized) and stack */ + .bss BLOCK(4K) : ALIGN(4K) + { + *(COMMON) + *(.bss) + } - // Now we have a C-worthy (haha!) environment ready to run the rest of our kernel. - // At this point, we can call our main C function. - call kernel_main - - // If, by some mysterious circumstances, the kernel's C code ever returns, all we want to do is to hang the CPU - hang: - cli // Disable CPU interrupts - hlt // Halt the CPU - jmp hang // If that didn't work, loop around and try again. + /* 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. */ +} \ No newline at end of file diff --git a/gems-kernel.git/source/time.h b/gems-kernel.git/source/time.h index 512d9cc47..e6877214a 100644 --- a/gems-kernel.git/source/time.h +++ b/gems-kernel.git/source/time.h @@ -1,7 +1,7 @@ //Made to create time at 100000000 TPS //by Sparksammy int time = 1; - +int halted = 0; void countOld() { static long long int i; static int state = 0; @@ -20,12 +20,12 @@ void countOld() { } -void halt() { -// do nothing. +void halt(int halt) { + halted = halt; } void wait(uint32_t millis) { - uint32_t countdown = millis * 1193181.66667; + uint32_t countdown = millis * 1000; while (countdown > 0) { uint32_t countdown = countdown - 1; } @@ -67,3 +67,14 @@ void disableCount() { void enableCount() { countstate = true; } + +void haltLoop() { + while (true) { + if (halted == 0) { + break; + } + if (halted == 1) { + continue; + } + } +} \ No newline at end of file diff --git a/gems-kernel.git/test-mute.sh b/gems-kernel.git/test-mute.sh old mode 100644 new mode 100755