l4re-base-25.08.0

This commit is contained in:
2025-09-12 15:55:45 +02:00
commit d959eaab98
37938 changed files with 9382688 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
provides: libio-io
requires: libio-vbus l4re
maintainer: warg@os.inf.tu-dresden.de

View File

@@ -0,0 +1,4 @@
PKGDIR = .
L4DIR ?= $(PKGDIR)/../../..
include $(L4DIR)/mk/subdir.mk

View File

@@ -0,0 +1,8 @@
PKGDIR = ..
L4DIR ?= $(PKGDIR)/../../..
CONTRIB_HEADERS = y
PKGNAME = libio-io/l4/io
include $(L4DIR)/mk/include.mk

View File

@@ -0,0 +1,260 @@
/**
* \file
*/
/*
* (c) 2008-2010 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
* Alexander Warg <warg@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/sys/compiler.h>
#include <l4/sys/types.h>
#include <l4/sys/linkage.h>
#include <l4/io/types.h>
L4_BEGIN_DECLS
/**
* \defgroup api_l4io IO interface
*/
/**
* \internal
* \brief Request an interrupt.
* \ingroup api_l4io
*
* \param irqnum IRQ number.
* \param irqcap Capability index to attach the IRQ to.
* \return 0 on success, <0 on error
*
* \note This function is internal because libirq is handling IRQs. This
* is rather the interface to get the IRQ.
*/
L4_CV long L4_EXPORT
l4io_request_irq(int irqnum, l4_cap_idx_t irqcap);
/**
* \brief Request the ICU object of the client.
*
* \return Client ICU object, an invalid capability selector is returned if
* no ICU is available.
*/
L4_CV l4_cap_idx_t L4_EXPORT
l4io_request_icu(void);
/**
* \internal
* \brief Release an interrupt.
* \ingroup api_l4io
*
* \param irqnum IRQ number.
* \return 0 on success, <0 on error
*
* \note This function is internal because libirq is handling IRQs. This
* is rather the interface to return the IRQ.
*/
L4_CV long L4_EXPORT
l4io_release_irq(int irqnum, l4_cap_idx_t irq_cap);
/**
* \brief Request an IO memory region.
* \ingroup api_l4io
* \param phys Physical address of the I/O memory region
* \param size Size of the region in Bytes, granularity pages.
* \param flags See #l4io_iomem_flags_t
* \param[in, out] virt Virtual address where the IO memory region should be
* mapped to. If the caller passes '0' a region in the
* caller's address space is searched and the virtual
* address is returned.
*
* \retval 0 Success.
* \retval -L4_ENOENT No area in the caller's address space could be
* found to map the IO memory region.
* \retval -L4_EPERM Operation not allowed.
* \retval -L4_EINVAL Invalid value.
* \retval -L4_EADDRNOTAVAIL The requested virtual address is not available.
* \retval -L4_ENOMEM The requested IO memory region could not be
* allocated.
* \retval <0 IPC errors.
*
* \note This function uses L4Re functionality to reserve a part of the
* virtual address space of the caller.
*/
L4_CV long L4_EXPORT
l4io_request_iomem(l4_addr_t phys, unsigned long size, int flags,
l4_addr_t *virt);
/**
* \brief Request an IO memory region and map it to a specified region.
* \ingroup api_l4io
* \param phys Physical address of the I/O memory region
* \param virt Virtual address.
* \param size Size of the region in Bytes, granularity pages.
* \param flags See #l4io_iomem_flags_t
*
* \retval 0 Success.
* \retval -L4_ENOENT No area could be found to map the IO memory
* region.
* \retval -L4_EPERM Operation not allowed.
* \retval -L4_EINVAL Invalid value.
* \retval -L4_EADDRNOTAVAIL The requested virtual address is not available.
* \retval -L4_ENOMEM The requested IO memory region could not be
* allocated.
* \retval <0 IPC errors.
*
* \note This function uses L4Re functionality to reserve a part of the
* virtual address space of the caller.
*/
L4_CV long L4_EXPORT
l4io_request_iomem_region(l4_addr_t phys, l4_addr_t virt,
unsigned long size, int flags);
/**
* \brief Release an IO memory region.
* \ingroup api_l4io
* \param virt Virtual address of region to free, see #l4io_request_iomem
* \param size Size of the region to release.
* \return 0 on success, <0 on error
*/
L4_CV long L4_EXPORT
l4io_release_iomem(l4_addr_t virt, unsigned long size);
/**
* \brief Request an IO port region.
* \ingroup api_l4io
* \param portnum Start of port range to request
* \param len Length of range to request
* \return 0 on success, <0 on error
*
* \note X86 architecture only
*/
L4_CV long L4_EXPORT
l4io_request_ioport(unsigned portnum, unsigned len);
/**
* \brief Release an IO port region.
* \ingroup api_l4io
* \param portnum Start of port range to release
* \param len Length of range to request
* \return 0 on success, <0 on error
*
* \note X86 architecture only
*/
L4_CV long L4_EXPORT
l4io_release_ioport(unsigned portnum, unsigned len);
/* -------------- Device handling --------------------- */
/**
* \brief Get root device handle of the device bus.
*
* \return root device handle
*/
L4_INLINE
l4io_device_handle_t l4io_get_root_device(void);
/**
* \brief Iterate over the device bus.
*
* \param[in,out] devhandle Device handle to start iterating at.
* The next device handle is returned here.
* \param[out] dev Device information, may be NULL.
* \param[out] reshandle Resource handle.
*
* \return 0 on success, error code otherwise
*/
L4_CV int L4_EXPORT
l4io_iterate_devices(l4io_device_handle_t *devhandle,
l4io_device_t *dev, l4io_resource_handle_t *reshandle);
/**
* \brief Find a device by name.
* \ingroup api_l4io
*
* \param devname Name of device.
* \param[out] dev_handle Device handle for found device, can be NULL.
* \param[out] dev Device information, filled by the function, can be
* NULL.
* \param[out] res_handle Resource handle, can be NULL.
*
* \return 0 on success, error code otherwise
*/
L4_CV int L4_EXPORT
l4io_lookup_device(const char *devname,
l4io_device_handle_t *dev_handle,
l4io_device_t *dev, l4io_resource_handle_t *res_handle);
/**
* \brief Request a specific resource from a device description.
* \ingroup api_l4io
*
* \param devhandle Device handle.
* \param type Type of resource to request (see
* #l4io_resource_types_t).
* \param[in,out] reshandle Resource handle, start with handle returned by
* device functions. The next resource handle is
* returned here.
* \param[out] res Device descriptor.
*
* \return 0 on success, error code otherwise, esp. -L4_ENOENT if no more
* resources found
*/
L4_CV int L4_EXPORT
l4io_lookup_resource(l4io_device_handle_t devhandle,
enum l4io_resource_types_t type,
l4io_resource_handle_t *reshandle,
l4io_resource_t *res);
/* --------------- Convenience functions ----------------- */
/**
* \brief Request IO memory.
* \ingroup api_l4io
*
* \param devhandle Device handle.
* \param[in,out] reshandle Resource handle from which IO memory should be
* requested. Upon successfull completion 'reshandle'
* points to the device's next resource.
*
* \retval 0 An error occured. The value of 'reshandle' is undefined.
* \retval >0 The virtual address of the IO memory mapping.
*/
L4_CV l4_addr_t L4_EXPORT
l4io_request_resource_iomem(l4io_device_handle_t devhandle,
l4io_resource_handle_t *reshandle);
/**
* \brief Request all available IO-port resources.
* \param res_cb Callback function called for every port resource found,
* give NULL for no callback.
*
*/
L4_CV void L4_EXPORT
l4io_request_all_ioports(void (*res_cb)(l4vbus_resource_t const *res));
/**
* \brief Check if a resource is available.
* \ingroup api_l4io
*
* \param type Type of resource
* \param start Minimal value.
* \param end Maximum value.
*/
L4_CV int L4_EXPORT
l4io_has_resource(enum l4io_resource_types_t type,
l4vbus_paddr_t start, l4vbus_paddr_t end);
/* ------------------------------------------------------- */
/* Implementations */
L4_INLINE
l4io_device_handle_t l4io_get_root_device(void)
{ return 0; }
L4_END_DECLS

