mirror of
https://github.com/taigrr/gopher-os
synced 2025-01-18 04:43:13 -08:00
Merge pull request #52 from achilleasa/detect-acpi-support-and-enumerate-acpi-tables
Detect ACPI support and enumerate ACPI tables
This commit is contained in:
commit
14aef16459
277
src/gopheros/device/acpi/acpi.go
Normal file
277
src/gopheros/device/acpi/acpi.go
Normal file
@ -0,0 +1,277 @@
|
||||
package acpi
|
||||
|
||||
import (
|
||||
"gopheros/device"
|
||||
"gopheros/device/acpi/table"
|
||||
"gopheros/kernel"
|
||||
"gopheros/kernel/kfmt"
|
||||
"gopheros/kernel/mem"
|
||||
"gopheros/kernel/mem/pmm"
|
||||
"gopheros/kernel/mem/vmm"
|
||||
"io"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
acpiRev1 uint8 = 0
|
||||
acpiRev2Plus uint8 = 2
|
||||
)
|
||||
|
||||
var (
|
||||
errMissingRSDP = &kernel.Error{Module: "acpi", Message: "could not locate ACPI RSDP"}
|
||||
errTableChecksumMismatch = &kernel.Error{Module: "acpi", Message: "detected checksum mismatch while parsing ACPI table header"}
|
||||
|
||||
mapFn = vmm.Map
|
||||
identityMapFn = vmm.IdentityMapRegion
|
||||
unmapFn = vmm.Unmap
|
||||
|
||||
// RDSP must be located in the physical memory region 0xe0000 to 0xfffff
|
||||
rsdpLocationLow uintptr = 0xe0000
|
||||
rsdpLocationHi uintptr = 0xfffff
|
||||
rsdpAlignment uintptr = 16
|
||||
|
||||
rsdpSignature = [8]byte{'R', 'S', 'D', ' ', 'P', 'T', 'R', ' '}
|
||||
fadtSignature = "FACP"
|
||||
)
|
||||
|
||||
type acpiDriver struct {
|
||||
// rsdtAddr holds the address to the root system descriptor table.
|
||||
rsdtAddr uintptr
|
||||
|
||||
// useXSDT specifies if the driver must use the XSDT or the RSDT table.
|
||||
useXSDT bool
|
||||
|
||||
// The ACPI table map allows the driver to lookup an ACPI table header
|
||||
// by the table name. All tables included in this map are mapped into
|
||||
// memory.
|
||||
tableMap map[string]*table.SDTHeader
|
||||
}
|
||||
|
||||
// DriverInit initializes this driver.
|
||||
func (drv *acpiDriver) DriverInit(w io.Writer) *kernel.Error {
|
||||
if err := drv.enumerateTables(w); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
drv.printTableInfo(w)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DriverName returns the name of this driver.
|
||||
func (*acpiDriver) DriverName() string {
|
||||
return "ACPI"
|
||||
}
|
||||
|
||||
// DriverVersion returns the version of this driver.
|
||||
func (*acpiDriver) DriverVersion() (uint16, uint16, uint16) {
|
||||
return 0, 0, 1
|
||||
}
|
||||
|
||||
func (drv *acpiDriver) printTableInfo(w io.Writer) {
|
||||
for name, header := range drv.tableMap {
|
||||
kfmt.Fprintf(w, "%s at 0x%16x %6x (%6s %8s)\n",
|
||||
name,
|
||||
uintptr(unsafe.Pointer(header)),
|
||||
header.Length,
|
||||
string(header.OEMID[:]),
|
||||
string(header.OEMTableID[:]),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// enumerateTables detects and maps all ACPI tables that are present. Besides
|
||||
// the table list defined by the RSDP, this method will also peek into the
|
||||
// FADT (if found) looking for the address of DSDT.
|
||||
func (drv *acpiDriver) enumerateTables(w io.Writer) *kernel.Error {
|
||||
header, sizeofHeader, err := mapACPITable(drv.rsdtAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
drv.tableMap = make(map[string]*table.SDTHeader)
|
||||
|
||||
var (
|
||||
acpiRev = header.Revision
|
||||
payloadLen = header.Length - uint32(sizeofHeader)
|
||||
sdtAddresses []uintptr
|
||||
)
|
||||
|
||||
// RSDT uses 4-byte long pointers whereas the XSDT uses 8-byte long.
|
||||
switch drv.useXSDT {
|
||||
case true:
|
||||
sdtAddresses = make([]uintptr, payloadLen>>3)
|
||||
for curPtr, i := drv.rsdtAddr+sizeofHeader, 0; i < len(sdtAddresses); curPtr, i = curPtr+8, i+1 {
|
||||
sdtAddresses[i] = uintptr(*(*uint64)(unsafe.Pointer(curPtr)))
|
||||
}
|
||||
default:
|
||||
sdtAddresses = make([]uintptr, payloadLen>>2)
|
||||
for curPtr, i := drv.rsdtAddr+sizeofHeader, 0; i < len(sdtAddresses); curPtr, i = curPtr+4, i+1 {
|
||||
sdtAddresses[i] = uintptr(*(*uint32)(unsafe.Pointer(curPtr)))
|
||||
}
|
||||
}
|
||||
|
||||
for _, addr := range sdtAddresses {
|
||||
if header, _, err = mapACPITable(addr); err != nil {
|
||||
switch err {
|
||||
case errTableChecksumMismatch:
|
||||
kfmt.Fprintf(w, "%s at 0x%16x %6x [checksum mismatch; skipping]\n",
|
||||
string(header.Signature[:]),
|
||||
uintptr(unsafe.Pointer(header)),
|
||||
header.Length,
|
||||
)
|
||||
continue
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
signature := string(header.Signature[:])
|
||||
drv.tableMap[signature] = header
|
||||
|
||||
// The FADT allows us to lookup the DSDT table address
|
||||
if signature == fadtSignature {
|
||||
fadt := (*table.FADT)(unsafe.Pointer(header))
|
||||
|
||||
dsdtAddr := uintptr(fadt.Dsdt)
|
||||
if acpiRev >= acpiRev2Plus {
|
||||
dsdtAddr = uintptr(fadt.Ext.Dsdt)
|
||||
}
|
||||
|
||||
if header, _, err = mapACPITable(dsdtAddr); err != nil {
|
||||
switch err {
|
||||
case errTableChecksumMismatch:
|
||||
kfmt.Fprintf(w, "%s at 0x%16x %6x [checksum mismatch; skipping]\n",
|
||||
string(header.Signature[:]),
|
||||
uintptr(unsafe.Pointer(header)),
|
||||
header.Length,
|
||||
)
|
||||
continue
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
drv.tableMap[string(header.Signature[:])] = header
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// mapACPITable attempts to map and parse the header for the ACPI table starting
|
||||
// at the given address. It then uses the length field for the header to expand
|
||||
// the mapping to cover the table contents and verifies the checksum before
|
||||
// returning a pointer to the table header.
|
||||
func mapACPITable(tableAddr uintptr) (header *table.SDTHeader, sizeofHeader uintptr, err *kernel.Error) {
|
||||
var headerPage vmm.Page
|
||||
|
||||
// Identity-map the table header so we can access its length field
|
||||
sizeofHeader = unsafe.Sizeof(table.SDTHeader{})
|
||||
if headerPage, err = identityMapFn(pmm.FrameFromAddress(tableAddr), mem.Size(sizeofHeader), vmm.FlagPresent); err != nil {
|
||||
return nil, sizeofHeader, err
|
||||
}
|
||||
|
||||
// Expand mapping to cover the table contents
|
||||
headerPageAddr := headerPage.Address() + vmm.PageOffset(tableAddr)
|
||||
header = (*table.SDTHeader)(unsafe.Pointer(headerPageAddr))
|
||||
if _, err = identityMapFn(pmm.FrameFromAddress(tableAddr), mem.Size(header.Length), vmm.FlagPresent); err != nil {
|
||||
return nil, sizeofHeader, err
|
||||
}
|
||||
|
||||
if !validTable(headerPageAddr, header.Length) {
|
||||
err = errTableChecksumMismatch
|
||||
}
|
||||
|
||||
return header, sizeofHeader, err
|
||||
}
|
||||
|
||||
// locateRSDT scans the memory region [rsdpLocationLow, rsdpLocationHi] looking
|
||||
// for the signature of the root system descriptor pointer (RSDP). If the RSDP
|
||||
// is found and is valid, locateRSDT returns the physical address of the root
|
||||
// system descriptor table (RSDT) or the extended system descriptor table (XSDT)
|
||||
// if the system supports ACPI 2.0+.
|
||||
func locateRSDT() (uintptr, bool, *kernel.Error) {
|
||||
var (
|
||||
rsdp *table.RSDPDescriptor
|
||||
rsdp2 *table.ExtRSDPDescriptor
|
||||
)
|
||||
|
||||
// Cleanup temporary identity mappings when the function returns
|
||||
defer func() {
|
||||
for curPage := vmm.PageFromAddress(rsdpLocationLow); curPage <= vmm.PageFromAddress(rsdpLocationHi); curPage++ {
|
||||
unmapFn(curPage)
|
||||
}
|
||||
}()
|
||||
|
||||
// Setup temporary identity mapping so we can scan for the header
|
||||
for curPage := vmm.PageFromAddress(rsdpLocationLow); curPage <= vmm.PageFromAddress(rsdpLocationHi); curPage++ {
|
||||
if err := mapFn(curPage, pmm.Frame(curPage), vmm.FlagPresent); err != nil {
|
||||
return 0, false, err
|
||||
}
|
||||
}
|
||||
|
||||
// The RSDP should be aligned on a 16-byte boundary
|
||||
checkNextBlock:
|
||||
for curPtr := rsdpLocationLow; curPtr < rsdpLocationHi; curPtr += rsdpAlignment {
|
||||
rsdp = (*table.RSDPDescriptor)(unsafe.Pointer(curPtr))
|
||||
for i, b := range rsdpSignature {
|
||||
if rsdp.Signature[i] != b {
|
||||
continue checkNextBlock
|
||||
}
|
||||
}
|
||||
|
||||
if rsdp.Revision == acpiRev1 {
|
||||
if !validTable(curPtr, uint32(unsafe.Sizeof(*rsdp))) {
|
||||
continue
|
||||
}
|
||||
|
||||
return uintptr(rsdp.RSDTAddr), false, nil
|
||||
}
|
||||
|
||||
// System uses ACPI revision > 1 and provides an extended RSDP
|
||||
// which can be accessed at the same place.
|
||||
rsdp2 = (*table.ExtRSDPDescriptor)(unsafe.Pointer(curPtr))
|
||||
if !validTable(curPtr, uint32(unsafe.Sizeof(*rsdp2))) {
|
||||
continue
|
||||
}
|
||||
|
||||
return uintptr(rsdp2.XSDTAddr), true, nil
|
||||
}
|
||||
|
||||
return 0, false, errMissingRSDP
|
||||
}
|
||||
|
||||
// validTable calculates the checksum for an ACPI table of length tableLength
|
||||
// that starts at tablePtr and returns true if the table is valid.
|
||||
func validTable(tablePtr uintptr, tableLength uint32) bool {
|
||||
var (
|
||||
i uint32
|
||||
sum uint8
|
||||
)
|
||||
|
||||
for i = 0; i < tableLength; i++ {
|
||||
sum += *(*uint8)(unsafe.Pointer(tablePtr + uintptr(i)))
|
||||
}
|
||||
|
||||
return sum == 0
|
||||
}
|
||||
|
||||
func probeForACPI() device.Driver {
|
||||
if rsdtAddr, useXSDT, err := locateRSDT(); err == nil {
|
||||
return &acpiDriver{
|
||||
rsdtAddr: rsdtAddr,
|
||||
useXSDT: useXSDT,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
device.RegisterDriver(&device.DriverInfo{
|
||||
Order: device.DetectOrderBeforeACPI,
|
||||
Probe: probeForACPI,
|
||||
})
|
||||
}
|
487
src/gopheros/device/acpi/acpi_test.go
Normal file
487
src/gopheros/device/acpi/acpi_test.go
Normal file
@ -0,0 +1,487 @@
|
||||
package acpi
|
||||
|
||||
import (
|
||||
"gopheros/device/acpi/table"
|
||||
"gopheros/kernel"
|
||||
"gopheros/kernel/mem"
|
||||
"gopheros/kernel/mem/pmm"
|
||||
"gopheros/kernel/mem/vmm"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
dsdtSignature = "DSDT"
|
||||
)
|
||||
|
||||
func TestProbe(t *testing.T) {
|
||||
defer func(rsdpLow, rsdpHi, rsdpAlign uintptr) {
|
||||
mapFn = vmm.Map
|
||||
unmapFn = vmm.Unmap
|
||||
rsdpLocationLow = rsdpLow
|
||||
rsdpLocationHi = rsdpHi
|
||||
rsdpAlignment = rsdpAlign
|
||||
}(rsdpLocationLow, rsdpLocationHi, rsdpAlignment)
|
||||
|
||||
t.Run("ACPI1", func(t *testing.T) {
|
||||
mapFn = func(_ vmm.Page, _ pmm.Frame, _ vmm.PageTableEntryFlag) *kernel.Error { return nil }
|
||||
unmapFn = func(_ vmm.Page) *kernel.Error { return nil }
|
||||
|
||||
// Allocate space for 2 descriptors; leave the first entry
|
||||
// blank to test that locateRSDT will jump over it and populate
|
||||
// the second descriptor
|
||||
sizeofRSDP := unsafe.Sizeof(table.RSDPDescriptor{})
|
||||
buf := make([]byte, 2*sizeofRSDP)
|
||||
rsdpHeader := (*table.RSDPDescriptor)(unsafe.Pointer(&buf[sizeofRSDP]))
|
||||
rsdpHeader.Signature = rsdpSignature
|
||||
rsdpHeader.Revision = acpiRev1
|
||||
rsdpHeader.RSDTAddr = 0xbadf00
|
||||
rsdpHeader.Checksum = -calcChecksum(uintptr(unsafe.Pointer(rsdpHeader)), uintptr(sizeofRSDP))
|
||||
|
||||
rsdpLocationLow = uintptr(unsafe.Pointer(&buf[0]))
|
||||
rsdpLocationHi = uintptr(unsafe.Pointer(&buf[2*sizeofRSDP-1]))
|
||||
// As we cannot ensure 16-byte alignment for our buffer we need to override the
|
||||
// alignment so we scan all bytes in the buffer for the descriptor signature
|
||||
rsdpAlignment = 1
|
||||
|
||||
drv := probeForACPI()
|
||||
if drv == nil {
|
||||
t.Fatal("ACPI probe failed")
|
||||
}
|
||||
|
||||
drv.DriverName()
|
||||
drv.DriverVersion()
|
||||
|
||||
acpiDrv := drv.(*acpiDriver)
|
||||
|
||||
if acpiDrv.rsdtAddr != uintptr(rsdpHeader.RSDTAddr) {
|
||||
t.Fatalf("expected probed RSDT address to be 0x%x; got 0x%x", uintptr(rsdpHeader.RSDTAddr), acpiDrv.rsdtAddr)
|
||||
}
|
||||
|
||||
if exp := false; acpiDrv.useXSDT != exp {
|
||||
t.Fatal("expected probe to locate the RSDT and not the XSDT")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("ACPI2+", func(t *testing.T) {
|
||||
mapFn = func(_ vmm.Page, _ pmm.Frame, _ vmm.PageTableEntryFlag) *kernel.Error { return nil }
|
||||
unmapFn = func(_ vmm.Page) *kernel.Error { return nil }
|
||||
|
||||
// Allocate space for 2 descriptors; leave the first entry
|
||||
// blank to test that locateRSDT will jump over it and populate
|
||||
// the second descriptor
|
||||
sizeofRSDP := unsafe.Sizeof(table.RSDPDescriptor{})
|
||||
sizeofExtRSDP := unsafe.Sizeof(table.ExtRSDPDescriptor{})
|
||||
buf := make([]byte, 2*sizeofExtRSDP)
|
||||
rsdpHeader := (*table.ExtRSDPDescriptor)(unsafe.Pointer(&buf[sizeofExtRSDP]))
|
||||
rsdpHeader.Signature = rsdpSignature
|
||||
rsdpHeader.Revision = acpiRev2Plus
|
||||
rsdpHeader.RSDTAddr = 0xbadf00 // we should ignore this and use XSDT instrad
|
||||
rsdpHeader.Checksum = -calcChecksum(uintptr(unsafe.Pointer(rsdpHeader)), uintptr(sizeofRSDP))
|
||||
|
||||
rsdpHeader.XSDTAddr = 0xc0ffee
|
||||
rsdpHeader.ExtendedChecksum = -calcChecksum(uintptr(unsafe.Pointer(rsdpHeader)), uintptr(sizeofExtRSDP))
|
||||
|
||||
rsdpLocationLow = uintptr(unsafe.Pointer(&buf[0]))
|
||||
rsdpLocationHi = uintptr(unsafe.Pointer(&buf[2*sizeofExtRSDP-1]))
|
||||
// As we cannot ensure 16-byte alignment for our buffer we need to override the
|
||||
// alignment so we scan all bytes in the buffer for the descriptor signature
|
||||
rsdpAlignment = 1
|
||||
|
||||
drv := probeForACPI()
|
||||
if drv == nil {
|
||||
t.Fatal("ACPI probe failed")
|
||||
}
|
||||
|
||||
acpiDrv := drv.(*acpiDriver)
|
||||
|
||||
if acpiDrv.rsdtAddr != uintptr(rsdpHeader.XSDTAddr) {
|
||||
t.Fatalf("expected probed RSDT address to be 0x%x; got 0x%x", uintptr(rsdpHeader.XSDTAddr), acpiDrv.rsdtAddr)
|
||||
}
|
||||
|
||||
if exp := true; acpiDrv.useXSDT != exp {
|
||||
t.Fatal("expected probe to locate the XSDT and not the RSDT")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("RSDP ACPI1 checksum mismatch", func(t *testing.T) {
|
||||
mapFn = func(_ vmm.Page, _ pmm.Frame, _ vmm.PageTableEntryFlag) *kernel.Error { return nil }
|
||||
unmapFn = func(_ vmm.Page) *kernel.Error { return nil }
|
||||
|
||||
sizeofRSDP := unsafe.Sizeof(table.RSDPDescriptor{})
|
||||
buf := make([]byte, sizeofRSDP)
|
||||
rsdpHeader := (*table.RSDPDescriptor)(unsafe.Pointer(&buf[0]))
|
||||
rsdpHeader.Signature = rsdpSignature
|
||||
rsdpHeader.Revision = acpiRev1
|
||||
|
||||
// Set wrong checksum
|
||||
rsdpHeader.Checksum = 0
|
||||
|
||||
// As we cannot ensure 16-byte alignment for our buffer we need to override the
|
||||
// alignment so we scan all bytes in the buffer for the descriptor signature
|
||||
rsdpLocationLow = uintptr(unsafe.Pointer(&buf[0]))
|
||||
rsdpLocationHi = uintptr(unsafe.Pointer(&buf[sizeofRSDP-1]))
|
||||
rsdpAlignment = 1
|
||||
|
||||
drv := probeForACPI()
|
||||
if drv != nil {
|
||||
t.Fatal("expected ACPI probe to fail")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("RSDP ACPI2+ checksum mismatch", func(t *testing.T) {
|
||||
mapFn = func(_ vmm.Page, _ pmm.Frame, _ vmm.PageTableEntryFlag) *kernel.Error { return nil }
|
||||
unmapFn = func(_ vmm.Page) *kernel.Error { return nil }
|
||||
|
||||
sizeofExtRSDP := unsafe.Sizeof(table.ExtRSDPDescriptor{})
|
||||
buf := make([]byte, sizeofExtRSDP)
|
||||
rsdpHeader := (*table.ExtRSDPDescriptor)(unsafe.Pointer(&buf[0]))
|
||||
rsdpHeader.Signature = rsdpSignature
|
||||
rsdpHeader.Revision = acpiRev2Plus
|
||||
|
||||
// Set wrong checksum for extended rsdp
|
||||
rsdpHeader.ExtendedChecksum = 0
|
||||
|
||||
// As we cannot ensure 16-byte alignment for our buffer we need to override the
|
||||
// alignment so we scan all bytes in the buffer for the descriptor signature
|
||||
rsdpLocationLow = uintptr(unsafe.Pointer(&buf[0]))
|
||||
rsdpLocationHi = uintptr(unsafe.Pointer(&buf[sizeofExtRSDP-1]))
|
||||
rsdpAlignment = 1
|
||||
|
||||
drv := probeForACPI()
|
||||
if drv != nil {
|
||||
t.Fatal("expected ACPI probe to fail")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("error mapping rsdp memory block", func(t *testing.T) {
|
||||
expErr := &kernel.Error{Module: "test", Message: "vmm.Map failed"}
|
||||
mapFn = func(_ vmm.Page, _ pmm.Frame, _ vmm.PageTableEntryFlag) *kernel.Error { return expErr }
|
||||
unmapFn = func(_ vmm.Page) *kernel.Error { return nil }
|
||||
|
||||
drv := probeForACPI()
|
||||
if drv != nil {
|
||||
t.Fatal("expected ACPI probe to fail")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestDriverInit(t *testing.T) {
|
||||
defer func() {
|
||||
identityMapFn = vmm.IdentityMapRegion
|
||||
}()
|
||||
|
||||
t.Run("success", func(t *testing.T) {
|
||||
rsdtAddr, _ := genTestRDST(t, acpiRev2Plus)
|
||||
identityMapFn = func(frame pmm.Frame, _ mem.Size, _ vmm.PageTableEntryFlag) (vmm.Page, *kernel.Error) {
|
||||
return vmm.Page(frame), nil
|
||||
}
|
||||
|
||||
drv := &acpiDriver{
|
||||
rsdtAddr: rsdtAddr,
|
||||
useXSDT: true,
|
||||
}
|
||||
|
||||
if err := drv.DriverInit(os.Stderr); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("map errors in enumerateTables", func(t *testing.T) {
|
||||
rsdtAddr, tableList := genTestRDST(t, acpiRev2Plus)
|
||||
|
||||
var (
|
||||
expErr = &kernel.Error{Module: "test", Message: "vmm.Map failed"}
|
||||
callCount int
|
||||
)
|
||||
|
||||
drv := &acpiDriver{
|
||||
rsdtAddr: rsdtAddr,
|
||||
useXSDT: true,
|
||||
}
|
||||
|
||||
specs := []func(frame pmm.Frame, _ mem.Size, _ vmm.PageTableEntryFlag) (vmm.Page, *kernel.Error){
|
||||
func(frame pmm.Frame, _ mem.Size, _ vmm.PageTableEntryFlag) (vmm.Page, *kernel.Error) {
|
||||
// fail while trying to map RSDT
|
||||
return 0, expErr
|
||||
},
|
||||
func(frame pmm.Frame, _ mem.Size, _ vmm.PageTableEntryFlag) (vmm.Page, *kernel.Error) {
|
||||
// fail while trying to map any other ACPI table
|
||||
callCount++
|
||||
if callCount > 2 {
|
||||
return 0, expErr
|
||||
}
|
||||
return vmm.Page(frame), nil
|
||||
},
|
||||
func(frame pmm.Frame, size mem.Size, _ vmm.PageTableEntryFlag) (vmm.Page, *kernel.Error) {
|
||||
// fail while trying to map DSDT
|
||||
for _, header := range tableList {
|
||||
if header.Length == uint32(size) && string(header.Signature[:]) == dsdtSignature {
|
||||
return 0, expErr
|
||||
}
|
||||
}
|
||||
return vmm.Page(frame), nil
|
||||
},
|
||||
}
|
||||
|
||||
// Test map errors for all map calls in enumerateTables
|
||||
for specIndex, spec := range specs {
|
||||
identityMapFn = spec
|
||||
if err := drv.DriverInit(os.Stderr); err != expErr {
|
||||
t.Errorf("[spec %d]; expected to get an error\n", specIndex)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestEnumerateTables(t *testing.T) {
|
||||
defer func() {
|
||||
identityMapFn = vmm.IdentityMapRegion
|
||||
}()
|
||||
|
||||
var expTables = []string{"SSDT", "APIC", "FACP", "DSDT"}
|
||||
|
||||
t.Run("ACPI1", func(t *testing.T) {
|
||||
rsdtAddr, tableList := genTestRDST(t, acpiRev1)
|
||||
|
||||
identityMapFn = func(frame pmm.Frame, _ mem.Size, _ vmm.PageTableEntryFlag) (vmm.Page, *kernel.Error) {
|
||||
// The frame encodes the table index we need to lookup (see genTestRDST)
|
||||
nextTableIndex := int(frame)
|
||||
if nextTableIndex >= len(tableList) {
|
||||
// This is the RSDT
|
||||
return vmm.Page(frame), nil
|
||||
}
|
||||
|
||||
header := tableList[nextTableIndex]
|
||||
return vmm.PageFromAddress(uintptr(unsafe.Pointer(header))), nil
|
||||
}
|
||||
|
||||
drv := &acpiDriver{
|
||||
rsdtAddr: rsdtAddr,
|
||||
useXSDT: false,
|
||||
}
|
||||
|
||||
if err := drv.enumerateTables(os.Stderr); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if exp, got := len(expTables), len(drv.tableMap); got != exp {
|
||||
t.Fatalf("expected enumerateTables to discover %d tables; got %d\n", exp, got)
|
||||
}
|
||||
|
||||
for _, tableName := range expTables {
|
||||
if drv.tableMap[tableName] == nil {
|
||||
t.Fatalf("expected enumerateTables to discover table %q", tableName)
|
||||
}
|
||||
}
|
||||
|
||||
drv.printTableInfo(os.Stderr)
|
||||
})
|
||||
|
||||
t.Run("ACPI2+", func(t *testing.T) {
|
||||
rsdtAddr, _ := genTestRDST(t, acpiRev2Plus)
|
||||
identityMapFn = func(frame pmm.Frame, _ mem.Size, _ vmm.PageTableEntryFlag) (vmm.Page, *kernel.Error) {
|
||||
return vmm.Page(frame), nil
|
||||
}
|
||||
|
||||
drv := &acpiDriver{
|
||||
rsdtAddr: rsdtAddr,
|
||||
useXSDT: true,
|
||||
}
|
||||
|
||||
if err := drv.enumerateTables(os.Stderr); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if exp, got := len(expTables), len(drv.tableMap); got != exp {
|
||||
t.Fatalf("expected enumerateTables to discover %d tables; got %d\n", exp, got)
|
||||
}
|
||||
|
||||
for _, tableName := range expTables {
|
||||
if drv.tableMap[tableName] == nil {
|
||||
t.Fatalf("expected enumerateTables to discover table %q", tableName)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("checksum mismatch", func(t *testing.T) {
|
||||
rsdtAddr, tableList := genTestRDST(t, acpiRev2Plus)
|
||||
identityMapFn = func(frame pmm.Frame, _ mem.Size, _ vmm.PageTableEntryFlag) (vmm.Page, *kernel.Error) {
|
||||
return vmm.Page(frame), nil
|
||||
}
|
||||
|
||||
// Set bad checksum for "SSDT" and "DSDT"
|
||||
for _, header := range tableList {
|
||||
switch string(header.Signature[:]) {
|
||||
case "SSDT", dsdtSignature:
|
||||
header.Checksum++
|
||||
}
|
||||
}
|
||||
|
||||
drv := &acpiDriver{
|
||||
rsdtAddr: rsdtAddr,
|
||||
useXSDT: true,
|
||||
}
|
||||
|
||||
if err := drv.enumerateTables(os.Stderr); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expTables := []string{"APIC", "FACP"}
|
||||
|
||||
if exp, got := len(expTables), len(drv.tableMap); got != exp {
|
||||
t.Fatalf("expected enumerateTables to discover %d tables; got %d\n", exp, got)
|
||||
}
|
||||
|
||||
for _, tableName := range expTables {
|
||||
if drv.tableMap[tableName] == nil {
|
||||
t.Fatalf("expected enumerateTables to discover table %q", tableName)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestMapACPITableErrors(t *testing.T) {
|
||||
defer func() {
|
||||
identityMapFn = vmm.IdentityMapRegion
|
||||
}()
|
||||
|
||||
var (
|
||||
callCount int
|
||||
expErr = &kernel.Error{Module: "test", Message: "identityMapRegion failed"}
|
||||
header table.SDTHeader
|
||||
)
|
||||
|
||||
identityMapFn = func(frame pmm.Frame, _ mem.Size, _ vmm.PageTableEntryFlag) (vmm.Page, *kernel.Error) {
|
||||
callCount++
|
||||
if callCount >= 2 {
|
||||
return 0, expErr
|
||||
}
|
||||
|
||||
return vmm.PageFromAddress(uintptr(unsafe.Pointer(&header))), nil
|
||||
}
|
||||
|
||||
// Test errors while mapping the table contents and the table header
|
||||
for i := 0; i < 2; i++ {
|
||||
if _, _, err := mapACPITable(0xf00); err != expErr {
|
||||
t.Errorf("[spec %d]; expected to get an error\n", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func genTestRDST(t *testing.T, acpiVersion uint8) (rsdtAddr uintptr, tableList []*table.SDTHeader) {
|
||||
dumpFiles, err := filepath.Glob(pkgDir() + "/table/tabletest/*.aml")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var fadt, dsdt *table.SDTHeader
|
||||
var dsdtIndex int
|
||||
|
||||
for index, df := range dumpFiles {
|
||||
dumpData, err := ioutil.ReadFile(df)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
header := (*table.SDTHeader)(unsafe.Pointer(&dumpData[0]))
|
||||
tableName := string(header.Signature[:])
|
||||
switch tableName {
|
||||
case dsdtSignature, fadtSignature:
|
||||
if tableName == dsdtSignature {
|
||||
dsdt = header
|
||||
dsdtIndex = index
|
||||
} else {
|
||||
fadt = header
|
||||
}
|
||||
}
|
||||
|
||||
tableList = append(tableList, header)
|
||||
}
|
||||
|
||||
// Setup the pointer to the DSDT
|
||||
if fadt != nil && dsdt != nil {
|
||||
fadtHeader := (*table.FADT)(unsafe.Pointer(fadt))
|
||||
if acpiVersion == acpiRev1 {
|
||||
// Since the tests run in 64-bit mode these 32-bit addresses
|
||||
// will be invalid and cause a page fault. So we cheat and
|
||||
// encode the table index and page offset as the pointer.
|
||||
// The test code will hook identityMapFn to reconstruct the
|
||||
// correct pointer to the table contents.
|
||||
offset := vmm.PageOffset(uintptr(unsafe.Pointer(dsdt)))
|
||||
encodedTableLoc := (uintptr(dsdtIndex) << mem.PageShift) + offset
|
||||
fadtHeader.Dsdt = uint32(encodedTableLoc)
|
||||
} else {
|
||||
fadtHeader.Ext.Dsdt = uint64(uintptr(unsafe.Pointer(dsdt)))
|
||||
}
|
||||
updateChecksum(fadt)
|
||||
}
|
||||
|
||||
// Assemble the RDST
|
||||
var (
|
||||
sizeofSDTHeader = unsafe.Sizeof(table.SDTHeader{})
|
||||
rsdtHeader *table.SDTHeader
|
||||
)
|
||||
|
||||
switch acpiVersion {
|
||||
case acpiRev1:
|
||||
buf := make([]byte, int(sizeofSDTHeader)+4*len(tableList))
|
||||
rsdtHeader = (*table.SDTHeader)(unsafe.Pointer(&buf[0]))
|
||||
rsdtHeader.Signature = [4]byte{'R', 'S', 'D', 'T'}
|
||||
rsdtHeader.Revision = acpiVersion
|
||||
rsdtHeader.Length = uint32(sizeofSDTHeader)
|
||||
|
||||
// Since the tests run in 64-bit mode these 32-bit addresses
|
||||
// will be invalid and cause a page fault. So we cheat and
|
||||
// encode the table index and page offset as the pointer.
|
||||
// The test code will hook identityMapFn to reconstruct the
|
||||
// correct pointer to the table contents.
|
||||
for index, tableHeader := range tableList {
|
||||
offset := vmm.PageOffset(uintptr(unsafe.Pointer(tableHeader)))
|
||||
encodedTableLoc := (uintptr(index) << mem.PageShift) + offset
|
||||
|
||||
*(*uint32)(unsafe.Pointer(&buf[rsdtHeader.Length])) = uint32(encodedTableLoc)
|
||||
rsdtHeader.Length += 4
|
||||
}
|
||||
default:
|
||||
buf := make([]byte, int(sizeofSDTHeader)+8*len(tableList))
|
||||
rsdtHeader = (*table.SDTHeader)(unsafe.Pointer(&buf[0]))
|
||||
rsdtHeader.Signature = [4]byte{'R', 'S', 'D', 'T'}
|
||||
rsdtHeader.Revision = acpiVersion
|
||||
rsdtHeader.Length = uint32(sizeofSDTHeader)
|
||||
for _, tableHeader := range tableList {
|
||||
// Do not include DSDT. This will be referenced via FADT
|
||||
if string(tableHeader.Signature[:]) == dsdtSignature {
|
||||
continue
|
||||
}
|
||||
*(*uint64)(unsafe.Pointer(&buf[rsdtHeader.Length])) = uint64(uintptr(unsafe.Pointer(tableHeader)))
|
||||
rsdtHeader.Length += 8
|
||||
}
|
||||
}
|
||||
|
||||
updateChecksum(rsdtHeader)
|
||||
return uintptr(unsafe.Pointer(rsdtHeader)), tableList
|
||||
}
|
||||
|
||||
func updateChecksum(header *table.SDTHeader) {
|
||||
header.Checksum = -calcChecksum(uintptr(unsafe.Pointer(header)), uintptr(header.Length))
|
||||
}
|
||||
|
||||
func calcChecksum(tableAddr, length uintptr) uint8 {
|
||||
var checksum uint8
|
||||
for ptr := tableAddr; ptr < tableAddr+length; ptr++ {
|
||||
checksum += *(*uint8)(unsafe.Pointer(ptr))
|
||||
}
|
||||
|
||||
return checksum
|
||||
}
|
||||
|
||||
func pkgDir() string {
|
||||
_, f, _, _ := runtime.Caller(1)
|
||||
return filepath.Dir(f)
|
||||
}
|
264
src/gopheros/device/acpi/table/tables.go
Normal file
264
src/gopheros/device/acpi/table/tables.go
Normal file
@ -0,0 +1,264 @@
|
||||
package table
|
||||
|
||||
// Resolver is an interface implemented by objects that can lookup an ACPI table
|
||||
// by its name.
|
||||
//
|
||||
// LookupTable attempts to locate a table by name returning back a pointer to
|
||||
// its standard header or nil if the table could not be found. The resolver
|
||||
// must make sure that the entire table contents are mapped so they can be
|
||||
// accessed by the caller.
|
||||
type Resolver interface {
|
||||
LookupTable(string) *SDTHeader
|
||||
}
|
||||
|
||||
// RSDPDescriptor defines the root system descriptor pointer for ACPI 1.0. This
|
||||
// is used as the entry-point for parsing ACPI data.
|
||||
type RSDPDescriptor struct {
|
||||
// The signature must contain "RSD PTR " (last byte is a space).
|
||||
Signature [8]byte
|
||||
|
||||
// A value that when added to the sum of all other bytes contained in
|
||||
// this descriptor should result in the value 0.
|
||||
Checksum uint8
|
||||
|
||||
OEMID [6]byte
|
||||
|
||||
// ACPI revision number. It is 0 for ACPI1.0 and 2 for versions 2.0 to 6.2.
|
||||
Revision uint8
|
||||
|
||||
// Physical address of 32-bit root system descriptor table.
|
||||
RSDTAddr uint32
|
||||
}
|
||||
|
||||
// ExtRSDPDescriptor extends RSDPDescriptor with additional fields. It is used
|
||||
// when RSDPDescriptor.revision > 1.
|
||||
type ExtRSDPDescriptor struct {
|
||||
RSDPDescriptor
|
||||
|
||||
// The size of the 64-bit root system descriptor table.
|
||||
Length uint32
|
||||
|
||||
// Physical address of 64-bit root system descriptor table.
|
||||
XSDTAddr uint64
|
||||
|
||||
// A value that when added to the sum of all other bytes contained in
|
||||
// this descriptor should result in the value 0.
|
||||
ExtendedChecksum uint8
|
||||
|
||||
reserved [3]byte
|
||||
}
|
||||
|
||||
// SDTHeader defines the common header for all ACPI-related tables.
|
||||
type SDTHeader struct {
|
||||
// The signature defines the table type.
|
||||
Signature [4]byte
|
||||
|
||||
// The length of the table
|
||||
Length uint32
|
||||
|
||||
// If this header belongs to a DSDT/SSDT table, the revision is also
|
||||
// used to indicate whether the AML VM should treat integers as 32-bits
|
||||
// (revision < 2) or 64-bits (revision >= 2).
|
||||
Revision uint8
|
||||
|
||||
// A value that when added to the sum of all other bytes in the table
|
||||
// should result in the value 0.
|
||||
Checksum uint8
|
||||
|
||||
// OEM specific information
|
||||
OEMID [6]byte
|
||||
OEMTableID [8]byte
|
||||
OEMRevision uint32
|
||||
|
||||
// Information about the ASL compiler that generated this table
|
||||
CreatorID uint32
|
||||
CreatorRevision uint32
|
||||
}
|
||||
|
||||
// AddressSpace defines the location where a set of registers resides.
|
||||
type AddressSpace uint8
|
||||
|
||||
// The list of supported address space types.
|
||||
const (
|
||||
AddressSpaceSysMemory AddressSpace = iota
|
||||
AddressSpaceSysIO
|
||||
AddressSpacePCI
|
||||
AddressSpaceEmbController
|
||||
AddressSpaceSMBus
|
||||
AddressSpaceFuncFixedHW = 0x7f
|
||||
)
|
||||
|
||||
// GenericAddress specifies a register range located in a particular address
|
||||
// space.
|
||||
type GenericAddress struct {
|
||||
Space AddressSpace
|
||||
BitWidth uint8
|
||||
BitOffset uint8
|
||||
AccessSize uint8
|
||||
Address uint64
|
||||
}
|
||||
|
||||
// PowerProfileType describes a power profile referenced by the FADT table.
|
||||
type PowerProfileType uint8
|
||||
|
||||
// The list of supported power profile types
|
||||
const (
|
||||
PowerProfileUnspecified PowerProfileType = iota
|
||||
PowerProfileDesktop
|
||||
PowerProfileMobile
|
||||
PowerProfileWorkstation
|
||||
PowerProfileEnterpriseServer
|
||||
PowerProfileSOHOServer
|
||||
PowerProfileAppliancePC
|
||||
PowerProfilePerformanceServer
|
||||
)
|
||||
|
||||
// FADT64 contains the 64-bit FADT extensions which are used by ACPI2+
|
||||
type FADT64 struct {
|
||||
FirmwareControl uint64
|
||||
|
||||
Dsdt uint64
|
||||
|
||||
PM1aEventBlock GenericAddress
|
||||
PM1bEventBlock GenericAddress
|
||||
PM1aControlBlock GenericAddress
|
||||
PM1bControlBlock GenericAddress
|
||||
PM2ControlBlock GenericAddress
|
||||
PMTimerBlock GenericAddress
|
||||
GPE0Block GenericAddress
|
||||
GPE1Block GenericAddress
|
||||
}
|
||||
|
||||
// FADT (Fixed ACPI Description Table) is an ACPI table containing information
|
||||
// about fixed register blocks used for power management.
|
||||
type FADT struct {
|
||||
SDTHeader
|
||||
|
||||
FirmwareCtrl uint32
|
||||
Dsdt uint32
|
||||
|
||||
reserved uint8
|
||||
|
||||
PreferredPowerManagementProfile PowerProfileType
|
||||
SCIInterrupt uint16
|
||||
SMICommandPort uint32
|
||||
AcpiEnable uint8
|
||||
AcpiDisable uint8
|
||||
S4BIOSReq uint8
|
||||
PSTATEControl uint8
|
||||
PM1aEventBlock uint32
|
||||
PM1bEventBlock uint32
|
||||
PM1aControlBlock uint32
|
||||
PM1bControlBlock uint32
|
||||
PM2ControlBlock uint32
|
||||
PMTimerBlock uint32
|
||||
GPE0Block uint32
|
||||
GPE1Block uint32
|
||||
PM1EventLength uint8
|
||||
PM1ControlLength uint8
|
||||
PM2ControlLength uint8
|
||||
PMTimerLength uint8
|
||||
GPE0Length uint8
|
||||
GPE1Length uint8
|
||||
GPE1Base uint8
|
||||
CStateControl uint8
|
||||
WorstC2Latency uint16
|
||||
WorstC3Latency uint16
|
||||
FlushSize uint16
|
||||
FlushStride uint16
|
||||
DutyOffset uint8
|
||||
DutyWidth uint8
|
||||
DayAlarm uint8
|
||||
MonthAlarm uint8
|
||||
Century uint8
|
||||
|
||||
// Reserved in ACPI 1.0; used since ACPI 2.0+
|
||||
BootArchitectureFlags uint16
|
||||
|
||||
reserved2 uint8
|
||||
Flags uint32
|
||||
|
||||
ResetReg GenericAddress
|
||||
|
||||
ResetValue uint8
|
||||
reserved3 [3]uint8
|
||||
|
||||
// 64-bit pointers to the above structures used by ACPI 2.0+
|
||||
Ext FADT64
|
||||
}
|
||||
|
||||
// MADT (Multiple APIC Description Table) is an ACPI table containing
|
||||
// information about the interrupt controllers and the number of installed
|
||||
// CPUs. Following the table header are a series of variable sized records
|
||||
// (MADTEntry) which contain additional information.
|
||||
type MADT struct {
|
||||
SDTHeader
|
||||
|
||||
LocalControllerAddress uint32
|
||||
Flags uint32
|
||||
}
|
||||
|
||||
// MADTEntryLocalAPIC describes a single physical processor and its local
|
||||
// interrupt controller.
|
||||
type MADTEntryLocalAPIC struct {
|
||||
ProcessorID uint8
|
||||
APICID uint8
|
||||
Flags uint32
|
||||
}
|
||||
|
||||
// MADTEntryIOAPIC describes an I/O Advanced Programmable Interrupt Controller.
|
||||
type MADTEntryIOAPIC struct {
|
||||
APICID uint8
|
||||
reserved uint8
|
||||
|
||||
// Address contains the address of the controller.
|
||||
Address uint32
|
||||
|
||||
// SysInterruptBase defines the first interrupt number that this
|
||||
// controller handles.
|
||||
SysInterruptBase uint32
|
||||
}
|
||||
|
||||
// MADTEntryInterruptSrcOverride contains the data for an Interrupt Source
|
||||
// Override. This mechanism is used to map IRQ sources to global system
|
||||
// interrupts.
|
||||
type MADTEntryInterruptSrcOverride struct {
|
||||
BusSrc uint8
|
||||
IRQSrc uint8
|
||||
GlobalInterrupt uint32
|
||||
Flags uint16
|
||||
}
|
||||
|
||||
// MADTEntryNMI describes a non-maskable interrupt that we need to set up for
|
||||
// a single processor or all processors.
|
||||
type MADTEntryNMI struct {
|
||||
// Processor specifies the local APIC that we need to configure for
|
||||
// this NMI. If set to 0xff we need to configure all processor APICs.
|
||||
Processor uint8
|
||||
|
||||
Flags uint16
|
||||
|
||||
// This value will be either 0 or 1 and specifies which entry in the
|
||||
// local vector table of the processor's local APIC we need to setup.
|
||||
LINT uint8
|
||||
}
|
||||
|
||||
// MADTEntryType describes the type of a MADT record.
|
||||
type MADTEntryType uint8
|
||||
|
||||
// The list of supported MADT entry types.
|
||||
const (
|
||||
MADTEntryTypeLocalAPIC MADTEntryType = iota
|
||||
MADTEntryTypeIOAPIC
|
||||
MADTEntryTypeIntSrcOverride
|
||||
MADTEntryTypeNMI
|
||||
)
|
||||
|
||||
// MADTEntry describes a MADT table entry that follows the MADT definition. As
|
||||
// MADT entries are variable sized records, this struct works as a union. The
|
||||
// consumer of this struct must check the type value before accessing the union
|
||||
// values.
|
||||
type MADTEntry struct {
|
||||
Type MADTEntryType
|
||||
Length uint8
|
||||
}
|
BIN
src/gopheros/device/acpi/table/tabletest/APIC.aml
Normal file
BIN
src/gopheros/device/acpi/table/tabletest/APIC.aml
Normal file
Binary file not shown.
BIN
src/gopheros/device/acpi/table/tabletest/DSDT.aml
Normal file
BIN
src/gopheros/device/acpi/table/tabletest/DSDT.aml
Normal file
Binary file not shown.
BIN
src/gopheros/device/acpi/table/tabletest/FACP.aml
Normal file
BIN
src/gopheros/device/acpi/table/tabletest/FACP.aml
Normal file
Binary file not shown.
BIN
src/gopheros/device/acpi/table/tabletest/SSDT.aml
Normal file
BIN
src/gopheros/device/acpi/table/tabletest/SSDT.aml
Normal file
Binary file not shown.
@ -10,6 +10,9 @@ import (
|
||||
"gopheros/kernel/hal/multiboot"
|
||||
"gopheros/kernel/kfmt"
|
||||
"sort"
|
||||
|
||||
// import and register acpi driver
|
||||
_ "gopheros/device/acpi"
|
||||
)
|
||||
|
||||
// managedDevices contains the devices discovered by the HAL.
|
||||
@ -44,7 +47,7 @@ func DetectHardware() {
|
||||
// probe executes the probe function for each driver and invokes
|
||||
// onDriverInit for each successfully initialized driver.
|
||||
func probe(driverInfoList device.DriverInfoList) {
|
||||
var w = kfmt.PrefixWriter{Sink: kfmt.GetOutputSink()}
|
||||
var w kfmt.PrefixWriter
|
||||
|
||||
for _, info := range driverInfoList {
|
||||
drv := info.Probe()
|
||||
@ -56,6 +59,7 @@ func probe(driverInfoList device.DriverInfoList) {
|
||||
major, minor, patch := drv.DriverVersion()
|
||||
kfmt.Fprintf(&strBuf, "[hal] %s(%d.%d.%d): ", drv.DriverName(), major, minor, patch)
|
||||
w.Prefix = strBuf.Bytes()
|
||||
w.Sink = kfmt.GetOutputSink()
|
||||
|
||||
if err := drv.DriverInit(&w); err != nil {
|
||||
kfmt.Fprintf(&w, "init failed: %s\n", err.Message)
|
||||
|
@ -24,3 +24,12 @@ func (f Frame) Valid() bool {
|
||||
func (f Frame) Address() uintptr {
|
||||
return uintptr(f << mem.PageShift)
|
||||
}
|
||||
|
||||
// FrameFromAddress returns a Frame that corresponds to
|
||||
// the given physical address. This function can handle
|
||||
// both page-aligned and not aligned addresses. in the
|
||||
// latter case, the input address will be rounded down
|
||||
// to the frame that contains it.
|
||||
func FrameFromAddress(physAddr uintptr) Frame {
|
||||
return Frame((physAddr & ^(uintptr(mem.PageSize - 1))) >> mem.PageShift)
|
||||
}
|
||||
|
@ -23,3 +23,21 @@ func TestFrameMethods(t *testing.T) {
|
||||
t.Error("expected InvalidFrame.Valid() to return false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFrameFromAddress(t *testing.T) {
|
||||
specs := []struct {
|
||||
input uintptr
|
||||
expFrame Frame
|
||||
}{
|
||||
{0, Frame(0)},
|
||||
{4095, Frame(0)},
|
||||
{4096, Frame(1)},
|
||||
{4123, Frame(1)},
|
||||
}
|
||||
|
||||
for specIndex, spec := range specs {
|
||||
if got := FrameFromAddress(spec.input); got != spec.expFrame {
|
||||
t.Errorf("[spec %d] expected returned frame to be %v; got %v", specIndex, spec.expFrame, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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))
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user