From 558cbf5f17c42e30c081d8a7144196bc863b81ef Mon Sep 17 00:00:00 2001 From: Achilleas Anagnostopoulos Date: Tue, 28 Mar 2017 08:22:46 +0100 Subject: [PATCH] Implement visitor for examining reported memory map entries Since the actual size of each memory entry is not known in advance (bootloaders may append additional information to it) but needs to be queried off the memory map tag header we cannot reserve space for it as no memory allocation is yet available. Instead, a visitor pattern was implemented to allow the memory manager initialization block to easily mark the appropriate pages as reserved --- kernel/multiboot/multiboot.go | 32 +++++++++++++++++ kernel/multiboot/multiboot_test.go | 56 ++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) diff --git a/kernel/multiboot/multiboot.go b/kernel/multiboot/multiboot.go index 84b84eb..977a40a 100644 --- a/kernel/multiboot/multiboot.go +++ b/kernel/multiboot/multiboot.go @@ -85,6 +85,10 @@ var ( infoData uintptr ) +// MemRegionVisitor defies a visitor function that gets invoked by VisitMemRegions +// for each memory region defined 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. @@ -92,6 +96,34 @@ 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) + } +} + // 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. diff --git a/kernel/multiboot/multiboot_test.go b/kernel/multiboot/multiboot_test.go index f97991a..7607e1c 100644 --- a/kernel/multiboot/multiboot_test.go +++ b/kernel/multiboot/multiboot_test.go @@ -40,7 +40,63 @@ func TestFindTagByTypeWithMissingTag(t *testing.T) { } } +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) + } +} + 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,