From 6df8b446165d8ea01defe29886c28a994f5e2b65 Mon Sep 17 00:00:00 2001 From: Christian Helmuth Date: Mon, 23 Sep 2019 15:29:05 +0200 Subject: [PATCH] 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 --- .../drivers/platform/spec/x86/pci_device.cc | 34 ++-- .../platform/spec/x86/pci_device_component.h | 12 +- .../platform/spec/x86/pci_device_config.h | 150 ++++++++++++++---- .../platform/spec/x86/pci_session_component.h | 20 +-- 4 files changed, 159 insertions(+), 57 deletions(-) diff --git a/repos/os/src/drivers/platform/spec/x86/pci_device.cc b/repos/os/src/drivers/platform/spec/x86/pci_device.cc index aaed6007b..cb2e1a4b7 100644 --- a/repos/os/src/drivers/platform/spec/x86/pci_device.cc +++ b/repos/os/src/drivers/platform/spec/x86/pci_device.cc @@ -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, diff --git a/repos/os/src/drivers/platform/spec/x86/pci_device_component.h b/repos/os/src/drivers/platform/spec/x86/pci_device_component.h index f2386b5ac..d7645891a 100644 --- a/repos/os/src/drivers/platform/spec/x86/pci_device_component.h +++ b/repos/os/src/drivers/platform/spec/x86/pci_device_component.h @@ -89,8 +89,10 @@ class Platform::Device_component : public Genode::Rpc_object, char _mem_irq_component[sizeof(Irq_session_component)]; - Genode::Io_port_connection *_io_port_conn [Device::NUM_RESOURCES]; - Genode::List _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[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, 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, ** 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, 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 diff --git a/repos/os/src/drivers/platform/spec/x86/pci_device_config.h b/repos/os/src/drivers/platform/spec/x86/pci_device_config.h index 8d42953f9..12f55e2bb 100644 --- a/repos/os/src/drivers/platform/spec/x86/pci_device_config.h +++ b/repos/os/src/drivers/platform/spec/x86/pci_device_config.h @@ -16,8 +16,81 @@ #include #include +#include #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]; } diff --git a/repos/os/src/drivers/platform/spec/x86/pci_session_component.h b/repos/os/src/drivers/platform/spec/x86/pci_session_component.h index 2a31dfa04..6c4376b9e 100644 --- a/repos/os/src/drivers/platform/spec/x86/pci_session_component.h +++ b/repos/os/src/drivers/platform/spec/x86/pci_session_component.h @@ -653,7 +653,7 @@ class Platform::Session_component : public Genode::Rpc_object 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 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 /* 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 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 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");