View File

@@ -0,0 +1,72 @@
/*
* (c) 2008-2010 Adam Lackorzynski <adam@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#pragma once
#include <l4/vbus/vbus_types.h>
/**
* \brief Flags for IO memory.
* \ingroup api_l4io
*/
enum l4io_iomem_flags_t
{
L4IO_MEM_NONCACHED = 0, /**< Non-cache memory */
L4IO_MEM_CACHED = 1, /**< Cache memory */
L4IO_MEM_USE_MTRR = 2, /**< Use MTRR */
L4IO_MEM_ATTR_MASK = 0xf,
// combinations
L4IO_MEM_WRITE_COMBINED = L4IO_MEM_USE_MTRR | L4IO_MEM_CACHED,
/** Use reserved area for mapping I/O memory. Flag only valid
* for l4io_request_iomem_region() */
L4IO_MEM_USE_RESERVED_AREA = 0x40 << 8,
/** Eagerly map the I/O memory. Passthrough to the l4re-rm. */
L4IO_MEM_EAGER_MAP = 0x80 << 8,
};
/**
* \brief Device types.
* \ingroup api_l4io
*/
enum l4io_device_types_t {
L4IO_DEVICE_INVALID = 0, /**< Invalid type */
L4IO_DEVICE_PCI, /**< PCI device */
L4IO_DEVICE_USB, /**< USB device */
L4IO_DEVICE_OTHER, /**< Any other device without unique IDs */
L4IO_DEVICE_ANY = ~0 /**< any type */
};
/**
* \brief Resource types
* \ingroup api_l4io
*/
enum l4io_resource_types_t {
L4IO_RESOURCE_INVALID = L4VBUS_RESOURCE_INVALID, /**< Invalid type */
L4IO_RESOURCE_IRQ = L4VBUS_RESOURCE_IRQ, /**< Interrupt resource */
L4IO_RESOURCE_MEM = L4VBUS_RESOURCE_MEM, /**< I/O memory resource */
L4IO_RESOURCE_PORT = L4VBUS_RESOURCE_PORT, /**< I/O port resource (x86 only) */
L4IO_RESOURCE_ANY = ~0 /**< any type */
};
typedef l4vbus_device_handle_t l4io_device_handle_t;
typedef unsigned l4io_resource_handle_t;
/**
* \brief Resource descriptor.
* \ingroup api_l4io
*
* For IRQ types, the end field is not used, i.e. only a single
* interrupt can be described with a l4io_resource_t
*/
typedef l4vbus_resource_t l4io_resource_t;
/** Device descriptor.
* \ingroup api_l4io
*/
typedef l4vbus_device_t l4io_device_t;

