mirror of
https://github.com/taigrr/gopher-os
synced 2025-01-18 04:43:13 -08:00
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
148 lines
3.7 KiB
Go
148 lines
3.7 KiB
Go
package multiboot
|
|
|
|
import "unsafe"
|
|
|
|
type tagType uint32
|
|
|
|
const (
|
|
tagMbSectionEnd tagType = iota
|
|
tagBootCmdLine
|
|
tagBootLoaderName
|
|
tagModules
|
|
tagBasicMemoryInfo
|
|
tagBiosBootDevice
|
|
tagMemoryMap
|
|
tagVbeInfo
|
|
tagFramebufferInfo
|
|
tagElfSymbols
|
|
tagApmTable
|
|
)
|
|
|
|
// info describes the multiboot info section header.
|
|
type info struct {
|
|
// Total size of multiboot info section.
|
|
totalSize uint32
|
|
|
|
// Always set to zero; reserved for future use
|
|
reserved uint32
|
|
}
|
|
|
|
// tagHeader describes the header the preceedes each tag.
|
|
type tagHeader struct {
|
|
// The type of the tag
|
|
tagType tagType
|
|
|
|
// The size of the tag including the header but *not* including any
|
|
// padding. According to the spec, each tag starts at a 8-byte aligned
|
|
// address.
|
|
size uint32
|
|
}
|
|
|
|
// mmapHeader describes the header for a memory map specification.
|
|
type mmapHeader struct {
|
|
// The size of each entry.
|
|
entrySize uint32
|
|
|
|
// The version of the entries that follow.
|
|
entryVersion uint32
|
|
}
|
|
|
|
// MemoryEntryType defines the type of a MemoryMapEntry.
|
|
type MemoryEntryType uint32
|
|
|
|
const (
|
|
// MemAvailable indicates that the memory region is available for use.
|
|
MemAvailable MemoryEntryType = iota + 1
|
|
|
|
// MemReserved indicates that the memory region is not available for use.
|
|
MemReserved
|
|
|
|
// MemAcpiReclaimable indicates a memory region that holds ACPI info that
|
|
// can be reused by the OS.
|
|
MemAcpiReclaimable
|
|
|
|
// MemNvs indicates memory that must be preserved when hibernating.
|
|
MemNvs
|
|
|
|
// Any value >= memUnknown will be mapped to MemReserved.
|
|
memUnknown
|
|
)
|
|
|
|
/// MemoryMapEntry describes a memory region entry, namely its physical address,
|
|
// its length and its type.
|
|
type MemoryMapEntry struct {
|
|
// The physical address for this memory region.
|
|
PhysAddress uint64
|
|
|
|
// The length of the memory region.
|
|
Length uint64
|
|
|
|
// The type of this entry.
|
|
Type MemoryEntryType
|
|
}
|
|
|
|
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.
|
|
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.
|
|
//
|
|
// If the tag is not present in the multiboot info, findTagSection will return
|
|
// back (0,0).
|
|
func findTagByType(tagType tagType) (uintptr, uint32) {
|
|
var ptrTagHeader *tagHeader
|
|
|
|
curPtr := infoData + 8
|
|
for ptrTagHeader = (*tagHeader)(unsafe.Pointer(curPtr)); ptrTagHeader.tagType != tagMbSectionEnd; ptrTagHeader = (*tagHeader)(unsafe.Pointer(curPtr)) {
|
|
if ptrTagHeader.tagType == tagType {
|
|
return curPtr + 8, ptrTagHeader.size - 8
|
|
}
|
|
|
|
// Tags are aligned at 8-byte aligned addresses
|
|
curPtr += uintptr(int32(ptrTagHeader.size+7) & ^7)
|
|
}
|
|
|
|
return 0, 0
|
|
}
|