[kernel][acpi_lite] add new lightweight library to parse necessary kernel ACPI tables

Add a simple library that replaces the simple part of finding the root
tables and decoding the simple ones that are used in the kernel.

Next change will wire it up to the acpi tables library which has test
code validating that the low level interface is working.

Testing: validate that it finds the ACPI table on a variety of hardware
including qemu and a few AMD and Intel machines.

Bug: 32149
Change-Id: Ic798bb950d49904987953a010de5ecba5ffa2819
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/427694
Commit-Queue: David Greenaway <dgreenaway@google.com>
Testability-Review: David Greenaway <dgreenaway@google.com>
Testability-Review: Adrian Danis <adanis@google.com>
Reviewed-by: Adrian Danis <adanis@google.com>
Reviewed-by: Christopher Anderson <cja@google.com>
Reviewed-by: Travis Geiselbrecht <travisg@google.com>
diff --git a/zircon/kernel/arch/x86/BUILD.zircon.gn b/zircon/kernel/arch/x86/BUILD.zircon.gn
index a47aa76..da5767cf 100644
--- a/zircon/kernel/arch/x86/BUILD.zircon.gn
+++ b/zircon/kernel/arch/x86/BUILD.zircon.gn
@@ -173,6 +173,7 @@
       "$zx/kernel/dev/hw_rng",
       "$zx/kernel/dev/iommu/dummy",
       "$zx/kernel/dev/iommu/intel",
+      "$zx/kernel/lib/acpi_lite",
       "$zx/kernel/lib/acpi_tables",
       "$zx/kernel/lib/arch",
       "$zx/kernel/lib/cmdline",
