dde_linux: introduce new lx_emul/lx_kit library

The re-newed approach currently supports ARM 64-bit only.
It depends on the Platform API of the ARM architecture.
It tries to meet the original semantic of the Linux kernel
functions as far as possible. To achieve this, device drivers
using this library should reference the original Linux kernel
headers at foremost. Only the headers in `src/include/lx_emul/shadow`
have to shadow clone the original ones.

Fix #4225
This commit is contained in:
Stefan Kalkowski 2021-07-19 16:21:43 +02:00 committed by Christian Helmuth
parent 1aba330ae6
commit 1a526e73a3
85 changed files with 5404 additions and 0 deletions

View File

@ -0,0 +1,34 @@
/*
* \brief Lx_emul support to allocate memory
* \author Stefan Kalkowski
* \date 2021-03-25
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _LX_EMUL__ALLOC_H_
#define _LX_EMUL__ALLOC_H_
#ifdef __cplusplus
extern "C" {
#endif
void * lx_emul_mem_alloc(unsigned long size);
void * lx_emul_mem_alloc_uncached(unsigned long size);
void * lx_emul_mem_alloc_aligned(unsigned long size, unsigned long align);
unsigned long lx_emul_mem_dma_addr(void * addr);
void lx_emul_mem_free(const void * ptr);
unsigned long lx_emul_mem_size(const void * ptr);
void lx_emul_mem_cache_clean_invalidate(const void * ptr, unsigned long size);
void lx_emul_mem_cache_invalidate(const void * ptr, unsigned long size);
#ifdef __cplusplus
}
#endif
#endif /* _LX_EMUL__ALLOC_H_ */

View File

@ -0,0 +1,33 @@
/*
* \brief Lx_emul support for device clocks
* \author Stefan Kalkowski
* \date 2021-04-14
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _LX_EMUL__CLOCK_H_
#define _LX_EMUL__CLOCK_H_
#ifdef __cplusplus
extern "C" {
#endif
struct device_node;
struct clk;
struct clk * lx_emul_clock_get(const struct device_node * node,
const char * name);
unsigned long lx_emul_clock_get_rate(struct clk * clk);
#ifdef __cplusplus
}
#endif
#endif /* _LX_EMUL__CLOCK_H_ */

View File

@ -0,0 +1,29 @@
/*
* \brief Lx_emul support to debug Linux kernel ports
* \author Stefan Kalkowski
* \date 2021-03-16
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _LX_EMUL__DEBUG_H_
#define _LX_EMUL__DEBUG_H_
#ifdef __cplusplus
extern "C" {
#endif
__attribute__((noreturn)) void lx_emul_trace_and_stop(const char * func);
void lx_emul_trace(const char * func);
#ifdef __cplusplus
}
#endif
#endif /* _LX_EMUL__DEBUG_H_ */

View File

@ -0,0 +1,35 @@
/*
* \brief Lx_emul support to register Linux kernel initialization
* \author Stefan Kalkowski
* \date 2021-03-10
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _LX_EMUL__INIT_H_
#define _LX_EMUL__INIT_H_
#ifdef __cplusplus
extern "C" {
#endif
void lx_emul_initcalls(void);
void lx_emul_register_initcall(int (*initcall)(void), const char * name);
void lx_emul_start_kernel(void * dtb);
int lx_emul_init_task_function(void * dtb);
extern void * lx_emul_init_task_struct;
#ifdef __cplusplus
}
#endif
#endif /* _LX_EMUL__INIT_H_ */

View File

@ -0,0 +1,27 @@
/*
* \brief Lx_emul support for I/O memory
* \author Stefan Kalkowski
* \date 2021-04-17
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _LX_EMUL__IO_MEM_H_
#define _LX_EMUL__IO_MEM_H_
#ifdef __cplusplus
extern "C" {
#endif
void * lx_emul_io_mem_map(unsigned long phys_addr, unsigned long size);
#ifdef __cplusplus
}
#endif
#endif /* _LX_EMUL__IO_MEM_H_ */

View File

@ -0,0 +1,39 @@
/*
* \brief Lx_emul support for interrupts
* \author Stefan Kalkowski
* \date 2021-04-14
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _LX_EMUL__IRQ_H_
#define _LX_EMUL__IRQ_H_
#ifdef __cplusplus
extern "C" {
#endif
struct irq_desc;
void lx_emul_irq_unmask(unsigned int irq);
void lx_emul_irq_mask(unsigned int irq);
void lx_emul_irq_eoi(unsigned int irq);
int lx_emul_irq_task_function(void * data);
extern void * lx_emul_irq_task_struct;
unsigned int lx_emul_irq_last(void);
#ifdef __cplusplus
}
#endif
#endif /* _LX_EMUL__IRQ_H_ */

View File

@ -0,0 +1,27 @@
/*
* \brief Lx_emul support to log messages from the kernel code
* \author Stefan Kalkowski
* \date 2021-03-24
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _LX_EMUL__LOG_H_
#define _LX_EMUL__LOG_H_
#ifdef __cplusplus
extern "C" {
#endif
void lx_emul_vprintf(char const *, va_list);
#ifdef __cplusplus
}
#endif
#endif /* _LX_EMUL__LOG_H_ */

View File

@ -0,0 +1,59 @@
/*
* \brief Lx_emul support for page-struct management
* \author Norman Feske
* \date 2021-07-01
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _LX_EMUL__PAGE_VIRT_H_
#define _LX_EMUL__PAGE_VIRT_H_
#ifdef __cplusplus
extern "C" {
#endif
struct page;
/*
* Accessors for the associative data structure implemented in page_virt.cc
*/
void lx_emul_associate_page_with_virt_addr(struct page *, void const *virt);
void lx_emul_disassociate_page_from_virt_addr(void const *virt);
struct page *lx_emul_associated_page(void const *virt, unsigned long size);
/**
* Return page struct for the page at a given virtual address
*
* If no page struct exists for the virtual address, it is created.
*/
struct page *lx_emul_virt_to_pages(void const *virt, unsigned num);
/**
* Release page structs for specified virtual-address range
*
* \param size size of range in bytes
*/
void lx_emul_forget_pages(void const *virt, unsigned long size);
/**
* Perform unit test
*/
void lx_emul_associate_page_selftest(void);
#ifdef __cplusplus
}
#endif
#endif /* _LX_EMUL__PAGE_VIRT_H_ */

View File

@ -0,0 +1,22 @@
/*
* \brief Shadows Linux kernel asm/bug.h
* \author Stefan Kalkowski
* \date 2021-04-14
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _LX_EMUL__SHADOW__ASM__BUG_H_
#define _LX_EMUL__SHADOW__ASM__BUG_H_
#include_next <asm/bug.h>
#undef __WARN_FLAGS
#define __WARN_FLAGS(flags)
#endif /* _LX_EMUL__SHADOW__ASM__BUG_H_ */

View File

@ -0,0 +1,25 @@
/*
* \brief Shadows Linux kernel asm/current.h
* \author Stefan Kalkowski
* \date 2021-04-14
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _LX_EMUL__SHADOW__ASM__CURRENT_H_
#define _LX_EMUL__SHADOW__ASM__CURRENT_H_
#include <lx_emul/task.h>
#ifndef __ASSEMBLY__
#define current lx_emul_task_get_current()
#endif /* __ASSEMBLY__ */
#endif /* _LX_EMUL__SHADOW__ASM__CURRENT_H_ */

View File

@ -0,0 +1,44 @@
/*
* \brief Shadows Linux kernel asm/irqflags.h
* \author Stefan Kalkowski
* \date 2021-04-14
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef __ASM_IRQFLAGS_H
#define __ASM_IRQFLAGS_H
static inline void arch_local_irq_enable(void)
{
}
static inline void arch_local_irq_disable(void)
{
}
static inline unsigned long arch_local_save_flags(void)
{
return 1;
}
static inline int arch_irqs_disabled_flags(unsigned long flags)
{
return flags;
}
static inline unsigned long arch_local_irq_save(void)
{
return 1;
}
static inline void arch_local_irq_restore(unsigned long flags)
{
}
#endif

View File

@ -0,0 +1,64 @@
/*
* \brief Shadows Linux kernel arch/.../asm/memory.h
* \author Norman Feske
* \date 2021-06-25
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef __ASM_MEMORY_H
#define __ASM_MEMORY_H
#ifndef __ASSEMBLY__
#include <asm/page-def.h>
#include <linux/sizes.h>
#include <lx_emul/debug.h>
#include <lx_emul/alloc.h>
#include <lx_emul/page_virt.h>
#define PCI_IO_SIZE SZ_16M
extern u64 vabits_actual;
#define __tag_reset(addr) (addr)
#define untagged_addr(addr) (addr)
#define VA_BITS (CONFIG_ARM64_VA_BITS)
#define VA_BITS_MIN (VA_BITS)
#define PAGE_OFFSET (0)
#define THREAD_SHIFT PAGE_SHIFT
#define THREAD_SIZE (UL(1) << THREAD_SHIFT)
#define BPF_JIT_REGION_START 0
#define BPF_JIT_REGION_END 0
#define MT_NORMAL 0
#define MT_NORMAL_TAGGED 1
#define MT_NORMAL_NC 2
#define MT_DEVICE_nGnRnE 4
#define MT_DEVICE_nGnRE 5
#define __va(x) ( lx_emul_trace_and_stop("__va"), (void *)0 )
#define __pa(v) lx_emul_mem_dma_addr((void *)(v))
#define page_to_phys(p) __pa((p)->virtual)
#define page_to_virt(p) ((p)->virtual)
static inline struct page *virt_to_page(void const *v) { return lx_emul_virt_to_pages(v, 1U); }
#define pfn_to_page(pfn) ( (struct page *)(__va(pfn << PAGE_SHIFT)) )
#define page_to_pfn(page) ( page_to_phys(page) >> PAGE_SHIFT )
#define PCI_IO_START 0
#endif /* __ASSEMBLY__ */
#endif /* __ASM_MEMORY_H */

View File

@ -0,0 +1,47 @@
/*
* \brief Shadows Linux kernel asm/page.h
* \author Norman Feske
* \date 2021-06-25
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef __ASM_GENERIC_PAGE_H
#define __ASM_GENERIC_PAGE_H
#ifndef __ASSEMBLY__
#include <asm/page-def.h> /* for PAGE_SHIFT */
#include <asm/pgtable-types.h>
/*
* The 'virtual' member of 'struct page' is needed by 'lx_emul_virt_to_phys'
* and 'page_to_virt'.
*/
#define WANT_PAGE_VIRTUAL
#define clear_page(page) memset((page), 0, PAGE_SIZE)
#define copy_page(to,from) memcpy((to), (from), PAGE_SIZE)
#define clear_user_page(page, vaddr, pg) clear_page(page)
#define copy_user_page(to, from, vaddr, pg) copy_page(to, from)
struct page;
typedef struct page *pgtable_t;
/* needed by mm/internal.h */
#define pfn_valid(pfn) (pfn != 0UL)
#define virt_addr_valid(kaddr) (kaddr != 0UL)
#endif /* __ASSEMBLY__ */
#include <asm-generic/getorder.h>
#endif /* __ASM_GENERIC_PAGE_H */

View File

@ -0,0 +1,27 @@
/*
* \brief Shadows Linux kernel asm/percpu.h
* \author Stefan Kalkowski
* \date 2021-04-14
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _LX_EMUL__SHADOW__ASM__PERCPU_H_
#define _LX_EMUL__SHADOW__ASM__PERCPU_H_
#include_next <asm/percpu.h>
static inline unsigned long __dummy_cpu_offset(void)
{
return 0;
}
#undef __my_cpu_offset
#define __my_cpu_offset __dummy_cpu_offset()
#endif /* _LX_EMUL__SHADOW__ASM__PERCPU_H_ */

View File

@ -0,0 +1,81 @@
/*
* \brief Shadows Linux kernel arch/.../asm/pgtable.h
* \author Norman Feske
* \date 2021-06-25
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef __ASM_PGTABLE_H
#define __ASM_PGTABLE_H
#ifndef __ASSEMBLY__
#include <asm/page.h>
#include <asm/pgtable-types.h>
#include <asm/pgtable-hwdef.h>
#include <asm/pgtable-prot.h>
#include <asm-generic/pgtable_uffd.h>
#include <linux/mm_types.h>
#include <lx_emul/debug.h>
pte_t pte_mkwrite(pte_t pte);
pte_t pte_get(pte_t pte);
pte_t pte_wrprotect(pte_t pte);
pte_t pte_modify(pte_t pte, pgprot_t prot);
pte_t pte_mkdirty(pte_t pte);
struct mm_struct;
bool mm_pmd_folded(struct mm_struct *mm); /* needed by linux/mm.h */
int pud_none(pud_t pud);
struct page *pmd_page(pmd_t pmd);
#define phys_to_ttbr(addr) (addr)
int pte_none(pte_t pte);
int pte_present(pte_t pte);
int pte_swp_soft_dirty(pte_t pte);
int pte_dirty(pte_t ptr);
int pte_write(pte_t ptr);
extern pgd_t reserved_pg_dir[PTRS_PER_PGD];
extern pgd_t swapper_pg_dir[PTRS_PER_PGD];
#define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) })
swp_entry_t __pmd_to_swp_entry(pmd_t pmd);
#define __swp_type(x) ( lx_emul_trace_and_stop(__func__), 0 )
#define __swp_offset(x) ( lx_emul_trace_and_stop(__func__), 0 )
#define __swp_entry(type, offset) ( lx_emul_trace_and_stop(__func__), (swp_entry_t) { 0 } )
#define __swp_entry_to_pte(swp) ((pte_t) { (swp).val })
pmd_t __swp_entry_to_pmd(swp_entry_t swp);
int pmd_none(pmd_t pmd);
int pmd_present(pmd_t pmd);
int pmd_trans_huge(pmd_t pmd);
int pmd_devmap(pmd_t pmd);
int pud_devmap(pud_t pud);
int pud_trans_huge(pud_t pud);
pgprot_t pgprot_writecombine(pgprot_t prot);
pte_t mk_pte(struct page * page, pgprot_t prot);
#define HPAGE_SHIFT PMD_SHIFT
#define HUGETLB_PAGE_ORDER (HPAGE_SHIFT - PAGE_SHIFT)
#endif /* __ASSEMBLY__ */
#endif /* __ASM_PGTABLE_H */

View File

