diff --git a/Makefile b/Makefile index 57b8ce6..8fd2d6d 100644 --- a/Makefile +++ b/Makefile @@ -18,12 +18,15 @@ GOARCH := 386 LD_FLAGS := -n -melf_i386 -T arch/$(ARCH)/script/linker.ld -static --no-ld-generated-unwind-info AS_FLAGS := -g -f elf32 -F dwarf -I arch/$(ARCH)/asm/ +MIN_OBJCOPY_VERSION := 2.26.0 +HAVE_VALID_OBJCOPY := $(shell objcopy -V | head -1 | awk -F ' ' '{print "$(MIN_OBJCOPY_VERSION)\n" $$NF}' | sort -ct. -k1,1n -k2,2n && echo "y") + asm_src_files := $(wildcard arch/$(ARCH)/asm/*.s) asm_obj_files := $(patsubst arch/$(ARCH)/asm/%.s, $(BUILD_DIR)/arch/$(ARCH)/asm/%.o, $(asm_src_files)) -.PHONY: kernel iso clean +.PHONY: kernel iso clean binutils_version_check -kernel: $(kernel_target) +kernel: binutils_version_check $(kernel_target) $(kernel_target): $(asm_obj_files) go.o @echo "[$(LD)] linking kernel-$(ARCH).bin" @@ -45,9 +48,16 @@ go.o: @# build/go.o is a elf32 object file but all go symbols are unexported. Our @# asm entrypoint code needs to know the address to 'main.main' so we use - @# objcopy to make that symbol exportable - @echo "[objcopy] export 'main.main' symbol in go.o" - @objcopy --globalize-symbol='main.main' $(BUILD_DIR)/go.o $(BUILD_DIR)/go.o + @# objcopy to make that symbol exportable. Since nasm does not support externs + @# with slashes we create a global symbol alias for kernel.Kmain + @echo "[objcopy] creating global symbol alias 'kernel.Kmain' for 'github.com/achilleasa/gopher-os/kernel.Kmain' in go.o" + @objcopy \ + --add-symbol kernel.Kmain=.text:0x`nm $(BUILD_DIR)/go.o | grep "kernel.Kmain" | cut -d' ' -f1` \ + $(BUILD_DIR)/go.o $(BUILD_DIR)/go.o + +binutils_version_check: + @echo "[binutils] checking that installed objcopy version is >= $(MIN_OBJCOPY_VERSION)" + @if [ "$(HAVE_VALID_OBJCOPY)" != "y" ]; then echo "[binutils] error: a more up to date binutils installation is required" ; exit 1 ; fi $(BUILD_DIR)/arch/$(ARCH)/asm/%.o: arch/$(ARCH)/asm/%.s @mkdir -p $(shell dirname $@) diff --git a/arch/x86/asm/rt0.s b/arch/x86/asm/rt0.s index 95ea421..8519c49 100644 --- a/arch/x86/asm/rt0.s +++ b/arch/x86/asm/rt0.s @@ -24,17 +24,29 @@ section .text bits 32 align 4 +MULTIBOOT_MAGIC equ 0x36d76289 + +err_unsupported_bootloader db '[rt0] kernel not loaded by multiboot-compliant bootloader', 0 +err_kmain_returned db '[rt0] kMain returned; halting system', 0 + ;------------------------------------------------------------------------------ ; Kernel arch-specific entry point ; ; The boot loader will jump to this symbol after setting up the CPU according ; to the multiboot standard. At this point: +; - A20 is enabled ; - The CPU is using 32-bit protected mode ; - Interrupts are disabled ; - Paging is disabled +; - EAX contains the magic value ‘0x36d76289’; the presence of this value indicates +; to the operating system that it was loaded by a Multiboot-compliant boot loader +; - EBX contains the 32-bit physical address of the Multiboot information structure ;------------------------------------------------------------------------------ global _rt0_entry _rt0_entry: + cmp eax, MULTIBOOT_MAGIC + jne unsupported_bootloader + ; Initalize our stack by pointing ESP to the BSS-allocated stack. In x86, ; stack grows downwards so we need to point ESP to stack_top mov esp, stack_top @@ -48,19 +60,60 @@ _rt0_entry: mov dword [g0_stack_lo], stack_bottom mov dword [g0_stackguard0], stack_bottom - extern main.main - call main.main + ; push multiboot info ptr to the stack and call the kernel entrypoint + push ebx + extern kernel.Kmain + call kernel.Kmain + + ; kmain should never return + mov edi, err_kmain_returned + call write_string ; Main should never return; halt the CPU +halt: cli hlt + +unsupported_bootloader: + mov edi, err_unsupported_bootloader + call write_string + jmp halt .end: +;------------------------------------------------------------------------------ +; Write the NULL-terminated string contained in edi to the screen using white +; text on red background. Assumes that text-mode is enabled and that its +; physical address is 0xb8000. +;------------------------------------------------------------------------------ +write_string: + push eax + push ebx + + mov ebx,0xb8000 + mov ah, 0x4F +next_char: + mov al, byte[edi] + test al, al + jz done + + mov word [ebx], ax + add ebx, 2 + inc edi + jmp next_char + +done: + pop ebx + pop eax + ret + ;------------------------------------------------------------------------------ ; Load GDT and flush CPU caches ;------------------------------------------------------------------------------ _rt0_load_gdt: + push eax + push ebx + ; Go code uses the GS register to access the TLS. Set the base address ; for the GS descriptor to point to our tls0 table mov eax, tls0 @@ -84,6 +137,8 @@ update_descriptors: mov ax, GS_SEG mov gs, ax + pop ebx + pop eax ret ;------------------------------------------------------------------------------ diff --git a/boot.go b/boot.go deleted file mode 100644 index fb85268..0000000 --- a/boot.go +++ /dev/null @@ -1,18 +0,0 @@ -package main - -import "github.com/achilleasa/gopher-os/kernel" - -// main is the only Go symbol that is visible (exported) from the rt0 initialization -// code. This function works as a trampoline for calling the actual kernel entrypoint -// (kernel.Kmain) and its intentionally defined to prevent the Go compiler from -// optimizing away the actual kernel code as its not aware of the presence of the -// rt0 code. -// -// The main function is invoked by the rt0 assembly code after setting up the GDT -// and setting up a a minimal g0 struct that allows Go code using the 4K stack -// allocated by the assembly code. -// -// main is not expected to return. If it does, the rt0 code will halt the CPU. -func main() { - kernel.Kmain() -} diff --git a/kernel/kmain.go b/kernel/kmain.go index 85c1df0..1943eff 100644 --- a/kernel/kmain.go +++ b/kernel/kmain.go @@ -1,5 +1,22 @@ package kernel -// Kmain is invoked by the boot.go and implements the actual kernel entrypoint. -func Kmain() { +import ( + _ "unsafe" // required for go:linkname + + "github.com/achilleasa/gopher-os/kernel/multiboot" +) + +// Kmain is the only Go symbol that is visible (exported) from the rt0 initialization +// code. This function is invoked by the rt0 assembly code after setting up the GDT +// and setting up a a minimal g0 struct that allows Go code using the 4K stack +// allocated by the assembly code. +// +// The rt0 code passes the address of the multiboot info payload provided by the +// bootloader. +// +// Kmain is not expected to return. If it does, the rt0 code will halt the CPU. +// +//go:noinline +func Kmain(multibootInfoPtr uint32) { + multiboot.SetInfoPtr(uintptr(multibootInfoPtr)) } diff --git a/kernel/multiboot/multiboot.go b/kernel/multiboot/multiboot.go new file mode 100644 index 0000000..a7336fb --- /dev/null +++ b/kernel/multiboot/multiboot.go @@ -0,0 +1,192 @@ +package multiboot + +import "unsafe" + +type tagType uint32 + +const ( + tagMbSectionEnd tagType = iota + tagBootCmdLine + tagBootLoaderName + tagModules + tagBasicMemoryInfo + tagBiosBootDevice + tagMemoryMap + tagVbeInfo + tagFramebufferInfo + tagElfSymbols + tagApmTable +) + +// info describes the multiboot info section header. +type info struct { + // Total size of multiboot info section. + totalSize uint32 + + // Always set to zero; reserved for future use + reserved uint32 +} + +// tagHeader describes the header the preceedes each tag. +type tagHeader struct { + // The type of the tag + tagType tagType + + // The size of the tag including the header but *not* including any + // padding. According to the spec, each tag starts at a 8-byte aligned + // address. + size uint32 +} + +// mmapHeader describes the header for a memory map specification. +type mmapHeader struct { + // The size of each entry. + entrySize uint32 + + // The version of the entries that follow. + entryVersion uint32 +} + +// FramebufferType defines the type of the initialized framebuffer. +type FramebufferType uint8 + +const ( + // FrameBufferTypeIndexed specifies a 256-color palette. + FrameBufferTypeIndexed FramebufferType = iota + + // FramebufferTypeRGB specifies direct RGB mode. + FramebufferTypeRGB + + // FramebufferTypeEGA specifies EGA text mode. + FramebufferTypeEGA +) + +// FramebufferInfo provides information about the initialized framebuffer. +type FramebufferInfo struct { + // The framebuffer physical address. + PhysAddr uint64 + + // Row pitch in bytes. + Pitch uint32 + + // Width and height in pixels (or characters if Type = FramebufferTypeEGA) + Width, Height uint32 + + // Bits per pixel (non EGA modes only). + Bpp uint8 + + // Framebuffer type. + Type FramebufferType +} + +// MemoryEntryType defines the type of a MemoryMapEntry. +type MemoryEntryType uint32 + +const ( + // MemAvailable indicates that the memory region is available for use. + MemAvailable MemoryEntryType = iota + 1 + + // MemReserved indicates that the memory region is not available for use. + MemReserved + + // MemAcpiReclaimable indicates a memory region that holds ACPI info that + // can be reused by the OS. + MemAcpiReclaimable + + // MemNvs indicates memory that must be preserved when hibernating. + MemNvs + + // Any value >= memUnknown will be mapped to MemReserved. + memUnknown +) + +/// MemoryMapEntry describes a memory region entry, namely its physical address, +// its length and its type. +type MemoryMapEntry struct { + // The physical address for this memory region. + PhysAddress uint64 + + // The length of the memory region. + Length uint64 + + // The type of this entry. + Type MemoryEntryType +} + +var ( + infoData uintptr +) + +// MemRegionVisitor defies a visitor function that gets invoked by VisitMemRegions +// for each memory region provided by the boot loader. +type MemRegionVisitor func(entry *MemoryMapEntry) + +// SetInfoPtr updates the internal multiboot information pointer to the given +// value. This function must be invoked before invoking any other function +// exported by this package. +func SetInfoPtr(ptr uintptr) { + infoData = ptr +} + +// VisitMemRegions will invoke the supplied visitor for each memory region that +// is defined by the multiboot info data that we received from the bootloader. +func VisitMemRegions(visitor MemRegionVisitor) { + curPtr, size := findTagByType(tagMemoryMap) + if size == 0 { + return + } + + // curPtr points to the memory map header (2 dwords long) + ptrMapHeader := (*mmapHeader)(unsafe.Pointer(curPtr)) + endPtr := curPtr + uintptr(size) + curPtr += 8 + + var entry *MemoryMapEntry + for curPtr != endPtr { + entry = (*MemoryMapEntry)(unsafe.Pointer(curPtr)) + + // Mark unknown entry types as reserved + if entry.Type == 0 || entry.Type > memUnknown { + entry.Type = MemReserved + } + + visitor(entry) + + curPtr += uintptr(ptrMapHeader.entrySize) + } +} + +// GetFramebufferInfo returns information about the framebuffer initialized by the +// bootloader. This function returns nil if no framebuffer info is available. +func GetFramebufferInfo() *FramebufferInfo { + var info *FramebufferInfo + + curPtr, size := findTagByType(tagFramebufferInfo) + if size != 0 { + info = (*FramebufferInfo)(unsafe.Pointer(curPtr)) + } + + return info +} + +// findTagByType scans the multiboot info data looking for the start of of the +// specified type. It returns a pointer to the tag contents start offset and +// the content length exluding the tag header. +// +// If the tag is not present in the multiboot info, findTagSection will return +// back (0,0). +func findTagByType(tagType tagType) (uintptr, uint32) { + var ptrTagHeader *tagHeader + + curPtr := infoData + 8 + for ptrTagHeader = (*tagHeader)(unsafe.Pointer(curPtr)); ptrTagHeader.tagType != tagMbSectionEnd; ptrTagHeader = (*tagHeader)(unsafe.Pointer(curPtr)) { + if ptrTagHeader.tagType == tagType { + return curPtr + 8, ptrTagHeader.size - 8 + } + + // Tags are aligned at 8-byte aligned addresses + curPtr += uintptr(int32(ptrTagHeader.size+7) & ^7) + } + + return 0, 0 +} diff --git a/kernel/multiboot/multiboot_test.go b/kernel/multiboot/multiboot_test.go new file mode 100644 index 0000000..62193b3 --- /dev/null +++ b/kernel/multiboot/multiboot_test.go @@ -0,0 +1,215 @@ +package multiboot + +import ( + "testing" + "unsafe" +) + +func TestFindTagByType(t *testing.T) { + specs := []struct { + tagType tagType + expSize uint32 + }{ + {tagBootCmdLine, 1}, + {tagBootLoaderName, 27}, + {tagBasicMemoryInfo, 8}, + {tagBiosBootDevice, 12}, + {tagMemoryMap, 152}, + {tagFramebufferInfo, 24}, + {tagElfSymbols, 972}, + {tagApmTable, 20}, + } + + SetInfoPtr(uintptr(unsafe.Pointer(&multibootInfoTestData[0]))) + + for specIndex, spec := range specs { + _, size := findTagByType(spec.tagType) + + if size != spec.expSize { + t.Errorf("[spec %d] expected tag size for tag type %d to be %d; got %d", specIndex, spec.tagType, spec.expSize, size) + + } + } +} + +func TestFindTagByTypeWithMissingTag(t *testing.T) { + SetInfoPtr(uintptr(unsafe.Pointer(&multibootInfoTestData[0]))) + + if offset, size := findTagByType(tagModules); offset != 0 || size != 0 { + t.Fatalf("expected findTagByType to return (0,0) for missing tag; got (%d, %d)", offset, size) + } +} + +func TestVisitMemRegion(t *testing.T) { + specs := []struct { + expPhys uint64 + expLen uint64 + expType MemoryEntryType + }{ + // This region type is actually MemAvailable but we patch it to + // a bogus value to test whether it gets flagged as reserved + {0, 654336, MemReserved}, + {654336, 1024, MemReserved}, + {983040, 65536, MemReserved}, + {1048576, 133038080, MemAvailable}, + {134086656, 131072, MemReserved}, + {4294705152, 262144, MemReserved}, + } + + var visitCount int + + SetInfoPtr(uintptr(unsafe.Pointer(&emptyInfoData[0]))) + VisitMemRegions(func(_ *MemoryMapEntry) { + visitCount++ + }) + + if visitCount != 0 { + t.Fatal("expected visitor not to be invoked when no memory map tag is present") + } + + // Set a bogus type for the first entry in the map + SetInfoPtr(uintptr(unsafe.Pointer(&multibootInfoTestData[0]))) + multibootInfoTestData[128] = 0xFF + + VisitMemRegions(func(entry *MemoryMapEntry) { + if entry.PhysAddress != specs[visitCount].expPhys { + t.Errorf("[visit %d] expected physical address to be %x; got %x", visitCount, specs[visitCount].expPhys, entry.PhysAddress) + } + if entry.Length != specs[visitCount].expLen { + t.Errorf("[visit %d] expected region len to be %x; got %x", visitCount, specs[visitCount].expLen, entry.Length) + } + if entry.Type != specs[visitCount].expType { + t.Errorf("[visit %d] expected region type to be %d; got %d", visitCount, specs[visitCount].expType, entry.Type) + } + visitCount++ + }) + + if visitCount != len(specs) { + t.Errorf("expected the visitor func to be invoked %d times; got %d", len(specs), visitCount) + } +} + +func TestGetFramebufferInfo(t *testing.T) { + SetInfoPtr(uintptr(unsafe.Pointer(&emptyInfoData[0]))) + + if GetFramebufferInfo() != nil { + t.Fatalf("expected GetFramebufferInfo() to return nil when no framebuffer tag is present") + } + + SetInfoPtr(uintptr(unsafe.Pointer(&multibootInfoTestData[0]))) + fbInfo := GetFramebufferInfo() + + if fbInfo.Type != FramebufferTypeEGA { + t.Errorf("expected framebuffer type to be %d; got %d", FramebufferTypeEGA, fbInfo.Type) + } + + if fbInfo.PhysAddr != 0xB8000 { + t.Errorf("expected physical address for EGA text mode to be 0xB8000; got %x", fbInfo.PhysAddr) + } + + if fbInfo.Width != 80 || fbInfo.Height != 25 { + t.Errorf("expected framebuffer dimensions to be 80x25; got %dx%d", fbInfo.Width, fbInfo.Height) + } + + if fbInfo.Pitch != 160 { + t.Errorf("expected pitch to be 160; got %x", fbInfo.Pitch) + } +} + +var ( + emptyInfoData = []byte{ + 0, 0, 0, 0, // size + 0, 0, 0, 0, // reserved + 0, 0, 0, 0, // tag with type zero and length zero + 0, 0, 0, 0, + } + + // A dump of multiboot data when running under qemu. + multibootInfoTestData = []byte{ + 72, 5, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 9, 0, 0, 0, + 0, 171, 253, 7, 118, 119, 123, 0, 2, 0, 0, 0, 35, 0, 0, 0, + 71, 82, 85, 66, 32, 50, 46, 48, 50, 126, 98, 101, 116, 97, 50, 45, + 57, 117, 98, 117, 110, 116, 117, 49, 46, 54, 0, 0, 0, 0, 0, 0, + 10, 0, 0, 0, 28, 0, 0, 0, 2, 1, 0, 240, 4, 213, 0, 0, + 0, 240, 0, 240, 3, 0, 240, 255, 240, 255, 240, 255, 0, 0, 0, 0, + 6, 0, 0, 0, 160, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 252, 9, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 252, 9, 0, 0, 0, 0, 0, + 0, 4, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, + 0, 0, 238, 7, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 254, 7, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252, 255, 0, 0, 0, 0, + 0, 0, 4, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, + 9, 0, 0, 0, 212, 3, 0, 0, 24, 0, 0, 0, 40, 0, 0, 0, + 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 0, 0, 0, + 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 16, 0, 0, 16, 0, 0, + 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, + 0, 0, 0, 0, 38, 0, 0, 0, 1, 0, 0, 0, 6, 0, 0, 0, + 0, 16, 16, 0, 0, 32, 0, 0, 135, 26, 4, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 44, 0, 0, 0, + 1, 0, 0, 0, 2, 0, 0, 0, 0, 48, 20, 0, 0, 64, 4, 0, + 194, 167, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, + 0, 0, 0, 0, 52, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, + 224, 215, 21, 0, 224, 231, 5, 0, 176, 6, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, + 1, 0, 0, 0, 2, 0, 0, 0, 144, 222, 21, 0, 144, 238, 5, 0, + 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, + 0, 0, 0, 0, 72, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, + 160, 222, 21, 0, 160, 238, 5, 0, 119, 23, 2, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 83, 0, 0, 0, + 7, 0, 0, 0, 2, 0, 0, 0, 32, 246, 23, 0, 32, 6, 8, 0, + 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, + 0, 0, 0, 0, 100, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, + 0, 0, 24, 0, 0, 16, 8, 0, 204, 5, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 106, 0, 0, 0, + 1, 0, 0, 0, 3, 0, 0, 0, 224, 5, 24, 0, 224, 21, 8, 0, + 178, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, + 0, 0, 0, 0, 117, 0, 0, 0, 8, 0, 0, 0, 3, 4, 0, 0, + 148, 15, 24, 0, 146, 31, 8, 0, 4, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 123, 0, 0, 0, + 8, 0, 0, 0, 3, 0, 0, 0, 0, 16, 24, 0, 146, 31, 8, 0, + 176, 61, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, + 0, 0, 0, 0, 128, 0, 0, 0, 8, 0, 0, 0, 3, 0, 0, 0, + 192, 77, 25, 0, 146, 31, 8, 0, 32, 56, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 138, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 224, 133, 25, 0, 146, 31, 8, 0, + 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 153, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 32, 134, 25, 0, 210, 31, 8, 0, 129, 26, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 169, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 161, 160, 25, 0, 83, 58, 8, 0, + 2, 201, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 181, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 163, 105, 27, 0, 85, 3, 10, 0, 25, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 195, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 188, 106, 27, 0, 110, 4, 10, 0, + 67, 153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 207, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 4, 28, 0, 184, 157, 10, 0, 252, 112, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 220, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 252, 116, 28, 0, 180, 14, 11, 0, + 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 231, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 12, 117, 28, 0, 196, 14, 11, 0, 239, 79, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, + 3, 0, 0, 0, 0, 0, 0, 0, 251, 196, 28, 0, 179, 94, 11, 0, + 247, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, + 244, 197, 28, 0, 108, 99, 11, 0, 80, 77, 0, 0, 23, 0, 0, 0, + 210, 4, 0, 0, 4, 0, 0, 0, 16, 0, 0, 0, 9, 0, 0, 0, + 3, 0, 0, 0, 0, 0, 0, 0, 68, 19, 29, 0, 188, 176, 11, 0, + 107, 104, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 16, 0, 0, 0, + 127, 2, 0, 0, 128, 251, 1, 0, 5, 0, 0, 0, 20, 0, 0, 0, + 224, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, + 8, 0, 0, 0, 32, 0, 0, 0, 0, 128, 11, 0, 0, 0, 0, 0, + 160, 0, 0, 0, 80, 0, 0, 0, 25, 0, 0, 0, 16, 2, 0, 0, + 14, 0, 0, 0, 28, 0, 0, 0, 82, 83, 68, 32, 80, 84, 82, 32, + 89, 66, 79, 67, 72, 83, 32, 0, 220, 24, 254, 7, 0, 0, 0, 0, + 0, 0, 0, 0, 8, 0, 0, 0, + } +) diff --git a/stub.go b/stub.go new file mode 100644 index 0000000..80ae494 --- /dev/null +++ b/stub.go @@ -0,0 +1,15 @@ +package main + +import "github.com/achilleasa/gopher-os/kernel" + +var multibootInfoPtr uint32 + +// main makes a dummy call to the actual kernel main entrypoint function. It +// is intentionally defined to prevent the Go compiler from optimizing away the +// real kernel code. +// +// A global variable is passed as an argument to Kmain to prevent the compiler +// from inlining the actual call and removing Kmain from the generated .o file. +func main() { + kernel.Kmain(multibootInfoPtr) +}