drivers/wifi: perform multi-staged construction

Since the wireless LAN driver is actually a 'Libc::Component' due to
its incorporation of the 'wpa_spplicant' application, we have to
intercept its construction because we have to initialize the Lx_kit
environment before any static ctors are executed. Most Linux initcalls
are implemented as ctors that will be otherwise implicitly executed
before the controll is given to us in 'Libc::Component::construct'.

Issue #4927.
This commit is contained in:
Josef Söntgen 2023-07-07 11:46:39 +02:00 committed by Norman Feske
parent fc24ffcdb8
commit dd1b8a106f
10 changed files with 134 additions and 162 deletions

View File

@ -1,9 +0,0 @@
SRC_CC = vfs.cc
DDE_LINUX_DIR := $(subst /src/include/lx_kit,,$(call select_from_repositories,src/include/lx_kit))
INC_DIR += $(DDE_LINUX_DIR)/src/include
vpath %.cc $(REP_DIR)/src/lib/vfs/wifi
SHARED_LIB := yes

View File

@ -114,7 +114,7 @@ install_config {
<resource name="RAM" quantum="32M"/>
<config ld_verbose="no">
<vfs>
<dir name="dev"> <log/> <rtc/> <null/> <wifi/>
<dir name="dev"> <log/> <rtc/> <null/>
<jitterentropy name="random"/>
<jitterentropy name="urandom"/>
</dir>

View File

@ -19,7 +19,7 @@ can be used:
! <config>
! <libc stdout="/dev/null" stderr="/dev/null" rtc="/dev/rtc"/>
! <vfs>
! <dir name="dev"> <log/> <null/> <rtc/> <wifi/>
! <dir name="dev"> <log/> <null/> <rtc/>
! <jitterentropy name="random"/>
! <jitterentropy name="urandom"/>
! </dir>
@ -47,7 +47,7 @@ snippet illustrates the use of the driver on the PinePhone:
! <config>
! <libc stdout="/dev/null" stderr="/dev/null" rtc="/dev/rtc"/>
! <vfs>
! <dir name="dev"> <log/> <null/> <rtc/> <wifi/>
! <dir name="dev"> <log/> <null/> <rtc/>
! <jitterentropy name="random"/>
! <jitterentropy name="urandom"/>
! </dir>
@ -177,3 +177,24 @@ concern. However, when the driver is built without the depot, the boot image
assemble-mechanism tries to include the non-existent 'wifi.lib.so' library. To
prevent that from failing one way is adding a dummy file - the
'repos/pc/run/pc_wifi.run' illustrates how to do that.
During development it might be necessary to automatically generate certain
dummy functions as is normally the case with drivers ported over from Linux.
To make the existing tooling (tool/dde_linux) work with the wifi driver it
is necessary to link the driver binary directly against the driver library
rather then stub ABI library. This is achieved by adapting the driver's
'target.mk' file. In
!TARGET := wifi_drv
!SRC_CC := main.cc wpa.cc access_firmware.cc
!LIBS := base wifi
![…]
'LIBS' must be changed as follows in case the PC wifi driver library is
used:
!LIBS := base pc_wifi
Afterwards the driver library is built by the 'drivers/wifi' target and
since it is now a direct dependency 'tool/dde_linux/create_dummies' can
by used to created the 'generated_dummies.c' file.

View File

@ -12,6 +12,8 @@
*/
/* Genode includes */
#include <base/component.h>
#include <base/shared_object.h>
#include <libc/component.h>
#include <base/attached_rom_dataspace.h>
#include <base/log.h>
@ -31,6 +33,7 @@
using namespace Genode;
static Msg_buffer _wifi_msg_buffer;
static Wifi::Frontend *_wifi_frontend = nullptr;
@ -92,6 +95,8 @@ extern void wifi_init(Genode::Env&, Genode::Blockade&);
extern void wifi_set_rfkill_sigh(Genode::Signal_context_capability);
static Genode::Blockade _wpa_startup_blockade { };
struct Main
{
Env &env;
@ -99,8 +104,6 @@ struct Main
Constructible<Wpa_thread> _wpa;
Constructible<Wifi::Frontend> _frontend;
Blockade _wpa_startup_blockade { };
struct Request_handler : Wifi::Firmware_request_handler
{
Signal_handler<Request_handler> _handler;
@ -174,8 +177,6 @@ struct Main
Wifi::firmware_establish_handler(_request_handler);
_wpa.construct(env, _wpa_startup_blockade);
wifi_init(env, _wpa_startup_blockade);
}
};
@ -191,6 +192,44 @@ void *wifi_get_buffer(void)
}
/*
* Since the wireless LAN driver incorporates the 'wpa_supplicant',
* which itself is a libc-using application, we have to initialize
* the libc environment. Normally this initialization is performed
* by the libc (see 'src/lib/libc/component.cc') but as the various
* initcalls of the Linux kernel are registered as ctor we have to
* initialize the Lx_kit::Env before the static ctors are executed.
* As those are called prior to calling 'Libc::Component::construct',
* which is implemented by us, we pose as regular component and
* call the libc 'Component::construct' explicitly after we have
* finished our initialization (Lx_kit::Env include).
*/
void Component::construct(Genode::Env &env)
{
try {
Genode::Heap shared_obj_heap(env.ram(), env.rm());
Shared_object shared_obj(env, shared_obj_heap, "libc.lib.so",
Shared_object::BIND_LAZY,
Shared_object::DONT_KEEP);
typedef void (*Construct_fn)(Genode::Env &);
Construct_fn const construct_fn =
shared_obj.lookup<Construct_fn>("_ZN9Component9constructERN6Genode3EnvE");
/* prepare Lx_kit::Env */
wifi_init(env, _wpa_startup_blockade);
construct_fn(env);
} catch (... /* intentional catch-all */) {
Genode::error("could not perform multi-staged construction");
throw;
}
}
void Libc::Component::construct(Libc::Env &env)
{
static Main server(env);

View File

@ -1,87 +0,0 @@
/*
* \brief Minimal VFS plugin for bringing up WLAN driver
* \author Josef Soentgen
* \date 2022-02-20
*
* The sole purpose of this VFS plugin is to call 'Lx_kit::initialize_env'
* at the right time before 'env.exec_static_constructors' is executed.
*/
/*
* Copyright (C) 2022 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
/* Genode includes */
#include <os/vfs.h>
#include <vfs/single_file_system.h>
/* DDE Linux includes */
#include <lx_kit/init.h>
namespace Vfs_wlan
{
using namespace Vfs;
using namespace Genode;
struct File_system;
}
struct Vfs_wlan::File_system : Single_file_system
{
File_system(Vfs::Env &env, Xml_node config)
:
Single_file_system { Vfs::Node_type::CONTINUOUS_FILE, name(),
Vfs::Node_rwx::ro(), config }
{
/*
* Various ports of a DDE Linux based library rely on the
* 'env' being set before any static constructor is executed.
* So we set it here and wait for the user of the library to
* execute the constructors at the proper time.
*/
Lx_kit::initialize(env.env());
}
Open_result open(char const *, unsigned,
Vfs::Vfs_handle **,
Allocator &) override
{
return OPEN_ERR_UNACCESSIBLE;
}
Stat_result stat(char const *, Stat &) override
{
return STAT_ERR_NO_ENTRY;
}
static char const *name() { return "wlan"; }
char const *type() override { return name(); }
};
/**************************
** VFS plugin interface **
**************************/
extern "C" Vfs::File_system_factory *vfs_file_system_factory(void)
{
struct Factory : Vfs::File_system_factory
{
Vfs::File_system *create(Vfs::Env &vfs_env,
Genode::Xml_node node) override
{
static Vfs::File_system *fs =
new (vfs_env.alloc()) Vfs_wlan::File_system(vfs_env, node);
return fs;
}
};
static Factory factory;
return &factory;
}

View File

@ -69,32 +69,6 @@ struct Rfkill_helper
}
};
Constructible<Rfkill_helper> rfkill_helper { };
void Wifi::set_rfkill(bool blocked)
{
if (!rfkill_task_struct_ptr)
return;
lx_emul_rfkill_switch_all(blocked);
lx_emul_task_unblock(rfkill_task_struct_ptr);
Lx_kit::env().scheduler.execute();
/*
* We have to open the device again after unblocking
* as otherwise we will get ENETDOWN. So unblock the uplink
* task _afterwards_ because there we call * 'dev_open()'
* unconditionally and that will bring the netdevice UP again.
*/
lx_emul_task_unblock(uplink_task_struct_ptr);
Lx_kit::env().scheduler.execute();
if (rfkill_helper.constructed())
rfkill_helper->submit_notification();
}
bool Wifi::rfkill_blocked(void)
{
@ -194,7 +168,7 @@ struct Firmware_helper
}
Firmware_helper(Genode::Entrypoint &ep,
Wifi::Firmware_request_handler &request_handler)
Wifi::Firmware_request_handler &request_handler)
:
_response_handler { ep, *this, &Firmware_helper::_handle_response },
_request_handler { request_handler }
@ -243,27 +217,6 @@ struct Firmware_helper
};
Constructible<Firmware_helper> firmware_helper { };
size_t _wifi_probe_firmware(char const *name)
{
if (firmware_helper.constructed())
return firmware_helper->perform_probing(name);
return 0;
}
int _wifi_request_firmware(char const *name, char *dst, size_t dst_len)
{
if (firmware_helper.constructed())
return firmware_helper->perform_requesting(name, dst, dst_len);
return -1;
}
extern "C" unsigned int wifi_ifindex(void)
{
/* TODO replace with actual qyery */
@ -306,8 +259,14 @@ struct Wlan
genode_uplink_notify_peers();
}
Constructible<Rfkill_helper> rfkill_helper { };
Constructible<Firmware_helper> firmware_helper { };
Wlan(Env &env) : _env { env }
{
Lx_kit::initialize(env);
genode_mac_address_reporter_init(env, Lx_kit::env().heap);
{
@ -343,30 +302,84 @@ extern "C" void wakeup_wpa()
}
static Wlan *_wlan_ptr;
void wifi_init(Env &env, Blockade &blockade)
{
wpa_blockade = &blockade;
static Wlan wlan(env);
_wlan_ptr = &wlan;
}
/*
* Rfkill handling
*/
void Wifi::rfkill_establish_handler(Wifi::Rfkill_notification_handler &handler)
{
rfkill_helper.construct(handler);
_wlan_ptr->rfkill_helper.construct(handler);
}
void Wifi::set_rfkill(bool blocked)
{
if (!rfkill_task_struct_ptr)
return;
lx_emul_rfkill_switch_all(blocked);
lx_emul_task_unblock(rfkill_task_struct_ptr);
Lx_kit::env().scheduler.execute();
/*
* We have to open the device again after unblocking
* as otherwise we will get ENETDOWN. So unblock the uplink
* task _afterwards_ because there we call * 'dev_open()'
* unconditionally and that will bring the netdevice UP again.
*/
lx_emul_task_unblock(uplink_task_struct_ptr);
Lx_kit::env().scheduler.execute();
if (_wlan_ptr->rfkill_helper.constructed())
_wlan_ptr->rfkill_helper->submit_notification();
}
/*
* Firmware handling
*/
void Wifi::firmware_establish_handler(Wifi::Firmware_request_handler &request_handler)
{
firmware_helper.construct(Lx_kit::env().env.ep(), request_handler);
_wlan_ptr->firmware_helper.construct(Lx_kit::env().env.ep(), request_handler);
}
Wifi::Firmware_request *Wifi::firmware_get_request()
{
if (firmware_helper.constructed())
return firmware_helper->request();
if (_wlan_ptr->firmware_helper.constructed())
return _wlan_ptr->firmware_helper->request();
return nullptr;
}
size_t _wifi_probe_firmware(char const *name)
{
if (_wlan_ptr->firmware_helper.constructed())
return _wlan_ptr->firmware_helper->perform_probing(name);
return 0;
}
int _wifi_request_firmware(char const *name, char *dst, size_t dst_len)
{
if (_wlan_ptr->firmware_helper.constructed())
return _wlan_ptr->firmware_helper->perform_requesting(name, dst, dst_len);
return -1;
}

View File

@ -29,7 +29,6 @@ void Sculpt::gen_wifi_drv_start_content(Xml_generator &xml)
xml.node("zero", [&] () {});
xml.node("log", [&] () {});
xml.node("null", [&] () {});
xml.node("wifi", [&] () {});
gen_named_node(xml, "jitterentropy", "random");
gen_named_node(xml, "jitterentropy", "urandom"); });
gen_named_node(xml, "inline", "rtc", [&] () {
@ -71,7 +70,6 @@ void Sculpt::gen_wifi_drv_start_content(Xml_generator &xml)
gen_parent_rom_route(xml, "libc.lib.so");
gen_parent_rom_route(xml, "libm.lib.so");
gen_parent_rom_route(xml, "vfs_jitterentropy.lib.so");
gen_parent_rom_route(xml, "vfs_wifi.lib.so");
gen_parent_rom_route(xml, "libssl.lib.so");
gen_parent_rom_route(xml, "wifi.lib.so");
gen_parent_rom_route(xml, "wifi_firmware.tar");

View File

@ -23,7 +23,7 @@ WS_PORT_DIR := $(call port_dir,$(DDE_LINUX_REP_DIR)/ports/wpa_supplicant)
LIBNL_PORT_DIR := $(call port_dir,$(DDE_LINUX_REP_DIR)/ports/libnl)
DDE_LINUX_LIB_MK := \
$(addprefix lib/mk/,libnl.inc libnl_include.mk wifi.inc vfs_wifi.mk) \
$(addprefix lib/mk/,libnl.inc libnl_include.mk wifi.inc) \
$(foreach SPEC,x86_32 x86_64,lib/mk/spec/$(SPEC)/libnl.mk) \
$(addprefix lib/mk/,wpa_driver_nl80211.mk wpa_supplicant.mk)
@ -34,7 +34,6 @@ MIRROR_FROM_DDE_LINUX_DIR := $(DDE_LINUX_LIB_MK) \
lib/symbols/wifi \
include/wifi \
$(shell cd $(DDE_LINUX_REP_DIR); find src/lib/libnl -type f) \
$(shell cd $(DDE_LINUX_REP_DIR); find src/lib/vfs/wifi -type f) \
$(shell cd $(DDE_LINUX_REP_DIR); find src/lib/wifi -type f) \
$(shell cd $(DDE_LINUX_REP_DIR); find src/lib/wpa_driver_nl80211 -type f) \
$(shell cd $(DDE_LINUX_REP_DIR); find src/lib/wpa_supplicant -type f)

View File

@ -90,7 +90,6 @@ set build_components {
server/dynamic_rom
server/nic_router
test/lwip/http_srv
lib/vfs_wifi
lib/vfs_lwip
}
@ -298,7 +297,6 @@ append config {
<dir name="dev"> <log/> <null/> <rtc/>
<jitterentropy name="random"/>
<jitterentropy name="urandom"/>
<wifi/>
</dir>
<dir name="firmware">
<tar name="wifi_firmware.tar"/>

View File

@ -267,7 +267,7 @@ append config { </wifi_config>
<libc stdout="/dev/null" stderr="/dev/log" rtc="/dev/rtc"/>
<vfs>
<dir name="dev">
<log/> <null/> <wifi/>
<log/> <null/>
<jitterentropy name="random"/>
<jitterentropy name="urandom"/>
<inline name="rtc">2018-01-01 00:01</inline>