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:
Christian Helmuth 2019-09-23 15:29:05 +02:00
parent e0af9c2d8b
commit 6df8b44616
4 changed files with 159 additions and 57 deletions

View File

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

View File

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

View File

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

View File

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