mirror of
https://github.com/taigrr/gopher-os
synced 2025-01-18 04:43:13 -08:00
304 lines
8.3 KiB
Go
304 lines
8.3 KiB
Go
package acpi
|
|
|
|
import (
|
|
"gopheros/device"
|
|
"gopheros/device/acpi/aml"
|
|
"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
|
|
|
|
// The AML interpreter used by the ACPI driver to execute various
|
|
// ACPI-related methods.
|
|
amlVM *aml.VM
|
|
}
|
|
|
|
// DriverInit initializes this driver.
|
|
func (drv *acpiDriver) DriverInit(w io.Writer) *kernel.Error {
|
|
var err *kernel.Error
|
|
|
|
if err = drv.enumerateTables(w); err != nil {
|
|
return err
|
|
}
|
|
drv.printTableInfo(w)
|
|
|
|
// Now that we have enumerated the available ACPI tables we can
|
|
// initialize the AML interpreter passing the driver instance as
|
|
// a table.Resolver.
|
|
drv.amlVM = aml.NewVM(w, drv)
|
|
if vmErr := drv.amlVM.Init(); vmErr != nil {
|
|
return &kernel.Error{
|
|
Module: "acpi",
|
|
Message: vmErr.Error(),
|
|
}
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// LookupTable implements the table.Resolver interface.
|
|
func (drv *acpiDriver) LookupTable(name string) *table.SDTHeader {
|
|
return drv.tableMap[name]
|
|
}
|
|
|
|
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 {
|
|
if len(drv.tableMap) != 0 {
|
|
return nil
|
|
}
|
|
|
|
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,
|
|
})
|
|
}
|