1
0
forked from dan/liminix

Compare commits

...

85 Commits

Author SHA1 Message Date
e5963ae3f7 deep thoughts 2024-03-06 23:19:47 +00:00
f164f19d95 service starts and stops 2024-03-06 23:19:47 +00:00
dd4ab41f6a rename run-event 2024-03-06 23:19:47 +00:00
5d5dff6729 WIP add failing test that service starts 2024-03-06 23:19:47 +00:00
570d29c368 pass command line params to run instead of reffing global 2024-03-06 23:19:47 +00:00
725af00dc9 improve test for dummy0 up
if we run off the end of the events fixture, it didn't work
2024-03-06 23:19:47 +00:00
e1b932ec27 remove hardcoded filename in test event generator 2024-03-06 23:19:47 +00:00
7173b6fb1c don't call os.exit 2024-03-06 23:19:47 +00:00
ed9548f21d pass event producer fn as param 2024-03-06 23:19:47 +00:00
0787807a7f ifwait: don't run on load if in test harness 2024-03-06 23:19:47 +00:00
38ed91f641 simplify assertion 2024-03-06 23:19:47 +00:00
ffe9603c39 remove file-scoped parameters var 2024-03-06 23:19:47 +00:00
cbd3dfefc5 ifwait fixture/test harness 2024-03-06 23:19:47 +00:00
018c1868b5 ifwait: use anoia.assoc 2024-03-06 23:19:47 +00:00
5184ff63f7 add anoia.nl, a convenience wrapper on netlink 2024-03-06 23:19:47 +00:00
35909c9a23 add netlink to fennelrepl 2024-03-06 23:19:47 +00:00
4383462199 deep thoughts 2024-03-06 23:19:47 +00:00
9730cdd63b add assoc to anoia 2024-03-06 23:19:47 +00:00
dan
095853214b Merge pull request 'Fix kernel build on belkin' (#10) from sinavir/liminix:fix_kernel_build_on_belkin into main
Reviewed-on: dan/liminix#10
2024-03-06 18:21:13 +00:00
9d6e50cbbc extract extneder example to a "profile"
this is a bit of an experiment to reduce the copy-paste in
examples by turning them into "application" modules.

planning to follow up with another module for "wifi router"
2024-02-27 23:13:12 +00:00
94dbc56595 fix doc 2024-02-27 20:08:30 +00:00
2cd7f932eb alignment may be null 2024-02-27 19:47:46 +00:00
sinavir
27c7735f02 belkin-RT3200: fix kernel options 2024-02-22 21:57:40 +01:00
sinavir
29c9de248d fix import of openwrt sources 2024-02-22 21:57:33 +01:00
3ca0d87c27 ci.nix: alphabetise systems 2024-02-21 19:49:14 +00:00
8f30db58ae New port to Zyxel NWA50AX: update NEWS and ci.nix 2024-02-21 19:32:50 +00:00
f9ab0590a6 Merge remote-tracking branch 'raito/nwa50ax' 2024-02-21 19:27:23 +00:00
84fa8d65f4 fennel: system: verbose log of command that was run 2024-02-21 19:27:14 +00:00
9b0149ecb7 deep thoughts 2024-02-21 19:26:33 +00:00
Raito Bezarius
baf3cf7413 devices/zyxel-nwa50ax: fix dual image mgmt after DTB expansion
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-02-19 03:13:35 +01:00
Raito Bezarius
c5145b5fc9 devices/zyxel-nwa50ax: make zyxel-bootconfig executable
Otherwise, it doesn't work well…

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-02-19 03:13:21 +01:00
Raito Bezarius
628f4dfdbe devices/zyxel-nwa50ax: developer todo
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-02-19 03:13:21 +01:00
Raito Bezarius
da59e2a349 devices/zyxel-nwa50ax: complete documentation
It covers everything I know more or less.

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-02-19 02:57:34 +01:00
Raito Bezarius
c0a9571a13 devices/zyxel-nwa50ax: upgrade MT7915 firmware from OpenWRT repository
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-02-19 02:57:34 +01:00
Raito Bezarius
d6ffdd7be6 devices/zyxel-nwa50ax: expose primary and secondary images
To support A/B a bit better.

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-02-19 02:57:34 +01:00
Raito Bezarius
985f982435 examples/nwa50ax-ap: support bridge between lan and ethernet
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-02-19 02:48:50 +01:00
Raito Bezarius
a893c0dc4c devices/zyxel-nwa50ax: use our own more advanced DTB
OpenWRT had a DTB for the NWA50AX LEDs that I didn't pick up.

Anyway, we need to include our own special DTB for the NWA platform in general
to support A/B operations, because OpenWRT original one just mark everything else read-only.

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-02-19 02:48:50 +01:00
Raito Bezarius
3ec29dc1b9 examples/nwa50ax-ap: ensure mtdutils is available for further flashing
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-02-19 02:48:50 +01:00
Raito Bezarius
0e81953b67 devices/zyxel-nwa50ax: cleanup of flash attribute and rootDevice
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-02-19 02:48:50 +01:00
Raito Bezarius
3c70a0d037 devices/zyxel-nwa50ax: ensure bridge is always available
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-02-19 02:48:50 +01:00
Raito Bezarius
422f3edab1 modules/zyxel-dual-image: init
This adds a simple boot blessing module, to be used, with the Zyxel NWA50AX.

There's a lot of elephant in the rooms: how do you upgrade kernel, etc.

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-02-19 02:48:50 +01:00
Raito Bezarius
c14b2f6356 modules/busybox: add dhcprelay
This enables to run a DHCP relay from multiple interfaces.

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-02-19 02:48:50 +01:00
Raito Bezarius
cdafff2095 examples/nwa50ax-ap: init
This is a quite comprehensive example using maximally the hardware
available to reach nice performance.

In the future, I will even add RADIUS examples.

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-02-19 02:48:50 +01:00
Raito Bezarius
13f1bb9f52 devices/zyxel-nwa50ax: init 2024-02-19 02:48:48 +01:00
Raito Bezarius
019fef6929 zyxel-bootconfig: init at no version
This tool is useful for manipulating the A/B boot status of the image.
2024-02-18 20:30:41 +01:00
Raito Bezarius
63007859c2 modules/outputs/zyxel-nwa-fit: init
Zyxel "firmware" format is just… a FIT with some metadata on the models.

This FIT is like this:

--------------------------
    uImage FIT header
--------------------------
    Linux kernel
--------------------------
    FDT DTB
--------------------------
    Padding so that
    this makes
    8192kb [1]
--------------------------
    UBI volume
    as a root filesystem
--------------------------

We just reproduce this in a very brutal and naive way.
In the future, this seems worth to generalize and modularize this idea
so that zyxel-nwa-fit is just an instance of a more general output.

[1]: https://git.openwrt.org/?p=openwrt/openwrt.git;a=blob;f=target/linux/ramips/image/mt7621.mk;h=ab1b829ba0086cb9fc9ca8cbbf3cbc14735034d6;hb=refs/heads/main#l3097

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-02-18 20:30:41 +01:00
Raito Bezarius
e9ab8d7183 modules/outputs/ubivolume: introduce ubinization
It creates an UBI image based on an UBI volume configuration.

For now, it creates only an empty rootfs.
2024-02-18 20:30:41 +01:00
Raito Bezarius
3dc58de0eb modules/outputs: expose commandLineDtbNode option
We allow `bootargs` and `bootargs-override` for now only.

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-02-18 20:30:41 +01:00
Raito Bezarius
dde8386f75 builders/uimage: support aligning the FIT
This is necessary when writing to a MTD partition with a certain erasesize.
2024-02-18 20:30:41 +01:00
Raito Bezarius
c59364d623 modules/outputs/ubifs: expose rootubifs rather than rootfs
I believe there should be another module exposing `rootubifs` as `rootfs`
or let any other module just subsume that component like `zyxel-nwa-fit` output.

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-02-18 20:30:41 +01:00
Raito Bezarius
b76c5b4abe modules/ubifs: revamp to offer directly access to the UBIfs partition
Adds the LEB and PEB option and let the user remove the boot image in case
where U-Boot does not support UBI boot.
2024-02-18 20:30:41 +01:00
Raito Bezarius
0a8343be66 pkgs/kernel/uimage: introduce commandLineDtbNode
Certain devices like the Zyxel NWA50AX will pass information on the command-line
to explain what is the current image (`bootImage=1` vs. `bootImage=0`).

Unfortunately, if we set the `chosen/bootargs` node, this will be overridden forcibly
by U-Boot.

To avoid this problem, it's easier to simply just use another DTB node like `bootargs-override` which
is what OpenWRT does [1].

[1]: https://git.openwrt.org/?p=openwrt/openwrt.git;a=blob;f=target/linux/ramips/patches-5.15/314-MIPS-add-bootargs-override-property.patch;h=e7dca7af886e8c0b69ba2b23f5855ddfeeb0d4a1;hb=refs/heads/main

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-02-18 20:30:41 +01:00
Raito Bezarius
d14ee41325 liminix-rebuild: use -f flag to reboot effectively
My AP does not reboot upon `reboot` but `reboot -f`… why?

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-02-17 11:45:17 +00:00
Raito Bezarius
8f814658fe hostapd: enable 802.11ax
For people enjoying WiFi 6 heaven… :>

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-02-17 11:45:10 +00:00
60508f4d4e update NEWS
* Turris Omnia
* possible wifi regressions
2024-02-16 21:00:59 +00:00
ca64e9035e gl-ar750 ath9k needs cal data from MTD 2024-02-16 20:44:56 +00:00
4bcc3d5b28 dhcpc6 scripts: simplify (and improve correctness) 2024-02-16 18:47:12 +00:00
28fe37d555 deep thoughts 2024-02-16 18:30:54 +00:00
175db9f604 tail -F for rotuer 2024-02-16 18:30:24 +00:00
b5722a0153 gl-ar750: ath10k wireless depends on firmware
so make sure the firmware is present _first_

the ath10k is still broken anyway, looking into why
2024-02-16 00:38:36 +00:00
c373152673 make tftpboot work on devices with old u-boot
Some devices have a U-boot variant that does not accept a third
parameter on the "bootm" command, meaning we can't override the dtb
in the bootloader so have to smush it back into the kernel image

This doesn't work in QEMU but I think the problem is with the
U-Boot configuration for QEMU. It does work on at least one
hardware device so I'm pushing it anyway

Based on
https://gti.telent.net/raboof/liminix/src/branch/tftp-old-uboot

Co-authored-by:  Arnout Engelen <arnout@bzzt.net>
2024-02-15 23:44:47 +00:00
7e7171556f subdue dnsmasq logs
we need to find a better way of doing this. people might _want_
to have All The Logs from evey dns query
2024-02-15 23:43:54 +00:00
6920ee765d deep thoughts 2024-02-15 09:11:54 +00:00
71a1ef286e deep thoughts 2024-02-13 22:32:57 +00:00
ffe0e9d26b use mkstate for dropbear keys 2024-02-13 22:12:26 +00:00
2b22c7aa91 dnsmasq: store dhcp lease file on /persist 2024-02-13 21:54:45 +00:00
3c950704e1 rename /run/service-state to /run/services/outputs 2024-02-13 21:41:43 +00:00
8578a554c7 deep thoughts 2024-02-13 21:11:30 +00:00
3851698d35 fix tftpboot compressed rootfs 2024-02-13 18:16:17 +00:00
f69ebbb6f5 fix doc CI target 2024-02-13 15:41:45 +00:00
16e4b05653 dhcp6c: set preferred and valid address lifetimes
also workaround a bug in rebinding/updates where we get an error
from "ip addr add" trying to add an address that's already present
2024-02-13 13:49:12 +00:00
8ac848b1e6 ath10k_pci: wifi modules must be modules 2024-02-13 12:56:03 +00:00
b7efbd3e21 update NEWS file 2024-02-12 21:10:52 +00:00
a654577ac2 improve port-forwarding comment 2024-02-12 21:05:01 +00:00
c50423f689 turris omnia: upgrade to mainline 6.7.4 kernel
On this device we don't need the openwrt kernel or patches. The
newer kernel also fixes the weird one minute pause at boot when
it was doing something with either mmc or switch.
2024-02-12 20:43:01 +00:00
65479e206b use regular kernel not backports for mac80211
the kernel on most devices is now newer than the version that the
backported drivers were backported from
2024-02-12 20:41:10 +00:00
79926c6fe7 remove call to deleted package 2024-02-12 14:56:12 +00:00
ae4856ea7c improve firewall comment 2024-02-12 13:56:56 +00:00
b9c0d93670 build modules at same time as main kernel vmlinux
This changes the practice for building kernel modules: now we expect
that the appropriate Kconfig symbols are set to =m in
config.kernel.config, and then use pkgs.kmodloader to create
a service that loads and unloads all the modules depended on by
a particular requirement.

Note that modules won't be installed on the target device just by
virue of having been built: only the modules that are referenced by a
kmodloader package will be in the closure.

An example may make this clearer: see modules/firewall/default.nix
in this commit.

Why?

If you have a compiled Linux kernel source tree and you change some
symbol from "is not set" to m and then run make modules, you cannot in
general expect that newly compiled module to work. This is because
there are places in the build of the main kernel where it looks to see
which modules _may_ be defined and uses that information to
accommodate them.

For example in an in-kernel build of

  https://github.com/torvalds/linux/blob/master/net/netfilter/core.c#L689

some symbols are defined only if CONFIG_NF_CONNTRACK is set, meaning
this code won't work if we have it unset initially then try later to
enable it and build modules only. Or see

  https://github.com/torvalds/linux/blob/master/include/linux/netdevice.h#L160
2024-02-11 23:47:11 +00:00
11287a8436 allow lan dns queries (ipv6) 2024-02-11 23:32:46 +00:00
57aece0709 rotuer: don't forward queries for local domain 2024-02-11 23:32:46 +00:00
c1d285a220 rotuer: network debugging tools 2024-02-11 23:32:46 +00:00
dce983ec79 move kernel module to its own subdir 2024-02-11 18:15:55 +00:00
812f497660 add kernel.version param to allow for version-specific patches
default to 5.15.137 to avoid breaking the devices that don't declare it
2024-02-11 16:19:52 +00:00
1206d02200 rotuer-secrets: remove root_password, add wifi ssid and domainName
this is step one towards getting rid of rotuer-secrets completely and
turning rotuer into a "profile" module that can be less hackily
customised for other people's networks
2024-02-11 15:56:14 +00:00
66 changed files with 2509 additions and 400 deletions

51
NEWS
View File

@ -31,5 +31,56 @@ Upstream changes that have led to incompatible Liminix changes are:
* newer U-Boot version
* util-linux can now be built (previously depended on systemd)
2024-01-30
New port! Thanks to Arnout Engelen <arnout@bzzt.net>, Liminix
now runs on the TP-Link Archer AX23.
2024-02-12
* We now build wifi drivers (mac80211) from the same kernel source as
the running kernel, instead of using drivers from the linux-backports
project. This may be a regression on some devices that depend on
OpenWrt patches for wireless functionality: if you have a device that
used to work and now doesn't, refer to OpenWrt
package/kernel/mac80211/patches/ to see if there's something in there
that needs to be applied.
* in general, we build kernel modules (e.g. for nftables) at the same
time as the kernel itself instead of expecting to be able to build
them afterwards as though they were "out of tree". Refer to commit
b9c0d93670275e69df24902b05bf4aa4f0fcbe96 for a fuller explanation
of how this simplifies things.
2024-02-13
So that we can be more consistent about services that would like their
state to be preserved across boots (assuming a writable filesystem)
these changes have been made
* /run/service-state has been moved to /run/services/outputs
to better reflect what it's used for
* /run/services/state is either a symlink to /persist/services/state
(if there's a writeable fs on /persist) or a directory (if there
isn't)
The change will lose your ssh host key(s) unless you copy them from
the old location to the new one before rebooting into the new system
mkdir -m 02751 -p /run/services/state/dropbear
cp /persist/secrets/dropbear/* /run/services/state/dropbear
The `output`, `mkoutputs` functions defined by ${serviceFns}
have been updated for the new location.
2024-02-16
New (or at least, previously unreported) port! Liminix now runs on the
Turris Omnia and has been serving my family's internet needs for most
of this week. Thanks to NGI0 Entrust and the NLnet Foundation for
sponsoring this development (and funding the hardware)
2024-02-21
New port! Thanks to Raito Bezarius, Liminix now runs on the Zyxel NWA50AX,
an MT7621 (MIPS EL) dual radio WiFi AP.

View File

@ -3952,3 +3952,340 @@ I can actually use it as a CPE. This means
- would be quite cool to run sniproxy instead of forwarding to
loaclhost (extra credit)
Sat Feb 10 18:23:54 GMT 2024
ARGH KERNEL
You can't define CONFIG_NETFILTER=y in a monolithic kernel and expect
later to separately build some modules that use it, because there are
a bunch of symbols that only get defined if certain other CONFIG
options are set at the time that the monolithic kernel is built.
https://github.com/torvalds/linux/blob/master/net/netfilter/core.c#L689
Another example is
https://github.com/torvalds/linux/blob/master/include/linux/netdevice.h#L160
- if you decide after building the kernel that you're going to build
some wireless modules, you can't do that without rebuilding the kernel
so that it knows to expect them
The moral of the story seems to be: if you have a compiled Linux kernel source tree and you change some symbol from "is not set" to m and then run make modules, you cannot in general expect that newly compiled module to work.
AP advertised VHT without HT, disabling HT/VHT/HE
TODO
- [done] support kernel version as parameter to builder pkgs/kernel/default.nix
- [done] extract the change in how module loading works from omnia device config,
and fix the other thing that uses it
- [axed] wlan module to take 'backported' as a parameter
half of the omnia conditionalConfig can go into the module
- [done] upgrade omnia to kernel v6
- figure out what mdns we need for local hostname resolution
(maybe bridging lan/wlan)?
- [DONE] slow wifi because "AP advertised VHT without HT, disabling HT/VHT/HE"
- [DONE] add local domain to secrets
- run sniproxy instead of forwarding
- [test] forward some port to loaclhost 22 for inbound ipv4 ssh
Mon Feb 12 21:50:35 GMT 2024
# find /run/service-state/dhcp6c.wan.link.pppoe/address/
/run/service-state/dhcp6c.wan.link.pppoe/address/
/run/service-state/dhcp6c.wan.link.pppoe/address/2001-8b0-1111-1111-0-ffff-51bb-4cf2_LFoo015bSsM
/run/service-state/dhcp6c.wan.link.pppoe/address/2001-8b0-1111-1111-0-ffff-51bb-4cf2_LFoo015bSsM/valid
/run/service-state/dhcp6c.wan.link.pppoe/address/2001-8b0-1111-1111-0-ffff-51bb-4cf2_LFoo015bSsM/preferred
/run/service-state/dhcp6c.wan.link.pppoe/address/2001-8b0-1111-1111-0-ffff-51bb-4cf2_LFoo015bSsM/len
/run/service-state/dhcp6c.wan.link.pppoe/address/2001-8b0-1111-1111-0-ffff-51bb-4cf2_LFoo015bSsM/address
#
valid 7199 preferred 3599
Tue Feb 13 19:44:57 GMT 2024
Before we put this back live, would be good to
[done] 1) move the leases file into /persist
I think we'll do /persist/service/<name>/ and change ssh to use the same
scheme.
we could put mkpersist() in serviceFns which would check for /persist
and return a directory in /persist/service/ or /run/service-state
(will something bad happen if we use /run/service-state? it will also
expose the thingy as an output, but whether it's accessible that way
will depend on whether there's a writable fs or not, which is unexpected)
: rename service-state to /run/services/outputs
: on boot
: if /persist
: create /persist/services/state and symlink /run/services/state to it
: else create /run/services/state
[done] 2) maybe change the local domain back to .lan? setting up
systemd-networkd with search domains is an awful faff
[done] 3) work out what to do with incoming ssh from wan
- For noetbook and thinkpad we have a vpn anyway so can expect to
reach loaclhost directly using ipv6
- stop ssh from ever trying to get to our ipv4 address.
- we could get rid of A record for loaclhost.telent.net but
there are a bunch of CNAMES pointing at it for web servers.
- we could reject incoming connections to tcp4 port 22 in firewall
and then there is a clear signal to Dont Do That Then
- for emergency use, dnat ipv4 2200 and 2201 to rotuer and loaclhost
Tue Feb 13 22:31:03 GMT 2024
* the reason we can't reboot is that there is a service to add each
lan device to the bridge which does ifwait $dev running, which doesn't
return until there's something plugged in. So s6-rc hangs indefinitely
until the lan switch is fully populated. This is definitely a "next
milestone" thing.
* another example of "thing that depends on other thing but which it
is actually OK if neither of them happen" might be "mount a
filesystem if there is a usb mass storage device attached"
* I don't know if failover also fits into the model we don't quite
have. LTE route depends on pppoe not being healthy
we can have services (or bundles) that aren't part of the default target,
and plumb them into events of some kind (netlink?) to bring them up/down?
we can use s6-rc instanced services:
https://skarnet.org/software/s6/instances.html
"s6-instance-create and s6-instance-delete are relatively expensive operations, because they have to recursively copy or delete directories and use the synchronization mechanism with the instance supervisor, compared to s6-instance-control which only has to send commands to already existing supervisors. If you are going to turn instances on and off on a regular basis, it is more efficient to keep the instance existing and control it with s6-instance-control than it is to repeatedly create and delete it. "
Probably we need something that reads netlink messages and converts
them to a format that we can use to control services. Is there a
benefit to using services here and not just running commands? it means
the system state change we desire will stay changed.
TODO items not to lose track of
- speed testing (iperf)
- make gl-ar750 tftpboot build again
- finish belkin
- install sniproxy
- is there something simple we can do to make it reboot again?
- turn rotuer,extneder examples into "profiles" that don't embed
hardware specifics
Thu Feb 15 11:50:56 GMT 2024
1) to make tftpboot work with old bootm implementations we need
- compressed root
- uncompressed root
- kernel with dtb
dtb needs to know where uncompressed rootfs is and how big
2) if the image is a zImage (arm32) or an Image (arm64) we have to stick
with the three-arg bootz, and the dtb has to be lower in ram than the kernel
Fri Feb 16 15:43:32 GMT 2024
DHCP6c refresh is still wrong. We get updates for an address that
hasn't changed prefix or length, when the expiry times have changed,
and we can't action that by remove;add because remove will wipe out
any routes through the interface but add won't put them back
We can use "change" for both adds and changes, but we need to know that
a change is not a delete
The "identity" of an address is the address itself: kernel won't
let you add the same address with two different prefixes.
Keeping it simple, we could call "change" on every address in the
new-addresses list and "del" on every address in old-addresses
that is no longer in new-addresses
If the upstream has changed length, "ip addr change" is ignored,
so it needs to be in deleted as well as added/changed
Fri Feb 16 19:37:08 GMT 2024
[ 3.839775] cfg80211: module verification failed: signature and/or required key missing - tainting kernel
[ 4.156952] ath10k_pci 0000:00:00.0: enabling device (0000 -> 0002)
[ 4.165756] ath10k_pci 0000:00:00.0: pci irq legacy oper_irq_mode 1 irq_mode 0 reset_mode 0
[ 4.399285] ath10k_pci 0000:00:00.0: qca9887 hw1.0 target 0x4100016d chip_id 0x004000ff sub 0000:0000
[ 4.408906] ath10k_pci 0000:00:00.0: kconfig debug 1 debugfs 0 tracing 0 dfs 0 testmode 0
[ 4.420096] ath10k_pci 0000:00:00.0: firmware ver 10.2.4-1.0-00047 api 5 features no-p2p,ignore-otp,ski
p-clock-init,mfp,allows-mesh-bcast crc32 62f7565f
[ 4.467443] ath10k_pci 0000:00:00.0: board_file api 1 bmi_id N/A crc32 546cca0d
[ 5.472096] ath10k_pci 0000:00:00.0: htt-ver 2.1 wmi-op 5 htt-op 2 cal file max-sta 128 raw 0 hwcrypto
[ 5.585796] ath: EEPROM regdomain: 0x0
[ 5.589712] ath: EEPROM indicates default country code should be used
[ 5.596364] ath: doing EEPROM country->regdmn map search
[ 5.601875] ath: country maps to regdmn code: 0x3a
[ 5.606831] ath: Country alpha2 being used: US
[ 5.611425] ath: Regpair used: 0x3a
[ 6.742365] ath10k_pci 0000:00:00.0: pdev param 0 not supported by firmware
[ 6.903389] random: hostapd: uninitialized urandom read (1027 bytes read)
[ 8.169901] ath10k_pci 0000:00:00.0: pdev param 0 not supported by firmware
[ 14.450193] ath10k_pci 0000:00:00.0: pdev param 0 not supported by firmware
[ 15.518682] random: hostapd: uninitialized urandom read (1027 bytes read)
[ 16.762697] ath10k_pci 0000:00:00.0: pdev param 0 not supported by firmware
[ 23.030622] ath10k_pci 0000:00:00.0: pdev param 0 not supported by firmware
[
Tue Feb 27 23:16:27 GMT 2024
We made it a full week with rotuer running internet chez nous and no
need for an intervention, so I am happy to call it "production". There are
still things that need fixing but they're mostly within scope for
a services refresh
I have embarked on "profiles" by creating a wap.nix
I think we could have a service module for resolvconf
It would be good to build a wap.nix example for the belkin and we
could start looking at ubifs
I've lost a chunk of notes about using events to drive desired service
state. There is probably only going to be one udev listener, so
what if we have udev as a config key thusly
udev.rules = [
{
match = {
SUBSYSTEM = "rpmsg";
ATTR.name = "DATA5_CNTL";
};
service = longrun {
name = "lte-modem";
run = "blah blah blah";
};
}
# this one would be provided by the bridge module instead of
# adding bridge member services to the default target
{
match = {
SUBSYSTEM="net";
ID_PATH="pci-0000:04:00.0";
ATTR.operstate = "up";
};
service = oneshot {
up = "ip link set dev $dev master $(output ${primary} ifname)";
down = "ip link set dev $(output ${member} ifname) nomaster";
}
}
]
This works for udev/sysfs, but we want a similar architecture(sic) for
user-generated target state so we could have services that run on e.g.
"is the ppp0 service healthy" or not. Probably there isn't a top-level
config key for each service though
services.wan = svc.ppoe.build { .... };
services.lte = watcher.build {
watching = services.wan;
match = {
# an expression matching the outputs of the service
# to be watched
health = "failing";
};
service = oneshot {
run = "start_lte_blah";
};
}
thing is, we could use this syntax also for sysfs watches, but not vice versa
... but it's not quite the same because here we're doing static matches
on contents of files, whereas the udev one is a query expression on the
sysfs database. we might need that flexibiity to implement "mount the
backup drive no matter _which_ damn sda_n_ device it appears as". I don't
know if there's the same need for service outputs - postulate the
existence of a collection of services which are all similar enough that
some other service can watch them all and do $something when one of
the changes state. Or a single service with very complicated outputs.
For example, something could watch the snmp database and update service
status depending on what it finds. Or something something mqtt...
we find that the "match" needs to be interpreted differently according
to the thing being watched. perhaps the service being watched needs to
provide a "watch me" interface somehow which accepts match criteria and
outputs a true/false. Something else then needs to
services.addmember = services.udev.watch {
match = {
SUBSYSTEM = "net";
ID_PATH = "pci-0000:04:00.0";
ATTR.operstate = "up";
};
service = oneshot {
up = "ip link set dev $dev master $(output ${primary} ifname)";
down = "ip link set dev $(output ${member} ifname) nomaster";
};
}
Sat Mar 2 15:37:29 GMT 2024
Simply put, what I think it boils down to is that we want a service
which acts as an actuator or control switch for another service,
and will start/stop that controlled service according to some
criteria.
services.addmember = svc.network.ifwatch.build {
interface = config.hardware.networkInterfaces.lan1;
# this should be part of the definition not the params
service = oneshot {
name = "member-${bridge}-${interface}";
up = "ip link set dev $dev master $(output ${primary} ifname)";
down = "ip link set dev $(output ${member} ifname) nomaster";
};
}
we could start by writing this. we need to adapt ifwait
Sun Mar 3 17:09:21 GMT 2024
this is annoyingly hard to test. the tests we'd like to write are
1) when it gets events that don't match the requirement, nothing happens
2) when it gets an event that should start the service, the
service starts
3) when stop should stop
4) when start and already started, nothing happens
5) when stop and already stopped, nothing happens
what do we do if service fails to start? s6-rc will eventually reset it
to "down", I think: do we need to take action?
Mon Mar 4 20:46:55 GMT 2024
# relevant but not correct for this model: https://www.forked.net/forums/viewtopic.php?f=13&t=3490
# power on port 5
snmpset -v 1 -c private 192.168.5.14 .1.3.6.1.4.1.318.1.1.4.4.2.1.3.5 integer 1
# power off port 5
snmpset -v 1 -c private 192.168.5.14 .1.3.6.1.4.1.318.1.1.4.4.2.1.3.5 integer 2
# toggle off/on port 5
snmpset -v 1 -c private 192.168.5.14 .1.3.6.1.4.1.318.1.1.4.4.2.1.3.5 integer 3
Wed Mar 6 18:24:29 GMT 2024
What happens when we attempt to start the service but it fails? We
assume the start was successful so we won't try and restart it again
next time we get an event that should cause it to start.

9
ci.nix
View File

@ -9,9 +9,14 @@ let
borderVmConf = ./bordervm.conf-example.nix;
inherit (pkgs.lib.attrsets) genAttrs;
devices = [
"gl-ar750" "gl-mt300n-v2" "gl-mt300a"
"qemu" "qemu-aarch64" "qemu-armv7l"
"gl-ar750"
"gl-mt300a"
"gl-mt300n-v2"
"qemu"
"qemu-aarch64"
"qemu-armv7l"
"tp-archer-ax23"
"zyxel-nwa50ax"
];
vanilla = ./vanilla-configuration.nix;
for-device = name:

View File

@ -3,6 +3,7 @@
, liminix-config ? <liminix-config>
, nixpkgs ? <nixpkgs>
, borderVmConf ? ./bordervm.conf.nix
, imageType ? "primary"
}:
let
@ -30,6 +31,9 @@ let
./modules/s6
./modules/users.nix
./modules/outputs.nix
{
boot.imageType = imageType;
}
];
};
config = eval.config;

View File

@ -73,7 +73,7 @@
MTK_INFRACFG = "y";
MTK_PMIC_WRAP = "y";
MTK_EFUSE="y";
NVMEM_MTK_EFUSE="y";
# MTK_HSDMA="y";
MTK_SCPSYS="y";
MTK_SCPSYS_PM_DOMAINS="y";
@ -92,7 +92,6 @@
MEDIATEK_GE_PHY = "y";
# MEDIATEK_MT6577_AUXADC = "y";
# MEDIATEK_WATCHDOG = "y";
NET_MEDIATEK_SOC = "y";
NET_MEDIATEK_SOC_WED = "y";
NET_MEDIATEK_STAR_EMAC = "y"; # this enables REGMAP_MMIO
@ -149,6 +148,13 @@
WATCHDOG = "y";
MEDIATEK_WATCHDOG = "y";
};
conditionalConfig = {
WLAN= {
MT7615E = "m";
MT7622_WMAC = "y";
MT7915E = "m";
};
};
};
boot = {
commandLine = [ "console=ttyS0,115200" ];
@ -169,12 +175,9 @@
hardware =
let
openwrt = pkgs.openwrt;
mac80211 = pkgs.mac80211.override {
drivers = [
"mt7615e"
"mt7915e"
];
klibBuild = config.system.outputs.kernel.modulesupport;
mac80211 = pkgs.kmodloader.override {
targets = ["mt7615e" "mt7915e"];
inherit (config.system.outputs) kernel;
};
in {
ubi = {

View File

@ -71,9 +71,10 @@
cp $blobdir/board.bin $out/ath10k/QCA9887/hw1.0/
'';
};
mac80211 = pkgs.mac80211.override {
drivers = ["ath9k" "ath10k_pci"];
klibBuild = config.system.outputs.kernel.modulesupport;
mac80211 = pkgs.kmodloader.override {
targets = ["ath9k" "ath10k_pci"];
inherit (config.system.outputs) kernel;
dependencies = [ ath10k_cal_data ];
};
ath10k_cal_data =
let
@ -132,7 +133,7 @@
};
wlan5 = link.build {
ifname = "wlan1";
dependencies = [ mac80211 ath10k_cal_data ];
dependencies = [ ath10k_cal_data mac80211 ];
};
};
};
@ -161,6 +162,8 @@
# OpenWrt kernel patches
extraPatchPhase = ''
${openwrt.applyPatches.ath79}
sed -i.bak -e '\,include <linux/hw_random.h>,a #include <linux/gpio/driver.h>' drivers/net/wireless/ath/ath9k/ath9k.h # context reqd for next patch
patch -p1 < ${openwrt.src}/package/kernel/mac80211/patches/ath9k/552-ath9k-ahb_of.patch
'';
config = {
@ -211,14 +214,21 @@
WATCHDOG = "y";
ATH79_WDT = "y"; # watchdog timer
# this is all copied from nixwrt ath79 config. Clearly not all
# of it is device config, some of it is wifi config or
# installation method config or ...
EARLY_PRINTK = "y";
PRINTK_TIME = "y";
};
conditionalConfig = {
WLAN = {
WLAN_VENDOR_ATH = "y";
ATH_COMMON = "m";
ATH9K = "m";
ATH9K_AHB = "y";
ATH10K = "m";
ATH10K_PCI = "m";
ATH10K_DEBUG = "y";
};
};
};
};
}

View File

@ -47,9 +47,9 @@
let
inherit (pkgs.liminix.networking) interface;
inherit (pkgs) openwrt;
mac80211 = pkgs.mac80211.override {
drivers = ["rt2800soc"];
klibBuild = config.system.outputs.kernel.modulesupport;
mac80211 = pkgs.kmodloader.override {
targets = ["rt2800soc"];
inherit (config.system.outputs) kernel;
};
in {
imports = [
@ -178,6 +178,14 @@
} // lib.optionalAttrs (config.system.service ? vlan) {
SWCONFIG = "y";
};
conditionalConfig = {
WLAN = {
WLAN_VENDOR_RALINK = "y";
RT2800SOC = "m";
RT2X00 = "m";
};
};
};
};
}

View File

@ -43,9 +43,9 @@
inherit (pkgs.pseudofile) dir symlink;
inherit (pkgs) openwrt;
mac80211 = pkgs.mac80211.override {
drivers = ["mt7603e"];
klibBuild = config.system.outputs.kernel.modulesupport;
mac80211 = pkgs.kmodloader.override {
targets = ["mt7603e"];
inherit (config.system.outputs) kernel;
};
wlan_firmware = pkgs.fetchurl {
url = "https://github.com/openwrt/mt76/raw/f24b56f935392ca1d35fae5fd6e56ef9deda4aad/firmware/mt7628_e2.bin";
@ -185,6 +185,15 @@
RALINK_WDT = "y"; # watchdog
MT7621_WDT = "y"; # or it might be this one
};
conditionalConfig = {
WLAN = {
WLAN_VENDOR_RALINK = "y";
WLAN_VENDOR_MEDIATEK = "y";
MT7603E = "m";
};
};
};
};
}

View File

@ -322,9 +322,14 @@
ZSTD_COMPRESS="y";
ZSTD_DECOMPRESS="y";
} // lib.optionalAttrs (config.system.service ? watchdog) {
RALINK_WDT = "y"; # watchdog
MT7621_WDT = "y"; # or it might be this one
};
RALINK_WDT = "y"; # watchdog
MT7621_WDT = "y"; # or it might be this one
};
conditionalConfig = {
WLAN = {
MT7915E = "m";
};
};
};
tplink-safeloader.board = "ARCHER-AX23-V1";
boot = {
@ -353,11 +358,11 @@
hardware =
let
openwrt = pkgs.openwrt;
mac80211 = pkgs.mac80211.override {
drivers = [
mac80211 = pkgs.kmodloader.override {
targets = [
"mt7915e"
];
klibBuild = config.system.outputs.kernel.modulesupport;
inherit (config.system.outputs) kernel;
};
in {
# from OEM bootlog (openwrt wiki):

View File

@ -182,12 +182,10 @@
kernel = {
src = pkgs.pkgsBuildBuild.fetchurl {
name = "linux.tar.gz";
url = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.137.tar.gz";
hash = "sha256-PkdzUKZ0IpBiWe/RS70J76JKnBFzRblWcKlaIFNxnHQ=";
url = "https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.7.4.tar.gz";
hash = "sha256-wIrmL0BS63nRwWfm4nw+dRNVPUzGh9M4X7LaHzAn5tU=";
};
extraPatchPhase = ''
${pkgs.openwrt.applyPatches.mvebu}
'';
version = "6.7.4";
config = {
PCI = "y";
OF = "y";
@ -203,6 +201,10 @@
RTC_CLASS = "y";
RTC_DRV_ARMADA38X = "y"; # this may be useful anyway?
EXPERT = "y";
ALLOW_DEV_COREDUMP = "n";
# dts has a compatible for this but dmesg is not
# showing it
EEPROM_AT24 = "y"; # atmel,24c64
@ -213,9 +215,9 @@
MACH_ARMADA_38X = "y";
SMP = "y";
# this is disabled for the moment because it relies on a GCC
# plugin that requires gmp.h to build, and I can't see right now
# how to confgure it to find gmp
# this is disabled for the moment because it relies on a
# GCC plugin that requires gmp.h to build, and I can't see
# right now how to confgure it to find gmp
STACKPROTECTOR_PER_TASK = "n";
NR_CPUS = "4";
VFP = "y";
@ -227,7 +229,7 @@
PSTORE = "y";
PSTORE_RAM = "y";
PSTORE_CONSOLE = "y";
PSTORE_DEFLATE_COMPRESS = "n";
# PSTORE_DEFLATE_COMPRESS = "n";
BLOCK = "y";
MMC="y";
@ -286,9 +288,17 @@
USB_XHCI_MVEBU = "y";
USB_XHCI_HCD = "y";
};
WLAN = {
WLAN_VENDOR_ATH = "y";
ATH_COMMON = "m";
ATH9K = "m";
ATH9K_PCI = "y";
ATH10K = "m";
ATH10K_PCI = "m";
ATH10K_DEBUG = "y";
};
};
};
boot = {
commandLine = [
"console=ttyS0,115200"
@ -328,9 +338,9 @@
};
hardware = let
mac80211 = pkgs.mac80211.override {
drivers = ["ath9k_pci" "ath10k_pci"];
klibBuild = config.system.outputs.kernel.modulesupport;
mac80211 = pkgs.kmodloader.override {
inherit (config.system.outputs) kernel;
targets = ["ath9k" "ath10k_pci"];
};
in {
defaultOutput = "mtdimage";
@ -339,9 +349,9 @@
rootDevice = "/dev/mmcblk0p1";
dts = {
src = "${config.system.outputs.kernel.modulesupport}/arch/arm/boot/dts/armada-385-turris-omnia.dts";
src = "${config.system.outputs.kernel.modulesupport}/arch/arm/boot/dts/marvell/armada-385-turris-omnia.dts";
includes = [
"${config.system.outputs.kernel.modulesupport}/arch/arm/boot/dts/"
"${config.system.outputs.kernel.modulesupport}/arch/arm/boot/dts/marvell/"
];
};
flash.eraseBlockSize = 65536; # only used for tftpboot

View File

@ -0,0 +1,155 @@
#include "mt7621.dtsi"
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/input/input.h>
/ {
aliases {
label-mac-device = &gmac0;
};
};
&nand {
status = "okay";
mediatek,nmbm;
mediatek,bmt-max-ratio = <15>;
mediatek,bmt-max-reserved-blocks = <64>;
mediatek,bmt-remap-range =
<0x0 0x980000>,
<0x2980000 0x7800000>;
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "u-boot";
reg = <0x0 0x80000>;
read-only;
};
partition@80000 {
label = "u-boot-env";
reg = <0x80000 0x80000>;
read-only;
};
factory: partition@100000 {
label = "factory";
reg = <0x100000 0x80000>;
read-only;
};
partition@180000 {
label = "firmware_a";
reg = <0x180000 0x2800000>;
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "kernel_a";
reg = <0x0 0x800000>;
};
partition@400000 {
label = "ubi";
reg = <0x800000 0x2000000>;
};
};
partition@2980000 {
label = "firmware_b";
reg = <0x2980000 0x2800000>;
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "kernel_b";
reg = <0x0 0x800000>;
};
partition@400000 {
label = "ubi_b";
reg = <0x800000 0x2000000>;
};
};
partition@5180000 {
label = "rootfs_data";
reg = <0x5180000 0x1400000>;
};
partition@6580000 {
label = "logs";
reg = <0x6580000 0xd00000>;
};
partition@7280000 {
label = "vendor-myzyxel";
reg = <0x7280000 0x480000>;
read-only;
};
partition@7700000 {
label = "bootconfig";
reg = <0x7700000 0x80000>;
};
mrd: partition@7780000 {
label = "mrd";
reg = <0x7780000 0x80000>;
read-only;
nvmem-layout {
compatible = "fixed-layout";
#address-cells = <1>;
#size-cells = <1>;
macaddr_mrd_1fff8: macaddr@1fff8 {
reg = <0x1fff8 0x6>;
};
};
};
};
};
&pcie {
status = "okay";
};
&pcie1 {
wlan_5g: wifi@0,0 {
reg = <0x0 0 0 0 0>;
compatible = "mediatek,mt76";
mediatek,mtd-eeprom = <&factory 0x0>;
/* MAC-Address set in userspace */
};
};
&gmac0 {
nvmem-cells = <&macaddr_mrd_1fff8>;
nvmem-cell-names = "mac-address";
};
&switch0 {
ports {
port@4 {
status = "okay";
label = "lan";
};
};
};
&state_default {
gpio {
groups = "uart3";
function = "gpio";
};
};