@ -0,0 +1,28 @@
/*
* \brief Shadow copy of linux/compiler-gcc.h
* \author Stefan Kalkowski
* \date 2021-03-17
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _LX_EMUL__SHADOW__LINUX__COMPILER_GCC_H_
#define _LX_EMUL__SHADOW__LINUX__COMPILER_GCC_H_
#include_next <linux/compiler-gcc.h>
/**
* We have to re-define `asm_volatile_goto`, because the original function
* uses `asm goto(...)`, which is a problem when building PIC code.
*/
#ifdef asm_volatile_goto
#undef asm_volatile_goto
#define asm_volatile_goto(x...) asm volatile("invalid use of asm_volatile_goto")
#endif
#endif /* _LX_EMUL__SHADOW__LINUX__COMPILER_GCC_H_ */

View File

@ -0,0 +1,35 @@
/*
* \brief Shadow copy of linux/init.h
* \author Stefan Kalkowski
* \date 2021-03-10
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _LX_EMUL__SHADOW__LINUX__INIT_H_
#define _LX_EMUL__SHADOW__LINUX__INIT_H_
#include_next <linux/init.h>
#include <lx_emul/init.h>
/**
* We have to re-define certain initcall macros, because the original function
* puts all initcalls into the .init section that is not exported by our
* linker script.
* Instead, we define ctor functions that register the initcalls and their
* priority in our lx_emul environment.
*/
#undef ___define_initcall
#undef __define_initcall
#define __define_initcall(fn, id) \
static void __initcall_##fn##id(void)__attribute__((constructor)); \
static void __initcall_##fn##id() { \
lx_emul_register_initcall(fn, __func__); };
#endif /* _LX_EMUL__SHADOW__LINUX__INIT_H_ */

View File

@ -0,0 +1,33 @@
/*
* \brief Shadows Linux kernel linux/pgtable.h
* \author Norman Feske
* \date 2021-06-25
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef __LINUX_PGTABLE_H
#define __LINUX_PGTABLE_H
#include <linux/init.h>
#include <asm/pgtable.h>
pmd_t *pmd_offset(pud_t *pud, unsigned long address);
pmd_t pmd_swp_clear_soft_dirty(pmd_t pmd);
int pmd_swp_soft_dirty(pmd_t pmd);
void __init pgtable_cache_init(void);
#define pgprot_decrypted(prot) (prot)
pte_t pte_swp_clear_uffd_wp(pte_t pte);
pte_t pte_swp_clear_soft_dirty(pte_t pte);
pte_t ptep_get(pte_t *ptep);
#endif /* __LINUX_PGTABLE_H */

View File

@ -0,0 +1,47 @@
/*
* \brief Lx_emul support to task handling
* \author Stefan Kalkowski
* \date 2021-03-25
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _LX_EMUL__TASK_H_
#define _LX_EMUL__TASK_H_
#ifdef __cplusplus
extern "C" {
#endif
enum { SWAPPER_PID, KIRQ_PID, FIRST_PID };
struct task_struct;
struct task_struct * lx_emul_task_get_current(void);
struct task_struct * lx_emul_task_get(int pid);
void lx_emul_task_create(struct task_struct * task,
const char * name,
int pid,
int (* threadfn)(void * data),
void * data);
void lx_emul_task_unblock(struct task_struct * task);
void lx_emul_task_priority(struct task_struct * task, unsigned long prio);
void lx_emul_task_schedule(int block);
void lx_emul_task_name(struct task_struct * task, const char * name);
#ifdef __cplusplus
}
#endif
#endif /* _LX_EMUL__TASK_H_ */

View File

@ -0,0 +1,35 @@
/*
* \brief Lx_emul support for time
* \author Stefan Kalkowski
* \date 2021-04-30
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _LX_EMUL__TIME_H_
#define _LX_EMUL__TIME_H_
#ifdef __cplusplus
extern "C" {
#endif
void lx_emul_time_init(void);
void lx_emul_time_event(unsigned long evt);
void lx_emul_time_stop(void);
unsigned long lx_emul_time_counter(void);
void lx_emul_time_handle(void);
#ifdef __cplusplus
}
#endif
#endif /* _LX_EMUL__TIME_H_ */

View File

@ -0,0 +1,44 @@
/*
* \brief Lx_kit byte-range utility
* \author Norman Feske
* \date 2021-07-02
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _LX_KIT__BYTE_RANGE_H_
#define _LX_KIT__BYTE_RANGE_H_
#include <base/stdint.h>
namespace Lx_kit {
using namespace Genode;
struct Byte_range;
}
struct Lx_kit::Byte_range
{
addr_t start;
size_t size;
bool intersects(Byte_range const &other) const
{
if (start > other.start + other.size - 1)
return false;
if (start + size - 1 < other.start)
return false;
return true;
}
};
#endif /* _LX_KIT__BYTE_RANGE_H_ */

View File

@ -0,0 +1,132 @@
/*
* \brief Lx_kit format string backend
* \author Stefan Kalkowski
* \author Sebastian Sumpf
* \date 2021-03-17
*
* Greatly inspired by the former DDE Linux Lx_kit implementation.
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _LX_KIT__CONSOLE_H_
#define _LX_KIT__CONSOLE_H_
#include <stdarg.h>
namespace Lx_kit {
class Console;
using namespace Genode;
}
class Lx_kit::Console
{
private:
enum { BUF_SIZE = 216 };
char _buf[BUF_SIZE + 1];
unsigned _idx = 0;
/**
* Convert digit to ASCII value
*/
static inline char _ascii(int digit, int uppercase = 0)
{
if (digit > 9)
return digit + (uppercase ? 'A' : 'a') - 10;
return digit + '0';
}
/**
* Output signed value with the specified base
*/
template <typename T>
void _out_signed(T value, unsigned base)
{
/**
* for base 8, the number of digits is the number of value bytes times 3
* at a max, because 0xff is 0o377 and accumulating this implies a
* strictly decreasing factor
*/
char buf[sizeof(value)*3];
/* set flag if value is negative */
int neg = value < 0 ? 1 : 0;
/* get absolute value */
value = value < 0 ? -value : value;
int i = 0;
/* handle zero as special case */
if (value == 0)
buf[i++] = _ascii(0);
/* fill buffer starting with the least significant digits */
else
for (; value > 0; value /= base)
buf[i++] = _ascii(value % base);
/* add sign to buffer for negative values */
if (neg)
_out_char('-');
/* output buffer in reverse order */
for (; i--; )
_out_char(buf[i]);
}
/**
* Output unsigned value with the specified base and padding
*/
template <typename T>
void _out_unsigned(T value, unsigned base, int pad)
{
/**
* for base 8, the number of digits is the number of value bytes times 3
* at a max, because 0xff is 0o377 and accumulating this implies a
* strictly decreasing factor
*/
char buf[sizeof(value)*3];
int i = 0;
/* handle zero as special case */
if (value == 0) {
buf[i++] = _ascii(0);
pad--;
}
/* fill buffer starting with the least significant digits */
for (; value > 0; value /= base, pad--)
buf[i++] = _ascii(value % base);
/* add padding zeros */
for (; pad-- > 0; )
_out_char(_ascii(0));
/* output buffer in reverse order */
for (; i--; )
_out_char(buf[i]);
}
void _out_char(char c);
void _out_string(const char *str);
void _flush();
public:
void vprintf(const char *format, va_list list);
};
#endif /* _LX_KIT__CONSOLE_H_ */

View File

@ -0,0 +1,162 @@
/*
* \brief Globally available Lx_kit environment, needed in the C-ish lx_emul
* \author Stefan Kalkowski
* \date 2021-04-14
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _LX_KIT__DEVICE_H_
#define _LX_KIT__DEVICE_H_
#include <base/env.h>
#include <base/heap.h>
#include <irq_session/client.h>
#include <io_mem_session/client.h>
#include <platform_session/device.h>
#include <util/list.h>
#include <util/xml_node.h>
namespace Lx_kit {
using namespace Genode;
class Device;
class Device_list;
}
struct clk {
unsigned long rate;
};
class Lx_kit::Device : List<Device>::Element
{
private:
friend class Device_list;
friend class List<Device>;
using Name = String<64>;
using Type = Platform::Device::Type;
struct Io_mem : List<Io_mem>::Element
{
using Index = Platform::Device::Mmio::Index;
Index idx;
addr_t addr;
size_t size;
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) {}
};
struct Irq_handler
{
private:
Irq_handler(Irq_handler const &);
Irq_handler &operator = (Irq_handler const &);
Platform::Device::Irq _irq;
Io_signal_handler<Irq_handler> _handler;
unsigned _number;
void _handle();
public:
Irq_handler(Platform::Device & dev,
Platform::Device::Irq::Index idx,
unsigned number);
void ack() { _irq.ack(); }
};
struct Irq : List<Irq>::Element
{
using Index = Platform::Device::Irq::Index;
Index idx;
unsigned number;
Constructible<Irq_handler> handler {};
Irq(unsigned idx, unsigned number)
: idx{idx}, number(number) {}
};
struct Clock : List<Clock>::Element
{
unsigned idx;
Name const name;
clk lx_clock;
Clock(unsigned idx, Name const name)
: idx(idx), name(name), lx_clock{0} {}
};
Device(Platform::Connection & plat,
Xml_node & xml,
Heap & heap);
Platform::Connection & _platform;
Name const _name;
Type const _type;
List<Io_mem> _io_mems {};
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_irq(FN const & fn) {
for (Irq * i = _irqs.first(); i; i = i->next()) fn(*i); }
template <typename FN>
void _for_each_clock(FN const & fn) {
for (Clock * c = _clocks.first(); c; c = c->next()) fn(*c); }
public:
const char * compatible();
const char * name();
void enable();
clk * clock(const char * name);
clk * clock(unsigned idx);
bool io_mem(addr_t phys_addr, size_t size);
void * io_mem_local_addr(addr_t phys_addr, size_t size);
bool irq_unmask(unsigned irq);
void irq_mask(unsigned irq);
void irq_ack(unsigned irq);
};
class Lx_kit::Device_list : List<Device>
{
private:
Platform::Connection & _platform;
public:
template <typename FN>
void for_each(FN const & fn) {
for (Device * d = first(); d; d = d->next()) fn(*d); }
Device_list(Heap & heap, Platform::Connection & platform);
};
#endif /* _LX_KIT__DEVICE_H_ */

View File

@ -0,0 +1,58 @@
/*
* \brief Globally available Lx_kit environment, needed in the C-ish lx_emul
* \author Stefan Kalkowski
* \date 2021-03-17
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _LX_KIT__ENV_H_
#define _LX_KIT__ENV_H_
#include <base/env.h>
#include <platform_session/connection.h>
#include <timer_session/connection.h>
#include <lx_kit/console.h>
#include <lx_kit/device.h>
#include <lx_kit/init.h>
#include <lx_kit/memory.h>
#include <lx_kit/scheduler.h>
#include <lx_kit/timeout.h>
namespace Lx_kit {
struct Env;
/**
* Returns the global Env object available
*
* \param env - pointer to Genode::Env used to construct object initially
*/
Env & env(Genode::Env * env = nullptr);
}
struct Lx_kit::Env
{
Genode::Env & env;
Genode::Heap heap { env.ram(), env.rm() };
Initcalls initcalls { heap };
Console console { };
Platform::Connection platform { env };
Timer::Connection timer { env };
Mem_allocator memory { env, heap, platform, CACHED };
Mem_allocator uncached_memory { env, heap, platform, UNCACHED };
Scheduler scheduler { };
Device_list devices { heap, platform };
Lx_kit::Timeout timeout { timer, scheduler };
unsigned int last_irq { 0 };
Env(Genode::Env & env) : env(env) { }
};
#endif /* _LX_KIT__ENV_H_ */

View File

@ -0,0 +1,51 @@
/*
* \brief Lx_kit backend for Linux kernel initialization
* \author Stefan Kalkowski
* \date 2021-03-10
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _LX_KIT__INIT_H_
#define _LX_KIT__INIT_H_
#include <base/env.h>
#include <base/heap.h>
namespace Lx_kit {
using namespace Genode;
void initialize(Env & env);
class Initcalls;
}
class Lx_kit::Initcalls
{
private:
struct E : List<E>::Element
{
unsigned int prio;
int (* call) (void);
E(unsigned int p, int (*fn)(void)) : prio(p), call(fn) {}
};
Heap & _heap;
List<E> _call_list {};
public:
void add(int (*initcall)(void), unsigned int prio);
void execute_in_order();
Initcalls(Heap & heap) : _heap(heap) {}
};
#endif /* _LX_KIT__INIT_H_ */

View File

@ -0,0 +1,103 @@
/*
* \brief Lx_kit associative data structure
* \author Norman Feske
* \date 2021-07-02
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _LX_KIT__MAP_H_
#define _LX_KIT__MAP_H_
#include <base/stdint.h>
namespace Lx_kit {
using namespace Genode;
template <typename ITEM>
struct Map;
}
template <typename ITEM>
class Lx_kit::Map : Noncopyable
{
private:
struct Item : Avl_node<Item>
{
ITEM const value;
template <typename QUERY>
static Item *_lookup(Item *curr, QUERY const &query)
{
for (;;) {
if (!curr)
return nullptr;
ITEM const &value = curr->value;
if (query.matches(value))
return curr;
curr = curr->Avl_node<Item>::child(value.higher(query.key()));
}
}
/**
* Avl_node interface
*/
bool higher(Item *other) const { return value.higher(other->value.key); }
template <typename QUERY>
Item *lookup(QUERY const &query) { return _lookup(this, query); }
template <typename... ARGS>
Item(ARGS &&... args) : value{args...} { }
};
Avl_tree<Item> _items { };
Allocator &_alloc;
template <typename QUERY>
Item *_lookup(QUERY const &query)
{
return _items.first() ? _items.first()->lookup(query) : nullptr;
}
public:
Map(Allocator &alloc) : _alloc(alloc) { }
template <typename... ARGS>
void insert(ARGS &&... args)
{
_items.insert(new (_alloc) Item(args...));
}
template <typename QUERY>
void remove(QUERY const &query)
{
while (Item *item_ptr = _lookup(query)) {
_items.remove(item_ptr);
destroy(_alloc, item_ptr);
}
}
template <typename QUERY, typename FN>
void apply(QUERY const &query, FN const &fn)
{
Item *item_ptr = _lookup(query);
if (item_ptr)
fn(item_ptr->value);
}
};
#endif /* _LX_KIT__MAP_H_ */

