forked from dan/liminix
Compare commits
409 Commits
module-bas
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | |||
| 9632a64b47 | |||
| fc5def2e15 | |||
| 9369fdf314 | |||
| d2e29543e2 | |||
| dad7c2c875 | |||
| 3459c04f64 | |||
| ff991508ae | |||
| e4ed9dbec9 | |||
| 9e0ef68c1f | |||
| 870e4d86cc | |||
| d6f96c0448 | |||
| e7747832ad | |||
| 921b4f24af | |||
| e505e37d9a | |||
| 2152a3f207 | |||
| ec1ff283da | |||
| 0bf98c5243 | |||
| dc42969ef6 | |||
| 1a041392aa | |||
| 6469408d5f | |||
| f020d5b25d | |||
| e5cbc2b86b | |||
| 29f35cb902 | |||
| dbf1ecdcb7 | |||
| aecc44aaa0 | |||
| 1042be912c | |||
| c931d84828 | |||
| 64a3f50248 | |||
| c5e9fcecc7 | |||
| f25c41b4d2 | |||
| bfa68d9c55 | |||
| ff0ef825a6 | |||
| 44a0cf364b | |||
| c7b2733bea | |||
| dfbc72dd51 | |||
| 9f851b229c | |||
| fe2d41a2b1 | |||
| 9b92bf8447 | |||
| 5f9ffa804f | |||
| 231c2cef03 | |||
| dbb82339bd | |||
| 4a606a4b19 | |||
| 9c894bdabf | |||
| a962f18369 | |||
| 9a29a042e8 | |||
| 46926a94db | |||
| ab0631c555 | |||
| 32c13c46bb | |||
| e5db2691e5 | |||
| 9ca9723c9d | |||
| d1e2d525a4 | |||
| f4f4387861 | |||
| 55fa9992d4 | |||
| 95d9e014fb | |||
| 80528376a2 | |||
| d707345891 | |||
| 133b64613d | |||
| c6c41e331e | |||
| b878d6481a | |||
| 601bb289ee | |||
| 876bd7d8ce | |||
| 39c338d710 | |||
| 6c8b2bbb83 | |||
| 4ddce6e926 | |||
| 5eeb277564 | |||
| c81e7c4d35 | |||
| 53fed8839a | |||
| ebaa7b2bcb | |||
| 15d570f749 | |||
|
|
aff312bbbe | ||
| bb8e974c2b | |||
| 317457f582 | |||
| 07e66c462b | |||
| 4229b42d82 | |||
| 03b17fa3ed | |||
| a8891461aa | |||
| 5adfb0230f | |||
| b519bd15df | |||
| f2daa0b669 | |||
| 3f74fad966 | |||
| ed925588f7 | |||
| f08c10c8ba | |||
| d25a804f13 | |||
| 0242cec977 | |||
| 5a2963543e | |||
|
|
644f42c35e | ||
| 98d3336926 | |||
| cb6ebbdc60 | |||
| bb335050fd | |||
| 395f624338 | |||
| e518ab667b | |||
| 382128b6cf | |||
| c803772074 | |||
| 32c24f3809 | |||
| cc73a98419 | |||
| e2ea145ce5 | |||
| b036a161f5 | |||
| 31a2969972 | |||
| 76a370cc92 | |||
| bf9f264f0c | |||
| e35b61b68c | |||
| a8f98ccfe7 | |||
| 27ce61ae4e | |||
| 3f0f621809 | |||
| b0ae314df4 | |||
| d789a23113 | |||
| 5ba14fd915 | |||
| 3df34428d6 | |||
| 62c788eb86 | |||
| bab6d346a8 | |||
| a202ae476a | |||
| 7c9297f91d | |||
| a0bd250963 | |||
| c8b2d58dd3 | |||
| e5223f093f | |||
| c563a6451f | |||
| f45326b9d3 | |||
| f9f4d97bb8 | |||
| abfb35a231 | |||
| 315907de98 | |||
| 185117843b | |||
| 0131686661 | |||
| 3da692f7ef | |||
| f61e737b54 | |||
| 262efaabe6 | |||
| 7cfb92e3ce | |||
| 22882dabee | |||
| 5e046490de | |||
| a9760d239c | |||
| 5729cfb4a7 | |||
| 7d5c7b9b44 | |||
| 23b3a2baef | |||
| 4cb4f904f8 | |||
| a9d847e2c0 | |||
| 6bbff2f5b3 | |||
| 5c1f5fabe2 | |||
| 6489a39424 | |||
| c94d12934f | |||
| c40eef25d6 | |||
| 46991e2761 | |||
| a135cb1217 | |||
| 863045b86b | |||
| 629624bb25 | |||
| 92b9bf959e | |||
| c5c5f1687a | |||
| 80793aa694 | |||
| 824536f9b3 | |||
| e6cb5e319b | |||
| c3ccee6506 | |||
| 6db982f25f | |||
| 86a5224f3c | |||
| 155a29d9b3 | |||
| e6ef4f78bb | |||
| d2f517a4e9 | |||
| 0f38ee0e9c | |||
| 61dc5beca8 | |||
| f3225c2bd5 | |||
| 8798ee9830 | |||
| 629914f65e | |||
| 0693cf23d8 | |||
| c341eb46b6 | |||
| 1a369ff3bf | |||
| 364c5faf9e | |||
| 80a09a9a9b | |||
| 3518e2ecca | |||
| fc2eb6ee4d | |||
| bd20f3e419 | |||
| f62ad0e1d7 | |||
| ed792e0dc0 | |||
| d025c33d30 | |||
| a755c9c3c5 | |||
| fdf74fa06b | |||
| b8dea2ed34 | |||
| c18f07f02f | |||
| 1c4412a1f4 | |||
| dbc16edf96 | |||
| 528d619d76 | |||
| 269e972970 | |||
| 2a5669c2cd | |||
| 4df248323c | |||
| b01840fa7f | |||
| a896c4e31c | |||
| caf8e85061 | |||
| 76f03ecf0f | |||
| f57997c605 | |||
| 3c483ebd9a | |||
| a30b658999 | |||
| 1ec7fcc197 | |||
| f9f934b40c | |||
| e3c8ab351e | |||
| 7e13eda490 | |||
| 3a2f074199 | |||
| 74f4e0a2f3 | |||
| 9133475103 | |||
| abd01a7809 | |||
| d0c2b3b274 | |||
| dfe7228b99 | |||
| ab147abd9b | |||
| bca0c9b26b | |||
| c59a228955 | |||
| 1673a71831 | |||
| 94e51db649 | |||
| 3205a38ac9 | |||
| 119d6ad379 | |||
| 8d356890b0 | |||
| be22fbbb0a | |||
| 9f87fd8625 | |||
| 3a3ee8bbbd | |||
| 381730d081 | |||
| 57eb55de58 | |||
| 4389fa15f7 | |||
| 4f29bdd3ed | |||
| f1c04c7979 | |||
| f75995e895 | |||
| 12b9feb8ee | |||
| b3a7a2246d | |||
| 6674344021 | |||
| c81ee62374 | |||
| dd0b1734c1 | |||
| 0a2588013a | |||
| 71b583a756 | |||
| 98e7536e59 | |||
| e72d78ab64 | |||
| 17035ca3b7 | |||
| dece70b336 | |||
| 50ea144dec | |||
| fc84435985 | |||
| 06b725cb77 | |||
| c74543c4ff | |||
| 54526c1e11 | |||
| f81aa54444 | |||
| 56261f77b0 | |||
| 8600dfc8cf | |||
| bb280c6d97 | |||
| b7e805c97f | |||
| 9223fa7ec4 | |||
| 0f31afee2b | |||
| 98c63e7498 | |||
| c6faf88dd1 | |||
| 35c7f1643f | |||
| 70fb9f86d3 | |||
| b36272f99e | |||
| 0abe4f96a7 | |||
| f7b30939b5 | |||
| 4fd1b5f08b | |||
| 106a429b3d | |||
| b3e505abf4 | |||
| 92e107d77c | |||
| fa040a194c | |||
| 3bdb7754d3 | |||
| 8f97c5bf3c | |||
| 7904c6bfe9 | |||
| 74b8c98aaf | |||
| 0a737c62cd | |||
| 343d3b6508 | |||
| 96e19767e9 | |||
| 6f2389dd75 | |||
| d49cbbb8ed | |||
| 7683ed69de | |||
| 3ff55d3aad | |||
| 22275f311c | |||
| 870da62a1e | |||
| 0312f7a999 | |||
| 9dd3cf23b4 | |||
| 4e9227dff3 | |||
| eaa45906ff | |||
| 7fc5d2934d | |||
| 12e25722fa | |||
| 09fe21260e | |||
| 4bd3ccc8fd | |||
| 3e163d4253 | |||
| 9487cb2567 | |||
| 907a9de773 | |||
| b25103be2e | |||
| 353a199ab2 | |||
| 91e957ced7 | |||
| 899f096346 | |||
| 0cf4733327 | |||
| 0a6af46364 | |||
| a24c2a23a0 | |||
| 9e52faa0b6 | |||
| 3bdc986dd7 | |||
| 83092b7b73 | |||
| 6805e0090d | |||
| 3b9c5635b8 | |||
| d3dc9752f8 | |||
| 7ad848cb77 | |||
| ef666c34cd | |||
| d7336679c4 | |||
| 66ccea1487 | |||
| 535eb70bb9 | |||
| 51ad051443 | |||
| 92970e8ed0 | |||
| e04ec2e959 | |||
| d60aab728d | |||
| 3609d8d5ee | |||
| 44c1fb7632 | |||
| e577caa15f | |||
| 7faf620c0b | |||
| b094220466 | |||
| f1dfb1f976 | |||
| 333327be75 | |||
| efa1919e04 | |||
| de77635490 | |||
| aecbe08f08 | |||
| ff2d3e1a63 | |||
| 8688d47c65 | |||
| e86daf9bbc | |||
| 23ccfec5fb | |||
| 00c8ea66ea | |||
| 31f0213b6f | |||
| 1580857fde | |||
| 04b59536d8 | |||
| 540a1dfd76 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -5,3 +5,5 @@ result-*
|
||||
*.qcow2
|
||||
_build
|
||||
*-secrets.nix
|
||||
examples/static-leases.nix
|
||||
/doc/hardware.rst
|
||||
|
||||
82
NEWS
Normal file
82
NEWS
Normal file
@@ -0,0 +1,82 @@
|
||||
A brief guide to backward-incompatible changes
|
||||
that are likely to break configurations or workflows
|
||||
|
||||
2023-07-13
|
||||
|
||||
* a significant re-arrangement of modules and services, which will
|
||||
probably break any configuration written before this time. For a
|
||||
detailed explanation, see
|
||||
https://www.liminix.org/doc/configuration.html#modules
|
||||
|
||||
2023-12-10
|
||||
|
||||
* configurations (usually) need no longer import modules from
|
||||
modules/outputs because devices are expected to do this instead. This
|
||||
change is because the outputs that make sense in any given context are
|
||||
usually a property of the device being installed onto.
|
||||
|
||||
2023-12-11
|
||||
|
||||
* rename outputs.flashimage to outputs.mtdimage (and also diskimage to
|
||||
mbrimage). This change is made in the expectation that "fooimage" is
|
||||
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)
|
||||
|
||||
22
README.md
22
README.md
@@ -18,22 +18,14 @@ outside word goes across it.
|
||||
|
||||
Liminix is pre-1.0. We are still finding new and better ways to do things,
|
||||
and there is no attempt to maintain backward compatibility with the old
|
||||
ways. This will change when it settles down.
|
||||
ways.
|
||||
|
||||
_In general:_ development mostly happens on the `main` branch, which is
|
||||
therefore not guaranteed to build or to work on every commit. For the
|
||||
latest functioning version, see [the CI system](https://build.liminix.org/jobset/liminix/build) and pick a revision with all jobs green.
|
||||
The [NEWS](NEWS) file (available wherever you found this README) is
|
||||
a high-level overview of breaking changes.
|
||||
|
||||
_In particular:_ as of July 2023, a significant re-arrangement of
|
||||
modules and services is ongoing:
|
||||
|
||||
* if you are using out-of-tree configurations created before commit
|
||||
2e50368, especially if they reference things under pkgs.liminix,
|
||||
they will need updating. Look at changes to examples/rotuer.nix
|
||||
for guidance
|
||||
|
||||
* the same is intermittently true for examples/{extensino,arhcive}.nix
|
||||
where I've updated rotuer and not updated them to match.
|
||||
Development mostly happens on the `main` branch, which is therefore
|
||||
not guaranteed to build or to work on every commit. For the latest
|
||||
functioning version, see [the CI system](https://build.liminix.org/jobset/liminix/build) and pick a revision with all jobs green.
|
||||
|
||||
|
||||
## Documentation
|
||||
@@ -41,7 +33,7 @@ modules and services is ongoing:
|
||||
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/)
|
||||
|
||||
2073
THOUGHTS.txt
2073
THOUGHTS.txt
File diff suppressed because it is too large
Load Diff
@@ -99,14 +99,16 @@ in {
|
||||
};
|
||||
};
|
||||
};
|
||||
environment.systemPackages = with pkgs; [
|
||||
tcpdump
|
||||
wireshark
|
||||
socat
|
||||
tufted
|
||||
iptables
|
||||
usbutils
|
||||
];
|
||||
environment.systemPackages =
|
||||
let wireshark-nogui = pkgs.wireshark.override { withQt = false ; };
|
||||
in with pkgs; [
|
||||
tcpdump
|
||||
wireshark-nogui
|
||||
socat
|
||||
tufted
|
||||
iptables
|
||||
usbutils
|
||||
];
|
||||
security.sudo.wheelNeedsPassword = false;
|
||||
networking = {
|
||||
hostName = "border";
|
||||
|
||||
65
ci.nix
65
ci.nix
@@ -8,7 +8,11 @@ let
|
||||
pkgs = (import nixpkgs {});
|
||||
borderVmConf = ./bordervm.conf-example.nix;
|
||||
inherit (pkgs.lib.attrsets) genAttrs;
|
||||
devices = [ "qemu" "gl-ar750" "gl-mt300n-v2" "gl-mt300a" ];
|
||||
devices = [
|
||||
"gl-ar750" "gl-mt300n-v2" "gl-mt300a"
|
||||
"qemu" "qemu-aarch64" "qemu-armv7l"
|
||||
"tp-archer-ax23"
|
||||
];
|
||||
vanilla = ./vanilla-configuration.nix;
|
||||
for-device = name:
|
||||
(import liminix {
|
||||
@@ -18,33 +22,50 @@ let
|
||||
}).outputs.default;
|
||||
tests = import ./tests/ci.nix;
|
||||
jobs =
|
||||
(genAttrs devices (name: for-device name)) //
|
||||
(genAttrs devices for-device) //
|
||||
tests //
|
||||
{
|
||||
buildEnv = (import liminix {
|
||||
inherit nixpkgs borderVmConf;
|
||||
inherit nixpkgs borderVmConf;
|
||||
device = import (liminix + "/devices/qemu");
|
||||
liminix-config = vanilla;
|
||||
}).buildEnv;
|
||||
doc = pkgs.stdenv.mkDerivation {
|
||||
name = "liminix-doc";
|
||||
nativeBuildInputs = with pkgs; [
|
||||
gnumake sphinx
|
||||
fennel luaPackages.lyaml
|
||||
];
|
||||
src = ./doc;
|
||||
buildPhase = ''
|
||||
cat ${(import ./doc/extract-options.nix).doc} > options.json
|
||||
cat options.json | fennel --correlate parse-options.fnl > modules.rst
|
||||
make html
|
||||
'';
|
||||
installPhase = ''
|
||||
mkdir -p $out/nix-support $out/share/doc/
|
||||
cp modules.rst options.json $out
|
||||
cp -a _build/html $out/share/doc/liminix
|
||||
echo "file source-dist \"$out/share/doc/liminix\"" \
|
||||
> $out/nix-support/hydra-build-products
|
||||
'';
|
||||
doc =
|
||||
let json =
|
||||
(import liminix {
|
||||
inherit nixpkgs 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 {
|
||||
name = "liminix-doc";
|
||||
nativeBuildInputs = with pkgs; [
|
||||
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
|
||||
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
|
||||
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;
|
||||
|
||||
17
default.nix
17
default.nix
@@ -13,13 +13,14 @@ let
|
||||
allowUnsupportedSystem = true; # mipsel
|
||||
permittedInsecurePackages = [
|
||||
"python-2.7.18.6" # kernel backports needs python <3
|
||||
"python-2.7.18.7"
|
||||
];
|
||||
};
|
||||
});
|
||||
|
||||
config = (pkgs.lib.evalModules {
|
||||
eval = pkgs.lib.evalModules {
|
||||
modules = [
|
||||
{ _module.args = { inherit pkgs; lib = pkgs.lib; }; }
|
||||
{ _module.args = { inherit pkgs; inherit (pkgs) lim; }; }
|
||||
./modules/hardware.nix
|
||||
./modules/base.nix
|
||||
./modules/busybox.nix
|
||||
@@ -30,7 +31,8 @@ let
|
||||
./modules/users.nix
|
||||
./modules/outputs.nix
|
||||
];
|
||||
}).config;
|
||||
};
|
||||
config = eval.config;
|
||||
|
||||
borderVm = ((import <nixpkgs/nixos/lib/eval-config.nix>) {
|
||||
system = builtins.currentSystem;
|
||||
@@ -43,6 +45,12 @@ let
|
||||
in {
|
||||
outputs = config.system.outputs // {
|
||||
default = config.system.outputs.${config.hardware.defaultOutput};
|
||||
optionsJson =
|
||||
let o = import ./doc/extract-options.nix {
|
||||
inherit pkgs eval;
|
||||
lib = pkgs.lib;
|
||||
};
|
||||
in pkgs.writeText "options.json" (builtins.toJSON o);
|
||||
};
|
||||
|
||||
# this is just here as a convenience, so that we can get a
|
||||
@@ -54,11 +62,12 @@ in {
|
||||
tufted
|
||||
routeros.routeros
|
||||
routeros.ros-exec-script
|
||||
mips-vm
|
||||
run-liminix-vm
|
||||
borderVm.build.vm
|
||||
go-l2tp
|
||||
min-copy-closure
|
||||
fennelrepl
|
||||
lzma
|
||||
];
|
||||
};
|
||||
}
|
||||
|
||||
238
devices/belkin-rt3200/default.nix
Normal file
238
devices/belkin-rt3200/default.nix
Normal file
@@ -0,0 +1,238 @@
|
||||
{
|
||||
description = ''
|
||||
Belkin RT-3200 / Linksys E8450
|
||||
******************************
|
||||
|
||||
This device is based on a 64 bit Mediatek MT7622 ARM platform,
|
||||
and is "work in progress" in Liminix.
|
||||
|
||||
.. note:: The factory flash image contains ECC errors that make it
|
||||
incompatible with Liminix: you need to 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).
|
||||
|
||||
Hardware summary
|
||||
================
|
||||
|
||||
- MediaTek MT7622BV (1350MHz)
|
||||
- 128MB NAND flash
|
||||
- 512MB RAM
|
||||
- b/g/n wireless using MediaTek MT7622BV (MT7615E driver)
|
||||
- a/n/ac/ax wireless using MediaTek MT7915E
|
||||
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
Installation is currently a manual process (you need a :ref:`serial <serial>` conection and
|
||||
TFTP) following the instructions at :ref:`system-outputs-ubimage`
|
||||
|
||||
'';
|
||||
|
||||
system = {
|
||||
crossSystem = {
|
||||
config = "aarch64-unknown-linux-musl";
|
||||
};
|
||||
};
|
||||
|
||||
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/aarch64.nix
|
||||
../../modules/outputs/tftpboot.nix
|
||||
../../modules/outputs/ubifs.nix
|
||||
];
|
||||
config = {
|
||||
kernel = {
|
||||
src = pkgs.pkgsBuildBuild.fetchurl {
|
||||
name = "linux.tar.gz";
|
||||
url = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.137.tar.gz";
|
||||
hash = "sha256-PkdzUKZ0IpBiWe/RS70J76JKnBFzRblWcKlaIFNxnHQ=";
|
||||
};
|
||||
extraPatchPhase = ''
|
||||
${pkgs.openwrt.applyPatches.mediatek}
|
||||
'';
|
||||
config = {
|
||||
PCI = "y";
|
||||
ARCH_MEDIATEK = "y";
|
||||
# ARM_MEDIATEK_CPUFREQ = "y";
|
||||
|
||||
# needed for "Cannot find regmap for /infracfg@10000000"
|
||||
MFD_SYSCON = "y";
|
||||
MTK_INFRACFG = "y";
|
||||
|
||||
MTK_PMIC_WRAP = "y";
|
||||
MTK_EFUSE="y";
|
||||
# MTK_HSDMA="y";
|
||||
MTK_SCPSYS="y";
|
||||
MTK_SCPSYS_PM_DOMAINS="y";
|
||||
# MTK_THERMAL="y";
|
||||
MTK_TIMER="y";
|
||||
|
||||
COMMON_CLK_MT7622 = "y";
|
||||
COMMON_CLK_MT7622_ETHSYS = "y";
|
||||
COMMON_CLK_MT7622_HIFSYS = "y";
|
||||
COMMON_CLK_MT7622_AUDSYS = "y";
|
||||
PM_CLK="y";
|
||||
|
||||
REGMAP_MMIO = "y";
|
||||
CLKSRC_MMIO = "y";
|
||||
REGMAP = "y";
|
||||
|
||||
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
|
||||
NET_VENDOR_MEDIATEK = "y";
|
||||
PCIE_MEDIATEK = "y";
|
||||
|
||||
BLOCK = "y"; # move this to base option
|
||||
|
||||
SPI_MASTER = "y";
|
||||
SPI = "y";
|
||||
SPI_MEM="y";
|
||||
SPI_MTK_NOR="y";
|
||||
SPI_MTK_SNFI = "y";
|
||||
|
||||
MTD = "y";
|
||||
MTD_BLOCK = "y";
|
||||
MTD_RAW_NAND = "y";
|
||||
MTD_NAND_MTK = "y";
|
||||
MTD_NAND_MTK_BMT = "y"; # Bad-block Management Table
|
||||
MTD_NAND_ECC_MEDIATEK= "y";
|
||||
MTD_NAND_ECC_SW_HAMMING= "y";
|
||||
MTD_SPI_NAND= "y";
|
||||
MTD_OF_PARTS = "y";
|
||||
MTD_NAND_CORE= "y";
|
||||
MTD_SPI_NOR= "y";
|
||||
MTD_SPLIT_FIRMWARE= "y";
|
||||
MTD_SPLIT_FIT_FW= "y";
|
||||
|
||||
|
||||
MMC = "y";
|
||||
MMC_BLOCK = "y";
|
||||
MMC_CQHCI = "y";
|
||||
MMC_MTK = "y";
|
||||
|
||||
# Distributed Switch Architecture is needed
|
||||
# to make the ethernet ports visible
|
||||
NET_DSA="y";
|
||||
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";
|
||||
};
|
||||
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 = ["mt7615e" "mt7915e"];
|
||||
inherit (config.system.outputs) kernel;
|
||||
};
|
||||
in {
|
||||
ubi = {
|
||||
minIOSize = "2048";
|
||||
eraseBlockSize = "126976";
|
||||
maxLEBcount = "1024"; # guessing
|
||||
};
|
||||
|
||||
defaultOutput = "ubimage";
|
||||
# the kernel expects this to be on a 2MB boundary. U-Boot
|
||||
# (I don't know why) has a default of 0x41080000, which isn't.
|
||||
# We put it at the 32MB mark so that tftpboot can put its rootfs
|
||||
# image and DTB underneath, but maybe this is a terrible waste of
|
||||
# RAM unless the kernel is able to reuse it later. Oh well
|
||||
loadAddress = lim.parseInt "0x42000000";
|
||||
entryPoint = lim.parseInt "0x42000000";
|
||||
rootDevice = "ubi0:liminix";
|
||||
dts = {
|
||||
src = "${openwrt.src}/target/linux/mediatek/dts/mt7622-linksys-e8450-ubi.dts";
|
||||
includes = [
|
||||
"${openwrt.src}/target/linux/mediatek/dts"
|
||||
"${config.system.outputs.kernel.modulesupport}/arch/arm64/boot/dts/mediatek/"
|
||||
];
|
||||
};
|
||||
|
||||
# - 0x000000000000-0x000008000000 : "spi-nand0"
|
||||
# - 0x000000000000-0x000000080000 : "bl2"
|
||||
# - 0x000000080000-0x0000001c0000 : "fip"
|
||||
# - 0x0000001c0000-0x0000002c0000 : "factory"
|
||||
# - 0x0000002c0000-0x000000300000 : "reserved"
|
||||
# - 0x000000300000-0x000008000000 : "ubi"
|
||||
|
||||
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"; };
|
||||
lan2 = link.build { ifname = "lan2"; };
|
||||
lan3 = link.build { ifname = "lan3"; };
|
||||
lan4 = link.build { ifname = "lan4"; };
|
||||
lan = lan3;
|
||||
|
||||
wlan = link.build {
|
||||
ifname = "wlan0";
|
||||
dependencies = [ mac80211 ];
|
||||
};
|
||||
wlan5 = link.build {
|
||||
ifname = "wlan1";
|
||||
dependencies = [ mac80211 ];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
57
devices/families/qemu.nix
Normal file
57
devices/families/qemu.nix
Normal file
@@ -0,0 +1,57 @@
|
||||
{ config, pkgs, ... }:
|
||||
{
|
||||
imports = [
|
||||
../../modules/outputs/jffs2.nix
|
||||
];
|
||||
config = {
|
||||
kernel = {
|
||||
src = pkgs.pkgsBuildBuild.fetchurl {
|
||||
name = "linux.tar.gz";
|
||||
url = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.137.tar.gz";
|
||||
hash = "sha256-PkdzUKZ0IpBiWe/RS70J76JKnBFzRblWcKlaIFNxnHQ=";
|
||||
};
|
||||
config = {
|
||||
MTD = "y";
|
||||
MTD_BLOCK = "y";
|
||||
MTD_CMDLINE_PARTS = "y";
|
||||
MTD_PHRAM = "y";
|
||||
|
||||
VIRTIO_MENU = "y";
|
||||
PCI = "y";
|
||||
VIRTIO_PCI = "y";
|
||||
BLOCK = "y";
|
||||
VIRTIO_BLK = "y";
|
||||
VIRTIO_NET = "y";
|
||||
};
|
||||
};
|
||||
hardware =
|
||||
let
|
||||
mac80211 = pkgs.mac80211.override {
|
||||
drivers = ["mac80211_hwsim"];
|
||||
klibBuild = config.system.outputs.kernel.modulesupport;
|
||||
};
|
||||
in {
|
||||
defaultOutput = "vmroot";
|
||||
rootDevice = "/dev/mtdblock0";
|
||||
dts.src = pkgs.lib.mkDefault null;
|
||||
flash.eraseBlockSize = 65536;
|
||||
networkInterfaces =
|
||||
let inherit (config.system.service.network) link;
|
||||
in {
|
||||
wan = link.build {
|
||||
devpath = "/devices/pci0000:00/0000:00:13.0/virtio0";
|
||||
ifname = "wan";
|
||||
};
|
||||
lan = link.build {
|
||||
devpath = "/devices/pci0000:00/0000:00:14.0/virtio1";
|
||||
ifname = "lan";
|
||||
};
|
||||
|
||||
wlan_24 = link.build {
|
||||
ifname = "wlan0";
|
||||
dependencies = [ mac80211 ];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -1,11 +1,3 @@
|
||||
|
||||
# I like GL.iNet devices because they're relatively accessible to
|
||||
# DIY users: the serial port connections have headers preinstalled
|
||||
# and don't need soldering
|
||||
|
||||
# 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
|
||||
|
||||
{
|
||||
system = {
|
||||
crossSystem = {
|
||||
@@ -18,27 +10,49 @@
|
||||
};
|
||||
|
||||
description = ''
|
||||
GL.INet GL-AR750 "Creta" travel router
|
||||
GL.iNet GL-AR750
|
||||
****************
|
||||
|
||||
Hardware summary
|
||||
================
|
||||
|
||||
The GL-AR750 "Creta" travel router features:
|
||||
|
||||
- QCA9531 @650Mhz SoC
|
||||
- dual band wireless: IEEE 802.11a/b/g/n/ac
|
||||
- two 10/100Mbps LAN ports and one WAN
|
||||
- 128MB DDR2 RAM / 16MB NOR Flash
|
||||
- "ath79" soc family
|
||||
https://www.gl-inet.com/products/gl-ar750/
|
||||
- 128MB DDR2 RAM
|
||||
- 16MB NOR Flash
|
||||
- supported in OpenWrt by the "ath79" SoC family
|
||||
|
||||
The GL-AR750 has two distinct sets of wifi hardware. The 2.4GHz
|
||||
radio is part of the QCA9531 SoC, i.e. it's on the same silicon as
|
||||
the CPU, the Ethernet, the USB etc. The device is connected to the
|
||||
host via AHB, the "Advanced High-Performance Bus" and it is
|
||||
supported in Linux using the ath9k driver. The 5GHz support, on the
|
||||
other hand, is provided by a QCA9887 PCIe (PCI embedded) WLAN chip:
|
||||
I haven't looked closely at the router innards to see if this is
|
||||
actually physically a separate board that could be unplugged, but
|
||||
as far as the Linux is concerned it behaves as one. This is
|
||||
host via `AHB <https://en.wikipedia.org/wiki/Advanced_Microcontroller_Bus_Architecture>`_ and it is
|
||||
supported in Linux using the ath9k driver. 5GHz wifi
|
||||
is provided by a QCA9887 PCIe (PCI embedded) WLAN chip,
|
||||
supported by the ath10k driver.
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
As with many GL.iNet devices, the stock vendor firmware
|
||||
is a fork of OpenWrt, meaning that the binary created by
|
||||
:ref:`system-outputs-mtdimage` can be flashed using the
|
||||
vendor web UI or the U-Boot emergency "unbrick" routine.
|
||||
|
||||
For flashing from an existing Liminix system (we believe that) it
|
||||
is necessary to first boot into a :ref:`system-outputs-kexecboot`
|
||||
system, otherwise you'll be overwriting flash partitions while
|
||||
they're in use - and that might not end well.
|
||||
|
||||
Vendor web page: https://www.gl-inet.com/products/gl-ar750/
|
||||
|
||||
OpenWrt web page: https://openwrt.org/toh/gl.inet/gl-ar750
|
||||
|
||||
'';
|
||||
|
||||
module = {pkgs, config, ... }:
|
||||
module = {pkgs, config, lim, ... }:
|
||||
let
|
||||
openwrt = pkgs.openwrt;
|
||||
firmwareBlobs = pkgs.pkgsBuildBuild.fetchFromGitHub {
|
||||
@@ -57,14 +71,15 @@
|
||||
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
|
||||
offset = 1024 * 20; # 0x5000
|
||||
size = 2048 + 68; # 0x844
|
||||
offset = lim.parseInt "0x5000";
|
||||
size = lim.parseInt "0x844";
|
||||
in pkgs.liminix.services.oneshot rec {
|
||||
name = "ath10k_cal_data";
|
||||
up = ''
|
||||
@@ -75,22 +90,29 @@
|
||||
dd if=/dev/$part of=data iflag=skip_bytes,fullblock bs=${toString size} skip=${toString offset} count=1
|
||||
)
|
||||
'';
|
||||
down = "true";
|
||||
};
|
||||
inherit (pkgs.pseudofile) dir symlink;
|
||||
inherit (pkgs.liminix.networking) interface;
|
||||
in {
|
||||
imports = [
|
||||
../../modules/network
|
||||
../../modules/arch/mipseb.nix
|
||||
../../modules/outputs/tftpboot.nix
|
||||
../../modules/outputs/mtdimage.nix
|
||||
../../modules/outputs/jffs2.nix
|
||||
];
|
||||
|
||||
programs.busybox.options = {
|
||||
FEATURE_DD_IBS_OBS = "y"; # ath10k_cal_data needs skip_bytes,fullblock
|
||||
};
|
||||
hardware = {
|
||||
defaultOutput = "tftpboot";
|
||||
loadAddress = "0x80060000";
|
||||
entryPoint = "0x80060000";
|
||||
defaultOutput = "mtdimage";
|
||||
loadAddress = lim.parseInt "0x80060000";
|
||||
entryPoint = lim.parseInt "0x80060000";
|
||||
flash = {
|
||||
address = "0x9F060000";
|
||||
size ="0xfa0000";
|
||||
eraseBlockSize = "65536";
|
||||
address = lim.parseInt "0x9F060000";
|
||||
size = lim.parseInt "0xfa0000";
|
||||
eraseBlockSize = 65536;
|
||||
};
|
||||
rootDevice = "/dev/mtdblock5";
|
||||
dts = {
|
||||
@@ -100,19 +122,20 @@
|
||||
];
|
||||
};
|
||||
|
||||
networkInterfaces = {
|
||||
lan = interface { device = "eth0"; };
|
||||
wan = interface { device = "eth1"; };
|
||||
|
||||
wlan_24 = interface {
|
||||
device = "wlan0";
|
||||
dependencies = [ mac80211 ];
|
||||
networkInterfaces =
|
||||
let inherit (config.system.service.network) link;
|
||||
in {
|
||||
lan = link.build { ifname = "eth0"; };
|
||||
wan = link.build { ifname = "eth1"; };
|
||||
wlan = link.build {
|
||||
ifname = "wlan0";
|
||||
dependencies = [ mac80211 ];
|
||||
};
|
||||
wlan5 = link.build {
|
||||
ifname = "wlan1";
|
||||
dependencies = [ ath10k_cal_data mac80211 ];
|
||||
};
|
||||
};
|
||||
wlan_5 = interface {
|
||||
device = "wlan1";
|
||||
dependencies = [ mac80211 ath10k_cal_data ];
|
||||
};
|
||||
};
|
||||
};
|
||||
filesystem = dir {
|
||||
lib = dir {
|
||||
@@ -125,21 +148,25 @@
|
||||
};
|
||||
};
|
||||
boot.tftp = {
|
||||
loadAddress = "0x00A00000";
|
||||
loadAddress = lim.parseInt "0x00A00000";
|
||||
};
|
||||
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/v5.x/linux-5.15.137.tar.gz";
|
||||
hash = "sha256-PkdzUKZ0IpBiWe/RS70J76JKnBFzRblWcKlaIFNxnHQ=";
|
||||
};
|
||||
|
||||
# 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 = {
|
||||
MIPS_ELF_APPENDED_DTB = "y";
|
||||
OF = "y";
|
||||
USE_OF = "y";
|
||||
ATH79 = "y";
|
||||
PCI = "y";
|
||||
PCI_AR724X = "y";
|
||||
@@ -159,7 +186,6 @@
|
||||
CONSOLE_LOGLEVEL_QUIET = "4";
|
||||
|
||||
NET = "y";
|
||||
NETDEVICES = "y";
|
||||
ETHERNET = "y";
|
||||
NET_VENDOR_ATHEROS = "y";
|
||||
AG71XX = "y"; # ethernet (qca,qca9530-eth)
|
||||
@@ -167,7 +193,6 @@
|
||||
AR8216_PHY = "y"; # eth1 is behind a switch
|
||||
|
||||
MTD_SPI_NOR = "y";
|
||||
MTD_SPI_NOR_USE_4K_SECTORS = "n"; # jffs2 needs min 8k erase block
|
||||
|
||||
SPI_ATH79 = "y"; # these are copied from OpenWrt.
|
||||
SPI_MASTER= "y"; # At least one of them is necessary
|
||||
@@ -184,25 +209,25 @@
|
||||
SYSFS = "y";
|
||||
SPI = "y";
|
||||
MTD = "y";
|
||||
MTD_CMDLINE_PARTS = "y";
|
||||
MTD_BLOCK = "y"; # fix undefined ref to register_mtd_blktrans_devs
|
||||
|
||||
WATCHDOG = "y";
|
||||
ATH79_WDT = "y"; # watchdog timer
|
||||
|
||||
CPU_BIG_ENDIAN= "y";
|
||||
|
||||
# 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 ...
|
||||
|
||||
CMDLINE_PARTITION = "y";
|
||||
EARLY_PRINTK = "y";
|
||||
|
||||
PARTITION_ADVANCED = "y";
|
||||
PRINTK_TIME = "y";
|
||||
SQUASHFS = "y";
|
||||
SQUASHFS_XZ = "y";
|
||||
};
|
||||
conditionalConfig = {
|
||||
WLAN = {
|
||||
WLAN_VENDOR_ATH = "y";
|
||||
ATH_COMMON = "m";
|
||||
ATH9K = "m";
|
||||
ATH9K_AHB = "y";
|
||||
ATH10K = "m";
|
||||
ATH10K_PCI = "m";
|
||||
ATH10K_DEBUG = "y";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# GL.INet GL-MT300A
|
||||
# GL.iNet GL-MT300A
|
||||
|
||||
{
|
||||
system = {
|
||||
@@ -12,25 +12,56 @@
|
||||
};
|
||||
|
||||
description = ''
|
||||
GL.iNet GL-MT300A
|
||||
*****************
|
||||
|
||||
The GL-MT300A is based on a MT7620 chipset.
|
||||
|
||||
For flashing from U-Boot, the firmware partition is from
|
||||
0xbc050000 to 0xbcfd0000.
|
||||
|
||||
WiFi on this device is provided by the rt2800soc module. It
|
||||
expects firmware to be present in the "factory" MTD partition, so
|
||||
- assuming we want to use the wireless - we need to build MTD
|
||||
support into the kernel even if we're using TFTP root
|
||||
support into the kernel even if we're using TFTP root.
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
The stock vendor firmware is a fork of OpenWrt, meaning that the
|
||||
binary created by :ref:`system-outputs-mtdimage` can be flashed
|
||||
using the vendor web UI or the U-Boot emergency "unbrick" routine.
|
||||
|
||||
For flashing from an existing Liminix system (we think) it
|
||||
is necessary to first boot into a :ref:`system-outputs-kexecboot`
|
||||
system, otherwise you'll be overwriting flash partitions while
|
||||
they're in use - and that might not end well.
|
||||
|
||||
Vendor web page: https://www.gl-inet.com/products/gl-mt300a/
|
||||
|
||||
OpenWrt web page: https://openwrt.org/toh/gl.inet/gl-mt300a
|
||||
|
||||
'';
|
||||
|
||||
module = { pkgs, config, ...}:
|
||||
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 {
|
||||
in {
|
||||
imports = [
|
||||
../../modules/arch/mipsel.nix
|
||||
../../modules/outputs/tftpboot.nix
|
||||
../../modules/outputs/mtdimage.nix
|
||||
../../modules/outputs/jffs2.nix
|
||||
];
|
||||
hardware = {
|
||||
defaultOutput = "tftpboot";
|
||||
loadAddress = "0x80000000";
|
||||
entryPoint = "0x80000000";
|
||||
defaultOutput = "mtdimage";
|
||||
loadAddress = lim.parseInt "0x80000000";
|
||||
entryPoint = lim.parseInt "0x80000000";
|
||||
|
||||
# Creating 5 MTD partitions on "spi0.0":
|
||||
# 0x000000000000-0x000000030000 : "u-boot"
|
||||
@@ -43,9 +74,9 @@
|
||||
# 0x000000260000-0x000000f80000 : "rootfs"
|
||||
|
||||
flash = {
|
||||
address = "0xbc050000";
|
||||
size ="0xf80000";
|
||||
eraseBlockSize = "65536";
|
||||
address = lim.parseInt "0xbc050000";
|
||||
size = lim.parseInt "0xf80000";
|
||||
eraseBlockSize = 65536;
|
||||
};
|
||||
rootDevice = "/dev/mtdblock5";
|
||||
|
||||
@@ -55,46 +86,58 @@
|
||||
"${openwrt.src}/target/linux/ramips/dts"
|
||||
];
|
||||
};
|
||||
networkInterfaces = rec {
|
||||
# lan and wan ports are both behind a switch on eth0
|
||||
eth = interface { device = "eth0"; };
|
||||
lan = interface {
|
||||
type = "vlan";
|
||||
device = "eth0.1";
|
||||
link = "eth0";
|
||||
id = "1";
|
||||
dependencies = [eth];
|
||||
networkInterfaces =
|
||||
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
|
||||
lan = vlan.build {
|
||||
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";
|
||||
dependencies = [ mac80211 ];
|
||||
};
|
||||
};
|
||||
wan = interface {
|
||||
type = "vlan";
|
||||
device = "eth0.2";
|
||||
id = "2";
|
||||
link = "eth0";
|
||||
dependencies = [eth];
|
||||
};
|
||||
wlan = interface {
|
||||
device = "wlan0";
|
||||
dependencies = [ mac80211 ];
|
||||
};
|
||||
};
|
||||
};
|
||||
boot.tftp = {
|
||||
loadAddress = "0x00A00000";
|
||||
loadAddress = lim.parseInt "0x00A00000";
|
||||
};
|
||||
|
||||
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=";
|
||||
url = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.137.tar.gz";
|
||||
hash = "sha256-PkdzUKZ0IpBiWe/RS70J76JKnBFzRblWcKlaIFNxnHQ=";
|
||||
};
|
||||
extraPatchPhase = ''
|
||||
${openwrt.applyPatches.ramips}
|
||||
'';
|
||||
config = {
|
||||
MIPS_ELF_APPENDED_DTB = "y";
|
||||
OF = "y";
|
||||
USE_OF = "y";
|
||||
|
||||
RALINK = "y";
|
||||
PCI = "y";
|
||||
@@ -109,12 +152,12 @@
|
||||
CONSOLE_LOGLEVEL_QUIET = "4";
|
||||
|
||||
NET = "y";
|
||||
NETDEVICES = "y";
|
||||
ETHERNET = "y";
|
||||
NET_VENDOR_RALINK = "y";
|
||||
NET_RALINK_MDIO = "y";
|
||||
NET_RALINK_MDIO_MT7620 = "y";
|
||||
NET_RALINK_MT7620 = "y";
|
||||
SWPHY = "y";
|
||||
|
||||
SPI = "y";
|
||||
MTD_SPI_NOR = "y";
|
||||
@@ -123,35 +166,26 @@
|
||||
SPI_MASTER= "y";
|
||||
SPI_MEM= "y";
|
||||
|
||||
# both the ethernet ports on this device (lan and wan)
|
||||
# are behind a switch, so we need VLANs to do anything
|
||||
# useful with them
|
||||
|
||||
VLAN_8021Q = "y";
|
||||
SWCONFIG = "y";
|
||||
SWPHY = "y";
|
||||
|
||||
BRIDGE = "y";
|
||||
BRIDGE_VLAN_FILTERING = "y";
|
||||
BRIDGE_IGMP_SNOOPING = "y";
|
||||
|
||||
MTD = "y";
|
||||
MTD_CMDLINE_PARTS = "y";
|
||||
MTD_BLOCK = "y"; # fix undefined ref to register_mtd_blktrans_devs
|
||||
|
||||
CPU_LITTLE_ENDIAN = "y";
|
||||
|
||||
CMDLINE_PARTITION = "y";
|
||||
EARLY_PRINTK = "y";
|
||||
|
||||
NEW_LEDS = "y";
|
||||
LEDS_CLASS = "y"; # required by rt2x00lib
|
||||
|
||||
PARTITION_ADVANCED = "y";
|
||||
PRINTK_TIME = "y";
|
||||
SQUASHFS = "y";
|
||||
SQUASHFS_XZ = "y";
|
||||
} // lib.optionalAttrs (config.system.service ? vlan) {
|
||||
SWCONFIG = "y";
|
||||
};
|
||||
conditionalConfig = {
|
||||
WLAN = {
|
||||
WLAN_VENDOR_RALINK = "y";
|
||||
RT2800SOC = "m";
|
||||
RT2X00 = "m";
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
# GL.INet GL-MT300N v2
|
||||
|
||||
{
|
||||
system = {
|
||||
crossSystem = {
|
||||
@@ -11,22 +9,55 @@
|
||||
};
|
||||
};
|
||||
|
||||
module = { pkgs, config, ...}:
|
||||
description = ''
|
||||
GL.iNet GL-MT300N-v2
|
||||
********************
|
||||
|
||||
The GL-MT300N-v2 "Mango" is is very similar to the :ref:`MT300A <GL.iNet 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.
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
The stock vendor firmware is a fork of OpenWrt, meaning that the
|
||||
binary created by :ref:`system-outputs-mtdimage` can be flashed
|
||||
using the vendor web UI or the U-Boot emergency "unbrick" routine.
|
||||
|
||||
For flashing from an existing Liminix system (we think) it
|
||||
is necessary to first boot into a :ref:`system-outputs-kexecboot`
|
||||
system, otherwise you'll be overwriting flash partitions while
|
||||
they're in use - and that might not end well.
|
||||
|
||||
Vendor web page: https://www.gl-inet.com/products/gl-mt300n-v2/
|
||||
|
||||
OpenWrt web page: https://openwrt.org/toh/gl.inet/gl-mt300n_v2
|
||||
|
||||
'';
|
||||
|
||||
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";
|
||||
hash = "sha256:1dkhfznmdz6s50kwc841x3wj0h6zg6icg5g2bim9pvg66as2vmh9";
|
||||
};
|
||||
in {
|
||||
imports = [
|
||||
../../modules/arch/mipsel.nix
|
||||
../../modules/outputs/tftpboot.nix
|
||||
../../modules/outputs/mtdimage.nix
|
||||
../../modules/outputs/jffs2.nix
|
||||
];
|
||||
filesystem = dir {
|
||||
lib = dir {
|
||||
firmware = dir {
|
||||
@@ -35,14 +66,14 @@
|
||||
};
|
||||
};
|
||||
hardware = {
|
||||
defaultOutput = "tftpboot";
|
||||
loadAddress = "0x80000000";
|
||||
entryPoint = "0x80000000";
|
||||
defaultOutput = "mtdimage";
|
||||
loadAddress = lim.parseInt "0x80000000";
|
||||
entryPoint = lim.parseInt "0x80000000";
|
||||
|
||||
flash = {
|
||||
address = "0xbc050000";
|
||||
size = "0xfb0000";
|
||||
eraseBlockSize = "65536";
|
||||
address = lim.parseInt "0xbc050000";
|
||||
size = lim.parseInt "0xfb0000";
|
||||
eraseBlockSize = 65536;
|
||||
};
|
||||
rootDevice = "/dev/mtdblock5";
|
||||
|
||||
@@ -52,69 +83,61 @@
|
||||
"${openwrt.src}/target/linux/ramips/dts"
|
||||
];
|
||||
};
|
||||
networkInterfaces = rec {
|
||||
# lan and wan ports are both behind a switch on eth0
|
||||
eth =
|
||||
let 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 = "swconfig dev switch0 set reset";
|
||||
};
|
||||
in interface {
|
||||
device = "eth0";
|
||||
dependencies = [swconfig];
|
||||
networkInterfaces =
|
||||
let
|
||||
inherit (config.system.service.network) link;
|
||||
inherit (config.system.service) vlan;
|
||||
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 = "swconfig dev switch0 set reset";
|
||||
};
|
||||
in rec {
|
||||
eth = link.build { ifname = "eth0"; dependencies = [swconfig]; };
|
||||
# lan and wan ports are both behind a switch on eth0
|
||||
lan = vlan.build {
|
||||
ifname = "eth0.1";
|
||||
primary = eth;
|
||||
vid = "1";
|
||||
};
|
||||
wan = vlan.build {
|
||||
ifname = "eth0.2";
|
||||
primary = eth;
|
||||
vid = "2";
|
||||
};
|
||||
wlan = link.build {
|
||||
ifname = "wlan0";
|
||||
dependencies = [ mac80211 ];
|
||||
};
|
||||
lan = interface {
|
||||
type = "vlan";
|
||||
device = "eth0.1";
|
||||
link = "eth0";
|
||||
id = "1";
|
||||
dependencies = [eth];
|
||||
};
|
||||
wan = interface {
|
||||
type = "vlan";
|
||||
device = "eth0.2";
|
||||
id = "2";
|
||||
link = "eth0";
|
||||
dependencies = [eth];
|
||||
};
|
||||
wlan = interface {
|
||||
device = "wlan0";
|
||||
dependencies = [ mac80211 ];
|
||||
};
|
||||
};
|
||||
};
|
||||
boot.tftp = {
|
||||
# 20MB seems to give enough room to uncompress the kernel
|
||||
# without anything getting trodden on. 10MB was too small
|
||||
loadAddress = "0x1400000";
|
||||
loadAddress = lim.parseInt "0x1400000";
|
||||
};
|
||||
|
||||
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=";
|
||||
url = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.137.tar.gz";
|
||||
hash = "sha256-PkdzUKZ0IpBiWe/RS70J76JKnBFzRblWcKlaIFNxnHQ=";
|
||||
};
|
||||
extraPatchPhase = ''
|
||||
${openwrt.applyPatches.ramips}
|
||||
'';
|
||||
config = {
|
||||
MIPS_ELF_APPENDED_DTB = "y";
|
||||
OF = "y";
|
||||
USE_OF = "y";
|
||||
|
||||
RALINK = "y";
|
||||
PCI = "y";
|
||||
SOC_MT7620 = "y";
|
||||
CPU_LITTLE_ENDIAN= "y";
|
||||
|
||||
SERIAL_8250_CONSOLE = "y";
|
||||
SERIAL_8250 = "y";
|
||||
@@ -125,7 +148,6 @@
|
||||
CONSOLE_LOGLEVEL_QUIET = "4";
|
||||
|
||||
MTD = "y";
|
||||
MTD_CMDLINE_PARTS = "y";
|
||||
MTD_BLOCK = "y"; # fix undefined ref to register_mtd_blktrans_dev
|
||||
|
||||
SPI = "y";
|
||||
@@ -138,7 +160,6 @@
|
||||
REGULATOR_FIXED_VOLTAGE = "y";
|
||||
|
||||
NET = "y";
|
||||
NETDEVICES = "y";
|
||||
ETHERNET = "y";
|
||||
|
||||
PHYLIB = "y";
|
||||
@@ -148,36 +169,31 @@
|
||||
NET_VENDOR_RALINK = "y";
|
||||
NET_RALINK_RT3050 = "y";
|
||||
NET_RALINK_SOC="y";
|
||||
|
||||
# both the ethernet ports on this device (lan and wan)
|
||||
# are behind a switch, so we need VLANs to do anything
|
||||
# useful with them
|
||||
|
||||
VLAN_8021Q = "y";
|
||||
SWCONFIG = "y";
|
||||
SWPHY = "y";
|
||||
|
||||
BRIDGE = "y";
|
||||
BRIDGE_VLAN_FILTERING = "y";
|
||||
BRIDGE_IGMP_SNOOPING = "y";
|
||||
|
||||
WATCHDOG = "y";
|
||||
RALINK_WDT = "y"; # watchdog
|
||||
MT7621_WDT = "y"; # or it might be this one
|
||||
|
||||
GPIOLIB="y";
|
||||
GPIO_MT7621 = "y";
|
||||
|
||||
PHY_RALINK_USB = "y";
|
||||
|
||||
CMDLINE_PARTITION = "y";
|
||||
EARLY_PRINTK = "y";
|
||||
|
||||
PARTITION_ADVANCED = "y";
|
||||
PRINTK_TIME = "y";
|
||||
SQUASHFS = "y";
|
||||
SQUASHFS_XZ = "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
|
||||
};
|
||||
conditionalConfig = {
|
||||
WLAN = {
|
||||
WLAN_VENDOR_RALINK = "y";
|
||||
WLAN_VENDOR_MEDIATEK = "y";
|
||||
MT7603E = "m";
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
51
devices/qemu-aarch64/default.nix
Normal file
51
devices/qemu-aarch64/default.nix
Normal file
@@ -0,0 +1,51 @@
|
||||
# This "device" generates images that can be used with the QEMU
|
||||
# emulator. The default output is a directory containing separate
|
||||
# kernel ("Image" format) and root filesystem (squashfs or jffs2)
|
||||
# images
|
||||
{
|
||||
system = {
|
||||
crossSystem = {
|
||||
config = "aarch64-unknown-linux-musl";
|
||||
};
|
||||
};
|
||||
|
||||
description = ''
|
||||
QEMU Aarch64
|
||||
************
|
||||
|
||||
This target produces an image for
|
||||
the `QEMU "virt" platform <https://www.qemu.org/docs/master/system/arm/virt.html>`_ using a 64 bit CPU type.
|
||||
|
||||
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.
|
||||
|
||||
'';
|
||||
|
||||
# this device is described by the "qemu" device
|
||||
installer = "vmroot";
|
||||
|
||||
module = {pkgs, config, lim, ... }: {
|
||||
imports = [
|
||||
../../modules/arch/aarch64.nix
|
||||
../families/qemu.nix
|
||||
];
|
||||
kernel = {
|
||||
config = {
|
||||
VIRTUALIZATION = "y";
|
||||
PCI_HOST_GENERIC="y";
|
||||
|
||||
SERIAL_AMBA_PL011 = "y";
|
||||
SERIAL_AMBA_PL011_CONSOLE = "y";
|
||||
};
|
||||
};
|
||||
boot.commandLine = [
|
||||
"console=ttyAMA0,38400"
|
||||
];
|
||||
hardware = let addr = lim.parseInt "0x40010000"; in {
|
||||
loadAddress = addr;
|
||||
entryPoint = addr;
|
||||
};
|
||||
};
|
||||
}
|
||||
53
devices/qemu-armv7l/default.nix
Normal file
53
devices/qemu-armv7l/default.nix
Normal file
@@ -0,0 +1,53 @@
|
||||
# This "device" generates images that can be used with the QEMU
|
||||
# emulator. The default output is a directory containing separate
|
||||
# kernel ("Image" format) and root filesystem (squashfs or jffs2)
|
||||
# images
|
||||
{
|
||||
system = {
|
||||
crossSystem = {
|
||||
config = "armv7l-unknown-linux-musleabihf";
|
||||
};
|
||||
};
|
||||
|
||||
# this device is described by the "qemu" device
|
||||
description = ''
|
||||
QEMU ARM v7
|
||||
***********
|
||||
|
||||
This target produces an image for
|
||||
the `QEMU "virt" platform <https://www.qemu.org/docs/master/system/arm/virt.html>`_ using a 32 bit CPU type.
|
||||
|
||||
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.
|
||||
'';
|
||||
installer = "vmroot";
|
||||
|
||||
module = {pkgs, config, lim, ... }: {
|
||||
imports = [
|
||||
../../modules/arch/arm.nix
|
||||
../families/qemu.nix
|
||||
];
|
||||
kernel = {
|
||||
config = {
|
||||
PCI_HOST_GENERIC = "y";
|
||||
ARCH_VIRT = "y";
|
||||
|
||||
VFP = "y";
|
||||
NEON = "y";
|
||||
AEABI = "y";
|
||||
|
||||
SERIAL_AMBA_PL011 = "y";
|
||||
SERIAL_AMBA_PL011_CONSOLE = "y";
|
||||
};
|
||||
};
|
||||
boot.commandLine = [
|
||||
"console=ttyAMA0"
|
||||
];
|
||||
hardware = let addr = lim.parseInt "0x40008000"; in {
|
||||
loadAddress = addr;
|
||||
entryPoint = addr;
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -12,59 +12,65 @@
|
||||
};
|
||||
};
|
||||
|
||||
module = {pkgs, config, ... }: {
|
||||
description = ''
|
||||
QEMU MIPS
|
||||
*********
|
||||
|
||||
This target produces an image for
|
||||
QEMU, the "generic and open source machine emulator and
|
||||
virtualizer".
|
||||
|
||||
MIPS QEMU emulates a "Malta" board, which was an ATX form factor
|
||||
evaluation board made by MIPS Technologies, but mostly in Liminix
|
||||
we use paravirtualized devices (Virtio) instead of emulating
|
||||
hardware.
|
||||
|
||||
Building an image for QEMU results in a :file:`result/` directory
|
||||
containing ``run.sh`` ``vmlinux``, and ``rootfs`` files. To invoke
|
||||
the emulator, run ``run.sh``.
|
||||
|
||||
The configuration includes two emulated "hardware" ethernet
|
||||
devices and the kernel :code:`mac80211_hwsim` module to
|
||||
provide an emulated wlan device. To read more about how
|
||||
to connect to this network, refer to :ref:`qemu-networking`
|
||||
in the Development manual.
|
||||
|
||||
'';
|
||||
module = {pkgs, config, lib, lim, ... }: {
|
||||
imports = [
|
||||
../../modules/arch/mipseb.nix
|
||||
../families/qemu.nix
|
||||
];
|
||||
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 = {
|
||||
MIPS_MALTA= "y";
|
||||
CPU_LITTLE_ENDIAN= "n";
|
||||
CPU_BIG_ENDIAN= "y";
|
||||
CPU_MIPS32_R2= "y";
|
||||
|
||||
MTD = "y";
|
||||
MTD_BLOCK2MTD = "y";
|
||||
MTD_BLKDEVS = "y";
|
||||
MTD_BLOCK = "y";
|
||||
|
||||
SQUASHFS = "y";
|
||||
SQUASHFS_XZ = "y";
|
||||
|
||||
VIRTIO_MENU = "y";
|
||||
PCI = "y";
|
||||
VIRTIO_PCI = "y";
|
||||
BLOCK = "y";
|
||||
VIRTIO_BLK = "y";
|
||||
NETDEVICES = "y";
|
||||
VIRTIO_NET = "y";
|
||||
POWER_RESET = "y";
|
||||
POWER_RESET_SYSCON = "y";
|
||||
|
||||
SERIAL_8250= "y";
|
||||
SERIAL_8250_CONSOLE= "y";
|
||||
};
|
||||
};
|
||||
hardware =
|
||||
let
|
||||
mac80211 = pkgs.mac80211.override {
|
||||
drivers = ["mac80211_hwsim"];
|
||||
klibBuild = config.system.outputs.kernel.modulesupport;
|
||||
};
|
||||
inherit (pkgs.liminix.networking) interface;
|
||||
# from arch/mips/mti-malta/Platform:load-$(CONFIG_MIPS_MALTA) += 0xffffffff80100000
|
||||
let addr = lim.parseInt "0x80100000";
|
||||
in {
|
||||
defaultOutput = "vmroot";
|
||||
flash.eraseBlockSize = "65536"; # c.f. pkgs/mips-vm/mips-vm.sh
|
||||
networkInterfaces = {
|
||||
lan = interface { device = "eth0"; };
|
||||
wan = interface { device = "eth1"; };
|
||||
loadAddress = addr;
|
||||
entryPoint = addr;
|
||||
|
||||
wlan_24 = interface {
|
||||
device = "wlan0";
|
||||
dependencies = [ mac80211 ];
|
||||
};
|
||||
# Unlike the arm qemu targets, we need a static dts when
|
||||
# running u-boot-using tests, qemu dumpdtb command doesn't
|
||||
# work for this board. I am not at all sure this dts is
|
||||
# *correct* but it does at least boot
|
||||
dts = lib.mkForce {
|
||||
src = "${config.system.outputs.kernel.modulesupport}/arch/mips/boot/dts/mti/malta.dts";
|
||||
includes = [
|
||||
"${config.system.outputs.kernel.modulesupport}/arch/mips/boot/dts/"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
442
devices/tp-archer-ax23/default.nix
Normal file
442
devices/tp-archer-ax23/default.nix
Normal file
@@ -0,0 +1,442 @@
|
||||
{
|
||||
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 = {
|
||||
src = pkgs.pkgsBuildBuild.fetchurl {
|
||||
name = "linux.tar.gz";
|
||||
url = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.137.tar.gz";
|
||||
hash = "sha256-PkdzUKZ0IpBiWe/RS70J76JKnBFzRblWcKlaIFNxnHQ=";
|
||||
};
|
||||
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";
|
||||
includes = [
|
||||
"${openwrt.src}/target/linux/ramips/dts"
|
||||
"${config.system.outputs.kernel.modulesupport}/arch/arm64/boot/dts/mediatek/"
|
||||
];
|
||||
};
|
||||
|
||||
networkInterfaces =
|
||||
let
|
||||
inherit (config.system.service.network) link;
|
||||
inherit (config.system.service) bridge;
|
||||
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 ];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
412
devices/turris-omnia/default.nix
Normal file
412
devices/turris-omnia/default.nix
Normal file
@@ -0,0 +1,412 @@
|
||||
{
|
||||
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 = {
|
||||
crossSystem = {
|
||||
config = "armv7l-unknown-linux-musleabihf";
|
||||
};
|
||||
};
|
||||
|
||||
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 {
|
||||
name = "mtd_by_name_links";
|
||||
up = ''
|
||||
mkdir -p /dev/mtd/by-name
|
||||
cd /dev/mtd/by-name
|
||||
for i in /sys/class/mtd/mtd*[0-9]; do
|
||||
ln -s ../../$(basename $i) $(cat $i/name)
|
||||
done
|
||||
'';
|
||||
};
|
||||
in {
|
||||
imports = [
|
||||
../../modules/arch/arm.nix
|
||||
../../modules/outputs/tftpboot.nix
|
||||
../../modules/outputs/mbrimage.nix
|
||||
../../modules/outputs/extlinux.nix
|
||||
];
|
||||
|
||||
config = {
|
||||
services.mtd-name-links = mtd_by_name_links;
|
||||
kernel = {
|
||||
src = pkgs.pkgsBuildBuild.fetchurl {
|
||||
name = "linux.tar.gz";
|
||||
url = "https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.7.4.tar.gz";
|
||||
hash = "sha256-wIrmL0BS63nRwWfm4nw+dRNVPUzGh9M4X7LaHzAn5tU=";
|
||||
};
|
||||
version = "6.7.4";
|
||||
config = {
|
||||
PCI = "y";
|
||||
OF = "y";
|
||||
MEMORY = "y"; # for MVEBU_DEVBUS
|
||||
DMADEVICES = "y"; # for MV_XOR
|
||||
CPU_V7 = "y";
|
||||
ARCH_MULTIPLATFORM = "y";
|
||||
ARCH_MVEBU = "y";
|
||||
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
|
||||
STACKPROTECTOR_PER_TASK = "n";
|
||||
NR_CPUS = "4";
|
||||
VFP = "y";
|
||||
NEON= "y";
|
||||
|
||||
# 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"; # ???
|
||||
PWRSEQ_SIMPLE="y"; # ???
|
||||
MMC_BLOCK="y";
|
||||
|
||||
MMC_SDHCI= "y";
|
||||
MMC_SDHCI_PLTFM= "y";
|
||||
MMC_SDHCI_PXAV3= "y";
|
||||
MMC_MVSDIO= "y";
|
||||
|
||||
SERIAL_8250 = "y";
|
||||
SERIAL_8250_CONSOLE = "y";
|
||||
SERIAL_OF_PLATFORM="y";
|
||||
SERIAL_MVEBU_UART = "y";
|
||||
SERIAL_MVEBU_CONSOLE = "y";
|
||||
|
||||
SERIAL_8250_DMA= "y";
|
||||
SERIAL_8250_DW= "y";
|
||||
SERIAL_8250_EXTENDED= "y";
|
||||
SERIAL_8250_MANY_PORTS= "y";
|
||||
SERIAL_8250_SHARE_IRQ= "y";
|
||||
OF_ADDRESS= "y";
|
||||
OF_MDIO= "y";
|
||||
|
||||
WATCHDOG = "y"; # watchdog is enabled by u-boot
|
||||
ORION_WATCHDOG = "y"; # so is non-optional to keep feeding
|
||||
|
||||
MVEBU_DEVBUS = "y"; # "Device Bus controller ... flash devices such as NOR, NAND, SRAM, and FPGA"
|
||||
MVMDIO = "y";
|
||||
MVNETA = "y";
|
||||
MVNETA_BM = "y";
|
||||
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";
|
||||
|
||||
# there is NOR flash on this device, which is used for U-Boot
|
||||
# and the rescue system (which we don't interfere with) but
|
||||
# also for the U-Boot environment variables (which we might
|
||||
# need to meddle with)
|
||||
MTD_SPI_NOR = "y";
|
||||
SPI = "y";
|
||||
SPI_MASTER = "y";
|
||||
SPI_ORION = "y";
|
||||
|
||||
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";
|
||||
};
|
||||
};
|
||||
};
|
||||
boot = {
|
||||
commandLine = [
|
||||
"console=ttyS0,115200"
|
||||
"pcie_aspm=off" # ath9k pci incompatible with PCIe ASPM
|
||||
];
|
||||
};
|
||||
filesystem =
|
||||
let
|
||||
inherit (pkgs.pseudofile) dir symlink;
|
||||
firmware = pkgs.stdenv.mkDerivation {
|
||||
name = "wlan-firmware";
|
||||
phases = ["installPhase"];
|
||||
installPhase = ''
|
||||
mkdir $out
|
||||
cp -r ${pkgs.linux-firmware}/lib/firmware/ath10k/QCA988X $out
|
||||
'';
|
||||
};
|
||||
in dir {
|
||||
lib = dir {
|
||||
firmware = dir {
|
||||
ath10k = symlink firmware;
|
||||
};
|
||||
};
|
||||
etc = dir {
|
||||
"fw_env.config" =
|
||||
let f = pkgs.writeText "fw_env.config" ''
|
||||
/dev/mtd/by-name/u-boot-env 0x0 0x10000 0x10000
|
||||
'';
|
||||
in symlink f;
|
||||
};
|
||||
};
|
||||
|
||||
boot.tftp = {
|
||||
loadAddress = lim.parseInt "0x1700000";
|
||||
kernelFormat = "zimage";
|
||||
compressRoot = true;
|
||||
};
|
||||
|
||||
hardware = let
|
||||
mac80211 = pkgs.kmodloader.override {
|
||||
inherit (config.system.outputs) kernel;
|
||||
targets = ["ath9k" "ath10k_pci"];
|
||||
};
|
||||
in {
|
||||
defaultOutput = "mtdimage";
|
||||
loadAddress = lim.parseInt "0x00800000"; # "0x00008000";
|
||||
entryPoint = lim.parseInt "0x00800000"; # "0x00008000";
|
||||
rootDevice = "/dev/mmcblk0p1";
|
||||
|
||||
dts = {
|
||||
src = "${config.system.outputs.kernel.modulesupport}/arch/arm/boot/dts/marvell/armada-385-turris-omnia.dts";
|
||||
includes = [
|
||||
"${config.system.outputs.kernel.modulesupport}/arch/arm/boot/dts/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.
|
||||
# It's connected to port 5 of the 88E6176 switch
|
||||
devpath = "/devices/platform/soc/soc:internal-regs/f1070000.ethernet";
|
||||
# name is unambiguous but not very semantic
|
||||
ifname = "en70000";
|
||||
};
|
||||
en30000 = link.build {
|
||||
# in armada-38x.dtsi this is eth1
|
||||
# It's connected to port 6 of the 88E6176 switch
|
||||
devpath = "/devices/platform/soc/soc:internal-regs/f1030000.ethernet";
|
||||
# name is unambiguous but not very semantic
|
||||
ifname = "en30000";
|
||||
};
|
||||
# the default (from the dts? I'm guessing) behavour for
|
||||
# lan ports on the switch is to attach them to
|
||||
# en30000. It should be possible to do something better,
|
||||
# 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.
|
||||
|
||||
wan = link.build {
|
||||
# in armada-38x.dtsi this is eth2. It may be connected to
|
||||
# an ethernet phy or to the SFP cage, depending on a gpio
|
||||
devpath = "/devices/platform/soc/soc:internal-regs/f1034000.ethernet";
|
||||
ifname = "wan";
|
||||
};
|
||||
|
||||
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";
|
||||
dependencies = [ mac80211 ];
|
||||
};
|
||||
wlan5 = link.build {
|
||||
ifname = "wlan1";
|
||||
dependencies = [ mac80211 ];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
198
doc/admin.rst
Normal file
198
doc/admin.rst
Normal file
@@ -0,0 +1,198 @@
|
||||
System Administration
|
||||
#####################
|
||||
|
||||
Services on a running system
|
||||
****************************
|
||||
|
||||
* add an s6-rc cheatsheet here
|
||||
|
||||
|
||||
Flashing and updating
|
||||
*********************
|
||||
|
||||
|
||||
|
||||
Flashing from Liminix
|
||||
=====================
|
||||
|
||||
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.
|
||||
|
||||
|
||||
Building the RAM-based image
|
||||
----------------------------
|
||||
|
||||
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.
|
||||
|
||||
For example
|
||||
|
||||
.. 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 -)
|
||||
|
||||
and then login to the device and run
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
cd /run/result
|
||||
sh ./boot.sh .
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
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:
|
||||
|
||||
.. 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 to the device: it doesn't update
|
||||
any profile to add it to ``$PATH``
|
||||
|
||||
|
||||
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
|
||||
|
||||
It doesn't delete old packages automatically: to do that run
|
||||
:command:`min-collect-garbage`, which will delete any packages not in
|
||||
the current system closure. Note that Liminix does not have the NixOS
|
||||
concept of environments or generations, and there is no way back from
|
||||
this except for building the previous configuration again.
|
||||
|
||||
|
||||
Caveats
|
||||
-------
|
||||
|
||||
* it needs there to be enough free space on the device for all the new
|
||||
packages in addition to all the packages already on it - which may be
|
||||
a problem if a lot of things have changed (e.g. a new version of
|
||||
nixpkgs).
|
||||
|
||||
* it cannot upgrade the kernel, only userland
|
||||
6
doc/adr/README
Normal file
6
doc/adr/README
Normal file
@@ -0,0 +1,6 @@
|
||||
Architecture Decision Records
|
||||
#############################
|
||||
|
||||
In this directory you will find descriptions of Liminix architecture
|
||||
decisions.
|
||||
|
||||
201
doc/adr/module-system.rst
Normal file
201
doc/adr/module-system.rst
Normal file
@@ -0,0 +1,201 @@
|
||||
Module system
|
||||
#############
|
||||
|
||||
**Status:** Adopted; implemented in July-September 2023
|
||||
|
||||
|
||||
Context
|
||||
*******
|
||||
|
||||
Liminix users need a way to assemble a full system configuration by
|
||||
combining smaller, more isolated and reusable components, otherwise
|
||||
systems will be unwieldy and copy-and-paste will be rife.
|
||||
|
||||
|
||||
Alternatives
|
||||
************
|
||||
|
||||
NixOS module system
|
||||
===================
|
||||
|
||||
The NixOS module system addresses many of these concerns. A module is
|
||||
a Nix function which accepts a ``configuration`` attrset and some
|
||||
other parameters, and returns a new fragment of ``configuration``
|
||||
which is merged into it. It includes a DSL describing the permitted
|
||||
types of values for each key in the configuration, which is used for
|
||||
checking that the supplied parameters are valid and also governs what
|
||||
to do if two modules both specify a value for the same key. (Usually
|
||||
they are "merged", using some type-appropriate concept of merging.)
|
||||
|
||||
Usually a NixOS module looks only (or mostly only) at a particular
|
||||
subtree of the overall configuration which is hardcoded in the module
|
||||
definition, but the configuration fragment it returns may touch any
|
||||
part of the schema. For example, the factorio module refers to
|
||||
``config.services.factorio``, and it returns values for keys in
|
||||
``systemd.services.factorio`` and ``networking.firewall``. There is no
|
||||
way to use this module to run **two** factorio services with different
|
||||
config (e.g. on different ports) - the only way to make that
|
||||
possible would be to extend the module definition so that it
|
||||
accepts a collection of game configurations and then create
|
||||
a systemd service for each.
|
||||
|
||||
|
||||
NixWRT module system
|
||||
====================
|
||||
|
||||
NixWRT, the (now defunct) predecessor of Liminix, used a homegrown
|
||||
module system modelled on the Nixpkgs overlay pattern. Each module is
|
||||
a function that accepts ``super`` and ``self`` parameters, and
|
||||
using <handwaves>that fixpoint magic thing</handwaves>
|
||||
is called in a chain with the configuration returned by the previous
|
||||
module and the final configuration.
|
||||
|
||||
NixWRT modules mostly don't refer to the configuration object to
|
||||
decide how to configure themselves, but accept their parameters
|
||||
directly as function parameters. For example, the configuration
|
||||
file for "arhcive" (a backup server) includes this text:
|
||||
|
||||
.. code-block:: nix
|
||||
|
||||
(sshd {
|
||||
hostkey = secrets.sshHostKey;
|
||||
authkeys = { root = lib.splitString "\n" secrets.myKeys; };
|
||||
})
|
||||
busybox
|
||||
(usbdisk {
|
||||
label = "backup-disk";
|
||||
mountpoint = "/srv";
|
||||
fstype = "ext4";
|
||||
options = "rw";
|
||||
})
|
||||
|
||||
This gives us flexibility that NixOS modules don't: for example, if we
|
||||
want to mount two USB disks, we can simply repeat that module twice
|
||||
with different parameters - and the module definition doesn't have to
|
||||
handle it specially.
|
||||
|
||||
However, the downside of this system is that we didn't implement any
|
||||
concept of "types" - there is no type information, so there is no
|
||||
systematic checking that parameters are valid, and if two modules set
|
||||
the same config key then the rules for merging are entirely ad hoc.
|
||||
|
||||
There is a further (arguable) downside, which is that the
|
||||
configuration is not just data - it's now part code. While it could be
|
||||
feasible (though I've never seen it done) to encode a NixOS
|
||||
configuration using Yaml or XML and then manipulate it as data, this
|
||||
is not even possible using the NixWRT system.
|
||||
|
||||
|
||||
Use services for everything
|
||||
===========================
|
||||
|
||||
The most common properties that a Liminix configuration needs to
|
||||
define are:
|
||||
|
||||
* which services (processes) to run
|
||||
* what packages to install
|
||||
* permitted users and groups
|
||||
* Linux kernel configuration options
|
||||
* Busybox applets
|
||||
* filesystem layout
|
||||
|
||||
Suppose we only had services?
|
||||
|
||||
A Liminix service is (also) a derivation, so it is able to
|
||||
create any files it likes inside its own store path, and
|
||||
transitively require other packages simply by referring to them.
|
||||
If it needs particular kernel options it could define them
|
||||
as kernel modules to be loaded on demand when the service
|
||||
starts (see the nftables module for an example). However:
|
||||
|
||||
* there is no way for a service to add busybox modules
|
||||
|
||||
* it cannot create files outside of its store path, so
|
||||
wouldn't be able to make e.g. :file:`/etc/something.conf`
|
||||
|
||||
* no way to create users/groups. We could steal the DynamicUsers idea
|
||||
from systemd and make them on demand, but this starts to get a bit
|
||||
more complicated.
|
||||
|
||||
These limitations force us to reject this option as a general
|
||||
solution - though we should strive *where possible* to implement
|
||||
functionality as services and to minimise the proportion of Liminix
|
||||
that manipulates the global configuration.
|
||||
|
||||
|
||||
Decision
|
||||
********
|
||||
|
||||
"Why not both?" None of these options is sufficient alone, so we are
|
||||
going to do a mixture.
|
||||
|
||||
We will use the NixOS module system, but instead of expecting modules
|
||||
to create systemd services as instances, they will expose "service
|
||||
templates": functions that accept an attrset and return an
|
||||
appropriately configured service that can be assigned by the caller
|
||||
to a key in ``config.services``.
|
||||
|
||||
We will typecheck the service template function parameters using the
|
||||
same type-checking code as NixOS uses for its modules.
|
||||
|
||||
An example may make this clearer: to add an NTP
|
||||
service you first add :file:`modules/ntp` to your ``imports`` list,
|
||||
then you create a service by calling
|
||||
:code:`config.system.service.ntp.build { .... }` with the appropriate
|
||||
service-dependent configuration parameters.
|
||||
|
||||
.. code-block:: nix
|
||||
|
||||
let svc = config.system.service;
|
||||
in {
|
||||
# ...
|
||||
imports = [
|
||||
./modules/ntp
|
||||
# ....
|
||||
];
|
||||
config.services.ntp = svc.ntp.build {
|
||||
pools = { "pool.ntp.org" = ["iburst"]; };
|
||||
makestep = { threshold = 1.0; limit = 3; };
|
||||
};
|
||||
|
||||
Merely including the module won't define the service on its own: it
|
||||
only creates the template in ``config.system.service.foo`` and you
|
||||
have to create the actual service using the template.
|
||||
|
||||
|
||||
|
||||
Consequences
|
||||
************
|
||||
|
||||
This decision has both good and bad consequences
|
||||
|
||||
Pro
|
||||
===
|
||||
|
||||
* We have a workable system for reusing configuration elements in
|
||||
Liminix.
|
||||
|
||||
* We have type checking for most imortant things, reducing the risk of
|
||||
deploying an invalid configuration.
|
||||
|
||||
* We have a simple mechanism for creating multiple services based on
|
||||
the same module, without buulding that logic into the module
|
||||
definition itself. For example, we could create two SSH daemons on
|
||||
different ports, or DHCP clients with different configurations on
|
||||
different network devices.
|
||||
|
||||
* We expect to be able to automate the generation of module
|
||||
documentation.
|
||||
|
||||
Con
|
||||
===
|
||||
|
||||
|
||||
* By departing somewhat from the NixOS conventions we increase the
|
||||
amount of code we have to write/maintain ourselves - and the
|
||||
learning burden on users who are already familiar with that system.
|
||||
|
||||
* Liminix configurations contain function calls and aren't just data,
|
||||
which means we can ony realistically interpret or introspect
|
||||
them with the Nix interpreter itself - we can't query them
|
||||
as data with other non-Nix tools.
|
||||
13
doc/conf.py
13
doc/conf.py
@@ -13,7 +13,10 @@ author = 'Daniel Barlow'
|
||||
# -- General configuration ---------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
||||
|
||||
extensions = []
|
||||
extensions = [
|
||||
'sphinx.ext.autosectionlabel'
|
||||
]
|
||||
autosectionlabel_prefix_document = True
|
||||
|
||||
templates_path = ['_templates']
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
@@ -25,3 +28,11 @@ exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
|
||||
html_theme = 'alabaster'
|
||||
html_static_path = ['_static']
|
||||
|
||||
html_theme_options = {
|
||||
'logo': '/logo.svg',
|
||||
'globaltoc_collapse': 'false',
|
||||
'page_width': '90%',
|
||||
'body_max_width': '90%',
|
||||
'description': 'A Nix-based OpenWrt-style embedded Linux system for consumer wifi routers'
|
||||
}
|
||||
|
||||
259
doc/configuration.rst
Normal file
259
doc/configuration.rst
Normal file
@@ -0,0 +1,259 @@
|
||||
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:
|
||||
|
||||
* which services (processes) to run
|
||||
* what packages to install
|
||||
* permitted users and groups
|
||||
* Linux kernel configuration options
|
||||
* Busybox applets
|
||||
* filesystem layout
|
||||
|
||||
|
||||
Modules
|
||||
*******
|
||||
|
||||
**Modules** are a means of abstraction which allow "bundling"
|
||||
of configuration options related to a common purpose or theme. For
|
||||
example, the ``dnsmasq`` module defines a template for a dnsmasq
|
||||
service, ensures that the dnsmasq package is installed, and provides a
|
||||
dnsmasq user and group for the service to run as. The ``ppp`` module
|
||||
defines a service template and also enables various PPP-related kernel
|
||||
configuration.
|
||||
|
||||
Not all modules are included in the configuration by default, because
|
||||
that would mean that the kernel (and the Busybox binary providing
|
||||
common CLI tools) was compiled with many unnecessary bells and whistles
|
||||
and therefore be bigger than needed. (This is not purely an academic concern
|
||||
if your device has little flash storage). Therefore, specifying a
|
||||
service is usually a two-step process. For example, to add an NTP
|
||||
service you first add :file:`modules/ntp` to your ``imports`` list,
|
||||
then you create a service by calling
|
||||
:code:`config.system.service.ntp.build { .... }` with the appropriate
|
||||
service-dependent configuration parameters.
|
||||
|
||||
.. code-block:: nix
|
||||
|
||||
let svc = config.system.service;
|
||||
in {
|
||||
# ...
|
||||
imports = [
|
||||
./modules/ntp
|
||||
# ....
|
||||
];
|
||||
config.services.ntp = svc.ntp.build {
|
||||
pools = { "pool.ntp.org" = ["iburst"]; };
|
||||
makestep = { threshold = 1.0; limit = 3; };
|
||||
};
|
||||
|
||||
Merely including the module won't define the service on its own: it
|
||||
only creates the template in ``config.system.service.foo`` and you
|
||||
have to create an actual service using the template. This is an
|
||||
intentional choice to allow the creation of multiple
|
||||
differently-configured services based on the same template - perhaps
|
||||
e.g. when you have multiple networks (VPNs etc) in different trust
|
||||
domains, or you want to run two SSH daemons on different ports.
|
||||
(For the background to this, please refer to the :doc:`architecture decision record <adr/module-system>`)
|
||||
|
||||
.. tip:: Liminix modules should be quite familiar (but also different)
|
||||
if you already know how to use NixOS modules. We use the
|
||||
NixOS module infrastructure code, meaning that you should
|
||||
recognise the syntax, the type system, the rules for
|
||||
combining configuration values from different sources. We
|
||||
don't use the NixOS modules themselves, because the
|
||||
underlying system is not similar enough for them to work.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
right parameters to ``build``.
|
||||
|
||||
Should you need to create a custom service of your own devising, use
|
||||
the `oneshot` or `longrun` functions:
|
||||
|
||||
* a "longrun" service is the "normal" service concept: it has a
|
||||
``run`` action which describes the process to start, and it watches
|
||||
that process to restart it if it exits. The process should not
|
||||
attempt to daemonize or "background" itself, otherwise s6-rc will think
|
||||
it died. Whatever it prints to standard output/standard error
|
||||
will be logged.
|
||||
|
||||
.. code-block:: nix
|
||||
|
||||
config.services.cowsayd = pkgs.liminix.services.longrun {
|
||||
name = "cowsayd";
|
||||
run = "${pkgs.cowsayd}/bin/cowsayd --port 3001 --breed hereford";
|
||||
# don't start this until the lan interface is ready
|
||||
dependencies = [ config.services.lan ];
|
||||
}
|
||||
|
||||
|
||||
* a "oneshot" service doesn't have a process attached. It consists of
|
||||
``up`` and ``down`` actions which are bits of shell script that
|
||||
are run at the appropriate points in the service lifecycle
|
||||
|
||||
.. code-block:: nix
|
||||
|
||||
config.services.greenled = pkgs.liminix.services.oneshot {
|
||||
name = "greenled";
|
||||
up = ''
|
||||
echo 17 > /sys/class/gpio/export
|
||||
echo out > /sys/class/gpio/gpio17/direction
|
||||
echo 0 > /sys/class/gpio/gpio17/value
|
||||
'';
|
||||
down = ''
|
||||
echo 0 > /sys/class/gpio/gpio17/value
|
||||
'';
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
..
|
||||
TODO: outputs that change, and services that poll other services
|
||||
|
||||
Module implementation
|
||||
*********************
|
||||
|
||||
Modules in Liminix conventionally live in
|
||||
:file:`modules/somename/default.nix`. If you want or need to
|
||||
write your own, you may wish to refer to the
|
||||
examples there in conjunction with reading this section.
|
||||
|
||||
A module is a function that accepts ``{lib, pkgs, config, ... }`` and
|
||||
returns an attrset with keys ``imports, options config``.
|
||||
|
||||
* ``imports`` is a list of paths to the other modules required by this one
|
||||
|
||||
* ``options`` is a nested set of option declarations
|
||||
|
||||
* ``config`` is a nested set of option definitions
|
||||
|
||||
The NixOS manual section `Writing NixOS Modules
|
||||
<https://nixos.org/manual/nixos/stable/#sec-writing-modules>`_ is a
|
||||
quite comprehensive reference to writing NixOS modules, which is also
|
||||
mostly applicable to Liminix except that it doesn't cover
|
||||
service templates.
|
||||
|
||||
Service templates
|
||||
=================
|
||||
|
||||
To expose a service template in a module, it needs the following:
|
||||
|
||||
* an option declaration for ``system.service.myservicename`` with the
|
||||
type of ``liminix.lib.types.serviceDefn``
|
||||
|
||||
.. code-block:: nix
|
||||
|
||||
options = {
|
||||
system.service.cowsay = mkOption {
|
||||
type = liminix.lib.types.serviceDefn;
|
||||
};
|
||||
};
|
||||
|
||||
* an option definition for the same key, which specifies where to
|
||||
import the service template from (often :file:`./service.nix`)
|
||||
and the types of its parameters.
|
||||
|
||||
.. code-block:: nix
|
||||
|
||||
config.system.service.cowsay = liminix.callService ./service.nix {
|
||||
address = mkOption {
|
||||
type = types.str;
|
||||
default = "0.0.0.0";
|
||||
description = "Listen on specified address";
|
||||
example = "127.0.0.1";
|
||||
};
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 22;
|
||||
description = "Listen on specified TCP port";
|
||||
};
|
||||
breed = mkOption {
|
||||
type = types.str;
|
||||
default = "British Friesian"
|
||||
description = "Breed of the cow";
|
||||
};
|
||||
};
|
||||
|
||||
Then you need to provide the service template itself, probably in
|
||||
:file:`./service.nix`:
|
||||
|
||||
.. code-block:: nix
|
||||
|
||||
{
|
||||
# any nixpkgs package can be named here
|
||||
liminix
|
||||
, cowsayd
|
||||
, serviceFns
|
||||
, lib
|
||||
}:
|
||||
# these are the parameters declared in the callService invocation
|
||||
{ address, port, breed} :
|
||||
let
|
||||
inherit (liminix.services) longrun;
|
||||
inherit (lib.strings) escapeShellArg;
|
||||
in longrun {
|
||||
name = "cowsayd";
|
||||
run = "${cowsayd}/bin/cowsayd --address ${address} --port ${builtins.toString port} --breed ${escapeShellArg breed}";
|
||||
}
|
||||
|
||||
.. tip::
|
||||
|
||||
Not relevant to module-based services specifically, but a common
|
||||
gotcha when specifiying services is forgetting to transform "rich"
|
||||
parameter values into text when composing a command for the shell
|
||||
to execute. Note here that the port number, an integer, is
|
||||
stringified with ``toString``, and the name of the breed,
|
||||
which may contain spaces, is
|
||||
escaped with ``escapeShellArg``
|
||||
|
||||
Types
|
||||
=====
|
||||
|
||||
All of the NixOS module types are available in Liminix. These
|
||||
Liminix-specific types also exist in ``pkgs.liminix.lib.types``:
|
||||
|
||||
* ``service``: an s6-rc service
|
||||
* ``interface``: an s6-rc service which specifies a network
|
||||
interface
|
||||
* ``serviceDefn``: a service "template" definition
|
||||
|
||||
In the future it is likely that we will extend this to include other
|
||||
useful types in the networking domain: for example; IP address,
|
||||
network prefix or netmask, protocol family and others as we find them.
|
||||
@@ -1,14 +1,14 @@
|
||||
Developer Manual
|
||||
################
|
||||
Development
|
||||
###########
|
||||
|
||||
As a developer working on Liminix, or implementing a service or
|
||||
module, you probably want to test your changes more conveniently
|
||||
than by building and flashing a new image every time. This manual
|
||||
than by building and flashing a new image every time. This section
|
||||
documents various affordances for iteration and experiments.
|
||||
|
||||
In general, packages and tools that run on the "build" machine are
|
||||
available in the ``buildEnv`` derivation and can most easily
|
||||
be added to your environment by running :command:`nix-shell`
|
||||
be added to your environment by running :command:`nix-shell`.
|
||||
|
||||
|
||||
|
||||
@@ -27,19 +27,18 @@ To build it,
|
||||
|
||||
nix-build -I liminix-config=path/to/your/configuration.nix --arg device "import ./devices/qemu" -A outputs.default
|
||||
|
||||
In a ``buildEnv`` nix-shell, you can use the :command:`mips-vm` command
|
||||
to run Qemu with appropriate options. It connects the Liminix
|
||||
This creates a :file:`result/` directory containing a :file:`vmlinux`
|
||||
and a :file:`rootfs`, and also a shell script :file:`run.sh` which
|
||||
invokes QEMU to run that kernel with that filesystem. It connects the Liminix
|
||||
serial console and the `QEMU monitor <https://www.qemu.org/docs/master/system/monitor.html>`_ to stdin/stdout. Use ^P (not ^A) to switch to the monitor.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
nix-shell --run "mips-vm result/vmlinux result/squashfs"
|
||||
|
||||
If you run with ``--background /path/to/some/directory`` as the first
|
||||
parameter, it will fork into the background and open Unix sockets in
|
||||
that directory for console and monitor. Use :command:`connect-vm`
|
||||
(also in the ``buildEnv`` environment) to connect to either of these
|
||||
sockets, and ^O to disconnect.
|
||||
that directory for console and monitor. Use :command:`nix-shell --run
|
||||
connect-vm` to connect to either of these sockets, and ^O to
|
||||
disconnect.
|
||||
|
||||
.. _qemu-networking:
|
||||
|
||||
Networking
|
||||
==========
|
||||
@@ -53,9 +52,11 @@ the right way:
|
||||
* multicast 230.0.0.1:1235 : lan
|
||||
* multicast 230.0.0.1:1236 : world (the internet)
|
||||
|
||||
A VM started with :command:`mips-vm` is connected to "lan" and "access", and
|
||||
the emulated border network gateway (see below) runs PPPoE and is
|
||||
connected to "access" and "world".
|
||||
Any VM started by a :command:`run.sh` script is connected to "lan" and
|
||||
"access", and the emulated border network gateway (see below) runs
|
||||
PPPoE and is connected to "access" and "world".
|
||||
|
||||
.. _border-network-gateway:
|
||||
|
||||
Border Network Gateway
|
||||
----------------------
|
||||
@@ -87,6 +88,70 @@ 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
|
||||
====
|
||||
|
||||
.. _tftp server:
|
||||
|
||||
How you get your image onto hardware will vary according to the
|
||||
device, but is likely to involve taking it apart to add wires to
|
||||
serial console pads/headers, then using U-Boot to fetch images over
|
||||
@@ -115,7 +180,7 @@ Now add the device and server IP addresses to your configuration:
|
||||
};
|
||||
|
||||
and then build the derivation for ``outputs.default`` or
|
||||
``outputs.flashimage`` (for which it will be an alias on any device
|
||||
``outputs.mtdimage`` (for which it will be an alias on any device
|
||||
where this is applicable). You should find it has created
|
||||
|
||||
* :file:`result/firmware.bin` which is the file you are going to flash
|
||||
@@ -148,6 +213,8 @@ U-Boot to transfer the kernel and filesystem over TFTP and boot the
|
||||
kernel from RAM.
|
||||
|
||||
|
||||
.. _bng:
|
||||
|
||||
Networking
|
||||
==========
|
||||
|
||||
51
doc/etc.rst
51
doc/etc.rst
@@ -1,51 +0,0 @@
|
||||
The Future
|
||||
##########
|
||||
|
||||
What about NixWRT?
|
||||
|
||||
This is an in-progress rewrite of NixWRT, incorporating Lessons
|
||||
Learned. That said, as of today it is not yet at feature parity.
|
||||
|
||||
Liminix will eventually provide these differentiators over NixWRT:
|
||||
|
||||
* a writable filesystem so that software updates or reconfiguration
|
||||
(e.g. changing passwords) don't require taking the device offline to
|
||||
reflash it.
|
||||
|
||||
* more flexible service management with dependencies, to allow
|
||||
configurations such as "route through PPPoE if it is healthy, with
|
||||
fallback to LTE"
|
||||
|
||||
* a spec for valid configuration options (a la NixOS module options)
|
||||
to that we can detect errors at evaluation time instead of producing
|
||||
a bad image.
|
||||
|
||||
* a network-based mechanism for secrets management so that changes can
|
||||
be pushed from a central location to several Liminix devices at once
|
||||
|
||||
* send device metrics and logs to a monitoring/alerting/o11y
|
||||
infrastructure
|
||||
|
||||
Today though, it does approximately none of these things and certainly
|
||||
not on real hardware.
|
||||
|
||||
|
||||
Articles of interest
|
||||
####################
|
||||
|
||||
* `Build Safety of Software in 28 Popular Home Routers <https://cyber-itl.org/assets/papers/2018/build_safety_of_software_in_28_popular_home_routers.pdf>`_: "of the access
|
||||
points and routers we reviewed, not a single one took full
|
||||
advantage of the basic application armoring features provided by
|
||||
the operating system. Indeed, only one or two models even came
|
||||
close, and no brand did well consistently across all models tested"
|
||||
|
||||
* `A PPPoE Implementation for Linux <https://static.usenix.org/publications/library/proceedings/als00/2000papers/papers/full_papers/skoll/skoll_html/index.html>`_:
|
||||
"Many DSL service providers use PPPoE for residential broadband
|
||||
Internet access. This paper briefly describes the PPPoE protocol,
|
||||
presents strategies for implementing it under Linux and describes in
|
||||
detail a user-space implementation of a PPPoE client."
|
||||
|
||||
* `PPP IPV6CP vs DHCPv6 at AAISP <https://www.revk.uk/2011/01/ppp-ipv6cp-vs-dhcpv6.html>`_
|
||||
|
||||
|
||||
* `Creating a Home IPv6 Network (James Bottomley) <https://blog.hansenpartnership.com/creating-a-home-ipv6-network/>`_
|
||||
@@ -1,30 +1,10 @@
|
||||
{ eval, lib, pkgs }:
|
||||
let
|
||||
overlay = import ../overlay.nix;
|
||||
pkgs = import <nixpkgs> ( {
|
||||
overlays = [overlay];
|
||||
config = {
|
||||
allowUnsupportedSystem = true; # mipsel
|
||||
permittedInsecurePackages = [
|
||||
"python-2.7.18.6" # kernel backports needs python <3
|
||||
];
|
||||
};
|
||||
});
|
||||
inherit (pkgs) lib;
|
||||
inherit (lib) types;
|
||||
modulenames =
|
||||
builtins.attrNames
|
||||
(lib.filterAttrsRecursive
|
||||
(n: t:
|
||||
(t=="directory") ||
|
||||
((t=="regular") && ((builtins.match ".*\\.nix$" n) != null)))
|
||||
(builtins.readDir ../modules));
|
||||
modulefiles = builtins.map (n: builtins.toPath "${../modules}/${n}") modulenames;
|
||||
eval = (lib.evalModules {
|
||||
modules = [
|
||||
{ _module.args = { inherit pkgs; lib = pkgs.lib; }; }
|
||||
] ++ modulefiles;
|
||||
});
|
||||
conf = eval.config;
|
||||
rootDir = builtins.toPath ./..;
|
||||
stripAnyPrefixes = lib.flip (lib.fold lib.removePrefix)
|
||||
["${rootDir}/"];
|
||||
optToDoc = name: opt : {
|
||||
inherit name;
|
||||
description = opt.description or null;
|
||||
@@ -41,16 +21,12 @@ let
|
||||
then
|
||||
let sd = lib.attrByPath item.loc ["not found"] conf;
|
||||
in item // {
|
||||
declarations = map stripAnyPrefixes item.declarations;
|
||||
parameters =
|
||||
let x = lib.mapAttrsToList optToDoc sd.parameters; in x;
|
||||
}
|
||||
else
|
||||
item;
|
||||
o = builtins.map spliceServiceDefn
|
||||
(pkgs.lib.optionAttrSetToDocList eval.options);
|
||||
in {
|
||||
doc = pkgs.writeText "options.yaml" ''
|
||||
# ${./..}
|
||||
${builtins.toJSON o}
|
||||
'';
|
||||
}
|
||||
item // { declarations = map stripAnyPrefixes item.declarations; };
|
||||
in
|
||||
builtins.map spliceServiceDefn
|
||||
(pkgs.lib.optionAttrSetToDocList eval.options)
|
||||
|
||||
38
doc/hardware.nix
Normal file
38
doc/hardware.nix
Normal file
@@ -0,0 +1,38 @@
|
||||
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;
|
||||
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}
|
||||
|
||||
''
|
||||
@@ -6,9 +6,13 @@ Liminix
|
||||
:caption: Contents:
|
||||
|
||||
intro
|
||||
user
|
||||
developer
|
||||
etc
|
||||
tutorial
|
||||
configuration
|
||||
admin
|
||||
development
|
||||
modules
|
||||
hardware
|
||||
outputs
|
||||
|
||||
|
||||
Indices and tables
|
||||
|
||||
4
doc/modules.rst
Normal file
4
doc/modules.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
Module options
|
||||
##############
|
||||
|
||||
.. include:: modules-generated.rst
|
||||
13
doc/outputs.rst
Normal file
13
doc/outputs.rst
Normal file
@@ -0,0 +1,13 @@
|
||||
Outputs
|
||||
#######
|
||||
|
||||
Liminix *outputs* are artefacts that can be installed somehow on a
|
||||
target device, or "installers" which run on the target device to
|
||||
perform the installation.
|
||||
|
||||
There are different outputs because different target devices need
|
||||
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
|
||||
19
doc/parse-options-outputs.fnl
Normal file
19
doc/parse-options-outputs.fnl
Normal file
@@ -0,0 +1,19 @@
|
||||
(local yaml (require :lyaml))
|
||||
|
||||
;; (local { : view } (require :fennel))
|
||||
|
||||
(fn output? [option]
|
||||
(match option.loc
|
||||
["system" "outputs" & _] true
|
||||
_ false))
|
||||
|
||||
(fn sorted-options [options]
|
||||
(table.sort
|
||||
options
|
||||
(fn [a b] (< a.name b.name)))
|
||||
options)
|
||||
|
||||
(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)))
|
||||
@@ -2,21 +2,25 @@
|
||||
|
||||
(local { : view } (require :fennel))
|
||||
|
||||
(fn basename [str ext]
|
||||
(-> str
|
||||
(string.gsub "(.*/)(.*)" "%2")
|
||||
(string.gsub (.. ext "$") "")))
|
||||
|
||||
(fn headline [name]
|
||||
(let [(_ _ basename) (string.find name ".*/([^/].*).nix")
|
||||
len (basename:len)]
|
||||
(.. basename "\n" (string.rep "=" len))))
|
||||
(let [title (assert (basename name ".nix"))
|
||||
len (title:len)]
|
||||
(.. title "\n" (string.rep "=" len))))
|
||||
|
||||
(fn read-preamble [pathname]
|
||||
(if (= (pathname:sub 1 1) "/")
|
||||
(let [pathname (if (string.match pathname ".nix$")
|
||||
pathname
|
||||
(.. pathname "/default.nix"))]
|
||||
(with-open [f (assert (io.open pathname :r))]
|
||||
(accumulate [lines nil
|
||||
l (f:lines)
|
||||
:until (not (= (string.sub l 1 2) "##"))]
|
||||
(.. (or lines "") (string.gsub l "^## *" "") "\n"))))))
|
||||
(let [pathname (if (string.match pathname ".nix$")
|
||||
pathname
|
||||
(.. pathname "/default.nix"))]
|
||||
(with-open [f (assert (io.open pathname :r))]
|
||||
(accumulate [lines nil
|
||||
l (f:lines)
|
||||
:until (not (= (string.sub l 1 2) "##"))]
|
||||
(.. (or lines "") (string.gsub l "^## *" "") "\n")))))
|
||||
|
||||
(fn relative-pathname [pathname]
|
||||
(let [pathname
|
||||
|
||||
324
doc/tutorial.rst
Normal file
324
doc/tutorial.rst
Normal file
@@ -0,0 +1,324 @@
|
||||
Tutorial
|
||||
########
|
||||
|
||||
Liminix is very configurable, which can make it initially quite
|
||||
daunting - especially if you're learning Nix or Linux or networking
|
||||
concepts at the same time. In this section we build some "worked
|
||||
example" Liminix images to introduce the concepts. If you follow the
|
||||
examples exactly, they should work. If you change things as you go
|
||||
along, they may work differently or not at all, but the experience
|
||||
should be educational either way.
|
||||
|
||||
|
||||
Requirements
|
||||
************
|
||||
|
||||
You will need a reasonably powerful computer running Nix. Target
|
||||
devices for Liminix are unlikely to have the CPU power and disk space
|
||||
to be able to build it in situ, so the build process is based around
|
||||
"cross-compilation" from another computer. The build machine can be
|
||||
any reasonably powerful desktop/laptop/server PC running NixOS.
|
||||
Standalone Nixpkgs installations on other Linux distributions - or on
|
||||
MacOS, or even in a Docker container - also ought to work but are
|
||||
untested.
|
||||
|
||||
|
||||
Running in Qemu
|
||||
***************
|
||||
|
||||
You can try out Liminix without even having a router to play with.
|
||||
Clone the Liminix git repository and change into its directory
|
||||
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
git clone https://gti.telent.net/dan/liminix
|
||||
cd liminix
|
||||
|
||||
Now build Liminix
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
nix-build -I liminix-config=./examples/hello-from-qemu.nix \
|
||||
--arg device "import ./devices/qemu" -A outputs.default
|
||||
|
||||
In this command ``liminix-config`` points to the desired software
|
||||
configuration (e.g. services, users, filesystem, secrets) and
|
||||
``device`` describes the hardware (or emulated hardware) to run it on.
|
||||
``outputs.default`` tells Liminix that we want the default image
|
||||
output for flashing to the device: for the Qemu "hardware" it's an
|
||||
alias for ``outputs.vmbuild``, which creates a directory containing a
|
||||
root filesystem image and a kernel.
|
||||
|
||||
.. tip:: The first time you run this it may take several hours,
|
||||
because it builds all of the dependencies including a full
|
||||
MIPS gcc and library toolchain. Once those intermediate build
|
||||
products are in the nix store, subsequent builds will be much
|
||||
faster - practically instant, if nothing has changed.
|
||||
|
||||
Now you can try it:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
./result/run.sh
|
||||
|
||||
This starts the Qemu emulator with a bunch of useful options, to run
|
||||
the Liminix configuration you just built. It connects the emulated
|
||||
device's serial console and the `QEMU monitor
|
||||
<https://www.qemu.org/docs/master/system/monitor.html>`_ to
|
||||
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
|
||||
(Control P) then c to enter the "QEMU Monitor", then type ``quit`` at
|
||||
the ``(qemu)`` prompt.
|
||||
|
||||
To see that it's running network services we need to connect to its
|
||||
emulated network. Start the machine again, if you had stopped it, and
|
||||
open up a second terminal on your build machine. We're going to run
|
||||
another virtual machine attached to the virtual network, which will
|
||||
request an IP address from our Liminix system and give you a shell you
|
||||
can run ssh from.
|
||||
|
||||
We use `System Rescue <https://www.system-rescue.org/>`_ in tty
|
||||
mode (no graphical output) for this example, but if you have some
|
||||
other favourite Linux Live CD ISO - or, for that matter, any other OS
|
||||
image that QEMU can boot - adjust the command to suit.
|
||||
|
||||
Download the System Rescue ISO:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
curl https://fastly-cdn.system-rescue.org/releases/10.01/systemrescue-10.01-amd64.iso -O
|
||||
|
||||
and run it
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
nix-shell -p qemu --run " \
|
||||
qemu-system-x86_64 \
|
||||
-echr 16 \
|
||||
-m 1024 \
|
||||
-cdrom systemrescue-10.01-amd64.iso \
|
||||
-netdev socket,mcast=230.0.0.1:1235,localaddr=127.0.0.1,id=lan \
|
||||
-device virtio-net,disable-legacy=on,disable-modern=off,netdev=lan,mac=ba:ad:3d:ea:21:01 \
|
||||
-display none -serial mon:stdio"
|
||||
|
||||
System Rescue displays a boot menu at which you should select the
|
||||
"serial console" option, then after a few moments it boots to a root
|
||||
prompt. You can now try things out:
|
||||
|
||||
* run :command:`ip a` and see that it's been allocated an IP address in the range 10.3.0.0/16.
|
||||
|
||||
* run :command:`ping 10.3.0.1` to see that the Liminix VM responds
|
||||
|
||||
* run :command:`ssh root@10.3.0.1` to try logging into it.
|
||||
|
||||
Congratulations! You have installed your first Liminix system - albeit
|
||||
it has no practical use and it's not even real. The next step is to try
|
||||
running it on hardware.
|
||||
|
||||
Installing on hardware
|
||||
**********************
|
||||
|
||||
For the next example, we're going to install onto an actual hardware
|
||||
device. These steps have been tested using a GL.iNet GL-MT300A, which
|
||||
has been chosen for the purpose because it's cheap and easy to
|
||||
unbrick if necessary.
|
||||
|
||||
.. warning:: There is always a risk of rendering your device
|
||||
unbootable by flashing it with an image that doesn't
|
||||
work. The GL-MT300A 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. Using some other
|
||||
Liminix-supported MIPS hardware device also *ought* to
|
||||
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
|
||||
allows you
|
||||
to see the boot monitor and kernel messages, and to login directly to
|
||||
the device if for some reason it doesn't bring its network up.
|
||||
|
||||
Now we can build Liminix. Although we could use the same example
|
||||
configuration as we did for Qemu, you might not want to plug a DHCP
|
||||
server into your working LAN because it will compete with the real
|
||||
DHCP service. So we're going to use a different configuration with a
|
||||
DHCP client: this is :file:`examples/hello-from-mt300.nix`
|
||||
|
||||
It's instructive to compare the two configurations:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
diff -u examples/hello-from-qemu.nix examples/hello-from-mt300.nix
|
||||
|
||||
You'll see a new ``boot.tftp`` stanza which you can ignore,
|
||||
``services.dns`` has been removed, and the static IP address allocation
|
||||
has been replaced by a ``dhcp.client`` service.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
nix-build -I liminix-config=./examples/hello-from-mt300.nix \
|
||||
--arg device "import ./devices/gl-mt300a" -A outputs.default
|
||||
|
||||
.. tip:: The first time you run this it may take several hours.
|
||||
Again? Yes, even if you ran the previous example. Qemu is
|
||||
set up as a big-endian system whereas the MediaTek SoC
|
||||
on this device is little-endian - so it requires building
|
||||
all of the dependencies including an entirely different
|
||||
MIPS gcc and library toolchain to the other one.
|
||||
|
||||
This time in :file:`result/` you will see a bunch of files. Most of
|
||||
them you can ignore for the moment, but :file:`result/firmware.bin` is
|
||||
the firmware image you can flash.
|
||||
|
||||
|
||||
Flashing
|
||||
========
|
||||
|
||||
Again, there are a number of different ways you could do this: using
|
||||
TFTP with a serial cable, through the stock firmware's web UI, or
|
||||
using the `vendor's "debrick" process
|
||||
<https://docs.gl-inet.com/router/en/3/tutorials/debrick/>`_. The last
|
||||
of these options has a lot to recommend it for a first attempt:
|
||||
|
||||
* it works no matter what firmware is currently installed
|
||||
|
||||
* it doesn't require plugging a router into the same network as your
|
||||
build system and potentially messing up your actual upstream
|
||||
|
||||
* no need to open the device and add cables
|
||||
|
||||
You can read detailed instructions on the vendor site, but the short version is:
|
||||
|
||||
1. turn the device off
|
||||
2. connect it by ethernet cable to a computer
|
||||
3. configure the computer to have static ip address 192.168.1.10
|
||||
4. while holding down the Reset button, turn the device on
|
||||
5. after about five seconds you can release the Reset button
|
||||
6. visit http://192.168.1.1/ using a web browser on the connected computer
|
||||
7. click on "Browse" and choose :file:`result/firmware.bin`
|
||||
8. click on "Update firmware"
|
||||
9. wait a minute or so while it updates.
|
||||
|
||||
There's no feedback from the web interface when the flashing is
|
||||
finished, but what should happen is that the router reboots and
|
||||
starts running Liminix. Now you need to figure out what address it got
|
||||
from DHCP - e.g. by checking the DHCP server logs, or maybe by pinging
|
||||
``hello.lan`` or something. Once you've found it on the
|
||||
network you can ping it and ssh to it just like you did the Qemu
|
||||
example, but this time for real.
|
||||
|
||||
.. warning:: Do not leave the default root password in place on any
|
||||
device exposed to the internet! Although it has no
|
||||
writable storage and no default route, a motivated attacker
|
||||
with some imagination could probably still do something
|
||||
awful using it.
|
||||
|
||||
Congratulations Part II! You have installed your first Liminix system on actual hardware - albeit that it *still* has no practical use.
|
||||
|
||||
Exercise for the reader: change the default password by editing
|
||||
:file:`examples/hello-from-mt300.nix`, and then create and upload a
|
||||
new image that has it set to something less hopeless.
|
||||
|
||||
Routing
|
||||
*******
|
||||
|
||||
The third example :file:`examples/demo.nix` is a fully-functional home
|
||||
"WiFi router" - although you will have to edit it a bit before it will
|
||||
actually work for you. Copy :file:`examples/demo.nix` to
|
||||
:file:`my-router.nix` (or other name of your choice) and open it in
|
||||
your favourite text editor. Everywhere that the text :code:`EDIT`
|
||||
appears is either a place you probably want to change or a place you
|
||||
almost certainly need to change.
|
||||
|
||||
There's a lot going on in this configuration:
|
||||
|
||||
* it provides a wireless access point using the :code:`hostapd`
|
||||
service: in this stanza you can change the ssid, the channel,
|
||||
the passphrase etc.
|
||||
|
||||
* the wireless lan and wired lan are bridged together with the
|
||||
:code:`bridge` service, so that your wired and wireless clients appear
|
||||
to be on the same network.
|
||||
|
||||
.. tip:: If you were using a hardware device that provides both 2.4GHz
|
||||
and 5GHz wifi, you'd probably find that it has two wireless
|
||||
devices (often called wlan0 and wlan1). In Liminix we handle
|
||||
this by running two :code:`hostapd` services, and adding
|
||||
both of them to the network bridge along with the wired lan.
|
||||
(You can see an example in :file:`examples/rotuer.nix`)
|
||||
|
||||
* we use the combination DNS and DHCP daemon provided by the
|
||||
:code:`dnsmasq` service, which you can configure
|
||||
|
||||
* the upstream network is "PPP over Ethernet", provided by the
|
||||
:code:`pppoe` service. Assuming that your ISP uses this standard,
|
||||
they will have provided you with a PPP username and password
|
||||
(sometimes this will be listed as "PAP" or "CHAP") which you can edit
|
||||
into the configuration
|
||||
|
||||
* this example supports the new [#ipv6]_ Internet Protocol v6
|
||||
as well as traditional IPv4. Configuring IPv6 seems to
|
||||
vary from one ISP to the next: this example expects them
|
||||
to be providing IP address allocation and "prefix delegation"
|
||||
using DHCP6.
|
||||
|
||||
Build it using the same method as the previous example
|
||||
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
nix-build -I liminix-config=./my-router.nix \
|
||||
--arg device "import ./devices/gl-mt300a" -A outputs.default
|
||||
|
||||
and then you can flash it to the device.
|
||||
|
||||
|
||||
Bonus: in-place updates
|
||||
=======================
|
||||
|
||||
This configuration uses a writable filesystem (see the line
|
||||
:code:`rootfsType = "jffs2"`), which means that once you've flashed it
|
||||
for the first time, you can make further updates over SSH onto the
|
||||
running router. To try this, make a small change (I'd suggest changing
|
||||
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""
|
||||
|
||||
(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`.
|
||||
|
||||
|
||||
Final thoughts
|
||||
**************
|
||||
|
||||
* These are demonstration configs for pedagogical purposes. If you'd
|
||||
like to see some more realistic uses of Liminix,
|
||||
:file:`examples/rotuer,arhcive,extneder.nix` are based on some
|
||||
actual real hosts in my home network.
|
||||
|
||||
* The technique used here for flashing was chosen mostly because it
|
||||
doesn't need much infrastructure/tooling, but it is a bit of a faff
|
||||
(requires physical access, vendor specific). There are slicker ways
|
||||
to do it that need a bit more setup - we'll talk about that later as
|
||||
well.
|
||||
|
||||
|
||||
|
||||
.. rubric:: Footnotes
|
||||
|
||||
.. [#ipv6] `RFC1883 Internet Protocol, Version 6 <https://datatracker.ietf.org/doc/html/rfc1883>`_ was published in 1995, so only "new" when Bill Clinton was US President
|
||||
347
doc/user.rst
347
doc/user.rst
@@ -1,347 +0,0 @@
|
||||
User Manual
|
||||
###########
|
||||
|
||||
This manual is an early work in progress, not least because Liminix is
|
||||
not yet really ready for users who are not also developers. Your
|
||||
feedback to improve it is very welcome.
|
||||
|
||||
Installation
|
||||
************
|
||||
|
||||
The Liminix installation process is not quite like installing NixOS on
|
||||
a real computer, but some NixOS experience will nevertheless be
|
||||
helpful in understanding it. The steps are as follows:
|
||||
|
||||
* Decide whether you want the device to be updatable in-place (there
|
||||
are advantages and disadvantages), or if you are happy to generate
|
||||
and flash a new image whenever changes are required.
|
||||
|
||||
* Create a :file:`configuration.nix` describing the system you want
|
||||
|
||||
* Build an image
|
||||
|
||||
* Flash it to the device
|
||||
|
||||
Supported devices
|
||||
=================
|
||||
|
||||
For a list of devices that Liminix (present or previous versions)
|
||||
has run on, refer to `devices/ in the source repo <https://gti.telent.net/dan/liminix/src/branch/main/devices>`_. For devices that _currently_ build,
|
||||
cross-reference it with `the CI status <https://build.liminix.org/jobset/liminix/build#tabs-jobs>`_. Everything that builds is (usually) expected
|
||||
to run, so if you end up with an image that builds but doesn't
|
||||
boot, please report it as a bug.
|
||||
|
||||
As of June 2023 the device list is a little thin. Adding devices based
|
||||
on the Atheros or Mediatek (Ralink) platform should be quite
|
||||
straightforward if you have some C/Linux kernel experience and are
|
||||
prepared to open it up and attach serial wires: please refer to the
|
||||
Developer Manual.
|
||||
|
||||
|
||||
Choosing a flavour (read-only or updatable)
|
||||
===========================================
|
||||
|
||||
Liminix installations come in two "flavours"- read-only or in-place
|
||||
updatable:
|
||||
|
||||
* a read-only installation can't be updated once it is flashed to your
|
||||
device, and so must be reinstalled in its entirety every time you
|
||||
want to change it. It uses the ``squashfs`` filesystem which has
|
||||
very good compression ratios and so you can pack quite a lot of
|
||||
useful stuff onto your device. This is good if you don't expect
|
||||
to change it often.
|
||||
|
||||
* an updatable installation has a writable filesystem so that you can
|
||||
update configuration, upgrade packages and install new packages over
|
||||
the network after installation. This uses the `jffs2
|
||||
<http://www.linux-mtd.infradead.org/doc/jffs2.html>`_ filesystem:
|
||||
although it does compress the data, the need to support writes means
|
||||
that it can't pack quite as small as squashfs, so you will not have
|
||||
as much space to play with.
|
||||
|
||||
Updatability caveats
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
At the time of writing this manual the read-only squashfs support is
|
||||
much more mature. Consider also that it may not be possible to perform
|
||||
"larger" updates in-place even if you do opt for updatability. If you
|
||||
have (for example) an 11MB system on a 16MB device, you won't be able
|
||||
to do an in-place update of something fundamental like the C library
|
||||
(libc), as this will temporarily require 22MB to install all the
|
||||
packages needing the new library before the packages using the old
|
||||
library can be removed. A writable system will be more useful for
|
||||
smaller updates such as installing a new package (perhaps you
|
||||
temporarily need tcpdump to diagnose a network problem) or for
|
||||
changing configuration files.
|
||||
|
||||
Note also that the kernel is not part of the filesystem so cannot be
|
||||
updated this way. Kernel changes require a full reflash.
|
||||
|
||||
|
||||
|
||||
Creating configuration.nix
|
||||
==========================
|
||||
|
||||
|
||||
You need to create a :file:`configuration.nix` that describes your
|
||||
device and the services that you want to run on it. The best way to
|
||||
get started is by reading one of the examples such as
|
||||
:file:`examples/rotuer.nix` and modifying it to your needs.
|
||||
|
||||
:file:`configuration.nix` conventionally describes the packages, services,
|
||||
user accounts etc of the device. It does not describe the hardware
|
||||
itself, which is specified separately in the build command (as you
|
||||
will see below).
|
||||
|
||||
Most of the functionality of a Liminix system is driven by *services*
|
||||
which are declared by *modules*: thus, to add for example an NTP service
|
||||
you first add :file:`modules/ntp` to your ``imports`` list, then
|
||||
you create a service by calling :code:`config.system.service.ntp.build { .... }`
|
||||
with the appropriate service-dependent configuration parameters.
|
||||
|
||||
.. code-block:: nix
|
||||
|
||||
let svc = config.system.service;
|
||||
in {
|
||||
# ...
|
||||
imports = [
|
||||
./modules/ntp
|
||||
# ....
|
||||
];
|
||||
config.services.ntp = svc.ntp.build {
|
||||
pools = { "pool.ntp.org" = ["iburst"]; };
|
||||
makestep = { threshold = 1.0; limit = 3; };
|
||||
};
|
||||
|
||||
A :ref:`full list of module options <module-options>` is provided
|
||||
later in this manual.
|
||||
|
||||
You *most likely* want to include the ``standard`` module unless you
|
||||
have a quite unusual use case for a very minimal system, in which case
|
||||
you will understand what it does and what happens if you leave it out.
|
||||
|
||||
.. code-block:: nix
|
||||
|
||||
imports = [
|
||||
./modules/standard.nix
|
||||
]
|
||||
configuration.rootfsType = "jffs2"; # or "squashfs"
|
||||
|
||||
|
||||
|
||||
Building
|
||||
========
|
||||
|
||||
Build Liminix using the :file:`default.nix` in the project toplevel
|
||||
directory, passing it arguments for configuration and hardware. For
|
||||
example:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
nix-build -I liminix-config=./tests/smoke/configuration.nix \
|
||||
--arg device "import ./devices/qemu" -A outputs.default
|
||||
|
||||
In this command ``<liminix-config>`` points to your
|
||||
:file:`configuration.nix`, ``device`` is the file for your hardware device
|
||||
definition, and ``outputs.default`` will generate some kind of
|
||||
Liminix image output appropriate to that device.
|
||||
|
||||
For the qemu device in this example, ``outputs.default`` is an alias
|
||||
for ``outputs.vmbuild``, which creates a directory containing a
|
||||
squashfs root image and a kernel. You can use the :command:`mips-vm` command to
|
||||
run this.
|
||||
|
||||
For the currently supported hardware devices, ``outputs.default``
|
||||
creates a directory containing a file called ``firmware.bin``. This
|
||||
is a raw image file that can be written directly to the firmware flash
|
||||
partition.
|
||||
|
||||
|
||||
Flashing
|
||||
========
|
||||
|
||||
|
||||
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 flash it using U-Boot.
|
||||
This is quite hardware-specific, and sometimes involves soldering:
|
||||
please refer to the Developer Manual.
|
||||
|
||||
|
||||
Flashing from an existing Liminix system with :command:`flashcp`
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The flash procedure from an existing Liminix-system is two-step.
|
||||
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.
|
||||
|
||||
|
||||
|
||||
Building the RAM-based image
|
||||
............................
|
||||
|
||||
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.
|
||||
|
||||
For example
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
nix-build --show-trace -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 -)
|
||||
|
||||
and then login to the device and run
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
cd /run/result
|
||||
sh ./boot.sh .
|
||||
|
||||
|
||||
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.
|
||||
|
||||
|
||||
Building the second (permanent) image
|
||||
.....................................
|
||||
|
||||
While running in the kexecboot system, you can copy the permanent
|
||||
image to the device with :command:`ssh`
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
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 intemediate 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. It might work, or it might
|
||||
crash in surprising ways.
|
||||
|
||||
|
||||
|
||||
Flashing from OpenWrt (not currently advised!)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. CAUTION:: At your own risk! This will (at least in some
|
||||
circumstances) lead to bricking the device: we think this
|
||||
flash method is currently incompatible with use of a
|
||||
writeable (jffs2) filesystem.
|
||||
|
||||
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 -r write /tmp/firmware.bin firmware
|
||||
|
||||
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:
|
||||
|
||||
.. 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 to the device: it doesn't update
|
||||
any profile to add it to ``$PATH``
|
||||
|
||||
|
||||
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
|
||||
|
||||
It doesn't delete old packages automatically: to do that run
|
||||
:command:`min-collect-garbage`, which will delete any packages not in
|
||||
the current system closure. Note that Liminix does not have the NixOS
|
||||
concept of environments or generations, and there is no way back from
|
||||
this except for building the previous configuration again.
|
||||
|
||||
|
||||
|
||||
Caveats
|
||||
~~~~~~~
|
||||
|
||||
* it needs there to be enough free space on the device for all the new
|
||||
packages in addition to all the packages already on it - which may be
|
||||
a problem if a lot of things have changed (e.g. a new version of
|
||||
nixpkgs).
|
||||
|
||||
* it cannot upgrade the kernel, only userland
|
||||
|
||||
Configuration options
|
||||
*********************
|
||||
|
||||
.. _module-options:
|
||||
|
||||
.. include:: modules.rst
|
||||
@@ -1,59 +0,0 @@
|
||||
(local { : merge : split : file-exists? : system } (require :anoia))
|
||||
(local svc (require :anoia.svc))
|
||||
|
||||
(fn parse-prefix [str]
|
||||
(fn parse-extra [s]
|
||||
(let [out {}]
|
||||
(each [name val (string.gmatch s ",(.-)=([^,]+)")]
|
||||
(tset out name val))
|
||||
out))
|
||||
(let [(prefix len preferred valid extra)
|
||||
(string.match str "(.-)::/(%d+),(%d+),(%d+)(.*)$")]
|
||||
(merge {: prefix : len : preferred : valid} (parse-extra extra))))
|
||||
|
||||
|
||||
;; Format: <prefix>/<length>,preferred,valid[,excluded=<excluded-prefix>/<length>][,class=<prefix class #>]
|
||||
|
||||
;;(parse-prefix "2001:8b0:de3a:40dc::/64,7198,7198")
|
||||
;;(parse-prefix "2001:8b0:de3a:1001::/64,7198,7188,excluded=1/2,thi=10")
|
||||
|
||||
|
||||
(local bound-states
|
||||
{ :bound true
|
||||
:rebound true
|
||||
:informed true
|
||||
:updated true
|
||||
:ra-updated true
|
||||
})
|
||||
|
||||
; (local { : view } (require :fennel))
|
||||
|
||||
(fn changes [old-prefixes new-prefixes]
|
||||
(let [added {}
|
||||
deleted {}
|
||||
old-set (collect [_ v (ipairs old-prefixes)] (values v true))
|
||||
new-set (collect [_ v (ipairs new-prefixes)] (values v true))]
|
||||
(each [_ prefix (ipairs new-prefixes)]
|
||||
(if (not (. old-set prefix))
|
||||
(table.insert added (parse-prefix prefix))))
|
||||
(each [_ prefix (ipairs old-prefixes)]
|
||||
(if (not (. new-set prefix))
|
||||
(table.insert deleted (parse-prefix prefix))))
|
||||
(values added deleted)))
|
||||
|
||||
(let [[state-directory lan-device] arg
|
||||
dir (svc.open state-directory)]
|
||||
(var prefixes [])
|
||||
(while true
|
||||
(while (not (dir:ready?)) (dir:wait))
|
||||
(if (. bound-states (dir:output "state"))
|
||||
(let [new-prefixes (split " " (dir:output "/prefixes"))
|
||||
(added deleted) (changes prefixes new-prefixes)]
|
||||
(each [_ p (ipairs added)]
|
||||
(system
|
||||
(.. "ip address add " p.prefix "::1/" p.len " dev " lan-device)))
|
||||
(each [_ p (ipairs deleted)]
|
||||
(system
|
||||
(.. "ip address del " p.prefix "::1/" p.len " dev " lan-device)))
|
||||
(set prefixes new-prefixes)))
|
||||
(dir:wait)))
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
writeFennelScript
|
||||
, linotify
|
||||
, anoia
|
||||
}:
|
||||
writeFennelScript "acquire-delegated-prefix"
|
||||
[ linotify anoia ]
|
||||
./acquire-delegated-prefix.fnl
|
||||
@@ -1,60 +0,0 @@
|
||||
(local { : merge : split : file-exists? : system } (require :anoia))
|
||||
(local svc (require :anoia.svc))
|
||||
|
||||
;; structurally this is remarkably similar to
|
||||
;; acquire-lan-prefix.fnl. maybe they should be merged: if not then
|
||||
;; we could at least extract some common code
|
||||
|
||||
;; (alternatively we could move all the parsing code into the thing in
|
||||
;; the odhcp service that *writes* this stuff)
|
||||
|
||||
; (parse-address "2001:8b0:1111:1111:0:ffff:51bb:4cf2/128,3600,7200")
|
||||
|
||||
|
||||
(fn parse-address [str]
|
||||
(fn parse-extra [s]
|
||||
(let [out {}]
|
||||
(each [name val (string.gmatch s ",(.-)=([^,]+)")]
|
||||
(tset out name val))
|
||||
out))
|
||||
(let [(address len preferred valid extra)
|
||||
(string.match str "(.-)/(%d+),(%d+),(%d+)(.*)$")]
|
||||
(merge {: address : len : preferred : valid} (parse-extra extra))))
|
||||
|
||||
(local bound-states
|
||||
{ :bound true
|
||||
:rebound true
|
||||
:informed true
|
||||
:updated true
|
||||
:ra-updated true
|
||||
})
|
||||
|
||||
(fn changes [old-addresses new-addresses]
|
||||
(let [added {}
|
||||
deleted {}
|
||||
old-set (collect [_ v (ipairs old-addresses)] (values v true))
|
||||
new-set (collect [_ v (ipairs new-addresses)] (values v true))]
|
||||
(each [_ address (ipairs new-addresses)]
|
||||
(if (not (. old-set address))
|
||||
(table.insert added (parse-address address))))
|
||||
(each [_ address (ipairs old-addresses)]
|
||||
(if (not (. new-set address))
|
||||
(table.insert deleted (parse-address address))))
|
||||
(values added deleted)))
|
||||
|
||||
(let [[state-directory wan-device] arg
|
||||
dir (svc.open state-directory)]
|
||||
(var addresses [])
|
||||
(while true
|
||||
(while (not (dir:ready?)) (dir:wait))
|
||||
(if (. bound-states (dir:output "state"))
|
||||
(let [new-addresses (split " " (dir:output "/addresses"))
|
||||
(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)))
|
||||
(set addresses new-addresses)))
|
||||
(dir:wait)))
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
writeFennelScript
|
||||
, linotify
|
||||
, anoia
|
||||
}:
|
||||
writeFennelScript "acquire-wan-address"
|
||||
[ linotify anoia ]
|
||||
./acquire-wan-address.fnl
|
||||
@@ -11,16 +11,10 @@
|
||||
...
|
||||
}: let
|
||||
secrets = import ./extneder-secrets.nix;
|
||||
inherit
|
||||
(pkgs.liminix.networking)
|
||||
address
|
||||
udhcpc
|
||||
interface
|
||||
route
|
||||
;
|
||||
inherit (pkgs.liminix.services) oneshot longrun bundle target;
|
||||
inherit (pkgs.pseudofile) dir symlink;
|
||||
inherit (pkgs) writeText dropbear ifwait serviceFns;
|
||||
svc = config.system.service;
|
||||
in rec {
|
||||
boot = {
|
||||
tftp = {
|
||||
@@ -30,10 +24,13 @@ in rec {
|
||||
};
|
||||
|
||||
imports = [
|
||||
../modules/standard.nix
|
||||
../modules/wlan.nix
|
||||
../modules/network
|
||||
../modules/vlan
|
||||
../modules/ssh
|
||||
../modules/watchdog
|
||||
../modules/mount
|
||||
];
|
||||
|
||||
hostname = "arhcive";
|
||||
|
||||
kernel = {
|
||||
@@ -52,7 +49,6 @@ in rec {
|
||||
SCSI = "y";
|
||||
BLK_DEV_SD = "y";
|
||||
USB_PRINTER = "y";
|
||||
PARTITION_ADVANCED = "y";
|
||||
MSDOS_PARTITION = "y";
|
||||
EFI_PARTITION = "y";
|
||||
EXT4_FS = "y";
|
||||
@@ -62,55 +58,17 @@ in rec {
|
||||
};
|
||||
|
||||
services.dhcpc =
|
||||
let iface = config.hardware.networkInterfaces.lan;
|
||||
in (udhcpc iface {
|
||||
let iface = config.hardware.networkInterfaces.lan;
|
||||
in svc.network.dhcp.client.build {
|
||||
interface = iface;
|
||||
dependencies = [ config.services.hostname ];
|
||||
}) // { inherit (iface) device; };
|
||||
|
||||
services.sshd = longrun {
|
||||
name = "sshd";
|
||||
run = ''
|
||||
mkdir -p /run/dropbear
|
||||
${dropbear}/bin/dropbear -E -P /run/dropbear.pid -R -F
|
||||
'';
|
||||
};
|
||||
|
||||
services.watchdog =
|
||||
let
|
||||
watched = with config.services ; [ sshd dhcpc ];
|
||||
spinupGrace = 60;
|
||||
script = pkgs.writeAshScript "gaspode" {
|
||||
runtimeInputs = [ pkgs.s6 ];
|
||||
} ''
|
||||
deadline=$(expr $(date +%s) + ${toString spinupGrace})
|
||||
services=$@
|
||||
echo started feeding the dog
|
||||
exec 3> ''${WATCHDOG-/dev/watchdog}
|
||||
|
||||
healthy(){
|
||||
test $(date +%s) -le $deadline && return 0
|
||||
|
||||
for i in $services; do
|
||||
if test "$(s6-svstat -o up /run/service/$i)" != "true" ; then
|
||||
echo "service $i is down"
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
while healthy ;do
|
||||
sleep 10
|
||||
echo >&3
|
||||
done
|
||||
echo "stopped feeding the dog"
|
||||
sleep 6000 # don't want s6-rc to restart
|
||||
'';
|
||||
in longrun {
|
||||
name = "watchdog";
|
||||
run =
|
||||
"${script} ${lib.concatStringsSep " " (builtins.map (s: s.name) watched)}";
|
||||
};
|
||||
|
||||
services.sshd = svc.ssh.build { };
|
||||
|
||||
services.watchdog = svc.watchdog.build {
|
||||
watched = with config.services ; [ sshd dhcpc ];
|
||||
};
|
||||
|
||||
services.resolvconf = oneshot rec {
|
||||
dependencies = [ services.dhcpc ];
|
||||
@@ -123,9 +81,6 @@ in rec {
|
||||
done
|
||||
)
|
||||
'';
|
||||
down = ''
|
||||
rm -rf /run/service-state/${name}/
|
||||
'';
|
||||
};
|
||||
filesystem = dir {
|
||||
etc = dir {
|
||||
@@ -134,35 +89,25 @@ in rec {
|
||||
srv = dir {};
|
||||
};
|
||||
|
||||
services.defaultroute4 = route {
|
||||
name = "defaultroute";
|
||||
services.defaultroute4 = svc.network.route.build {
|
||||
via = "$(output ${services.dhcpc} router)";
|
||||
target = "default";
|
||||
dependencies = [services.dhcpc];
|
||||
};
|
||||
|
||||
programs.busybox = {
|
||||
applets = ["blkid" "lsusb" "findfs" "tar"];
|
||||
applets = ["lsusb" "tar"];
|
||||
options = {
|
||||
FEATURE_LS_TIMESTAMPS = "y";
|
||||
FEATURE_LS_SORTFILES = "y";
|
||||
FEATURE_BLKID_TYPE = "y";
|
||||
FEATURE_MOUNT_FLAGS = "y";
|
||||
FEATURE_MOUNT_LABEL = "y";
|
||||
FEATURE_VOLUMEID_EXT = "y";
|
||||
};
|
||||
};
|
||||
|
||||
services.mount_external_disk = oneshot {
|
||||
name = "mount_external_disk";
|
||||
up = ''
|
||||
while ! findfs LABEL=backup-disk; do
|
||||
echo waiting for backup-disk
|
||||
sleep 1
|
||||
done
|
||||
mount -t ext4 LABEL=backup-disk /srv
|
||||
'';
|
||||
down = "umount /srv";
|
||||
services.mount_external_disk = svc.mount.build {
|
||||
device = "LABEL=backup-disk";
|
||||
mountpoint = "/srv";
|
||||
fstype = "ext4";
|
||||
};
|
||||
|
||||
services.rsync =
|
||||
@@ -199,22 +144,8 @@ in rec {
|
||||
] ;
|
||||
};
|
||||
|
||||
services.default = target {
|
||||
name = "default";
|
||||
contents =
|
||||
let links = config.hardware.networkInterfaces;
|
||||
in with config.services; [
|
||||
links.lo
|
||||
defaultroute4
|
||||
resolvconf
|
||||
sshd
|
||||
rsync
|
||||
watchdog
|
||||
];
|
||||
};
|
||||
|
||||
users.root = {
|
||||
passwd = lib.mkForce secrets.root_password;
|
||||
passwd = lib.mkForce secrets.root.passwd;
|
||||
# openssh.authorizedKeys.keys = [
|
||||
# (builtins.readFile "/home/dan/.ssh/id_rsa.pub")
|
||||
# ];
|
||||
|
||||
@@ -35,6 +35,7 @@ in {
|
||||
(drop "icmpv6 type destination-unreachable ct state invalid,untracked")
|
||||
];
|
||||
};
|
||||
|
||||
forward-ip6 = {
|
||||
type = "filter";
|
||||
family = "ip6";
|
||||
@@ -95,19 +96,23 @@ in {
|
||||
# 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,8 +128,8 @@ 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"
|
||||
@@ -132,6 +137,7 @@ in {
|
||||
# how does this even make sense in an input chain?
|
||||
(accept "oifname \"int\" iifname \"ppp0\" ct state established,related")
|
||||
(accept "iifname \"int\" oifname \"ppp0\" ")
|
||||
"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"
|
||||
"oifname \"int\" 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).
|
||||
];
|
||||
};
|
||||
|
||||
}
|
||||
202
examples/demo.nix
Normal file
202
examples/demo.nix
Normal file
@@ -0,0 +1,202 @@
|
||||
# This is an example configuration for a "typical" small office/home
|
||||
# router and wifi access point.
|
||||
|
||||
# You need to copy it to another filename and change the configuration
|
||||
# wherever the text "EDIT" appears - please consult the tutorial
|
||||
# documentation for details.
|
||||
|
||||
{ config, pkgs, lib, ... } :
|
||||
let
|
||||
inherit (pkgs.liminix.services) bundle oneshot longrun;
|
||||
inherit (pkgs) serviceFns;
|
||||
# EDIT: you can pick your preferred RFC1918 address space
|
||||
# for NATted connections, if you don't like this one.
|
||||
ipv4LocalNet = "10.8.0";
|
||||
svc = config.system.service;
|
||||
|
||||
in rec {
|
||||
boot = {
|
||||
tftp = {
|
||||
freeSpaceBytes = 3 * 1024 * 1024;
|
||||
serverip = "10.0.0.1";
|
||||
ipaddr = "10.0.0.8";
|
||||
};
|
||||
};
|
||||
|
||||
imports = [
|
||||
../modules/bridge
|
||||
../modules/dhcp6c
|
||||
../modules/dnsmasq
|
||||
../modules/firewall
|
||||
../modules/hostapd
|
||||
../modules/network
|
||||
../modules/ntp
|
||||
../modules/ppp
|
||||
../modules/ssh
|
||||
../modules/vlan
|
||||
../modules/wlan.nix
|
||||
];
|
||||
rootfsType = "jffs2";
|
||||
hostname = "the-internet"; # EDIT
|
||||
|
||||
services.hostap = svc.hostapd.build {
|
||||
interface = config.hardware.networkInterfaces.wlan;
|
||||
# EDIT: you will want to change the obvious things
|
||||
# here to values of your choice
|
||||
params = {
|
||||
ssid = "the-internet";
|
||||
channel = "1";
|
||||
country_code = "GB";
|
||||
wpa_passphrase = "not a real wifi password";
|
||||
|
||||
hw_mode="g";
|
||||
ieee80211n = 1;
|
||||
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;
|
||||
};
|
||||
};
|
||||
|
||||
services.int = svc.network.address.build {
|
||||
interface = svc.bridge.primary.build { ifname = "int"; };
|
||||
family = "inet"; address = "${ipv4LocalNet}.1"; prefixLength = 16;
|
||||
};
|
||||
|
||||
services.bridge = svc.bridge.members.build {
|
||||
primary = services.int;
|
||||
members = with config.hardware.networkInterfaces;
|
||||
[ wlan lan ];
|
||||
};
|
||||
|
||||
services.ntp = svc.ntp.build {
|
||||
pools = { "pool.ntp.org" = ["iburst"]; };
|
||||
makestep = { threshold = 1.0; limit = 3; };
|
||||
};
|
||||
|
||||
services.sshd = svc.ssh.build { };
|
||||
|
||||
users.root = {
|
||||
# EDIT: choose a root password and then use
|
||||
# "mkpasswd -m sha512crypt" to determine the hash.
|
||||
# It should start wirh $6$.
|
||||
passwd = "$6$6HG7WALLQQY1LQDE$428cnouMJ7wVmyK9.dF1uWs7t0z9ztgp3MHvN5bbeo0M4Kqg/u2ThjoSHIjCEJQlnVpDOaEKcOjXAlIClHWN21";
|
||||
openssh.authorizedKeys.keys = [
|
||||
# EDIT: you can add your ssh pubkey here
|
||||
# "ssh-rsa AAAAB3NzaC1....H6hKd user@example.com";
|
||||
];
|
||||
};
|
||||
|
||||
services.dns =
|
||||
let interface = services.int;
|
||||
in svc.dnsmasq.build {
|
||||
resolvconf = services.resolvconf;
|
||||
inherit interface;
|
||||
ranges = [
|
||||
"${ipv4LocalNet}.10,${ipv4LocalNet}.249"
|
||||
# EDIT: ... maybe. In this example we use "ra-stateless",
|
||||
# meaning dnsmasq 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.
|
||||
# If you didn't understand the preceding sentence then
|
||||
# the default is _probably_ fine, but if you need
|
||||
# a DHCP-only IPv6 network or some other different
|
||||
# configuration, this is the place to change it.
|
||||
"::,constructor:$(output ${interface} ifname),ra-stateless"
|
||||
];
|
||||
# EDIT: choose a domain name for the DNS names issued for your
|
||||
# DHCP-issued hosts
|
||||
domain = "lan.example.com";
|
||||
};
|
||||
|
||||
services.wan = svc.pppoe.build {
|
||||
interface = config.hardware.networkInterfaces.wan;
|
||||
ppp-options = [
|
||||
"debug" "+ipv6" "noauth"
|
||||
# EDIT: change the strings "chap-username"
|
||||
# and "chap-secret" to match the username/password
|
||||
# provided by your ISP for PPP logins
|
||||
"name" "chap-username"
|
||||
"password" "chap-secret"
|
||||
];
|
||||
};
|
||||
|
||||
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 { };
|
||||
|
||||
# We expect the ISP uses DHCP6 to issue IPv6 addresses. There is a
|
||||
# service to request address information in the form of a DHCP
|
||||
# lease, and two dependent services that listen for updates to the
|
||||
# DHCP address information and update the addresses of the WAN and
|
||||
# LAN interfaces respectively.
|
||||
|
||||
services.dhcp6c =
|
||||
let client = svc.dhcp6c.client.build {
|
||||
interface = services.wan;
|
||||
};
|
||||
in bundle {
|
||||
name = "dhcp6c";
|
||||
contents = [
|
||||
(svc.dhcp6c.prefix.build {
|
||||
# if your ISP provides you a real IPv6 prefix for your local
|
||||
# network (usually a /64 or /48 or something in between the
|
||||
# two), this service subscribes to that "prefix delegation"
|
||||
# information, and uses it to assign an address to the LAN
|
||||
# device. dnsmasq will notice this address and use it to
|
||||
# form the addresses it hands out to devices on the lan
|
||||
inherit client;
|
||||
interface = services.int;
|
||||
})
|
||||
(svc.dhcp6c.address.build {
|
||||
# if your ISP provides you a regular global IPv6 address,
|
||||
# this service subscribes to that information and assigns
|
||||
# the address to the WAN device.
|
||||
inherit client;
|
||||
interface = services.wan;
|
||||
})
|
||||
];
|
||||
};
|
||||
|
||||
defaultProfile.packages = with pkgs; [
|
||||
min-collect-garbage
|
||||
];
|
||||
}
|
||||
@@ -11,18 +11,10 @@
|
||||
...
|
||||
}: let
|
||||
secrets = import ./extneder-secrets.nix;
|
||||
inherit
|
||||
(pkgs.liminix.networking)
|
||||
address
|
||||
udhcpc
|
||||
hostapd
|
||||
interface
|
||||
route
|
||||
;
|
||||
inherit (pkgs.liminix.services) oneshot longrun bundle target;
|
||||
inherit (pkgs.pseudofile) dir symlink;
|
||||
inherit (pkgs) dropbear ifwait serviceFns
|
||||
;
|
||||
inherit (pkgs) dropbear ifwait serviceFns;
|
||||
svc = config.system.service;
|
||||
in rec {
|
||||
boot = {
|
||||
tftp = {
|
||||
@@ -33,7 +25,11 @@ in rec {
|
||||
|
||||
imports = [
|
||||
../modules/wlan.nix
|
||||
../modules/standard.nix
|
||||
../modules/vlan
|
||||
../modules/network
|
||||
../modules/hostapd
|
||||
../modules/bridge
|
||||
../modules/ssh
|
||||
];
|
||||
|
||||
hostname = "extneder";
|
||||
@@ -46,7 +42,9 @@ in rec {
|
||||
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
|
||||
# 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";
|
||||
@@ -71,7 +69,8 @@ in rec {
|
||||
};
|
||||
};
|
||||
|
||||
services.hostap = hostapd (config.hardware.networkInterfaces.wlan) {
|
||||
services.hostap = svc.hostapd.build {
|
||||
interface = config.hardware.networkInterfaces.wlan;
|
||||
params = {
|
||||
country_code = "GB";
|
||||
hw_mode = "g";
|
||||
@@ -86,42 +85,25 @@ in rec {
|
||||
};
|
||||
};
|
||||
|
||||
services.int = interface {
|
||||
type = "bridge";
|
||||
device = "int";
|
||||
services.int = svc.bridge.primary.build {
|
||||