Compare commits
148 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 92643e03b3 | |||
| e55043a5a9 | |||
| bad55a5f66 | |||
| 1977d3b8a1 | |||
| e1d8c5de61 | |||
| 6a89b5b4d4 | |||
| d749538e9e | |||
| f728b584a2 | |||
| e04724d7fc | |||
| 2721481ce1 | |||
| fd9f5cb45f | |||
| b3bdb5a9c4 | |||
| 8e5f3e29a7 | |||
| 46769563b7 | |||
| 73f1a566c3 | |||
| a720f8f114 | |||
| f9f55ee6bc | |||
| 16168dc730 | |||
| b3f0c33347 | |||
| 2f092e126a | |||
| eb16a35ff3 | |||
| 6a7aa0898e | |||
| a792fab8ac | |||
| 9910ecefc0 | |||
| 98a377b551 | |||
| 9f9a4a2dc7 | |||
| 643e2ec376 | |||
| 887fe16020 | |||
| 6b61ba3b52 | |||
| a0b36be5e4 | |||
| 9bc9a077e3 | |||
| 9f501018bf | |||
| de2757b798 | |||
| de861a2ee0 | |||
| 936eb1a7cd | |||
| e4aad311a8 | |||
| 6674826990 | |||
| 97ff2f3009 | |||
| 210b41efc0 | |||
| 53c6d506cf | |||
| 01fe2159b4 | |||
| d7d19b5ed0 | |||
| ee683f2202 | |||
| d9723aeb87 | |||
| 46ed8f0199 | |||
| dd44fbaec1 | |||
| 89065be6cd | |||
| 420320e434 | |||
| 9ecd2b4fb4 | |||
| 1a6160bcab | |||
| b1bf13bb01 | |||
| c3f550698d | |||
| 05991225de | |||
| 7ce1c6bb7d | |||
| 8440378a39 | |||
| e5cfd41013 | |||
| 0ae5689a40 | |||
| 45047dc023 | |||
| 3673804b93 | |||
| be03e9e8c8 | |||
| 4e51977ae0 | |||
| 2b0972ed73 | |||
| f22237a3b3 | |||
| 9dc0f25587 | |||
| 9ab77a7d7e | |||
| c6918fec00 | |||
| d4e46dbe28 | |||
| d1f87a56e0 | |||
| 8c39b47cae | |||
| 2c7a16d792 | |||
| d6b06abb63 | |||
| 6b32aa569e | |||
| 234d1bd87e | |||
| c38f180fb7 | |||
| 9a8b22997c | |||
| c32d09bd83 | |||
| 6649ebeccd | |||
| 929226ed9e | |||
| a98f026210 | |||
| f4dc001b71 | |||
| 024c018262 | |||
| e1293e3778 | |||
| 0c406058e9 | |||
| 19d441333c | |||
| a726c09ae4 | |||
| 7e2b0068e6 | |||
| 13cc5a8992 | |||
| 3f889c7119 | |||
| 7f17125039 | |||
| 4bb081ffcf | |||
| 6587813577 | |||
| 1d780de0f1 | |||
| 8cf602da91 | |||
| c92aacc6fd | |||
| eff255fe12 | |||
| 453baede61 | |||
| 2295ed3110 | |||
| e71d92eb3d | |||
| f77da6f14c | |||
| 61eaaa82eb | |||
| 95dd1a1fab | |||
| 2f9b0f12f9 | |||
| 9fd9b8b878 | |||
| 26f206d0e1 | |||
| 8cd068ea68 | |||
| 350ddde260 | |||
| 13cb8d3692 | |||
| 62b7aea8ab | |||
| 76e3fd9a55 | |||
| 92284fa9ba | |||
| a2bb55e885 | |||
| 74027b44d7 | |||
| ea5370b3f4 | |||
| 55ed365920 | |||
| aa2160dd05 | |||
| df414b796f | |||
| 7377f7ceb2 | |||
| 49432aeda5 | |||
| 3caf8a75bb | |||
| cc94ef57fa | |||
| fd28f0ce04 | |||
| 497307588f | |||
| 788169586f | |||
| 3af9e86624 | |||
| 28d39cd66d | |||
| 9dd169d500 | |||
| 2e513eb4a7 | |||
| f2e4e77d73 | |||
| 48dfbe0c01 | |||
| 6f697db57c | |||
| fe1ee12e3d | |||
| 4d273a9469 | |||
| 40db175b41 | |||
| ab07212a7e | |||
| f5e08ac9d9 | |||
| 0cb18eabcd | |||
| 24151425b8 | |||
| e06295ed83 | |||
| 608d3e3abf | |||
| 3e19f1b927 | |||
| 3f6e9b6384 | |||
| 294492a176 | |||
| 67a1cd3718 | |||
| 452aaa2f60 | |||
| 52967f746b | |||
| a89f866bf0 | |||
| f3fadd5cd7 | |||
| 1ff779c1a9 |
@@ -1,57 +0,0 @@
|
||||
# Liminix community spaces Code of Conduct
|
||||
|
||||
As of Feb 2023, "RESPONSE TEAM" and "LEADERSHIP TEAM" in the text that follows
|
||||
both refer to me, Daniel Barlow, as there are not yet any other project members.
|
||||
|
||||
Liminix is dedicated to providing a harassment-free experience for everyone. We do not tolerate harassment of participants in any form.
|
||||
|
||||
This code of conduct applies to all Liminix spaces, including the IRC channel, mailing lists, and Github forums, both online and off. Anyone who violates this code of conduct may be sanctioned or expelled from these spaces at the discretion of the RESPONSE TEAM.
|
||||
|
||||
Some Liminix spaces may have additional rules in place, which will be made clearly available to participants. Participants are responsible for knowing and abiding by these rules.
|
||||
|
||||
Harassment includes:
|
||||
|
||||
*Offensive comments related to gender, gender identity and expression, sexual orientation, disability, mental illness, neuro(a)typicality, physical appearance, body size, age, race, or religion.
|
||||
*Unwelcome comments regarding a person’s lifestyle choices and practices, including those related to food, health, parenting, drugs, and employment.
|
||||
*Deliberate misgendering or use of ‘dead’ or rejected names.
|
||||
*Gratuitous or off-topic sexual images or behaviour in spaces where they’re not appropriate.
|
||||
*Physical contact and simulated physical contact (eg, textual descriptions like “*hug*” or “*backrub*”) without consent or after a request to stop.
|
||||
*Threats of violence.
|
||||
*Incitement of violence towards any individual, including encouraging a person to commit suicide or to engage in self-harm.
|
||||
*Deliberate intimidation.
|
||||
*Stalking or following.
|
||||
*Harassing photography or recording, including logging online activity for harassment purposes.
|
||||
*Sustained disruption of discussion.
|
||||
*Unwelcome sexual attention.
|
||||
*Pattern of inappropriate social contact, such as requesting/assuming inappropriate levels of intimacy with others
|
||||
*Continued one-on-one communication after requests to cease.
|
||||
*Deliberate “outing” of any aspect of a person’s identity without their consent except as necessary to protect vulnerable people from intentional abuse.
|
||||
*Publication of non-harassing private communication.
|
||||
Liminix prioritizes marginalized people’s safety over privileged people’s comfort. RESPONSE TEAM reserves the right not to act on complaints regarding:
|
||||
*‘Reverse’ -isms, including ‘reverse racism,’ ‘reverse sexism,’ and ‘cisphobia’
|
||||
*Reasonable communication of boundaries, such as “leave me alone,” “go away,” or “I’m not discussing this with you.”
|
||||
*Communicating in a ‘tone’ you don’t find congenial
|
||||
*Criticizing racist, sexist, cissexist, or otherwise oppressive behavior or assumptions
|
||||
|
||||
## Reporting
|
||||
|
||||
If you are being harassed by a member of Liminix, notice that someone else is being harassed, or have any other concerns, please contact the RESPONSE TEAM at [email address or other contact point]. If the person who is harassing you is on the team, they will recuse themselves from handling your incident. We will respond as promptly as we can.
|
||||
|
||||
This code of conduct applies to Liminix spaces, but if you are being harassed by a member of Liminix outside our spaces, we still want to know about it. We will take all good-faith reports of harassment by Liminix members, especially LEADERSHIP TEAM, seriously. This includes harassment outside our spaces and harassment that took place at any point in time. The abuse team reserves the right to exclude people from Liminix based on their past behavior, including behavior outside Liminix spaces and behavior towards people who are not in Liminix.
|
||||
|
||||
In order to protect volunteers from abuse and burnout, we reserve the right to reject any report we believe to have been made in bad faith. Reports intended to silence legitimate criticism may be deleted without response.
|
||||
|
||||
We will respect confidentiality requests for the purpose of protecting victims of abuse. At our discretion, we may publicly name a person about whom we’ve received harassment complaints, or privately warn third parties about them, if we believe that doing so will increase the safety of Liminix members or the general public. We will not name harassment victims without their affirmative consent.
|
||||
|
||||
### Consequences
|
||||
|
||||
Participants asked to stop any harassing behavior are expected to comply immediately.
|
||||
|
||||
If a participant engages in harassing behavior, RESPONSE TEAM may take any action they deem appropriate, up to and including expulsion from all Liminix spaces and identification of the participant as a harasser to other Liminix members or the general public.
|
||||
|
||||
## License and attribution
|
||||
|
||||
The policy is based on the Geek Feminism
|
||||
[Community anti-harassment/Policy](https://geekfeminism.fandom.com/wiki/Community_anti-harassment/Policy)
|
||||
and is the work of Annalee Flower Horne with assistance from Valerie
|
||||
Aurora, Alex Skud Bayley, Tim Chevalier, and Mary Gardiner.
|
||||
@@ -156,4 +156,16 @@ https://www.liminix.org/doc/admin.html#updating-an-installed-system
|
||||
2024-12-22
|
||||
|
||||
outputs.zimage is now outputs.kernel.zImage. This is unlikely to
|
||||
affect many people at all but I mention it anyway.
|
||||
affect many people at all but I mention it anyway.
|
||||
|
||||
2024-03-11
|
||||
|
||||
The fennel function (svc.open ...) now expects to be given the store
|
||||
directory of a service derivation, not a direct path to the .outputs
|
||||
directory. Thus
|
||||
|
||||
(svc.open "/nix/store/eeeeeeeeeeeeee-hellod")
|
||||
not
|
||||
(svc.open "/nix/store/eeeeeeeeeeeeee-hellod/.outputs")
|
||||
|
||||
This simplifies most extant uses of it
|
||||
|
||||
+967
@@ -6670,3 +6670,970 @@ Mon Dec 23 18:28:50 GMT 2024
|
||||
|
||||
it might be worth moving ubi option decls into the hardware module, if
|
||||
they're hardware-dependent
|
||||
|
||||
Tue Dec 24 16:14:50 GMT 2024
|
||||
|
||||
Where next?
|
||||
|
||||
a config for haproxy would be good
|
||||
|
||||
> a connection or request arrives on a frontend, then the
|
||||
information carried with this request or connection are processed, and
|
||||
at this point it is possible to write ACLs-based conditions making use
|
||||
of these information to decide what backend will process the request.
|
||||
|
||||
http://docs.haproxy.org/3.1/intro.html#3.4.4
|
||||
|
||||
listen foo
|
||||
bind :443
|
||||
|
||||
frontend foo
|
||||
mode tcp
|
||||
|
||||
Thu Dec 26 14:27:26 GMT 2024
|
||||
|
||||
What's the plan?
|
||||
|
||||
1) build the updater target for rotuer
|
||||
2) ssh forward through bordervm to install it
|
||||
3) carve the ngix sni proxy into examples/module-sniproxy
|
||||
with a fat warning comment
|
||||
3.5) should we add the examples to ci?
|
||||
4) see if it builds
|
||||
5) using curl on bordervm, see if it forwards
|
||||
5000) swap the real rotuer hardware
|
||||
|
||||
|
||||
Sun Dec 29 18:22:42 GMT 2024
|
||||
|
||||
To make sniproxy work it needs dns, which means it needs an upstream.
|
||||
But if we move the bordervm cable from lan to wan, we won't be able to
|
||||
rebuild over ssh unless it's sufficiently unbroken for pppoe to be working
|
||||
|
||||
... unless we add a "management" address to the wan interface along with pppoe
|
||||
|
||||
to permit wan stuff through the firewall, now that we've fixed the
|
||||
firewall :embarrassed:, do
|
||||
|
||||
/nix/store/*nftables*/bin/nft insert rule table-ip input-ip4 position 19 iifname "wan" jump incoming-allowed-ip4
|
||||
|
||||
What else do we need to try rt3200 as prod rotuer?
|
||||
|
||||
- pstore logging
|
||||
- log shipping (maybe via wan to bordervm for now)
|
||||
- enable sniproxy module
|
||||
|
||||
zcat /proc/config.gz | grep PSTORE
|
||||
|
||||
Tue Dec 31 22:45:33 GMT 2024
|
||||
|
||||
/nix/store/i1khbsqpyx020xrhvfbdazc1bnmirc72-kernel-aarch64-unknown-linux-musl-modulesupport/vmlinux
|
||||
|
||||
|
||||
these 3 derivations will be built:
|
||||
/nix/store/fvxrvyx64cm86cc4na0584qj9xw6s406-kernel-aarch64-unknown-linux-musl.drv
|
||||
/nix/store/ksg93bd8x2z1xpfmj24ixm2srg547mjx-dtb-aarch64-unknown-linux-musl.drv
|
||||
/nix/store/n33ij1npzwjmlsw23sz6yhyrh9hgxwaw-kernel.image-aarch64-unknown-linux-musl.drv
|
||||
building '/nix/store/fvxrvyx64cm86cc4na0584qj9xw6s406-kernel-aarch64-unknown-linux-musl.drv'...
|
||||
zcat /proc/config.gz | grep PSTORE
|
||||
|
||||
we think tftpboot works (check!)
|
||||
build the FIT from the updater target with squashfs added in rotuer.nix
|
||||
boot it with tftp by copy-pasting the boot.scr and changing it
|
||||
|
||||
setenv serverip 10.0.0.1
|
||||
setenv ipaddr 10.0.0.8
|
||||
tftpboot 0x4007ff28 r2/rootfs; tftpboot 0x41086f28 r2/image; tftpboot 0x4107ff28 r2/dtb
|
||||
bootm 0x41086f28 - 0x4107ff28
|
||||
|
||||
tftpboot works
|
||||
tftpboot with outputs.uimage works
|
||||
1458ed2cdeaa6bf4c02c6511a6d7369d /nix/store/4n3jc4n7lsp73yc2ccnmgibc7079rxms-kernel.image-aarch64-unknown-linux-musl
|
||||
|
||||
$ grep fit /nix/store/ihi7vski37b9ph4xcyqpabymc5nsngjd-system-configuration-aarch64-unknown-linux-musl/etc/nix-store-paths
|
||||
/nix/store/5jphs9mnb8hzhvwfi2z8h6cnaz6qx4y6-boot-fit
|
||||
$ md5sum /nix/store/5jphs9mnb8hzhvwfi2z8h6cnaz6qx4y6-boot-fit/fit
|
||||
1458ed2cdeaa6bf4c02c6511a6d7369d /nix/store/5jphs9mnb8hzhvwfi2z8h6cnaz6qx4y6-boot-fit/fit
|
||||
|
||||
something is changing /persist/boot from a symlink to a directory and I don't know what
|
||||
... plot twist: or is it? maybe it's ubimage that makes it a directory
|
||||
|
||||
rt3200 has no pmsg-size according to dtc -I fs /proc/device-tree/ -O dts
|
||||
|
||||
should be more like
|
||||
|
||||
reserved-memory {
|
||||
ramoops@03f00000 {
|
||||
compatible = "ramoops";
|
||||
reg = <0x03f00000 0x10000>;
|
||||
pmsg-size = <0x10000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
Wed Jan 1 14:57:35 GMT 2025
|
||||
|
||||
At last I have working persistent logging.
|
||||
|
||||
I think we need to do something at boot time to move the persistent logs into
|
||||
the regular s6 log thing
|
||||
|
||||
foreground {
|
||||
redirfd -w 1 /run/prior-boot2
|
||||
elglob file /sys/fs/pstore/* cat $file
|
||||
}
|
||||
|
||||
Thu Jan 2 14:31:11 GMT 2025
|
||||
|
||||
to change to a previous "generation" we could run any of
|
||||
"/nix/store/*system-configuration*/bin/install /mnt" from a rescue
|
||||
system. It would populate /boot and bin/activate
|
||||
|
||||
supposing we are in such a rescue system, how do we find *which*
|
||||
system-configuration is the one we want to revert to? The derivation
|
||||
should be pure, so if we're going to timestamp anything we have to do
|
||||
that in the imperative step i.e. update.sh
|
||||
|
||||
perhaps a symlink from /persist/configuration/yyyymmddtmmhhss -> /nix/store/eeee-blah
|
||||
|
||||
|
||||
Thu Jan 2 23:38:59 GMT 2025
|
||||
|
||||
Stuff we should tidy up:
|
||||
|
||||
1) all devices to set credible default output, rootfs, etc
|
||||
2) expunge remaining references to kexecboot
|
||||
3) dynamic uid assigment for users
|
||||
4) lessen logging noise from firewall
|
||||
5) update.sh --fast sends wlan services for a spin
|
||||
|
||||
|
||||
|
||||
Fri Jan 3 16:05:10 GMT 2025
|
||||
|
||||
* build actual rotuer config and install on rt3200
|
||||
* find usb ethernet dongle for laptop
|
||||
* plug it in?
|
||||
|
||||
Sat Jan 4 15:32:03 GMT 2025
|
||||
|
||||
didn't work so well, because the installer version is coupled with the
|
||||
dtb and the dtb can't be upgraded without upgrading the kenrel
|
||||
|
||||
kernel 6.6.67 builds and boots but seems to have no wired ethernet.
|
||||
looking through the openwrt config changes ...
|
||||
|
||||
+CONFIG_MTD_UBI_NVMEM=y
|
||||
+CONFIG_NVMEM_BLOCK=y
|
||||
+CONFIG_NVMEM_LAYOUT_ADTRAN=y
|
||||
+CONFIG_PHYLIB_LEDS=y
|
||||
+CONFIG_MTK_REGULATOR_COUPLER=y
|
||||
+CONFIG_FW_LOADER_SYSFS=y
|
||||
|
||||
|
||||
Sun Jan 5 12:58:52 GMT 2025
|
||||
|
||||
We are running with rt3200 and everything appears to work :-)
|
||||
|
||||
Sun Jan 5 20:34:18 GMT 2025
|
||||
|
||||
what customization do we want from the firewall?
|
||||
|
||||
- what's allowed wan->lan
|
||||
- what's allowed lan->wan
|
||||
- which dropped packets get logged or don't
|
||||
|
||||
plus fix whatever it was RoS found
|
||||
plus stop hardcoding the interface names
|
||||
|
||||
Q: if pppd _makes_ an interface, how do we know what the name of it
|
||||
is going to be before it's up and passing packets so that we can
|
||||
have the firewall active before it starts
|
||||
|
||||
or could we have ppp service install a "drop everything" firewall
|
||||
before it starts? what if there's more than one upstream interface,
|
||||
they shouldn't wipe each other out
|
||||
|
||||
so can we have a "default deny" firewall in which every allowed flow
|
||||
is qualified by the interface name, and any service that brings up an
|
||||
interface is required to add firewall rules for it according to its
|
||||
role
|
||||
|
||||
or maybe the firewall service could watch for interfaces being added
|
||||
(and removed) and update the ruleset as appropriate for the interface
|
||||
role (lan, wan, dmz, management, guest, ???). but how does it know
|
||||
the role based on the interface name?
|
||||
|
||||
Tue Jan 14 19:33:28 GMT 2025
|
||||
|
||||
each interface can add its own chain when it comes up, and then we can
|
||||
figure out some way to jump to the correct chain (vmaps) based on
|
||||
interface name
|
||||
|
||||
% nft add map filter mydict { type ipv4_addr : verdict\; }
|
||||
% nft add element filter mydict { 192.168.0.10 : drop, 192.168.0.11 : accept }
|
||||
% nft add rule filter input ip saddr vmap @mydict
|
||||
|
||||
_but_ we might be better off declaring static "zones" (lan, world,
|
||||
dmz, guest, etc etc) with a map for each, and then replace the hardcoded
|
||||
interface names with a map lookup
|
||||
|
||||
% nft add map filter ifzone { type ifname : string ; }
|
||||
|
||||
|
||||
zones { type ifname: ipv4_addr\; }
|
||||
... and presumably we also need identical maps for nat and
|
||||
... any other chain type where we need to distinguish
|
||||
... inside from outside
|
||||
% nft add element nat porttoip { 80 : 192.168.1.100, 8888 : 192.168.1.101 }
|
||||
|
||||
% nft add rule ip nat postrouting snat to tcp dport map @porttoip
|
||||
|
||||
Mon Jan 20 20:32:58 GMT 2025
|
||||
|
||||
1) maybe we can add a type="chain" or type="set" attribute to the
|
||||
firewallgen input, then we cna have sets in the default firewall rules
|
||||
|
||||
2) then we convert the default firewall rules to use sets instead of
|
||||
hardcoding ifname
|
||||
|
||||
3) then find a nice place to hook "new interface is available" and
|
||||
add it to the appropriate zone (separately for ip4 and ip6, gah)
|
||||
|
||||
4) and how do we find
|
||||
|
||||
# nft add element ip table-ip lan { int }
|
||||
# nft add element ip table-ip wan { ppp0 }
|
||||
# nft add element ip6 table-ip6 lan { int }
|
||||
# nft add element ip6 table-ip6 wan { ppp0 }
|
||||
|
||||
Tue Jan 28 21:51:46 GMT 2025
|
||||
|
||||
Going back and forth on the firewall stuff, in respect of where to aim
|
||||
for with "general" vs "useful". Specifically, if we have hooks
|
||||
every time interfaces are added/removed that
|
||||
expect some specific sets to (a) exist and (b) have particular
|
||||
semantics, there is necessary coupling between those hooks and the
|
||||
firewall definition. So, practically speaking, the "a new interface
|
||||
appears" rules need to be bundled with the firewall ruleset
|
||||
|
||||
Which also means that the firewall needs to know which zone the
|
||||
interface is assigned to, which is a problem if it can't tell from the
|
||||
name (for example wg0 wg1 wg2 ... could be wan or lan or dmz or
|
||||
anything)
|
||||
|
||||
So the service that owns the interface needs to communicate "another
|
||||
one for the lan zone" to the firewall and it can't do that by adding
|
||||
to sets directly unless it knows what the sets are called. Implying we
|
||||
need an interface between the interface service that knows "new
|
||||
interface ppp7 added in wan zone" and the firewall service that knows
|
||||
how to accommodate this. For extra credit this actually should be more
|
||||
like pubsub: the interface shouldn't really have to know the firewall
|
||||
exists.
|
||||
|
||||
outputs?
|
||||
|
||||
- what if each interface wrote a "zone" output and the firewall
|
||||
subscribed to them? would need the firewall to know which of all the
|
||||
available services were interfaces
|
||||
|
||||
- a zone service that every interface in the zone depends on. it
|
||||
doesn't do much in itself but it means the interface updown scripts
|
||||
know a service directory where they can touch lan-zone/eth0 or
|
||||
whatever. This could work. The firewall service definition specifies
|
||||
the zone services and uses inotify watcher thingies to update
|
||||
interface sets when contents change.
|
||||
|
||||
Wed Jan 29 17:19:24 GMT 2025
|
||||
|
||||
1) make a zone service defn that can be instantiated for each zone.
|
||||
it should create $output/interfaces
|
||||
|
||||
2) add a `zone` attribute to interface definitions, causing
|
||||
- the zone service to be added to the dependencies
|
||||
- the interface "up" script to include writing to the zone/interfaces output
|
||||
|
||||
2b) any other service that creates an interface (e.g. ppp) needs to also
|
||||
have `zone` and do the same
|
||||
|
||||
3) firewallgen to be able to make sets
|
||||
|
||||
4) firewall service to watch the zone outputs
|
||||
|
||||
Fri Jan 31 17:11:16 GMT 2025
|
||||
|
||||
Do we need zone services? I think we could put zones in the outputs of
|
||||
the firewall service?
|
||||
|
||||
Sun Feb 2 20:59:56 GMT 2025
|
||||
|
||||
What's the smallest first step?
|
||||
|
||||
- [done] how can we make firewallgen output sets (or could we
|
||||
make the firewall service tack them on afterwards)
|
||||
|
||||
- make a longrun that watches its own zones output and updates the
|
||||
appropriate sets
|
||||
|
||||
The sticking point is that if you give the firewall `rules` instead of
|
||||
`extraRules` then the longrun may or may not work depending on (1)
|
||||
whether you made the zone sets; (2) whether your rules use
|
||||
them. Conclusion: if you supply `rules` then you also have to say
|
||||
whether you want the longrun or not. So add a param
|
||||
watchForInterfaceUpdates which defaults true
|
||||
|
||||
Mon Feb 3 21:12:55 GMT 2025
|
||||
|
||||
the thing that updates sets has to know they exist, so the interface watcher
|
||||
service must live in the firewall module
|
||||
|
||||
the firewall service defn should return the firewall service after
|
||||
adding the interface watcher as a dependency of it. Or: the watcher
|
||||
should make the sets and then the firewall service could depend on _it_.
|
||||
That would mean that the firewall service would fail if it used sets
|
||||
that the watcher didn't make, is that good or bad or indifferent?
|
||||
|
||||
the interface services have to know about the watcher as well in order
|
||||
to write into its outputs, so it can't be hidden inside the module
|
||||
|
||||
maybe the watcher service should _be_ the firewall service.
|
||||
|
||||
we could add a "notify" param to an interface which would be an output
|
||||
reference to (the firewall service / zones / lan ) that the interface would
|
||||
write its ifname into when the service is up
|
||||
|
||||
Wed Feb 5 00:14:29 GMT 2025
|
||||
|
||||
another thought: the firewall service could have params to say
|
||||
which interface services are in which zones
|
||||
|
||||
we'd have to ensure that the interface services did not end up as
|
||||
dependencies of the firewall
|
||||
|
||||
then the firewall could
|
||||
|
||||
- create the sets
|
||||
- watch each interface service for the ifname output and add it to the right zone
|
||||
|
||||
Sun Feb 9 21:33:57 GMT 2025
|
||||
|
||||
nft update set @lan
|
||||
|
||||
echo 'flush set table-ip lan; add element table-ip lan { eth0,lo }' | nft -f -
|
||||
|
||||
Tue Feb 11 18:30:09 GMT 2025
|
||||
|
||||
outstanding for 1.0:
|
||||
|
||||
1) security audit fedback
|
||||
|
||||
a) ask ROS if I can ship their report, with a response doc
|
||||
showing the commits that address each finding/non-finding
|
||||
b) firewall rules: icmp rate limit, DNS, doc for icmpv6 packet dropping
|
||||
c) look over env var inputs and parse them properly instead of
|
||||
string glommeration
|
||||
|
||||
2) docs:
|
||||
- for each device, add "finishedness" status and link to build status
|
||||
- generally read them over and spruce up
|
||||
- porting guide
|
||||
|
||||
3) some kconfig magic to generate minimal kconfig files so that
|
||||
device modules don't end up as copy-pastes of the openwrt defconfig
|
||||
|
||||
|
||||
---
|
||||
|
||||
apparently 5% of available bandwidth is a reasonable rate limit for
|
||||
icmp
|
||||
|
||||
% nft add rule filter input limit rate over 10 mbytes/second drop
|
||||
|
||||
but nftables has no way to get interface bandwidth and indeed nor does
|
||||
the device generally: the 1000Mb/s ethernet interface might be
|
||||
connected to a 70Mb/s pppoe upstream and how would it know? So the
|
||||
site operator needs to say somewhere what the upstream bandwidth is.
|
||||
|
||||
Sun Feb 16 22:16:29 GMT 2025
|
||||
|
||||
we probably didn't need to write that service, we could have used the
|
||||
thing that makes templated config files _and_ if we somehow contrive
|
||||
to write the interface bandwidth as an interface output we could get
|
||||
that the same way
|
||||
|
||||
if only I could remember how it worked :-)
|
||||
|
||||
----
|
||||
|
||||
* watch-output watches only _one_ service and is called with a list of
|
||||
outputs inside that service, so not exactly what we need. we can
|
||||
extend it easily enough to watch multiple services using poll() if we
|
||||
can figure out the syntax we want. Luckily all the places that call it
|
||||
go through modules/secrets/subscriber.nix so it's easy enough to change
|
||||
existing uses
|
||||
|
||||
we could do
|
||||
watch-outputs -r foo /nix/store/blah/.outputs/ifname /nix/store/eee/.outputs/ifname ...
|
||||
|
||||
or
|
||||
watch-outputs -r foo /nix/store/blah:ifname /nix/store/eee:ifname /nix/store/eee:bandwidth
|
||||
|
||||
or
|
||||
|
||||
watch-outputs -r foo /nix/store/blah:ifname /nix/store/eee:ifname:bandwidth
|
||||
|
||||
which I quite like insofar as it's shorter but has no other real merit
|
||||
|
||||
then we need to decide how to represent an output reference in a firewall rule.
|
||||
Since each rule is basically text already, might just put the handlebars straight in
|
||||
|
||||
let qq = builtins.toJSON ;
|
||||
in "icmp6 limit rate over {{ tonumber(output(${qq (intf "service")}, ${qq (intf "bandwidth")})) / 20 }} bytes/second drop"
|
||||
|
||||
probably we should do a separate rule for each interface in the wan zone
|
||||
|
||||
Sun Feb 23 00:34:34 GMT 2025
|
||||
|
||||
looks like we have no tests for anything involving watched services or subscribers,
|
||||
or if we do I can't see what
|
||||
|
||||
Thu Feb 27 20:47:03 GMT 2025
|
||||
|
||||
- use output-template to write firewall rule file
|
||||
- wrap firewall in svc.secrets.subscriber.build (c.f. e745991) with zones as
|
||||
watched services
|
||||
- put the handlebars in the firewall config
|
||||
|
||||
we have uncommitted changes to watch-outputs that I'm relunctant to
|
||||
commit until I have some way to see if they're working. the pppoe test
|
||||
will check both firewall zones so _should_ start to fail with the
|
||||
current watch-outputs (because only one service) and then pass when we
|
||||
put the new one in
|
||||
|
||||
Fri Feb 28 01:00:03 GMT 2025
|
||||
|
||||
Well, it works at least well enough to pass the test. There is an awful hack
|
||||
though, because nftables doesn't accept "elements = { }" as valid syntax
|
||||
for a set with no elements, so we post-process the file to wipe those lines
|
||||
|
||||
I wonder if we could instead create the set empty and then use the "other"
|
||||
nftables format to generate commands that add the elements. If it's
|
||||
all in the same file (or included files) it will continue to be atomic
|
||||
|
||||
Other options
|
||||
|
||||
- is the nftables json format any better? we will have to rebuild it
|
||||
with json support, may be bugger
|
||||
- write lua bindings to libnftables
|
||||
|
||||
Fri Feb 28 23:31:06 GMT 2025
|
||||
|
||||
adding json would add 76 + 88k to the image, but I think it would also
|
||||
mean we have to rewrite all the default rules in json format
|
||||
|
||||
|
||||
|
||||
with json
|
||||
[dan@loaclhost:~/src/liminix]$ du result/
|
||||
20 result/share/doc/nftables/examples
|
||||
24 result/share/doc/nftables
|
||||
28 result/share/doc
|
||||
12 result/share/man/man3
|
||||
16 result/share/man/man5
|
||||
44 result/share/man/man8
|
||||
76 result/share/man
|
||||
60 result/share/nftables
|
||||
168 result/share
|
||||
36 result/etc/nftables/osf
|
||||
40 result/etc/nftables
|
||||
44 result/etc
|
||||
76 result/bin
|
||||
8 result/include/nftables
|
||||
12 result/include
|
||||
8 result/lib/pkgconfig
|
||||
1172 result/lib
|
||||
1476 result/
|
||||
|
||||
[dan@loaclhost:~/src/liminix]$ du /nix/store/l0zsvldsskiv52b4c9b21ziq5z1qr7vn-jansson-mips-unknown-linux-musl-2.14/
|
||||
84 /nix/store/l0zsvldsskiv52b4c9b21ziq5z1qr7vn-jansson-mips-unknown-linux-musl-2.14/lib
|
||||
88 /nix/store/l0zsvldsskiv52b4c9b21ziq5z1qr7vn-jansson-mips-unknown-linux-musl-2.14/
|
||||
|
||||
without:
|
||||
[dan@loaclhost:~/src/liminix]$ du result/
|
||||
20 result/share/doc/nftables/examples
|
||||
24 result/share/doc/nftables
|
||||
28 result/share/doc
|
||||
12 result/share/man/man3
|
||||
16 result/share/man/man5
|
||||
44 result/share/man/man8
|
||||
76 result/share/man
|
||||
60 result/share/nftables
|
||||
168 result/share
|
||||
36 result/etc/nftables/osf
|
||||
40 result/etc/nftables
|
||||
44 result/etc
|
||||
76 result/bin
|
||||
8 result/include/nftables
|
||||
12 result/include
|
||||
8 result/lib/pkgconfig
|
||||
1096 result/lib
|
||||
1400 result/
|
||||
|
||||
|
||||
Sat Mar 1 23:43:17 GMT 2025
|
||||
|
||||
I don't think json is going to help because either we'd have to do
|
||||
|
||||
elements = map (f: "{{ lookup(f, \"ifname\") }}") zones.${zone}
|
||||
|
||||
and there would be null elements in the places for the interfaces that don't exist
|
||||
yet, or we'd have to write actual json syntax at runtime, at which point why don't
|
||||
we write the trad nftables syntax instead?
|
||||
|
||||
let's write a firewall .nftables file consisting of the zone set
|
||||
elements plus an "include" directive for the rest of the firewall. NOTE THAT
|
||||
we may still need to template the rest of the firewall if we want to have
|
||||
other variables (rate limits) in it, because the rules for that need to be
|
||||
inserted ahead of the rules for accepting icmp, and there's no way to
|
||||
do that without
|
||||
|
||||
Sun Mar 9 21:46:30 GMT 2025
|
||||
|
||||
OK, we have updating firewall zones that are good neough to pass the test
|
||||
(and may even work ...). We need
|
||||
|
||||
* to write a rule with a rate limit for incoming icmp6 for each interface,
|
||||
capping it to 5% of the interface bandwidth
|
||||
|
||||
* some place to say what the bandwidth is, which I am thinking we could do with
|
||||
a passthru attribute of some kind on services
|
||||
|
||||
{
|
||||
run = "mdcnvdfngkj";
|
||||
name = "dtret";
|
||||
properties.bandwidthKbps = 80000;
|
||||
}
|
||||
|
||||
and insert logic in the up/run commands to copy the properties to
|
||||
outputs. Or we could use `data` or `env` directories, which means
|
||||
that the properties would be there even while the service is down, if
|
||||
that's a concern, but would need a different way to look them up. And
|
||||
also, would we allow them to change? What if there's some kind of interface
|
||||
where we _can_ interrogate the bandwidth at runtime?
|
||||
|
||||
Mon Mar 10 21:00:17 GMT 2025
|
||||
|
||||
The idea that occured to me at lunchtime is "what if we made the
|
||||
(svc:output ) method fall back to properties if no output was present".
|
||||
To do this, we'd have to
|
||||
|
||||
(1) arrange for /nix/store/eeee-service/.properties to exist
|
||||
|
||||
- add properties attribute to service functions
|
||||
- write them to .properties in liminix-tools/services/builder.sh
|
||||
- make sure they get passed through whne provided to all the service
|
||||
builder functions
|
||||
|
||||
[done] (2) pass the store directory to svc.open instead of ..../.outputs
|
||||
|
||||
(3) make service:output look in both places
|
||||
|
||||
(4) write the damn firewall rule
|
||||
|
||||
Mon Mar 17 21:13:36 GMT 2025
|
||||
|
||||
Argh why is it never simple?
|
||||
|
||||
We need to write a rate-limiting firewall rule for each interface to
|
||||
restrict icmp on that interface. This is not easy to reconcile with
|
||||
putting them in default-rules because how do we generate multiple
|
||||
array elements by config file templating?
|
||||
|
||||
There are two things in my mind now:
|
||||
|
||||
1) could we have some better way of manipulating the firewall rules
|
||||
such that the rules from different modules are composable
|
||||
|
||||
this is complicated somewhat by ordering: if every rule in a chain is
|
||||
"drop" or "accept" then it's easy to add another, but if the same
|
||||
chain does first one then the other, doing the other and then the one
|
||||
will not work
|
||||
|
||||
today we do e.g.
|
||||
|
||||
input-ip6
|
||||
-> reject-bogons
|
||||
-> accept non-bogus-icmp
|
||||
-> process per-zone allowlist
|
||||
-> allow established,related on wan
|
||||
-> allow all on lan (so why did we need an allowlist?)
|
||||
|
||||
could we express this in a less sequential form? the
|
||||
specification of what's allowed
|
||||
|
||||
|
||||
input-ip6 for wan: is input-ip6 with the wan allowlist
|
||||
input-ip6 for lan: is input-ip6 with the lan allowlist
|
||||
|
||||
|
||||
input-ip6 for ppp0: is input-ip6 with the wan allowlist with a rate
|
||||
limit for icmp
|
||||
|
||||
ssh module wishes to modify the allowlist for lan/wan/both so that
|
||||
it includes port 22
|
||||
|
||||
am wondering if we could do default deny and _all_ the rules
|
||||
(except for bogons) are allows
|
||||
|
||||
maybe we have the concept of "subtraction": a rule can be an allow
|
||||
preceded by some number of drops which (at least by convention; this is
|
||||
probably hard to enforce) are "carve outs" of the packets that are
|
||||
being allowed.
|
||||
|
||||
... it's hard to express the forward-ipv6 in these terms, though.
|
||||
we end up with "some drops and then multiple accepts"
|
||||
|
||||
we have
|
||||
(and (not (or drop1 drop2 ...)) (or accept1 accept2 ...))
|
||||
|
||||
and to add ssh we need to break into the second clause instead of
|
||||
composing at the top level
|
||||
|
||||
(and (not (or drop1 drop2 ...))
|
||||
(or accept1 accept2 accept-ssh...))
|
||||
|
||||
|
||||
then the icmp bogons composite rule is "drop weird icmp and then
|
||||
allow what's left"
|
||||
|
||||
(side note: maybe we could use a map to do interface name -> bandwidth
|
||||
for rate limiting)
|
||||
|
||||
|
||||
|
||||
|
||||
a composite rule might be a bunch of denies and then an allow
|
||||
for anything the
|
||||
|
||||
2) some kinda syntax for referencing outputs (or properties) that's not
|
||||
just string interpolation
|
||||
|
||||
|
||||
----
|
||||
|
||||
I think we could address the immediate problem by writing a rule for
|
||||
rate-limiting that looks up the rate in a map, and some maps (with
|
||||
extraText) that get the rates from service properties? And that would
|
||||
suffice for addressing the RoS audit, at least
|
||||
|
||||
Tue Mar 18 18:48:22 GMT 2025
|
||||
|
||||
Unless the interface exists, we do not (at least, may not) know its
|
||||
name because that's an output. So the fact that it has a permanent
|
||||
property is not per se terribly useful
|
||||
|
||||
limit rate 50000 bytes / minute accept
|
||||
|
||||
|
||||
nft add rule ip nat postrouting snat to ip saddr map { 192.168.1.0/24 : 10.0.0.1, 192.168.2.0/24 : 10.0.0.2 }
|
||||
|
||||
nft add rule table-ip input-ip4 ip daddr 2.2.2.3/32 limit rate iifname map { "eth0": 10, "ppp0": 20 } kbytes/second accept
|
||||
|
||||
nft add map 'table-ip intf-limits { typeof 5000000 ; elements = { lan: 50000000, ppp0: 3500000 } ; }'
|
||||
|
||||
OK, we can't do rate lookup in a map because the nftables grammar only
|
||||
supports a numeric literal for limit_rate_bytes. so we're back to writing
|
||||
a collection of rules, one for each interface with an ifname output,
|
||||
that sets the limit for that interface
|
||||
|
||||
* we could do this all in one element of a rules list, with newlines
|
||||
between each actual rule
|
||||
|
||||
* we could add extraText to the ruleset syntax - but does it go at the
|
||||
start of the rules or the end or somewhere in the middle? this is
|
||||
almost worse
|
||||
|
||||
* we could pick up where we left off on march 17 and redesign the
|
||||
firewall module
|
||||
|
||||
gonna be option 1 isn't it?
|
||||
|
||||
Tue Mar 25 00:13:31 GMT 2025
|
||||
|
||||
the logic for watch-outputs is not correct
|
||||
|
||||
(1) when we first read outputs, how do we know if the controlled
|
||||
service is up to date wrt those outputs? we should perform the action
|
||||
for the first time after reading initial state and before waiting
|
||||
|
||||
(2) it isn't writing the new outputs back into trees, so will refresh
|
||||
continually after the fisrt change
|
||||
|
||||
set tree to empty for each service
|
||||
loop:
|
||||
for each service
|
||||
read new tree
|
||||
if different
|
||||
do action
|
||||
set tree = new tree
|
||||
wait for changes
|
||||
next iteration
|
||||
|
||||
Fri Mar 28 23:25:49 GMT 2025
|
||||
|
||||
the ppp connection doesn't come back after a&a reset the lns, this has
|
||||
happened twice now. Possibly-relevant log entries:
|
||||
|
||||
|
||||
2025-03-27 01:10:20.189135502 rotuer dhcp6c.prefix.dhcp6c.wan.link.pppoe.int.link.a.192.168.8.1 Executed "ip address change 2001:8b0:de3a:40de::1/64 dev int valid_lft 5682 preferred_lft 5682", exit code true
|
||||
2025-03-27 01:10:20.189182063 rotuer dhcp6c.addr.dhcp6c.wan.link.pppoe.wan.link.pppoe Executed "ip address change 2001:8b0:1111:1111:0:ffff:d9a9:14de/128 dev ppp0 valid_lft 5682 preferred_lft 2082", exit code true
|
||||
|
||||
# am assuming these are just normal updates from remote dhcp6
|
||||
|
||||
2025-03-27 01:11:19.179528428 rotuer wan.link.pppoe rcvd [LCP TermReq id=0x0]
|
||||
2025-03-27 01:11:19.179542189 rotuer wan.link.pppoe LCP terminated by peer
|
||||
2025-03-27 01:11:19.179545869 rotuer wan.link.pppoe Connect time 54273.0 minutes.
|
||||
2025-03-27 01:11:19.179550109 rotuer wan.link.pppoe Sent 3022278191 bytes, received 2202783935 bytes.
|
||||
2025-03-27 01:11:19.183056872 rotuer wan.link.pppoe sent [LCP TermAck id=0x0]
|
||||
# ppp died
|
||||
2025-03-27 01:11:19.183071352 rotuer dhcp6c.wan.link.pppoe odhcp6c[615]: carrier => 0 event on
|
||||
2025-03-27 01:11:19.183076072 rotuer dhcp6c.wan.link.pppoe odhcp6c[615]: Failed to send RS (Network unreachable)
|
||||
2025-03-27 01:11:19.183081592 rotuer dhcp6c.wan.link.pppoe odhcp6c[615]: Starting RELEASE transaction (timeout 4294967295s, max rc 5)
|
||||
2025-03-27 01:11:19.183086312 rotuer dhcp6c.wan.link.pppoe odhcp6c[615]: Send RELEASE message (elapsed 0ms, rc 0)
|
||||
2025-03-27 01:11:19.183091992 rotuer dhcp6c.wan.link.pppoe odhcp6c[615]: Failed to send RELEASE message to ff02::1:2 (Network unreachable)
|
||||
2025-03-27 01:11:19.183096392 rotuer dhcp6c.wan.link.pppoe odhcp6c[615]: (re)starting transaction on ppp0
|
||||
|
||||
# dhcp6c noticed no ppp so tried to release address. it can't because
|
||||
# ppp is down. how did it know it was time to say byebye?
|
||||
|
||||
2025-03-27 01:11:19.198178337 rotuer int.link.a.192.168.8.1.dnsmasq dnsmasq-dhcp[631]: router advertisement on 2001:8b0:de3a:40de::, old prefix for int
|
||||
2025-03-27 01:11:19.199068668 rotuer dhcp6c.prefix.dhcp6c.wan.link.pppoe.int.link.a.192.168.8.1 Executed "ip address del 2001:8b0:de3a:40de::1/64 dev int", exit code true
|
||||
# our prefix delegation is no longer ours
|
||||
|
||||
2025-03-27 01:11:19.199381632 rotuer dhcp6c.addr.dhcp6c.wan.link.pppoe.wan.link.pppoe ip: RTNETLINK answers: Address not available
|
||||
2025-03-27 01:11:19.199401552 rotuer dhcp6c.addr.dhcp6c.wan.link.pppoe.wan.link.pppoe /nix/store/anz85qjb2qw084f5xhpx4qndp260xr9i-lua-tty-aarch64-unknown-linux-musl/bin/lua: ...ch64-unknown-linux-musl-0.1/share/lua/5.3/anoia/init.lua:93: Error executing "ip address del 2001:8b0:1111:1111:0:ffff:d9a9:14de/128 dev ppp0" (exit)
|
||||
|
||||
this is the ip6 address that ppp negotiated. is this command failing because
|
||||
it was removed already?
|
||||
|
||||
[....]
|
||||
2025-03-27 01:11:25.545493523 rotuer wan.link.pppoe Script /nix/store/9n2314z07z1g9dd3sqr0kndinjv0vma3-ip6-up started (pid 10346)
|
||||
2025-03-27 01:11:25.551376155 rotuer wan.link.pppoe Script /nix/store/9n2314z07z1g9dd3sqr0kndinjv0vma3-ip6-up finished (pid 10346), status = 0x0
|
||||
2025-03-27 01:11:25.552949055 rotuer wan.link.pppoe sh: write error: Broken pipe
|
||||
2025-03-27 01:11:25.553494501 rotuer wan.link.pppoe Script /nix/store/jkvnjhiqjahydmns33z5lni1hf9sl3x8-ip-up finished (pid 10345), status = 0x1
|
||||
2025-03-27 01:11:26.596566491 rotuer wlan0.link.hostapd wlan0: STA-OPMODE-SMPS-MODE-CHANGED 96:75:33:6c:6b:86 static
|
||||
2025-03-27 01:11:26.656620107 rotuer wlan0.link.hostapd wlan0: STA-OPMODE-SMPS-MODE-CHANGED 96:75:33:6c:6b:86 off
|
||||
2025-03-27 01:11:27.189590642 rotuer dhcp6c.wan.link.pppoe odhcp6c[615]: Failed to send RS (Network unreachable)
|
||||
|
||||
it's supposed to send only one notification (each up-script only sends
|
||||
if an output the other has created is there).
|
||||
|
||||
~~maybe we're not wiping the outputs when it terminates?~~ checked, we are.
|
||||
|
||||
|
||||
2025-03-27 01:11:27.279458704 rotuer int.link.a.192.168.8.1.dnsmasq dnsmasq-dhcp[631]: no address range available for DHCPv6 request via int
|
||||
2025-03-27 01:11:27.557594914 rotuer dhcp6c.wan.link.pppoe odhcp6c[615]: Failed to send SOLICIT message to ff02::1:2 (Network unreachable)
|
||||
2025-03-27 01:11:31.193583256 rotuer dhcp6c.wan.link.pppoe odhcp6c[615]: Failed to send RS (Network unreachable)
|
||||
|
||||
Hypothesis
|
||||
|
||||
1) when the remote end bounces ppp, s6 restarts the ppp process
|
||||
but this doesn't cause dependent services to notice, so odhcpc,
|
||||
which is bound to the interface, doesn't see that we now have a
|
||||
new interface but with the same name as the old one
|
||||
|
||||
and/or
|
||||
|
||||
2) there is a race between acquire-*.fnl starting and the
|
||||
prefix/address outputs being updated so that we miss the first
|
||||
value change.
|
||||
|
||||
Tue Apr 1 21:29:03 BST 2025
|
||||
|
||||
what to do with the web site?
|
||||
|
||||
* we could add the "community" and "development" pages to the manual,
|
||||
but we probably don't want the blog entries to be in the liminix git
|
||||
repo because workflow
|
||||
|
||||
* if we do that, the ->html conversion in the manual needs to have
|
||||
better nav
|
||||
|
||||
links we want on the homepage
|
||||
|
||||
"I am new to Liminix and want to find if it's for me"
|
||||
- introduction/tutorial (how do i use it)
|
||||
- hardware list (is my device supported)
|
||||
- which includes how to install the system
|
||||
|
||||
"I am ready to run, and need a reference for stuff"
|
||||
[this is a merge of the existing config and admin sections]
|
||||
- what is a module, what is a service
|
||||
- how to do secrets
|
||||
- list of service and module options
|
||||
- using a running system: services and logs
|
||||
(move controlled services stuff up a level, its not runtime)
|
||||
- updating/changing the configuration
|
||||
writable fs: use outputs.updater
|
||||
readonly fs: use levitate
|
||||
|
||||
"I want to stay in touch/find out what's new"
|
||||
- blog
|
||||
- mailing lists
|
||||
- irc
|
||||
- CoC
|
||||
|
||||
"I want to contribute"
|
||||
Developer manual, plus
|
||||
- mailing lists
|
||||
- git repo
|
||||
- how to send patches
|
||||
- also transplant "module implementation" here
|
||||
- porting
|
||||
- how to bring up
|
||||
- what we look for in a new device patch (kconfig style
|
||||
|
||||
|
||||
perhaps we can get rid of generic "installation" in the manual
|
||||
and have each method transcluded into the hardware device descriptions
|
||||
|
||||
|
||||
|
||||
see also:
|
||||
1) "Continuous updates" is misleading as it gives the impression
|
||||
downstreams would be restarted if an upstream restarts. Not true.
|
||||
We should describe secrets subscriber here
|
||||
|
||||
|
||||
-----
|
||||
|
||||
leaving us with these titles remaining
|
||||
|
||||
* tutorial
|
||||
* hardware
|
||||
* reference (could just call it "manual")
|
||||
* development
|
||||
* blog
|
||||
|
||||
and these which get moved
|
||||
|
||||
* configuration/admin -> tutorial
|
||||
* Module options -> sublevel in Reference
|
||||
* outputs -> transcluded in hardware
|
||||
(might also add as an appendix, so that porters can see all in one place)
|
||||
|
||||
maybe also footer link for
|
||||
- CoC
|
||||
- privacy policy
|
||||
- ?
|
||||
|
||||
source for all of these except blog and landing page is the doc/ folder
|
||||
|
||||
landing page will have some big rectangles that essentially duplicate
|
||||
the menu items
|
||||
|
||||
----
|
||||
|
||||
to get there from here:
|
||||
|
||||
1) we need to know the heading titles for a ToC to use as drop-down
|
||||
menus in the template, so will have to pass twice over the documents.
|
||||
Note that the same template is used for each doc so we can't expect the
|
||||
titles of the other docs to be provided by pandoc as variables
|
||||
|
||||
(but maybe we could pass them in on the command line so as not to
|
||||
rewrite template.json?)
|
||||
|
||||
note that we also need to use the template with the blog files,
|
||||
although for those we also need title and date frontmatter
|
||||
|
||||
if the blog sources the template at build time to make pages, it's
|
||||
always going to be out of sync with the manual.
|
||||
|
||||
would javascript be acceptable?
|
||||
|
||||
should we just monorepo the two things together? we could run jekyll as
|
||||
part of the doc target, or even pandoc the markdown directly
|
||||
|
||||
apparently myhtic.telent.net is running an rsync server?
|
||||
|
||||
1) it needs doc/ removing from its path
|
||||
path = "/var/www/blogs/www.liminix.org/_site/doc";
|
||||
in hosts/myhtic.nix
|
||||
(and moving to rmeote)
|
||||
|
||||
2) make install should add doc to the path, for the manual,
|
||||
and not for the blog entries which should be in yyyy/mm/dd folders
|
||||
|
||||
Sun Apr 6 17:48:33 BST 2025
|
||||
|
||||
This is all getting quite involved and I'm not convinced it's the
|
||||
simplest way forward
|
||||
|
||||
- convert the manual to asciidoctor and make it have a sidebar
|
||||
- use --embedded flag to splice in header/footer
|
||||
|
||||
- the landing page can have big display-sized links to different
|
||||
manual sections and to the blog
|
||||
|
||||
- get rid of the blog content that's not actually posts, merge it into
|
||||
the manual
|
||||
|
||||
- the blog also needs the header/footer spliced in
|
||||
|
||||
site nav has three options: "home", "news", "manual"
|
||||
- unless we want to put the manual deeplinks from the homepage in
|
||||
there as well
|
||||
- if we do that, need to indicate somehow that they're part of the
|
||||
manual, not distinct from it
|
||||
|
||||
do we need to move urls around? maybe not so much
|
||||
|
||||
what to do about the "hardware" page?
|
||||
|
||||
- do we really expect the reader to scroll through every device
|
||||
that's not theirs?
|
||||
- there's going to be a lot of repeated text for similar devices
|
||||
- "generic" instructions might go in the body of the manual with
|
||||
devices as an appendix. However, you have to read the relevant
|
||||
appendix section to make sense of the generic instructions anyway.
|
||||
If only we had hypertext
|
||||
|
||||
|
||||
Mon Apr 7 23:49:40 BST 2025
|
||||
|
||||
DOC TODO
|
||||
|
||||
[done] * need to explain the mailing lists and IRC somewhere
|
||||
|
||||
[done] * markup the devices section as an appendix.
|
||||
|
||||
[done] * docs for outputs? maybe appendix ii
|
||||
|
||||
[done] * move "module implementation" from admin to dev (maybe?)
|
||||
|
||||
[done] * CI: move from website to Development
|
||||
|
||||
[done] * move info on the Community page to Introduction
|
||||
|
||||
[halfarsed done] * write doc for porting to new hw
|
||||
- how to bring up
|
||||
- what we look for in a new device patch (kconfig style etc)
|
||||
|
||||
[done] * add rsyncy thing to rmeote
|
||||
|
||||
* we still have serial connection guff in both installation and
|
||||
development sections
|
||||
|
||||
* convert all the embedded rst to adoc
|
||||
|
||||
* remove pages that are not posts from jekyll blog
|
||||
|
||||
* landing page
|
||||
|
||||
* "Continuous updates" is misleading as it gives the impression
|
||||
downstreams would be restarted if an upstream restarts. Not true.
|
||||
We should describe secrets subscriber here
|
||||
|
||||
* some basic css to make it not the same as (I assume) every single
|
||||
other asciidoc site
|
||||
|
||||
+31
-9
@@ -2,19 +2,41 @@
|
||||
# use expect as its "Script program" instead of runscript. Try
|
||||
# Ctrl+A O -> Filenames and paths -> D
|
||||
|
||||
fconfigure stderr -buffering none
|
||||
fconfigure stdout -buffering none
|
||||
|
||||
proc waitprompt { } {
|
||||
expect {
|
||||
"BusyBox" { puts stderr "DONE\r"; exit 0 }
|
||||
"READY" { puts stderr ";;; READY\r"; }
|
||||
timeout { puts stderr ";;; timed out waiting after $line\r" }
|
||||
}
|
||||
}
|
||||
|
||||
proc sendline { line } {
|
||||
send "$line; echo \$ready \r"
|
||||
sleep 0.1
|
||||
}
|
||||
|
||||
log_user 0
|
||||
log_file -a -open stderr
|
||||
|
||||
set f [open "result/boot.scr"]
|
||||
send "version\r"
|
||||
set timeout 60
|
||||
|
||||
send "setenv ready REA\r"
|
||||
sleep 0.1
|
||||
send "setenv ready \${ready}DY\r"
|
||||
sleep 0.1
|
||||
|
||||
set timeout 300
|
||||
expect_before timeout abort
|
||||
while {[gets $f line] >= 0} {
|
||||
puts stderr "next line $line\r"
|
||||
puts stderr "waiting for prompt\r"
|
||||
expect {
|
||||
"ath>" {}
|
||||
"BusyBox" { puts stderr "DONE"; exit 0 }
|
||||
}
|
||||
send "$line\r\n"
|
||||
puts stderr ";;; next line $line\r"
|
||||
puts stderr ";;; waiting for prompt\r"
|
||||
puts stderr ";;; sending\r"
|
||||
sendline $line
|
||||
waitprompt
|
||||
}
|
||||
|
||||
puts stderr "done\r\n"
|
||||
close $f
|
||||
+46
-19
@@ -1,8 +1,21 @@
|
||||
{ config, pkgs, lib, ... }:
|
||||
{
|
||||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
cfg = config.bordervm;
|
||||
inherit (lib) mkOption mkEnableOption mdDoc types optional optionals;
|
||||
in {
|
||||
inherit (lib)
|
||||
mkOption
|
||||
mkEnableOption
|
||||
mdDoc
|
||||
types
|
||||
optional
|
||||
optionals
|
||||
;
|
||||
in
|
||||
{
|
||||
options.bordervm = {
|
||||
keys = mkOption {
|
||||
type = types.listOf types.str;
|
||||
@@ -107,13 +120,14 @@ in {
|
||||
from = "host";
|
||||
host.port = 7654;
|
||||
# guest.address = "10.0.2.15";
|
||||
guest.port =7654;
|
||||
guest.port = 7654;
|
||||
}
|
||||
{
|
||||
host.port = 2222;
|
||||
guest.address = "10.0.2.15";
|
||||
guest.port = 22;
|
||||
}];
|
||||
}
|
||||
];
|
||||
qemu = {
|
||||
networkingOptions = [ ];
|
||||
options =
|
||||
@@ -139,28 +153,41 @@ in {
|
||||
|
||||
services.tang = {
|
||||
enable = true;
|
||||
ipAddressAllow = [ "10.0.0.0/24" "0.0.0.0/0" ];
|
||||
ipAddressAllow = [
|
||||
"10.0.0.0/24"
|
||||
"0.0.0.0/0"
|
||||
];
|
||||
};
|
||||
|
||||
environment.systemPackages =
|
||||
let wireshark-nogui = pkgs.wireshark.override { withQt = false ; };
|
||||
in with pkgs; [
|
||||
tcpdump
|
||||
wireshark-nogui
|
||||
socat
|
||||
tufted
|
||||
iptables
|
||||
usbutils
|
||||
busybox
|
||||
clevis
|
||||
];
|
||||
let
|
||||
wireshark-nogui = pkgs.wireshark.override { withQt = false; };
|
||||
in
|
||||
with pkgs;
|
||||
[
|
||||
tcpdump
|
||||
wireshark-nogui
|
||||
socat
|
||||
tufted
|
||||
iptables
|
||||
usbutils
|
||||
busybox
|
||||
clevis
|
||||
];
|
||||
security.sudo.wheelNeedsPassword = false;
|
||||
networking = {
|
||||
hostName = "border";
|
||||
firewall = { enable = false; };
|
||||
firewall = {
|
||||
enable = false;
|
||||
};
|
||||
interfaces.eth1 = {
|
||||
useDHCP = false;
|
||||
ipv4.addresses = [ { address = "10.0.0.1"; prefixLength = 24;}];
|
||||
ipv4.addresses = [
|
||||
{
|
||||
address = "10.0.0.1";
|
||||
prefixLength = 24;
|
||||
}
|
||||
];
|
||||
};
|
||||
nat = {
|
||||
enable = true;
|
||||
|
||||
@@ -11,11 +11,14 @@ let
|
||||
"qemu-aarch64"
|
||||
"qemu-armv7l"
|
||||
"tp-archer-ax23"
|
||||
"openwrt-one"
|
||||
"zyxel-nwa50ax"
|
||||
"turris-omnia"
|
||||
"belkin-rt3200"
|
||||
];
|
||||
vanilla = ./vanilla-configuration.nix;
|
||||
for-device = name:
|
||||
for-device =
|
||||
name:
|
||||
(import liminix {
|
||||
inherit borderVmConf;
|
||||
device = import (liminix + "/devices/${name}");
|
||||
@@ -32,49 +35,13 @@ let
|
||||
device = import (liminix + "/devices/qemu");
|
||||
liminix-config = vanilla;
|
||||
}).buildEnv;
|
||||
doc =
|
||||
let
|
||||
json =
|
||||
(import liminix {
|
||||
inherit borderVmConf;
|
||||
device = import (liminix + "/devices/qemu");
|
||||
liminix-config =
|
||||
{ ... }:
|
||||
{
|
||||
imports = [ ./modules/all-modules.nix ];
|
||||
};
|
||||
}).outputs.optionsJson;
|
||||
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.inc.rst
|
||||
cat ${json} | fennel --correlate doc/parse-options-outputs.fnl > doc/outputs-generated.inc.rst
|
||||
cp ${(import ./doc/hardware.nix)} doc/hardware.rst
|
||||
make -C doc html
|
||||
'';
|
||||
installPhase = ''
|
||||
mkdir -p $out/nix-support $out/share/doc/
|
||||
cd doc
|
||||
cp *-generated.inc.rst hardware.rst $out
|
||||
ln -s ${json} $out/options.json
|
||||
cp -a _build/html $out/share/doc/liminix
|
||||
echo "file source-dist \"$out/share/doc/liminix\"" \
|
||||
> $out/nix-support/hydra-build-products
|
||||
'';
|
||||
};
|
||||
doc = pkgs.callPackage ./doc.nix { inherit liminix borderVmConf; } ;
|
||||
};
|
||||
in jobs //
|
||||
{
|
||||
all = pkgs.mkShell {
|
||||
name = "all tests";
|
||||
contents = pkgs.lib.collect pkgs.lib.isDerivation jobs;
|
||||
};
|
||||
}
|
||||
in
|
||||
jobs
|
||||
// {
|
||||
all = pkgs.mkShell {
|
||||
name = "all tests";
|
||||
contents = pkgs.lib.collect pkgs.lib.isDerivation jobs;
|
||||
};
|
||||
}
|
||||
|
||||
+32
-22
@@ -27,12 +27,18 @@ let
|
||||
modulesPath = builtins.toString ./modules;
|
||||
};
|
||||
modules = [
|
||||
{ _module.args = { inherit pkgs; inherit (pkgs) lim; }; }
|
||||
{
|
||||
_module.args = {
|
||||
inherit pkgs;
|
||||
inherit (pkgs) lim;
|
||||
};
|
||||
}
|
||||
./modules/hardware.nix
|
||||
./modules/base.nix
|
||||
./modules/busybox.nix
|
||||
./modules/hostname.nix
|
||||
./modules/kernel
|
||||
./modules/logging.nix
|
||||
./modules/klogd.nix
|
||||
device.module
|
||||
liminix-config
|
||||
@@ -46,30 +52,34 @@ let
|
||||
};
|
||||
config = eval.config;
|
||||
|
||||
borderVm = ((import <nixpkgs/nixos/lib/eval-config.nix>) {
|
||||
system = builtins.currentSystem;
|
||||
modules = [
|
||||
{
|
||||
nixpkgs.overlays = [
|
||||
(final: prev: {
|
||||
go-l2tp = final.callPackage ./pkgs/go-l2tp {};
|
||||
tufted = final.callPackage ./pkgs/tufted {};
|
||||
})
|
||||
];
|
||||
}
|
||||
(import ./bordervm-configuration.nix)
|
||||
borderVmConf
|
||||
];
|
||||
}).config.system;
|
||||
in {
|
||||
borderVm =
|
||||
((import <nixpkgs/nixos/lib/eval-config.nix>) {
|
||||
system = builtins.currentSystem;
|
||||
modules = [
|
||||
{
|
||||
nixpkgs.overlays = [
|
||||
(final: prev: {
|
||||
go-l2tp = final.callPackage ./pkgs/go-l2tp { };
|
||||
tufted = final.callPackage ./pkgs/tufted { };
|
||||
})
|
||||
];
|
||||
}
|
||||
(import ./bordervm-configuration.nix)
|
||||
borderVmConf
|
||||
];
|
||||
}).config.system;
|
||||
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);
|
||||
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
|
||||
|
||||
+263
-187
@@ -4,13 +4,8 @@
|
||||
******************************
|
||||
|
||||
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: use the `OpenWrt
|
||||
UBI Installer <https://github.com/dangowrt/owrt-ubi-installer>`_ to
|
||||
rewrite the partition layout before you can use
|
||||
Liminix with it
|
||||
and is mostly feature-complete in Liminix but as of Dec 2024
|
||||
has seen very little actual use.
|
||||
|
||||
Hardware summary
|
||||
================
|
||||
@@ -25,7 +20,11 @@
|
||||
Installation
|
||||
============
|
||||
|
||||
Installation is currently a manual process.
|
||||
Liminix on this device uses the UBI volume management system to
|
||||
perform wear leveling on the flash. This is not set up from the
|
||||
factory, so a one-time step is needed to prepare it before Liminix
|
||||
can be installed.
|
||||
|
||||
|
||||
Preparation
|
||||
-----------
|
||||
@@ -33,9 +32,16 @@
|
||||
To prepare the device for Liminix you first need to use the
|
||||
`OpenWrt UBI Installer
|
||||
<https://github.com/dangowrt/owrt-ubi-installer>`_ image to
|
||||
rewrite the flash layout. You can do this in onw of two ways:
|
||||
rewrite the flash layout. As of Jan 2025 there are two versions
|
||||
of the installer available: the release version 1.0.2 and the
|
||||
pre-release 1.1.3 and for Liminix you nee the pre-relese.
|
||||
The release version of the installer creates UBI volumes
|
||||
according to an older layout that is not compatible with
|
||||
the Linux 6.6.67 kernel used in Liminix.
|
||||
|
||||
You can run the installer in one of two ways:
|
||||
either follow the instructions to do it through the vendor web
|
||||
interface, or you can drop to U-boot and use TFTP
|
||||
interface, or you can drop to U-Boot and use TFTP
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
@@ -44,16 +50,64 @@
|
||||
MT7622> tftpboot 0x42000000 openwrt-mediatek-mt7622-linksys_e8450-ubi-initramfs-recovery-installer.itb
|
||||
MT7622> bootm 0x42000000
|
||||
|
||||
This will write the new flash layout and then boot into a
|
||||
"recovery" OpenWrt installation. Once it's finished booting into
|
||||
Linux you can safely reboot
|
||||
This will write the new flash layout and then boot into a
|
||||
"recovery" OpenWrt installation.
|
||||
|
||||
Installing Liminix
|
||||
------------------
|
||||
Building/installing Liminix
|
||||
----------------
|
||||
|
||||
This is a manual process: you need a :ref:`serial <serial>` conection and TFTP : follow the instructions at :ref:`system-outputs-ubimage`
|
||||
The default target for this device is ``outputs.ubimage`` which
|
||||
makes a ubifs image suitable for use with :command:`ubiupdatevol`.
|
||||
To write this to the device we use the OpenWrt recovery system
|
||||
installed in the previous step. In this configuration the
|
||||
device assigns itself the IP address 192.168.1.1/24 on its LAN
|
||||
ports and expects the connected computer to have 192.168.1.254
|
||||
|
||||
'';
|
||||
.. warning:: The `ubi0_7` device in these instructions is correct
|
||||
as of Dec 2024 (dangowrt/owrt-ubi-installer commit
|
||||
d79e7928). If you are installing some time later, it
|
||||
is important to check the output from
|
||||
:command:`ubinfo -a` and make sure you are updating
|
||||
the "liminix" volume and not some other one which had
|
||||
been introduced since I wrote this.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ nix-build -I liminix-config=./my-configuration.nix --arg device "import ./devices/belkin-rt3200" -A outputs.default
|
||||
$ cat result/rootfs | ssh root@192.168.1.1 "cat > /tmp/rootfs"
|
||||
$ ssh root@192.168.1.1
|
||||
root@OpenWrt:~# ubimkvol /dev/ubi0 --name=liminix --maxavsize
|
||||
root@OpenWrt:~# ubinfo -a
|
||||
[...]
|
||||
Volume ID: 7 (on ubi0)
|
||||
Type: dynamic
|
||||
Alignment: 1
|
||||
Size: 851 LEBs (108056576 bytes, 103.0 MiB)
|
||||
State: OK
|
||||
Name: liminix
|
||||
Character device major/minor: 250:8
|
||||
root@OpenWrt:~# ubiupdatevol /dev/ubi0_7 /tmp/rootfs
|
||||
|
||||
To make the new system bootable we also need to change some U-Boot variables.
|
||||
``boot_production`` needs to mount the filesystem and boot the FIT image
|
||||
found there, and :code:`bootcmd` needs to be told _not_ to boot the rescue
|
||||
image if there are records in pstore, because that interferes with
|
||||
``config.log.persistent``
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
root@OpenWrt:~# fw_setenv orig_boot_production $(fw_printenv -n boot_production)
|
||||
root@OpenWrt:~# fw_setenv orig_bootcmd $(fw_printenv -n bootcmd)
|
||||
root@OpenWrt:~# fw_setenv boot_production 'led $bootled_pwr on ; ubifsmount ubi0:liminix && ubifsload ''${loadaddr} boot/fit && bootm ''${loadaddr}'
|
||||
root@OpenWrt:~# fw_setenv bootcmd 'run boot_ubi'
|
||||
|
||||
For subsequent Liminix reinstalls, just run the
|
||||
:command:`ubiupdatevol` command again. You don't need to repeat
|
||||
the "Preparation" step and in fact should seek to avoid it if
|
||||
possible, as it will reset the erase counters used for write
|
||||
levelling. Using UBI-aware tools is therefore preferred over any
|
||||
kind of "factory" wipe which will reset them.
|
||||
'';
|
||||
|
||||
system = {
|
||||
crossSystem = {
|
||||
@@ -61,196 +115,218 @@
|
||||
};
|
||||
};
|
||||
|
||||
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 {
|
||||
module =
|
||||
{
|
||||
pkgs,
|
||||
config,
|
||||
lib,
|
||||
lim,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (lib) mkIf;
|
||||
firmware = pkgs.stdenv.mkDerivation {
|
||||
name = "wlan-firmware";
|
||||
phases = [ "installPhase" ];
|
||||
installPhase = ''
|
||||
mkdir $out
|
||||
cp ${pkgs.linux-firmware}/lib/firmware/mediatek/{mt7915,mt7615,mt7622}* $out
|
||||
'';
|
||||
};
|
||||
openwrt = pkgs.openwrt_24_10;
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
../../modules/arch/aarch64.nix
|
||||
../../modules/outputs/tftpboot.nix
|
||||
../../modules/outputs/ubifs.nix
|
||||
../../modules/outputs/ubimage.nix
|
||||
];
|
||||
config = {
|
||||
kernel = {
|
||||
extraPatchPhase = ''
|
||||
${pkgs.openwrt.applyPatches.mediatek}
|
||||
'';
|
||||
config = {
|
||||
PCI = "y";
|
||||
ARCH_MEDIATEK = "y";
|
||||
# ARM_MEDIATEK_CPUFREQ = "y";
|
||||
kernel = {
|
||||
extraPatchPhase = ''
|
||||
${openwrt.applyPatches.mediatek}
|
||||
'';
|
||||
src = openwrt.kernelSrc;
|
||||
version = openwrt.kernelVersion;
|
||||
config = {
|
||||
PCI = "y";
|
||||
ARCH_MEDIATEK = "y";
|
||||
# ARM_MEDIATEK_CPUFREQ = "y";
|
||||
|
||||
# needed for "Cannot find regmap for /infracfg@10000000"
|
||||
MFD_SYSCON = "y";
|
||||
MTK_INFRACFG = "y";
|
||||
# needed for "Cannot find regmap for /infracfg@10000000"
|
||||
MFD_SYSCON = "y";
|
||||
MTK_INFRACFG = "y";
|
||||
|
||||
MTK_PMIC_WRAP = "y";
|
||||
NVMEM_MTK_EFUSE="y";
|
||||
# MTK_HSDMA="y";
|
||||
MTK_SCPSYS="y";
|
||||
MTK_SCPSYS_PM_DOMAINS="y";
|
||||
# MTK_THERMAL="y";
|
||||
MTK_TIMER="y";
|
||||
MTK_PMIC_WRAP = "y";
|
||||
DMADEVICES = "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";
|
||||
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";
|
||||
REGMAP_MMIO = "y";
|
||||
CLKSRC_MMIO = "y";
|
||||
REGMAP = "y";
|
||||
|
||||
MEDIATEK_GE_PHY = "y";
|
||||
# MEDIATEK_MT6577_AUXADC = "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";
|
||||
MEDIATEK_GE_PHY = "y";
|
||||
# MEDIATEK_MT6577_AUXADC = "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
|
||||
BLOCK = "y"; # move this to base option
|
||||
|
||||
SPI_MASTER = "y";
|
||||
SPI = "y";
|
||||
SPI_MEM="y";
|
||||
SPI_MTK_NOR="y";
|
||||
SPI_MTK_SNFI = "y";
|
||||
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";
|
||||
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";
|
||||
|
||||
MTD_UBI_NVMEM = "y";
|
||||
NVMEM_MTK_EFUSE = "y";
|
||||
NVMEM_BLOCK = "y";
|
||||
NVMEM_LAYOUT_ADTRAN = "y";
|
||||
|
||||
MMC = "y";
|
||||
MMC_BLOCK = "y";
|
||||
MMC_CQHCI = "y";
|
||||
MMC_MTK = "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";
|
||||
# Distributed Switch Architecture is needed
|
||||
# to make the ethernet ports visible
|
||||
NET_DSA = "y";
|
||||
NET_DSA_MT7530 = "y";
|
||||
NET_DSA_TAG_MTK = "y";
|
||||
NET_DSA_MT7530_MDIO = "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";
|
||||
|
||||
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";
|
||||
logicalEraseBlockSize = "126976";
|
||||
physicalEraseBlockSize = "131072";
|
||||
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";
|
||||
includePaths = [
|
||||
"${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;
|
||||
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 ];
|
||||
# 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 "0x48000000";
|
||||
imageFormat = "fit";
|
||||
loader.fit.enable = lib.mkDefault true; # override this if you are building tftpboot
|
||||
};
|
||||
rootfsType = lib.mkDefault "ubifs"; # override this if you are building tftpboot
|
||||
filesystem =
|
||||
let
|
||||
inherit (pkgs.pseudofile) dir symlink;
|
||||
in
|
||||
dir {
|
||||
lib = dir {
|
||||
firmware = dir {
|
||||
mediatek = symlink firmware;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
hardware =
|
||||
let
|
||||
mac80211 = pkgs.kmodloader.override {
|
||||
targets = [
|
||||
"mt7615e"
|
||||
"mt7915e"
|
||||
];
|
||||
inherit (config.system.outputs) kernel;
|
||||
};
|
||||
in
|
||||
{
|
||||
ubi = {
|
||||
minIOSize = "2048";
|
||||
logicalEraseBlockSize = "126976";
|
||||
physicalEraseBlockSize = "131072";
|
||||
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";
|
||||
includePaths = [
|
||||
"${openwrt.src}/target/linux/mediatek/dts"
|
||||
"${config.system.outputs.kernel.modulesupport}/arch/arm64/boot/dts/mediatek/"
|
||||
];
|
||||
includes = mkIf config.logging.persistent.enable [
|
||||
./pstore-pmsg.dtsi
|
||||
];
|
||||
};
|
||||
|
||||
# - 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;
|
||||
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 ];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
/ {
|
||||
reserved-memory {
|
||||
/* make sure address matches upstream */
|
||||
ramoops@42ff0000 {
|
||||
pmsg-size = <0x10000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -19,31 +19,34 @@
|
||||
VIRTIO_NET = "y";
|
||||
};
|
||||
conditionalConfig = {
|
||||
WLAN= {
|
||||
WLAN = {
|
||||
MAC80211_HWSIM = "m";
|
||||
};
|
||||
};
|
||||
};
|
||||
hardware =
|
||||
let
|
||||
mac80211 = pkgs.kmodloader.override {
|
||||
mac80211 = pkgs.kmodloader.override {
|
||||
inherit (config.system.outputs) kernel;
|
||||
targets = ["mac80211_hwsim"];
|
||||
targets = [ "mac80211_hwsim" ];
|
||||
};
|
||||
in {
|
||||
in
|
||||
{
|
||||
defaultOutput = "vmroot";
|
||||
rootDevice = "/dev/mtdblock0";
|
||||
dts.src = pkgs.lib.mkDefault null;
|
||||
flash.eraseBlockSize = 65536;
|
||||
networkInterfaces =
|
||||
let inherit (config.system.service.network) link;
|
||||
in {
|
||||
let
|
||||
inherit (config.system.service.network) link;
|
||||
in
|
||||
{
|
||||
wan = link.build {
|
||||
devpath = "/devices/pci0000:00/0000:00:13.0/virtio0";
|
||||
devpath = "/devices/pci0000:00/0000:00:12.0/virtio0";
|
||||
ifname = "wan";
|
||||
};
|
||||
lan = link.build {
|
||||
devpath = "/devices/pci0000:00/0000:00:14.0/virtio1";
|
||||
devpath = "/devices/pci0000:00/0000:00:13.0/virtio1";
|
||||
ifname = "lan";
|
||||
};
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
config = "mips-unknown-linux-musl";
|
||||
gcc = {
|
||||
abi = "32";
|
||||
arch = "24kc"; # maybe mips_24kc-
|
||||
arch = "24kc"; # maybe mips_24kc-
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -41,10 +41,11 @@
|
||||
: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.
|
||||
Flashing over an existing Liminix system is not possible while
|
||||
that system is running, otherwise you'll be overwriting flash
|
||||
partitions while they're in use - and that might not end well.
|
||||
Configure the system with :ref:`levitate` if you need to
|
||||
make it upgradable.
|
||||
|
||||
Vendor web page: https://www.gl-inet.com/products/gl-ar750/
|
||||
|
||||
@@ -52,7 +53,14 @@
|
||||
|
||||
'';
|
||||
|
||||
module = {pkgs, config, lim, lib, ... }:
|
||||
module =
|
||||
{
|
||||
pkgs,
|
||||
config,
|
||||
lim,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (lib) mkIf;
|
||||
openwrt = pkgs.openwrt;
|
||||
@@ -64,7 +72,7 @@
|
||||
};
|
||||
firmware = pkgs.stdenv.mkDerivation {
|
||||
name = "wlan-firmware";
|
||||
phases = ["installPhase"];
|
||||
phases = [ "installPhase" ];
|
||||
installPhase = ''
|
||||
mkdir -p $out/ath10k/QCA9887/hw1.0/
|
||||
blobdir=${firmwareBlobs}/QCA9887/hw1.0
|
||||
@@ -73,7 +81,10 @@
|
||||
'';
|
||||
};
|
||||
mac80211 = pkgs.kmodloader.override {
|
||||
targets = ["ath9k" "ath10k_pci"];
|
||||
targets = [
|
||||
"ath9k"
|
||||
"ath10k_pci"
|
||||
];
|
||||
inherit (config.system.outputs) kernel;
|
||||
dependencies = [ ath10k_cal_data ];
|
||||
};
|
||||
@@ -81,7 +92,8 @@
|
||||
let
|
||||
offset = lim.parseInt "0x5000";
|
||||
size = lim.parseInt "0x844";
|
||||
in pkgs.liminix.services.oneshot rec {
|
||||
in
|
||||
pkgs.liminix.services.oneshot rec {
|
||||
name = "ath10k_cal_data";
|
||||
up = ''
|
||||
part=$(basename $(dirname $(grep -l art /sys/class/mtd/*/name)))
|
||||
@@ -90,10 +102,11 @@
|
||||
(in_outputs ${name}
|
||||
dd if=/dev/$part of=data iflag=skip_bytes,fullblock bs=${toString size} skip=${toString offset} count=1
|
||||
)
|
||||
'';
|
||||
};
|
||||
'';
|
||||
};
|
||||
inherit (pkgs.pseudofile) dir symlink;
|
||||
in {
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
../../modules/network
|
||||
../../modules/arch/mipseb.nix
|
||||
@@ -117,7 +130,7 @@
|
||||
rootDevice = "/dev/mtdblock5";
|
||||
dts = {
|
||||
src = "${openwrt.src}/target/linux/ath79/dts/qca9531_glinet_gl-ar750.dts";
|
||||
includePaths = [
|
||||
includePaths = [
|
||||
"${openwrt.src}/target/linux/ath79/dts"
|
||||
];
|
||||
includes = mkIf config.logging.persistent.enable [
|
||||
@@ -126,8 +139,10 @@
|
||||
};
|
||||
|
||||
networkInterfaces =
|
||||
let inherit (config.system.service.network) link;
|
||||
in {
|
||||
let
|
||||
inherit (config.system.service.network) link;
|
||||
in
|
||||
{
|
||||
lan = link.build {
|
||||
ifname = "lan";
|
||||
devpath = "/devices/platform/ahb/1a000000.eth";
|
||||
@@ -142,7 +157,10 @@
|
||||
};
|
||||
wlan5 = link.build {
|
||||
ifname = "wlan1";
|
||||
dependencies = [ ath10k_cal_data mac80211 ];
|
||||
dependencies = [
|
||||
ath10k_cal_data
|
||||
mac80211
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -192,31 +210,31 @@
|
||||
NET = "y";
|
||||
ETHERNET = "y";
|
||||
NET_VENDOR_ATHEROS = "y";
|
||||
AG71XX = "y"; # ethernet (qca,qca9530-eth)
|
||||
MFD_SYSCON = "y"; # ethernet (compatible "syscon")
|
||||
AR8216_PHY = "y"; # eth1 is behind a switch
|
||||
AG71XX = "y"; # ethernet (qca,qca9530-eth)
|
||||
MFD_SYSCON = "y"; # ethernet (compatible "syscon")
|
||||
AR8216_PHY = "y"; # eth1 is behind a switch
|
||||
|
||||
MTD_SPI_NOR = "y";
|
||||
|
||||
SPI_ATH79 = "y"; # these are copied from OpenWrt.
|
||||
SPI_MASTER= "y"; # At least one of them is necessary
|
||||
SPI_MEM= "y";
|
||||
SPI_AR934X= "y";
|
||||
SPI_BITBANG= "y";
|
||||
SPI_GPIO= "y";
|
||||
SPI_ATH79 = "y"; # these are copied from OpenWrt.
|
||||
SPI_MASTER = "y"; # At least one of them is necessary
|
||||
SPI_MEM = "y";
|
||||
SPI_AR934X = "y";
|
||||
SPI_BITBANG = "y";
|
||||
SPI_GPIO = "y";
|
||||
|
||||
GPIO_ATH79 = "y";
|
||||
GPIOLIB = "y";
|
||||
EXPERT="y";
|
||||
EXPERT = "y";
|
||||
GPIO_SYSFS = "y"; # required by patches-5.15/0004-phy-add-ath79-usb-phys.patch
|
||||
OF_GPIO = "y";
|
||||
SYSFS = "y";
|
||||
SPI = "y";
|
||||
MTD = "y";
|
||||
MTD_BLOCK = "y"; # fix undefined ref to register_mtd_blktrans_devs
|
||||
MTD_BLOCK = "y"; # fix undefined ref to register_mtd_blktrans_devs
|
||||
|
||||
WATCHDOG = "y";
|
||||
ATH79_WDT = "y"; # watchdog timer
|
||||
ATH79_WDT = "y"; # watchdog timer
|
||||
|
||||
EARLY_PRINTK = "y";
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
config = "mipsel-unknown-linux-musl";
|
||||
gcc = {
|
||||
abi = "32";
|
||||
arch = "mips32"; # maybe mips_24kc-
|
||||
arch = "mips32"; # maybe mips_24kc-
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -32,10 +32,11 @@
|
||||
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.
|
||||
Flashing over an existing Liminix system is not possible while
|
||||
that system is running, otherwise you'll be overwriting flash
|
||||
partitions while they're in use - and that might not end well.
|
||||
Configure the system with :ref:`levitate` if you need to
|
||||
make it upgradable.
|
||||
|
||||
Vendor web page: https://www.gl-inet.com/products/gl-mt300a/
|
||||
|
||||
@@ -43,14 +44,22 @@
|
||||
|
||||
'';
|
||||
|
||||
module = { pkgs, config, lib, lim, ...}:
|
||||
module =
|
||||
{
|
||||
pkgs,
|
||||
config,
|
||||
lib,
|
||||
lim,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (pkgs) openwrt;
|
||||
mac80211 = pkgs.kmodloader.override {
|
||||
targets = ["rt2800soc"];
|
||||
targets = [ "rt2800soc" ];
|
||||
inherit (config.system.outputs) kernel;
|
||||
};
|
||||
in {
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
../../modules/arch/mipsel.nix
|
||||
../../modules/outputs/tftpboot.nix
|
||||
@@ -89,7 +98,8 @@
|
||||
let
|
||||
inherit (config.system.service.network) link;
|
||||
inherit (config.system.service) vlan;
|
||||
in rec {
|
||||
in
|
||||
rec {
|
||||
eth = link.build { ifname = "eth0"; };
|
||||
# lan and wan ports are both behind a switch on eth0
|
||||
lan = vlan.build {
|
||||
@@ -111,54 +121,56 @@
|
||||
boot.tftp = {
|
||||
loadAddress = lim.parseInt "0x00A00000";
|
||||
appendDTB = true;
|
||||
};
|
||||
};
|
||||
|
||||
kernel = {
|
||||
extraPatchPhase = ''
|
||||
${openwrt.applyPatches.ramips}
|
||||
${openwrt.applyPatches.rt2x00}
|
||||
'';
|
||||
config = {
|
||||
config =
|
||||
{
|
||||
|
||||
RALINK = "y";
|
||||
PCI = "y";
|
||||
SOC_MT7620 = "y";
|
||||
RALINK = "y";
|
||||
PCI = "y";
|
||||
SOC_MT7620 = "y";
|
||||
|
||||
SERIAL_8250_CONSOLE = "y";
|
||||
SERIAL_8250 = "y";
|
||||
SERIAL_CORE_CONSOLE = "y";
|
||||
SERIAL_OF_PLATFORM = "y";
|
||||
SERIAL_8250_CONSOLE = "y";
|
||||
SERIAL_8250 = "y";
|
||||
SERIAL_CORE_CONSOLE = "y";
|
||||
SERIAL_OF_PLATFORM = "y";
|
||||
|
||||
CONSOLE_LOGLEVEL_DEFAULT = "8";
|
||||
CONSOLE_LOGLEVEL_QUIET = "4";
|
||||
CONSOLE_LOGLEVEL_DEFAULT = "8";
|
||||
CONSOLE_LOGLEVEL_QUIET = "4";
|
||||
|
||||
NET = "y";
|
||||
ETHERNET = "y";
|
||||
NET_VENDOR_RALINK = "y";
|
||||
NET_RALINK_MDIO = "y";
|
||||
NET_RALINK_MDIO_MT7620 = "y";
|
||||
NET_RALINK_MT7620 = "y";
|
||||
SWPHY = "y";
|
||||
NET = "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";
|
||||
SPI_MT7621 = "y"; # } probably don't need both of these
|
||||
SPI_RT2880 = "y"; # }
|
||||
SPI_MASTER= "y";
|
||||
SPI_MEM= "y";
|
||||
SPI = "y";
|
||||
MTD_SPI_NOR = "y";
|
||||
SPI_MT7621 = "y"; # } probably don't need both of these
|
||||
SPI_RT2880 = "y"; # }
|
||||
SPI_MASTER = "y";
|
||||
SPI_MEM = "y";
|
||||
|
||||
MTD = "y";
|
||||
MTD_BLOCK = "y"; # fix undefined ref to register_mtd_blktrans_devs
|
||||
MTD = "y";
|
||||
MTD_BLOCK = "y"; # fix undefined ref to register_mtd_blktrans_devs
|
||||
|
||||
EARLY_PRINTK = "y";
|
||||
EARLY_PRINTK = "y";
|
||||
|
||||
NEW_LEDS = "y";
|
||||
LEDS_CLASS = "y"; # required by rt2x00lib
|
||||
NEW_LEDS = "y";
|
||||
LEDS_CLASS = "y"; # required by rt2x00lib
|
||||
|
||||
PRINTK_TIME = "y";
|
||||
} // lib.optionalAttrs (config.system.service ? vlan) {
|
||||
SWCONFIG = "y";
|
||||
};
|
||||
PRINTK_TIME = "y";
|
||||
}
|
||||
// lib.optionalAttrs (config.system.service ? vlan) {
|
||||
SWCONFIG = "y";
|
||||
};
|
||||
conditionalConfig = {
|
||||
WLAN = {
|
||||
WLAN_VENDOR_RALINK = "y";
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
config = "mipsel-unknown-linux-musl";
|
||||
gcc = {
|
||||
abi = "32";
|
||||
arch = "mips32"; # maybe mips_24kc-
|
||||
arch = "mips32"; # maybe mips_24kc-
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -25,10 +25,11 @@
|
||||
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.
|
||||
Flashing over an existing Liminix system is not possible while
|
||||
that system is running, otherwise you'll be overwriting flash
|
||||
partitions while they're in use - and that might not end well.
|
||||
Configure the system with :ref:`levitate` if you need to
|
||||
make it upgradable.
|
||||
|
||||
Vendor web page: https://www.gl-inet.com/products/gl-mt300n-v2/
|
||||
|
||||
@@ -36,21 +37,29 @@
|
||||
|
||||
'';
|
||||
|
||||
module = { pkgs, config, lib, lim, ...}:
|
||||
module =
|
||||
{
|
||||
pkgs,
|
||||
config,
|
||||
lib,
|
||||
lim,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (pkgs.liminix.services) oneshot;
|
||||
inherit (pkgs.pseudofile) dir symlink;
|
||||
inherit (pkgs) openwrt;
|
||||
|
||||
mac80211 = pkgs.kmodloader.override {
|
||||
targets = ["mt7603e"];
|
||||
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 {
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
../../modules/arch/mipsel.nix
|
||||
../../modules/outputs/tftpboot.nix
|
||||
@@ -98,8 +107,12 @@
|
||||
'';
|
||||
down = "${pkgs.swconfig}/bin/swconfig dev switch0 set reset";
|
||||
};
|
||||
in rec {
|
||||
eth = link.build { ifname = "eth0"; dependencies = [swconfig]; };
|
||||
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";
|
||||
@@ -128,58 +141,61 @@
|
||||
extraPatchPhase = ''
|
||||
${openwrt.applyPatches.ramips}
|
||||
'';
|
||||
config = {
|
||||
config =
|
||||
{
|
||||
|
||||
RALINK = "y";
|
||||
PCI = "y";
|
||||
SOC_MT7620 = "y";
|
||||
RALINK = "y";
|
||||
PCI = "y";
|
||||
SOC_MT7620 = "y";
|
||||
|
||||
SERIAL_8250_CONSOLE = "y";
|
||||
SERIAL_8250 = "y";
|
||||
SERIAL_CORE_CONSOLE = "y";
|
||||
SERIAL_OF_PLATFORM = "y";
|
||||
SERIAL_8250_CONSOLE = "y";
|
||||
SERIAL_8250 = "y";
|
||||
SERIAL_CORE_CONSOLE = "y";
|
||||
SERIAL_OF_PLATFORM = "y";
|
||||
|
||||
CONSOLE_LOGLEVEL_DEFAULT = "8";
|
||||
CONSOLE_LOGLEVEL_QUIET = "4";
|
||||
CONSOLE_LOGLEVEL_DEFAULT = "8";
|
||||
CONSOLE_LOGLEVEL_QUIET = "4";
|
||||
|
||||
MTD = "y";
|
||||
MTD_BLOCK = "y"; # fix undefined ref to register_mtd_blktrans_dev
|
||||
MTD = "y";
|
||||
MTD_BLOCK = "y"; # fix undefined ref to register_mtd_blktrans_dev
|
||||
|
||||
SPI = "y";
|
||||
MTD_SPI_NOR = "y";
|
||||
SPI_MT7621 = "y";
|
||||
SPI_MASTER= "y";
|
||||
SPI_MEM= "y";
|
||||
SPI = "y";
|
||||
MTD_SPI_NOR = "y";
|
||||
SPI_MT7621 = "y";
|
||||
SPI_MASTER = "y";
|
||||
SPI_MEM = "y";
|
||||
|
||||
REGULATOR = "y";
|
||||
REGULATOR_FIXED_VOLTAGE = "y";
|
||||
REGULATOR = "y";
|
||||
REGULATOR_FIXED_VOLTAGE = "y";
|
||||
|
||||
NET = "y";
|
||||
ETHERNET = "y";
|
||||
NET = "y";
|
||||
ETHERNET = "y";
|
||||
|
||||
PHYLIB = "y";
|
||||
AT803X_PHY="y";
|
||||
FIXED_PHY="y";
|
||||
GENERIC_PHY="y";
|
||||
NET_VENDOR_RALINK = "y";
|
||||
NET_RALINK_RT3050 = "y";
|
||||
NET_RALINK_SOC="y";
|
||||
SWPHY = "y";
|
||||
PHYLIB = "y";
|
||||
AT803X_PHY = "y";
|
||||
FIXED_PHY = "y";
|
||||
GENERIC_PHY = "y";
|
||||
NET_VENDOR_RALINK = "y";
|
||||
NET_RALINK_RT3050 = "y";
|
||||
NET_RALINK_SOC = "y";
|
||||
SWPHY = "y";
|
||||
|
||||
GPIOLIB="y";
|
||||
GPIO_MT7621 = "y";
|
||||
GPIOLIB = "y";
|
||||
GPIO_MT7621 = "y";
|
||||
|
||||
PHY_RALINK_USB = "y";
|
||||
PHY_RALINK_USB = "y";
|
||||
|
||||
EARLY_PRINTK = "y";
|
||||
EARLY_PRINTK = "y";
|
||||
|
||||
PRINTK_TIME = "y";
|
||||
} // lib.optionalAttrs (config.system.service ? vlan) {
|
||||
SWCONFIG = "y";
|
||||
} // lib.optionalAttrs (config.system.service ? watchdog) {
|
||||
RALINK_WDT = "y"; # watchdog
|
||||
MT7621_WDT = "y"; # or it might be this one
|
||||
};
|
||||
PRINTK_TIME = "y";
|
||||
}
|
||||
// lib.optionalAttrs (config.system.service ? vlan) {
|
||||
SWCONFIG = "y";
|
||||
}
|
||||
// lib.optionalAttrs (config.system.service ? watchdog) {
|
||||
RALINK_WDT = "y"; # watchdog
|
||||
MT7621_WDT = "y"; # or it might be this one
|
||||
};
|
||||
conditionalConfig = {
|
||||
WLAN = {
|
||||
WLAN_VENDOR_RALINK = "y";
|
||||
|
||||
@@ -0,0 +1,751 @@
|
||||
{
|
||||
description = ''
|
||||
OpenWrt One
|
||||
***********
|
||||
|
||||
Hardware summary
|
||||
================
|
||||
|
||||
- MediaTek MT7981B (1300MHz)
|
||||
- 256MB NAND Flash
|
||||
- 1024MB RAM
|
||||
- WLan hardware: Mediatek MT7976C
|
||||
|
||||
Status
|
||||
======
|
||||
|
||||
- Only tested over TFTP so far.
|
||||
- WiFi (2.4G and 5G) works.
|
||||
- 2.5G ethernet port works.
|
||||
|
||||
Limitations
|
||||
===========
|
||||
|
||||
- adding `he_bss_color="128"` causes `Invalid argument` for hostap
|
||||
- nvme support untested
|
||||
- I don't think the front LEDs work yet
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
TODO: add instructions on how to boot directly from TFTP to memory
|
||||
and how to install from TFTP to flash without going through OpenWrt.
|
||||
|
||||
The instructions below assume you can boot and SSH into OpenWrt:
|
||||
|
||||
Boot into OpenWrt and create a 'liminix' UBI partition:
|
||||
|
||||
root@OpenWrt:~# ubimkvol /dev/ubi0 --name=liminix --maxavsize
|
||||
|
||||
Remember the 'Volume ID' that was created for this new partition
|
||||
|
||||
Build the UBI image and write it to this new partition:
|
||||
|
||||
$ nix-build -I liminix-config=./my-configuration.nix --arg device "import ./devices/openwrt-one" -A outputs.default
|
||||
$ cat result/rootfs | ssh root@192.168.1.1 "cat > /tmp/rootfs"
|
||||
$ ssh root@192.168.1.1
|
||||
root@OpenWrt:~# ubiupdatevol /dev/ubi0_X /tmp/rootfs # replace X with the volume id, if needed check with `ubinfo`
|
||||
|
||||
Reboot into the U-Boot prompt and boot with:
|
||||
|
||||
OpenWrt One> ubifsmount ubi0:liminix && ubifsload ''${loadaddr} boot/fit && bootm ''${loadaddr}'
|
||||
|
||||
If this works, reboot into OpenWrt and configure U-Boot to boot ubifs by default:
|
||||
|
||||
root@OpenWrt:~# fw_setenv orig_boot_production $(fw_printenv -n boot_production)
|
||||
root@OpenWrt:~# fw_setenv boot_production 'led white on ; ubifsmount ubi0:liminix && ubifsload ''${loadaddr} boot/fit && bootm ''${loadaddr}'
|
||||
|
||||
Troubleshooting
|
||||
===============
|
||||
|
||||
The instructions above assume you can boot and SSH into the (recovery)
|
||||
OpenWrt installation. If you have broken your device to the point where that
|
||||
is no longer possible, you could re-install OpenWrt, but probably you could
|
||||
also install directly from U-Boot:
|
||||
|
||||
https://github.com/u-boot/u-boot/blob/master/doc/README.ubi
|
||||
'';
|
||||
|
||||
system = {
|
||||
crossSystem = {
|
||||
config = "aarch64-unknown-linux-musl";
|
||||
gcc = {
|
||||
# https://openwrt.org/docs/techref/instructionset/aarch64_cortex-a53
|
||||
# openwrt ./target/linux/mediatek/filogic/target.mk
|
||||
# https://gcc.gnu.org/onlinedocs/gcc/AArch64-Options.html
|
||||
# https://en.wikipedia.org/wiki/Comparison_of_ARM_processors
|
||||
arch = "armv8-a";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
module =
|
||||
{
|
||||
pkgs,
|
||||
config,
|
||||
lib,
|
||||
lim,
|
||||
...
|
||||
}:
|
||||
let
|
||||
openwrt = pkgs.openwrt_24_10;
|
||||
mediatek-firmware = pkgs.stdenv.mkDerivation {
|
||||
name = "wlan-firmware";
|
||||
phases = [ "installPhase" ];
|
||||
installPhase = ''
|
||||
mkdir $out
|
||||
|
||||
cp ${pkgs.linux-firmware}/lib/firmware/mediatek/{mt7915,mt7615,mt7986_eeprom_mt7976,mt7981}* $out
|
||||
'';
|
||||
};
|
||||
airoha-firmware = pkgs.stdenv.mkDerivation {
|
||||
name = "airoha-firmware";
|
||||
phases = [ "installPhase" ];
|
||||
installPhase = ''
|
||||
mkdir $out
|
||||
|
||||
cp ${pkgs.linux-firmware}/lib/firmware/airoha/* $out
|
||||
'';
|
||||
};
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
../../modules/arch/aarch64.nix
|
||||
../../modules/outputs/tftpboot.nix
|
||||
../../modules/outputs/ubifs.nix
|
||||
];
|
||||
config = {
|
||||
kernel = {
|
||||
src = openwrt.kernelSrc;
|
||||
version = openwrt.kernelVersion;
|
||||
extraPatchPhase = ''
|
||||
${openwrt.applyPatches.mediatek}
|
||||
'';
|
||||
config =
|
||||
{
|
||||
NET = "y"; # unlock NET_XGRESS
|
||||
SERIAL_8250 = "y"; # unlock SERIAL_8250_FSL
|
||||
SERIAL_8250_CONSOLE = "y"; # to get the serial console
|
||||
WATCHDOG = "y"; # unlock WATCHDOG_CORE
|
||||
NEW_LEDS = "y"; # unlock LEDS_PWM
|
||||
LEDS_CLASS = "y"; # unlock LEDS_PWM
|
||||
LEDS_TRIGGERS = "y"; # unlock LEDS_TRIGGER_PATTERN
|
||||
DEFERRED_STRUCT_PAGE_INIT = "y"; # trigger PADATA
|
||||
# Taken from openwrt's ./target/linux/mediatek/filogic/config-6.6
|
||||
"64BIT" = "y";
|
||||
AIROHA_EN8801SC_PHY = "y";
|
||||
ARCH_BINFMT_ELF_EXTRA_PHDRS = "y";
|
||||
ARCH_CORRECT_STACKTRACE_ON_KRETPROBE = "y";
|
||||
ARCH_DEFAULT_KEXEC_IMAGE_VERIFY_SIG = "y";
|
||||
ARCH_DMA_ADDR_T_64BIT = "y";
|
||||
ARCH_FORCE_MAX_ORDER = "10";
|
||||
ARCH_KEEP_MEMBLOCK = "y";
|
||||
ARCH_MEDIATEK = "y";
|
||||
ARCH_MHP_MEMMAP_ON_MEMORY_ENABLE = "y";
|
||||
ARCH_MMAP_RND_BITS = "18";
|
||||
ARCH_MMAP_RND_BITS_MAX = "24";
|
||||
ARCH_MMAP_RND_BITS_MIN = "18";
|
||||
ARCH_MMAP_RND_COMPAT_BITS_MIN = "11";
|
||||
ARCH_PROC_KCORE_TEXT = "y";
|
||||
ARCH_SPARSEMEM_ENABLE = "y";
|
||||
ARCH_STACKWALK = "y";
|
||||
ARCH_SUSPEND_POSSIBLE = "y";
|
||||
ARCH_WANTS_NO_INSTR = "y";
|
||||
ARCH_WANTS_THP_SWAP = "y";
|
||||
ARM64 = "y";
|
||||
ARM64_4K_PAGES = "y";
|
||||
ARM64_ERRATUM_843419 = "y";
|
||||
ARM64_LD_HAS_FIX_ERRATUM_843419 = "y";
|
||||
ARM64_PAGE_SHIFT = "12";
|
||||
ARM64_PA_BITS = "48";
|
||||
ARM64_PA_BITS_48 = "y";
|
||||
ARM64_TAGGED_ADDR_ABI = "y";
|
||||
ARM64_VA_BITS = "39";
|
||||
ARM64_VA_BITS_39 = "y";
|
||||
ARM_AMBA = "y";
|
||||
ARM_ARCH_TIMER = "y";
|
||||
ARM_ARCH_TIMER_EVTSTREAM = "y";
|
||||
ARM_GIC = "y";
|
||||
ARM_GIC_V2M = "y";
|
||||
ARM_GIC_V3 = "y";
|
||||
ARM_GIC_V3_ITS = "y";
|
||||
ARM_GIC_V3_ITS_PCI = "y";
|
||||
ARM_MEDIATEK_CPUFREQ = "y";
|
||||
ARM_PMU = "y";
|
||||
ARM_PMUV3 = "y";
|
||||
ARM_PSCI_FW = "y";
|
||||
ATA = "y";
|
||||
AUDIT_ARCH_COMPAT_GENERIC = "y";
|
||||
BLK_DEV_LOOP = "y";
|
||||
BLK_DEV_SD = "y";
|
||||
BLK_MQ_PCI = "y";
|
||||
BLK_PM = "y";
|
||||
BLOCK_NOTIFIERS = "y";
|
||||
BSD_PROCESS_ACCT = "y";
|
||||
BSD_PROCESS_ACCT_V3 = "y";
|
||||
BUFFER_HEAD = "y";
|
||||
BUILTIN_RETURN_ADDRESS_STRIPS_PAC = "y";
|
||||
CC_HAVE_SHADOW_CALL_STACK = "y";
|
||||
CC_HAVE_STACKPROTECTOR_SYSREG = "y";
|
||||
#CC_IMPLICIT_FALLTHROUGH="-Wimplicit-fallthrough=5";
|
||||
CC_NO_ARRAY_BOUNDS = "y";
|
||||
CLKSRC_MMIO = "y";
|
||||
CLONE_BACKWARDS = "y";
|
||||
CMDLINE_OVERRIDE = "y";
|
||||
COMMON_CLK = "y";
|
||||
COMMON_CLK_MEDIATEK = "y";
|
||||
COMMON_CLK_MT7981 = "y";
|
||||
COMMON_CLK_MT7981_ETHSYS = "y";
|
||||
COMMON_CLK_MT7986 = "y";
|
||||
COMMON_CLK_MT7986_ETHSYS = "y";
|
||||
COMMON_CLK_MT7988 = "y";
|
||||
COMPACT_UNEVICTABLE_DEFAULT = "1";
|
||||
CONFIGFS_FS = "y";
|
||||
CONSOLE_LOGLEVEL_DEFAULT = "15";
|
||||
CONTEXT_TRACKING = "y";
|
||||
CONTEXT_TRACKING_IDLE = "y";
|
||||
CPU_FREQ = "y";
|
||||
CPU_FREQ_DEFAULT_GOV_USERSPACE = "y";
|
||||
CPU_FREQ_GOV_ATTR_SET = "y";
|
||||
CPU_FREQ_GOV_COMMON = "y";
|
||||
CPU_FREQ_GOV_CONSERVATIVE = "y";
|
||||
CPU_FREQ_GOV_ONDEMAND = "y";
|
||||
CPU_FREQ_GOV_PERFORMANCE = "y";
|
||||
CPU_FREQ_GOV_POWERSAVE = "y";
|
||||
CPU_FREQ_GOV_SCHEDUTIL = "y";
|
||||
CPU_FREQ_GOV_USERSPACE = "y";
|
||||
CPU_FREQ_STAT = "y";
|
||||
CPU_LITTLE_ENDIAN = "y";
|
||||
CPU_RMAP = "y";
|
||||
CPU_THERMAL = "y";
|
||||
CRC16 = "y";
|
||||
CRC_CCITT = "y";
|
||||
CRYPTO_AES_ARM64 = "y";
|
||||
CRYPTO_AES_ARM64_CE = "y";
|
||||
CRYPTO_AES_ARM64_CE_BLK = "y";
|
||||
CRYPTO_AES_ARM64_CE_CCM = "y";
|
||||
CRYPTO_CMAC = "y";
|
||||
CRYPTO_CRC32 = "y";
|
||||
CRYPTO_CRC32C = "y";
|
||||
CRYPTO_CRYPTD = "y";
|
||||
CRYPTO_DEFLATE = "y";
|
||||
CRYPTO_DRBG = "y";
|
||||
CRYPTO_DRBG_HMAC = "y";
|
||||
CRYPTO_DRBG_MENU = "y";
|
||||
CRYPTO_ECB = "y";
|
||||
CRYPTO_ECC = "y";
|
||||
CRYPTO_ECDH = "y";
|
||||
CRYPTO_GHASH_ARM64_CE = "y";
|
||||
CRYPTO_HASH_INFO = "y";
|
||||
CRYPTO_HMAC = "y";
|
||||
CRYPTO_JITTERENTROPY = "y";
|
||||
CRYPTO_LIB_BLAKE2S_GENERIC = "y";
|
||||
CRYPTO_LIB_GF128MUL = "y";
|
||||
CRYPTO_LIB_SHA1 = "y";
|
||||
CRYPTO_LIB_SHA256 = "y";
|
||||
CRYPTO_LIB_UTILS = "y";
|
||||
CRYPTO_LZO = "y";
|
||||
CRYPTO_RNG = "y";
|
||||
CRYPTO_RNG2 = "y";
|
||||
CRYPTO_RNG_DEFAULT = "y";
|
||||
CRYPTO_SHA256 = "y";
|
||||
CRYPTO_SHA256_ARM64 = "y";
|
||||
CRYPTO_SHA2_ARM64_CE = "y";
|
||||
CRYPTO_SHA3 = "y";
|
||||
CRYPTO_SHA512 = "y";
|
||||
CRYPTO_SM4 = "y";
|
||||
CRYPTO_SM4_ARM64_CE_BLK = "y";
|
||||
CRYPTO_SM4_ARM64_CE_CCM = "y";
|
||||
CRYPTO_SM4_ARM64_CE_GCM = "y";
|
||||
CRYPTO_ZSTD = "y";
|
||||
DCACHE_WORD_ACCESS = "y";
|
||||
#DEBUG_INFO="y";
|
||||
DEBUG_MISC = "y";
|
||||
DIMLIB = "y";
|
||||
DMADEVICES = "y";
|
||||
DMATEST = "y";
|
||||
DMA_BOUNCE_UNALIGNED_KMALLOC = "y";
|
||||
DMA_DIRECT_REMAP = "y";
|
||||
DMA_ENGINE = "y";
|
||||
DMA_ENGINE_RAID = "y";
|
||||
DMA_OF = "y";
|
||||
DMA_VIRTUAL_CHANNELS = "y";
|
||||
DTC = "y";
|
||||
EDAC_SUPPORT = "y";
|
||||
EINT_MTK = "y";
|
||||
EXCLUSIVE_SYSTEM_RAM = "y";
|
||||
EXT4_FS = "y";
|
||||
F2FS_FS = "y";
|
||||
FIXED_PHY = "y";
|
||||
FIX_EARLYCON_MEM = "y";
|
||||
FRAME_POINTER = "y";
|
||||
FS_IOMAP = "y";
|
||||
FS_MBCACHE = "y";
|
||||
FUNCTION_ALIGNMENT = "4";
|
||||
FUNCTION_ALIGNMENT_4B = "y";
|
||||
FWNODE_MDIO = "y";
|
||||
FW_LOADER_PAGED_BUF = "y";
|
||||
#FW_LOADER_SYSFS="y";
|
||||
#GCC11_NO_ARRAY_BOUNDS="y";
|
||||
#GCC_ASM_GOTO_OUTPUT_WORKAROUND="y";
|
||||
GCC_SUPPORTS_DYNAMIC_FTRACE_WITH_ARGS = "y";
|
||||
GENERIC_ALLOCATOR = "y";
|
||||
GENERIC_ARCH_TOPOLOGY = "y";
|
||||
GENERIC_BUG = "y";
|
||||
GENERIC_BUG_RELATIVE_POINTERS = "y";
|
||||
GENERIC_CLOCKEVENTS = "y";
|
||||
GENERIC_CLOCKEVENTS_BROADCAST = "y";
|
||||
GENERIC_CPU_AUTOPROBE = "y";
|
||||
GENERIC_CPU_VULNERABILITIES = "y";
|
||||
GENERIC_CSUM = "y";
|
||||
GENERIC_EARLY_IOREMAP = "y";
|
||||
GENERIC_GETTIMEOFDAY = "y";
|
||||
GENERIC_IDLE_POLL_SETUP = "y";
|
||||
GENERIC_IOREMAP = "y";
|
||||
GENERIC_IRQ_EFFECTIVE_AFF_MASK = "y";
|
||||
GENERIC_IRQ_SHOW = "y";
|
||||
GENERIC_IRQ_SHOW_LEVEL = "y";
|
||||
GENERIC_LIB_DEVMEM_IS_ALLOWED = "y";
|
||||
GENERIC_MSI_IRQ = "y";
|
||||
GENERIC_PCI_IOMAP = "y";
|
||||
GENERIC_PHY = "y";
|
||||
GENERIC_PINCONF = "y";
|
||||
GENERIC_PINCTRL_GROUPS = "y";
|
||||
GENERIC_PINMUX_FUNCTIONS = "y";
|
||||
GENERIC_SCHED_CLOCK = "y";
|
||||
GENERIC_SMP_IDLE_THREAD = "y";
|
||||
GENERIC_STRNCPY_FROM_USER = "y";
|
||||
GENERIC_STRNLEN_USER = "y";
|
||||
GENERIC_TIME_VSYSCALL = "y";
|
||||
GLOB = "y";
|
||||
GPIO_CDEV = "y";
|
||||
GPIO_WATCHDOG = "y";
|
||||
GPIO_WATCHDOG_ARCH_INITCALL = "y";
|
||||
GRO_CELLS = "y";
|
||||
HARDIRQS_SW_RESEND = "y";
|
||||
HAS_DMA = "y";
|
||||
HAS_IOMEM = "y";
|
||||
HAS_IOPORT = "y";
|
||||
HAS_IOPORT_MAP = "y";
|
||||
HWMON = "y";
|
||||
HW_RANDOM = "y";
|
||||
HW_RANDOM_MTK = "y";
|
||||
I2C = "y";
|
||||
I2C_BOARDINFO = "y";
|
||||
I2C_CHARDEV = "y";
|
||||
I2C_MT65XX = "y";
|
||||
ICPLUS_PHY = "y";
|
||||
ILLEGAL_POINTER_VALUE = "0xdead000000000000";
|
||||
#INITRAMFS_SOURCE="""";
|
||||
IRQCHIP = "y";
|
||||
IRQ_DOMAIN = "y";
|
||||
IRQ_DOMAIN_HIERARCHY = "y";
|
||||
IRQ_FORCED_THREADING = "y";
|
||||
IRQ_TIME_ACCOUNTING = "y";
|
||||
IRQ_WORK = "y";
|
||||
JBD2 = "y";
|
||||
JUMP_LABEL = "y";
|
||||
LEDS_PWM = "y";
|
||||
LEDS_SMARTRG_LED = "y";
|
||||
LIBFDT = "y";
|
||||
LOCK_DEBUGGING_SUPPORT = "y";
|
||||
LOCK_SPIN_ON_OWNER = "y";
|
||||
LZO_COMPRESS = "y";
|
||||
LZO_DECOMPRESS = "y";
|
||||
MAGIC_SYSRQ = "y";
|
||||
MAXLINEAR_GPHY = "y";
|
||||
MDIO_BUS = "y";
|
||||
MDIO_DEVICE = "y";
|
||||
MDIO_DEVRES = "y";
|
||||
MEDIATEK_2P5GE_PHY = "y";
|
||||
MEDIATEK_GE_PHY = "y";
|
||||
MEDIATEK_GE_SOC_PHY = "y";
|
||||
MEDIATEK_WATCHDOG = "y";
|
||||
MESSAGE_LOGLEVEL_DEFAULT = "7";
|
||||
MFD_SYSCON = "y";
|
||||
MIGRATION = "y";
|
||||
MMC = "y";
|
||||
MMC_BLOCK = "y";
|
||||
MMC_CQHCI = "y";
|
||||
MMC_MTK = "y";
|
||||
MMU_LAZY_TLB_REFCOUNT = "y";
|
||||
MODULES_TREE_LOOKUP = "y";
|
||||
MODULES_USE_ELF_RELA = "y";
|
||||
MTD_NAND_CORE = "y";
|
||||
MTD_NAND_ECC = "y";
|
||||
MTD_NAND_ECC_MEDIATEK = "y";
|
||||
MTD_NAND_ECC_SW_HAMMING = "y";
|
||||
MTD_NAND_MTK = "y";
|
||||
MTD_NAND_MTK_BMT = "y";
|
||||
MTD_PARSER_TRX = "y";
|
||||
MTD_RAW_NAND = "y";
|
||||
MTD_SPI_NAND = "y";
|
||||
MTD_SPI_NOR = "y";
|
||||
MTD_SPLIT_FIRMWARE = "y";
|
||||
MTD_SPLIT_FIT_FW = "y";
|
||||
MTD_UBI = "y";
|
||||
MTD_UBI_BEB_LIMIT = "20";
|
||||
MTD_UBI_BLOCK = "y";
|
||||
MTD_UBI_FASTMAP = "y";
|
||||
MTD_UBI_NVMEM = "y";
|
||||
MTD_UBI_WL_THRESHOLD = "4096";
|
||||
MTK_CPUX_TIMER = "y";
|
||||
MTK_HSDMA = "y";
|
||||
MTK_INFRACFG = "y";
|
||||
MTK_LVTS_THERMAL = "y";
|
||||
MTK_LVTS_THERMAL_DEBUGFS = "y";
|
||||
MTK_PMIC_WRAP = "y";
|
||||
MTK_REGULATOR_COUPLER = "y";
|
||||
MTK_SCPSYS = "y";
|
||||
MTK_SCPSYS_PM_DOMAINS = "y";
|
||||
MTK_SOC_THERMAL = "y";
|
||||
MTK_THERMAL = "y";
|
||||
MTK_TIMER = "y";
|
||||
MUTEX_SPIN_ON_OWNER = "y";
|
||||
NEED_DMA_MAP_STATE = "y";
|
||||
NEED_SG_DMA_LENGTH = "y";
|
||||
NET_DEVLINK = "y";
|
||||
NET_DSA = "y";
|
||||
NET_DSA_MT7530 = "y";
|
||||
NET_DSA_MT7530_MDIO = "y";
|
||||
NET_DSA_MT7530_MMIO = "y";
|
||||
NET_DSA_TAG_MTK = "y";
|
||||
#NET_EGRESS="y";
|
||||
NET_FLOW_LIMIT = "y";
|
||||
#NET_INGRESS="y";
|
||||
NET_MEDIATEK_SOC = "y";
|
||||
NET_MEDIATEK_SOC_WED = "y";
|
||||
NET_SELFTESTS = "y";
|
||||
NET_SWITCHDEV = "y";
|
||||
NET_VENDOR_MEDIATEK = "y";
|
||||
#NET_XGRESS="y";
|
||||
NLS = "y";
|
||||
NO_HZ_COMMON = "y";
|
||||
NO_HZ_IDLE = "y";
|
||||
NR_CPUS = "4";
|
||||
NVMEM = "y";
|
||||
NVMEM_BLOCK = "y";
|
||||
NVMEM_LAYOUTS = "y";
|
||||
NVMEM_LAYOUT_ADTRAN = "y";
|
||||
NVMEM_MTK_EFUSE = "y";
|
||||
NVMEM_SYSFS = "y";
|
||||
OF = "y";
|
||||
OF_ADDRESS = "y";
|
||||
OF_DYNAMIC = "y";
|
||||
OF_EARLY_FLATTREE = "y";
|
||||
OF_FLATTREE = "y";
|
||||
OF_GPIO = "y";
|
||||
OF_IRQ = "y";
|
||||
OF_KOBJ = "y";
|
||||
OF_MDIO = "y";
|
||||
OF_OVERLAY = "y";
|
||||
OF_RESOLVE = "y";
|
||||
PADATA = "y";
|
||||
PAGE_POOL = "y";
|
||||
PAGE_POOL_STATS = "y";
|
||||
PAGE_SIZE_LESS_THAN_256KB = "y";
|
||||
PAGE_SIZE_LESS_THAN_64KB = "y";
|
||||
#PAHOLE_HAS_LANG_EXCLUDE="y";
|
||||
PARTITION_PERCPU = "y";
|
||||
PCI = "y";
|
||||
PCIEAER = "y";
|
||||
PCIEASPM = "y";
|
||||
PCIEASPM_PERFORMANCE = "y";
|
||||
PCIEPORTBUS = "y";
|
||||
PCIE_MEDIATEK_GEN3 = "y";
|
||||
PCIE_PME = "y";
|
||||
PCI_DEBUG = "y";
|
||||
PCI_DOMAINS = "y";
|
||||
PCI_DOMAINS_GENERIC = "y";
|
||||
PCI_MSI = "y";
|
||||
PCS_MTK_LYNXI = "y";
|
||||
PCS_MTK_USXGMII = "y";
|
||||
PERF_EVENTS = "y";
|
||||
PER_VMA_LOCK = "y";
|
||||
PGTABLE_LEVELS = "3";
|
||||
PHYLIB = "y";
|
||||
PHYLIB_LEDS = "y";
|
||||
PHYLINK = "y";
|
||||
PHYS_ADDR_T_64BIT = "y";
|
||||
PHY_MTK_TPHY = "y";
|
||||
PHY_MTK_XFI_TPHY = "y";
|
||||
PHY_MTK_XSPHY = "y";
|
||||
PINCTRL = "y";
|
||||
PINCTRL_MT7981 = "y";
|
||||
PINCTRL_MT7986 = "y";
|
||||
PINCTRL_MT7988 = "y";
|
||||
PINCTRL_MTK_MOORE = "y";
|
||||
PINCTRL_MTK_V2 = "y";
|
||||
PM = "y";
|
||||
PM_CLK = "y";
|
||||
PM_GENERIC_DOMAINS = "y";
|
||||
PM_GENERIC_DOMAINS_OF = "y";
|
||||
PM_OPP = "y";
|
||||
POLYNOMIAL = "y";
|
||||
POSIX_CPU_TIMERS_TASK_WORK = "y";
|
||||
POWER_RESET = "y";
|
||||
POWER_RESET_SYSCON = "y";
|
||||
POWER_SUPPLY = "y";
|
||||
PREEMPT_NONE_BUILD = "y";
|
||||
PRINTK_TIME = "y";
|
||||
PSTORE = "y";
|
||||
PSTORE_COMPRESS = "y";
|
||||
PSTORE_CONSOLE = "y";
|
||||
PSTORE_PMSG = "y";
|
||||
PSTORE_RAM = "y";
|
||||
PTP_1588_CLOCK_OPTIONAL = "y";
|
||||
PWM = "y";
|
||||
PWM_MEDIATEK = "y";
|
||||
PWM_SYSFS = "y";
|
||||
QUEUED_RWLOCKS = "y";
|
||||
QUEUED_SPINLOCKS = "y";
|
||||
RANDSTRUCT_NONE = "y";
|
||||
RAS = "y";
|
||||
RATIONAL = "y";
|
||||
REALTEK_PHY = "y";
|
||||
REED_SOLOMON = "y";
|
||||
REED_SOLOMON_DEC8 = "y";
|
||||
REED_SOLOMON_ENC8 = "y";
|
||||
REGMAP = "y";
|
||||
REGMAP_I2C = "y";
|
||||
REGMAP_MMIO = "y";
|
||||
REGULATOR = "y";
|
||||
REGULATOR_FIXED_VOLTAGE = "y";
|
||||
REGULATOR_MT6380 = "y";
|
||||
REGULATOR_RT5190A = "y";
|
||||
RESET_CONTROLLER = "y";
|
||||
RESET_TI_SYSCON = "y";
|
||||
RFS_ACCEL = "y";
|
||||
RODATA_FULL_DEFAULT_ENABLED = "y";
|
||||
RPS = "y";
|
||||
RTC_CLASS = "y";
|
||||
RTC_DRV_MT7622 = "y";
|
||||
RTC_I2C_AND_SPI = "y";
|
||||
RWSEM_SPIN_ON_OWNER = "y";
|
||||
SCHED_MC = "y";
|
||||
SCSI = "y";
|
||||
SCSI_COMMON = "y";
|
||||
SERIAL_8250_FSL = "y";
|
||||
SERIAL_8250_MT6577 = "y";
|
||||
SERIAL_8250_NR_UARTS = "3";
|
||||
SERIAL_8250_RUNTIME_UARTS = "3";
|
||||
SERIAL_DEV_BUS = "y";
|
||||
SERIAL_DEV_CTRL_TTYPORT = "y";
|
||||
SERIAL_MCTRL_GPIO = "y";
|
||||
SERIAL_OF_PLATFORM = "y";
|
||||
SGL_ALLOC = "y";
|
||||
SG_POOL = "y";
|
||||
SMP = "y";
|
||||
SOCK_RX_QUEUE_MAPPING = "y";
|
||||
SOFTIRQ_ON_OWN_STACK = "y";
|
||||
SPARSEMEM = "y";
|
||||
SPARSEMEM_EXTREME = "y";
|
||||
SPARSEMEM_VMEMMAP = "y";
|
||||
SPARSEMEM_VMEMMAP_ENABLE = "y";
|
||||
SPARSE_IRQ = "y";
|
||||
SPI = "y";
|
||||
SPI_DYNAMIC = "y";
|
||||
SPI_MASTER = "y";
|
||||
SPI_MEM = "y";
|
||||
SPI_MT65XX = "y";
|
||||
SPI_MTK_SNFI = "y";
|
||||
#SQUASHFS_DECOMP_MULTI_PERCPU="y";
|
||||
SWIOTLB = "y";
|
||||
SWPHY = "y";
|
||||
SYSCTL_EXCEPTION_TRACE = "y";
|
||||
THERMAL = "y";
|
||||
THERMAL_DEFAULT_GOV_STEP_WISE = "y";
|
||||
THERMAL_EMERGENCY_POWEROFF_DELAY_MS = "0";
|
||||
THERMAL_GOV_BANG_BANG = "y";
|
||||
THERMAL_GOV_FAIR_SHARE = "y";
|
||||
THERMAL_GOV_STEP_WISE = "y";
|
||||
THERMAL_GOV_USER_SPACE = "y";
|
||||
THERMAL_HWMON = "y";
|
||||
THERMAL_OF = "y";
|
||||
THERMAL_WRITABLE_TRIPS = "y";
|
||||
THREAD_INFO_IN_TASK = "y";
|
||||
TICK_CPU_ACCOUNTING = "y";
|
||||
TIMER_OF = "y";
|
||||
TIMER_PROBE = "y";
|
||||
TRACE_IRQFLAGS_NMI_SUPPORT = "y";
|
||||
TREE_RCU = "y";
|
||||
TREE_SRCU = "y";
|
||||
UBIFS_FS = "y";
|
||||
UIMAGE_FIT_BLK = "y";
|
||||
USB_SUPPORT = "y";
|
||||
VMAP_STACK = "y";
|
||||
WATCHDOG_CORE = "y";
|
||||
WATCHDOG_PRETIMEOUT_DEFAULT_GOV_PANIC = "y";
|
||||
WATCHDOG_PRETIMEOUT_GOV = "y";
|
||||
WATCHDOG_PRETIMEOUT_GOV_PANIC = "y";
|
||||
WATCHDOG_PRETIMEOUT_GOV_SEL = "m";
|
||||
WATCHDOG_SYSFS = "y";
|
||||
XPS = "y";
|
||||
XXHASH = "y";
|
||||
ZLIB_DEFLATE = "y";
|
||||
ZLIB_INFLATE = "y";
|
||||
ZONE_DMA32 = "y";
|
||||
ZSTD_COMMON = "y";
|
||||
ZSTD_COMPRESS = "y";
|
||||
ZSTD_DECOMPRESS = "y";
|
||||
# from DEVICE_PACKAGES in the openwrt_one section of
|
||||
# openwrt's ./target/linux/mediatek/image/filogic.mk:
|
||||
# chop off the 'kmod-' prefix and search for 'KernelPackage/...'
|
||||
# in ./package/kernel/linux/modules/*.mk, and remember to add
|
||||
# modules to kmodloader targets below
|
||||
AIR_EN8811H_PHY = "m";
|
||||
RTC_DRV_PCF8563 = "m";
|
||||
NVME_CORE = "m";
|
||||
BLK_DEV_NVME = "m";
|
||||
NVME_MULTIPATH = "n";
|
||||
NVME_HWMON = "y";
|
||||
# ???
|
||||
AQUANTIA_PHY = "m";
|
||||
MT798X_WMAC = "y";
|
||||
}
|
||||
// lib.optionalAttrs (config.system.service ? watchdog) {
|
||||
RALINK_WDT = "y"; # watchdog
|
||||
MT7621_WDT = "y"; # or it might be this one
|
||||
};
|
||||
conditionalConfig = {
|
||||
WLAN = {
|
||||
MT7915E = "m";
|
||||
};
|
||||
};
|
||||
};
|
||||
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 0x44000000, and the bootlog
|
||||
# suggests it loads the fit to 0x46000000
|
||||
loadAddress = lim.parseInt "0x46000000";
|
||||
};
|
||||
imageFormat = "fit";
|
||||
loader.fit.enable = lib.mkDefault true; # override this if you are building tftpboot
|
||||
};
|
||||
rootfsType = lib.mkDefault "ubifs"; # override this if you are building tftpboot
|
||||
filesystem =
|
||||
let
|
||||
inherit (pkgs.pseudofile) dir symlink;
|
||||
in
|
||||
dir {
|
||||
lib = dir {
|
||||
firmware = dir {
|
||||
mediatek = symlink mediatek-firmware;
|
||||
airoha = symlink airoha-firmware;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
hardware =
|
||||
let
|
||||
phy = pkgs.kmodloader.override {
|
||||
targets = [
|
||||
"air_en8811h"
|
||||
];
|
||||
inherit (config.system.outputs) kernel;
|
||||
};
|
||||
mac80211 = pkgs.kmodloader.override {
|
||||
targets = [
|
||||
"mt7915e"
|
||||
"rtc-pcf8563"
|
||||
"nvme_core"
|
||||
"nvme"
|
||||
#"mt7996e"
|
||||
"aquantia"
|
||||
];
|
||||
inherit (config.system.outputs) kernel;
|
||||
};
|
||||
in
|
||||
{
|
||||
# from OEM bootlog
|
||||
# Creating 4 MTD partitions on "spi0.0":
|
||||
# 0x000000000000-0x000000040000 : "bl2-nor"
|
||||
# 0x000000040000-0x000000100000 : "factory"
|
||||
# 0x000000100000-0x000000180000 : "fip-nor"
|
||||
# 0x000000180000-0x000000e00000 : "recovery"
|
||||
# spi-nand spi1.1: calibration result: 0x3
|
||||
# spi-nand spi1.1: Winbond SPI NAND was found.
|
||||
# spi-nand spi1.1: 256 MiB, block size: 128 KiB, page size: 2048, OOB size: 128
|
||||
# 2 fixed-partitions partitions found on MTD device spi1.1
|
||||
# Creating 2 MTD partitions on "spi1.1":
|
||||
# 0x000000000000-0x000000100000 : "bl2"
|
||||
# 0x000000100000-0x000010000000 : "ubi"
|
||||
|
||||
flash = {
|
||||
# from the OEM bootlog:
|
||||
# ## Checking Image at 46000000 ...
|
||||
# FIT image found
|
||||
# FIT description: ARM64 OpenWrt FIT (Flattened Image Tree)
|
||||
# Image 0 (kernel-1)
|
||||
# Description: ARM64 OpenWrt Linux-6.6.57
|
||||
# Type: Kernel Image
|
||||
# Compression: gzip compressed
|
||||
# Data Start: 0x46001000
|
||||
# Data Size: 5751840 Bytes = 5.5 MiB
|
||||
# Architecture: AArch64
|
||||
# OS: Linux
|
||||
# Load Address: 0x44000000
|
||||
# Entry Point: 0x44000000
|
||||
|
||||
address = lim.parseInt "0x44000000";
|
||||
size = lim.parseInt "0xf60000";
|
||||
# /proc/mtd on a running system:
|
||||
# dev: size erasesize name
|
||||
# mtd0: 00040000 00010000 "bl2-nor"
|
||||
# mtd1: 000c0000 00010000 "factory"
|
||||
# mtd2: 00080000 00010000 "fip-nor"
|
||||
# mtd3: 00c80000 00010000 "recovery"
|
||||
# mtd4: 00100000 00020000 "bl2"
|
||||
# mtd5: 0ff00000 00020000 "ubi"
|
||||
eraseBlockSize = 65536;
|
||||
};
|
||||
ubi = {
|
||||
# TODO taken from belkin-rt3200, to review
|
||||
minIOSize = "2048";
|
||||
logicalEraseBlockSize = "126976";
|
||||
physicalEraseBlockSize = "131072";
|
||||
maxLEBcount = "1024"; # guessing
|
||||
};
|
||||
|
||||
defaultOutput = "ubimage";
|
||||
loadAddress = lim.parseInt "0x44000000";
|
||||
entryPoint = lim.parseInt "0x44000000";
|
||||
# TODO AFAICT this should be 2048, but I got 'FIT: image rootfs-1 start not aligned to page boundaries' with that...
|
||||
#alignment = 2048;
|
||||
alignment = 4096;
|
||||
rootDevice = "ubi0:liminix";
|
||||
dts = {
|
||||
src = "${openwrt.src}/target/linux/mediatek/dts/mt7981b-openwrt-one.dts";
|
||||
includePaths = [
|
||||
"${openwrt.src}/target/linux/mediatek/dts"
|
||||
"${config.system.outputs.kernel.modulesupport}/arch/arm64/boot/dts/mediatek/"
|
||||
];
|
||||
};
|
||||
|
||||
networkInterfaces =
|
||||
let
|
||||
inherit (config.system.service.network) link;
|
||||
in
|
||||
rec {
|
||||
eth0 = link.build {
|
||||
ifname = "eth0";
|
||||
dependencies = [ phy ];
|
||||
};
|
||||
eth1 = link.build { ifname = "eth1"; };
|
||||
|
||||
wlan0 = link.build {
|
||||
ifname = "wlan0";
|
||||
dependencies = [ mac80211 ];
|
||||
};
|
||||
wlan1 = link.build {
|
||||
ifname = "wlan1";
|
||||
dependencies = [ mac80211 ];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -26,26 +26,32 @@
|
||||
# this device is described by the "qemu" device
|
||||
installer = "vmroot";
|
||||
|
||||
module = { config, lim, ... }: {
|
||||
imports = [
|
||||
../../modules/arch/aarch64.nix
|
||||
../families/qemu.nix
|
||||
];
|
||||
kernel = {
|
||||
config = {
|
||||
VIRTUALIZATION = "y";
|
||||
PCI_HOST_GENERIC="y";
|
||||
module =
|
||||
{ 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";
|
||||
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;
|
||||
};
|
||||
};
|
||||
boot.commandLine = [
|
||||
"console=ttyAMA0,38400"
|
||||
];
|
||||
hardware = let addr = lim.parseInt "0x40010000"; in {
|
||||
loadAddress = addr;
|
||||
entryPoint = addr;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
{
|
||||
system = {
|
||||
crossSystem = {
|
||||
config = "armv7l-unknown-linux-musleabihf";
|
||||
config = "armv7l-unknown-linux-musleabihf";
|
||||
};
|
||||
};
|
||||
|
||||
@@ -24,30 +24,36 @@
|
||||
'';
|
||||
installer = "vmroot";
|
||||
|
||||
module = { config, lim, ... }: {
|
||||
imports = [
|
||||
../../modules/arch/arm.nix
|
||||
../families/qemu.nix
|
||||
];
|
||||
kernel = {
|
||||
config = {
|
||||
PCI_HOST_GENERIC = "y";
|
||||
ARCH_VIRT = "y";
|
||||
module =
|
||||
{ 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";
|
||||
VFP = "y";
|
||||
NEON = "y";
|
||||
AEABI = "y";
|
||||
|
||||
SERIAL_AMBA_PL011 = "y";
|
||||
SERIAL_AMBA_PL011_CONSOLE = "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;
|
||||
};
|
||||
};
|
||||
boot.commandLine = [
|
||||
"console=ttyAMA0"
|
||||
];
|
||||
hardware = let addr = lim.parseInt "0x40008000"; in {
|
||||
loadAddress = addr;
|
||||
entryPoint = addr;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
+43
-34
@@ -7,7 +7,7 @@
|
||||
config = "mips-unknown-linux-musl";
|
||||
gcc = {
|
||||
abi = "32";
|
||||
arch = "mips32"; # maybe mips_24kc-
|
||||
arch = "mips32"; # maybe mips_24kc-
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -36,41 +36,50 @@
|
||||
in the Development manual.
|
||||
|
||||
'';
|
||||
module = { config, lib, lim, ... }: {
|
||||
imports = [
|
||||
../../modules/arch/mipseb.nix
|
||||
../families/qemu.nix
|
||||
];
|
||||
kernel = {
|
||||
config = {
|
||||
MIPS_MALTA= "y";
|
||||
CPU_MIPS32_R2= "y";
|
||||
module =
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
lim,
|
||||
...
|
||||
}:
|
||||
{
|
||||
imports = [
|
||||
../../modules/arch/mipseb.nix
|
||||
../families/qemu.nix
|
||||
];
|
||||
kernel = {
|
||||
config = {
|
||||
MIPS_MALTA = "y";
|
||||
CPU_MIPS32_R2 = "y";
|
||||
|
||||
POWER_RESET = "y";
|
||||
POWER_RESET_SYSCON = "y";
|
||||
POWER_RESET = "y";
|
||||
POWER_RESET_SYSCON = "y";
|
||||
|
||||
SERIAL_8250= "y";
|
||||
SERIAL_8250_CONSOLE= "y";
|
||||
};
|
||||
};
|
||||
hardware =
|
||||
# from arch/mips/mti-malta/Platform:load-$(CONFIG_MIPS_MALTA) += 0xffffffff80100000
|
||||
let addr = lim.parseInt "0x80100000";
|
||||
in {
|
||||
loadAddress = addr;
|
||||
entryPoint = addr;
|
||||
|
||||
# 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";
|
||||
includePaths = [
|
||||
"${config.system.outputs.kernel.modulesupport}/arch/mips/boot/dts/"
|
||||
];
|
||||
SERIAL_8250 = "y";
|
||||
SERIAL_8250_CONSOLE = "y";
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
hardware =
|
||||
# from arch/mips/mti-malta/Platform:load-$(CONFIG_MIPS_MALTA) += 0xffffffff80100000
|
||||
let
|
||||
addr = lim.parseInt "0x80100000";
|
||||
in
|
||||
{
|
||||
loadAddress = addr;
|
||||
entryPoint = addr;
|
||||
|
||||
# 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";
|
||||
includePaths = [
|
||||
"${config.system.outputs.kernel.modulesupport}/arch/mips/boot/dts/"
|
||||
];
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
+381
-367
@@ -33,16 +33,25 @@
|
||||
};
|
||||
};
|
||||
|
||||
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 {
|
||||
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
|
||||
@@ -53,273 +62,275 @@
|
||||
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
|
||||
};
|
||||
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";
|
||||
@@ -340,97 +351,100 @@
|
||||
};
|
||||
};
|
||||
filesystem =
|
||||
let inherit (pkgs.pseudofile) dir symlink;
|
||||
in
|
||||
dir {
|
||||
lib = dir {
|
||||
firmware = dir {
|
||||
mediatek = symlink firmware;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
hardware =
|
||||
let
|
||||
openwrt = pkgs.openwrt;
|
||||
mac80211 = pkgs.kmodloader.override {
|
||||
targets = [
|
||||
"mt7915e"
|
||||
];
|
||||
inherit (config.system.outputs) kernel;
|
||||
};
|
||||
in {
|
||||
# from OEM bootlog (openwrt wiki):
|
||||
# 4 cmdlinepart partitions found on MTD device raspi
|
||||
# Creating 4 MTD partitions on "raspi":
|
||||
# 0x000000000000-0x000000040000 : "uboot"
|
||||
# 0x000000040000-0x000000440000 : "uImage"
|
||||
# 0x000000440000-0x000000ff0000 : "rootfs"
|
||||
# 0x000000ff0000-0x000001000000 : "ART"
|
||||
# from openwrt bootlog (openwrt wiki):
|
||||
# 5 fixed-partitions partitions found on MTD device spi0.0
|
||||
# OF: Bad cell count for /palmbus@1e000000/spi@b00/flash@0/partitions
|
||||
# OF: Bad cell count for /palmbus@1e000000/spi@b00/flash@0/partitions
|
||||
# OF: Bad cell count for /palmbus@1e000000/spi@b00/flash@0/partitions
|
||||
# OF: Bad cell count for /palmbus@1e000000/spi@b00/flash@0/partitions
|
||||
# Creating 5 MTD partitions on "spi0.0":
|
||||
# 0x000000000000-0x000000040000 : "u-boot"
|
||||
# 0x000000040000-0x000000fa0000 : "firmware"
|
||||
# 2 uimage-fw partitions found on MTD device firmware
|
||||
# Creating 2 MTD partitions on "firmware":
|
||||
# 0x000000000000-0x0000002c0000 : "kernel"
|
||||
# 0x0000002c0000-0x000000f60000 : "rootfs"
|
||||
# mtd: setting mtd3 (rootfs) as root device
|
||||
# 1 squashfs-split partitions found on MTD device rootfs
|
||||
# 0x000000640000-0x000000f60000 : "rootfs_data"
|
||||
# 0x000000fa0000-0x000000fb0000 : "config"
|
||||
# 0x000000fb0000-0x000000ff0000 : "tplink"
|
||||
# 0x000000ff0000-0x000001000000 : "radio"
|
||||
flash = {
|
||||
# from the OEM bootlog 'Booting image at bc040000'
|
||||
# (0x40000 from 0xbc000000)
|
||||
address = lim.parseInt "0xbc040000";
|
||||
# 0x000000040000-0x000000fa0000
|
||||
size = lim.parseInt "0xf60000";
|
||||
# TODO: find in /proc/mtd on a running system
|
||||
eraseBlockSize = 65536;
|
||||
};
|
||||
|
||||
# since this is mentioned in the partition table as well?
|
||||
defaultOutput = "tplink-safeloader";
|
||||
# taken from openwrt sysupgrade image:
|
||||
# openwrt-23.05.2-ramips-mt7621-tplink_archer-ax23-v1-squashfs-sysupgrade.bin: u-boot legacy uImage, MIPS OpenWrt Linux-5.15.137, Linux/MIPS, OS Kernel Image (lzma), 2797386 bytes, Tue Nov 14 13:38:11 2023, Load Address: 0X80001000, Entry Point: 0X80001000, Header CRC: 0X19F74C5B, Data CRC: 0XF685563C
|
||||
loadAddress = lim.parseInt "0x80001000";
|
||||
entryPoint = lim.parseInt "0x80001000";
|
||||
rootDevice = "/dev/mtdblock3";
|
||||
dts = {
|
||||
src = "${openwrt.src}/target/linux/ramips/dts/mt7621_tplink_archer-ax23-v1.dts";
|
||||
includePaths = [
|
||||
"${openwrt.src}/target/linux/ramips/dts"
|
||||
"${config.system.outputs.kernel.modulesupport}/arch/arm64/boot/dts/mediatek/"
|
||||
];
|
||||
};
|
||||
|
||||
networkInterfaces =
|
||||
let
|
||||
inherit (config.system.service.network) link;
|
||||
in rec {
|
||||
lan1 = link.build { ifname = "lan1"; };
|
||||
lan2 = link.build { ifname = "lan2"; };
|
||||
lan3 = link.build { ifname = "lan3"; };
|
||||
lan4 = link.build { ifname = "lan4"; };
|
||||
wan = link.build { ifname = "wan"; };
|
||||
|
||||
wlan = link.build {
|
||||
ifname = "wlan0";
|
||||
dependencies = [ mac80211 ];
|
||||
};
|
||||
wlan5 = link.build {
|
||||
ifname = "wlan1";
|
||||
dependencies = [ mac80211 ];
|
||||
let
|
||||
inherit (pkgs.pseudofile) dir symlink;
|
||||
in
|
||||
dir {
|
||||
lib = dir {
|
||||
firmware = dir {
|
||||
mediatek = symlink firmware;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
hardware =
|
||||
let
|
||||
openwrt = pkgs.openwrt;
|
||||
mac80211 = pkgs.kmodloader.override {
|
||||
targets = [
|
||||
"mt7915e"
|
||||
];
|
||||
inherit (config.system.outputs) kernel;
|
||||
};
|
||||
in
|
||||
{
|
||||
# from OEM bootlog (openwrt wiki):
|
||||
# 4 cmdlinepart partitions found on MTD device raspi
|
||||
# Creating 4 MTD partitions on "raspi":
|
||||
# 0x000000000000-0x000000040000 : "uboot"
|
||||
# 0x000000040000-0x000000440000 : "uImage"
|
||||
# 0x000000440000-0x000000ff0000 : "rootfs"
|
||||
# 0x000000ff0000-0x000001000000 : "ART"
|
||||
# from openwrt bootlog (openwrt wiki):
|
||||
# 5 fixed-partitions partitions found on MTD device spi0.0
|
||||
# OF: Bad cell count for /palmbus@1e000000/spi@b00/flash@0/partitions
|
||||
# OF: Bad cell count for /palmbus@1e000000/spi@b00/flash@0/partitions
|
||||
# OF: Bad cell count for /palmbus@1e000000/spi@b00/flash@0/partitions
|
||||
# OF: Bad cell count for /palmbus@1e000000/spi@b00/flash@0/partitions
|
||||
# Creating 5 MTD partitions on "spi0.0":
|
||||
# 0x000000000000-0x000000040000 : "u-boot"
|
||||
# 0x000000040000-0x000000fa0000 : "firmware"
|
||||
# 2 uimage-fw partitions found on MTD device firmware
|
||||
# Creating 2 MTD partitions on "firmware":
|
||||
# 0x000000000000-0x0000002c0000 : "kernel"
|
||||
# 0x0000002c0000-0x000000f60000 : "rootfs"
|
||||
# mtd: setting mtd3 (rootfs) as root device
|
||||
# 1 squashfs-split partitions found on MTD device rootfs
|
||||
# 0x000000640000-0x000000f60000 : "rootfs_data"
|
||||
# 0x000000fa0000-0x000000fb0000 : "config"
|
||||
# 0x000000fb0000-0x000000ff0000 : "tplink"
|
||||
# 0x000000ff0000-0x000001000000 : "radio"
|
||||
flash = {
|
||||
# from the OEM bootlog 'Booting image at bc040000'
|
||||
# (0x40000 from 0xbc000000)
|
||||
address = lim.parseInt "0xbc040000";
|
||||
# 0x000000040000-0x000000fa0000
|
||||
size = lim.parseInt "0xf60000";
|
||||
# TODO: find in /proc/mtd on a running system
|
||||
eraseBlockSize = 65536;
|
||||
};
|
||||
|
||||
# since this is mentioned in the partition table as well?
|
||||
defaultOutput = "tplink-safeloader";
|
||||
# taken from openwrt sysupgrade image:
|
||||
# openwrt-23.05.2-ramips-mt7621-tplink_archer-ax23-v1-squashfs-sysupgrade.bin: u-boot legacy uImage, MIPS OpenWrt Linux-5.15.137, Linux/MIPS, OS Kernel Image (lzma), 2797386 bytes, Tue Nov 14 13:38:11 2023, Load Address: 0X80001000, Entry Point: 0X80001000, Header CRC: 0X19F74C5B, Data CRC: 0XF685563C
|
||||
loadAddress = lim.parseInt "0x80001000";
|
||||
entryPoint = lim.parseInt "0x80001000";
|
||||
rootDevice = "/dev/mtdblock3";
|
||||
dts = {
|
||||
src = "${openwrt.src}/target/linux/ramips/dts/mt7621_tplink_archer-ax23-v1.dts";
|
||||
includePaths = [
|
||||
"${openwrt.src}/target/linux/ramips/dts"
|
||||
"${config.system.outputs.kernel.modulesupport}/arch/arm64/boot/dts/mediatek/"
|
||||
];
|
||||
};
|
||||
|
||||
networkInterfaces =
|
||||
let
|
||||
inherit (config.system.service.network) link;
|
||||
in
|
||||
rec {
|
||||
lan1 = link.build { ifname = "lan1"; };
|
||||
lan2 = link.build { ifname = "lan2"; };
|
||||
lan3 = link.build { ifname = "lan3"; };
|
||||
lan4 = link.build { ifname = "lan4"; };
|
||||
wan = link.build { ifname = "wan"; };
|
||||
|
||||
wlan = link.build {
|
||||
ifname = "wlan0";
|
||||
dependencies = [ mac80211 ];
|
||||
};
|
||||
wlan5 = link.build {
|
||||
ifname = "wlan1";
|
||||
dependencies = [ mac80211 ];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -153,11 +153,18 @@
|
||||
};
|
||||
};
|
||||
|
||||
module = {pkgs, config, lib, lim, ... }:
|
||||
module =
|
||||
{
|
||||
pkgs,
|
||||
config,
|
||||
lib,
|
||||
lim,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (pkgs.liminix.services) oneshot;
|
||||
inherit (pkgs) liminix;
|
||||
mtd_by_name_links = pkgs.liminix.services.oneshot rec {
|
||||
mtd_by_name_links = pkgs.liminix.services.oneshot rec {
|
||||
name = "mtd_by_name_links";
|
||||
up = ''
|
||||
mkdir -p /dev/mtd/by-name
|
||||
@@ -167,7 +174,8 @@
|
||||
done
|
||||
'';
|
||||
};
|
||||
in {
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
../../modules/arch/arm.nix
|
||||
../../modules/outputs/tftpboot.nix
|
||||
@@ -175,6 +183,9 @@
|
||||
];
|
||||
|
||||
config = {
|
||||
rootfsType = lib.mkDefault "btrfs"; # override this if you are building tftpboot
|
||||
rootOptions = lib.mkDefault "subvol=@";
|
||||
|
||||
services.mtd-name-links = mtd_by_name_links;
|
||||
kernel = {
|
||||
src = pkgs.pkgsBuildBuild.fetchurl {
|
||||
@@ -191,7 +202,7 @@
|
||||
CPU_V7 = "y";
|
||||
ARCH_MULTIPLATFORM = "y";
|
||||
ARCH_MVEBU = "y";
|
||||
ARCH_MULTI_V7= "y";
|
||||
ARCH_MULTI_V7 = "y";
|
||||
PCI_MVEBU = "y";
|
||||
AHCI_MVEBU = "y";
|
||||
|
||||
@@ -201,7 +212,6 @@
|
||||
EXPERT = "y";
|
||||
ALLOW_DEV_COREDUMP = "n";
|
||||
|
||||
|
||||
# dts has a compatible for this but dmesg is not
|
||||
# showing it
|
||||
EEPROM_AT24 = "y"; # atmel,24c64
|
||||
@@ -212,44 +222,44 @@
|
||||
|
||||
MACH_ARMADA_38X = "y";
|
||||
SMP = "y";
|
||||
# this is disabled for the moment because it relies on a
|
||||
# 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";
|
||||
NEON = "y";
|
||||
|
||||
# WARNING: unmet direct dependencies detected for ARCH_WANT_LIBATA_LEDS
|
||||
ATA = "y";
|
||||
|
||||
BLOCK = "y";
|
||||
MMC="y";
|
||||
PWRSEQ_EMMC="y"; # ???
|
||||
PWRSEQ_SIMPLE="y"; # ???
|
||||
MMC_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";
|
||||
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_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";
|
||||
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
|
||||
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";
|
||||
@@ -292,6 +302,7 @@
|
||||
};
|
||||
};
|
||||
boot = {
|
||||
loader.extlinux.enable = lib.mkDefault true; # override this if you are building tftpboot
|
||||
commandLine = [
|
||||
"console=ttyS0,115200"
|
||||
"pcie_aspm=off" # ath9k pci incompatible with PCIe ASPM
|
||||
@@ -302,13 +313,14 @@
|
||||
inherit (pkgs.pseudofile) dir symlink;
|
||||
firmware = pkgs.stdenv.mkDerivation {
|
||||
name = "wlan-firmware";
|
||||
phases = ["installPhase"];
|
||||
phases = [ "installPhase" ];
|
||||
installPhase = ''
|
||||
mkdir $out
|
||||
cp -r ${pkgs.linux-firmware}/lib/firmware/ath10k/QCA988X $out
|
||||
'';
|
||||
};
|
||||
in dir {
|
||||
in
|
||||
dir {
|
||||
lib = dir {
|
||||
firmware = dir {
|
||||
ath10k = symlink firmware;
|
||||
@@ -316,10 +328,12 @@
|
||||
};
|
||||
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;
|
||||
let
|
||||
f = pkgs.writeText "fw_env.config" ''
|
||||
/dev/mtd/by-name/u-boot-env 0x0 0x10000 0x10000
|
||||
'';
|
||||
in
|
||||
symlink f;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -329,75 +343,81 @@
|
||||
compressRoot = true;
|
||||
};
|
||||
|
||||
hardware = let
|
||||
mac80211 = pkgs.kmodloader.override {
|
||||
inherit (config.system.outputs) kernel;
|
||||
targets = ["ath9k" "ath10k_pci"];
|
||||
};
|
||||
in {
|
||||
defaultOutput = "updater";
|
||||
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";
|
||||
includePaths = [
|
||||
"${config.system.outputs.kernel.modulesupport}/arch/arm/boot/dts/marvell/"
|
||||
];
|
||||
};
|
||||
flash.eraseBlockSize = 65536; # only used for tftpboot
|
||||
networkInterfaces =
|
||||
let
|
||||
inherit (config.system.service.network) link;
|
||||
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 ];
|
||||
};
|
||||
hardware =
|
||||
let
|
||||
mac80211 = pkgs.kmodloader.override {
|
||||
inherit (config.system.outputs) kernel;
|
||||
targets = [
|
||||
"ath9k"
|
||||
"ath10k_pci"
|
||||
];
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
defaultOutput = "updater";
|
||||
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";
|
||||
includePaths = [
|
||||
"${config.system.outputs.kernel.modulesupport}/arch/arm/boot/dts/marvell/"
|
||||
];
|
||||
};
|
||||
flash.eraseBlockSize = 65536; # only used for tftpboot
|
||||
networkInterfaces =
|
||||
let
|
||||
inherit (config.system.service.network) link;
|
||||
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 ];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
config = "mipsel-unknown-linux-musl";
|
||||
gcc = {
|
||||
abi = "32";
|
||||
arch = "mips32"; # mips32r2?
|
||||
arch = "mips32"; # mips32r2?
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -101,7 +101,14 @@
|
||||
|
||||
'';
|
||||
|
||||
module = { pkgs, config, lib, lim, ...}:
|
||||
module =
|
||||
{
|
||||
pkgs,
|
||||
config,
|
||||
lib,
|
||||
lim,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (pkgs.pseudofile) dir symlink;
|
||||
inherit (pkgs) openwrt;
|
||||
@@ -123,7 +130,8 @@
|
||||
url = "https://github.com/openwrt/mt76/raw/1b88dd07f153b202e57fe29734806744ed006b0e/firmware/mt7915_rom_patch.bin";
|
||||
hash = "sha256-ifriAjWzFACrxVWCANZpUaEZgB/0pdbhnTVQytx6ddg=";
|
||||
};
|
||||
in {
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
# We include it to ensure the bridge functionality
|
||||
# is available on the target kernel.
|
||||
@@ -201,7 +209,8 @@
|
||||
networkInterfaces =
|
||||
let
|
||||
inherit (config.system.service.network) link;
|
||||
in {
|
||||
in
|
||||
{
|
||||
eth = link.build { ifname = "eth0"; };
|
||||
lan = link.build { ifname = "lan"; };
|
||||
wlan0 = link.build {
|
||||
@@ -253,107 +262,110 @@
|
||||
extraPatchPhase = ''
|
||||
${openwrt.applyPatches.ramips}
|
||||
'';
|
||||
config = {
|
||||
config =
|
||||
{
|
||||
|
||||
RALINK = "y";
|
||||
PCI = "y";
|
||||
PHY_MT7621_PCI = "y";
|
||||
PCIE_MT7621 = "y";
|
||||
SOC_MT7621 = "y";
|
||||
CLK_MT7621 = "y";
|
||||
CLOCKSOURCE_WATCHDOG = "y";
|
||||
RALINK = "y";
|
||||
PCI = "y";
|
||||
PHY_MT7621_PCI = "y";
|
||||
PCIE_MT7621 = "y";
|
||||
SOC_MT7621 = "y";
|
||||
CLK_MT7621 = "y";
|
||||
CLOCKSOURCE_WATCHDOG = "y";
|
||||
|
||||
SERIAL_8250_CONSOLE = "y";
|
||||
SERIAL_8250 = "y";
|
||||
SERIAL_CORE_CONSOLE = "y";
|
||||
SERIAL_OF_PLATFORM = "y";
|
||||
SERIAL_8250_NR_UARTS = "3";
|
||||
SERIAL_8250_RUNTIME_UARTS = "3";
|
||||
SERIAL_MCTRL_GPIO = "y";
|
||||
SERIAL_8250_CONSOLE = "y";
|
||||
SERIAL_8250 = "y";
|
||||
SERIAL_CORE_CONSOLE = "y";
|
||||
SERIAL_OF_PLATFORM = "y";
|
||||
SERIAL_8250_NR_UARTS = "3";
|
||||
SERIAL_8250_RUNTIME_UARTS = "3";
|
||||
SERIAL_MCTRL_GPIO = "y";
|
||||
|
||||
CONSOLE_LOGLEVEL_DEFAULT = "8";
|
||||
CONSOLE_LOGLEVEL_QUIET = "4";
|
||||
CONSOLE_LOGLEVEL_DEFAULT = "8";
|
||||
CONSOLE_LOGLEVEL_QUIET = "4";
|
||||
|
||||
# MTD_UBI_BEB_LIMIT = "20";
|
||||
# MTD_UBI_WL_THRESHOLD = "4096";
|
||||
# MTD_UBI_BEB_LIMIT = "20";
|
||||
# MTD_UBI_WL_THRESHOLD = "4096";
|
||||
|
||||
MTD = "y";
|
||||
MTD_BLOCK = "y"; # fix undefined ref to register_mtd_blktrans_dev
|
||||
MTD_RAW_NAND = "y";
|
||||
MTD_NAND_MT7621 = "y";
|
||||
MTD_NAND_MTK_BMT = "y"; # Bad-block Management Table
|
||||
MTD_NAND_ECC_SW_HAMMING= "y";
|
||||
MTD_SPI_NAND= "y";
|
||||
MTD_OF_PARTS = "y";
|
||||
MTD_NAND_CORE= "y";
|
||||
MTD_SPLIT_FIRMWARE= "y";
|
||||
MTD_SPLIT_FIT_FW= "y";
|
||||
MTD = "y";
|
||||
MTD_BLOCK = "y"; # fix undefined ref to register_mtd_blktrans_dev
|
||||
MTD_RAW_NAND = "y";
|
||||
MTD_NAND_MT7621 = "y";
|
||||
MTD_NAND_MTK_BMT = "y"; # Bad-block Management Table
|
||||
MTD_NAND_ECC_SW_HAMMING = "y";
|
||||
MTD_SPI_NAND = "y";
|
||||
MTD_OF_PARTS = "y";
|
||||
MTD_NAND_CORE = "y";
|
||||
MTD_SPLIT_FIRMWARE = "y";
|
||||
MTD_SPLIT_FIT_FW = "y";
|
||||
|
||||
PINCTRL = "y";
|
||||
PINCTRL_MT7621 = "y";
|
||||
PINCTRL = "y";
|
||||
PINCTRL_MT7621 = "y";
|
||||
|
||||
I2C = "y";
|
||||
I2C_MT7621 = "y";
|
||||
I2C = "y";
|
||||
I2C_MT7621 = "y";
|
||||
|
||||
SPI = "y";
|
||||
MTD_SPI_NOR = "y";
|
||||
SPI_MT7621 = "y";
|
||||
SPI_MASTER = "y";
|
||||
SPI_MEM = "y";
|
||||
SPI = "y";
|
||||
MTD_SPI_NOR = "y";
|
||||
SPI_MT7621 = "y";
|
||||
SPI_MASTER = "y";
|
||||
SPI_MEM = "y";
|
||||
|
||||
REGULATOR = "y";
|
||||
REGULATOR_FIXED_VOLTAGE = "y";
|
||||
RESET_CONTROLLER = "y";
|
||||
POWER_RESET = "y";
|
||||
POWER_RESET_GPIO = "y";
|
||||
POWER_SUPPLY = "y";
|
||||
LED_TRIGGER_PHY = "y";
|
||||
REGULATOR = "y";
|
||||
REGULATOR_FIXED_VOLTAGE = "y";
|
||||
RESET_CONTROLLER = "y";
|
||||
POWER_RESET = "y";
|
||||
POWER_RESET_GPIO = "y";
|
||||
POWER_SUPPLY = "y";
|
||||
LED_TRIGGER_PHY = "y";
|
||||
|
||||
PCI_DISABLE_COMMON_QUIRKS = "y";
|
||||
PCI_DOMAINS = "y";
|
||||
PCI_DOMAINS_GENERIC = "y";
|
||||
PCI_DRIVERS_GENERIC = "y";
|
||||
PCS_MTK_LYNXI = "y";
|
||||
PCI_DISABLE_COMMON_QUIRKS = "y";
|
||||
PCI_DOMAINS = "y";
|
||||
PCI_DOMAINS_GENERIC = "y";
|
||||
PCI_DRIVERS_GENERIC = "y";
|
||||
PCS_MTK_LYNXI = "y";
|
||||
|
||||
SOC_BUS = "y";
|
||||
SOC_BUS = "y";
|
||||
|
||||
NET = "y";
|
||||
ETHERNET = "y";
|
||||
WLAN = "y";
|
||||
NET = "y";
|
||||
ETHERNET = "y";
|
||||
WLAN = "y";
|
||||
|
||||
PHYLIB = "y";
|
||||
AT803X_PHY = "y";
|
||||
FIXED_PHY = "y";
|
||||
GENERIC_PHY = "y";
|
||||
NET_DSA = "y";
|
||||
NET_DSA_MT7530 = "y";
|
||||
NET_DSA_MT7530_MDIO = "y";
|
||||
NET_DSA_TAG_MTK = "y";
|
||||
NET_MEDIATEK_SOC = "y";
|
||||
NET_SWITCHDEV = "y";
|
||||
NET_VENDOR_MEDIATEK = "y";
|
||||
PHYLIB = "y";
|
||||
AT803X_PHY = "y";
|
||||
FIXED_PHY = "y";
|
||||
GENERIC_PHY = "y";
|
||||
NET_DSA = "y";
|
||||
NET_DSA_MT7530 = "y";
|
||||
NET_DSA_MT7530_MDIO = "y";
|
||||
NET_DSA_TAG_MTK = "y";
|
||||
NET_MEDIATEK_SOC = "y";
|
||||
NET_SWITCHDEV = "y";
|
||||
NET_VENDOR_MEDIATEK = "y";
|
||||
|
||||
SWPHY = "y";
|
||||
SWPHY = "y";
|
||||
|
||||
GPIOLIB = "y";
|
||||
GPIO_MT7621 = "y";
|
||||
OF_GPIO = "y";
|
||||
GPIOLIB = "y";
|
||||
GPIO_MT7621 = "y";
|
||||
OF_GPIO = "y";
|
||||
|
||||
EARLY_PRINTK = "y";
|
||||
EARLY_PRINTK = "y";
|
||||
|
||||
NEW_LEDS = "y";
|
||||
LEDS_TRIGGERS = "y";
|
||||
LEDS_CLASS = "y"; # required by rt2x00lib
|
||||
LEDS_CLASS_MULTICOLOR = "y";
|
||||
LEDS_BRIGHTNESS_HW_CHANGED = "y";
|
||||
NEW_LEDS = "y";
|
||||
LEDS_TRIGGERS = "y";
|
||||
LEDS_CLASS = "y"; # required by rt2x00lib
|
||||
LEDS_CLASS_MULTICOLOR = "y";
|
||||
LEDS_BRIGHTNESS_HW_CHANGED = "y";
|
||||
|
||||
PRINTK_TIME = "y";
|
||||
} // lib.optionalAttrs (config.system.service ? vlan) {
|
||||
SWCONFIG = "y";
|
||||
} // lib.optionalAttrs (config.system.service ? watchdog) {
|
||||
RALINK_WDT = "y"; # watchdog
|
||||
MT7621_WDT = "y"; # or it might be this one
|
||||
};
|
||||
PRINTK_TIME = "y";
|
||||
}
|
||||
// lib.optionalAttrs (config.system.service ? vlan) {
|
||||
SWCONFIG = "y";
|
||||
}
|
||||
// lib.optionalAttrs (config.system.service ? watchdog) {
|
||||
RALINK_WDT = "y"; # watchdog
|
||||
MT7621_WDT = "y"; # or it might be this one
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
{ stdenv,
|
||||
lib,
|
||||
liminix,
|
||||
gnumake,
|
||||
fennel,
|
||||
pandoc,
|
||||
luaPackages,
|
||||
asciidoctor,
|
||||
borderVmConf
|
||||
}:
|
||||
let
|
||||
json =
|
||||
(import liminix {
|
||||
inherit borderVmConf;
|
||||
device = import (liminix + "/devices/qemu");
|
||||
liminix-config =
|
||||
{ ... }:
|
||||
{
|
||||
imports = [ ./modules/all-modules.nix ];
|
||||
};
|
||||
}).outputs.optionsJson;
|
||||
in
|
||||
stdenv.mkDerivation {
|
||||
name = "liminix-doc";
|
||||
nativeBuildInputs = [
|
||||
gnumake
|
||||
fennel
|
||||
pandoc
|
||||
asciidoctor
|
||||
luaPackages.lyaml
|
||||
];
|
||||
|
||||
src = lib.sources.sourceFilesBySuffices
|
||||
(lib.cleanSource ./. ) [
|
||||
".adoc"
|
||||
".nix" ".rst" "Makefile" ".svg"
|
||||
".fnl" ".py" ".css" ".html"
|
||||
".md" ".html.in"
|
||||
];
|
||||
|
||||
buildPhase = ''
|
||||
cat ${json} | fennel --correlate doc/parse-options.fnl > doc/module-options-generated.inc.rst
|
||||
cat ${json} | fennel --correlate doc/parse-options-outputs.fnl > doc/outputs-generated.inc.rst
|
||||
cp ${(import ./doc/hardware.nix)} doc/hardware.rst
|
||||
make -C doc html
|
||||
'';
|
||||
installPhase = ''
|
||||
mkdir -p $out/nix-support
|
||||
cd doc
|
||||
make install prefix=$out
|
||||
ln -s ${json} $out/options.json
|
||||
echo "file source-dist \"$out/share/doc/liminix\"" \
|
||||
> $out/nix-support/hydra-build-products
|
||||
'';
|
||||
}
|
||||
+19
-20
@@ -1,24 +1,23 @@
|
||||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
DOCS=\
|
||||
admin \
|
||||
configuration \
|
||||
development \
|
||||
index \
|
||||
installation \
|
||||
intro \
|
||||
hardware \
|
||||
module-options-generated.inc \
|
||||
outputs-generated.inc \
|
||||
tutorial
|
||||
|
||||
# You can set these variables from the command line, and also
|
||||
# from the environment for the first two.
|
||||
SPHINXOPTS ?=
|
||||
SPHINXBUILD ?= sphinx-build
|
||||
SOURCEDIR = .
|
||||
BUILDDIR = _build
|
||||
%.adoc: %.rst
|
||||
pandoc -f rst -t asciidoc $< | sed -E -e 's/^(=*) /=\1 /g' > $@
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
hardware.adoc: hardware.nix
|
||||
|
||||
hardware.rst: hardware.nix
|
||||
@rm -f hardware.rst || true
|
||||
@cp $$(nix-build hardware.nix) hardware.rst
|
||||
html: Makefile $(patsubst %,%.adoc,$(DOCS))
|
||||
asciidoctor -D _build -d book index.adoc
|
||||
|
||||
.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).
|
||||
html: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
install:
|
||||
mkdir -p $(prefix)/share/doc/liminix
|
||||
cp -a _build/* $(prefix)/share/doc/liminix
|
||||
|
||||
+352
@@ -0,0 +1,352 @@
|
||||
== System Administration
|
||||
|
||||
=== Services on a running system
|
||||
|
||||
Liminix services are built on s6-rc, which is itself layered on s6.
|
||||
Services are defined at build time in your configuration (see
|
||||
<<_services>> for information) and can't be added
|
||||
to/changed at runtime, but to monitor events or diagnose problems you
|
||||
may need to inspect them on the running system. Here are some of the
|
||||
most commonly used s6,-rc commands:
|
||||
|
||||
.Service management quick reference
|
||||
[width="100%",cols="55%,45%",options="header",]
|
||||
|===
|
||||
|What |How
|
||||
|List all running services |`+s6-rc -a list+`
|
||||
|
||||
|List all services that are *not* running |`+s6-rc -da list+`
|
||||
|
||||
|List services that `+wombat+` depends on
|
||||
|`+s6-rc-db dependencies wombat+`
|
||||
|
||||
|... transitively |`+s6-rc-db all-dependencies wombat+`
|
||||
|
||||
|List services that depend on service `+wombat+`
|
||||
|`+s6-rc-db -d dependencies wombat+`
|
||||
|
||||
|... transitively |`+s6-rc-db -d all-dependencies wombat+`
|
||||
|
||||
|Stop service `+wombat+` and everything depending on it
|
||||
|`+s6-rc -d change wombat+`
|
||||
|
||||
|Start service `+wombat+` (but not any services depending on it)
|
||||
|`+s6-rc -u change wombat+`
|
||||
|
||||
|Start service `+wombat+` and all* services depending on it
|
||||
|`+s6-rc-up-tree wombat+`
|
||||
|===
|
||||
|
||||
`+s6-rc-up-tree+` brings up a service and all services that depend on
|
||||
it, except for any services that depend on a "controlled" service that
|
||||
is not currently running. Controlled services are not started at boot
|
||||
time but in response to external events (e.g. plugging in a particular
|
||||
piece of hardware) so you probably don't want to be starting them by
|
||||
hand if the conditions aren't there.
|
||||
|
||||
A service may be *up* or *down* (there are no intermediate states like
|
||||
"started" or "stopping" or "dying" or "cogitating"). Some (but not all)
|
||||
services have "readiness" notifications: the dependents of a service
|
||||
with a readiness notification won't be started until the service signals
|
||||
(by writing to a nominated file descriptor) that it's prepared to start
|
||||
work. Most services defined by Liminix also have a `+timeout-up+`
|
||||
parameter, which means that if a service has readiness notifications and
|
||||
doesn't become ready in the allotted time (defaults 20 seconds) it will
|
||||
be terminated and its state set to *down*.
|
||||
|
||||
If the process providing a service dies, it will be restarted
|
||||
automatically. Liminix does not automatically set it to *down*.
|
||||
|
||||
(If the process providing a service dies without ever notifying
|
||||
readiness, Liminix will restart it as many times as it has to until the
|
||||
timeout period elapses, and then stop it and mark it down.)
|
||||
|
||||
==== Controlled services
|
||||
|
||||
*Controlled* services are those which are started/stopped on demand by a
|
||||
*controller* (another service) instead of being started at boot time.
|
||||
For example:
|
||||
|
||||
* `+svc.uevent-rule.build+` creates a controlled service which is active
|
||||
when a particular hardware device (identified by uevent/sysfs directory)
|
||||
is present.
|
||||
* `+svc.round-robin.build+` creates a service controller that invokes
|
||||
two or more services in turn, running the next one when the process
|
||||
providing the previous one exits. We use this for failover from one
|
||||
network connection to a backup connection, for example.
|
||||
* `+svc.health-check.build+` creates a service controller that runs a
|
||||
controlled service and periodically tests whether it is healthy by
|
||||
running an external health check command or script. If the check command
|
||||
repeatedly fails, the controlled service is restarted.
|
||||
+
|
||||
The Configuration section of the manual describes controlled services in
|
||||
more detail. Some operational considerations
|
||||
* `+round-robin+` detects a service status by looking at its `+outputs+`
|
||||
directory, so it won't work unless the service creates some outputs.
|
||||
This is considered a bug and will be fixed in a future release
|
||||
* `+health-check+` works for longruns but not for oneshots, as it
|
||||
internally relies on `+s6-svc+` to restart the process
|
||||
|
||||
==== Logs
|
||||
|
||||
Logs for all services are collated into `+/run/log/current+`. The log
|
||||
file is rotated when it reaches a threshold size, into another file in
|
||||
the same directory whose name contains a TAI64 timestamp.
|
||||
|
||||
Each log line is prefixed with a TAI64 timestamp and the name of the
|
||||
service, if it is a longrun. If it is a oneshot, a timestamp and the
|
||||
name of some other service. To convert the timestamp into a
|
||||
human-readable format, use `+s6-tai64nlocal+`.
|
||||
|
||||
[source,console]
|
||||
----
|
||||
# ls -l /run/log/
|
||||
-rw-r--r-- 1 0 lock
|
||||
-rw-r--r-- 1 0 state
|
||||
-rwxr--r-- 1 98059 @4000000000025cb629c311ac.s
|
||||
-rwxr--r-- 1 98061 @40000000000260f7309c7fb4.s
|
||||
-rwxr--r-- 1 98041 @40000000000265233a6cc0b6.s
|
||||
-rwxr--r-- 1 98019 @400000000002695d10c06929.s
|
||||
-rwxr--r-- 1 98064 @4000000000026d84189559e0.s
|
||||
-rwxr--r-- 1 98055 @40000000000271ce1e031d91.s
|
||||
-rwxr--r-- 1 98054 @400000000002760229733626.s
|
||||
-rwxr--r-- 1 98104 @4000000000027a2e3b6f4e12.s
|
||||
-rwxr--r-- 1 98023 @4000000000027e6f0ed24a6c.s
|
||||
-rw-r--r-- 1 42374 current
|
||||
|
||||
# tail -2 /run/log/current
|
||||
@40000000000284f130747343 wan.link.pppoe Connect: ppp0 <--> /dev/pts/0
|
||||
@40000000000284f230acc669 wan.link.pppoe sent [LCP ConfReq id=0x1 <asyncmap 0x0> <magic 0x667a9594> <pcomp> <accomp>]
|
||||
# tail -2 /run/log/current | s6-tai64nlocal
|
||||
1970-01-02 21:51:45.828598156 wan.link.pppoe sent [LCP ConfReq id=0x1 <asyncmap 0x0> <magic 0x667a9594> <pcomp> <accom
|
||||
p>]
|
||||
1970-01-02 21:51:48.832588765 wan.link.pppoe sent [LCP ConfReq id=0x1 <asyncmap 0x0> <magic 0x667a9594> <pcomp> <accom
|
||||
p>]
|
||||
----
|
||||
|
||||
===== Log persistence
|
||||
|
||||
Logs written to `+/run/log/+` will not survive a reboot or crash, as it
|
||||
is an ephemeral filesystem.
|
||||
|
||||
On supported hardware you can enable logging to
|
||||
https://www.kernel.org/doc/Documentation/ABI/testing/pstore[pstore]
|
||||
which means the most recent log messages will be preserved on reboot.
|
||||
Set the config option `+logging.persistent.enable = true+` to log
|
||||
messages to `+/dev/pmsg0+` as well as to the regular log. This is a
|
||||
circular buffer, so when it fills up newer messages will overwrite the
|
||||
oldest messages.
|
||||
|
||||
Logs found in pstore after a reboot will be moved at startup to
|
||||
`+/run/log/previous-boot+`
|
||||
|
||||
=== Updating an installed system
|
||||
|
||||
If your system has a writable root filesystem (JFFS2, btrfs etc
|
||||
-anything but squashfs), we have mechanisms for in-places updates
|
||||
analogous to `+nixos-rebuild+`, but the operation is a bit different
|
||||
because it expects to run on a build machine and then copy to the host
|
||||
device using `+ssh+`.
|
||||
|
||||
To use this, build the `+outputs.updater+` target and then run the
|
||||
`+update.sh+` script it generates.
|
||||
|
||||
[source,console]
|
||||
----
|
||||
nix-build -I liminix-config=./my-configuration.nix \
|
||||
--arg device "import ./devices/mydevice" \
|
||||
-A outputs.updater
|
||||
./result/bin/update.sh root@the-device
|
||||
----
|
||||
|
||||
The update script uses min-copy-closure to copy new or changed packages
|
||||
to the device, then (perhaps) reboots it. The reboot behaviour can be
|
||||
affected by flags:
|
||||
|
||||
* [.title-ref]#--no-reboot# will cause it not to reboot at all, if you
|
||||
would rather do that yourself. Note that none of the newly-installed or
|
||||
updated services will be running until you do.
|
||||
* [.title-ref]#--fast# causes it tn not do a full reboot, but instead to
|
||||
restart only the services that have been changed. This will restart all
|
||||
of the services that have updated store paths (and anything that depends
|
||||
on them), but will not affect services that haven't changed.
|
||||
|
||||
It doesn't delete old packages automatically: to do that run
|
||||
`+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 there is little flash storage or if a lot of things have
|
||||
changed (e.g. a new version of nixpkgs).
|
||||
* it may not be able to upgrade the kernel: this is device-dependent. If
|
||||
your device boots from a kernel image on a raw MTD partition or or UBI
|
||||
volume, update.sh is unable to alter the kernel partition. If your
|
||||
device boots from a kernel inside the filesystem (e.g. using
|
||||
bootloader.extlinux or bootloder.fit) then the kernel will be upgraded
|
||||
along with the userland
|
||||
|
||||
==== Recovery/downgrades
|
||||
|
||||
The `+update.sh+` script also creates a timestamped symlink on the
|
||||
device which points to the system configuration it installs. If you
|
||||
install a configuration that doesn't work, you can revert to any other
|
||||
installed configuration by
|
||||
|
||||
[arabic]
|
||||
. booting to some kind of rescue or recovery system (which may be some
|
||||
vendor-provided rescue option, or your own recovery system perhaps based
|
||||
on `+examples/recovery.nix+`) and mounting your Liminix filesystem on
|
||||
/mnt
|
||||
. picking another previously-installed configuration that _link:[did]
|
||||
work, and switching back to it:
|
||||
|
||||
[source,console]
|
||||
----
|
||||
# ls -ld /mnt/*configuration
|
||||
lrwxrwxrwx 1 90 /mnt/20252102T182104.configuration -> nix/store/v1w0h4zw65ah4c2r0k7nyy125qrxhq78-system-configuration-aarch64-unknown-linux-musl
|
||||
lrwxrwxrwx 1 90 /mnt/20251802T181822.configuration -> nix/store/wqjl9s9xljl2wg8257292zghws9ssidk-system-configuration-aarch64-unknown-linux-musl
|
||||
# : 20251802T181822 is the working system, so reinstall it
|
||||
# /mnt/20251802T181822.configuration/bin/install /mnt
|
||||
# umount /mnt
|
||||
# reboot
|
||||
----
|
||||
|
||||
This will install the previous configuration's activation binary into
|
||||
/bin, and copy its kernel and initramfs into /boot. Note that it depends
|
||||
on the previous system not having been garbage-collected.
|
||||
|
||||
==== Adding packages
|
||||
|
||||
If you simply wish to add a package without any change to services, you
|
||||
can call `+min-copy-closure+` directly to install any package in Nixpkgs
|
||||
or in the Liminix overlay
|
||||
|
||||
[source,console]
|
||||
----
|
||||
nix-build -I liminix-config=./my-configuration.nix \
|
||||
--arg device "import ./devices/mydevice" -A pkgs.tcpdump
|
||||
|
||||
nix-shell -p min-copy-closure root@the-device result/
|
||||
----
|
||||
|
||||
Note that this only copies the package and its dependencies to the
|
||||
device: it doesn't update any profile to add it to `+$PATH+`
|
||||
|
||||
[reftext="Levitate"]
|
||||
[[levitate]]
|
||||
=== Levitate: Reinstalling on a running system
|
||||
|
||||
Liminix is initially installed from a monolithic `+firmware.bin+` - and
|
||||
unless you're running a writable filesystem, the only way to update it
|
||||
is to build and install a whole new `+firmware.bin+`. However, you
|
||||
probably would prefer not to have to remove it from its installation
|
||||
site, unplug it from the network and stick serial cables in it all over
|
||||
again.
|
||||
|
||||
It is not (generally) safe to install a new firmware onto the flash
|
||||
partitions that the active system is running on. To address this we have
|
||||
`+levitate+`, which a way for a running Liminix system to "soft restart"
|
||||
into a ramdisk running only a limited set of services, so that the main
|
||||
partitions can then be safely flashed.
|
||||
|
||||
==== Configuration
|
||||
|
||||
Levitate _needs to be configured when you create the initial system_ to
|
||||
specify which services/packages/etc to run in maintenance mode. Most
|
||||
likely you want to configure a network interface and an ssh for example
|
||||
so that you can login to reflash it.
|
||||
|
||||
[source,nix]
|
||||
----
|
||||
defaultProfile.packages = with pkgs; [
|
||||
...
|
||||
(levitate.override {
|
||||
config = {
|
||||
services = {
|
||||
inherit (config.services) dhcpc sshd watchdog;
|
||||
};
|
||||
defaultProfile.packages = [ mtdutils ];
|
||||
users.root = config.users.root;
|
||||
};
|
||||
})
|
||||
];
|
||||
----
|
||||
|
||||
==== Use
|
||||
|
||||
Connect (with ssh, probably) to the running Liminix system that you wish
|
||||
to upgrade.
|
||||
|
||||
[source,console]
|
||||
----
|
||||
bash$ ssh root@the-device
|
||||
----
|
||||
|
||||
Run `+levitate+`. This takes a little while (perhaps a few tens of
|
||||
seconds) to execute, and copies all config required for maintenance mode
|
||||
to `+/run/maintenance+`.
|
||||
|
||||
[source,console]
|
||||
----
|
||||
# levitate
|
||||
----
|
||||
|
||||
Reboot into maintenance mode. You will be logged out
|
||||
|
||||
[source,console]
|
||||
----
|
||||
# reboot
|
||||
----
|
||||
|
||||
Connect to the device again - note that the ssh host key will have
|
||||
changed.
|
||||
|
||||
[source,console]
|
||||
----
|
||||
# ssh -o UserKnownHostsFile=/dev/null root@the-device
|
||||
----
|
||||
|
||||
Check we're in maintenance mode
|
||||
|
||||
[source,console]
|
||||
----
|
||||
# cat /etc/banner
|
||||
|
||||
LADIES AND GENTLEMEN WE ARE FLOATING IN SPACE
|
||||
|
||||
Most services are disabled. The system is operating
|
||||
with a ram-based root filesystem, making it safe to
|
||||
overwrite the flash devices in order to perform
|
||||
upgrades and maintenance.
|
||||
|
||||
Don't forget to reboot when you have finished.
|
||||
----
|
||||
|
||||
Perform the upgrade, using flashcp. This is an example, your device will
|
||||
differ
|
||||
|
||||
[source,console]
|
||||
----
|
||||
# cat /proc/mtd
|
||||
dev: size erasesize name
|
||||
mtd0: 00030000 00010000 "u-boot"
|
||||
mtd1: 00010000 00010000 "u-boot-env"
|
||||
mtd2: 00010000 00010000 "factory"
|
||||
mtd3: 00f80000 00010000 "firmware"
|
||||
mtd4: 00220000 00010000 "kernel"
|
||||
mtd5: 00d60000 00010000 "rootfs"
|
||||
mtd6: 00010000 00010000 "art"
|
||||
# flashcp -v firmware.bin mtd:firmware
|
||||
----
|
||||
|
||||
All done
|
||||
|
||||
[source,console]
|
||||
----
|
||||
# reboot
|
||||
----
|
||||
-342
@@ -1,342 +0,0 @@
|
||||
System Administration
|
||||
#####################
|
||||
|
||||
Services on a running system
|
||||
****************************
|
||||
|
||||
Liminix services are built on s6-rc, which is itself layered on s6.
|
||||
Services are defined at build time in your configuration (see
|
||||
:ref:`configuration-services` for information) and can't be added
|
||||
to/changed at runtime, but to monitor
|
||||
events or diagnose problems you may need to inspect them on the
|
||||
running system. Here are some of the most commonly used s6,-rc
|
||||
commands:
|
||||
|
||||
.. list-table:: Service management quick reference
|
||||
:widths: 55 45
|
||||
:header-rows: 1
|
||||
|
||||
* - What
|
||||
- How
|
||||
* - List all running services
|
||||
- ``s6-rc -a list``
|
||||
* - List all services that are **not** running
|
||||
- ``s6-rc -da list``
|
||||
* - List services that ``wombat`` depends on
|
||||
- ``s6-rc-db dependencies wombat``
|
||||
* - ... transitively
|
||||
- ``s6-rc-db all-dependencies wombat``
|
||||
* - List services that depend on service ``wombat``
|
||||
- ``s6-rc-db -d dependencies wombat``
|
||||
* - ... transitively
|
||||
- ``s6-rc-db -d all-dependencies wombat``
|
||||
* - Stop service ``wombat`` and everything depending on it
|
||||
- ``s6-rc -d change wombat``
|
||||
* - Start service ``wombat`` (but not any services depending on it)
|
||||
- ``s6-rc -u change wombat``
|
||||
* - Start service ``wombat`` and all* services depending on it
|
||||
- ``s6-rc-up-tree wombat``
|
||||
|
||||
:command:`s6-rc-up-tree` brings up a service and all services that
|
||||
depend on it, except for any services that depend on a "controlled"
|
||||
service that is not currently running. Controlled services are not
|
||||
started at boot time but in response to external events (e.g. plugging
|
||||
in a particular piece of hardware) so you probably don't want to be
|
||||
starting them by hand if the conditions aren't there.
|
||||
|
||||
A service may be **up** or **down** (there are no intermediate states
|
||||
like "started" or "stopping" or "dying" or "cogitating"). Some (but
|
||||
not all) services have "readiness" notifications: the dependents of a
|
||||
service with a readiness notification won't be started until the
|
||||
service signals (by writing to a nominated file descriptor) that it's
|
||||
prepared to start work. Most services defined by Liminix also have a
|
||||
``timeout-up`` parameter, which means that if a service has readiness
|
||||
notifications and doesn't become ready in the allotted time (defaults
|
||||
20 seconds) it will be terminated and its state set to **down**.
|
||||
|
||||
If the process providing a service dies, it will be restarted
|
||||
automatically. Liminix does not automatically set it to **down**.
|
||||
|
||||
(If the process providing a service dies without ever notifying
|
||||
readiness, Liminix will restart it as many times as it has to until the
|
||||
timeout period elapses, and then stop it and mark it down.)
|
||||
|
||||
Controlled services
|
||||
===================
|
||||
|
||||
**Controlled** services are those which are started/stopped on demand
|
||||
by a **controller** (another service) instead of being started at boot
|
||||
time. For example:
|
||||
|
||||
* ``svc.uevent-rule.build`` creates a controlled service which is
|
||||
active when a particular hardware device (identified by uevent/sysfs
|
||||
directory) is present.
|
||||
|
||||
* ``svc.round-robin.build`` creates a service controller that
|
||||
invokes two or more services in turn, running the next one when the
|
||||
process providing the previous one exits. We use this for failover
|
||||
from one network connection to a backup connection, for example.
|
||||
|
||||
* ``svc.health-check.build`` creates a service controller that
|
||||
runs a controlled service and periodically tests whether it is
|
||||
healthy by running an external health check command or script. If the
|
||||
check command repeatedly fails, the controlled service is
|
||||
restarted.
|
||||
|
||||
The Configuration section of the manual describes controlled
|
||||
services in more detail. Some operational considerations
|
||||
|
||||
* ``round-robin`` detects a service status by looking at its
|
||||
:file:`outputs` directory, so it won't work unless the service
|
||||
creates some outputs. This is considered a bug and will be
|
||||
fixed in a future release
|
||||
|
||||
* ``health-check`` works for longruns but not for oneshots, as it
|
||||
internally relies on ``s6-svc`` to restart the process
|
||||
|
||||
Logs
|
||||
====
|
||||
|
||||
Logs for all services are collated into :file:`/run/log/current`.
|
||||
The log file is rotated when it reaches a threshold size, into another
|
||||
file in the same directory whose name contains a TAI64 timestamp.
|
||||
|
||||
Each log line is prefixed with a TAI64 timestamp and the name of the
|
||||
service, if it is a longrun. If it is a oneshot, a timestamp and the
|
||||
name of some other service. To convert the timestamp into a
|
||||
human-readable format, use :command:`s6-tai64nlocal`.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# ls -l /run/log/
|
||||
-rw-r--r-- 1 0 lock
|
||||
-rw-r--r-- 1 0 state
|
||||
-rwxr--r-- 1 98059 @4000000000025cb629c311ac.s
|
||||
-rwxr--r-- 1 98061 @40000000000260f7309c7fb4.s
|
||||
-rwxr--r-- 1 98041 @40000000000265233a6cc0b6.s
|
||||
-rwxr--r-- 1 98019 @400000000002695d10c06929.s
|
||||
-rwxr--r-- 1 98064 @4000000000026d84189559e0.s
|
||||
-rwxr--r-- 1 98055 @40000000000271ce1e031d91.s
|
||||
-rwxr--r-- 1 98054 @400000000002760229733626.s
|
||||
-rwxr--r-- 1 98104 @4000000000027a2e3b6f4e12.s
|
||||
-rwxr--r-- 1 98023 @4000000000027e6f0ed24a6c.s
|
||||
-rw-r--r-- 1 42374 current
|
||||
|
||||
# tail -2 /run/log/current
|
||||
@40000000000284f130747343 wan.link.pppoe Connect: ppp0 <--> /dev/pts/0
|
||||
@40000000000284f230acc669 wan.link.pppoe sent [LCP ConfReq id=0x1 <asyncmap 0x0> <magic 0x667a9594> <pcomp> <accomp>]
|
||||
# tail -2 /run/log/current | s6-tai64nlocal
|
||||
1970-01-02 21:51:45.828598156 wan.link.pppoe sent [LCP ConfReq id=0x1 <asyncmap 0x0> <magic 0x667a9594> <pcomp> <accom
|
||||
p>]
|
||||
1970-01-02 21:51:48.832588765 wan.link.pppoe sent [LCP ConfReq id=0x1 <asyncmap 0x0> <magic 0x667a9594> <pcomp> <accom
|
||||
p>]
|
||||
|
||||
Log persistence
|
||||
---------------
|
||||
|
||||
Logs written to :file:`/run/log/` will not survive a reboot or crash,
|
||||
as it is an ephemeral filesystem.
|
||||
|
||||
On supported hardware you can enable logging to `pstore
|
||||
<https://www.kernel.org/doc/Documentation/ABI/testing/pstore>`_ which
|
||||
means the most recent log messages will be preserved on reboot. Set
|
||||
the config option ``logging.persistent.enable = true`` to log messages
|
||||
to :file:`/dev/pmsg0` as well as to the regular log. This is a
|
||||
circular buffer, so when it fills up newer messages will overwrite the
|
||||
oldest messages.
|
||||
|
||||
To check the previous messages after a (planned or forced) reboot,
|
||||
you need to mooun the pstore filesystem.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# mount -t pstore pstore /sys/fs/pstore/
|
||||
# ls -l /sys/fs/pstore/
|
||||
-r--r--r-- 1 43071 pmsg-ramoops-0
|
||||
# cat /sys/fs/pstore/pmsg-ramoops-0
|
||||
@40000000000000282c997d29 mydevice klogd <6>[ 30.793756] int: port 2(wlan0) entered blocking state
|
||||
[log messages from before the reboot follow]
|
||||
|
||||
|
||||
|
||||
Updating an installed system
|
||||
****************************
|
||||
|
||||
If your system has a writable root filesystem (JFFS2, btrfs etc -
|
||||
anything but squashfs), we have mechanisms for in-places updates
|
||||
analogous to :command:`nixos-rebuild`, but the operation is a bit
|
||||
different because it expects to run on a build machine and then copy
|
||||
to the host device using :command:`ssh`.
|
||||
|
||||
To use this, build the ``outputs.updater``
|
||||
target and then run the :command:`update.sh` script it generates.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
nix-build -I liminix-config=./my-configuration.nix \
|
||||
--arg device "import ./devices/mydevice" \
|
||||
-A outputs.updater
|
||||
./result/bin/update.sh root@the-device
|
||||
|
||||
The update script uses min-copy-closure to copy new or changed
|
||||
packages to the device, then (perhaps) reboots it. The reboot
|
||||
behaviour can be affected by flags:
|
||||
|
||||
* `--no-reboot` will cause it not to reboot at all, if you would
|
||||
rather do that yourself. Note that none of the newly-installed or
|
||||
updated services will be running until you do.
|
||||
|
||||
* `--fast` causes it tn not do a full reboot, but instead to restart
|
||||
only the services that have been changed. This will restart all of
|
||||
the services that have updated store paths (and anything that
|
||||
depends on them), but will not affect services that haven't changed.
|
||||
|
||||
It doesn't delete old packages automatically: to do that run
|
||||
:command:`min-collect-garbage`, which will delete any packages not in
|
||||
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
|
||||
|
||||
.. _levitate:
|
||||
|
||||
Adding packages
|
||||
===============
|
||||
|
||||
If you simply wish to add a package without any change to services,
|
||||
you can call :command:`min-copy-closure` directly to install
|
||||
any package in Nixpkgs or in the Liminix overlay
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
nix-build -I liminix-config=./my-configuration.nix \
|
||||
--arg device "import ./devices/mydevice" -A pkgs.tcpdump
|
||||
|
||||
nix-shell -p min-copy-closure root@the-device result/
|
||||
|
||||
Note that this only copies the package and its dependencies to the
|
||||
device: it doesn't update any profile to add it to ``$PATH``
|
||||
|
||||
|
||||
.. _rebuilding the system:
|
||||
|
||||
Reinstalling on a running system
|
||||
********************************
|
||||
|
||||
Liminix is initially installed from a monolithic
|
||||
:file:`firmware.bin` - and unless you're running a writable
|
||||
filesystem, the only way to update it is to build and install a whole
|
||||
new :file:`firmware.bin`. However, you probably would prefer not to
|
||||
have to remove it from its installation site, unplug it from the
|
||||
network and stick serial cables in it all over again.
|
||||
|
||||
It is not (generally) safe to install a new firmware onto the flash
|
||||
partitions that the active system is running on. To address this we
|
||||
have :command:`levitate`, which a way for a running Liminix system to
|
||||
"soft restart" into a ramdisk running only a limited set of services,
|
||||
so that the main partitions can then be safely flashed.
|
||||
|
||||
|
||||
|
||||
Configuration
|
||||
=============
|
||||
|
||||
Levitate *needs to be configured when you create the initial system*
|
||||
to specify which services/packages/etc to run in maintenance
|
||||
mode. Most likely you want to configure a network interface and an ssh
|
||||
for example so that you can login to reflash it.
|
||||
|
||||
.. code-block:: nix
|
||||
|
||||
defaultProfile.packages = with pkgs; [
|
||||
...
|
||||
(levitate.override {
|
||||
config = {
|
||||
services = {
|
||||
inherit (config.services) dhcpc sshd watchdog;
|
||||
};
|
||||
defaultProfile.packages = [ mtdutils ];
|
||||
users.root = config.users.root;
|
||||
};
|
||||
})
|
||||
];
|
||||
|
||||
|
||||
|
||||
Use
|
||||
===
|
||||
|
||||
Connect (with ssh, probably) to the running Liminix system that you
|
||||
wish to upgrade.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
bash$ ssh root@the-device
|
||||
|
||||
Run :command:`levitate`. This takes a little while (perhaps a few
|
||||
tens of seconds) to execute, and copies all config required for
|
||||
maintenance mode to :file:`/run/maintenance`.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# levitate
|
||||
|
||||
Reboot into maintenance mode. You will be logged out
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# reboot
|
||||
|
||||
Connect to the device again - note that the ssh host key will have changed.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# ssh -o UserKnownHostsFile=/dev/null root@the-device
|
||||
|
||||
Check we're in maintenance mode
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# cat /etc/banner
|
||||
|
||||
LADIES AND GENTLEMEN WE ARE FLOATING IN SPACE
|
||||
|
||||
Most services are disabled. The system is operating
|
||||
with a ram-based root filesystem, making it safe to
|
||||
overwrite the flash devices in order to perform
|
||||
upgrades and maintenance.
|
||||
|
||||
Don't forget to reboot when you have finished.
|
||||
|
||||
Perform the upgrade, using flashcp. This is an example,
|
||||
your device will differ
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# cat /proc/mtd
|
||||
dev: size erasesize name
|
||||
mtd0: 00030000 00010000 "u-boot"
|
||||
mtd1: 00010000 00010000 "u-boot-env"
|
||||
mtd2: 00010000 00010000 "factory"
|
||||
mtd3: 00f80000 00010000 "firmware"
|
||||
mtd4: 00220000 00010000 "kernel"
|
||||
mtd5: 00d60000 00010000 "rootfs"
|
||||
mtd6: 00010000 00010000 "art"
|
||||
# flashcp -v firmware.bin mtd:firmware
|
||||
|
||||
All done
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# reboot
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
= Code of Conduct
|
||||
|
||||
NOTE: As of Feb 2023, "`RESPONSE TEAM`" and "`LEADERSHIP TEAM`" in the text
|
||||
that follows both refer to me, Daniel Barlow, as the project leader.
|
||||
|
||||
Liminix is dedicated to providing a harassment-free experience for
|
||||
everyone. We do not tolerate harassment of participants in any form.
|
||||
|
||||
This code of conduct applies to all Liminix spaces, including the IRC
|
||||
channel, mailing lists, and other forums, both online and off. Anyone
|
||||
who violates this code of conduct may be sanctioned or expelled from
|
||||
these spaces at the discretion of the RESPONSE TEAM.
|
||||
|
||||
Some Liminix spaces may have additional rules in place, which will be
|
||||
made clearly available to participants. Participants are responsible for
|
||||
knowing and abiding by these rules.
|
||||
|
||||
Harassment includes:
|
||||
|
||||
* Offensive comments related to gender, gender identity and expression,
|
||||
sexual orientation, disability, mental illness, neuro(a)typicality,
|
||||
physical appearance, body size, age, race, or religion.
|
||||
* Unwelcome comments regarding a person’s lifestyle choices and
|
||||
practices, including those related to food, health, parenting, drugs,
|
||||
and employment.
|
||||
* Deliberate misgendering or use of '`dead`' or rejected names.
|
||||
* Gratuitous or off-topic sexual images or behaviour in spaces where
|
||||
they’re not appropriate.
|
||||
* Physical contact and simulated physical contact (eg, textual
|
||||
descriptions like "`__hug__`" or "`__backrub__`") without consent or
|
||||
after a request to stop.
|
||||
* Threats of violence.
|
||||
* Incitement of violence towards any individual, including encouraging a
|
||||
person to commit suicide or to engage in self-harm.
|
||||
* Deliberate intimidation.
|
||||
* Stalking or following.
|
||||
* Harassing photography or recording, including logging online activity
|
||||
for harassment purposes.
|
||||
* Sustained disruption of discussion.
|
||||
* Unwelcome sexual attention.
|
||||
* Pattern of inappropriate social contact, such as requesting/assuming
|
||||
inappropriate levels of intimacy with others
|
||||
* Continued one-on-one communication after requests to cease.
|
||||
* Deliberate "`outing`" of any aspect of a person’s identity without
|
||||
their consent except as necessary to protect vulnerable people from
|
||||
intentional abuse.
|
||||
* Publication of non-harassing private communication.
|
||||
|
||||
Liminix prioritizes marginalized people’s safety over privileged
|
||||
people’s comfort. RESPONSE TEAM reserves the right not to act on
|
||||
complaints regarding:
|
||||
|
||||
* '`Reverse`' -isms, including '`reverse racism,`' '`reverse sexism,`'
|
||||
and '`cisphobia`'
|
||||
* Reasonable communication of boundaries, such as "`leave me alone,`"
|
||||
"`go away,`" or "`I’m not discussing this with you.`"
|
||||
* Communicating in a '`tone`' you don’t find congenial
|
||||
* Criticizing racist, sexist, cissexist, or otherwise oppressive
|
||||
behavior or assumptions
|
||||
|
||||
== Reporting
|
||||
|
||||
If you are being harassed by a member of Liminix, notice that someone
|
||||
else is being harassed, or have any other concerns, please contact the
|
||||
RESPONSE TEAM at [email address or other contact point]. If the person
|
||||
who is harassing you is on the team, they will recuse themselves from
|
||||
handling your incident. We will respond as promptly as we can.
|
||||
|
||||
This code of conduct applies to Liminix spaces, but if you are being
|
||||
harassed by a member of Liminix outside our spaces, we still want to
|
||||
know about it. We will take all good-faith reports of harassment by
|
||||
Liminix members, especially LEADERSHIP TEAM, seriously. This includes
|
||||
harassment outside our spaces and harassment that took place at any
|
||||
point in time. The abuse team reserves the right to exclude people from
|
||||
Liminix based on their past behavior, including behavior outside Liminix
|
||||
spaces and behavior towards people who are not in Liminix.
|
||||
|
||||
In order to protect volunteers from abuse and burnout, we reserve the
|
||||
right to reject any report we believe to have been made in bad faith.
|
||||
Reports intended to silence legitimate criticism may be deleted without
|
||||
response.
|
||||
|
||||
We will respect confidentiality requests for the purpose of protecting
|
||||
victims of abuse. At our discretion, we may publicly name a person about
|
||||
whom we’ve received harassment complaints, or privately warn third
|
||||
parties about them, if we believe that doing so will increase the safety
|
||||
of Liminix members or the general public. We will not name harassment
|
||||
victims without their affirmative consent.
|
||||
|
||||
== Consequences
|
||||
|
||||
Participants asked to stop any harassing behavior are expected to comply
|
||||
immediately.
|
||||
|
||||
If a participant engages in harassing behavior, RESPONSE TEAM may take
|
||||
any action they deem appropriate, up to and including expulsion from all
|
||||
Liminix spaces and identification of the participant as a harasser to
|
||||
other Liminix members or the general public.
|
||||
|
||||
== License and attribution
|
||||
|
||||
The policy is based on the Geek Feminism
|
||||
https://geekfeminism.fandom.com/wiki/Community_anti-harassment/Policy[Community
|
||||
anti-harassment/Policy] and is the work of Annalee Flower Horne with
|
||||
assistance from Valerie Aurora, Alex Skud Bayley, Tim Chevalier, and
|
||||
Mary Gardiner.
|
||||
@@ -0,0 +1,223 @@
|
||||
== Configuration
|
||||
|
||||
There are many things you can specify in a configuration, but most
|
||||
commonly you 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 `+modules/ntp+` to your `+imports+` list, then you
|
||||
create a service by calling `+config.system.service.ntp.build { .... }+`
|
||||
with the appropriate service-dependent configuration parameters.
|
||||
|
||||
[source,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 `+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.
|
||||
====
|
||||
|
||||
[[configuration-services]]
|
||||
=== Services
|
||||
|
||||
In Liminix a service is any kind of long-running task or process on the
|
||||
system, that is managed (started, stopped, and monitored) by a service
|
||||
supervisor. A typical SOHO router might have services to
|
||||
|
||||
* answer DHCP and DNS requests from the LAN
|
||||
* provide a wireless access point
|
||||
* connect using PPPoE or L2TP to an upstream network
|
||||
* start/stop the firewall
|
||||
* enable/disable IP packet forwarding
|
||||
* mount filesystems
|
||||
|
||||
(Some of these might not be considered services using other definitions
|
||||
of the term: for example, this L2TP process would be a "client" in the
|
||||
client/server classification; and enabling packet forwarding doesn't
|
||||
require any long-lived process - just a setting to be toggled. However,
|
||||
there is value in being able to use the same abstractions for all the
|
||||
things to manage them and specify their dependency relationships - so in
|
||||
Liminix "everything is a service")
|
||||
|
||||
The service supervision system enables service health monitoring,
|
||||
restart of unhealthy services, and failover to "backup" services when a
|
||||
primary service fails or its dependencies are unavailable. The intention
|
||||
is that you have a framework in which you can specify policy
|
||||
requirements like "ethernet wan dhcp-client should be restarted if it
|
||||
crashes, but if it can't start because the hardware link is down, then
|
||||
4G ppp service should be started instead".
|
||||
|
||||
Any attribute in [.title-ref]#config.services# will become part of the
|
||||
default set of services that s6-rc will try to bring up. Services are
|
||||
usually started at boot time, but *controlled services* are those that
|
||||
are required only in particular contexts. For example, a service to
|
||||
mount a USB backup drive should run only when the drive is attached to
|
||||
the system. Liminix currently implements three kinds of controlled
|
||||
service:
|
||||
|
||||
* "uevent-rule" service controllers use sysfs/uevent to identify when
|
||||
particular hardware devices are present, and start/stop a controlled
|
||||
service appropriately.
|
||||
* the "round-robin" service controller is used for service failover: it
|
||||
allows you to specify a list of services and runs each of them in turn
|
||||
until it exits, then runs the next.
|
||||
* the "health-check" service wraps another service, and runs a "health
|
||||
check" command at regular intervals. When the health check fails,
|
||||
indicating that the wrapped service is not working, it is terminated and
|
||||
allowed to restart.
|
||||
|
||||
=== Runtime secrets (external vault)
|
||||
|
||||
Secrets (such as wifi passphrases, PPP username/password, SSH keys, etc)
|
||||
that you provide as literal values in `+configuration.nix+` are
|
||||
processed into into config files and scripts at build time, and
|
||||
eventually end up in various files in the (world-readable)
|
||||
`+/nix/store+` before being baked into a flashable image. To change a
|
||||
secret - whether due to a compromise, or just as part of to a routine
|
||||
key rotation - you need to rebuild the configuration and potentially
|
||||
reflash the affected devices.
|
||||
|
||||
To avoid this, you may instead use a "secrets service", which is a
|
||||
mechanism for your device to fetch secrets from a source external to the
|
||||
Nix store, and create at runtime the configuration files and scripts
|
||||
that start the services which require them.
|
||||
|
||||
Not every possible parameter to every possible service is configurable
|
||||
using a secrets service. Parameters which can be configured this way are
|
||||
those with the type `+liminix.lib.types.replacable+`. At the time this
|
||||
document was written, these include:
|
||||
|
||||
* ppp (pppoe and l2tp): `+username+`, `+password+`
|
||||
* ssh: `+authorizedKeys+`
|
||||
* hostapd: all parameters (most likely to be useful for
|
||||
`+wpa_passphrase+`)
|
||||
|
||||
To use a runtime secret for any of these parameters:
|
||||
|
||||
* create a secrets service to specify the source of truth for secrets
|
||||
* use the `+outputRef+` function in the service parameter to specify the
|
||||
secrets service and path
|
||||
|
||||
For example, given you had an HTTPS server hosting a JSON file with the
|
||||
structure
|
||||
|
||||
[source,json]
|
||||
----
|
||||
"ssh": {
|
||||
"authorizedKeys": {
|
||||
"root": [ "ssh-rsa ....", "ssh-rsa ....", ... ]
|
||||
"guest": [ "ssh-rsa ....", "ssh-rsa ....", ... ]
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
you could use a `+configuration.nix+` fragment something like this to
|
||||
make those keys visible to ssh:
|
||||
|
||||
[source,nix]
|
||||
----
|
||||
services.secrets = svc.secrets.outboard.build {
|
||||
name = "secret-service";
|
||||
url = "http://10.0.0.1/secrets.json";
|
||||
username = "secrets";
|
||||
password = "liminix";
|
||||
interval = 30; # minutes
|
||||
dependencies = [ config.services.lan ];
|
||||
};
|
||||
services.sshd = svc.ssh.build {
|
||||
authorizedKeys = outputRef config.services.secrets "ssh/authorizedKeys";
|
||||
};
|
||||
----
|
||||
|
||||
There are presently two implementations of a secrets service:
|
||||
|
||||
===== Outboard secrets (HTTPS)
|
||||
|
||||
This service expects a URL to a JSON file containing all the secrets.
|
||||
|
||||
You may specify a username and password along with the URL, which are
|
||||
used if the file is password-protected (HTTP Basic authentication). Note
|
||||
that this is not a protection against a malicious local user: the
|
||||
username and password are normal build-time parameters so will be
|
||||
readable in the Nix store. This is a mitigation against the URL being
|
||||
accidentally discovered due to e.g. a log file or error message on the
|
||||
server leaking.
|
||||
|
||||
===== Tang secrets (encrypted local file)
|
||||
|
||||
Aternatively, secrets may be stored locally on the device, in a file
|
||||
that has been encrypted using https://github.com/latchset/tang[Tang].
|
||||
|
||||
____
|
||||
Tang is a server for binding data to network presence.
|
||||
|
||||
This sounds fancy, but the concept is simple. You have some data, but
|
||||
you only want it to be available when the system containing the data is
|
||||
on a certain, usually secure, network.
|
||||
____
|
||||
|
||||
[source,nix]
|
||||
----
|
||||
services.secrets = svc.secrets.tang.build {
|
||||
name = "secret-service";
|
||||
path = "/run/mnt/usbstick/secrets.json.jwe";
|
||||
interval = 30; # minutes
|
||||
dependencies = [ config.services.mount-usbstick ];
|
||||
};
|
||||
----
|
||||
|
||||
The encryption uses the same scheme/algorithm as
|
||||
https://github.com/latchset/clevis[Clevis] : you may use the
|
||||
https://github.com/latchset/clevis?tab=readme-ov-file#pin-tang[Clevis
|
||||
instructions] to encrypt the file on another host and then copy it to
|
||||
your Liminix device, or you can use `+tangc encrypt+` to encrypt
|
||||
directly on the device. (That latter approach may pose a chicken/egg
|
||||
problem if the device needs secrets to boot up and run the services you
|
||||
are relying on in order to login).
|
||||
|
||||
@@ -1,482 +0,0 @@
|
||||
.. _configuration:
|
||||
|
||||
Configuration
|
||||
#############
|
||||
|
||||
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.
|
||||
|
||||
.. _configuration-services:
|
||||
|
||||
Services
|
||||
********
|
||||
|
||||
In Liminix a service is any kind of long-running task or process on
|
||||
the system, that is managed (started, stopped, and monitored) by a
|
||||
service supervisor. A typical SOHO router might have services to
|
||||
|
||||
* answer DHCP and DNS requests from the LAN
|
||||
* provide a wireless access point
|
||||
* connect using PPPoE or L2TP to an upstream network
|
||||
* start/stop the firewall
|
||||
* enable/disable IP packet forwarding
|
||||
* mount filesystems
|
||||
|
||||
(Some of these might not be considered services using other
|
||||
definitions of the term: for example, this L2TP process would be a
|
||||
"client" in the client/server classification; and enabling packet
|
||||
forwarding doesn't require any long-lived process - just a setting to
|
||||
be toggled. However, there is value in being able to use the same
|
||||
abstractions for all the things to manage them and specify their
|
||||
dependency relationships - so in Liminix "everything is a service")
|
||||
|
||||
The service supervision system enables service health monitoring,
|
||||
restart of unhealthy services, and failover to "backup" services when
|
||||
a primary service fails or its dependencies are unavailable. The
|
||||
intention is that you have a framework in which you can specify policy
|
||||
requirements like "ethernet wan dhcp-client should be restarted if it
|
||||
crashes, but if it can't start because the hardware link is down, then
|
||||
4G ppp service should be started instead".
|
||||
|
||||
Any attribute in `config.services` will become part of the default set
|
||||
of services that s6-rc will try to bring up. Services are usually
|
||||
started at boot time, but **controlled services** are those that are
|
||||
required only in particular contexts. For example, a service to mount
|
||||
a USB backup drive should run only when the drive is attached to the
|
||||
system. Liminix currently implements three kinds of controlled service:
|
||||
|
||||
* "uevent-rule" service controllers use sysfs/uevent to identify when
|
||||
particular hardware devices are present, and start/stop a controlled
|
||||
service appropriately.
|
||||
|
||||
* the "round-robin" service controller is used for service failover:
|
||||
it allows you to specify a list of services and runs each of them
|
||||
in turn until it exits, then runs the next.
|
||||
|
||||
* the "health-check" service wraps another service, and runs a "health
|
||||
check" command at regular intervals. When the health check fails,
|
||||
indicating that the wrapped service is not working, it is terminated
|
||||
and allowed to restart.
|
||||
|
||||
Runtime secrets (external vault)
|
||||
================================
|
||||
|
||||
Secrets (such as wifi passphrases, PPP username/password, SSH keys,
|
||||
etc) that you provide as literal values in :file:`configuration.nix`
|
||||
are processed into into config files and scripts at build time, and
|
||||
eventually end up in various files in the (world-readable)
|
||||
:file:`/nix/store` before being baked into a flashable image. To
|
||||
change a secret - whether due to a compromise, or just as part of to a
|
||||
routine key rotation - you need to rebuild the configuration and
|
||||
potentially reflash the affected devices.
|
||||
|
||||
To avoid this, you may instead use a "secrets service", which is a
|
||||
mechanism for your device to fetch secrets from a source external to
|
||||
the Nix store, and create at runtime the configuration files and
|
||||
scripts that start the services which require them.
|
||||
|
||||
Not every possible parameter to every possible service is configurable
|
||||
using a secrets service. Parameters which can be configured this way
|
||||
are those with the type ``liminix.lib.types.replacable``. At the time
|
||||
this document was written, these include:
|
||||
|
||||
* ppp (pppoe and l2tp): ``username``, ``password``
|
||||
* ssh: ``authorizedKeys``
|
||||
* hostapd: all parameters (most likely to be useful for ``wpa_passphrase``)
|
||||
|
||||
To use a runtime secret for any of these parameters:
|
||||
|
||||
* create a secrets service to specify the source of truth for secrets
|
||||
* use the :code:`outputRef` function in the service parameter to specify the secrets service and path
|
||||
|
||||
For example, given you had an HTTPS server hosting a JSON file with the structure
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
"ssh": {
|
||||
"authorizedKeys": {
|
||||
"root": [ "ssh-rsa ....", "ssh-rsa ....", ... ]
|
||||
"guest": [ "ssh-rsa ....", "ssh-rsa ....", ... ]
|
||||
}
|
||||
}
|
||||
|
||||
you could use a :file:`configuration.nix` fragment something like this
|
||||
to make those keys visible to ssh:
|
||||
|
||||
.. code-block:: nix
|
||||
|
||||
services.secrets = svc.secrets.outboard.build {
|
||||
name = "secret-service";
|
||||
url = "http://10.0.0.1/secrets.json";
|
||||
username = "secrets";
|
||||
password = "liminix";
|
||||
interval = 30; # minutes
|
||||
dependencies = [ config.services.lan ];
|
||||
};
|
||||
services.sshd = svc.ssh.build {
|
||||
authorizedKeys = outputRef config.services.secrets "ssh/authorizedKeys";
|
||||
};
|
||||
|
||||
|
||||
|
||||
There are presently two implementations of a secrets service:
|
||||
|
||||
Outboard secrets (HTTPS)
|
||||
------------------------
|
||||
|
||||
This service expects a URL to a JSON file containing all the secrets.
|
||||
|
||||
You may specify a username and password along with the URL, which are
|
||||
used if the file is password-protected (HTTP Basic
|
||||
authentication). Note that this is not a protection against a
|
||||
malicious local user: the username and password are normal build-time
|
||||
parameters so will be readable in the Nix store. This is a mitigation
|
||||
against the URL being accidentally discovered due to e.g. a log file
|
||||
or error message on the server leaking.
|
||||
|
||||
|
||||
Tang secrets (encrypted local file)
|
||||
-----------------------------------
|
||||
|
||||
Aternatively, secrets may be stored locally on the device, in a file
|
||||
that has been encrypted using `Tang <https://github.com/latchset/tang>`_.
|
||||
|
||||
Tang is a server for binding data to network presence.
|
||||
|
||||
This sounds fancy, but the concept is simple. You have some data, but you only want it to be available when the system containing the data is on a certain, usually secure, network.
|
||||
|
||||
|
||||
.. code-block:: nix
|
||||
|
||||
services.secrets = svc.secrets.tang.build {
|
||||
name = "secret-service";
|
||||
path = "/run/mnt/usbstick/secrets.json.jwe";
|
||||
interval = 30; # minutes
|
||||
dependencies = [ config.services.mount-usbstick ];
|
||||
};
|
||||
|
||||
The encryption uses the
|
||||
same scheme/algorithm as `Clevis <https://github.com/latchset/clevis>`_ : you may use the `Clevis instructions <https://github.com/latchset/clevis?tab=readme-ov-file#pin-tang>`_ to
|
||||
encrypt the file on another host and then copy it to your Liminix
|
||||
device, or you can use :command:`tangc encrypt` to encrypt directly on
|
||||
the device. (That latter approach may pose a chicken/egg problem if
|
||||
the device needs secrets to boot up and run the services you are
|
||||
relying on in order to login).
|
||||
|
||||
|
||||
Writing services
|
||||
================
|
||||
|
||||
For the most part, for common use cases, hopefully the services you
|
||||
need will be defined by modules and you will only have to pass the
|
||||
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.
|
||||
|
||||
Service outputs
|
||||
===============
|
||||
|
||||
Outputs are a mechanism by which a service can provide data which may
|
||||
be required by other services. For example:
|
||||
|
||||
* the DHCP client service can expect to receive nameserver address
|
||||
information as one of the fields in the response from the DHCP
|
||||
server: we provide that as an output which a dependent service for a
|
||||
stub name resolver can use to configure its upstream servers.
|
||||
|
||||
* a service that creates a new network interface (e.g. ppp) will
|
||||
provide the name of the interface (:code:`ppp0`, or :code:`ppp1` or
|
||||
:code:`ppp7`) as an output so that a dependent service can reference
|
||||
it to set up a route, or to configure firewall rules.
|
||||
|
||||
A service :code:`myservice` should write its outputs as files in
|
||||
:file:`/run/services/outputs/myservice`: you can look around this
|
||||
directory on a running Liminix system to see how it's used currently.
|
||||
Usually we use the :code:`in_outputs` shell function in the
|
||||
:command:`up` or :command:`run` attributes of the service:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
(in_outputs ${name}
|
||||
for i in lease mask ip router siaddr dns serverid subnet opt53 interface ; do
|
||||
(printenv $i || true) > $i
|
||||
done)
|
||||
|
||||
The outputs are just files, so technically you can read them using
|
||||
anything that can read a file. Liminix has two "preferred"
|
||||
mechanisms, though:
|
||||
|
||||
One-off lookups
|
||||
---------------
|
||||
|
||||
In any context that ends up being evaluated by the shell, use
|
||||
:code:`output` to print the value of an output
|
||||
|
||||
.. code-block:: nix
|
||||
|
||||
services.defaultroute4 = svc.network.route.build {
|
||||
via = "$(output ${services.wan} address)";
|
||||
target = "default";
|
||||
dependencies = [ services.wan ];
|
||||
};
|
||||
|
||||
|
||||
Continuous updates
|
||||
------------------
|
||||
|
||||
The downside of using shell functions in downstream service startup
|
||||
scripts is that they only run when the service starts up: if a service
|
||||
output *changes*, the downstream service would have to be restarted to
|
||||
notice the change. Sometimes this is OK but other times the downstream
|
||||
has no other need to restart, if it can only get its new data.
|
||||
|
||||
For this case, there is the :code:`anoia.svc` Fennel library, which
|
||||
allows you to write a simple loop which is iterated over whenever a
|
||||
service's outputs change. This code is from
|
||||
:file:`modules/dhcp6c/acquire-wan-address.fnl`
|
||||
|
||||
.. code-block:: fennel
|
||||
|
||||
(fn update-addresses [wan-device addresses new-addresses exec]
|
||||
;; run some appropriate "ip address [add|remove]" commands
|
||||
)
|
||||
|
||||
(fn run []
|
||||
(let [[state-directory wan-device] arg
|
||||
dir (svc.open state-directory)]
|
||||
(accumulate [addresses []
|
||||
v (dir:events)]
|
||||
(update-addresses wan-device addresses
|
||||
(or (v:output "address") []) system))))
|
||||
|
||||
The :code:`output` method seen here accepts a filename (relative
|
||||
to the service's output directory), or a directory name. It
|
||||
returns the first line of that file, or for directories it
|
||||
returns a table (Lua's key/value datastructure, similar to
|
||||
a hash/dictionary) of the outputs in that directory.
|
||||
|
||||
|
||||
Output design considerations
|
||||
----------------------------
|
||||
|
||||
For preference, outputs should be short and simple, and not require
|
||||
downstream services to do complicated parsing in order to use them.
|
||||
Shell commands in Liminix are run using the Busybox shell which
|
||||
doesn't have the niceties of an advanced shell like Bash let alone
|
||||
those of a real programming language.
|
||||
|
||||
Note also that the Lua :code:`svc` library only reads the first line
|
||||
of each output.
|
||||
|
||||
|
||||
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 = config.system.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.
|
||||
@@ -0,0 +1,637 @@
|
||||
= For Developers
|
||||
|
||||
In any Nix-based system the line between "configuration"
|
||||
and "development" is less of a line and more of a continuum.
|
||||
This section covers some topics further towards the latter end.
|
||||
|
||||
== Writing modules and services
|
||||
|
||||
It helps here to know NixOS! Liminix uses the NixOS module
|
||||
infrastructure code, meaning that everything that has been written for
|
||||
NixOS about the syntax, the type system, and the rules for combining
|
||||
configuration values from different sources is just as applicable
|
||||
here.
|
||||
|
||||
=== Services
|
||||
|
||||
For the most part, for common use cases, we hope that Liminix modules
|
||||
provide service templates for all the services you will need, and you
|
||||
will only have to pass the right parameters to `+build+`.
|
||||
|
||||
But if you're reading this then our hopes are in vain. To create a
|
||||
custom service of your own devising, use the [.title-ref]#oneshot# or
|
||||
[.title-ref]#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.
|
||||
|
||||
[source,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
|
||||
|
||||
[source,nix]
|
||||