diff --git a/src/gopheros/kernel/mem/vmm/map.go b/src/gopheros/kernel/mem/vmm/map.go index fe56f3b..ddc4b80 100644 --- a/src/gopheros/kernel/mem/vmm/map.go +++ b/src/gopheros/kernel/mem/vmm/map.go @@ -130,6 +130,24 @@ func MapRegion(frame pmm.Frame, size mem.Size, flags PageTableEntryFlag) (Page, return PageFromAddress(startPage), nil } +// IdentityMapRegion establishes an identity 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. +// IdentityMapRegion returns back the Page that corresponds to the region +// start. +func IdentityMapRegion(startFrame pmm.Frame, size mem.Size, flags PageTableEntryFlag) (Page, *kernel.Error) { + startPage := Page(startFrame) + pageCount := Page(((size + (mem.PageSize - 1)) & ^(mem.PageSize - 1)) >> mem.PageShift) + + for curPage := startPage; curPage < startPage+pageCount; curPage++ { + if err := mapFn(curPage, pmm.Frame(curPage), flags); err != nil { + return 0, err + } + } + + return 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 7a0d0f8..e5a68e9 100644 --- a/src/gopheros/kernel/mem/vmm/map_test.go +++ b/src/gopheros/kernel/mem/vmm/map_test.go @@ -164,6 +164,40 @@ func TestMapRegion(t *testing.T) { }) } +func TestIdentityMapRegion(t *testing.T) { + defer func() { + mapFn = Map + }() + + t.Run("success", func(t *testing.T) { + mapCallCount := 0 + mapFn = func(_ Page, _ pmm.Frame, flags PageTableEntryFlag) *kernel.Error { + mapCallCount++ + return nil + } + + if _, err := IdentityMapRegion(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) + } + }) + + t.Run("Map fails", func(t *testing.T) { + expErr := &kernel.Error{Module: "test", Message: "map failed"} + + mapFn = func(_ Page, _ pmm.Frame, flags PageTableEntryFlag) *kernel.Error { + return expErr + } + + if _, err := IdentityMapRegion(pmm.Frame(0xdf0000), 128000, FlagPresent|FlagRW); err != expErr { + t.Fatalf("expected error: %v; got %v", expErr, err) + } + }) +} + func TestMapTemporaryErrorsAmd64(t *testing.T) { if runtime.GOARCH != "amd64" { t.Skip("test requires amd64 runtime; skipping") diff --git a/src/gopheros/kernel/mem/vmm/translate.go b/src/gopheros/kernel/mem/vmm/translate.go index 4493f32..e1f07cf 100644 --- a/src/gopheros/kernel/mem/vmm/translate.go +++ b/src/gopheros/kernel/mem/vmm/translate.go @@ -13,7 +13,12 @@ func Translate(virtAddr uintptr) (uintptr, *kernel.Error) { // Calculate the physical address by taking the physical frame address and // appending the offset from the virtual address - physAddr := pte.Frame().Address() + (virtAddr & ((1 << pageLevelShifts[pageLevels-1]) - 1)) - + physAddr := pte.Frame().Address() + PageOffset(virtAddr) return physAddr, nil } + +// PageOffset returns the offset within the page specified by a virtual +// address. +func PageOffset(virtAddr uintptr) uintptr { + return (virtAddr & ((1 << pageLevelShifts[pageLevels-1]) - 1)) +}