From 8c060d66225dcf396b2371a73b27401e91b1d94b Mon Sep 17 00:00:00 2001 From: Alexander Boettcher Date: Thu, 5 Nov 2015 09:06:29 +0100 Subject: [PATCH] x86: support attaching rmrr region to device_pd Issue #1764 --- .../platform/spec/x86/device_pd/main.cc | 50 ++- .../platform/spec/x86/pci_device_pd_ipc.h | 71 ++-- .../platform/spec/x86/pci_session_component.h | 365 +++++++++++------- 3 files changed, 305 insertions(+), 181 deletions(-) diff --git a/repos/os/src/drivers/platform/spec/x86/device_pd/main.cc b/repos/os/src/drivers/platform/spec/x86/device_pd/main.cc index 25cc13f710..1303514014 100644 --- a/repos/os/src/drivers/platform/spec/x86/device_pd/main.cc +++ b/repos/os/src/drivers/platform/spec/x86/device_pd/main.cc @@ -19,37 +19,64 @@ #include #include -#include +#include #include "../pci_device_pd_ipc.h" +static bool map_eager(Genode::addr_t const page, unsigned log2_order) +{ + using Genode::addr_t; -void Platform::Device_pd_component::attach_dma_mem(Genode::Ram_dataspace_capability ds_cap) + Genode::Thread_base * myself = Genode::Thread_base::myself(); + Nova::Utcb * utcb = reinterpret_cast(myself->utcb()); + Nova::Rights const mapping_rw(true, true, false); + + addr_t const page_fault_portal = myself->tid().exc_pt_sel + 14; + + /* setup faked page fault information */ + utcb->set_msg_word(((addr_t)&utcb->qual[2] - (addr_t)utcb->msg) / sizeof(addr_t)); + utcb->ip = reinterpret_cast(map_eager); + utcb->qual[1] = page; + utcb->crd_rcv = Nova::Mem_crd(page >> 12, log2_order - 12, mapping_rw); + + /* trigger faked page fault */ + Genode::uint8_t res = Nova::call(page_fault_portal); + return res == Nova::NOVA_OK; +} + +void Platform::Device_pd_component::attach_dma_mem(Genode::Dataspace_capability ds_cap) { using namespace Genode; Dataspace_client ds_client(ds_cap); + addr_t const phys = ds_client.phys_addr(); + size_t const size = ds_client.size(); + addr_t page = ~0UL; try { - page = env()->rm_session()->attach_at(ds_cap, ds_client.phys_addr()); + page = env()->rm_session()->attach_at(ds_cap, phys); } catch (...) { } /* sanity check */ - if ((page == ~0UL) || (page != ds_client.phys_addr())) { + if ((page == ~0UL) || (page != phys)) { if (page != ~0UL) env()->rm_session()->detach(page); - PERR("attachment of DMA memory @ %lx+%zx failed", - ds_client.phys_addr(), ds_client.size()); + PERR("attachment of DMA memory @ %lx+%zx failed", phys, size); return; } - /* trigger mapping of whole memory area */ - for (size_t rounds = (ds_client.size() + 1) / 4096; rounds; - page += 4096, rounds --) - touch_read(reinterpret_cast(page)); + Genode::Flexpage_iterator it(page, size, page, size, 0); + for (Genode::Flexpage flex = it.page(); flex.valid(); flex = it.page()) { + if (map_eager(flex.addr, flex.log2_order)) + continue; + + PERR("attachment of DMA memory @ %lx+%zx failed at %lx", phys, size, + flex.addr); + return; + } } void Platform::Device_pd_component::assign_pci(Genode::Io_mem_dataspace_capability io_mem_cap) @@ -64,7 +91,8 @@ void Platform::Device_pd_component::assign_pci(Genode::Io_mem_dataspace_capabili throw Rm_session::Region_conflict(); /* trigger mapping of whole memory area */ - touch_read(reinterpret_cast(page)); + if (!map_eager(page, 12)) + PERR("assignment of PCI device failed - %lx", page); /* try to assign pci device to this protection domain */ if (!env()->pd_session()->assign_pci(page)) diff --git a/repos/os/src/drivers/platform/spec/x86/pci_device_pd_ipc.h b/repos/os/src/drivers/platform/spec/x86/pci_device_pd_ipc.h index 6bdc43a744..8d8034749b 100644 --- a/repos/os/src/drivers/platform/spec/x86/pci_device_pd_ipc.h +++ b/repos/os/src/drivers/platform/spec/x86/pci_device_pd_ipc.h @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2013-2013 Genode Labs GmbH + * Copyright (C) 2013-2015 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU General Public License version 2. @@ -16,40 +16,41 @@ #include #include -#include namespace Platform { - - struct Device_pd : Genode::Session - { - static const char *service_name() { return "DEVICE_PD"; } - - GENODE_RPC(Rpc_attach_dma_mem, void, attach_dma_mem, - Genode::Ram_dataspace_capability); - GENODE_RPC(Rpc_assign_pci, void, assign_pci, - Genode::Io_mem_dataspace_capability); - - GENODE_RPC_INTERFACE(Rpc_attach_dma_mem, Rpc_assign_pci); - }; - - struct Device_pd_client : Genode::Rpc_client - { - Device_pd_client(Capability cap) - : - Rpc_client(cap) { } - - void attach_dma_mem(Genode::Ram_dataspace_capability cap) { - call(cap); } - - void assign_pci(Genode::Io_mem_dataspace_capability cap) { - call(cap); } - }; - - struct Device_pd_component : Genode::Rpc_object - { - void attach_dma_mem(Genode::Ram_dataspace_capability); - void assign_pci(Genode::Io_mem_dataspace_capability); - }; - + struct Device_pd; + struct Device_pd_client; + struct Device_pd_component; } + +struct Platform::Device_pd : Genode::Session +{ + static const char *service_name() { return "DEVICE_PD"; } + + GENODE_RPC(Rpc_attach_dma_mem, void, attach_dma_mem, + Genode::Dataspace_capability); + GENODE_RPC(Rpc_assign_pci, void, assign_pci, + Genode::Io_mem_dataspace_capability); + + GENODE_RPC_INTERFACE(Rpc_attach_dma_mem, Rpc_assign_pci); +}; + +struct Platform::Device_pd_client : Genode::Rpc_client +{ + Device_pd_client(Capability cap) + : + Rpc_client(cap) { } + + void attach_dma_mem(Genode::Dataspace_capability cap) { + call(cap); } + + void assign_pci(Genode::Io_mem_dataspace_capability cap) { + call(cap); } +}; + +struct Platform::Device_pd_component : Genode::Rpc_object +{ + void attach_dma_mem(Genode::Dataspace_capability); + void assign_pci(Genode::Io_mem_dataspace_capability); +}; 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 2919bc077c..5acb705ee3 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 @@ -22,6 +22,8 @@ #include #include +#include + /* os */ #include #include @@ -35,8 +37,51 @@ namespace Platform { bool bus_valid(int bus = 0); unsigned short bridge_bdf(unsigned char bus); + + class Rmrr; + class Root; } +class Platform::Rmrr : public Genode::List::Element +{ + private: + + Genode::uint64_t _start, _end; + Genode::Io_mem_dataspace_capability _cap; + Genode::uint8_t _bus, _dev, _func; + + public: + + Rmrr(Genode::uint64_t start, Genode::uint64_t end, + Genode::uint8_t bus, Genode::uint8_t dev, Genode::uint8_t func) + : _start(start), _end(end), _bus(bus), _dev(dev), _func(func) + { } + + Genode::Io_mem_dataspace_capability match(Device_config config) { + Genode::uint8_t bus = config.bus_number(); + Genode::uint8_t device = config.device_number(); + Genode::uint8_t function = config.function_number(); + + if (!(_bus == bus && _dev == device && _func == function)) + return Genode::Io_mem_dataspace_capability(); + + if (_cap.valid()) + return _cap; + + Genode::Io_mem_connection io_mem(_start, _end - _start + 1); + io_mem.on_destruction(Genode::Io_mem_connection::KEEP_OPEN); + _cap = io_mem.dataspace(); + + return _cap; + } + + static Genode::List *list() + { + static Genode::List _list; + return &_list; + } +}; + namespace Platform { class Session_component : public Genode::Rpc_object @@ -602,8 +647,16 @@ namespace Platform { Io_mem_dataspace_capability io_mem = device->get_config_space(); - if (_child.valid()) - _child.assign_pci(io_mem); + if (!_child.valid()) + return Io_mem_dataspace_capability(); + + _child.assign_pci(io_mem); + + for (Rmrr *r = Rmrr::list()->first(); r; r = r->next()) { + Io_mem_dataspace_capability rmrr_cap = r->match(device->config()); + if (rmrr_cap.valid()) + _child.attach_dma_mem(rmrr_cap); + } /* * By now forbid usage of extended pci config space dataspace, @@ -639,14 +692,18 @@ namespace Platform { Ram_capability ram_cap; try { + ram_cap = Genode::retry( + [&] () { return _ram->alloc(size, Genode::UNCACHED); }, + [&] () { Genode::env()->parent()->upgrade(_ram->cap(), "ram_quota=4K"); }); + } catch (Genode::Ram_session::Quota_exceeded) { } - ram_cap = _ram->alloc(size, Genode::UNCACHED); - } catch (Genode::Ram_session::Quota_exceeded) { + if (!ram_cap.valid()) { + _ram->transfer_quota(Genode::env()->ram_session_cap(), size); _md_alloc.upgrade(size); - return Ram_capability(); + return ram_cap; } - if (!ram_cap.valid() || !_child.valid()) + if (!_child.valid()) return ram_cap; _child.attach_dma_mem(ram_cap); @@ -662,158 +719,196 @@ namespace Platform { Device_capability device(String const &name) override; }; +} +class Platform::Root : public Genode::Root_component +{ + private: - class Root : public Genode::Root_component - { - private: + Genode::Root_capability _device_pd_root; + /* Ram_session for allocation of dma capable dataspaces */ + Genode::Ram_connection _ram; - Genode::Root_capability _device_pd_root; - /* Ram_session for allocation of dma capable dataspaces */ - Genode::Ram_connection _ram; + void _parse_report_rom(const char * acpi_rom) + { + using namespace Genode; - void _parse_report_rom(const char * acpi_rom) - { - using namespace Genode; + Config_access config_access; - Config_access config_access; + Xml_node xml_acpi(acpi_rom); + if (!xml_acpi.has_type("acpi")) + throw 1; - try { - Xml_node xml_acpi(acpi_rom); - if (!xml_acpi.has_type("acpi")) - throw 1; + for (unsigned i = 0; i < xml_acpi.num_sub_nodes(); i++) { + Xml_node node = xml_acpi.sub_node(i); - unsigned i; + if (node.has_type("bdf")) { - for (i = 0; i < xml_acpi.num_sub_nodes(); i++) - { - Xml_node node = xml_acpi.sub_node(i); + uint32_t bdf_start = 0; + uint32_t func_count = 0; + addr_t base = 0; - if (node.has_type("bdf")) { + node.attribute("start").value(&bdf_start); + node.attribute("count").value(&func_count); + node.attribute("base").value(&base); - uint32_t bdf_start = 0; - uint32_t func_count = 0; - addr_t base = 0; + Session_component::add_config_space(bdf_start, func_count, + base); + } - node.attribute("start").value(&bdf_start); - node.attribute("count").value(&func_count); - node.attribute("base").value(&base); + if (node.has_type("irq_override")) { + unsigned irq = 0xff; + unsigned gsi = 0xff; + unsigned flags = 0xff; - Session_component::add_config_space(bdf_start, - func_count, - base); - } + node.attribute("irq").value(&irq); + node.attribute("gsi").value(&gsi); + node.attribute("flags").value(&flags); - if (node.has_type("irq_override")) { - unsigned irq = 0xff; - unsigned gsi = 0xff; - unsigned flags = 0xff; + using Platform::Irq_override; + Irq_override * o = new (env()->heap()) Irq_override(irq, + gsi, + flags); + Irq_override::list()->insert(o); + } - node.attribute("irq").value(&irq); - node.attribute("gsi").value(&gsi); - node.attribute("flags").value(&flags); + if (node.has_type("rmrr")) { + uint64_t mem_start, mem_end; + node.attribute("start").value(&mem_start); + node.attribute("end").value(&mem_end); - using Platform::Irq_override; - Irq_override::list()->insert(new (env()->heap()) Irq_override(irq, gsi, flags)); - } + if (node.num_sub_nodes() == 0) + throw 2; - if (node.has_type("routing")) { - unsigned gsi; - unsigned bridge_bdf; - unsigned device; - unsigned device_pin; + for (unsigned s = 0; s < node.num_sub_nodes(); s++) { + Xml_node scope = node.sub_node(s); + if (!scope.num_sub_nodes() || !scope.has_type("scope")) + throw 3; - node.attribute("gsi").value(&gsi); - node.attribute("bridge_bdf").value(&bridge_bdf); - node.attribute("device").value(&device); - node.attribute("device_pin").value(&device_pin); + unsigned bus, dev, func; + scope.attribute("bus_start").value(&bus); - /* check that bridge bdf is actually a valid device */ - Device_config config((bridge_bdf >> 8 & 0xff), - (bridge_bdf >> 3) & 0x1f, - bridge_bdf & 0x7, + for (unsigned p = 0; p < scope.num_sub_nodes(); p++) { + Xml_node path = scope.sub_node(p); + if (!path.has_type("path")) + throw 4; + + path.attribute("dev").value(&dev); + path.attribute("func").value(&func); + + Device_config bridge(bus, dev, func, &config_access); - - if (config.valid()) { - if (!config.is_pci_bridge() && bridge_bdf != 0) - /** - * If the bridge bdf has not a type header - * of a bridge in the pci config space, - * then it should be the host bridge - * device. The host bridge device need not - * to be necessarily at 0:0.0, it may be - * on another location. The irq routing - * information for the host bridge however - * contain entries for the bridge bdf to be - * 0:0.0 - therefore we override it here - * for the irq rerouting information of - * host bridge devices. - */ - bridge_bdf = 0; - - Irq_routing::list()->insert(new (env()->heap()) Irq_routing(gsi, bridge_bdf, device, device_pin)); - } + if (bridge.is_pci_bridge()) + /* PCI bridge spec 3.2.5.3, 3.2.5.4 */ + bus = bridge.read(&config_access, 0x19, + Device::ACCESS_8BIT); } + + Rmrr * rmrr = new (env()->heap()) Rmrr(mem_start, + mem_end, bus, + dev, func); + Rmrr::list()->insert(rmrr); } + } + + if (!node.has_type("routing")) + continue; + + unsigned gsi; + unsigned bridge_bdf; + unsigned device; + unsigned device_pin; + + node.attribute("gsi").value(&gsi); + node.attribute("bridge_bdf").value(&bridge_bdf); + node.attribute("device").value(&device); + node.attribute("device_pin").value(&device_pin); + + /* check that bridge bdf is actually a valid device */ + Device_config config((bridge_bdf >> 8 & 0xff), + (bridge_bdf >> 3) & 0x1f, + bridge_bdf & 0x7, &config_access); + + if (!config.valid()) + continue; + + if (!config.is_pci_bridge() && bridge_bdf != 0) + /** + * If the bridge bdf has not a type header of a bridge in + * the pci config space, then it should be the host bridge + * device. The host bridge device need not to be + * necessarily at 0:0.0, it may be on another location. The + * irq routing information for the host bridge however + * contain entries for the bridge bdf to be 0:0.0 - + * therefore we override it here for the irq rerouting + * information of host bridge devices. + */ + bridge_bdf = 0; + + Irq_routing * r = new (env()->heap()) Irq_routing(gsi, + bridge_bdf, + device, + device_pin); + Irq_routing::list()->insert(r); + } + } + + protected: + + Session_component *_create_session(const char *args) + { + try { + return new (md_alloc()) Session_component(ep(), md_alloc(), + _device_pd_root, + &_ram, args); + } catch (Genode::Session_policy::No_policy_defined) { + PERR("Invalid session request, no matching policy for '%s'", + Genode::Session_label(args).string()); + throw Genode::Root::Unavailable(); + } + } + + + void _upgrade_session(Session_component *s, const char *args) override + { + long ram_quota = Genode::Arg_string::find_arg(args, "ram_quota").long_value(0); + s->upgrade_ram_quota(ram_quota); + } + + + public: + + /** + * Constructor + * + * \param ep entry point to be used for serving the PCI session + * and PCI device interface + * \param md_alloc meta-data allocator for allocating PCI-session + * components and PCI-device components + */ + Root(Genode::Rpc_entrypoint *ep, Genode::Allocator *md_alloc, + Genode::size_t pci_device_pd_ram_quota, + Genode::Root_capability &device_pd_root, const char *acpi_rom) + : + Genode::Root_component(ep, md_alloc), + _device_pd_root(device_pd_root), + /* restrict physical address to 3G on 32bit with device_pd */ + _ram("dma", 0, (device_pd_root.valid() && sizeof(void *) == 4) ? + 0xc0000000UL : 0x100000000ULL) + { + /* enforce initial bus scan */ + bus_valid(); + + if (acpi_rom) { + try { + _parse_report_rom(acpi_rom); } catch (...) { PERR("PCI config space data could not be parsed."); } } - protected: - - Session_component *_create_session(const char *args) - { - try { - return new (md_alloc()) Session_component(ep(), md_alloc(), - _device_pd_root, - &_ram, args); - } catch (Genode::Session_policy::No_policy_defined) { - PERR("Invalid session request, no matching policy for '%s'", - Genode::Session_label(args).string()); - throw Genode::Root::Unavailable(); - } - } - - - void _upgrade_session(Session_component *s, const char *args) override - { - long ram_quota = Genode::Arg_string::find_arg(args, "ram_quota").long_value(0); - s->upgrade_ram_quota(ram_quota); - } - - - public: - - /** - * Constructor - * - * \param ep entry point to be used for serving the PCI session and - * PCI device interface - * \param md_alloc meta-data allocator for allocating PCI-session - * components and PCI-device components - */ - Root(Genode::Rpc_entrypoint *ep, Genode::Allocator *md_alloc, - Genode::size_t pci_device_pd_ram_quota, - Genode::Root_capability &device_pd_root, - const char *acpi_rom) - : - Genode::Root_component(ep, md_alloc), - _device_pd_root(device_pd_root), - /* restrict physical address to 3G on 32bit with device_pd */ - _ram("dma", 0, (device_pd_root.valid() && sizeof(void *) == 4) ? - 0xc0000000UL : 0x100000000ULL) - { - /* enforce initial bus scan */ - bus_valid(); - - if (acpi_rom) - _parse_report_rom(acpi_rom); - - /* associate _ram session with ram_session of process */ - _ram.ref_account(Genode::env()->ram_session_cap()); - Genode::env()->ram_session()->transfer_quota(_ram.cap(), 0x1000); - } - }; - -} + /* associate _ram session with ram_session of process */ + _ram.ref_account(Genode::env()->ram_session_cap()); + Genode::env()->ram_session()->transfer_quota(_ram.cap(), 0x1000); + } +};