From 1e379cb3a9c7252a12137d2791fa09d97460cb8b Mon Sep 17 00:00:00 2001 From: Martin Stein Date: Tue, 9 Jul 2019 18:28:12 +0200 Subject: [PATCH] drivers/acpi: provide plain SMBIOS table as report Ref #3430 --- repos/base/run/platform_drv.inc | 3 +- repos/os/include/os/smbios.h | 293 ++++++++++++++++++ repos/os/src/drivers/acpi/efi_system_table.h | 86 +++++ repos/os/src/drivers/acpi/main.cc | 6 +- .../src/drivers/acpi/smbios_table_reporter.cc | 171 ++++++++++ .../src/drivers/acpi/smbios_table_reporter.h | 35 +++ repos/os/src/drivers/acpi/spec/x86/target.mk | 7 +- 7 files changed, 595 insertions(+), 6 deletions(-) create mode 100644 repos/os/include/os/smbios.h create mode 100644 repos/os/src/drivers/acpi/efi_system_table.h create mode 100644 repos/os/src/drivers/acpi/smbios_table_reporter.cc create mode 100644 repos/os/src/drivers/acpi/smbios_table_reporter.h diff --git a/repos/base/run/platform_drv.inc b/repos/base/run/platform_drv.inc index 63b6dd2a6..ea2ed0d98 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 000000000..c4ca242b1 --- /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 000000000..b01ad2c28 --- /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 b1a49b9bf..15e21405b 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 000000000..f2be91667 --- /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 000000000..697f792ec --- /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 92431747b..c8ee90bb0 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)/../..