lx_kit & lx_emul: use generic platform API

* Remove wrapper for legacy x86 platform API
* Move PCI configuration space quirks to corresponding driver
  (pc_usb_host_drv, pc_wifi_drv, pc_intel_fb_drv)
* Adapt driver test run-scripts to changed configuration

Ref genodelabs/genode#4578
This commit is contained in:
Stefan Kalkowski
2022-07-29 17:31:20 +02:00
committed by Christian Helmuth
parent 9f9a5186e0
commit 5528434fb6
53 changed files with 1308 additions and 1835 deletions

View File

@ -14,7 +14,6 @@ SRC_C += usb/host/uhci-hcd.c
SRC_C += usb/host/xhci-pci.c
SRC_CC += lx_kit/mapped_io_mem_range.cc
SRC_CC += lx_kit/pci.cc
SRC_CC += spec/x86/platform.cc

View File

@ -0,0 +1,56 @@
/*
* \brief Lx_emul support for accessing PCI devices
* \author Stefan Kalkowski
* \date 2022-05-23
*/
/*
* Copyright (C) 2022 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _LX_EMUL__PCI_H_
#define _LX_EMUL__PCI_H_
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*lx_emul_add_resource_callback_t)
(void * dev,
unsigned number,
unsigned long addr,
unsigned long size,
int io_port);
typedef void (*lx_emul_add_device_callback_t)
(void * bus,
unsigned number,
char const * const name,
unsigned short vendor_id,
unsigned short device_id,
unsigned short sub_vendor,
unsigned short sub_device,
unsigned int class_code,
unsigned char revision,
unsigned irq);
void lx_emul_pci_for_each_resource(const char * const name, void * dev,
lx_emul_add_resource_callback_t fn);
void lx_emul_pci_for_each_device(void * bus, lx_emul_add_device_callback_t fn);
void lx_emul_pci_enable(const char * const name);
void * lx_emul_pci_root_bus(void);
#ifdef __cplusplus
}
#endif
#endif /* _LX_EMUL__PCI_H_ */

View File

@ -1,28 +0,0 @@
/*
* \brief Lx_emul support for accessing PCI(e) config space
* \author Josef Soentgen
* \date 2022-01-18
*/
/*
* Copyright (C) 2022 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _LX_EMUL__PCI_CONFIG_SPACE_H_
#define _LX_EMUL__PCI_CONFIG_SPACE_H_
#ifdef __cplusplus
extern "C" {
#endif
int lx_emul_pci_read_config(unsigned bus, unsigned devfn, unsigned reg, unsigned len, unsigned *val);
int lx_emul_pci_write_config(unsigned bus, unsigned devfn, unsigned reg, unsigned len, unsigned val);
#ifdef __cplusplus
}
#endif
#endif /* _LX_EMUL__PCI_CONFIG_SPACE_H_ */

View File