View File

@ -0,0 +1,77 @@
/*
* \brief Lx_kit memory allocation backend
* \author Stefan Kalkowski
* \date 2021-03-25
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _LX_KIT__MEMORY_H_
#define _LX_KIT__MEMORY_H_
#include <base/allocator_avl.h>
#include <base/cache.h>
#include <base/env.h>
#include <base/heap.h>
#include <base/registry.h>
namespace Platform { class Connection; };
namespace Lx_kit {
using namespace Genode;
class Mem_allocator;
}
class Lx_kit::Mem_allocator
{
private:
class Buffer : Interface
{
private:
Attached_dataspace _ds;
addr_t const _dma_addr;
public:
Buffer(Region_map & rm,
Dataspace_capability cap,
addr_t dma_addr)
: _ds(rm, cap), _dma_addr(dma_addr) {}
addr_t dma_addr() const { return _dma_addr; }
Attached_dataspace & ds() { return _ds; }
};
using Buffer_registry = Registry<Registered<Buffer>>;
Env & _env;
Heap & _heap;
Platform::Connection & _platform;
Cache _cache_attr;
Allocator_avl _mem;
Buffer_registry _buffers {};
public:
Mem_allocator(Env & env,
Heap & heap,
Platform::Connection & platform,
Cache cache_attr);
Attached_dataspace & alloc_dataspace(size_t size);
void * alloc(size_t size, size_t align);
addr_t dma_addr(void * addr);
size_t size(const void * ptr);
void free(Attached_dataspace * ds);
bool free(const void * ptr);
};
#endif /* _LX_KIT__MEMORY_H_ */

View File

@ -0,0 +1,60 @@
/*
* \brief Scheduler for executing Task objects
* \author Sebastian Sumpf
* \author Josef Soentgen
* \author Norman Feske
* \author Stefan Kalkowski
* \date 2014-10-10
*/
/*
* Copyright (C) 2014-2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <util/list.h>
#include <lx_kit/task.h>
namespace Lx_kit {
class Scheduler;
class Task;
using namespace Genode;
}
class Lx_kit::Scheduler
{
private:
List<Task> _present_list { };
Task * _current { nullptr };
public:
Task & current();
bool active() const;
void add(Task & task);
void remove(Task & task);
void schedule();
void unblock_irq_handler();
void unblock_time_handler();
Task & task(void * t);
template <typename FN>
void for_each_task(FN const & fn);
};
template <typename FN>
void Lx_kit::Scheduler::for_each_task(FN const & fn)
{
for (Task * t = _present_list.first(); t; t = t->next())
fn(*t);
}

View File

@ -0,0 +1,104 @@
/*
* \brief Task represents a cooperatively scheduled thread of control
* \author Sebastian Sumpf
* \author Josef Soentgen
* \author Norman Feske
* \author Stefan Kalkowski
* \date 2014-10-10
*/
/*
* Copyright (C) 2014-2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _LX_KIT__TASK_H_
#define _LX_KIT__TASK_H_
#include <util/list.h>
#include <util/string.h>
#include <lx_kit/arch_execute.h>
namespace Lx_kit {
class Scheduler;
class Task;
using namespace Genode;
}
class Lx_kit::Task : public Genode::List<Lx_kit::Task>::Element
{
public:
using Name = String<64>;
enum State { INIT, RUNNING, BLOCKED };
enum Type { NORMAL, IRQ_HANDLER, TIME_HANDLER };
private:
Task(Task const &);
Task &operator = (Task const &);
State _state { INIT };
int _priority { 120 }; /* initial value of swapper task */
Type _type;
Scheduler & _scheduler;
void * const _lx_task; /* pointer of Linux task struct */
int const _pid; /* Linux process identifier */
Name _name; /* name of task */
void * _stack; /* stack pointer */
jmp_buf _env; /* execution state */
jmp_buf _saved_env; /* saved state of thread calling run */
int (* _func) (void *); /* function to call */
void * _arg; /* argument for function */
public:
Task(int (* func) (void*),
void * arg,
void * task,
int pid,
char const * name,
Scheduler & scheduler,
Type type);
~Task();
State state() const;
Type type() const;
int priority() const;
Name name() const;
void * lx_task() const;
int pid() const;
void block();
void unblock();
void priority(int prio);
void name(const char * name);
bool runnable() const;
/**
* Run task until next preemption point
*/
void run();
/**
* Request scheduling (of other tasks)
*
* Note, this task may not be blocked when calling schedule() depending
* on the use case.
*/
void schedule();
/**
* Shortcut to enter blocking state and request scheduling
*/
void block_and_schedule();
};
#endif /* _LX_KIT__TASK_H_ */

View File

@ -0,0 +1,47 @@
/*
* \brief Lx_kit timeout backend
* \author Stefan Kalkowski
* \date 2021-05-05
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _LX_KIT__TIMEOUT_H_
#define _LX_KIT__TIMEOUT_H_
#include <timer_session/connection.h>
namespace Lx_kit {
class Scheduler;
class Timeout;
using namespace Genode;
}
class Lx_kit::Timeout
{
private:
void _handle(Duration);
using One_shot = Timer::One_shot_timeout<Timeout>;
Scheduler & _scheduler;
One_shot _timeout;
public:
void start(unsigned long us);
void stop();
Timeout(Timer::Connection & timer,
Scheduler & scheduler);
};
#endif /* _LX_KIT__TIMEOUT_H_ */

View File

@ -0,0 +1,27 @@
/*
* \brief Initialization of activity after Linux kernel initialization finished
* \author Stefan Kalkowski
* \date 2021-06-29
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _LX_USER__INIT_H_
#define _LX_USER__INIT_H_
#ifdef __cplusplus
extern "C" {
#endif
void lx_user_init(void);
#ifdef __cplusplus
}
#endif
#endif /* _LX_USER__INIT_H_ */

View File

@ -0,0 +1,27 @@
/*
* \brief I/O activity on top of Linux kernel functionality
* \author Stefan Kalkowski
* \date 2021-06-29
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _LX_USER__IO_H_
#define _LX_USER__IO_H_
#ifdef __cplusplus
extern "C" {
#endif
void lx_user_handle_io(void);
#ifdef __cplusplus
}
#endif
#endif /* _LX_USER__IO_H_ */

View File

@ -0,0 +1,43 @@
/**
* \brief Platform specific code
* \author Christian Prochaska
* \date 2019-07-01
*/
/*
* Copyright (C) 2019 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _ARCH_EXECUTE_H_
#define _ARCH_EXECUTE_H_
#ifdef __cplusplus
extern "C" {
#endif
#define _JBLEN 31
typedef struct _jmp_buf { long _jb[_JBLEN + 1]; } jmp_buf[1];
void _longjmp(jmp_buf, int);
int _setjmp(jmp_buf);
#ifdef __cplusplus
}
#endif
static inline
void arch_execute(void *sp, void *func, void *arg)
{
asm volatile ("mov x0, %2;" /* set arg */
"mov sp, %0;" /* set stack */
"mov x29, xzr;" /* clear frame pointer */
"br %1;" /* call func */
""
: : "r"(sp), "r"(func), "r"(arg) : "r0");
}
#endif /* _ARCH_EXECUTE_H_ */

View File

@ -0,0 +1,93 @@
/*
* \brief Lx_emul backend for memory allocation
* \author Stefan Kalkowski
* \date 2021-03-22
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <base/log.h>
#include <cpu/cache.h>
#include <lx_emul/alloc.h>
#include <lx_emul/page_virt.h>
#include <lx_kit/env.h>
extern "C" void * lx_emul_mem_alloc_aligned(unsigned long size, unsigned long align)
{
void * const ptr = Lx_kit::env().memory.alloc(size, align);
lx_emul_forget_pages(ptr, size);
return ptr;
};
extern "C" void * lx_emul_mem_alloc(unsigned long size)
{
/* always align memory objects to 32 bytes, like malloc, heap etc. */
void * const ptr = Lx_kit::env().memory.alloc(size, 32);
lx_emul_forget_pages(ptr, size);
return ptr;
};
extern "C" void * lx_emul_mem_alloc_uncached(unsigned long size)
{
/* always align memory objects to 32 bytes, like malloc, heap etc. */
void * const ptr = Lx_kit::env().uncached_memory.alloc(size, 32);
lx_emul_forget_pages(ptr, size);
return ptr;
};
extern "C" unsigned long lx_emul_mem_dma_addr(void * addr)
{
unsigned long ret = Lx_kit::env().memory.dma_addr(addr);
if (ret)
return ret;
if (!(ret = Lx_kit::env().uncached_memory.dma_addr(addr)))
Genode::error(__func__, " called with invalid addr ", addr);
return ret;
}
extern "C" void lx_emul_mem_free(const void * ptr)
{
if (!ptr)
return;
if (Lx_kit::env().memory.free(ptr))
return;
if (Lx_kit::env().uncached_memory.free(ptr))
return;
Genode::error(__func__, " called with invalid ptr ", ptr);
};
extern "C" unsigned long lx_emul_mem_size(const void * ptr)
{
unsigned long ret = 0;
if (!ptr)
return ret;
if ((ret = Lx_kit::env().memory.size(ptr)))
return ret;
if (!(ret = Lx_kit::env().uncached_memory.size(ptr)))
Genode::error(__func__, " called with invalid ptr ", ptr);
return ret;
};
extern "C" void lx_emul_mem_cache_clean_invalidate(const void * addr,
unsigned long size)
{
Genode::cache_clean_invalidate_data((Genode::addr_t)addr, size);
}
extern "C" void lx_emul_mem_cache_invalidate(const void * addr,
unsigned long size)
{
Genode::cache_invalidate_data((Genode::addr_t)addr, size);
}

View File

