diff --git a/repos/dde_linux/src/include/lx_emul/io_port.h b/repos/dde_linux/src/include/lx_emul/io_port.h new file mode 100644 index 0000000000..76db1d5308 --- /dev/null +++ b/repos/dde_linux/src/include/lx_emul/io_port.h @@ -0,0 +1,33 @@ +/* + * \brief Lx_emul support for I/O port access + * \author Josef Soentgen + * \date 2022-02-22 + */ + +/* + * Copyright (C) 2022 Genode Labs GmbH + * + * This file is distributed under the terms of the GNU General Public License + * version 2. + */ + +#ifndef _LX_EMUL__IO_PORT_H_ +#define _LX_EMUL__IO_PORT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +unsigned char lx_emul_io_port_inb(unsigned short addr); +unsigned short lx_emul_io_port_inw(unsigned short addr); +unsigned int lx_emul_io_port_inl(unsigned short addr); + +void lx_emul_io_port_outb(unsigned char value, unsigned short addr); +void lx_emul_io_port_outw(unsigned short value, unsigned short addr); +void lx_emul_io_port_outl(unsigned int value, unsigned short addr); + +#ifdef __cplusplus +} +#endif + +#endif /* _LX_EMUL__IO_PORT_H_ */ diff --git a/repos/dde_linux/src/include/lx_kit/device.h b/repos/dde_linux/src/include/lx_kit/device.h index cfce48ee10..ddf1a80f4e 100644 --- a/repos/dde_linux/src/include/lx_kit/device.h +++ b/repos/dde_linux/src/include/lx_kit/device.h @@ -74,6 +74,22 @@ class Lx_kit::Device : List::Element void handle(); }; + struct Io_port : List::Element + { + using Index = Platform::Device::Io_port_range::Index; + + Index idx; + uint16_t addr; + uint16_t size; + + Constructible io_port {}; + + bool match(uint16_t addr); + + Io_port(unsigned idx, uint16_t addr, uint16_t size) + : idx{idx}, addr(addr), size(size) {} + }; + struct Clock : List::Element { unsigned idx; @@ -92,15 +108,20 @@ class Lx_kit::Device : List::Element Platform::Connection & _platform; Name const _name; Type const _type; - List _io_mems {}; - List _irqs {}; - List _clocks {}; - Constructible _pdev {}; + List _io_mems {}; + List _io_ports {}; + List _irqs {}; + List _clocks {}; + Constructible _pdev {}; template void _for_each_io_mem(FN const & fn) { for (Io_mem * i = _io_mems.first(); i; i = i->next()) fn(*i); } + template + void _for_each_io_port(FN const & fn) { + for (Io_port * i = _io_ports.first(); i; i = i->next()) fn(*i); } + template void _for_each_irq(FN const & fn) { for (Irq * i = _irqs.first(); i; i = i->next()) fn(*i); } @@ -125,6 +146,14 @@ class Lx_kit::Device : List::Element bool read_config(unsigned reg, unsigned len, unsigned *val); bool write_config(unsigned reg, unsigned len, unsigned val); + + bool io_port(uint16_t addr); + uint8_t io_port_inb(uint16_t addr); + uint16_t io_port_inw(uint16_t addr); + uint32_t io_port_inl(uint16_t addr); + void io_port_outb(uint16_t addr, uint8_t val); + void io_port_outw(uint16_t addr, uint16_t val); + void io_port_outl(uint16_t addr, uint32_t val); }; diff --git a/repos/dde_linux/src/include/spec/x86/lx_emul/shadow/asm/io.h b/repos/dde_linux/src/include/spec/x86/lx_emul/shadow/asm/io.h new file mode 100644 index 0000000000..3a7750d14e --- /dev/null +++ b/repos/dde_linux/src/include/spec/x86/lx_emul/shadow/asm/io.h @@ -0,0 +1,30 @@ +/** + * \brief Shadow copy of asm/io.h + * \author Josef Soentgen + * \date 2022-02-21 + */ + +#ifndef _ASM_X86_IO_H +#define _ASM_X86_IO_H + +#include +#include +#include +#include + +#include + +void __iomem *ioremap(resource_size_t offset, unsigned long size); +void iounmap(volatile void __iomem *addr); + +#define inb lx_emul_io_port_inb +#define inw lx_emul_io_port_inw +#define inl lx_emul_io_port_inl + +#define outb lx_emul_io_port_outb +#define outw lx_emul_io_port_outw +#define outl lx_emul_io_port_outl + +#include + +#endif /* _ASM_X86_IO_H */ diff --git a/repos/dde_linux/src/include/spec/x86/lx_kit/platform_session/device.h b/repos/dde_linux/src/include/spec/x86/lx_kit/platform_session/device.h index 9fcf7301a4..f8eab2fe88 100644 --- a/repos/dde_linux/src/include/spec/x86/lx_kit/platform_session/device.h +++ b/repos/dde_linux/src/include/spec/x86/lx_kit/platform_session/device.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -39,6 +40,7 @@ class Platform::Device : Interface, Noncopyable struct Range { addr_t start; size_t size; }; struct Mmio; + struct Io_port_range; struct Irq; struct Config_space; @@ -104,6 +106,36 @@ class Platform::Device::Mmio : Range }; +class Platform::Device::Io_port_range : Noncopyable +{ + public: + + struct Index { unsigned value; }; + + private: + + Device &_device; + Index _index; + + Constructible _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: diff --git a/repos/dde_linux/src/lib/lx_emul/io_port.cc b/repos/dde_linux/src/lib/lx_emul/io_port.cc new file mode 100644 index 0000000000..b94d85aab3 --- /dev/null +++ b/repos/dde_linux/src/lib/lx_emul/io_port.cc @@ -0,0 +1,101 @@ +/* + * \brief Lx_emul backend for I/O port access + * \author Josef Soentgen + * \date 2022-02-22 + */ + +/* + * Copyright (C) 2022 Genode Labs GmbH + * + * This file is distributed under the terms of the GNU General Public License + * version 2. + */ + +#include +#include + + +template +T _io_port_in(unsigned short addr) +{ + using namespace Lx_kit; + using namespace Genode; + + unsigned ret = 0; + bool valid_ret = false; + env().devices.for_each([&] (Device & d) { + if (d.io_port(addr)) { + valid_ret = true; + switch (sizeof (T)) { + case sizeof (unsigned char): ret = d.io_port_inb(addr); break; + case sizeof (unsigned short): ret = d.io_port_inw(addr); break; + case sizeof (unsigned int): ret = d.io_port_inl(addr); break; + default: valid_ret = false; break; + } + } + }); + + if (!valid_ret) + error("could not read I/O port resource ", Hex(addr)); + + return static_cast(ret); +} + + +unsigned char lx_emul_io_port_inb(unsigned short addr) +{ + return _io_port_in(addr); +} + + +unsigned short lx_emul_io_port_inw(unsigned short addr) +{ + return _io_port_in(addr); +} + + +unsigned int lx_emul_io_port_inl(unsigned short addr) +{ + return _io_port_in(addr); +} + +template +void _io_port_out(unsigned short addr, T value) +{ + using namespace Lx_kit; + using namespace Genode; + + bool valid_ret = false; + env().devices.for_each([&] (Device & d) { + if (d.io_port(addr)) { + valid_ret = true; + switch (sizeof (T)) { + case sizeof (unsigned char): d.io_port_outb(addr, (unsigned char)value); break; + case sizeof (unsigned short): d.io_port_outw(addr, (unsigned short)value); break; + case sizeof (unsigned int): d.io_port_outl(addr, (unsigned int)value); break; + default: valid_ret = false; break; + } + } + }); + + if (!valid_ret) + error("could not write I/O port resource ", Hex(addr)); +} + + +void lx_emul_io_port_outb(unsigned char value, unsigned short addr) +{ + _io_port_out(addr, value); +} + + +void lx_emul_io_port_outw(unsigned short value, unsigned short addr) +{ + _io_port_out(addr, value); +} + + +void lx_emul_io_port_outl(unsigned int value, unsigned short addr) +{ + _io_port_out(addr, value); +} diff --git a/repos/dde_linux/src/lib/lx_kit/device.cc b/repos/dde_linux/src/lib/lx_kit/device.cc index 947f7f5c8e..42b68f4346 100644 --- a/repos/dde_linux/src/lib/lx_kit/device.cc +++ b/repos/dde_linux/src/lib/lx_kit/device.cc @@ -27,6 +27,17 @@ bool Device::Io_mem::match(addr_t addr, size_t size) } +/********************** + ** Device::Io_port ** + **********************/ + +bool Device::Io_port::match(uint16_t addr) +{ + return (this->addr <= addr) && + ((this->addr + this->size) >= addr); +} + + /**************** ** Device::Irq** ****************/ @@ -168,6 +179,110 @@ void Device::irq_ack(unsigned number) } +bool Device::io_port(uint16_t addr) +{ + bool ret = false; + _for_each_io_port([&] (Io_port & io) { + if (io.match(addr)) + ret = true; + }); + return ret; +} + + +uint8_t Device::io_port_inb(uint16_t addr) +{ + uint8_t ret = 0; + _for_each_io_port([&] (Device::Io_port & io) { + if (!io.match(addr)) + return; + + if (!io.io_port.constructed()) + io.io_port.construct(*_pdev, io.idx); + + ret = io.io_port->inb(addr); + }); + + return ret; +} + + +uint16_t Device::io_port_inw(uint16_t addr) +{ + uint16_t ret = 0; + _for_each_io_port([&] (Device::Io_port & io) { + if (!io.match(addr)) + return; + + if (!io.io_port.constructed()) + io.io_port.construct(*_pdev, io.idx); + + ret = io.io_port->inw(addr); + }); + + return ret; +} + + +uint32_t Device::io_port_inl(uint16_t addr) +{ + uint32_t ret = 0; + _for_each_io_port([&] (Device::Io_port & io) { + if (!io.match(addr)) + return; + + if (!io.io_port.constructed()) + io.io_port.construct(*_pdev, io.idx); + + ret = io.io_port->inl(addr); + }); + + return ret; +} + + +void Device::io_port_outb(uint16_t addr, uint8_t val) +{ + _for_each_io_port([&] (Device::Io_port & io) { + if (!io.match(addr)) + return; + + if (!io.io_port.constructed()) + io.io_port.construct(*_pdev, io.idx); + + io.io_port->outb(addr, val); + }); +} + + +void Device::io_port_outw(uint16_t addr, uint16_t val) +{ + _for_each_io_port([&] (Device::Io_port & io) { + if (!io.match(addr)) + return; + + if (!io.io_port.constructed()) + io.io_port.construct(*_pdev, io.idx); + + io.io_port->outw(addr, val); + }); +} + + +void Device::io_port_outl(uint16_t addr, uint32_t val) +{ + _for_each_io_port([&] (Device::Io_port & io) { + if (!io.match(addr)) + return; + + if (!io.io_port.constructed()) + io.io_port.construct(*_pdev, io.idx); + + io.io_port->outl(addr, val); + }); +} + + void Device::enable() { if (_pdev.constructed()) @@ -208,6 +323,13 @@ Device::Device(Entrypoint & ep, _io_mems.insert(new (heap) Io_mem(i++, addr, size)); }); + i = 0; + xml.for_each_sub_node("io_port", [&] (Xml_node node) { + uint16_t addr = node.attribute_value("phys_addr", 0U); + uint16_t size = node.attribute_value("size", 0U); + _io_ports.insert(new (heap) Io_port(i++, addr, size)); + }); + i = 0; xml.for_each_sub_node("irq", [&] (Xml_node node) { _irqs.insert(new (heap) Irq(ep, i++, node.attribute_value("number", 0U))); diff --git a/repos/dde_linux/src/lib/lx_kit/spec/x86/platform.cc b/repos/dde_linux/src/lib/lx_kit/spec/x86/platform.cc index 662ef05981..b974936714 100644 --- a/repos/dde_linux/src/lib/lx_kit/spec/x86/platform.cc +++ b/repos/dde_linux/src/lib/lx_kit/spec/x86/platform.cc @@ -229,6 +229,13 @@ static unsigned bar_size(Platform::Device const &dev, 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; @@ -271,7 +278,7 @@ Platform::Device::Device(Connection &platform, Name name) unsigned Platform::Device::Config_space::read(unsigned char address, Access_size size) { - // 32bit BARs only for now + /* 32bit BARs only for now */ if (address >= 0x10 && address <= 0x24) { unsigned const bar = (address - 0x10) / 4; if (_device._bar_checked_for_size[bar]) { @@ -286,9 +293,6 @@ unsigned Platform::Device::Config_space::read(unsigned char address, if (address == 0x34) return 0u; - if (address > 0x3f) - 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); @@ -299,7 +303,7 @@ void Platform::Device::Config_space::write(unsigned char address, unsigned value, Access_size size) { - // 32bit BARs only for now + /* 32bit BARs only for now */ if (address >= 0x10 && address <= 0x24) { unsigned const bar = (address - 0x10) / 4; if (value == 0xffffffffu) @@ -307,8 +311,30 @@ void Platform::Device::Config_space::write(unsigned char address, return; } - if (address != 0x04) + /* 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; + } Legacy_platform::Device::Access_size const as = convert(size); Legacy_platform::Device_client device { _device._device_cap }; @@ -343,6 +369,53 @@ void *Platform::Device::Mmio::_local_addr() } +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 },