diff --git a/Makefile b/Makefile index 11f945b..69d64a8 100644 --- a/Makefile +++ b/Makefile @@ -52,7 +52,7 @@ go.o: @# 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` \ + --add-symbol kernel.Kmain=.text:0x`nm $(BUILD_DIR)/go.o | grep "kmain.Kmain$$" | cut -d' ' -f1` \ $(BUILD_DIR)/go.o $(BUILD_DIR)/go.o binutils_version_check: diff --git a/kernel/error.go b/kernel/error.go new file mode 100644 index 0000000..c3accfd --- /dev/null +++ b/kernel/error.go @@ -0,0 +1,18 @@ +package kernel + +// Error describes a kernel kerror. All kernel errors must be defined as global +// variables that are pointers to the Error structure. This requirement stems +// from the fact that the Go allocator is not available to us so we cannot use +// errors.New. +type Error struct { + // The module where the error occurred. + Module string + + // The error message + Message string +} + +// Error implements the error interface. +func (e *Error) Error() string { + return e.Message +} diff --git a/kernel/error_test.go b/kernel/error_test.go new file mode 100644 index 0000000..64d1b43 --- /dev/null +++ b/kernel/error_test.go @@ -0,0 +1,14 @@ +package kernel + +import "testing" + +func TestKernelError(t *testing.T) { + err := &Error{ + Module: "foo", + Message: "error message", + } + + if err.Error() != err.Message { + t.Fatalf("expected to err.Error() to return %q; got %q", err.Message, err.Error()) + } +} diff --git a/kernel/kmain.go b/kernel/kmain/kmain.go similarity index 98% rename from kernel/kmain.go rename to kernel/kmain/kmain.go index b2a4ae6..9ee1f8f 100644 --- a/kernel/kmain.go +++ b/kernel/kmain/kmain.go @@ -1,4 +1,4 @@ -package kernel +package kmain import ( "github.com/achilleasa/gopher-os/kernel/hal" diff --git a/kernel/mem/constants_amd64.go b/kernel/mem/constants_amd64.go index aea2911..068b23e 100644 --- a/kernel/mem/constants_amd64.go +++ b/kernel/mem/constants_amd64.go @@ -3,6 +3,10 @@ package mem const ( + // PointerShift is equal to log2(unsafe.Sizeof(uintptr)). The pointer + // size for this architecture is defined as (1 << PointerShift). + PointerShift = 3 + // PageShift is equal to log2(PageSize). This constant is used when // we need to convert a physical address to a page number (shift right by PageShift) // and vice-versa. diff --git a/kernel/mem/pmm/bootmem_allocator.go b/kernel/mem/pmm/bootmem_allocator.go index 93f7020..fe3af20 100644 --- a/kernel/mem/pmm/bootmem_allocator.go +++ b/kernel/mem/pmm/bootmem_allocator.go @@ -1,6 +1,7 @@ package pmm import ( + "github.com/achilleasa/gopher-os/kernel" "github.com/achilleasa/gopher-os/kernel/hal/multiboot" "github.com/achilleasa/gopher-os/kernel/kfmt/early" "github.com/achilleasa/gopher-os/kernel/mem" @@ -11,6 +12,9 @@ var ( // which is used to bootstrap the kernel before initializing a more // advanced memory allocator. EarlyAllocator BootMemAllocator + + errBootAllocUnsupportedPageSize = &kernel.Error{Module: "pmm.BootMemAllocator", Message: "allocator only support allocation requests of order(0)"} + errBootAllocOutOfMemory = &kernel.Error{Module: "pmm.BootMemAllocator", Message: "out of memory"} ) // BootMemAllocator implements a rudimentary physical memory allocator which is used @@ -56,18 +60,13 @@ func (alloc *BootMemAllocator) Init() { } // AllocFrame scans the system memory regions reported by the bootloader and -// reseves the next available free frame. AllocFrame returns false if no more -// memory can be allocated. +// reserves the next available free frame. // -// The allocator only supports allocating blocks equal to the page size. -// Requests for a page order > 0 will cause the allocator to return false. -// -// The use of a bool return value is intentional; if this method returned an -// error then the compiler would call runtime.convT2I which in turn invokes the -// yet uninitialized Go allocator. -func (alloc *BootMemAllocator) AllocFrame(order mem.PageOrder) (Frame, bool) { +// AllocFrame returns an error if no more memory can be allocated or when the +// requested page order is > 0. +func (alloc *BootMemAllocator) AllocFrame(order mem.PageOrder) (Frame, *kernel.Error) { if order > 0 { - return InvalidFrame, false + return InvalidFrame, errBootAllocUnsupportedPageSize } var ( @@ -103,11 +102,11 @@ func (alloc *BootMemAllocator) AllocFrame(order mem.PageOrder) (Frame, bool) { }) if foundPageIndex == -1 { - return InvalidFrame, false + return InvalidFrame, errBootAllocOutOfMemory } alloc.allocCount++ alloc.lastAllocIndex = foundPageIndex - return Frame(foundPageIndex), true + return Frame(foundPageIndex), nil } diff --git a/kernel/mem/pmm/bootmem_allocator_test.go b/kernel/mem/pmm/bootmem_allocator_test.go index 8632b66..52ccd30 100644 --- a/kernel/mem/pmm/bootmem_allocator_test.go +++ b/kernel/mem/pmm/bootmem_allocator_test.go @@ -36,9 +36,12 @@ func TestBootMemoryAllocator(t *testing.T) { allocFrameCount uint64 ) for alloc.Init(); ; allocFrameCount++ { - frame, ok := alloc.AllocFrame(mem.PageOrder(0)) - if !ok { - break + frame, err := alloc.AllocFrame(mem.PageOrder(0)) + if err != nil { + if err == errBootAllocOutOfMemory { + break + } + t.Fatalf("[frame %d] unexpected allocator error: %v", allocFrameCount, err) } expAddress := uintptr(uint64(alloc.lastAllocIndex) * uint64(mem.PageSize)) @@ -49,14 +52,6 @@ func TestBootMemoryAllocator(t *testing.T) { if !frame.IsValid() { t.Errorf("[frame %d] expected IsValid() to return true", allocFrameCount) } - - if got := frame.PageOrder(); got != mem.PageOrder(0) { - t.Errorf("[frame %d] expected allocated frame page order to be 0; got %d", allocFrameCount, got) - } - - if got := frame.Size(); got != mem.PageSize { - t.Errorf("[frame %d] expected allocated frame size to be %d; got %d", allocFrameCount, mem.PageSize, got) - } } if allocFrameCount != totalFreeFrames { @@ -64,8 +59,8 @@ func TestBootMemoryAllocator(t *testing.T) { } // This allocator only works with order(0) blocks - if frame, ok := alloc.AllocFrame(mem.PageOrder(1)); ok || frame.IsValid() { - t.Fatalf("expected allocator to return false and an invalid frame when requested to allocate a block with order > 0; got %t, %v", ok, frame) + if frame, err := alloc.AllocFrame(mem.PageOrder(1)); err != errBootAllocUnsupportedPageSize || frame.IsValid() { + t.Fatalf("expected allocator to return errBootAllocUnsupportedPageSize and an invalid frame when requested to allocate a block with order > 0; got %v, %v", err, frame) } } diff --git a/kernel/mem/pmm/frame.go b/kernel/mem/pmm/frame.go index 478406c..5b03dce 100644 --- a/kernel/mem/pmm/frame.go +++ b/kernel/mem/pmm/frame.go @@ -8,7 +8,7 @@ import ( ) // Frame describes a physical memory page index. -type Frame uint64 +type Frame uintptr const ( // InvalidFrame is returned by page allocators when @@ -25,14 +25,3 @@ func (f Frame) IsValid() bool { func (f Frame) Address() uintptr { return uintptr(f << mem.PageShift) } - -// PageOrder returns the page order of this frame. The page order is encoded in the -// 8 MSB of the frame number. -func (f Frame) PageOrder() mem.PageOrder { - return mem.PageOrder((f >> 56) & 0xFF) -} - -// Size returns the size of this frame. -func (f Frame) Size() mem.Size { - return mem.PageSize << ((f >> 56) & 0xFF) -} diff --git a/kernel/mem/pmm/frame_test.go b/kernel/mem/pmm/frame_test.go index 786e465..990794c 100644 --- a/kernel/mem/pmm/frame_test.go +++ b/kernel/mem/pmm/frame_test.go @@ -15,17 +15,9 @@ func TestFrameMethods(t *testing.T) { t.Errorf("[order %d] expected frame %d to be valid", order, frameIndex) } - if got := frame.PageOrder(); got != order { - t.Errorf("[order %d] expected frame (%d, index: %d) call to PageOrder() to return %d; got %d", order, frame, frameIndex, order, got) - } - if exp, got := uintptr(frameIndex<