View File

@ -0,0 +1,155 @@
#include "mt7621.dtsi"
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/input/input.h>
/ {
aliases {
label-mac-device = &gmac0;
};
};
&nand {
status = "okay";
mediatek,nmbm;
mediatek,bmt-max-ratio = <15>;
mediatek,bmt-max-reserved-blocks = <64>;
mediatek,bmt-remap-range =
<0x0 0x980000>,
<0x2980000 0x7800000>;
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "u-boot";
reg = <0x0 0x80000>;
read-only;
};
partition@80000 {
label = "u-boot-env";
reg = <0x80000 0x80000>;
read-only;
};
factory: partition@100000 {
label = "factory";
reg = <0x100000 0x80000>;
read-only;
};
partition@2980000 {
label = "firmware_b";
reg = <0x2980000 0x2800000>;
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "kernel_b";
reg = <0x0 0x800000>;
};
partition@400000 {
label = "ubi";
reg = <0x800000 0x2000000>;
};
};
partition@180000 {
label = "firmware_a";
reg = <0x180000 0x2800000>;
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "kernel_a";
reg = <0x0 0x800000>;
};
partition@400000 {
label = "ubi_a";
reg = <0x800000 0x2000000>;
};
};
partition@5180000 {
label = "rootfs_data";
reg = <0x5180000 0x1400000>;
};
partition@6580000 {
label = "logs";
reg = <0x6580000 0xd00000>;
};
partition@7280000 {
label = "vendor-myzyxel";
reg = <0x7280000 0x480000>;
read-only;
};
partition@7700000 {
label = "bootconfig";
reg = <0x7700000 0x80000>;
};
mrd: partition@7780000 {
label = "mrd";
reg = <0x7780000 0x80000>;
read-only;
nvmem-layout {
compatible = "fixed-layout";
#address-cells = <1>;
#size-cells = <1>;
macaddr_mrd_1fff8: macaddr@1fff8 {
reg = <0x1fff8 0x6>;
};
};
};
};
};
&pcie {
status = "okay";
};
&pcie1 {
wlan_5g: wifi@0,0 {
reg = <0x0 0 0 0 0>;
compatible = "mediatek,mt76";
mediatek,mtd-eeprom = <&factory 0x0>;
/* MAC-Address set in userspace */
};
};
&gmac0 {
nvmem-cells = <&macaddr_mrd_1fff8>;
nvmem-cell-names = "mac-address";
};
&switch0 {
ports {
port@4 {
status = "okay";
label = "lan";
};
};
};
&state_default {
gpio {
groups = "uart3";
function = "gpio";
};
};

