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/hal/multiboot"
|
||||||
"gopheros/kernel/kfmt"
|
"gopheros/kernel/kfmt"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
|
// import and register acpi driver
|
||||||
|
_ "gopheros/device/acpi"
|
||||||
)
|
)
|
||||||
|
|
||||||
// managedDevices contains the devices discovered by the HAL.
|
// managedDevices contains the devices discovered by the HAL.
|
||||||
@ -44,7 +47,7 @@ func DetectHardware() {
|
|||||||
// probe executes the probe function for each driver and invokes
|
// probe executes the probe function for each driver and invokes
|
||||||
// onDriverInit for each successfully initialized driver.
|
// onDriverInit for each successfully initialized driver.
|
||||||
func probe(driverInfoList device.DriverInfoList) {
|
func probe(driverInfoList device.DriverInfoList) {
|
||||||
var w = kfmt.PrefixWriter{Sink: kfmt.GetOutputSink()}
|
var w kfmt.PrefixWriter
|
||||||
|
|
||||||
for _, info := range driverInfoList {
|
for _, info := range driverInfoList {
|
||||||
drv := info.Probe()
|
drv := info.Probe()
|
||||||
@ -56,6 +59,7 @@ func probe(driverInfoList device.DriverInfoList) {
|
|||||||
major, minor, patch := drv.DriverVersion()
|
major, minor, patch := drv.DriverVersion()
|
||||||
kfmt.Fprintf(&strBuf, "[hal] %s(%d.%d.%d): ", drv.DriverName(), major, minor, patch)
|
kfmt.Fprintf(&strBuf, "[hal] %s(%d.%d.%d): ", drv.DriverName(), major, minor, patch)
|
||||||
w.Prefix = strBuf.Bytes()
|
w.Prefix = strBuf.Bytes()
|
||||||
|
w.Sink = kfmt.GetOutputSink()
|
||||||
|
|
||||||
if err := drv.DriverInit(&w); err != nil {
|
if err := drv.DriverInit(&w); err != nil {
|
||||||
kfmt.Fprintf(&w, "init failed: %s\n", err.Message)
|
kfmt.Fprintf(&w, "init failed: %s\n", err.Message)
|
||||||
|
@ -24,3 +24,12 @@ func (f Frame) Valid() bool {
|
|||||||
func (f Frame) Address() uintptr {
|
func (f Frame) Address() uintptr {
|
||||||
return uintptr(f << mem.PageShift)
|
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")
|
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
|
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
|
// MapTemporary establishes a temporary RW mapping of a physical memory frame
|
||||||
// to a fixed virtual address overwriting any previous mapping. The temporary
|
// to a fixed virtual address overwriting any previous mapping. The temporary
|
||||||
// mapping mechanism is primarily used by the kernel to access and initialize
|
// 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) {
|
func TestMapTemporaryErrorsAmd64(t *testing.T) {
|
||||||
if runtime.GOARCH != "amd64" {
|
if runtime.GOARCH != "amd64" {
|
||||||
t.Skip("test requires amd64 runtime; skipping")
|
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
|
// Calculate the physical address by taking the physical frame address and
|
||||||
// appending the offset from the virtual address
|
// 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
|
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