From 78d5fac5507610e9a311afaec8ece1c1b7645983 Mon Sep 17 00:00:00 2001 From: Achilleas Anagnostopoulos Date: Mon, 28 Aug 2017 07:18:13 +0100 Subject: [PATCH] acpi: probe for RSDT and enumerate/map other ACPI tables (inc. DSDT) --- src/gopheros/device/acpi/acpi.go | 277 ++++++++++ src/gopheros/device/acpi/acpi_test.go | 487 ++++++++++++++++++ .../device/acpi/table/tabletest/APIC.aml | Bin 0 -> 84 bytes .../device/acpi/table/tabletest/DSDT.aml | Bin 0 -> 8648 bytes .../device/acpi/table/tabletest/FACP.aml | Bin 0 -> 244 bytes .../device/acpi/table/tabletest/SSDT.aml | Bin 0 -> 460 bytes 6 files changed, 764 insertions(+) create mode 100644 src/gopheros/device/acpi/acpi.go create mode 100644 src/gopheros/device/acpi/acpi_test.go create mode 100644 src/gopheros/device/acpi/table/tabletest/APIC.aml create mode 100644 src/gopheros/device/acpi/table/tabletest/DSDT.aml create mode 100644 src/gopheros/device/acpi/table/tabletest/FACP.aml create mode 100644 src/gopheros/device/acpi/table/tabletest/SSDT.aml 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 0000000000000000000000000000000000000000..a7a3661b8df11e252ffa99b1465037303c7ef13a GIT binary patch literal 84 zcmZ<^@N^DgU|?WM4s-I4P*4DojzBR+pqOK@k3u4lW?*>m55#BU0%9;9$l~M#vUnL7 RI2af}a*RA6^1wfkGyq8<3jqKC literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..dda299580f9a7a17e1f79517c5e5262d0a8baa14 GIT binary patch literal 8648 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 PHqGsuYis^s_SgRdhEg|q literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..82d0a7ddc22f53f3375baadac58285d19b932fe1 GIT binary patch literal 244 zcmYk0F%E+;3`M^~D#6APe1cwqgjP&QAR$#$EOZxaJ>V+FInJa_>e72&0LSSci;iu_*c1T!Yp0@sUdUN kX=}#*X8R1*6|VF*UP4;9^Sr{(E{e+bu;70w`($v)2EZZ|0RR91 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..e659e096abc7a873656a13f3b7ff402001c32d36 GIT binary patch literal 460 zcmWFzb_qGd$iTojH_XXDLO}sUItPS?FflMNc>0C-Xs`+>2)Ikd#0Lb$M>lf=r3`>F MK(Kfe47Lye0G?P45C8xG literal 0 HcmV?d00001