mirror of
https://github.com/genodelabs/genode.git
synced 2025-04-12 13:48:30 +00:00
Remove legacy platform_drv, API, and platform.inc
Fix genodelabs/genode#4671
This commit is contained in:
parent
08378dd148
commit
9e61fb90c5
@ -1,261 +0,0 @@
|
||||
proc have_platform_drv {} {
|
||||
return [expr [have_board pc]]
|
||||
}
|
||||
|
||||
|
||||
##
|
||||
# Return name of the USB driver binary
|
||||
#
|
||||
proc usb_host_drv_binary { } {
|
||||
if {[have_board rpi]} { return legacy_rpi_usb_host_drv }
|
||||
if {[have_board imx6q_sabrelite]} { return legacy_imx6q_sabrelite_usb_host_drv }
|
||||
if {[have_board pc]} { return pc_usb_host_drv }
|
||||
return no_usb_drv_available
|
||||
}
|
||||
|
||||
|
||||
##
|
||||
# Return name of the audio driver binary
|
||||
#
|
||||
proc audio_drv_binary { } {
|
||||
if {[have_board linux]} { return linux_audio_drv }
|
||||
if {[have_board pc]} { return pci_audio_drv }
|
||||
return no_audio_drv_available
|
||||
}
|
||||
|
||||
##
|
||||
# Return attributes of the audio driver's <start> node
|
||||
#
|
||||
proc audio_drv_start_attr { } {
|
||||
if {[have_board linux]} { return {ld="no"} }
|
||||
return ""
|
||||
}
|
||||
|
||||
proc acpi_drv_name { } {
|
||||
global use_acpica_as_acpi_drv
|
||||
if {[info exists use_acpica_as_acpi_drv] && $use_acpica_as_acpi_drv} {
|
||||
return acpica }
|
||||
|
||||
return acpi_drv
|
||||
}
|
||||
|
||||
proc platform_drv_build_components {} {
|
||||
set drv_build_components ""
|
||||
if {[have_board pc]} {
|
||||
lappend drv_build_components drivers/platform/legacy/x86
|
||||
lappend drv_build_components server/report_rom
|
||||
if {[acpi_drv_name] eq "acpi_drv"} {
|
||||
lappend drv_build_components drivers/acpi
|
||||
}
|
||||
if {[acpi_drv_name] eq "acpica"} {
|
||||
lappend drv_build_components app/acpica
|
||||
}
|
||||
}
|
||||
return $drv_build_components
|
||||
}
|
||||
|
||||
|
||||
proc append_platform_drv_build_components {} {
|
||||
global build_components
|
||||
append build_components { } [platform_drv_build_components]
|
||||
}
|
||||
|
||||
proc platform_drv_binary {} {
|
||||
if {[have_board pc]} { return legacy_pc_platform_drv }
|
||||
return no_platform_drv_available
|
||||
}
|
||||
|
||||
proc platform_drv_boot_modules {} {
|
||||
set drv_boot_modules ""
|
||||
lappend_if [have_platform_drv] drv_boot_modules [platform_drv_binary]
|
||||
|
||||
if {[have_board pc]} {
|
||||
lappend drv_boot_modules report_rom
|
||||
lappend drv_boot_modules [acpi_drv_name]
|
||||
}
|
||||
|
||||
return $drv_boot_modules
|
||||
}
|
||||
|
||||
proc append_platform_drv_boot_modules {} {
|
||||
global boot_modules
|
||||
append boot_modules { } [platform_drv_boot_modules]
|
||||
}
|
||||
|
||||
|
||||
proc platform_drv_policy {} {
|
||||
|
||||
if {![have_board pc]} {
|
||||
return {}
|
||||
}
|
||||
|
||||
set drv_policy ""
|
||||
|
||||
if {[acpi_drv_name] eq "acpica"} {
|
||||
append drv_policy {
|
||||
<policy label="acpi_drv -> "> <pci class="ALL"/> </policy>}
|
||||
}
|
||||
|
||||
append drv_policy {
|
||||
<policy label_prefix="ps2_drv"> <device name="PS2"/> </policy>
|
||||
<policy label_prefix="nic_drv"> <pci class="ETHERNET"/> </policy>
|
||||
<policy label_prefix="fb_drv"> <pci class="VGA"/> </policy>
|
||||
<policy label_prefix="wifi_drv" msix="false"> <pci class="WIFI"/> </policy>
|
||||
<policy label_prefix="usb_drv"> <pci class="USB"/> </policy>
|
||||
<policy label_prefix="ahci_drv"> <pci class="AHCI"/> </policy>
|
||||
<policy label_prefix="nvme_drv"> <pci class="NVME"/> </policy>
|
||||
<policy label_prefix="audio_drv"> <pci class="AUDIO"/> <pci class="HDAUDIO"/> </policy>
|
||||
<policy label_prefix="intel_fb_drv">
|
||||
<pci class="VGA"/>
|
||||
<pci bus="0" device="0" function="0"/>
|
||||
<pci class="ISABRIDGE"/>
|
||||
</policy>}
|
||||
|
||||
return $drv_policy
|
||||
}
|
||||
|
||||
|
||||
proc platform_drv_priority {} { return "" }
|
||||
proc platform_drv_add_routing {} {
|
||||
|
||||
if {[acpi_drv_name] eq "acpica"} {
|
||||
return {
|
||||
<service name="ROM" label="acpi_ready"> <child name="acpi_report_rom"/> </service>}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
|
||||
proc platform_drv_config_config {} {
|
||||
if {[acpi_drv_name] eq "acpica"} {
|
||||
return {
|
||||
<config acpi_ready="yes">}
|
||||
}
|
||||
|
||||
return {<config>}
|
||||
}
|
||||
|
||||
|
||||
proc platform_drv_config {} {
|
||||
set drv_config ""
|
||||
|
||||
if {[have_board pc]} {
|
||||
|
||||
append drv_config {
|
||||
<start name="acpi_drv" } [platform_drv_priority] { caps="350" >
|
||||
<binary name="} [acpi_drv_name] {"/>}
|
||||
|
||||
if {[acpi_drv_name] eq "acpica"} {
|
||||
append drv_config {
|
||||
<resource name="RAM" quantum="5M"/>
|
||||
<config acpi_ready="yes" act_as_acpi_drv="yes" report="yes"/>}
|
||||
} else {
|
||||
append drv_config {
|
||||
<resource name="RAM" quantum="4M"/>}
|
||||
}
|
||||
|
||||
append drv_config {
|
||||
<route>
|
||||
<service name="IO_MEM"> <parent/> </service>
|
||||
<service name="LOG"> <parent/> </service>
|
||||
<service name="PD"> <parent/> </service>
|
||||
<service name="RM"> <parent/> </service>
|
||||
<service name="CPU"> <parent/> </service>
|
||||
<service name="ROM"> <parent/> </service>}
|
||||
|
||||
append_if [expr {[acpi_drv_name] eq "acpica"}] drv_config {
|
||||
<service name="IO_PORT"> <parent/> </service>
|
||||
<service name="IRQ"> <parent/> </service>
|
||||
<service name="Timer"> <child name="timer"/> </service>
|
||||
<service name="Platform"> <child name="platform_drv"/> </service>
|
||||
<service name="Acpi"> <child name="platform_drv"/> </service>}
|
||||
|
||||
append drv_config {
|
||||
<service name="Report"> <child name="acpi_report_rom"/> </service>
|
||||
<service name="ROM" label="platform_info"> <parent/> </service>
|
||||
</route>
|
||||
</start>}
|
||||
|
||||
append drv_config "
|
||||
<start name=\"acpi_report_rom\" [platform_drv_priority]>"
|
||||
|
||||
append drv_config {
|
||||
<binary name="report_rom"/>
|
||||
<resource name="RAM" quantum="2M"/>
|
||||
<provides>
|
||||
<service name="ROM" />
|
||||
<service name="Report" />
|
||||
</provides>
|
||||
<config>
|
||||
<policy label="intel_fb_drv -> intel_opregion" report="acpi_drv -> intel_opregion"/>
|
||||
<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"/>}
|
||||
|
||||
append drv_config {
|
||||
</config>
|
||||
<route>
|
||||
<service name="LOG"> <parent/> </service>
|
||||
<service name="PD"> <parent/> </service>
|
||||
<service name="CPU"> <parent/> </service>
|
||||
<service name="ROM"> <parent/> </service>
|
||||
</route>
|
||||
</start>}
|
||||
|
||||
}
|
||||
|
||||
if {[have_platform_drv]} {
|
||||
|
||||
append drv_config {
|
||||
<start name="platform_drv" } [platform_drv_priority] { caps="800" managing_system="yes">
|
||||
<binary name="} [platform_drv_binary] {"/>
|
||||
<resource name="RAM" quantum="4M"/>
|
||||
<provides>
|
||||
<service name="Platform"/>}
|
||||
|
||||
append_if [have_board pc] drv_config {
|
||||
<service name="Acpi"/>}
|
||||
|
||||
append_if [have_spec arm] drv_config {
|
||||
<service name="Regulator"/>}
|
||||
|
||||
append drv_config {
|
||||
</provides>
|
||||
<route>}
|
||||
|
||||
append drv_config "[platform_drv_add_routing]"
|
||||
|
||||
append_if [have_board pc] drv_config {
|
||||
<service name="ROM" label="acpi"> <child name="acpi_report_rom"/> </service>}
|
||||
|
||||
append_if [expr [have_board pc]] drv_config {
|
||||
<service name="Report"> <child name="acpi_report_rom"/> </service>}
|
||||
|
||||
append_if [expr [have_board rpi] || [have_board pc]] drv_config {
|
||||
<service name="Timer"> <any-child/> </service>}
|
||||
|
||||
append drv_config {
|
||||
<any-service> <parent/> </any-service>
|
||||
</route>}
|
||||
|
||||
append drv_config [platform_drv_config_config]
|
||||
append drv_config [platform_drv_policy]
|
||||
|
||||
append drv_config {
|
||||
</config>
|
||||
</start>}
|
||||
|
||||
}
|
||||
|
||||
return $drv_config
|
||||
}
|
||||
|
||||
|
||||
proc append_platform_drv_config {} {
|
||||
global config
|
||||
append config [platform_drv_config]
|
||||
return $config
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
/*
|
||||
* \brief Device capability type
|
||||
* \author Norman Feske
|
||||
* \date 2008-08-16
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2008-2017 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <base/capability.h>
|
||||
#include <legacy/x86/platform_device/platform_device.h>
|
||||
|
||||
namespace Platform { typedef Genode::Capability<Device> Device_capability; }
|
@ -1,64 +0,0 @@
|
||||
/*
|
||||
* \brief Client-side interface for PCI device
|
||||
* \author Norman Feske
|
||||
* \date 2008-01-28
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2008-2017 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 _INCLUDE__LEGACY__X86__PLATFORM_DEVICE__CLIENT_H_
|
||||
#define _INCLUDE__LEGACY__X86__PLATFORM_DEVICE__CLIENT_H_
|
||||
|
||||
#include <legacy/x86/platform_session/platform_session.h>
|
||||
#include <legacy/x86/platform_device/platform_device.h>
|
||||
#include <base/rpc_client.h>
|
||||
#include <io_mem_session/io_mem_session.h>
|
||||
|
||||
namespace Platform { struct Device_client; }
|
||||
|
||||
|
||||
struct Platform::Device_client : public Rpc_client<Device>
|
||||
{
|
||||
Device_client(Device_capability device)
|
||||
: Rpc_client<Device>(device) { }
|
||||
|
||||
void bus_address(unsigned char *bus, unsigned char *dev, unsigned char *fn) override {
|
||||
call<Rpc_bus_address>(bus, dev, fn); }
|
||||
|
||||
unsigned short vendor_id() override {
|
||||
return call<Rpc_vendor_id>(); }
|
||||
|
||||
unsigned short device_id() override {
|
||||
return call<Rpc_device_id>(); }
|
||||
|
||||
unsigned class_code() override {
|
||||
return call<Rpc_class_code>(); }
|
||||
|
||||
Resource resource(int resource_id) override {
|
||||
return call<Rpc_resource>(resource_id); }
|
||||
|
||||
unsigned config_read(unsigned char address, Access_size size) override {
|
||||
return call<Rpc_config_read>(address, size); }
|
||||
|
||||
void config_write(unsigned char address, unsigned value, Access_size size) override {
|
||||
call<Rpc_config_write>(address, value, size); }
|
||||
|
||||
Irq_session_capability irq(uint8_t id) override {
|
||||
return call<Rpc_irq>(id); }
|
||||
|
||||
Io_port_session_capability io_port(uint8_t id) override {
|
||||
return call<Rpc_io_port>(id); }
|
||||
|
||||
Io_mem_session_capability io_mem(uint8_t id,
|
||||
Cache cache = Cache::UNCACHED,
|
||||
addr_t offset = 0,
|
||||
size_t size = ~0UL) override {
|
||||
return call<Rpc_io_mem>(id, cache, offset, size); }
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__LEGACY__X86__PLATFORM_DEVICE__CLIENT_H_ */
|
@ -1,38 +0,0 @@
|
||||
/*
|
||||
* \brief Abstract platform device interface
|
||||
* \author Alexander Boettcher
|
||||
* \date 2015-03-15
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015-2017 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 _INCLUDE__LEGACY__X86__PLATFORM_DEVICE__DEVICE_H_
|
||||
#define _INCLUDE__LEGACY__X86__PLATFORM_DEVICE__DEVICE_H_
|
||||
|
||||
#include <util/interface.h>
|
||||
#include <base/cache.h>
|
||||
#include <irq_session/capability.h>
|
||||
#include <io_mem_session/capability.h>
|
||||
|
||||
namespace Platform { class Abstract_device; }
|
||||
|
||||
struct Platform::Abstract_device : Genode::Interface
|
||||
{
|
||||
/**
|
||||
* Get IRQ session capability
|
||||
*/
|
||||
virtual Genode::Irq_session_capability irq(Genode::uint8_t) = 0;
|
||||
|
||||
/**
|
||||
* Get IO mem session capability of specified resource id
|
||||
*/
|
||||
virtual Genode::Io_mem_session_capability io_mem(Genode::uint8_t, Genode::Cache,
|
||||
Genode::addr_t, Genode::size_t) = 0;
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__LEGACY__X86__PLATFORM_DEVICE__DEVICE_H_ */
|
@ -1,262 +0,0 @@
|
||||
/*
|
||||
* \brief PCI-device interface
|
||||
* \author Norman Feske
|
||||
* \author Christian Helmuth
|
||||
* \date 2008-01-28
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2008-2017 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 _INCLUDE__LEGACY__X86__PLATFORM_DEVICE__PLATFORM_DEVICE_H_
|
||||
#define _INCLUDE__LEGACY__X86__PLATFORM_DEVICE__PLATFORM_DEVICE_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/rpc.h>
|
||||
#include <base/signal.h>
|
||||
#include <base/exception.h>
|
||||
#include <base/ram_allocator.h>
|
||||
#include <io_mem_session/io_mem_session.h>
|
||||
#include <io_port_session/capability.h>
|
||||
#include <irq_session/capability.h>
|
||||
|
||||
/* os includes */
|
||||
#include <legacy/x86/platform_device/device.h>
|
||||
|
||||
namespace Platform {
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
struct Device;
|
||||
}
|
||||
|
||||
|
||||
struct Platform::Device : Platform::Abstract_device
|
||||
{
|
||||
/*********************
|
||||
** Exception types **
|
||||
*********************/
|
||||
|
||||
class Resource
|
||||
{
|
||||
private:
|
||||
|
||||
unsigned _bar = 0; /* content of base-address register */
|
||||
unsigned _size = 0; /* resource size */
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Resource type, either port I/O resource or memory-mapped resource
|
||||
*/
|
||||
enum Type { IO, MEMORY, INVALID };
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
Resource() { }
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* \param bar content of base-address register
|
||||
* \param size resource size
|
||||
*
|
||||
* This constructor is only used by the PCI bus driver
|
||||
* that implements the server-side of the the PCI session.
|
||||
* If bar is set to zero, the constructed resource description
|
||||
* represents an INVALID resource.
|
||||
*/
|
||||
Resource(unsigned bar, unsigned size)
|
||||
: _bar(bar), _size(size) { }
|
||||
|
||||
/**
|
||||
* Return base address of resource
|
||||
*/
|
||||
unsigned base() const
|
||||
{
|
||||
/*
|
||||
* Mask out the resource-description bits of the base
|
||||
* address register. I/O resources use the lowest 2
|
||||
* bits, memory resources use the lowest 4 bits.
|
||||
*/
|
||||
return _bar & ((type() == IO) ? ~3 : ~15);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return resource size in bytes
|
||||
*/
|
||||
unsigned size() const { return _size; }
|
||||
|
||||
/**
|
||||
* Return true if resource is prefetchable memory
|
||||
*/
|
||||
bool prefetchable() const
|
||||
{
|
||||
return type() == MEMORY && (_bar & (1 << 3));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return resource type
|
||||
*/
|
||||
Type type() const
|
||||
{
|
||||
if (_bar == 0)
|
||||
return INVALID;
|
||||
|
||||
return (_bar & 1) ? IO : MEMORY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return raw register content
|
||||
*/
|
||||
unsigned bar() const { return _bar; }
|
||||
};
|
||||
|
||||
enum { NUM_RESOURCES = 6 };
|
||||
|
||||
virtual ~Device() { }
|
||||
|
||||
/**
|
||||
* Return bus, device, and function number of the device
|
||||
*/
|
||||
virtual void bus_address(unsigned char *bus, unsigned char *dev,
|
||||
unsigned char *fn) = 0;
|
||||
|
||||
/**
|
||||
* Return vendor ID obtained from the PCI config space
|
||||
*/
|
||||
virtual unsigned short vendor_id() = 0;
|
||||
|
||||
/**
|
||||
* Return device ID obtained from the PCI config space
|
||||
*/
|
||||
virtual unsigned short device_id() = 0;
|
||||
|
||||
/**
|
||||
* Return device class code from the PCI config space
|
||||
*/
|
||||
virtual unsigned class_code() = 0;
|
||||
|
||||
/**
|
||||
* Query PCI-resource information
|
||||
*
|
||||
* \param resource_id index of according PCI resource of the device
|
||||
*
|
||||
* \return resource description
|
||||
* \retval INVALID the supplied resource ID is invalid
|
||||
*/
|
||||
virtual Resource resource(int resource_id) = 0;
|
||||
|
||||
/**
|
||||
* Access size for operations directly accessing the config space
|
||||
*/
|
||||
enum Access_size { ACCESS_8BIT, ACCESS_16BIT, ACCESS_32BIT };
|
||||
|
||||
/**
|
||||
* Read configuration space
|
||||
*/
|
||||
virtual unsigned config_read(unsigned char address, Access_size size) = 0;
|
||||
|
||||
/**
|
||||
* Write configuration space
|
||||
*
|
||||
* \throw Out_of_ram
|
||||
* \throw Out_of_caps
|
||||
*/
|
||||
virtual void config_write(unsigned char address, unsigned value,
|
||||
Access_size size) = 0;
|
||||
|
||||
/**
|
||||
* Query Io_port of specified bar
|
||||
*
|
||||
* \param id index of according PCI resource of the device
|
||||
*
|
||||
* \throw Out_of_ram
|
||||
* \throw Out_of_caps
|
||||
*/
|
||||
virtual Io_port_session_capability io_port(uint8_t id) = 0;
|
||||
|
||||
/*
|
||||
* The base classes are defined as follows:
|
||||
*
|
||||
* 0x00 | legacy device
|
||||
* 0x01 | mass-storage controller
|
||||
* 0x02 | network controller
|
||||
* 0x03 | display controller
|
||||
* 0x04 | multimedia device
|
||||
* 0x05 | memory controller
|
||||
* 0x06 | bridge device
|
||||
* 0x07 | simple-communication controller
|
||||
* 0x08 | base-system peripheral
|
||||
* 0x09 | input device
|
||||
* 0x0a | docking station
|
||||
* 0x0b | processor
|
||||
* 0x0c | serial bus controller
|
||||
* 0x0d | wireless controller
|
||||
* 0x0e | intelligent I/O controller
|
||||
* 0x0f | satellite-communications controller
|
||||
* 0x10 | encryption/decryption controller
|
||||
* 0x11 | data-acquisition and signal-processing controller
|
||||
* 0x12 | reserved
|
||||
* ... |
|
||||
* 0xff | device does not fit in any of the defined classes
|
||||
*/
|
||||
|
||||
unsigned base_class() { return class_code() >> 16; }
|
||||
unsigned sub_class() { return (class_code() >> 8) & 0xff; }
|
||||
|
||||
/**
|
||||
* Convenience method to translate a PCI physical BAR id to a Genode
|
||||
* virtual one usable with the io_port and io_mem methods. The virtual id
|
||||
* is solely valid for the specific BAR type.
|
||||
*/
|
||||
uint8_t phys_bar_to_virt(uint8_t phys_bar)
|
||||
{
|
||||
uint8_t virt_io_port = 0, virt_io_mem = 0;
|
||||
|
||||
for (unsigned i = 0; i < phys_bar; i++) {
|
||||
Resource::Type type = resource(i).type();
|
||||
if (type == Resource::Type::IO)
|
||||
virt_io_port ++;
|
||||
else if (type == Resource::Type::MEMORY)
|
||||
virt_io_mem ++;
|
||||
}
|
||||
|
||||
Resource::Type type = resource(phys_bar).type();
|
||||
return type == Resource::Type::IO ? virt_io_port : virt_io_mem;
|
||||
}
|
||||
|
||||
/*********************
|
||||
** RPC declaration **
|
||||
*********************/
|
||||
|
||||
GENODE_RPC(Rpc_bus_address, void, bus_address,
|
||||
unsigned char *, unsigned char *, unsigned char *);
|
||||
GENODE_RPC(Rpc_vendor_id, unsigned short, vendor_id);
|
||||
GENODE_RPC(Rpc_device_id, unsigned short, device_id);
|
||||
GENODE_RPC(Rpc_class_code, unsigned, class_code);
|
||||
GENODE_RPC(Rpc_resource, Resource, resource, int);
|
||||
GENODE_RPC(Rpc_config_read, unsigned, config_read,
|
||||
unsigned char, Access_size);
|
||||
GENODE_RPC_THROW(Rpc_config_write, void, config_write,
|
||||
GENODE_TYPE_LIST(Out_of_ram, Out_of_caps),
|
||||
unsigned char, unsigned, Access_size);
|
||||
GENODE_RPC(Rpc_irq, Irq_session_capability, irq, uint8_t);
|
||||
GENODE_RPC_THROW(Rpc_io_port, Io_port_session_capability, io_port,
|
||||
GENODE_TYPE_LIST(Out_of_ram, Out_of_caps),
|
||||
uint8_t);
|
||||
GENODE_RPC_THROW(Rpc_io_mem, Io_mem_session_capability, io_mem,
|
||||
GENODE_TYPE_LIST(Out_of_ram, Out_of_caps),
|
||||
uint8_t, Cache, addr_t, size_t);
|
||||
|
||||
GENODE_RPC_INTERFACE(Rpc_bus_address, Rpc_vendor_id, Rpc_device_id,
|
||||
Rpc_class_code, Rpc_resource, Rpc_config_read,
|
||||
Rpc_config_write, Rpc_irq, Rpc_io_port, Rpc_io_mem);
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__LEGACY__X86__PLATFORM_DEVICE__PLATFORM_DEVICE_H_ */
|
@ -1,22 +0,0 @@
|
||||
/*
|
||||
* \brief Platform session capability type
|
||||
* \author Stefan Kalkowski
|
||||
* \date 2013-04-29
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2013-2017 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 _INCLUDE__LEGACY__X86__PLATFORM_SESSION__CAPABILITY_H_
|
||||
#define _INCLUDE__LEGACY__X86__PLATFORM_SESSION__CAPABILITY_H_
|
||||
|
||||
#include <base/capability.h>
|
||||
#include <legacy/x86/platform_session/platform_session.h>
|
||||
|
||||
namespace Platform { typedef Genode::Capability<Session> Session_capability; }
|
||||
|
||||
#endif /* _INCLUDE__LEGACY__X86__PLATFORM_SESSION__CAPABILITY_H_ */
|
@ -1,53 +0,0 @@
|
||||
/*
|
||||
* \brief Client-side PCI-session interface
|
||||
* \author Norman Feske
|
||||
* \date 2008-01-28
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2008-2017 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 _INCLUDE__LEGACY__X86__PLATFORM_SESSION_H_
|
||||
#define _INCLUDE__LEGACY__X86__PLATFORM_SESSION_H_
|
||||
|
||||
#include <base/rpc_client.h>
|
||||
#include <legacy/x86/platform_device/client.h>
|
||||
#include <legacy/x86/platform_session/capability.h>
|
||||
|
||||
namespace Platform { struct Client; }
|
||||
|
||||
|
||||
struct Platform::Client : public Rpc_client<Session>
|
||||
{
|
||||
Client(Session_capability session) : Rpc_client<Session>(session) { }
|
||||
|
||||
Device_capability first_device(unsigned device_class = 0,
|
||||
unsigned class_mask = 0) override {
|
||||
return call<Rpc_first_device>(device_class, class_mask); }
|
||||
|
||||
Device_capability next_device(Device_capability prev_device,
|
||||
unsigned device_class = 0,
|
||||
unsigned class_mask = 0) override {
|
||||
return call<Rpc_next_device>(prev_device, device_class, class_mask); }
|
||||
|
||||
void release_device(Device_capability device) override {
|
||||
call<Rpc_release_device>(device); }
|
||||
|
||||
Ram_dataspace_capability alloc_dma_buffer(size_t size, Cache cache) override {
|
||||
return call<Rpc_alloc_dma_buffer>(size, cache); }
|
||||
|
||||
void free_dma_buffer(Ram_dataspace_capability cap) override {
|
||||
call<Rpc_free_dma_buffer>(cap); }
|
||||
|
||||
addr_t dma_addr(Ram_dataspace_capability cap) override {
|
||||
return call<Rpc_dma_addr>(cap); }
|
||||
|
||||
Device_capability device(Device_name const &device) override {
|
||||
return call<Rpc_device>(device); }
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__LEGACY__X86__PLATFORM_SESSION_H_ */
|
@ -1,51 +0,0 @@
|
||||
/*
|
||||
* \brief Connection to Platform service
|
||||
* \author Norman Feske
|
||||
* \date 2008-08-22
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2008-2017 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 _INCLUDE__LEGACY__X86__PLATFORM_SESSION__CONNECTION_H_
|
||||
#define _INCLUDE__LEGACY__X86__PLATFORM_SESSION__CONNECTION_H_
|
||||
|
||||
#include <util/retry.h>
|
||||
#include <base/connection.h>
|
||||
#include <legacy/x86/platform_session/client.h>
|
||||
|
||||
namespace Platform { struct Connection; }
|
||||
|
||||
|
||||
struct Platform::Connection : Genode::Connection<Session>, Client
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Connection(Env &env)
|
||||
:
|
||||
Genode::Connection<Session>(env, session(env.parent(),
|
||||
"ram_quota=16K, cap_quota=%u",
|
||||
CAP_QUOTA)),
|
||||
Client(cap())
|
||||
{ }
|
||||
|
||||
template <typename FUNC>
|
||||
auto with_upgrade(FUNC func) -> decltype(func())
|
||||
{
|
||||
return retry<Out_of_ram>(
|
||||
[&] () {
|
||||
return retry<Out_of_caps>(
|
||||
[&] () { return func(); },
|
||||
[&] () { this->upgrade_caps(2); });
|
||||
},
|
||||
[&] () { this->upgrade_ram(4096); }
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__LEGACY__X86__PLATFORM_SESSION__CONNECTION_H_ */
|
@ -1,112 +0,0 @@
|
||||
/*
|
||||
* \brief Platform session interface
|
||||
* \author Norman Feske
|
||||
* \date 2008-01-28
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2008-2017 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 _INCLUDE__LEGACY__X86__PLATFORM_SESSION__PLATFORM_SESSION_H_
|
||||
#define _INCLUDE__LEGACY__X86__PLATFORM_SESSION__PLATFORM_SESSION_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <session/session.h>
|
||||
#include <base/ram_allocator.h>
|
||||
#include <base/cache.h>
|
||||
|
||||
/* os includes */
|
||||
#include <legacy/x86/platform_device/platform_device.h>
|
||||
#include <legacy/x86/platform_device/capability.h>
|
||||
|
||||
namespace Platform { struct Session; }
|
||||
|
||||
|
||||
struct Platform::Session : Genode::Session
|
||||
{
|
||||
/**
|
||||
* \noapi
|
||||
*/
|
||||
static const char *service_name() { return "Platform"; }
|
||||
|
||||
enum { RAM_QUOTA = 16 * 1024, CAP_QUOTA = 2 };
|
||||
|
||||
virtual ~Session() { }
|
||||
|
||||
/**
|
||||
* Find first accessible device
|
||||
*/
|
||||
virtual Device_capability first_device(unsigned, unsigned) = 0;
|
||||
|
||||
/**
|
||||
* Find next accessible device
|
||||
*
|
||||
* \param prev_device previous device
|
||||
*
|
||||
* The 'prev_device' argument is used to iterate through all
|
||||
* devices.
|
||||
*/
|
||||
virtual Device_capability next_device(Device_capability prev_device,
|
||||
unsigned, unsigned) = 0;
|
||||
|
||||
/**
|
||||
* Free server-internal data structures representing the device
|
||||
*
|
||||
* Use this method to relax the heap partition of your PCI session.
|
||||
*/
|
||||
virtual void release_device(Device_capability device) = 0;
|
||||
|
||||
typedef Rpc_in_buffer<8> Device_name;
|
||||
|
||||
/**
|
||||
* Provide non-PCI device known by unique name
|
||||
*/
|
||||
virtual Device_capability device(Device_name const &string) = 0;
|
||||
|
||||
/**
|
||||
* Allocate memory suitable for DMA
|
||||
*/
|
||||
virtual Ram_dataspace_capability alloc_dma_buffer(size_t, Cache) = 0;
|
||||
|
||||
/**
|
||||
* Free previously allocated DMA memory
|
||||
*/
|
||||
virtual void free_dma_buffer(Ram_dataspace_capability) = 0;
|
||||
|
||||
/**
|
||||
* Return the bus address of the previously allocated DMA memory
|
||||
*/
|
||||
virtual addr_t dma_addr(Ram_dataspace_capability) = 0;
|
||||
|
||||
/*********************
|
||||
** RPC declaration **
|
||||
*********************/
|
||||
|
||||
GENODE_RPC_THROW(Rpc_first_device, Device_capability, first_device,
|
||||
GENODE_TYPE_LIST(Out_of_ram, Out_of_caps),
|
||||
unsigned, unsigned);
|
||||
GENODE_RPC_THROW(Rpc_next_device, Device_capability, next_device,
|
||||
GENODE_TYPE_LIST(Out_of_ram, Out_of_caps),
|
||||
Device_capability, unsigned, unsigned);
|
||||
GENODE_RPC(Rpc_release_device, void, release_device, Device_capability);
|
||||
GENODE_RPC_THROW(Rpc_alloc_dma_buffer, Ram_dataspace_capability,
|
||||
alloc_dma_buffer,
|
||||
GENODE_TYPE_LIST(Out_of_ram, Out_of_caps),
|
||||
size_t, Cache);
|
||||
GENODE_RPC(Rpc_free_dma_buffer, void, free_dma_buffer,
|
||||
Ram_dataspace_capability);
|
||||
GENODE_RPC(Rpc_dma_addr, addr_t, dma_addr, Ram_dataspace_capability);
|
||||
GENODE_RPC_THROW(Rpc_device, Device_capability, device,
|
||||
GENODE_TYPE_LIST(Out_of_ram, Out_of_caps),
|
||||
Device_name const &);
|
||||
|
||||
GENODE_RPC_INTERFACE(Rpc_first_device, Rpc_next_device,
|
||||
Rpc_release_device, Rpc_alloc_dma_buffer,
|
||||
Rpc_free_dma_buffer, Rpc_dma_addr, Rpc_device);
|
||||
};
|
||||
|
||||
#endif /* _INCLUDE__LEGACY__X86__PLATFORM_SESSION__PLATFORM_SESSION_H_ */
|
@ -1,9 +1,3 @@
|
||||
INCLUDE_SUB_DIRS := platform_session \
|
||||
legacy/x86/platform_session \
|
||||
legacy/x86/platform_device
|
||||
|
||||
INCLUDE_DIRS := $(addprefix include/,$(INCLUDE_SUB_DIRS))
|
||||
|
||||
MIRRORED_FROM_REP_DIR := $(INCLUDE_DIRS)
|
||||
MIRRORED_FROM_REP_DIR := include/platform_session
|
||||
|
||||
include $(REP_DIR)/recipes/api/session.inc
|
||||
|
@ -1,207 +0,0 @@
|
||||
This directory contains the implementation of Genode's x86 platform driver.
|
||||
|
||||
Behavior
|
||||
--------
|
||||
|
||||
On startup the driver scans the PCI bus hierarchy and stores the found devices.
|
||||
Per client a policy must be configured that states which client can
|
||||
access certain devices to form a virtual pci bus per client. The client may
|
||||
iterate through the virtual pci bus using the 'first' and 'next' methods of
|
||||
the platform_session interface to discover all available devices of the virtual
|
||||
bus. Non PCI devices may be discovered by using 'device' of the
|
||||
platform_session interface. As a result of the discovery a client obtains a
|
||||
device capability.
|
||||
|
||||
With the device capability the resources of the devices can be obtained, e.g.
|
||||
io_port, io_mem and irq of the platform_device interface.
|
||||
|
||||
Policy usage
|
||||
------------
|
||||
|
||||
A policy may contain several nodes describing several devices. The entries of
|
||||
a policy may describe PCI devices as non PCI devices. A PCI device is
|
||||
explicitly configured by the triple 'bus', 'device', 'function':
|
||||
|
||||
!<start name="platform_drv" managing_system="yes">
|
||||
! <resource name="RAM" quantum="8M"/>
|
||||
! ...
|
||||
! <config>
|
||||
! <policy label_prefix="usb_drv">
|
||||
! <pci bus="0" device="19" function="0"/>
|
||||
! <pci bus="0" device="18" function="3"/>
|
||||
! </policy>
|
||||
! </config>
|
||||
! ...
|
||||
|
||||
or more fuzzy by a device class alias:
|
||||
|
||||
!<start name="platform_drv" managing_system="yes">
|
||||
! <resource name="RAM" quantum="8M"/>
|
||||
! ...
|
||||
! <config>
|
||||
! <policy label_prefix="usb_drv">
|
||||
! <pci class="USB"/>
|
||||
! </policy>
|
||||
! </config>
|
||||
! ...
|
||||
|
||||
|
||||
Non PCI devices, as the PS2 controller are named by a "device" node in the policy:
|
||||
|
||||
!<start name="platform_drv" managing_system="yes">
|
||||
! <resource name="RAM" quantum="8M"/>
|
||||
! <config>
|
||||
! <policy label_prefix="ps2_drv">
|
||||
! <device name="PS2/>
|
||||
! </policy>
|
||||
! </config>
|
||||
! ...
|
||||
|
||||
|
||||
The first entry ('pci' or 'dev') of the policy node that matches will grant
|
||||
access of a device or device class to the client. Subsequent entries will not
|
||||
be checked. If a 'bus', 'device', 'function' triple was specified in one of the
|
||||
policies and in another policy a fuzzy pci class alias which would include
|
||||
the device specified by the triple, the device will not appear during device
|
||||
discovery by the client with the fuzzy pci class policy.
|
||||
|
||||
|
||||
By default the driver will try to use first MSI-X, then MSI and finally GSI.
|
||||
However, the MSI-X/MSI feature depends on the support by the used kernel and
|
||||
the PCI capabilities of the device. The MSI and MSI-X feature can be disabled
|
||||
explicitly by configuration, e.g.:
|
||||
|
||||
!<start name="platform_drv" managing_system="yes">
|
||||
! <resource name="RAM" quantum="8M"/>
|
||||
! <config>
|
||||
! <policy label_prefix="nic_drv" msi="false" msix="false">
|
||||
! ...
|
||||
! </policy>
|
||||
! </config>
|
||||
! ...
|
||||
|
||||
The managing_system attribute is evaluated by init. If set to "yes" it
|
||||
permits a component, the platform driver, to restrict the allocation of memory to
|
||||
specific physical RAM ranges. The platform driver uses this feature to ensure that
|
||||
the allocation of DMA capable memory consider several restrictions. For
|
||||
example, some drivers, as the UHCI controller, requires a
|
||||
physical memory address below 4G. Another example is that on 32bit hosts
|
||||
physical to virtual identical mappings of DMA memory for the device_pd
|
||||
(required when IOMMU is used) must be below the kernel memory boundary (3G).
|
||||
On some systems, e.g., base-hw kernel on certain ARM platforms, it allows the
|
||||
platform driver to call system management firmware via kernel syscalls.
|
||||
|
||||
The platform driver waits on startup on the first valid ACPI report, typically
|
||||
provided dynamically by the acpi driver.
|
||||
The report contains further information about the hardware the platform driver can
|
||||
not discover (e.g. IRQ re-routing information, PCI ECAM/MMCONF information).
|
||||
|
||||
A specific route to a report_rom service named 'acpi_report_rom' looks as
|
||||
in the following:
|
||||
|
||||
!<start name="platform_drv">
|
||||
! ...
|
||||
! <route>
|
||||
! <service name="ROM" label="acpi">
|
||||
! <child name="acpi_report_rom"/>
|
||||
! </service>
|
||||
! ...
|
||||
! </route>
|
||||
! ...
|
||||
|
||||
Synchronize ACPI startup and platform driver
|
||||
--------------------------------------------
|
||||
|
||||
If the config attribute 'acpi_ready' is set to 'yes', the platform driver
|
||||
monitors a ROM in XML format named 'acpi_ready'.
|
||||
|
||||
!<start name="platform_drv">
|
||||
! <config acpi_ready="yes">
|
||||
|
||||
The platform driver will announce its service not as 'Platform', but instead
|
||||
as 'Acpi' first.
|
||||
|
||||
An ACPI application like acpica can connect to the platform driver and may
|
||||
reconfigure hardware devices according to the ACPI table findings. If the
|
||||
system state changes to "acpi_ready in the XML ROM 'acpi_ready':
|
||||
|
||||
!<system state="acpi_ready"/>
|
||||
|
||||
the platform driver will announce the platform session as 'Platform', so
|
||||
that drivers may start to operate with the platform driver.
|
||||
|
||||
Supported PCI class aliases
|
||||
---------------------------
|
||||
|
||||
The following class names are supported which correspond to the
|
||||
specified PCI base class (B), sub class (S) and programming interface
|
||||
(P) combinations. ('-' matches all devices in the category)
|
||||
|
||||
alias B S P
|
||||
|
||||
ALL - - -
|
||||
AHCI 01 06 -
|
||||
AUDIO 04 01 -
|
||||
ETHERNET 02 00 -
|
||||
HDAUDIO 04 03 -
|
||||
ISABRIDGE 06 01 -
|
||||
NVME 01 08 02
|
||||
USB 0c 03 00 10 20 30
|
||||
USB4 0c 03 40
|
||||
VGA 03 00 00
|
||||
WIFI 02 80 -
|
||||
|
||||
Fixups for insufficient PCI BAR configuration
|
||||
---------------------------------------------
|
||||
|
||||
If PCI devices happen to miss complete configuration after boot, the
|
||||
platform driver supports <pci-fixup> nodes for concrete devices
|
||||
(specified by bus-device-functions tuples). As depicted below, the
|
||||
<bar> node instructs the platform driver to remap BAR id 0 to address
|
||||
0x4017002000, which amends the BIOS configuration and is stringently
|
||||
required for BARs with address 0.
|
||||
|
||||
!<pci-fixup bus="0" device="0x15" function="3">
|
||||
! <bar id="0" address="0x4017002000"/>
|
||||
!</pci-fixup>
|
||||
|
||||
|
||||
Supported non PCI devices
|
||||
-------------------------
|
||||
|
||||
The driver provides for the PS2 and PIT device the IO_PORT and IRQ resources.
|
||||
|
||||
!<start name="platform_drv" managing_system="yes">
|
||||
! <resource name="RAM" quantum="8M"/>
|
||||
! <config>
|
||||
! <policy label_prefix="ps2_drv">
|
||||
! <dev name="PS2/>
|
||||
! </policy>
|
||||
! <policy label_prefix="pit_timer_drv">
|
||||
! <dev name="PIT/>
|
||||
! </policy>
|
||||
! </config>
|
||||
!</start>
|
||||
|
||||
Also, known ACPI device resources can be statically configured on
|
||||
startup like follows.
|
||||
|
||||
!<config>
|
||||
! <policy label_prefix="driver">
|
||||
! <device name="INT34C5"/>
|
||||
! <device name="ACPI0000"/>
|
||||
! </policy>
|
||||
!
|
||||
! <device hid="INT34C5" type="acpi">
|
||||
! <irq number="14" mode="level" polarity="low"/>
|
||||
! <io_mem address="0xfd690000" size="0x1000"/>
|
||||
! <io_mem address="0xfd6a0000" size="0x1000"/>
|
||||
! <io_mem address="0xfd6d0000" size="0x1000"/>
|
||||
! <io_mem address="0xfd6e0000" size="0x1000"/>
|
||||
! </device>
|
||||
! <device name="ACPI0000" type="acpi">
|
||||
! <irq number="99"/>
|
||||
! <io_mem address="0xfc000000" size="0x100000"/>
|
||||
! <io_port_range address="0x4000" size="4"/>
|
||||
! </device>
|
||||
!</config>
|
@ -1,207 +0,0 @@
|
||||
/*
|
||||
* \brief ACPI device information from config
|
||||
* \author Christian Helmuth
|
||||
* \date 2022-05-16
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 "acpi_devices.h"
|
||||
|
||||
namespace Platform { namespace Acpi {
|
||||
struct Device_impl;
|
||||
|
||||
typedef String<16> Str;
|
||||
}}
|
||||
|
||||
|
||||
void Platform::Acpi::Device::Resource::print(Output &o) const
|
||||
{
|
||||
using Genode::print;
|
||||
switch (type) {
|
||||
case Type::IRQ:
|
||||
print(o, "IRQ [", base);
|
||||
print(o, " ", irq == Irq::EDGE ? "edge" : irq == Irq::LEVEL_LOW ? "level-low" : "level-high");
|
||||
print(o, "]");
|
||||
break;
|
||||
case Type::IOMEM:
|
||||
print(o, "IOMEM ");
|
||||
print(o, "[", Hex(base, Hex::Prefix::OMIT_PREFIX, Hex::PAD));
|
||||
print(o, "-", Hex(base + (size - 1), Hex::Prefix::OMIT_PREFIX, Hex::PAD));
|
||||
print(o, "]");
|
||||
break;
|
||||
case Type::IOPORT:
|
||||
print(o, "IOPORT ");
|
||||
print(o, "[", Hex((unsigned short)base, Hex::Prefix::OMIT_PREFIX, Hex::PAD));
|
||||
print(o, "-", Hex((unsigned short)(base + (size - 1)), Hex::Prefix::OMIT_PREFIX, Hex::PAD));
|
||||
print(o, "]");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Platform::Acpi::Device_impl : private Registry<Device>::Element,
|
||||
public Platform::Acpi::Device
|
||||
{
|
||||
private:
|
||||
|
||||
Hid _hid; /* ACPI Spec 6.1.5 Hardware ID */
|
||||
|
||||
struct Resource_element : Registry<Resource_element>::Element
|
||||
{
|
||||
unsigned id;
|
||||
Resource res;
|
||||
|
||||
Resource_element(Registry<Resource_element> ®istry,
|
||||
unsigned id,
|
||||
Resource res)
|
||||
:
|
||||
Registry<Resource_element>::Element(registry, *this),
|
||||
id(id), res(res)
|
||||
{ }
|
||||
};
|
||||
|
||||
Registry<Resource_element> _resource_registry { };
|
||||
|
||||
Resource_result _lookup_resource(Resource::Type type, unsigned const id) const
|
||||
{
|
||||
Resource const *res = nullptr;
|
||||
_resource_registry.for_each([&] (Resource_element const &e) {
|
||||
if (e.res.type == type && e.id == id)
|
||||
res = &e.res;
|
||||
});
|
||||
|
||||
if (res)
|
||||
return *res;
|
||||
else
|
||||
return Invalid_resource { };
|
||||
}
|
||||
|
||||
unsigned _max_irq_id { 0 };
|
||||
unsigned _max_iomem_id { 0 };
|
||||
unsigned _max_ioport_id { 0 };
|
||||
|
||||
public:
|
||||
|
||||
Device_impl(Registry<Device> ®istry,
|
||||
Allocator &heap, Xml_node config)
|
||||
:
|
||||
Registry<Device>::Element(registry, *this),
|
||||
_hid(config.attribute_value("name", Hid("ACPI0000")))
|
||||
{
|
||||
config.for_each_sub_node("irq", [&] (Xml_node node) {
|
||||
auto irq = [&] (Str const &mode, Str const &polarity) {
|
||||
if (mode == "level") {
|
||||
if (polarity == "high")
|
||||
return Resource::Irq::LEVEL_HIGH;
|
||||
else
|
||||
return Resource::Irq::LEVEL_LOW;
|
||||
} else {
|
||||
return Resource::Irq::EDGE;
|
||||
}
|
||||
};
|
||||
new (heap)
|
||||
Resource_element {
|
||||
_resource_registry,
|
||||
_max_irq_id++,
|
||||
Resource {
|
||||
.type = Resource::Type::IRQ,
|
||||
.base = node.attribute_value("number", addr_t(0)),
|
||||
.irq = irq(node.attribute_value("mode", Str("unchanged")),
|
||||
node.attribute_value("polarity", Str("unchanged"))) } };
|
||||
});
|
||||
config.for_each_sub_node("io_mem", [&] (Xml_node node) {
|
||||
new (heap)
|
||||
Resource_element {
|
||||
_resource_registry,
|
||||
_max_iomem_id++,
|
||||
Resource {
|
||||
.type = Resource::Type::IOMEM,
|
||||
.base = node.attribute_value("address", addr_t(0)),
|
||||
.size = node.attribute_value("size", size_t(0)) } };
|
||||
});
|
||||
config.for_each_sub_node("io_port_range", [&] (Xml_node node) {
|
||||
new (heap)
|
||||
Resource_element {
|
||||
_resource_registry,
|
||||
_max_ioport_id++,
|
||||
Resource {
|
||||
.type = Resource::Type::IOPORT,
|
||||
.base = node.attribute_value("address", addr_t(0)),
|
||||
.size = node.attribute_value("size", size_t(0)) } };
|
||||
});
|
||||
}
|
||||
|
||||
~Device_impl() { error("unexpected call of ", __func__); }
|
||||
|
||||
/* Platform::Acpi::Device interface */
|
||||
|
||||
Hid hid() const override { return _hid; }
|
||||
|
||||
Resource_result resource(unsigned idx) const override
|
||||
{
|
||||
/*
|
||||
* Index of all IOMEM and IOPORT resources - no IRQ!
|
||||
*
|
||||
* first _max_iomem_id IOMEM, then _max_ioport_id IOPORT
|
||||
*/
|
||||
if (idx < _max_iomem_id)
|
||||
return iomem(idx);
|
||||
else if (idx < _max_iomem_id + _max_ioport_id)
|
||||
return ioport(idx - _max_iomem_id);
|
||||
else
|
||||
return Invalid_resource { };
|
||||
}
|
||||
|
||||
Resource_result irq(unsigned id) const override
|
||||
{
|
||||
return _lookup_resource(Resource::Type::IRQ, id);
|
||||
}
|
||||
|
||||
Resource_result iomem(unsigned id) const override
|
||||
{
|
||||
return _lookup_resource(Resource::Type::IOMEM, id);
|
||||
}
|
||||
|
||||
Resource_result ioport(unsigned id) const override
|
||||
{
|
||||
return _lookup_resource(Resource::Type::IOPORT, id);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Platform::Acpi::Device_registry::Lookup_result
|
||||
Platform::Acpi::Device_registry::lookup(Platform::Acpi::Device::Hid hid) const
|
||||
{
|
||||
Device const *found = nullptr;
|
||||
|
||||
this->for_each([&] (Device const &device) {
|
||||
if (device.hid() == hid)
|
||||
found = &device;
|
||||
});
|
||||
|
||||
if (found)
|
||||
return found;
|
||||
else
|
||||
return Lookup_failed { };
|
||||
}
|
||||
|
||||
|
||||
void Platform::Acpi::Device_registry::init_devices(Allocator &heap, Xml_node config)
|
||||
{
|
||||
/* init only once */
|
||||
if (_initialized)
|
||||
return;
|
||||
|
||||
config.for_each_sub_node("device", [&] (Xml_node node) {
|
||||
if (node.attribute_value("type", Str()) == "acpi")
|
||||
new (heap) Device_impl(*this, heap, node);
|
||||
});
|
||||
|
||||
_initialized = true;
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
/*
|
||||
* \brief ACPI device information from config
|
||||
* \author Christian Helmuth
|
||||
* \date 2022-05-16
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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/allocator.h>
|
||||
#include <base/registry.h>
|
||||
#include <util/xml_node.h>
|
||||
#include <util/attempt.h>
|
||||
|
||||
|
||||
namespace Platform { namespace Acpi {
|
||||
using namespace Genode;
|
||||
|
||||
class Device;
|
||||
|
||||
class Device_registry;
|
||||
} }
|
||||
|
||||
|
||||
struct Platform::Acpi::Device : Interface
|
||||
{
|
||||
typedef String<10> Hid; /* ACPI Spec 6.1.5 Hardware ID */
|
||||
|
||||
struct Resource
|
||||
{
|
||||
enum class Type { IRQ, IOMEM, IOPORT };
|
||||
|
||||
enum class Irq { EDGE, LEVEL_LOW, LEVEL_HIGH };
|
||||
|
||||
Type type;
|
||||
addr_t base;
|
||||
|
||||
union {
|
||||
size_t size;
|
||||
Irq irq;
|
||||
};
|
||||
|
||||
void print(Output &o) const;
|
||||
};
|
||||
|
||||
enum struct Invalid_resource { };
|
||||
typedef Attempt<Resource, Invalid_resource> Resource_result;
|
||||
|
||||
virtual Hid hid() const = 0;
|
||||
virtual Resource_result resource(unsigned idx) const = 0;
|
||||
virtual Resource_result irq(unsigned id) const = 0;
|
||||
virtual Resource_result iomem(unsigned id) const = 0;
|
||||
virtual Resource_result ioport(unsigned id) const = 0;
|
||||
};
|
||||
|
||||
|
||||
struct Platform::Acpi::Device_registry : Genode::Registry<Device>
|
||||
{
|
||||
bool _initialized { false };
|
||||
|
||||
void init_devices(Allocator &heap, Xml_node config);
|
||||
|
||||
enum struct Lookup_failed { };
|
||||
typedef Attempt<Device const *, Lookup_failed> Lookup_result;
|
||||
|
||||
Lookup_result lookup(Device::Hid name) const;
|
||||
};
|
@ -1,117 +0,0 @@
|
||||
/*
|
||||
* \brief Pci device protection domain service for platform driver
|
||||
* \author Alexander Boettcher
|
||||
* \date 2013-02-10
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2013-2017 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/log.h>
|
||||
#include <dataspace/client.h>
|
||||
#include <region_map/client.h>
|
||||
#include <pd_session/client.h>
|
||||
|
||||
#include <util/retry.h>
|
||||
|
||||
#include "device_pd.h"
|
||||
|
||||
void Platform::Device_pd::attach_dma_mem(Dataspace_capability ds_cap,
|
||||
addr_t const dma_addr)
|
||||
{
|
||||
using namespace Genode;
|
||||
|
||||
Dataspace_client ds_client(ds_cap);
|
||||
|
||||
size_t const size = ds_client.size();
|
||||
|
||||
addr_t page = ~0UL;
|
||||
|
||||
using Attach_dma_error = Pd_session::Attach_dma_error;
|
||||
|
||||
bool retry = false;
|
||||
|
||||
do {
|
||||
_pd.attach_dma(ds_cap, dma_addr).with_result(
|
||||
|
||||
[&] (Pd_session::Attach_dma_ok) {
|
||||
|
||||
page = dma_addr;
|
||||
|
||||
/* trigger eager mapping of memory */
|
||||
_pd.map(page, size);
|
||||
retry = false;
|
||||
},
|
||||
[&] (Attach_dma_error e) {
|
||||
switch (e) {
|
||||
case Attach_dma_error::OUT_OF_RAM:
|
||||
_address_space.upgrade_ram();
|
||||
retry = true;
|
||||
break;
|
||||
case Attach_dma_error::OUT_OF_CAPS:
|
||||
_address_space.upgrade_caps();
|
||||
retry = true;
|
||||
break;
|
||||
case Attach_dma_error::DENIED:
|
||||
warning("Pd_session::attach_dma denied");
|
||||
page = dma_addr;
|
||||
retry = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
);
|
||||
} while (retry);
|
||||
|
||||
/* sanity check */
|
||||
if ((page == ~0UL) || (page != dma_addr)) {
|
||||
if (page != ~0UL)
|
||||
_address_space.detach(page);
|
||||
|
||||
error(_label, ": attachment of DMA memory @ ",
|
||||
Hex(dma_addr), "+", Hex(size), " " "failed page=", Hex(page));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void Platform::Device_pd::assign_pci(Io_mem_dataspace_capability const io_mem_cap,
|
||||
addr_t const offset, uint16_t const rid)
|
||||
{
|
||||
Dataspace_client ds_client(io_mem_cap);
|
||||
|
||||
addr_t page = _address_space.attach(io_mem_cap, 0x1000, offset);
|
||||
|
||||
/* sanity check */
|
||||
if (!page)
|
||||
throw Region_map::Region_conflict();
|
||||
|
||||
/* trigger eager mapping of memory */
|
||||
_pd.map(page, 0x1000);
|
||||
|
||||
/* utility to print rid value */
|
||||
struct Rid
|
||||
{
|
||||
uint16_t const v;
|
||||
explicit Rid(uint16_t rid) : v(rid) { }
|
||||
void print(Output &out) const
|
||||
{
|
||||
using Genode::print;
|
||||
print(out, Hex((uint8_t)(v >> 8), Hex::Prefix::OMIT_PREFIX, Hex::PAD), ":",
|
||||
Hex((uint8_t)((v >> 3) & 0x1f), Hex::Prefix::OMIT_PREFIX, Hex::PAD), ".",
|
||||
Hex(v & 0x7, Hex::Prefix::OMIT_PREFIX));
|
||||
}
|
||||
};
|
||||
|
||||
/* try to assign pci device to this protection domain */
|
||||
if (!_pd.assign_pci(page, rid))
|
||||
error(_label, ": assignment of PCI device ", Rid(rid), " failed ",
|
||||
"virt=", Hex(page));
|
||||
else
|
||||
log(_label,": assignment of PCI device ", Rid(rid), " succeeded");
|
||||
|
||||
/* we don't need the mapping anymore */
|
||||
_address_space.detach(page);
|
||||
}
|
@ -1,124 +0,0 @@
|
||||
/*
|
||||
* \brief Device PD handling for the platform driver
|
||||
* \author Alexander Boettcher
|
||||
* \date 2015-11-05
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015-2021 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 _DEVICE_PD_H_
|
||||
#define _DEVICE_PD_H_
|
||||
|
||||
/* base */
|
||||
#include <base/env.h>
|
||||
#include <base/quota_guard.h>
|
||||
#include <region_map/client.h>
|
||||
#include <pd_session/connection.h>
|
||||
#include <io_mem_session/connection.h>
|
||||
|
||||
/* os */
|
||||
#include <legacy/x86/platform_session/platform_session.h>
|
||||
|
||||
namespace Platform { class Device_pd; }
|
||||
|
||||
|
||||
class Platform::Device_pd
|
||||
{
|
||||
private:
|
||||
|
||||
Pd_connection _pd;
|
||||
Session_label const &_label;
|
||||
|
||||
/**
|
||||
* Custom handling of PD-session depletion during attach operations
|
||||
*
|
||||
* The default implementation of 'env.rm()' automatically issues a resource
|
||||
* request if the PD session quota gets exhausted. For the device PD, we don't
|
||||
* want to issue resource requests but let the platform driver reflect this
|
||||
* condition to its client.
|
||||
*/
|
||||
struct Expanding_region_map_client : Region_map_client
|
||||
{
|
||||
Env &_env;
|
||||
Pd_connection &_pd;
|
||||
Ram_quota_guard &_ram_guard;
|
||||
Cap_quota_guard &_cap_guard;
|
||||
|
||||
Expanding_region_map_client(Env &env,
|
||||
Pd_connection &pd,
|
||||
Ram_quota_guard &ram_guard,
|
||||
Cap_quota_guard &cap_guard)
|
||||
:
|
||||
Region_map_client(pd.address_space()), _env(env), _pd(pd),
|
||||
_ram_guard(ram_guard), _cap_guard(cap_guard)
|
||||
{ }
|
||||
|
||||
Local_addr attach(Dataspace_capability ds,
|
||||
size_t size = 0, off_t offset = 0,
|
||||
bool use_local_addr = false,
|
||||
Local_addr local_addr = (void *)0,
|
||||
bool executable = false,
|
||||
bool writeable = true) override
|
||||
{
|
||||
return retry<Out_of_ram>(
|
||||
[&] () {
|
||||
return retry<Out_of_caps>(
|
||||
[&] () {
|
||||
return Region_map_client::attach(ds, size, offset,
|
||||
use_local_addr,
|
||||
local_addr,
|
||||
executable,
|
||||
writeable); },
|
||||
[&] () {
|
||||
upgrade_caps();
|
||||
}
|
||||
);
|
||||
},
|
||||
[&] () {
|
||||
upgrade_ram();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void upgrade_ram()
|
||||
{
|
||||
enum { UPGRADE_RAM_QUOTA = 4096 };
|
||||
Ram_quota const ram { UPGRADE_RAM_QUOTA };
|
||||
_ram_guard.withdraw(ram);
|
||||
_env.pd().transfer_quota(_pd.rpc_cap(), ram);
|
||||
}
|
||||
|
||||
void upgrade_caps()
|
||||
{
|
||||
enum { UPGRADE_CAP_QUOTA = 2 };
|
||||
Cap_quota const caps { UPGRADE_CAP_QUOTA };
|
||||
_cap_guard.withdraw(caps);
|
||||
_env.pd().transfer_quota(_pd.rpc_cap(), caps);
|
||||
}
|
||||
} _address_space;
|
||||
|
||||
public:
|
||||
|
||||
Device_pd(Env &env,
|
||||
Session_label const &label,
|
||||
Ram_quota_guard &ram_guard,
|
||||
Cap_quota_guard &cap_guard)
|
||||
:
|
||||
_pd(env, Pd_connection::Device_pd()),
|
||||
_label(label),
|
||||
_address_space(env, _pd, ram_guard, cap_guard)
|
||||
{
|
||||
_pd.ref_account(env.pd_session_cap());
|
||||
}
|
||||
|
||||
void attach_dma_mem(Dataspace_capability, addr_t dma_addr);
|
||||
void assign_pci(Io_mem_dataspace_capability const,
|
||||
addr_t const, uint16_t const);
|
||||
};
|
||||
|
||||
#endif /* _DEVICE_PD_H_ */
|
@ -1,296 +0,0 @@
|
||||
/*
|
||||
* \brief Implementation of shared IRQs in platform driver
|
||||
* \author Alexander Boettcher
|
||||
* \date 2015-03-27
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015-2017 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.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <util/bit_allocator.h>
|
||||
#include <base/log.h>
|
||||
#include <irq_session/connection.h>
|
||||
|
||||
/* Platform driver include */
|
||||
#include "irq.h"
|
||||
#include "pci_session_component.h"
|
||||
|
||||
|
||||
namespace Platform {
|
||||
class Irq_component;
|
||||
class Irq_allocator;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A simple allocator implementation used by the Irq_proxy
|
||||
*/
|
||||
class Platform::Irq_allocator
|
||||
{
|
||||
private:
|
||||
|
||||
/*
|
||||
* We partition the IRQ space (128 GSIs) into 40 legacy IRQs and 64 MSIs (and
|
||||
* hope the partitions will never overlap on any bizarre platform.)
|
||||
*/
|
||||
enum { LEGACY = 40, MSI = 64, LEGACY_ARRAY = 64 };
|
||||
|
||||
Bit_array<LEGACY_ARRAY> _legacy { };
|
||||
Bit_allocator<MSI> _msi { };
|
||||
|
||||
public:
|
||||
|
||||
Irq_allocator()
|
||||
{
|
||||
/* reserve the last 24 legacy IRQs - 40 IRQs remaining */
|
||||
_legacy.set(LEGACY, LEGACY_ARRAY - LEGACY);
|
||||
}
|
||||
|
||||
unsigned alloc_msi()
|
||||
{
|
||||
try {
|
||||
return _msi.alloc();
|
||||
} catch (Bit_allocator<MSI>::Out_of_indices) { return ~0U; }
|
||||
}
|
||||
|
||||
void free_msi(unsigned msi) { _msi.free(msi); }
|
||||
|
||||
bool alloc_irq(addr_t addr)
|
||||
{
|
||||
try {
|
||||
_legacy.set(addr, 1);
|
||||
return true;
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* One allocator for managing in use IRQ numbers and one IRQ thread waiting
|
||||
* for Genode signals of all hardware IRQs.
|
||||
*/
|
||||
static Platform::Irq_allocator irq_alloc;
|
||||
|
||||
|
||||
/**
|
||||
* Irq_proxy interface implementation
|
||||
*/
|
||||
class Platform::Irq_component : public Platform::Irq_proxy
|
||||
{
|
||||
private:
|
||||
|
||||
Irq_connection _irq;
|
||||
Signal_handler<Platform::Irq_component> _irq_dispatcher;
|
||||
|
||||
bool _associated;
|
||||
|
||||
public:
|
||||
|
||||
void _ack_irq() {
|
||||
/*
|
||||
* Associate handler only when required, because our partner may
|
||||
* also implement shared irq and would expect to get ack_irq()
|
||||
* form us even if we have no client ...
|
||||
*/
|
||||
if (!_associated) {
|
||||
_associated = true;
|
||||
/* register signal handler on irq_session */
|
||||
_irq.sigh(_irq_dispatcher);
|
||||
}
|
||||
|
||||
_irq.ack_irq();
|
||||
}
|
||||
|
||||
virtual bool remove_sharer(Platform::Irq_sigh *s) override {
|
||||
if (!Irq_proxy::remove_sharer(s))
|
||||
return false;
|
||||
|
||||
/* De-associate handler. */
|
||||
_associated = false;
|
||||
_irq.sigh(Signal_context_capability());
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Irq_component(Env &env, unsigned gsi,
|
||||
Irq_session::Trigger trigger,
|
||||
Irq_session::Polarity polarity)
|
||||
:
|
||||
Irq_proxy(gsi),
|
||||
_irq(env, gsi, trigger, polarity),
|
||||
_irq_dispatcher(env.ep(), *this, &Platform::Irq_proxy::notify_about_irq),
|
||||
_associated(false)
|
||||
{ }
|
||||
|
||||
static Irq_component *get_irq_proxy(unsigned irq_number,
|
||||
Irq_allocator *irq_alloc = nullptr,
|
||||
Irq_session::Trigger trigger = Irq_session::TRIGGER_UNCHANGED,
|
||||
Irq_session::Polarity polarity = Irq_session::POLARITY_UNCHANGED,
|
||||
Env *env = nullptr,
|
||||
Allocator *heap = nullptr)
|
||||
{
|
||||
static List<Irq_proxy> proxies;
|
||||
static Mutex proxies_mutex;
|
||||
|
||||
Mutex::Guard mutex_guard(proxies_mutex);
|
||||
|
||||
/* lookup proxy in database */
|
||||
for (Irq_proxy *p = proxies.first(); p; p = p->next())
|
||||
if (p->irq_number() == irq_number)
|
||||
return static_cast<Irq_component *>(p);
|
||||
|
||||
/* try to create proxy */
|
||||
if (!irq_alloc || !env || !heap || !irq_alloc->alloc_irq(irq_number))
|
||||
return 0;
|
||||
|
||||
Irq_component *new_proxy = new (heap) Irq_component(*env, irq_number, trigger,
|
||||
polarity);
|
||||
proxies.insert(new_proxy);
|
||||
return new_proxy;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*******************************
|
||||
** PCI IRQ session component **
|
||||
*******************************/
|
||||
|
||||
void Platform::Irq_session_component::ack_irq()
|
||||
{
|
||||
if (msi()) {
|
||||
_msi_conn->ack_irq();
|
||||
return;
|
||||
}
|
||||
|
||||
/* shared irq handling */
|
||||
Irq_component *irq_obj = Irq_component::get_irq_proxy(_gsi);
|
||||
if (!irq_obj) {
|
||||
error("expected to find IRQ proxy for IRQ ", Hex(_gsi));
|
||||
return;
|
||||
}
|
||||
|
||||
if (irq_obj->ack_irq())
|
||||
irq_obj->_ack_irq();
|
||||
}
|
||||
|
||||
|
||||
Platform::Irq_session_component::Irq_session_component(unsigned irq,
|
||||
addr_t pci_config_space,
|
||||
Env &env,
|
||||
Allocator &heap,
|
||||
Irq_session::Trigger trigger,
|
||||
Irq_session::Polarity polarity)
|
||||
:
|
||||
_gsi(irq)
|
||||
{
|
||||
if (pci_config_space != ~0UL) {
|
||||
/* msi way */
|
||||
unsigned msi = irq_alloc.alloc_msi();
|
||||
if (msi != ~0U) {
|
||||
try {
|
||||
using namespace Genode;
|
||||
|
||||
_msi_conn.construct(env, msi, Irq_session::TRIGGER_UNCHANGED,
|
||||
Irq_session::POLARITY_UNCHANGED,
|
||||
pci_config_space);
|
||||
|
||||
_msi_info = _msi_conn->info();
|
||||
if (_msi_info.type == Irq_session::Info::Type::MSI) {
|
||||
_gsi = msi;
|
||||
return;
|
||||
}
|
||||
} catch (Service_denied) { }
|
||||
|
||||
irq_alloc.free_msi(msi);
|
||||
}
|
||||
}
|
||||
|
||||
/* invalid irq number for pci_devices */
|
||||
if (_gsi >= INVALID_IRQ)
|
||||
return;
|
||||
|
||||
_gsi = Platform::Irq_override::irq_override(_gsi, trigger, polarity);
|
||||
if (_gsi != irq || trigger != Irq_session::TRIGGER_UNCHANGED ||
|
||||
polarity != POLARITY_UNCHANGED) {
|
||||
|
||||
log("IRQ override ", irq, "->", _gsi, ", "
|
||||
"trigger mode: ", trigger, ", ", "polarity: ", polarity);
|
||||
}
|
||||
|
||||
try {
|
||||
/* check if shared IRQ object was used before */
|
||||
if (Irq_component::get_irq_proxy(_gsi, &irq_alloc, trigger,
|
||||
polarity, &env, &heap))
|
||||
return;
|
||||
} catch (Service_denied) { }
|
||||
|
||||
error("unavailable IRQ ", Hex(_gsi), " requested");
|
||||
}
|
||||
|
||||
|
||||
Platform::Irq_session_component::~Irq_session_component()
|
||||
{
|
||||
if (msi()) {
|
||||
_msi_conn->sigh(Signal_context_capability());
|
||||
|
||||
irq_alloc.free_msi(_gsi);
|
||||
return;
|
||||
}
|
||||
|
||||
/* shared irq handling */
|
||||
Irq_component *irq_obj = Irq_component::get_irq_proxy(_gsi);
|
||||
if (!irq_obj) return;
|
||||
|
||||
if (_irq_sigh.valid())
|
||||
irq_obj->remove_sharer(&_irq_sigh);
|
||||
}
|
||||
|
||||
|
||||
void Platform::Irq_session_component::sigh(Signal_context_capability sigh)
|
||||
{
|
||||
if (_msi_conn.constructed()) {
|
||||
/* register signal handler for msi directly at parent */
|
||||
_msi_conn->sigh(sigh);
|
||||
return;
|
||||
}
|
||||
|
||||
/* shared irq handling */
|
||||
Irq_component *irq_obj = Irq_component::get_irq_proxy(_gsi);
|
||||
if (!irq_obj) {
|
||||
error("signal handler got not registered - irq object unavailable");
|
||||
return;
|
||||
}
|
||||
|
||||
Signal_context_capability old = _irq_sigh;
|
||||
|
||||
if (old.valid() && !sigh.valid())
|
||||
irq_obj->remove_sharer(&_irq_sigh);
|
||||
|
||||
_irq_sigh = sigh;
|
||||
|
||||
if (!old.valid() && sigh.valid())
|
||||
irq_obj->add_sharer(&_irq_sigh);
|
||||
}
|
||||
|
||||
|
||||
unsigned short Platform::Irq_routing::rewrite(Pci::Bdf const bdf, unsigned char pin)
|
||||
{
|
||||
unsigned const bridge_bdf_bus = Platform::bridge_bdf(bdf.bus);
|
||||
|
||||
for (Irq_routing *i = list()->first(); i; i = i->next()) {
|
||||
if ((bdf.device == i->_device) && (pin - 1 == i->_device_pin) &&
|
||||
(i->_bridge_bdf == bridge_bdf_bus))
|
||||
return i->_gsi;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,189 +0,0 @@
|
||||
/*
|
||||
* \brief IRQ session interface
|
||||
* \author Alexander Boettcher
|
||||
* \date 2015-03-25
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015-2017 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 _X86__IRQ_H_
|
||||
#define _X86__IRQ_H_
|
||||
|
||||
#include <base/rpc_server.h>
|
||||
#include <base/allocator.h>
|
||||
#include <util/list.h>
|
||||
#include <irq_session/connection.h>
|
||||
|
||||
/* platform local includes */
|
||||
#include <pci_config_access.h>
|
||||
#include <irq_proxy.h>
|
||||
|
||||
|
||||
namespace Platform {
|
||||
class Irq_session_component;
|
||||
class Irq_override;
|
||||
class Irq_routing;
|
||||
}
|
||||
|
||||
|
||||
class Platform::Irq_session_component : public Rpc_object<Irq_session>,
|
||||
private List<Irq_session_component>::Element
|
||||
{
|
||||
private:
|
||||
|
||||
friend class List<Irq_session_component>;
|
||||
|
||||
unsigned _gsi;
|
||||
Platform::Irq_sigh _irq_sigh { };
|
||||
Irq_session::Info _msi_info { };
|
||||
|
||||
Constructible<Irq_connection> _msi_conn { };
|
||||
|
||||
public:
|
||||
|
||||
enum { INVALID_IRQ = 0xffU };
|
||||
|
||||
Irq_session_component(unsigned, addr_t, Env &, Allocator &heap,
|
||||
Trigger trigger = TRIGGER_UNCHANGED,
|
||||
Polarity polarity = POLARITY_UNCHANGED);
|
||||
|
||||
~Irq_session_component();
|
||||
|
||||
bool msi()
|
||||
{
|
||||
return _msi_conn.constructed() &&
|
||||
_msi_info.type == Irq_session::Info::Type::MSI;
|
||||
}
|
||||
|
||||
unsigned gsi() { return _gsi; }
|
||||
|
||||
unsigned long msi_address() const { return _msi_info.address; }
|
||||
unsigned long msi_data() const { return _msi_info.value; }
|
||||
|
||||
/***************************
|
||||
** Irq session interface **
|
||||
***************************/
|
||||
|
||||
void ack_irq() override;
|
||||
void sigh(Signal_context_capability) override;
|
||||
|
||||
Info info() override
|
||||
{
|
||||
return { .type = Info::Type::INVALID, .address = 0, .value = 0 };
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* List that holds interrupt override information
|
||||
*/
|
||||
class Platform::Irq_override : public List<Platform::Irq_override>::Element
|
||||
{
|
||||
private:
|
||||
|
||||
unsigned short const _irq; /* source IRQ */
|
||||
unsigned short const _gsi; /* target GSI */
|
||||
Irq_session::Trigger const _trigger; /* interrupt trigger mode */
|
||||
Irq_session::Polarity const _polarity; /* interrupt polarity */
|
||||
|
||||
Irq_session::Trigger _mode2trigger(unsigned mode)
|
||||
{
|
||||
enum { EDGE = 0x4, LEVEL = 0xc };
|
||||
|
||||
switch (mode & 0xc) {
|
||||
case EDGE:
|
||||
return Irq_session::TRIGGER_EDGE;
|
||||
case LEVEL:
|
||||
return Irq_session::TRIGGER_LEVEL;
|
||||
default:
|
||||
return Irq_session::TRIGGER_UNCHANGED;
|
||||
}
|
||||
}
|
||||
|
||||
Irq_session::Polarity _mode2polarity(unsigned mode)
|
||||
{
|
||||
using namespace Genode;
|
||||
enum { HIGH = 0x1, LOW = 0x3 };
|
||||
|
||||
switch (mode & 0x3) {
|
||||
case HIGH:
|
||||
return Irq_session::POLARITY_HIGH;
|
||||
case LOW:
|
||||
return Irq_session::POLARITY_LOW;
|
||||
default:
|
||||
return Irq_session::POLARITY_UNCHANGED;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Irq_override(unsigned irq, unsigned gsi, unsigned mode)
|
||||
:
|
||||
_irq(irq), _gsi(gsi),
|
||||
_trigger(_mode2trigger(mode)), _polarity(_mode2polarity(mode))
|
||||
{ }
|
||||
|
||||
static List<Irq_override> *list()
|
||||
{
|
||||
static List<Irq_override> _list;
|
||||
return &_list;
|
||||
}
|
||||
|
||||
unsigned short irq() const { return _irq; }
|
||||
unsigned short gsi() const { return _gsi; }
|
||||
Irq_session::Trigger trigger() const { return _trigger; }
|
||||
Irq_session::Polarity polarity() const { return _polarity; }
|
||||
|
||||
static unsigned irq_override(unsigned irq,
|
||||
Irq_session::Trigger &trigger,
|
||||
Irq_session::Polarity &polarity)
|
||||
{
|
||||
for (Irq_override *i = list()->first(); i; i = i->next())
|
||||
if (i->irq() == irq) {
|
||||
trigger = i->trigger();
|
||||
polarity = i->polarity();
|
||||
return i->gsi();
|
||||
}
|
||||
|
||||
/* trigger and polarity not touched in this case! */
|
||||
return irq;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* List that holds interrupt rewrite information
|
||||
*/
|
||||
class Platform::Irq_routing : public List<Platform::Irq_routing>::Element
|
||||
{
|
||||
private:
|
||||
|
||||
unsigned short _gsi;
|
||||
unsigned short _bridge_bdf;
|
||||
unsigned short _device;
|
||||
unsigned char _device_pin;
|
||||
|
||||
public:
|
||||
|
||||
static List<Irq_routing> *list()
|
||||
{
|
||||
static List<Irq_routing> _list;
|
||||
return &_list;
|
||||
}
|
||||
|
||||
Irq_routing(unsigned short gsi, unsigned short bridge_bdf,
|
||||
unsigned char device, unsigned char device_pin)
|
||||
:
|
||||
_gsi(gsi), _bridge_bdf(bridge_bdf), _device(device),
|
||||
_device_pin(device_pin)
|
||||
{ }
|
||||
|
||||
static unsigned short rewrite(Pci::Bdf, unsigned char pin);
|
||||
};
|
||||
|
||||
#endif /* _X86__IRQ_H_ */
|
@ -1,141 +0,0 @@
|
||||
/**
|
||||
* \brief Shared-interrupt support
|
||||
* \author Christian Helmuth
|
||||
* \author Sebastian Sumpf
|
||||
* \date 2009-12-15
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2009-2020 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 _DRIVERS__PLATFORM__SPEC__X86__IRQ_PROXY_H_
|
||||
#define _DRIVERS__PLATFORM__SPEC__X86__IRQ_PROXY_H_
|
||||
|
||||
#include <base/mutex.h>
|
||||
|
||||
namespace Platform {
|
||||
class Irq_sigh;
|
||||
class Irq_proxy;
|
||||
}
|
||||
|
||||
|
||||
class Platform::Irq_sigh : public Signal_context_capability,
|
||||
public List<Platform::Irq_sigh>::Element
|
||||
{
|
||||
public:
|
||||
|
||||
Irq_sigh & operator= (Signal_context_capability const &cap)
|
||||
{
|
||||
Signal_context_capability::operator=(cap);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Irq_sigh() { }
|
||||
|
||||
void notify() { Signal_transmitter(*this).submit(1); }
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Proxy thread that associates to the interrupt and unblocks waiting irqctrl
|
||||
* threads.
|
||||
*
|
||||
* XXX resources are not accounted as the interrupt is shared
|
||||
*/
|
||||
class Platform::Irq_proxy : private List<Platform::Irq_proxy>::Element
|
||||
{
|
||||
private:
|
||||
|
||||
friend class List<Platform::Irq_proxy>;
|
||||
|
||||
protected:
|
||||
|
||||
unsigned const _irq_number;
|
||||
|
||||
List<Irq_sigh> _sigh_list { };
|
||||
|
||||
Mutex _mutex { }; /* protects this object */
|
||||
int _num_sharers = 0; /* number of clients sharing this IRQ */
|
||||
int _num_acknowledgers = 0; /* number of currently blocked clients */
|
||||
bool _woken_up = false; /* client decided to wake me up -
|
||||
this prevents multiple wakeups
|
||||
to happen during initialization */
|
||||
public:
|
||||
|
||||
using List<Platform::Irq_proxy>::Element::next;
|
||||
|
||||
Irq_proxy(unsigned irq_number) : _irq_number(irq_number) { }
|
||||
|
||||
virtual ~Irq_proxy() { }
|
||||
|
||||
/**
|
||||
* Acknowledgements of clients
|
||||
*/
|
||||
virtual bool ack_irq()
|
||||
{
|
||||
Mutex::Guard mutex_guard(_mutex);
|
||||
|
||||
_num_acknowledgers++;
|
||||
|
||||
/*
|
||||
* The proxy thread has to be woken up if no client woke it up
|
||||
* before and this client is the last aspired acknowledger.
|
||||
*/
|
||||
if (!_woken_up && _num_acknowledgers == _num_sharers) {
|
||||
_woken_up = true;
|
||||
}
|
||||
|
||||
return _woken_up;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify all clients about irq
|
||||
*/
|
||||
void notify_about_irq()
|
||||
{
|
||||
Mutex::Guard mutex_guard(_mutex);
|
||||
|
||||
/* reset acknowledger state */
|
||||
_num_acknowledgers = 0;
|
||||
_woken_up = false;
|
||||
|
||||
/* inform blocked clients */
|
||||
for (Irq_sigh * s = _sigh_list.first(); s ; s = s->next())
|
||||
s->notify();
|
||||
}
|
||||
|
||||
unsigned irq_number() const { return _irq_number; }
|
||||
|
||||
virtual bool add_sharer(Irq_sigh *s)
|
||||
{
|
||||
Mutex::Guard mutex_guard(_mutex);
|
||||
|
||||
++_num_sharers;
|
||||
_sigh_list.insert(s);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool remove_sharer(Irq_sigh *s)
|
||||
{
|
||||
Mutex::Guard mutex_guard(_mutex);
|
||||
|
||||
_sigh_list.remove(s);
|
||||
--_num_sharers;
|
||||
|
||||
if (_woken_up)
|
||||
return _num_sharers == 0;
|
||||
|
||||
if (_num_acknowledgers == _num_sharers) {
|
||||
_woken_up = true;
|
||||
}
|
||||
|
||||
return _num_sharers == 0;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _DRIVERS__PLATFORM__SPEC__X86__IRQ_PROXY_H_ */
|
@ -1,215 +0,0 @@
|
||||
/*
|
||||
* \brief Platform driver for x86
|
||||
* \author Norman Feske
|
||||
* \author Christian Helmuth
|
||||
* \date 2008-01-28
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2008-2017 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_rom_dataspace.h>
|
||||
#include <base/component.h>
|
||||
#include <base/env.h>
|
||||
|
||||
#include <util/reconstructible.h>
|
||||
|
||||
#include "pci_session_component.h"
|
||||
#include "pci_device_config.h"
|
||||
#include "device_pd.h"
|
||||
#include "acpi_devices.h"
|
||||
|
||||
namespace Platform {
|
||||
struct Main;
|
||||
|
||||
namespace Nonpci { void acpi_device_registry(Acpi::Device_registry &); }
|
||||
};
|
||||
|
||||
struct Platform::Main
|
||||
{
|
||||
Env &_env;
|
||||
|
||||
Heap _heap { _env.ram(), _env.rm() };
|
||||
|
||||
Acpi::Device_registry _acpi_device_registry { };
|
||||
|
||||
/*
|
||||
* Use sliced heap to allocate each session component at a separate
|
||||
* dataspace.
|
||||
*/
|
||||
Sliced_heap sliced_heap { _env.ram(), _env.rm() };
|
||||
|
||||
Attached_rom_dataspace _config { _env, "config" };
|
||||
|
||||
Constructible<Attached_rom_dataspace> acpi_rom { };
|
||||
Constructible<Platform::Root> root { };
|
||||
|
||||
Constructible<Attached_rom_dataspace> system_state { };
|
||||
Constructible<Attached_rom_dataspace> acpi_ready { };
|
||||
|
||||
Signal_handler<Main> _acpi_report { _env.ep(), *this,
|
||||
&Main::acpi_update };
|
||||
Signal_handler<Main> _system_report { _env.ep(), *this,
|
||||
&Main::system_update };
|
||||
Signal_handler<Main> _config_handler { _env.ep(), *this,
|
||||
&Main::config_update };
|
||||
|
||||
Capability<Typed_root<Platform::Session_component> > root_cap { };
|
||||
|
||||
bool _acpi_ready = false;
|
||||
|
||||
void _attempt_acpi_reset();
|
||||
|
||||
void acpi_update()
|
||||
{
|
||||
if (!root.constructed()) {
|
||||
acpi_rom->update();
|
||||
|
||||
if (!acpi_rom->valid())
|
||||
return;
|
||||
|
||||
bool msi_platform = false;
|
||||
bool acpi_platform = false;
|
||||
|
||||
try {
|
||||
Attached_rom_dataspace info { _env, "platform_info" };
|
||||
info.xml().with_optional_sub_node("kernel", [&] (Xml_node const &node) {
|
||||
acpi_platform = node.attribute_value("acpi", acpi_platform);
|
||||
msi_platform = node.attribute_value("msi" , msi_platform);
|
||||
});
|
||||
} catch (...) { }
|
||||
|
||||
root.construct(_env, _heap, sliced_heap, _config,
|
||||
acpi_rom->local_addr<const char>(), acpi_platform,
|
||||
msi_platform);
|
||||
}
|
||||
|
||||
if (root_cap.valid())
|
||||
return;
|
||||
|
||||
/* don't announce service if no policy entry is available */
|
||||
if (!root->config_with_policy())
|
||||
return;
|
||||
|
||||
root_cap = _env.ep().manage(*root);
|
||||
|
||||
if (_acpi_ready) {
|
||||
Parent::Service_name announce_for_acpi("Acpi");
|
||||
_env.parent().announce(announce_for_acpi, root_cap);
|
||||
} else
|
||||
_env.parent().announce(root_cap);
|
||||
}
|
||||
|
||||
void system_update()
|
||||
{
|
||||
if (acpi_ready.constructed())
|
||||
acpi_ready->update();
|
||||
|
||||
if (!root.constructed())
|
||||
return;
|
||||
|
||||
if (acpi_ready.constructed() && acpi_ready->valid()) {
|
||||
Xml_node system(acpi_ready->local_addr<char>(), acpi_ready->size());
|
||||
|
||||
typedef String<16> Value;
|
||||
const Value state = system.attribute_value("state", Value("unknown"));
|
||||
|
||||
if (state == "acpi_ready" && root_cap.valid()) {
|
||||
_env.parent().announce(root_cap);
|
||||
root_cap = Capability<Typed_root<Platform::Session_component> > ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void config_update()
|
||||
{
|
||||
_config.update();
|
||||
|
||||
if (!_config.valid())
|
||||
return;
|
||||
|
||||
if (!root_cap.valid())
|
||||
acpi_update();
|
||||
|
||||
bool const system_state_was_constructed = system_state.constructed();
|
||||
|
||||
system_state.conditional(_config.xml().attribute_value("system", false),
|
||||
_env, "system");
|
||||
|
||||
if (system_state.constructed() && !system_state_was_constructed)
|
||||
system_state->sigh(_config_handler);
|
||||
|
||||
if (system_state.constructed()) {
|
||||
system_state->update();
|
||||
if (system_state->xml().attribute_value("state", String<16>()) == "reset")
|
||||
_attempt_acpi_reset();
|
||||
}
|
||||
|
||||
if (root.constructed()) {
|
||||
root->generate_pci_report();
|
||||
root->config_update();
|
||||
}
|
||||
|
||||
_acpi_device_registry.init_devices(_heap, _config.xml());
|
||||
}
|
||||
|
||||
Main(Env &env) : _env(env)
|
||||
{
|
||||
_config.sigh(_config_handler);
|
||||
|
||||
if (_config.valid())
|
||||
_acpi_ready = _config.xml().attribute_value("acpi_ready", false);
|
||||
|
||||
if (_acpi_ready) {
|
||||
acpi_ready.construct(env, "acpi_ready");
|
||||
acpi_ready->sigh(_system_report);
|
||||
}
|
||||
|
||||
/* wait for the first valid acpi report */
|
||||
acpi_rom.construct(env, "acpi");
|
||||
acpi_rom->sigh(_acpi_report);
|
||||
|
||||
/* check if already valid */
|
||||
config_update();
|
||||
acpi_update();
|
||||
system_update();
|
||||
|
||||
Nonpci::acpi_device_registry(_acpi_device_registry);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void Platform::Main::_attempt_acpi_reset()
|
||||
{
|
||||
if (!acpi_rom.constructed())
|
||||
return;
|
||||
|
||||
acpi_rom->xml().with_optional_sub_node("reset", [&] (Xml_node reset) {
|
||||
|
||||
uint16_t const io_port = reset.attribute_value("io_port", (uint16_t)0);
|
||||
uint8_t const value = reset.attribute_value("value", (uint8_t)0);
|
||||
|
||||
log("trigger reset by writing value ", value, " to I/O port ", Hex(io_port));
|
||||
|
||||
try {
|
||||
Io_port_connection reset_port { _env, io_port, 1 };
|
||||
reset_port.outb(io_port, value);
|
||||
}
|
||||
catch (...) {
|
||||
error("unable to access reset I/O port ", Hex(io_port));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void Component::construct(Genode::Env &env)
|
||||
{
|
||||
/* XXX execute constructors of global statics */
|
||||
env.exec_static_constructors();
|
||||
|
||||
static Platform::Main main(env);
|
||||
}
|
@ -1,363 +0,0 @@
|
||||
/*
|
||||
* \brief Non PCI devices, e.g. PS2
|
||||
* \author Alexander Boettcher
|
||||
* \author Christian Helmuth
|
||||
* \date 2015-04-17
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015-2021 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 "pci_session_component.h"
|
||||
#include "irq.h"
|
||||
#include "acpi_devices.h"
|
||||
|
||||
namespace Platform { namespace Nonpci { class Ps2; class Pit; } }
|
||||
|
||||
|
||||
class Platform::Nonpci::Ps2 : public Device_component
|
||||
{
|
||||
private:
|
||||
|
||||
enum {
|
||||
IRQ_KEYBOARD = 1,
|
||||
IRQ_MOUSE = 12,
|
||||
|
||||
ACCESS_WIDTH = 1,
|
||||
REG_DATA = 0x60,
|
||||
REG_STATUS = 0x64,
|
||||
};
|
||||
|
||||
Rpc_entrypoint &_ep;
|
||||
Platform::Irq_session_component _irq_mouse;
|
||||
Io_port_connection _data;
|
||||
Io_port_connection _status;
|
||||
|
||||
public:
|
||||
|
||||
Ps2(Env &env,
|
||||
Attached_io_mem_dataspace &pciconf,
|
||||
Session_component &session,
|
||||
Allocator &heap_for_irq,
|
||||
Pci::Config::Delayer &delayer,
|
||||
Device_bars_pool &devices_bars)
|
||||
:
|
||||
Device_component(env, pciconf, session, IRQ_KEYBOARD,
|
||||
heap_for_irq, delayer, devices_bars),
|
||||
_ep(env.ep().rpc_ep()),
|
||||
_irq_mouse(IRQ_MOUSE, ~0UL, env, heap_for_irq),
|
||||
_data(env, REG_DATA, ACCESS_WIDTH),
|
||||
_status(env, REG_STATUS, ACCESS_WIDTH)
|
||||
{
|
||||
_ep.manage(&_irq_mouse);
|
||||
}
|
||||
|
||||
~Ps2() { _ep.dissolve(&_irq_mouse); }
|
||||
|
||||
Irq_session_capability irq(uint8_t virt_irq) override
|
||||
{
|
||||
switch (virt_irq) {
|
||||
case 0:
|
||||
log("PS2 uses IRQ, vector ", Hex(IRQ_KEYBOARD));
|
||||
return Device_component::irq(virt_irq);
|
||||
case 1:
|
||||
log("PS2 uses IRQ, vector ", Hex(IRQ_MOUSE));
|
||||
return _irq_mouse.cap();
|
||||
default:
|
||||
return Irq_session_capability();
|
||||
}
|
||||
}
|
||||
|
||||
Io_port_session_capability io_port(uint8_t io_port) override
|
||||
{
|
||||
if (io_port == 0)
|
||||
return _data.cap();
|
||||
if (io_port == 1)
|
||||
return _status.cap();
|
||||
|
||||
return Io_port_session_capability();
|
||||
}
|
||||
|
||||
Io_mem_session_capability io_mem(uint8_t, Cache, addr_t, size_t) override
|
||||
{
|
||||
return Io_mem_session_capability();
|
||||
}
|
||||
|
||||
Device_name_string name() const override { return "PS2"; }
|
||||
};
|
||||
|
||||
|
||||
class Platform::Nonpci::Pit : public Device_component
|
||||
{
|
||||
private:
|
||||
|
||||
enum {
|
||||
IRQ_PIT = 0,
|
||||
|
||||
PIT_PORT = 0x40,
|
||||
PORTS_WIDTH = 4
|
||||
};
|
||||
|
||||
Io_port_connection _ports;
|
||||
|
||||
public:
|
||||
|
||||
Pit(Env &env,
|
||||
Attached_io_mem_dataspace &pciconf,
|
||||
Session_component &session,
|
||||
Allocator &heap_for_irq,
|
||||
Pci::Config::Delayer &delayer,
|
||||
Device_bars_pool &devices_bars)
|
||||
:
|
||||
Device_component(env, pciconf, session, IRQ_PIT,
|
||||
heap_for_irq, delayer, devices_bars),
|
||||
_ports(env, PIT_PORT, PORTS_WIDTH)
|
||||
{ }
|
||||
|
||||
Io_port_session_capability io_port(uint8_t io_port) override
|
||||
{
|
||||
if (io_port == 0)
|
||||
return _ports.cap();
|
||||
|
||||
return Io_port_session_capability();
|
||||
}
|
||||
|
||||
Device_name_string name() const override { return "PIT"; }
|
||||
};
|
||||
|
||||
|
||||
namespace Platform { namespace Nonpci {
|
||||
class Acpi;
|
||||
|
||||
void acpi_device_registry(Platform::Acpi::Device_registry &);
|
||||
} }
|
||||
|
||||
|
||||
static Platform::Acpi::Device_registry *_acpi_device_registry;
|
||||
|
||||
void Platform::Nonpci::acpi_device_registry(Platform::Acpi::Device_registry ®istry)
|
||||
{
|
||||
_acpi_device_registry = ®istry;
|
||||
}
|
||||
|
||||
|
||||
class Platform::Nonpci::Acpi : public Device_component
|
||||
{
|
||||
private:
|
||||
|
||||
Env &_env;
|
||||
|
||||
Allocator &_session_heap;
|
||||
|
||||
Platform::Acpi::Device const &_acpi_device;
|
||||
|
||||
Irq_session_component *_irq0 = nullptr;
|
||||
|
||||
/*
|
||||
* Noncopyable
|
||||
*/
|
||||
Acpi(Acpi const &) = delete;
|
||||
Acpi &operator = (Acpi const &) = delete;
|
||||
|
||||
public:
|
||||
|
||||
Acpi(Platform::Acpi::Device const &acpi_device,
|
||||
Env &env,
|
||||
Attached_io_mem_dataspace &pciconf,
|
||||
Session_component &session,
|
||||
Allocator &session_heap,
|
||||
Allocator &global_heap,
|
||||
Pci::Config::Delayer &delayer,
|
||||
Device_bars_pool &devices_bars)
|
||||
:
|
||||
Device_component(env, pciconf, session, 0,
|
||||
global_heap, delayer, devices_bars),
|
||||
_env(env), _session_heap(session_heap), _acpi_device(acpi_device)
|
||||
{ }
|
||||
|
||||
Device_name_string name() const override { return _acpi_device.hid(); }
|
||||
|
||||
/* Platform::Device interface */
|
||||
|
||||
void bus_address(unsigned char *bus, unsigned char *dev,
|
||||
unsigned char *fn) override
|
||||
{
|
||||
*bus = 0; *dev = 0; *fn = 0;
|
||||
}
|
||||
|
||||
unsigned short vendor_id() override { return ~0; }
|
||||
|
||||
unsigned short device_id() override { return ~0; }
|
||||
|
||||
unsigned class_code() override { return ~0; }
|
||||
|
||||
Resource resource(int resource_id) override
|
||||
{
|
||||
using Acpi_device = Platform::Acpi::Device;
|
||||
|
||||
return _acpi_device.resource(resource_id).convert<Resource>(
|
||||
[&] (Acpi_device::Resource r) {
|
||||
/* craft artificial BAR values from resource info */
|
||||
switch (r.type) {
|
||||
case Acpi_device::Resource::Type::IOMEM:
|
||||
return Resource((r.base & 0xfffffff0) | 0b0000, r.size);
|
||||
|
||||
case Acpi_device::Resource::Type::IOPORT:
|
||||
return Resource((r.base & 0xfffffffc) | 0b01, r.size);
|
||||
|
||||
case Acpi_device::Resource::Type::IRQ:
|
||||
return Resource();
|
||||
}
|
||||
return Resource();
|
||||
},
|
||||
[&] (Acpi_device::Invalid_resource) { return Resource(); });
|
||||
}
|
||||
|
||||
unsigned config_read(unsigned char, Access_size) override
|
||||
{
|
||||
warning("ignore config_read from ACPI device ", _acpi_device.hid());
|
||||
return 0;
|
||||
}
|
||||
|
||||
void config_write(unsigned char, unsigned, Access_size) override
|
||||
{
|
||||
warning("ignore config_write to ACPI device ", _acpi_device.hid());
|
||||
}
|
||||
|
||||
Irq_session_capability irq(uint8_t v_id) override
|
||||
{
|
||||
using Acpi_device = Platform::Acpi::Device;
|
||||
|
||||
/* TODO more than one IRQ */
|
||||
if (v_id != 0) {
|
||||
warning("ACPI device with more than one IRQ not supported (requested id ", v_id, ")");
|
||||
return Irq_session_capability();
|
||||
}
|
||||
if (_irq0) return _irq0->cap();
|
||||
|
||||
/* TODO needs try see pci_device.cc ::iomem() */
|
||||
return _acpi_device.irq(v_id).convert<Irq_session_capability>(
|
||||
[&] (Acpi_device::Resource r) {
|
||||
Platform::Irq_session_component &irq =
|
||||
*new(_session_heap)
|
||||
Platform::Irq_session_component(r.base, ~0UL, _env, _session_heap,
|
||||
Irq_session::TRIGGER_LEVEL,
|
||||
Irq_session::POLARITY_LOW);
|
||||
_env.ep().manage(irq);
|
||||
_irq0 = &irq;
|
||||
return irq.cap();
|
||||
},
|
||||
[&] (Acpi_device::Invalid_resource) { return Irq_session_capability(); });
|
||||
}
|
||||
|
||||
Io_port_session_capability io_port(uint8_t v_id) override
|
||||
{
|
||||
using Acpi_device = Platform::Acpi::Device;
|
||||
|
||||
/* TODO needs try see pci_device.cc ::iomem() */
|
||||
|
||||
return _acpi_device.ioport(v_id).convert<Io_port_session_capability>(
|
||||
[&] (Acpi_device::Resource r) {
|
||||
Io_port_connection &ioport =
|
||||
*new (_session_heap) Io_port_connection(_env, r.base, r.size);
|
||||
return ioport.cap();
|
||||
},
|
||||
[&] (Acpi_device::Invalid_resource) { return Io_port_session_capability(); });
|
||||
}
|
||||
|
||||
Io_mem_session_capability io_mem(uint8_t v_id,
|
||||
Cache /* ignored */,
|
||||
addr_t /* ignored */,
|
||||
size_t /* ignored */) override
|
||||
{
|
||||
using Acpi_device = Platform::Acpi::Device;
|
||||
|
||||
/* TODO needs try see pci_device.cc ::iomem() */
|
||||
|
||||
return _acpi_device.iomem(v_id).convert<Io_mem_session_capability>(
|
||||
[&] (Acpi_device::Resource r) {
|
||||
Io_mem_connection &iomem =
|
||||
*new (_session_heap) Io_mem_connection(_env, r.base, r.size);
|
||||
return iomem.cap();
|
||||
},
|
||||
[&] (Acpi_device::Invalid_resource) { return Io_mem_session_capability(); });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Platform session component devices which are non PCI devices, e.g. PS2
|
||||
*/
|
||||
Platform::Device_capability Platform::Session_component::device(Device_name const &name)
|
||||
{
|
||||
if (!name.valid_string())
|
||||
return Device_capability();
|
||||
|
||||
Device_name_string const device_name { name.string() };
|
||||
|
||||
enum class Type { UNKNOWN, PS2, PIT, ACPI } device_type { Type::UNKNOWN };
|
||||
|
||||
Platform::Acpi::Device const *acpi_device = nullptr;
|
||||
|
||||
if (device_name == "PS2")
|
||||
device_type = Type::PS2;
|
||||
|
||||
else if (device_name == "PIT")
|
||||
device_type = Type::PIT;
|
||||
|
||||
else if (_acpi_device_registry)
|
||||
device_type = _acpi_device_registry->lookup(device_name).convert<Type>(
|
||||
[&] (Acpi::Device const *device) {
|
||||
acpi_device = device;
|
||||
return Type::ACPI;
|
||||
},
|
||||
[&] (Acpi::Device_registry::Lookup_failed) { return Type::UNKNOWN; });
|
||||
|
||||
bool const found = device_type != Type::UNKNOWN;
|
||||
|
||||
if (!found) {
|
||||
error("unknown device name '", device_name, "'");
|
||||
return Device_capability();
|
||||
}
|
||||
|
||||
if (!permit_device(device_name.string())) {
|
||||
error("denied access to device '", device_name, "' for "
|
||||
"session '", _label, "'");
|
||||
return Device_capability();
|
||||
}
|
||||
|
||||
try {
|
||||
Device_component * dev = nullptr;
|
||||
|
||||
switch (device_type) {
|
||||
case Type::PS2:
|
||||
dev = new (_md_alloc) Nonpci::Ps2(_env, _pciconf, *this,
|
||||
_global_heap, _delayer,
|
||||
_devices_bars);
|
||||
break;
|
||||
case Type::PIT:
|
||||
dev = new (_md_alloc) Nonpci::Pit(_env, _pciconf, *this,
|
||||
_global_heap, _delayer,
|
||||
_devices_bars);
|
||||
break;
|
||||
case Type::ACPI:
|
||||
dev = new (_md_alloc) Nonpci::Acpi(*acpi_device,
|
||||
_env, _pciconf, *this,
|
||||
_md_alloc,
|
||||
_global_heap, _delayer,
|
||||
_devices_bars);
|
||||
break;
|
||||
default:
|
||||
return Device_capability();
|
||||
}
|
||||
|
||||
_device_list.insert(dev);
|
||||
return _env.ep().rpc_ep().manage(dev);
|
||||
}
|
||||
catch (Out_of_ram) { throw; }
|
||||
catch (Service_denied) { return Device_capability(); }
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
/*
|
||||
* \brief PCI bridge discovery
|
||||
* \author Sebastian Sumpf <sebastian.sumpf@genode-labs.com>
|
||||
* \date 2012-02-25
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2009-2017 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 _PCI_BRIDGE_H_
|
||||
#define _PCI_BRIDGE_H_
|
||||
|
||||
#include <util/list.h>
|
||||
|
||||
namespace Platform { class Bridge; }
|
||||
|
||||
|
||||
/**
|
||||
* List of PCI-bridge devices
|
||||
*/
|
||||
class Platform::Bridge : public List<Bridge>::Element
|
||||
{
|
||||
private:
|
||||
|
||||
/* PCI config space fields of bridge */
|
||||
unsigned char _bus;
|
||||
unsigned char _dev;
|
||||
unsigned char _fun;
|
||||
|
||||
unsigned char _secondary_bus;
|
||||
unsigned char _subordinate_bus;
|
||||
|
||||
public:
|
||||
|
||||
Bridge(unsigned char bus, unsigned char dev, unsigned char fun,
|
||||
unsigned char secondary_bus, unsigned char subordinate_bus)
|
||||
:
|
||||
_bus(bus), _dev(dev), _fun(fun), _secondary_bus(secondary_bus),
|
||||
_subordinate_bus(subordinate_bus)
|
||||
{ }
|
||||
|
||||
bool part_of (unsigned char bus) const
|
||||
{
|
||||
return _secondary_bus <= bus && bus <= _subordinate_bus;
|
||||
}
|
||||
|
||||
unsigned short bdf()
|
||||
{
|
||||
unsigned short bdf = _bus;
|
||||
bdf = (bdf << 8) | ((_dev & 0x1f) << 3) | (_fun & 0x7);
|
||||
return bdf;
|
||||
}
|
||||
|
||||
enum { INVALID_ROOT_BRIDGE = 0x10000U };
|
||||
static unsigned root_bridge_bdf;
|
||||
};
|
||||
|
||||
#endif /* _PCI_BRIDGE_H_ */
|
@ -1,265 +0,0 @@
|
||||
/*
|
||||
* \brief PCI configuration access for the platform driver
|
||||
* \author Norman Feske
|
||||
* \date 2008-01-28
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2008-2021 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 _X86_PCI_CONFIG_ACCESS_H_
|
||||
#define _X86_PCI_CONFIG_ACCESS_H_
|
||||
|
||||
#include <base/attached_io_mem_dataspace.h>
|
||||
#include <base/attached_rom_dataspace.h>
|
||||
#include <legacy/x86/platform_device/platform_device.h>
|
||||
#include <util/bit_array.h>
|
||||
#include <util/mmio.h>
|
||||
|
||||
namespace Platform { namespace Pci { struct Bdf; struct Config; } }
|
||||
|
||||
|
||||
struct Platform::Pci::Bdf
|
||||
{
|
||||
unsigned bus, device, function;
|
||||
|
||||
static Bdf from_value(uint16_t const bdf)
|
||||
{
|
||||
return Bdf { .bus = (bdf >> 8) & 0xffu,
|
||||
.device = (bdf >> 3) & 0x1fu,
|
||||
.function = bdf & 0x07u };
|
||||
}
|
||||
|
||||
static Bdf from_xml(Xml_node node)
|
||||
{
|
||||
return Bdf { .bus = node.attribute_value("bus", 0U),
|
||||
.device = node.attribute_value("device", 0U),
|
||||
.function = node.attribute_value("function", 0U) };
|
||||
}
|
||||
|
||||
|
||||
uint16_t value() const {
|
||||
return ((bus & 0xff) << 8) | ((device & 0x1f) << 3) | (function & 7); }
|
||||
|
||||
bool operator == (Bdf const &other) const {
|
||||
return value() == other.value(); }
|
||||
|
||||
void print(Output &out) const
|
||||
{
|
||||
using Genode::print;
|
||||
print(out, Hex((uint8_t)bus, Hex::Prefix::OMIT_PREFIX, Hex::Pad::PAD),
|
||||
":", Hex((uint8_t)device, Hex::Prefix::OMIT_PREFIX, Hex::Pad::PAD),
|
||||
".", Hex(function, Hex::Prefix::OMIT_PREFIX));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
namespace Platform {
|
||||
|
||||
class Config_access
|
||||
{
|
||||
private:
|
||||
|
||||
Attached_io_mem_dataspace &_pciconf;
|
||||
|
||||
size_t const _pciconf_size;
|
||||
|
||||
/**
|
||||
* Calculate device offset from BDF
|
||||
*
|
||||
* \return device base address
|
||||
*/
|
||||
unsigned _dev_base(Pci::Bdf const bdf)
|
||||
{
|
||||
return unsigned(bdf.value()) << 12;
|
||||
}
|
||||
|
||||
Bit_array<256> _used { };
|
||||
|
||||
void _use_register(unsigned char addr, unsigned short width)
|
||||
{
|
||||
for (unsigned i = 0; i < width; i++)
|
||||
if (!_used.get(addr + i, 1))
|
||||
_used.set(addr + i, 1);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
class Invalid_mmio_access : Exception { };
|
||||
|
||||
Config_access(Attached_io_mem_dataspace &pciconf)
|
||||
:
|
||||
_pciconf(pciconf),
|
||||
_pciconf_size(Dataspace_client(_pciconf.cap()).size())
|
||||
{ }
|
||||
|
||||
Config_access(Config_access &c)
|
||||
: _pciconf(c._pciconf), _pciconf_size(c._pciconf_size) { }
|
||||
|
||||
/**
|
||||
* Read value from config space of specified device/function
|
||||
*
|
||||
* \param bdf target PCI bus, device & function ID
|
||||
* \param addr target byte within targeted PCI config space
|
||||
* \param size bit width of read access
|
||||
*
|
||||
* \return value read from specified config-space address
|
||||
*
|
||||
* There is no range check for the input values.
|
||||
*/
|
||||
unsigned read(Pci::Bdf const bdf, unsigned char const addr,
|
||||
Device::Access_size const size, bool const track = true)
|
||||
{
|
||||
unsigned const offset = _dev_base(bdf) + addr;
|
||||
char const * const field_ptr = _pciconf.local_addr<char>() + offset;
|
||||
|
||||
if (offset >= _pciconf_size)
|
||||
throw Invalid_mmio_access();
|
||||
|
||||
switch (size) {
|
||||
|
||||
case Device::ACCESS_8BIT:
|
||||
if (track)
|
||||
_use_register(addr, 1);
|
||||
return *(uint8_t const *)field_ptr;
|
||||
|
||||
case Device::ACCESS_16BIT:
|
||||
if (track)
|
||||
_use_register(addr, 2);
|
||||
return *(uint16_t const *)field_ptr;
|
||||
|
||||
case Device::ACCESS_32BIT:
|
||||
if (track)
|
||||
_use_register(addr, 4);
|
||||
return *(uint32_t const *)field_ptr;
|
||||
}
|
||||
return ~0U;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write to config space of specified device/function
|
||||
*
|
||||
* \param bdf target PCI bus, device & function ID
|
||||
* \param addr target byte within targeted PCI config space
|
||||
* \param value value to be written
|
||||
* \param size bit width of write access
|
||||
*
|
||||
* There is no range check for the input values.
|
||||
*/
|
||||
void write(Pci::Bdf const bdf, unsigned char const addr,
|
||||
unsigned const value, Device::Access_size const size,
|
||||
bool const track = true)
|
||||
{
|
||||
unsigned const offset = _dev_base(bdf) + addr;
|
||||
char * const field_ptr = _pciconf.local_addr<char>() + offset;
|
||||
|
||||
if (offset >= _pciconf_size)
|
||||
throw Invalid_mmio_access();
|
||||
|
||||
/*
|
||||
* Write value to targeted address, see read() comment above
|
||||
* for an explanation of the assembly templates
|
||||
*/
|
||||
switch (size) {
|
||||
|
||||
case Device::ACCESS_8BIT:
|
||||
if (track)
|
||||
_use_register(addr, 1);
|
||||
*(uint8_t volatile *)field_ptr = value;
|
||||
break;
|
||||
|
||||
case Device::ACCESS_16BIT:
|
||||
if (track)
|
||||
_use_register(addr, 2);
|
||||
*(uint16_t volatile *)field_ptr = value;
|
||||
break;
|
||||
|
||||
case Device::ACCESS_32BIT:
|
||||
if (track)
|
||||
_use_register(addr, 4);
|
||||
*(uint32_t volatile *)field_ptr = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool reg_in_use(unsigned char addr, Device::Access_size size)
|
||||
{
|
||||
switch (size) {
|
||||
case Device::ACCESS_8BIT:
|
||||
return _used.get(addr, 1);
|
||||
case Device::ACCESS_16BIT:
|
||||
return _used.get(addr, 2);
|
||||
case Device::ACCESS_32BIT:
|
||||
return _used.get(addr, 4);
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Type-safe, fine-grained access to a PCI config space of a device
|
||||
*
|
||||
* It is similar to Genode::Mmio but uses Config_access as backend.
|
||||
*/
|
||||
struct Platform::Pci::Config: Register_set<Platform::Pci::Config>
|
||||
{
|
||||
private:
|
||||
|
||||
friend Register_set_plain_access;
|
||||
|
||||
Config_access &_config;
|
||||
Pci::Bdf _bdf;
|
||||
uint16_t _cap;
|
||||
|
||||
template <typename ACCESS_T>
|
||||
inline ACCESS_T _read(off_t const &offset) const
|
||||
{
|
||||
addr_t const cap = _cap + offset;
|
||||
|
||||
if (sizeof(ACCESS_T) == 1)
|
||||
return _config.read(_bdf, cap, Device::ACCESS_8BIT);
|
||||
if (sizeof(ACCESS_T) == 2)
|
||||
return _config.read(_bdf, cap, Device::ACCESS_16BIT);
|
||||
if (sizeof(ACCESS_T) == 4)
|
||||
return _config.read(_bdf, cap, Device::ACCESS_32BIT);
|
||||
|
||||
warning("unsupported read ", sizeof(ACCESS_T));
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <typename ACCESS_T>
|
||||
inline void _write(off_t const offset, ACCESS_T const value)
|
||||
{
|
||||
addr_t const cap = _cap + offset;
|
||||
|
||||
switch (sizeof(ACCESS_T)) {
|
||||
case 1 :
|
||||
_config.write(_bdf, cap, value, Device::ACCESS_8BIT);
|
||||
break;
|
||||
case 2 :
|
||||
_config.write(_bdf, cap, value, Device::ACCESS_16BIT);
|
||||
break;
|
||||
case 4 :
|
||||
_config.write(_bdf, cap, value, Device::ACCESS_32BIT);
|
||||
break;
|
||||
default:
|
||||
warning("unsupported write ", sizeof(ACCESS_T));
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Config(Config_access &config, Pci::Bdf const &bdf, uint16_t cap)
|
||||
:
|
||||
Register_set<Config>(*this), _config(config), _bdf(bdf), _cap(cap)
|
||||
{ }
|
||||
|
||||
};
|
||||
|
||||
#endif /* _X86_PCI_CONFIG_ACCESS_H_ */
|
@ -1,341 +0,0 @@
|
||||
/*
|
||||
* \brief PCI device component implementation
|
||||
* \author Alexander Boettcher
|
||||
* \author Christian Helmuth
|
||||
* \date 2022-06-24
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015-2017 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 "pci_session_component.h"
|
||||
#include "pci_device_component.h"
|
||||
|
||||
|
||||
Genode::Io_port_session_capability Platform::Device_component::io_port(uint8_t const v_id)
|
||||
{
|
||||
uint8_t const max = sizeof(_io_port_conn) / sizeof(_io_port_conn[0]);
|
||||
uint8_t r_id = 0;
|
||||
|
||||
for (unsigned i = 0; i < max; ++i) {
|
||||
Pci::Resource res = _device_config.resource(i);
|
||||
|
||||
if (!res.valid() || res.mem())
|
||||
continue;
|
||||
|
||||
if (v_id != r_id) {
|
||||
++r_id;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_io_port_conn[v_id] != nullptr)
|
||||
return _io_port_conn[v_id]->cap();
|
||||
|
||||
try {
|
||||
_io_port_conn[v_id] = new (_slab_ioport)
|
||||
Io_port_connection(_env, res.base(), res.size());
|
||||
return _io_port_conn[v_id]->cap();
|
||||
} catch (...) {
|
||||
return Io_port_session_capability();
|
||||
}
|
||||
}
|
||||
|
||||
return Io_port_session_capability();
|
||||
}
|
||||
|
||||
Genode::Io_mem_session_capability Platform::Device_component::io_mem(uint8_t const v_id,
|
||||
Cache const caching,
|
||||
addr_t const offset,
|
||||
size_t const size)
|
||||
{
|
||||
uint8_t max = sizeof(_io_mem) / sizeof(_io_mem[0]);
|
||||
uint8_t r_id = 0;
|
||||
|
||||
for (unsigned i = 0; i < max; ++i) {
|
||||
Pci::Resource res = _device_config.resource(i);
|
||||
|
||||
if (!res.valid() || !res.mem())
|
||||
continue;
|
||||
|
||||
if (v_id != r_id) {
|
||||
++r_id;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* limit IO_MEM session size to resource size */
|
||||
size_t const res_size = min(size, res.size());
|
||||
|
||||
if (offset >= res.size() || offset > res.size() - res_size)
|
||||
return Io_mem_session_capability();
|
||||
|
||||
/* error if MEM64 resource base address above 4G on 32-bit */
|
||||
if (res.base() > ~(addr_t)0) {
|
||||
error("request for MEM64 resource of ", _device_config,
|
||||
" at ", Hex(res.base()), " not supported on 32-bit system");
|
||||
return Io_mem_session_capability();
|
||||
}
|
||||
|
||||
try {
|
||||
bool const wc = caching == Cache::WRITE_COMBINED;
|
||||
Io_mem * io_mem = new (_slab_iomem) Io_mem(_env,
|
||||
res.base() + offset,
|
||||
res_size, wc);
|
||||
_io_mem[i].insert(io_mem);
|
||||
return io_mem->cap();
|
||||
}
|
||||
catch (Out_of_caps) {
|
||||
warning("Out_of_caps in Device_component::io_mem");
|
||||
throw;
|
||||
}
|
||||
catch (Out_of_ram) {
|
||||
warning("Out_of_ram in Device_component::io_mem");
|
||||
throw;
|
||||
}
|
||||
catch (...) {
|
||||
warning("unhandled exception in 'Device_component::io_mem'");
|
||||
return Io_mem_session_capability();
|
||||
}
|
||||
}
|
||||
|
||||
return Io_mem_session_capability();
|
||||
}
|
||||
|
||||
|
||||
unsigned Platform::Device_component::config_read(unsigned char address, Access_size size)
|
||||
{
|
||||
if (pci_device())
|
||||
return _device_config.read(_config_access, address, size,
|
||||
_device_config.DONT_TRACK_ACCESS);
|
||||
|
||||
return ~0;
|
||||
}
|
||||
|
||||
|
||||
void Platform::Device_component::config_write(unsigned char address,
|
||||
unsigned value,
|
||||
Access_size size)
|
||||
{
|
||||
if (!pci_device())
|
||||
return;
|
||||
|
||||
/* white list of ports which we permit to write */
|
||||
switch (address) {
|
||||
case 0x40 ... 0xff:
|
||||
/* allow access to device-specific registers if not used by us */
|
||||
if (!_device_config.reg_in_use(_config_access, address, size))
|
||||
break;
|
||||
|
||||
error(_device_config, " write access to "
|
||||
"address=", Hex(address), " "
|
||||
"value=", Hex(value), " "
|
||||
"size=", Hex(size), " "
|
||||
"denied - it is used by the platform driver.");
|
||||
return;
|
||||
case Device_config::PCI_CMD_REG: /* COMMAND register - first byte */
|
||||
if (size == Access_size::ACCESS_16BIT)
|
||||
break;
|
||||
[[fallthrough]];
|
||||
case Device_config::PCI_CMD_REG + 1: /* COMMAND register - second byte */
|
||||
case 0xd: /* Latency timer */
|
||||
if (size == Access_size::ACCESS_8BIT)
|
||||
break;
|
||||
[[fallthrough]];
|
||||
default:
|
||||
warning(_device_config, " write access to "
|
||||
"address=", Hex(address), " "
|
||||
"value=", Hex(value), " "
|
||||
"size=", Hex(size), " "
|
||||
"got dropped");
|
||||
return;
|
||||
}
|
||||
|
||||
/* assign device to device_pd */
|
||||
if (!_device_assigned &&
|
||||
address == Device_config::PCI_CMD_REG &&
|
||||
(value & Device_config::PCI_CMD_DMA)) {
|
||||
|
||||
try { _session.assign_device(this); }
|
||||
catch (Out_of_ram) { throw; }
|
||||
catch (Out_of_caps) { throw; }
|
||||
catch (...) {
|
||||
error("assignment to device failed");
|
||||
}
|
||||
_device_assigned = true;
|
||||
_device_used = true;
|
||||
}
|
||||
|
||||
_device_config.write(_config_access, address, value, size,
|
||||
_device_config.DONT_TRACK_ACCESS);
|
||||
}
|
||||
|
||||
Genode::Irq_session_capability Platform::Device_component::irq(uint8_t id)
|
||||
{
|
||||
if (id != 0)
|
||||
return Irq_session_capability();
|
||||
|
||||
if (_irq_session)
|
||||
return _irq_session->cap();
|
||||
|
||||
if (!pci_device()) {
|
||||
/* Non PCI devices */
|
||||
_irq_session = construct_at<Irq_session_component>(_mem_irq_component,
|
||||
_irq_line, ~0UL,
|
||||
_env,
|
||||
_global_heap);
|
||||
|
||||
_env.ep().rpc_ep().manage(_irq_session);
|
||||
return _irq_session->cap();
|
||||
}
|
||||
|
||||
uint16_t const msi_cap = _msi_cap();
|
||||
uint16_t const msix_cap = _msix_cap();
|
||||
bool const try_msi_msix = (_session.msi_usage() && msi_cap) ||
|
||||
(_session.msix_usage() && msix_cap);
|
||||
_irq_session = construct_at<Irq_session_component>(_mem_irq_component,
|
||||
_configure_irq(_irq_line, msi_cap, msix_cap),
|
||||
try_msi_msix ? _config_space : ~0UL,
|
||||
_env, _global_heap);
|
||||
_env.ep().rpc_ep().manage(_irq_session);
|
||||
|
||||
bool msix_used = false;
|
||||
bool msi_used = false;
|
||||
|
||||
if (_irq_session->msi()) {
|
||||
if (_session.msix_usage() && msix_cap)
|
||||
msix_used = _setup_msix(msix_cap);
|
||||
if (!msix_used && msi_cap)
|
||||
msi_used = _setup_msi(msi_cap);
|
||||
}
|
||||
|
||||
if (_irq_session->msi())
|
||||
log(_device_config, " uses ",
|
||||
msix_used ? "MSI-X " : "",
|
||||
(msix_used && msi_cap) ? "(supports MSI) " : "",
|
||||
msi_used ? "MSI ": "",
|
||||
(msi_used && msix_cap) ? "(supports MSI-X) " : "",
|
||||
(!msi_used && !msix_used) ? "no MSI/-X/IRQ " : "",
|
||||
"vector ", Hex(_irq_session->msi_data()), ", "
|
||||
"address ", Hex(_irq_session->msi_address()));
|
||||
else
|
||||
log(_device_config, " uses IRQ, vector ",
|
||||
Hex(_irq_line),
|
||||
(msi_cap || msix_cap) ? ", supports:" : "",
|
||||
msi_cap ? " MSI" : "",
|
||||
msix_cap ? " MSI-X" : "");
|
||||
|
||||
return _irq_session->cap();
|
||||
}
|
||||
|
||||
bool Platform::Device_component::_setup_msi(uint16_t const msi_cap)
|
||||
{
|
||||
try {
|
||||
addr_t const msi_address = _irq_session->msi_address();
|
||||
uint32_t const msi_value = _irq_session->msi_data();
|
||||
|
||||
uint16_t msi = _read_config_16(msi_cap + 2);
|
||||
|
||||
_write_config_32(msi_cap + 0x4, msi_address);
|
||||
|
||||
if (msi & CAP_MSI_64) {
|
||||
uint32_t upper_address = sizeof(msi_address) > 4
|
||||
? uint64_t(msi_address) >> 32
|
||||
: 0UL;
|
||||
|
||||
_write_config_16(msi_cap + 0x8, upper_address);
|
||||
_write_config_16(msi_cap + 0xc, msi_value);
|
||||
} else
|
||||
_write_config_16(msi_cap + 0x8, msi_value);
|
||||
|
||||
/* enable MSI */
|
||||
_device_config.write(_config_access, msi_cap + 2,
|
||||
msi ^ MSI_ENABLED,
|
||||
Platform::Device::ACCESS_8BIT);
|
||||
|
||||
msi = _read_config_16(msi_cap + 2);
|
||||
|
||||
return msi & MSI_ENABLED;
|
||||
} catch (...) { }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Platform::Device_component::_setup_msix(uint16_t const msix_cap)
|
||||
{
|
||||
try {
|
||||
struct Table_pba : Register<32>
|
||||
{
|
||||
struct Bir : Bitfield<0, 3> { };
|
||||
struct Offset : Bitfield<3, 29> { };
|
||||
};
|
||||
|
||||
addr_t const msi_address = _irq_session->msi_address();
|
||||
uint32_t const msi_value = _irq_session->msi_data();
|
||||
|
||||
uint16_t ctrl = _read_config_16(msix_cap + 2);
|
||||
|
||||
uint32_t const slots = Msix_ctrl::Slots::get(ctrl) + 1;
|
||||
|
||||
uint32_t const table = _read_config_32(msix_cap + 4);
|
||||
uint8_t const table_bir = Table_pba::Bir::masked(table);
|
||||
uint32_t const table_off = Table_pba::Offset::masked(table);
|
||||
|
||||
enum { SIZEOF_MSI_TABLE_ENTRY = 16, SIZE_IOMEM = 0x1000 };
|
||||
|
||||
Pci::Resource res = _device_config.resource(table_bir);
|
||||
if (!slots || !res.valid() || res.size() < SIZE_IOMEM ||
|
||||
table_off > res.size() - SIZE_IOMEM)
|
||||
return false;
|
||||
|
||||
if (slots * SIZEOF_MSI_TABLE_ENTRY > SIZE_IOMEM)
|
||||
return false;
|
||||
|
||||
uint64_t const msix_table_phys = res.base() + table_off;
|
||||
|
||||
apply_msix_table(res, msix_table_phys, SIZE_IOMEM,
|
||||
[&](addr_t const msix_table)
|
||||
{
|
||||
struct Msi_entry : public Mmio {
|
||||
Msi_entry(addr_t const base) : Mmio(base) { }
|
||||
|
||||
struct Address_low : Register<0x0, 32> { };
|
||||
struct Address_high : Register<0x4, 32> { };
|
||||
struct Value : Register<0x8, 32> { };
|
||||
struct Vector : Register<0xc, 32> {
|
||||
struct Mask : Bitfield <0, 1> { };
|
||||
};
|
||||
} msi_entry_0 (msix_table);
|
||||
|
||||
/* setup first msi-x table entry */
|
||||
msi_entry_0.write<Msi_entry::Address_low>(msi_address & ~(0x3UL));
|
||||
msi_entry_0.write<Msi_entry::Address_high>(sizeof(msi_address) == 4 ? 0 : msi_address >> 32);
|
||||
msi_entry_0.write<Msi_entry::Value>(msi_value);
|
||||
msi_entry_0.write<Msi_entry::Vector::Mask>(0);
|
||||
|
||||
/* disable all msi-x table entries beside the first one */
|
||||
for (unsigned i = 1; i < slots; i++) {
|
||||
struct Msi_entry unused (msix_table + SIZEOF_MSI_TABLE_ENTRY * i);
|
||||
unused.write<Msi_entry::Vector::Mask>(1);
|
||||
}
|
||||
|
||||
/* enable MSI-X */
|
||||
Msix_ctrl::Fmask::set(ctrl, 0);
|
||||
Msix_ctrl::Enable::set(ctrl, 1);
|
||||
_write_config_16(msix_cap + 2, ctrl);
|
||||
});
|
||||
|
||||
/* check back that MSI-X got enabled */
|
||||
ctrl = _read_config_16(msix_cap + 2);
|
||||
|
||||
return Msix_ctrl::Enable::get(ctrl);
|
||||
} catch (Out_of_caps) {
|
||||
warning("Out_of_caps during MSI-X enablement"); }
|
||||
catch (Out_of_ram) {
|
||||
warning("Out_of_ram during MSI-X enablement"); }
|
||||
catch (...) { warning("MSI-X enablement failed"); }
|
||||
|
||||
return false;
|
||||
}
|
@ -1,608 +0,0 @@
|
||||
/*
|
||||
* \brief platform device component
|
||||
* \author Norman Feske
|
||||
* \author Christian Helmuth
|
||||
* \date 2008-01-28
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2008-2017 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 _PCI_DEVICE_COMPONENT_H_
|
||||
#define _PCI_DEVICE_COMPONENT_H_
|
||||
|
||||
/* base */
|
||||
#include <base/rpc_server.h>
|
||||
#include <io_port_session/connection.h>
|
||||
#include <io_mem_session/connection.h>
|
||||
#include <util/list.h>
|
||||
#include <util/mmio.h>
|
||||
#include <util/construct_at.h>
|
||||
|
||||
/* os */
|
||||
#include <legacy/x86/platform_device/platform_device.h>
|
||||
|
||||
/* local */
|
||||
#include "pci_device_config.h"
|
||||
#include "irq.h"
|
||||
|
||||
namespace Platform {
|
||||
class Device_component;
|
||||
class Session_component;
|
||||
|
||||
typedef String<10> Device_name_string;
|
||||
|
||||
typedef Registry<Registered<Device_config::Device_bars> > Device_bars_pool;
|
||||
}
|
||||
|
||||
|
||||
class Platform::Device_component : public Rpc_object<Platform::Device>,
|
||||
private List<Device_component>::Element
|
||||
{
|
||||
private:
|
||||
|
||||
friend class List<Device_component>;
|
||||
|
||||
/*
|
||||
* Noncopyable
|
||||
*/
|
||||
Device_component(Device_component const &);
|
||||
Device_component &operator = (Device_component const &);
|
||||
|
||||
Env &_env;
|
||||
Pci::Config::Delayer &_delayer;
|
||||
Device_bars_pool &_devices_bars;
|
||||
Device_config _device_config { };
|
||||
addr_t _config_space;
|
||||
Config_access _config_access;
|
||||
Platform::Session_component &_session;
|
||||
Irq_session_component *_irq_session = nullptr;
|
||||
unsigned short _irq_line;
|
||||
bool _device_used { false };
|
||||
bool _device_assigned { false };
|
||||
Allocator &_global_heap;
|
||||
|
||||
class Io_mem : public Io_mem_connection,
|
||||
private List<Io_mem>::Element
|
||||
{
|
||||
private:
|
||||
|
||||
friend class List<Io_mem>;
|
||||
friend class Platform::Device_component;
|
||||
|
||||
public:
|
||||
|
||||
addr_t const base;
|
||||
size_t const size;
|
||||
|
||||
Io_mem(Env &env, addr_t base, size_t size, bool wc)
|
||||
:
|
||||
Io_mem_connection(env, base, size, wc),
|
||||
base(base), size(size)
|
||||
{ }
|
||||
};
|
||||
|
||||
enum {
|
||||
IO_BLOCK_SIZE = sizeof(Io_port_connection) *
|
||||
Device::NUM_RESOURCES + 32 + 8 * sizeof(void *),
|
||||
IO_MEM_SIZE = sizeof(Io_mem) *
|
||||
Device::NUM_RESOURCES + 32 + 8 * sizeof(void *),
|
||||
PCI_IRQ_LINE = 0x3c,
|
||||
PCI_IRQ_PIN = 0x3d,
|
||||
|
||||
CAP_MSI_64 = 0x80,
|
||||
MSI_ENABLED = 0x1
|
||||
};
|
||||
|
||||
struct Msix_ctrl : Register<16>
|
||||
{
|
||||
struct Slots : Bitfield< 0, 10> { };
|
||||
struct Fmask : Bitfield<14, 1> { };
|
||||
struct Enable : Bitfield<15, 1> { };
|
||||
};
|
||||
|
||||
struct Pci_express : Pci::Config
|
||||
{
|
||||
Pci_express(Device_component &dev, uint16_t cap)
|
||||
: Pci::Config(dev._config_access, dev._device_config.bdf(), cap) { }
|
||||
|
||||
struct Capabilities : Register<0x04, 32> {
|
||||
struct Reset : Bitfield< 28, 1> { }; };
|
||||
struct Control: Register<0x08, 16> {
|
||||
struct Reset : Bitfield< 15, 1> { }; };
|
||||
struct Status: Register<0x0a, 16> {
|
||||
struct Pending : Bitfield< 5, 1> { }; };
|
||||
struct Capabilities2 : Register<0x24, 32> {
|
||||
struct Readiness : Bitfield<31, 1> { }; };
|
||||
struct Status2 : Register<0x32, 16> {
|
||||
struct Readiness_status : Bitfield<15, 1> { }; };
|
||||
};
|
||||
|
||||
struct Pci_power: Pci::Config
|
||||
{
|
||||
Pci_power(Device_component &dev, uint16_t cap)
|
||||
: Pci::Config(dev._config_access, dev._device_config.bdf(), cap) { }
|
||||
|
||||
struct Capabilities : Register<0x02, 16>
|
||||
{
|
||||
struct Specific_init : Bitfield< 5, 1> { };
|
||||
};
|
||||
struct Control : Register<0x04, 16>
|
||||
{
|
||||
struct D0_3 : Bitfield< 0, 2> { };
|
||||
struct No_soft_reset : Bitfield< 3, 1> { };
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
Tslab<Io_port_connection, IO_BLOCK_SIZE> _slab_ioport;
|
||||
char _slab_ioport_block_data[IO_BLOCK_SIZE];
|
||||
|
||||
Tslab<Io_mem, IO_MEM_SIZE> _slab_iomem;
|
||||
char _slab_iomem_block_data[IO_MEM_SIZE];
|
||||
|
||||
char _mem_irq_component[sizeof(Irq_session_component)];
|
||||
|
||||
Io_port_connection *_io_port_conn[Device::NUM_RESOURCES];
|
||||
|
||||
/* list of requested resource chunks per BAR */
|
||||
List<Io_mem> _io_mem[Device::NUM_RESOURCES];
|
||||
|
||||
struct Status : Register<8>
|
||||
{
|
||||
struct Capabilities : Bitfield<4,1> { };
|
||||
|
||||
inline static access_t read(uint8_t t) { return t; }
|
||||
};
|
||||
|
||||
/**
|
||||
* Convenience functions to increase readability of code
|
||||
*/
|
||||
uint16_t _read_config_16(uint16_t const cap)
|
||||
{
|
||||
return _device_config.read(_config_access, cap,
|
||||
Platform::Device::ACCESS_16BIT);
|
||||
}
|
||||
|
||||
void _write_config_16(uint16_t const cap, uint16_t const value)
|
||||
{
|
||||
_device_config.write(_config_access, cap, value,
|
||||
Platform::Device::ACCESS_16BIT);
|
||||
}
|
||||
|
||||
uint32_t _read_config_32(uint16_t const cap)
|
||||
{
|
||||
return _device_config.read(_config_access, cap,
|
||||
Platform::Device::ACCESS_32BIT);
|
||||
}
|
||||
|
||||
void _write_config_32(uint16_t const cap, uint32_t const value)
|
||||
{
|
||||
_device_config.write(_config_access, cap, value,
|
||||
Platform::Device::ACCESS_32BIT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read out msi capabilities of the device.
|
||||
*/
|
||||
uint16_t _msi_cap()
|
||||
{
|
||||
enum { CAP_MSI = 0x5 };
|
||||
return _lookup_cap(CAP_MSI);
|
||||
}
|
||||
|
||||
uint16_t _msix_cap()
|
||||
{
|
||||
enum { CAP_MSI_X = 0x11 };
|
||||
return _lookup_cap(CAP_MSI_X);
|
||||
}
|
||||
|
||||
uint16_t _power_cap()
|
||||
{
|
||||
enum { CAP_POWER = 0x1 };
|
||||
return _lookup_cap(CAP_POWER);
|
||||
}
|
||||
|
||||
/* PCI express cap (not PCI express extended cap!) */
|
||||
uint16_t _pcie_cap()
|
||||
{
|
||||
enum { CAP_PCIE = 0x10 };
|
||||
return _lookup_cap(CAP_PCIE);
|
||||
}
|
||||
|
||||
uint16_t _lookup_cap(uint16_t const target_cap)
|
||||
{
|
||||
enum { PCI_STATUS = 0x6, PCI_CAP_OFFSET = 0x34 };
|
||||
|
||||
Status::access_t status = Status::read(_read_config_16(PCI_STATUS));
|
||||
if (!Status::Capabilities::get(status))
|
||||
return 0;
|
||||
|
||||
uint8_t cap = _read_config_16(PCI_CAP_OFFSET);
|
||||
|
||||
for (uint16_t val = 0; cap; cap = val >> 8) {
|
||||
val = _read_config_16(cap);
|
||||
if ((val & 0xff) != target_cap)
|
||||
continue;
|
||||
|
||||
return cap;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Disable MSI/MSI-X if already enabled.
|
||||
*/
|
||||
unsigned _configure_irq(unsigned irq, uint16_t const msi_cap,
|
||||
uint16_t const msix_cap)
|
||||
{
|
||||
uint8_t pin = _device_config.read(_config_access, PCI_IRQ_PIN,
|
||||
Platform::Device::ACCESS_8BIT);
|
||||
if (!pin)
|
||||
return Irq_session_component::INVALID_IRQ;
|
||||
|
||||
/* lookup rewrite information as provided by acpi table */
|
||||
uint16_t irq_r = Irq_routing::rewrite(_device_config.bdf(), pin);
|
||||
if (irq_r) {
|
||||
log(_device_config, " adjust IRQ as reported by ACPI: ",
|
||||
irq, " -> ", irq_r);
|
||||
|
||||
_irq_line = irq = irq_r;
|
||||
}
|
||||
|
||||
if (msi_cap) {
|
||||
uint16_t msi = _read_config_16(msi_cap + 2);
|
||||
|
||||
if (msi & MSI_ENABLED)
|
||||
/* disable MSI */
|
||||
_device_config.write(_config_access, msi_cap + 2,
|
||||
msi ^ MSI_ENABLED,
|
||||
Platform::Device::ACCESS_8BIT);
|
||||
}
|
||||
|
||||
if (msix_cap) {
|
||||
uint16_t msix = _read_config_16(msix_cap + 2);
|
||||
|
||||
if (Msix_ctrl::Enable::get(msix)) {
|
||||
Msix_ctrl::Enable::set(msix, 0);
|
||||
|
||||
_write_config_16(msix_cap + 2, msix);
|
||||
}
|
||||
}
|
||||
|
||||
return irq;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Disable bus master dma if already enabled.
|
||||
*/
|
||||
void _disable_bus_master_dma()
|
||||
{
|
||||
/*
|
||||
* Disabling a bridge may make the devices behind non-functional,
|
||||
* as we have no driver which will switch it on again
|
||||
*/
|
||||
if (_device_config.pci_bridge() ||
|
||||
_device_config.bdf() == Pci::Bdf::from_value(Platform::Bridge::root_bridge_bdf))
|
||||
return;
|
||||
|
||||
_device_config.disable_bus_master_dma(_config_access);
|
||||
}
|
||||
|
||||
bool _setup_msi(uint16_t);
|
||||
bool _setup_msix(uint16_t);
|
||||
|
||||
template <typename FUNC>
|
||||
void apply_msix_table(Pci::Resource const &lookup,
|
||||
addr_t const msix_table_phys,
|
||||
size_t const msix_table_size,
|
||||
FUNC const &fn)
|
||||
{
|
||||
uint8_t max = sizeof(_io_mem) / sizeof(_io_mem[0]);
|
||||
for (unsigned i = 0; i < max; ++i) {
|
||||
Pci::Resource res = _device_config.resource(i);
|
||||
|
||||
if (!res.valid() || !res.mem())
|
||||
continue;
|
||||
|
||||
if (res.base() != lookup.base() || res.size() != lookup.size())
|
||||
continue;
|
||||
|
||||
for (Io_mem * io_mem = _io_mem[i].first(); io_mem; io_mem = io_mem->next()) {
|
||||
|
||||
if (!(io_mem->base <= msix_table_phys &&
|
||||
msix_table_phys + msix_table_size <= io_mem->base + io_mem->size))
|
||||
continue;
|
||||
|
||||
size_t const offset = msix_table_phys - io_mem->base;
|
||||
|
||||
Attached_dataspace mem_io(_env.rm(), io_mem->dataspace());
|
||||
|
||||
fn(reinterpret_cast<addr_t>(mem_io.local_addr<void>()) + offset);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* requested io_mem not allocated by Pci::Resource - try direct */
|
||||
Io_mem io_mem(_env, msix_table_phys, msix_table_size, false);
|
||||
Attached_dataspace mem_io(_env.rm(), io_mem.dataspace());
|
||||
addr_t const offset = msix_table_phys & 0xfffull;
|
||||
addr_t const msix_table = reinterpret_cast<addr_t>(mem_io.local_addr<void>()) + offset;
|
||||
|
||||
fn(msix_table);
|
||||
}
|
||||
|
||||
void _device_reset()
|
||||
{
|
||||
uint16_t const cap = _pcie_cap();
|
||||
if (!cap)
|
||||
return;
|
||||
|
||||
Pci_express pci_cap(*this, cap);
|
||||
|
||||
if (!pci_cap.read<Pci_express::Capabilities::Reset>())
|
||||
return;
|
||||
|
||||
log(_device_config, " reset function");
|
||||
|
||||
pci_cap.write<Pci_express::Control::Reset>(1);
|
||||
|
||||
try {
|
||||
/* optional use FLR Time if available instead of heuristic */
|
||||
pci_cap.wait_for(Pci::Config::Attempts(100),
|
||||
Pci::Config::Microseconds(10000), _delayer,
|
||||
Pci_express::Status::Pending::Equal(0));
|
||||
} catch (Pci::Config::Polling_timeout) {
|
||||
warning(_device_config, " reset timeout raised");
|
||||
}
|
||||
}
|
||||
|
||||
void _power_off()
|
||||
{
|
||||
/* don't touch unused device */
|
||||
if (!_device_used)
|
||||
return;
|
||||
|
||||
uint16_t const cap = _power_cap();
|
||||
if (!cap) {
|
||||
_disable_bus_master_dma();
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* PCI Power Management - 8.2.2 D3 State
|
||||
*
|
||||
* "If the device driver is not capable of fully reinitializing"
|
||||
* "a function, the operating system should not put the function"
|
||||
* "into D3"
|
||||
*
|
||||
* Actually, at this point we don't know about the capabilities of
|
||||
* the actual driver.
|
||||
*/
|
||||
log(_device_config, " power off");
|
||||
|
||||
/*
|
||||
* "When placing a function into D3, the operating system software"
|
||||
* "is required to disable I/O and memory space as well as bus"
|
||||
* "mastering via the PCI Command register.
|
||||
*/
|
||||
Device_config::Pci_header header (_config_access, _device_config.bdf());
|
||||
|
||||
auto command = header.read<Device_config::Pci_header::Command>();
|
||||
Device_config::Pci_header::Command::Dma::set(command, 0);
|
||||
Device_config::Pci_header::Command::Memory::set(command, 0);
|
||||
Device_config::Pci_header::Command::Ioport::set(command, 0);
|
||||
|
||||
header.write<Device_config::Pci_header::Command>(command);
|
||||
|
||||
/* power off */
|
||||
Pci_power pci_cap(*this, cap);
|
||||
pci_cap.write<Pci_power::Control::D0_3>(3);
|
||||
}
|
||||
|
||||
void _power_on()
|
||||
{
|
||||
uint16_t const cap = _power_cap();
|
||||
if (!cap)
|
||||
return;
|
||||
|
||||
Pci_power pci_cap(*this, cap);
|
||||
|
||||
if (pci_cap.read<Pci_power::Control::D0_3>() == 0)
|
||||
return;
|
||||
|
||||
/* since it was off before, it got used by powering it on */
|
||||
_device_used = true;
|
||||
|
||||
log(_device_config, " power on",
|
||||
pci_cap.read<Pci_power::Control::No_soft_reset>() ? ", no_soft_reset" : "",
|
||||
pci_cap.read<Pci_power::Capabilities::Specific_init>() ? ", specific_init_required" : "");
|
||||
|
||||
/* power on */
|
||||
pci_cap.write<Pci_power::Control::D0_3>(0);
|
||||
|
||||
/*
|
||||
* PCI Express 4.3 - 5.3.1.4. D3 State
|
||||
*
|
||||
* "Unless Readiness Notifications mechanisms are used ..."
|
||||
* "a minimum recovery time following a D3 hot → D0 transition of"
|
||||
* "at least 10 ms ..."
|
||||
*/
|
||||
_delayer.usleep(10'000);
|
||||
|
||||
/*
|
||||
* PCI Power Management - 3.2.4 - PMCSR Power Management Control/Status
|
||||
*
|
||||
* "no additional operating system intervention is required ..."
|
||||
* "beyond writing the PowerState"
|
||||
*/
|
||||
if (pci_cap.read<Pci_power::Control::No_soft_reset>())
|
||||
return;
|
||||
|
||||
_device_reset();
|
||||
|
||||
_devices_bars.for_each([&](auto const &bars) {
|
||||
if (!(bars.bdf == _device_config.bdf()))
|
||||
return;
|
||||
|
||||
_device_config.restore_bars(_config_access, bars);
|
||||
});
|
||||
|
||||
/* re-read the resources which set to valid ones after power on */
|
||||
_device_config = Device_config(_device_config.bdf(),
|
||||
&_config_access);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor for PCI devices
|
||||
*/
|
||||
Device_component(Env &env,
|
||||
Device_config device_config, addr_t addr,
|
||||
Config_access &config_access,
|
||||
Platform::Session_component &session,
|
||||
Allocator &md_alloc,
|
||||
Allocator &global_heap,
|
||||
Pci::Config::Delayer &delayer,
|
||||
Device_bars_pool &devices_bars)
|
||||
:
|
||||
_env(env),
|
||||
_delayer(delayer),
|
||||
_devices_bars(devices_bars),
|
||||
_device_config(device_config), _config_space(addr),
|
||||
_config_access(config_access),
|
||||
_session(session),
|
||||
_irq_line(_device_config.read(_config_access, PCI_IRQ_LINE,
|
||||
Platform::Device::ACCESS_8BIT)),
|
||||
_global_heap(global_heap),
|
||||
_slab_ioport(&md_alloc, &_slab_ioport_block_data),
|
||||
_slab_iomem(&md_alloc, &_slab_iomem_block_data)
|
||||
{
|
||||
for (unsigned i = 0; i < Device::NUM_RESOURCES; i++) {
|
||||
_io_port_conn[i] = nullptr;
|
||||
}
|
||||
|
||||
_power_on();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for non PCI devices
|
||||
*/
|
||||
Device_component(Env &env,
|
||||
Attached_io_mem_dataspace &pciconf,
|
||||
Platform::Session_component &session, unsigned irq,
|
||||
Allocator &global_heap,
|
||||
Pci::Config::Delayer &delayer,
|
||||
Device_bars_pool &devices_bars)
|
||||
:
|
||||
_env(env),
|
||||
_delayer(delayer),
|
||||
_devices_bars(devices_bars),
|
||||
_config_space(~0UL),
|
||||
_config_access(pciconf),
|
||||
_session(session),
|
||||
_irq_line(irq),
|
||||
_global_heap(global_heap),
|
||||
_slab_ioport(nullptr, &_slab_ioport_block_data),
|
||||
_slab_iomem(nullptr, &_slab_iomem_block_data)
|
||||
{
|
||||
for (unsigned i = 0; i < Device::NUM_RESOURCES; i++)
|
||||
_io_port_conn[i] = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* De-constructor
|
||||
*/
|
||||
~Device_component()
|
||||
{
|
||||
if (_irq_session) {
|
||||
_env.ep().rpc_ep().dissolve(_irq_session);
|
||||
_irq_session->~Irq_session_component();
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < Device::NUM_RESOURCES; i++) {
|
||||
if (_io_port_conn[i])
|
||||
destroy(_slab_ioport, _io_port_conn[i]);
|
||||
|
||||
while (Io_mem * io_mem = _io_mem[i].first()) {
|
||||
_io_mem[i].remove(io_mem);
|
||||
destroy(_slab_iomem, io_mem);
|
||||
}
|
||||
}
|
||||
|
||||
if (!pci_device())
|
||||
return;
|
||||
|
||||
_power_off();
|
||||
}
|
||||
|
||||
/* distinct non-PCI and PCI devices */
|
||||
bool pci_device() const { return _device_config.valid(); }
|
||||
|
||||
/****************************************
|
||||
** Methods used solely by pci session **
|
||||
****************************************/
|
||||
|
||||
Device_config device_config() const { return _device_config; }
|
||||
addr_t config_space() const { return _config_space; }
|
||||
|
||||
virtual Device_name_string name() const { return "PCI"; }
|
||||
|
||||
template <typename FUNC>
|
||||
void for_each_device(FUNC const &fn) const
|
||||
{
|
||||
fn(*this);
|
||||
|
||||
for (auto *dev = this; dev; dev = dev->next()) {
|
||||
fn(*dev); }
|
||||
}
|
||||
|
||||
/**************************
|
||||
** PCI-device interface **
|
||||
**************************/
|
||||
|
||||
void bus_address(unsigned char *bus, unsigned char *dev,
|
||||
unsigned char *fn) override
|
||||
{
|
||||
*bus = _device_config.bdf().bus;
|
||||
*dev = _device_config.bdf().device;
|
||||
*fn = _device_config.bdf().function;
|
||||
}
|
||||
|
||||
unsigned short vendor_id() override { return _device_config.vendor_id(); }
|
||||
|
||||
unsigned short device_id() override { return _device_config.device_id(); }
|
||||
|
||||
unsigned class_code() override { return _device_config.class_code(); }
|
||||
|
||||
Resource resource(int resource_id) override
|
||||
{
|
||||
if (pci_device())
|
||||
return _device_config.resource(resource_id).api_resource();
|
||||
|
||||
/* return invalid resource if device is invalid */
|
||||
return Resource(0, 0);
|
||||
}
|
||||
|
||||
unsigned config_read(unsigned char address, Access_size size) override;
|
||||
|
||||
void config_write(unsigned char address, unsigned value,
|
||||
Access_size size) override;
|
||||
|
||||
Irq_session_capability irq(uint8_t) override;
|
||||
|
||||
Io_port_session_capability io_port(uint8_t) override;
|
||||
|
||||
Io_mem_session_capability io_mem(uint8_t, Cache, addr_t, size_t) override;
|
||||
};
|
||||
|
||||
#endif /* _PCI_DEVICE_COMPONENT_H_ */
|
@ -1,458 +0,0 @@
|
||||
/*
|
||||
* \brief PCI device configuration
|
||||
* \author Norman Feske
|
||||
* \author Christian Helmuth
|
||||
* \date 2008-01-29
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2008-2017 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 _X86__PCI_DEVICE_CONFIG_H_
|
||||
#define _X86__PCI_DEVICE_CONFIG_H_
|
||||
|
||||
#include <base/output.h>
|
||||
#include <legacy/x86/platform_device/platform_device.h>
|
||||
#include <util/register.h>
|
||||
#include "pci_config_access.h"
|
||||
|
||||
|
||||
namespace Platform { namespace Pci { struct Resource; } }
|
||||
|
||||
|
||||
class Platform::Pci::Resource
|
||||
{
|
||||
public:
|
||||
|
||||
struct Bar : Register<32>
|
||||
{
|
||||
struct Space : Bitfield<0,1> { enum { MEM = 0, PORT = 1 }; };
|
||||
|
||||
struct Mem_type : Bitfield<1,2> { enum { MEM32 = 0, MEM64 = 2 }; };
|
||||
struct Mem_prefetch : Bitfield<3,1> { };
|
||||
struct Mem_address_mask : Bitfield<4,28> { };
|
||||
struct Port_address_mask : Bitfield<2,14> { };
|
||||
|
||||
static bool mem(access_t r) { return Space::get(r) == Space::MEM; }
|
||||
static bool mem64(access_t r) { return mem(r)
|
||||
&& Mem_type::get(r) == Mem_type::MEM64; }
|
||||
static uint64_t mem_address(access_t r0, uint64_t r1) { return (r1 << 32) | Mem_address_mask::masked(r0); }
|
||||
static uint64_t mem_size(access_t r0, uint64_t r1) { return ~mem_address(r0, r1) + 1; }
|
||||
|
||||
static uint16_t port_address(access_t r) { return Port_address_mask::masked(r); }
|
||||
static uint16_t port_size(access_t r) { return ~port_address(r) + 1; }
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
uint32_t _bar[2] { 0, 0 }; /* contains two consecutive BARs for MEM64 */
|
||||
uint64_t _size { 0 };
|
||||
|
||||
public:
|
||||
|
||||
/* invalid resource */
|
||||
Resource() { }
|
||||
|
||||
/* PORT or MEM32 resource */
|
||||
Resource(uint32_t bar, uint32_t size)
|
||||
:
|
||||
_bar{bar, 0}, _size(mem() ? Bar::mem_size(size, ~0) : Bar::port_size(size))
|
||||
{ }
|
||||
|
||||
/* MEM64 resource */
|
||||
Resource(uint32_t bar0, uint32_t size0, uint32_t bar1, uint32_t size1)
|
||||
: _bar{bar0, bar1}, _size(Bar::mem_size(size0, size1))
|
||||
{ }
|
||||
|
||||
bool valid() const { return !!_bar[0]; } /* no base address -> invalid */
|
||||
bool mem() const { return Bar::mem(_bar[0]); }
|
||||
bool mem64() const { return mem() && Bar::mem64(_bar[0]); }
|
||||
uint64_t base() const { return mem() ? Bar::mem_address(_bar[0], _bar[1])
|
||||
: Bar::port_address(_bar[0]); }
|
||||
uint64_t size() const { return _size; }
|
||||
|
||||
Platform::Device::Resource api_resource()
|
||||
{
|
||||
/*
|
||||
* The API type limits to 32-bit currently (defined in
|
||||
* spec/x86/platform_device/platform_device.h)
|
||||
*/
|
||||
return Device::Resource((unsigned)_bar[0], (unsigned)_size);
|
||||
}
|
||||
|
||||
void print(Output &out) const
|
||||
{
|
||||
Genode::print(out, Hex_range(base(), size()));
|
||||
Genode::print(out, " (");
|
||||
Genode::print(out, valid() ? mem() ? Bar::mem64(_bar[0]) ? "MEM64" : "MEM" : "IO" : "invalid");
|
||||
Genode::print(out, ")");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
namespace Platform {
|
||||
|
||||
class Device_config
|
||||
{
|
||||
private:
|
||||
|
||||
Pci::Bdf _bdf;
|
||||
|
||||
/*
|
||||
* Information provided by the PCI config space
|
||||
*/
|
||||
unsigned _vendor_id = 0, _device_id = 0;
|
||||
unsigned _class_code = 0;
|
||||
unsigned _header_type = 0;
|
||||
|
||||
/*
|
||||
* Header type definitions
|
||||
*/
|
||||
enum {
|
||||
HEADER_FUNCTION = 0,
|
||||
HEADER_PCI_TO_PCI = 1,
|
||||
HEADER_CARD_BUS = 2
|
||||
};
|
||||
|
||||
Platform::Pci::Resource _resource[Device::NUM_RESOURCES];
|
||||
|
||||
bool _resource_id_is_valid(int resource_id) const
|
||||
{
|
||||
/*
|
||||
* The maximum number of PCI resources depends on the
|
||||
* header type of the device.
|
||||
*/
|
||||
int max_num = _header_type == HEADER_FUNCTION ? Device::NUM_RESOURCES
|
||||
: _header_type == HEADER_PCI_TO_PCI ? 2
|
||||
: 0;
|
||||
|
||||
return resource_id >= 0 && resource_id < max_num;
|
||||
}
|
||||
|
||||
enum { INVALID_VENDOR = 0xffffU };
|
||||
|
||||
public:
|
||||
|
||||
enum { MAX_BUSES = 256, MAX_DEVICES = 32, MAX_FUNCTIONS = 8 };
|
||||
|
||||
enum {
|
||||
PCI_CMD_REG = 0x4,
|
||||
PCI_CMD_MASK = 0x7, /* IOPORT (1), MEM(2), DMA(4) */
|
||||
PCI_CMD_DMA = 0x4,
|
||||
};
|
||||
|
||||
struct Pci_header : Pci::Config
|
||||
{
|
||||
Pci_header(Config_access &access, Pci::Bdf const bdf)
|
||||
: Pci::Config(access, bdf, 0 /* from start */) { }
|
||||
|
||||
struct Command : Register<0x04, 16>
|
||||
{
|
||||
struct Ioport : Bitfield< 0, 1> { };
|
||||
struct Memory : Bitfield< 1, 1> { };
|
||||
struct Dma : Bitfield< 2, 1> { };
|
||||
};
|
||||
};
|
||||
|
||||
struct Device_bars
|
||||
{
|
||||
Pci::Bdf bdf;
|
||||
|
||||
uint32_t bar_addr[Device::NUM_RESOURCES] { };
|
||||
|
||||
bool all_invalid() const
|
||||
{
|
||||
for (unsigned i = 0; i < Device::NUM_RESOURCES; i++) {
|
||||
if (bar_addr[i] != 0 && bar_addr[i] != ~0U)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Device_bars(Pci::Bdf bdf) : bdf(bdf) { }
|
||||
virtual ~Device_bars() { };
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Device_config() : _bdf({ .bus = 0, .device = 0, .function = 0 }) {
|
||||
_vendor_id = INVALID_VENDOR; }
|
||||
|
||||
Device_config(Pci::Bdf bdf) : _bdf(bdf) { }
|
||||
|
||||
Device_config(Pci::Bdf bdf, Config_access *pci_config) : _bdf(bdf)
|
||||
{
|
||||
_vendor_id = pci_config->read(bdf, 0, Device::ACCESS_16BIT);
|
||||
|
||||
/* break here if device is invalid */
|
||||
if (_vendor_id == INVALID_VENDOR)
|
||||
return;
|
||||
|
||||
_device_id = pci_config->read(bdf, 2, Device::ACCESS_16BIT);
|
||||
_class_code = pci_config->read(bdf, 8, Device::ACCESS_32BIT) >> 8;
|
||||
_class_code &= 0xffffff;
|
||||
_header_type = pci_config->read(bdf, 0xe, Device::ACCESS_8BIT);
|
||||
_header_type &= 0x7f;
|
||||
|
||||
/*
|
||||
* We prevent scanning function 1-7 of non-multi-function
|
||||
* devices by checking bit 7 (mf bit) of function 0 of the
|
||||
* device. Note, the mf bit of function 1-7 is not significant
|
||||
* and may be set or unset.
|
||||
*/
|
||||
if (bdf.function != 0) {
|
||||
Pci::Bdf const dev { .bus = bdf.bus, .device = bdf.device, .function = 0 };
|
||||
if (!(pci_config->read(dev, 0xe, Device::ACCESS_8BIT) & 0x80)) {
|
||||
_vendor_id = INVALID_VENDOR;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We iterate over all BARs but check for 64-bit memory
|
||||
* resources, which are stored in two consecutive BARs. The
|
||||
* MEM64 information is stored in the first resource entry and
|
||||
* the second resource is marked invalid.
|
||||
*/
|
||||
int i = 0;
|
||||
while (_resource_id_is_valid(i)) {
|
||||
|
||||
using Pci::Resource;
|
||||
|
||||
/* index of base-address register in configuration space */
|
||||
unsigned const bar_idx = 0x10 + 4 * i;
|
||||
|
||||
/* First, save initial base-address register value. */
|
||||
unsigned const bar_value = pci_config->read(bdf, bar_idx, Device::ACCESS_32BIT);
|
||||
|
||||
/*
|
||||
* Second, determine resource size (and validity) by writing
|
||||
* a magic value (all bits set) to the base-address
|
||||
* register. In response, the device clears a number of
|
||||
* lowest-significant bits corresponding to the resource
|
||||
* size.
|
||||
*/
|
||||
pci_config->write(bdf, bar_idx, ~0, Device::ACCESS_32BIT);
|
||||
unsigned const bar_size = pci_config->read(bdf, bar_idx, Device::ACCESS_32BIT);
|
||||
|
||||
/* skip invalid resource BARs */
|
||||
if (bar_value == ~0U || bar_size == 0U) {
|
||||
_resource[i] = Resource();
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Finally, we write back the bar-address value as assigned
|
||||
* by the BIOS.
|
||||
*/
|
||||
pci_config->write(bdf, bar_idx, bar_value, Device::ACCESS_32BIT);
|
||||
|
||||
if (!Resource::Bar::mem64(bar_value)) {
|
||||
_resource[i] = Resource(bar_value, bar_size);
|
||||
++i;
|
||||
} else {
|
||||
/* also consume next BAR for MEM64 */
|
||||
unsigned const bar2_idx = bar_idx + 4;
|
||||
unsigned const bar2_value =
|
||||
pci_config->read(bdf, bar2_idx, Device::ACCESS_32BIT);
|
||||
pci_config->write(bdf, bar2_idx, ~0, Device::ACCESS_32BIT);
|
||||
unsigned const bar2_size =
|
||||
pci_config->read(bdf, bar2_idx, Device::ACCESS_32BIT);
|
||||
pci_config->write(bdf, bar2_idx, bar2_value, Device::ACCESS_32BIT);
|
||||
|
||||
/* combine into first resource and mark second as invalid */
|
||||
_resource[i] = Resource(bar_value, bar_size,
|
||||
bar2_value, bar2_size);
|
||||
++i;
|
||||
_resource[i] = Resource();
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Accessor function for device location
|
||||
*/
|
||||
Pci::Bdf bdf() const { return _bdf; }
|
||||
|
||||
void print(Output &out) const { Genode::print(out, bdf()); }
|
||||
|
||||
/**
|
||||
* Accessor functions for device information
|
||||
*/
|
||||
unsigned short device_id() { return _device_id; }
|
||||
unsigned short vendor_id() { return _vendor_id; }
|
||||
unsigned int class_code() { return _class_code; }
|
||||
|
||||
/**
|
||||
* Return true if device is a PCI bridge
|
||||
*/
|
||||
bool pci_bridge() const { return _header_type == HEADER_PCI_TO_PCI; }
|
||||
|
||||
/**
|
||||
* Return true if device is valid
|
||||
*/
|
||||
bool valid() const { return _vendor_id != INVALID_VENDOR; }
|
||||
|
||||
/**
|
||||
* Return resource description by resource ID
|
||||
*/
|
||||
Platform::Pci::Resource resource(int resource_id)
|
||||
{
|
||||
/* return invalid resource if sanity check fails */
|
||||
if (!_resource_id_is_valid(resource_id))
|
||||
return Platform::Pci::Resource();
|
||||
|
||||
return _resource[resource_id];
|
||||
}
|
||||
|
||||
void remap_resource(Config_access &config, int const id,
|
||||
uint64_t const base_address)
|
||||
{
|
||||
if (!_resource_id_is_valid(id))
|
||||
return;
|
||||
|
||||
using Pci::Resource;
|
||||
|
||||
Resource &res = _resource[id];
|
||||
|
||||
log(*this, " remap BAR", id, " ", res, " to ", Hex(base_address));
|
||||
|
||||
struct Resource_params { uint32_t bar; uint32_t size; };
|
||||
|
||||
auto update_bar = [&] (int const id, uint32_t const address) {
|
||||
unsigned const off = 0x10 + 4 * id;
|
||||
|
||||
config.write(_bdf, off, ~0U, Device::ACCESS_32BIT);
|
||||
|
||||
uint32_t const size = config.read(_bdf, off, Device::ACCESS_32BIT);
|
||||
|
||||
config.write(_bdf, off, address, Device::ACCESS_32BIT);
|
||||
|
||||
return Resource_params {
|
||||
.bar = config.read(_bdf, off, Device::ACCESS_32BIT),
|
||||
.size = size
|
||||
};
|
||||
};
|
||||
|
||||
Resource_params const bar0 = update_bar(id, base_address & 0xffffffff);
|
||||
|
||||
if (!res.mem64()) {
|
||||
res = Resource(bar0.bar, bar0.size);
|
||||
return;
|
||||
}
|
||||
|
||||
Resource_params const bar1 = update_bar(id + 1, (base_address >> 32) & 0xffffffff);
|
||||
|
||||
res = Resource(bar0.bar, bar0.size, bar1.bar, bar1.size);
|
||||
}
|
||||
|
||||
template <typename FN> void for_each_resource(FN const &fn) const
|
||||
{
|
||||
for (unsigned r = 0; r < Device::NUM_RESOURCES; r++) {
|
||||
if (!_resource_id_is_valid(r))
|
||||
break;
|
||||
|
||||
if (_resource[r].valid())
|
||||
fn(r, _resource[r]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read configuration space
|
||||
*/
|
||||
enum { DONT_TRACK_ACCESS = false };
|
||||
unsigned read(Config_access &pci_config, unsigned char address,
|
||||
Device::Access_size size, bool track = true)
|
||||
{
|
||||
return pci_config.read(_bdf, address, size, track);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write configuration space
|
||||
*/
|
||||
void write(Config_access &pci_config, unsigned char address,
|
||||
unsigned long value, Device::Access_size size,
|
||||
bool track = true)
|
||||
{
|
||||
pci_config.write(_bdf, address, value, size, track);
|
||||
}
|
||||
|
||||
bool reg_in_use(Config_access &pci_config, unsigned char address,
|
||||
Device::Access_size size) {
|
||||
return pci_config.reg_in_use(address, size); }
|
||||
|
||||
void disable_bus_master_dma(Config_access &pci_config)
|
||||
{
|
||||
Pci_header header (pci_config, _bdf);
|
||||
|
||||
if (header.read<Pci_header::Command::Dma>())
|
||||
header.write<Pci_header::Command::Dma>(0);
|
||||
}
|
||||
|
||||
Device_bars save_bars()
|
||||
{
|
||||
Device_bars bars (_bdf);
|
||||
|
||||
for (unsigned r = 0; r < Device::NUM_RESOURCES; r++) {
|
||||
if (!_resource_id_is_valid(r))
|
||||
break;
|
||||
|
||||
bars.bar_addr[r] = _resource[r].base();
|
||||
}
|
||||
|
||||
return bars;
|
||||
};
|
||||
|
||||
void restore_bars(Config_access &config, Device_bars const &bars)
|
||||
{
|
||||
for (unsigned r = 0; r < Device::NUM_RESOURCES; r++) {
|
||||
if (!_resource_id_is_valid(r))
|
||||
break;
|
||||
|
||||
/* index of base-address register in configuration space */
|
||||
unsigned const bar_idx = 0x10 + 4 * r;
|
||||
|
||||
/* PCI protocol to write address after requesting size */
|
||||
config.write(_bdf, bar_idx, ~0U, Device::ACCESS_32BIT);
|
||||
config.read (_bdf, bar_idx, Device::ACCESS_32BIT);
|
||||
config.write(_bdf, bar_idx, bars.bar_addr[r],
|
||||
Device::ACCESS_32BIT);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class Config_space : private List<Config_space>::Element
|
||||
{
|
||||
private:
|
||||
|
||||
friend class List<Config_space>;
|
||||
|
||||
uint32_t _bdf_start;
|
||||
uint32_t _func_count;
|
||||
addr_t _base;
|
||||
|
||||
public:
|
||||
|
||||
using List<Config_space>::Element::next;
|
||||
|
||||
Config_space(uint32_t bdf_start, uint32_t func_count, addr_t base)
|
||||
:
|
||||
_bdf_start(bdf_start), _func_count(func_count), _base(base) {}
|
||||
|
||||
addr_t lookup_config_space(Pci::Bdf const bdf)
|
||||
{
|
||||
if ((_bdf_start <= bdf.value()) && (bdf.value() <= _bdf_start + _func_count - 1))
|
||||
return _base + (unsigned(bdf.value()) << 12);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* _X86__PCI_DEVICE_CONFIG_H_ */
|
File diff suppressed because it is too large
Load Diff
@ -1,161 +0,0 @@
|
||||
/*
|
||||
* \brief platform session component
|
||||
* \author Norman Feske
|
||||
* \date 2008-01-28
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2008-2017 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 "pci_session_component.h"
|
||||
#include "pci_bridge.h"
|
||||
|
||||
|
||||
/* set during ACPI ROM parsing to valid value */
|
||||
unsigned Platform::Bridge::root_bridge_bdf = INVALID_ROOT_BRIDGE;
|
||||
|
||||
|
||||
static Genode::List<Platform::Bridge> *bridges()
|
||||
{
|
||||
static Genode::List<Platform::Bridge> list;
|
||||
return &list;
|
||||
}
|
||||
|
||||
|
||||
unsigned short Platform::bridge_bdf(unsigned char bus)
|
||||
{
|
||||
for (Platform::Bridge *bridge = bridges()->first(); bridge;
|
||||
bridge = bridge->next())
|
||||
{
|
||||
if (bridge->part_of(bus))
|
||||
return bridge->bdf();
|
||||
}
|
||||
/* XXX Ideally, this case should never happen */
|
||||
return Platform::Bridge::root_bridge_bdf;
|
||||
}
|
||||
|
||||
void Platform::Pci_buses::_scan_bus(Config_access &config_access,
|
||||
Allocator &heap,
|
||||
Device_bars_pool &devices_bars,
|
||||
unsigned char bus,
|
||||
Xml_node const &config_node)
|
||||
{
|
||||
for (unsigned dev = 0; dev < Device_config::MAX_DEVICES; ++dev) {
|
||||
for (unsigned fun = 0; fun < Device_config::MAX_FUNCTIONS; ++fun) {
|
||||
|
||||
Pci::Bdf const bdf { .bus = bus, .device = dev, .function = fun };
|
||||
|
||||
/* read config space */
|
||||
Device_config config(bdf, &config_access);
|
||||
|
||||
if (!config.valid())
|
||||
continue;
|
||||
|
||||
/* apply fixups to BAR memory resources */
|
||||
config.for_each_resource([&] (int const id, Platform::Pci::Resource const res)
|
||||
{
|
||||
uint64_t remap_address = 0;
|
||||
config_node.for_each_sub_node("pci-fixup", [&] (Xml_node node) {
|
||||
if (!node.has_attribute("bus")
|
||||
|| !node.has_attribute("device")
|
||||
|| !node.has_attribute("function")
|
||||
|| !(bdf == Pci::Bdf::from_xml(node)))
|
||||
return;
|
||||
|
||||
node.for_each_sub_node("bar", [&] (Xml_node node) {
|
||||
if (node.attribute_value("id", (long)-1) == id)
|
||||
remap_address = node.attribute_value("address", (uint64_t)0);
|
||||
});
|
||||
});
|
||||
|
||||
if (remap_address) {
|
||||
config.remap_resource(config_access, id, 0x4017002000);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!res.base() && res.mem())
|
||||
warning(bdf, " BAR", id, " ", res,
|
||||
" has invalid base address - consider <pci-fixup>");
|
||||
});
|
||||
|
||||
/* remember Device BARs required after power off and/or reset */
|
||||
Device_config::Device_bars bars = config.save_bars();
|
||||
if (!bars.all_invalid())
|
||||
new (heap) Registered<Device_config::Device_bars>(devices_bars, bars);
|
||||
|
||||
/*
|
||||
* Switch off PCI bus master DMA for some classes of devices,
|
||||
* which caused trouble.
|
||||
* Some devices are enabled by BIOS/UEFI or/and bootloaders and
|
||||
* aren't switch off when handing over to the kernel and Genode.
|
||||
* By disabling bus master DMA they should stop to issue DMA
|
||||
* operations and IRQs. IRQs are problematic, if it is a shared
|
||||
* IRQ - e.g. Ethernet and graphic card share a GSI IRQ. If the
|
||||
* graphic card driver is started without a Ethernet driver,
|
||||
* the graphic card may ack all IRQs. We may end up in a endless
|
||||
* IRQ/ACK loop, since no Ethernet driver acknowledge/disable IRQ
|
||||
* generation on the Ethernet device.
|
||||
*
|
||||
* Switching off PCI bus master DMA in general is a bad idea,
|
||||
* since some device classes require a explicit handover protocol
|
||||
* between BIOS/UEFI and device, e.g. USB. Violating such protocols
|
||||
* lead to hard hangs on some machines.
|
||||
*/
|
||||
if (config.class_code() >> 8) {
|
||||
uint16_t classcode = config.class_code() >> 16;
|
||||
uint16_t subclass = (config.class_code() >> 8) & 0xff;
|
||||
|
||||
if ((classcode == 0x2 && subclass == 0x00) /* ETHERNET */) {
|
||||
config.disable_bus_master_dma(config_access);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* There is at least one device on the current bus, so
|
||||
* we mark it as valid.
|
||||
*/
|
||||
if (!_valid.get(bus, 1))
|
||||
_valid.set(bus, 1);
|
||||
|
||||
/* scan behind bridge */
|
||||
if (config.pci_bridge()) {
|
||||
/* PCI bridge spec 3.2.5.3, 3.2.5.4 */
|
||||
unsigned char sec_bus = config.read(config_access, 0x19,
|
||||
Device::ACCESS_8BIT);
|
||||
unsigned char sub_bus = config.read(config_access, 0x20,
|
||||
Device::ACCESS_8BIT);
|
||||
|
||||
bridges()->insert(new (heap) Bridge(bus, dev, fun, sec_bus,
|
||||
sub_bus));
|
||||
|
||||
uint16_t cmd = config.read(config_access,
|
||||
Device_config::PCI_CMD_REG,
|
||||
Platform::Device::ACCESS_16BIT);
|
||||
|
||||
const bool enabled = (cmd & Device_config::PCI_CMD_MASK)
|
||||
== Device_config::PCI_CMD_MASK;
|
||||
|
||||
if (!enabled) {
|
||||
config.write(config_access, Device_config::PCI_CMD_REG,
|
||||
cmd | Device_config::PCI_CMD_MASK,
|
||||
Platform::Device::ACCESS_16BIT);
|
||||
}
|
||||
|
||||
log(config, " - bridge ",
|
||||
Hex(sec_bus, Hex::Prefix::OMIT_PREFIX, Hex::Pad::PAD),
|
||||
":00.0", !enabled ? " enabled" : "");
|
||||
|
||||
_scan_bus(config_access, heap, devices_bars, sec_bus, config_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
using Platform::Session_component;
|
||||
|
||||
Genode::Bit_array<Session_component::MAX_PCI_DEVICES> Session_component::bdf_in_use;
|
@ -1,9 +0,0 @@
|
||||
TARGET = legacy_pc_platform_drv
|
||||
REQUIRES = x86
|
||||
SRC_CC = main.cc irq.cc pci_device.cc nonpci_devices.cc session.cc
|
||||
SRC_CC += device_pd.cc acpi_devices.cc
|
||||
LIBS = base
|
||||
|
||||
INC_DIR = $(PRG_DIR)
|
||||
|
||||
CC_CXX_WARN_STRICT_CONVERSION =
|
Loading…
x
Reference in New Issue
Block a user