@ -19,6 +19,7 @@
#include <irq_session/client.h>
#include <io_mem_session/client.h>
#include <platform_session/device.h>
#include <pci/types.h>
#include <util/list.h>
#include <util/xml_node.h>
@ -35,10 +36,7 @@ struct clk {
class Lx_kit::Device : List<Device>::Element
{
private:
friend class Device_list;
friend class List<Device>;
public:
using Name = String<64>;
using Type = Platform::Device::Type;
@ -47,16 +45,17 @@ class Lx_kit::Device : List<Device>::Element
{
using Index = Platform::Device::Mmio::Index;
Index idx;
addr_t addr;
size_t size;
Index idx;
addr_t addr;
size_t size;
unsigned pci_bar;
Constructible<Platform::Device::Mmio> io_mem {};
bool match(addr_t addr, size_t size);
Io_mem(unsigned idx, addr_t addr, size_t size)
: idx{idx}, addr(addr), size(size) {}
Io_mem(unsigned idx, addr_t addr, size_t size, unsigned pci_bar)
: idx{idx}, addr(addr), size(size), pci_bar(pci_bar) {}
};
struct Irq : List<Irq>::Element
@ -81,13 +80,15 @@ class Lx_kit::Device : List<Device>::Element
Index idx;
uint16_t addr;
uint16_t size;
unsigned pci_bar;
Constructible<Platform::Device::Io_port_range> io_port {};
bool match(uint16_t addr);
Io_port(unsigned idx, uint16_t addr, uint16_t size)
: idx{idx}, addr(addr), size(size) {}
Io_port(unsigned idx, uint16_t addr, uint16_t size,
unsigned pci_bar)
: idx{idx}, addr(addr), size(size), pci_bar(pci_bar) {}
};
struct Clock : List<Clock>::Element
@ -100,6 +101,21 @@ class Lx_kit::Device : List<Device>::Element
: idx(idx), name(name), lx_clock{0} {}
};
struct Pci_config
{
Pci::vendor_t vendor_id;
Pci::device_t device_id;
Pci::class_t class_code;
Pci::rev_t rev;
Pci::vendor_t sub_v_id;
Pci::device_t sub_d_id;
};
private:
friend class Device_list;
friend class List<Device>;
Device(Entrypoint & ep,
Platform::Connection & plat,
Xml_node & xml,
@ -108,23 +124,12 @@ class Lx_kit::Device : List<Device>::Element
Platform::Connection & _platform;
Name const _name;
Type const _type;
List<Io_mem> _io_mems {};
List<Io_port> _io_ports {};
List<Irq> _irqs {};
List<Clock> _clocks {};
Constructible<Platform::Device> _pdev {};
template <typename FN>
void _for_each_io_mem(FN const & fn) {
for (Io_mem * i = _io_mems.first(); i; i = i->next()) fn(*i); }
template <typename FN>
void _for_each_io_port(FN const & fn) {
for (Io_port * i = _io_ports.first(); i; i = i->next()) fn(*i); }
template <typename FN>
void _for_each_irq(FN const & fn) {
for (Irq * i = _irqs.first(); i; i = i->next()) fn(*i); }
List<Io_mem> _io_mems {};
List<Io_port> _io_ports {};
List<Irq> _irqs {};
List<Clock> _clocks {};
Constructible<Pci_config> _pci_config {};
Constructible<Platform::Device> _pdev {};
template <typename FN>
void _for_each_clock(FN const & fn) {
@ -133,7 +138,23 @@ class Lx_kit::Device : List<Device>::Element
public:
const char * compatible();
const char * name();
Name name();
template <typename FN>
void for_each_io_mem(FN const & fn) {
for (Io_mem * i = _io_mems.first(); i; i = i->next()) fn(*i); }
template <typename FN>
void for_each_io_port(FN const & fn) {
for (Io_port * i = _io_ports.first(); i; i = i->next()) fn(*i); }
template <typename FN>
void for_each_irq(FN const & fn) {
for (Irq * i = _irqs.first(); i; i = i->next()) fn(*i); }
template <typename FN>
void for_pci_config(FN const & fn) {
if (_pci_config.constructed()) fn(*_pci_config); }
void enable();
clk * clock(const char * name);
@ -163,12 +184,21 @@ class Lx_kit::Device_list : List<Device>
Platform::Connection & _platform;
void _handle_signal() {}
public:
template <typename FN>
void for_each(FN const & fn) {
for (Device * d = first(); d; d = d->next()) fn(*d); }
template <typename FN>
void with_xml(FN const & fn)
{
_platform.update();
_platform.with_xml([&] (Xml_node & xml) { fn(xml); });
}
Device_list(Entrypoint & ep,
Heap & heap,
Platform::Connection & platform);

View File

@ -1,87 +0,0 @@
/*
* \brief Dummy - platform session device interface
* \author Stefan Kalkowski
* \date 2022-01-07
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _PLATFORM_SESSION__CONNECTION_H_
#define _PLATFORM_SESSION__CONNECTION_H_
#include <util/mmio.h>
#include <util/string.h>
#include <base/exception.h>
#include <io_mem_session/client.h>
#include <irq_session/client.h>
namespace Platform {
struct Connection;
class Dma_buffer;
using namespace Genode;
}
#define Platform Legacy_platform
#include <legacy/x86/platform_session/connection.h>
#undef Platform
struct Platform::Connection
{
Env &_env;
Region_map &_rm { _env.rm() };
char _devices_node_buffer[4096u] { };
Constructible<Xml_node> _devices_node { };
Constructible<Legacy_platform::Connection> _legacy_platform { };
struct Device
{
using Name = String<16>;
Name name { };
Legacy_platform::Device_capability cap { };
Device(Name const &name, Legacy_platform::Device_capability cap)
: name { name }, cap { cap } { }
};
enum : unsigned char { MAX_DEVICES = 4 };
Constructible<Device> _devices_list[MAX_DEVICES] { };
Legacy_platform::Device_capability device_cap(char const *name);
Connection(Env &);
/********************************
** Platform session interface **
********************************/
void update();
template <typename FN> void with_xml(FN const & fn)
{
if (_devices_node.constructed())
fn(*_devices_node);
}
Ram_dataspace_capability alloc_dma_buffer(size_t size, Cache cache);
void free_dma_buffer(Ram_dataspace_capability);
addr_t dma_addr(Ram_dataspace_capability);
template <typename FUNC>
auto retry_with_upgrade(Ram_quota ram, Cap_quota caps, FUNC func) -> decltype(func())
{
return _legacy_platform->retry_with_upgrade<FUNC>(ram, caps, func);
}
};
#endif /* _PLATFORM_SESSION__CONNECTION_H_ */

View File

@ -1,181 +0,0 @@
/*
* \brief Dummy - platform session device interface
* \author Stefan Kalkowski
* \date 2022-01-07
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _PLATFORM_SESSION__DEVICE_H_
#define _PLATFORM_SESSION__DEVICE_H_
/* Genode includes */
#include <base/attached_dataspace.h>
#include <base/exception.h>
#include <io_mem_session/client.h>
#include <io_port_session/client.h>
#include <irq_session/client.h>
#include <platform_session/connection.h>
#include <util/mmio.h>
#include <util/string.h>
namespace Platform {
struct Connection;
class Device;
using namespace Genode;
}
class Platform::Device : Interface, Noncopyable
{
public:
struct Range { addr_t start; size_t size; };
struct Mmio;
struct Io_port_range;
struct Irq;
struct Config_space;
using Name = String<64>;
private:
Connection &_platform;
Legacy_platform::Device_capability _device_cap { };
Name _name { };
unsigned _class_code { };
public:
int _bar_checked_for_size[6] { };
struct Index { unsigned value; };
explicit Device(Connection &platform)
: _platform { platform } { }
struct Type { String<64> name; };
Device(Connection &platform, Type type);
Device(Connection &platform, Name name);
~Device() { }
Name const &name() const
{
return _name;
}
};
class Platform::Device::Mmio : Range
{
public:
struct Index { unsigned value; };
private:
Constructible<Attached_dataspace> _attached_ds { };
void *_local_addr();
Device &_device;
Index _index;
public:
Mmio(Device &device, Index index)
: _device { device }, _index { index } { }
explicit Mmio(Device &device)
: _device { device }, _index { ~0u } { }
size_t size() const;
template <typename T>
T *local_addr() { return reinterpret_cast<T *>(_local_addr()); }
};
class Platform::Device::Io_port_range : Noncopyable
{
public:
struct Index { unsigned value; };
private:
Device &_device;
Index _index;
Constructible<Io_port_session_client> _io_port { };
public:
Io_port_range(Device &device, Index index);
explicit Io_port_range(Device &device)
: _device { device }, _index { ~0u } { }
uint8_t inb(uint16_t addr);
uint16_t inw(uint16_t addr);
uint32_t inl(uint16_t addr);
void outb(uint16_t addr, uint8_t val);
void outw(uint16_t addr, uint16_t val);
void outl(uint16_t addr, uint32_t val);
};
class Platform::Device::Irq : Noncopyable
{
public:
struct Index { unsigned value; };
private:
Device &_device;
Index _index;
Constructible<Irq_session_client> _irq { };
public:
Irq(Device &device, Index index);
explicit Irq(Device &device)
: _device { device }, _index { ~0u } { }
void ack();
void sigh(Signal_context_capability);
void sigh_omit_initial_signal(Signal_context_capability);
};
class Platform::Device::Config_space : Noncopyable
{
public:
Device &_device;
enum class Access_size : unsigned { ACCESS_8BIT, ACCESS_16BIT, ACCESS_32BIT };
Config_space(Device &device) : _device { device } { }
unsigned read(unsigned char address, Access_size size);
void write(unsigned char address, unsigned value, Access_size size);
};
#endif /* _PLATFORM_SESSION__DEVICE_H_ */

View File

@ -0,0 +1,104 @@
/*
* \brief Lx_emul backend for PCI devices
* \author Stefan Kalkowski
* \date 2022-05-23
*/
/*
* Copyright (C) 2022 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <base/log.h>
#include <pci/types.h>
#include <lx_emul/init.h>
#include <lx_emul/pci.h>
#include <lx_kit/env.h>
extern "C" void lx_emul_pci_enable(const char * const name)
{
using namespace Lx_kit;
env().devices.for_each([&] (Device & d) {
if (d.name() == name) d.enable(); });
};
extern "C" void
lx_emul_pci_for_each_resource(const char * const name, void * dev,
lx_emul_add_resource_callback_t fn)
{
using namespace Lx_kit;
using namespace Genode;
using namespace Pci;
env().devices.for_each([&] (Device & d) {
if (d.name() != name)
return;
d.for_each_io_mem([&] (Device::Io_mem & io_mem) {
fn(dev, io_mem.pci_bar, io_mem.addr, io_mem.size, 0);
});
d.for_each_io_port([&] (Device::Io_port & io_port) {
fn(dev, io_port.pci_bar, io_port.addr, io_port.size, 1);
});
});
}
extern "C" void
lx_emul_pci_for_each_device(void * bus, lx_emul_add_device_callback_t fn)
{
using namespace Lx_kit;
using namespace Genode;
using namespace Pci;
unsigned num = 0;
env().devices.for_each([&] (Device & d) {
unsigned irq = 0;
d.for_each_irq([&] (Device::Irq & i) {
if (!irq) irq = i.number; });
d.for_pci_config([&] (Device::Pci_config & cfg) {
fn(bus, num++, d.name().string(), cfg.vendor_id, cfg.device_id,
cfg.sub_v_id, cfg.sub_d_id, cfg.class_code, cfg.rev, irq);
});
});
}
static const char * lx_emul_pci_final_fixups[] = {
"__pci_fixup_final_quirk_usb_early_handoff",
"END_OF_PCI_FIXUPS"
};
extern "C" __attribute__((weak)) int inhibit_pci_fixup(char const *)
{
return 0;
}
extern "C" void lx_emul_register_pci_fixup(void (*fn)(struct pci_dev*), const char * name)
{
if (inhibit_pci_fixup(name))
return;
for (unsigned i = 0; i < (sizeof(lx_emul_pci_final_fixups) / sizeof(char*));
i++) {
if (Genode::strcmp(name, lx_emul_pci_final_fixups[i]) == 0) {
Lx_kit::env().pci_fixup_calls.add(fn);
return;
}
}
Genode::error(__func__, " ignore unkown PCI fixup '", name, "'");
}
extern "C" void lx_emul_execute_pci_fixup(struct pci_dev *pci_dev)
{
Lx_kit::env().pci_fixup_calls.execute(pci_dev);
}

View File

@ -0,0 +1,199 @@
/*
* \brief PCI backend
* \author Josef Soentgen
* \date 2022-05-10
*/
/*
* Copyright (C) 2022 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <lx_emul.h>
#include <lx_emul/io_mem.h>
#include <lx_emul/pci.h>
#include <linux/irq.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
extern void lx_backtrace(void);
int arch_probe_nr_irqs(void)
{
/* needed for 'irq_get_irq_data()' in 'pci_assign_irq()' below */
return 256;
}
static const struct attribute_group *pci_dev_attr_groups[] = {
NULL,
};
const struct device_type pci_dev_type = {
.groups = pci_dev_attr_groups,
};
extern const struct device_type pci_dev_type;
static struct pci_bus *_pci_bus;
void * lx_emul_pci_root_bus()
{
return _pci_bus;
}
static struct device * host_bridge_device = NULL;
extern struct device * pci_get_host_bridge_device(struct pci_dev * dev);
struct device * pci_get_host_bridge_device(struct pci_dev * dev)
{
return host_bridge_device;
}
static struct pci_dev *_pci_alloc_dev(struct pci_bus *bus)
{
struct pci_dev *dev;
dev = kzalloc(sizeof(struct pci_dev), GFP_KERNEL);
if (!dev)
return NULL;
INIT_LIST_HEAD(&dev->bus_list);
dev->dev.type = &pci_dev_type;
dev->bus = bus;
return dev;
}
static void pci_add_resource_to_device_callback(void * data,
unsigned number,
unsigned long addr,
unsigned long size,
int io_port)
{
struct pci_dev * dev = (struct pci_dev*) data;
dev->resource[number].start = addr;
dev->resource[number].end = dev->resource[number].start + size - 1;
if (io_port)
dev->resource[number].flags |= IORESOURCE_IO;
}
static void pci_add_single_device_callback(void * data,
unsigned number,
char const * const name,
u16 vendor_id,
u16 device_id,
u16 sub_vendor,
u16 sub_device,
u32 class_code,
u8 revision,
unsigned irq)
{
struct pci_bus * bus = (struct pci_bus*) data;
struct pci_dev * dev = _pci_alloc_dev(bus);
if (!dev) {
printk("Error: out of memory, cannot allocate pci device %s\n", name);
return;
}
dev->devfn = number * 8;
dev->vendor = vendor_id;
dev->device = device_id;
dev->subsystem_vendor = sub_vendor;
dev->subsystem_device = sub_device;
dev->irq = irq;
dev->dma_mask = 0xffffffff;
dev->dev.bus = &pci_bus_type;
dev->revision = revision;
dev->class = class_code;
dev->current_state = PCI_UNKNOWN;
lx_emul_pci_for_each_resource(name, dev,
pci_add_resource_to_device_callback);
list_add_tail(&dev->bus_list, &bus->devices);
device_initialize(&dev->dev);
dev_set_name(&dev->dev, name);
dev->dev.dma_mask = &dev->dma_mask;
if (number == 0) { /* host bridge */
host_bridge_device = &dev->dev;
bus->bridge = &dev->dev;
}
dev->match_driver = false;
if (device_add(&dev->dev)) {
list_del(&dev->bus_list);
kfree(dev);
printk("Error: could not add pci device %s\n", name);
return;
}
lx_emul_execute_pci_fixup(dev);
dev->match_driver = true;
if (device_attach(&dev->dev)) {
list_del(&dev->bus_list);
kfree(dev);
printk("Error: could not attach pci device %s\n", name);
return;
}
}
static int __init pci_subsys_init(void)
{
struct pci_bus *b;
struct pci_sysdata *sd;
/* pci_alloc_bus(NULL) */
b = kzalloc(sizeof (struct pci_bus), GFP_KERNEL);
if (!b)
return -ENOMEM;
sd = kzalloc(sizeof (struct pci_sysdata), GFP_KERNEL);
if (!sd) {
kfree(b);
return -ENOMEM;
}
/* needed by intel_fb */
sd->domain = 0;
b->sysdata = sd;
INIT_LIST_HEAD(&b->node);
INIT_LIST_HEAD(&b->children);
INIT_LIST_HEAD(&b->devices);
INIT_LIST_HEAD(&b->slots);
INIT_LIST_HEAD(&b->resources);
b->max_bus_speed = PCI_SPEED_UNKNOWN;
b->cur_bus_speed = PCI_SPEED_UNKNOWN;
_pci_bus = b;
/* add host bridge device */
pci_add_single_device_callback(b, 0, "00:00.0", 0x8086, 0x44, 0x17aa, 0x2193, 0x60000, 2, 0);
/* attach PCI devices */
lx_emul_pci_for_each_device(b, pci_add_single_device_callback);
return 0;
}
subsys_initcall(pci_subsys_init);

