mirror of
https://github.com/taigrr/gopher-os
synced 2025-01-18 04:43:13 -08:00
Merge pull request #4 from achilleasa/parse-multiboot-info
Add support for parsing multiboot info sections
This commit is contained in:
commit
04c3987219
20
Makefile
20
Makefile
@ -18,12 +18,15 @@ GOARCH := 386
|
|||||||
LD_FLAGS := -n -melf_i386 -T arch/$(ARCH)/script/linker.ld -static --no-ld-generated-unwind-info
|
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/
|
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_src_files := $(wildcard arch/$(ARCH)/asm/*.s)
|
||||||
asm_obj_files := $(patsubst arch/$(ARCH)/asm/%.s, $(BUILD_DIR)/arch/$(ARCH)/asm/%.o, $(asm_src_files))
|
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
|
$(kernel_target): $(asm_obj_files) go.o
|
||||||
@echo "[$(LD)] linking kernel-$(ARCH).bin"
|
@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
|
@# 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
|
@# asm entrypoint code needs to know the address to 'main.main' so we use
|
||||||
@# objcopy to make that symbol exportable
|
@# objcopy to make that symbol exportable. Since nasm does not support externs
|
||||||
@echo "[objcopy] export 'main.main' symbol in go.o"
|
@# with slashes we create a global symbol alias for kernel.Kmain
|
||||||
@objcopy --globalize-symbol='main.main' $(BUILD_DIR)/go.o $(BUILD_DIR)/go.o
|
@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
|
$(BUILD_DIR)/arch/$(ARCH)/asm/%.o: arch/$(ARCH)/asm/%.s
|
||||||
@mkdir -p $(shell dirname $@)
|
@mkdir -p $(shell dirname $@)
|
||||||
|
@ -24,17 +24,29 @@ section .text
|
|||||||
bits 32
|
bits 32
|
||||||
align 4
|
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
|
; Kernel arch-specific entry point
|
||||||
;
|
;
|
||||||
; The boot loader will jump to this symbol after setting up the CPU according
|
; The boot loader will jump to this symbol after setting up the CPU according
|
||||||
; to the multiboot standard. At this point:
|
; to the multiboot standard. At this point:
|
||||||
|
; - A20 is enabled
|
||||||
; - The CPU is using 32-bit protected mode
|
; - The CPU is using 32-bit protected mode
|
||||||
; - Interrupts are disabled
|
; - Interrupts are disabled
|
||||||
; - Paging is 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
|
global _rt0_entry
|
||||||
_rt0_entry:
|
_rt0_entry:
|
||||||
|
cmp eax, MULTIBOOT_MAGIC
|
||||||
|
jne unsupported_bootloader
|
||||||
|
|
||||||
; Initalize our stack by pointing ESP to the BSS-allocated stack. In x86,
|
; 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
|
; stack grows downwards so we need to point ESP to stack_top
|
||||||
mov esp, stack_top
|
mov esp, stack_top
|
||||||
@ -48,19 +60,60 @@ _rt0_entry:
|
|||||||
mov dword [g0_stack_lo], stack_bottom
|
mov dword [g0_stack_lo], stack_bottom
|
||||||
mov dword [g0_stackguard0], stack_bottom
|
mov dword [g0_stackguard0], stack_bottom
|
||||||
|
|
||||||
extern main.main
|
; push multiboot info ptr to the stack and call the kernel entrypoint
|
||||||
call main.main
|
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
|
; Main should never return; halt the CPU
|
||||||
|
halt:
|
||||||
cli
|
cli
|
||||||
hlt
|
hlt
|
||||||
|
|
||||||
|
unsupported_bootloader:
|
||||||
|
mov edi, err_unsupported_bootloader
|
||||||
|
call write_string
|
||||||
|
jmp halt
|
||||||
.end:
|
.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
|
; Load GDT and flush CPU caches
|
||||||
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
||||||
|
|
||||||
_rt0_load_gdt:
|
_rt0_load_gdt:
|
||||||
|
push eax
|
||||||
|
push ebx
|
||||||
|
|
||||||
; Go code uses the GS register to access the TLS. Set the base address
|
; Go code uses the GS register to access the TLS. Set the base address
|
||||||
; for the GS descriptor to point to our tls0 table
|
; for the GS descriptor to point to our tls0 table
|
||||||
mov eax, tls0
|
mov eax, tls0
|
||||||
@ -84,6 +137,8 @@ update_descriptors:
|
|||||||
mov ax, GS_SEG
|
mov ax, GS_SEG
|
||||||
mov gs, ax
|
mov gs, ax
|
||||||
|
|
||||||
|
pop ebx
|
||||||
|
pop eax
|
||||||
ret
|
ret
|
||||||
|
|
||||||
;------------------------------------------------------------------------------
|
;------------------------------------------------------------------------------
|
||||||
|
18
boot.go
18
boot.go
@ -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()
|
|
||||||
}
|
|
@ -1,5 +1,22 @@
|
|||||||
package kernel
|
package kernel
|
||||||
|
|
||||||
// Kmain is invoked by the boot.go and implements the actual kernel entrypoint.
|
import (
|
||||||
func Kmain() {
|
_ "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))
|
||||||
}
|
}
|
||||||
|
192
kernel/multiboot/multiboot.go
Normal file
192
kernel/multiboot/multiboot.go
Normal file
@ -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
|
||||||
|
}
|
215
kernel/multiboot/multiboot_test.go
Normal file
215
kernel/multiboot/multiboot_test.go
Normal file
@ -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,
|
||||||
|
}
|
||||||
|
)
|
15
stub.go
Normal file
15
stub.go
Normal file
@ -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)
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user