mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-21 06:33:31 +00:00
cfd013a01a
Issue #5227
280 lines
6.7 KiB
C++
280 lines
6.7 KiB
C++
/*
|
|
* \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 = (uint8_t)(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
|
|
{
|
|
bool smbios_3(addr_t const anchor,
|
|
addr_t const ep_phy,
|
|
auto const &phy_mem_fn,
|
|
auto const &handle_ep_fn)
|
|
{
|
|
if (memcmp((char *)anchor, "_SM3_", 5)) {
|
|
return false;
|
|
}
|
|
Smbios_3_entry_point const &ep { *(Smbios_3_entry_point *)
|
|
phy_mem_fn(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_fn(ep);
|
|
return true;
|
|
}
|
|
|
|
bool smbios(addr_t const anchor,
|
|
addr_t const ep_phy,
|
|
auto const &phy_mem_fn,
|
|
auto const &handle_ep_fn)
|
|
{
|
|
if (memcmp((char *)anchor, "_SM_", 4)) {
|
|
return false;
|
|
}
|
|
Smbios_entry_point const &ep { *(Smbios_entry_point *)
|
|
phy_mem_fn(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_fn(ep);
|
|
return true;
|
|
}
|
|
|
|
bool dmi(addr_t const anchor,
|
|
addr_t const ep_phy,
|
|
auto const &phy_mem_fn,
|
|
auto const &handle_ep_fn)
|
|
{
|
|
if (memcmp((char *)anchor, "_DMI_", 5)) {
|
|
return false;
|
|
}
|
|
Dmi_entry_point const &ep { *(Dmi_entry_point *)
|
|
phy_mem_fn(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_fn(ep);
|
|
return true;
|
|
}
|
|
|
|
void from_scan(auto const &phy_mem_fn,
|
|
auto const &handle_smbios_3_ep_fn,
|
|
auto const &handle_smbios_ep_fn,
|
|
auto const &handle_dmi_ep_fn)
|
|
{
|
|
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_fn(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_fn, handle_smbios_3_ep_fn)) {
|
|
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_fn, handle_smbios_ep_fn)) {
|
|
return;
|
|
}
|
|
}
|
|
for (addr_t curr { scan_base }; curr < scan_end; curr += SCAN_STEP ) {
|
|
if (dmi(curr, SCAN_BASE_PHY + (curr - scan_base), phy_mem_fn, handle_dmi_ep_fn)) {
|
|
return;
|
|
}
|
|
}
|
|
} catch (...) { }
|
|
}
|
|
|
|
void from_pointer(addr_t const table_phy,
|
|
auto const &phy_mem_fn,
|
|
auto const &handle_smbios_3_ep_fn,
|
|
auto const &handle_smbios_ep_fn,
|
|
auto const &handle_dmi_ep_fn)
|
|
{
|
|
addr_t const anchor { (addr_t)phy_mem_fn(table_phy, 5) };
|
|
if (smbios_3(anchor, table_phy, phy_mem_fn, handle_smbios_3_ep_fn)) {
|
|
return;
|
|
}
|
|
if (smbios(anchor, table_phy, phy_mem_fn, handle_smbios_ep_fn)) {
|
|
return;
|
|
}
|
|
dmi(anchor, table_phy, phy_mem_fn, handle_dmi_ep_fn);
|
|
}
|
|
};
|
|
|
|
#endif /* _OS__SMBIOS_H_ */
|