mirror of
https://github.com/genodelabs/genode.git
synced 2025-03-25 21:38:25 +00:00
platform/x86: support 64-bit base address registers
The API still exports 32-bit address and size values only, which works as the actual MMIO resources are allocated in platform_drv internally. Fixes #3494
This commit is contained in:
parent
e0af9c2d8b
commit
6df8b44616
@ -13,18 +13,19 @@
|
||||
#include "pci_session_component.h"
|
||||
#include "pci_device_component.h"
|
||||
|
||||
Genode::Io_port_session_capability Platform::Device_component::io_port(Genode::uint8_t v_id)
|
||||
Genode::Io_port_session_capability Platform::Device_component::io_port(Genode::uint8_t const v_id)
|
||||
{
|
||||
Genode::uint8_t max = sizeof(_io_port_conn) / sizeof(_io_port_conn[0]);
|
||||
Genode::uint8_t i = 0, r_id = 0;
|
||||
Genode::uint8_t const max = sizeof(_io_port_conn) / sizeof(_io_port_conn[0]);
|
||||
Genode::uint8_t r_id = 0;
|
||||
|
||||
for (Resource res = resource(0); i < max; i++, res = resource(i))
|
||||
{
|
||||
if (res.type() != Resource::IO)
|
||||
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 ++;
|
||||
++r_id;
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -48,15 +49,16 @@ Genode::Io_mem_session_capability Platform::Device_component::io_mem(Genode::uin
|
||||
Genode::size_t const size)
|
||||
{
|
||||
Genode::uint8_t max = sizeof(_io_mem) / sizeof(_io_mem[0]);
|
||||
Genode::uint8_t i = 0, r_id = 0;
|
||||
Genode::uint8_t r_id = 0;
|
||||
|
||||
for (Resource res = resource(0); i < max; i++, res = resource(i))
|
||||
{
|
||||
if (res.type() != Resource::MEMORY)
|
||||
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 ++;
|
||||
++r_id;
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -66,6 +68,14 @@ Genode::Io_mem_session_capability Platform::Device_component::io_mem(Genode::uin
|
||||
if (offset >= res.size() || offset > res.size() - res_size)
|
||||
return Genode::Io_mem_session_capability();
|
||||
|
||||
/* error if MEM64 resource base address above 4G on 32-bit */
|
||||
if (res.base() > ~(addr_t)0) {
|
||||
Genode::error("request for MEM64 resource of ", _device_config,
|
||||
" at ", Genode::Hex(res.base()),
|
||||
" not supported on 32-bit system");
|
||||
return Genode::Io_mem_session_capability();
|
||||
}
|
||||
|
||||
try {
|
||||
bool const wc = caching == Genode::Cache_attribute::WRITE_COMBINED;
|
||||
Io_mem * io_mem = new (_slab_iomem) Io_mem(_env,
|
||||
|
@ -89,8 +89,10 @@ class Platform::Device_component : public Genode::Rpc_object<Platform::Device>,
|
||||
|
||||
char _mem_irq_component[sizeof(Irq_session_component)];
|
||||
|
||||
Genode::Io_port_connection *_io_port_conn [Device::NUM_RESOURCES];
|
||||
Genode::List<Io_mem> _io_mem [Device::NUM_RESOURCES];
|
||||
Genode::Io_port_connection *_io_port_conn[Device::NUM_RESOURCES];
|
||||
|
||||
/* list of requested resource chunks per BAR */
|
||||
Genode::List<Io_mem> _io_mem[Device::NUM_RESOURCES];
|
||||
|
||||
struct Status : Genode::Register<8> {
|
||||
struct Capabilities : Bitfield<4,1> { };
|
||||
@ -189,7 +191,7 @@ class Platform::Device_component : public Genode::Rpc_object<Platform::Device>,
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* Constructor for PCI devices
|
||||
*/
|
||||
Device_component(Genode::Env &env,
|
||||
Device_config device_config, Genode::addr_t addr,
|
||||
@ -263,7 +265,7 @@ class Platform::Device_component : public Genode::Rpc_object<Platform::Device>,
|
||||
** Methods used solely by pci session **
|
||||
****************************************/
|
||||
|
||||
Device_config config() const { return _device_config; }
|
||||
Device_config device_config() const { return _device_config; }
|
||||
Genode::addr_t config_space() const { return _config_space; }
|
||||
|
||||
/**************************
|
||||
@ -290,7 +292,7 @@ class Platform::Device_component : public Genode::Rpc_object<Platform::Device>,
|
||||
if (!_device_config.valid())
|
||||
return Resource(0, 0);
|
||||
|
||||
return _device_config.resource(resource_id);
|
||||
return _device_config.resource(resource_id).api_resource();
|
||||
}
|
||||
|
||||
unsigned config_read(unsigned char address, Access_size size) override
|
||||
|
@ -16,8 +16,81 @@
|
||||
|
||||
#include <base/output.h>
|
||||
#include <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 : Genode::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]); }
|
||||
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(Genode::Output &out) const
|
||||
{
|
||||
Genode::print(out, Genode::Hex_range(base(), size()));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
namespace Platform {
|
||||
|
||||
class Device_config
|
||||
@ -44,7 +117,7 @@ namespace Platform {
|
||||
HEADER_CARD_BUS = 2
|
||||
};
|
||||
|
||||
Device::Resource _resource[Device::NUM_RESOURCES];
|
||||
Platform::Pci::Resource _resource[Device::NUM_RESOURCES];
|
||||
|
||||
bool _resource_id_is_valid(int resource_id)
|
||||
{
|
||||
@ -111,45 +184,62 @@ namespace Platform {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; _resource_id_is_valid(i); i++) {
|
||||
/*
|
||||
* 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 bar_idx = 0x10 + 4 * i;
|
||||
unsigned const bar_idx = 0x10 + 4 * i;
|
||||
|
||||
/* read original base-address register value */
|
||||
unsigned orig_bar = pci_config->read(bus, device, function, bar_idx, Device::ACCESS_32BIT);
|
||||
/* read base-address register value */
|
||||
unsigned const bar_value =
|
||||
pci_config->read(bus, device, function, bar_idx, Device::ACCESS_32BIT);
|
||||
|
||||
/* check for invalid resource */
|
||||
if (orig_bar == (unsigned)~0) {
|
||||
_resource[i] = Device::Resource(0, 0);
|
||||
/* skip invalid resource BARs */
|
||||
if (bar_value == ~0U || bar_value == 0U) {
|
||||
_resource[i] = Resource();
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine resource size 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.
|
||||
* Finally, we write back the original value as assigned by the BIOS.
|
||||
* Determine resource size 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. Finally, we write
|
||||
* back the bar-address value as assigned by the BIOS.
|
||||
*/
|
||||
pci_config->write(bus, device, function, bar_idx, ~0, Device::ACCESS_32BIT);
|
||||
unsigned bar = pci_config->read(bus, device, function, bar_idx, Device::ACCESS_32BIT);
|
||||
pci_config->write(bus, device, function, bar_idx, orig_bar, Device::ACCESS_32BIT);
|
||||
unsigned const bar_size = pci_config->read(bus, device, function, bar_idx, Device::ACCESS_32BIT);
|
||||
pci_config->write(bus, device, function, bar_idx, bar_value, Device::ACCESS_32BIT);
|
||||
|
||||
/*
|
||||
* Scan base-address-register value for the lowest set bit but
|
||||
* ignore the lower bits that are used to describe the resource type.
|
||||
* I/O resources use the lowest 3 bits, memory resources use the
|
||||
* lowest four bits for the resource description.
|
||||
*/
|
||||
unsigned start = (bar & 1) ? 3 : 4;
|
||||
unsigned size = 1 << start;
|
||||
for (unsigned bit = start; bit < 32; bit++, size += size)
|
||||
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(bus, device, function, bar2_idx, Device::ACCESS_32BIT);
|
||||
pci_config->write(bus, device, function, bar2_idx, ~0, Device::ACCESS_32BIT);
|
||||
unsigned const bar2_size =
|
||||
pci_config->read(bus, device, function, bar2_idx, Device::ACCESS_32BIT);
|
||||
pci_config->write(bus, device, function, bar2_idx, bar2_value, Device::ACCESS_32BIT);
|
||||
|
||||
/* stop at the lowest-significant set bit */
|
||||
if (bar & (1 << bit))
|
||||
break;
|
||||
|
||||
_resource[i] = Device::Resource(orig_bar, size);
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -192,11 +282,11 @@ namespace Platform {
|
||||
/**
|
||||
* Return resource description by resource ID
|
||||
*/
|
||||
Device::Resource resource(int resource_id)
|
||||
Platform::Pci::Resource resource(int resource_id)
|
||||
{
|
||||
/* return invalid resource if sanity check fails */
|
||||
if (!_resource_id_is_valid(resource_id))
|
||||
return Device::Resource(0, 0);
|
||||
return Platform::Pci::Resource();
|
||||
|
||||
return _resource[resource_id];
|
||||
}
|
||||
|
@ -653,7 +653,7 @@ class Platform::Session_component : public Genode::Rpc_object<Session>
|
||||
int bus = 0, device = 0, function = -1;
|
||||
|
||||
if (prev) {
|
||||
Device_config config = prev->config();
|
||||
Device_config config = prev->device_config();
|
||||
bus = config.bus_number();
|
||||
device = config.device_number();
|
||||
function = config.function_number();
|
||||
@ -736,9 +736,9 @@ class Platform::Session_component : public Genode::Rpc_object<Session>
|
||||
if (!device)
|
||||
return;
|
||||
|
||||
unsigned const bus = device->config().bus_number();
|
||||
unsigned const dev = device->config().device_number();
|
||||
unsigned const func = device->config().function_number();
|
||||
unsigned const bus = device->device_config().bus_number();
|
||||
unsigned const dev = device->device_config().device_number();
|
||||
unsigned const func = device->device_config().function_number();
|
||||
|
||||
if (bdf_in_use.get(Device_config::MAX_BUSES * bus +
|
||||
Device_config::MAX_DEVICES * dev +
|
||||
@ -753,7 +753,7 @@ class Platform::Session_component : public Genode::Rpc_object<Session>
|
||||
/* lookup device component for previous device */
|
||||
_env.ep().rpc_ep().apply(device_cap, lambda);
|
||||
|
||||
if (device && device->config().valid())
|
||||
if (device && device->device_config().valid())
|
||||
destroy(_md_alloc, device);
|
||||
}
|
||||
|
||||
@ -765,9 +765,9 @@ class Platform::Session_component : public Genode::Rpc_object<Session>
|
||||
return;
|
||||
|
||||
try {
|
||||
addr_t const function = device->config().bus_number() * 32 * 8 +
|
||||
device->config().device_number() * 8 +
|
||||
device->config().function_number();
|
||||
addr_t const function = device->device_config().bus_number() * 32 * 8 +
|
||||
device->device_config().device_number() * 8 +
|
||||
device->device_config().function_number();
|
||||
addr_t const base_ecam = Dataspace_client(_pciconf.cap()).phys_addr();
|
||||
addr_t const base_offset = 0x1000UL * function;
|
||||
|
||||
@ -775,12 +775,12 @@ class Platform::Session_component : public Genode::Rpc_object<Session>
|
||||
throw 1;
|
||||
|
||||
for (Rmrr *r = Rmrr::list()->first(); r; r = r->next()) {
|
||||
Io_mem_dataspace_capability rmrr_cap = r->match(_env, device->config());
|
||||
Io_mem_dataspace_capability rmrr_cap = r->match(_env, device->device_config());
|
||||
if (rmrr_cap.valid())
|
||||
_device_pd.attach_dma_mem(rmrr_cap);
|
||||
}
|
||||
|
||||
_device_pd.assign_pci(_pciconf.cap(), base_offset, device->config().bdf());
|
||||
_device_pd.assign_pci(_pciconf.cap(), base_offset, device->device_config().bdf());
|
||||
|
||||
} catch (...) {
|
||||
Genode::error("assignment to device pd or of RMRR region failed");
|
||||
|
Loading…
x
Reference in New Issue
Block a user