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_session_component.h"
|
||||||
#include "pci_device_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 const max = sizeof(_io_port_conn) / sizeof(_io_port_conn[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))
|
for (unsigned i = 0; i < max; ++i) {
|
||||||
{
|
Pci::Resource res = _device_config.resource(i);
|
||||||
if (res.type() != Resource::IO)
|
|
||||||
|
if (!res.valid() || res.mem())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (v_id != r_id) {
|
if (v_id != r_id) {
|
||||||
r_id ++;
|
++r_id;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,15 +49,16 @@ Genode::Io_mem_session_capability Platform::Device_component::io_mem(Genode::uin
|
||||||
Genode::size_t const size)
|
Genode::size_t const size)
|
||||||
{
|
{
|
||||||
Genode::uint8_t max = sizeof(_io_mem) / sizeof(_io_mem[0]);
|
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))
|
for (unsigned i = 0; i < max; ++i) {
|
||||||
{
|
Pci::Resource res = _device_config.resource(i);
|
||||||
if (res.type() != Resource::MEMORY)
|
|
||||||
|
if (!res.valid() || !res.mem())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (v_id != r_id) {
|
if (v_id != r_id) {
|
||||||
r_id ++;
|
++r_id;
|
||||||
continue;
|
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)
|
if (offset >= res.size() || offset > res.size() - res_size)
|
||||||
return Genode::Io_mem_session_capability();
|
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 {
|
try {
|
||||||
bool const wc = caching == Genode::Cache_attribute::WRITE_COMBINED;
|
bool const wc = caching == Genode::Cache_attribute::WRITE_COMBINED;
|
||||||
Io_mem * io_mem = new (_slab_iomem) Io_mem(_env,
|
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)];
|
char _mem_irq_component[sizeof(Irq_session_component)];
|
||||||
|
|
||||||
Genode::Io_port_connection *_io_port_conn [Device::NUM_RESOURCES];
|
Genode::Io_port_connection *_io_port_conn[Device::NUM_RESOURCES];
|
||||||
Genode::List<Io_mem> _io_mem [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 Status : Genode::Register<8> {
|
||||||
struct Capabilities : Bitfield<4,1> { };
|
struct Capabilities : Bitfield<4,1> { };
|
||||||
|
@ -189,7 +191,7 @@ class Platform::Device_component : public Genode::Rpc_object<Platform::Device>,
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor for PCI devices
|
||||||
*/
|
*/
|
||||||
Device_component(Genode::Env &env,
|
Device_component(Genode::Env &env,
|
||||||
Device_config device_config, Genode::addr_t addr,
|
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 **
|
** 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; }
|
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())
|
if (!_device_config.valid())
|
||||||
return Resource(0, 0);
|
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
|
unsigned config_read(unsigned char address, Access_size size) override
|
||||||
|
|
|
@ -16,8 +16,81 @@
|
||||||
|
|
||||||
#include <base/output.h>
|
#include <base/output.h>
|
||||||
#include <platform_device/platform_device.h>
|
#include <platform_device/platform_device.h>
|
||||||
|
#include <util/register.h>
|
||||||
#include "pci_config_access.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 {
|
namespace Platform {
|
||||||
|
|
||||||
class Device_config
|
class Device_config
|
||||||
|
@ -44,7 +117,7 @@ namespace Platform {
|
||||||
HEADER_CARD_BUS = 2
|
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)
|
bool _resource_id_is_valid(int resource_id)
|
||||||
{
|
{
|
||||||
|
@ -111,45 +184,62 @@ namespace Platform {
|
||||||
return;
|
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 */
|
/* 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 */
|
/* read base-address register value */
|
||||||
unsigned orig_bar = pci_config->read(bus, device, function, bar_idx, Device::ACCESS_32BIT);
|
unsigned const bar_value =
|
||||||
|
pci_config->read(bus, device, function, bar_idx, Device::ACCESS_32BIT);
|
||||||
|
|
||||||
/* check for invalid resource */
|
/* skip invalid resource BARs */
|
||||||
if (orig_bar == (unsigned)~0) {
|
if (bar_value == ~0U || bar_value == 0U) {
|
||||||
_resource[i] = Device::Resource(0, 0);
|
_resource[i] = Resource();
|
||||||
|
++i;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Determine resource size by writing a magic value (all bits set)
|
* Determine resource size by writing a magic value (all
|
||||||
* to the base-address register. In response, the device clears a number
|
* bits set) to the base-address register. In response, the
|
||||||
* of lowest-significant bits corresponding to the resource size.
|
* device clears a number of lowest-significant bits
|
||||||
* Finally, we write back the original value as assigned by the BIOS.
|
* 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);
|
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);
|
unsigned const bar_size = pci_config->read(bus, device, function, bar_idx, Device::ACCESS_32BIT);
|
||||||
pci_config->write(bus, device, function, bar_idx, orig_bar, Device::ACCESS_32BIT);
|
pci_config->write(bus, device, function, bar_idx, bar_value, Device::ACCESS_32BIT);
|
||||||
|
|
||||||
/*
|
if (!Resource::Bar::mem64(bar_value)) {
|
||||||
* Scan base-address-register value for the lowest set bit but
|
_resource[i] = Resource(bar_value, bar_size);
|
||||||
* ignore the lower bits that are used to describe the resource type.
|
++i;
|
||||||
* I/O resources use the lowest 3 bits, memory resources use the
|
} else {
|
||||||
* lowest four bits for the resource description.
|
/* also consume next BAR for MEM64 */
|
||||||
*/
|
unsigned const bar2_idx = bar_idx + 4;
|
||||||
unsigned start = (bar & 1) ? 3 : 4;
|
unsigned const bar2_value =
|
||||||
unsigned size = 1 << start;
|
pci_config->read(bus, device, function, bar2_idx, Device::ACCESS_32BIT);
|
||||||
for (unsigned bit = start; bit < 32; bit++, size += size)
|
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 */
|
/* combine into first resource and mark second as invalid */
|
||||||
if (bar & (1 << bit))
|
_resource[i] = Resource(bar_value, bar_size,
|
||||||
break;
|
bar2_value, bar2_size);
|
||||||
|
++i;
|
||||||
_resource[i] = Device::Resource(orig_bar, size);
|
_resource[i] = Resource();
|
||||||
|
++i;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,11 +282,11 @@ namespace Platform {
|
||||||
/**
|
/**
|
||||||
* Return resource description by resource ID
|
* 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 */
|
/* return invalid resource if sanity check fails */
|
||||||
if (!_resource_id_is_valid(resource_id))
|
if (!_resource_id_is_valid(resource_id))
|
||||||
return Device::Resource(0, 0);
|
return Platform::Pci::Resource();
|
||||||
|
|
||||||
return _resource[resource_id];
|
return _resource[resource_id];
|
||||||
}
|
}
|
||||||
|
|
|
@ -653,7 +653,7 @@ class Platform::Session_component : public Genode::Rpc_object<Session>
|
||||||
int bus = 0, device = 0, function = -1;
|
int bus = 0, device = 0, function = -1;
|
||||||
|
|
||||||
if (prev) {
|
if (prev) {
|
||||||
Device_config config = prev->config();
|
Device_config config = prev->device_config();
|
||||||
bus = config.bus_number();
|
bus = config.bus_number();
|
||||||
device = config.device_number();
|
device = config.device_number();
|
||||||
function = config.function_number();
|
function = config.function_number();
|
||||||
|
@ -736,9 +736,9 @@ class Platform::Session_component : public Genode::Rpc_object<Session>
|
||||||
if (!device)
|
if (!device)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
unsigned const bus = device->config().bus_number();
|
unsigned const bus = device->device_config().bus_number();
|
||||||
unsigned const dev = device->config().device_number();
|
unsigned const dev = device->device_config().device_number();
|
||||||
unsigned const func = device->config().function_number();
|
unsigned const func = device->device_config().function_number();
|
||||||
|
|
||||||
if (bdf_in_use.get(Device_config::MAX_BUSES * bus +
|
if (bdf_in_use.get(Device_config::MAX_BUSES * bus +
|
||||||
Device_config::MAX_DEVICES * dev +
|
Device_config::MAX_DEVICES * dev +
|
||||||
|
@ -753,7 +753,7 @@ class Platform::Session_component : public Genode::Rpc_object<Session>
|
||||||
/* lookup device component for previous device */
|
/* lookup device component for previous device */
|
||||||
_env.ep().rpc_ep().apply(device_cap, lambda);
|
_env.ep().rpc_ep().apply(device_cap, lambda);
|
||||||
|
|
||||||
if (device && device->config().valid())
|
if (device && device->device_config().valid())
|
||||||
destroy(_md_alloc, device);
|
destroy(_md_alloc, device);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -765,9 +765,9 @@ class Platform::Session_component : public Genode::Rpc_object<Session>
|
||||||
return;
|
return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
addr_t const function = device->config().bus_number() * 32 * 8 +
|
addr_t const function = device->device_config().bus_number() * 32 * 8 +
|
||||||
device->config().device_number() * 8 +
|
device->device_config().device_number() * 8 +
|
||||||
device->config().function_number();
|
device->device_config().function_number();
|
||||||
addr_t const base_ecam = Dataspace_client(_pciconf.cap()).phys_addr();
|
addr_t const base_ecam = Dataspace_client(_pciconf.cap()).phys_addr();
|
||||||
addr_t const base_offset = 0x1000UL * function;
|
addr_t const base_offset = 0x1000UL * function;
|
||||||
|
|
||||||
|
@ -775,12 +775,12 @@ class Platform::Session_component : public Genode::Rpc_object<Session>
|
||||||
throw 1;
|
throw 1;
|
||||||
|
|
||||||
for (Rmrr *r = Rmrr::list()->first(); r; r = r->next()) {
|
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())
|
if (rmrr_cap.valid())
|
||||||
_device_pd.attach_dma_mem(rmrr_cap);
|
_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 (...) {
|
} catch (...) {
|
||||||
Genode::error("assignment to device pd or of RMRR region failed");
|
Genode::error("assignment to device pd or of RMRR region failed");
|
||||||
|
|
Loading…
Reference in New Issue
Block a user