View File

@ -0,0 +1,367 @@
{
system = {
crossSystem = {
config = "mipsel-unknown-linux-musl";
gcc = {
abi = "32";
arch = "mips32"; # mips32r2?
};
};
};
description = ''
Zyxel NWA50AX
********************
Zyxel NWA50AX is quite close to the GL-MT300N-v2 "Mango" device, but it is based on the MT7621
chipset instead of the MT7628.
Installation
============
This device is pretty, but, due to its A/B capabilities, can be a bit hard
to use completely.
The stock vendor firmware is a downstream fork of U-Boot: <https://github.com/RaitoBezarius/uboot-nwa50ax>
with restricted boot commands. Fortunately, OpenWrt folks figured out trivial command injections,
so you can use most of the OpenWrt commands without trouble by just command injecting
atns, atna or atnf, e.g. atns "; $real_command".
From factory web UI, you can upload the result of the zyxel-nwa-fit output.
From another operating system, you need to `dumpimage -T flat_dt -p 0 $zyxel-nwa-fit -o firmware.bin`,
`flash_erase $(mtd partition of the target partition firmware or zy_firmware) 0 0`, then you complete by
`nandwrite -p $(mtd partition of the target partition firmware or zy_firmware) firmware.bin`.
How to put the firmware.bin on the machine is left to you as an exercise, e.g. SSH, TFTP, whatever.
From serial, you have two choices:
- Flash this system via U-Boot:
same reasoning as from an existing Linux system, two choices:
- ymodem the binary, perform the write manually, you can inspire yourself
from the `script` contained in the vendor firmware, those are just a FIT containing a script.
- prepare a FIT containing a script executing your commands, tftpboot this.
- boot from an existing Liminix system, e.g. TFTPBOOT image.
- boot from an OpenWrt system, i.e. follow OpenWrt steps.
Once you are in a Linux system, understand that this device has A/B boot.
OpenWrt provides you with `zyxel-bootconfig` to set/unset the image status and choice.
The kernel is booted with `bootImage=<number>` which tells you which slot are you on.
You should find yourself with 10ish MTD partitions, the most interesting ones are two:
- firmware: 40MB
- firmware_1: 40MB
In the current setup, they are split further into kernel (8MB) and ubi (32MB).
Once you are done with first installation, note that if you want to use the A/B feature,
you need to write a _secondary_ image on the slot B. There is no proper flashing code
that will set the being-updated slot to `new` and boot on it to verify if it's working.
This is a WIP.
Upgrading your system can be achieved via:
- `liminix-rebuild` for the userspace.
- `flash_erase` + `nandwrite` for the kernelspace to the other slot than the one you are booted on,
note that you can just nandwrite the mtd partition corresponding to the *kernel* and not the whole firmware.
If you soft-bricked your AP, i.e. you cannot boot anything in U-Boot, no worries, just plug the serial console,
prepare a TFTP server (via `tufted` for example), download vendor firmware, set up `atns`, `atnf`, etc. and run `atnz`.
This will reflash everything back to normal via TFTP.
If you hard-bricked your AP, i.e. U-Boot is telling you to transfer a valid bootloader via ymodem, just extract
a U-Boot from the vendor OS, send it via ymodem and use the previous operations to perform a full flash this time
of all partitions.
Note that if you erased your MRD partition, you lost your serial and MAC address. There's no way to recover the original one
except by reading the physical label on your device!
If you super-hard-bricked your AP, i.e. no output on serial console, congratulations, you reached one of the rare state
of this device. You need an external NAND flasher to repair it and write the first stage from Mediatek to continue the previous
recovery operations.
Development TODO list:
- Better support for upgrade automation w.r.t. to A/B, e.g. automagic scripts.
- Mount the logs partition, mount / as overlayfs of firmware ? rootfs and rootfs_data for extended data.
- Jitter-based entropy injection? Device can be slow to initialize its CRNG and hostapd will reject few clients at the start because of that.
- Defaults for hostapd based on MT7915 capabilities? See the example for one possible list.
- Remove primary/secondary hack and put it in preinit.
- Offer ways to reflash the *bootloader* itself to support direct boot via UBI and kernel upgrades via filesystem rewrite.
Vendor web page: https://www.zyxel.com/fr/fr/products/wireless/ax1800-wifi-6-dual-radio-nebulaflex-access-point-nwa50ax
OpenWrt web page: https://openwrt.org/inbox/toh/zyxel/nwa50ax
OpenWrt tech data: https://openwrt.org/toh/hwdata/zyxel/zyxel_nwa50ax
'';
module = { pkgs, config, lib, lim, ...}:
let
inherit (pkgs.liminix.networking) interface;
inherit (pkgs.liminix.services) oneshot;
inherit (pkgs.pseudofile) dir symlink;
inherit (pkgs) openwrt;
mac80211 = pkgs.mac80211.override {
drivers = [ "mt7915e" ];
klibBuild = config.system.outputs.kernel.modulesupport;
};
# v204520220929
wlan_firmware = pkgs.fetchurl {
url = "https://github.com/openwrt/mt76/raw/1b88dd07f153b202e57fe29734806744ed006b0e/firmware/mt7915_wa.bin";
hash = "sha256-wooyefzb0i8640+lwq3vNhcBXRFCtGuo+jiL7afZaKA=";
};
wlan_firmware' = pkgs.fetchurl {
url = "https://github.com/openwrt/mt76/raw/1b88dd07f153b202e57fe29734806744ed006b0e/firmware/mt7915_wm.bin";
hash = "sha256-k62nQewRuKjBLd5R3RxU4F74YKnQx5zr6gqMMImqVQw=";
};
wlan_firmware'' = pkgs.fetchurl {
url = "https://github.com/openwrt/mt76/raw/1b88dd07f153b202e57fe29734806744ed006b0e/firmware/mt7915_rom_patch.bin";
hash = "sha256-ifriAjWzFACrxVWCANZpUaEZgB/0pdbhnTVQytx6ddg=";
};
in {
imports = [
# We include it to ensure the bridge functionality
# is available on the target kernel.
../../modules/bridge
../../modules/arch/mipsel.nix
../../modules/outputs/tftpboot.nix
../../modules/outputs/zyxel-nwa-fit.nix
../../modules/zyxel-dual-image
];
filesystem = dir {
lib = dir {
firmware = dir {
mediatek = dir {
"mt7915_wa.bin" = symlink wlan_firmware;
"mt7915_wm.bin" = symlink wlan_firmware';
"mt7915_rom_patch.bin" = symlink wlan_firmware'';
};
};
};
};
rootfsType = "ubifs";
hardware = {
# Taken from OpenWRT
# root@OpenWrt:/# ubinfo /dev/ubi0
# ubi0
# Volumes count: 2
# Logical eraseblock size: 126976 bytes, 124.0 KiB
# Total amount of logical eraseblocks: 256 (32505856 bytes, 31.0 MiB)
# Amount of available logical eraseblocks: 0 (0 bytes)
# Maximum count of volumes 128
# Count of bad physical eraseblocks: 0
# Count of reserved physical eraseblocks: 19
# Current maximum erase counter value: 2
# Minimum input/output unit size: 2048 bytes
# Character device major/minor: 250:0
# Present volumes: 0, 1
ubi = {
minIOSize = "2048";
logicalEraseBlockSize = "126976";
physicalEraseBlockSize = "128KiB";
maxLEBcount = "256";
};
# This is a FIT containing a kernel padded and
# a UBI volume rootfs.
defaultOutput = "zyxel-nwa-fit";
loadAddress = lim.parseInt "0x80001000";
entryPoint = lim.parseInt "0x80001000";
# Aligned on 2kb.
alignment = 2048;
rootDevice = "ubi:rootfs";
dts = {
# Actually, this is not what we want.
# This DTS is insufficient.
src = ./mt7621_zyxel_nwa50ax.dtsi;
includes = [
# Here's one weird trick to make `ubi` detection
# out of the box.
# We will write ubi on /dev/firmware_a:rootfs location
# and same for /dev/firmware_b:rootfs.
# How do we distinguish both?
# We can just use the DTS to point ubi at A or B.
# This, unfortunately, means that we have "two images".
# But they are really just 1 image with 2 different DTS.
# TODO: improve this hack in preinit?
(if config.boot.imageType == "primary" then "${./a_image}" else "${./b_image}")
"${openwrt.src}/target/linux/ramips/dts"
];
};
networkInterfaces =
let
inherit (config.system.service.network) link;
in {
eth = link.build { ifname = "eth0"; };
lan = link.build { ifname = "lan"; };
wlan0 = link.build {
ifname = "wlan0";
dependencies = [ mac80211 ];
};
wlan1 = link.build {
ifname = "wlan1";
dependencies = [ mac80211 ];
};
};
};
boot = {
# Critical because NWA50AX will extend your cmdline with the image number booted.
# and some bootloader version.
# You don't want to find yourself being overridden.
commandLineDtbNode = "bootargs-override";
imageFormat = "fit";
tftp = {
# 5MB is nice.
freeSpaceBytes = 5 * 1024 * 1024;
loadAddress = lim.parseInt "0x2000000";
};
};
# Dual image management service in userspace.
services.zyxel-dual-image = config.boot.zyxel-dual-image.build {
ensureActiveImage = "primary";
# TODO: use mtd names rather…
# primary and secondary are always /dev/mtd3 by virtue of the
# dtb being not too wrong…
# TODO: remove this hack.
primaryMtdPartition = "/dev/mtd3";
secondaryMtdPartition = "/dev/mtd3";
bootConfigurationMtdPartition = "/dev/mtd12";
};
# DEVICE_VENDOR := ZyXEL
# KERNEL_SIZE := 8192k
# DEVICE_PACKAGES := kmod-mt7915-firmware zyxel-bootconfig
# KERNEL := kernel-bin | lzma | fit lzma $$(KDIR)/image-$$(firstword $$(DEVICE_DTS)).dtb
# IMAGES += factory.bin ramboot-factory.bin
# IMAGE/factory.bin := append-kernel | pad-to $$(KERNEL_SIZE) | append-ubi | zyxel-nwa-fit
# IMAGE/ramboot-factory.bin := append-kernel | pad-to $$(KERNEL_SIZE) | append-ubi
kernel = {
src = pkgs.fetchurl {
name = "linux.tar.gz";
url = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.137.tar.gz";
hash = "sha256-PkdzUKZ0IpBiWe/RS70J76JKnBFzRblWcKlaIFNxnHQ=";
};
extraPatchPhase = ''
${openwrt.applyPatches.ramips}
'';
config = {
RALINK = "y";
PCI = "y";
PHY_MT7621_PCI = "y";
PCIE_MT7621 = "y";
SOC_MT7621 = "y";
CLK_MT7621 = "y";
CLOCKSOURCE_WATCHDOG = "y";
SERIAL_8250_CONSOLE = "y";
SERIAL_8250 = "y";
SERIAL_CORE_CONSOLE = "y";
SERIAL_OF_PLATFORM = "y";
SERIAL_8250_NR_UARTS = "3";
SERIAL_8250_RUNTIME_UARTS = "3";
SERIAL_MCTRL_GPIO = "y";
CONSOLE_LOGLEVEL_DEFAULT = "8";
CONSOLE_LOGLEVEL_QUIET = "4";
# MTD_UBI_BEB_LIMIT = "20";
# MTD_UBI_WL_THRESHOLD = "4096";
MTD = "y";
MTD_BLOCK = "y"; # fix undefined ref to register_mtd_blktrans_dev
MTD_RAW_NAND = "y";
MTD_NAND_MT7621 = "y";
MTD_NAND_MTK_BMT = "y"; # Bad-block Management Table
MTD_NAND_ECC_SW_HAMMING= "y";
MTD_SPI_NAND= "y";
MTD_OF_PARTS = "y";
MTD_NAND_CORE= "y";
MTD_SPLIT_FIRMWARE= "y";
MTD_SPLIT_FIT_FW= "y";
PINCTRL = "y";
PINCTRL_MT7621 = "y";
I2C = "y";
I2C_MT7621 = "y";
SPI = "y";
MTD_SPI_NOR = "y";
SPI_MT7621 = "y";
SPI_MASTER = "y";
SPI_MEM = "y";
REGULATOR = "y";
REGULATOR_FIXED_VOLTAGE = "y";
RESET_CONTROLLER = "y";
POWER_RESET = "y";
POWER_RESET_GPIO = "y";
POWER_SUPPLY = "y";
LED_TRIGGER_PHY = "y";
PCI_DISABLE_COMMON_QUIRKS = "y";
PCI_DOMAINS = "y";
PCI_DOMAINS_GENERIC = "y";
PCI_DRIVERS_GENERIC = "y";
PCS_MTK_LYNXI = "y";
SOC_BUS = "y";
NET = "y";
ETHERNET = "y";
WLAN = "y";
PHYLIB = "y";
AT803X_PHY = "y";
FIXED_PHY = "y";
GENERIC_PHY = "y";
NET_DSA = "y";
NET_DSA_MT7530 = "y";
NET_DSA_MT7530_MDIO = "y";
NET_DSA_TAG_MTK = "y";
NET_MEDIATEK_SOC = "y";
NET_SWITCHDEV = "y";
NET_VENDOR_MEDIATEK = "y";
SWPHY = "y";
GPIOLIB = "y";
GPIO_MT7621 = "y";
OF_GPIO = "y";
EARLY_PRINTK = "y";
NEW_LEDS = "y";
LEDS_TRIGGERS = "y";
LEDS_CLASS = "y"; # required by rt2x00lib
LEDS_CLASS_MULTICOLOR = "y";
LEDS_BRIGHTNESS_HW_CHANGED = "y";
PRINTK_TIME = "y";
} // lib.optionalAttrs (config.system.service ? vlan) {
SWCONFIG = "y";
} // lib.optionalAttrs (config.system.service ? watchdog) {
RALINK_WDT = "y"; # watchdog
MT7621_WDT = "y"; # or it might be this one
};
};
};
}