View File

@@ -0,0 +1,9 @@
PKGDIR ?= ..
L4DIR ?= $(PKGDIR)/../../..
TARGET := include src
include $(L4DIR)/mk/subdir.mk
src: include

View File

@@ -0,0 +1,14 @@
PKGDIR ?= ../..
L4DIR ?= $(PKGDIR)/../../..
TARGET := libio-io.a libio-io.so
SRC_CC := io.cc
CONTRIB_INCDIR = libio-io
REQUIRES_LIBS := libio-vbus
PC_FILENAME = libio-io
include $(L4DIR)/mk/lib.mk
CXXFLAGS += -fvisibility=hidden
CFLAGS += -fvisibility=hidden

View File

@@ -0,0 +1,360 @@
/*
* (c) 2008-2010 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
* Alexander Warg <warg@os.inf.tu-dresden.de>,
* Torsten Frenzel <frenzel@os.inf.tu-dresden.de>,
* Henning Schild <hschild@os.inf.tu-dresden.de>
* economic rights: Technische Universität Dresden (Germany)
* License: see LICENSE.spdx (in this directory or the directories above)
*/
#include <l4/io/io.h>
#include <l4/vbus/vbus.h>
#include <l4/io/types.h>
#include <l4/re/namespace>
#include <l4/re/rm>
#include <l4/re/env>
#include <l4/re/error_helper>
#include <l4/re/util/cap_alloc>
#include <l4/sys/factory>
#include <l4/sys/icu>
#include <l4/sys/irq>
#include <l4/sys/task>
#include <l4/sys/kdebug.h>
#include <l4/util/splitlog2.h>
#include <l4/crtn/initpriorities.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using L4::Cap;
namespace {
struct Internals
{
Cap<void> _vbus;
Cap<L4::Icu> _icu;
Internals()
: _vbus(Cap<void>::No_init), _icu(Cap<void>::No_init)
{
_vbus = L4Re::Env::env()->get_cap<void>("vbus");
if (!_vbus)
{
printf("libio: Warning: Query of 'vbus' failed!\n");
return;
}
l4vbus_device_handle_t handle = L4VBUS_NULL;
int ret = l4vbus_get_device_by_hid(_vbus.cap(), L4VBUS_ROOT_BUS, &handle,
"L40009", L4VBUS_MAX_DEPTH, 0);
if (ret)
{
printf("libio: Warning: Finding 'icu' in system bus failed with '%s'\n",
l4sys_errtostr(ret));
return;
}
_icu = L4Re::Util::cap_alloc.alloc<L4::Icu>();
if (!_icu)
{
printf("libio: cannot allocate ICU cap\n");
return;
}
ret = l4vbus_vicu_get_cap(_vbus.cap(), handle, _icu.cap());
if (ret)
{
printf("libio: Warning: Getting 'icu' device failed.\n");
L4Re::Util::cap_alloc.free(_icu);
_icu.invalidate();
}
}
};
static Internals _internal __attribute__((init_priority(INIT_PRIO_LIBIO_INIT)));
static Cap<void> &vbus()
{
return _internal._vbus;
}
static Cap<L4::Icu> &icu()
{
return _internal._icu;
}
}
/***********************************************************************
* IRQ
***********************************************************************/
long
l4io_request_irq(int irqnum, l4_cap_idx_t irq_cap)
{
L4::Cap<L4::Irq> irq(irq_cap);
long ret = l4_error(L4Re::Env::env()->factory()->create(irq));
if (ret < 0) {
printf("Create irq failed with %ld\n", ret);
return ret;
}
ret = l4_error(icu()->bind(irqnum, irq));
if (ret < 0) {
printf("Bind irq to icu failed with %ld\n", ret);
return ret;
}
return 0;
}
l4_cap_idx_t
l4io_request_icu()
{ return icu().cap(); }
long
l4io_release_irq(int irqnum, l4_cap_idx_t irq_cap)
{
long ret = l4_error(icu()->unbind(irqnum, L4::Cap<L4::Irq>(irq_cap)));
if (ret) {
printf("Unbind irq %d from icu failed with %ld\n", irqnum, ret);
return -1;
}
l4_task_unmap(L4_BASE_TASK_CAP,
l4_obj_fpage(irq_cap, 0, L4_CAP_FPAGE_RWS),
L4_FP_ALL_SPACES);
return 0;
}
/***********************************************************************
* I/O memory
***********************************************************************/
static long
__map_iomem(l4_addr_t phys, l4_addr_t* virt, unsigned long size, int flags)
{
Cap<L4Re::Dataspace> iomem = L4::cap_cast<L4Re::Dataspace>(vbus());
unsigned char align = L4_PAGESHIFT;
l4_addr_t offset = phys & ~L4_PAGEMASK;
if (!iomem.is_valid())
return -L4_ENOENT;
if (size >= L4_SUPERPAGESIZE)
align = L4_SUPERPAGESHIFT;
L4Re::Rm::Flags rmflags = L4Re::Rm::F::RW;
if (flags & L4IO_MEM_EAGER_MAP)
rmflags |= L4Re::Rm::F::Eager_map;
switch (flags & L4IO_MEM_ATTR_MASK)
{
case L4IO_MEM_NONCACHED:
rmflags |= L4Re::Rm::F::Cache_uncached;
break;
case L4IO_MEM_WRITE_COMBINED:
rmflags |= L4Re::Rm::F::Cache_buffered;
break;
case L4IO_MEM_CACHED:
rmflags |= L4Re::Rm::F::Cache_normal;
break;
}
if (*virt && (flags & L4IO_MEM_USE_RESERVED_AREA))
rmflags |= L4Re::Rm::F::In_area;
if (!*virt)
rmflags |= L4Re::Rm::F::Search_addr;
else
{
/* Check for reasonable caller behavior:
* Offsets into a page should be the same. */
if ((*virt & ~L4_PAGEMASK) != offset)
return -L4_EINVAL;
}
long r = L4Re::Env::env()->rm()->attach(virt, size, rmflags,
L4::Ipc::make_cap_rw(iomem),
phys, align);
*virt += offset;
return r;
}
long
l4io_request_iomem(l4_addr_t phys, unsigned long size,
int flags, l4_addr_t *virt)
{
*virt = 0;
return __map_iomem(phys, virt, size, flags);
}
long
l4io_request_iomem_region(l4_addr_t phys, l4_addr_t virt, unsigned long size,
int flags)
{
if (!virt)
return -L4_EADDRNOTAVAIL;
return __map_iomem(phys, &virt, size, flags);
}
long
l4io_release_iomem(l4_addr_t virt, unsigned long /* size */)
{
return L4Re::Env::env()->rm()->detach(virt, 0);
}
/***********************************************************************
* I/O ports
***********************************************************************/
long
l4io_request_ioport(unsigned portnum, unsigned len)
{
l4vbus_resource_t res;
res.type = L4IO_RESOURCE_PORT;
res.start = portnum;
res.end = portnum + len - 1;
return l4vbus_request_ioport(vbus().cap(), &res);
}
long
l4io_release_ioport(unsigned portnum, unsigned len)
{
l4vbus_resource_t res;
res.type = L4IO_RESOURCE_PORT;
res.start = portnum;
res.end = portnum + len - 1;
return l4vbus_release_ioport(vbus().cap(), &res);
}
/***********************************************************************
* General
***********************************************************************/
int
l4io_iterate_devices(l4io_device_handle_t *devhandle,
l4io_device_t *dev, l4io_resource_handle_t *reshandle)
{
if (!vbus().is_valid())
return -L4_ENOENT;
if (reshandle)
*reshandle = 0;
return l4vbus_get_next_device(vbus().cap(), L4VBUS_ROOT_BUS,
devhandle, L4VBUS_MAX_DEPTH, dev);
}
int
l4io_lookup_device(const char *devname,
l4io_device_handle_t *dev_handle, l4io_device_t *dev,
l4io_resource_handle_t *res_handle)
{
int r;
l4io_device_handle_t dh = L4VBUS_NULL;
if (!vbus().is_valid())
return -L4_ENOENT;
if ((r = l4vbus_get_device_by_hid(vbus().cap(), L4VBUS_ROOT_BUS,
&dh, devname, L4VBUS_MAX_DEPTH, dev)))
return r;
if (dev_handle)
*dev_handle = dh;
if (res_handle)
*res_handle = 0;
return -L4_EOK;
}
int
l4io_lookup_resource(l4io_device_handle_t devhandle,
enum l4io_resource_types_t type,
l4io_resource_handle_t *res_handle,
l4io_resource_t *desc)
{
l4vbus_resource_t resource;
while (!l4vbus_get_resource(vbus().cap(), devhandle, *res_handle, &resource))
{
++(*res_handle);
// copy device description
if (resource.type == type || type == L4IO_RESOURCE_ANY)
{
*desc = resource;
return -L4_EOK;
}
}
return -L4_ENOENT;
}
l4_addr_t
l4io_request_resource_iomem(l4io_device_handle_t devhandle,
l4io_resource_handle_t *reshandle)
{
l4io_resource_t res;
l4_addr_t v;
if (l4io_lookup_resource(devhandle, L4IO_RESOURCE_MEM,
reshandle, &res))
return 0;
v = 0;
if (l4io_request_iomem(res.start, res.end - res.start + 1,
L4IO_MEM_NONCACHED, &v))
return 0;
return v;
}
void
l4io_request_all_ioports(void (*res_cb)(l4vbus_resource_t const *res))
{
l4vbus_device_handle_t next_dev = L4VBUS_NULL;
l4vbus_device_t info;
while (!l4vbus_get_next_device(vbus().cap(), l4io_get_root_device(),
&next_dev, L4VBUS_MAX_DEPTH, &info))
{
l4vbus_resource_t resource;
for (unsigned r = 0; r < info.num_resources; ++r)
{
l4vbus_get_resource(vbus().cap(), next_dev, r, &resource);
if (resource.type == L4IO_RESOURCE_PORT)
{
l4vbus_request_ioport(vbus().cap(), &resource);
if (res_cb)
res_cb(&resource);
}
}
}
}
int
l4io_has_resource(enum l4io_resource_types_t type,
l4vbus_paddr_t start, l4vbus_paddr_t end)
{
l4io_device_handle_t dh = L4VBUS_NULL;
l4io_device_t dev;
l4io_resource_handle_t reshandle;
while (1)
{
l4io_resource_t res;
if (l4io_iterate_devices(&dh, &dev, &reshandle))
break;
if (dev.num_resources)
while (!l4io_lookup_resource(dh, type, &reshandle, &res))
if (start >= res.start && end <= res.end)
return 1;
}
return 0;
}