diff --git a/repos/os/src/drivers/acpi/acpi.cc b/repos/os/src/drivers/acpi/acpi.cc index a4ff2d1dd0..41670466ad 100644 --- a/repos/os/src/drivers/acpi/acpi.cc +++ b/repos/os/src/drivers/acpi/acpi.cc @@ -11,7 +11,7 @@ */ /* - * Copyright (C) 2009-2017 Genode Labs GmbH + * Copyright (C) 2009-2022 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. @@ -28,6 +28,7 @@ #include "acpi.h" #include "memory.h" +#include "intel_opregion.h" using namespace Genode; @@ -412,6 +413,95 @@ class Pci_config_space : public List::Element static List _list; return &_list; } + + struct Config_space : Mmio + { + struct Vendor : Register<0x00, 16> { enum { INTEL = 0x8086 }; }; + struct Class : Register<0x0b, 8> { enum { DISPLAY = 0x3 }; }; + struct Asls : Register<0xfc, 32> { }; + + Config_space(addr_t mmio) : Mmio(mmio) { } + }; + + struct Opregion : Mmio + { + struct Minor : Register<0x16, 8> { }; + struct Major : Register<0x17, 8> { }; + struct MBox : Register<0x58, 32> { + struct Asle : Bitfield<2, 1> { }; + }; + struct Asle_ardy : Register<0x300, 32> { }; + struct Asle_rvda : Register<0x3ba, 64> { }; + struct Asle_rvds : Register<0x3c2, 32> { }; + + Opregion(addr_t mmio) : Mmio(mmio) { } + }; + + static void intel_opregion(Env &env) + { + for (auto *e = list()->first(); e; e = e->next()) { + if (e->_bdf_start != 0u) /* BDF 0:0.0 */ + continue; + + auto const config_offset = 8u * 2; /* BDF 0:2.0 */ + auto const config_size = 4096; + + if (e->_func_count <= config_offset) + continue; + + Attached_io_mem_dataspace pci_config(env, e->_base + + config_offset * config_size, + config_size); + Config_space device((addr_t)pci_config.local_addr()); + + if ((device.read() != Config_space::Vendor::INTEL) || + (device.read() != Config_space::Class::DISPLAY)) + continue; + + enum { + OPREGION_SIZE = 2 * 4096 + }; + + addr_t const phys_asls = device.read(); + if (!phys_asls) + continue; + + addr_t asls_size = OPREGION_SIZE; + + { + Attached_io_mem_dataspace map_asls(env, phys_asls, asls_size); + Opregion opregion((addr_t)map_asls.local_addr()); + + auto const rvda = opregion.read(); + auto const rvds = opregion.read(); + + if (opregion.read() && + opregion.read() >= 2 && rvda && rvds) { + + /* 2.0 rvda is physical, 2.1+ rvda is relative offset */ + if (opregion.read() > 2 || + opregion.read() >= 1) { + + if (rvda > asls_size) + asls_size += rvda - asls_size; + asls_size += opregion.read(); + } else { + warning("rvda/rvds unsupported case"); + } + } + } + + /* + * Intel_opregion requires access to the opregion memory later + * on used by acpica. Therefore the code must be executed here + * and finished, before the acpi report is sent. + * With a valid acpi report the acpica driver starts to run + * and would collide with Intel_opregion. + */ + static Acpi::Intel_opregion opregion_report { env, phys_asls, + asls_size }; + } + } }; @@ -1580,5 +1670,11 @@ void Acpi::generate_report(Genode::Env &env, Genode::Allocator &alloc) } } } + + /* + * Intel opregion lookup & parsing must be finished before acpi + * report is sent, therefore the invocation is placed exactly here. + */ + Pci_config_space::intel_opregion(env); }); } diff --git a/repos/os/src/drivers/acpi/intel_opregion.cc b/repos/os/src/drivers/acpi/intel_opregion.cc new file mode 100644 index 0000000000..488714b8eb --- /dev/null +++ b/repos/os/src/drivers/acpi/intel_opregion.cc @@ -0,0 +1,57 @@ +/* + * \brief Lookup Intel opregion region and report it as is (plain data) + * \author Alexander Boettcher + * \date 2022-05-25 + */ + + /* + * Copyright (C) 2022 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. + */ + +#include +#include +#include + +#include "intel_opregion.h" + +void Acpi::Intel_opregion::generate_report(Genode::Env &env, + addr_t const region_phys, + addr_t const region_size) +{ + try { + addr_t const phys_addr_offset = region_phys & 0xffful; + addr_t const memory_size = region_size + phys_addr_offset; + + /* create ram dataspace with space for io_mem address + size */ + Attached_io_mem_dataspace io_mem { env, region_phys, memory_size }; + Attached_ram_dataspace report_mem { env.ram(), env.rm(), + memory_size + sizeof(addr_t) * 2 }; + + auto mem_local = report_mem.local_addr(); + + /* copy io_mem to ram dataspace and preserve offset */ + memcpy(mem_local + phys_addr_offset, io_mem.local_addr(), + region_size); + + Dataspace_client report_ds(report_mem.cap()); + + /* report also io_mem address and io_mem size (!equal to ds size) */ + auto report_phys_ptr = (addr_t*)(mem_local + report_ds.size() - sizeof(addr_t) * 2); + auto report_phys_size = (addr_t*)(mem_local + report_ds.size() - sizeof(addr_t)); + + *report_phys_ptr = region_phys; + *report_phys_size = region_size; + + /* create report */ + _report.construct(env, "intel_opregion", "intel_opregion", + report_ds.size()); + _report->enabled(true); + _report->report(report_mem.local_addr(), report_ds.size()); + + } catch (...) { + Genode::warning("Intel opregion region copy failed"); + } +} diff --git a/repos/os/src/drivers/acpi/intel_opregion.h b/repos/os/src/drivers/acpi/intel_opregion.h new file mode 100644 index 0000000000..1023fdc89a --- /dev/null +++ b/repos/os/src/drivers/acpi/intel_opregion.h @@ -0,0 +1,41 @@ +/* + * \brief Lookup Intel opregion and report it as is (plain data) + * \author Alexander Boettcher + * \date 2022-05-25 + */ + + /* + * Copyright (C) 2022 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 _INTEL_OPREGION_REPORTER_H_ +#define _INTEL_OPREGION_REPORTER_H_ + +/* Genode includes */ +#include + +namespace Acpi { + class Intel_opregion; + using namespace Genode; +} + +class Acpi::Intel_opregion +{ + private: + + Constructible _report { }; + + void generate_report(Env &env, addr_t, addr_t); + + public: + + Intel_opregion(Env &env, addr_t phys_base, addr_t size) + { + generate_report(env, phys_base, size); + } +}; + +#endif /* _INTEL_OPREGION_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 831a337726..e03b8e5f72 100644 --- a/repos/os/src/drivers/acpi/spec/x86/target.mk +++ b/repos/os/src/drivers/acpi/spec/x86/target.mk @@ -1,6 +1,6 @@ TARGET = acpi_drv REQUIRES = x86 -SRC_CC = main.cc acpi.cc smbios_table_reporter.cc +SRC_CC = main.cc acpi.cc smbios_table_reporter.cc intel_opregion.cc LIBS = base INC_DIR = $(PRG_DIR)/../.. @@ -8,5 +8,6 @@ INC_DIR = $(PRG_DIR)/../.. vpath main.cc $(PRG_DIR)/../.. vpath acpi.cc $(PRG_DIR)/../.. vpath smbios_table_reporter.cc $(PRG_DIR)/../.. +vpath intel_opregion.cc $(PRG_DIR)/../.. CC_CXX_WARN_STRICT_CONVERSION =