Remove legacy platform_drv, API, and platform.inc

Fix genodelabs/genode#4671
This commit is contained in:
Stefan Kalkowski 2022-11-15 15:59:31 +01:00 committed by Christian Helmuth
parent 08378dd148
commit 9e61fb90c5
28 changed files with 1 additions and 5947 deletions

View File

@ -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
}

View File

@ -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; }

View File

@ -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_ */

View File

@ -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_ */

View File

@ -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_ */

View File

@ -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_ */

View File

@ -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_ */

View File

@ -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_ */

View File

@ -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_ */

View File

@ -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

View File

@ -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>

View File

@ -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> &registry,
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> &registry,
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;
}

View File

@ -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;
};

View File

@ -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);
}

View File

@ -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_ */

View File

@ -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;
}

View File

@ -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_ */

View File

@ -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_ */

View File

@ -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);
}

View File

@ -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 &registry)
{
_acpi_device_registry = &registry;
}
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(); }
}

View File

@ -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_ */

View File

@ -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_ */

View File

@ -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;
}

View File

@ -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_ */

View File

@ -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_ */

View File

@ -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;

View File

@ -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 =