From 8ac2ba82cc271bc55732a524f17e9de867a5a4a5 Mon Sep 17 00:00:00 2001 From: Achilleas Anagnostopoulos Date: Sat, 8 Jul 2017 16:04:39 +0100 Subject: [PATCH] Implement function for mapping a contiguous physical memory region The MapRegion function can be used by device drivers to ensure that they can access memory mapped by the various devices. --- src/gopheros/kernel/mem/vmm/map.go | 25 +++++++++ src/gopheros/kernel/mem/vmm/map_test.go | 67 +++++++++++++++++++++++++ 2 files changed, 92 insertions(+) diff --git a/src/gopheros/kernel/mem/vmm/map.go b/src/gopheros/kernel/mem/vmm/map.go index 1edaa5a..fe56f3b 100644 --- a/src/gopheros/kernel/mem/vmm/map.go +++ b/src/gopheros/kernel/mem/vmm/map.go @@ -47,6 +47,8 @@ var ( // which will cause a fault if called in user-mode. flushTLBEntryFn = cpu.FlushTLBEntry + earlyReserveRegionFn = EarlyReserveRegion + errNoHugePageSupport = &kernel.Error{Module: "vmm", Message: "huge pages are not supported"} errAttemptToRWMapReservedFrame = &kernel.Error{Module: "vmm", Message: "reserved blank frame cannot be mapped with a RW flag"} ) @@ -105,6 +107,29 @@ func Map(page Page, frame pmm.Frame, flags PageTableEntryFlag) *kernel.Error { return err } +// MapRegion establishes a mapping to the physical memory region which starts +// at the given frame and ends at frame + pages(size). The size argument is +// always rounded up to the nearest page boundary. MapRegion reserves the next +// available region in the active virtual address space, establishes the +// mapping and returns back the Page that corresponds to the region start. +func MapRegion(frame pmm.Frame, size mem.Size, flags PageTableEntryFlag) (Page, *kernel.Error) { + // Reserve next free block in the address space + size = (size + (mem.PageSize - 1)) & ^(mem.PageSize - 1) + startPage, err := earlyReserveRegionFn(size) + if err != nil { + return 0, err + } + + pageCount := size >> mem.PageShift + for page := PageFromAddress(startPage); pageCount > 0; pageCount, page, frame = pageCount-1, page+1, frame+1 { + if err := mapFn(page, frame, flags); err != nil { + return 0, err + } + } + + return PageFromAddress(startPage), nil +} + // MapTemporary establishes a temporary RW mapping of a physical memory frame // to a fixed virtual address overwriting any previous mapping. The temporary // mapping mechanism is primarily used by the kernel to access and initialize diff --git a/src/gopheros/kernel/mem/vmm/map_test.go b/src/gopheros/kernel/mem/vmm/map_test.go index 70116c1..7a0d0f8 100644 --- a/src/gopheros/kernel/mem/vmm/map_test.go +++ b/src/gopheros/kernel/mem/vmm/map_test.go @@ -97,6 +97,73 @@ func TestMapTemporaryAmd64(t *testing.T) { } } +func TestMapRegion(t *testing.T) { + defer func() { + mapFn = Map + earlyReserveRegionFn = EarlyReserveRegion + }() + + t.Run("success", func(t *testing.T) { + mapCallCount := 0 + mapFn = func(_ Page, _ pmm.Frame, flags PageTableEntryFlag) *kernel.Error { + mapCallCount++ + return nil + } + + earlyReserveRegionCallCount := 0 + earlyReserveRegionFn = func(_ mem.Size) (uintptr, *kernel.Error) { + earlyReserveRegionCallCount++ + return 0xf00, nil + } + + if _, err := MapRegion(pmm.Frame(0xdf0000), 4097, FlagPresent|FlagRW); err != nil { + t.Fatal(err) + } + + if exp := 2; mapCallCount != exp { + t.Errorf("expected Map to be called %d time(s); got %d", exp, mapCallCount) + } + + if exp := 1; earlyReserveRegionCallCount != exp { + t.Errorf("expected EarlyReserveRegion to be called %d time(s); got %d", exp, earlyReserveRegionCallCount) + } + }) + + t.Run("EarlyReserveRegion fails", func(t *testing.T) { + expErr := &kernel.Error{Module: "test", Message: "out of address space"} + + earlyReserveRegionFn = func(_ mem.Size) (uintptr, *kernel.Error) { + return 0, expErr + } + + if _, err := MapRegion(pmm.Frame(0xdf0000), 128000, FlagPresent|FlagRW); err != expErr { + t.Fatalf("expected error: %v; got %v", expErr, err) + } + }) + + t.Run("Map fails", func(t *testing.T) { + expErr := &kernel.Error{Module: "test", Message: "map failed"} + + earlyReserveRegionCallCount := 0 + earlyReserveRegionFn = func(_ mem.Size) (uintptr, *kernel.Error) { + earlyReserveRegionCallCount++ + return 0xf00, nil + } + + mapFn = func(_ Page, _ pmm.Frame, flags PageTableEntryFlag) *kernel.Error { + return expErr + } + + if _, err := MapRegion(pmm.Frame(0xdf0000), 128000, FlagPresent|FlagRW); err != expErr { + t.Fatalf("expected error: %v; got %v", expErr, err) + } + + if exp := 1; earlyReserveRegionCallCount != exp { + t.Errorf("expected EarlyReserveRegion to be called %d time(s); got %d", exp, earlyReserveRegionCallCount) + } + }) +} + func TestMapTemporaryErrorsAmd64(t *testing.T) { if runtime.GOARCH != "amd64" { t.Skip("test requires amd64 runtime; skipping")