@ -0,0 +1,45 @@
/*
* \brief Lx_emul backend for peripheral clocks
* \author Stefan Kalkowski
* \date 2021-03-22
*/
/*
* Copyright (C) 2021 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/clock.h>
extern "C" int of_device_is_compatible(const struct device_node * node,
const char * compat);
struct clk * lx_emul_clock_get(const struct device_node * node,
const char * name)
{
using namespace Lx_kit;
struct clk * ret = nullptr;
env().devices.for_each([&] (Device & d) {
if (!of_device_is_compatible(node, d.compatible()))
return;
ret = name ? d.clock(name) : d.clock(0U);
if (!ret) warning("No clock ", name, " found for device ", d.name());
});
return ret;
}
unsigned long lx_emul_clock_get_rate(struct clk * clk)
{
if (!clk)
return 0;
return clk->rate;
}

View File

@ -0,0 +1,105 @@
/*
* \brief Linux DDE timer
* \author Stefan Kalkowski
* \date 2021-03-22
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <linux/clockchips.h>
#include <linux/clocksource.h>
#include <linux/sched_clock.h>
#include <linux/smp.h>
#include <lx_emul/debug.h>
#include <lx_emul/time.h>
static u32 dde_timer_rate = 1000000; /* we use microseconds as rate */
static int dde_set_next_event(unsigned long evt,
struct clock_event_device *clk)
{
lx_emul_time_event(evt);
return 0;
}
static int dde_set_state_shutdown(struct clock_event_device *clk)
{
lx_emul_time_stop();
return 0;
}
static u64 dde_timer_read_counter(void)
{
return lx_emul_time_counter();
}
static u64 dde_clocksource_read_counter(struct clocksource * cs)
{
return lx_emul_time_counter();
}
static u64 dde_cyclecounter_read_counter(const struct cyclecounter * cc)
{
return lx_emul_time_counter();
}
static struct clock_event_device * dde_clock_event_device;
void lx_emul_time_init()
{
static struct clocksource clocksource = {
.name = "dde_counter",
.rating = 400,
.read = dde_clocksource_read_counter,
.mask = CLOCKSOURCE_MASK(56),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
static struct cyclecounter cyclecounter = {
.read = dde_cyclecounter_read_counter,
.mask = CLOCKSOURCE_MASK(56),
};
static struct timecounter timecounter;
static struct clock_event_device clock_event_device = {
.name = "dde_timer",
.features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_SOURCE_VALID_FOR_HRES,
.rating = 400,
.set_state_shutdown = dde_set_state_shutdown,
.set_state_oneshot_stopped = dde_set_state_shutdown,
.set_next_event = dde_set_next_event,
};
u64 start_count = dde_timer_read_counter();
clock_event_device.cpumask = cpumask_of(smp_processor_id()),
dde_clock_event_device = &clock_event_device;
clocksource_register_hz(&clocksource, dde_timer_rate);
cyclecounter.mult = clocksource.mult;
cyclecounter.shift = clocksource.shift;
timecounter_init(&timecounter, &cyclecounter, start_count);
clockevents_config_and_register(&clock_event_device, dde_timer_rate, 0xf, 0x7fffffff);
sched_clock_register(dde_timer_read_counter, 64, dde_timer_rate);
}
void lx_emul_time_handle(void)
{
dde_clock_event_device->event_handler(dde_clock_event_device);
}
struct of_device_id __clk_of_table[] = { };

View File

@ -0,0 +1,32 @@
/*
* \brief Lx_emul backend for Linux kernel' debug functions
* \author Stefan Kalkowski
* \date 2021-03-16
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <base/log.h>
#include <base/sleep.h>
#include <os/backtrace.h>
#include <lx_emul/debug.h>
extern "C" void lx_emul_trace_and_stop(const char * func)
{
using namespace Genode;
error("Function ", func, " not implemented yet!");
log("Backtrace follows:");
backtrace();
log("Will sleep forever...");
sleep_forever();
}
extern "C" void lx_emul_trace(const char *) {}

View File

@ -0,0 +1,55 @@
/*
* \brief Lx_emul backend for Linux kernel initialization
* \author Stefan Kalkowski
* \date 2021-03-10
*/
/*
* Copyright (C) 2021 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_kit/task.h>
#include <lx_emul/irq.h>
#include <lx_emul/init.h>
#include <lx_emul/task.h>
#include <lx_emul/initcall_order.h>
extern "C" void lx_emul_initcalls()
{
Lx_kit::env().initcalls.execute_in_order();
}
extern "C" void lx_emul_register_initcall(int (*initcall)(void),
const char * name)
{
for (unsigned i = 0; i < (sizeof(lx_emul_initcall_order) / sizeof(char*));
i++) {
if (Genode::strcmp(name, lx_emul_initcall_order[i]) == 0) {
Lx_kit::env().initcalls.add(initcall, i);
return;
}
}
Genode::error("Initcall ", name, " unknown in initcall database!");
}
void lx_emul_start_kernel(void * dtb)
{
using namespace Lx_kit;
new (env().heap) Task(lx_emul_init_task_function, dtb,
lx_emul_init_task_struct, SWAPPER_PID, "swapper",
env().scheduler, Task::TIME_HANDLER);
new (env().heap) Task(lx_emul_irq_task_function, nullptr,
lx_emul_irq_task_struct, KIRQ_PID, "kirqd",
env().scheduler, Task::IRQ_HANDLER);
env().scheduler.schedule();
}

View File

@ -0,0 +1,33 @@
/*
* \brief Lx_emul backend for I/O memory
* \author Stefan Kalkowski
* \date 2021-03-10
*/
/*
* Copyright (C) 2021 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/io_mem.h>
void * lx_emul_io_mem_map(unsigned long phys_addr,
unsigned long size)
{
using namespace Lx_kit;
using namespace Genode;
void * ret = nullptr;
env().devices.for_each([&] (Device & d) {
if (d.io_mem(phys_addr, size))
ret = d.io_mem_local_addr(phys_addr, size);
});
if (!ret)
error("memory-mapped I/O resource ", Hex(phys_addr),
" (size=", Hex(size), ") unavailable");
return ret;
}

View File

@ -0,0 +1,45 @@
/*
* \brief Lx_emul backend for interrupts
* \author Stefan Kalkowski
* \date 2021-04-22
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <lx_emul/irq.h>
#include <lx_kit/env.h>
extern "C" void lx_emul_irq_unmask(unsigned int irq)
{
bool found = false;
Lx_kit::env().devices.for_each([&] (Lx_kit::Device & d) {
if (d.irq_unmask(irq)) found = true; });
if (!found)
Genode::error("irq ", irq, " unavailable");
}
extern "C" void lx_emul_irq_mask(unsigned int irq)
{
Lx_kit::env().devices.for_each([&] (Lx_kit::Device & d) {
d.irq_mask(irq); });
}
extern "C" void lx_emul_irq_eoi(unsigned int irq)
{
Lx_kit::env().devices.for_each([&] (Lx_kit::Device & d) {
d.irq_ack(irq); });
}
extern "C" unsigned int lx_emul_irq_last()
{
return Lx_kit::env().last_irq;
}

View File

@ -0,0 +1,200 @@
/*
* \brief Linux DDE interrupt controller
* \author Stefan Kalkowski
* \date 2021-03-10
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <lx_emul/debug.h>
#include <lx_emul/irq.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/slab.h>
#include <linux/irqchip.h>
#include <../kernel/irq/internals.h>
static int dde_irq_set_wake(struct irq_data *d, unsigned int on)
{
lx_emul_trace_and_stop(__func__);
return 0;
}
static void dde_irq_unmask(struct irq_data *d)
{
lx_emul_irq_unmask(d->hwirq);
}
static void dde_irq_mask(struct irq_data *d)
{
lx_emul_irq_mask(d->hwirq);
}
static void dde_irq_eoi(struct irq_data *d)
{
lx_emul_irq_eoi(d->hwirq);
}
static int dde_irq_set_type(struct irq_data *d, unsigned int type)
{
if (type != IRQ_TYPE_LEVEL_HIGH)
lx_emul_trace_and_stop(__func__);
return 0;
}
static struct irq_chip dde_irqchip_data_chip = {
.name = "dde-irqs",
.irq_eoi = dde_irq_eoi,
.irq_mask = dde_irq_mask,
.irq_unmask = dde_irq_unmask,
.irq_set_wake = dde_irq_set_wake,
.irq_set_type = dde_irq_set_type,
};
static int dde_domain_translate(struct irq_domain * d,
struct irq_fwspec * fwspec,
unsigned long * hwirq,
unsigned int * type)
{
if (is_of_node(fwspec->fwnode)) {
if (fwspec->param_count != 3 || fwspec->param[0] != 0)
return -EINVAL;
*hwirq = fwspec->param[1] + 32;
*type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK;
return 0;
}
return -EINVAL;
}
static int dde_domain_alloc(struct irq_domain * domain,
unsigned int irq,
unsigned int nr_irqs,
void * data)
{
struct irq_fwspec *fwspec = data;
irq_hw_number_t hwirq;
unsigned int type;
int err;
int i;
err = dde_domain_translate(domain, fwspec, &hwirq, &type);
if (err)
return err;
for (i = 0; i < nr_irqs; i++) {
irq_domain_set_info(domain, irq+i, hwirq+i, &dde_irqchip_data_chip,
domain->host_data, handle_fasteoi_irq, NULL, NULL);
irq_set_probe(irq);
irqd_set_single_target(irq_desc_get_irq_data(irq_to_desc(irq)));
}
return 0;
}
static const struct irq_domain_ops dde_irqchip_data_domain_ops = {
.translate = dde_domain_translate,
.alloc = dde_domain_alloc,
.free = irq_domain_free_irqs_common,
};
static const struct of_device_id dde_of_match[] = {
{ .compatible = "arm,gic-v3", .data = (const void *) 4 },
{ .compatible = "arm,gic-400", .data = (const void *) 4 },
{ /* END */ }
};
static struct irq_domain *dde_irq_domain;
static int __init dde_irqchip_init(struct device_node *node,
struct device_node *parent)
{
dde_irq_domain = irq_domain_create_tree(&node->fwnode,
&dde_irqchip_data_domain_ops, NULL);
if (!dde_irq_domain)
return -ENOMEM;
irq_set_default_host(dde_irq_domain);
return 0;
}
struct of_device_id __irqchip_of_table[] = {
{
.compatible = "arm,gic-v3",
.data = dde_irqchip_init
},
{
.compatible = "arm,gic-400",
.data = dde_irqchip_init
}
};
int lx_emul_irq_task_function(void * data)
{
int irq;
for (;;) {
lx_emul_task_schedule(true);
if (!dde_irq_domain)
continue;
irq = irq_find_mapping(dde_irq_domain, lx_emul_irq_last());
if (!irq) {
ack_bad_irq(irq);
WARN_ONCE(true, "Unexpected interrupt %d received!\n", lx_emul_irq_last());
} else {
generic_handle_irq(irq);
}
}
return 0;
}
struct task_struct irq_task = {
.state = 0,
.usage = REFCOUNT_INIT(2),
.flags = PF_KTHREAD,
.prio = MAX_PRIO - 20,
.static_prio = MAX_PRIO - 20,
.normal_prio = MAX_PRIO - 20,
.policy = SCHED_NORMAL,
.cpus_ptr = &irq_task.cpus_mask,
.cpus_mask = CPU_MASK_ALL,
.nr_cpus_allowed = 1,
.mm = NULL,
.active_mm = NULL,
.tasks = LIST_HEAD_INIT(irq_task.tasks),
.real_parent = &irq_task,
.parent = &irq_task,
.children = LIST_HEAD_INIT(irq_task.children),
.sibling = LIST_HEAD_INIT(irq_task.sibling),
.group_leader = &irq_task,
.comm = "kirqd",
.thread = INIT_THREAD,
.pending = {
.list = LIST_HEAD_INIT(irq_task.pending.list),
.signal = {{0}}
},
.blocked = {{0}},
};
void * lx_emul_irq_task_struct = &irq_task;

View File

@ -0,0 +1,18 @@
/*
* \brief Linux Kernel log messages
* \author Stefan Kalkowski
* \date 2021-03-22
*/
/*
* Copyright (C) 2021 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/log.h>
extern "C" void lx_emul_vprintf(char const *fmt, va_list va) {
Lx_kit::env().console.vprintf(fmt, va); }

View File

@ -0,0 +1,91 @@
/*
* \brief Lx_emul backend for page-struct management
* \author Norman Feske
* \date 2021-07-01
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <base/log.h>
#include <util/avl_tree.h>
#include <lx_kit/env.h>
#include <lx_kit/map.h>
#include <lx_kit/byte_range.h>
#include <lx_emul/page_virt.h>
namespace Lx_emul { class Page_info; }
using addr_t = Genode::addr_t;
using size_t = Genode::size_t;
struct Lx_emul::Page_info
{
struct Key { addr_t virt; } key;
struct page * page_ptr;
bool higher(Key const other_key) const
{
return key.virt > other_key.virt;
}
struct Query_virt_range
{
addr_t virt;
size_t size;
bool matches(Page_info const &page_info) const
{
size_t const page_size = 4096;
Lx_kit::Byte_range page_range { page_info.key.virt, page_size };
Lx_kit::Byte_range virt_range { virt, size };
return page_range.intersects(virt_range);
}
Key key() const { return Key { virt }; }
};
struct Query_virt_addr : Query_virt_range
{
Query_virt_addr(void const *virt) : Query_virt_range{(addr_t)virt, 1} { }
};
};
static Lx_kit::Map<Lx_emul::Page_info> &page_registry()
{
static Lx_kit::Map<Lx_emul::Page_info> map { Lx_kit::env().heap };
return map;
}
extern "C" void lx_emul_associate_page_with_virt_addr(struct page *page, void const *virt)
{
page_registry().insert(Lx_emul::Page_info::Key { (addr_t)virt }, page);
}
void lx_emul_disassociate_page_from_virt_addr(void const *virt)
{
page_registry().remove(Lx_emul::Page_info::Query_virt_addr(virt));
}
struct page *lx_emul_associated_page(void const *virt, unsigned long size)
{
Lx_emul::Page_info::Query_virt_range query { .virt = (addr_t)virt, .size = size };
struct page *page_ptr = nullptr;
page_registry().apply(query, [&] (Lx_emul::Page_info const &page_info) {
page_ptr = page_info.page_ptr; });
return page_ptr;
}

View File

@ -0,0 +1,16 @@
/*
* \brief Replaces arch/arm64/kernel/smp.c
* \author Stefan Kalkowski
* \date 2021-03-16
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <asm/smp.h>
int cpu_number = 0;

View File

@ -0,0 +1,25 @@
/*
* \brief Replaces arch/arm64/mm/ioremap.c
* \author Stefan Kalkowski
* \date 2021-03-16
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <asm/io.h>
#include <lx_emul/io_mem.h>
void __iomem * __ioremap(phys_addr_t phys_addr, size_t size, pgprot_t prot)
{
return lx_emul_io_mem_map(phys_addr, size);
}
void iounmap(volatile void __iomem * io_addr) { }

View File

@ -0,0 +1,24 @@
/*
* \brief Replaces drivers/base/power/common.c
* \author Stefan Kalkowski
* \date 2021-03-16
*
* We do not support power-management by now, so leave it empty.
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <linux/pm_domain.h>
int dev_pm_domain_attach(struct device * dev, bool power_on)
{
return 0;
}
void dev_pm_domain_detach(struct device * dev, bool power_off) { }

View File

@ -0,0 +1,37 @@
/*
* \brief Replaces drivers/base/power/main.c
* \author Stefan Kalkowski
* \date 2021-03-16
*
* We do not support power-management by now, so leave it empty.
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <linux/pm.h>
#include <../drivers/base/power/power.h>
void device_pm_add(struct device * dev) { }
void device_pm_move_last(struct device * dev) { }
void device_pm_remove(struct device * dev) { }
void device_pm_sleep_init(struct device * dev) { }
void device_pm_check_callbacks(struct device * dev) { }
void device_pm_lock(void) { }
void device_pm_unlock(void) { }

View File

@ -0,0 +1,67 @@
/*
* \brief Replaces drivers/base/power/runtime.c
* \author Stefan Kalkowski
* \date 2021-03-16
*
* We do not support power-management by now, so leave it empty.
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <linux/pm_runtime.h>
#include <../drivers/base/power/power.h>
void pm_runtime_init(struct device * dev) { }
int __pm_runtime_resume(struct device * dev,int rpmflags)
{
return 0;
}
int __pm_runtime_idle(struct device * dev,int rpmflags)
{
return 0;
}
void pm_runtime_get_suppliers(struct device * dev) { }
int pm_runtime_barrier(struct device * dev)
{
return 0;
}
void pm_runtime_reinit(struct device * dev) { }
void pm_runtime_put_suppliers(struct device * dev) { }
int pm_generic_runtime_resume(struct device * dev)
{
return 0;
}
int pm_generic_runtime_suspend(struct device * dev)
{
return 0;
}
void pm_runtime_clean_up_links(struct device * dev) { }
void pm_runtime_drop_link(struct device_link * link) { }
void pm_runtime_new_link(struct device * dev) { }

View File

@ -0,0 +1,47 @@
/*
* \brief Replaces drivers/clk/clk.c
* \author Stefan Kalkowski
* \date 2021-03-16
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <linux/clk.h>
#include <lx_emul/clock.h>
unsigned long clk_get_rate(struct clk * clk)
{
return lx_emul_clock_get_rate(clk);
}
int clk_set_rate(struct clk * clk,unsigned long rate)
{
if (lx_emul_clock_get_rate(clk) != rate)
printk("Error: cannot change clock rate dynamically to %ld\n", rate);
return 0;
}
int clk_prepare(struct clk * clk)
{
return 0;
}
int clk_enable(struct clk * clk)
{
return 0;
}
void clk_disable(struct clk * clk) { }
void clk_unprepare(struct clk * clk) { }

View File

@ -0,0 +1,27 @@
/*
* \brief Replaces drivers/clk/clkdev.c
* \author Stefan Kalkowski
* \date 2021-03-16
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <linux/clkdev.h>
#include <linux/device.h>
#include <lx_emul/clock.h>
struct clk *clk_get(struct device *dev, const char *con_id)
{
if (!dev || !dev->of_node)
return NULL;
return lx_emul_clock_get(dev->of_node, con_id);
}
void clk_put(struct clk *clk) {}

View File

@ -0,0 +1,21 @@
/*
* \brief Replaces fs/exec.c
* \author Stefan Kalkowski
* \date 2021-03-16
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <linux/sched.h>
#include <lx_emul/task.h>
void __set_task_comm(struct task_struct * tsk,const char * buf,bool exec)
{
strlcpy(tsk->comm, buf, sizeof(tsk->comm));
lx_emul_task_name(tsk, tsk->comm);
}

View File

@ -0,0 +1,45 @@
/*
* \brief Replaces kernel/cpu.c
* \author Stefan Kalkowski
* \date 2021-03-16
*
* We hardcode support for a single cpu only.
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <linux/cpu.h>
#include <linux/cpumask.h>
struct cpumask __cpu_online_mask = { .bits[0] = 1 };
struct cpumask __cpu_possible_mask = { .bits[0] = 1 };
struct cpumask __cpu_present_mask = { .bits[0] = 1 };
#ifdef CONFIG_HOTPLUG_CPU
void cpus_read_lock(void) { }
void cpus_read_unlock(void) { }
void lockdep_assert_cpus_held(void) { }
#endif /* CONFIG_HOTPLUG_CPU */
#define MASK_DECLARE_1(x) [x+1][0] = (1UL << (x))
#define MASK_DECLARE_2(x) MASK_DECLARE_1(x), MASK_DECLARE_1(x+1)
#define MASK_DECLARE_4(x) MASK_DECLARE_2(x), MASK_DECLARE_2(x+2)
#define MASK_DECLARE_8(x) MASK_DECLARE_4(x), MASK_DECLARE_4(x+4)
const unsigned long cpu_bit_bitmap[BITS_PER_LONG+1][BITS_TO_LONGS(NR_CPUS)] = {
MASK_DECLARE_8(0), MASK_DECLARE_8(8),
MASK_DECLARE_8(16), MASK_DECLARE_8(24),
#if BITS_PER_LONG > 32
MASK_DECLARE_8(32), MASK_DECLARE_8(40),
MASK_DECLARE_8(48), MASK_DECLARE_8(56),
#endif
};
const DECLARE_BITMAP(cpu_all_bits, NR_CPUS) = CPU_BITS_ALL;
EXPORT_SYMBOL(cpu_all_bits);