View File

@ -1,82 +0,0 @@
/*
* \brief Lx_emul backend for accessing PCI(e) config space
* \author Josef Soentgen
* \date 2022-01-18
*/
/*
* Copyright (C) 2022 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <lx_kit/env.h>
#include <lx_emul/pci_config_space.h>
using Device_name = Genode::String<16>;
template <typename T, typename... TAIL>
static Device_name to_string(T const &arg, TAIL &&... args)
{
return Device_name(arg, args...);
}
static Device_name assemble(unsigned bus, unsigned devfn)
{
using namespace Genode;
return to_string("pci-",
Hex(bus, Hex::OMIT_PREFIX), ":",
Hex((devfn >> 3) & 0x1fu, Hex::OMIT_PREFIX), ".",
Hex(devfn & 0x7u, Hex::OMIT_PREFIX));
}
int lx_emul_pci_read_config(unsigned bus, unsigned devfn,
unsigned reg, unsigned len, unsigned *val)
{
using namespace Lx_kit;
using namespace Genode;
Device_name name = assemble(bus, devfn);
bool result = false;
bool matched = false;
env().devices.for_each([&] (Device & d) {
matched = name == d.name();
if (matched && val)
result = d.read_config(reg, len, val);
});
if (!result && matched)
error("could not read config space register ", Hex(reg));
return result ? 0 : -1;
}
int lx_emul_pci_write_config(unsigned bus, unsigned devfn,
unsigned reg, unsigned len, unsigned val)
{
using namespace Lx_kit;
using namespace Genode;
Device_name name = assemble(bus, devfn);
bool result = false;
bool matched = false;
env().devices.for_each ([&] (Device & d) {
matched = name == d.name();
if (matched)
result = d.write_config(reg, len, val);
});
if (!result && matched)
error("could not write config space register ", Hex(reg),
" with ", Hex(val));
return result ? 0 : -1;
}

View File

@ -1,46 +0,0 @@
/*
* \brief Lx_emul backend for PCI fixup calls
* \author Josef Soentgen
* \date 2022-02-04
*/
/*
* Copyright (C) 2022 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <base/log.h>
#include <lx_kit/env.h>
#include <lx_emul/init.h>
#include <lx_emul/pci_fixups.h>
extern "C" __attribute__((weak)) int inhibit_pci_fixup(char const *)
{
return 0;
}
extern "C" void lx_emul_register_pci_fixup(void (*fn)(struct pci_dev*), const char * name)
{
if (inhibit_pci_fixup(name))
return;
for (unsigned i = 0; i < (sizeof(lx_emul_pci_final_fixups) / sizeof(char*));
i++) {
if (Genode::strcmp(name, lx_emul_pci_final_fixups[i]) == 0) {
Lx_kit::env().pci_fixup_calls.add(fn);
return;
}
}
Genode::error(__func__, " ignore unkown PCI fixup '", name, "'");
}
extern "C" void lx_emul_execute_pci_fixup(struct pci_dev *pci_dev)
{
Lx_kit::env().pci_fixup_calls.execute(pci_dev);
}

View File

@ -0,0 +1,11 @@
#include <linux/pci.h>
void pcibios_resource_to_bus(struct pci_bus *bus, struct pci_bus_region *region,
struct resource *res)
{
region->start = res->start;
region->end = res->end;
}
void pci_put_host_bridge_device(struct device * dev) { }

View File

@ -0,0 +1,34 @@
#include <linux/pci.h>
static struct attribute *pci_bus_attrs[] = {
NULL,
};
static const struct attribute_group pci_bus_group = {
.attrs = pci_bus_attrs,
};
const struct attribute_group *pci_bus_groups[] = {
&pci_bus_group,
NULL,
};
static struct attribute *pci_dev_attrs[] = {
NULL,
};
static const struct attribute_group pci_dev_group = {
.attrs = pci_dev_attrs,
};
const struct attribute_group *pci_dev_groups[] = {
&pci_dev_group,
NULL,
};

View File

@ -0,0 +1,36 @@
#include <linux/pci.h>
#include <lx_emul/pci.h>
int pci_enable_device(struct pci_dev * dev)
{
lx_emul_pci_enable(dev_name(&dev->dev));
return 0;
}
int pcim_enable_device(struct pci_dev *pdev)
{
/* for now ignore devres */
return pci_enable_device(pdev);
}
void pci_set_master(struct pci_dev * dev) { }
int pci_set_mwi(struct pci_dev * dev)
{
return 1;
}
bool pci_dev_run_wake(struct pci_dev * dev)
{
return false;
}
u8 pci_find_capability(struct pci_dev * dev,int cap)
{
return 0;
}