View File

@ -0,0 +1,56 @@
#include "mt7621_zyxel_nwa-ax-for-ab.dtsi"
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/input/input.h>
/ {
compatible = "zyxel,nwa50ax", "mediatek,mt7621-soc";
model = "ZyXEL NWA50AX";
aliases {
led-boot = &led_system_green;
led-failsafe = &led_system_red;
led-running = &led_system_green;
led-upgrade = &led_system_red;
};
leds {
compatible = "gpio-leds";
led_system_red: system_red {
label = "red:system";
gpios = <&gpio 6 GPIO_ACTIVE_HIGH>;
};
led_system_green: system_green {
label = "green:system";
gpios = <&gpio 7 GPIO_ACTIVE_HIGH>;
};
system_blue {
label = "blue:system";
gpios = <&gpio 8 GPIO_ACTIVE_HIGH>;
};
};
keys {
compatible = "gpio-keys";
reset {
label = "reset";
gpios = <&gpio 30 GPIO_ACTIVE_LOW>;
linux,code = <KEY_RESTART>;
};
};
};
&ethernet {
pinctrl-0 = <&mdio_pins>, <&rgmii1_pins>;
};
&state_default {
gpio {
groups = "uart3", "rgmii2";
function = "gpio";
};
};

View File

@ -145,7 +145,7 @@ in rec {
};
users.root = {
passwd = lib.mkForce secrets.root_password;
passwd = lib.mkForce secrets.root.passwd;
# openssh.authorizedKeys.keys = [
# (builtins.readFile "/home/dan/.ssh/id_rsa.pub")
# ];

View File

@ -107,6 +107,7 @@ in {
rules = [
(accept "udp dport 547") # dhcp, could restrict to daddr ff02::1:2
(accept "udp dport 53") # dns
(accept "tcp dport 22")
];
};
@ -232,9 +233,11 @@ in {
type = "filter";
family = "ip";
rules = [
# this is where you put permitted incoming
# connections. Practically there's not a lot of use for this
# chain unless you have routable ipv4 addresses
# This is where you put permitted incoming connections. If
# you're using NAT and want to forward a port from outside to
# devices on the LAN, then you need a DNAT rule in nat-rx chain
# *and* to accept the packet in this chain (specifying the
# internal (RFC1918) address).
];
};

View File

@ -11,10 +11,6 @@
...
}: let
secrets = import ./extneder-secrets.nix;
inherit (pkgs.liminix.services) oneshot longrun bundle target;
inherit (pkgs.pseudofile) dir symlink;
inherit (pkgs) dropbear ifwait serviceFns;
svc = config.system.service;
in rec {
boot = {
tftp = {
@ -24,12 +20,8 @@ in rec {
};
imports = [
../modules/wlan.nix
../modules/profiles/wap.nix
../modules/vlan
../modules/network
../modules/hostapd
../modules/bridge
../modules/ssh
];
hostname = "extneder";
@ -69,68 +61,24 @@ in rec {
};
};
services.hostap = svc.hostapd.build {
interface = config.hardware.networkInterfaces.wlan;
params = {
country_code = "GB";
hw_mode = "g";
wmm_enabled = 1;
ieee80211n = 1;
inherit (secrets) ssid channel wpa_passphrase;
auth_algs = 1; # 1=wpa2, 2=wep, 3=both
wpa = 2; # 1=wpa, 2=wpa2, 3=both
wpa_key_mgmt = "WPA-PSK";
wpa_pairwise = "TKIP CCMP"; # auth for wpa (may not need this?)
rsn_pairwise = "CCMP"; # auth for wpa2
};
};
services.int = svc.bridge.primary.build {
ifname = "int";
};
services.dhcpc = svc.network.dhcp.client.build {
interface = services.int;
dependencies = [ config.services.hostname ];
};
services.bridge = svc.bridge.members.build {
primary = services.int;
members = with config.hardware.networkInterfaces; [
profile.wap = {
interfaces = with config.hardware.networkInterfaces; [
lan
wlan
];
};
services.sshd = svc.ssh.build {};
services.resolvconf = oneshot rec {
dependencies = [ services.dhcpc ];
name = "resolvconf";
# CHECK: https://udhcp.busybox.net/README.udhcpc says
# 'A list of DNS server' but doesn't say what separates the
# list members. Assuming it's a space or other IFS character
up = ''
. ${serviceFns}
( in_outputs ${name}
for i in $(output ${services.dhcpc} dns); do
echo "nameserver $i" > resolv.conf
done
)
'';
};
filesystem = dir {
etc = dir {
"resolv.conf" = symlink "${services.resolvconf}/.outputs/resolv.conf";
wireless = {
networks.${secrets.ssid} = {
interface = config.hardware.networkInterfaces.wlan;
inherit (secrets) channel wpa_passphrase;
country_code = "GB";
hw_mode = "g";
wmm_enabled = 1;
ieee80211n = 1;
};
};
};
services.defaultroute4 = svc.network.route.build {
via = "$(output ${services.dhcpc} router)";
target = "default";
dependencies = [services.dhcpc];
};
users.root.passwd = lib.mkForce secrets.root_password;
users.root.passwd = lib.mkForce secrets.root.passwd;
defaultProfile.packages = with pkgs; [nftables strace tcpdump swconfig];
}

120
examples/nwa50ax-ap.nix Normal file
View File

@ -0,0 +1,120 @@
{ config, pkgs, ... } :
let
inherit (pkgs.liminix.services) oneshot longrun bundle target;
inherit (pkgs) writeText;
svc = config.system.service;
secrets-1 = {
ssid = "Zyxel 2G (N)";
wpa_passphrase = "diamond dogs";
};
secrets-2 = {
ssid = "Zyxel 5G (AX)";
wpa_passphrase = "diamond dogs";
};
baseParams = {
country_code = "FR";
hw_mode = "g";
channel = 6;
wmm_enabled = 1;
ieee80211n = 1;
ht_capab = "[LDPC][GF][HT40-][HT40+][SHORT-GI-40][MAX-AMSDU-7935][TX-STBC]";
auth_algs = 1;
wpa = 2;
wpa_key_mgmt = "WPA-PSK";
wpa_pairwise = "TKIP CCMP";
rsn_pairwise = "CCMP";
};
modernParams = {
hw_mode = "a";
he_su_beamformer = 1;
he_su_beamformee = 1;
he_mu_beamformer = 1;
preamble = 1;
# Allow radar detection.
ieee80211d = 1;
ieee80211h = 1;
ieee80211ac = 1;
ieee80211ax = 1;
vht_capab = "[MAX-MPDU-7991][SU-BEAMFORMEE][SU-BEAMFORMER][RXLDPC][SHORT-GI-80][MAX-A-MPDU-LEN-EXP3][RX-ANTENNA-PATTERN][TX-ANTENNA-PATTERN][TX-STBC-2BY1][RX-STBC-1][MU-BEAMFORMER]";
vht_oper_chwidth = 1;
he_oper_chwidth = 1;
channel = 36;
vht_oper_centr_freq_seg0_idx = 42;
he_oper_centr_freq_seg0_idx = 42;
require_vht = 1;
};
mkWifiSta = params: interface: secrets: svc.hostapd.build {
inherit interface;
params = params // {
inherit (secrets) ssid wpa_passphrase;
};
};
in rec {
imports = [
../modules/wlan.nix
../modules/network
../modules/hostapd
../modules/ssh
../modules/ntp
../modules/vlan
../modules/bridge
];
hostname = "zyxel";
users.root = {
# EDIT: choose a root password and then use
# "mkpasswd -m sha512crypt" to determine the hash.
# It should start wirh $6$.
passwd = "$y$j9T$f8GhLiqYmr3lc58eKhgyD0$z7P/7S9u.kq/cANZExxhS98bze/6i7aBxU6tbl7RMi.";
openssh.authorizedKeys.keys = [
# EDIT: you can add your ssh pubkey here
# "ssh-rsa AAAAB3NzaC1....H6hKd user@example.com";
];
};
services.int = svc.bridge.primary.build {
ifname = "int";
};
services.bridge = svc.bridge.members.build {
primary = services.int;
members = with config.hardware.networkInterfaces; [
lan
wlan0
wlan1
];
};
services.dhcpv4 =
let iface = services.int;
in svc.network.dhcp.client.build { interface = iface; };
services.defaultroute4 = svc.network.route.build {
via = "$(output ${services.dhcpv4} address)";
target = "default";
dependencies = [ services.dhcpv4 ];
};
services.packet_forwarding = svc.network.forward.build { };
services.sshd = svc.ssh.build {
allowRoot = true;
};
services.ntp = config.system.service.ntp.build {
pools = { "pool.ntp.org" = ["iburst"] ; };
};
boot.tftp = {
serverip = "192.0.2.10";
ipaddr = "192.0.2.12";
};
# wlan0 is the 2.4GHz interface.
services.hostap-1 = mkWifiSta baseParams config.hardware.networkInterfaces.wlan0 secrets-1;
# wlan1 is the 5GHz interface, e.g. AX capable.
services.hostap-2 = mkWifiSta (baseParams // modernParams) config.hardware.networkInterfaces.wlan1 secrets-2;
defaultProfile.packages = with pkgs; [ zyxel-bootconfig iw min-collect-garbage mtdutils ];
}

View File

@ -1,5 +1,6 @@
rec {
{
wpa_passphrase = "you bring light in";
ssid = "liminix";
l2tp = {
name = "abcde@a.1";
password = "NotMyIspPassword";
@ -10,9 +11,9 @@ rec {
openssh.authorizedKeys.keys = [
];
};
root_password = root.passwd;
lan = {
prefix = "10.8.0"; # "192.168.8";
prefix = "10.8.0";
};
}

View File

@ -8,7 +8,10 @@
{ config, pkgs, lib, ... } :
let
secrets = import ./rotuer-secrets.nix;
secrets = {
domainName = "fake.liminix.org";
firewallRules = {};
} // (import ./rotuer-secrets.nix);
inherit (pkgs.liminix.services) oneshot longrun bundle;
inherit (pkgs) serviceFns;
svc = config.system.service;
@ -55,7 +58,7 @@ in rec {
services.hostap = svc.hostapd.build {
interface = config.hardware.networkInterfaces.wlan;
params = {
ssid = "liminix";
ssid = secrets.ssid;
hw_mode="g";
channel = "2";
ieee80211n = 1;
@ -65,7 +68,7 @@ in rec {
services.hostap5 = svc.hostapd.build {
interface = config.hardware.networkInterfaces.wlan5;
params = rec {
ssid = "liminix_5";
ssid = "${secrets.ssid}5";
hw_mode="a";
channel = 36;
ht_capab = "[HT40+]";
@ -120,8 +123,8 @@ in rec {
# You can add static addresses for the DHCP server here. I'm
# not putting my actual MAC addresses in a public git repo ...
hosts = { } // lib.optionalAttrs (builtins.pathExists ./static-leases.nix) (import ./static-leases.nix);
domain = "fake.liminix.org";
upstreams = [ "/${secrets.domainName}/" ];
domain = secrets.domainName;
};
services.wan = svc.pppoe.build {
@ -194,9 +197,18 @@ in rec {
defaultProfile.packages = with pkgs; [
min-collect-garbage
nftables
strace
tcpdump
s6
];
programs.busybox.applets = [
"fdisk" "sfdisk"
];
programs.busybox = {
applets = [
"fdisk" "sfdisk"
];
options = {
FEATURE_FANCY_TAIL = "y";
};
};
}

View File

@ -16,7 +16,7 @@
./hostname.nix
./outputs/initramfs.nix
./outputs/jffs2.nix
./kernel.nix
./kernel
./outputs/kexecboot.nix
./mount
./network
@ -31,6 +31,7 @@
./ssh
./outputs/tftpboot.nix
./outputs/ubifs.nix
./ubinize.nix
./users.nix
./vlan
./watchdog

View File

@ -13,7 +13,7 @@ let
in {
imports = [
./kernel.nix # kernel is a separate module for doc purposes
./kernel # kernel is a separate module for doc purposes
];
options = {
defaultProfile = {
@ -58,6 +58,15 @@ in {
default = [];
description = "Kernel command line";
};
commandLineDtbNode = mkOption {
type = types.enum [ "bootargs" "bootargs-override" ];
default = "bootargs";
description = "Kernel command line's devicetree node";
};
imageType = mkOption {
type = types.enum [ "primary" "secondary" ];
default = "primary";
};
imageFormat = mkOption {
type = types.enum ["fit" "uimage"];
default = "uimage";

View File

@ -32,23 +32,21 @@ let
(a: symlink "${busybox}/bin/busybox");
minimalApplets = [
# this is probably less minimal than it could be
"arch" "ash" "base64" "basename" "bc" "brctl" "bunzip2" "bzcat"
"bzip2" "cal" "cat" "chattr" "chgrp" "chmod" "chown" "chpst"
"chroot" "clear" "cmp" "comm" "cp" "cpio" "cut" "date" "dd" "df"
"dirname" "dmesg" "du" "echo" "egrep" "env" "expand" "expr"
"false" "fdisk" "fgrep" "find" "free" "fuser" "grep" "gunzip"
"gzip" "head" "hexdump" "hostname" "hwclock" "ifconfig" "ip"
"ipaddr" "iplink" "ipneigh" "iproute" "iprule" "kill" "killall"
"killall5" "less" "ln" "ls" "lsattr" "lsof" "md5sum" "mkdir"
"mknod" "mktemp" "mount" "mv" "nc" "netstat" "nohup" "od" "pgrep"
"pidof" "ping" "ping6" "pkill" "pmap" "printenv" "printf" "ps"
"pwd" "readlink" "realpath" "reset" "rm" "rmdir" "route" "sed"
"seq" "setsid" "sha1sum" "sha256sum" "sha512sum" "sleep" "sort"
"stat" "strings" "stty" "su" "sum" "swapoff" "swapon" "sync"
"tail" "tee" "test" "time" "touch" "tr" "traceroute" "traceroute6"
"true" "truncate" "tty" "udhcpc" "umount" "uname"
"unexpand" "uniq" "unlink" "unlzma" "unxz" "unzip" "uptime"
"watch" "wc" "whoami" "xargs" "xxd" "xz" "xzcat" "yes" "zcat"
"arch" "ash" "base64" "basename" "bc" "brctl" "bunzip2" "bzcat" "bzip2"
"cal" "cat" "chattr" "chgrp" "chmod" "chown" "chpst" "chroot" "clear" "cmp"
"comm" "cp" "cpio" "cut" "date" "dhcprelay" "dd" "df" "dirname" "dmesg"
"du" "echo" "egrep" "env" "expand" "expr" "false" "fdisk" "fgrep" "find"
"free" "fuser" "grep" "gunzip" "gzip" "head" "hexdump" "hostname" "hwclock"
"ifconfig" "ip" "ipaddr" "iplink" "ipneigh" "iproute" "iprule" "kill"
"killall" "killall5" "less" "ln" "ls" "lsattr" "lsof" "md5sum" "mkdir"
"mknod" "mktemp" "mount" "mv" "nc" "netstat" "nohup" "od" "pgrep" "pidof"
"ping" "ping6" "pkill" "pmap" "printenv" "printf" "ps" "pwd" "readlink"
"realpath" "reset" "rm" "rmdir" "route" "sed" "seq" "setsid" "sha1sum"
"sha256sum" "sha512sum" "sleep" "sort" "stat" "strings" "stty" "su" "sum"
"swapoff" "swapon" "sync" "tail" "tee" "test" "time" "touch" "tr"
"traceroute" "traceroute6" "true" "truncate" "tty" "udhcpc" "umount"
"uname" "unexpand" "uniq" "unlink" "unlzma" "unxz" "unzip" "uptime" "watch"
"wc" "whoami" "xargs" "xxd" "xz" "xzcat" "yes" "zcat"
];
in {
options = {

View File

@ -1,31 +1,32 @@
(local { : system } (require :anoia))
(local svc (require :anoia.svc))
(fn changes [old-addresses new-addresses]
(let [added {}
deleted {}]
(each [n address (pairs new-addresses)]
(if (not (. old-addresses n))
(table.insert added address)))
(fn deletions [old-addresses new-addresses]
(let [deleted {}]
(each [n address (pairs old-addresses)]
(if (not (. new-addresses n))
(table.insert deleted address)))
(values added deleted)))
(let [now (. new-addresses n)]
(if (or (not now) (not (= now.len address.len)))
(table.insert deleted address))))
deleted))
(fn update-prefixes [device prefixes new-prefixes]
(let [(added deleted) (changes prefixes new-prefixes)]
(each [_ p (ipairs added)]
(system
(.. "ip address add " p.address "1/" p.len " dev " device)))
(each [_ p (ipairs deleted)]
(system
(.. "ip address del " p.address "1/" p.len " dev " device)))))
(fn update-prefixes [wan-device addresses new-addresses exec]
(each [_ p (ipairs (deletions addresses new-addresses))]
(exec
(.. "ip address del " p.address "1/" p.len " dev " wan-device)))
(each [_ p (pairs new-addresses)]
(exec
(.. "ip address change " p.address "1/" p.len
" dev " wan-device
" valid_lft " p.valid
" preferred_lft " p.preferred
)))
new-addresses)
(fn run []
(let [[state-directory lan-device] arg
dir (svc.open state-directory)]
(accumulate [addresses []
v (dir:events)]
(update-prefixes lan-device addresses (v:output "prefix")))))
(update-prefixes lan-device addresses (v:output "prefix") system))))
{ : changes : run }

View File

@ -5,23 +5,45 @@
(local a1
{
"2001-ab-cd-ef_hjgKHGhKJH" {
:address "2001:ab:cd:ef"
:len "64"
:preferred "200"
:valid "200"
}
"2001-ab-cd-ef" {
:address "2001:ab:cd:ef"
:len "64"
:preferred "3600"
:valid "7200"
}
}
)
(local a156
{
"2001-ab-cd-ef" {
:address "2001:ab:cd:ef"
:len "56"
:preferred "3600"
:valid "7200"
}
}
)
(local a2
{
"2001-0-1-2-3_aNteBnb" {
:address "2001:0:1:2:3"
:len "64"
:preferred "200"
:valid "200"
}
"2001-0-1-2-3" {
:address "2001:0:1:2:3"
:len "64"
:preferred "3600"
:valid "7200"
}
}
)
(local a21
{
"2001-0-1-2-3" {
:address "2001:0:1:2:3"
:len "64"
:preferred "1800"
:valid "5400"
}
}
)
@ -30,39 +52,85 @@
`(when (not ,assertion)
(assert false ,msg))))
(macro expect= [actual expected]
`(let [ve# (view ,expected)
va# (view ,actual)]
(when (not (= ve# va#))
(assert false
(.. "\nexpected " ve# "\ngot " va#)
))))
(fn first-address []
(let [(add del)
(subject.changes
(let [deleted
(subject.deletions
{ }
a1
)]
(expect (= (# del) 0))
(expect (= (# add) 1))
(let [[first] add]
(expect (= first.address "2001:ab:cd:ef")))))
(expect= deleted [])))
(fn second-address []
(let [(add del)
(subject.changes
(let [del
(subject.deletions
a1
(merge (dup a1) a2)
)]
(expect (= (# del) 0))
(expect (= (# add) 1))
(let [[first] add] (expect (= first.address "2001:0:1:2:3")))))
(expect= del [])))
(fn less-address []1
(let [(add del)
(subject.changes
(fn old-address-is-deleted []
(let [del
(subject.deletions
(merge (dup a1) a2)
a1
)]
(expect (= (# add) 0))
(expect (= (# del) 1))
(expect= (. del 1) (. a2 "2001-0-1-2-3"))
))
(let [[first] del] (expect (= first.address "2001:0:1:2:3")))))
(fn changed-lifetime-not-deleted []
(let [del
(subject.deletions
(merge (dup a1) a2)
(merge (dup a1) a21)
)]
;; when an address lifetime changes, "ip address change"
;; will update that so it need not (should not) be deleted
(expect= del [])))
(fn changed-prefix-is-deleted []
(let [del
(subject.deletions a1 a156)]
;; when an address prefix changes, "ip address change"
;; ignores that cjhange, so we have to remove the
;; address before reinstating it
(expect= del [(. a1 "2001-ab-cd-ef")])))
(first-address)
(second-address)
(less-address)
(old-address-is-deleted)
(changed-lifetime-not-deleted)
(changed-prefix-is-deleted)
(let [cmds []]
(subject.update-addresses
"ppp0" a1 (merge (dup a1) a2)
(fn [a] (table.insert cmds a)))
(expect=
(doto cmds table.sort)
[
;; order of changes is unimportant
"ip address change 2001:0:1:2:3/64 dev ppp0 valid_lft 7200 preferred_lft 3600"
"ip address change 2001:ab:cd:ef/64 dev ppp0 valid_lft 7200 preferred_lft 3600"
]))
(let [cmds []]
(subject.update-addresses
"ppp0" (merge (dup a1) a2) a1
(fn [a] (table.insert cmds a)))
(expect=
cmds
[
;; deletes are executed before changes
"ip address del 2001:0:1:2:3/64 dev ppp0"
"ip address change 2001:ab:cd:ef/64 dev ppp0 valid_lft 7200 preferred_lft 3600"
]))
(print "OK")

View File

@ -1,35 +1,32 @@
(local { : system } (require :anoia))
(local svc (require :anoia.svc))
;; acquire-delegated-prefix has very similar code: we'd like to move
;; this to anoia.svc when we see what the general form would look like
(fn changes [old-addresses new-addresses]
(let [added {}
deleted {}]
(each [n address (pairs new-addresses)]
(if (not (. old-addresses n))
(table.insert added address)))
(fn deletions [old-addresses new-addresses]
(let [deleted {}]
(each [n address (pairs old-addresses)]
(if (not (. new-addresses n))
(table.insert deleted address)))
(values added deleted)))
(let [now (. new-addresses n)]
(if (or (not now) (not (= now.len address.len)))
(table.insert deleted address))))
deleted))
(fn update-addresses [wan-device addresses new-addresses]
(let [(added deleted) (changes addresses new-addresses)]
(each [_ p (ipairs added)]
(system
(.. "ip address add " p.address "/" p.len " dev " wan-device)))
(each [_ p (ipairs deleted)]
(system
(.. "ip address del " p.address "/" p.len " dev " wan-device)))
new-addresses))
(fn update-addresses [wan-device addresses new-addresses exec]
(each [_ p (ipairs (deletions addresses new-addresses))]
(exec
(.. "ip address del " p.address "/" p.len " dev " wan-device)))
(each [_ p (pairs new-addresses)]
(exec
(.. "ip address change " p.address "/" p.len
" dev " wan-device
" valid_lft " p.valid
" preferred_lft " p.preferred
)))
new-addresses)
(fn run []
(let [[state-directory wan-device] arg
dir (svc.open state-directory)]
(accumulate [addresses []
v (dir:events)]
(update-addresses wan-device addresses (v:output "address")))))
(update-addresses wan-device addresses (v:output "address") system))))
{ : update-addresses : changes : run }
{ : update-addresses : deletions : run }

View File

@ -11,6 +11,6 @@ let
script = callPackage ./acquire-wan-address.nix { };
in longrun {
inherit name;
run = "${script} /run/service-state/${client.name} $(output ${interface} ifname)";
run = "${script} $SERVICE_OUTPUTS/${client.name} $(output ${interface} ifname)";
dependencies = [ client interface ];
}

View File

@ -13,7 +13,7 @@ in longrun {
inherit name;
notification-fd = 10;
run = ''
export SERVICE_STATE=/run/service-state/${name}
export SERVICE_STATE=$SERVICE_OUTPUTS/${name}
${odhcp6c}/bin/odhcp6c -s ${odhcp-script} -e -v -p /run/${name}.pid -P0 $(output ${interface} ifname)
)
'';

View File

@ -11,6 +11,6 @@ let
script = callPackage ./acquire-delegated-prefix.nix { };
in longrun {
inherit name;
run = "${script} /run/service-state/${client.name} $(output ${interface} ifname)";
run = "${script} $SERVICE_OUTPUTS/${client.name} $(output ${interface} ifname)";
dependencies = [ client interface ];
}

View File

@ -41,10 +41,11 @@ longrun {
--no-hosts \
--log-dhcp \
--enable-ra \
--log-debug \
--log-queries \
--log-facility=- \
--dhcp-leasefile=/run/${name}.leases \
--dhcp-leasefile=$(mkstate ${name})/leases \
--pid-file=/run/${name}.pid
'';
# --log-debug \
# --log-queries \
}

View File

@ -10,45 +10,8 @@ let
inherit (pkgs) liminix;
inherit (pkgs.liminix.services) oneshot;
kconf = isModule :
# setting isModule false is utterly untested and mostly
# unimplemented: I say this to preempt any "how on earth is this
# even supposed to work?" questions
let yes = if isModule then "m" else "y";
in {
NETFILTER = "y";
NETFILTER_ADVANCED = "y";
NETFILTER_NETLINK = yes;
NF_CONNTRACK = yes;
IP6_NF_IPTABLES= yes;
IP_NF_IPTABLES = yes;
IP_NF_NAT = yes;
IP_NF_TARGET_MASQUERADE = yes;
NFT_CT = yes;
NFT_FIB_IPV4 = yes;
NFT_FIB_IPV6 = yes;
NFT_LOG = yes;
NFT_MASQ = yes;
NFT_NAT = yes;
NFT_REJECT = yes;
NFT_REJECT_INET = yes;
NF_CT_PROTO_DCCP = "y";
NF_CT_PROTO_SCTP = "y";
NF_CT_PROTO_UDPLITE = "y";
NF_LOG_SYSLOG = yes;
NF_NAT = yes;
NF_NAT_MASQUERADE = "y";
NF_TABLES = yes;
NF_TABLES_INET = "y";
NF_TABLES_IPV4 = "y";
NF_TABLES_IPV6 = "y";
};
kmodules = pkgs.kernel-modules.override {
kernelSrc = config.system.outputs.kernel.src;
modulesupport = config.system.outputs.kernel.modulesupport;
kmodules = pkgs.kmodloader.override {
inherit (config.system.outputs) kernel;
targets = [
"nft_fib_ipv4"
"nft_fib_ipv6"
@ -82,12 +45,6 @@ let
"xt_nat"
"xt_tcpudp"
];
kconfig = kconf true;
};
loadModules = oneshot {
name = "firewall-modules";
up = "sh ${kmodules}/load.sh";
down = "sh ${kmodules}/unload.sh";
};
in
{
@ -107,11 +64,41 @@ in
in svc // {
build = args :
let args' = args // {
dependencies = (args.dependencies or []) ++ [loadModules];
dependencies = (args.dependencies or []) ++ [kmodules];
};
in svc.build args' ;
};
kernel.config = kconf true;
kernel.config = {
NETFILTER = "y";
NETFILTER_ADVANCED = "y";
NETFILTER_NETLINK = "m";
NF_CONNTRACK = "m";
IP6_NF_IPTABLES= "m";
IP_NF_IPTABLES = "m";
IP_NF_NAT = "m";
IP_NF_TARGET_MASQUERADE = "m";
NFT_CT = "m";
NFT_FIB_IPV4 = "m";
NFT_FIB_IPV6 = "m";
NFT_LOG = "m";
NFT_MASQ = "m";
NFT_NAT = "m";
NFT_REJECT = "m";
NFT_REJECT_INET = "m";
NF_CT_PROTO_DCCP = "y";
NF_CT_PROTO_SCTP = "y";
NF_CT_PROTO_UDPLITE = "y";
NF_LOG_SYSLOG = "m";
NF_NAT = "m";
NF_NAT_MASQUERADE = "y";
NF_TABLES = "m";
NF_TABLES_INET = "y";
NF_TABLES_IPV4 = "y";
NF_TABLES_IPV6 = "y";
};
};
}

View File

@ -67,6 +67,7 @@ in {
};
loadAddress = mkOption { type = types.ints.unsigned; default = null; };
entryPoint = mkOption { type = types.ints.unsigned; };
alignment = mkOption { type = types.nullOr types.ints.unsigned; default = null; description = "Alignment passed to `mkimage` for FIT"; };
radios = mkOption {
description = ''
Kernel modules (from mac80211 package) required for the

View File

@ -27,6 +27,7 @@ in {
options = {
kernel = {
src = mkOption { type = types.path; } ;
version = mkOption { type = types.str; default = "5.15.137";} ;
modular = mkOption {
type = types.bool;
default = true;
@ -79,7 +80,7 @@ in {
config.kernel.conditionalConfig;
k = liminix.builders.kernel.override {
config = mergedConfig;
inherit (config.kernel) src extraPatchPhase;
inherit (config.kernel) version src extraPatchPhase;
targets = config.kernel.makeTargets;
};
in {

View File

@ -111,7 +111,8 @@ in
};
uimage = liminix.builders.uimage {
commandLine = concatStringsSep " " config.boot.commandLine;
inherit (config.hardware) loadAddress entryPoint;
inherit (config.boot) commandLineDtbNode;
inherit (config.hardware) loadAddress entryPoint alignment;
inherit (config.boot) imageFormat;
inherit (o) kernel dtb;
};

View File

@ -7,6 +7,8 @@
let
inherit (lib) mkOption types concatStringsSep;
cfg = config.boot.tftp;
hw = config.hardware;
arch = pkgs.stdenv.hostPlatform.linuxArch;
in {
imports = [ ../ramdisk.nix ];
options.boot.tftp = {
@ -22,6 +24,10 @@ in {
type = types.bool;
default = false;
};
appendDTB = mkOption {
type = types.bool;
default = false;
};
};
options.system.outputs = {
tftpboot = mkOption {
@ -62,32 +68,46 @@ in {
uimage = "bootm";
zimage = "bootz";
}; in choices.${cfg.kernelFormat};
cmdline = concatStringsSep " " config.boot.commandLine;
objcopy = "${pkgs.stdenv.cc.bintools.targetPrefix}objcopy";
stripAndZip = ''
${objcopy} -O binary -R .reginfo -R .notes -R .note -R .comment -R .mdebug -R .note.gnu.build-id -S vmlinux.elf vmlinux.bin
rm -f vmlinux.bin.lzma ; lzma -k -z vmlinux.bin
'';
in
pkgs.runCommand "tftpboot" { nativeBuildInputs = with pkgs.pkgsBuildBuild; [ lzma dtc ]; } ''
pkgs.runCommand "tftpboot" { nativeBuildInputs = with pkgs.pkgsBuildBuild; [ lzma dtc pkgs.stdenv.cc ubootTools ]; } ''
mkdir $out
cd $out
binsize() { local s=$(stat -L -c %s $1); echo $(($s + 0x1000 &(~0xfff))); }
binsize64k() { local s=$(stat -L -c %s $1); echo $(($s + 0x10000 &(~0xffff))); }
hex() { printf "0x%x" $1; }
rootfsStart=${toString cfg.loadAddress}
rootfsSize=$(binsize64k ${o.rootfs} )
rootfsSize=$(($rootfsSize + ${toString cfg.freeSpaceBytes} ))
dtbStart=$(($rootfsStart + $rootfsSize))
imageSize=$(binsize ${image})
ln -s ${o.manifest} manifest
ln -s ${image} image
ln -s ${o.kernel} vmlinux # handy for gdb
# if we are transferring kernel and dtb separately, the
# dtb has to precede the kernel in ram, because zimage
# decompression code will assume that any memory after the
# end of the kernel is free
dtbStart=$(($rootfsStart + $rootfsSize))
${if cfg.compressRoot
then ''
lzma -z9cv ${o.rootfs} > rootfs.lz
rootfsLzStart=$(($imageStart + $imageSize))
rootfsLzStart=$dtbStart
rootfsLzSize=$(binsize rootfs.lz)
dtbStart=$(($dtbStart + $rootfsLzSize))
''
else ''
ln -s ${o.rootfs} rootfs
''
else "ln -s ${o.rootfs} rootfs"
}
cat ${o.dtb} > dtb
address_cells=$(fdtget dtb / '#address-cells')
size_cells=$(fdtget dtb / '#size-cells')
@ -101,26 +121,40 @@ in {
fdtput -p -t s dtb /reserved-memory/$node compatible phram
fdtput -p -t lx dtb /reserved-memory/$node reg $ac_prefix $(hex $rootfsStart) $sz_prefix $(hex $rootfsSize)
dtbSize=$(binsize ./dtb )
imageStart=$(($dtbStart + $dtbSize))
cmd="liminix ${cmdline} mtdparts=phram0:''${rootfsSize}(rootfs) phram.phram=phram0,''${rootfsStart},''${rootfsSize},${toString config.hardware.flash.eraseBlockSize} root=/dev/mtdblock0";
fdtput -t s dtb /chosen bootargs "$cmd"
# dtc -I dtb -O dts -o /dev/stdout dtb | grep -A10 chosen ; exit 1
dtbSize=$(binsize ./dtb )
${if cfg.appendDTB then ''
imageStart=$dtbStart
# re-package image with updated dtb
cat ${o.kernel} > vmlinux.elf
${objcopy} --update-section .appended_dtb=dtb vmlinux.elf
${stripAndZip}
mkimage -A ${arch} -O linux -T kernel -C lzma -a $(hex ${toString hw.loadAddress}) -e $(hex ${toString hw.entryPoint}) -n '${lib.toUpper arch} Liminix Linux tftpboot' -d vmlinux.bin.lzma image
# dtc -I dtb -O dts -o /dev/stdout dtb | grep -A10 chosen ; exit 1
tftpcmd="tftpboot $(hex $imageStart) result/image "
bootcmd="bootm $(hex $imageStart)"
'' else ''
imageStart=$(($dtbStart + $dtbSize))
tftpcmd="tftpboot $(hex $imageStart) result/image; tftpboot $(hex $dtbStart) result/dtb "
ln -s ${image} image
bootcmd="${bootCommand} $(hex $imageStart) - $(hex $dtbStart)"
''}
cat > boot.scr << EOF
setenv serverip ${cfg.serverip}
setenv ipaddr ${cfg.ipaddr}
tftpboot $(hex $imageStart) result/image ; ${
${
if cfg.compressRoot
then "tftpboot $(hex $rootfsLzStart) result/rootfs.lz"
else "tftpboot $(hex $rootfsStart) result/rootfs"
}; tftpboot $(hex $dtbStart) result/dtb
}; $tftpcmd
${if cfg.compressRoot
then "lzmadec $(hex $rootfsLzStart) $(hex $rootfsStart); "
else ""
} ${bootCommand} $(hex $imageStart) - $(hex $dtbStart)
} $bootcmd
EOF
'';

View File

@ -12,9 +12,16 @@ in
imports = [
./initramfs.nix
];
options.system.outputs.rootubifs = mkOption {
type = types.package;
internal = true;
};
options.hardware.ubi = {
minIOSize = mkOption { type = types.str; };
eraseBlockSize = mkOption { type = types.str; }; # LEB
logicalEraseBlockSize = mkOption { type = types.str; }; # LEB
physicalEraseBlockSize = mkOption { type = types.str; }; # PEB
maxLEBcount = mkOption { type = types.str; }; # LEB
};
@ -26,7 +33,7 @@ in
};
boot.initramfs.enable = true;
system.outputs = {
rootfs =
rootubifs =
let
inherit (pkgs.pkgsBuildBuild) runCommand mtdutils;
cfg = config.hardware.ubi;
@ -35,7 +42,7 @@ in
} ''
mkdir tmp
tree=${o.bootablerootdir}
mkfs.ubifs -x favor_lzo -c ${cfg.maxLEBcount} -m ${cfg.minIOSize} -e ${cfg.eraseBlockSize} -y -r $tree --output $out --squash-uids -o $out
mkfs.ubifs -x favor_lzo -c ${cfg.maxLEBcount} -m ${cfg.minIOSize} -e ${cfg.logicalEraseBlockSize} -y -r $tree --output $out --squash-uids -o $out
'';
};
};

View File

@ -0,0 +1,91 @@
{
config
, pkgs
, lib
, ...
}:
let
inherit (pkgs) liminix;
inherit (lib) mkIf mkOption types concatStringsSep optionalString;
in
{
imports = [
./initramfs.nix
./ubifs.nix
];
options.hardware.ubi = {
minIOSize = mkOption { type = types.str; };
eraseBlockSize = mkOption { type = types.str; }; # LEB
maxLEBcount = mkOption { type = types.str; }; # LEB
};
config = mkIf (config.rootfsType == "ubifs") {
kernel.config = {
MTD_UBI="y";
UBIFS_FS = "y";
UBIFS_FS_SECURITY = "n";
};
boot.initramfs.enable = true;
system.outputs.rootfs =
let
inherit (pkgs.pkgsBuildBuild) runCommand;
ubiVolume = ({ name, volumeId, image, flags ? [] }:
''
[${name}]
mode=ubi
vol_id=${toString volumeId}
vol_type=dynamic
vol_name=${name}
vol_alignment=1
${optionalString (image != null) ''
image=${image}
''}
${optionalString (image == null) ''
vol_size=1MiB
''}
${optionalString (flags != []) ''
vol_flags=${concatStringsSep "," flags}
''}
'');
ubiImage = (volumes:
let
ubinizeConfig = pkgs.writeText "ubinize.conf" (concatStringsSep "\n" volumes);
inherit (pkgs.pkgsBuildBuild) mtdutils;
in
runCommand "ubinize" {
depsBuildBuild = [ mtdutils ];
# block size := 128kb
# page size := 2048
# ubninize opts := -E 5
} ''
ubinize -Q "$SOURCE_DATE_EPOCH" -o $out \
-p ${config.hardware.ubi.physicalEraseBlockSize} -m ${config.hardware.ubi.minIOSize} \
-e ${config.hardware.ubi.logicalEraseBlockSize} \
${ubinizeConfig}
'');
ubiDisk = ({ initramfs }:
let
initramfsUbi = ubiVolume {
name = "rootfs";
volumeId = 0;
image = initramfs;
flags = [ "autoresize" ];
};
in
ubiImage [
initramfsUbi
]);
disk = ubiDisk {
initramfs = config.system.outputs.rootubifs; # liminix.builders.squashfs config.filesystem.contents; # # assert this is a proper FIT.
};
in
disk;
};
}

View File

@ -0,0 +1,71 @@
{
config
, pkgs
, lib
, ...
}:
let
inherit (lib) mkIf mkEnableOption mkOption types concatStringsSep;
models = "6b e1 6f e1 ff ff ff ff ff ff";
in {
options.system.outputs = {
zyxel-nwa-fit = mkOption {
type = types.package;
description = ''
zyxel-nwa-fit
*************
This output provides a FIT image for Zyxel NWA series
containing a kernel image and an UBIFS rootfs.
It can usually be used as a factory image to install Liminix
on a system with pre-existing firmware and OS.
'';
};
};
imports = [
./ubivolume.nix
];
config = mkIf (config.rootfsType == "ubifs") {
system.outputs.zyxel-nwa-fit =
let
o = config.system.outputs;
# 8129kb padding.
paddedKernel = pkgs.runCommand "padded-kernel" {} ''
cp --no-preserve=mode ${o.uimage} $out
dd if=/dev/zero of=$out bs=1 count=1 seek=8388607
'';
firmwareImage = pkgs.runCommand "firmware-image" {} ''
cat ${paddedKernel} ${o.rootfs} > $out
'';
dts = pkgs.writeText "image.its" ''
/dts-v1/;
/ {
description = "Zyxel FIT (Flattened Image Tree)";
compat-models = [${models}];
#address-cells = <1>;
images {
firmware {
data = /incbin/("${firmwareImage}");
type = "firmware";
compression = "none";
hash@1 {
algo = "sha1";
};
};
};
};
'';
in
pkgs.runCommand "zyxel-nwa-fit-${config.boot.imageType}" {
nativeBuildInputs = [ pkgs.pkgsBuildBuild.ubootTools pkgs.pkgsBuildBuild.dtc ];
} ''
mkimage -f ${dts} $out
'';
};
}

100
modules/profiles/wap.nix Normal file
View File

@ -0,0 +1,100 @@
{
config,
pkgs,
lib,
...
}: let
inherit (pkgs) liminix;
inherit (lib) mkEnableOption mkOption types isDerivation hasAttr ;
inherit (pkgs.liminix.services) oneshot longrun bundle target;
inherit (pkgs.pseudofile) dir symlink;
inherit (pkgs) serviceFns;
svc = config.system.service;
cfg = config.profile.wap;
hostaps =
let
defaults = {
auth_algs = 1; # 1=wpa2, 2=wep, 3=both
wpa = 2; # 1=wpa, 2=wpa2, 3=both
wpa_key_mgmt = "WPA-PSK";
wpa_pairwise = "TKIP CCMP"; # auth for wpa (may not need this?)
rsn_pairwise = "CCMP"; # auth for wpa2
};
in lib.mapAttrs'
(name : value :
let
attrs = defaults // { ssid = name; } // value;
in lib.nameValuePair
"hostap-${name}"
(svc.hostapd.build {
interface = attrs.interface;
params = lib.filterAttrs (k: v: k != "interface") attrs;
}))
cfg.wireless.networks;
in {
imports = [
../wlan.nix
../network
../hostapd
../bridge
../ssh
{ config.services = hostaps; }
];
options.profile.wap = {
interfaces = mkOption {
type = types.listOf liminix.lib.types.interface;
default = [];
};
wireless = mkOption {
type = types.attrsOf types.anything;
};
};
config = {
services.sshd = svc.ssh.build {};
services.int = svc.bridge.primary.build {
ifname = "int";
};
services.bridge = svc.bridge.members.build {
primary = config.services.int;
members = cfg.interfaces;
};
services.dhcpc = svc.network.dhcp.client.build {
interface = config.services.int;
dependencies = [ config.services.hostname ];
};
services.defaultroute4 = svc.network.route.build {
via = "$(output ${config.services.dhcpc} router)";
target = "default";
dependencies = [config.services.dhcpc];
};
services.resolvconf = oneshot rec {
dependencies = [ config.services.dhcpc ];
name = "resolvconf";
# CHECK: https://udhcp.busybox.net/README.udhcpc says
# 'A list of DNS server' but doesn't say what separates the
# list members. Assuming it's a space or other IFS character
up = ''
. ${serviceFns}
( in_outputs ${name}
for i in $(output ${config.services.dhcpc} dns); do
echo "nameserver $i" > resolv.conf
done
)
'';
};
filesystem = dir {
etc = dir {
"resolv.conf" = symlink "${config.services.resolvconf}/.outputs/resolv.conf";
};
};
};
}

View File

@ -22,8 +22,15 @@ mount -t tmpfs none /tmp
mkdir /dev/pts
mount -t devpts none /dev/pts
mkdir -m 0751 /run/service-state
chgrp system /run/service-state
mkdir -m 0751 -p /run/services/outputs
chgrp system /run/services/outputs
if test -d /persist; then
mkdir -m 0751 -p /persist/services/state
(cd /run/services && ln -s ../../persist/services/state .)
else
mkdir -m 0751 -p /run/services/state
fi
### If your services are managed by s6-rc:
### (replace /run/service with your scandir)

View File

@ -29,15 +29,12 @@ let
in
longrun {
name = "sshd";
# we need /run/dropbear to point to hostkey storage, as that
# pathname is hardcoded into the binary.
# env -i clears the environment so we don't pass anything weird to
# ssh sessions
run = ''
if test -d /persist; then
mkdir -p /persist/secrets/dropbear
ln -s /persist/secrets/dropbear /run
else
mkdir -p /run/dropbear
fi
ln -s $(mkstate dropbear) /run
. /etc/profile # sets PATH but do we need this? it's the same file as ashrc
exec env -i ENV=/etc/ashrc PATH=$PATH ${dropbear}/bin/dropbear ${concatStringsSep " " options}
'';

View File

@ -46,6 +46,14 @@ in {
CRYPTO_SHA1 = "y";
ENCRYPTED_KEYS = "y";
KEYS = "y";
WLAN = "y";
CFG80211 = "m";
MAC80211 = "m";
EXPERT = "y";
CFG80211_CERTIFICATION_ONUS = "y";
CFG80211_REQUIRE_SIGNED_REGDB = "n"; # depends on ONUS
CFG80211_CRDA_SUPPORT = "n";
};
};
};

View File

@ -0,0 +1,60 @@
## Boot blessing via Zyxel
## =======================
## Boot blessing is the process to bless a particular boot configuration
## It is commonly encountered in devices with redundant partitions
## for automatic recovery of broken upgrades.
## This is also known as A/B schemas, where A represents the primary partition
## and B the secondary partition used for recovery.
## To use boot blessing on Liminix, you need to have the support of
## your bootloader to help you boot on the secondary partition in case of
## failure on the primary partition. The exact details are specifics to your device.
## See the Zyxel NWA50AX for an example.
## TODO: generalize this module.
{ config, lib, pkgs, ... }:
let
inherit (lib) mkOption types;
inherit (pkgs) liminix;
in
{
options.boot.zyxel-dual-image = mkOption {
type = liminix.lib.types.serviceDefn;
};
config.boot.zyxel-dual-image = liminix.callService ./service.nix {
ensureActiveImage = mkOption {
type = types.enum [ "primary" "secondary" ];
default = "primary";
description = ''At boot, ensure that the active image is the one specified.
If you are already on a broken image, you need to manually boot
into the right image via `atgo <image index>` in U-Boot.
'';
};
kernelCommandLineSource = mkOption {
type = types.enum [ "/proc/cmdline" "/proc/device-tree/chosen/bootargs" ];
default = "/proc/device-tree/chosen/bootargs";
description = ''Kernel command line arguments source file.
On MIPS, Liminix embeds the kernel command line in /proc/device-tree/chosen/bootargs-override.
In this instance, it does not get concatenated with `/proc/cmdline`.
Therefore you may prefer to source it from another place, like `/proc/device-tree/chosen/bootargs`.
'';
};
primaryMtdPartition = mkOption {
type = types.str;
description = "Primary MTD partition device node, i.e. for image 0.";
};
secondaryMtdPartition = mkOption {
type = types.str;
description = "Secondary MTD partition device node, i.e. for image 1.";
};
bootConfigurationMtdPartition = mkOption {
type = types.str;
description = "Boot configuration MTD partition device node.";
};
};
}

View File

@ -0,0 +1,33 @@
{
liminix
, lib
, zyxel-bootconfig
}:
{ ensureActiveImage, primaryMtdPartition, secondaryMtdPartition, bootConfigurationMtdPartition, kernelCommandLineSource }:
let
inherit (liminix.services) oneshot;
activeImageIndex = if ensureActiveImage == "primary" then 0 else 1;
in oneshot {
name = "zyxel-boot-configure";
up = ''
set -- $(cat /proc/device-tree/chosen/bootargs)
for x in "$@"; do
case "$x" in
bootImage=*)
BOOT_IMAGE="''${x#bootImage=}"
echo "Current boot image is $BOOT_IMAGE."
;;
esac
done
if test -z "$BOOT_IMAGE"; then
echo "No valid image was provided in the kernel command line."
exit 1
else
${lib.getExe zyxel-bootconfig} ${bootConfigurationMtdPartition} set-image-status "$BOOT_IMAGE" valid
${lib.getExe zyxel-bootconfig} ${bootConfigurationMtdPartition} set-active-image ${toString activeImageIndex}
echo "Active image is now ${ensureActiveImage}"
fi
'';
}

View File

@ -104,6 +104,7 @@ extraPkgs // {
"CONFIG_DRIVER_NL80211=y"
"CONFIG_IAPP=y"
"CONFIG_IEEE80211AC=y"
"CONFIG_IEEE80211AX=y"
"CONFIG_IEEE80211N=y"
"CONFIG_IEEE80211W=y"
"CONFIG_INTERNAL_LIBTOMMATH=y"

View File

@ -1,3 +1,7 @@
(fn assoc [tbl k v]
(tset tbl k v)
tbl)
(fn merge [table1 table2]
(collect [k v (pairs table2) &into table1]
k v))
@ -16,7 +20,7 @@
(fn system [s]
(match (os.execute s)
res res
res (do (print (.. "Executed \"" s "\", exit code " (tostring res))) res)
(nil err) (error (.. "Error executing \"" s "\" (" err ")"))))
(fn hash [str]
@ -62,4 +66,4 @@
(s:sub 1 (- (# s) pad))))
{ : merge : split : file-exists? : system : hash : base64url : dup }
{ : assoc : merge : split : file-exists? : system : hash : base64url : dup }

15
pkgs/anoia/nl.fnl Normal file
View File

@ -0,0 +1,15 @@
(local netlink (require :netlink))
(local { : view } (require :fennel))
(fn events [groups]
(let [sock (netlink.socket)]
(coroutine.wrap
(fn []
(each [_ e (ipairs (sock:query groups))]
(coroutine.yield e))
(while (sock:poll)
(each [_ e (ipairs (sock:event))]
(coroutine.yield e)))))))
{ : events }

7
pkgs/anoia/test-nl.fnl Normal file
View File

@ -0,0 +1,7 @@
(local nl (require :anoia.nl))
(local { : view } (require :fennel))
(let [events (nl.events {:link true})]
(each [ev events]
(print "got one ")
(print (view ev))))

View File

@ -66,7 +66,7 @@ in {
ifwait = callPackage ./ifwait {};
initramfs-peek = callPackage ./initramfs-peek {};
kernel-backport = callPackage ./kernel-backport {};
kernel-modules = callPackage ./kernel-modules {};
kmodloader = callPackage ./kmodloader {};
levitate = callPackage ./levitate {};
libubootenv = callPackage ./libubootenv {};
linotify = callPackage ./linotify {};
@ -79,6 +79,7 @@ in {
lzma = callPackage ./lzma {};
mac80211 = callPackage ./mac80211 {};
zyxel-bootconfig = callPackage ./zyxel-bootconfig {};
min-collect-garbage = callPackage ./min-collect-garbage {};
min-copy-closure = callPackage ./min-copy-closure {};
netlink-lua = callPackage ./netlink-lua {};

View File

@ -8,12 +8,14 @@
, writeScriptBin
, linotify
, anoia
, netlink-lua
, fennel
}:
let packages = [
linotify
anoia
fennel
netlink-lua
lua.pkgs.luafilesystem
];
join = ps: builtins.concatStringsSep ";" ps;

195
pkgs/ifwait/events-fixture Normal file
View File

@ -0,0 +1,195 @@
{:event "newlink"
:hwaddr "00:00:00:00:00:00"
:index 1
:mtu 65536
:name "lo"
:running "yes"
:stamp 857161382
:up "yes"}
{:event "newlink"
:hwaddr "50:3e:aa:08:df:52"
:index 2
:mtu 1500
:name "enp1s0"
:running "no"
:stamp 857161382
:up "yes"}
{:event "newlink"
:hwaddr "1c:1b:0d:9c:39:2d"
:index 3
:mtu 1500
:name "enp0s31f6"
:running "yes"
:stamp 857161382
:up "yes"}
{:event "newlink"
:hwaddr "da:4d:53:c3:54:43"
:index 4
:mtu 1500
:name "vbridge0"
:running "yes"
:stamp 857161382
:up "yes"}
{:event "newlink"
:hwaddr "00:28:f8:69:fa:14"
:index 6
:mtu 1500
:name "wlp4s0"
:running "no"
:stamp 857161382
:up "no"}
{:event "newlink"
:hwaddr "02:42:b1:e6:e5:bd"
:index 7
:mtu 1500
:name "br-7ddfef4820c5"
:running "no"
:stamp 857161382
:up "yes"}
{:event "newlink"
:hwaddr "02:42:8d:d4:36:34"
:index 8
:mtu 1500
:name "br-95da8b40a7cc"
:running "yes"
:stamp 857161382
:up "yes"}
{:event "newlink"
:hwaddr "02:42:bc:cf:a8:5e"
:index 9
:mtu 1500
:name "docker0"
:running "no"
:stamp 857161382
:up "yes"}
{:event "newlink"
:hwaddr "b6:66:50:69:33:a6"
:index 11
:mtu 1500
:name "veth2ff6ec3"
:running "yes"
:stamp 857161382
:up "yes"}
{:event "newlink"
:hwaddr "e6:94:c8:48:f3:97"
:index 13
:mtu 1500
:name "veth0913974"
:running "yes"
:stamp 857161382
:up "yes"}
{:event "newlink"
:hwaddr "9a:87:d8:f2:c6:96"
:index 15
:mtu 1500
:name "veth0e74156"
:running "yes"
:stamp 857161382
:up "yes"}
{:event "newlink"
:hwaddr "5e:d2:92:b9:5f:6d"
:index 17
:mtu 1500
:name "veth89a36b3"
:running "yes"
:stamp 857161382
:up "yes"}
{:event "newlink"
:hwaddr "ca:88:3f:09:bc:51"
:index 19
:mtu 1500
:name "veth73c1e0b"
:running "yes"
:stamp 857161382
:up "yes"}
{:event "newlink"
:hwaddr "b6:7d:5c:38:89:1d"
:index 21
:mtu 1500
:name "dummy0"
:running "no"
:stamp 857161382
:up "no"}
{:event "newlink"
:hwaddr "52:f0:46:da:0c:0c"
:index 22
:mtu 1500
:name "dummy1"
:running "yes"
:stamp 857161382
:up "yes"}
{:event "newneigh"
:hwaddr "00:22:61:3d:f7:54"
:index 4
:ip "192.168.8.140"
:probes 1
:stamp 857165355
:state "stale"}
{:event "delneigh"
:hwaddr "5c:60:ba:58:34:93"
:index 3
:stamp 857166891
:state "stale"}
{:event "newneigh"
:hwaddr "80:64:6f:9e:15:02"
:index 4
:ip "192.168.8.161"
:probes 1
:stamp 857172523
:state "stale"}
{:event "newneigh"
:hwaddr "e4:95:6e:42:c2:6c"
:index 3
:stamp 857174763
:state "reachable"}
{:event "newneigh"
:hwaddr "e4:b3:18:76:1b:23"
:index 4
:ip "2001:8b0:de3a:40de:4708:c700:4de2:9264"
:probes 1
:stamp 857175595
:state "stale"}
{:event "newneigh"
:hwaddr "80:64:6f:9e:10:c6"
:index 4
:ip "192.168.8.53"
:probes 1
:stamp 857176619
:state "stale"}
{:event "newneigh"
:hwaddr "80:64:6f:9e:15:02"
:index 4
:ip "192.168.8.161"
:probes 1
:stamp 857177643
:state "probe"}
{:event "newneigh"
:hwaddr "80:64:6f:9e:15:02"
:index 4
:ip "192.168.8.161"
:probes 1
:stamp 857177644
:state "reachable"}
{:event "newlink"
:hwaddr "b6:7d:5c:38:89:1d"
:index 21
:mtu 1500
:name "dummy0"
:running "yes"
:stamp 857178258
:up "yes"}
{:event "newlink"
:hwaddr "b6:7d:5c:38:89:1d"
:index 21
:mtu 1500
:name "dummy0"
:running "no"
:stamp 857181661
:up "no"}
{:event "newneigh"
:hwaddr "80:64:6f:9e:10:c6"
:index 4
:ip "192.168.8.53"
:probes 1
:stamp 857182251
:state "probe"}

View File

@ -1,52 +1,64 @@
(local netlink (require :netlink))
(local sock (netlink.socket))
(local nl (require :anoia.nl))
(local { : assoc : system } (require :anoia))
; (local { : view} (require :fennel))
(fn assoc [tbl k v]
(tset tbl k v)
tbl)
(fn parse-args [args]
(match args
["-v" & rest] (assoc (parse-args rest) :verbose true)
["-t" timeout & rest] (assoc (parse-args rest) :timeout (tonumber timeout))
["-s" service & rest] (assoc (parse-args rest) :service service)
[linkname "up"] {:link linkname :expecting "up"}
[linkname "running"] {:link linkname :expecting "running"}
[linkname "present"] {:link linkname :expecting "present"}
[linkname nil] {:link linkname :expecting "present"}
_ nil))
(local parameters
(or
(parse-args arg)
(assert false (.. "Usage: " (. arg 0) " [-v] ifname [present|up|running]"))))
(fn event-matches? [params v]
(let [got
(match v
;; - up: Reflects the administrative state of the interface (IFF_UP)
;; - running: Reflects the operational state (IFF_RUNNING).
{:event "newlink" :name params.link :up :yes :running :yes}
{:present true :up true :running true}
(fn run-events [evs]
(each [_ v (ipairs evs)]
(let [got
(match v
;; - up: Reflects the administrative state of the interface (IFF_UP)
;; - running: Reflects the operational state (IFF_RUNNING).
{:event "newlink" :name parameters.link :up :yes :running :yes}
{:present true :up true :running true}
{:event "newlink" :name params.link :up :yes}
{:present :true :up true}
{:event "newlink" :name parameters.link :up :yes}
{:present :true :up true}
{:event "newlink" :name params.link}
{:present true }
{:event "newlink" :name parameters.link}
{:present true }
_
{})]
(not (not (. got params.expecting)))))
_
{})]
(when (. got parameters.expecting)
(os.exit 0)))))
(var up :unknown)
(fn toggle-service [service wanted?]
(when (not (= up wanted?))
(set up
(if wanted?
(pcall system (.. "s6-rc -u change " service))
(not (pcall system (.. "s6-rc -d change " service)))))
))
(fn run [args event-fn]
(set up :unknown)
(let [parameters
(assert (parse-args args)
(.. "Usage: ifwait [-v] ifname [present|up|running]"))]
(when parameters.verbose
(print (.. "ifwait: waiting for "
parameters.link " to be " parameters.expecting)))
(when parameters.verbose
(print (.. (. arg 0) ": waiting for "
parameters.link " to be " parameters.expecting)))
(if parameters.service
(each [e (event-fn)]
(if (= e.name parameters.link)
(toggle-service parameters.service (event-matches? parameters e))))
(each [e (event-fn)
&until (event-matches? parameters e)]
true))))
(run-events (sock:query {:link true}))
(when (not (= (. arg 0) "test"))
(run arg #(nl.events {:link true})))
(while (sock:poll) (run-events (sock:event)))
{ : run }

119
pkgs/ifwait/test-ifwait.fnl Normal file
View File

@ -0,0 +1,119 @@
(local { : view &as fennel } (require :fennel))
(local anoia (require :anoia))
(var fake-system (fn [s] (print "executing " s)))
(tset anoia :system #(fake-system $1))
(macro expect= [actual expected]
`(let [ve# (view ,expected)
va# (view ,actual)]
(when (not (= ve# va#))
(assert false
(.. "\nexpected " ve# "\ngot " va#)
))))
(fn event-generator [events]
(coroutine.wrap
(fn []
(each [_ e (ipairs events)] (coroutine.yield e)))))
(fn file-events [path]
(let [data (with-open [e (io.open path "r")] (e:read "*a"))
parse (fennel.parser data)]
(icollect [_ ast parse]
ast)))
(set _G.arg (doto [] (tset 0 "test")))
(local ifwait (require :ifwait))
(let [gen (event-generator (file-events "events-fixture"))]
(ifwait.run ["dummy0" "up"] #gen)
(match (pcall gen)
(true _) true
(false msg) (error "didn't detect dummy0 up event")))
(var upsies [])
(set fake-system
(fn [s]
(if (s:match "-u change addmember")
(table.insert upsies :u)
(s:match "-d change addmember")
(table.insert upsies :d))))
(fn newlink [name up running]
{:event "newlink"
:hwaddr "b6:7d:5c:38:89:1d"
:index (string.unpack ">i2" name)
:mtu 1500
: name
: running
:stamp 857161382
: up })
"when it gets events that don't match the interface, nothing happens"
(let [gen (-> [(newlink "eth1" "no" "no")] event-generator)]
(set upsies [])
(ifwait.run [ "-s" "addmember" "dummy0" "up"] #gen)
(expect= upsies []))
"when it gets an event that should start the service, the service starts"
(let [gen (->
[(newlink "dummy0" "no" "no")
(newlink "dummy0" "yes" "no")
(newlink "eth1" "no" "no")]
event-generator)]
(set upsies [])
(ifwait.run ["-s" "addmember" "dummy0" "up"] #gen)
(expect= upsies [:d :u]))
"when it gets an event that should stop the service, the service stops"
(let [gen (->
[(newlink "dummy0" "no" "no")
(newlink "dummy0" "yes" "no")
(newlink "dummy0" "no" "no")
]
event-generator)]
(set upsies [])
(ifwait.run ["-s" "addmember" "dummy0" "up"] #gen)
(expect= upsies [:d :u :d]))
"it does not call s6-rc again if the service is already in required state"
(let [gen (->
[(newlink "dummy0" "no" "no")
(newlink "dummy0" "yes" "no")
(newlink "dummy0" "yes" "yes")
(newlink "dummy0" "yes" "yes")
(newlink "dummy0" "yes" "no")
(newlink "dummy0" "no" "no")
]
event-generator)]
(set upsies [])
(ifwait.run ["-s" "addmember" "dummy0" "up"] #gen)
(expect= upsies [:d :u :d]))
"it handles an error return from s6-rc"
(set fake-system
(fn [s]
(if (s:match "-u change addmember")
(table.insert upsies :u)
(s:match "-d change addmember")
(table.insert upsies :d))
(error "false")
))
(let [gen (->
[(newlink "dummy0" "yes" "no")
(newlink "dummy0" "yes" "yes")
(newlink "dummy0" "yes" "yes")
(newlink "dummy0" "yes" "no")
(newlink "dummy0" "no" "no")
]
event-generator)]
(set upsies [])
(ifwait.run ["-s" "addmember" "dummy0" "up"] #gen)
(expect= upsies [:u :u :u :u]))

View File

@ -1,3 +0,0 @@
# obj-m += net/ipv4/netfilter/nft_fib_ipv4.o

View File

@ -1,50 +0,0 @@
{
stdenv
, buildPackages
, kernelSrc ? null
, modulesupport ? null
, targets ? []
, kconfig ? {}
, openssl
, writeText
, lib
}:
let
writeConfig = import ../kernel/write-kconfig.nix { inherit lib writeText; };
arch = stdenv.hostPlatform.linuxArch;
in stdenv.mkDerivation {
name = "kernel-modules";
nativeBuildInputs = [buildPackages.stdenv.cc] ++
(with buildPackages.pkgs; [
bc bison flex
openssl
cpio
kmod
]);
CC = "${stdenv.cc.bintools.targetPrefix}gcc";
HOST_EXTRACFLAGS = with buildPackages.pkgs;
"-I${buildPackages.openssl.dev}/include -L${buildPackages.openssl.out}/lib";
CROSS_COMPILE = stdenv.cc.bintools.targetPrefix;
ARCH = arch;
KBUILD_BUILD_HOST = "liminix.builder";
buildPhase = ''
cat ${writeConfig "kconfig" kconfig} > .more-config
cat .more-config >> .config
make olddefconfig
for v in $(cat .more-config) ; do grep $v .config || (echo Missing $v && exit 1);done
make modules
'';
src = modulesupport;
installPhase = ''
mkdir -p $out/lib/modules/0.0
find . -name \*.ko | cpio --verbose --make-directories -p $out/lib/modules/0.0
depmod -b $out -v 0.0
touch $out/load.sh
for i in ${lib.concatStringsSep " " targets}; do
modprobe -S 0.0 -d $out --show-depends $i >> $out/load.sh
done
tac < $out/load.sh | sed 's/^insmod/rmmod/g' > $out/unload.sh
'';
}

View File

@ -6,6 +6,7 @@
, config
, src
, version ? "0"
, extraPatchPhase ? "echo"
, targets ? ["vmlinux"]
} :
@ -51,9 +52,9 @@ stdenv.mkDerivation rec {
patches = [
./cmdline-cookie.patch
./phram-allow-cached-mappings.patch
./mips-malta-fdt-from-bootloader.patch
];
] ++ lib.optional (lib.versionOlder version "5.18.0")
./phram-allow-cached-mappings.patch;
# this is here to work around what I think is a bug in nixpkgs
# packaging of ncurses: it installs pkg-config data files which
@ -103,8 +104,7 @@ stdenv.mkDerivation rec {
mkdir -p $headers
cp -a include .config $headers/
mkdir -p $modulesupport
cp modules.* $modulesupport
make clean modules_prepare
make modules
cp -a . $modulesupport
'';
}

View File

@ -15,10 +15,12 @@ let
in {
kernel
, commandLine
, commandLineDtbNode ? "bootargs"
, entryPoint
, extraName ? "" # e.g. socFamily
, loadAddress
, imageFormat
, alignment ? null
, dtb ? null
} : stdenv.mkDerivation {
name = "kernel.image";
@ -39,7 +41,7 @@ in {
'';
mungeDtbPhase = ''
dtc -I dtb -O dts -o tmp.dts ${dtb}
echo '/{ chosen { bootargs = ${builtins.toJSON commandLine}; }; };' >> tmp.dts
echo '/{ chosen { ${commandLineDtbNode} = ${builtins.toJSON commandLine}; }; };' >> tmp.dts
dtc -I dts -O dtb -o tmp.dtb tmp.dts
'';
@ -69,7 +71,7 @@ in {
};
};
_VARS
mkimage -f mkimage.its kernel.uimage
mkimage -f mkimage.its ${lib.optionalString (alignment != null) "-B 0x${lib.toHexString alignment}"} kernel.uimage
mkimage -l kernel.uimage
'';

View File

@ -0,0 +1,43 @@
{
liminix
, lib
, targets ? []
, kernel ? null
, runCommand
, pkgsBuildBuild
, dependencies ? []
} :
let
inherit (liminix.services) oneshot;
inherit (lib) concatStringsSep;
loader = runCommand "modules" {
nativeBuildInputs = with pkgsBuildBuild ;[
kmod cpio gawk
];
} ''
kernel=${kernel.modulesupport}
mkdir -p lib/modules/0.0
(cd $kernel && find . -name \*.ko | cpio --verbose --make-directories -p $NIX_BUILD_TOP/lib/modules/0.0)
cp $kernel/modules.* lib/modules/0.0
depmod -b . 0.0
(for i in ${lib.concatStringsSep " " targets}; do
modprobe -S 0.0 -d $NIX_BUILD_TOP --show-depends $i | sed "s,^insmod $NIX_BUILD_TOP/lib/modules/0.0/,,g"
done) | awk '!a[$0]++' > load-order
mkdir $out
for i in $(cat load-order); do
install -v $NIX_BUILD_TOP/lib/modules/0.0/$i -D $out/$i
done
echo "O=$out" > $out/load.sh
sed "s,^,insmod \$O/,g" < load-order >> $out/load.sh
echo "O=$out" > $out/unload.sh
tac load-order | sed "s,^,rmmod \$O/,g" > $out/unload.sh
'';
in oneshot {
name = "kmodloader-" + (concatStringsSep "-" targets);
up = "sh ${loader}/load.sh";
down = "sh ${loader}/unload.sh";
inherit dependencies;
}

View File

@ -15,6 +15,6 @@ for i in run notification-fd up down consumer-for producer-for pipeline-name ; d
test -n "$(printenv $i)" && (echo "$(printenv $i)" > $out/${name}/$i)
done
( cd $out && ln -s /run/service-state/${name} ./.outputs )
( cd $out && ln -s /run/services/outputs/${name} ./.outputs )
for i in $out/${name}/{down,up,run} ; do test -f $i && chmod +x $i; done
true

View File

@ -9,7 +9,7 @@
}:
let
inherit (builtins) concatStringsSep;
prefix = "/run/service-state";
prefix = "/run/services/outputs";
output = service: name: "${prefix}/${service.name}/${name}";
serviceScript = commands : ''
#!/bin/sh

View File

@ -20,7 +20,7 @@ if toplevel=$(nix-build "$@" -A outputs.systemConfiguration --no-out-link); then
echo systemConfiguration $toplevel
min-copy-closure $target_host $toplevel
$ssh_command $target_host $toplevel/bin/install
$ssh_command $target_host "sync; source /etc/profile; reboot"
$ssh_command $target_host "sync; source /etc/profile; reboot -f"
else
echo Rebuild failed
fi

View File

@ -20,23 +20,21 @@
out))
(let [(address len preferred valid extra)
(string.match str "(.-)/(%d+),(%d+),(%d+)(.*)$")]
(merge {: address : len : preferred : valid} (parse-extra extra))))
(merge {: address : len
:preferred (or preferred "forever")
:valid (or valid "forever")
}
(parse-extra extra))))
(fn write-addresses [prefix addresses]
(each [_ a (ipairs (split " " addresses))]
(let [address (parse-address a)
suffix (base64url (string.pack "n" (hash a)))
;; keydir should be a function of all the address
;; attributes: we want it to change whenever anything changes
;; so that clients can see which addresses are new without
;; deep table comparisons
keydir (..
prefix
(-> address.address
(: :gsub "::$" "")
(: :gsub ":" "-"))
"_"
suffix)]
(: :gsub ":" "-")))]
(mktree (.. state-directory "/" keydir))
(each [k v (pairs address)]
(write-value (.. keydir "/" k) v)))))

View File

@ -14,6 +14,7 @@ let
cp -av ${src}/target/linux/generic/files/* .
chmod -R u+w .
cp -av ${src}/target/linux/${family}/files/* .
chmod -R u+w .
test -d ${src}/target/linux/${family}/files-5.15/ && cp -av ${src}/target/linux/${family}/files-5.15/* .
chmod -R u+w .
patches() {

View File

@ -2,8 +2,15 @@
writeText "service-fns.sh" ''
output() { cat $1/.outputs/$2; }
output_path() { echo $(realpath $1/.outputs)/$2; }
SERVICE_OUTPUTS=/run/services/outputs
SERVICE_STATE=/run/services/state
mkoutputs() {
d=/run/service-state/$1
d=$SERVICE_OUTPUTS/$1
mkdir -m 2751 -p $d && chown root:system $d
echo $d
}
mkstate() {
d=$SERVICE_STATE/$1
mkdir -m 2751 -p $d && chown root:system $d
echo $d
}

View File

@ -0,0 +1,16 @@
{
stdenv
, openwrt
}:
stdenv.mkDerivation {
name = "zyxel-bootconfig";
inherit (openwrt) src;
sourceRoot = "openwrt-source/package/utils/zyxel-bootconfig/src";
installPhase = ''
mkdir -p $out/bin
install -Dm544 zyxel-bootconfig $out/bin/zyxel-bootconfig
'';
meta = {
mainProgram = "zyxel-bootconfig";
};
}

View File

@ -12,7 +12,7 @@ let derivation = (import liminix {
img = derivation.outputs.tftpboot;
uboot = derivation.outputs.u-boot;
pkgsBuild = derivation.pkgs.pkgsBuildBuild;
in pkgsBuild.runCommand "check" {
in pkgsBuild.runCommand "check-${deviceName}" {
nativeBuildInputs = with pkgsBuild; [
expect
socat
@ -44,4 +44,11 @@ in {
mipsLz = check "qemu" {
boot.tftp.compressRoot = true;
};
# this works on real hardware but I haven't figured out how
# to make it work on qemu: it says
# "OF: fdt: No chosen node found, continuing without"
# mipsOldUboot = check "qemu" {
# boot.tftp.appendDTB = true;
# };
}