From d5a4c4340655708b2fe4193ed7881159fc871b82 Mon Sep 17 00:00:00 2001 From: Achilleas Anagnostopoulos Date: Wed, 31 May 2017 14:10:49 +0100 Subject: [PATCH 1/4] Define type for kernel error messages Since the Go memory allocator is not available to us we need to define our own error type. --- kernel/error.go | 18 ++++++++++++++++++ kernel/error_test.go | 14 ++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 kernel/error.go create mode 100644 kernel/error_test.go 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()) + } +} From ec6ce4b70e0b81b289b0a75507fe4e47c13360c1 Mon Sep 17 00:00:00 2001 From: Achilleas Anagnostopoulos Date: Wed, 31 May 2017 15:07:02 +0100 Subject: [PATCH 2/4] Move Kmain into its own package This allows us to keep the error definition in the kernel package without causing circular import errors --- Makefile | 2 +- kernel/{ => kmain}/kmain.go | 2 +- stub.go | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) rename kernel/{ => kmain}/kmain.go (98%) 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/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/stub.go b/stub.go index 28fe2b4..5263408 100644 --- a/stub.go +++ b/stub.go @@ -1,6 +1,6 @@ package main -import "github.com/achilleasa/gopher-os/kernel" +import "github.com/achilleasa/gopher-os/kernel/kmain" var multibootInfoPtr uintptr @@ -11,5 +11,5 @@ var multibootInfoPtr uintptr // 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) + kmain.Kmain(multibootInfoPtr) } From d7eb2547dd704dd43481b6744433f9d0862fdc3c Mon Sep 17 00:00:00 2001 From: Achilleas Anagnostopoulos Date: Wed, 31 May 2017 14:07:13 +0100 Subject: [PATCH 3/4] Change the Frame type to uintptr and remove Size/Order methods To keep the implementation portable, the Frame type had to be changed from uint64 to uintptr. Using uintptr ensures that the frame will always match the pointer size of the platform. --- kernel/mem/constants_amd64.go | 4 ++++ kernel/mem/pmm/bootmem_allocator_test.go | 8 -------- kernel/mem/pmm/frame.go | 13 +------------ kernel/mem/pmm/frame_test.go | 8 -------- 4 files changed, 5 insertions(+), 28 deletions(-) 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_test.go b/kernel/mem/pmm/bootmem_allocator_test.go index 8632b66..fc9d394 100644 --- a/kernel/mem/pmm/bootmem_allocator_test.go +++ b/kernel/mem/pmm/bootmem_allocator_test.go @@ -49,14 +49,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 { 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< Date: Wed, 31 May 2017 14:48:07 +0100 Subject: [PATCH 4/4] Change boot allocator signature so it returns a kernel error --- kernel/mem/pmm/bootmem_allocator.go | 23 +++++++++++------------ kernel/mem/pmm/bootmem_allocator_test.go | 13 ++++++++----- 2 files changed, 19 insertions(+), 17 deletions(-) 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 fc9d394..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)) @@ -56,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) } }