View File

@ -0,0 +1,44 @@
#include <lx_emul/pci.h>
#include <linux/pci.h>
struct pci_dev * pci_get_class(unsigned int class, struct pci_dev *from)
{
struct pci_dev *dev;
struct pci_bus *bus = (struct pci_bus *) lx_emul_pci_root_bus();
/*
* Break endless loop (see 'intel_dsm_detect()') by only querying
* the bus on the first executuin.
*/
if (from)
return NULL;
list_for_each_entry(dev, &bus->devices, bus_list) {
if (dev->class == class) {
return dev;
}
}
return NULL;
}
struct pci_dev *pci_get_domain_bus_and_slot(int domain, unsigned int bus,
unsigned int devfn)
{
struct pci_dev *dev;
struct pci_bus *pbus = (struct pci_bus *) lx_emul_pci_root_bus();
list_for_each_entry(dev, &pbus->devices, bus_list) {
if (dev->devfn == devfn && dev->class == 0x60000)
return dev;
}
return NULL;
}
struct pci_dev * pci_get_device(unsigned int vendor,unsigned int device,struct pci_dev * from)
{
return NULL;
}

View File

@ -0,0 +1,24 @@
#include <linux/pci.h>
#include <linux/irq.h>
extern struct irq_chip dde_irqchip_data_chip;
void pci_assign_irq(struct pci_dev * dev)
{
struct irq_data *irq_data;
/*
* Be lazy and treat irq as hwirq as this is used by the
* dde_irqchip_data_chip for (un-)masking.
*/
irq_data = irq_get_irq_data(dev->irq);
irq_data->hwirq = dev->irq;
irq_set_chip_and_handler(dev->irq, &dde_irqchip_data_chip,
handle_level_irq);
}

View File

@ -0,0 +1,11 @@
#include <linux/pci.h>
resource_size_t __weak pcibios_align_resource(void *data,
const struct resource *res,
resource_size_t size,
resource_size_t align)
{
return res->start;
}

