diff --git a/repos/base/run/platform_drv.inc b/repos/base/run/platform_drv.inc
index 63b6dd2a63..ea2ed0d98b 100644
--- a/repos/base/run/platform_drv.inc
+++ b/repos/base/run/platform_drv.inc
@@ -230,7 +230,8 @@ proc platform_drv_config {} {
- }
+
+ }
append_if [expr {[acpi_drv_name] eq "acpica"}] drv_config {
}
diff --git a/repos/os/include/os/smbios.h b/repos/os/include/os/smbios.h
new file mode 100644
index 0000000000..c4ca242b15
--- /dev/null
+++ b/repos/os/include/os/smbios.h
@@ -0,0 +1,293 @@
+/*
+ * \brief Utilities for accessing information of the System Management BIOS
+ * \author Martin Stein
+ * \date 2019-07-02
+ */
+
+/*
+ * Copyright (C) 2019 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU Affero General Public License version 3.
+ */
+
+#ifndef _OS__SMBIOS_H_
+#define _OS__SMBIOS_H_
+
+/* Genode includes */
+#include
+#include
+#include
+
+namespace Genode {
+
+ namespace Smbios_table { };
+
+ inline bool smbios_checksum_correct(uint8_t const *base, uint8_t size)
+ {
+ uint8_t sum { 0 };
+ for (uint8_t idx = 0; idx < size; idx++) {
+ sum += base[idx];
+ }
+ return sum == 0;
+ }
+
+ struct Smbios_3_entry_point;
+ struct Smbios_entry_point;
+ struct Dmi_entry_point;
+ struct Smbios_structure;
+}
+
+
+struct Genode::Smbios_structure
+{
+ enum Type {
+ BIOS = 0,
+ SYSTEM = 1,
+ BASE_BOARD = 2,
+ };
+
+ uint8_t type;
+ uint8_t length;
+ uint16_t handle;
+
+} __attribute__((packed));
+
+
+struct Genode::Dmi_entry_point
+{
+ enum { LENGTH = 15 };
+
+ uint8_t anchor_string[5];
+ uint8_t checksum;
+ uint16_t struct_table_length;
+ uint32_t struct_table_addr;
+ uint16_t nr_of_structs;
+ uint8_t bcd_revision;
+
+ bool checksum_correct() const
+ {
+ return smbios_checksum_correct(anchor_string, LENGTH);
+ }
+
+} __attribute__((packed));
+
+
+struct Genode::Smbios_3_entry_point
+{
+ enum { MAX_LENGTH = 32 };
+
+ uint8_t anchor_string[5];
+ uint8_t checksum;
+ uint8_t length;
+ uint8_t version_major;
+ uint8_t version_minor;
+ uint8_t docrev;
+ uint8_t revision;
+ uint8_t reserved_0;
+ uint32_t struct_table_max_size;
+ uint64_t struct_table_addr;
+
+ bool length_valid() const
+ {
+ return length <= MAX_LENGTH;
+ }
+
+ bool checksum_correct() const
+ {
+ return smbios_checksum_correct(anchor_string, length);
+ }
+
+} __attribute__((packed));
+
+
+struct Genode::Smbios_entry_point
+{
+ enum { MAX_LENGTH = 32 };
+ enum { INTERM_LENGTH = 15 };
+
+ uint8_t anchor_string[4];
+ uint8_t checksum;
+ uint8_t length;
+ uint8_t version_major;
+ uint8_t version_minor;
+ uint16_t max_struct_size;
+ uint8_t revision;
+ uint8_t formatted_area[5];
+ uint8_t interm_anchor_string[5];
+ uint8_t interm_checksum;
+ uint16_t struct_table_length;
+ uint32_t struct_table_addr;
+ uint16_t nr_of_structs;
+ uint8_t bcd_revision;
+
+ bool length_valid() const
+ {
+ return length <= MAX_LENGTH;
+ }
+
+ bool checksum_correct() const
+ {
+ return smbios_checksum_correct(anchor_string, length);
+ }
+
+ bool interm_checksum_correct() const
+ {
+ return smbios_checksum_correct(interm_anchor_string, INTERM_LENGTH);
+ }
+
+ Dmi_entry_point const &dmi_ep() const
+ {
+ return *(Dmi_entry_point *)&interm_anchor_string;
+ }
+
+} __attribute__((packed));
+
+
+namespace Genode::Smbios_table
+{
+ template
+ bool smbios_3(addr_t const anchor,
+ addr_t const ep_phy,
+ PHY_MEM_FUNC const &phy_mem,
+ EP_FUNC const &handle_ep)
+ {
+ if (memcmp((char *)anchor, "_SM3_", 5)) {
+ return false;
+ }
+ Smbios_3_entry_point const &ep { *(Smbios_3_entry_point *)
+ phy_mem(ep_phy, sizeof(Smbios_3_entry_point)) };
+
+ if (!ep.length_valid()) {
+ warning("SMBIOS 3 entry point has bad length");
+ return false;
+ }
+ if (!ep.checksum_correct()) {
+ warning("SMBIOS 3 entry point has bad checksum");
+ return false;
+ }
+ if (ep.struct_table_addr > (uint64_t)(~(addr_t)0)) {
+ warning("SMBIOS 3 entry point has bad structure-table address");
+ return false;
+ }
+ log("SMBIOS 3 table (entry point: ", Hex(anchor), " structures: ", Hex(ep.struct_table_addr), ")");
+ handle_ep(ep);
+ return true;
+ }
+
+ template
+ bool smbios(addr_t const anchor,
+ addr_t const ep_phy,
+ PHY_MEM_FUNC const &phy_mem,
+ EP_FUNC const &handle_ep)
+ {
+ if (memcmp((char *)anchor, "_SM_", 4)) {
+ return false;
+ }
+ Smbios_entry_point const &ep { *(Smbios_entry_point *)
+ phy_mem(ep_phy, sizeof(Smbios_entry_point)) };
+
+ if (!ep.length_valid()) {
+ warning("SMBIOS entry point has bad length");
+ return false;
+ }
+ if (!ep.checksum_correct()) {
+ warning("SMBIOS entry point has bad checksum");
+ return false;
+ }
+ if (String<6>((char const *)&ep.interm_anchor_string) != "_DMI_") {
+ warning("SMBIOS entry point has bad intermediate anchor string");
+ return false;
+ }
+ if (!ep.interm_checksum_correct()) {
+ warning("SMBIOS entry point has bad intermediate checksum");
+ return false;
+ }
+ log("SMBIOS table (entry point: ", Hex(anchor), " structures: ", Hex(ep.struct_table_addr), ")");
+ handle_ep(ep);
+ return true;
+ }
+
+ template
+ bool dmi(addr_t const anchor,
+ addr_t const ep_phy,
+ PHY_MEM_FUNC const &phy_mem,
+ EP_FUNC const &handle_ep)
+ {
+ if (memcmp((char *)anchor, "_DMI_", 5)) {
+ return false;
+ }
+ Dmi_entry_point const &ep { *(Dmi_entry_point *)
+ phy_mem(ep_phy, sizeof(Dmi_entry_point)) };
+
+ if (!ep.checksum_correct()) {
+ warning("DMI entry point has bad checksum");
+ return false;
+ }
+ log("DMI table (entry point: ", Hex(anchor), " structures: ", Hex(ep.struct_table_addr), ")");
+ handle_ep(ep);
+ return true;
+ }
+
+ template
+ void from_scan(PHY_MEM_FUNC const &phy_mem,
+ SMBIOS_3_FUNC const &handle_smbios_3_ep,
+ SMBIOS_FUNC const &handle_smbios_ep,
+ DMI_FUNC const &handle_dmi_ep)
+ {
+ enum { SCAN_BASE_PHY = 0xf0000 };
+ enum { SCAN_SIZE = 0x10000 };
+ enum { SCAN_SIZE_SMBIOS = 0xfff0 };
+ enum { SCAN_STEP = 0x10 };
+
+ addr_t const scan_base { (addr_t)phy_mem(SCAN_BASE_PHY, SCAN_SIZE) };
+ try {
+ addr_t const scan_end { scan_base + SCAN_SIZE };
+ size_t const scan_end_smbios { scan_base + SCAN_SIZE_SMBIOS };
+
+ for (addr_t curr { scan_base }; curr < scan_end_smbios; curr += SCAN_STEP ) {
+ if (smbios_3(curr, SCAN_BASE_PHY + (curr - scan_base), phy_mem, handle_smbios_3_ep)) {
+ return;
+ }
+ }
+ for (addr_t curr { scan_base }; curr < scan_end_smbios; curr += SCAN_STEP ) {
+ if (smbios(curr, SCAN_BASE_PHY + (curr - scan_base), phy_mem, handle_smbios_ep)) {
+ return;
+ }
+ }
+ for (addr_t curr { scan_base }; curr < scan_end; curr += SCAN_STEP ) {
+ if (dmi(curr, SCAN_BASE_PHY + (curr - scan_base), phy_mem, handle_dmi_ep)) {
+ return;
+ }
+ }
+ } catch (...) { }
+ }
+
+ template
+ void from_pointer(addr_t const table_phy,
+ PHY_MEM_FUNC const &phy_mem,
+ SMBIOS_3_FUNC const &handle_smbios_3_ep,
+ SMBIOS_FUNC const &handle_smbios_ep,
+ DMI_FUNC const &handle_dmi_ep)
+ {
+ addr_t const anchor { (addr_t)phy_mem(table_phy, 5) };
+ if (smbios_3(anchor, table_phy, phy_mem, handle_smbios_3_ep)) {
+ return;
+ }
+ if (smbios(anchor, table_phy, phy_mem, handle_smbios_ep)) {
+ return;
+ }
+ dmi(anchor, table_phy, phy_mem, handle_dmi_ep);
+ }
+};
+
+#endif /* _OS__SMBIOS_H_ */
diff --git a/repos/os/src/drivers/acpi/efi_system_table.h b/repos/os/src/drivers/acpi/efi_system_table.h
new file mode 100644
index 0000000000..b01ad2c28d
--- /dev/null
+++ b/repos/os/src/drivers/acpi/efi_system_table.h
@@ -0,0 +1,86 @@
+/*
+ * \brief Utilities for accessing an EFI system table
+ * \author Martin Stein
+ * \date 2019-07-02
+ */
+
+/*
+ * Copyright (C) 2019 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU Affero General Public License version 3.
+ */
+
+#ifndef _EFI_SYSTEM_TABLE_H_
+#define _EFI_SYSTEM_TABLE_H_
+
+/* Genode includes */
+#include
+
+namespace Genode { struct Efi_system_table; }
+
+struct Genode::Efi_system_table
+{
+ struct Header
+ {
+ uint64_t signature;
+ uint32_t revision;
+ uint32_t header_size;
+ uint32_t crc32;
+ uint32_t reserved;
+
+ } __attribute__((packed));
+
+ struct Guid
+ {
+ uint32_t data_1;
+ uint16_t data_2;
+ uint16_t data_3;
+ uint8_t data_4[8];
+
+ } __attribute__((packed));
+
+ struct Configuration_table
+ {
+ Guid vendor_guid;
+ uint64_t vendor_table;
+
+ } __attribute__((packed));
+
+ Header header;
+ uint64_t firmware_vendor;
+ uint32_t firmware_revision;
+ uint32_t reserved_0;
+ uint64_t console_in_handle;
+ uint64_t console_in;
+ uint64_t console_out_handle;
+ uint64_t console_out;
+ uint64_t standard_error_handle;
+ uint64_t standard_error;
+ uint64_t runtime_services;
+ uint64_t boot_services;
+ uint64_t nr_of_table_entries;
+ uint64_t config_table;
+
+ template
+ void for_smbios_table(PHY_MEM_FN const &phy_mem,
+ HANDLE_TABLE_FN const &handle_table) const
+ {
+ Guid const guid { 0xeb9d2d31, 0x2d88, 0x11d3,
+ { 0x9a, 0x16, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d } };
+
+ Configuration_table const *cfg_table { (Configuration_table *)
+ phy_mem(config_table, nr_of_table_entries *
+ sizeof(Configuration_table)) };
+
+ for (unsigned long idx = 0; idx < nr_of_table_entries; idx++) {
+ if (memcmp(&guid, &cfg_table[idx].vendor_guid, sizeof(Guid)) == 0) {
+ handle_table((addr_t)cfg_table[idx].vendor_table);
+ }
+ }
+ }
+
+} __attribute__((packed));
+
+#endif /* _EFI_SYSTEM_TABLE_H_ */
diff --git a/repos/os/src/drivers/acpi/main.cc b/repos/os/src/drivers/acpi/main.cc
index b1a49b9bf4..15e21405b6 100644
--- a/repos/os/src/drivers/acpi/main.cc
+++ b/repos/os/src/drivers/acpi/main.cc
@@ -19,6 +19,7 @@
/* local includes */
#include
+#include
namespace Acpi {
@@ -29,8 +30,9 @@ namespace Acpi {
struct Acpi::Main
{
- Genode::Env &env;
- Genode::Heap heap { env.ram(), env.rm() };
+ Genode::Env &env;
+ Genode::Heap heap { env.ram(), env.rm() };
+ Smbios_table_reporter smbt_reporter { env, heap };
Main(Env &env) : env(env)
{
diff --git a/repos/os/src/drivers/acpi/smbios_table_reporter.cc b/repos/os/src/drivers/acpi/smbios_table_reporter.cc
new file mode 100644
index 0000000000..f2be916676
--- /dev/null
+++ b/repos/os/src/drivers/acpi/smbios_table_reporter.cc
@@ -0,0 +1,171 @@
+/*
+ * \brief Finding and reporting an SMBIOS table as is (plain data)
+ * \author Martin Stein
+ * \date 2019-07-09
+ */
+
+ /*
+ * Copyright (C) 2019 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU Affero General Public License version 3.
+ */
+
+/* local includes */
+#include
+#include
+
+/* Genode includes */
+#include
+#include
+#include
+#include
+
+using namespace Genode;
+
+constexpr size_t get_page_size_log2() { return 12; }
+constexpr size_t get_page_size() { return 1 << get_page_size_log2(); }
+
+
+Smbios_table_reporter::Smbios_table_reporter(Env &env,
+ Allocator &alloc)
+{
+ struct Io_region
+ {
+ Env &_env;
+ addr_t _base_page;
+ size_t _size_pages;
+ Fifo > &_fifo;
+ Attached_io_mem_dataspace _io_mem { _env, _base_page, _size_pages };
+ Fifo_element _fifo_elem { *this };
+
+ Io_region(Env &env,
+ addr_t base_page,
+ size_t size_pages,
+ Fifo > &fifo)
+ :
+ _env { env },
+ _base_page { base_page },
+ _size_pages { size_pages },
+ _fifo { fifo }
+ {
+ _fifo.enqueue(_fifo_elem);
+ }
+
+ ~Io_region()
+ {
+ _fifo.remove(_fifo_elem);
+ }
+ };
+
+ Fifo > io_regions;
+ addr_t const page_mask { ~(addr_t)((1 << get_page_size_log2()) - 1) };
+ addr_t const page_off_mask { get_page_size() - 1 };
+ auto phy_mem = [&] (addr_t base, size_t size) {
+
+ addr_t const end { base + size };
+ Io_region *reuse_io { nullptr };
+ io_regions.for_each([&] (Fifo_element &elem) {
+ Io_region &io { elem.object() };
+ if (io._base_page <= base &&
+ io._base_page + io._size_pages >= end) {
+
+ reuse_io = &io;
+ }
+ });
+ if (reuse_io) {
+ addr_t const off { base - reuse_io->_base_page };
+ return (addr_t)reuse_io->_io_mem.local_addr() + off;
+ }
+ addr_t const base_page { base & page_mask };
+ addr_t const base_off { base - base_page };
+ size += base_off;
+ size_t const size_pages { (size + page_off_mask) & page_mask };
+ addr_t alloc_base { base_page };
+ addr_t alloc_end { base_page + size_pages };
+
+ io_regions.for_each([&] (Fifo_element &elem) {
+
+ Io_region &io { elem.object() };
+ addr_t const io_base { io._base_page };
+ addr_t const io_end { io._base_page + io._size_pages };
+ bool io_destroy { false };
+
+ if (io_base < alloc_base && io_end > alloc_base) {
+ alloc_base = io_base;
+ io_destroy = true;
+ }
+ if (io_base < alloc_end && io_end > alloc_end) {
+ alloc_end = io_end;
+ io_destroy = true;
+ }
+ if (io_base >= alloc_base && io_end <= alloc_end) {
+ io_destroy = true;
+ }
+ if (io_destroy) {
+ destroy(&alloc, &io);
+ }
+ });
+ size_t alloc_size { alloc_end - alloc_base };
+ Io_region *io {
+ new (alloc) Io_region(env, alloc_base, alloc_size, io_regions) };
+
+ addr_t const off { base - io->_base_page };
+ return (addr_t)io->_io_mem.local_addr() + off;
+ };
+ auto report_smbios = [&] (void *ep_vir, size_t ep_size,
+ addr_t st_phy, size_t st_size)
+ {
+ addr_t const st_vir { phy_mem(st_phy, st_size) };
+ size_t const ram_size { ep_size + st_size };
+ addr_t const ram_vir { (addr_t)alloc.alloc(ram_size) };
+
+ memcpy((void *)ram_vir, ep_vir, ep_size);
+ memcpy((void *)(ram_vir + ep_size), (void *)st_vir, st_size);
+
+ _reporter.construct(env, "smbios_table", "smbios_table", ram_size);
+ _reporter->enabled(true);
+ _reporter->report((void *)ram_vir, ram_size);
+
+ alloc.free((void *)ram_vir, ep_size + st_size);
+ };
+ auto handle_smbios_3 = [&] (Smbios_3_entry_point const &ep)
+ {
+ report_smbios((void *)&ep, ep.length, ep.struct_table_addr,
+ ep.struct_table_max_size);
+ };
+ auto handle_smbios = [&] (Smbios_entry_point const &ep)
+ {
+ report_smbios((void *)&ep, ep.length, ep.struct_table_addr,
+ ep.struct_table_length);
+ };
+ auto handle_dmi = [&] (Dmi_entry_point const &ep)
+ {
+ report_smbios((void *)&ep, ep.LENGTH, ep.struct_table_addr,
+ ep.struct_table_length);
+ };
+
+ addr_t efi_sys_tab_phy = 0;
+ try {
+ Attached_rom_dataspace info(env, "platform_info");
+ Xml_node xml(info.local_addr(), info.size());
+ Xml_node acpi_node = xml.sub_node("efi-system-table");
+ efi_sys_tab_phy = acpi_node.attribute_value("address", 0UL);
+ } catch (...) { }
+
+ if (!efi_sys_tab_phy) {
+ Smbios_table::from_scan(phy_mem, handle_smbios_3,
+ handle_smbios, handle_dmi);
+ } else {
+ Efi_system_table const &efi_sys_tab_vir { *(Efi_system_table *)
+ phy_mem(efi_sys_tab_phy, sizeof(Efi_system_table)) };
+
+ efi_sys_tab_vir.for_smbios_table(phy_mem, [&] (addr_t table_phy) {
+ Smbios_table::from_pointer(table_phy, phy_mem, handle_smbios_3,
+ handle_smbios, handle_dmi);
+ });
+ }
+ io_regions.for_each([&] (Fifo_element &elem) {
+ destroy(alloc, &elem.object());
+ });
+}
diff --git a/repos/os/src/drivers/acpi/smbios_table_reporter.h b/repos/os/src/drivers/acpi/smbios_table_reporter.h
new file mode 100644
index 0000000000..697f792ec8
--- /dev/null
+++ b/repos/os/src/drivers/acpi/smbios_table_reporter.h
@@ -0,0 +1,35 @@
+/*
+ * \brief Finding and reporting an SMBIOS table as is (plain data)
+ * \author Martin Stein
+ * \date 2019-07-09
+ */
+
+ /*
+ * Copyright (C) 2019 Genode Labs GmbH
+ *
+ * This file is part of the Genode OS framework, which is distributed
+ * under the terms of the GNU Affero General Public License version 3.
+ */
+
+#ifndef _SMBIOS_TABLE_REPORTER_H_
+#define _SMBIOS_TABLE_REPORTER_H_
+
+/* Genode includes */
+#include
+#include
+
+namespace Genode { class Smbios_table_reporter; }
+
+class Genode::Smbios_table_reporter
+{
+ private:
+
+ Constructible _reporter { };
+
+ public:
+
+ Smbios_table_reporter(Env &env,
+ Allocator &alloc);
+};
+
+#endif /* _SMBIOS_TABLE_REPORTER_H_ */
diff --git a/repos/os/src/drivers/acpi/spec/x86/target.mk b/repos/os/src/drivers/acpi/spec/x86/target.mk
index 92431747b7..c8ee90bb07 100644
--- a/repos/os/src/drivers/acpi/spec/x86/target.mk
+++ b/repos/os/src/drivers/acpi/spec/x86/target.mk
@@ -1,9 +1,10 @@
TARGET = acpi_drv
REQUIRES = x86
-SRC_CC = main.cc acpi.cc
+SRC_CC = main.cc acpi.cc smbios_table_reporter.cc
LIBS = base
INC_DIR = $(PRG_DIR)/../..
-vpath main.cc $(PRG_DIR)/../..
-vpath acpi.cc $(PRG_DIR)/../..
+vpath main.cc $(PRG_DIR)/../..
+vpath acpi.cc $(PRG_DIR)/../..
+vpath smbios_table_reporter.cc $(PRG_DIR)/../..