/* * \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()); }); }