View File

@ -1,194 +0,0 @@
/*
* \brief Linux kernel PCI
* \author Josef Soentgen
* \date 2022-01-14
*/
/*
* Copyright (C) 2022 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <lx_emul.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
int arch_probe_nr_irqs(void)
{
return 16;
}
#include <asm/x86_init.h>
static int x86_init_pci_init(void)
{
return 1;
}
static void x86_init_pci_init_irq(void) { }
struct x86_init_ops x86_init = {
.pci = {
.init = x86_init_pci_init,
.init_irq = x86_init_pci_init_irq,
},
};
#include <lx_emul/pci_config_space.h>
#include <linux/pci.h>
#include <asm/pci.h>
#include <asm/pci_x86.h>
static int pci_raw_ops_read(unsigned int domain, unsigned int bus, unsigned int devfn,
int reg, int len, u32 *val)
{
return lx_emul_pci_read_config(bus, devfn, (unsigned)reg, (unsigned)len, val);
}
static int pci_raw_ops_write(unsigned int domain, unsigned int bus, unsigned int devfn,
int reg, int len, u32 val)
{
return lx_emul_pci_write_config(bus, devfn, (unsigned)reg, (unsigned)len, val);
}
const struct pci_raw_ops genode_raw_pci_ops = {
.read = pci_raw_ops_read,
.write = pci_raw_ops_write,
};
const struct pci_raw_ops *raw_pci_ops = &genode_raw_pci_ops;
static int pci_read(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *value)
{
return pci_raw_ops_read(0, bus->number, devfn, where, size, value);
}
static int pci_write(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 value)
{
return pci_raw_ops_write(0, bus->number, devfn, where, size, value);
}
struct pci_ops pci_root_ops = {
.read = pci_read,
.write = pci_write,
};
#include <lx_emul/init.h>
static struct resource _dummy_parent;
void pcibios_scan_root(int busnum)
{
struct pci_bus *bus;
struct pci_sysdata *sd;
struct pci_dev *dev;
LIST_HEAD(resources);
sd = kzalloc(sizeof(*sd), GFP_KERNEL);
if (!sd) {
return;
}
sd->node = NUMA_NO_NODE;
pci_add_resource(&resources, &ioport_resource);
pci_add_resource(&resources, &iomem_resource);
bus = pci_scan_root_bus(NULL, busnum, &pci_root_ops, sd, &resources);
if (!bus) {
pci_free_resource_list(&resources);
kfree(sd);
return;
}
pci_bus_add_devices(bus);
/* handle early quirks */
list_for_each_entry(dev, &bus->devices, bus_list) {
/*
* As pci_enable_resources() is only going to check if
* the parent of the resource is set register the dummy.
*/
struct resource *r;
int i;
for (i = 0; i < PCI_NUM_RESOURCES; i++) {
r = &dev->resource[i];
r->parent = &_dummy_parent;
}
lx_emul_execute_pci_fixup(dev);
}
}
#include <linux/irq.h>
#include <linux/pci.h>
extern struct irq_chip dde_irqchip_data_chip;
void pci_assign_irq(struct pci_dev * dev)
{
struct irq_data *irq_data;
/*
* Be lazy and treat irq as hwirq as this is used by the
* dde_irqchip_data_chip for (un-)masking.
*/
irq_data = irq_get_irq_data(dev->irq);
irq_data->hwirq = dev->irq;
irq_set_chip_and_handler(dev->irq, &dde_irqchip_data_chip,
handle_level_irq);
}
#include <linux/pci.h>
unsigned long pci_mem_start = 0xaeedbabe;
const struct attribute_group aspm_ctrl_attr_group[] = { 0 };
const struct attribute_group pci_dev_vpd_attr_group = { };
struct pci_fixup __start_pci_fixups_early[] = { 0 };
struct pci_fixup __end_pci_fixups_early[] = { 0 };
struct pci_fixup __start_pci_fixups_header[] = { 0 };
struct pci_fixup __end_pci_fixups_header[] = { 0 };
struct pci_fixup __start_pci_fixups_final[] = { 0 };
struct pci_fixup __end_pci_fixups_final[] = { 0 };
struct pci_fixup __start_pci_fixups_enable[] = { 0 };
struct pci_fixup __end_pci_fixups_enable[] = { 0 };
struct pci_fixup __start_pci_fixups_resume[] = { 0 };
struct pci_fixup __end_pci_fixups_resume[] = { 0 };
struct pci_fixup __start_pci_fixups_resume_early[] = { 0 };
struct pci_fixup __end_pci_fixups_resume_early[] = { 0 };
struct pci_fixup __start_pci_fixups_suspend[] = { 0 };
struct pci_fixup __end_pci_fixups_suspend[] = { 0 };
struct pci_fixup __start_pci_fixups_suspend_late[] = { 0 };
struct pci_fixup __end_pci_fixups_suspend_late[] = { 0 };
int pcibios_last_bus = -1;
extern int __init pcibios_init(void);
int __init pcibios_init(void)
{
lx_emul_trace(__func__);
return 0;
}

View File

