1
0
mirror of https://github.com/taigrr/gopher-os synced 2025-01-18 04:43:13 -08:00
Achilleas Anagnostopoulos a2d58f8949 Switch to a visitor-based no-alloc implementation for fetching ELF sections
This change is required as the code to create a new PDT should execute
before the Go allocator is bootstrapped.
2017-07-13 00:08:40 +01:00

376 lines
9.7 KiB
Go

package multiboot
import (
"reflect"
"strings"
"unsafe"
)
var (
infoData uintptr
cmdLineKV map[string]string
)
type tagType uint32
// nolint
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
}
// FramebufferType defines the type of the initialized framebuffer.
type FramebufferType uint8
const (
// FramebufferTypeIndexed specifies a 256-color palette.
FramebufferTypeIndexed FramebufferType = iota
// FramebufferTypeRGB specifies direct RGB mode.
FramebufferTypeRGB
// FramebufferTypeEGA specifies EGA text mode.
FramebufferTypeEGA
)
// FramebufferInfo provides information about the initialized framebuffer.
type FramebufferInfo struct {
// The framebuffer physical address.
PhysAddr uint64
// Row pitch in bytes.
Pitch uint32
// Width and height in pixels (or characters if Type = FramebufferTypeEGA)
Width, Height uint32
// Bits per pixel (non EGA modes only).
Bpp uint8
// Framebuffer type.
Type FramebufferType
reserved uint16
// The colorInfo data begins after the reserved block and has different
// contents depending on the framebuffer type. This dummy field is used
// for obtaining a pointer to the color info block data.
colorInfo [0]byte
}
// RGBColorInfo returns the FramebufferRGBColorInfo for a RGB framebuffer.
func (i *FramebufferInfo) RGBColorInfo() *FramebufferRGBColorInfo {
if i.Type != FramebufferTypeRGB {
return nil
}
// The color info data begins after the reserved attribute. To access
// it, a pointer is created to the dummy colorInfo attribute which
// points to the color info data start.
return (*FramebufferRGBColorInfo)(unsafe.Pointer(&i.colorInfo))
}
// FramebufferRGBColorInfo describes the order and width of each color component
// for a 15-, 16-, 24- or 32-bit framebuffer.
type FramebufferRGBColorInfo struct {
// The position and width (in bits) of the red component.
RedPosition uint8
RedMaskSize uint8
// The position and width (in bits) of the green component.
GreenPosition uint8
GreenMaskSize uint8
// The position and width (in bits) of the blue component.
BluePosition uint8
BlueMaskSize uint8
}
// 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
)
// MemRegionVisitor defies a visitor function that gets invoked by VisitMemRegions
// for each memory region provided by the boot loader. The visitor must return true
// to continue or false to abort the scan.
type MemRegionVisitor func(*MemoryMapEntry) bool
// 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
}
// String implements fmt.Stringer for MemoryEntryType.
func (t MemoryEntryType) String() string {
switch t {
case MemAvailable:
return "available"
case MemReserved:
return "reserved"
case MemAcpiReclaimable:
return "ACPI (reclaimable)"
case MemNvs:
return "NVS"
default:
return "unknown"
}
}
type elfSections struct {
numSections uint16
sectionSize uint32
strtabSectionIndex uint32
sectionData [0]byte
}
/*
type elfSection32 struct {
nameIndex uint32
sectionType uint32
flags uint32
address uint32
offset uint32
size uint32
link uint32
info uint32
addrAlign uint32
entSize uint32
}
*/
type elfSection64 struct {
nameIndex uint32
sectionType uint32
flags uint64
address uint64
offset uint64
size uint64
link uint32
info uint32
addrAlign uint64
entSize uint64
}
// ElfSectionFlag defines an OR-able flag associated with an ElfSection.
type ElfSectionFlag uint32
const (
// ElfSectionWritable marks the section as writable.
ElfSectionWritable ElfSectionFlag = 1 << iota
// ElfSectionAllocated means that the section is allocated in memory
// when the image is loaded (e.g .bss sections)
ElfSectionAllocated
// ElfSectionExecutable marks the section as executable.
ElfSectionExecutable
)
// ElfSectionVisitor defies a visitor function that gets invoked by VisitElfSections
// for rach ELF section that belongs to the loaded kernel image.
type ElfSectionVisitor func(name string, flags ElfSectionFlag, address uintptr, size uint64)
// VisitElfSections invokes visitor for each ELF entry that belongs to the
// loaded kernel image.
func VisitElfSections(visitor ElfSectionVisitor) {
curPtr, size := findTagByType(tagElfSymbols)
if size == 0 {
return
}
var (
sectionPayload elfSection64
ptrElfSections = (*elfSections)(unsafe.Pointer(curPtr))
secPtr = uintptr(unsafe.Pointer(&ptrElfSections.sectionData))
sizeofSection = unsafe.Sizeof(sectionPayload)
strTableSection = (*elfSection64)(unsafe.Pointer(secPtr + uintptr(ptrElfSections.strtabSectionIndex)*sizeofSection))
secName string
secNameHeader = (*reflect.StringHeader)(unsafe.Pointer(&secName))
)
for secIndex := uint16(0); secIndex < ptrElfSections.numSections; secIndex, secPtr = secIndex+1, secPtr+sizeofSection {
secData := (*elfSection64)(unsafe.Pointer(secPtr))
if secData.size == 0 {
continue
}
// String table entries are C-style NULL-terminated strings
end := uintptr(secData.nameIndex)
for ; *(*byte)(unsafe.Pointer(uintptr(strTableSection.address) + end)) != 0; end++ {
}
secNameHeader.Len = int(end - uintptr(secData.nameIndex))
secNameHeader.Data = uintptr(unsafe.Pointer(uintptr(strTableSection.address) + uintptr(secData.nameIndex)))
visitor(secName, ElfSectionFlag(secData.flags), uintptr(secData.address), secData.size)
}
}
// 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
}
if !visitor(entry) {
return
}
curPtr += uintptr(ptrMapHeader.entrySize)
}
}
// GetFramebufferInfo returns information about the framebuffer initialized by the
// bootloader. This function returns nil if no framebuffer info is available.
func GetFramebufferInfo() *FramebufferInfo {
var info *FramebufferInfo
curPtr, size := findTagByType(tagFramebufferInfo)
if size != 0 {
info = (*FramebufferInfo)(unsafe.Pointer(curPtr))
}
return info
}
// GetBootCmdLine returns the command line key-value pairs passed to the
// kernel. This function must only be invoked after bootstrapping the memory
// allocator.
func GetBootCmdLine() map[string]string {
if cmdLineKV != nil {
return cmdLineKV
}
cmdLineKV = make(map[string]string)
curPtr, size := findTagByType(tagBootCmdLine)
if size != 0 {
// The command line is a C-style NULL-terminated string
cmdLine := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
Len: int(size - 1),
Cap: int(size - 1),
Data: curPtr,
}))
pairs := strings.Fields(string(cmdLine))
for _, pair := range pairs {
kv := strings.Split(pair, "=")
switch len(kv) {
case 2: // foo=bar
cmdLineKV[kv[0]] = kv[1]
case 1: // nofoo
cmdLineKV[kv[0]] = kv[0]
}
}
}
return cmdLineKV
}
// 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
}