acpi_drv: report Intel opregion copy

acpica and the Intel display driver tries to use the Intel Opregion
simultaneously on Genode, which is not supported nor wanted for IO_MEM region as
which it is handled.

Attempts to remove the access to the region was not successful, since some
SSDT table contains ACPI AML code which is executed regularly and read/write
the Opregion.

The patch adds support to make a copy of the Intel Opregion and report it as
is. The copy was sufficient to make the Intel display driver working to find
and lookup the Intel VBT (video bios table) information to setup all
connectors on a Fujitsu U7411 docking station.

Issue #4531
This commit is contained in:
Alexander Boettcher 2022-05-25 11:15:09 +02:00 committed by Christian Helmuth
parent b1195508ba
commit c2d9fbca9b
4 changed files with 197 additions and 2 deletions

View File

@ -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 * This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3. * under the terms of the GNU Affero General Public License version 3.
@ -28,6 +28,7 @@
#include "acpi.h" #include "acpi.h"
#include "memory.h" #include "memory.h"
#include "intel_opregion.h"
using namespace Genode; using namespace Genode;
@ -412,6 +413,95 @@ class Pci_config_space : public List<Pci_config_space>::Element
static List<Pci_config_space> _list; static List<Pci_config_space> _list;
return &_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<void>());
if ((device.read<Config_space::Vendor>() != Config_space::Vendor::INTEL) ||
(device.read<Config_space::Class>() != Config_space::Class::DISPLAY))
continue;
enum {
OPREGION_SIZE = 2 * 4096
};
addr_t const phys_asls = device.read<Config_space::Asls>();
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<void>());
auto const rvda = opregion.read<Opregion::Asle_rvda>();
auto const rvds = opregion.read<Opregion::Asle_rvds>();
if (opregion.read<Opregion::MBox::Asle>() &&
opregion.read<Opregion::Major>() >= 2 && rvda && rvds) {
/* 2.0 rvda is physical, 2.1+ rvda is relative offset */
if (opregion.read<Opregion::Major>() > 2 ||
opregion.read<Opregion::Minor>() >= 1) {
if (rvda > asls_size)
asls_size += rvda - asls_size;
asls_size += opregion.read<Opregion::Asle_rvds>();
} 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);
}); });
} }

View File

@ -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 <base/attached_io_mem_dataspace.h>
#include <base/attached_ram_dataspace.h>
#include <os/reporter.h>
#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<char>();
/* copy io_mem to ram dataspace and preserve offset */
memcpy(mem_local + phys_addr_offset, io_mem.local_addr<char>(),
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<void>(), report_ds.size());
} catch (...) {
Genode::warning("Intel opregion region copy failed");
}
}

View File

@ -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 <os/reporter.h>
namespace Acpi {
class Intel_opregion;
using namespace Genode;
}
class Acpi::Intel_opregion
{
private:
Constructible<Reporter> _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_ */

View File

@ -1,6 +1,6 @@
TARGET = acpi_drv TARGET = acpi_drv
REQUIRES = x86 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 LIBS = base
INC_DIR = $(PRG_DIR)/../.. INC_DIR = $(PRG_DIR)/../..
@ -8,5 +8,6 @@ INC_DIR = $(PRG_DIR)/../..
vpath main.cc $(PRG_DIR)/../.. vpath main.cc $(PRG_DIR)/../..
vpath acpi.cc $(PRG_DIR)/../.. vpath acpi.cc $(PRG_DIR)/../..
vpath smbios_table_reporter.cc $(PRG_DIR)/../.. vpath smbios_table_reporter.cc $(PRG_DIR)/../..
vpath intel_opregion.cc $(PRG_DIR)/../..
CC_CXX_WARN_STRICT_CONVERSION = CC_CXX_WARN_STRICT_CONVERSION =