mirror of
https://github.com/taigrr/gopher-os
synced 2025-01-18 04:43:13 -08:00
acpi: probe for RSDT and enumerate/map other ACPI tables (inc. DSDT)
This commit is contained in:
parent
49dfc5c9de
commit
78d5fac550
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)
|
||||||
|
}
|
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.
Loading…
x
Reference in New Issue
Block a user