diff --git a/zircon/kernel/lib/acpi_lite/BUILD.zircon.gn b/zircon/kernel/lib/acpi_lite/BUILD.zircon.gn
new file mode 100644
index 0000000..7fedd4d
--- /dev/null
+++ b/zircon/kernel/lib/acpi_lite/BUILD.zircon.gn
@@ -0,0 +1,11 @@
+# Copyright 2020 The Fuchsia Authors
+#
+# Use of this source code is governed by a MIT-style
+# license that can be found in the LICENSE file or at
+# https://opensource.org/licenses/MIT
+
+zx_library("acpi_lite") {
+  kernel = true
+  sources = [ "acpi_lite.cc" ]
+  deps = [ "$zx/system/ulib/pretty" ]
+}
diff --git a/zircon/kernel/lib/acpi_lite/acpi_lite.cc b/zircon/kernel/lib/acpi_lite/acpi_lite.cc
new file mode 100644
index 0000000..b23e843
--- /dev/null
+++ b/zircon/kernel/lib/acpi_lite/acpi_lite.cc
@@ -0,0 +1,274 @@
+// Copyright 2020 The Fuchsia Authors
+//
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT
+
+#include "lib/acpi_lite.h"
+
+#include <inttypes.h>
+#include <trace.h>
+#include <zircon/compiler.h>
+
+#include <pretty/hexdump.h>
+#include <vm/physmap.h>
+
+#define LOCAL_TRACE 0
+
+// global state of the acpi lite library
+struct acpi_lite_state {
+  const acpi_rsdp* rsdp;
+  const acpi_rsdt_xsdt* sdt;
+  size_t num_tables;  // number of top level tables
+  bool xsdt;          // are the pointers 64 or 32bit?
+} acpi;
+
+static const void* phys_to_ptr(uintptr_t pa) {
+  if (!is_physmap_phys_addr(pa)) {
+    return nullptr;
+  }
+  // consider 0 to be invalid
+  if (pa == 0) {
+    return nullptr;
+  }
+
+  return static_cast<const void*>(paddr_to_physmap(pa));
+}
+
+static uint8_t acpi_checksum(const void* _buf, size_t len) {
+  uint8_t c = 0;
+
+  const uint8_t* buf = static_cast<const uint8_t*>(_buf);
+  for (size_t i = 0; i < len; i++) {
+    c = (uint8_t)(c + buf[i]);
+  }
+
+  return c;
+}
+
+static bool validate_rsdp(const acpi_rsdp* rsdp) {
+  // check the signature
+  if (memcmp(ACPI_RSDP_SIG, rsdp->sig, 8)) {
+    return false;
+  }
+
+  // validate the v1 checksum on the first 20 bytes of the table
+  uint8_t c = acpi_checksum(rsdp, 20);
+  if (c) {
+    return false;
+  }
+
+  // is it v2?
+  if (rsdp->revision >= 2) {
+    if (rsdp->length < 36 || rsdp->length > 4096) {
+      // keep the table length within reason
+      return false;
+    }
+
+    c = acpi_checksum(rsdp, rsdp->length);
+    if (c) {
+      return false;
+    }
+  }
+
+  // seems okay
+  return true;
+}
+
+static zx_paddr_t find_rsdp_pc() {
+  // search for it in the BIOS EBDA area (0xe0000..0xfffff) on 16 byte boundaries
+  for (zx_paddr_t ptr = 0xe0000; ptr <= 0xfffff; ptr += 16) {
+    const auto rsdp = static_cast<const acpi_rsdp*>(phys_to_ptr(ptr));
+
+    if (validate_rsdp(rsdp)) {
+      return ptr;
+    }
+  }
+
+  return 0;
+}
+
+static bool validate_sdt(const acpi_rsdt_xsdt* sdt, size_t* num_tables, bool* xsdt) {
+  LTRACEF("%p\n", sdt);
+
+  // bad pointer
+  if (!sdt) {
+    return false;
+  }
+
+  // check the signature and see if it's a rsdt or xsdt
+  if (!memcmp(sdt->header.sig, "XSDT", 4)) {
+    *xsdt = true;
+  } else if (!memcmp(sdt->header.sig, "RSDT", 4)) {
+    *xsdt = false;
+  } else {
+    return false;
+  }
+
+  // is the length sane?
+  if (sdt->header.length < 36 || sdt->header.length > 4096) {
+    return false;
+  }
+
+  // is it a revision we understand?
+  if (sdt->header.revision != 1) {
+    return false;
+  }
+
+  // checksum the entire table
+  uint8_t c = acpi_checksum(sdt, sdt->header.length);
+  if (c) {
+    return false;
+  }
+
+  // compute the number of pointers to tables we have
+  *num_tables = (sdt->header.length - 36u) / (*xsdt ? 8u : 4u);
+
+  // looks okay
+  return true;
+}
+
+const acpi_sdt_header* acpi_get_table_at_index(size_t index) {
+  if (index >= acpi.num_tables) {
+    return nullptr;
+  }
+
+  zx_paddr_t pa;
+  if (acpi.xsdt) {
+    pa = acpi.sdt->addr64[index];
+  } else {
+    pa = acpi.sdt->addr32[index];
+  }
+
+  return static_cast<const acpi_sdt_header*>(phys_to_ptr(pa));
+}
+
+const acpi_sdt_header* acpi_get_table_by_sig(const char* sig) {
+  // walk the list of tables
+  for (size_t i = 0; i < acpi.num_tables; i++) {
+    const auto header = acpi_get_table_at_index(i);
+    if (!header) {
+      continue;
+    }
+
+    if (!memcmp(sig, header->sig, 4)) {
+      // validate the checksum
+      uint8_t c = acpi_checksum(header, header->length);
+      if (c == 0) {
+        return header;
+      }
+    }
+  }
+
+  return nullptr;
+}
+
+zx_status_t acpi_lite_init(zx_paddr_t rsdp_pa) {
+  LTRACEF("passed in rsdp %#" PRIxPTR "\n", rsdp_pa);
+
+  // see if the rsdp pointer is valid
+  if (rsdp_pa == 0) {
+    // search around for it in a platform-specific way
+#if __x86_64__
+    rsdp_pa = find_rsdp_pc();
+    if (rsdp_pa == 0) {
+      dprintf(INFO, "ACPI LITE: couldn't find ACPI RSDP in BIOS area\n");
+    }
+#endif
+
+    if (rsdp_pa == 0) {
+      return ZX_ERR_NOT_FOUND;
+    }
+  }
+
+  const void* ptr = phys_to_ptr(rsdp_pa);
+  if (!ptr) {
+    dprintf(INFO, "ACPI LITE: failed to translate RSDP address %#" PRIxPTR " to virtual\n",
+            rsdp_pa);
+    return ZX_ERR_NOT_FOUND;
+  }
+
+  // see if the RSDP is there
+  acpi.rsdp = static_cast<const acpi_rsdp*>(ptr);
+  if (!validate_rsdp(acpi.rsdp)) {
+    dprintf(INFO, "ACPI LITE: RSDP structure does not check out\n");
+    return ZX_ERR_NOT_FOUND;
+  }
+
+  dprintf(SPEW, "ACPI LITE: RSDP checks out, found at %#lx\n", rsdp_pa);
+
+  // find the pointer to either the RSDT or XSDT
+  acpi.sdt = nullptr;
+  if (acpi.rsdp->revision < 2) {
+    // v1 RSDP, pointing at a RSDT
+    acpi.sdt = static_cast<const acpi_rsdt_xsdt*>(phys_to_ptr(acpi.rsdp->rsdt_address));
+  } else {
+    // v2+ RSDP, pointing at a XSDT
+    // try to use the 64bit address first
+    acpi.sdt = static_cast<const acpi_rsdt_xsdt*>(phys_to_ptr(acpi.rsdp->xsdt_address));
+    if (!acpi.sdt) {
+      acpi.sdt = static_cast<const acpi_rsdt_xsdt*>(phys_to_ptr(acpi.rsdp->rsdt_address));
+    }
+  }
+
+  if (!validate_sdt(acpi.sdt, &acpi.num_tables, &acpi.xsdt)) {
+    dprintf(INFO, "ACPI LITE: RSDT/XSDT structure does not check out\n");
+    return ZX_ERR_NOT_FOUND;
+  }
+
+  dprintf(SPEW, "ACPI LITE: RSDT/XSDT checks out, %zu tables\n", acpi.num_tables);
+
+  if (LOCAL_TRACE) {
+    acpi_lite_dump_tables();
+  }
+
+  return ZX_OK;
+}
+
+void acpi_lite_dump_tables() {
+  if (!acpi.sdt) {
+    return;
+  }
+
+  printf("root table:\n");
+  hexdump(acpi.sdt, acpi.sdt->header.length);
+
+  // walk the table list
+  for (size_t i = 0; i < acpi.num_tables; i++) {
+    const auto header = acpi_get_table_at_index(i);
+    if (!header) {
+      continue;
+    }
+
+    printf("table %zu: '%c%c%c%c' len %u\n", i, header->sig[0], header->sig[1], header->sig[2],
+           header->sig[3], header->length);
+    hexdump(header, header->length);
+  }
+}
+
+zx_status_t acpi_process_madt_entries_etc(const uint8_t search_type,
+                                          const MadtEntryCallback& callback) {
+  const acpi_madt_table* madt =
+      reinterpret_cast<const acpi_madt_table*>(acpi_get_table_by_sig(ACPI_MADT_SIG));
+  if (!madt) {
+    return ZX_ERR_NOT_FOUND;
+  }
+
+  // bytewise array of the same table
+  const uint8_t* madt_array = reinterpret_cast<const uint8_t*>(madt);
+
+  // walk the table off the end of the header, looking for the requested type
+  size_t off = sizeof(*madt);
+  while (off < madt->header.length) {
+    uint8_t type = madt_array[off];
+    uint8_t length = madt_array[off + 1];
+
+    if (type == search_type) {
+      callback(static_cast<const void*>(&madt_array[off]), length);
+    }
+
+    off += length;
+  }
+
+  return ZX_OK;
+}
diff --git a/zircon/kernel/lib/acpi_lite/include/lib/acpi_lite.h b/zircon/kernel/lib/acpi_lite/include/lib/acpi_lite.h
new file mode 100644
index 0000000..6c5e752
--- /dev/null
+++ b/zircon/kernel/lib/acpi_lite/include/lib/acpi_lite.h
@@ -0,0 +1,276 @@
+// Copyright 2020 The Fuchsia Authors
+//
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT
+
+#ifndef ZIRCON_KERNEL_LIB_ACPI_LITE_INCLUDE_LIB_ACPI_LITE_H_
+#define ZIRCON_KERNEL_LIB_ACPI_LITE_INCLUDE_LIB_ACPI_LITE_H_
+
+#include <stdint.h>
+#include <zircon/types.h>
+
+#include <fbl/function.h>
+
+// Root System Description Pointer (RSDP)
+//
+// Reference: ACPI v6.3 Section 5.2.5.3.
+struct acpi_rsdp {
+  uint8_t sig[8];
+  uint8_t checksum;
+  uint8_t oemid[6];
+  uint8_t revision;
+  uint32_t rsdt_address;
+
+  // rev 2+
+  uint32_t length;
+  uint64_t xsdt_address;
+  uint8_t extended_checksum;
+  uint8_t reserved[3];
+} __PACKED;
+static_assert(sizeof(acpi_rsdp) == 36);
+
+#define ACPI_RSDP_SIG "RSD PTR "
+
+// Standard system description table header, used as the header of
+// multiple structures below.
+//
+// Reference: ACPI v6.3 Section 5.2.6.
+struct acpi_sdt_header {
+  uint8_t sig[4];
+  uint32_t length;
+  uint8_t revision;
+  uint8_t checksum;
+  uint8_t oemid[6];
+  uint8_t oem_table_id[8];
+  uint32_t oem_revision;
+  uint32_t creator_id;
+  uint32_t creator_revision;
+} __PACKED;
+static_assert(sizeof(acpi_sdt_header) == 36);
+
+// Root System Description Table (RSDT) and Extended System Description Table (XSDT)
+//
+// Reference: ACPI v6.3 Section 5.2.7 -- 5.2.8.
+struct acpi_rsdt_xsdt {
+  acpi_sdt_header header;
+
+  // array of uint32s or uint64 addresses are placed immediately afterwards
+  union {
+    uint32_t addr32[0];
+    uint64_t addr64[0];
+  };
+} __PACKED;
+static_assert(sizeof(acpi_rsdt_xsdt) == 36);
+
+// ACPI Generic Address
+//
+// Reference: ACPI v6.3 Section 5.2.3.2
+struct acpi_generic_address {
+  uint8_t address_space_id;
+  uint8_t register_bit_width;
+  uint8_t register_bit_offset;
+  uint8_t access_size;
+  uint64_t address;
+} __PACKED;
+static_assert(sizeof(acpi_generic_address) == 12);
+
+#define ACPI_ADDR_SPACE_MEMORY 0
+#define ACPI_ADDR_SPACE_IO 1
+
+#define ACPI_RSDT_SIG "RSDT"
+#define ACPI_XSDT_SIG "XSDT"
+
+// Multiple APIC Description Table
+//
+// The table is followed by interrupt control structures, each with
+// a "acpi_sub_table_header" header.
+//
+// Reference: ACPI v6.3 5.2.12.
+struct acpi_madt_table {
+  acpi_sdt_header header;
+
+  uint32_t local_int_controller_address;
+  uint32_t flags;
+} __PACKED;
+static_assert(sizeof(acpi_madt_table) == 44);
+
+#define ACPI_MADT_SIG "APIC"
+
+struct acpi_sub_table_header {
+  uint8_t type;
+  uint8_t length;
+} __PACKED;
+static_assert(sizeof(acpi_sub_table_header) == 2);
+
+// High Precision Event Timer Table
+//
+// Reference: IA-PC HPET (High Precision Event Timers) v1.0a, Section 3.2.4.
+#define ACPI_HPET_SIG "HPET"
+struct acpi_hpet_table {
+  acpi_sdt_header header;
+  uint32_t id;
+  acpi_generic_address address;
+  uint8_t sequence;
+  uint16_t minimum_tick;
+  uint8_t flags;
+} __PACKED;
+static_assert(sizeof(acpi_hpet_table) == 56);
+
+// SRAT table and descriptors.
+//
+// Reference: ACPI v6.3 Section 5.2.16.
+#define ACPI_SRAT_SIG "SRAT"
+struct acpi_srat_table {
+  acpi_sdt_header header;
+  uint32_t _reserved;  // should be 1
+  uint64_t _reserved2;
+} __PACKED;
+static_assert(sizeof(acpi_srat_table) == 48);
+
+// Type 0: processor local apic/sapic affinity structure
+//
+// Reference: ACPI v6.3 Section 5.2.16.1.
+#define ACPI_SRAT_TYPE_PROCESSOR_AFFINITY 0
+struct acpi_srat_processor_affinity_entry {
+  acpi_sub_table_header header;
+  uint8_t proximity_domain_low;
+  uint8_t apic_id;
+  uint32_t flags;
+  uint8_t sapic_eid;
+  uint8_t proximity_domain_high[3];
+  uint32_t clock_domain;
+} __PACKED;
+static_assert(sizeof(acpi_srat_processor_affinity_entry) == 16);
+
+#define ACPI_SRAT_FLAG_ENABLED 1
+
+// Type 1: memory affinity structure
+//
+// Reference: ACPI v6.3 Section 5.2.16.2.
+#define ACPI_SRAT_TYPE_MEMORY_AFFINITY 1
+struct acpi_srat_memory_affinity_entry {
+  acpi_sub_table_header header;
+  uint32_t proximity_domain;
+  uint16_t _reserved;
+  uint32_t base_address_low;
+  uint32_t base_address_high;
+  uint32_t length_low;
+  uint32_t length_high;
+  uint32_t _reserved2;
+  uint32_t flags;
+  uint32_t _reserved3;
+  uint32_t _reserved4;
+} __PACKED;
+static_assert(sizeof(acpi_srat_memory_affinity_entry) == 40);
+
+// Type 2: processor x2apic affinity structure
+//
+// Reference: ACPI v6.3 Section 5.2.16.3.
+#define ACPI_SRAT_TYPE_PROCESSOR_X2APIC_AFFINITY 2
+struct acpi_srat_processor_x2apic_affinity_entry {
+  acpi_sub_table_header header;
+  uint16_t _reserved;
+  uint32_t proximity_domain;
+  uint32_t x2apic_id;
+  uint32_t flags;
+  uint32_t clock_domain;
+  uint32_t _reserved2;
+} __PACKED;
+static_assert(sizeof(acpi_srat_processor_x2apic_affinity_entry) == 24);
+
+zx_status_t acpi_lite_init(zx_paddr_t rsdt);
+void acpi_lite_dump_tables();
+
+const acpi_sdt_header* acpi_get_table_by_sig(const char* sig);
+const acpi_sdt_header* acpi_get_table_at_index(size_t index);
+
+// Multiple APIC Description Table (MADT) entries.
+
+// MADT entry type 0: Processor Local APIC (ACPI v6.3 Section 5.2.12.2)
+#define ACPI_MADT_TYPE_LOCAL_APIC 0
+struct acpi_madt_local_apic_entry {
+  acpi_sub_table_header header;
+  uint8_t processor_id;
+  uint8_t apic_id;
+  uint32_t flags;
+} __PACKED;
+static_assert(sizeof(acpi_madt_local_apic_entry) == 8);
+
+#define ACPI_MADT_FLAG_ENABLED 0x1
+
+// MADT entry type 1: I/O APIC (ACPI v6.3 Section 5.2.12.3)
+#define ACPI_MADT_TYPE_IO_APIC 1
+struct acpi_madt_io_apic_entry {
+  acpi_sub_table_header header;
+  uint8_t io_apic_id;
+  uint8_t reserved;
+  uint32_t io_apic_address;
+  uint32_t global_system_interrupt_base;
+} __PACKED;
+static_assert(sizeof(acpi_madt_io_apic_entry) == 12);
+
+// MADT entry type 2: Interrupt Source Override (ACPI v6.3 Section 5.2.12.5)
+#define ACPI_MADT_TYPE_INT_SOURCE_OVERRIDE 2
+struct acpi_madt_int_source_override_entry {
+  acpi_sub_table_header header;
+  uint8_t bus;
+  uint8_t source;
+  uint32_t global_sys_interrupt;
+  uint16_t flags;
+} __PACKED;
+static_assert(sizeof(acpi_madt_int_source_override_entry) == 10);
+
+#define ACPI_MADT_FLAG_POLARITY_CONFORMS 0b00
+#define ACPI_MADT_FLAG_POLARITY_HIGH 0b01
+#define ACPI_MADT_FLAG_POLARITY_LOW 0b11
+#define ACPI_MADT_FLAG_POLARITY_MASK 0b11
+
+#define ACPI_MADT_FLAG_TRIGGER_CONFORMS 0b0000
+#define ACPI_MADT_FLAG_TRIGGER_EDGE 0b0100
+#define ACPI_MADT_FLAG_TRIGGER_LEVEL 0b1100
+#define ACPI_MADT_FLAG_TRIGGER_MASK 0b1100
+
+// DBG2 table
+#define ACPI_DBG2_SIG "DBG2"
+struct acpi_dbg2_table {
+  acpi_sdt_header header;
+  uint32_t offset;
+  uint32_t num_entries;
+} __PACKED;
+static_assert(sizeof(acpi_dbg2_table) == 44);
+
+struct acpi_dbg2_device {
+  uint8_t revision;
+  uint16_t length;
+  uint8_t register_count;
+  uint16_t namepath_length;
+  uint16_t namepath_offset;
+  uint16_t oem_data_length;
+  uint16_t oem_data_offset;
+  uint16_t port_type;
+  uint16_t port_subtype;
+  uint16_t reserved;
+  uint16_t base_address_offset;
+  uint16_t address_size_offset;
+} __PACKED;
+static_assert(sizeof(acpi_dbg2_device) == 22);
+
+// debug port types
+#define ACPI_DBG2_TYPE_SERIAL_PORT 0x8000
+#define ACPI_DBG2_TYPE_1394_PORT 0x8001
+#define ACPI_DBG2_TYPE_USB_PORT 0x8002
+#define ACPI_DBG2_TYPE_NET_PORT 0x8003
+
+// debug port subtypes
+#define ACPI_DBG2_SUBTYPE_16550_COMPATIBLE 0x0000
+#define ACPI_DBG2_SUBTYPE_16550_SUBSET 0x0001
+#define ACPI_DBG2_SUBTYPE_1394_STANDARD 0x0000
+#define ACPI_DBG2_SUBTYPE_USB_XHCI 0x0000
+#define ACPI_DBG2_SUBTYPE_USB_EHCI 0x0001
+
+// A routine to iterate over all the MADT entries of a particular type via a callback
+using MadtEntryCallback = fbl::Function<void(const void* entry, size_t entry_len)>;
+zx_status_t acpi_process_madt_entries_etc(uint8_t search_type, const MadtEntryCallback&);
+
+#endif  // ZIRCON_KERNEL_LIB_ACPI_LITE_INCLUDE_LIB_ACPI_LITE_H_