View File

@ -0,0 +1,107 @@
/*
* \brief Replaces kernel/dma/mapping.c
* \author Stefan Kalkowski
* \date 2021-03-16
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <lx_emul/alloc.h>
#include <lx_emul/debug.h>
#include <linux/dma-mapping.h>
void * dma_alloc_attrs(struct device * dev,
size_t size,
dma_addr_t * dma_handle,
gfp_t flag,
unsigned long attrs)
{
void * addr;
if (dev && dev->dma_mem) {
printk("We do not support device DMA memory yet!\n");
lx_emul_trace_and_stop(__func__);
}
addr = lx_emul_mem_alloc_uncached(size);
*dma_handle = lx_emul_mem_dma_addr(addr);
return addr;
}
void dma_free_attrs(struct device * dev,
size_t size,
void * cpu_addr,
dma_addr_t dma_handle,
unsigned long attrs)
{
lx_emul_mem_free(cpu_addr);
}
int dma_set_mask(struct device *dev, u64 mask)
{
mask = (dma_addr_t)mask;
if (!dev->dma_mask || !dma_supported(dev, mask))
return -EIO;
*dev->dma_mask = mask;
return 0;
}
int dma_set_coherent_mask(struct device *dev, u64 mask)
{
mask = (dma_addr_t)mask;
if (!dma_supported(dev, mask))
return -EIO;
dev->coherent_dma_mask = mask;
return 0;
}
int dma_map_sg_attrs(struct device * dev,
struct scatterlist * sgl,
int nents,
enum dma_data_direction dir,
unsigned long attrs)
{
int i;
struct scatterlist *sg;
for_each_sg(sgl, sg, nents, i) {
sg->dma_address = lx_emul_mem_dma_addr(page_address(sg_page(sg))) + sg->offset;
if (!sg->dma_address)
return 0;
sg_dma_len(sg) = sg->length;
lx_emul_mem_cache_clean_invalidate(page_address(sg_page(sg))
+ sg->offset, sg->length);
}
return nents;
}
void dma_unmap_sg_attrs(struct device * dev,
struct scatterlist * sgl,
int nents,
enum dma_data_direction dir,
unsigned long attrs)
{
int i;
struct scatterlist *sg;
for_each_sg(sgl, sg, nents, i) {
lx_emul_mem_cache_invalidate(page_address(sg_page(sg)) + sg->offset,
sg->length);
}
}

View File

@ -0,0 +1,43 @@
/*
* \brief Replaces kernel/exit.c
* \author Stefan Kalkowski
* \date 2021-03-16
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/rcuwait.h>
#include <lx_emul/debug.h>
int rcuwait_wake_up(struct rcuwait * w)
{
if (w && w->task)
return wake_up_process(w->task);
return 0;
}
void __noreturn do_exit(long code)
{
struct task_struct *tsk = current;
tsk->exit_code = code;
set_special_state(TASK_DEAD);
if (tsk->vfork_done)
complete(tsk->vfork_done);
current->flags |= PF_NOFREEZE;
schedule();
BUG();
}

View File

@ -0,0 +1,59 @@
/*
* \brief Replaces kernel/fork.c
* \author Stefan Kalkowski
* \date 2021-03-16
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/refcount.h>
#include <linux/list.h>
#include <asm/processor.h>
#include <lx_emul/task.h>
#include <linux/sched/task.h>
pid_t kernel_thread(int (* fn)(void *),void * arg,unsigned long flags)
{
static int pid_counter = FIRST_PID;
struct task_struct * task = kmalloc(sizeof(struct task_struct), GFP_KERNEL);
*task = (struct task_struct){
.state = 0,
.usage = REFCOUNT_INIT(2),
.flags = PF_KTHREAD,
.prio = MAX_PRIO - 20,
.static_prio = MAX_PRIO - 20,
.normal_prio = MAX_PRIO - 20,
.policy = SCHED_NORMAL,
.cpus_ptr = &task->cpus_mask,
.cpus_mask = CPU_MASK_ALL,
.nr_cpus_allowed = 1,
.mm = NULL,
.active_mm = NULL,
.tasks = LIST_HEAD_INIT(task->tasks),
.real_parent = lx_emul_task_get_current(),
.parent = lx_emul_task_get_current(),
.children = LIST_HEAD_INIT(task->children),
.sibling = LIST_HEAD_INIT(task->sibling),
.group_leader = task,
.thread = INIT_THREAD,
.blocked = {{0}},
.pid = pid_counter++,
.pending = {
.list = LIST_HEAD_INIT(task->pending.list),
.signal = {{0}}
}};
task->thread_info.preempt_count = 0;
lx_emul_task_create(task, "kthread", task->pid, fn, arg);
return task->pid;
}

View File

@ -0,0 +1,17 @@
/*
* \brief Replaces kernel/irq/spurious.c
* \author Stefan Kalkowski
* \date 2021-03-16
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <linux/types.h>
extern bool noirqdebug;
bool noirqdebug = 1;

View File

@ -0,0 +1,91 @@
/*
* \brief Replaces kernel/locking/spinlock.c
* \author Stefan Kalkowski
* \date 2021-03-16
*
* We run single-core, cooperatively scheduled. We should never spin.
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <linux/spinlock.h>
#include <linux/rwlock_api_smp.h>
#include <lx_emul/debug.h>
#include <lx_emul/task.h>
void __lockfunc _raw_spin_lock(raw_spinlock_t * lock)
{
if (atomic_read(&lock->raw_lock.val)) {
printk("Error: spinlock contention!");
lx_emul_trace_and_stop(__func__);
}
atomic_set(&lock->raw_lock.val, 1);
}
unsigned long __lockfunc _raw_spin_lock_irqsave(raw_spinlock_t * lock)
{
unsigned long flags;
local_irq_save(flags);
_raw_spin_lock(lock);
return flags;
}
void __lockfunc _raw_spin_unlock(raw_spinlock_t * lock)
{
atomic_set(&lock->raw_lock.val, 0);
}
void __lockfunc _raw_spin_unlock_irqrestore(raw_spinlock_t * lock,
unsigned long flags)
{
_raw_spin_unlock(lock);
local_irq_restore(flags);
}
void __lockfunc _raw_spin_lock_irq(raw_spinlock_t * lock)
{
_raw_spin_lock_irqsave(lock);
}
void __lockfunc _raw_spin_unlock_irq(raw_spinlock_t * lock)
{
_raw_spin_unlock_irqrestore(lock, 0);
}
int __lockfunc _raw_spin_trylock(raw_spinlock_t * lock)
{
if (atomic_read(&lock->raw_lock.val))
return 0;
_raw_spin_lock(lock);
return 1;
}
void __lockfunc _raw_write_lock(rwlock_t * lock)
{
if (lock->raw_lock.wlocked) {
printk("Error: rwlock contention!");
lx_emul_trace_and_stop(__func__);
}
lock->raw_lock.wlocked = 1;
}
void __lockfunc _raw_write_unlock(rwlock_t * lock)
{
lock->raw_lock.wlocked = 0;
}

View File

@ -0,0 +1,35 @@
/*
* \brief Replaces kernel/pid.c
* \author Stefan Kalkowski
* \date 2021-03-16
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <linux/pid_namespace.h>
#include <linux/refcount.h>
struct pid init_struct_pid = {
.count = REFCOUNT_INIT(1),
.tasks = {
{ .first = NULL },
{ .first = NULL },
{ .first = NULL },
},
.level = 0,
.numbers = { {
.nr = 0,
.ns = &init_pid_ns,
}, }
};
struct task_struct *find_task_by_pid_ns(pid_t nr, struct pid_namespace *ns)
{
return lx_emul_task_get(nr);
}

View File

@ -0,0 +1,42 @@
/*
* \brief Replaces kernel/printk/printk.c
* \author Stefan Kalkowski
* \date 2021-03-16
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <linux/printk.h>
#include <lx_emul/log.h>
#include <lx_emul/debug.h>
asmlinkage __visible int printk(const char * fmt,...)
{
va_list args;
va_start(args, fmt);
lx_emul_vprintf(fmt, args);
va_end(args);
return 0;
}
asmlinkage int vprintk(const char * fmt, va_list args)
{
lx_emul_vprintf(fmt, args);
return 0;
}
asmlinkage int vprintk_emit(int facility, int level,
const struct dev_printk_info *dev_info,
const char * fmt, va_list args)
{
lx_emul_vprintf(fmt, args);
return 0;
}

View File

@ -0,0 +1,43 @@
/*
* \brief Replaces kernel/rcu/srcutree.c.c
* \author Stefan Kalkowski
* \date 2021-03-16
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <linux/percpu.h>
#include <linux/srcu.h>
int __srcu_read_lock(struct srcu_struct * ssp)
{
int idx = READ_ONCE(ssp->srcu_idx) & 0x1;
return idx;
}
void __srcu_read_unlock(struct srcu_struct * ssp, int idx) { }
int init_srcu_struct(struct srcu_struct * ssp)
{
mutex_init(&ssp->srcu_cb_mutex);
mutex_init(&ssp->srcu_gp_mutex);
ssp->srcu_idx = 0;
ssp->srcu_gp_seq = 0;
ssp->srcu_barrier_seq = 0;
mutex_init(&ssp->srcu_barrier_mutex);
atomic_set(&ssp->srcu_barrier_cpu_cnt, 0);
/* INIT_DELAYED_WORK(&ssp->work, process_srcu); */
ssp->sda = alloc_percpu(struct srcu_data);
/* init_srcu_struct_nodes(ssp, false); */
ssp->srcu_gp_seq_needed_exp = 0;
ssp->srcu_last_gp_end = ktime_get_mono_fast_ns();
smp_store_release(&ssp->srcu_gp_seq_needed, 0); /* Init done. */
return ssp->sda ? 0 : -ENOMEM;
}

View File

@ -0,0 +1,26 @@
/*
* \brief Replaces kernel/rcu/tree.c.c
* \author Stefan Kalkowski
* \date 2021-03-16
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <linux/types.h>
extern void __rcu_read_lock(void);
void __rcu_read_lock(void) { }
extern void __rcu_read_unlock(void);
void __rcu_read_unlock(void) { }
int rcu_needs_cpu(u64 basemono, u64 *nextevt)
{
return 0;
}

View File

@ -0,0 +1,177 @@
/*
* \brief Replaces kernel/sched/core.c
* \author Stefan Kalkowski
* \date 2021-03-16
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#define CREATE_TRACE_POINTS
#include <trace/events/sched.h>
#undef CREATE_TRACE_POINTS
#include <asm/preempt.h>
#include <linux/preempt.h>
#include <linux/sched.h>
#include <linux/sched/debug.h>
#include <linux/sched/stat.h>
#include <linux/sched/nohz.h>
#include <lx_emul/debug.h>
#include <lx_emul/task.h>
#include <../kernel/sched/sched.h>
void set_user_nice(struct task_struct * p, long nice)
{
p->static_prio = NICE_TO_PRIO(nice);
p->prio = p->static_prio;
p->normal_prio = p->static_prio;
lx_emul_task_priority(p, p->static_prio);
}
int set_cpus_allowed_ptr(struct task_struct * p,
const struct cpumask * new_mask)
{
return 0;
}
static int
try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags)
{
if (!p) lx_emul_trace_and_stop(__func__);
if (!(p->state & state))
return 0;
if (p != lx_emul_task_get_current())
lx_emul_task_unblock(p);
p->state = TASK_RUNNING;
return 1;
}
int wake_up_process(struct task_struct * p)
{
return try_to_wake_up(p, TASK_NORMAL, 0);
}
int default_wake_function(wait_queue_entry_t *curr,
unsigned mode,
int wake_flags,
void *key)
{
return try_to_wake_up(curr->private, mode, wake_flags);
}
asmlinkage __visible void __sched schedule(void)
{
if (preempt_count()) {
printk("schedule: unexpected preempt_count=%d\n", preempt_count());
lx_emul_trace_and_stop("abort");
}
lx_emul_task_schedule(current->state != TASK_RUNNING);
}
#ifdef CONFIG_DEBUG_PREEMPT
void preempt_count_add(int val)
{
current_thread_info()->preempt.count += val;
}
void preempt_count_sub(int val)
{
current_thread_info()->preempt.count -= val;
}
#endif /* CONFIG_DEBUG_PREEMPT */
asmlinkage __visible void __sched notrace preempt_schedule(void)
{
if (likely(!preemptible()))
return;
schedule();
}
asmlinkage __visible void __sched notrace preempt_schedule_notrace(void)
{
if (likely(!preemptible()))
return;
schedule();
}
unsigned long nr_iowait_cpu(int cpu)
{
return 0;
}
void scheduler_tick(void)
{
sched_clock_tick();
}
void __sched schedule_preempt_disabled(void)
{
lx_emul_task_schedule(current->state != TASK_RUNNING);
}
int sched_setscheduler_nocheck(struct task_struct * p, int policy,
const struct sched_param * param)
{
return 0;
}
unsigned long wait_task_inactive(struct task_struct * p,long match_state)
{
struct rq *rq = task_rq(p);
if (task_running(rq, p))
schedule();
if (task_running(rq, p))
return 0;
return 1;
}
int wake_up_state(struct task_struct * p, unsigned int state)
{
p->state = TASK_RUNNING;
lx_emul_task_unblock(p);
return 0;
}
#ifdef CONFIG_SMP
#ifdef CONFIG_NO_HZ_COMMON
int get_nohz_timer_target(void)
{
return 0;
}
#endif
#endif