@ -34,7 +34,7 @@ bool Device::Io_mem::match(addr_t addr, size_t size)
bool Device::Io_port::match(uint16_t addr)
{
return (this->addr <= addr) &&
((this->addr + this->size) >= addr);
((this->addr + this->size) > addr);
}
@ -67,9 +67,9 @@ const char * Device::compatible()
}
const char * Device::name()
Device::Name Device::name()
{
return _name.string();
return _name;
}
@ -102,7 +102,7 @@ clk * Device::clock(unsigned idx)
bool Device::io_mem(addr_t phys_addr, size_t size)
{
bool ret = false;
_for_each_io_mem([&] (Io_mem & io) {
for_each_io_mem([&] (Io_mem & io) {
if (io.match(phys_addr, size))
ret = true;
});
@ -113,7 +113,7 @@ bool Device::io_mem(addr_t phys_addr, size_t size)
void * Device::io_mem_local_addr(addr_t phys_addr, size_t size)
{
void * ret = nullptr;
_for_each_io_mem([&] (Io_mem & io) {
for_each_io_mem([&] (Io_mem & io) {
if (!io.match(phys_addr, size))
return;
@ -132,7 +132,7 @@ bool Device::irq_unmask(unsigned number)
{
bool ret = false;
_for_each_irq([&] (Irq & irq) {
for_each_irq([&] (Irq & irq) {
if (irq.number != number)
return;
@ -156,7 +156,7 @@ void Device::irq_mask(unsigned number)
if (!_pdev.constructed())
return;
_for_each_irq([&] (Irq & irq) {
for_each_irq([&] (Irq & irq) {
if (irq.number != number)
return;
irq.session.destruct();
@ -170,7 +170,7 @@ void Device::irq_ack(unsigned number)
if (!_pdev.constructed())
return;
_for_each_irq([&] (Irq & irq) {
for_each_irq([&] (Irq & irq) {
if (irq.number != number || !irq.session.constructed())
return;
irq.session->ack();
@ -181,7 +181,7 @@ void Device::irq_ack(unsigned number)
bool Device::io_port(uint16_t addr)
{
bool ret = false;
_for_each_io_port([&] (Io_port & io) {
for_each_io_port([&] (Io_port & io) {
if (io.match(addr))
ret = true;
});
@ -192,7 +192,7 @@ bool Device::io_port(uint16_t addr)
uint8_t Device::io_port_inb(uint16_t addr)
{
uint8_t ret = 0;
_for_each_io_port([&] (Device::Io_port & io) {
for_each_io_port([&] (Device::Io_port & io) {
if (!io.match(addr))
return;
@ -209,7 +209,7 @@ uint8_t Device::io_port_inb(uint16_t addr)
uint16_t Device::io_port_inw(uint16_t addr)
{
uint16_t ret = 0;
_for_each_io_port([&] (Device::Io_port & io) {
for_each_io_port([&] (Device::Io_port & io) {
if (!io.match(addr))
return;
@ -226,7 +226,7 @@ uint16_t Device::io_port_inw(uint16_t addr)
uint32_t Device::io_port_inl(uint16_t addr)
{
uint32_t ret = 0;
_for_each_io_port([&] (Device::Io_port & io) {
for_each_io_port([&] (Device::Io_port & io) {
if (!io.match(addr))
return;
@ -242,7 +242,7 @@ uint32_t Device::io_port_inl(uint16_t addr)
void Device::io_port_outb(uint16_t addr, uint8_t val)
{
_for_each_io_port([&] (Device::Io_port & io) {
for_each_io_port([&] (Device::Io_port & io) {
if (!io.match(addr))
return;
@ -256,7 +256,7 @@ void Device::io_port_outb(uint16_t addr, uint8_t val)
void Device::io_port_outw(uint16_t addr, uint16_t val)
{
_for_each_io_port([&] (Device::Io_port & io) {
for_each_io_port([&] (Device::Io_port & io) {
if (!io.match(addr))
return;
@ -270,7 +270,7 @@ void Device::io_port_outw(uint16_t addr, uint16_t val)
void Device::io_port_outl(uint16_t addr, uint32_t val)
{
_for_each_io_port([&] (Device::Io_port & io) {
for_each_io_port([&] (Device::Io_port & io) {
if (!io.match(addr))
return;
@ -317,16 +317,18 @@ Device::Device(Entrypoint & ep,
{
unsigned i = 0;
xml.for_each_sub_node("io_mem", [&] (Xml_node node) {
addr_t addr = node.attribute_value("phys_addr", 0UL);
size_t size = node.attribute_value("size", 0UL);
_io_mems.insert(new (heap) Io_mem(i++, addr, size));
addr_t addr = node.attribute_value("phys_addr", 0UL);
size_t size = node.attribute_value("size", 0UL);
unsigned bar = node.attribute_value("pci_bar", 0U);
_io_mems.insert(new (heap) Io_mem(i++, addr, size, bar));
});
i = 0;
xml.for_each_sub_node("io_port", [&] (Xml_node node) {
xml.for_each_sub_node("io_port_range", [&] (Xml_node node) {
uint16_t addr = node.attribute_value<uint16_t>("phys_addr", 0U);
uint16_t size = node.attribute_value<uint16_t>("size", 0U);
_io_ports.insert(new (heap) Io_port(i++, addr, size));
unsigned bar = node.attribute_value("pci_bar", 0U);
_io_ports.insert(new (heap) Io_port(i++, addr, size, bar));
});
i = 0;
@ -339,6 +341,17 @@ Device::Device(Entrypoint & ep,
Device::Name name = node.attribute_value("name", Device::Name());
_clocks.insert(new (heap) Device::Clock(i++, name));
});
xml.for_each_sub_node("pci-config", [&] (Xml_node node) {
using namespace Pci;
_pci_config.construct(Pci_config{
node.attribute_value<vendor_t>("vendor_id", 0xffff),
node.attribute_value<device_t>("device_id", 0xffff),
node.attribute_value<class_t>("class", 0xff),
node.attribute_value<rev_t>("revision", 0xff),
node.attribute_value<vendor_t>("sub_vendor_id", 0xffff),
node.attribute_value<device_t>("sub_device_id", 0xffff)});
});
}
@ -352,9 +365,26 @@ Device_list::Device_list(Entrypoint & ep,
:
_platform(platform)
{
_platform.with_xml([&] (Xml_node & xml) {
xml.for_each_sub_node("device", [&] (Xml_node node) {
insert(new (heap) Device(ep, _platform, node, heap));
bool initialized = false;
Constructible<Io_signal_handler<Device_list>> handler {};
while (!initialized) {
_platform.update();
_platform.with_xml([&] (Xml_node & xml) {
if (!xml.num_sub_nodes()) {
if (!handler.constructed()) {
handler.construct(ep, *this, &Device_list::_handle_signal);
_platform.sigh(*handler);
}
ep.wait_and_dispatch_one_io_signal();
} else {
_platform.sigh(Signal_context_capability());
handler.destruct();
xml.for_each_sub_node("device", [&] (Xml_node node) {
insert(new (heap) Device(ep, _platform, node, heap));
});
initialized = true;
}
});
});
}
}

View File

@ -1,581 +0,0 @@
/*
* \brief Legacy platform session wrapper
* \author Josef Soentgen
* \date 2022-01-14
*/
/*
* Copyright (C) 2022 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
/* Genode includes */
#include <base/ram_allocator.h>
#include <util/xml_generator.h>
/* DDE includes */
#include <lx_kit/env.h>
#include <lx_kit/device.h>
#include <platform_session/device.h>
using namespace Genode;
using Str = String<16>;
template <typename T, typename... TAIL>
static Str to_string(T const &arg, TAIL &&... args)
{
return Str(arg, args...);
}
template <typename FN>
static void scan_resources(Legacy_platform::Device &device,
FN const &fn)
{
using R = Legacy_platform::Device::Resource;
for (unsigned resource_id = 0; resource_id < 6; resource_id++) {
R const resource = device.resource(resource_id);
if (resource.type() != R::INVALID)
fn(resource_id, resource);
}
}
static Str create_device_node(Xml_generator &xml,
Legacy_platform::Device &device)
{
using namespace Genode;
/* start arbitrarily and count up */
static unsigned char irq = 8;
/* start arbitrarily at the dev 1 for the first device */
static unsigned char bdf[3] = { 0, 1, 0 };
/* start arbitrarily at 1 GiB */
static unsigned long mmio_phys_addr = 0x40000000;
unsigned char pbdf[3];
device.bus_address(&pbdf[0], &pbdf[1], &pbdf[2]);
/*
* The host-bridge is only required by the Intel framebuffer
* driver and has to be located at 00:00.0. For every other
* type of device we simply count upwards.
*/
unsigned char vbdf[3] = { 0, 0, 0 };
unsigned const class_code = device.class_code() >> 8;
if (class_code != 0x600 /* host-bridge */) {
vbdf[0] = bdf[0]; vbdf[1] = bdf[1]; vbdf[2] = bdf[2];
bdf[1]++;
}
log("override physical BDF ",
Hex(pbdf[0], Hex::OMIT_PREFIX), ":",
Hex(pbdf[1], Hex::OMIT_PREFIX), ".",
Hex(pbdf[2], Hex::OMIT_PREFIX), " -> ",
Hex(vbdf[0], Hex::OMIT_PREFIX), ":",
Hex(vbdf[1], Hex::OMIT_PREFIX), ".",
Hex(vbdf[2], Hex::OMIT_PREFIX));
Str name = to_string("pci-",
Hex(vbdf[0], Hex::OMIT_PREFIX), ":",
Hex(vbdf[1], Hex::OMIT_PREFIX), ".",
Hex(vbdf[2], Hex::OMIT_PREFIX));
xml.node("device", [&] () {
xml.attribute("name", name);
xml.attribute("type", "pci");
xml.node("irq", [&] () {
xml.attribute("number", irq++);
});
using R = Legacy_platform::Device::Resource;
scan_resources(device, [&] (unsigned id, R const &r) {
bool const memory = r.type() == R::MEMORY;
if (memory) {
xml.node("io_mem", [&] () {
xml.attribute("phys_addr", to_string(Hex(mmio_phys_addr)));
xml.attribute("size", to_string(Hex(r.size())));
xml.attribute("bar", id);
});
mmio_phys_addr += align_addr(r.size(), 12);
} else {
xml.node("io_port", [&] () {
xml.attribute("phys_addr", to_string(Hex(r.bar())));
xml.attribute("size", to_string(Hex(r.size())));
xml.attribute("bar", id);
});
}
});
});
return name;
}
Platform::Connection::Connection(Env &env)
:
_env { env }
{
try {
_legacy_platform.construct(env);
} catch (...) {
error("could not construct legacy platform connection");
throw;
}
/* empirically determined */
_legacy_platform->upgrade_ram(32768);
_legacy_platform->upgrade_caps(8);
Xml_generator xml { _devices_node_buffer,
sizeof (_devices_node_buffer),
"devices", [&] () {
_legacy_platform->with_upgrade([&] () {
/* scan the virtual bus but limit to MAX_DEVICES */
Legacy_platform::Device_capability cap { };
for (auto &dev : _devices_list) {
cap = _legacy_platform->next_device(cap, 0x0u, 0x0u);
if (!cap.valid()) break;
Legacy_platform::Device_client device { cap };
Str name = create_device_node(xml, device);
dev.construct(name, cap);
}
});
} };
_devices_node.construct(_devices_node_buffer,
sizeof (_devices_node_buffer));
}
Legacy_platform::Device_capability
Platform::Connection::device_cap(char const *name)
{
for (auto const &dev : _devices_list) {
if (!dev.constructed())
continue;
if (dev->name == name)
return dev->cap;
}
return Legacy_platform::Device_capability();
}
void Platform::Connection::update() { }
Ram_dataspace_capability
Platform::Connection::alloc_dma_buffer(size_t size, Cache)
{
return _legacy_platform->with_upgrade([&] () {
return _legacy_platform->alloc_dma_buffer(size, Cache::UNCACHED);
});
}
void Platform::Connection::free_dma_buffer(Ram_dataspace_capability ds_cap)
{
_legacy_platform->free_dma_buffer(ds_cap);
}
addr_t Platform::Connection::dma_addr(Ram_dataspace_capability ds_cap)
{
return _legacy_platform->dma_addr(ds_cap);
}
static Legacy_platform::Device::Access_size convert(Platform::Device::Config_space::Access_size size)
{
using PAS = Platform::Device::Config_space::Access_size;
using LAS = Legacy_platform::Device::Access_size;
switch (size) {
case PAS::ACCESS_8BIT:
return LAS::ACCESS_8BIT;
case PAS::ACCESS_16BIT:
return LAS::ACCESS_16BIT;
case PAS::ACCESS_32BIT:
return LAS::ACCESS_32BIT;
}
return LAS::ACCESS_8BIT;
}
template <typename FN>
static void apply(Platform::Device const &device,
Xml_node const &devices,
FN const &fn)
{
using namespace Genode;
using N = Platform::Device::Name;
auto lookup_device = [&] (Xml_node const &node) {
if (node.attribute_value("name", N()) == device.name())
fn(node);
};
devices.for_each_sub_node("device", lookup_device);
}
static unsigned bar_size(Platform::Device const &dev,
Xml_node const &devices, unsigned bar)
{
if (bar > 6)
return 0;
using namespace Genode;
unsigned val = 0;
apply(dev, devices, [&] (Xml_node device) {
device.for_each_sub_node("io_mem", [&] (Xml_node node) {
if (node.attribute_value("bar", 6u) != bar)
return;
val = node.attribute_value("size", 0u);
});
device.for_each_sub_node("io_port", [&] (Xml_node node) {
if (node.attribute_value("bar", 6u) != bar)
return;
val = node.attribute_value("size", 0u);
});
});
return val;
}
static unsigned bar_address(Platform::Device const &dev,
Xml_node const &devices, unsigned bar)
{
if (bar > 6)
return 0;
using namespace Genode;
unsigned val = 0;
apply(dev, devices, [&] (Xml_node device) {
device.for_each_sub_node("io_mem", [&] (Xml_node node) {
if (node.attribute_value("bar", 6u) != bar)
return;
val = node.attribute_value("phys_addr", 0u);
});
device.for_each_sub_node("io_port", [&] (Xml_node node) {
if (node.attribute_value("bar", 6u) != bar)
return;
val = node.attribute_value("phys_addr", 0u);
});
});
return val;
}
static unsigned char irq_line(Platform::Device const &dev,
Xml_node const &devices)
{
enum : unsigned char { INVALID_IRQ_LINE = 0xffu };
unsigned char irq = INVALID_IRQ_LINE;
apply(dev, devices, [&] (Xml_node device) {
device.for_each_sub_node("irq", [&] (Xml_node node) {
irq = node.attribute_value("number", (unsigned char)INVALID_IRQ_LINE);
});
});
return irq;
}
Platform::Device::Device(Connection &platform, Type)
: _platform { platform } { }
Platform::Device::Device(Connection &platform, Name name)
:
_platform { platform },
_device_cap { _platform.device_cap(name.string()) },
_name { name }
{
if (!_device_cap.valid()) {
error(__func__, ": could not get device capability");
throw -1;
}
Legacy_platform::Device_client device(_device_cap);
_class_code = device.class_code() >> 8;
}
unsigned Platform::Device::Config_space::read(unsigned char address,
Access_size size)
{
/* 32bit BARs only for now */
if (address >= 0x10 && address <= 0x24) {
unsigned const bar = (address - 0x10) / 4;
if (_device._bar_checked_for_size[bar]) {
_device._bar_checked_for_size[bar] = 0;
return bar_size(_device, *_device._platform._devices_node, bar);
}
return bar_address(_device, *_device._platform._devices_node, bar);
}
/*
* If any PCI device reports 0 as interrupt PIN, drivers may try to force
* MSI setup (e.g., xhci). So, we clamp the interrupt PIN to 1 to let
* drivers finish initialization and don't bother the platform driver.
*/
if (address == 0x3d)
return 0x01;
if (address == 0x3c)
return irq_line(_device, *_device._platform._devices_node);
if (address == 0x34)
return 0u;
Legacy_platform::Device::Access_size const as = convert(size);
Legacy_platform::Device_client device { _device._device_cap };
return device.config_read(address, as);
}
void Platform::Device::Config_space::write(unsigned char address,
unsigned value,
Access_size size)
{
/* 32bit BARs only for now */
if (address >= 0x10 && address <= 0x24) {
unsigned const bar = (address - 0x10) / 4;
if (value == 0xffffffffu)
_device._bar_checked_for_size[bar] = 1;
return;
}
enum {
CLASS_CODE_USB = 0xc03,
};
switch(_device._class_code) {
case CLASS_CODE_USB:
/* reject writes to unknown addresses */
switch (address) {
case 0x04:
/*
* Doing this multiple times induces multiple "assignment of PCI
* device" diasgnostics currently.
*/
/* command register (value for enable io, memory, bus master) */
value = 7;
break;
/*
* Write in [0x40,0xff] should be okay if there are is no capability
* list (status register bit 4 + capability list pointer). Otherwise,
* writes into capability-list entries should be rejected.
*/
case 0xc0: /* UHCI BIOS handoff (UHCI_USBLEGSUP) */ break;
case 0xc4: /* UHCI INTEL resume-enable (USBRES_INTEL) */ break;
case 0x60 ... 0x6f: /* EHCI BIOS handoff (just empiric, address not fixed) */ break;
default:
return;
}
break;
}
Legacy_platform::Device::Access_size const as = convert(size);
Legacy_platform::Device_client device { _device._device_cap };
device.config_write(address, value, as);
}
size_t Platform::Device::Mmio::size() const
{
return _attached_ds.constructed() ? _attached_ds->size() : 0;
}
void *Platform::Device::Mmio::_local_addr()
{
if (!_attached_ds.constructed()) {
Legacy_platform::Device_client device { _device._device_cap };
Cache cache = Cache::UNCACHED;
{
uint8_t phys_bar_id = 0;
/* convert virtual bar id into phys bar id */
for (unsigned virt_id = 0, i = 0; i < 6; i++) {
auto const type = device.resource(i).type();
if (type == Legacy_platform::Device::Resource::Type::MEMORY) {
virt_id ++;
phys_bar_id = uint8_t(i);
}
if (virt_id > _index.value)
break;
}
if (device.resource(phys_bar_id).prefetchable())
cache = Cache::WRITE_COMBINED;
auto const r = device.resource(phys_bar_id);
Range::start = (r.base() & 0xfffu);
}
Io_mem_session_capability io_mem_cap =
device.io_mem(uint8_t(_index.value), cache);
Io_mem_session_client io_mem_client(io_mem_cap);
_attached_ds.construct(Lx_kit::env().env.rm(),
io_mem_client.dataspace());
}
return (void*)(_attached_ds->local_addr<char>() + Range::start);
}
Platform::Device::Io_port_range::Io_port_range(Device &device, Index index)
:
_device { device },
_index { index }
{
Legacy_platform::Device_client client { _device._device_cap };
_io_port.construct(client.io_port((uint8_t)index.value));
}
unsigned char Platform::Device::Io_port_range::inb(uint16_t addr)
{
return _io_port->inb(addr);
}
unsigned short Platform::Device::Io_port_range::inw(uint16_t addr)
{
return _io_port->inw(addr);
}
unsigned int Platform::Device::Io_port_range::inl(uint16_t addr)
{
return _io_port->inl(addr);
}
void Platform::Device::Io_port_range::outb(uint16_t addr, uint8_t val)
{
return _io_port->outb(addr, val);
}
void Platform::Device::Io_port_range::outw(uint16_t addr, uint16_t val)
{
return _io_port->outw(addr, val);
}
void Platform::Device::Io_port_range::outl(uint16_t addr, uint32_t val)
{
return _io_port->outl(addr, val);
}
Platform::Device::Irq::Irq(Platform::Device &device, Index index)
:
_device { device },
_index { index }
{
Legacy_platform::Device_client client { _device._device_cap };
_irq.construct(client.irq((uint8_t)index.value));
}
void Platform::Device::Irq::ack()
{
_irq->ack_irq();
}
void Platform::Device::Irq::sigh(Signal_context_capability sigh)
{
_irq->sigh(sigh);
_irq->ack_irq();
}
void Platform::Device::Irq::sigh_omit_initial_signal(Signal_context_capability sigh)
{
_irq->sigh(sigh);
}
static Platform::Device::Config_space::Access_size access_size(unsigned len)
{
using AS = Platform::Device::Config_space::Access_size;
AS as = AS::ACCESS_8BIT;
if (len == 4) as = AS::ACCESS_32BIT;
else if (len == 2) as = AS::ACCESS_16BIT;
else as = AS::ACCESS_8BIT;
return as;
}
bool Lx_kit::Device::read_config(unsigned reg, unsigned len, unsigned *val)
{
if (!_pdev.constructed())
enable();
if (!val)
return false;
using AS = Platform::Device::Config_space::Access_size;
AS const as = access_size(len);
*val = Platform::Device::Config_space(*_pdev).read((unsigned char)reg, as);
return true;
}
bool Lx_kit::Device::write_config(unsigned reg, unsigned len, unsigned val)
{
if (!_pdev.constructed())
return false;
using AS = Platform::Device::Config_space::Access_size;
AS const as = access_size(len);
Platform::Device::Config_space(*_pdev).write((unsigned char)reg, val, as);
return true;
}