diff --git a/src/gopheros/device/acpi/acpi.go b/src/gopheros/device/acpi/acpi.go new file mode 100644 index 0000000..5042bd0 --- /dev/null +++ b/src/gopheros/device/acpi/acpi.go @@ -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, + }) +} diff --git a/src/gopheros/device/acpi/acpi_test.go b/src/gopheros/device/acpi/acpi_test.go new file mode 100644 index 0000000..de235d3 --- /dev/null +++ b/src/gopheros/device/acpi/acpi_test.go @@ -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) +} diff --git a/src/gopheros/device/acpi/table/tabletest/APIC.aml b/src/gopheros/device/acpi/table/tabletest/APIC.aml new file mode 100644 index 0000000..a7a3661 Binary files /dev/null and b/src/gopheros/device/acpi/table/tabletest/APIC.aml differ diff --git a/src/gopheros/device/acpi/table/tabletest/DSDT.aml b/src/gopheros/device/acpi/table/tabletest/DSDT.aml new file mode 100644 index 0000000..dda2995 Binary files /dev/null and b/src/gopheros/device/acpi/table/tabletest/DSDT.aml differ diff --git a/src/gopheros/device/acpi/table/tabletest/FACP.aml b/src/gopheros/device/acpi/table/tabletest/FACP.aml new file mode 100644 index 0000000..82d0a7d Binary files /dev/null and b/src/gopheros/device/acpi/table/tabletest/FACP.aml differ diff --git a/src/gopheros/device/acpi/table/tabletest/SSDT.aml b/src/gopheros/device/acpi/table/tabletest/SSDT.aml new file mode 100644 index 0000000..e659e09 Binary files /dev/null and b/src/gopheros/device/acpi/table/tabletest/SSDT.aml differ