forked from dan/liminix
Compare commits
595 Commits
main
...
tftpboot-f
Author | SHA1 | Date | |
---|---|---|---|
|
a3f6234ef0 | ||
2e513eb4a7 | |||
f2e4e77d73 | |||
48dfbe0c01 | |||
6f697db57c | |||
fe1ee12e3d | |||
4d273a9469 | |||
40db175b41 | |||
ab07212a7e | |||
f5e08ac9d9 | |||
0cb18eabcd | |||
24151425b8 | |||
e06295ed83 | |||
608d3e3abf | |||
3e19f1b927 | |||
3f6e9b6384 | |||
294492a176 | |||
67a1cd3718 | |||
|
f8a275d1a3 | ||
0ee9c76c33 | |||
452aaa2f60 | |||
52967f746b | |||
|
a89f866bf0 | ||
f3fadd5cd7 | |||
bc20f4c6b7 | |||
848214d104 | |||
ede8f12d2b | |||
6cd5b90678 | |||
db4f098c02 | |||
1347937345 | |||
a7b5f80674 | |||
5c78338d71 | |||
ed02d02767 | |||
f07a38b0fd | |||
ac189f2977 | |||
ebb4d4a831 | |||
6bfbdf352d | |||
4ea1cf7f32 | |||
f60b74f415 | |||
812e35b7b9 | |||
172f368633 | |||
1af9a39db1 | |||
420552ce98 | |||
56c667cfd5 | |||
f9b4f0bc9c | |||
ba5e4704a0 | |||
3357d21d7f | |||
ffaca615ba | |||
77cd4492b2 | |||
81f5550bf0 | |||
b52133a28b | |||
|
1ff779c1a9 | ||
44caefcd3b | |||
6e6b8790eb | |||
2e5a8a572e | |||
464d046b5a | |||
ac8b971cc0 | |||
13087d17e3 | |||
5572c0ecb0 | |||
4cbe3ba683 | |||
20f4a12689 | |||
33e5c436d5 | |||
cde30bcd54 | |||
1f7d6544e3 | |||
1bca072509 | |||
7b98724643 | |||
b1625763ee | |||
91bdfc2766 | |||
14bfebc5c3 | |||
0447ac0ff9 | |||
e35a1514ab | |||
4a0120487c | |||
888688ce28 | |||
9e3f48768e | |||
72171021e3 | |||
17517dd34f | |||
5112eab4da | |||
e383f1b3d3 | |||
da1245432e | |||
541b1c61c2 | |||
55c7410a55 | |||
0f50648157 | |||
f1c260d4f7 | |||
3d611d3ba2 | |||
e6b7d86381 | |||
83fbffb39b | |||
f8c579b41e | |||
ca9efc4b26 | |||
336fc7e495 | |||
4cc0add2ad | |||
2d7e6188ac | |||
b9999857cb | |||
ba03ddeb38 | |||
493c5f69d7 | |||
1a915e91ff | |||
197e2eb5b1 | |||
7ca822c826 | |||
e5631783e1 | |||
635590d37a | |||
17630f2678 | |||
707a471bc2 | |||
d3fce5edd4 | |||
5771108fed | |||
9e5f2d663d | |||
21eeb1671e | |||
44762d38fc | |||
1f6cfc3679 | |||
8ec00f1710 | |||
6a6dd32dea | |||
9b1fc11a59 | |||
aaa6e353db | |||
69bf6cb5fb | |||
9f58e7b926 | |||
5a5c27ab9f | |||
277c91acdf | |||
e0725489ca | |||
cc47515cf8 | |||
464913cc8f | |||
e604d628e3 | |||
e2a597589b | |||
|
a139a262c1 | ||
6a5fed83dd | |||
bcf5ab24e8 | |||
32bf80c6fa | |||
12275f6896 | |||
a60c2539a6 | |||
146a2d9ac0 | |||
091d863710 | |||
c7bcfbfa34 | |||
500a3c1025 | |||
0c0d0eed8a | |||
699cf97206 | |||
cd0093279c | |||
034d6aacc4 | |||
e590c0ad3f | |||
14abdd9998 | |||
6287b92000 | |||
d2215d3e56 | |||
3cf2308bee | |||
3913989be3 | |||
43e5e6876e | |||
7d6c80570c | |||
e745991b9d | |||
defbfce1fb | |||
0df2c83382 | |||
01c28de88d | |||
2bf197cad8 | |||
a8a19977ca | |||
8a9284af1e | |||
7351e143c5 | |||
283c3154a7 | |||
34f37d60d9 | |||
fe7b092075 | |||
b56f121e04 | |||
d5d621f310 | |||
da95a9fa62 | |||
85071c88e7 | |||
74093b7ee3 | |||
41733e58d6 | |||
9041d5d63a | |||
001ebdc601 | |||
1f97409474 | |||
a41839f3d1 | |||
ff76d854fc | |||
81a6480a4f | |||
c7164a6f4a | |||
83ca86fe42 | |||
1b4106e2a3 | |||
89912c766b | |||
9828b007ae | |||
f34abc85ae | |||
b475a680fb | |||
43612af71a | |||
5695c47496 | |||
e3ec514710 | |||
99f68e5421 | |||
9c30b6f882 | |||
dd75322c10 | |||
869a508c0a | |||
e835473945 | |||
055268d5d2 | |||
ff38bcacbb | |||
a6128955e7 | |||
531cb113be | |||
daede666cb | |||
2992771c7e | |||
4cc82e1502 | |||
21f2320d86 | |||
d40ada4251 | |||
4053ea9481 | |||
54d3415885 | |||
264d83c98d | |||
97defc2076 | |||
ddaa5476d3 | |||
bcd9d56624 | |||
e2c883356c | |||
d79a941504 | |||
2f82e0dab8 | |||
fc03965915 | |||
d2d3af2587 | |||
310ac30f24 | |||
45a7f96bd4 | |||
79445fd962 | |||
a9ddd78482 | |||
4fb8253e57 | |||
ff3a1905a5 | |||
3c353e4aff | |||
ba21384fde | |||
2480fdef5b | |||
409c1cfb16 | |||
9767078878 | |||
d760c2d27b | |||
1e139c22fd | |||
a1ff07b063 | |||
9550772cec | |||
64cd1626c6 | |||
eb79928b37 | |||
0a629df48d | |||
64afd18e2a | |||
47e96ddc15 | |||
5db9d7269e | |||
985df8792d | |||
528afae8b1 | |||
384835c89d | |||
5051625d31 | |||
c4d00e062a | |||
8fa3443923 | |||
8091e207b6 | |||
39020607ad | |||
fe735408a1 | |||
a9d1582b53 | |||
eca8e37e7a | |||
d300373b96 | |||
70ca7fac17 | |||
79a3a45061 | |||
612d6d7a51 | |||
e1ae986cf6 | |||
bce0c7ffb6 | |||
28ca1e68ab | |||
acf33a100f | |||
7f9cae9d5c | |||
3012c91b47 | |||
1edf20c08f | |||
7195cb10ce | |||
135a445672 | |||
3899daee56 | |||
b17f623d03 | |||
df395a4d5d | |||
75e9f8210c | |||
1c3242cab1 | |||
44ea683391 | |||
725d8b608f | |||
bc9ced5d38 | |||
73ae7788b9 | |||
d34919766a | |||
2fe0cd2f48 | |||
241f1013ed | |||
2ce361d4e3 | |||
3f8cc24dcc | |||
57e3b449f8 | |||
3964505131 | |||
941479b144 | |||
ac551536da | |||
6f908156af | |||
534a49e827 | |||
07a6eb73cd | |||
159bfa3057 | |||
8f0ab5be40 | |||
7f9971512d | |||
f0f6cc80d7 | |||
afcc6a6436 | |||
2e8e05f31a | |||
143137cbc6 | |||
8d228f2bef | |||
5751058d59 | |||
5ac7e1e9b2 | |||
c75452549b | |||
2663f58807 | |||
9dbc285605 | |||
8b6aa2134e | |||
3df1ec76ff | |||
0d3218127f | |||
e94bf62ec1 | |||
16a2499d74 | |||
d4d8093f97 | |||
7c9c801afc | |||
c4185617c0 | |||
06d28e9b08 | |||
9540fc2641 | |||
adc84108ad | |||
eae99051fa | |||
49d1703428 | |||
1d337588f9 | |||
29a869b4fa | |||
5ae1b0a193 | |||
473a4947a5 | |||
50bad5c604 | |||
c22e3fb2ef | |||
f898e4dca2 | |||
5121a8563d | |||
78be354b6e | |||
be13ab23ca | |||
4b30cd7a75 | |||
b15542b668 | |||
6daeaf29a0 | |||
e6ca5ea064 | |||
e6e4665a18 | |||
2c10790a6d | |||
571adf84c0 | |||
c8c79fd75a | |||
884d8d194e | |||
f091bbd706 | |||
37d7e20582 | |||
04b068f7a3 | |||
53f57c1a8c | |||
19aba0d873 | |||
7d00b39249 | |||
7aa8633cde | |||
58bec8a40f | |||
a3fca5bf05 | |||
e0bd7aec1e | |||
e815f61bb5 | |||
af9200a136 | |||
898958fa10 | |||
fa0f262706 | |||
71aeb27b2f | |||
530b4080c9 | |||
58cd007ccc | |||
3a56798eb5 | |||
758c7ef657 | |||
73225a70b2 | |||
ab304dd3f1 | |||
0d49f0f7a7 | |||
e64390460a | |||
c0ef6ce282 | |||
bd6ec5201f | |||
b4068da9fe | |||
aa4b09da85 | |||
471c63b399 | |||
782feaeafa | |||
ac54c89427 | |||
5a3646cb29 | |||
e249f48cff | |||
6661e42684 | |||
b9ba9ef835 | |||
8b69dcc209 | |||
9b3a3b9ff7 | |||
7d08497bcb | |||
0e84adaa0e | |||
660ed5df8f | |||
792a11c8c0 | |||
7e4a05bbf8 | |||
a4ba5c85e1 | |||
723ef73d5a | |||
3d4e782929 | |||
1b6a05aec5 | |||
80628a3d90 | |||
bf0cafffed | |||
e49aba127c | |||
324465bc18 | |||
b33249a050 | |||
b9c084415e | |||
cf9cadd212 | |||
a116fe084a | |||
74cf3e0711 | |||
9795f03da4 | |||
cdb23b147c | |||
dbd1264352 | |||
834858d5bc | |||
18335b95e3 | |||
6bee2f67ac | |||
b4ba3eea21 | |||
16af3984c9 | |||
ce7e395295 | |||
7e13e017eb | |||
bbf2f53c0e | |||
032d0f8aca | |||
b8ac9e5279 | |||
ff2604ca5d | |||
72789984ce | |||
90d9d0e811 | |||
97a8ae1c84 | |||
52eb283a26 | |||
cbb1de804e | |||
f9c03998b8 | |||
50de1b090f | |||
648382f64a | |||
e9370358ae | |||
762ce7b6b8 | |||
b1c0560f4f | |||
e34135c41a | |||
712c9b266f | |||
4df963996c | |||
349bfecbb8 | |||
450d3820b2 | |||
771585546d | |||
73abf952d5 | |||
8af4e9fd5b | |||
7e19d80130 | |||
0f0688c802 | |||
b43f17f655 | |||
adf62d4483 | |||
68eb1360f6 | |||
19ad6cd278 | |||
00076c7b81 | |||
721e7499f3 | |||
fc723b9a35 | |||
a5f16dfa81 | |||
41a4b1f7ef | |||
42a5699326 | |||
ea2b25168e | |||
5564cf0554 | |||
f3a13630d3 | |||
f233acf9ff | |||
b6a054c588 | |||
b231664a06 | |||
f4bf3029fa | |||
05f2c9a2f7 | |||
5df5c822ea | |||
4795dd05b7 | |||
a192f08881 | |||
a873dc6608 | |||
2fb4756a7f | |||
04f5174425 | |||
dca2e4def1 | |||
b60126775a | |||
76f11bcc93 | |||
efcfdcc21d | |||
77f1a78331 | |||
28a5dec7dd | |||
fad0a47b75 | |||
af52aafc84 | |||
34442b6069 | |||
b8a46fc05e | |||
8ac2c6cec1 | |||
8879b2d1ba | |||
83e346d5a0 | |||
156b1fe64a | |||
1a314e55b7 | |||
9263b21faa | |||
0a820a702a | |||
4ea518e296 | |||
98318b450d | |||
e4ac7f19dc | |||
9c22744850 | |||
c697be8c28 | |||
202a37221a | |||
436eb03a7b | |||
e5963ae3f7 | |||
f164f19d95 | |||
dd4ab41f6a | |||
5d5dff6729 | |||
570d29c368 | |||
725af00dc9 | |||
e1b932ec27 | |||
7173b6fb1c | |||
ed9548f21d | |||
0787807a7f | |||
38ed91f641 | |||
ffe9603c39 | |||
cbd3dfefc5 | |||
018c1868b5 | |||
5184ff63f7 | |||
35909c9a23 | |||
4383462199 | |||
9730cdd63b | |||
095853214b | |||
9d6e50cbbc | |||
94dbc56595 | |||
2cd7f932eb | |||
|
27c7735f02 | ||
|
29c9de248d | ||
3ca0d87c27 | |||
8f30db58ae | |||
f9ab0590a6 | |||
84fa8d65f4 | |||
9b0149ecb7 | |||
|
baf3cf7413 | ||
|
c5145b5fc9 | ||
|
628f4dfdbe | ||
|
da59e2a349 | ||
|
c0a9571a13 | ||
|
d6ffdd7be6 | ||
|
985f982435 | ||
|
a893c0dc4c | ||
|
3ec29dc1b9 | ||
|
0e81953b67 | ||
|
3c70a0d037 | ||
|
422f3edab1 | ||
|
c14b2f6356 | ||
|
cdafff2095 | ||
|
13f1bb9f52 | ||
|
019fef6929 | ||
|
63007859c2 | ||
|
e9ab8d7183 | ||
|
3dc58de0eb | ||
|
dde8386f75 | ||
|
c59364d623 | ||
|
b76c5b4abe | ||
|
0a8343be66 | ||
|
d14ee41325 | ||
|
8f814658fe | ||
60508f4d4e | |||
ca64e9035e | |||
4bcc3d5b28 | |||
28fe37d555 | |||
175db9f604 | |||
b5722a0153 | |||
c373152673 | |||
7e7171556f | |||
6920ee765d | |||
71a1ef286e | |||
ffe0e9d26b | |||
2b22c7aa91 | |||
3c950704e1 | |||
8578a554c7 | |||
3851698d35 | |||
f69ebbb6f5 | |||
16e4b05653 | |||
8ac848b1e6 | |||
b7efbd3e21 | |||
a654577ac2 | |||
c50423f689 | |||
65479e206b | |||
79926c6fe7 | |||
ae4856ea7c | |||
b9c0d93670 | |||
11287a8436 | |||
57aece0709 | |||
c1d285a220 | |||
dce983ec79 | |||
812f497660 | |||
1206d02200 | |||
7c196bf9b4 | |||
86d19c54b3 | |||
aca3e11631 | |||
273c66b2d3 | |||
87f6a31a06 | |||
a9ea01428e | |||
92b0bec038 | |||
82537bbe68 | |||
efb29c5901 | |||
29e61be26c | |||
6f1f9d6f20 | |||
34291292c0 | |||
c9e4c1b0da | |||
|
63e3f2aa58 | ||
|
61494fdc0c | ||
891d6e5f20 | |||
c4041b00f6 | |||
f875622100 | |||
49ec4a2961 | |||
c8154a2db9 | |||
02cf2c6b80 | |||
b0709a6443 | |||
86f5c9b568 | |||
ef707de8b1 | |||
89c88dd472 | |||
c1ad139310 | |||
f682b26c29 | |||
84ce618213 | |||
9e199c6957 | |||
c8e3d84bf4 | |||
dd8ec18881 | |||
1730cf07b1 | |||
de51bfe13d | |||
b09723345c | |||
1781d4b6e4 | |||
c219350d7c | |||
6f83282ff5 | |||
|
04895f9cf6 | ||
5f2d1660bd | |||
|
7642e23c0a | ||
83ee488e4c | |||
f19a937eda | |||
f0490f37d5 | |||
c1101d3af5 | |||
9a3d7a387e | |||
228c0a1668 | |||
63f034e362 | |||
6971d03520 | |||
7bc9cb6c55 | |||
a251ceeb99 | |||
38a7f0b03b | |||
c0c4752350 | |||
3c941b4ce2 | |||
243295aab8 | |||
45e8db09e1 | |||
2a93f24a58 | |||
64898eada8 | |||
|
d5026c2074 | ||
136c5e6f32 | |||
fa9a2c6413 | |||
049cdbb610 | |||
5ee4adff10 |
.gitignoreNEWSREADME.mdTHOUGHTS.txtboot.expectbordervm-configuration.nixbordervm.conf-example.nixci.nixdefault.nix
devices
belkin-rt3200
families
gl-ar750
gl-mt300a
gl-mt300n-v2
qemu-aarch64
qemu-armv7l
qemu
tp-archer-ax23
turris-omnia
zyxel-nwa50ax
doc
Makefileadmin.rstconf.pyconfiguration.rstdevelopment.rstextract-options.nixhardware.nixindex.rstinstallation.rstmodules.rstoutputs.rstparse-options-outputs.fnltutorial.rst
examples
arhcive.nixdemo.nixextneder.nixhello-from-mt300.nixhello-from-qemu.nixmodule-https-proxy.nixnwa50ax-ap.nixrecovery.nixrotuer-secrets.example.nixrotuer.nixrouter-with-l2tp.nixsecrets.jsonturris.nix
modules
all-modules.nix
arch
base.nixbridge
busybox.nixdhcp6c
acquire-delegated-prefix.fnlacquire-delegated-prefix.nixacquire-wan-address-test.fnlacquire-wan-address.fnlacquire-wan-address.nixaddress.nixclient.nixdefault.nixprefix.nix
dnsmasq
firewall
hardware.nixhealth-check
hostapd
ifwait
kernel
klogd.nixmdevd.nixmount
network
ntp
outputs.nixoutputs
1
.gitignore
vendored
1
.gitignore
vendored
@ -6,3 +6,4 @@ result-*
|
||||
_build
|
||||
*-secrets.nix
|
||||
examples/static-leases.nix
|
||||
/doc/hardware.rst
|
||||
|
132
NEWS
132
NEWS
@ -23,5 +23,137 @@ the name of an outputs that gloms together other filesystem-like
|
||||
outputs with some kind of partition table - so we might in future have
|
||||
gptimage or lvmimage or ubimage.
|
||||
|
||||
2024-01-03
|
||||
|
||||
Liminix is now targeted to Nixpkgs 23.11 (not 23.05 as previously).
|
||||
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.
|
||||
|
||||
2024-04-29
|
||||
|
||||
The setup for using `levitate` has changed: now it accepts an entire
|
||||
config fragment, not just a list of services. Hopefully this makes it
|
||||
a bit more useful :-)
|
||||
|
||||
defaultProfile.packages = with pkgs; [
|
||||
...
|
||||
(levitate.override {
|
||||
config = {
|
||||
services = {
|
||||
inherit (config.services) dhcpc sshd watchdog;
|
||||
};
|
||||
defaultProfile.packages = [ mtdutils ];
|
||||
users.root.openssh.authorizedKeys.keys = secrets.root.keys;
|
||||
};
|
||||
})
|
||||
];
|
||||
|
||||
2024-07-16
|
||||
|
||||
* structured parameters are available for the pppoe service
|
||||
|
||||
* The "wan" configuration in modules/profiles/gateway.nix has changed:
|
||||
instead of passing options that are used to create a pppoe interface,
|
||||
callers should create a (pppoe or other) interface and pass that as
|
||||
the value of profile.gateway.wan. For the pppoe case this is now only
|
||||
very slightly more verbose, and it allows using the gateway profile
|
||||
with other kinds of upstream.
|
||||
|
||||
2024-8-16
|
||||
|
||||
As part of implementing log shipping, the default directory for system
|
||||
logs has beenchanged from /run/uncaught-logs to /run/log
|
||||
|
||||
2024-10-09
|
||||
|
||||
liminix-rebuild is being deprecated. From hereon in, the preferred way
|
||||
to do an incremental update on an installed device with a writable
|
||||
filesystem is to build the systemConfiguration output
|
||||
|
||||
nix-build -I liminix-config=hosts/myhost.nix --argstr deviceName turris-omnia -A outputs.systemConfiguration
|
||||
|
||||
and then run the generated `install.sh` script
|
||||
|
||||
result/install.sh root@192.168.8.1
|
||||
|
||||
2024-12-16
|
||||
|
||||
Config options changed: if you had set config.hardware.dts.includes
|
||||
(maybe in an out-of-tree device port) to specify the search paths
|
||||
in which dtc finds include files, you will need to change this to
|
||||
hardware.dts.includePaths.
|
||||
|
||||
The "new" hardware.dts.includes option is now for dtsi files which
|
||||
should be merged into the device tree.
|
||||
|
||||
2024-12-19
|
||||
|
||||
Incremental updates changed again (but not massively). From hereon in,
|
||||
the preferred way to do an incremental update on an installed device
|
||||
with a writable filesystem is to build the updater output
|
||||
|
||||
nix-build -I liminix-config=hosts/myhost.nix --argstr deviceName turris-omnia -A outputs.updater
|
||||
|
||||
and then run the generated `update.sh` script. See
|
||||
https://www.liminix.org/doc/admin.html#updating-an-installed-system
|
||||
|
||||
2024-12-22
|
||||
|
||||
outputs.zimage is now outputs.kernel.zImage. This is unlikely to
|
||||
affect many people at all but I mention it anyway.
|
@ -33,7 +33,7 @@ functioning version, see [the CI system](https://build.liminix.org/jobset/limini
|
||||
Documentation is in the [doc](doc/) directory. You can build it
|
||||
by running
|
||||
|
||||
nix-shell -p sphinx --run "make -C doc html"
|
||||
nix-shell -p sphinx --run "make -C doc hardware.rst html"
|
||||
|
||||
Rendered documentation corresponding to the latest commit on `main`
|
||||
is published to [https://www.liminix.org/doc/](https://www.liminix.org/doc/)
|
||||
|
2931
THOUGHTS.txt
2931
THOUGHTS.txt
File diff suppressed because it is too large
Load Diff
38
boot.expect
Normal file
38
boot.expect
Normal file
@ -0,0 +1,38 @@
|
||||
# This is for use with minicom, but needs you to configure it to
|
||||
# use expect as its "Script program" instead of runscript. Try
|
||||
# Ctrl+A O -> Filenames and paths -> D
|
||||
|
||||
fconfigure stderr -buffering none
|
||||
fconfigure stdout -buffering none
|
||||
|
||||
proc waitprompt { } {
|
||||
expect {
|
||||
"BusyBox" { puts stderr "DONE\r"; exit 0 }
|
||||
"READY" { puts stderr ";;; READY\r"; }
|
||||
timeout { puts stderr ";;; timed out waiting after $line\r" }
|
||||
}
|
||||
}
|
||||
|
||||
proc sendline { line } {
|
||||
send "$line; echo \$ready \r"
|
||||
}
|
||||
|
||||
log_user 0
|
||||
log_file -a -open stderr
|
||||
|
||||
set f [open "result/boot.scr"]
|
||||
|
||||
send "setenv ready REA\rsetenv ready \${ready}DY\r"
|
||||
|
||||
set timeout 300
|
||||
expect_before timeout abort
|
||||
while {[gets $f line] >= 0} {
|
||||
puts stderr ";;; next line $line\r"
|
||||
puts stderr ";;; waiting for prompt\r"
|
||||
puts stderr ";;; sending\r"
|
||||
sendline $line
|
||||
waitprompt
|
||||
}
|
||||
|
||||
puts stderr "done\r\n"
|
||||
close $f
|
@ -4,6 +4,10 @@ let
|
||||
inherit (lib) mkOption mkEnableOption mdDoc types optional optionals;
|
||||
in {
|
||||
options.bordervm = {
|
||||
keys = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
};
|
||||
l2tp = {
|
||||
host = mkOption {
|
||||
description = mdDoc ''
|
||||
@ -51,18 +55,17 @@ in {
|
||||
<nixpkgs/nixos/modules/virtualisation/qemu-vm.nix>
|
||||
];
|
||||
config = {
|
||||
boot.kernelParams = [
|
||||
"loglevel=9"
|
||||
];
|
||||
boot.kernelParams = [ "loglevel=9" ];
|
||||
systemd.services.pppoe =
|
||||
let conf = pkgs.writeText "kpppoed.toml"
|
||||
''
|
||||
interface_name = "eth1"
|
||||
services = [ "myservice" ]
|
||||
lns_ipaddr = "${cfg.l2tp.host}:${builtins.toString cfg.l2tp.port}"
|
||||
ac_name = "kpppoed-1.0"
|
||||
'';
|
||||
in {
|
||||
let
|
||||
conf = pkgs.writeText "kpppoed.toml" ''
|
||||
interface_name = "eth1"
|
||||
services = [ "myservice" ]
|
||||
lns_ipaddr = "${cfg.l2tp.host}:${builtins.toString cfg.l2tp.port}"
|
||||
ac_name = "kpppoed-1.0"
|
||||
'';
|
||||
in
|
||||
{
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network-online.target" ];
|
||||
serviceConfig = {
|
||||
@ -76,29 +79,69 @@ in {
|
||||
};
|
||||
};
|
||||
services.openssh.enable = true;
|
||||
services.dnsmasq = {
|
||||
enable = true;
|
||||
resolveLocalQueries = false;
|
||||
settings = {
|
||||
# domain-needed = true;
|
||||
dhcp-range = [ "10.0.0.10,10.0.0.240" ];
|
||||
interface = "eth1";
|
||||
};
|
||||
};
|
||||
|
||||
services.nginx = {
|
||||
enable = true;
|
||||
user = "liminix";
|
||||
virtualHosts.${config.networking.hostName} = {
|
||||
root = "/home/liminix";
|
||||
default = true;
|
||||
};
|
||||
};
|
||||
systemd.services.nginx.serviceConfig.ProtectHome = "read-only";
|
||||
|
||||
systemd.services.sshd.wantedBy = pkgs.lib.mkForce [ "multi-user.target" ];
|
||||
|
||||
virtualisation = {
|
||||
forwardPorts = [
|
||||
{
|
||||
from = "host";
|
||||
host.port = 7654;
|
||||
# guest.address = "10.0.2.15";
|
||||
guest.port =7654;
|
||||
}
|
||||
{
|
||||
host.port = 2222;
|
||||
guest.address = "10.0.2.15";
|
||||
guest.port = 22;
|
||||
}];
|
||||
qemu = {
|
||||
networkingOptions = [];
|
||||
options = [] ++
|
||||
optional cfg.ethernet.pci.enable
|
||||
"-device vfio-pci,host=${cfg.ethernet.pci.id}" ++
|
||||
optionals cfg.ethernet.usb.enable [
|
||||
networkingOptions = [ ];
|
||||
options =
|
||||
[ ]
|
||||
++ optional cfg.ethernet.pci.enable "-device vfio-pci,host=${cfg.ethernet.pci.id}"
|
||||
++ optionals cfg.ethernet.usb.enable [
|
||||
"-device usb-ehci,id=ehci"
|
||||
"-device usb-host,bus=ehci.0,vendorid=${cfg.ethernet.usb.vendor},productid=${cfg.ethernet.usb.product}"
|
||||
] ++ [
|
||||
]
|
||||
++ [
|
||||
"-nographic"
|
||||
"-serial mon:stdio"
|
||||
];
|
||||
};
|
||||
sharedDirectories = {
|
||||
liminix = {
|
||||
securityModel = "none";
|
||||
source = builtins.toString ./.;
|
||||
target = "/home/liminix/liminix";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
services.tang = {
|
||||
enable = true;
|
||||
ipAddressAllow = [ "10.0.0.0/24" "0.0.0.0/0" ];
|
||||
};
|
||||
|
||||
environment.systemPackages =
|
||||
let wireshark-nogui = pkgs.wireshark.override { withQt = false ; };
|
||||
in with pkgs; [
|
||||
@ -108,6 +151,8 @@ in {
|
||||
tufted
|
||||
iptables
|
||||
usbutils
|
||||
busybox
|
||||
clevis
|
||||
];
|
||||
security.sudo.wheelNeedsPassword = false;
|
||||
networking = {
|
||||
@ -117,11 +162,17 @@ in {
|
||||
useDHCP = false;
|
||||
ipv4.addresses = [ { address = "10.0.0.1"; prefixLength = 24;}];
|
||||
};
|
||||
nat = {
|
||||
enable = true;
|
||||
internalInterfaces = [ "eth1" ];
|
||||
externalInterface = "eth0";
|
||||
};
|
||||
};
|
||||
users.users.liminix = {
|
||||
isNormalUser = true;
|
||||
uid = 1000;
|
||||
extraGroups = [ "wheel"];
|
||||
extraGroups = [ "wheel" ];
|
||||
openssh.authorizedKeys.keys = cfg.keys;
|
||||
};
|
||||
services.getty.autologinUser = "liminix";
|
||||
};
|
||||
|
@ -1,8 +1,12 @@
|
||||
{...}:
|
||||
{ ... }:
|
||||
{
|
||||
bordervm = {
|
||||
# ethernet.pci = { id = "01:00.0"; enable = true; };
|
||||
ethernet.usb = { vendor = "0x0bda"; product = "0x8153"; enable = true; };
|
||||
ethernet.usb = {
|
||||
vendor = "0x0bda";
|
||||
product = "0x8153";
|
||||
enable = true;
|
||||
};
|
||||
l2tp = {
|
||||
host = "l2tp.aa.net.uk";
|
||||
};
|
||||
|
90
ci.nix
90
ci.nix
@ -1,76 +1,80 @@
|
||||
{
|
||||
nixpkgs
|
||||
, unstable
|
||||
, liminix
|
||||
, ... }:
|
||||
let
|
||||
inherit (builtins) map;
|
||||
pkgs = (import nixpkgs {});
|
||||
borderVmConf = ./bordervm.conf-example.nix;
|
||||
pkgs = import <nixpkgs> { };
|
||||
liminix = <liminix>;
|
||||
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"
|
||||
"turris-omnia"
|
||||
];
|
||||
vanilla = ./vanilla-configuration.nix;
|
||||
for-device = name:
|
||||
(import liminix {
|
||||
inherit nixpkgs borderVmConf;
|
||||
inherit borderVmConf;
|
||||
device = import (liminix + "/devices/${name}");
|
||||
liminix-config = vanilla;
|
||||
}).outputs.default;
|
||||
tests = import ./tests/ci.nix;
|
||||
jobs =
|
||||
(genAttrs devices for-device) //
|
||||
tests //
|
||||
{
|
||||
buildEnv = (import liminix {
|
||||
inherit nixpkgs borderVmConf;
|
||||
device = import (liminix + "/devices/qemu");
|
||||
liminix-config = vanilla;
|
||||
}).buildEnv;
|
||||
(genAttrs devices for-device)
|
||||
// tests
|
||||
// {
|
||||
buildEnv =
|
||||
(import liminix {
|
||||
inherit borderVmConf;
|
||||
device = import (liminix + "/devices/qemu");
|
||||
liminix-config = vanilla;
|
||||
}).buildEnv;
|
||||
doc =
|
||||
let json =
|
||||
(import liminix {
|
||||
inherit nixpkgs borderVmConf;
|
||||
device = import (liminix + "/devices/qemu");
|
||||
liminix-config = {...} : {
|
||||
let
|
||||
json =
|
||||
(import liminix {
|
||||
inherit borderVmConf;
|
||||
device = import (liminix + "/devices/qemu");
|
||||
liminix-config =
|
||||
{ ... }:
|
||||
{
|
||||
imports = [ ./modules/all-modules.nix ];
|
||||
};
|
||||
}).outputs.optionsJson;
|
||||
installers = map (f: "system.outputs.${f}") [
|
||||
"vmroot"
|
||||
"mtdimage"
|
||||
"ubimage"
|
||||
];
|
||||
inherit (pkgs.lib) concatStringsSep;
|
||||
in pkgs.stdenv.mkDerivation {
|
||||
}).outputs.optionsJson;
|
||||
in
|
||||
pkgs.stdenv.mkDerivation {
|
||||
name = "liminix-doc";
|
||||
nativeBuildInputs = with pkgs; [
|
||||
gnumake sphinx fennel luaPackages.lyaml
|
||||
gnumake
|
||||
sphinx
|
||||
fennel
|
||||
luaPackages.lyaml
|
||||
];
|
||||
src = ./.;
|
||||
buildPhase = ''
|
||||
cat ${json} | fennel --correlate doc/parse-options.fnl > doc/modules-generated.rst
|
||||
cat ${json} | fennel --correlate doc/parse-options-outputs.fnl > doc/outputs-generated.rst
|
||||
cat ${json} | fennel --correlate doc/parse-options.fnl > doc/modules-generated.inc.rst
|
||||
cat ${json} | fennel --correlate doc/parse-options-outputs.fnl > doc/outputs-generated.inc.rst
|
||||
cp ${(import ./doc/hardware.nix)} doc/hardware.rst
|
||||
make -C doc html
|
||||
'';
|
||||
installPhase = ''
|
||||
mkdir -p $out/nix-support $out/share/doc/
|
||||
cd doc
|
||||
cp *-generated.rst $out
|
||||
cp *-generated.inc.rst hardware.rst $out
|
||||
ln -s ${json} $out/options.json
|
||||
cp -a _build/html $out/share/doc/liminix
|
||||
echo "file source-dist \"$out/share/doc/liminix\"" \
|
||||
> $out/nix-support/hydra-build-products
|
||||
'';
|
||||
};
|
||||
with-unstable = (import liminix {
|
||||
nixpkgs = unstable;
|
||||
inherit borderVmConf;
|
||||
device = import (liminix + "/devices/qemu");
|
||||
liminix-config = vanilla;
|
||||
}).outputs.default;
|
||||
};
|
||||
in jobs
|
||||
in jobs //
|
||||
{
|
||||
all = pkgs.mkShell {
|
||||
name = "all tests";
|
||||
contents = pkgs.lib.collect pkgs.lib.isDerivation jobs;
|
||||
};
|
||||
}
|
||||
|
51
default.nix
51
default.nix
@ -1,35 +1,47 @@
|
||||
{
|
||||
device
|
||||
, liminix-config ? <liminix-config>
|
||||
, nixpkgs ? <nixpkgs>
|
||||
, borderVmConf ? ./bordervm.conf.nix
|
||||
deviceName ? null,
|
||||
device ? (import ./devices/${deviceName}),
|
||||
liminix-config ? <liminix-config>,
|
||||
borderVmConf ? ./bordervm.conf.nix,
|
||||
imageType ? "primary",
|
||||
}:
|
||||
|
||||
let
|
||||
overlay = import ./overlay.nix;
|
||||
pkgs = import nixpkgs (device.system // {
|
||||
overlays = [overlay];
|
||||
config = {
|
||||
allowUnsupportedSystem = true; # mipsel
|
||||
permittedInsecurePackages = [
|
||||
"python-2.7.18.6" # kernel backports needs python <3
|
||||
"python-2.7.18.7"
|
||||
];
|
||||
};
|
||||
});
|
||||
pkgs = import <nixpkgs> (
|
||||
device.system
|
||||
// {
|
||||
overlays = [ overlay ];
|
||||
config = {
|
||||
allowUnsupportedSystem = true; # mipsel
|
||||
permittedInsecurePackages = [
|
||||
"python-2.7.18.6" # kernel backports needs python <3
|
||||
"python-2.7.18.7"
|
||||
];
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
eval = pkgs.lib.evalModules {
|
||||
specialArgs = {
|
||||
modulesPath = builtins.toString ./modules;
|
||||
};
|
||||
modules = [
|
||||
{ _module.args = { inherit pkgs; inherit (pkgs) lim; }; }
|
||||
./modules/hardware.nix
|
||||
./modules/base.nix
|
||||
./modules/busybox.nix
|
||||
./modules/hostname.nix
|
||||
./modules/kernel
|
||||
./modules/klogd.nix
|
||||
device.module
|
||||
liminix-config
|
||||
./modules/s6
|
||||
./modules/users.nix
|
||||
./modules/outputs.nix
|
||||
{
|
||||
boot.imageType = imageType;
|
||||
}
|
||||
];
|
||||
};
|
||||
config = eval.config;
|
||||
@ -37,7 +49,14 @@ let
|
||||
borderVm = ((import <nixpkgs/nixos/lib/eval-config.nix>) {
|
||||
system = builtins.currentSystem;
|
||||
modules = [
|
||||
({ ... } : { nixpkgs.overlays = [ overlay ]; })
|
||||
{
|
||||
nixpkgs.overlays = [
|
||||
(final: prev: {
|
||||
go-l2tp = final.callPackage ./pkgs/go-l2tp {};
|
||||
tufted = final.callPackage ./pkgs/tufted {};
|
||||
})
|
||||
];
|
||||
}
|
||||
(import ./bordervm-configuration.nix)
|
||||
borderVmConf
|
||||
];
|
||||
@ -67,6 +86,8 @@ in {
|
||||
go-l2tp
|
||||
min-copy-closure
|
||||
fennelrepl
|
||||
lzma
|
||||
lua
|
||||
];
|
||||
};
|
||||
}
|
||||
|
@ -4,15 +4,14 @@
|
||||
******************************
|
||||
|
||||
This device is based on a 64 bit Mediatek MT7622 ARM platform,
|
||||
and is "work in progress" in Liminix.
|
||||
and is mostly feature-complete in Liminix but as of Dec 2024
|
||||
has seen very little actual use.
|
||||
|
||||
.. note:: The factory flash image contains ECC errors that make it
|
||||
incompatible with Liminix: you need to use the `OpenWrt
|
||||
incompatible with Liminix: use the `OpenWrt
|
||||
UBI Installer <https://github.com/dangowrt/owrt-ubi-installer>`_ to
|
||||
rewrite the partition layout before you can flash
|
||||
Liminix onto it (or even use it with
|
||||
:ref:`system-outputs-tftpboot`, if you want the wireless
|
||||
to work).
|
||||
rewrite the partition layout before you can use
|
||||
Liminix with it
|
||||
|
||||
Hardware summary
|
||||
================
|
||||
@ -27,10 +26,76 @@
|
||||
Installation
|
||||
============
|
||||
|
||||
Installation is currently a manual process (you need a :ref:`serial <serial>` conection and
|
||||
TFTP) following the instructions at :ref:`system-outputs-ubimage`
|
||||
Liminix on this device uses the UBI volume management system to
|
||||
perform wear leveling on the flash. This is not set up from the
|
||||
factory, so a one-time step is needed to prepare it before Liminix
|
||||
can be installed.
|
||||
|
||||
'';
|
||||
|
||||
Preparation
|
||||
-----------
|
||||
|
||||
To prepare the device for Liminix you first need to use the
|
||||
`OpenWrt UBI Installer
|
||||
<https://github.com/dangowrt/owrt-ubi-installer>`_ image to
|
||||
rewrite the flash layout. You can do this in one of two ways:
|
||||
either follow the instructions to do it through the vendor web
|
||||
interface, or you can drop to U-boot and use TFTP
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
MT7622> setenv ipaddr 10.0.0.6
|
||||
MT7622> setenv serverip 10.0.0.1
|
||||
MT7622> tftpboot 0x42000000 openwrt-mediatek-mt7622-linksys_e8450-ubi-initramfs-recovery-installer.itb
|
||||
MT7622> bootm 0x42000000
|
||||
|
||||
This will write the new flash layout and then boot into a
|
||||
"recovery" OpenWrt installation.
|
||||
|
||||
Building/installing Liminix
|
||||
----------------
|
||||
|
||||
The default target for this device is ``outputs.ubimage`` which
|
||||
makes a ubifs image suitable for use with :command:`ubiupdatevol`.
|
||||
To write this to the device we use the OpenWrt recovery system
|
||||
installed in the previous step. In this configuration the
|
||||
device assigns itself the IP address 192.168.1.1/24 on its LAN
|
||||
ports and expects the connected computer to have 192.168.1.254
|
||||
|
||||
.. warning:: The `ubi0_7` device in these instructions is correct
|
||||
as of Dec 2024 (dangowrt/owrt-ubi-installer commit
|
||||
d79e7928). If you are installing some time later, it
|
||||
is important to check the output from
|
||||
:command:`ubinfo -a` and make sure you are updating
|
||||
the "liminix" volume and not some other one which had
|
||||
been introduced since I wrote this.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ nix-build -I liminix-config=./my-configuration.nix --arg device "import ./devices/belkin-rt3200" -A outputs.default
|
||||
$ cat result/rootfs | ssh root@192.168.1.1 "cat > /tmp/rootfs"
|
||||
$ ssh root@192.168.1.1
|
||||
root@OpenWrt:~# ubimkvol /dev/ubi0 --name=liminix --maxavsize
|
||||
root@OpenWrt:~# ubinfo -a
|
||||
[...]
|
||||
Volume ID: 7 (on ubi0)
|
||||
Type: dynamic
|
||||
Alignment: 1
|
||||
Size: 851 LEBs (108056576 bytes, 103.0 MiB)
|
||||
State: OK
|
||||
Name: liminix
|
||||
Character device major/minor: 250:8
|
||||
root@OpenWrt:~# ubiupdatevol /dev/ubi0_7 /tmp/rootfs
|
||||
root@OpenWrt:~# fw_setenv orig_boot_production $(fw_printenv boot_production | sed -E 's/.+?=//')
|
||||
root@OpenWrt:~# fw_setenv boot_production 'led $bootled_pwr on ; ubifsmount ubi0:liminix && ubifsload ''${loadaddr} boot/fit && bootm ''${loadaddr}'
|
||||
|
||||
|
||||
For subsequent Liminix reinstalls, you don't need to repeat the
|
||||
"Preparation" step and in fact should seek to avoid it if
|
||||
possible. Updating volumes with :command:`ubiupdatevol` will
|
||||
preserve the erase counters used for write levelling, so is
|
||||
preferred over any kind of "factory" wipe which will reset them.
|
||||
'';
|
||||
|
||||
system = {
|
||||
crossSystem = {
|
||||
@ -55,11 +120,6 @@
|
||||
];
|
||||
config = {
|
||||
kernel = {
|
||||
src = pkgs.pkgsBuildBuild.fetchurl {
|
||||
name = "linux.tar.gz";
|
||||
url = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.71.tar.gz";
|
||||
hash = "sha256-yhO2cXIeIgUxkSZf/4aAsF11uxyh+UUZu6D1h92vCD8=";
|
||||
};
|
||||
extraPatchPhase = ''
|
||||
${pkgs.openwrt.applyPatches.mediatek}
|
||||
'';
|
||||
@ -73,7 +133,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 +152,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
|
||||
@ -133,24 +192,32 @@
|
||||
NET_DSA_MT7530="y";
|
||||
NET_DSA_TAG_MTK="y";
|
||||
|
||||
PSTORE = "y";
|
||||
PSTORE_RAM = "y";
|
||||
PSTORE_CONSOLE = "y";
|
||||
PSTORE_DEFLATE_COMPRESS = "n";
|
||||
|
||||
SERIAL_8250 = "y";
|
||||
SERIAL_8250_CONSOLE = "y";
|
||||
SERIAL_8250_MT6577="y";
|
||||
# SERIAL_8250_NR_UARTS="3";
|
||||
# SERIAL_8250_RUNTIME_UARTS="3";
|
||||
SERIAL_OF_PLATFORM="y";
|
||||
|
||||
# Must enble hardware watchdog drivers. Else the device reboots after several seconds
|
||||
WATCHDOG = "y";
|
||||
MEDIATEK_WATCHDOG = "y";
|
||||
};
|
||||
conditionalConfig = {
|
||||
WLAN= {
|
||||
MT7615E = "m";
|
||||
MT7622_WMAC = "y";
|
||||
MT7915E = "m";
|
||||
};
|
||||
};
|
||||
};
|
||||
boot = {
|
||||
commandLine = [ "console=ttyS0,115200" ];
|
||||
tftp.loadAddress = lim.parseInt "0x4007ff28";
|
||||
imageFormat = "fit";
|
||||
loader.fit.enable = lib.mkDefault true; # override this if you are building tftpboot
|
||||
};
|
||||
rootfsType = lib.mkDefault "ubifs"; # override this if you are building tftpboot
|
||||
filesystem =
|
||||
let inherit (pkgs.pseudofile) dir symlink;
|
||||
in
|
||||
@ -165,17 +232,15 @@
|
||||
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 = {
|
||||
minIOSize = "2048";
|
||||
eraseBlockSize = "126976";
|
||||
logicalEraseBlockSize = "126976";
|
||||
physicalEraseBlockSize = "131072";
|
||||
maxLEBcount = "1024"; # guessing
|
||||
};
|
||||
|
||||
@ -190,7 +255,7 @@
|
||||
rootDevice = "ubi0:liminix";
|
||||
dts = {
|
||||
src = "${openwrt.src}/target/linux/mediatek/dts/mt7622-linksys-e8450-ubi.dts";
|
||||
includes = [
|
||||
includePaths = [
|
||||
"${openwrt.src}/target/linux/mediatek/dts"
|
||||
"${config.system.outputs.kernel.modulesupport}/arch/arm64/boot/dts/mediatek/"
|
||||
];
|
||||
@ -206,7 +271,6 @@
|
||||
networkInterfaces =
|
||||
let
|
||||
inherit (config.system.service.network) link;
|
||||
inherit (config.system.service) bridge;
|
||||
in rec {
|
||||
wan = link.build { ifname = "wan"; };
|
||||
lan1 = link.build { ifname = "lan1"; };
|
||||
|
@ -5,11 +5,6 @@
|
||||
];
|
||||
config = {
|
||||
kernel = {
|
||||
src = pkgs.pkgsBuildBuild.fetchurl {
|
||||
name = "linux.tar.gz";
|
||||
url = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.71.tar.gz";
|
||||
hash = "sha256-yhO2cXIeIgUxkSZf/4aAsF11uxyh+UUZu6D1h92vCD8=";
|
||||
};
|
||||
config = {
|
||||
MTD = "y";
|
||||
MTD_BLOCK = "y";
|
||||
@ -23,12 +18,17 @@
|
||||
VIRTIO_BLK = "y";
|
||||
VIRTIO_NET = "y";
|
||||
};
|
||||
conditionalConfig = {
|
||||
WLAN= {
|
||||
MAC80211_HWSIM = "m";
|
||||
};
|
||||
};
|
||||
};
|
||||
hardware =
|
||||
let
|
||||
mac80211 = pkgs.mac80211.override {
|
||||
drivers = ["mac80211_hwsim"];
|
||||
klibBuild = config.system.outputs.kernel.modulesupport;
|
||||
mac80211 = pkgs.kmodloader.override {
|
||||
inherit (config.system.outputs) kernel;
|
||||
targets = ["mac80211_hwsim"];
|
||||
};
|
||||
in {
|
||||
defaultOutput = "vmroot";
|
||||
|
@ -52,8 +52,9 @@
|
||||
|
||||
'';
|
||||
|
||||
module = {pkgs, config, lim, ... }:
|
||||
module = {pkgs, config, lim, lib, ... }:
|
||||
let
|
||||
inherit (lib) mkIf;
|
||||
openwrt = pkgs.openwrt;
|
||||
firmwareBlobs = pkgs.pkgsBuildBuild.fetchFromGitHub {
|
||||
owner = "kvalo";
|
||||
@ -71,9 +72,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
|
||||
@ -91,7 +93,6 @@
|
||||
'';
|
||||
};
|
||||
inherit (pkgs.pseudofile) dir symlink;
|
||||
inherit (pkgs.liminix.networking) interface;
|
||||
in {
|
||||
imports = [
|
||||
../../modules/network
|
||||
@ -116,23 +117,32 @@
|
||||
rootDevice = "/dev/mtdblock5";
|
||||
dts = {
|
||||
src = "${openwrt.src}/target/linux/ath79/dts/qca9531_glinet_gl-ar750.dts";
|
||||
includes = [
|
||||
includePaths = [
|
||||
"${openwrt.src}/target/linux/ath79/dts"
|
||||
];
|
||||
includes = mkIf config.logging.persistent.enable [
|
||||
./pstore-ramoops.dtsi
|
||||
];
|
||||
};
|
||||
|
||||
networkInterfaces =
|
||||
let inherit (config.system.service.network) link;
|
||||
in {
|
||||
lan = link.build { ifname = "eth0"; };
|
||||
wan = link.build { ifname = "eth1"; };
|
||||
lan = link.build {
|
||||
ifname = "lan";
|
||||
devpath = "/devices/platform/ahb/1a000000.eth";
|
||||
};
|
||||
wan = link.build {
|
||||
ifname = "wan";
|
||||
devpath = "/devices/platform/ahb/19000000.eth";
|
||||
};
|
||||
wlan = link.build {
|
||||
ifname = "wlan0";
|
||||
dependencies = [ mac80211 ];
|
||||
};
|
||||
wlan5 = link.build {
|
||||
ifname = "wlan1";
|
||||
dependencies = [ mac80211 ath10k_cal_data ];
|
||||
dependencies = [ ath10k_cal_data mac80211 ];
|
||||
};
|
||||
};
|
||||
};
|
||||
@ -148,19 +158,16 @@
|
||||
};
|
||||
boot.tftp = {
|
||||
loadAddress = lim.parseInt "0x00A00000";
|
||||
appendDTB = true;
|
||||
};
|
||||
kernel = {
|
||||
src = pkgs.pkgsBuildBuild.fetchurl {
|
||||
name = "linux.tar.gz";
|
||||
url = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.71.tar.gz";
|
||||
hash = "sha256-yhO2cXIeIgUxkSZf/4aAsF11uxyh+UUZu6D1h92vCD8=";
|
||||
};
|
||||
|
||||
# Mainline linux 5.19 doesn't have device-tree support for
|
||||
# this device or even for the SoC, so we use the extensive
|
||||
# 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 +218,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";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
9
devices/gl-ar750/pstore-ramoops.dtsi
Normal file
9
devices/gl-ar750/pstore-ramoops.dtsi
Normal file
@ -0,0 +1,9 @@
|
||||
/ {
|
||||
reserved-memory {
|
||||
ramoops@03f00000 {
|
||||
compatible = "ramoops";
|
||||
reg = <0x03f00000 0x10000>;
|
||||
pmsg-size = <0x10000>;
|
||||
};
|
||||
};
|
||||
};
|
@ -45,11 +45,10 @@
|
||||
|
||||
module = { pkgs, config, lib, lim, ...}:
|
||||
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 = [
|
||||
@ -82,7 +81,7 @@
|
||||
|
||||
dts = {
|
||||
src = "${openwrt.src}/target/linux/ramips/dts/mt7620a_glinet_gl-mt300a.dts";
|
||||
includes = [
|
||||
includePaths = [
|
||||
"${openwrt.src}/target/linux/ramips/dts"
|
||||
];
|
||||
};
|
||||
@ -90,19 +89,6 @@
|
||||
let
|
||||
inherit (config.system.service.network) link;
|
||||
inherit (config.system.service) vlan;
|
||||
inherit (pkgs.liminix.services) oneshot;
|
||||
swconfig = oneshot {
|
||||
name = "swconfig";
|
||||
up = ''
|
||||
PATH=${pkgs.swconfig}/bin:$PATH
|
||||
swconfig dev switch0 set reset
|
||||
swconfig dev switch0 set enable_vlan 1
|
||||
swconfig dev switch0 vlan 1 set ports '1 2 3 4 6t'
|
||||
swconfig dev switch0 vlan 2 set ports '0 6t'
|
||||
swconfig dev switch0 set apply
|
||||
'';
|
||||
down = "${pkgs.swconfig}/bin/swconfig dev switch0 set reset";
|
||||
};
|
||||
in rec {
|
||||
eth = link.build { ifname = "eth0"; };
|
||||
# lan and wan ports are both behind a switch on eth0
|
||||
@ -110,13 +96,11 @@
|
||||
ifname = "eth0.1";
|
||||
primary = eth;
|
||||
vid = "1";
|
||||
dependencies = [swconfig eth];
|
||||
};
|
||||
wan = vlan.build {
|
||||
ifname = "eth0.2";
|
||||
primary = eth;
|
||||
vid = "2";
|
||||
dependencies = [swconfig eth];
|
||||
};
|
||||
wlan = link.build {
|
||||
ifname = "wlan0";
|
||||
@ -126,16 +110,13 @@
|
||||
};
|
||||
boot.tftp = {
|
||||
loadAddress = lim.parseInt "0x00A00000";
|
||||
};
|
||||
appendDTB = true;
|
||||
};
|
||||
|
||||
kernel = {
|
||||
src = pkgs.fetchurl {
|
||||
name = "linux.tar.gz";
|
||||
url = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.71.tar.gz";
|
||||
hash = "sha256-yhO2cXIeIgUxkSZf/4aAsF11uxyh+UUZu6D1h92vCD8=";
|
||||
};
|
||||
extraPatchPhase = ''
|
||||
${openwrt.applyPatches.ramips}
|
||||
${openwrt.applyPatches.rt2x00}
|
||||
'';
|
||||
config = {
|
||||
|
||||
@ -178,6 +159,14 @@
|
||||
} // lib.optionalAttrs (config.system.service ? vlan) {
|
||||
SWCONFIG = "y";
|
||||
};
|
||||
conditionalConfig = {
|
||||
WLAN = {
|
||||
WLAN_VENDOR_RALINK = "y";
|
||||
RT2800SOC = "m";
|
||||
RT2X00 = "m";
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -13,7 +13,7 @@
|
||||
GL.iNet GL-MT300N-v2
|
||||
********************
|
||||
|
||||
The GL-MT300N-v2 "Mango" is is very similar to the :ref:`MT300A <GL.iNet GL-MT300A>, but is
|
||||
The GL-MT300N-v2 "Mango" is is very similar to the :ref:`gl-mt300a`, but is
|
||||
based on the MT7628 chipset instead of MT7620. It's also marginally cheaper
|
||||
and comes in a yellow case not a blue one. Be sure your device is
|
||||
v2 not v1, which is a different animal and has only half as much RAM.
|
||||
@ -38,14 +38,13 @@
|
||||
|
||||
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 = ["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";
|
||||
@ -79,7 +78,7 @@
|
||||
|
||||
dts = {
|
||||
src = "${openwrt.src}/target/linux/ramips/dts/mt7628an_glinet_gl-mt300n-v2.dts";
|
||||
includes = [
|
||||
includePaths = [
|
||||
"${openwrt.src}/target/linux/ramips/dts"
|
||||
];
|
||||
};
|
||||
@ -97,7 +96,7 @@
|
||||
swconfig dev switch0 vlan 2 set ports '0 6t'
|
||||
swconfig dev switch0 set apply
|
||||
'';
|
||||
down = "swconfig dev switch0 set reset";
|
||||
down = "${pkgs.swconfig}/bin/swconfig dev switch0 set reset";
|
||||
};
|
||||
in rec {
|
||||
eth = link.build { ifname = "eth0"; dependencies = [swconfig]; };
|
||||
@ -122,14 +121,10 @@
|
||||
# 20MB seems to give enough room to uncompress the kernel
|
||||
# without anything getting trodden on. 10MB was too small
|
||||
loadAddress = lim.parseInt "0x1400000";
|
||||
appendDTB = true;
|
||||
};
|
||||
|
||||
kernel = {
|
||||
src = pkgs.fetchurl {
|
||||
name = "linux.tar.gz";
|
||||
url = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.71.tar.gz";
|
||||
hash = "sha256-yhO2cXIeIgUxkSZf/4aAsF11uxyh+UUZu6D1h92vCD8=";
|
||||
};
|
||||
extraPatchPhase = ''
|
||||
${openwrt.applyPatches.ramips}
|
||||
'';
|
||||
@ -185,6 +180,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";
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
}
|
||||
|
@ -19,14 +19,14 @@
|
||||
ARM targets differ from MIPS in that the kernel format expected
|
||||
by QEMU is an "Image" (raw binary file) rather than an ELF
|
||||
file, but this is taken care of by :command:`run.sh`. Check the
|
||||
documentation for the :ref:`QEMU` (MIPS) target for more information.
|
||||
documentation for the :ref:`qemu` target for more information.
|
||||
|
||||
'';
|
||||
|
||||
# this device is described by the "qemu" device
|
||||
installer = "vmroot";
|
||||
|
||||
module = {pkgs, config, lim, ... }: {
|
||||
module = { config, lim, ... }: {
|
||||
imports = [
|
||||
../../modules/arch/aarch64.nix
|
||||
../families/qemu.nix
|
||||
|
@ -24,7 +24,7 @@
|
||||
'';
|
||||
installer = "vmroot";
|
||||
|
||||
module = {pkgs, config, lim, ... }: {
|
||||
module = { config, lim, ... }: {
|
||||
imports = [
|
||||
../../modules/arch/arm.nix
|
||||
../families/qemu.nix
|
||||
|
@ -36,7 +36,7 @@
|
||||
in the Development manual.
|
||||
|
||||
'';
|
||||
module = {pkgs, config, lib, lim, ... }: {
|
||||
module = { config, lib, lim, ... }: {
|
||||
imports = [
|
||||
../../modules/arch/mipseb.nix
|
||||
../families/qemu.nix
|
||||
@ -66,7 +66,7 @@
|
||||
# *correct* but it does at least boot
|
||||
dts = lib.mkForce {
|
||||
src = "${config.system.outputs.kernel.modulesupport}/arch/mips/boot/dts/mti/malta.dts";
|
||||
includes = [
|
||||
includePaths = [
|
||||
"${config.system.outputs.kernel.modulesupport}/arch/mips/boot/dts/"
|
||||
];
|
||||
};
|
||||
|
436
devices/tp-archer-ax23/default.nix
Normal file
436
devices/tp-archer-ax23/default.nix
Normal file
@ -0,0 +1,436 @@
|
||||
{
|
||||
description = ''
|
||||
TP-Link Archer AX23 / AX1800 Dual Band Wi-Fi 6 Router
|
||||
*****************************************************
|
||||
|
||||
Hardware summary
|
||||
================
|
||||
|
||||
- MediaTek MT7621 (880MHz)
|
||||
- 16MB Flash
|
||||
- 128MB RAM
|
||||
- WLan hardware: Mediatek MT7905, MT7975
|
||||
|
||||
Limitations
|
||||
===========
|
||||
|
||||
Status LEDs do not work yet.
|
||||
|
||||
Uploading an image via tftp doesn't work yet, because the Archer uboot
|
||||
version is so old it doesn't support overriding the DTB from the mboot
|
||||
command. The tftpboot module doesn't support this yet, see
|
||||
https://gti.telent.net/dan/liminix/pulls/5 for the WiP.
|
||||
'';
|
||||
|
||||
system = {
|
||||
crossSystem = {
|
||||
config = "mipsel-unknown-linux-musl";
|
||||
gcc = {
|
||||
abi = "32";
|
||||
# https://openwrt.org/docs/techref/instructionset/mipsel_24kc
|
||||
arch = "24kc";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
module = {pkgs, config, lib, lim, ... }:
|
||||
let firmware = pkgs.stdenv.mkDerivation {
|
||||
name = "wlan-firmware";
|
||||
phases = ["installPhase"];
|
||||
installPhase = ''
|
||||
mkdir $out
|
||||
cp ${pkgs.linux-firmware}/lib/firmware/mediatek/{mt7915,mt7615,mt7622}* $out
|
||||
'';
|
||||
};
|
||||
in {
|
||||
imports = [
|
||||
../../modules/arch/mipsel.nix
|
||||
../../modules/outputs/tftpboot.nix
|
||||
../../modules/outputs/tplink-safeloader.nix
|
||||
];
|
||||
config = {
|
||||
kernel = {
|
||||
extraPatchPhase = ''
|
||||
${pkgs.openwrt.applyPatches.ramips}
|
||||
'';
|
||||
config = {
|
||||
# Initially taken from openwrt's ./target/linux/ramips/mt7621/config-5.15,
|
||||
# then tweaked here and there
|
||||
ARCH_32BIT_OFF_T="y";
|
||||
ARCH_HIBERNATION_POSSIBLE="y";
|
||||
ARCH_KEEP_MEMBLOCK="y";
|
||||
ARCH_MMAP_RND_BITS_MAX="15";
|
||||
ARCH_MMAP_RND_COMPAT_BITS_MAX="15";
|
||||
ARCH_SUSPEND_POSSIBLE="y";
|
||||
AT803X_PHY="y";
|
||||
BLK_MQ_PCI="y";
|
||||
BOARD_SCACHE="y";
|
||||
CEVT_R4K="y";
|
||||
CLKSRC_MIPS_GIC="y";
|
||||
CLK_MT7621="y";
|
||||
CLOCKSOURCE_WATCHDOG="y";
|
||||
CLONE_BACKWARDS="y";
|
||||
CMDLINE_BOOL="y";
|
||||
COMMON_CLK="y";
|
||||
COMPAT_32BIT_TIME="y";
|
||||
CPU_GENERIC_DUMP_TLB="y";
|
||||
CPU_HAS_DIEI="y";
|
||||
CPU_HAS_PREFETCH="y";
|
||||
CPU_HAS_RIXI="y";
|
||||
CPU_HAS_SYNC="y";
|
||||
CPU_LITTLE_ENDIAN="y";
|
||||
CPU_MIPS32="y";
|
||||
CPU_MIPS32_R2="y";
|
||||
CPU_MIPSR2="y";
|
||||
CPU_MIPSR2_IRQ_EI="y";
|
||||
CPU_MIPSR2_IRQ_VI="y";
|
||||
CPU_NEEDS_NO_SMARTMIPS_OR_MICROMIPS="y";
|
||||
CPU_R4K_CACHE_TLB="y";
|
||||
CPU_RMAP="y";
|
||||
CPU_SUPPORTS_32BIT_KERNEL="y";
|
||||
CPU_SUPPORTS_HIGHMEM="y";
|
||||
CPU_SUPPORTS_MSA="y";
|
||||
CRC16="y";
|
||||
CRYPTO_DEFLATE="y";
|
||||
CRYPTO_HASH_INFO="y";
|
||||
CRYPTO_LIB_BLAKE2S_GENERIC="y";
|
||||
CRYPTO_LIB_POLY1305_RSIZE="2";
|
||||
CRYPTO_LZO="y";
|
||||
CRYPTO_ZSTD="y";
|
||||
CSRC_R4K="y";
|
||||
DIMLIB="y";
|
||||
DMA_NONCOHERENT="y";
|
||||
DTB_RT_NONE="y";
|
||||
DTC="y";
|
||||
EARLY_PRINTK="y";
|
||||
FIXED_PHY="y";
|
||||
FWNODE_MDIO="y";
|
||||
FW_LOADER_PAGED_BUF="y";
|
||||
GENERIC_ATOMIC64="y";
|
||||
GENERIC_CLOCKEVENTS="y";
|
||||
GENERIC_CMOS_UPDATE="y";
|
||||
GENERIC_CPU_AUTOPROBE="y";
|
||||
GENERIC_FIND_FIRST_BIT="y";
|
||||
GENERIC_GETTIMEOFDAY="y";
|
||||
GENERIC_IOMAP="y";
|
||||
GENERIC_IRQ_CHIP="y";
|
||||
GENERIC_IRQ_EFFECTIVE_AFF_MASK="y";
|
||||
GENERIC_IRQ_SHOW="y";
|
||||
GENERIC_LIB_ASHLDI3="y";
|
||||
GENERIC_LIB_ASHRDI3="y";
|
||||
GENERIC_LIB_CMPDI2="y";
|
||||
GENERIC_LIB_LSHRDI3="y";
|
||||
GENERIC_LIB_UCMPDI2="y";
|
||||
GENERIC_PCI_IOMAP="y";
|
||||
GENERIC_PHY="y";
|
||||
GENERIC_PINCONF="y";
|
||||
GENERIC_SCHED_CLOCK="y";
|
||||
GENERIC_SMP_IDLE_THREAD="y";
|
||||
GENERIC_TIME_VSYSCALL="y";
|
||||
GLOB="y";
|
||||
GPIOLIB_IRQCHIP="y";
|
||||
GPIO_CDEV="y";
|
||||
GPIO_GENERIC="y";
|
||||
GPIO_MT7621="y";
|
||||
GRO_CELLS="y";
|
||||
HANDLE_DOMAIN_IRQ="y";
|
||||
HARDWARE_WATCHPOINTS="y";
|
||||
HAS_DMA="y";
|
||||
HAS_IOMEM="y";
|
||||
HAS_IOPORT_MAP="y";
|
||||
I2C="y";
|
||||
I2C_ALGOBIT="y";
|
||||
I2C_BOARDINFO="y";
|
||||
I2C_CHARDEV="y";
|
||||
I2C_GPIO="y";
|
||||
I2C_MT7621="y";
|
||||
ICPLUS_PHY="y";
|
||||
IRQCHIP="y";
|
||||
IRQ_DOMAIN="y";
|
||||
IRQ_DOMAIN_HIERARCHY="y";
|
||||
IRQ_FORCED_THREADING="y";
|
||||
IRQ_MIPS_CPU="y";
|
||||
IRQ_WORK="y";
|
||||
LIBFDT="y";
|
||||
LOCK_DEBUGGING_SUPPORT="y";
|
||||
LZO_COMPRESS="y";
|
||||
LZO_DECOMPRESS="y";
|
||||
MDIO_BUS="y";
|
||||
MDIO_DEVICE="y";
|
||||
MDIO_DEVRES="y";
|
||||
MEDIATEK_GE_PHY="y";
|
||||
MEMFD_CREATE="y";
|
||||
MFD_SYSCON="y";
|
||||
MIGRATION="y";
|
||||
MIKROTIK="y";
|
||||
MIKROTIK_RB_SYSFS="y";
|
||||
MIPS="y";
|
||||
MIPS_ASID_BITS="8";
|
||||
MIPS_ASID_SHIFT="0";
|
||||
MIPS_CLOCK_VSYSCALL="y";
|
||||
MIPS_CM="y";
|
||||
MIPS_CPC="y";
|
||||
MIPS_CPS="y";
|
||||
MIPS_CPU_SCACHE="y";
|
||||
MIPS_GIC="y";
|
||||
MIPS_L1_CACHE_SHIFT="5";
|
||||
MIPS_LD_CAN_LINK_VDSO="y";
|
||||
MIPS_MT="y";
|
||||
MIPS_MT_FPAFF="y";
|
||||
MIPS_MT_SMP="y";
|
||||
MIPS_NR_CPU_NR_MAP="4";
|
||||
MIPS_PERF_SHARED_TC_COUNTERS="y";
|
||||
MIPS_SPRAM="y";
|
||||
MODULES_USE_ELF_REL="y";
|
||||
MTD_CMDLINE_PARTS="y";
|
||||
MTD_NAND_CORE="y";
|
||||
MTD_NAND_ECC="y";
|
||||
MTD_NAND_ECC_SW_HAMMING="y";
|
||||
MTD_NAND_MT7621="y";
|
||||
MTD_NAND_MTK_BMT="y";
|
||||
MTD_RAW_NAND="y";
|
||||
MTD_ROUTERBOOT_PARTS="y";
|
||||
MTD_SERCOMM_PARTS="y";
|
||||
MTD_SPI_NOR="y";
|
||||
MTD_SPLIT_FIT_FW="y";
|
||||
MTD_SPLIT_MINOR_FW="y";
|
||||
MTD_SPLIT_SEAMA_FW="y";
|
||||
MTD_SPLIT_TPLINK_FW="y";
|
||||
MTD_SPLIT_TRX_FW="y";
|
||||
MTD_SPLIT_UIMAGE_FW="y";
|
||||
MTD_UBI="y";
|
||||
MTD_UBI_BEB_LIMIT="20";
|
||||
MTD_UBI_BLOCK="y";
|
||||
MTD_UBI_WL_THRESHOLD="4096";
|
||||
MTD_VIRT_CONCAT="y";
|
||||
NEED_DMA_MAP_STATE="y";
|
||||
NET_DEVLINK="y";
|
||||
NET_DSA="y";
|
||||
NET_DSA_MT7530="y";
|
||||
NET_DSA_MT7530_MDIO="y";
|
||||
NET_DSA_TAG_MTK="y";
|
||||
NET_FLOW_LIMIT="y";
|
||||
NET_MEDIATEK_SOC="y";
|
||||
NET_SELFTESTS="y";
|
||||
NET_SWITCHDEV="y";
|
||||
NET_VENDOR_MEDIATEK="y";
|
||||
NO_HZ_COMMON="y";
|
||||
NO_HZ_IDLE="y";
|
||||
NR_CPUS="4";
|
||||
NVMEM="y";
|
||||
OF="y";
|
||||
OF_ADDRESS="y";
|
||||
OF_EARLY_FLATTREE="y";
|
||||
OF_FLATTREE="y";
|
||||
OF_GPIO="y";
|
||||
OF_IRQ="y";
|
||||
OF_KOBJ="y";
|
||||
OF_MDIO="y";
|
||||
PAGE_POOL="y";
|
||||
PAGE_POOL_STATS="y";
|
||||
PCI="y";
|
||||
PCIE_MT7621="y";
|
||||
PCI_DISABLE_COMMON_QUIRKS="y";
|
||||
PCI_DOMAINS="y";
|
||||
PCI_DOMAINS_GENERIC="y";
|
||||
PCI_DRIVERS_GENERIC="y";
|
||||
PCS_MTK_LYNXI="y";
|
||||
PERF_USE_VMALLOC="y";
|
||||
PGTABLE_LEVELS="2";
|
||||
PHYLIB="y";
|
||||
PHYLINK="y";
|
||||
PHY_MT7621_PCI="y";
|
||||
PINCTRL="y";
|
||||
PINCTRL_AW9523="y";
|
||||
PINCTRL_MT7621="y";
|
||||
PINCTRL_RALINK="y";
|
||||
PINCTRL_SX150X="y";
|
||||
POWER_RESET="y";
|
||||
POWER_RESET_GPIO="y";
|
||||
POWER_SUPPLY="y";
|
||||
PTP_1588_CLOCK_OPTIONAL="y";
|
||||
QUEUED_RWLOCKS="y";
|
||||
QUEUED_SPINLOCKS="y";
|
||||
RALINK="y";
|
||||
RATIONAL="y";
|
||||
REGMAP="y";
|
||||
REGMAP_I2C="y";
|
||||
REGMAP_MMIO="y";
|
||||
REGULATOR="y";
|
||||
REGULATOR_FIXED_VOLTAGE="y";
|
||||
RESET_CONTROLLER="y";
|
||||
RFS_ACCEL="y";
|
||||
RPS="y";
|
||||
RTC_CLASS="y";
|
||||
RTC_DRV_BQ32K="y";
|
||||
RTC_DRV_PCF8563="y";
|
||||
RTC_I2C_AND_SPI="y";
|
||||
SCHED_SMT="y";
|
||||
SERIAL_8250="y";
|
||||
SERIAL_8250_CONSOLE="y";
|
||||
SERIAL_8250_NR_UARTS="3";
|
||||
SERIAL_8250_RUNTIME_UARTS="3";
|
||||
SERIAL_MCTRL_GPIO="y";
|
||||
SERIAL_OF_PLATFORM="y";
|
||||
SGL_ALLOC="y";
|
||||
SMP="y";
|
||||
SMP_UP="y";
|
||||
SOCK_RX_QUEUE_MAPPING="y";
|
||||
SOC_BUS="y";
|
||||
SOC_MT7621="y";
|
||||
SPI="y";
|
||||
SPI_MASTER="y";
|
||||
SPI_MEM="y";
|
||||
SPI_MT7621="y";
|
||||
SRCU="y";
|
||||
SWPHY="y";
|
||||
SYNC_R4K="y";
|
||||
SYSCTL_EXCEPTION_TRACE="y";
|
||||
SYS_HAS_CPU_MIPS32_R1="y";
|
||||
SYS_HAS_CPU_MIPS32_R2="y";
|
||||
SYS_HAS_EARLY_PRINTK="y";
|
||||
SYS_SUPPORTS_32BIT_KERNEL="y";
|
||||
SYS_SUPPORTS_ARBIT_HZ="y";
|
||||
SYS_SUPPORTS_HIGHMEM="y";
|
||||
SYS_SUPPORTS_HOTPLUG_CPU="y";
|
||||
SYS_SUPPORTS_LITTLE_ENDIAN="y";
|
||||
SYS_SUPPORTS_MIPS16="y";
|
||||
SYS_SUPPORTS_MIPS_CPS="y";
|
||||
SYS_SUPPORTS_MULTITHREADING="y";
|
||||
SYS_SUPPORTS_SCHED_SMT="y";
|
||||
SYS_SUPPORTS_SMP="y";
|
||||
SYS_SUPPORTS_ZBOOT="y";
|
||||
TARGET_ISA_REV="2";
|
||||
TICK_CPU_ACCOUNTING="y";
|
||||
TIMER_OF="y";
|
||||
TIMER_PROBE="y";
|
||||
TREE_RCU="y";
|
||||
TREE_SRCU="y";
|
||||
UBIFS_FS="y";
|
||||
USB_SUPPORT="y";
|
||||
USE_OF="y";
|
||||
WEAK_ORDERING="y";
|
||||
XPS="y";
|
||||
XXHASH="y";
|
||||
ZLIB_DEFLATE="y";
|
||||
ZLIB_INFLATE="y";
|
||||
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
|
||||
};
|
||||
conditionalConfig = {
|
||||
WLAN = {
|
||||
MT7915E = "m";
|
||||
};
|
||||
};
|
||||
};
|
||||
tplink-safeloader.board = "ARCHER-AX23-V1";
|
||||
boot = {
|
||||
commandLine = [ "console=ttyS0,115200" ];
|
||||
tftp = {
|
||||
# Should be a segment of free RAM, where the tftp artifact
|
||||
# can be stored before unpacking it to the 'hardware.loadAddress'
|
||||
# The 'hardware.loadAddress' is 0x80001000, which suggests the
|
||||
# RAM would start at 0x8000000 and (being 128MB) go to
|
||||
# to 0x8800000. Let's put it at the 100MB mark at
|
||||
# 0x8000000+0x0640000=0x86400000
|
||||
loadAddress = lim.parseInt "0x86400000";
|
||||
};
|
||||
};
|
||||
filesystem =
|
||||
let inherit (pkgs.pseudofile) dir symlink;
|
||||
in
|
||||
dir {
|
||||
lib = dir {
|
||||
firmware = dir {
|
||||
mediatek = symlink firmware;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
hardware =
|
||||
let
|
||||
openwrt = pkgs.openwrt;
|
||||
mac80211 = pkgs.kmodloader.override {
|
||||
targets = [
|
||||
"mt7915e"
|
||||
];
|
||||
inherit (config.system.outputs) kernel;
|
||||
};
|
||||
in {
|
||||
# from OEM bootlog (openwrt wiki):
|
||||
# 4 cmdlinepart partitions found on MTD device raspi
|
||||
# Creating 4 MTD partitions on "raspi":
|
||||
# 0x000000000000-0x000000040000 : "uboot"
|
||||
# 0x000000040000-0x000000440000 : "uImage"
|
||||
# 0x000000440000-0x000000ff0000 : "rootfs"
|
||||
# 0x000000ff0000-0x000001000000 : "ART"
|
||||
# from openwrt bootlog (openwrt wiki):
|
||||
# 5 fixed-partitions partitions found on MTD device spi0.0
|
||||
# OF: Bad cell count for /palmbus@1e000000/spi@b00/flash@0/partitions
|
||||
# OF: Bad cell count for /palmbus@1e000000/spi@b00/flash@0/partitions
|
||||
# OF: Bad cell count for /palmbus@1e000000/spi@b00/flash@0/partitions
|
||||
# OF: Bad cell count for /palmbus@1e000000/spi@b00/flash@0/partitions
|
||||
# Creating 5 MTD partitions on "spi0.0":
|
||||
# 0x000000000000-0x000000040000 : "u-boot"
|
||||
# 0x000000040000-0x000000fa0000 : "firmware"
|
||||
# 2 uimage-fw partitions found on MTD device firmware
|
||||
# Creating 2 MTD partitions on "firmware":
|
||||
# 0x000000000000-0x0000002c0000 : "kernel"
|
||||
# 0x0000002c0000-0x000000f60000 : "rootfs"
|
||||
# mtd: setting mtd3 (rootfs) as root device
|
||||
# 1 squashfs-split partitions found on MTD device rootfs
|
||||
# 0x000000640000-0x000000f60000 : "rootfs_data"
|
||||
# 0x000000fa0000-0x000000fb0000 : "config"
|
||||
# 0x000000fb0000-0x000000ff0000 : "tplink"
|
||||
# 0x000000ff0000-0x000001000000 : "radio"
|
||||
flash = {
|
||||
# from the OEM bootlog 'Booting image at bc040000'
|
||||
# (0x40000 from 0xbc000000)
|
||||
address = lim.parseInt "0xbc040000";
|
||||
# 0x000000040000-0x000000fa0000
|
||||
size = lim.parseInt "0xf60000";
|
||||
# TODO: find in /proc/mtd on a running system
|
||||
eraseBlockSize = 65536;
|
||||
};
|
||||
|
||||
# since this is mentioned in the partition table as well?
|
||||
defaultOutput = "tplink-safeloader";
|
||||
# taken from openwrt sysupgrade image:
|
||||
# openwrt-23.05.2-ramips-mt7621-tplink_archer-ax23-v1-squashfs-sysupgrade.bin: u-boot legacy uImage, MIPS OpenWrt Linux-5.15.137, Linux/MIPS, OS Kernel Image (lzma), 2797386 bytes, Tue Nov 14 13:38:11 2023, Load Address: 0X80001000, Entry Point: 0X80001000, Header CRC: 0X19F74C5B, Data CRC: 0XF685563C
|
||||
loadAddress = lim.parseInt "0x80001000";
|
||||
entryPoint = lim.parseInt "0x80001000";
|
||||
rootDevice = "/dev/mtdblock3";
|
||||
dts = {
|
||||
src = "${openwrt.src}/target/linux/ramips/dts/mt7621_tplink_archer-ax23-v1.dts";
|
||||
includePaths = [
|
||||
"${openwrt.src}/target/linux/ramips/dts"
|
||||
"${config.system.outputs.kernel.modulesupport}/arch/arm64/boot/dts/mediatek/"
|
||||
];
|
||||
};
|
||||
|
||||
networkInterfaces =
|
||||
let
|
||||
inherit (config.system.service.network) link;
|
||||
in rec {
|
||||
lan1 = link.build { ifname = "lan1"; };
|
||||
lan2 = link.build { ifname = "lan2"; };
|
||||
lan3 = link.build { ifname = "lan3"; };
|
||||
lan4 = link.build { ifname = "lan4"; };
|
||||
wan = link.build { ifname = "wan"; };
|
||||
|
||||
wlan = link.build {
|
||||
ifname = "wlan0";
|
||||
dependencies = [ mac80211 ];
|
||||
};
|
||||
wlan5 = link.build {
|
||||
ifname = "wlan1";
|
||||
dependencies = [ mac80211 ];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
@ -2,6 +2,149 @@
|
||||
description = ''
|
||||
Turris Omnia
|
||||
************
|
||||
|
||||
This is a 32 bit ARMv7 MVEBU device, which is usually shipped with
|
||||
TurrisOS, an OpenWrt-based system. Rather than reformatting the
|
||||
builtin storage, we install Liminix on to the existing btrfs
|
||||
filesystem so that the vendor snapshot/recovery system continues
|
||||
to work (and provides you an easy rollback if you decide you don't
|
||||
like Liminix after all).
|
||||
|
||||
The install process has two stages, and is intended that you
|
||||
should not need to open the device and add a serial console
|
||||
(although it may be handy for visibility, and in case anything
|
||||
goes wrong). First we build a minimal installation/recovery
|
||||
system, then we reboot into that recovery image to prepare the
|
||||
device for the full target install.
|
||||
|
||||
Installation using a USB stick
|
||||
==============================
|
||||
|
||||
First, build the image for the USB stick. Review
|
||||
:file:`examples/recovery.nix` in order to change the default
|
||||
root password (which is ``secret``) and/or the SSH keys, then
|
||||
build it with
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ nix-build -I liminix-config=./examples/recovery.nix \
|
||||
--arg device "import ./devices/turris-omnia" \
|
||||
-A outputs.mbrimage -o mbrimage
|
||||
$ file -L mbrimage
|
||||
mbrimage: DOS/MBR boot sector; partition 1 : ID=0x83, active, start-CHS (0x0,0,5), end-CHS (0x6,130,26), startsector 4, 104602 sectors
|
||||
|
||||
Next, copy the image from your build machine to a USB storage
|
||||
medium using :command:`dd` or your other most favoured file copying
|
||||
tool, which might be a comand something like this:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ dd if=mbrimage of=/dev/path/to/the/usb/stick \
|
||||
bs=1M conv=fdatasync status=progress
|
||||
|
||||
The Omnia's default boot order only checks USB after it has failed
|
||||
to boot from eMMC, which is not ideal for our purpose. Unless you
|
||||
have a serial cable, the easiest way to change this is by booting
|
||||
to TurrisOS and logging in with ssh:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
root@turris:/# fw_printenv boot_targets
|
||||
boot_targets=mmc0 nvme0 scsi0 usb0 pxe dhcp
|
||||
root@turris:/# fw_setenv boot_targets usb0 mmc0
|
||||
root@turris:/# fw_printenv boot_targets
|
||||
boot_targets=usb0 mmc0
|
||||
root@turris:/# reboot -f
|
||||
|
||||
It should now boot into the recovery image. It expects a network
|
||||
cable to be plugged into LAN2 with something on the other end of
|
||||
it that serves DHCP requests. Check your DHCP server logs for a
|
||||
request from a ``liminix-recovery`` host and figure out what IP
|
||||
address was assigned.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ ssh liminix-recovery.lan
|
||||
|
||||
You should get a "Busybox" banner and a root prompt. Now you can
|
||||
start preparing the device to install Liminix on it. First we'll
|
||||
mount the root filesystem and take a snapshot:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# mkdir /dest && mount /dev/mmcblk0p1 /dest
|
||||
# schnapps -d /dest create "pre liminix"
|
||||
# schnapps -d /dest list
|
||||
ERROR: not a valid btrfs filesystem: /
|
||||
# | Type | Size | Date | Description
|
||||
------+-----------+-------------+---------------------------+------------------------------------
|
||||
1 | single | 16.00KiB | 1970-01-01 00:11:49 +0000 | pre liminix
|
||||
|
||||
(``not a valid btrfs filesystem: /`` is not a real error)
|
||||
|
||||
then we can remove all the files
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# rm -r /dest/@/*
|
||||
|
||||
and then it's ready to install the real Liminix system onto. On
|
||||
your build system, create the Liminix configuration you wish to
|
||||
install: here we'll use the ``rotuer`` example.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
build$ nix-build -I liminix-config=./examples/rotuer.nix \
|
||||
--arg device "import ./devices/turris-omnia" \
|
||||
-A outputs.systemConfiguration
|
||||
|
||||
and then use :command:`min-copy-closure` to copy it to the device.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
build$ nix-shell --run \
|
||||
"min-copy-closure -r /dest/@ root@liminix-recovery.lan result"
|
||||
|
||||
and activate it
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
build$ ssh root@liminix-recovery.lan \
|
||||
"/dest/@/$(readlink result)/bin/install /dest/@"
|
||||
|
||||
The final steps are performed directly on the device again: add
|
||||
a symlink so U-Boot can find :file:`/boot`, then restore the
|
||||
default boot order and reboot into the new configuration.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# cd /dest && ln -s @/boot .
|
||||
# fw_setenv boot_targets "mmc0 nvme0 scsi0 usb0 pxe dhcp"
|
||||
# cd / ; umount /dest
|
||||
# reboot
|
||||
|
||||
|
||||
Installation using a TFTP server and serial console
|
||||
===================================================
|
||||
|
||||
If you have a :ref:`serial` console connection and a TFTP server,
|
||||
and would rather use them than fiddling with USB sticks, the
|
||||
:file:`examples/recovery.nix` configuration also works
|
||||
using the ``tftpboot`` output. So you can do
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
build$ nix-build -I liminix-config=./examples/recovery.nix \
|
||||
--arg device "import ./devices/turris-omnia" \
|
||||
-A outputs.tftpboot
|
||||
|
||||
and then paste the generated :file:`result/boot.scr` into
|
||||
U-Boot, and you will end up with the same system as you would
|
||||
have had after booting from USB. If you don't have a serial
|
||||
console connection you could probably even get clever with
|
||||
elaborate use of :command:`fw_setenv`, but that is left as
|
||||
an exercise for the reader.
|
||||
|
||||
'';
|
||||
|
||||
system = {
|
||||
@ -12,8 +155,6 @@
|
||||
|
||||
module = {pkgs, config, lib, lim, ... }:
|
||||
let
|
||||
openwrt = pkgs.openwrt;
|
||||
inherit (lib) mkOption types;
|
||||
inherit (pkgs.liminix.services) oneshot;
|
||||
inherit (pkgs) liminix;
|
||||
mtd_by_name_links = pkgs.liminix.services.oneshot rec {
|
||||
@ -30,9 +171,7 @@
|
||||
imports = [
|
||||
../../modules/arch/arm.nix
|
||||
../../modules/outputs/tftpboot.nix
|
||||
../../modules/outputs/ext4fs.nix
|
||||
../../modules/outputs/mbrimage.nix
|
||||
../../modules/outputs/extlinux.nix
|
||||
];
|
||||
|
||||
config = {
|
||||
@ -40,12 +179,10 @@
|
||||
kernel = {
|
||||
src = pkgs.pkgsBuildBuild.fetchurl {
|
||||
name = "linux.tar.gz";
|
||||
url = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.71.tar.gz";
|
||||
hash = "sha256-yhO2cXIeIgUxkSZf/4aAsF11uxyh+UUZu6D1h92vCD8=";
|
||||
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";
|
||||
@ -57,11 +194,27 @@
|
||||
ARCH_MULTI_V7= "y";
|
||||
PCI_MVEBU = "y";
|
||||
AHCI_MVEBU = "y";
|
||||
|
||||
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
|
||||
|
||||
I2C = "y";
|
||||
I2C_MUX = "y";
|
||||
I2C_MUX_PCA954x = "y";
|
||||
|
||||
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";
|
||||
@ -70,11 +223,6 @@
|
||||
# WARNING: unmet direct dependencies detected for ARCH_WANT_LIBATA_LEDS
|
||||
ATA = "y";
|
||||
|
||||
PSTORE = "y";
|
||||
PSTORE_RAM = "y";
|
||||
PSTORE_CONSOLE = "y";
|
||||
PSTORE_DEFLATE_COMPRESS = "n";
|
||||
|
||||
BLOCK = "y";
|
||||
MMC="y";
|
||||
PWRSEQ_EMMC="y"; # ???
|
||||
@ -110,6 +258,7 @@
|
||||
MVNETA_BM_ENABLE = "y";
|
||||
SRAM = "y"; # mmio-sram is "compatible" for bm_bppi reqd by BM
|
||||
PHY_MVEBU_A38X_COMPHY = "y"; # for eth2
|
||||
MARVELL_PHY = "y";
|
||||
|
||||
MVPP2 = "y";
|
||||
MV_XOR = "y";
|
||||
@ -126,14 +275,27 @@
|
||||
NET_DSA = "y";
|
||||
NET_DSA_MV88E6XXX = "y"; # depends on PTP_1588_CLOCK_OPTIONAL
|
||||
};
|
||||
conditionalConfig = {
|
||||
USB = {
|
||||
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";
|
||||
};
|
||||
};
|
||||
};
|
||||
rootfsType = "ext4";
|
||||
boot = {
|
||||
commandLine = [
|
||||
"console=ttyS0,115200"
|
||||
"pcie_aspm=off" # ath9k pci incompatible with PCIe ASPM
|
||||
];
|
||||
imageFormat = "fit";
|
||||
};
|
||||
filesystem =
|
||||
let
|
||||
@ -142,9 +304,9 @@
|
||||
name = "wlan-firmware";
|
||||
phases = ["installPhase"];
|
||||
installPhase = ''
|
||||
mkdir $out
|
||||
cp -r ${pkgs.linux-firmware}/lib/firmware/ath10k/QCA988X $out
|
||||
'';
|
||||
mkdir $out
|
||||
cp -r ${pkgs.linux-firmware}/lib/firmware/ath10k/QCA988X $out
|
||||
'';
|
||||
};
|
||||
in dir {
|
||||
lib = dir {
|
||||
@ -162,33 +324,32 @@
|
||||
};
|
||||
|
||||
boot.tftp = {
|
||||
loadAddress = lim.parseInt "0x1000000";
|
||||
loadAddress = lim.parseInt "0x1700000";
|
||||
kernelFormat = "zimage";
|
||||
compressRoot = true;
|
||||
};
|
||||
|
||||
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";
|
||||
defaultOutput = "updater";
|
||||
loadAddress = lim.parseInt "0x00800000"; # "0x00008000";
|
||||
entryPoint = lim.parseInt "0x00800000"; # "0x00008000";
|
||||
rootDevice = "/dev/mtdblock0";
|
||||
rootDevice = "/dev/mmcblk0p1";
|
||||
|
||||
dts = {
|
||||
src = "${config.system.outputs.kernel.modulesupport}/arch/arm/boot/dts/armada-385-turris-omnia.dts";
|
||||
includes = [
|
||||
"${config.system.outputs.kernel.modulesupport}/arch/arm/boot/dts/"
|
||||
src = "${config.system.outputs.kernel.modulesupport}/arch/arm/boot/dts/marvell/armada-385-turris-omnia.dts";
|
||||
includePaths = [
|
||||
"${config.system.outputs.kernel.modulesupport}/arch/arm/boot/dts/marvell/"
|
||||
];
|
||||
};
|
||||
flash.eraseBlockSize = 65536; # only used for tftpboot
|
||||
networkInterfaces =
|
||||
let
|
||||
inherit (config.system.service.network) link;
|
||||
inherit (config.system.service) bridge;
|
||||
in rec {
|
||||
en70000 = link.build {
|
||||
# in armada-38x.dtsi this is eth0.
|
||||
@ -210,7 +371,7 @@
|
||||
# per
|
||||
# https://www.kernel.org/doc/html/latest/networking/dsa/configuration.html#affinity-of-user-ports-to-cpu-ports
|
||||
# but apparently OpenWrt doesn't either so maybe it's more
|
||||
# complicated than it looks
|
||||
# complicated than it looks.
|
||||
|
||||
wan = link.build {
|
||||
# in armada-38x.dtsi this is eth2. It may be connected to
|
||||
@ -219,9 +380,13 @@
|
||||
ifname = "wan";
|
||||
};
|
||||
|
||||
lan = link.build {
|
||||
ifname = "lan1";
|
||||
};
|
||||
lan0 = link.build { ifname = "lan0"; };
|
||||
lan1 = link.build { ifname = "lan1"; };
|
||||
lan2 = link.build { ifname = "lan2"; };
|
||||
lan3 = link.build { ifname = "lan3"; };
|
||||
lan4 = link.build { ifname = "lan4"; };
|
||||
lan5 = link.build { ifname = "lan5"; };
|
||||
lan = lan0; # maybe we should build a bridge?
|
||||
|
||||
wlan = link.build {
|
||||
ifname = "wlan0";
|
||||
|
155
devices/zyxel-nwa50ax/a_image/mt7621_zyxel_nwa-ax-for-ab.dtsi
Normal file
155
devices/zyxel-nwa50ax/a_image/mt7621_zyxel_nwa-ax-for-ab.dtsi
Normal 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";
|
||||
};
|
||||
};
|
155
devices/zyxel-nwa50ax/b_image/mt7621_zyxel_nwa-ax-for-ab.dtsi
Normal file
155
devices/zyxel-nwa50ax/b_image/mt7621_zyxel_nwa-ax-for-ab.dtsi
Normal 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";
|
||||
};
|
||||
};
|
359
devices/zyxel-nwa50ax/default.nix
Normal file
359
devices/zyxel-nwa50ax/default.nix
Normal file
@ -0,0 +1,359 @@
|
||||
{
|
||||
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.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;
|
||||
includePaths = [
|
||||
# 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 = {
|
||||
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
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
56
devices/zyxel-nwa50ax/mt7621_zyxel_nwa50ax.dtsi
Normal file
56
devices/zyxel-nwa50ax/mt7621_zyxel_nwa50ax.dtsi
Normal 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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
ðernet {
|
||||
pinctrl-0 = <&mdio_pins>, <&rgmii1_pins>;
|
||||
};
|
||||
|
||||
&state_default {
|
||||
gpio {
|
||||
groups = "uart3", "rgmii2";
|
||||
function = "gpio";
|
||||
};
|
||||
};
|
@ -12,9 +12,13 @@ BUILDDIR = _build
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
hardware.rst: hardware.nix
|
||||
@rm -f hardware.rst || true
|
||||
@cp $$(nix-build hardware.nix) hardware.rst
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
html: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
436
doc/admin.rst
436
doc/admin.rst
@ -4,181 +4,192 @@ System Administration
|
||||
Services on a running system
|
||||
****************************
|
||||
|
||||
* add an s6-rc cheatsheet here
|
||||
Liminix services are built on s6-rc, which is itself layered on s6.
|
||||
Services are defined at build time in your configuration (see
|
||||
:ref:`configuration-services` for information) and can't be added
|
||||
to/changed at runtime, but to monitor
|
||||
events or diagnose problems you may need to inspect them on the
|
||||
running system. Here are some of the most commonly used s6,-rc
|
||||
commands:
|
||||
|
||||
.. list-table:: Service management quick reference
|
||||
:widths: 55 45
|
||||
:header-rows: 1
|
||||
|
||||
* - What
|
||||
- How
|
||||
* - List all running services
|
||||
- ``s6-rc -a list``
|
||||
* - List all services that are **not** running
|
||||
- ``s6-rc -da list``
|
||||
* - List services that ``wombat`` depends on
|
||||
- ``s6-rc-db dependencies wombat``
|
||||
* - ... transitively
|
||||
- ``s6-rc-db all-dependencies wombat``
|
||||
* - List services that depend on service ``wombat``
|
||||
- ``s6-rc-db -d dependencies wombat``
|
||||
* - ... transitively
|
||||
- ``s6-rc-db -d all-dependencies wombat``
|
||||
* - Stop service ``wombat`` and everything depending on it
|
||||
- ``s6-rc -d change wombat``
|
||||
* - Start service ``wombat`` (but not any services depending on it)
|
||||
- ``s6-rc -u change wombat``
|
||||
* - Start service ``wombat`` and all* services depending on it
|
||||
- ``s6-rc-up-tree wombat``
|
||||
|
||||
Flashing and updating
|
||||
*********************
|
||||
:command:`s6-rc-up-tree` brings up a service and all services that
|
||||
depend on it, except for any services that depend on a "controlled"
|
||||
service that is not currently running. Controlled services are not
|
||||
started at boot time but in response to external events (e.g. plugging
|
||||
in a particular piece of hardware) so you probably don't want to be
|
||||
starting them by hand if the conditions aren't there.
|
||||
|
||||
A service may be **up** or **down** (there are no intermediate states
|
||||
like "started" or "stopping" or "dying" or "cogitating"). Some (but
|
||||
not all) services have "readiness" notifications: the dependents of a
|
||||
service with a readiness notification won't be started until the
|
||||
service signals (by writing to a nominated file descriptor) that it's
|
||||
prepared to start work. Most services defined by Liminix also have a
|
||||
``timeout-up`` parameter, which means that if a service has readiness
|
||||
notifications and doesn't become ready in the allotted time (defaults
|
||||
20 seconds) it will be terminated and its state set to **down**.
|
||||
|
||||
If the process providing a service dies, it will be restarted
|
||||
automatically. Liminix does not automatically set it to **down**.
|
||||
|
||||
Flashing from Liminix
|
||||
=====================
|
||||
(If the process providing a service dies without ever notifying
|
||||
readiness, Liminix will restart it as many times as it has to until the
|
||||
timeout period elapses, and then stop it and mark it down.)
|
||||
|
||||
The flash procedure from an existing Liminix-system has two steps.
|
||||
First we reboot the device (using "kexec") into an "ephemeral"
|
||||
RAM-based version of the new configuration, then when we're happy it
|
||||
works we can flash the image - and if it doesn't work we can reboot
|
||||
the device again and it will boot from the old image.
|
||||
Controlled services
|
||||
===================
|
||||
|
||||
**Controlled** services are those which are started/stopped on demand
|
||||
by a **controller** (another service) instead of being started at boot
|
||||
time. For example:
|
||||
|
||||
Building the RAM-based image
|
||||
----------------------------
|
||||
* ``svc.uevent-rule.build`` creates a controlled service which is
|
||||
active when a particular hardware device (identified by uevent/sysfs
|
||||
directory) is present.
|
||||
|
||||
To create the ephemeral image, build ``outputs.kexecboot`` instead of
|
||||
``outputs.default``. This generates a directory containing the root
|
||||
filesystem image and kernel, along with an executable called `kexec`
|
||||
and a `boot.sh` script that runs it with appropriate arguments.
|
||||
* ``svc.round-robin.build`` creates a service controller that
|
||||
invokes two or more services in turn, running the next one when the
|
||||
process providing the previous one exits. We use this for failover
|
||||
from one network connection to a backup connection, for example.
|
||||
|
||||
For example
|
||||
* ``svc.health-check.build`` creates a service controller that
|
||||
runs a controlled service and periodically tests whether it is
|
||||
healthy by running an external health check command or script. If the
|
||||
check command repeatedly fails, the controlled service is
|
||||
restarted.
|
||||
|
||||
The Configuration section of the manual describes controlled
|
||||
services in more detail. Some operational considerations
|
||||
|
||||
* ``round-robin`` detects a service status by looking at its
|
||||
:file:`outputs` directory, so it won't work unless the service
|
||||
creates some outputs. This is considered a bug and will be
|
||||
fixed in a future release
|
||||
|
||||
* ``health-check`` works for longruns but not for oneshots, as it
|
||||
internally relies on ``s6-svc`` to restart the process
|
||||
|
||||
Logs
|
||||
====
|
||||
|
||||
Logs for all services are collated into :file:`/run/log/current`.
|
||||
The log file is rotated when it reaches a threshold size, into another
|
||||
file in the same directory whose name contains a TAI64 timestamp.
|
||||
|
||||
Each log line is prefixed with a TAI64 timestamp and the name of the
|
||||
service, if it is a longrun. If it is a oneshot, a timestamp and the
|
||||
name of some other service. To convert the timestamp into a
|
||||
human-readable format, use :command:`s6-tai64nlocal`.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
nix-build -I liminix-config=./examples/arhcive.nix \
|
||||
--arg device "import ./devices/gl-ar750"
|
||||
-A outputs.kexecboot && \
|
||||
(tar chf - result | ssh root@the-device tar -C /run -xvf -)
|
||||
# ls -l /run/log/
|
||||
-rw-r--r-- 1 0 lock
|
||||
-rw-r--r-- 1 0 state
|
||||
-rwxr--r-- 1 98059 @4000000000025cb629c311ac.s
|
||||
-rwxr--r-- 1 98061 @40000000000260f7309c7fb4.s
|
||||
-rwxr--r-- 1 98041 @40000000000265233a6cc0b6.s
|
||||
-rwxr--r-- 1 98019 @400000000002695d10c06929.s
|
||||
-rwxr--r-- 1 98064 @4000000000026d84189559e0.s
|
||||
-rwxr--r-- 1 98055 @40000000000271ce1e031d91.s
|
||||
-rwxr--r-- 1 98054 @400000000002760229733626.s
|
||||
-rwxr--r-- 1 98104 @4000000000027a2e3b6f4e12.s
|
||||
-rwxr--r-- 1 98023 @4000000000027e6f0ed24a6c.s
|
||||
-rw-r--r-- 1 42374 current
|
||||
|
||||
# tail -2 /run/log/current
|
||||
@40000000000284f130747343 wan.link.pppoe Connect: ppp0 <--> /dev/pts/0
|
||||
@40000000000284f230acc669 wan.link.pppoe sent [LCP ConfReq id=0x1 <asyncmap 0x0> <magic 0x667a9594> <pcomp> <accomp>]
|
||||
# tail -2 /run/log/current | s6-tai64nlocal
|
||||
1970-01-02 21:51:45.828598156 wan.link.pppoe sent [LCP ConfReq id=0x1 <asyncmap 0x0> <magic 0x667a9594> <pcomp> <accom
|
||||
p>]
|
||||
1970-01-02 21:51:48.832588765 wan.link.pppoe sent [LCP ConfReq id=0x1 <asyncmap 0x0> <magic 0x667a9594> <pcomp> <accom
|
||||
p>]
|
||||
|
||||
and then login to the device and run
|
||||
Log persistence
|
||||
---------------
|
||||
|
||||
Logs written to :file:`/run/log/` will not survive a reboot or crash,
|
||||
as it is an ephemeral filesystem.
|
||||
|
||||
On supported hardware you can enable logging to `pstore
|
||||
<https://www.kernel.org/doc/Documentation/ABI/testing/pstore>`_ which
|
||||
means the most recent log messages will be preserved on reboot. Set
|
||||
the config option ``logging.persistent.enable = true`` to log messages
|
||||
to :file:`/dev/pmsg0` as well as to the regular log. This is a
|
||||
circular buffer, so when it fills up newer messages will overwrite the
|
||||
oldest messages.
|
||||
|
||||
To check the previous messages after a (planned or forced) reboot,
|
||||
you need to mooun the pstore filesystem.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
cd /run/result
|
||||
sh ./boot.sh .
|
||||
# mount -t pstore pstore /sys/fs/pstore/
|
||||
# ls -l /sys/fs/pstore/
|
||||
-r--r--r-- 1 43071 pmsg-ramoops-0
|
||||
# cat /sys/fs/pstore/pmsg-ramoops-0
|
||||
@40000000000000282c997d29 mydevice klogd <6>[ 30.793756] int: port 2(wlan0) entered blocking state
|
||||
[log messages from before the reboot follow]
|
||||
|
||||
|
||||
This will load the new kernel and map the root filesystem into a RAM
|
||||
disk, then start executing the new kernel. *This is effectively a
|
||||
reboot - be sure to close all open files and finish anything else
|
||||
you were doing first.*
|
||||
|
||||
If the new system crashes or is rebooted, then the device will revert
|
||||
to the old configuration it finds in flash.
|
||||
Updating an installed system
|
||||
****************************
|
||||
|
||||
If your system has a writable root filesystem (JFFS2, btrfs etc -
|
||||
anything but squashfs), we have mechanisms for in-places updates
|
||||
analogous to :command:`nixos-rebuild`, but the operation is a bit
|
||||
different because it expects to run on a build machine and then copy
|
||||
to the host device using :command:`ssh`.
|
||||
|
||||
Building the second (permanent) image
|
||||
-------------------------------------
|
||||
|
||||
While running in the kexecboot system, you can build the permanent
|
||||
image and copy it to the device with :command:`ssh`
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
build-machine$ nix-build -I liminix-config=./examples/arhcive.nix \
|
||||
--arg device "import ./devices/gl-ar750"
|
||||
-A outputs.default && \
|
||||
(tar chf - result | ssh root@the-device tar -C /run -xvf -)
|
||||
|
||||
build-machine$ tar chf - result/firmware.bin | \
|
||||
ssh root@the-device tar -C /run -xvf -
|
||||
|
||||
Next you need to connect to the device and locate the "firmware"
|
||||
partition, which you can do with a combination of :command:`dmesg`
|
||||
output and the contents of :file:`/proc/mtd`
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
<5>[ 0.469841] Creating 4 MTD partitions on "spi0.0":
|
||||
<5>[ 0.474837] 0x000000000000-0x000000040000 : "u-boot"
|
||||
<5>[ 0.480796] 0x000000040000-0x000000050000 : "u-boot-env"
|
||||
<5>[ 0.487056] 0x000000050000-0x000000060000 : "art"
|
||||
<5>[ 0.492753] 0x000000060000-0x000001000000 : "firmware"
|
||||
|
||||
# cat /proc/mtd
|
||||
dev: size erasesize name
|
||||
mtd0: 00040000 00001000 "u-boot"
|
||||
mtd1: 00010000 00001000 "u-boot-env"
|
||||
mtd2: 00010000 00001000 "art"
|
||||
mtd3: 00fa0000 00001000 "firmware"
|
||||
mtd4: 002a0000 00001000 "kernel"
|
||||
mtd5: 00d00000 00001000 "rootfs"
|
||||
|
||||
Now run (in this example)
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
flashcp -v firmware.bin /dev/mtd3
|
||||
|
||||
|
||||
"I know my new image is good, can I skip the intermediate step?"
|
||||
----------------------------------------------------------------
|
||||
|
||||
In addition to giving you a chance to see if the new image works, this
|
||||
two-step process ensures that you're not copying the new image over
|
||||
the top of the active root filesystem. Sometimes it works, but you
|
||||
will at least need physical access to the device to power-cycle it
|
||||
because it will be effectively frozen afterwards.
|
||||
|
||||
|
||||
Flashing from the boot monitor
|
||||
==============================
|
||||
|
||||
If you are prepared to open the device and have a TTL serial adaptor
|
||||
of some kind to connect it to, you can probably use U-Boot and a TFTP
|
||||
server to download and flash the image. This is quite
|
||||
hardware-specific, and sometimes involves soldering: please refer
|
||||
to :ref:`serial`.
|
||||
|
||||
|
||||
Flashing from OpenWrt
|
||||
=====================
|
||||
|
||||
.. CAUTION:: Untested! A previous version of these instructions
|
||||
(without the -e flag) led to bricking the device
|
||||
when flashing a jffs2 image. If you are reading
|
||||
this message, nobody has yet reported on whether the
|
||||
new instructions are any better.
|
||||
|
||||
If your device is running OpenWrt then it probably has the
|
||||
:command:`mtd` command installed. After transferring the image onto the
|
||||
device using e.g. :command:`ssh`, you can run it as follows:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
mtd -e -r write /tmp/firmware.bin firmware
|
||||
|
||||
The options to this command are for "erase before writing" and "reboot
|
||||
after writing".
|
||||
|
||||
For more information, please see the `OpenWrt manual <https://openwrt.org/docs/guide-user/installation/sysupgrade.cli>`_ which may also contain (hardware-dependent) instructions on how to flash an image using the vendor firmware - perhaps even from a web interface.
|
||||
|
||||
Updating an installed system (JFFS2)
|
||||
************************************
|
||||
|
||||
|
||||
Adding packages
|
||||
===============
|
||||
|
||||
If your device is running a JFFS2 root filesystem, you can build
|
||||
extra packages for it on your build system and copy them to the
|
||||
device: any package in Nixpkgs or in the Liminix overlay is available
|
||||
with the ``pkgs`` prefix:
|
||||
To use this, build the ``outputs.updater``
|
||||
target and then run the :command:`update.sh` script it generates.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
nix-build -I liminix-config=./my-configuration.nix \
|
||||
--arg device "import ./devices/mydevice" -A pkgs.tcpdump
|
||||
--arg device "import ./devices/mydevice" \
|
||||
-A outputs.updater
|
||||
./result/bin/update.sh root@the-device
|
||||
|
||||
nix-shell -p min-copy-closure root@the-device result/
|
||||
The update script uses min-copy-closure to copy new or changed
|
||||
packages to the device, then (perhaps) reboots it. The reboot
|
||||
behaviour can be affected by flags:
|
||||
|
||||
Note that this only copies the package to the device: it doesn't update
|
||||
any profile to add it to ``$PATH``
|
||||
* `--no-reboot` will cause it not to reboot at all, if you would
|
||||
rather do that yourself. Note that none of the newly-installed or
|
||||
updated services will be running until you do.
|
||||
|
||||
|
||||
Rebuilding the system
|
||||
=====================
|
||||
|
||||
:command:`liminix-rebuild` is the Liminix analogue of :command:`nixos-rebuild`, although its operation is a bit different because it expects to run on a build machine and then copy to the host device. Run it with the same ``liminix-config`` and ``device`` parameters as you would run :command:`nix-build`, and it will build any new/changed packages and then copy them to the device using SSH. For example:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
liminix-rebuild root@the-device -I liminix-config=./examples/rotuer.nix --arg device "import ./devices/gl-ar750"
|
||||
|
||||
This will
|
||||
|
||||
* build anything that needs building
|
||||
* copy new or changed packages to the device
|
||||
* reboot the device
|
||||
* `--fast` causes it tn not do a full reboot, but instead to restart
|
||||
only the services that have been changed. This will restart all of
|
||||
the services that have updated store paths (and anything that
|
||||
depends on them), but will not affect services that haven't changed.
|
||||
|
||||
It doesn't delete old packages automatically: to do that run
|
||||
:command:`min-collect-garbage`, which will delete any packages not in
|
||||
@ -196,3 +207,136 @@ Caveats
|
||||
nixpkgs).
|
||||
|
||||
* it cannot upgrade the kernel, only userland
|
||||
|
||||
.. _levitate:
|
||||
|
||||
Adding packages
|
||||
===============
|
||||
|
||||
If you simply wish to add a package without any change to services,
|
||||
you can call :command:`min-copy-closure` directly to install
|
||||
any package in Nixpkgs or in the Liminix overlay
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
nix-build -I liminix-config=./my-configuration.nix \
|
||||
--arg device "import ./devices/mydevice" -A pkgs.tcpdump
|
||||
|
||||
nix-shell -p min-copy-closure root@the-device result/
|
||||
|
||||
Note that this only copies the package and its dependencies to the
|
||||
device: it doesn't update any profile to add it to ``$PATH``
|
||||
|
||||
|
||||
.. _rebuilding the system:
|
||||
|
||||
Reinstalling on a running system
|
||||
********************************
|
||||
|
||||
Liminix is initially installed from a monolithic
|
||||
:file:`firmware.bin` - and unless you're running a writable
|
||||
filesystem, the only way to update it is to build and install a whole
|
||||
new :file:`firmware.bin`. However, you probably would prefer not to
|
||||
have to remove it from its installation site, unplug it from the
|
||||
network and stick serial cables in it all over again.
|
||||
|
||||
It is not (generally) safe to install a new firmware onto the flash
|
||||
partitions that the active system is running on. To address this we
|
||||
have :command:`levitate`, which a way for a running Liminix system to
|
||||
"soft restart" into a ramdisk running only a limited set of services,
|
||||
so that the main partitions can then be safely flashed.
|
||||
|
||||
|
||||
|
||||
Configuration
|
||||
=============
|
||||
|
||||
Levitate *needs to be configured when you create the initial system*
|
||||
to specify which services/packages/etc to run in maintenance
|
||||
mode. Most likely you want to configure a network interface and an ssh
|
||||
for example so that you can login to reflash it.
|
||||
|
||||
.. code-block:: nix
|
||||
|
||||
defaultProfile.packages = with pkgs; [
|
||||
...
|
||||
(levitate.override {
|
||||
config = {
|
||||
services = {
|
||||
inherit (config.services) dhcpc sshd watchdog;
|
||||
};
|
||||
defaultProfile.packages = [ mtdutils ];
|
||||
users.root = config.users.root;
|
||||
};
|
||||
})
|
||||
];
|
||||
|
||||
|
||||
|
||||
Use
|
||||
===
|
||||
|
||||
Connect (with ssh, probably) to the running Liminix system that you
|
||||
wish to upgrade.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
bash$ ssh root@the-device
|
||||
|
||||
Run :command:`levitate`. This takes a little while (perhaps a few
|
||||
tens of seconds) to execute, and copies all config required for
|
||||
maintenance mode to :file:`/run/maintenance`.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# levitate
|
||||
|
||||
Reboot into maintenance mode. You will be logged out
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# reboot
|
||||
|
||||
Connect to the device again - note that the ssh host key will have changed.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# ssh -o UserKnownHostsFile=/dev/null root@the-device
|
||||
|
||||
Check we're in maintenance mode
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# cat /etc/banner
|
||||
|
||||
LADIES AND GENTLEMEN WE ARE FLOATING IN SPACE
|
||||
|
||||
Most services are disabled. The system is operating
|
||||
with a ram-based root filesystem, making it safe to
|
||||
overwrite the flash devices in order to perform
|
||||
upgrades and maintenance.
|
||||
|
||||
Don't forget to reboot when you have finished.
|
||||
|
||||
Perform the upgrade, using flashcp. This is an example,
|
||||
your device will differ
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# cat /proc/mtd
|
||||
dev: size erasesize name
|
||||
mtd0: 00030000 00010000 "u-boot"
|
||||
mtd1: 00010000 00010000 "u-boot-env"
|
||||
mtd2: 00010000 00010000 "factory"
|
||||
mtd3: 00f80000 00010000 "firmware"
|
||||
mtd4: 00220000 00010000 "kernel"
|
||||
mtd5: 00d60000 00010000 "rootfs"
|
||||
mtd6: 00010000 00010000 "art"
|
||||
# flashcp -v firmware.bin mtd:firmware
|
||||
|
||||
All done
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# reboot
|
||||
|
||||
|
@ -7,19 +7,19 @@
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
||||
|
||||
project = 'Liminix'
|
||||
copyright = '2023, Daniel Barlow'
|
||||
copyright = '2023-2024 Daniel Barlow'
|
||||
author = 'Daniel Barlow'
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
||||
|
||||
extensions = [
|
||||
'sphinx.ext.autosectionlabel'
|
||||
# 'sphinx.ext.autosectionlabel'
|
||||
]
|
||||
autosectionlabel_prefix_document = True
|
||||
|
||||
templates_path = ['_templates']
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
exclude_patterns = ['*.inc.rst', '_build', 'Thumbs.db', '.DS_Store']
|
||||
|
||||
|
||||
|
||||
|
@ -1,27 +1,8 @@
|
||||
.. _configuration:
|
||||
|
||||
Configuration
|
||||
#############
|
||||
|
||||
Liminix uses the Nix language to provide congruent configuration
|
||||
management. This means that to change anything about the way in
|
||||
which a Liminix system works, you make that change in
|
||||
your :file:`configuration.nix` (or one of the other files it references),
|
||||
and rerun :command:`nix-build` or :command:`liminix-rebuild` to action
|
||||
the change. It is not possible (at least, without shenanigans) to make
|
||||
changes by logging into the device and running imperative commands
|
||||
whose effects may later be overridden: :file:`configuration.nix`
|
||||
always describes the entire system and can be used to recreate that
|
||||
system at any time. You can usefully keep it under version control.
|
||||
|
||||
If you are familiar with NixOS, you will notice some similarities
|
||||
between NixOS and Liminix configuration, and also some
|
||||
differences. Sometimes the differences are due to the
|
||||
resource-constrained devices we deploy onto, sometimes due to
|
||||
differences in the uses these devices are put to.
|
||||
|
||||
|
||||
Configuration taxonomy
|
||||
**********************
|
||||
|
||||
There are many things you can specify in a configuration, but these
|
||||
are the ones you most commonly need to change:
|
||||
|
||||
@ -86,14 +67,166 @@ domains, or you want to run two SSH daemons on different ports.
|
||||
don't use the NixOS modules themselves, because the
|
||||
underlying system is not similar enough for them to work.
|
||||
|
||||
.. _configuration-services:
|
||||
|
||||
Services
|
||||
********
|
||||
|
||||
We use the `s6-rc service manager <https://www.skarnet.org/software/s6-rc/overview.html>`_ to start/stop/restart services and handle
|
||||
service dependencies. Any attribute in `config.services` will become
|
||||
part of the default set of services that s6-rc will try to bring up on
|
||||
boot.
|
||||
In Liminix a service is any kind of long-running task or process on
|
||||
the system, that is managed (started, stopped, and monitored) by a
|
||||
service supervisor. A typical SOHO router might have services to
|
||||
|
||||
* answer DHCP and DNS requests from the LAN
|
||||
* provide a wireless access point
|
||||
* connect using PPPoE or L2TP to an upstream network
|
||||
* start/stop the firewall
|
||||
* enable/disable IP packet forwarding
|
||||
* mount filesystems
|
||||
|
||||
(Some of these might not be considered services using other
|
||||
definitions of the term: for example, this L2TP process would be a
|
||||
"client" in the client/server classification; and enabling packet
|
||||
forwarding doesn't require any long-lived process - just a setting to
|
||||
be toggled. However, there is value in being able to use the same
|
||||
abstractions for all the things to manage them and specify their
|
||||
dependency relationships - so in Liminix "everything is a service")
|
||||
|
||||
The service supervision system enables service health monitoring,
|
||||
restart of unhealthy services, and failover to "backup" services when
|
||||
a primary service fails or its dependencies are unavailable. The
|
||||
intention is that you have a framework in which you can specify policy
|
||||
requirements like "ethernet wan dhcp-client should be restarted if it
|
||||
crashes, but if it can't start because the hardware link is down, then
|
||||
4G ppp service should be started instead".
|
||||
|
||||
Any attribute in `config.services` will become part of the default set
|
||||
of services that s6-rc will try to bring up. Services are usually
|
||||
started at boot time, but **controlled services** are those that are
|
||||
required only in particular contexts. For example, a service to mount
|
||||
a USB backup drive should run only when the drive is attached to the
|
||||
system. Liminix currently implements three kinds of controlled service:
|
||||
|
||||
* "uevent-rule" service controllers use sysfs/uevent to identify when
|
||||
particular hardware devices are present, and start/stop a controlled
|
||||
service appropriately.
|
||||
|
||||
* the "round-robin" service controller is used for service failover:
|
||||
it allows you to specify a list of services and runs each of them
|
||||
in turn until it exits, then runs the next.
|
||||
|
||||
* the "health-check" service wraps another service, and runs a "health
|
||||
check" command at regular intervals. When the health check fails,
|
||||
indicating that the wrapped service is not working, it is terminated
|
||||
and allowed to restart.
|
||||
|
||||
Runtime secrets (external vault)
|
||||
================================
|
||||
|
||||
Secrets (such as wifi passphrases, PPP username/password, SSH keys,
|
||||
etc) that you provide as literal values in :file:`configuration.nix`
|
||||
are processed into into config files and scripts at build time, and
|
||||
eventually end up in various files in the (world-readable)
|
||||
:file:`/nix/store` before being baked into a flashable image. To
|
||||
change a secret - whether due to a compromise, or just as part of to a
|
||||
routine key rotation - you need to rebuild the configuration and
|
||||
potentially reflash the affected devices.
|
||||
|
||||
To avoid this, you may instead use a "secrets service", which is a
|
||||
mechanism for your device to fetch secrets from a source external to
|
||||
the Nix store, and create at runtime the configuration files and
|
||||
scripts that start the services which require them.
|
||||
|
||||
Not every possible parameter to every possible service is configurable
|
||||
using a secrets service. Parameters which can be configured this way
|
||||
are those with the type ``liminix.lib.types.replacable``. At the time
|
||||
this document was written, these include:
|
||||
|
||||
* ppp (pppoe and l2tp): ``username``, ``password``
|
||||
* ssh: ``authorizedKeys``
|
||||
* hostapd: all parameters (most likely to be useful for ``wpa_passphrase``)
|
||||
|
||||
To use a runtime secret for any of these parameters:
|
||||
|
||||
* create a secrets service to specify the source of truth for secrets
|
||||
* use the :code:`outputRef` function in the service parameter to specify the secrets service and path
|
||||
|
||||
For example, given you had an HTTPS server hosting a JSON file with the structure
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
"ssh": {
|
||||
"authorizedKeys": {
|
||||
"root": [ "ssh-rsa ....", "ssh-rsa ....", ... ]
|
||||
"guest": [ "ssh-rsa ....", "ssh-rsa ....", ... ]
|
||||
}
|
||||
}
|
||||
|
||||
you could use a :file:`configuration.nix` fragment something like this
|
||||
to make those keys visible to ssh:
|
||||
|
||||
.. code-block:: nix
|
||||
|
||||
services.secrets = svc.secrets.outboard.build {
|
||||
name = "secret-service";
|
||||
url = "http://10.0.0.1/secrets.json";
|
||||
username = "secrets";
|
||||
password = "liminix";
|
||||
interval = 30; # minutes
|
||||
dependencies = [ config.services.lan ];
|
||||
};
|
||||
services.sshd = svc.ssh.build {
|
||||
authorizedKeys = outputRef config.services.secrets "ssh/authorizedKeys";
|
||||
};
|
||||
|
||||
|
||||
|
||||
There are presently two implementations of a secrets service:
|
||||
|
||||
Outboard secrets (HTTPS)
|
||||
------------------------
|
||||
|
||||
This service expects a URL to a JSON file containing all the secrets.
|
||||
|
||||
You may specify a username and password along with the URL, which are
|
||||
used if the file is password-protected (HTTP Basic
|
||||
authentication). Note that this is not a protection against a
|
||||
malicious local user: the username and password are normal build-time
|
||||
parameters so will be readable in the Nix store. This is a mitigation
|
||||
against the URL being accidentally discovered due to e.g. a log file
|
||||
or error message on the server leaking.
|
||||
|
||||
|
||||
Tang secrets (encrypted local file)
|
||||
-----------------------------------
|
||||
|
||||
Aternatively, secrets may be stored locally on the device, in a file
|
||||
that has been encrypted using `Tang <https://github.com/latchset/tang>`_.
|
||||
|
||||
Tang is a server for binding data to network presence.
|
||||
|
||||
This sounds fancy, but the concept is simple. You have some data, but you only want it to be available when the system containing the data is on a certain, usually secure, network.
|
||||
|
||||
|
||||
.. code-block:: nix
|
||||
|
||||
services.secrets = svc.secrets.tang.build {
|
||||
name = "secret-service";
|
||||
path = "/run/mnt/usbstick/secrets.json.jwe";
|
||||
interval = 30; # minutes
|
||||
dependencies = [ config.services.mount-usbstick ];
|
||||
};
|
||||
|
||||
The encryption uses the
|
||||
same scheme/algorithm as `Clevis <https://github.com/latchset/clevis>`_ : you may use the `Clevis instructions <https://github.com/latchset/clevis?tab=readme-ov-file#pin-tang>`_ to
|
||||
encrypt the file on another host and then copy it to your Liminix
|
||||
device, or you can use :command:`tangc encrypt` to encrypt directly on
|
||||
the device. (That latter approach may pose a chicken/egg problem if
|
||||
the device needs secrets to boot up and run the services you are
|
||||
relying on in order to login).
|
||||
|
||||
|
||||
Writing services
|
||||
================
|
||||
|
||||
For the most part, for common use cases, hopefully the services you
|
||||
need will be defined by modules and you will only have to pass the
|
||||
@ -141,11 +274,101 @@ Services may have dependencies: as you see above in the ``cowsayd``
|
||||
example, it depends on some service called ``config.services.lan``,
|
||||
meaning that it won't be started until that other service is up.
|
||||
|
||||
..
|
||||
TODO: explain service outputs
|
||||
Service outputs
|
||||
===============
|
||||
|
||||
Outputs are a mechanism by which a service can provide data which may
|
||||
be required by other services. For example:
|
||||
|
||||
* the DHCP client service can expect to receive nameserver address
|
||||
information as one of the fields in the response from the DHCP
|
||||
server: we provide that as an output which a dependent service for a
|
||||
stub name resolver can use to configure its upstream servers.
|
||||
|
||||
* a service that creates a new network interface (e.g. ppp) will
|
||||
provide the name of the interface (:code:`ppp0`, or :code:`ppp1` or
|
||||
:code:`ppp7`) as an output so that a dependent service can reference
|
||||
it to set up a route, or to configure firewall rules.
|
||||
|
||||
A service :code:`myservice` should write its outputs as files in
|
||||
:file:`/run/services/outputs/myservice`: you can look around this
|
||||
directory on a running Liminix system to see how it's used currently.
|
||||
Usually we use the :code:`in_outputs` shell function in the
|
||||
:command:`up` or :command:`run` attributes of the service:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
(in_outputs ${name}
|
||||
for i in lease mask ip router siaddr dns serverid subnet opt53 interface ; do
|
||||
(printenv $i || true) > $i
|
||||
done)
|
||||
|
||||
The outputs are just files, so technically you can read them using
|
||||
anything that can read a file. Liminix has two "preferred"
|
||||
mechanisms, though:
|
||||
|
||||
One-off lookups
|
||||
---------------
|
||||
|
||||
In any context that ends up being evaluated by the shell, use
|
||||
:code:`output` to print the value of an output
|
||||
|
||||
.. code-block:: nix
|
||||
|
||||
services.defaultroute4 = svc.network.route.build {
|
||||
via = "$(output ${services.wan} address)";
|
||||
target = "default";
|
||||
dependencies = [ services.wan ];
|
||||
};
|
||||
|
||||
|
||||
Continuous updates
|
||||
------------------
|
||||
|
||||
The downside of using shell functions in downstream service startup
|
||||
scripts is that they only run when the service starts up: if a service
|
||||
output *changes*, the downstream service would have to be restarted to
|
||||
notice the change. Sometimes this is OK but other times the downstream
|
||||
has no other need to restart, if it can only get its new data.
|
||||
|
||||
For this case, there is the :code:`anoia.svc` Fennel library, which
|
||||
allows you to write a simple loop which is iterated over whenever a
|
||||
service's outputs change. This code is from
|
||||
:file:`modules/dhcp6c/acquire-wan-address.fnl`
|
||||
|
||||
.. code-block:: fennel
|
||||
|
||||
(fn update-addresses [wan-device addresses new-addresses exec]
|
||||
;; run some appropriate "ip address [add|remove]" commands
|
||||
)
|
||||
|
||||
(fn run []
|
||||
(let [[state-directory wan-device] arg
|
||||
dir (svc.open state-directory)]
|
||||
(accumulate [addresses []
|
||||
v (dir:events)]
|
||||
(update-addresses wan-device addresses
|
||||
(or (v:output "address") []) system))))
|
||||
|
||||
The :code:`output` method seen here accepts a filename (relative
|
||||
to the service's output directory), or a directory name. It
|
||||
returns the first line of that file, or for directories it
|
||||
returns a table (Lua's key/value datastructure, similar to
|
||||
a hash/dictionary) of the outputs in that directory.
|
||||
|
||||
|
||||
Output design considerations
|
||||
----------------------------
|
||||
|
||||
For preference, outputs should be short and simple, and not require
|
||||
downstream services to do complicated parsing in order to use them.
|
||||
Shell commands in Liminix are run using the Busybox shell which
|
||||
doesn't have the niceties of an advanced shell like Bash let alone
|
||||
those of a real programming language.
|
||||
|
||||
Note also that the Lua :code:`svc` library only reads the first line
|
||||
of each output.
|
||||
|
||||
..
|
||||
TODO: outputs that change, and services that poll other services
|
||||
|
||||
Module implementation
|
||||
*********************
|
||||
@ -192,7 +415,7 @@ To expose a service template in a module, it needs the following:
|
||||
|
||||
.. code-block:: nix
|
||||
|
||||
config.system.service.cowsay = liminix.callService ./service.nix {
|
||||
config.system.service.cowsay = config.system.callService ./service.nix {
|
||||
address = mkOption {
|
||||
type = types.str;
|
||||
default = "0.0.0.0";
|
||||
|
@ -88,64 +88,6 @@ time with configurations for RP-PPPoE and/or Accel PPP.`
|
||||
Hardware devices
|
||||
****************
|
||||
|
||||
.. _serial:
|
||||
|
||||
U-Boot and serial shenanigans
|
||||
=============================
|
||||
|
||||
Every device that we have so far encountered in Liminix uses `U-Boot,
|
||||
the "Universal Boot Loader" <https://docs.u-boot.org/en/latest/>`_ so
|
||||
it's worth knowing a bit about it. "Universal" is in this context a
|
||||
bit of a misnomer, though: encountering *mainline* U-Boot is very rare
|
||||
and often you'll find it is a fork from some version last updated
|
||||
in 2008. Upgrading U-Boot is more or less complicated depending on the
|
||||
device and is outside scope for Liminix.
|
||||
|
||||
To speak to U-Boot on your device you'll usually need a serial
|
||||
connection to it. This is device-specific. Usually it involves
|
||||
opening the box, locating the serial header pins (TX, RX and GND) and
|
||||
connecting a USB TTL converter to them.
|
||||
|
||||
The Rolls Royce of USB/UART cables is the `FTDI cable
|
||||
<https://cpc.farnell.com/ftdi/ttl-232r-rpi/cable-debug-ttl-232-usb-rpi/dp/SC12825?st=usb%20to%20uart%20cable>`_,
|
||||
but there are cheaper alternatives based on the PL2303 and CP2102 chipsets. Or
|
||||
get creative and use the `UART GPIO pins <https://pinout.xyz/>`_ on a Raspberry Pi. Whatever you do, make sure
|
||||
that the voltages are compatible: if your device is 3.3V (this is
|
||||
typical but not universal), you don't want to be sending it 5v or
|
||||
(even worse) 12v.
|
||||
|
||||
Run a terminal emulator such as Minicom on the computer at other end
|
||||
of the link. 115200 8N1 is the typical speed.
|
||||
|
||||
.. NOTE::
|
||||
|
||||
TTL serial connections typically have no form of flow control and
|
||||
so don't always like having massive chunks of text pasted into
|
||||
them - and U-Boot may drop characters while it's busy. So don't
|
||||
necessarily expect to copy-paste large chunks of text into the
|
||||
terminal emulator and have it work just like that.
|
||||
|
||||
If using Minicom, you may find it helps to bring up the "Termimal
|
||||
settings" dialog (C^A T), then configure "Newline tx delay" to
|
||||
some small but non-zero value.
|
||||
|
||||
When you turn the router on you should be greeted with some messages
|
||||
from U-Boot, followed by the instruction to hit some key to stop
|
||||
autoboot. Do this and you will get to the prompt. If you didn't see
|
||||
anything, the strong likelihood is that TX and RX are the wrong way
|
||||
around. If you see garbage, try a different speed.
|
||||
|
||||
Interesting commands to try first in U-Boot are :command:`help` and
|
||||
:command:`printenv`.
|
||||
|
||||
To do anything useful with U-Boot you will probably need a way to get
|
||||
large binary files onto the device, and the usual way to do this is by
|
||||
adding a network connection and using TFTP to download them. It's
|
||||
quite common that the device's U-Boot doesn't speak DHCP so it will
|
||||
need a static LAN address. You might also want to keep it away from
|
||||
your "real" LAN: see :ref:`bng` for some potentially useful tooling
|
||||
to use it on an isolated network.
|
||||
|
||||
|
||||
TFTP
|
||||
====
|
||||
@ -290,18 +232,13 @@ Running tests
|
||||
*************
|
||||
|
||||
You can run all of the tests by evaluating :file:`ci.nix`, which is the
|
||||
input I use in Hydra. Note that it expects Nixpkgs stable `and` unstable
|
||||
as inputs, because it builds the qemu device against both.
|
||||
input I use in Hydra.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
nix-build --argstr liminix `pwd` --arg nixpkgs "<nixpkgs>" \
|
||||
--argstr unstable `pwd`/../unstable-nixpkgs/ ci.nix
|
||||
|
||||
To run a single named test, use the ``-A`` flag. For example, ``-A pppoe``
|
||||
|
||||
|
||||
|
||||
nix-build -I liminix=`pwd` ci.nix -A pppoe # run one job
|
||||
nix-build -I liminix=`pwd` ci.nix -A all # run all jobs
|
||||
|
||||
|
||||
Troubleshooting
|
||||
***************
|
||||
|
@ -1,11 +1,9 @@
|
||||
{ eval, lib, pkgs }:
|
||||
let
|
||||
inherit (lib) types;
|
||||
conf = eval.config;
|
||||
rootDir = builtins.toPath ./..;
|
||||
stripAnyPrefixes = lib.flip (lib.fold lib.removePrefix)
|
||||
["${rootDir}/"];
|
||||
optToDoc = name: opt : {
|
||||
stripAnyPrefixes = lib.flip (lib.fold lib.removePrefix) [ "${rootDir}/" ];
|
||||
optToDoc = name: opt: {
|
||||
inherit name;
|
||||
description = opt.description or null;
|
||||
default = opt.default or null;
|
||||
@ -26,7 +24,6 @@ let
|
||||
let x = lib.mapAttrsToList optToDoc sd.parameters; in x;
|
||||
}
|
||||
else
|
||||
item // { declarations = map stripAnyPrefixes item.declarations; };
|
||||
item // { declarations = map stripAnyPrefixes item.declarations; };
|
||||
in
|
||||
builtins.map spliceServiceDefn
|
||||
(pkgs.lib.optionAttrSetToDocList eval.options)
|
||||
builtins.map spliceServiceDefn (pkgs.lib.optionAttrSetToDocList eval.options)
|
||||
|
@ -1,29 +1,36 @@
|
||||
with import <nixpkgs> {} ;
|
||||
with import <nixpkgs> { };
|
||||
|
||||
let
|
||||
inherit (builtins) stringLength readDir filter;
|
||||
devices = filter (n: n != "families")
|
||||
(lib.mapAttrsToList (n: t: n) (readDir ../devices));
|
||||
texts = map (n:
|
||||
let d = import ../devices/${n}/default.nix;
|
||||
d' = {
|
||||
description = "${n}\n${substring 0 (stringLength n) "********************************"}\n";
|
||||
} // d;
|
||||
installer =
|
||||
if d ? description && d ? installer
|
||||
then ''
|
||||
|
||||
The default installation route for this device is
|
||||
:ref:`system-outputs-${d.installer}`
|
||||
''
|
||||
else "";
|
||||
in d'.description)
|
||||
devices;
|
||||
devices = filter (n: n != "families") (lib.mapAttrsToList (n: t: n) (readDir ../devices));
|
||||
texts = map (
|
||||
n:
|
||||
let
|
||||
d = import ../devices/${n}/default.nix;
|
||||
tag = ".. _${lib.strings.replaceStrings [" "] ["-"] n}:";
|
||||
d' = {
|
||||
description = ''
|
||||
${n}
|
||||
${substring 0 (stringLength n) "********************************"}
|
||||
'';
|
||||
} // d;
|
||||
in
|
||||
"${tag}\n\n${d'.description}"
|
||||
) devices;
|
||||
in
|
||||
writeText "hwdoc" ''
|
||||
Supported hardware
|
||||
##################
|
||||
|
||||
For development, the `GL.iNet GL-MT300A <https://www.gl-inet.com/products/gl-mt300a/>`_
|
||||
is an attractive choice as it has a builtin "debrick" procedure in the
|
||||
boot monitor and is also comparatively simple to
|
||||
attach serial cables to (soldering not required), so it
|
||||
is lower-risk than some devices.
|
||||
|
||||
For a more powerful device, something with an ath10k would be the safe bet,
|
||||
or the Linksys E8450 which seems popular in the openwrt community.
|
||||
|
||||
${lib.concatStringsSep "\n\n" texts}
|
||||
|
||||
''
|
||||
|
@ -7,6 +7,7 @@ Liminix
|
||||
|
||||
intro
|
||||
tutorial
|
||||
installation
|
||||
configuration
|
||||
admin
|
||||
development
|
||||
|
211
doc/installation.rst
Normal file
211
doc/installation.rst
Normal file
@ -0,0 +1,211 @@
|
||||
Installation
|
||||
############
|
||||
|
||||
Hardware devices vary wildly in their affordances for installing new
|
||||
operating systems, so it should be no surprise that the Liminix
|
||||
installation procedure is hardware-dependent. This section contains
|
||||
generic instructions, but please refer to the documentation for your
|
||||
device to find whether and how well they apply.
|
||||
|
||||
|
||||
Building a firmware image
|
||||
*************************
|
||||
|
||||
Liminix uses the Nix language to provide congruent configuration
|
||||
management. This means that to change anything about the way in
|
||||
which a Liminix system works, you make that change in
|
||||
your :file:`configuration.nix` (or one of the other files it references),
|
||||
and rerun :command:`nix-build` to action
|
||||
the change. It is not possible (at least, without shenanigans) to make
|
||||
changes by logging into the device and running imperative commands
|
||||
whose effects may later be overridden: :file:`configuration.nix`
|
||||
always describes the entire system and can be used to recreate that
|
||||
system at any time. You can usefully keep it under version control.
|
||||
|
||||
If you are familiar with NixOS, you will notice some similarities
|
||||
between NixOS and Liminix configuration, and also some
|
||||
differences. Sometimes the differences are due to the
|
||||
resource-constrained devices we deploy onto, sometimes due to
|
||||
differences in the uses these devices are put to.
|
||||
|
||||
For a more full description of how to configure Liminix, see
|
||||
:ref:`configuration`. Assuming for the moment that you want a typical
|
||||
home wireless gateway/router, the best way to get started is to copy
|
||||
:file:`examples/rotuer.nix` and edit it for your requirements.
|
||||
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ cp examples/rotuer.nix configuration.nix
|
||||
$ vi configuration.nix # other editors are available
|
||||
$ # adjust this next command for your hardware device
|
||||
$ nix-build -I liminix-config=./configuration.nix \
|
||||
--arg device "import ./devices/gl-mt300a" -A outputs.default
|
||||
|
||||
Usually (not always, *please check the documentation for your device*)
|
||||
this will leave you with a file :file:`result/firmware.bin`
|
||||
which you now need to flash to the device.
|
||||
|
||||
|
||||
Flashing from the boot monitor (TFTP install)
|
||||
*********************************************
|
||||
|
||||
If you are prepared to open the device and have a TTL serial adaptor
|
||||
of some kind to connect it to, you can probably use U-Boot and a TFTP
|
||||
server to download and flash the image.
|
||||
|
||||
This is quite hardware-specific and may even involve soldering - see
|
||||
the documention for your device. However, it is in some ways the most
|
||||
"reliable" option: if you can see what's happening (or not happening)
|
||||
in early boot, the risk of "bricking" is substantially reduced and you
|
||||
have options for recovering if you misstep or flash a bad image.
|
||||
|
||||
|
||||
.. _serial:
|
||||
|
||||
U-Boot and serial shenanigans
|
||||
=============================
|
||||
|
||||
Every device that we have so far encountered in Liminix uses `U-Boot,
|
||||
the "Universal Boot Loader" <https://docs.u-boot.org/en/latest/>`_ so
|
||||
it's worth knowing a bit about it. "Universal" is in this context a
|
||||
bit of a misnomer, though: encountering *mainline* U-Boot is very rare
|
||||
and often you'll find it is a fork from some version last updated
|
||||
in 2008. Upgrading U-Boot is more or less complicated depending on the
|
||||
device and is outside scope for Liminix.
|
||||
|
||||
To speak to U-Boot on your device you'll usually need a serial
|
||||
connection to it. This typically involves opening the box, locating
|
||||
the serial header pins (TX, RX and GND) and connecting a USB TTL
|
||||
converter to them.
|
||||
|
||||
The Rolls Royce of USB/UART cables is the `FTDI cable
|
||||
<https://cpc.farnell.com/ftdi/ttl-232r-rpi/cable-debug-ttl-232-usb-rpi/dp/SC12825?st=usb%20to%20uart%20cable>`_,
|
||||
but there are cheaper alternatives based on the PL2303 and CP2102 chipsets - or you could even
|
||||
get creative and use the `UART GPIO pins <https://pinout.xyz/>`_ on a Raspberry Pi. Whatever you do, make sure
|
||||
that the voltages are compatible: if your device is 3.3V (this is
|
||||
typical but not universal), you don't want to be sending it 5v or
|
||||
(even worse) 12v.
|
||||
|
||||
Run a terminal emulator such as Minicom on the computer at other end
|
||||
of the link. 115200 8N1 is the typical speed.
|
||||
|
||||
.. NOTE::
|
||||
|
||||
TTL serial connections often have no flow control and
|
||||
so don't always like having massive chunks of text pasted into
|
||||
them - and U-Boot may drop characters while it's busy. So don't
|
||||
do that.
|
||||
|
||||
If using Minicom, you may find it helps to bring up the "Termimal
|
||||
settings" dialog (C^A T), then configure "Newline tx delay" to
|
||||
some small but non-zero value.
|
||||
|
||||
When you turn the router on you should be greeted with some messages
|
||||
from U-Boot, followed by the instruction to hit some key to stop
|
||||
autoboot. Do this and you will get to the prompt. If you didn't see
|
||||
anything, the strong likelihood is that TX and RX are the wrong way
|
||||
around. If you see garbage, try a different speed.
|
||||
|
||||
Interesting commands to try first in U-Boot are :command:`help` and
|
||||
:command:`printenv`.
|
||||
|
||||
You will also need to configure a TFTP server on a network that's
|
||||
accessible to the device: how you do that will vary according to which
|
||||
TFTP server you're using and so is out of scope for this document.
|
||||
|
||||
|
||||
|
||||
Buildiing and installing the image
|
||||
==================================
|
||||
|
||||
Follow the device-specific instructions for "TFTP install": usually,
|
||||
the steps are
|
||||
|
||||
* build the `outputs.mtdimage` output
|
||||
* copy :file:`result/firmware.bin` to your TFTP server
|
||||
* copy/paste the commands in :file:`result/flash.scr` one at a time into the U-Boot command line
|
||||
* reset the device
|
||||
|
||||
You should now see messages from U-Boot, then from the Linux kernel
|
||||
and eventually a shell prompt.
|
||||
|
||||
.. NOTE:: Before you reboot, check which networks the device is
|
||||
plugged into, and disconnect as necessary. If you've just
|
||||
installed a DHCP server or anything similar that responds to
|
||||
broadcasts, you may not want it to do that on the network
|
||||
that you temporarily connected it to for installing it.
|
||||
|
||||
|
||||
|
||||
Flashing from OpenWrt
|
||||
*********************
|
||||
|
||||
.. CAUTION:: Untested! A previous version of these instructions
|
||||
(without the -e flag) led to bricking the device
|
||||
when flashing a jffs2 image. If you are reading
|
||||
this message, nobody has yet reported on whether the
|
||||
new instructions are any better.
|
||||
|
||||
If your device is running OpenWrt then it probably has the
|
||||
:command:`mtd` command installed. Build the `outputs.mtdimage` output
|
||||
(as you would for a TFTP install) and then transfer
|
||||
:file:`result/firmware.bin` onto the device using e.g.
|
||||
:command:`scp`. Now flash as follows:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
mtd -e -r write /tmp/firmware.bin firmware
|
||||
|
||||
The options to this command are for "erase before writing" and "reboot
|
||||
after writing".
|
||||
|
||||
For more information, please see the `OpenWrt manual <https://openwrt.org/docs/guide-user/installation/sysupgrade.cli>`_ which may also contain (hardware-dependent) instructions on how to flash an image using the vendor firmware - perhaps even from a web interface.
|
||||
|
||||
|
||||
Flashing from Liminix
|
||||
*********************
|
||||
|
||||
If the device is already running Liminix and has been configured with
|
||||
:command:`levitate`, you can use that to safely flash your new image.
|
||||
Refer to :ref:`levitate` for an explanation.
|
||||
|
||||
If the device is running Liminix but doesn't have :command:`levitate`
|
||||
your options are more limited. You may attempt to use
|
||||
:command:`flashcp` but it doesn't always work: as it copies the new
|
||||
image over the top of the active root filesystem, surprise may ensue.
|
||||
Consider instead using a serial connection: you may need one anyway
|
||||
after trying flashcp if it corrupts the image.
|
||||
|
||||
flashcp (not generally recommended)
|
||||
===================================
|
||||
|
||||
Connect to the device and locate the "firmware" partition, which you
|
||||
can do with a combination of :command:`dmesg` output and the contents
|
||||
of :file:`/proc/mtd`
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
<5>[ 0.469841] Creating 4 MTD partitions on "spi0.0":
|
||||
<5>[ 0.474837] 0x000000000000-0x000000040000 : "u-boot"
|
||||
<5>[ 0.480796] 0x000000040000-0x000000050000 : "u-boot-env"
|
||||
<5>[ 0.487056] 0x000000050000-0x000000060000 : "art"
|
||||
<5>[ 0.492753] 0x000000060000-0x000001000000 : "firmware"
|
||||
|
||||
# cat /proc/mtd
|
||||
dev: size erasesize name
|
||||
mtd0: 00040000 00001000 "u-boot"
|
||||
mtd1: 00010000 00001000 "u-boot-env"
|
||||
mtd2: 00010000 00001000 "art"
|
||||
mtd3: 00fa0000 00001000 "firmware"
|
||||
mtd4: 002a0000 00001000 "kernel"
|
||||
mtd5: 00d00000 00001000 "rootfs"
|
||||
|
||||
Copy :file:`result/firmware.bin` to the device and now run (in this
|
||||
example)
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
flashcp -v firmware.bin /dev/mtd3
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
Module options
|
||||
##############
|
||||
|
||||
.. include:: modules-generated.rst
|
||||
.. include:: modules-generated.inc.rst
|
||||
|
@ -10,4 +10,4 @@ different artefacts, or have different ways to get that artefact
|
||||
installed. The options available for a particular device are described in
|
||||
the section for that device.
|
||||
|
||||
.. include:: outputs-generated.rst
|
||||
.. include:: outputs-generated.inc.rst
|
||||
|
@ -16,4 +16,4 @@
|
||||
(each [_ option (ipairs (sorted-options (yaml.load (io.read "*a"))))]
|
||||
(when (and (output? option) (not option.internal))
|
||||
(print (.. ".. _" (string.gsub option.name "%." "-") ":") "\n")
|
||||
(print option.description)))
|
||||
(print option.description "\n")))
|
||||
|
@ -71,7 +71,7 @@ stdin/stdout.
|
||||
You should now see Linux boot messages and after a few seconds be
|
||||
presented with a root shell prompt. You can run commands to look at
|
||||
the filesystem, see what processes are running, view log messages (in
|
||||
:file:/run/uncaught-logs.current), etc. To kill the emulator, press ^P
|
||||
:file:/run/log/current), etc. To kill the emulator, press ^P
|
||||
(Control P) then c to enter the "QEMU Monitor", then type ``quit`` at
|
||||
the ``(qemu)`` prompt.
|
||||
|
||||
@ -138,6 +138,8 @@ unbrick if necessary.
|
||||
work here, but you accept the slightly greater bricking
|
||||
risk if it doesn't.
|
||||
|
||||
See :doc:`hardware` for device support status.
|
||||
|
||||
You may want to read and inwardly digest the Develoment Manual section
|
||||
:ref:`serial` when you start working with Liminix on real hardware. You
|
||||
won't *need* serial access for this example, assuming it works, but it
|
||||
@ -291,14 +293,17 @@ the hostname) and then run
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
nix-shell --run "liminix-rebuild root@address-of-the-device -I liminix-config=./my-router.nix --arg device "import ./devices/gl-ar750""
|
||||
nix-build -I liminix-config=./my-router.nix \
|
||||
--arg device "import ./devices/gl-ar750" \
|
||||
-A outputs.systemConfiguration && \
|
||||
result/install.sh root@address-of-the-device
|
||||
|
||||
(This requires the device to be network-accessible from your build
|
||||
machine, which for a test/demo system might involve a second network
|
||||
device in your build system - USB ethernet adapters are cheap - or
|
||||
a bit of messing around unplugging cables.)
|
||||
|
||||
For more information about :code:`liminix-rebuild`, see the manual section :ref:`admin:Rebuilding the system`.
|
||||
For more information about in-place-updates, see the manual section :ref:`Rebuilding the system`.
|
||||
|
||||
|
||||
Final thoughts
|
||||
|
@ -11,15 +11,15 @@
|
||||
...
|
||||
}: let
|
||||
secrets = import ./extneder-secrets.nix;
|
||||
inherit (pkgs.liminix.services) oneshot longrun bundle target;
|
||||
inherit (pkgs.liminix.services) oneshot longrun target;
|
||||
inherit (pkgs.pseudofile) dir symlink;
|
||||
inherit (pkgs) writeText dropbear ifwait serviceFns;
|
||||
inherit (pkgs) writeText serviceFns;
|
||||
svc = config.system.service;
|
||||
in rec {
|
||||
boot = {
|
||||
tftp = {
|
||||
serverip = "192.168.8.148";
|
||||
ipaddr = "192.168.8.251";
|
||||
serverip = "10.0.0.1";
|
||||
ipaddr = "10.0.0.8";
|
||||
};
|
||||
};
|
||||
|
||||
@ -28,34 +28,12 @@ in rec {
|
||||
../modules/network
|
||||
../modules/vlan
|
||||
../modules/ssh
|
||||
../modules/usb.nix
|
||||
../modules/watchdog
|
||||
../modules/mount
|
||||
];
|
||||
hostname = "arhcive";
|
||||
|
||||
kernel = {
|
||||
config = {
|
||||
USB = "y";
|
||||
USB_EHCI_HCD = "y";
|
||||
USB_EHCI_HCD_PLATFORM = "y";
|
||||
USB_OHCI_HCD = "y";
|
||||
USB_OHCI_HCD_PLATFORM = "y";
|
||||
USB_SUPPORT = "y";
|
||||
USB_COMMON = "y";
|
||||
USB_STORAGE = "y";
|
||||
USB_STORAGE_DEBUG = "n";
|
||||
USB_UAS = "y";
|
||||
USB_ANNOUNCE_NEW_DEVICES = "y";
|
||||
SCSI = "y";
|
||||
BLK_DEV_SD = "y";
|
||||
USB_PRINTER = "y";
|
||||
MSDOS_PARTITION = "y";
|
||||
EFI_PARTITION = "y";
|
||||
EXT4_FS = "y";
|
||||
EXT4_USE_FOR_EXT2 = "y";
|
||||
FS_ENCRYPTION = "y";
|
||||
};
|
||||
};
|
||||
|
||||
services.dhcpc =
|
||||
let iface = config.hardware.networkInterfaces.lan;
|
||||
@ -74,7 +52,6 @@ in rec {
|
||||
dependencies = [ services.dhcpc ];
|
||||
name = "resolvconf";
|
||||
up = ''
|
||||
. ${serviceFns}
|
||||
( in_outputs ${name}
|
||||
for i in $(output ${services.dhcpc} dns); do
|
||||
echo "nameserver $i" > resolv.conf
|
||||
@ -105,7 +82,7 @@ in rec {
|
||||
};
|
||||
|
||||
services.mount_external_disk = svc.mount.build {
|
||||
device = "LABEL=backup-disk";
|
||||
partlabel = "backup-disk";
|
||||
mountpoint = "/srv";
|
||||
fstype = "ext4";
|
||||
};
|
||||
@ -115,7 +92,6 @@ in rec {
|
||||
secrets_file = oneshot rec {
|
||||
name = "rsync-secrets";
|
||||
up = ''
|
||||
. ${serviceFns}
|
||||
(in_outputs ${name}
|
||||
echo "backup:${secrets.rsync_secret}" > secrets)
|
||||
'';
|
||||
@ -141,23 +117,37 @@ in rec {
|
||||
secrets_file
|
||||
services.mount_external_disk
|
||||
config.hardware.networkInterfaces.lan
|
||||
] ;
|
||||
];
|
||||
};
|
||||
|
||||
users.root = {
|
||||
passwd = lib.mkForce secrets.root_password;
|
||||
# openssh.authorizedKeys.keys = [
|
||||
# (builtins.readFile "/home/dan/.ssh/id_rsa.pub")
|
||||
# ];
|
||||
passwd = lib.mkForce secrets.root.passwd;
|
||||
openssh.authorizedKeys.keys = secrets.root.keys;
|
||||
};
|
||||
|
||||
users.backup = {
|
||||
uid=500; gid=500; gecos="Storage owner"; dir="/srv";
|
||||
shell="/dev/null";
|
||||
uid = 500;
|
||||
gid = 500;
|
||||
gecos = "Storage owner";
|
||||
dir = "/srv";
|
||||
shell = "/dev/null";
|
||||
};
|
||||
groups.backup = {
|
||||
gid=500; usernames = ["backup"];
|
||||
gid = 500;
|
||||
usernames = [ "backup" ];
|
||||
};
|
||||
|
||||
defaultProfile.packages = with pkgs; [e2fsprogs strace tcpdump ];
|
||||
defaultProfile.packages = with pkgs; [
|
||||
e2fsprogs
|
||||
mtdutils
|
||||
(levitate.override {
|
||||
config = {
|
||||
services = {
|
||||
inherit (config.services) dhcpc sshd watchdog;
|
||||
};
|
||||
defaultProfile.packages = [ mtdutils ];
|
||||
users.root.openssh.authorizedKeys.keys = secrets.root.keys;
|
||||
};
|
||||
})
|
||||
];
|
||||
}
|
||||
|
@ -5,9 +5,9 @@
|
||||
# wherever the text "EDIT" appears - please consult the tutorial
|
||||
# documentation for details.
|
||||
|
||||
{ config, pkgs, lib, ... } :
|
||||
{ config, pkgs, ... }:
|
||||
let
|
||||
inherit (pkgs.liminix.services) bundle oneshot longrun;
|
||||
inherit (pkgs.liminix.services) bundle oneshot;
|
||||
inherit (pkgs) serviceFns;
|
||||
# EDIT: you can pick your preferred RFC1918 address space
|
||||
# for NATted connections, if you don't like this one.
|
||||
@ -49,31 +49,40 @@ in rec {
|
||||
country_code = "GB";
|
||||
wpa_passphrase = "not a real wifi password";
|
||||
|
||||
hw_mode="g";
|
||||
hw_mode = "g";
|
||||
ieee80211n = 1;
|
||||
auth_algs = 1; # 1=wpa2, 2=wep, 3=both
|
||||
wpa = 2; # 1=wpa, 2=wpa2, 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
|
||||
wpa_pairwise = "TKIP CCMP"; # auth for wpa (may not need this?)
|
||||
rsn_pairwise = "CCMP"; # auth for wpa2
|
||||
wmm_enabled = 1;
|
||||
};
|
||||
};
|
||||
|
||||
services.int = svc.network.address.build {
|
||||
interface = svc.bridge.primary.build { ifname = "int"; };
|
||||
family = "inet"; address = "${ipv4LocalNet}.1"; prefixLength = 16;
|
||||
family = "inet";
|
||||
address = "${ipv4LocalNet}.1";
|
||||
prefixLength = 16;
|
||||
};
|
||||
|
||||
services.bridge = svc.bridge.members.build {
|
||||
services.bridge = svc.bridge.members.build {
|
||||
primary = services.int;
|
||||
members = with config.hardware.networkInterfaces;
|
||||
[ wlan lan ];
|
||||
members = with config.hardware.networkInterfaces; [
|
||||
wlan
|
||||
lan
|
||||
];
|
||||
};
|
||||
|
||||
services.ntp = svc.ntp.build {
|
||||
pools = { "pool.ntp.org" = ["iburst"]; };
|
||||
makestep = { threshold = 1.0; limit = 3; };
|
||||
pools = {
|
||||
"pool.ntp.org" = [ "iburst" ];
|
||||
};
|
||||
makestep = {
|
||||
threshold = 1.0;
|
||||
limit = 3;
|
||||
};
|
||||
};
|
||||
|
||||
services.sshd = svc.ssh.build { };
|
||||
@ -128,7 +137,6 @@ in rec {
|
||||
dependencies = [ services.wan ];
|
||||
name = "resolvconf";
|
||||
up = ''
|
||||
. ${serviceFns}
|
||||
( in_outputs ${name}
|
||||
echo "nameserver $(output ${services.wan} ns1)" > resolv.conf
|
||||
echo "nameserver $(output ${services.wan} ns2)" >> resolv.conf
|
||||
@ -157,9 +165,7 @@ in rec {
|
||||
interface = services.wan;
|
||||
};
|
||||
|
||||
services.firewall = svc.firewall.build {
|
||||
ruleset = import ./demo-firewall.nix;
|
||||
};
|
||||
services.firewall = svc.firewall.build { };
|
||||
|
||||
services.packet_forwarding = svc.network.forward.build { };
|
||||
|
||||
@ -196,7 +202,5 @@ in rec {
|
||||
];
|
||||
};
|
||||
|
||||
defaultProfile.packages = with pkgs; [
|
||||
min-collect-garbage
|
||||
];
|
||||
defaultProfile.packages = with pkgs; [ min-collect-garbage ];
|
||||
}
|
||||
|
@ -8,12 +8,10 @@
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
modulesPath,
|
||||
...
|
||||
}: 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 = {
|
||||
@ -24,113 +22,32 @@ in rec {
|
||||
};
|
||||
|
||||
imports = [
|
||||
../modules/wlan.nix
|
||||
../modules/vlan
|
||||
../modules/network
|
||||
../modules/hostapd
|
||||
../modules/bridge
|
||||
../modules/ssh
|
||||
"${modulesPath}/profiles/wap.nix"
|
||||
"${modulesPath}/vlan"
|
||||
"${modulesPath}/ssh"
|
||||
];
|
||||
|
||||
hostname = "extneder";
|
||||
|
||||
kernel = {
|
||||
config = {
|
||||
|
||||
NETFILTER_XT_MATCH_CONNTRACK = "y";
|
||||
|
||||
IP6_NF_IPTABLES = "y"; # do we still need these
|
||||
IP_NF_IPTABLES = "y"; # if using nftables directly
|
||||
|
||||
# these are copied from rotuer and need review.
|
||||
# we're not running a firewall, so why do we need
|
||||
# nftables config?
|
||||
IP_NF_NAT = "y";
|
||||
IP_NF_TARGET_MASQUERADE = "y";
|
||||
NETFILTER = "y";
|
||||
NETFILTER_ADVANCED = "y";
|
||||
NETFILTER_XTABLES = "y";
|
||||
|
||||
NFT_COMPAT = "y";
|
||||
NFT_CT = "y";
|
||||
NFT_LOG = "y";
|
||||
NFT_MASQ = "y";
|
||||
NFT_NAT = "y";
|
||||
NFT_REJECT = "y";
|
||||
NFT_REJECT_INET = "y";
|
||||
|
||||
NF_CONNTRACK = "y";
|
||||
NF_NAT = "y";
|
||||
NF_NAT_MASQUERADE = "y";
|
||||
NF_TABLES = "y";
|
||||
NF_TABLES_INET = "y";
|
||||
NF_TABLES_IPV4 = "y";
|
||||
NF_TABLES_IPV6 = "y";
|
||||
};
|
||||
};
|
||||
|
||||
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;
|
||||
services.sshd = svc.ssh.build {};
|
||||
users.root.passwd = lib.mkForce secrets.root.passwd;
|
||||
defaultProfile.packages = with pkgs; [nftables strace tcpdump swconfig];
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
{ config, pkgs, lib, ... } :
|
||||
{ config, pkgs, ... } :
|
||||
let
|
||||
inherit (pkgs) serviceFns;
|
||||
svc = config.system.service;
|
||||
|
||||
in rec {
|
||||
|
@ -1,6 +1,5 @@
|
||||
{ config, pkgs, lib, ... } :
|
||||
{ config, pkgs, ... } :
|
||||
let
|
||||
inherit (pkgs) serviceFns;
|
||||
svc = config.system.service;
|
||||
|
||||
in rec {
|
||||
|
81
examples/module-https-proxy.nix
Normal file
81
examples/module-https-proxy.nix
Normal file
@ -0,0 +1,81 @@
|
||||
# This is "contrib"-level code. This module solves a particular
|
||||
# problem for my particular setup and is provided as is, as an example
|
||||
# of how you might write something similar if you had a similar
|
||||
# problem. Don't expect it to work unmolested in your setup (you will
|
||||
# at the absolute minimum have to change the domain name), nor even to
|
||||
# continue to exist without possibly being changed beyond recognition.
|
||||
|
||||
# The computers on my LAN have globally unique routable IPv6
|
||||
# addresses, but I have only one public IPv4 address. I want to expose
|
||||
# HTTPS services to the Internet _whatever_ machine is hosting them,
|
||||
# so I publish an AAAA record to the machine itself, and an A record
|
||||
# to the public v4 address of the router which is running this nginx.
|
||||
# This nginx checks the SNI in the incoming connection and forwards
|
||||
# the connection to the (IPv6 address of the) same hostname
|
||||
|
||||
# See https://ww.telent.net/2020/12/2/six_into_4_won_t_go for
|
||||
# the original solution to this problem, which used sniproxy (now
|
||||
# unmaintained) instead of nginx
|
||||
|
||||
{ config, pkgs, ... }:
|
||||
let
|
||||
inherit (pkgs.liminix.services) longrun;
|
||||
inherit (pkgs) writeText;
|
||||
in {
|
||||
config = {
|
||||
users.nginx = {
|
||||
uid = 52; gid= 52;
|
||||
dir = "/run/";
|
||||
shell = "/bin/false";
|
||||
};
|
||||
groups.nginx = {
|
||||
gid= 52;
|
||||
usernames = ["nginx"];
|
||||
};
|
||||
|
||||
services.sniproxy =
|
||||
let
|
||||
nginx = pkgs.nginx-small.override {
|
||||
pcre = null;
|
||||
zlib = null;
|
||||
options = [
|
||||
"stream"
|
||||
"stream_ssl_module" "stream_ssl_preread_module"
|
||||
"stream_map_module"
|
||||
];
|
||||
};
|
||||
conf = writeText "nginx.conf" ''
|
||||
worker_processes auto;
|
||||
error_log /proc/self/fd/1 info;
|
||||
pid /dev/null;
|
||||
user nginx;
|
||||
daemon off;
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
stream {
|
||||
log_format proxy '$remote_addr -> $ssl_target';
|
||||
access_log /proc/self/fd/1 proxy;
|
||||
map $ssl_preread_server_name $ssl_target {
|
||||
hostnames;
|
||||
.telent.net $ssl_preread_server_name:443;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443;
|
||||
resolver 127.0.0.1 ipv6=on ipv4=off;
|
||||
resolver_timeout 1s;
|
||||
proxy_pass $ssl_target;
|
||||
ssl_preread on;
|
||||
}
|
||||
}
|
||||
'';
|
||||
in longrun {
|
||||
name = "sniproxy";
|
||||
run = ''
|
||||
${nginx}/bin/nginx -c ${conf}
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
119
examples/nwa50ax-ap.nix
Normal file
119
examples/nwa50ax-ap.nix
Normal file
@ -0,0 +1,119 @@
|
||||
{ config, pkgs, ... } :
|
||||
let
|
||||
inherit (pkgs.liminix.services) target;
|
||||
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 ];
|
||||
}
|
113
examples/recovery.nix
Normal file
113
examples/recovery.nix
Normal file
@ -0,0 +1,113 @@
|
||||
{ config, pkgs, lib, ... } :
|
||||
let
|
||||
inherit (pkgs) serviceFns;
|
||||
svc = config.system.service;
|
||||
inherit (pkgs.pseudofile) dir symlink;
|
||||
inherit (pkgs.liminix.services) oneshot target;
|
||||
some-util-linux = pkgs.runCommand "some-util-linux" { } ''
|
||||
mkdir -p $out/bin
|
||||
cd ${pkgs.util-linux-small}/bin
|
||||
cp fdisk sfdisk mkswap $out/bin
|
||||
'';
|
||||
in rec {
|
||||
imports = [
|
||||
../modules/network
|
||||
../modules/ssh
|
||||
../modules/usb.nix
|
||||
../modules/schnapps
|
||||
../modules/outputs/mtdimage.nix
|
||||
../modules/outputs/mbrimage.nix
|
||||
../modules/outputs/tftpboot.nix
|
||||
../modules/outputs/ubifs.nix
|
||||
../modules/outputs/ubimage.nix
|
||||
../modules/outputs/jffs2.nix
|
||||
../modules/outputs/ext4fs.nix
|
||||
];
|
||||
|
||||
kernel.config = {
|
||||
BTRFS_FS = "y";
|
||||
};
|
||||
|
||||
boot.tftp = {
|
||||
ipaddr = "10.0.0.8"; # my address
|
||||
serverip = "10.0.0.1"; # build machine or other tftp server
|
||||
freeSpaceBytes = 1024 * 1024 * 4;
|
||||
};
|
||||
boot.loader.extlinux.enable = true;
|
||||
|
||||
hostname = "liminix-recovery";
|
||||
|
||||
services.dhcpc = svc.network.dhcp.client.build {
|
||||
interface = config.hardware.networkInterfaces.lan2;
|
||||
|
||||
# don't start DHCP until the hostname is configured,
|
||||
# so it can identify itself to the DHCP server
|
||||
dependencies = [ config.services.hostname ];
|
||||
};
|
||||
|
||||
services.sshd = svc.ssh.build {
|
||||
dependencies = [ config.services.growfs ];
|
||||
};
|
||||
|
||||
services.defaultroute4 = svc.network.route.build {
|
||||
via = "$(output ${services.dhcpc} router)";
|
||||
target = "default";
|
||||
dependencies = [ services.dhcpc ];
|
||||
};
|
||||
|
||||
services.resolvconf = oneshot rec {
|
||||
dependencies = [ services.dhcpc ];
|
||||
name = "resolvconf";
|
||||
up = ''
|
||||
( in_outputs ${name}
|
||||
for i in $(output ${services.dhcpc} dns); do
|
||||
echo "nameserver $i" > resolv.conf
|
||||
done
|
||||
)
|
||||
'';
|
||||
};
|
||||
|
||||
services.growfs = let name = "growfs"; in oneshot {
|
||||
inherit name;
|
||||
up = ''
|
||||
device=$(grep /persist /proc/1/mountinfo | cut -f9 -d' ')
|
||||
${pkgs.e2fsprogs}/bin/resize2fs $device
|
||||
'';
|
||||
};
|
||||
|
||||
filesystem = dir {
|
||||
etc = dir {
|
||||
"resolv.conf" = symlink "${services.resolvconf}/.outputs/resolv.conf";
|
||||
};
|
||||
mnt = dir {};
|
||||
};
|
||||
rootfsType = "ext4";
|
||||
|
||||
# sda is most likely correct for the boot-from-USB case. For tftp
|
||||
# it's overridden by the boot.scr anyway, so maybe it all works out
|
||||
hardware.rootDevice = lib.mkForce "/dev/sda1";
|
||||
|
||||
users.root = {
|
||||
# the password is "secret". Use mkpasswd -m sha512crypt to
|
||||
# create this hashed password string
|
||||
passwd = "$6$y7WZ5hM6l5nriLmo$5AJlmzQZ6WA.7uBC7S8L4o19ESR28Dg25v64/vDvvCN01Ms9QoHeGByj8lGlJ4/b.dbwR9Hq2KXurSnLigt1W1";
|
||||
|
||||
|
||||
openssh.authorizedKeys.keys =
|
||||
let fromBuild =
|
||||
(builtins.readFile
|
||||
((builtins.toPath (builtins.getEnv "HOME")) + "/.ssh/authorized_keys")
|
||||
);
|
||||
in lib.splitString "\n" fromBuild;
|
||||
};
|
||||
|
||||
defaultProfile.packages = with pkgs; [
|
||||
e2fsprogs # ext4
|
||||
btrfs-progs
|
||||
mtdutils # mtd, jffs2, ubifs
|
||||
dtc # you never know when you might need device tree stuff
|
||||
some-util-linux
|
||||
libubootenv # fw_{set,print}env
|
||||
pciutils
|
||||
];
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
rec {
|
||||
{
|
||||
wpa_passphrase = "you bring light in";
|
||||
ssid = "liminix";
|
||||
l2tp = {
|
||||
name = "abcde@a.1";
|
||||
password = "NotMyIspPassword";
|
||||
@ -7,8 +8,10 @@ rec {
|
||||
root = {
|
||||
# mkpasswd -m sha512crypt
|
||||
passwd = "$6$6pt0mpbgcB7kC2RJ$kSBoCYGyi1.qxt7dqmexLj1l8E6oTZJZmfGyJSsMYMW.jlsETxdgQSdv6ptOYDM7DHAwf6vLG0pz3UD31XBfC1";
|
||||
openssh.authorizedKeys.keys = [
|
||||
];
|
||||
openssh.authorizedKeys.keys = [ ];
|
||||
};
|
||||
|
||||
lan = {
|
||||
prefix = "10.8.0";
|
||||
};
|
||||
root_password = root.passwd;
|
||||
}
|
||||
|
@ -1,25 +1,19 @@
|
||||
# This is not part of Liminix per se. This is my "scratchpad"
|
||||
# configuration for the device I'm testing with.
|
||||
#
|
||||
# Parts of it do do things that Liminix eventually needs to do, but
|
||||
# don't look in here for solutions - just for identifying the
|
||||
# problems.
|
||||
# This is an example that uses the "gateway" profile to create a
|
||||
# "typical home wireless router" configuration suitable for a Gl.inet
|
||||
# gl-ar750 router. It should be fairly simple to edit it for other
|
||||
# devices: mostly you will need to attend to the number of wlan and lan
|
||||
# interfaces
|
||||
|
||||
|
||||
{ config, pkgs, lib, ... } :
|
||||
{ config, pkgs, lib, modulesPath, ... } :
|
||||
let
|
||||
secrets = import ./rotuer-secrets.nix;
|
||||
inherit (pkgs.liminix.services) oneshot longrun bundle;
|
||||
inherit (pkgs) serviceFns;
|
||||
secrets = {
|
||||
domainName = "fake.liminix.org";
|
||||
firewallRules = { };
|
||||
} // (import ./rotuer-secrets.nix);
|
||||
svc = config.system.service;
|
||||
wirelessConfig = {
|
||||
wirelessConfig = {
|
||||
country_code = "GB";
|
||||
inherit (secrets) 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
|
||||
wmm_enabled = 1;
|
||||
};
|
||||
|
||||
@ -33,53 +27,71 @@ in rec {
|
||||
};
|
||||
|
||||
imports = [
|
||||
../modules/wlan.nix
|
||||
../modules/network
|
||||
../modules/ppp
|
||||
../modules/dnsmasq
|
||||
../modules/dhcp6c
|
||||
../modules/firewall
|
||||
../modules/hostapd
|
||||
../modules/bridge
|
||||
../modules/ntp
|
||||
../modules/ssh
|
||||
"${modulesPath}/profiles/gateway.nix"
|
||||
];
|
||||
hostname = "rotuer";
|
||||
|
||||
services.hostap = svc.hostapd.build {
|
||||
interface = config.hardware.networkInterfaces.wlan;
|
||||
params = {
|
||||
ssid = "liminix";
|
||||
hw_mode="g";
|
||||
channel = "2";
|
||||
ieee80211n = 1;
|
||||
} // wirelessConfig;
|
||||
};
|
||||
profile.gateway = {
|
||||
lan = {
|
||||
interfaces = with config.hardware.networkInterfaces;
|
||||
[
|
||||
# EDIT: these are the interfaces exposed by the gl.inet gl-ar750:
|
||||
# if your device has more or differently named lan interfaces,
|
||||
# specify them here
|
||||
wlan wlan5
|
||||
lan
|
||||
];
|
||||
inherit (secrets.lan) prefix;
|
||||
address = {
|
||||
family = "inet"; address ="${secrets.lan.prefix}.1"; prefixLength = 24;
|
||||
};
|
||||
dhcp = {
|
||||
start = 10;
|
||||
end = 240;
|
||||
hosts = { } // lib.optionalAttrs (builtins.pathExists ./static-leases.nix) (import ./static-leases.nix);
|
||||
localDomain = "lan";
|
||||
};
|
||||
};
|
||||
wan = {
|
||||
# wan interface depends on your upstream - could be dhcp, static
|
||||
# ethernet, a pppoe, ppp over serial, a complicated bonded
|
||||
# failover ... who knows what else?
|
||||
interface = svc.pppoe.build {
|
||||
interface = config.hardware.networkInterfaces.wan;
|
||||
username = secrets.l2tp.name;
|
||||
password = secrets.l2tp.password;
|
||||
};
|
||||
# once the wan has ipv4 connnectivity, should we run dhcp6
|
||||
# client to potentially get an address range ("prefix
|
||||
# delegation")
|
||||
dhcp6.enable = true;
|
||||
};
|
||||
firewall = {
|
||||
enable = true;
|
||||
rules = secrets.firewallRules;
|
||||
};
|
||||
wireless.networks = {
|
||||
# EDIT: if you have more or fewer wireless radios, here is where
|
||||
# you need to say so. hostapd tuning is hardware-specific and
|
||||
# left as an exercise for the reader :-).
|
||||
|
||||
services.hostap5 = svc.hostapd.build {
|
||||
interface = config.hardware.networkInterfaces.wlan5;
|
||||
params = rec {
|
||||
ssid = "liminix_5";
|
||||
hw_mode="a";
|
||||
channel = 36;
|
||||
ht_capab = "[HT40+]";
|
||||
vht_oper_chwidth = 1;
|
||||
vht_oper_centr_freq_seg0_idx = channel + 6;
|
||||
ieee80211ac = 1;
|
||||
} // wirelessConfig;
|
||||
};
|
||||
|
||||
services.int = svc.network.address.build {
|
||||
interface = svc.bridge.primary.build { ifname = "int"; };
|
||||
family = "inet"; address ="10.8.0.1"; prefixLength = 16;
|
||||
};
|
||||
|
||||
services.bridge = svc.bridge.members.build {
|
||||
primary = services.int;
|
||||
members = with config.hardware.networkInterfaces;
|
||||
[ wlan
|
||||
wlan5
|
||||
lan ];
|
||||
"${secrets.ssid}" = {
|
||||
interface = config.hardware.networkInterfaces.wlan;
|
||||
hw_mode = "g";
|
||||
channel = "2";
|
||||
ieee80211n = 1;
|
||||
} // wirelessConfig;
|
||||
"${secrets.ssid}5" = rec {
|
||||
interface = config.hardware.networkInterfaces.wlan5;
|
||||
hw_mode = "a";
|
||||
channel = 36;
|
||||
ht_capab = "[HT40+]";
|
||||
vht_oper_chwidth = 1;
|
||||
vht_oper_centr_freq_seg0_idx = channel + 6;
|
||||
ieee80211n = 1;
|
||||
ieee80211ac = 1;
|
||||
} // wirelessConfig;
|
||||
};
|
||||
};
|
||||
|
||||
services.ntp = svc.ntp.build {
|
||||
@ -91,98 +103,20 @@ in rec {
|
||||
|
||||
users.root = secrets.root;
|
||||
|
||||
services.dns =
|
||||
let interface = services.int;
|
||||
in svc.dnsmasq.build {
|
||||
resolvconf = services.resolvconf;
|
||||
inherit interface;
|
||||
ranges = [
|
||||
"10.8.0.10,10.8.0.240"
|
||||
# ra-stateless: sends router advertisements with the O and A
|
||||
# bits set, and provides a stateless DHCP service. The client
|
||||
# will use a SLAAC address, and use DHCP for other
|
||||
# configuration information.
|
||||
"::,constructor:$(output ${interface} ifname),ra-stateless"
|
||||
];
|
||||
|
||||
# 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";
|
||||
};
|
||||
|
||||
services.wan = svc.pppoe.build {
|
||||
interface = config.hardware.networkInterfaces.wan;
|
||||
ppp-options = [
|
||||
"debug" "+ipv6" "noauth"
|
||||
"name" secrets.l2tp.name
|
||||
"password" secrets.l2tp.password
|
||||
];
|
||||
};
|
||||
|
||||
services.resolvconf = oneshot rec {
|
||||
dependencies = [ services.wan ];
|
||||
name = "resolvconf";
|
||||
up = ''
|
||||
. ${serviceFns}
|
||||
( in_outputs ${name}
|
||||
echo "nameserver $(output ${services.wan} ns1)" > resolv.conf
|
||||
echo "nameserver $(output ${services.wan} ns2)" >> resolv.conf
|
||||
chmod 0444 resolv.conf
|
||||
)
|
||||
'';
|
||||
};
|
||||
|
||||
filesystem =
|
||||
let inherit (pkgs.pseudofile) dir symlink;
|
||||
in dir {
|
||||
etc = dir {
|
||||
"resolv.conf" = symlink "${services.resolvconf}/.outputs/resolv.conf";
|
||||
};
|
||||
};
|
||||
|
||||
services.defaultroute4 = svc.network.route.build {
|
||||
via = "$(output ${services.wan} address)";
|
||||
target = "default";
|
||||
dependencies = [ services.wan ];
|
||||
};
|
||||
|
||||
services.defaultroute6 = svc.network.route.build {
|
||||
via = "$(output ${services.wan} ipv6-peer-address)";
|
||||
target = "default";
|
||||
interface = services.wan;
|
||||
};
|
||||
|
||||
services.firewall = svc.firewall.build {
|
||||
ruleset = import ./demo-firewall.nix;
|
||||
};
|
||||
|
||||
services.packet_forwarding = svc.network.forward.build { };
|
||||
|
||||
services.dhcp6c =
|
||||
let client = svc.dhcp6c.client.build {
|
||||
interface = services.wan;
|
||||
};
|
||||
in bundle {
|
||||
name = "dhcp6c";
|
||||
contents = [
|
||||
(svc.dhcp6c.prefix.build {
|
||||
inherit client;
|
||||
interface = services.int;
|
||||
})
|
||||
(svc.dhcp6c.address.build {
|
||||
inherit client;
|
||||
interface = services.wan;
|
||||
})
|
||||
];
|
||||
};
|
||||
|
||||
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";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
211
examples/router-with-l2tp.nix
Normal file
211
examples/router-with-l2tp.nix
Normal file
@ -0,0 +1,211 @@
|
||||
# A demonstration config for a home/soho router with PPPoE upstream
|
||||
# and fallback to an L2TP tunnel over a USB WWAN device
|
||||
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}: let
|
||||
secrets = import ./extneder-secrets.nix;
|
||||
rsecrets = import ./rotuer-secrets.nix;
|
||||
|
||||
# https://support.aa.net.uk/Category:Incoming_L2TP says:
|
||||
# "Please use the DNS name (l2tp.aa.net.uk) instead of hardcoding an
|
||||
# IP address; IP addresses can and do change. If you have to use an
|
||||
# IP, use 194.4.172.12, but do check the DNS for l2tp.aa.net.uk in
|
||||
# case it changes."
|
||||
|
||||
# but (1) we don't want to use the wwan stick's dns as our main
|
||||
# resolver: it's provided by some mobile ISP and they aren't
|
||||
# necessarily the best at providing unfettered services without
|
||||
# deciding to do something weird; (2) it's not simple to arrange
|
||||
# that xl2tpd gets a different resolver than every other process;
|
||||
# (3) there's no way to specify an lns address to xl2tpd at runtime
|
||||
# except by rewriting its config file. So what we will do is lookup
|
||||
# the lns hostname using the mobile ISP's dns server and then refuse
|
||||
# to start l2tp unless the expected lns address is one of the
|
||||
# addresses returned. I think this satisfies "do check the DNS"
|
||||
|
||||
lns = { hostname = "l2tp.aaisp.net.uk"; address = "194.4.172.12"; };
|
||||
|
||||
inherit (pkgs.liminix.services) oneshot longrun target;
|
||||
inherit (pkgs.liminix) outputRef;
|
||||
inherit (pkgs.pseudofile) dir symlink;
|
||||
inherit (pkgs) serviceFns;
|
||||
svc = config.system.service;
|
||||
wirelessConfig = {
|
||||
country_code = "GB";
|
||||
inherit (rsecrets) wpa_passphrase;
|
||||
wmm_enabled = 1;
|
||||
};
|
||||
in rec {
|
||||
boot = {
|
||||
tftp = {
|
||||
serverip = "10.0.0.1";
|
||||
ipaddr = "10.0.0.8";
|
||||
};
|
||||
};
|
||||
|
||||
imports = [
|
||||
../modules/wwan
|
||||
../modules/network
|
||||
../modules/ssh
|
||||
../modules/usb.nix
|
||||
../modules/ppp
|
||||
../modules/round-robin
|
||||
../modules/health-check
|
||||
../modules/secrets
|
||||
../modules/profiles/gateway.nix
|
||||
];
|
||||
hostname = "thing";
|
||||
|
||||
services.wan-address-for-secrets = svc.network.address.build {
|
||||
interface = config.hardware.networkInterfaces.wan;
|
||||
family = "inet"; address ="10.0.0.10"; prefixLength = 24;
|
||||
};
|
||||
|
||||
services.secrets = svc.secrets.outboard.build {
|
||||
name = "secret-service";
|
||||
url = "http://10.0.0.1/liminix/examples/real-secrets.json";
|
||||
username = "demo";
|
||||
password = "demo";
|
||||
interval = 5;
|
||||
dependencies = [ services.wan-address-for-secrets ];
|
||||
};
|
||||
|
||||
services.wwan = svc.wwan.huawei-e3372.build {
|
||||
apn = "data.uk";
|
||||
username = "user";
|
||||
password = "one2one";
|
||||
authType = "chap";
|
||||
};
|
||||
|
||||
profile.gateway = {
|
||||
lan = {
|
||||
interfaces = with config.hardware.networkInterfaces;
|
||||
[
|
||||
# EDIT: these are the interfaces exposed by the gl.inet gl-ar750:
|
||||
# if your device has more or differently named lan interfaces,
|
||||
# specify them here
|
||||
wlan wlan5
|
||||
lan
|
||||
];
|
||||
inherit (rsecrets.lan) prefix;
|
||||
address = {
|
||||
family = "inet"; address ="${rsecrets.lan.prefix}.1"; prefixLength = 24;
|
||||
};
|
||||
dhcp = {
|
||||
start = 10;
|
||||
end = 240;
|
||||
hosts = { } // lib.optionalAttrs (builtins.pathExists ./static-leases.nix) (import ./static-leases.nix);
|
||||
localDomain = "lan";
|
||||
};
|
||||
};
|
||||
wan =
|
||||
let
|
||||
secret = outputRef config.services.secrets;
|
||||
username = secret "ppp/username";
|
||||
password = secret "ppp/password";
|
||||
in {
|
||||
interface =
|
||||
let
|
||||
pppoe = svc.pppoe.build {
|
||||
interface = config.hardware.networkInterfaces.wan;
|
||||
debug = true;
|
||||
inherit username password;
|
||||
};
|
||||
|
||||
l2tp =
|
||||
let
|
||||
check-address = oneshot rec {
|
||||
name = "check-lns-address";
|
||||
up = "grep -Fx ${lns.address} $(output_path ${services.lns-address} addresses)";
|
||||
dependencies = [ services.lns-address ];
|
||||
};
|
||||
route = svc.network.route.build {
|
||||
via = "$(output ${services.bootstrap-dhcpc} router)";
|
||||
target = lns.address;
|
||||
dependencies = [services.bootstrap-dhcpc check-address];
|
||||
};
|
||||
l2tpd= svc.l2tp.build {
|
||||
lns = lns.address;
|
||||
inherit username password;
|
||||
dependencies = [config.services.lns-address route check-address];
|
||||
};
|
||||
in
|
||||
svc.health-check.build {
|
||||
service = l2tpd;
|
||||
threshold = 3;
|
||||
interval = 2;
|
||||
healthCheck = pkgs.writeAshScript "ping-check" {} "ping 1.1.1.1";
|
||||
};
|
||||
in svc.round-robin.build {
|
||||
name = "wan";
|
||||
services = [
|
||||
pppoe
|
||||
l2tp
|
||||
];
|
||||
};
|
||||
dhcp6.enable = true;
|
||||
};
|
||||
|
||||
wireless.networks = {
|
||||
"${rsecrets.ssid}" = {
|
||||
interface = config.hardware.networkInterfaces.wlan;
|
||||
hw_mode = "g";
|
||||
channel = "6";
|
||||
ieee80211n = 1;
|
||||
} // wirelessConfig // {
|
||||
wpa_passphrase = outputRef config.services.secrets "wpa_passphrase";
|
||||
};
|
||||
|
||||
"${rsecrets.ssid}5" = rec {
|
||||
interface = config.hardware.networkInterfaces.wlan5;
|
||||
hw_mode = "a";
|
||||
channel = 36;
|
||||
ht_capab = "[HT40+]";
|
||||
vht_oper_chwidth = 1;
|
||||
vht_oper_centr_freq_seg0_idx = channel + 6;
|
||||
ieee80211n = 1;
|
||||
ieee80211ac = 1;
|
||||
} // wirelessConfig // {
|
||||
wpa_passphrase = outputRef config.services.secrets "wpa_passphrase";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
services.bootstrap-dhcpc = svc.network.dhcp.client.build {
|
||||
interface = config.services.wwan;
|
||||
dependencies = [ config.services.hostname ];
|
||||
};
|
||||
|
||||
services.sshd = svc.ssh.build {
|
||||
authorizedKeys = outputRef config.services.secrets "ssh/authorizedKeys";
|
||||
};
|
||||
|
||||
services.lns-address = let
|
||||
ns = "$(output_word ${services.bootstrap-dhcpc} dns 1)";
|
||||
route-to-bootstrap-nameserver = svc.network.route.build {
|
||||
via = "$(output ${services.bootstrap-dhcpc} router)";
|
||||
target = ns;
|
||||
dependencies = [services.bootstrap-dhcpc];
|
||||
};
|
||||
in oneshot rec {
|
||||
name = "resolve-l2tp-server";
|
||||
dependencies = [ services.bootstrap-dhcpc route-to-bootstrap-nameserver ];
|
||||
up = ''
|
||||
(in_outputs ${name}
|
||||
DNSCACHEIP="${ns}" ${pkgs.s6-dns}/bin/s6-dnsip4 ${lns.hostname} \
|
||||
> addresses
|
||||
)
|
||||
'';
|
||||
};
|
||||
|
||||
users.root = rsecrets.root;
|
||||
|
||||
programs.busybox.options = {
|
||||
FEATURE_FANCY_TAIL = "y";
|
||||
};
|
||||
|
||||
}
|
19
examples/secrets.json
Normal file
19
examples/secrets.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"wpa_passphrase": "you bring light in",
|
||||
"ssid": "liminix",
|
||||
"l2tp": {
|
||||
"name": "abcde@a.1",
|
||||
"password": "NotMyIspPassword"
|
||||
},
|
||||
"root": {
|
||||
"passwd": "$6$6pt0mpbgcB7kC2RJ$kSBoCYGyi1.qxt7dqmexLj1l8E6oTZJZmfGyJSsMYMW.jlsETxdgQSdv6ptOYDM7DHAwf6vLG0pz3UD31XBfC1",
|
||||
"openssh": {
|
||||
"authorizedKeys": {
|
||||
"keys": [ ]
|
||||
}
|
||||
}
|
||||
},
|
||||
"lan": {
|
||||
"prefix": "10.8.0"
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
{ config, pkgs, lib, lim, ... } :
|
||||
{ config, pkgs, lim, ... } :
|
||||
let
|
||||
inherit (pkgs) serviceFns;
|
||||
svc = config.system.service;
|
||||
|
||||
in rec {
|
||||
|
@ -9,28 +9,28 @@
|
||||
./busybox.nix
|
||||
./dhcp6c
|
||||
./dnsmasq
|
||||
./outputs/ext4fs.nix
|
||||
./firewall
|
||||
./hardware.nix
|
||||
./hostapd
|
||||
./hostname.nix
|
||||
./outputs/initramfs.nix
|
||||
./outputs/jffs2.nix
|
||||
./kernel.nix
|
||||
./outputs/kexecboot.nix
|
||||
./kernel
|
||||
./mdevd.nix
|
||||
./mount
|
||||
./network
|
||||
./ntp
|
||||
./outputs.nix
|
||||
./outputs/vmroot.nix
|
||||
./outputs/ubimage.nix
|
||||
./outputs/ext4fs.nix
|
||||
./outputs/initramfs.nix
|
||||
./outputs/jffs2.nix
|
||||
./outputs/kexecboot.nix
|
||||
./outputs/mtdimage.nix
|
||||
./ppp
|
||||
./ramdisk.nix
|
||||
./squashfs.nix
|
||||
./ssh
|
||||
./outputs/tftpboot.nix
|
||||
./outputs/ubifs.nix
|
||||
./outputs/ubimage.nix
|
||||
./outputs/vmroot.nix
|
||||
./ppp
|
||||
./ramdisk.nix
|
||||
./ssh
|
||||
./users.nix
|
||||
./vlan
|
||||
./watchdog
|
||||
|
@ -1,4 +1,4 @@
|
||||
{ lib, lim, pkgs, config, ...}:
|
||||
{ lim, pkgs, config, ...}:
|
||||
{
|
||||
config = {
|
||||
kernel.config = {
|
||||
|
@ -1,4 +1,4 @@
|
||||
{ lib, lim, pkgs, config, ...}:
|
||||
{ lim, pkgs, config, ...}:
|
||||
{
|
||||
config = {
|
||||
kernel.config = {
|
||||
|
@ -1,4 +1,4 @@
|
||||
{ lib, pkgs, config, lim, ...}:
|
||||
{ config, lim, ...}:
|
||||
{
|
||||
config = {
|
||||
kernel.config = {
|
||||
|
@ -1,4 +1,4 @@
|
||||
{ lib, pkgs, config, ...}:
|
||||
{ pkgs, config, ...}:
|
||||
{
|
||||
imports = [ ./mips.nix ];
|
||||
config = {
|
||||
|
@ -1,4 +1,4 @@
|
||||
{ lib, pkgs, config, ...}:
|
||||
{ config, ...}:
|
||||
{
|
||||
imports = [ ./mips.nix ];
|
||||
config = {
|
||||
|
@ -4,17 +4,12 @@
|
||||
|
||||
{ lib, pkgs, config, ...}:
|
||||
let
|
||||
inherit (lib) mkEnableOption mkOption types isDerivation hasAttr ;
|
||||
inherit (lib) mkOption types;
|
||||
inherit (pkgs.pseudofile) dir symlink;
|
||||
inherit (pkgs.liminix.networking) address interface;
|
||||
inherit (pkgs.liminix.services) bundle;
|
||||
|
||||
type_service = pkgs.liminix.lib.types.service;
|
||||
|
||||
in {
|
||||
imports = [
|
||||
./kernel.nix # kernel is a separate module for doc purposes
|
||||
];
|
||||
options = {
|
||||
defaultProfile = {
|
||||
packages = mkOption {
|
||||
@ -29,6 +24,10 @@ in {
|
||||
services = mkOption {
|
||||
type = types.attrsOf type_service;
|
||||
};
|
||||
system.callService = mkOption {
|
||||
type = types.functionTo (types.functionTo types.anything);
|
||||
};
|
||||
|
||||
filesystem = mkOption {
|
||||
type = types.anything;
|
||||
description = ''
|
||||
@ -37,23 +36,47 @@ in {
|
||||
'';
|
||||
# internal = true; # probably a good case to make this internal
|
||||
};
|
||||
rootfsType = mkOption {
|
||||
rootfsType = mkOption {
|
||||
default = "squashfs";
|
||||
type = types.enum [
|
||||
"btrfs"
|
||||
"ext4"
|
||||
"jffs2"
|
||||
"squashfs"
|
||||
"ubifs"
|
||||
];
|
||||
};
|
||||
rootOptions = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
};
|
||||
|
||||
boot = {
|
||||
commandLine = mkOption {
|
||||
type = types.listOf types.nonEmptyStr;
|
||||
default = [];
|
||||
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"];
|
||||
type = types.enum [
|
||||
"fit"
|
||||
"uimage"
|
||||
];
|
||||
default = "uimage";
|
||||
};
|
||||
tftp = {
|
||||
@ -69,7 +92,7 @@ in {
|
||||
};
|
||||
# These names match the uboot environment variables. I reserve
|
||||
# the right to change them if I think of better ones.
|
||||
ipaddr = mkOption {
|
||||
ipaddr = mkOption {
|
||||
type = types.str;
|
||||
description = ''
|
||||
Our IP address to use when creating scripts to
|
||||
@ -94,7 +117,31 @@ in {
|
||||
"root=${config.hardware.rootDevice}"
|
||||
"rootfstype=${config.rootfsType}"
|
||||
"fw_devlink=off"
|
||||
];
|
||||
] ++ lib.optional (config.rootOptions != null) "rootflags=${config.rootOptions}";
|
||||
|
||||
system.callService = path : parameters :
|
||||
let
|
||||
typeChecked = caller: type: value:
|
||||
let
|
||||
inherit (lib) types mergeDefinitions;
|
||||
defs = [{ file = caller; inherit value; }];
|
||||
type' = types.submodule { options = type; };
|
||||
in (mergeDefinitions [] type' defs).mergedValue;
|
||||
cp = lib.callPackageWith(pkgs // { svc = config.system.service; });
|
||||
pkg = cp path {};
|
||||
checkTypes = t : p : typeChecked (builtins.toString path) t p;
|
||||
in {
|
||||
inherit parameters;
|
||||
build = { dependencies ? [], ... } @ args :
|
||||
let
|
||||
s = pkg (checkTypes parameters
|
||||
(builtins.removeAttrs args ["dependencies"]));
|
||||
in s.overrideAttrs (o: {
|
||||
dependencies = dependencies ++ o.dependencies;
|
||||
buildInputs = dependencies ++ o.buildInputs;
|
||||
});
|
||||
};
|
||||
|
||||
users.root = {
|
||||
uid = 0; gid= 0; gecos = "Root of all evaluation";
|
||||
dir = "/home/root/";
|
||||
@ -134,6 +181,7 @@ in {
|
||||
proc = dir {};
|
||||
run = dir {};
|
||||
sys = dir {};
|
||||
tmp = dir {};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -10,10 +10,11 @@
|
||||
{ lib, pkgs, config, ...}:
|
||||
let
|
||||
inherit (lib) mkOption types;
|
||||
inherit (pkgs.liminix.services) oneshot;
|
||||
inherit (pkgs) liminix;
|
||||
in
|
||||
{
|
||||
imports = [ ../ifwait ];
|
||||
|
||||
options = {
|
||||
system.service.bridge = {
|
||||
primary = mkOption { type = liminix.lib.types.serviceDefn; };
|
||||
@ -21,13 +22,13 @@ in
|
||||
};
|
||||
};
|
||||
config.system.service.bridge = {
|
||||
primary = liminix.callService ./primary.nix {
|
||||
primary = config.system.callService ./primary.nix {
|
||||
ifname = mkOption {
|
||||
type = types.str;
|
||||
description = "bridge interface name to create";
|
||||
};
|
||||
};
|
||||
members = liminix.callService ./members.nix {
|
||||
members = config.system.callService ./members.nix {
|
||||
primary = mkOption {
|
||||
type = liminix.lib.types.interface;
|
||||
description = "primary bridge interface";
|
||||
@ -47,5 +48,5 @@ in
|
||||
# a better way to test for the existence of vlan config:
|
||||
# maybe the module should set an `enabled` attribute?
|
||||
BRIDGE_VLAN_FILTERING = "y";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -1,23 +1,28 @@
|
||||
{
|
||||
liminix
|
||||
, ifwait
|
||||
, lib
|
||||
, svc
|
||||
}:
|
||||
{ members, primary } :
|
||||
|
||||
let
|
||||
inherit (liminix.networking) interface;
|
||||
inherit (liminix.services) bundle oneshot;
|
||||
inherit (lib) mkOption types;
|
||||
addif = member :
|
||||
oneshot {
|
||||
name = "${primary.name}.member.${member.name}";
|
||||
up = ''
|
||||
dev=$(output ${member} ifname)
|
||||
${ifwait}/bin/ifwait $dev running && ip link set dev $dev master $(output ${primary} ifname)
|
||||
'';
|
||||
down = "ip link set dev $(output ${member} ifname) nomaster";
|
||||
# how do we get sight of services from here? maybe we need to
|
||||
# implement ifwait as a regualr derivation instead of a
|
||||
# servicedefinition
|
||||
svc.ifwait.build {
|
||||
state = "running";
|
||||
interface = member;
|
||||
dependencies = [ primary member ];
|
||||
service = oneshot {
|
||||
name = "${primary.name}.member.${member.name}";
|
||||
up = ''
|
||||
ip link set dev $(output ${member} ifname) master $(output ${primary} ifname)
|
||||
'';
|
||||
down = "ip link set dev $(output ${member} ifname) nomaster";
|
||||
};
|
||||
};
|
||||
in bundle {
|
||||
name = "${primary.name}.members";
|
||||
|
@ -1,12 +1,10 @@
|
||||
{
|
||||
liminix
|
||||
, ifwait
|
||||
, lib
|
||||
}:
|
||||
{ ifname } :
|
||||
let
|
||||
inherit (liminix.services) bundle oneshot;
|
||||
inherit (lib) mkOption types;
|
||||
inherit (liminix.services) oneshot;
|
||||
in oneshot rec {
|
||||
name = "${ifname}.link";
|
||||
up = ''
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
{ lib, pkgs, config, ...}:
|
||||
let
|
||||
inherit (lib) mkOption mkEnableOption types mapAttrsToList;
|
||||
inherit (lib) mkOption types mapAttrsToList;
|
||||
inherit (pkgs.pseudofile) dir symlink;
|
||||
inherit (lib.strings) toUpper;
|
||||
|
||||
@ -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 = {
|
||||
@ -87,10 +85,13 @@ in {
|
||||
};
|
||||
};
|
||||
filesystem = dir {
|
||||
bin = dir ({
|
||||
busybox = symlink "${busybox}/bin/busybox";
|
||||
sh = symlink "${busybox}/bin/busybox";
|
||||
} // makeLinks);
|
||||
bin = dir (
|
||||
{
|
||||
busybox = symlink "${busybox}/bin/busybox";
|
||||
sh = symlink "${busybox}/bin/busybox";
|
||||
}
|
||||
// makeLinks
|
||||
);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -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 (or (v:output "prefix") []) system))))
|
||||
|
||||
{ : changes : run }
|
||||
|
@ -2,9 +2,9 @@
|
||||
writeFennel
|
||||
, linotify
|
||||
, anoia
|
||||
, lua
|
||||
, lualinux
|
||||
}:
|
||||
writeFennel "acquire-delegated-prefix" {
|
||||
packages = [ linotify anoia lua.pkgs.luafilesystem ];
|
||||
packages = [ linotify anoia lualinux ];
|
||||
mainFunction = "run";
|
||||
} ./acquire-delegated-prefix.fnl
|
||||
|
@ -1,68 +1,124 @@
|
||||
(local subject (require :acquire-wan-address))
|
||||
(local { : view } (require :fennel))
|
||||
(import-macros { : expect= } :anoia.assert)
|
||||
(local { : merge : dup } (require :anoia))
|
||||
|
||||
;; nix-shell --run "cd modules/dhcp6c && fennelrepl acquire-wan-address-test.fnl"
|
||||
|
||||
(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"
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
(macro expect [assertion]
|
||||
(let [msg (.. "expectation failed: " (view assertion))]
|
||||
`(when (not ,assertion)
|
||||
(assert false ,msg))))
|
||||
(local a21
|
||||
{
|
||||
"2001-0-1-2-3" {
|
||||
:address "2001:0:1:2:3"
|
||||
:len "64"
|
||||
:preferred "1800"
|
||||
:valid "5400"
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
(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")
|
||||
|
@ -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 (or (v:output "address") []) system))))
|
||||
|
||||
{ : update-addresses : changes : run }
|
||||
{ : update-addresses : deletions : run }
|
||||
|
@ -2,9 +2,9 @@
|
||||
writeFennel
|
||||
, linotify
|
||||
, anoia
|
||||
, lua
|
||||
, lualinux
|
||||
}:
|
||||
writeFennel "acquire-wan-address" {
|
||||
packages = [ linotify anoia lua.pkgs.luafilesystem ];
|
||||
packages = [ linotify anoia lualinux ];
|
||||
mainFunction = "run";
|
||||
} ./acquire-wan-address.fnl
|
||||
|
@ -1,16 +1,14 @@
|
||||
{
|
||||
liminix
|
||||
, lib
|
||||
, callPackage
|
||||
}:
|
||||
{ client, interface } :
|
||||
let
|
||||
inherit (liminix.services) longrun;
|
||||
inherit (lib) mkOption types;
|
||||
name = "dhcp6c.addr.${client.name}.${interface.name}";
|
||||
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 ];
|
||||
}
|
||||
|
@ -1,19 +1,17 @@
|
||||
{
|
||||
liminix
|
||||
, lib
|
||||
, odhcp6c
|
||||
, odhcp-script
|
||||
}:
|
||||
{ interface } :
|
||||
let
|
||||
inherit (liminix.services) longrun;
|
||||
inherit (lib) mkOption types;
|
||||
name = "dhcp6c.${interface.name}";
|
||||
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)
|
||||
)
|
||||
'';
|
||||
|
@ -12,7 +12,6 @@
|
||||
{ lib, pkgs, config, ...}:
|
||||
let
|
||||
inherit (lib) mkOption types;
|
||||
inherit (pkgs.liminix.services) oneshot;
|
||||
inherit (pkgs) liminix;
|
||||
in
|
||||
{
|
||||
@ -24,13 +23,13 @@ in
|
||||
};
|
||||
};
|
||||
config.system.service.dhcp6c = {
|
||||
client = liminix.callService ./client.nix {
|
||||
client = config.system.callService ./client.nix {
|
||||
interface = mkOption {
|
||||
type = liminix.lib.types.interface;
|
||||
description = "interface (usually WAN) to query for DHCP6";
|
||||
};
|
||||
};
|
||||
address = liminix.callService ./address.nix {
|
||||
address = config.system.callService ./address.nix {
|
||||
client = mkOption {
|
||||
type = types.anything; # liminix.lib.types.service;
|
||||
};
|
||||
@ -39,7 +38,7 @@ in
|
||||
description = "interface to assign the address to";
|
||||
};
|
||||
};
|
||||
prefix = liminix.callService ./prefix.nix {
|
||||
prefix = config.system.callService ./prefix.nix {
|
||||
client = mkOption {
|
||||
type = types.anything; # liminix.lib.types.service;
|
||||
};
|
||||
|
@ -1,16 +1,14 @@
|
||||
{
|
||||
liminix
|
||||
, lib
|
||||
, callPackage
|
||||
}:
|
||||
{ client, interface } :
|
||||
let
|
||||
inherit (liminix.services) longrun;
|
||||
inherit (lib) mkOption types;
|
||||
name = "dhcp6c.prefix.${client.name}.${interface.name}";
|
||||
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 ];
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ in {
|
||||
};
|
||||
};
|
||||
config = {
|
||||
system.service.dnsmasq = liminix.callService ./service.nix {
|
||||
system.service.dnsmasq = config.system.callService ./service.nix {
|
||||
user = mkOption {
|
||||
type = types.str;
|
||||
default = "dnsmasq";
|
||||
|
@ -18,7 +18,7 @@ let
|
||||
name = "${interface.name}.dnsmasq";
|
||||
inherit (liminix.services) longrun;
|
||||
inherit (lib) concatStrings concatStringsSep mapAttrsToList;
|
||||
hostOpt = name : { mac, v4, v6, leasetime } @ attrs:
|
||||
hostOpt = name : { mac, v4, v6, leasetime }:
|
||||
let v6s = concatStrings (map (a : ",[${a}]") v6);
|
||||
in "--dhcp-host=${mac},${v4}${v6s},${name},${builtins.toString leasetime}";
|
||||
in
|
||||
@ -26,7 +26,6 @@ longrun {
|
||||
inherit name;
|
||||
dependencies = [ interface ];
|
||||
run = ''
|
||||
. ${serviceFns}
|
||||
${dnsmasq}/bin/dnsmasq \
|
||||
--user=${user} \
|
||||
--domain=${domain} \
|
||||
@ -41,10 +40,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 \
|
||||
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ in {
|
||||
(drop "icmpv6 type destination-unreachable ct state invalid,untracked")
|
||||
];
|
||||
};
|
||||
|
||||
forward-ip6 = {
|
||||
type = "filter";
|
||||
family = "ip6";
|
||||
@ -89,25 +90,29 @@ in {
|
||||
# accept inbound from the WAN
|
||||
(if allow-incoming
|
||||
then accept "oifname \"int\" iifname \"ppp0\""
|
||||
else "oifname \"int\" iifname \"ppp0\" jump incoming-allowed-ip6"
|
||||
else "iifname \"ppp0\" jump incoming-allowed-ip6"
|
||||
)
|
||||
# allow all outbound and any inbound that's part of a
|
||||
# recognised (outbound-initiated) flow
|
||||
(accept "oifname \"int\" iifname \"ppp0\" ct state established,related")
|
||||
(accept "iifname \"int\" oifname \"ppp0\" ")
|
||||
|
||||
"log prefix \"DENIED CHAIN=forward-ip6 \""
|
||||
];
|
||||
};
|
||||
input-lan = {
|
||||
|
||||
input-ip6-lan = {
|
||||
type = "filter";
|
||||
family = "ip6";
|
||||
|
||||
rules = [
|
||||
(accept "udp dport 547") # dhcp, could restrict to daddr ff02::1:2
|
||||
(accept "udp dport 53") # dns
|
||||
(accept "tcp dport 22")
|
||||
];
|
||||
};
|
||||
|
||||
input-wan = {
|
||||
input-ip6-wan = {
|
||||
type = "filter";
|
||||
family = "ip6";
|
||||
|
||||
@ -123,15 +128,16 @@ in {
|
||||
hook = "input";
|
||||
rules = [
|
||||
(accept "meta l4proto icmpv6")
|
||||
"iifname int jump input-lan"
|
||||
"iifname ppp0 jump input-wan"
|
||||
"iifname int jump input-ip6-lan"
|
||||
"iifname ppp0 jump input-ip6-wan"
|
||||
(if allow-incoming
|
||||
then accept "oifname \"int\" iifname \"ppp0\""
|
||||
else "oifname \"int\" iifname \"ppp0\" jump incoming-allowed-ip6"
|
||||
then accept "iifname \"ppp0\""
|
||||
else "iifname \"ppp0\" jump incoming-allowed-ip6"
|
||||
)
|
||||
# how does this even make sense in an input chain?
|
||||
(accept "oifname \"int\" iifname \"ppp0\" ct state established,related")
|
||||
(accept "iifname \"int\" oifname \"ppp0\" ")
|
||||
(accept "iifname \"ppp0\" ct state established,related")
|
||||
(accept "iifname \"int\" ")
|
||||
"log prefix \"DENIED CHAIN=input-ip6 \""
|
||||
];
|
||||
};
|
||||
|
||||
@ -154,6 +160,7 @@ in {
|
||||
"oifname \"ppp0\" masquerade"
|
||||
];
|
||||
};
|
||||
|
||||
nat-rx = {
|
||||
type = "nat";
|
||||
hook = "prerouting";
|
||||
@ -167,4 +174,71 @@ in {
|
||||
# packet replies. "
|
||||
];
|
||||
};
|
||||
|
||||
# these chains are for rules that have to be present for things to
|
||||
# basically work at all: for example, the router won't issue DHCP
|
||||
# unless it's allowed to receive DHCP requests. For "site policy"
|
||||
# rules you may prefer to use incoming-allowed-ip[46] instead
|
||||
|
||||
input-ip4-lan = {
|
||||
type = "filter";
|
||||
family = "ip";
|
||||
|
||||
rules = [
|
||||
(accept "udp dport 67") # dhcp
|
||||
(accept "udp dport 53") # dns
|
||||
(accept "tcp dport 22") # ssh
|
||||
];
|
||||
};
|
||||
|
||||
input-ip4-wan = {
|
||||
type = "filter";
|
||||
family = "ip";
|
||||
|
||||
rules = [
|
||||
(accept "udp sport 53")
|
||||
];
|
||||
};
|
||||
|
||||
input-ip4 = {
|
||||
type = "filter";
|
||||
family = "ip";
|
||||
policy = "drop";
|
||||
hook = "input";
|
||||
rules = [
|
||||
"iifname lo accept"
|
||||
"icmp type { echo-request, echo-reply } accept"
|
||||
"iifname int jump input-ip4-lan"
|
||||
"iifname ppp0 jump input-ip4-wan"
|
||||
"iifname ppp0 jump incoming-allowed-ip4"
|
||||
"ct state established,related accept"
|
||||
"log prefix \"DENIED CHAIN=input-ip4 \""
|
||||
];
|
||||
};
|
||||
|
||||
forward-ip4 = {
|
||||
type = "filter";
|
||||
family = "ip";
|
||||
policy = "drop";
|
||||
hook = "forward";
|
||||
rules = [
|
||||
"iifname \"int\" accept"
|
||||
"ct state established,related accept"
|
||||
"oifname \"int\" iifname \"ppp0\" jump incoming-allowed-ip4"
|
||||
"log prefix \"DENIED CHAIN=forward-ip4 \""
|
||||
];
|
||||
};
|
||||
|
||||
incoming-allowed-ip4 = {
|
||||
type = "filter";
|
||||
family = "ip";
|
||||
rules = [
|
||||
# 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).
|
||||
];
|
||||
};
|
||||
|
||||
}
|
@ -8,36 +8,42 @@
|
||||
let
|
||||
inherit (lib) mkOption types;
|
||||
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 {
|
||||
NFT_FIB_IPV4 = yes;
|
||||
NFT_FIB_IPV6 = yes;
|
||||
NF_TABLES = yes;
|
||||
NF_CT_PROTO_DCCP = "y";
|
||||
NF_CT_PROTO_SCTP = "y";
|
||||
NF_CT_PROTO_UDPLITE = "y";
|
||||
# NF_CONNTRACK_FTP = yes;
|
||||
NFT_CT = yes;
|
||||
};
|
||||
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"
|
||||
"nf_log_syslog"
|
||||
|
||||
"ip6_tables"
|
||||
"ip_tables"
|
||||
"iptable_nat"
|
||||
"nf_conntrack"
|
||||
"nf_defrag_ipv4"
|
||||
"nf_defrag_ipv6"
|
||||
"nf_log_syslog"
|
||||
"nf_nat"
|
||||
"nf_reject_ipv4"
|
||||
"nf_reject_ipv6"
|
||||
"nf_tables"
|
||||
"nft_chain_nat"
|
||||
"nft_ct"
|
||||
"nft_fib"
|
||||
"nft_fib_ipv4"
|
||||
"nft_fib_ipv6"
|
||||
"nft_log"
|
||||
"nft_masq"
|
||||
"nft_nat"
|
||||
"nft_reject"
|
||||
"nft_reject_inet"
|
||||
"nft_reject_ipv4"
|
||||
"nft_reject_ipv6"
|
||||
"x_tables"
|
||||
"xt_MASQUERADE"
|
||||
"xt_nat"
|
||||
"xt_tcpudp"
|
||||
];
|
||||
kconfig = kconf true;
|
||||
};
|
||||
loadModules = oneshot {
|
||||
name = "firewall-modules";
|
||||
up = "sh ${kmodules}/load.sh";
|
||||
down = "sh ${kmodules}/unload.sh";
|
||||
};
|
||||
in
|
||||
{
|
||||
@ -48,45 +54,57 @@ in
|
||||
};
|
||||
config = {
|
||||
system.service.firewall =
|
||||
let svc = liminix.callService ./service.nix {
|
||||
ruleset = mkOption {
|
||||
let svc = config.system.callService ./service.nix {
|
||||
extraRules = mkOption {
|
||||
type = types.attrsOf types.attrs;
|
||||
description = "firewall ruleset";
|
||||
default = {};
|
||||
};
|
||||
rules = mkOption {
|
||||
type = types.attrsOf types.attrs; # we could usefully tighten this a bit :-)
|
||||
default = import ./default-rules.nix;
|
||||
description = "firewall ruleset";
|
||||
};
|
||||
};
|
||||
in svc // {
|
||||
build = args : (svc.build args) // {
|
||||
dependencies = [ loadModules ] ++ (svc.dependencies or []);
|
||||
};
|
||||
build = args :
|
||||
let args' = args // {
|
||||
dependencies = (args.dependencies or []) ++ [kmodules];
|
||||
};
|
||||
in svc.build args' ;
|
||||
};
|
||||
|
||||
# For historical reasons the kernel config is split between
|
||||
# monolithic options and modules. TODO: go through this list
|
||||
# and see what can be moved into the "kconf" definiton above
|
||||
programs.busybox.applets = [
|
||||
"insmod" "rmmod"
|
||||
];
|
||||
kernel.config = {
|
||||
NETFILTER_XT_MATCH_CONNTRACK = "y";
|
||||
|
||||
IP6_NF_IPTABLES= "y";
|
||||
IP_NF_IPTABLES= "y";
|
||||
|
||||
IP_NF_NAT = "y";
|
||||
IP_NF_TARGET_MASQUERADE = "y";
|
||||
NETFILTER = "y";
|
||||
NETFILTER_ADVANCED = "y";
|
||||
NETFILTER_XTABLES = "y";
|
||||
NETFILTER_NETLINK = "m";
|
||||
NF_CONNTRACK = "m";
|
||||
|
||||
NFT_COMPAT = "y";
|
||||
NFT_CT = "y";
|
||||
NFT_LOG = "y";
|
||||
NFT_MASQ = "y";
|
||||
NFT_NAT = "y";
|
||||
NFT_REJECT = "y";
|
||||
NFT_REJECT_INET = "y";
|
||||
NETLINK_DIAG = "y";
|
||||
|
||||
NF_CONNTRACK = "y";
|
||||
NF_NAT = "y";
|
||||
NF_NAT_MASQUERADE = "y";
|
||||
NF_TABLES= "y";
|
||||
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";
|
||||
|
@ -4,12 +4,10 @@
|
||||
, firewallgen
|
||||
, nftables
|
||||
}:
|
||||
{ ruleset }:
|
||||
{ rules, extraRules }:
|
||||
let
|
||||
inherit (liminix.services) oneshot;
|
||||
inherit (liminix.lib) typeChecked;
|
||||
inherit (lib) mkOption types;
|
||||
script = firewallgen "firewall.nft" ruleset;
|
||||
script = firewallgen "firewall.nft" (lib.recursiveUpdate rules extraRules);
|
||||
in oneshot {
|
||||
name = "firewall";
|
||||
up = script;
|
||||
|
@ -5,15 +5,20 @@
|
||||
## you want to run on it, and would usually be set in the "device" file:
|
||||
## :file:`devices/manuf-model/default.nix`
|
||||
|
||||
|
||||
{ lib, pkgs, config, ...}:
|
||||
{ lib, ... }:
|
||||
let
|
||||
inherit (lib) mkEnableOption mkOption types isDerivation hasAttr ;
|
||||
in {
|
||||
inherit (lib) mkOption types;
|
||||
in
|
||||
{
|
||||
options = {
|
||||
boot = {
|
||||
};
|
||||
boot = { };
|
||||
hardware = {
|
||||
ubi = {
|
||||
minIOSize = mkOption { type = types.str; };
|
||||
logicalEraseBlockSize = mkOption { type = types.str; }; # LEB
|
||||
physicalEraseBlockSize = mkOption { type = types.str; }; # PEB
|
||||
maxLEBcount = mkOption { type = types.str; }; # LEB
|
||||
};
|
||||
dts = {
|
||||
src = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
@ -25,11 +30,16 @@ in {
|
||||
only for QEMU.
|
||||
'';
|
||||
};
|
||||
includes = mkOption {
|
||||
default = [];
|
||||
includePaths = mkOption {
|
||||
default = [ ];
|
||||
description = "List of directories to search for DTS includes (.dtsi files)";
|
||||
type = types.listOf types.path;
|
||||
};
|
||||
includes = mkOption {
|
||||
default = [ ];
|
||||
description = "\"dtsi\" fragments to include in the generated device tree";
|
||||
type = types.listOf types.path;
|
||||
};
|
||||
};
|
||||
defaultOutput = mkOption {
|
||||
description = "\"Default\" output: what gets built for this device when outputs.default is requested. Typically this is \"mtdimage\" or \"vmroot\"";
|
||||
@ -67,6 +77,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
|
||||
@ -76,7 +87,11 @@ in {
|
||||
default = [];
|
||||
example = ["ath9k" "ath10k"];
|
||||
};
|
||||
rootDevice = mkOption { };
|
||||
rootDevice = mkOption {
|
||||
description = "Full path to preferred root device";
|
||||
type = types.str;
|
||||
example = "/dev/mtdblock3";
|
||||
};
|
||||
networkInterfaces = mkOption {
|
||||
type = types.attrsOf types.anything;
|
||||
};
|
||||
|
43
modules/health-check/default.nix
Normal file
43
modules/health-check/default.nix
Normal file
@ -0,0 +1,43 @@
|
||||
## Health check
|
||||
##
|
||||
## Runs a service and a separate periodic health process. When the
|
||||
## health check starts failing over a period of time, kill the service.
|
||||
## (Usually that means the supervisor will restart it, but you can
|
||||
## have other behaviours by e.g. combining this service with a round-robin
|
||||
## for failover)
|
||||
|
||||
|
||||
{ lib, pkgs, config, ...}:
|
||||
let
|
||||
inherit (lib) mkOption types;
|
||||
inherit (pkgs) liminix;
|
||||
# inherit (pkgs.liminix.services) longrun;
|
||||
in {
|
||||
options = {
|
||||
system.service.health-check = mkOption {
|
||||
description = "run a service while periodically checking it is healthy";
|
||||
type = liminix.lib.types.serviceDefn;
|
||||
};
|
||||
};
|
||||
config.system.service.health-check = config.system.callService ./service.nix {
|
||||
service = mkOption {
|
||||
type = liminix.lib.types.service;
|
||||
};
|
||||
interval = mkOption {
|
||||
description = "interval between checks, in seconds";
|
||||
type = types.int;
|
||||
default = 10;
|
||||
example = 10;
|
||||
};
|
||||
threshold = mkOption {
|
||||
description = "number of consecutive failures required for the service to be kicked";
|
||||
type = types.int;
|
||||
example = 3;
|
||||
};
|
||||
healthCheck = mkOption {
|
||||
description = "health check command or script. Expected to exit 0 if the service is healthy or any other exit status otherwise";
|
||||
type = types.path;
|
||||
};
|
||||
};
|
||||
config.programs.busybox.applets = ["expr"];
|
||||
}
|
37
modules/health-check/service.nix
Normal file
37
modules/health-check/service.nix
Normal file
@ -0,0 +1,37 @@
|
||||
{
|
||||
liminix, lib, lim, s6
|
||||
}:
|
||||
{ service, interval, threshold, healthCheck } :
|
||||
let
|
||||
inherit (liminix.services) oneshot longrun;
|
||||
inherit (builtins) toString;
|
||||
inherit (service) name;
|
||||
checker = let name' = "check-${name}"; in longrun {
|
||||
name = name';
|
||||
run = ''
|
||||
fails=0
|
||||
echo waiting for /run/service/${name}
|
||||
${s6}/bin/s6-svwait -U /run/service/${name} || exit
|
||||
while sleep ${toString interval} ; do
|
||||
${healthCheck}
|
||||
if test $? -gt 0; then
|
||||
fails=$(expr $fails + 1)
|
||||
else
|
||||
fails=0
|
||||
fi
|
||||
echo fails $fails/${toString threshold} for ${name}
|
||||
if test "$fails" -gt "${toString threshold}" ; then
|
||||
echo time to die
|
||||
${s6}/bin/s6-svc -r /run/service/${name}
|
||||
echo bounced
|
||||
fails=0
|
||||
echo waiting for /run/service/${name}
|
||||
${s6}/bin/s6-svwait -U /run/service/${name}
|
||||
fi
|
||||
done
|
||||
'';
|
||||
};
|
||||
in service.overrideAttrs(o: {
|
||||
buildInputs = (lim.orEmpty o.buildInputs) ++ [ checker ];
|
||||
dependencies = (lim.orEmpty o.dependencies) ++ [ checker ];
|
||||
})
|
@ -16,13 +16,14 @@ let
|
||||
inherit (lib) mkOption types;
|
||||
inherit (pkgs) liminix;
|
||||
in {
|
||||
imports = [ ../secrets ];
|
||||
options = {
|
||||
system.service.hostapd = mkOption {
|
||||
type = liminix.lib.types.serviceDefn;
|
||||
};
|
||||
};
|
||||
config = {
|
||||
system.service.hostapd = liminix.callService ./service.nix {
|
||||
system.service.hostapd = config.system.callService ./service.nix {
|
||||
interface = mkOption {
|
||||
type = liminix.lib.types.service;
|
||||
};
|
||||
|
@ -1,15 +1,16 @@
|
||||
{
|
||||
liminix
|
||||
, svc
|
||||
, hostapd
|
||||
, output-template
|
||||
, writeText
|
||||
, lib
|
||||
}:
|
||||
{ interface, params} :
|
||||
let
|
||||
inherit (liminix.services) longrun;
|
||||
inherit (lib) concatStringsSep mapAttrsToList;
|
||||
inherit (liminix.lib) typeChecked;
|
||||
inherit (lib) mkOption types;
|
||||
inherit (lib) concatStringsSep mapAttrsToList unique ;
|
||||
inherit (builtins) map filter attrValues length head typeOf;
|
||||
|
||||
# This is not a friendly interface to configuring a wireless AP: it
|
||||
# just passes everything straight through to the hostapd config.
|
||||
@ -22,18 +23,35 @@ let
|
||||
driver = "nl80211";
|
||||
logger_syslog = "-1";
|
||||
logger_syslog_level = 1;
|
||||
ctrl_interface = "/run/hostapd";
|
||||
ctrl_interface = "/run/${name}";
|
||||
ctrl_interface_group = 0;
|
||||
};
|
||||
attrs = defaults // params ;
|
||||
literal_or_output = o: ({
|
||||
string = builtins.toJSON;
|
||||
int = builtins.toJSON;
|
||||
lambda = (o: "output(${builtins.toJSON (o "service")}, ${builtins.toJSON (o "path")})");
|
||||
}.${builtins.typeOf o}) o;
|
||||
|
||||
conf = writeText "hostapd.conf"
|
||||
(concatStringsSep
|
||||
"\n"
|
||||
(mapAttrsToList
|
||||
(name: value: "${name}=${toString value}")
|
||||
(defaults // params)));
|
||||
in longrun {
|
||||
inherit name;
|
||||
dependencies = [ interface ];
|
||||
run = "${hostapd}/bin/hostapd -i $(output ${interface} ifname) -P /run/${name}.pid -S ${conf}";
|
||||
conf =
|
||||
(writeText "hostapd.conf.in"
|
||||
((concatStringsSep
|
||||
"\n"
|
||||
(mapAttrsToList
|
||||
(n : v : "${n}={{ ${literal_or_output v} }}")
|
||||
attrs)) + "\n"));
|
||||
service = longrun {
|
||||
inherit name;
|
||||
dependencies = [ interface ];
|
||||
run = ''
|
||||
mkdir -p /run/${name}
|
||||
chmod 0700 /run/${name}
|
||||
${output-template}/bin/output-template '{{' '}}' < ${conf} > /run/${name}/hostapd.conf
|
||||
exec ${hostapd}/bin/hostapd -i $(output ${interface} ifname) -P /run/${name}/hostapd.pid -S /run/${name}/hostapd.conf
|
||||
'';
|
||||
};
|
||||
watch = filter (f: typeOf f == "lambda") (attrValues attrs);
|
||||
in svc.secrets.subscriber.build {
|
||||
inherit service watch;
|
||||
action = "restart-all";
|
||||
}
|
||||
|
18
modules/ifwait/default.nix
Normal file
18
modules/ifwait/default.nix
Normal file
@ -0,0 +1,18 @@
|
||||
{ config, pkgs, lib, ... } :
|
||||
let
|
||||
inherit (pkgs) liminix;
|
||||
inherit (lib) mkOption types;
|
||||
in {
|
||||
options.system.service.ifwait =
|
||||
mkOption { type = liminix.lib.types.serviceDefn; };
|
||||
|
||||
config.system.service.ifwait = config.system.callService ./ifwait.nix {
|
||||
state = mkOption { type = types.str; };
|
||||
interface = mkOption {
|
||||
type = liminix.lib.types.interface;
|
||||
};
|
||||
service = mkOption {
|
||||
type = liminix.lib.types.service;
|
||||
};
|
||||
};
|
||||
}
|
16
modules/ifwait/ifwait.nix
Normal file
16
modules/ifwait/ifwait.nix
Normal file
@ -0,0 +1,16 @@
|
||||
{ ifwait, liminix } :
|
||||
{
|
||||
state
|
||||
, interface
|
||||
, service
|
||||
}:
|
||||
let
|
||||
inherit (liminix.services) longrun;
|
||||
in longrun {
|
||||
name = "ifwait.${interface.name}";
|
||||
buildInputs = [ service ];
|
||||
restart-on-upgrade = true;
|
||||
run = ''
|
||||
${ifwait}/bin/ifwait -s ${service.name} $(output ${interface} ifname) ${state}
|
||||
'';
|
||||
}
|
@ -5,18 +5,24 @@
|
||||
|
||||
{ lib, pkgs, config, ...}:
|
||||
let
|
||||
inherit (lib) mkEnableOption mkOption types isDerivation hasAttr ;
|
||||
inherit (pkgs.pseudofile) dir symlink;
|
||||
inherit (pkgs.liminix.networking) address interface;
|
||||
inherit (pkgs.liminix.services) bundle;
|
||||
inherit (pkgs) liminix;
|
||||
|
||||
type_service = pkgs.liminix.lib.types.service;
|
||||
inherit (lib) mkOption types ;
|
||||
inherit (pkgs) liminix openwrt;
|
||||
|
||||
mergeConditionals = conf : conditions :
|
||||
# for each key in conditions, if it is present in conf
|
||||
# then merge the associated value into conf
|
||||
lib.foldlAttrs
|
||||
(acc: name: value:
|
||||
if (conf ? ${name}) && (conf.${name} != "n")
|
||||
then acc // value
|
||||
else acc)
|
||||
conf
|
||||
conditions;
|
||||
in {
|
||||
options = {
|
||||
kernel = {
|
||||
src = mkOption { type = types.path; } ;
|
||||
src = mkOption { type = types.path; default = openwrt.kernelSrc; } ;
|
||||
version = mkOption { type = types.str; default = openwrt.kernelVersion;} ;
|
||||
modular = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
@ -42,20 +48,35 @@ in {
|
||||
};
|
||||
'';
|
||||
};
|
||||
conditionalConfig = mkOption {
|
||||
description = ''
|
||||
Kernel config options that should only be applied when
|
||||
some other option is present.
|
||||
'';
|
||||
type = types.attrsOf (types.attrsOf types.nonEmptyStr);
|
||||
default = {};
|
||||
example = {
|
||||
USB = {
|
||||
USB_XHCI_MVEBU = "y";
|
||||
USB_XHCI_HCD = "y";
|
||||
};
|
||||
};
|
||||
};
|
||||
makeTargets = mkOption {
|
||||
type = types.listOf types.str;
|
||||
};
|
||||
};
|
||||
};
|
||||
config = {
|
||||
system.outputs =
|
||||
let k = liminix.builders.kernel.override {
|
||||
inherit (config.kernel) config src extraPatchPhase;
|
||||
targets = config.kernel.makeTargets;
|
||||
};
|
||||
in {
|
||||
kernel = k.vmlinux;
|
||||
zimage = k.zImage;
|
||||
system.outputs.kernel =
|
||||
let
|
||||
mergedConfig = mergeConditionals
|
||||
config.kernel.config
|
||||
config.kernel.conditionalConfig;
|
||||
in liminix.builders.kernel.override {
|
||||
config = mergedConfig;
|
||||
inherit (config.kernel) version src extraPatchPhase;
|
||||
targets = config.kernel.makeTargets;
|
||||
};
|
||||
|
||||
kernel = rec {
|
||||
@ -66,9 +87,8 @@ in {
|
||||
IKCONFIG_PROC = "y";
|
||||
PROC_FS = "y";
|
||||
|
||||
KEXEC = "y";
|
||||
MODULES = if modular then "y" else "n";
|
||||
MODULE_SIG = if modular then "y" else "n";
|
||||
MODULE_UNLOAD = if modular then "y" else "n";
|
||||
DEBUG_FS = "y";
|
||||
|
||||
# basic networking protocols
|
15
modules/klogd.nix
Normal file
15
modules/klogd.nix
Normal file
@ -0,0 +1,15 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
let
|
||||
inherit (pkgs.liminix.services) longrun;
|
||||
in {
|
||||
config.services.klogd = longrun {
|
||||
name = "klogd";
|
||||
run = ''
|
||||
echo "1 2 1 8" > /proc/sys/kernel/printk
|
||||
cat /proc/kmsg
|
||||
'';
|
||||
finish = ''
|
||||
echo "8 4 1 8" > /proc/sys/kernel/printk
|
||||
'';
|
||||
};
|
||||
}
|
32
modules/mdevd.nix
Normal file
32
modules/mdevd.nix
Normal file
@ -0,0 +1,32 @@
|
||||
{ config, pkgs, ...} :
|
||||
let inherit (pkgs.liminix.services) oneshot longrun;
|
||||
in {
|
||||
config = {
|
||||
services = rec {
|
||||
mdevd = longrun {
|
||||
name = "mdevd";
|
||||
notification-fd = 3;
|
||||
run = "${pkgs.mdevd}/bin/mdevd -D 3 -b 200000 -O4";
|
||||
};
|
||||
devout = longrun {
|
||||
name = "devout";
|
||||
notification-fd = 10;
|
||||
timeout-up = 60 * 1000;
|
||||
run = "exec ${pkgs.devout}/bin/devout /run/devout.sock 4";
|
||||
dependencies = [ mdevd ];
|
||||
};
|
||||
coldplug = oneshot {
|
||||
name = "coldplug";
|
||||
# would love to know what mdevd-coldplug/udevadm trigger does
|
||||
# that this doesn't
|
||||
up = ''
|
||||
for i in $(find /sys -name uevent); do ( echo change > $i ) ; done
|
||||
'';
|
||||
dependencies = [
|
||||
devout
|
||||
mdevd
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
@ -7,11 +7,6 @@
|
||||
let
|
||||
inherit (lib) mkOption types;
|
||||
inherit (pkgs) liminix;
|
||||
mkBoolOption = description : mkOption {
|
||||
type = types.bool;
|
||||
inherit description;
|
||||
default = true;
|
||||
};
|
||||
|
||||
in {
|
||||
options = {
|
||||
@ -19,28 +14,39 @@ in {
|
||||
type = liminix.lib.types.serviceDefn;
|
||||
};
|
||||
};
|
||||
config.system.service = {
|
||||
mount = liminix.callService ./service.nix {
|
||||
device = mkOption {
|
||||
type = types.str;
|
||||
example = "/dev/sda1";
|
||||
};
|
||||
mountpoint = mkOption {
|
||||
type = types.str;
|
||||
example = "/mnt/media";
|
||||
};
|
||||
options = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
example = ["noatime" "ro" "sync"];
|
||||
};
|
||||
fstype = mkOption {
|
||||
type = types.str;
|
||||
default = "auto";
|
||||
example = "vfat";
|
||||
};
|
||||
imports = [ ../mdevd.nix ../uevent-rule ];
|
||||
config.system.service.mount =
|
||||
let svc = config.system.callService ./service.nix {
|
||||
partlabel = mkOption {
|
||||
type = types.str;
|
||||
example = "my-usb-stick";
|
||||
};
|
||||
mountpoint = mkOption {
|
||||
type = types.str;
|
||||
example = "/mnt/media";
|
||||
};
|
||||
options = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
example = ["noatime" "ro" "sync"];
|
||||
};
|
||||
fstype = mkOption {
|
||||
type = types.str;
|
||||
default = "auto";
|
||||
example = "vfat";
|
||||
};
|
||||
};
|
||||
in svc // {
|
||||
build = args:
|
||||
let args' = args // {
|
||||
dependencies = (args.dependencies or []) ++ [
|
||||
config.services.mdevd
|
||||
config.services.devout
|
||||
];
|
||||
};
|
||||
in svc.build args' ;
|
||||
};
|
||||
};
|
||||
|
||||
config.programs.busybox = {
|
||||
applets = ["blkid" "findfs"];
|
||||
options = {
|
||||
|
@ -1,18 +1,27 @@
|
||||
{
|
||||
liminix
|
||||
, lib
|
||||
, svc
|
||||
}:
|
||||
{ device, mountpoint, options, fstype }:
|
||||
{ partlabel, mountpoint, options, fstype }:
|
||||
let
|
||||
inherit (liminix.services) oneshot;
|
||||
device = "/dev/disk/by-partlabel/${partlabel}";
|
||||
name = "mount.${lib.strings.sanitizeDerivationName (lib.escapeURL mountpoint)}";
|
||||
options_string =
|
||||
if options == [] then "" else "-o ${lib.concatStringsSep "," options}";
|
||||
controller = svc.uevent-rule.build {
|
||||
serviceName = name;
|
||||
symlink = device;
|
||||
terms = {
|
||||
partname = partlabel;
|
||||
devtype = "partition";
|
||||
};
|
||||
};
|
||||
in oneshot {
|
||||
name = "mount.${lib.escapeURL mountpoint}";
|
||||
up = ''
|
||||
while ! findfs ${device}; do
|
||||
echo waiting for device ${device}
|
||||
sleep 1
|
||||
done
|
||||
mount -t ${fstype} -o ${lib.concatStringsSep "," options} ${device} ${mountpoint}
|
||||
'';
|
||||
inherit name;
|
||||
timeout-up = 3600;
|
||||
up = "mount -t ${fstype} ${options_string} ${device} ${mountpoint}";
|
||||
down = "umount ${mountpoint}";
|
||||
inherit controller;
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
{
|
||||
liminix
|
||||
, ifwait
|
||||
, serviceFns
|
||||
, lib
|
||||
}:
|
||||
@ -12,7 +11,6 @@ let
|
||||
# prefixes, or the same but different protocols
|
||||
name = "${interface.name}.a.${address}";
|
||||
up = ''
|
||||
. ${serviceFns}
|
||||
dev=$(output ${interface} ifname)
|
||||
ip address add ${address}/${toString prefixLength} dev $dev
|
||||
(in_outputs ${name}
|
||||
|
@ -64,7 +64,7 @@ in {
|
||||
services.loopback = config.hardware.networkInterfaces.lo;
|
||||
|
||||
system.service.network = {
|
||||
link = liminix.callService ./link.nix {
|
||||
link = config.system.callService ./link.nix {
|
||||
ifname = mkOption {
|
||||
type = types.str;
|
||||
example = "eth0";
|
||||
@ -89,7 +89,7 @@ in {
|
||||
example = 1480;
|
||||
};
|
||||
};
|
||||
address = liminix.callService ./address.nix {
|
||||
address = config.system.callService ./address.nix {
|
||||
interface = mkOption {
|
||||
type = liminix.lib.types.service;
|
||||
};
|
||||
@ -104,7 +104,7 @@ in {
|
||||
};
|
||||
};
|
||||
|
||||
route = liminix.callService ./route.nix {
|
||||
route = config.system.callService ./route.nix {
|
||||
interface = mkOption {
|
||||
type = types.nullOr liminix.lib.types.interface;
|
||||
default = null;
|
||||
@ -125,7 +125,7 @@ in {
|
||||
};
|
||||
};
|
||||
|
||||
forward = liminix.callService ./forward.nix {
|
||||
forward = config.system.callService ./forward.nix {
|
||||
enableIPv4 = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
@ -136,7 +136,7 @@ in {
|
||||
};
|
||||
};
|
||||
|
||||
dhcp.client = liminix.callService ./dhcpc.nix {
|
||||
dhcp.client = config.system.callService ./dhcpc.nix {
|
||||
interface = mkOption {
|
||||
type = liminix.lib.types.service;
|
||||
};
|
||||
|
@ -17,7 +17,7 @@ let
|
||||
ip address replace $ip/$mask dev $interface
|
||||
(in_outputs ${name}
|
||||
for i in lease mask ip router siaddr dns serverid subnet opt53 interface ; do
|
||||
printenv $i > $i
|
||||
(printenv $i || true) > $i
|
||||
done)
|
||||
}
|
||||
case $action in
|
||||
@ -40,7 +40,7 @@ let
|
||||
'';
|
||||
in longrun {
|
||||
inherit name;
|
||||
run = "/bin/udhcpc -f -i $(output ${interface} ifname) -x hostname:$(cat /proc/sys/kernel/hostname) -s ${script}";
|
||||
run = "exec /bin/udhcpc -f -i $(output ${interface} ifname) -x hostname:$(cat /proc/sys/kernel/hostname) -s ${script}";
|
||||
notification-fd = 10;
|
||||
dependencies = [ interface ];
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
{
|
||||
liminix
|
||||
, ifwait
|
||||
, serviceFns
|
||||
, lib
|
||||
}:
|
||||
{ enableIPv4, enableIPv6 }:
|
||||
|
@ -1,7 +1,5 @@
|
||||
{
|
||||
liminix
|
||||
, ifwait
|
||||
, serviceFns
|
||||
, lib
|
||||
}:
|
||||
{
|
||||
@ -11,8 +9,7 @@
|
||||
# if devpath is supplied, we rename the interface at that
|
||||
# path to have the specified name.
|
||||
let
|
||||
inherit (liminix.services) longrun oneshot;
|
||||
inherit (lib) concatStringsSep;
|
||||
inherit (liminix.services) oneshot;
|
||||
name = "${ifname}.link";
|
||||
rename = if devpath != null
|
||||
then ''
|
||||
|
@ -1,15 +1,15 @@
|
||||
{
|
||||
liminix
|
||||
, ifwait
|
||||
, serviceFns
|
||||
, lib
|
||||
}:
|
||||
{ target, via, interface ? null, metric }:
|
||||
let
|
||||
inherit (liminix.services) oneshot;
|
||||
with_dev = if interface != null then "dev $(output ${interface} ifname)" else "";
|
||||
target_hash = builtins.substring 0 12 (builtins.hashString "sha256" target);
|
||||
via_hash = builtins.substring 0 12 (builtins.hashString "sha256" via);
|
||||
in oneshot {
|
||||
name = "route-${target}-${builtins.substring 0 12 (builtins.hashString "sha256" "${via}-${if interface!=null then interface.name else ""}")}";
|
||||
name = "route-${target_hash}-${builtins.substring 0 12 (builtins.hashString "sha256" "${via_hash}-${if interface!=null then interface.name else ""}")}";
|
||||
up = ''
|
||||
ip route add ${target} via ${via} metric ${toString metric} ${with_dev}
|
||||
'';
|
||||
|
@ -18,7 +18,7 @@ in {
|
||||
};
|
||||
};
|
||||
config = {
|
||||
system.service.ntp = liminix.callService ./service.nix {
|
||||
system.service.ntp = config.system.callService ./service.nix {
|
||||
user = mkOption {
|
||||
type = types.str;
|
||||
default = "ntp";
|
||||
|
@ -1,18 +1,14 @@
|
||||
{
|
||||
liminix
|
||||
, chrony
|
||||
, serviceFns
|
||||
, lib
|
||||
, writeText
|
||||
}:
|
||||
params:
|
||||
let
|
||||
name = "ntp"; # bad name, needs to be unique
|
||||
inherit (liminix.services) longrun;
|
||||
inherit (lib) concatStringsSep mapAttrsToList;
|
||||
inherit (liminix.lib) typeChecked;
|
||||
inherit (lib) mkOption types;
|
||||
|
||||
serverOpts = types.listOf types.str;
|
||||
configFile = p:
|
||||
(mapAttrsToList (name: opts: "server ${name} ${concatStringsSep "" opts}")
|
||||
p.servers)
|
||||
@ -28,11 +24,15 @@ let
|
||||
++ (lib.optional (p.bindaddress != null) "bindaddress ${p.bindaddress}")
|
||||
++ (lib.optional (p.binddevice != null) "binddevice ${p.binddevice}")
|
||||
++ (lib.optional (p.dumpdir != null) "dumpdir ${p.dumpdir}")
|
||||
++ [
|
||||
"bindcmdaddress /" # disable unix socket
|
||||
"pidfile /run/${name}.pid"
|
||||
]
|
||||
++ [p.extraConfig];
|
||||
|
||||
config = writeText "chrony.conf"
|
||||
(concatStringsSep "\n" (configFile params));
|
||||
in longrun {
|
||||
name = "ntp"; # bad name, needs to be unique
|
||||
inherit name;
|
||||
run = "${chrony}/bin/chronyd -f ${config} -d";
|
||||
}
|
||||
|
@ -1,19 +1,24 @@
|
||||
{
|
||||
config
|
||||
, pkgs
|
||||
, lib
|
||||
, ...
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (lib) mkOption types concatStringsSep;
|
||||
inherit (pkgs) liminix callPackage writeText;
|
||||
inherit (pkgs) liminix writeText;
|
||||
o = config.system.outputs;
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
./squashfs.nix
|
||||
./outputs/squashfs.nix
|
||||
./outputs/jffs2.nix
|
||||
./outputs/vmroot.nix
|
||||
./outputs/extlinux.nix
|
||||
./outputs/boot-extlinux.nix
|
||||
./outputs/boot-fit.nix
|
||||
./outputs/uimage.nix
|
||||
./outputs/updater
|
||||
./outputs/ubimage.nix
|
||||
];
|
||||
options = {
|
||||
system.outputs = {
|
||||
@ -22,27 +27,17 @@ in
|
||||
# but only part of one.
|
||||
kernel = mkOption {
|
||||
type = types.package;
|
||||
internal = true;
|
||||
internal = true;
|
||||
description = ''
|
||||
kernel
|
||||
******
|
||||
|
||||
Kernel vmlinux file (usually ELF)
|
||||
'';
|
||||
};
|
||||
zimage = mkOption {
|
||||
type = types.package;
|
||||
internal = true;
|
||||
description = ''
|
||||
zimage
|
||||
******
|
||||
|
||||
Kernel in compressed self-extracting package
|
||||
Kernel package (multi-output derivation)
|
||||
'';
|
||||
};
|
||||
dtb = mkOption {
|
||||
type = types.package;
|
||||
internal = true;
|
||||
internal = true;
|
||||
description = ''
|
||||
dtb
|
||||
***
|
||||
@ -50,22 +45,15 @@ in
|
||||
Compiled device tree (FDT) for the target device
|
||||
'';
|
||||
};
|
||||
uimage = mkOption {
|
||||
tplink-safeloader = mkOption {
|
||||
type = types.package;
|
||||
internal = true;
|
||||
description = ''
|
||||
uimage
|
||||
******
|
||||
|
||||
Combined kernel and FDT in uImage (U-Boot compatible) format
|
||||
'';
|
||||
};
|
||||
u-boot = mkOption {
|
||||
type = types.package;
|
||||
};
|
||||
manifest = mkOption {
|
||||
type = types.package;
|
||||
internal = true;
|
||||
internal = true;
|
||||
description = ''
|
||||
Debugging aid. JSON rendition of config.filesystem, on
|
||||
which can run "nix-store -q --tree" on it and find
|
||||
@ -79,6 +67,12 @@ in
|
||||
directory of files to package into root filesystem
|
||||
'';
|
||||
};
|
||||
bootfiles = mkOption {
|
||||
type = types.nullOr types.package;
|
||||
internal = true;
|
||||
default = null;
|
||||
# description = "";
|
||||
};
|
||||
bootablerootdir = mkOption {
|
||||
type = types.package;
|
||||
internal = true;
|
||||
@ -101,17 +95,11 @@ in
|
||||
system.outputs = rec {
|
||||
dtb = liminix.builders.dtb {
|
||||
inherit (config.boot) commandLine;
|
||||
dts = config.hardware.dts.src;
|
||||
includes = config.hardware.dts.includes ++ [
|
||||
dts = [config.hardware.dts.src] ++ config.hardware.dts.includes;
|
||||
includes = config.hardware.dts.includePaths ++ [
|
||||
"${o.kernel.headers}/include"
|
||||
];
|
||||
};
|
||||
uimage = liminix.builders.uimage {
|
||||
commandLine = concatStringsSep " " config.boot.commandLine;
|
||||
inherit (config.hardware) loadAddress entryPoint;
|
||||
inherit (config.boot) imageFormat;
|
||||
inherit (o) kernel dtb;
|
||||
};
|
||||
rootdir =
|
||||
let
|
||||
inherit (pkgs.pkgsBuildBuild) runCommand;
|
||||
@ -128,8 +116,8 @@ in
|
||||
let inherit (pkgs.pkgsBuildBuild) runCommand;
|
||||
in runCommand "add-slash-boot" { } ''
|
||||
cp -a ${o.rootdir} $out
|
||||
${if config.boot.loader.extlinux.enable
|
||||
then "(cd $out && chmod -R +w . && rmdir boot && cp -a ${o.extlinux} boot)"
|
||||
${if o.bootfiles != null
|
||||
then "(cd $out && chmod -R +w . && rmdir boot && cp -a ${o.bootfiles} boot)"
|
||||
else ""
|
||||
}
|
||||
'';
|
||||
|
@ -6,34 +6,34 @@
|
||||
}:
|
||||
let
|
||||
inherit (lib) mkIf mkEnableOption mkOption types concatStringsSep;
|
||||
inherit (pkgs.pseudofile) dir symlink;
|
||||
cfg = config.boot.loader.extlinux;
|
||||
o = config.system.outputs;
|
||||
cmdline = concatStringsSep " " config.boot.commandLine;
|
||||
wantsDtb = config.hardware.dts ? src && config.hardware.dts.src != null;
|
||||
in {
|
||||
options.system.outputs.extlinux = mkOption {
|
||||
type = types.package;
|
||||
# description = "";
|
||||
};
|
||||
options.boot.loader.extlinux.enable = mkEnableOption "extlinux";
|
||||
|
||||
config = { # mkIf cfg.enable {
|
||||
system.outputs.extlinux = pkgs.runCommand "extlinux" {} ''
|
||||
config = mkIf cfg.enable {
|
||||
system.outputs.bootfiles = pkgs.runCommand "extlinux" {} ''
|
||||
mkdir $out
|
||||
cd $out
|
||||
${if wantsDtb then "cp ${o.dtb} dtb" else "true"}
|
||||
cp ${o.initramfs} initramfs
|
||||
cp ${o.zimage} kernel
|
||||
cp ${o.kernel.zImage} kernel
|
||||
mkdir extlinux
|
||||
cat > extlinux/extlinux.conf << _EOF
|
||||
menu title Liminix
|
||||
timeout 100
|
||||
timeout 40
|
||||
label Liminix
|
||||
kernel /boot/kernel
|
||||
# initrd /boot/initramfs
|
||||
append ${cmdline} root=/dev/vda1
|
||||
append ${cmdline}
|
||||
${if wantsDtb then "fdt /boot/dtb" else ""}
|
||||
_EOF
|
||||
'';
|
||||
filesystem = dir {
|
||||
boot = symlink config.system.outputs.bootfiles;
|
||||
};
|
||||
};
|
||||
}
|
27
modules/outputs/boot-fit.nix
Normal file
27
modules/outputs/boot-fit.nix
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
config
|
||||
, pkgs
|
||||
, lib
|
||||
, ...
|
||||
}:
|
||||
let
|
||||
inherit (lib) mkIf mkEnableOption mkOption types concatStringsSep;
|
||||
inherit (pkgs.pseudofile) dir symlink;
|
||||
cfg = config.boot.loader.fit;
|
||||
o = config.system.outputs;
|
||||
cmdline = concatStringsSep " " config.boot.commandLine;
|
||||
wantsDtb = config.hardware.dts ? src && config.hardware.dts.src != null;
|
||||
in {
|
||||
options.boot.loader.fit.enable = mkEnableOption "FIT in /boot";
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
system.outputs.bootfiles = pkgs.runCommand "boot-fit" {} ''
|
||||
mkdir $out
|
||||
cd $out
|
||||
cp ${o.uimage} fit
|
||||
'';
|
||||
filesystem = dir {
|
||||
boot = symlink config.system.outputs.bootfiles;
|
||||
};
|
||||
};
|
||||
}
|
37
modules/outputs/btrfs.nix
Normal file
37
modules/outputs/btrfs.nix
Normal file
@ -0,0 +1,37 @@
|
||||
{
|
||||
config
|
||||
, pkgs
|
||||
, lib
|
||||
, ...
|
||||
}:
|
||||
let
|
||||
inherit (lib) mkIf;
|
||||
o = config.system.outputs;
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
./initramfs.nix
|
||||
];
|
||||
config = mkIf (config.rootfsType == "btrfs") {
|
||||
kernel.config = {
|
||||
BTRFS_FS = "y";
|
||||
};
|
||||
boot.initramfs.enable = true;
|
||||
system.outputs = {
|
||||
rootfs =
|
||||
let
|
||||
inherit (pkgs.pkgsBuildBuild) runCommand e2fsprogs;
|
||||
in runCommand "mkfs.btrfs" {
|
||||
depsBuildBuild = [ e2fsprogs ];
|
||||
} ''
|
||||
tree=${o.bootablerootdir}
|
||||
size=$(du -s --apparent-size --block-size 1024 $tree |cut -f1)
|
||||
# add 25% for filesystem overhead
|
||||
size=$(( 5 * $size / 4))
|
||||
dd if=/dev/zero of=$out bs=1024 count=$size
|
||||
echo "not implemented" ; exit 1
|
||||
# mke2fs -t ext4 -j -d $tree $out
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
@ -5,7 +5,7 @@
|
||||
, ...
|
||||
}:
|
||||
let
|
||||
inherit (lib) mkIf mkOption types;
|
||||
inherit (lib) mkIf;
|
||||
o = config.system.outputs;
|
||||
in
|
||||
{
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user