View File

@ -0,0 +1,18 @@
/*
* \brief Replaces kernel/smp.c
* \author Stefan Kalkowski
* \date 2021-03-16
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <linux/cpumask.h>
unsigned int nr_cpu_ids = 1;
unsigned long irq_err_count = 0;

View File

@ -0,0 +1,74 @@
/*
* \brief Replaces kernel/softirq.c
* \author Stefan Kalkowski
* \date 2021-03-16
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <linux/interrupt.h>
#include <linux/bottom_half.h>
#define CREATE_TRACE_POINTS
#include <trace/events/irq.h>
#include <lx_emul/debug.h>
irq_cpustat_t irq_stat;
int __init __weak arch_probe_nr_irqs(void)
{
return 0;
}
int __init __weak arch_early_irq_init(void)
{
return 0;
}
unsigned int __weak arch_dynirq_lower_bound(unsigned int from)
{
return from;
}
static struct softirq_action actions[NR_SOFTIRQS];
void open_softirq(int nr, void (* action)(struct softirq_action *))
{
if (nr >= NR_SOFTIRQS) {
printk("Error: %s nr=%d exceeds softirq limit\n", __func__, nr);
return;
}
actions[nr].action = action;
}
inline void raise_softirq_irqoff(unsigned int nr)
{
if (nr >= NR_SOFTIRQS || !actions[nr].action)
return;
actions[nr].action(&actions[nr]);
}
void raise_softirq(unsigned int nr)
{
raise_softirq_irqoff(nr);
}
void __local_bh_enable_ip(unsigned long ip,unsigned int cnt)
{
/*
* Called by write_unlock_bh, which reverts preempt_cnt by the
* value SOFTIRQ_LOCK_OFFSET.
*/
__preempt_count_sub(cnt);
}

View File

@ -0,0 +1,19 @@
/*
* \brief Replaces kernel/stop_machine.c
* \author Stefan Kalkowski
* \date 2021-03-16
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <linux/stop_machine.h>
int stop_machine(cpu_stop_fn_t fn,void * data,const struct cpumask * cpus)
{
return (*fn)(data);
}

View File

@ -0,0 +1,28 @@
/*
* \brief Replaces lib/devres.c
* \author Stefan Kalkowski
* \date 2021-03-16
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <linux/device.h>
#include <lx_emul/io_mem.h>
void __iomem *devm_ioremap_resource(struct device *dev,
const struct resource *res)
{
return lx_emul_io_mem_map(res->start, resource_size(res));
}
void __iomem *devm_ioremap(struct device *dev, resource_size_t offset,
resource_size_t size)
{
return lx_emul_io_mem_map(offset, size);
}

View File

@ -0,0 +1,19 @@
/*
* \brief Replaces lib/smp_processor_id.c
* \author Stefan Kalkowski
* \date 2021-03-16
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <linux/smp.h>
notrace unsigned int debug_smp_processor_id(void)
{
return 0;
}

View File

@ -0,0 +1,24 @@
/*
* \brief Replaces mm/memblock.c
* \author Stefan Kalkowski
* \date 2021-03-16
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <linux/memblock.h>
#include <lx_emul/alloc.h>
void * __init memblock_alloc_try_nid(phys_addr_t size,
phys_addr_t align,
phys_addr_t min_addr,
phys_addr_t max_addr,
int nid)
{
return lx_emul_mem_alloc_aligned(size, align);
}

View File

@ -0,0 +1,38 @@
/*
* \brief Replaces mm/page_alloc.c
* \author Stefan Kalkowski
* \date 2021-06-03
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <linux/gfp.h>
#include <lx_emul/page_virt.h>
struct page * __alloc_pages_nodemask(gfp_t gfp_mask, unsigned int order,
int preferred_nid, nodemask_t * nodemask)
{
unsigned const num_pages = (1 << order);
void * const ptr = lx_emul_mem_alloc_aligned(PAGE_SIZE*num_pages, PAGE_SIZE);
return lx_emul_virt_to_pages(ptr, num_pages);
}
void __free_pages(struct page * page, unsigned int order)
{
unsigned i;
unsigned const num_pages = (1 << order);
void * const virt_addr = page->virtual;
for (i = 0; i < num_pages; i++)
lx_emul_disassociate_page_from_virt_addr(page[i].virtual);
lx_emul_mem_free(virt_addr);
lx_emul_mem_free(page);
}

View File

@ -0,0 +1,32 @@
/*
* \brief Replaces mm/percpu.c
* \author Stefan Kalkowski
* \date 2021-03-16
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <linux/percpu.h>
#include <lx_emul/alloc.h>
void __percpu * __alloc_percpu(size_t size, size_t align)
{
return lx_emul_mem_alloc_aligned(size, align);
}
void __percpu * __alloc_percpu_gfp(size_t size,size_t align,gfp_t gfp)
{
return __alloc_percpu(size, align);
}
void free_percpu(void __percpu * ptr)
{
lx_emul_mem_free(ptr);
}

View File

@ -0,0 +1,40 @@
/*
* \brief Replaces mm/slub.c
* \author Stefan Kalkowski
* \date 2021-03-16
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <linux/mm.h>
#include <linux/slab.h>
#include <../mm/slab.h>
#include <lx_emul/alloc.h>
#include <lx_emul/debug.h>
void * krealloc(const void * p,size_t new_size,gfp_t flags)
{
if (!p)
return kmalloc(new_size, flags);
if (!new_size) {
kfree(p);
return NULL;
}
lx_emul_trace_and_stop(__func__);
}
size_t ksize(const void * objp)
{
if (objp == NULL)
return 0;
return __ksize(objp);
}

View File

@ -0,0 +1,149 @@
/*
* \brief Replaces mm/slub.c
* \author Stefan Kalkowski
* \date 2021-03-16
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <linux/mm.h>
#include <linux/slab.h>
#include <../mm/slab.h>
#include <lx_emul/alloc.h>
#include <lx_emul/debug.h>
/*
* We do not use those caches now,
* but it is referenced by some compilation unit
*/
struct kmem_cache *
kmalloc_caches[NR_KMALLOC_TYPES][KMALLOC_SHIFT_HIGH + 1] = { NULL };
void kfree(const void * x)
{
lx_emul_mem_free(x);
}
void * __kmalloc(size_t size, gfp_t flags)
{
/* DMA memory is not implemented yet */
if (flags & GFP_DMA) lx_emul_trace_and_stop(__func__);
return lx_emul_mem_alloc(size);
}
struct kmem_cache * kmem_cache_create(const char * name,
unsigned int size,
unsigned int align,
slab_flags_t flags,
void (* ctor)(void *))
{
struct kmem_cache * cache =
__kmalloc(sizeof(struct kmem_cache), GFP_KERNEL);
cache->size = size;
cache->align = align;
return cache;
}
void kmem_cache_free(struct kmem_cache * s, void * x)
{
lx_emul_mem_free(x);
}
void * __kmalloc_track_caller(size_t size,
gfp_t gfpflags,
unsigned long caller)
{
return __kmalloc(size, gfpflags);
}
void * __kmalloc_node_track_caller(size_t size,
gfp_t gfpflags,
int node,
unsigned long caller)
{
return __kmalloc_track_caller(size, gfpflags, caller);
}
static inline unsigned int kmem_cache_array_size_per_idx(unsigned idx)
{
switch (idx) {
case 0: return 0;
case 1: return 96;
case 2: return 192;
default: return 1 << idx;
};
}
void __init kmem_cache_init(void)
{
unsigned i;
for (i = 0; i <= KMALLOC_SHIFT_HIGH; i++) {
unsigned int sz = kmem_cache_array_size_per_idx(i);
kmalloc_caches[KMALLOC_NORMAL][i] =
kmem_cache_create("", sz, sz, GFP_KERNEL, NULL);
}
}
void * kmem_cache_alloc(struct kmem_cache * s, gfp_t flags)
{
if (!s)
lx_emul_trace_and_stop(__func__);
return lx_emul_mem_alloc_aligned(s->size, s->align ? s->align : 32);
}
size_t __ksize(const void * object)
{
return lx_emul_mem_size(object);
}
#ifdef CONFIG_NUMA
void * __kmalloc_node(size_t size, gfp_t flags, int node)
{
return __kmalloc(size, flags);
}
void * kmem_cache_alloc_node(struct kmem_cache * s, gfp_t gfpflags, int node)
{
return kmem_cache_alloc(s, gfpflags);
}
#endif /* CONFIG_NUMA */
#ifdef CONFIG_TRACING
void * kmem_cache_alloc_node_trace(struct kmem_cache * s,
gfp_t gfpflags,
int node,
size_t size)
{
return kmem_cache_alloc(s, gfpflags);
}
void * kmem_cache_alloc_trace(struct kmem_cache * s,
gfp_t gfpflags,
size_t size)
{
return __kmalloc(size, gfpflags);
}
#endif /* CONFIG_TRACING */

View File

@ -0,0 +1,163 @@
/*
* \brief Linux Kernel initialization
* \author Stefan Kalkowski
* \date 2021-03-16
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <lx_emul/init.h>
#include <lx_emul/page_virt.h>
#include <lx_emul/time.h>
#include <lx_user/init.h>
#include <asm/irq_regs.h>
#include <linux/cpu.h>
#include <linux/delay.h>
#include <linux/init_task.h>
#include <linux/interrupt.h>
#include <linux/irqchip.h>
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/slab.h>
#include <linux/tick.h>
#include <linux/of.h>
#include <linux/of_clk.h>
#include <linux/of_fdt.h>
/* definitions in drivers/base/base.h */
extern int devices_init(void);
extern int buses_init(void);
extern int classes_init(void);
extern int platform_bus_init(void);
enum system_states system_state;
static __initdata DECLARE_COMPLETION(kthreadd_done);
static int kernel_init(void * args)
{
struct task_struct *tsk = current;
set_task_comm(tsk, "init");
wait_for_completion(&kthreadd_done);
workqueue_init();
/* the following calls are from driver_init() of drivers/base/init.c */
devices_init();
buses_init();
classes_init();
of_core_init();
platform_bus_init();
lx_emul_initcalls();
system_state = SYSTEM_RUNNING;
lx_user_init();
lx_emul_task_schedule(true);
return 0;
}
static void timer_loop(void)
{
for (;;) {
tick_nohz_idle_enter();
lx_emul_task_schedule(true);
lx_emul_time_handle();
tick_nohz_idle_exit();
}
}
int lx_emul_init_task_function(void * dtb)
{
int pid;
/* Set dummy task registers used in IRQ and time handling */
static struct pt_regs regs;
set_irq_regs(&regs);
/* Run emulation library self-tests before starting kernel */
lx_emul_associate_page_selftest();
/**
* Here we do the minimum normally done start_kernel() of init/main.c
*/
/* calls from setup_arch of arch/arm64/kernel/setup.c */
early_init_dt_scan(dtb);
unflatten_device_tree();
jump_label_init();
kmem_cache_init();
radix_tree_init();
workqueue_init_early();
early_irq_init();
irqchip_init();
tick_init();
init_timers();
hrtimers_init();
timekeeping_init();
/* arch/arm64/kernel/time.c */
lx_emul_time_init(); /* replaces timer_probe() */
tick_setup_hrtimer_broadcast();
lpj_fine = 1000000 / HZ;
/* arch/arm64/kernel/time.c end */
sched_clock_init();
kernel_thread(kernel_init, NULL, CLONE_FS);
pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
kthreadd_task = find_task_by_pid_ns(pid, NULL);;
system_state = SYSTEM_SCHEDULING;
complete(&kthreadd_done);
lx_emul_task_schedule(false);
timer_loop();
return 0;
}
struct task_struct init_task = {
.state = 0,
.usage = REFCOUNT_INIT(2),
.flags = PF_KTHREAD,
.prio = MAX_PRIO - 20,
.static_prio = MAX_PRIO - 20,
.normal_prio = MAX_PRIO - 20,
.policy = SCHED_NORMAL,
.cpus_ptr = &init_task.cpus_mask,
.cpus_mask = CPU_MASK_ALL,
.nr_cpus_allowed = 1,
.mm = NULL,
.active_mm = NULL,
.tasks = LIST_HEAD_INIT(init_task.tasks),
.real_parent = &init_task,
.parent = &init_task,
.children = LIST_HEAD_INIT(init_task.children),
.sibling = LIST_HEAD_INIT(init_task.sibling),
.group_leader = &init_task,
.comm = INIT_TASK_COMM,
.thread = INIT_THREAD,
.pending = {
.list = LIST_HEAD_INIT(init_task.pending.list),
.signal = {{0}}
},
.blocked = {{0}},
};
void * lx_emul_init_task_struct = &init_task;

View File

