mirror of
https://github.com/mmueller41/genode.git
synced 2026-01-21 12:32:56 +01:00
Querying the RFKILL state led to execution of the Lx_kit::scheduler by the pthread running the wpa_supplicant. As this may not happen the RFKILL state is now solely managed by the driver and only the cached state is read by the supplicant. Fixes #4537.
592 lines
11 KiB
C
592 lines
11 KiB
C
/*
|
|
* \brief Linux emulation environment specific to this driver
|
|
* \author Josef Soentgen
|
|
* \date 2022-02-09
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2022 Genode Labs GmbH
|
|
*
|
|
* This file is distributed under the terms of the GNU General Public License
|
|
* version 2.
|
|
*/
|
|
|
|
#include <lx_emul.h>
|
|
#include <linux/slab.h>
|
|
|
|
#include <lx_emul/random.h>
|
|
#include <lx_emul/alloc.h>
|
|
#include <lx_emul/io_mem.h>
|
|
|
|
|
|
#include <asm-generic/delay.h>
|
|
#include <linux/delay.h>
|
|
|
|
void __const_udelay(unsigned long xloops)
|
|
{
|
|
unsigned long usecs = xloops / 0x10C7UL;
|
|
if (usecs < 100)
|
|
lx_emul_time_udelay(usecs);
|
|
else
|
|
usleep_range(usecs, usecs * 10);
|
|
}
|
|
|
|
|
|
#include <linux/slab.h>
|
|
|
|
struct kmem_cache * kmem_cache_create_usercopy(const char * name,
|
|
unsigned int size,
|
|
unsigned int align,
|
|
slab_flags_t flags,
|
|
unsigned int useroffset,
|
|
unsigned int usersize,
|
|
void (* ctor)(void *))
|
|
{
|
|
return kmem_cache_create(name, size, align, flags, ctor);
|
|
}
|
|
|
|
|
|
void kmem_cache_free_bulk(struct kmem_cache *s, size_t size, void **p)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < size; i++) {
|
|
kmem_cache_free(s, p[i]);
|
|
}
|
|
}
|
|
|
|
|
|
#include <linux/fs.h>
|
|
|
|
int register_filesystem(struct file_system_type * fs)
|
|
{
|
|
lx_emul_trace(__func__);
|
|
return 0;
|
|
}
|
|
|
|
|
|
#include <linux/slab.h>
|
|
#include <linux/mount.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/fs_context.h>
|
|
#include <linux/pseudo_fs.h>
|
|
|
|
struct pseudo_fs_context * init_pseudo(struct fs_context *fc,
|
|
unsigned long magic)
|
|
{
|
|
struct pseudo_fs_context *pfs_ctx;
|
|
|
|
pfs_ctx = kzalloc(sizeof (struct pseudo_fs_context), GFP_KERNEL);
|
|
if (pfs_ctx) {
|
|
pfs_ctx->magic = magic;
|
|
fc->fs_private = pfs_ctx;
|
|
}
|
|
return pfs_ctx;
|
|
}
|
|
|
|
|
|
struct vfsmount * kern_mount(struct file_system_type * type)
|
|
{
|
|
struct vfsmount *m;
|
|
|
|
/*
|
|
* This sets everything up so that 'new_inode_pseudo()'
|
|
* called from 'sock_alloc()' properly allocates the inode.
|
|
*/
|
|
|
|
m = kzalloc(sizeof (struct vfsmount), 0);
|
|
if (m) {
|
|
|
|
struct fs_context fs_ctx;
|
|
|
|
if (type->init_fs_context) {
|
|
type->init_fs_context(&fs_ctx);
|
|
|
|
m->mnt_sb = kzalloc(sizeof (struct super_block), GFP_KERNEL);
|
|
m->mnt_sb->s_type = type;
|
|
m->mnt_sb->s_op =
|
|
((struct pseudo_fs_context*)fs_ctx.fs_private)->ops;
|
|
} else {
|
|
kfree(m);
|
|
m = (struct vfsmount*)ERR_PTR(-ENOMEM);
|
|
}
|
|
}
|
|
|
|
return m;
|
|
}
|
|
|
|
|
|
struct inode * new_inode_pseudo(struct super_block * sb)
|
|
{
|
|
const struct super_operations *ops = sb->s_op;
|
|
struct inode *inode;
|
|
|
|
if (ops->alloc_inode) {
|
|
inode = ops->alloc_inode(sb);
|
|
}
|
|
|
|
if (!inode)
|
|
return (struct inode*)ERR_PTR(-ENOMEM);
|
|
|
|
return inode;
|
|
}
|
|
|
|
|
|
#include <linux/firmware.h>
|
|
|
|
#if 0
|
|
struct firmware_work {
|
|
struct work_struct work;
|
|
struct firmware const *firmware;
|
|
void *context;
|
|
void (*cont)(struct firmware const *, void *);
|
|
};
|
|
|
|
|
|
static void request_firmware_work_func(struct work_struct *work)
|
|
{
|
|
struct firmware_work *fw_work =
|
|
container_of(work, struct firmware_work, work);
|
|
|
|
fw_work->cont(fw_work->firmware, fw_work->context);
|
|
|
|
kfree(fw_work);
|
|
}
|
|
#endif
|
|
|
|
|
|
extern int lx_emul_request_firmware_nowait(const char *name, void *dest, size_t *result);
|
|
extern void lx_emul_release_firmware(void const *data, size_t size);
|
|
|
|
extern void rtnl_lock(void);
|
|
extern void rtnl_unlock(void);
|
|
|
|
int request_firmware_nowait(struct module * module,
|
|
bool uevent, const char * name,
|
|
struct device * device, gfp_t gfp,
|
|
void * context,
|
|
void (* cont)(const struct firmware * fw,
|
|
void * context))
|
|
{
|
|
struct firmware *fw = kzalloc(sizeof (struct firmware), GFP_KERNEL);
|
|
#if 0
|
|
struct firmware_work *fw_work;
|
|
#endif
|
|
bool reg_db;
|
|
|
|
if (lx_emul_request_firmware_nowait(name, &fw->data, &fw->size)) {
|
|
kfree(fw);
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Normally we would schedule fw_work but for reasons not
|
|
* yet understood doing so will lead to a page-fault. So
|
|
* for the time being we will execute the callback directly
|
|
* and we have to make sure to manage the RTNL lock as the
|
|
* callback will grab it while we already hold it.
|
|
*/
|
|
reg_db = strcmp(name, "regulatory.db") == 0;
|
|
|
|
if (reg_db)
|
|
rtnl_unlock();
|
|
|
|
cont(fw, context);
|
|
|
|
if (reg_db)
|
|
rtnl_lock();
|
|
return 0;
|
|
|
|
#if 0
|
|
fw_work = kzalloc(sizeof (struct firmware_work), GFP_KERNEL);
|
|
if (!fw_work) {
|
|
kfree(fw);
|
|
return -1;
|
|
}
|
|
|
|
fw_work->firmware = fw;
|
|
fw_work->context = context;
|
|
fw_work->cont = cont;
|
|
|
|
INIT_WORK(&fw_work->work, request_firmware_work_func);
|
|
schedule_work(&fw_work->work);
|
|
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
|
|
int request_firmware(const struct firmware ** firmware_p,
|
|
const char * name, struct device * device)
|
|
{
|
|
struct firmware *fw;
|
|
|
|
if (!*firmware_p)
|
|
return -1;
|
|
|
|
fw = kzalloc(sizeof (struct firmware), GFP_KERNEL);
|
|
|
|
if (lx_emul_request_firmware_nowait(name, &fw->data, &fw->size)) {
|
|
kfree(fw);
|
|
return -1;
|
|
}
|
|
|
|
*firmware_p = fw;
|
|
return 0;
|
|
}
|
|
|
|
|
|
void release_firmware(const struct firmware * fw)
|
|
{
|
|
lx_emul_release_firmware(fw->data, fw->size);
|
|
kfree(fw);
|
|
}
|
|
|
|
|
|
/*
|
|
* This function is only called when using newer WIFI6 devices to
|
|
* load 'iwl-debug-yoyo.bin'. We simply deny the request.
|
|
*/
|
|
int firmware_request_nowarn(const struct firmware ** firmware,const char * name,struct device * device)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
|
|
#include <linux/pci.h>
|
|
|
|
int pcim_iomap_regions_request_all(struct pci_dev * pdev,int mask,const char * name)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
|
|
#include <linux/pci.h>
|
|
|
|
static unsigned long *_pci_iomap_table;
|
|
|
|
void __iomem * const * pcim_iomap_table(struct pci_dev * pdev)
|
|
{
|
|
unsigned i;
|
|
|
|
if (!_pci_iomap_table)
|
|
_pci_iomap_table = kzalloc(sizeof (unsigned long*) * 6, GFP_KERNEL);
|
|
|
|
if (!_pci_iomap_table)
|
|
return NULL;
|
|
|
|
for (i = 0; i < 6; i++) {
|
|
struct resource *r = &pdev->resource[i];
|
|
unsigned long phys_addr = r->start;
|
|
unsigned long size = r->end - r->start;
|
|
|
|
if (!phys_addr || !size)
|
|
continue;
|
|
|
|
_pci_iomap_table[i] =
|
|
(unsigned long)lx_emul_io_mem_map(phys_addr, size);
|
|
}
|
|
|
|
return (void const *)_pci_iomap_table;
|
|
}
|
|
|
|
|
|
#include <linux/task_work.h>
|
|
|
|
int task_work_add(struct task_struct * task,struct callback_head * work,enum task_work_notify_mode notify)
|
|
{
|
|
printk("%s: task: %p work: %p notify: %u\n", __func__, task, work, notify);
|
|
return -1;
|
|
}
|
|
|
|
|
|
#include <linux/interrupt.h>
|
|
|
|
void __raise_softirq_irqoff(unsigned int nr)
|
|
{
|
|
raise_softirq(nr);
|
|
}
|
|
|
|
|
|
#include <linux/slab.h>
|
|
|
|
void kfree_sensitive(const void *p)
|
|
{
|
|
size_t ks;
|
|
void *mem = (void *)p;
|
|
|
|
ks = ksize(mem);
|
|
if (ks)
|
|
memset(mem, 0, ks);
|
|
|
|
kfree(mem);
|
|
}
|
|
|
|
|
|
#include <linux/gfp.h>
|
|
#include <linux/slab.h>
|
|
|
|
unsigned long get_zeroed_page(gfp_t gfp_mask)
|
|
{
|
|
return (unsigned long)kzalloc(PAGE_SIZE, gfp_mask | __GFP_ZERO);
|
|
}
|
|
|
|
|
|
#include <linux/sched.h>
|
|
|
|
pid_t __task_pid_nr_ns(struct task_struct * task,
|
|
enum pid_type type,
|
|
struct pid_namespace * ns)
|
|
{
|
|
(void)type;
|
|
(void)ns;
|
|
|
|
return lx_emul_task_pid(task);
|
|
}
|
|
|
|
|
|
#include <linux/uaccess.h>
|
|
|
|
unsigned long _copy_from_user(void * to, const void __user * from,
|
|
unsigned long n)
|
|
{
|
|
memcpy(to, from, n);
|
|
return 0;
|
|
}
|
|
|
|
|
|
#include <linux/uio.h>
|
|
|
|
size_t _copy_from_iter(void * addr, size_t bytes, struct iov_iter * i)
|
|
{
|
|
char *kdata;
|
|
struct iovec const *iov;
|
|
size_t len;
|
|
|
|
if (bytes > i->count)
|
|
bytes = i->count;
|
|
|
|
if (bytes == 0)
|
|
return 0;
|
|
|
|
kdata = (char*)(addr);
|
|
iov = i->iov;
|
|
|
|
len = bytes;
|
|
while (len > 0) {
|
|
if (iov->iov_len) {
|
|
size_t copy_len = (size_t)len < iov->iov_len ? len
|
|
: iov->iov_len;
|
|
memcpy(kdata, iov->iov_base, copy_len);
|
|
|
|
len -= copy_len;
|
|
kdata += copy_len;
|
|
}
|
|
iov++;
|
|
}
|
|
|
|
return bytes;
|
|
}
|
|
|
|
|
|
#include <linux/uio.h>
|
|
|
|
size_t _copy_to_iter(const void * addr, size_t bytes, struct iov_iter * i)
|
|
{
|
|
char *kdata;
|
|
struct iovec const *iov;
|
|
size_t len;
|
|
|
|
if (bytes > i->count)
|
|
bytes = i->count;
|
|
|
|
if (bytes == 0)
|
|
return 0;
|
|
|
|
kdata = (char*)(addr);
|
|
iov = i->iov;
|
|
|
|
len = bytes;
|
|
while (len > 0) {
|
|
if (iov->iov_len) {
|
|
size_t copy_len = (size_t)len < iov->iov_len ? len
|
|
: iov->iov_len;
|
|
memcpy(iov->iov_base, kdata, copy_len);
|
|
|
|
len -= copy_len;
|
|
kdata += copy_len;
|
|
}
|
|
iov++;
|
|
}
|
|
|
|
return bytes;
|
|
}
|
|
|
|
|
|
#include <linux/printk.h>
|
|
|
|
asmlinkage __visible void dump_stack(void)
|
|
{
|
|
lx_backtrace();
|
|
}
|
|
|
|
|
|
#include <linux/mm.h>
|
|
|
|
void __put_page(struct page * page)
|
|
{
|
|
__free_pages(page, 0);
|
|
}
|
|
|
|
|
|
#include <linux/random.h>
|
|
|
|
u32 get_random_u32(void)
|
|
{
|
|
return lx_emul_gen_random_uint();
|
|
}
|
|
|
|
|
|
int __must_check get_random_bytes_arch(void *buf, int nbytes)
|
|
{
|
|
if (nbytes < 0)
|
|
return -1;
|
|
|
|
lx_emul_gen_random_bytes(buf, (unsigned long)nbytes);
|
|
return 0;
|
|
}
|
|
|
|
|
|
void get_random_bytes(void *buf, int nbytes)
|
|
{
|
|
int const err = get_random_bytes_arch(buf, nbytes);
|
|
(void)err;
|
|
}
|
|
|
|
|
|
#include <linux/prandom.h>
|
|
|
|
void prandom_bytes(void *buf, size_t bytes)
|
|
{
|
|
lx_emul_gen_random_bytes(buf, bytes);
|
|
}
|
|
|
|
|
|
u32 prandom_u32(void)
|
|
{
|
|
return lx_emul_gen_random_uint();
|
|
}
|
|
|
|
|
|
#include <linux/gfp.h>
|
|
|
|
void *page_frag_alloc_align(struct page_frag_cache *nc,
|
|
unsigned int fragsz, gfp_t gfp_mask,
|
|
unsigned int align_mask)
|
|
{
|
|
unsigned int const order = fragsz / PAGE_SIZE;
|
|
struct page *page = __alloc_pages(gfp_mask, order, 0, NULL);
|
|
|
|
if (!page)
|
|
return NULL;
|
|
|
|
/* see page_frag_free */
|
|
if (order > 0)
|
|
printk("%s: alloc might leak memory: fragsz: %u PAGE_SIZE: %u "
|
|
"order: %u page: %p addr: %p\n", __func__, fragsz, PAGE_SIZE, order, page, page->virtual);
|
|
|
|
return page->virtual;
|
|
}
|
|
|
|
|
|
#include <linux/gfp.h>
|
|
|
|
void page_frag_free(void * addr)
|
|
{
|
|
struct page *page = lx_emul_virt_to_pages(addr, 1ul);
|
|
if (!page) {
|
|
printk("BUG %s: page for addr: %p not found\n", __func__, addr);
|
|
lx_backtrace();
|
|
}
|
|
|
|
__free_pages(page, 0ul);
|
|
}
|
|
|
|
|
|
#include <linux/miscdevice.h>
|
|
|
|
int misc_register(struct miscdevice *misc)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void misc_deregister(struct miscdevice *misc)
|
|
{
|
|
}
|
|
|
|
|
|
/* rfkill support */
|
|
|
|
#include <linux/rfkill.h>
|
|
#include <net/rfkill/rfkill.h>
|
|
|
|
int __init rfkill_handler_init(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static struct
|
|
{
|
|
int rfkilled;
|
|
int blocked;
|
|
} _rfkill_state;
|
|
|
|
|
|
struct task_struct *rfkill_task_struct_ptr;
|
|
|
|
|
|
int lx_emul_rfkill_get_any(void)
|
|
{
|
|
/*
|
|
* Since this function may also be called from non EPs
|
|
* _do not_ execute _any_ kernel code.
|
|
*/
|
|
return _rfkill_state.rfkilled;
|
|
}
|
|
|
|
|
|
void lx_emul_rfkill_switch_all(int blocked)
|
|
{
|
|
_rfkill_state.blocked = blocked;
|
|
}
|
|
|
|
|
|
static int rfkill_task_function(void *arg)
|
|
{
|
|
(void)arg;
|
|
|
|
for (;;) {
|
|
|
|
bool rfkilled = !!rfkill_get_global_sw_state(RFKILL_TYPE_WLAN);
|
|
|
|
if (rfkilled != _rfkill_state.blocked)
|
|
rfkill_switch_all(RFKILL_TYPE_WLAN, !!_rfkill_state.blocked);
|
|
|
|
_rfkill_state.rfkilled = rfkilled;
|
|
|
|
lx_emul_task_schedule(true);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
void rfkill_init(void)
|
|
{
|
|
pid_t pid;
|
|
|
|
pid = kernel_thread(rfkill_task_function, NULL, CLONE_FS | CLONE_FILES);
|
|
|
|
rfkill_task_struct_ptr = find_task_by_pid_ns(pid, NULL);
|
|
}
|