drivers/acpi: provide plain SMBIOS table as report

Ref #3430
This commit is contained in:
Martin Stein 2019-07-09 18:28:12 +02:00 committed by Christian Helmuth
parent ead385dd17
commit 1e379cb3a9
7 changed files with 595 additions and 6 deletions

View File

@ -230,7 +230,8 @@ proc platform_drv_config {} {
<service name="Report" />
</provides>
<config>
<policy label="platform_drv -> acpi" report="acpi_drv -> acpi"/>}
<policy label="smbios_decoder -> smbios_table" report="acpi_drv -> smbios_table"/>
<policy label="platform_drv -> acpi" report="acpi_drv -> acpi"/>}
append_if [expr {[acpi_drv_name] eq "acpica"}] drv_config {
<policy label="platform_drv -> acpi_ready" report="acpi_drv -> acpi_ready"/>}

View File

@ -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 <base/stdint.h>
#include <util/string.h>
#include <base/log.h>
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 <typename PHY_MEM_FUNC,
typename EP_FUNC>
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 <typename PHY_MEM_FUNC,
typename EP_FUNC>
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 <typename PHY_MEM_FUNC,
typename EP_FUNC>
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 <typename PHY_MEM_FUNC,
typename SMBIOS_3_FUNC,
typename SMBIOS_FUNC,
typename DMI_FUNC>
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 <typename PHY_MEM_FUNC,
typename SMBIOS_3_FUNC,
typename SMBIOS_FUNC,
typename DMI_FUNC>
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_ */

View File

@ -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 <base/stdint.h>
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 <typename PHY_MEM_FN,
typename HANDLE_TABLE_FN>
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_ */

View File

@ -19,6 +19,7 @@
/* local includes */
#include <acpi.h>
#include <smbios_table_reporter.h>
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)
{

View File

@ -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 <smbios_table_reporter.h>
#include <efi_system_table.h>
/* Genode includes */
#include <base/attached_io_mem_dataspace.h>
#include <base/attached_rom_dataspace.h>
#include <os/smbios.h>
#include <util/fifo.h>
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_element<Io_region> > &_fifo;
Attached_io_mem_dataspace _io_mem { _env, _base_page, _size_pages };
Fifo_element<Io_region> _fifo_elem { *this };
Io_region(Env &env,
addr_t base_page,
size_t size_pages,
Fifo<Fifo_element<Io_region> > &fifo)
:
_env { env },
_base_page { base_page },
_size_pages { size_pages },
_fifo { fifo }
{
_fifo.enqueue(_fifo_elem);
}
~Io_region()
{
_fifo.remove(_fifo_elem);
}
};
Fifo<Fifo_element<Io_region> > 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<Io_region> &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<int>() + 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<Io_region> &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<int>() + 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<char>(), 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<Io_region> &elem) {
destroy(alloc, &elem.object());
});
}

View File

@ -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 <os/reporter.h>
#include <base/allocator.h>
namespace Genode { class Smbios_table_reporter; }
class Genode::Smbios_table_reporter
{
private:
Constructible<Reporter> _reporter { };
public:
Smbios_table_reporter(Env &env,
Allocator &alloc);
};
#endif /* _SMBIOS_TABLE_REPORTER_H_ */

View File

@ -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)/../..