@ -0,0 +1,77 @@
/*
* \brief Lx_emul task backend
* \author Stefan Kalkowski
* \date 2021-05-05
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <lx_emul/task.h>
#include <lx_kit/env.h>
#include <lx_kit/task.h>
extern "C" struct task_struct * lx_emul_task_get_current(void)
{
return (struct task_struct*)
Lx_kit::env().scheduler.current().lx_task();
}
extern "C"
void lx_emul_task_create(struct task_struct * task,
const char * name,
int pid,
int (* threadfn)(void * data),
void * data)
{
new (Lx_kit::env().heap) Lx_kit::Task(threadfn,
data,
(void*)task, pid, name,
Lx_kit::env().scheduler,
Lx_kit::Task::NORMAL);
}
extern "C" void lx_emul_task_unblock(struct task_struct * t)
{
Lx_kit::env().scheduler.task((void*)t).unblock();
}
extern "C" void lx_emul_task_priority(struct task_struct * t,
unsigned long prio)
{
Lx_kit::env().scheduler.task((void*)t).priority(prio);
}
extern "C" void lx_emul_task_schedule(int block)
{
Lx_kit::Task & task = Lx_kit::env().scheduler.current();
if (block) task.block();
task.schedule();
}
extern "C" struct task_struct * lx_emul_task_get(int pid)
{
void * ret = nullptr;
Lx_kit::env().scheduler.for_each_task([&] (Lx_kit::Task & task) {
if (pid == task.pid())
ret = task.lx_task();
});
return (task_struct*) ret;
}
extern "C" void lx_emul_task_name(struct task_struct * t, const char * name)
{
Lx_kit::env().scheduler.task((void*)t).name(name);
}

View File

@ -0,0 +1,36 @@
/*
* \brief Lx_emul time backend
* \author Stefan Kalkowski
* \date 2021-05-05
*/
/*
* Copyright (C) 2021 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/debug.h>
#include <lx_emul/time.h>
extern "C" void lx_emul_time_event(unsigned long evt)
{
Lx_kit::env().timeout.start(evt);
}
extern "C" void lx_emul_time_stop()
{
Lx_kit::env().timeout.stop();
}
extern "C" unsigned long lx_emul_time_counter()
{
unsigned long ret = Lx_kit::env().timer.curr_time().trunc_to_plain_us().value;
return ret;
}

View File

@ -0,0 +1,93 @@
/*
* \brief Linux DDE virt-to-page implementation
* \author Norman Feske
* \date 2021-07-02
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <linux/slab.h>
#include <linux/page_ref.h>
#include <lx_emul/page_virt.h>
struct page *lx_emul_virt_to_pages(void const *virt, unsigned count)
{
/* sanitize argument */
void * const page_aligned_virt = (void *)((uintptr_t)virt & PAGE_MASK);
struct page *page = lx_emul_associated_page(page_aligned_virt, 1);
if (!page) {
unsigned i;
struct page * p = kzalloc(sizeof(struct page)*count, 0);
page = p;
for (i = 0; i < count; i++, p++) {
p->virtual = (void*)((uintptr_t)page_aligned_virt + i*PAGE_SIZE);
init_page_count(page);
lx_emul_associate_page_with_virt_addr(p, p->virtual);
}
}
/* consistency check */
if (page_aligned_virt != page->virtual)
BUG();
return page;
}
void lx_emul_forget_pages(void const *virt, unsigned long size)
{
for (;;) {
struct page *page = lx_emul_associated_page(virt, size);
if (!page)
return;
lx_emul_disassociate_page_from_virt_addr(page->virtual);
kfree(page);
}
}
#define LX_EMUL_ASSERT(cond) { if (!(cond)) {\
printk("assertion failed at line %d: %s\n", __LINE__, #cond); \
lx_emul_trace_and_stop("abort"); } }
void lx_emul_associate_page_selftest()
{
struct page *p1 = (struct page *)1;
struct page *p2 = (struct page *)2;
struct page *p3 = (struct page *)3;
void *v1 = (void *)0x11000;
void *v2 = (void *)0x12000;
void *v3 = (void *)0x13000;
lx_emul_associate_page_with_virt_addr(p1, v1);
lx_emul_associate_page_with_virt_addr(p2, v2);
lx_emul_associate_page_with_virt_addr(p3, v3);
LX_EMUL_ASSERT(lx_emul_associated_page(v1, 1) == p1);
LX_EMUL_ASSERT(lx_emul_associated_page(v2, 1) == p2);
LX_EMUL_ASSERT(lx_emul_associated_page(v3, 1) == p3);
LX_EMUL_ASSERT(lx_emul_associated_page((void *)((uintptr_t)v1 + 4095), 1) == p1);
LX_EMUL_ASSERT(lx_emul_associated_page((void *)((uintptr_t)v1 - 1), 1) == NULL);
LX_EMUL_ASSERT(lx_emul_associated_page((void *)((uintptr_t)v2 & PAGE_MASK), 1) == p2);
LX_EMUL_ASSERT(lx_emul_associated_page((void *)0x10000, 0x10000) == p2);
lx_emul_disassociate_page_from_virt_addr(v2);
LX_EMUL_ASSERT(lx_emul_associated_page((void *)0x10000, 0x10000) == p3);
lx_emul_disassociate_page_from_virt_addr(v3);
LX_EMUL_ASSERT(lx_emul_associated_page((void *)0x10000, 0x10000) == p1);
lx_emul_disassociate_page_from_virt_addr(v1);
LX_EMUL_ASSERT(lx_emul_associated_page((void *)0x10000, 0x10000) == NULL);
}

View File

@ -0,0 +1,338 @@
/*
* \brief Lx_kit format string backend
* \author Stefan Kalkowski
* \author Sebastian Sumpf
* \date 2021-03-17
*
* Greatly inspired by the former DDE Linux Lx_kit implementation.
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
/* Genode includes */
#include <base/log.h>
#include <util/string.h>
/* local includes */
#include <lx_kit/console.h>
namespace Lx_kit { class Format_command; }
/**
* Format string command representation
*/
class Lx_kit::Format_command
{
public:
enum Type { INT, UINT, STRING, CHAR, PTR, PERCENT, VA_FORMAT, MAC,
IPV4, INVALID };
enum Length { DEFAULT, LONG, SIZE_T, LONG_LONG };
private:
/**
* Read decimal value from string
*/
int decode_decimal(const char *str, int *consumed)
{
int res = 0;
while (1) {
char c = str[*consumed];
if (!c || c < '0' || c > '0' + 9)
return res;
res = (res * 10) + c - '0';
(*consumed)++;
}
}
public:
Type type = INVALID; /* format argument type */
Length length = DEFAULT; /* format argument length */
int padding = 0; /* min number of characters to print */
int base = 10; /* base of numeric arguments */
bool zeropad = false; /* pad with zero instead of space */
bool uppercase = false; /* use upper case for hex numbers */
bool prefix = false; /* prefix with 0x */
int consumed = 0; /* nb of consumed format string chars */
/**
* Constructor
*
* \param format begin of command in format string
*/
explicit Format_command(const char *format)
{
/* check for command begin and eat the character */
if (format[consumed] != '%')
return;
if (!format[++consumed])
return;
/* check for %$x syntax */
prefix = (format[consumed] == '#') || (format[consumed] == '.');
if (prefix && !format[++consumed])
return;
/* heading zero indicates zero-padding */
zeropad = (format[consumed] == '0');
/* read decimal padding value */
padding = decode_decimal(format, &consumed);
if (!format[consumed])
return;
/* decode length */
switch (format[consumed]) {
case 'l':
{
/* long long ints are marked by a subsequenting 'l' character */
bool is_long_long = (format[consumed + 1] == 'l');
length = is_long_long ? LONG_LONG : LONG;
consumed += is_long_long ? 2 : 1;
break;
}
case 'z':
case 'Z':
length = SIZE_T;
consumed++;
break;
case 'p':
length = LONG;
break;
default:
break;
}
if (!format[consumed])
return;
/* decode type */
switch (format[consumed]) {
case 'd':
case 'i': type = INT; base = 10; break;
case 'o': type = UINT; base = 8; break;
case 'u': type = UINT; base = 10; break;
case 'x': type = UINT; base = 16; break;
case 'X': type = UINT; base = 16; uppercase = 1; break;
case 'p': type = PTR; base = 16; break;
case 'c': type = CHAR; break;
case 's': type = STRING; break;
case '%': type = PERCENT; break;
case 0: return;
default: break;
}
/* eat type character */
consumed++;
if (type != PTR || !format[consumed])
return;
switch (format[consumed]) {
case 'V': type = VA_FORMAT; break;
case 'M': type = MAC; base = 16; padding = 2; break;
case 'I':
if (format[consumed + 1] != '4') break;
consumed++;
type = IPV4; base = 10;
break;
default: return;
}
consumed++;
}
int numeric()
{
return (type == INT || type == UINT || type == PTR);
}
};
void Lx_kit::Console::_flush()
{
if (!_idx)
return;
_buf[_idx] = 0;
log(Cstring(_buf));
_idx = 0;
}
void Lx_kit::Console::_out_char(char c)
{
if (c == '\n' || _idx == BUF_SIZE || c == 0)
_flush();
else
_buf[_idx++] = c;
}
void Lx_kit::Console::_out_string(const char *str)
{
if (str)
while (*str) _out_char(*str++);
else
_flush();
}
void Lx_kit::Console::vprintf(const char *format, va_list list)
{
while (*format) {
/* eat and output plain characters */
if (*format != '%') {
_out_char(*format++);
continue;
}
/* parse format argument descriptor */
Format_command cmd(format);
/* read numeric argument from va_list */
long long numeric_arg = 0;
if (cmd.numeric()) {
switch (cmd.length) {
case Format_command::LONG_LONG:
numeric_arg = va_arg(list, long long);
break;
case Format_command::LONG:
numeric_arg = (cmd.type == Format_command::UINT) ?
(long long)va_arg(list, unsigned long) : va_arg(list, long);
break;
case Format_command::SIZE_T:
numeric_arg = va_arg(list, size_t);
break;
case Format_command::DEFAULT:
numeric_arg = (cmd.type == Format_command::UINT) ?
(long long)va_arg(list, unsigned int) : va_arg(list, int);
break;
}
}
/* call type-specific output routines */
switch (cmd.type) {
case Format_command::INT:
if (cmd.length == Format_command::LONG_LONG)
_out_signed<long long>(numeric_arg, cmd.base);
else
_out_signed<long>(numeric_arg, cmd.base);
break;
case Format_command::UINT:
if (cmd.prefix && cmd.base == 16)
_out_string("0x");
if (cmd.length == Format_command::LONG_LONG) {
_out_unsigned<unsigned long long>(numeric_arg, cmd.base, cmd.padding);
break;
}
/* fall through */
case Format_command::PTR:
_out_unsigned<unsigned long>(numeric_arg, cmd.base, cmd.padding);
break;
case Format_command::CHAR:
_out_char(va_arg(list, int));
break;
case Format_command::STRING:
_out_string(va_arg(list, const char *));
break;
case Format_command::PERCENT:
_out_char('%');
break;
case Format_command::VA_FORMAT: /* %pV */
{
struct va_format
{
const char *fmt;
va_list *va;
};
va_list va;
va_format *vf = va_arg(list, va_format *);
va_copy(va, *vf->va);
vprintf(vf->fmt, va);
va_end(va);
}
break;
case Format_command::MAC: /* %pM */
{
unsigned char const *mac = va_arg(list, unsigned char const *);
for (int i = 0; i < 6; i++) {
if (i) _out_char(':');
_out_unsigned<unsigned char>(mac[i], cmd.base, cmd.padding);
}
break;
}
case Format_command::IPV4: /* %pI4 */
{
unsigned char const *ip = va_arg(list, unsigned char const *);
for (int i = 0; i < 4; i++) {
if (i) _out_char('.');
_out_unsigned<unsigned char>(ip[i], cmd.base, cmd.padding);
}
}
break;
case Format_command::INVALID:
_out_string("<warning: unsupported format string argument>");
/* consume the argument of the unsupported command */
va_arg(list, long);
break;
}
/* proceed with format string after command */
format += cmd.consumed;
}
}

View File

@ -0,0 +1,240 @@
/*
* \brief Lx_kit device
* \author Stefan Kalkowski
* \date 2021-05-05
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <lx_kit/env.h>
using namespace Lx_kit;
/********************
** Device::Io_mem **
********************/
bool Device::Io_mem::match(addr_t addr, size_t size)
{
return (this->addr <= addr) &&
((this->addr + this->size) >= (addr + size));
}
/*************************
** Device::Irq_handler **
*************************/
void Device::Irq_handler::_handle()
{
env().last_irq = _number;
env().scheduler.unblock_irq_handler();
env().scheduler.schedule();
}
Device::Irq_handler::Irq_handler(Platform::Device & dev,
Platform::Device::Irq::Index idx,
unsigned number)
:
_irq(dev, idx),
_handler(Lx_kit::env().env.ep(), *this, &Irq_handler::_handle),
_number(number)
{
_irq.sigh_omit_initial_signal(_handler);
_irq.ack();
}
/************
** Device **
************/
const char * Device::compatible()
{
return _type.name.string();
}
const char * Device::name()
{
return _name.string();
}
clk * Device::clock(const char * name)
{
clk * ret = nullptr;
_for_each_clock([&] (Clock & c) {
if (c.name == name) {
enable();
ret = &c.lx_clock;
}
});
return ret;
}
clk * Device::clock(unsigned idx)
{
clk * ret = nullptr;
_for_each_clock([&] (Clock & c) {
if (c.idx == idx) {
enable();
ret = &c.lx_clock;
}
});
return ret;
}
bool Device::io_mem(addr_t phys_addr, size_t size)
{
bool ret = false;
_for_each_io_mem([&] (Io_mem & io) {
if (io.match(phys_addr, size))
ret = true;
});
return ret;
}
void * Device::io_mem_local_addr(addr_t phys_addr, size_t size)
{
void * ret = nullptr;
_for_each_io_mem([&] (Io_mem & io) {
if (!io.match(phys_addr, size))
return;
enable();
if (!io.io_mem.constructed())
io.io_mem.construct(*_pdev, io.idx);
ret = (void*)((addr_t)io.io_mem->local_addr<void>()
+ (phys_addr - io.addr));
});
return ret;
}
bool Device::irq_unmask(unsigned number)
{
bool ret = false;
_for_each_irq([&] (Irq & irq) {
if (irq.number != number)
return;
enable();
if (irq.handler.constructed())
return;
irq.handler.construct(*_pdev, irq.idx, number);
ret = true;
});
return ret;
}
void Device::irq_mask(unsigned number)
{
if (!_pdev.constructed())
return;
_for_each_irq([&] (Irq & irq) {
if (irq.number != number)
return;
irq.handler.destruct();
});
}
void Device::irq_ack(unsigned number)
{
if (!_pdev.constructed())
return;
_for_each_irq([&] (Irq & irq) {
if (irq.number != number)
return;
irq.handler->ack();
});
}
void Device::enable()
{
if (_pdev.constructed())
return;
_pdev.construct(_platform, _type);
_platform.update();
_platform.with_xml([&] (Xml_node & xml) {
xml.for_each_sub_node("device", [&] (Xml_node node) {
if (_name != node.attribute_value("name", Device::Name()))
return;
node.for_each_sub_node("clock", [&] (Xml_node node) {
clk * c = clock(node.attribute_value("name", Device::Name()).string());
if (!c)
return;
c->rate = node.attribute_value("rate", 0UL);
});
});
});
}
Device::Device(Platform::Connection & plat,
Xml_node & xml,
Heap & heap)
:
_platform(plat),
_name(xml.attribute_value("name", Device::Name())),
_type{xml.attribute_value("type", Device::Name())}
{
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));
});
i = 0;
xml.for_each_sub_node("irq", [&] (Xml_node node) {
_irqs.insert(new (heap) Irq(i++, node.attribute_value("number", 0U)));
});
i = 0;
xml.for_each_sub_node("clock", [&] (Xml_node node) {
Device::Name name = node.attribute_value("name", Device::Name());
_clocks.insert(new (heap) Device::Clock(i++, name));
});
}
/*****************
** Device_list **
*****************/
Device_list::Device_list(Heap & heap, Platform::Connection & platform)
:
_platform(platform)
{
_platform.with_xml([&] (Xml_node & xml) {
xml.for_each_sub_node("device", [&] (Xml_node node) {
insert(new (heap) Device(_platform, node, heap));
});
});
}

