holy crap i got it to boot! 😄

This commit is contained in:
ThatLinuxFan 2024-01-26 18:19:23 -06:00
parent 962d1d1339
commit e5679210b0
13 changed files with 263 additions and 115 deletions

View file

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

Binary file not shown.

Binary file not shown.

Binary file not shown.

52
gems-kernel.git/linker.ld Normal file
View file

@ -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. */
}

5
gems-kernel.git/quick-test.sh Executable file
View file

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

View file

@ -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
; 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 <stdio.h> 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:

View file

@ -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);
}

View file

@ -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 )
}
/* 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;
.text : ALIGN(0x1000) {
/* link multiboot struct */
. = ALIGN(8);
KEEP(*(.multiboot))
/* keep this */
_TEXT_START_ = .;
*(.text)
_TEXT_END_ = .;
}
/* 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)
}
.data : ALIGN(0x1000) {
/* link multiboot struct */
. = ALIGN(8);
KEEP(*(.multiboot))
/* keep this */
_DATA_START_ = .;
*(.data)
_DATA_END_ = .;
}
/* Read-only data. */
.rodata BLOCK(4K) : ALIGN(4K)
{
*(.rodata)
}
.bss : ALIGN(0x1000) {
/* link multiboot struct */
. = ALIGN(8);
KEEP(*(.multiboot))
/* keep this */
_BSS_START_ = .;
*(.bss)
_BSS_END_ = .;
}
/* 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. */
}

View file

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

View file

@ -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. */
}

View file

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

0
gems-kernel.git/test-mute.sh Normal file → Executable file
View file