mirror of
https://github.com/genodelabs/genode.git
synced 2025-05-10 20:43:06 +00:00
The classes Genode::Mmio, Genode::Register_set, Genode::Attached_mmio, and Platform::Device::Mmio now receive a template parameter 'size_t SIZE'. In each type that derives from one of these classes, it is now statically checked that the range of each Genode::Register::Register- and Genode::Register_set::Register_array-deriving sub-type is within [0..SIZE). That said, SIZE is the minimum size of the memory region provided to the above mentioned Mmio classes in order to avoid page faults or memory corruption when accessing the registers and register arrays declared inside. Note, that the range end of a register array is not the end of the last item but the end of integer access that is used for accessing the last bit in the last item. The constructors of Genode::Mmio, Genode::Attached_mmio, and Platform::Device::Mmio now receive an argument 'Byte_range_ptr range' that is expected to be the range of the backing memory region. In each type that derives from on of these classes, it is now dynamically checked that 'range.num_bytes >= SIZE', thereby implementing the above mention protection against page faults and memory corruption. The rest of the commit adapts the code throughout the Genode Labs repositories regarding the changes. Note that for that code inside Core, the commits mostly uses a simplified approach by constructing MMIO objects with range [base..base+SIZE) and not with a mapping- or specification-related range size. This should be fixed in the future. Furthermore, there are types that derive from an MMIO class but don't declare any registers or register arrays (especially with Platform::Device::Mmio). In this case SIZE is set to 0. This way, the parameters must be actively corrected by someone who later wants to add registers or register arrays, plus the places can be easily found by grep'ing for Mmio<0>. Fix #4081
489 lines
12 KiB
C++
489 lines
12 KiB
C++
/*
|
|
* \brief Audio driver BSD API emulation
|
|
* \author Josef Soentgen
|
|
* \date 2014-11-16
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2014-2017 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.
|
|
*/
|
|
|
|
/* Genode includes */
|
|
#include <base/allocator_avl.h>
|
|
#include <base/log.h>
|
|
#include <base/object_pool.h>
|
|
#include <dataspace/client.h>
|
|
#include <platform_session/device.h>
|
|
#include <platform_session/dma_buffer.h>
|
|
#include <util/retry.h>
|
|
|
|
/* local includes */
|
|
#include <audio/audio.h>
|
|
#include <bsd.h>
|
|
#include <bsd_emul.h>
|
|
#include <scheduler.h>
|
|
|
|
#include <extern_c_begin.h>
|
|
# include <dev/pci/pcidevs.h>
|
|
#include <extern_c_end.h>
|
|
|
|
static constexpr bool debug = false;
|
|
|
|
extern "C" int probe_cfdata(struct pci_attach_args *);
|
|
|
|
static void run_irq(void *args);
|
|
|
|
namespace {
|
|
|
|
class Pci_driver
|
|
{
|
|
private:
|
|
|
|
enum { DMA_SIZE = 256 * 1024 };
|
|
|
|
Genode::Env & _env;
|
|
Platform::Connection _pci { _env };
|
|
Platform::Dma_buffer _buffer { _pci, DMA_SIZE, Genode::CACHED };
|
|
Genode::Allocator_avl _alloc;
|
|
|
|
struct Device
|
|
{
|
|
Platform::Device dev;
|
|
Platform::Device::Irq irq;
|
|
Platform::Device::Mmio<0> mmio;
|
|
|
|
Device(Platform::Connection &pci,
|
|
Platform::Device::Name const &name)
|
|
: dev { pci, name }, irq { dev }, mmio { dev }
|
|
{ }
|
|
};
|
|
|
|
Genode::Constructible<Device> _device { };
|
|
|
|
uint16_t _vendor_id { 0U };
|
|
uint16_t _device_id { 0U };
|
|
uint32_t _class_code { 0U };
|
|
uint16_t _sub_vendor_id { 0U };
|
|
uint16_t _sub_device_id { 0U };
|
|
|
|
typedef int (*intrh_t)(void*);
|
|
|
|
intrh_t _irq_func { nullptr };
|
|
void * _irq_arg { nullptr };
|
|
Bsd::Task _irq_task { run_irq, this, "irq", Bsd::Task::PRIORITY_3,
|
|
Bsd::scheduler(), 1024 * sizeof(long) };
|
|
|
|
Genode::Io_signal_handler<Pci_driver> _irq_handler { _env.ep(), *this,
|
|
&Pci_driver::_irq_handle };
|
|
|
|
struct pci_attach_args _pa { 0, 0, 0, 0, 0 };
|
|
|
|
void _irq_handle()
|
|
{
|
|
_irq_task.unblock();
|
|
Bsd::scheduler().schedule();
|
|
}
|
|
|
|
Genode::addr_t _buffer_base() {
|
|
return (Genode::addr_t)_buffer.local_addr<char>(); }
|
|
|
|
void _handle_device_list() { /* intentionally left empty */ }
|
|
|
|
void _wait_for_device_list(Genode::Entrypoint &ep,
|
|
Platform::Connection &pci)
|
|
{
|
|
using namespace Genode;
|
|
Constructible<Io_signal_handler<Pci_driver>> handler { };
|
|
|
|
bool device_list = false;
|
|
while (!device_list) {
|
|
pci.update();
|
|
pci.with_xml([&] (Xml_node & xml) {
|
|
if (xml.num_sub_nodes()) {
|
|
pci.sigh(Signal_context_capability());
|
|
if (handler.constructed())
|
|
handler.destruct();
|
|
device_list = true;
|
|
return;
|
|
}
|
|
|
|
if (!handler.constructed()) {
|
|
handler.construct(ep, *this,
|
|
&Pci_driver::_handle_device_list);
|
|
pci.sigh(*handler);
|
|
}
|
|
|
|
ep.wait_and_dispatch_one_io_signal();
|
|
});
|
|
}
|
|
}
|
|
|
|
public:
|
|
|
|
Pci_driver(Genode::Env &env, Genode::Allocator & alloc)
|
|
:
|
|
_env(env), _alloc(&alloc)
|
|
{
|
|
_alloc.add_range(_buffer_base(), DMA_SIZE);
|
|
|
|
/* will "block" until device list becomes available */
|
|
_wait_for_device_list(_env.ep(), _pci);
|
|
}
|
|
|
|
uint16_t sub_device_id() { return _sub_device_id; }
|
|
uint16_t sub_vendor_id() { return _sub_vendor_id; }
|
|
|
|
int probe()
|
|
{
|
|
using namespace Genode;
|
|
|
|
_pci.upgrade_ram(8*1024);
|
|
|
|
/*
|
|
* We hide ourself in the bus_dma_tag_t as well as
|
|
* in the pci_chipset_tag_t field because they are
|
|
* used in all pci or bus related functions and are
|
|
* our access window, hence.
|
|
*/
|
|
_pa.pa_dmat = (bus_dma_tag_t)this;
|
|
_pa.pa_pc = (pci_chipset_tag_t)this;
|
|
|
|
bool found = false;
|
|
_pci.update();
|
|
_pci.with_xml([&] (Xml_node node) {
|
|
node.for_each_sub_node("device", [&] (Xml_node node)
|
|
{
|
|
/* only use the first successfully probed device */
|
|
if (found) return;
|
|
|
|
String<16> name = node.attribute_value("name", String<16>());
|
|
|
|
node.with_optional_sub_node("pci-config", [&] (Xml_node node)
|
|
{
|
|
_vendor_id = node.attribute_value("vendor_id", 0U);
|
|
_device_id = node.attribute_value("device_id", 0U);
|
|
_class_code = node.attribute_value("class", 0U);
|
|
_sub_vendor_id = node.attribute_value("sub_vendor_id", 0U);
|
|
_sub_device_id = node.attribute_value("sub_device_id", 0U);
|
|
|
|
if (_device.constructed())
|
|
_device.destruct();
|
|
|
|
_device.construct(_pci, name);
|
|
_device->irq.sigh(_irq_handler);
|
|
|
|
/* we do the shifting to match OpenBSD's assumptions */
|
|
_pa.pa_tag = 0x80000000UL;
|
|
_pa.pa_class = _class_code << 8;
|
|
_pa.pa_id = _vendor_id | _device_id << 16;
|
|
|
|
if (probe_cfdata(&_pa))
|
|
found = true;
|
|
});
|
|
});
|
|
});
|
|
|
|
return found ? 1 : 0;
|
|
}
|
|
|
|
void irq_handler(intrh_t handler, void * arg)
|
|
{
|
|
_irq_func = handler;
|
|
_irq_arg = arg;
|
|
}
|
|
|
|
void handle_irq()
|
|
{
|
|
_irq_func(_irq_arg);
|
|
_device->irq.ack();
|
|
}
|
|
|
|
|
|
/*********************
|
|
** Mmio management **
|
|
*********************/
|
|
|
|
Genode::addr_t mmio_base() { return _device->mmio.base(); }
|
|
Genode::size_t mmio_size() { return _device->mmio.size(); }
|
|
|
|
template <typename T>
|
|
T read(Genode::size_t offset) {
|
|
return *(volatile T*)(_device->mmio.base() + offset); }
|
|
|
|
template <typename T>
|
|
void write(Genode::size_t offset, T value) {
|
|
*(volatile T*)(_device->mmio.base() + offset) = value; }
|
|
|
|
|
|
/********************
|
|
** DMA management **
|
|
********************/
|
|
|
|
Genode::addr_t alloc(Genode::size_t size, unsigned align)
|
|
{
|
|
using namespace Genode;
|
|
|
|
return _alloc.alloc_aligned(size, align).convert<Genode::addr_t>(
|
|
[&] (void *ptr) { return (addr_t)ptr; },
|
|
[&] (Allocator_avl::Alloc_error) { return 0UL; });
|
|
}
|
|
|
|
void free(Genode::addr_t virt, Genode::size_t size) {
|
|
_alloc.free((void*)virt, size); }
|
|
|
|
Genode::addr_t virt_to_phys(Genode::addr_t virt) {
|
|
return virt - _buffer_base() + _buffer.dma_addr(); }
|
|
|
|
Genode::addr_t phys_to_virt(Genode::addr_t phys) {
|
|
return phys - _buffer.dma_addr() + _buffer_base(); }
|
|
};
|
|
|
|
|
|
/**********************
|
|
** dev/pci/pcivar.h **
|
|
**********************/
|
|
|
|
extern "C" int pci_intr_map(struct pci_attach_args *pa, pci_intr_handle_t *ih) {
|
|
return 0; }
|
|
|
|
|
|
extern "C" void *pci_intr_establish(pci_chipset_tag_t pc, pci_intr_handle_t ih,
|
|
int ipl, int (*intrh)(void *), void *intarg,
|
|
const char *intrstr)
|
|
{
|
|
Pci_driver * drv = (Pci_driver*) pc;
|
|
drv->irq_handler(intrh, intarg);
|
|
return drv;
|
|
}
|
|
|
|
} /* anonymous namespace */
|
|
|
|
static void run_irq(void *args)
|
|
{
|
|
Pci_driver & pci_drv = *(Pci_driver*)args;
|
|
|
|
while (true) {
|
|
Bsd::scheduler().current()->block_and_schedule();
|
|
pci_drv.handle_irq();
|
|
}
|
|
}
|
|
|
|
|
|
int Bsd::probe_drivers(Genode::Env &env, Genode::Allocator &alloc)
|
|
{
|
|
Genode::log("--- probe drivers ---");
|
|
static Pci_driver drv(env, alloc);
|
|
return drv.probe();
|
|
}
|
|
|
|
|
|
/**********************
|
|
** dev/pci/pcivar.h **
|
|
**********************/
|
|
|
|
extern "C" int pci_matchbyid(struct pci_attach_args *pa, const struct pci_matchid *ids, int num)
|
|
{
|
|
pci_vendor_id_t vid = PCI_VENDOR(pa->pa_id);
|
|
pci_product_id_t pid = PCI_PRODUCT(pa->pa_id);
|
|
|
|
for (int i = 0; i < num; i++) {
|
|
if (vid == ids[i].pm_vid && pid == ids[i].pm_pid)
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
extern "C" int pci_mapreg_map(struct pci_attach_args *pa,
|
|
int reg, pcireg_t type,
|
|
int flags, bus_space_tag_t *tagp,
|
|
bus_space_handle_t *handlep, bus_addr_t *basep,
|
|
bus_size_t *sizep, bus_size_t maxsize)
|
|
{
|
|
/* calculate BAR from given register */
|
|
int r = (reg - 0x10) / 4;
|
|
|
|
if (r) {
|
|
Genode::error("MAP BAR ", r, " not implemented yet");
|
|
return -1;
|
|
}
|
|
|
|
Pci_driver *drv = (Pci_driver*)pa->pa_pc;
|
|
*tagp = (Genode::addr_t)drv;
|
|
*handlep = drv->mmio_base();
|
|
|
|
if (basep != 0)
|
|
*basep = drv->mmio_base();
|
|
if (sizep != 0)
|
|
*sizep = maxsize > 0 && drv->mmio_size() > maxsize ? maxsize : drv->mmio_size();
|
|
return 0;
|
|
}
|
|
|
|
|
|
/***************************
|
|
** machine/pci_machdep.h **
|
|
***************************/
|
|
|
|
extern "C" pcireg_t pci_conf_read(pci_chipset_tag_t pc, pcitag_t tag, int reg)
|
|
{
|
|
Pci_driver *drv = (Pci_driver *)pc;
|
|
|
|
switch (reg) {
|
|
case 0x4: return 0x207; /* command register */
|
|
case 0x10: return drv->mmio_base();
|
|
case 0x2c: return drv->sub_device_id() << 16 | drv->sub_vendor_id();
|
|
default:
|
|
if (debug)
|
|
Genode::warning("Ignore reading of PCI config space @ ", reg);
|
|
};
|
|
return 0;
|
|
}
|
|
|
|
|
|
extern "C" void pci_conf_write(pci_chipset_tag_t pc, pcitag_t tag, int reg,
|
|
pcireg_t val)
|
|
{
|
|
if (debug)
|
|
Genode::warning("Ignore writing of PCI config space @ ",
|
|
reg, " val=", val);
|
|
}
|
|
|
|
|
|
/*******************
|
|
** machine/bus.h **
|
|
*******************/
|
|
|
|
extern "C" u_int8_t bus_space_read_1(bus_space_tag_t space,
|
|
bus_space_handle_t handle,
|
|
bus_size_t offset)
|
|
{
|
|
return ((Pci_driver*)space)->read<Genode::uint8_t>(offset);
|
|
}
|
|
|
|
|
|
extern "C" u_int16_t bus_space_read_2(bus_space_tag_t space,
|
|
bus_space_handle_t handle,
|
|
bus_size_t offset)
|
|
{
|
|
return ((Pci_driver*)space)->read<Genode::uint16_t>(offset);
|
|
}
|
|
|
|
|
|
extern "C" u_int32_t bus_space_read_4(bus_space_tag_t space,
|
|
bus_space_handle_t handle,
|
|
bus_size_t offset)
|
|
{
|
|
return ((Pci_driver*)space)->read<Genode::uint32_t>(offset);
|
|
}
|
|
|
|
|
|
extern "C" void bus_space_write_1(bus_space_tag_t space,
|
|
bus_space_handle_t handle,
|
|
bus_size_t offset, u_int8_t value)
|
|
{
|
|
((Pci_driver*)space)->write(offset, value);
|
|
}
|
|
|
|
|
|
extern "C" void bus_space_write_2(bus_space_tag_t space,
|
|
bus_space_handle_t handle,
|
|
bus_size_t offset, u_int16_t value)
|
|
{
|
|
((Pci_driver*)space)->write(offset, value);
|
|
}
|
|
|
|
|
|
extern "C" void bus_space_write_4(bus_space_tag_t space,
|
|
bus_space_handle_t handle,
|
|
bus_size_t offset, u_int32_t value)
|
|
{
|
|
((Pci_driver*)space)->write(offset, value);
|
|
}
|
|
|
|
|
|
extern "C" int bus_dmamap_create(bus_dma_tag_t tag, bus_size_t size, int nsegments,
|
|
bus_size_t maxsegsz, bus_size_t boundart,
|
|
int flags, bus_dmamap_t *dmamp)
|
|
{
|
|
struct bus_dmamap *map;
|
|
map = (struct bus_dmamap*) malloc(sizeof(struct bus_dmamap), M_DEVBUF, M_ZERO);
|
|
|
|
map->size = size;
|
|
map->maxsegsz = maxsegsz;
|
|
map->nsegments = nsegments;
|
|
|
|
*dmamp = map;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
extern "C" void bus_dmamap_destroy(bus_dma_tag_t tag, bus_dmamap_t map) {
|
|
free(map, 0, 0); }
|
|
|
|
|
|
extern "C" int bus_dmamap_load(bus_dma_tag_t tag, bus_dmamap_t dmam, void *buf,
|
|
bus_size_t buflen, struct proc *p, int flags)
|
|
{
|
|
Pci_driver * drv = (Pci_driver*) tag;
|
|
|
|
Genode::addr_t virt = (Genode::addr_t)buf;
|
|
dmam->dm_segs[0].ds_addr = drv->virt_to_phys(virt);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
extern "C" int bus_dmamem_alloc(bus_dma_tag_t tag, bus_size_t size, bus_size_t alignment,
|
|
bus_size_t boundary, bus_dma_segment_t *segs, int nsegs,
|
|
int *rsegs, int flags)
|
|
{
|
|
Pci_driver * drv = (Pci_driver*) tag;
|
|
|
|
Genode::addr_t virt = drv->alloc(size, Genode::log2(alignment));
|
|
if (virt == 0)
|
|
return -1;
|
|
|
|
segs->ds_addr = drv->virt_to_phys(virt);
|
|
segs->ds_size = size;
|
|
*rsegs = 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
extern "C" void bus_dmamem_free(bus_dma_tag_t tag, bus_dma_segment_t *segs, int nsegs)
|
|
{
|
|
Pci_driver * drv = (Pci_driver*) tag;
|
|
|
|
for (int i = 0; i < nsegs; i++) {
|
|
Genode::addr_t phys = (Genode::addr_t)segs[i].ds_addr;
|
|
Genode::addr_t virt = drv->phys_to_virt(phys);
|
|
|
|
drv->free(virt, segs[i].ds_size);
|
|
}
|
|
}
|
|
|
|
|
|
extern "C" int bus_dmamem_map(bus_dma_tag_t tag, bus_dma_segment_t *segs, int nsegs,
|
|
size_t size, caddr_t *kvap, int flags)
|
|
{
|
|
if (nsegs > 1) {
|
|
Genode::error(__func__, ": cannot map more than 1 segment");
|
|
return -1;
|
|
}
|
|
|
|
Pci_driver * drv = (Pci_driver*) tag;
|
|
|
|
Genode::addr_t phys = (Genode::addr_t)segs[0].ds_addr;
|
|
Genode::addr_t virt = drv->phys_to_virt(phys);
|
|
|
|
*kvap = (caddr_t)virt;
|
|
|
|
return 0;
|
|
}
|