View File

@ -0,0 +1,21 @@
/*
* \brief Lx_kit environment
* \author Stefan Kalkowski
* \date 2021-03-16
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <lx_kit/env.h>
Lx_kit::Env & Lx_kit::env(Genode::Env * env)
{
static Lx_kit::Env environment(*env);
return environment;
}

View File

@ -0,0 +1,47 @@
/*
* \brief Lx_kit backend for Linux kernel initialization
* \author Stefan Kalkowski
* \date 2021-03-10
*/
/*
* Copyright (C) 2021 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_kit/init.h>
namespace Lx_kit { class Initcalls; }
void Lx_kit::Initcalls::add(int (*initcall)(void), unsigned int prio) {
_call_list.insert(new (_heap) E(prio, initcall)); }
void Lx_kit::Initcalls::execute_in_order()
{
unsigned min = ~0U;
unsigned max = 0;
for (E * entry = _call_list.first(); entry; entry = entry->next()) {
if (entry->prio < min) min = entry->prio;
if (entry->prio > max) max = entry->prio;
}
for (unsigned i = min; i <= max; i++) {
for (E * entry = _call_list.first(); entry; entry = entry->next()) {
if (entry->prio == i) entry->call();
}
}
}
void Lx_kit::initialize(Genode::Env & env)
{
Lx_kit::env(&env);
env.exec_static_constructors();
}

View File

@ -0,0 +1,148 @@
/*
* \brief Lx_kit memory allocation backend
* \author Stefan Kalkowski
* \date 2021-03-25
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
/* Genode includes */
#include <base/log.h>
#include <os/backtrace.h>
#include <platform_session/connection.h>
#include <util/touch.h>
/* local includes */
#include <lx_kit/memory.h>
Genode::Attached_dataspace & Lx_kit::Mem_allocator::alloc_dataspace(size_t size)
{
Ram_dataspace_capability ds_cap;
try {
size_t ds_size = align_addr(size, 12);
ds_cap = _platform.alloc_dma_buffer(ds_size, _cache_attr);
addr_t dma_addr = _platform.dma_addr(ds_cap);
Buffer & buffer = *new (_heap)
Registered<Buffer>(_buffers, _env.rm(), ds_cap, dma_addr);
addr_t addr = (addr_t)buffer.ds().local_addr<void>();
/* map eager by touching all pages once */
for (size_t sz = 0; sz < ds_size; sz += 4096) {
touch_read((unsigned char const volatile*)(addr + sz)); }
return buffer.ds();
} catch (Out_of_caps) {
_platform.free_dma_buffer(ds_cap);
throw;
}
}
void * Lx_kit::Mem_allocator::alloc(size_t size, size_t align)
{
if (!size)
return nullptr;
void * out_addr = nullptr;
if (_mem.alloc_aligned(size, &out_addr, log2(align)).error()) {
/*
* Restrict the minimum buffer size to avoid the creation of
* a separate dataspaces for tiny allocations.
*/
size_t const min_buffer_size = 256*1024;
/*
* Allocate one excess byte that is not officially registered at
* the '_mem' ranges. This way, two virtual consecutive ranges
* (that must be assumed to belong to non-contiguous physical
* ranges) can never be merged when freeing an allocation. Such
* a merge would violate the assumption that a both the virtual
* and physical addresses of a multi-page allocation are always
* contiguous.
*/
Attached_dataspace & ds = alloc_dataspace(max(size + 1, min_buffer_size));
_mem.add_range((addr_t)ds.local_addr<void>(), ds.size() - 1);
/* re-try allocation */
_mem.alloc_aligned(size, &out_addr, log2(align));
}
if (!out_addr) {
error("memory allocation failed for ", size, " align ", align);
backtrace();
}
else
memset(out_addr, 0, size);
return out_addr;
}
Genode::addr_t Lx_kit::Mem_allocator::dma_addr(void * addr)
{
addr_t ret = 0UL;
_buffers.for_each([&] (Buffer & b) {
addr_t other = (addr_t)addr;
addr_t addr = (addr_t)b.ds().local_addr<void>();
if (addr > other || (addr+b.ds().size()) <= other)
return;
/* byte offset of 'addr' from start of block */
addr_t const offset = other - addr;
ret = b.dma_addr() + offset;
});
return ret;
}
bool Lx_kit::Mem_allocator::free(const void * ptr)
{
if (!_mem.valid_addr((addr_t)ptr))
return false;
_mem.free(const_cast<void*>(ptr));
return true;
}
void Lx_kit::Mem_allocator::free(Attached_dataspace * ds)
{
Dataspace_capability cap = ds->cap();
Registered<Buffer> * buffer = nullptr;
_buffers.for_each([&] (Buffer & b) {
if (&b.ds() == ds)
buffer = static_cast<Registered<Buffer>*>(&b);
});
destroy(_heap, buffer);
_platform.free_dma_buffer(static_cap_cast<Ram_dataspace>(cap));
}
Genode::size_t Lx_kit::Mem_allocator::size(const void * ptr)
{
return ptr ? _mem.size_at(ptr) : 0;
}
Lx_kit::Mem_allocator::Mem_allocator(Genode::Env & env,
Heap & heap,
Platform::Connection & platform,
Cache cache_attr)
:
_env(env), _heap(heap), _platform(platform),
_cache_attr(cache_attr), _mem(&heap) {}

View File

@ -0,0 +1,129 @@
/*
* \brief Scheduler for executing Lx::Task objects
* \author Sebastian Sumpf
* \author Josef Soentgen
* \author Norman Feske
* \author Stefan Kalkowski
* \date 2014-10-10
*/
/*
* Copyright (C) 2014-2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
/* Genode includes */
#include <base/log.h>
#include <base/sleep.h>
#include <os/backtrace.h>
#include <lx_kit/scheduler.h>
#include <lx_kit/task.h>
using namespace Genode;
using namespace Lx_kit;
Task & Scheduler::current()
{
if (!_current) {
error("Lx_kit::Scheduler::_current is zero!");
backtrace();
sleep_forever();
}
return *_current;
}
bool Scheduler::active() const
{
return _current != nullptr;
}
void Scheduler::add(Task & task)
{
Task * prev = nullptr;
Task * next = _present_list.first();
for (; next; prev = next, next = prev->next()) {
if (next->priority() >= task.priority())
break;
}
_present_list.insert(&task, prev);
}
void Scheduler::remove(Task & task)
{
_present_list.remove(&task);
}
void Scheduler::unblock_irq_handler()
{
for (Task * t = _present_list.first(); t; t = t->next()) {
if (t->type() == Task::IRQ_HANDLER) t->unblock();
}
}
void Scheduler::unblock_time_handler()
{
for (Task * t = _present_list.first(); t; t = t->next()) {
if (t->type() == Task::TIME_HANDLER) t->unblock();
}
}
Task & Scheduler::task(void * lx_task)
{
for (Task * t = _present_list.first(); t; t = t->next()) {
if (t->lx_task() == lx_task)
return *t;
}
error("Lx_kit::Scheduler cannot find task ", lx_task);
sleep_forever();
}
void Scheduler::schedule()
{
/*
* Iterate over all tasks and run first runnable.
*
* (1) If one runnable tasks was run start over from beginning of
* list.
*
* (2) If no task is runnable quit scheduling (break endless
* loop).
*/
while (true) {
bool at_least_one = false;
/* update jiffies before running task */
//Lx::timer_update_jiffies();
for (Task * t = _present_list.first(); t; t = t->next()) {
if (!t->runnable())
continue;
/* update current before running task */
_current = t;
t->run();
at_least_one = true;
if (!t->runnable())
break;
}
if (!at_least_one)
break;
}
/* clear current as no task is running */
_current = nullptr;
}

View File

@ -0,0 +1,108 @@
/*
* Copyright (c) 2014 Andrew Turner
* Copyright (c) 2014 The FreeBSD Foundation
* All rights reserved.
*
* Portions of this software were developed by Andrew Turner
* under sponsorship from the FreeBSD Foundation
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
/* needed parts from <machine/asm.h> */
#define __FBSDID(x)
#define ENTRY(sym) .text; .globl sym; .align 2; .type sym,#function; sym:
#define END(sym) .size sym, . - sym
/* end of <machine/asm.h> */
__FBSDID("$FreeBSD: releng/12.0/lib/libc/aarch64/gen/setjmp.S 313146 2017-02-03 11:51:06Z andrew $");
/* needed parts from <machine/setjmp.h> */
#define _JB_SIGMASK 22
#define _JB_MAGIC__SETJMP 0xfb5d25837d7ff700
/* end of <machine/asm.h> */
ENTRY(_setjmp)
/* Store the magic value and stack pointer */
ldr x8, .Lmagic
mov x9, sp
stp x8, x9, [x0], #16
/* Store the general purpose registers and lr */
stp x19, x20, [x0], #16
stp x21, x22, [x0], #16
stp x23, x24, [x0], #16
stp x25, x26, [x0], #16
stp x27, x28, [x0], #16
stp x29, lr, [x0], #16
#ifndef _STANDALONE
/* Store the vfp registers */
stp d8, d9, [x0], #16
stp d10, d11, [x0], #16
stp d12, d13, [x0], #16
stp d14, d15, [x0]
#endif
/* Return value */
mov x0, #0
ret
.align 3
.Lmagic:
.quad _JB_MAGIC__SETJMP
END(_setjmp)
ENTRY(_longjmp)
/* Check the magic value */
ldr x8, [x0], #8
ldr x9, .Lmagic
cmp x8, x9
b.ne botch
/* Restore the stack pointer */
ldr x8, [x0], #8
mov sp, x8
/* Restore the general purpose registers and lr */
ldp x19, x20, [x0], #16
ldp x21, x22, [x0], #16
ldp x23, x24, [x0], #16
ldp x25, x26, [x0], #16
ldp x27, x28, [x0], #16
ldp x29, lr, [x0], #16
#ifndef _STANDALONE
/* Restore the vfp registers */
ldp d8, d9, [x0], #16
ldp d10, d11, [x0], #16
ldp d12, d13, [x0], #16
ldp d14, d15, [x0]
#endif
/* Load the return value */
mov x0, x1
ret
botch:
b botch
END(_longjmp)

View File

@ -0,0 +1,152 @@
/*
* \brief Lx::Task represents a cooperatively scheduled thread of control
* \author Sebastian Sumpf
* \author Josef Soentgen
* \author Norman Feske
* \date 2014-10-10
*/
/*
* Copyright (C) 2014-2017 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <base/thread.h>
#include <base/sleep.h>
#include <lx_kit/task.h>
#include <lx_kit/scheduler.h>
#include <os/backtrace.h>
using namespace Lx_kit;
bool Task::runnable() const
{
switch (_state) {
case INIT: return true;
case RUNNING: return true;
case BLOCKED: return false;
}
error("Invalid task state?!");
return false;
}
static inline void * _alloc_stack(const char * name)
{
enum { STACK_SIZE = 32 * 1024 };
Genode::Thread * th = Genode::Thread::myself();
return th->alloc_secondary_stack(name, STACK_SIZE);
}
Task::State Task::state() const { return _state; }
Task::Type Task::type() const { return _type; }
void * Task::lx_task() const { return _lx_task; }
int Task::pid() const { return _pid; }
int Task::priority() const { return _priority; }
void Task::priority(int prio)
{
_scheduler.remove(*this);
_priority = prio;
_scheduler.add(*this);
}
void Task::name(const char * name) { _name = Task::Name(name); }
Task::Name Task::name() const { return _name; }
void Task::block()
{
if (_state == RUNNING) _state = BLOCKED;
}
void Task::unblock()
{
if (_state == BLOCKED) _state = RUNNING;
}
void Task::run()
{
/*
* Save the execution environment. The scheduled task returns to this point
* after execution, i.e., at the next preemption point.
*/
if (_setjmp(_saved_env))
return;
if (_state == INIT) {
/* setup execution environment and call task's function */
_state = RUNNING;
/* switch stack and call '_func(_arg)' */
arch_execute(_stack, (void *)_func, _arg);
} else {
/* restore execution environment */
_longjmp(_env, 1);
}
/* never reached */
error("unexpected return of task");
sleep_forever();
}
void Task::schedule()
{
/*
* Save the execution environment. The task will resume from here on next
* schedule.
*/
if (_setjmp(_env))
return;
/* return to thread calling run() */
_longjmp(_saved_env, 1);
}
void Task::block_and_schedule()
{
block();
schedule();
}
Task::Task(int (* func)(void*),
void * arg,
void * lx_task,
int pid,
char const * name,
Scheduler & scheduler,
Type type)
: _type(type),
_scheduler(scheduler),
_lx_task(lx_task),
_pid(pid),
_name(name),
_stack(_alloc_stack(name)),
_func(func),
_arg(arg)
{
_scheduler.add(*this);
}
Task::~Task() { _scheduler.remove(*this); }

View File

@ -0,0 +1,39 @@
/*
* \brief Lx_kit timeout backend
* \author Stefan Kalkowski
* \date 2021-05-05
*/
/*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <lx_kit/env.h>
void Lx_kit::Timeout::start(unsigned long us)
{
_timeout.schedule(Microseconds(us));
}
void Lx_kit::Timeout::stop()
{
_timeout.discard();
}
void Lx_kit::Timeout::_handle(Genode::Duration)
{
_scheduler.unblock_time_handler();
_scheduler.schedule();
}
Lx_kit::Timeout::Timeout(Timer::Connection & timer,
Scheduler & scheduler)
:
_scheduler(scheduler),
_timeout(timer, *this, &Timeout::_handle) { }