Compare commits
No commits in common. "main" and "main" have entirely different histories.
57
CODE-OF-CONDUCT.md
Normal file
57
CODE-OF-CONDUCT.md
Normal file
@ -0,0 +1,57 @@
|
||||
# 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.
|
87
NEWS
87
NEWS
@ -83,89 +83,4 @@ sponsoring this development (and funding the hardware)
|
||||
2024-02-21
|
||||
|
||||
New port! Thanks to Raito Bezarius, Liminix now runs on the Zyxel NWA50AX,
|
||||
an MT7621 (MIPS EL) dual radio WiFi AP.
|
||||
|
||||
2024-04-29
|
||||
|
||||
The setup for using `levitate` has changed: now it accepts an entire
|
||||
config fragment, not just a list of services. Hopefully this makes it
|
||||
a bit more useful :-)
|
||||
|
||||
defaultProfile.packages = with pkgs; [
|
||||
...
|
||||
(levitate.override {
|
||||
config = {
|
||||
services = {
|
||||
inherit (config.services) dhcpc sshd watchdog;
|
||||
};
|
||||
defaultProfile.packages = [ mtdutils ];
|
||||
users.root.openssh.authorizedKeys.keys = secrets.root.keys;
|
||||
};
|
||||
})
|
||||
];
|
||||
|
||||
2024-07-16
|
||||
|
||||
* structured parameters are available for the pppoe service
|
||||
|
||||
* The "wan" configuration in modules/profiles/gateway.nix has changed:
|
||||
instead of passing options that are used to create a pppoe interface,
|
||||
callers should create a (pppoe or other) interface and pass that as
|
||||
the value of profile.gateway.wan. For the pppoe case this is now only
|
||||
very slightly more verbose, and it allows using the gateway profile
|
||||
with other kinds of upstream.
|
||||
|
||||
2024-8-16
|
||||
|
||||
As part of implementing log shipping, the default directory for system
|
||||
logs has beenchanged from /run/uncaught-logs to /run/log
|
||||
|
||||
2024-10-09
|
||||
|
||||
liminix-rebuild is being deprecated. From hereon in, the preferred way
|
||||
to do an incremental update on an installed device with a writable
|
||||
filesystem is to build the systemConfiguration output
|
||||
|
||||
nix-build -I liminix-config=hosts/myhost.nix --argstr deviceName turris-omnia -A outputs.systemConfiguration
|
||||
|
||||
and then run the generated `install.sh` script
|
||||
|
||||
result/install.sh root@192.168.8.1
|
||||
|
||||
2024-12-16
|
||||
|
||||
Config options changed: if you had set config.hardware.dts.includes
|
||||
(maybe in an out-of-tree device port) to specify the search paths
|
||||
in which dtc finds include files, you will need to change this to
|
||||
hardware.dts.includePaths.
|
||||
|
||||
The "new" hardware.dts.includes option is now for dtsi files which
|
||||
should be merged into the device tree.
|
||||
|
||||
2024-12-19
|
||||
|
||||
Incremental updates changed again (but not massively). From hereon in,
|
||||
the preferred way to do an incremental update on an installed device
|
||||
with a writable filesystem is to build the updater output
|
||||
|
||||
nix-build -I liminix-config=hosts/myhost.nix --argstr deviceName turris-omnia -A outputs.updater
|
||||
|
||||
and then run the generated `update.sh` script. See
|
||||
https://www.liminix.org/doc/admin.html#updating-an-installed-system
|
||||
|
||||
2024-12-22
|
||||
|
||||
outputs.zimage is now outputs.kernel.zImage. This is unlikely to
|
||||
affect many people at all but I mention it anyway.
|
||||
|
||||
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
|
||||
an MT7621 (MIPS EL) dual radio WiFi AP.
|
3500
THOUGHTS.txt
3500
THOUGHTS.txt
File diff suppressed because it is too large
Load Diff
42
boot.expect
42
boot.expect
@ -1,42 +0,0 @@
|
||||
# This is for use with minicom, but needs you to configure it to
|
||||
# use expect as its "Script program" instead of runscript. Try
|
||||
# Ctrl+A O -> Filenames and paths -> D
|
||||
|
||||
fconfigure stderr -buffering none
|
||||
fconfigure stdout -buffering none
|
||||
|
||||
proc waitprompt { } {
|
||||
expect {
|
||||
"BusyBox" { puts stderr "DONE\r"; exit 0 }
|
||||
"READY" { puts stderr ";;; READY\r"; }
|
||||
timeout { puts stderr ";;; timed out waiting after $line\r" }
|
||||
}
|
||||
}
|
||||
|
||||
proc sendline { line } {
|
||||
send "$line; echo \$ready \r"
|
||||
sleep 0.1
|
||||
}
|
||||
|
||||
log_user 0
|
||||
log_file -a -open stderr
|
||||
|
||||
set f [open "result/boot.scr"]
|
||||
|
||||
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"
|
||||
puts stderr ";;; sending\r"
|
||||
sendline $line
|
||||
waitprompt
|
||||
}
|
||||
|
||||
puts stderr "done\r\n"
|
||||
close $f
|
@ -1,26 +1,9 @@
|
||||
{
|
||||
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;
|
||||
default = [ ];
|
||||
};
|
||||
l2tp = {
|
||||
host = mkOption {
|
||||
description = mdDoc ''
|
||||
@ -68,17 +51,18 @@ in
|
||||
<nixpkgs/nixos/modules/virtualisation/qemu-vm.nix>
|
||||
];
|
||||
config = {
|
||||
boot.kernelParams = [ "loglevel=9" ];
|
||||
boot.kernelParams = [
|
||||
"loglevel=9"
|
||||
];
|
||||
systemd.services.pppoe =
|
||||
let
|
||||
conf = pkgs.writeText "kpppoed.toml" ''
|
||||
interface_name = "eth1"
|
||||
services = [ "myservice" ]
|
||||
lns_ipaddr = "${cfg.l2tp.host}:${builtins.toString cfg.l2tp.port}"
|
||||
ac_name = "kpppoed-1.0"
|
||||
'';
|
||||
in
|
||||
{
|
||||
let conf = pkgs.writeText "kpppoed.toml"
|
||||
''
|
||||
interface_name = "eth1"
|
||||
services = [ "myservice" ]
|
||||
lns_ipaddr = "${cfg.l2tp.host}:${builtins.toString cfg.l2tp.port}"
|
||||
ac_name = "kpppoed-1.0"
|
||||
'';
|
||||
in {
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network-online.target" ];
|
||||
serviceConfig = {
|
||||
@ -92,114 +76,52 @@ in
|
||||
};
|
||||
};
|
||||
services.openssh.enable = true;
|
||||
services.dnsmasq = {
|
||||
enable = true;
|
||||
resolveLocalQueries = false;
|
||||
settings = {
|
||||
# domain-needed = true;
|
||||
dhcp-range = [ "10.0.0.10,10.0.0.240" ];
|
||||
interface = "eth1";
|
||||
};
|
||||
};
|
||||
|
||||
services.nginx = {
|
||||
enable = true;
|
||||
user = "liminix";
|
||||
virtualHosts.${config.networking.hostName} = {
|
||||
root = "/home/liminix";
|
||||
default = true;
|
||||
};
|
||||
};
|
||||
systemd.services.nginx.serviceConfig.ProtectHome = "read-only";
|
||||
|
||||
systemd.services.sshd.wantedBy = pkgs.lib.mkForce [ "multi-user.target" ];
|
||||
|
||||
virtualisation = {
|
||||
forwardPorts = [
|
||||
{
|
||||
from = "host";
|
||||
host.port = 7654;
|
||||
# guest.address = "10.0.2.15";
|
||||
guest.port = 7654;
|
||||
}
|
||||
{
|
||||
host.port = 2222;
|
||||
guest.address = "10.0.2.15";
|
||||
guest.port = 22;
|
||||
}
|
||||
];
|
||||
qemu = {
|
||||
networkingOptions = [ ];
|
||||
options =
|
||||
[ ]
|
||||
++ optional cfg.ethernet.pci.enable "-device vfio-pci,host=${cfg.ethernet.pci.id}"
|
||||
++ optionals cfg.ethernet.usb.enable [
|
||||
networkingOptions = [];
|
||||
options = [] ++
|
||||
optional cfg.ethernet.pci.enable
|
||||
"-device vfio-pci,host=${cfg.ethernet.pci.id}" ++
|
||||
optionals cfg.ethernet.usb.enable [
|
||||
"-device usb-ehci,id=ehci"
|
||||
"-device usb-host,bus=ehci.0,vendorid=${cfg.ethernet.usb.vendor},productid=${cfg.ethernet.usb.product}"
|
||||
]
|
||||
++ [
|
||||
] ++ [
|
||||
"-nographic"
|
||||
"-serial mon:stdio"
|
||||
];
|
||||
};
|
||||
sharedDirectories = {
|
||||
liminix = {
|
||||
securityModel = "none";
|
||||
source = builtins.toString ./.;
|
||||
target = "/home/liminix/liminix";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
services.tang = {
|
||||
enable = true;
|
||||
ipAddressAllow = [
|
||||
"10.0.0.0/24"
|
||||
"0.0.0.0/0"
|
||||
];
|
||||
};
|
||||
|
||||
environment.systemPackages =
|
||||
let
|
||||
wireshark-nogui = pkgs.wireshark.override { withQt = false; };
|
||||
in
|
||||
with pkgs;
|
||||
[
|
||||
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
|
||||
];
|
||||
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;
|
||||
}
|
||||
];
|
||||
};
|
||||
nat = {
|
||||
enable = true;
|
||||
internalInterfaces = [ "eth1" ];
|
||||
externalInterface = "eth0";
|
||||
ipv4.addresses = [ { address = "10.0.0.1"; prefixLength = 24;}];
|
||||
};
|
||||
};
|
||||
users.users.liminix = {
|
||||
isNormalUser = true;
|
||||
uid = 1000;
|
||||
extraGroups = [ "wheel" ];
|
||||
openssh.authorizedKeys.keys = cfg.keys;
|
||||
extraGroups = [ "wheel"];
|
||||
};
|
||||
services.getty.autologinUser = "liminix";
|
||||
};
|
||||
|
@ -1,12 +1,8 @@
|
||||
{ ... }:
|
||||
{...}:
|
||||
{
|
||||
bordervm = {
|
||||
# ethernet.pci = { id = "01:00.0"; enable = true; };
|
||||
ethernet.usb = {
|
||||
vendor = "0x0bda";
|
||||
product = "0x8153";
|
||||
enable = true;
|
||||
};
|
||||
ethernet.usb = { vendor = "0x0bda"; product = "0x8153"; enable = true; };
|
||||
l2tp = {
|
||||
host = "l2tp.aa.net.uk";
|
||||
};
|
||||
|
89
ci.nix
89
ci.nix
@ -1,7 +1,12 @@
|
||||
{
|
||||
nixpkgs
|
||||
, unstable
|
||||
, liminix
|
||||
, ... }:
|
||||
let
|
||||
pkgs = import <nixpkgs> { };
|
||||
liminix = <liminix>;
|
||||
borderVmConf = ./bordervm.conf-example.nix;
|
||||
inherit (builtins) map;
|
||||
pkgs = (import nixpkgs {});
|
||||
borderVmConf = ./bordervm.conf-example.nix;
|
||||
inherit (pkgs.lib.attrsets) genAttrs;
|
||||
devices = [
|
||||
"gl-ar750"
|
||||
@ -11,37 +16,67 @@ 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;
|
||||
inherit nixpkgs borderVmConf;
|
||||
device = import (liminix + "/devices/${name}");
|
||||
liminix-config = vanilla;
|
||||
}).outputs.default;
|
||||
tests = import ./tests/ci.nix;
|
||||
jobs =
|
||||
(genAttrs devices for-device)
|
||||
// tests
|
||||
// {
|
||||
buildEnv =
|
||||
(import liminix {
|
||||
inherit borderVmConf;
|
||||
device = import (liminix + "/devices/qemu");
|
||||
liminix-config = vanilla;
|
||||
}).buildEnv;
|
||||
doc = pkgs.callPackage ./doc.nix { inherit liminix borderVmConf; } ;
|
||||
(genAttrs devices for-device) //
|
||||
tests //
|
||||
{
|
||||
buildEnv = (import liminix {
|
||||
inherit nixpkgs borderVmConf;
|
||||
device = import (liminix + "/devices/qemu");
|
||||
liminix-config = vanilla;
|
||||
}).buildEnv;
|
||||
doc =
|
||||
let json =
|
||||
(import liminix {
|
||||
inherit nixpkgs borderVmConf;
|
||||
device = import (liminix + "/devices/qemu");
|
||||
liminix-config = {...} : {
|
||||
imports = [ ./modules/all-modules.nix ];
|
||||
};
|
||||
}).outputs.optionsJson;
|
||||
installers = map (f: "system.outputs.${f}") [
|
||||
"vmroot"
|
||||
"mtdimage"
|
||||
"ubimage"
|
||||
];
|
||||
inherit (pkgs.lib) concatStringsSep;
|
||||
in pkgs.stdenv.mkDerivation {
|
||||
name = "liminix-doc";
|
||||
nativeBuildInputs = with pkgs; [
|
||||
gnumake sphinx fennel luaPackages.lyaml
|
||||
];
|
||||
src = ./.;
|
||||
buildPhase = ''
|
||||
cat ${json} | fennel --correlate doc/parse-options.fnl > doc/modules-generated.rst
|
||||
cat ${json} | fennel --correlate doc/parse-options-outputs.fnl > doc/outputs-generated.rst
|
||||
cp ${(import ./doc/hardware.nix)} doc/hardware.rst
|
||||
make -C doc html
|
||||
'';
|
||||
installPhase = ''
|
||||
mkdir -p $out/nix-support $out/share/doc/
|
||||
cd doc
|
||||
cp *-generated.rst $out
|
||||
ln -s ${json} $out/options.json
|
||||
cp -a _build/html $out/share/doc/liminix
|
||||
echo "file source-dist \"$out/share/doc/liminix\"" \
|
||||
> $out/nix-support/hydra-build-products
|
||||
'';
|
||||
};
|
||||
with-unstable = (import liminix {
|
||||
nixpkgs = unstable;
|
||||
inherit borderVmConf;
|
||||
device = import (liminix + "/devices/qemu");
|
||||
liminix-config = vanilla;
|
||||
}).outputs.default;
|
||||
};
|
||||
in
|
||||
jobs
|
||||
// {
|
||||
all = pkgs.mkShell {
|
||||
name = "all tests";
|
||||
contents = pkgs.lib.collect pkgs.lib.isDerivation jobs;
|
||||
};
|
||||
}
|
||||
in jobs
|
||||
|
86
default.nix
86
default.nix
@ -1,45 +1,31 @@
|
||||
{
|
||||
deviceName ? null,
|
||||
device ? (import ./devices/${deviceName}),
|
||||
liminix-config ? <liminix-config>,
|
||||
borderVmConf ? ./bordervm.conf.nix,
|
||||
imageType ? "primary",
|
||||
device
|
||||
, liminix-config ? <liminix-config>
|
||||
, nixpkgs ? <nixpkgs>
|
||||
, borderVmConf ? ./bordervm.conf.nix
|
||||
, imageType ? "primary"
|
||||
}:
|
||||
|
||||
let
|
||||
overlay = import ./overlay.nix;
|
||||
pkgs = import <nixpkgs> (
|
||||
device.system
|
||||
// {
|
||||
overlays = [ overlay ];
|
||||
config = {
|
||||
allowUnsupportedSystem = true; # mipsel
|
||||
permittedInsecurePackages = [
|
||||
"python-2.7.18.6" # kernel backports needs python <3
|
||||
"python-2.7.18.7"
|
||||
];
|
||||
};
|
||||
}
|
||||
);
|
||||
pkgs = import nixpkgs (device.system // {
|
||||
overlays = [overlay];
|
||||
config = {
|
||||
allowUnsupportedSystem = true; # mipsel
|
||||
permittedInsecurePackages = [
|
||||
"python-2.7.18.6" # kernel backports needs python <3
|
||||
"python-2.7.18.7"
|
||||
];
|
||||
};
|
||||
});
|
||||
|
||||
eval = pkgs.lib.evalModules {
|
||||
specialArgs = {
|
||||
modulesPath = builtins.toString ./modules;
|
||||
};
|
||||
modules = [
|
||||
{
|
||||
_module.args = {
|
||||
inherit pkgs;
|
||||
inherit (pkgs) lim;
|
||||
};
|
||||
}
|
||||
{ _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
|
||||
./modules/s6
|
||||
@ -52,34 +38,23 @@ 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 = [ overlay ]; })
|
||||
(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
|
||||
@ -97,7 +72,6 @@ in
|
||||
min-copy-closure
|
||||
fennelrepl
|
||||
lzma
|
||||
lua
|
||||
];
|
||||
};
|
||||
}
|
||||
|
@ -4,8 +4,15 @@
|
||||
******************************
|
||||
|
||||
This device is based on a 64 bit Mediatek MT7622 ARM platform,
|
||||
and is mostly feature-complete in Liminix but as of Dec 2024
|
||||
has seen very little actual use.
|
||||
and is "work in progress" in Liminix.
|
||||
|
||||
.. note:: The factory flash image contains ECC errors that make it
|
||||
incompatible with Liminix: you need to use the `OpenWrt
|
||||
UBI Installer <https://github.com/dangowrt/owrt-ubi-installer>`_ to
|
||||
rewrite the partition layout before you can flash
|
||||
Liminix onto it (or even use it with
|
||||
:ref:`system-outputs-tftpboot`, if you want the wireless
|
||||
to work).
|
||||
|
||||
Hardware summary
|
||||
================
|
||||
@ -20,94 +27,10 @@
|
||||
Installation
|
||||
============
|
||||
|
||||
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.
|
||||
Installation is currently a manual process (you need a :ref:`serial <serial>` conection and
|
||||
TFTP) following the instructions at :ref:`system-outputs-ubimage`
|
||||
|
||||
|
||||
Preparation
|
||||
-----------
|
||||
|
||||
To prepare the device for Liminix you first need to use the
|
||||
`OpenWrt UBI Installer
|
||||
<https://github.com/dangowrt/owrt-ubi-installer>`_ image to
|
||||
rewrite the flash layout. 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
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
MT7622> setenv ipaddr 10.0.0.6
|
||||
MT7622> setenv serverip 10.0.0.1
|
||||
MT7622> tftpboot 0x42000000 openwrt-mediatek-mt7622-linksys_e8450-ubi-initramfs-recovery-installer.itb
|
||||
MT7622> bootm 0x42000000
|
||||
|
||||
This will write the new flash layout and then boot into a
|
||||
"recovery" OpenWrt installation.
|
||||
|
||||
Building/installing Liminix
|
||||
----------------
|
||||
|
||||
The default target for this device is ``outputs.ubimage`` which
|
||||
makes a ubifs image suitable for use with :command:`ubiupdatevol`.
|
||||
To write this to the device we use the OpenWrt recovery system
|
||||
installed in the previous step. In this configuration the
|
||||
device assigns itself the IP address 192.168.1.1/24 on its LAN
|
||||
ports and expects the connected computer to have 192.168.1.254
|
||||
|
||||
.. warning:: The `ubi0_7` device in these instructions is correct
|
||||
as of Dec 2024 (dangowrt/owrt-ubi-installer commit
|
||||
d79e7928). If you are installing some time later, it
|
||||
is important to check the output from
|
||||
:command:`ubinfo -a` and make sure you are updating
|
||||
the "liminix" volume and not some other one which had
|
||||
been introduced since I wrote this.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ nix-build -I liminix-config=./my-configuration.nix --arg device "import ./devices/belkin-rt3200" -A outputs.default
|
||||
$ cat result/rootfs | ssh root@192.168.1.1 "cat > /tmp/rootfs"
|
||||
$ ssh root@192.168.1.1
|
||||
root@OpenWrt:~# ubimkvol /dev/ubi0 --name=liminix --maxavsize
|
||||
root@OpenWrt:~# ubinfo -a
|
||||
[...]
|
||||
Volume ID: 7 (on ubi0)
|
||||
Type: dynamic
|
||||
Alignment: 1
|
||||
Size: 851 LEBs (108056576 bytes, 103.0 MiB)
|
||||
State: OK
|
||||
Name: liminix
|
||||
Character device major/minor: 250:8
|
||||
root@OpenWrt:~# ubiupdatevol /dev/ubi0_7 /tmp/rootfs
|
||||
|
||||
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 = {
|
||||
@ -115,218 +38,201 @@
|
||||
};
|
||||
};
|
||||
|
||||
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
|
||||
{
|
||||
module = {pkgs, config, lib, lim, ... }:
|
||||
let firmware = pkgs.stdenv.mkDerivation {
|
||||
name = "wlan-firmware";
|
||||
phases = ["installPhase"];
|
||||
installPhase = ''
|
||||
mkdir $out
|
||||
cp ${pkgs.linux-firmware}/lib/firmware/mediatek/{mt7915,mt7615,mt7622}* $out
|
||||
'';
|
||||
};
|
||||
in {
|
||||
imports = [
|
||||
../../modules/arch/aarch64.nix
|
||||
../../modules/outputs/tftpboot.nix
|
||||
../../modules/outputs/ubifs.nix
|
||||
];
|
||||
config = {
|
||||
kernel = {
|
||||
extraPatchPhase = ''
|
||||
${openwrt.applyPatches.mediatek}
|
||||
'';
|
||||
src = openwrt.kernelSrc;
|
||||
version = openwrt.kernelVersion;
|
||||
config = {
|
||||
PCI = "y";
|
||||
ARCH_MEDIATEK = "y";
|
||||
# ARM_MEDIATEK_CPUFREQ = "y";
|
||||
kernel = {
|
||||
src = pkgs.pkgsBuildBuild.fetchurl {
|
||||
name = "linux.tar.gz";
|
||||
url = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.137.tar.gz";
|
||||
hash = "sha256-PkdzUKZ0IpBiWe/RS70J76JKnBFzRblWcKlaIFNxnHQ=";
|
||||
};
|
||||
extraPatchPhase = ''
|
||||
${pkgs.openwrt.applyPatches.mediatek}
|
||||
'';
|
||||
config = {
|
||||
PCI = "y";
|
||||
ARCH_MEDIATEK = "y";
|
||||
# ARM_MEDIATEK_CPUFREQ = "y";
|
||||
|
||||
# needed for "Cannot find regmap for /infracfg@10000000"
|
||||
MFD_SYSCON = "y";
|
||||
MTK_INFRACFG = "y";
|
||||
# needed for "Cannot find regmap for /infracfg@10000000"
|
||||
MFD_SYSCON = "y";
|
||||
MTK_INFRACFG = "y";
|
||||
|
||||
MTK_PMIC_WRAP = "y";
|
||||
DMADEVICES = "y";
|
||||
MTK_HSDMA = "y";
|
||||
MTK_SCPSYS = "y";
|
||||
MTK_SCPSYS_PM_DOMAINS = "y";
|
||||
# MTK_THERMAL="y";
|
||||
MTK_TIMER = "y";
|
||||
MTK_PMIC_WRAP = "y";
|
||||
MTK_EFUSE="y";
|
||||
# MTK_HSDMA="y";
|
||||
MTK_SCPSYS="y";
|
||||
MTK_SCPSYS_PM_DOMAINS="y";
|
||||
# MTK_THERMAL="y";
|
||||
MTK_TIMER="y";
|
||||
|
||||
COMMON_CLK_MT7622 = "y";
|
||||
COMMON_CLK_MT7622_ETHSYS = "y";
|
||||
COMMON_CLK_MT7622_HIFSYS = "y";
|
||||
COMMON_CLK_MT7622_AUDSYS = "y";
|
||||
PM_CLK = "y";
|
||||
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";
|
||||
# MEDIATEK_WATCHDOG = "y";
|
||||
NET_MEDIATEK_SOC = "y";
|
||||
NET_MEDIATEK_SOC_WED = "y";
|
||||
NET_MEDIATEK_STAR_EMAC = "y"; # this enables REGMAP_MMIO
|
||||
NET_VENDOR_MEDIATEK = "y";
|
||||
PCIE_MEDIATEK = "y";
|
||||
|
||||
BLOCK = "y"; # move this to base option
|
||||
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";
|
||||
NET_DSA_MT7530_MDIO = "y";
|
||||
# Distributed Switch Architecture is needed
|
||||
# to make the ethernet ports visible
|
||||
NET_DSA="y";
|
||||
NET_DSA_MT7530="y";
|
||||
NET_DSA_TAG_MTK="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";
|
||||
PSTORE = "y";
|
||||
PSTORE_RAM = "y";
|
||||
PSTORE_CONSOLE = "y";
|
||||
PSTORE_DEFLATE_COMPRESS = "n";
|
||||
|
||||
# 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";
|
||||
};
|
||||
};
|
||||
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 "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 ];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
boot = {
|
||||
commandLine = [ "console=ttyS0,115200" ];
|
||||
tftp.loadAddress = lim.parseInt "0x4007ff28";
|
||||
imageFormat = "fit";
|
||||
};
|
||||
filesystem =
|
||||
let inherit (pkgs.pseudofile) dir symlink;
|
||||
in
|
||||
dir {
|
||||
lib = dir {
|
||||
firmware = dir {
|
||||
mediatek = symlink firmware;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
hardware =
|
||||
let
|
||||
openwrt = pkgs.openwrt;
|
||||
mac80211 = pkgs.kmodloader.override {
|
||||
targets = ["mt7615e" "mt7915e"];
|
||||
inherit (config.system.outputs) kernel;
|
||||
};
|
||||
in {
|
||||
ubi = {
|
||||
minIOSize = "2048";
|
||||
eraseBlockSize = "126976";
|
||||
maxLEBcount = "1024"; # guessing
|
||||
};
|
||||
|
||||
defaultOutput = "ubimage";
|
||||
# the kernel expects this to be on a 2MB boundary. U-Boot
|
||||
# (I don't know why) has a default of 0x41080000, which isn't.
|
||||
# We put it at the 32MB mark so that tftpboot can put its rootfs
|
||||
# image and DTB underneath, but maybe this is a terrible waste of
|
||||
# RAM unless the kernel is able to reuse it later. Oh well
|
||||
loadAddress = lim.parseInt "0x42000000";
|
||||
entryPoint = lim.parseInt "0x42000000";
|
||||
rootDevice = "ubi0:liminix";
|
||||
dts = {
|
||||
src = "${openwrt.src}/target/linux/mediatek/dts/mt7622-linksys-e8450-ubi.dts";
|
||||
includes = [
|
||||
"${openwrt.src}/target/linux/mediatek/dts"
|
||||
"${config.system.outputs.kernel.modulesupport}/arch/arm64/boot/dts/mediatek/"
|
||||
];
|
||||
};
|
||||
|
||||
# - 0x000000000000-0x000008000000 : "spi-nand0"
|
||||
# - 0x000000000000-0x000000080000 : "bl2"
|
||||
# - 0x000000080000-0x0000001c0000 : "fip"
|
||||
# - 0x0000001c0000-0x0000002c0000 : "factory"
|
||||
# - 0x0000002c0000-0x000000300000 : "reserved"
|
||||
# - 0x000000300000-0x000008000000 : "ubi"
|
||||
|
||||
networkInterfaces =
|
||||
let
|
||||
inherit (config.system.service.network) link;
|
||||
inherit (config.system.service) bridge;
|
||||
in rec {
|
||||
wan = link.build { ifname = "wan"; };
|
||||
lan1 = link.build { ifname = "lan1"; };
|
||||
lan2 = link.build { ifname = "lan2"; };
|
||||
lan3 = link.build { ifname = "lan3"; };
|
||||
lan4 = link.build { ifname = "lan4"; };
|
||||
lan = lan3;
|
||||
|
||||
wlan = link.build {
|
||||
ifname = "wlan0";
|
||||
dependencies = [ mac80211 ];
|
||||
};
|
||||
wlan5 = link.build {
|
||||
ifname = "wlan1";
|
||||
dependencies = [ mac80211 ];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -1,8 +0,0 @@
|
||||
/ {
|
||||
reserved-memory {
|
||||
/* make sure address matches upstream */
|
||||
ramoops@42ff0000 {
|
||||
pmsg-size = <0x10000>;
|
||||
};
|
||||
};
|
||||
};
|
@ -5,6 +5,11 @@
|
||||
];
|
||||
config = {
|
||||
kernel = {
|
||||
src = pkgs.pkgsBuildBuild.fetchurl {
|
||||
name = "linux.tar.gz";
|
||||
url = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.137.tar.gz";
|
||||
hash = "sha256-PkdzUKZ0IpBiWe/RS70J76JKnBFzRblWcKlaIFNxnHQ=";
|
||||
};
|
||||
config = {
|
||||
MTD = "y";
|
||||
MTD_BLOCK = "y";
|
||||
@ -18,35 +23,27 @@
|
||||
VIRTIO_BLK = "y";
|
||||
VIRTIO_NET = "y";
|
||||
};
|
||||
conditionalConfig = {
|
||||
WLAN = {
|
||||
MAC80211_HWSIM = "m";
|
||||
};
|
||||
};
|
||||
};
|
||||
hardware =
|
||||
let
|
||||
mac80211 = pkgs.kmodloader.override {
|
||||
inherit (config.system.outputs) kernel;
|
||||
targets = [ "mac80211_hwsim" ];
|
||||
mac80211 = pkgs.mac80211.override {
|
||||
drivers = ["mac80211_hwsim"];
|
||||
klibBuild = config.system.outputs.kernel.modulesupport;
|
||||
};
|
||||
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:12.0/virtio0";
|
||||
devpath = "/devices/pci0000:00/0000:00:13.0/virtio0";
|
||||
ifname = "wan";
|
||||
};
|
||||
lan = link.build {
|
||||
devpath = "/devices/pci0000:00/0000:00:13.0/virtio1";
|
||||
devpath = "/devices/pci0000:00/0000:00:14.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,11 +41,10 @@
|
||||
:ref:`system-outputs-mtdimage` can be flashed using the
|
||||
vendor web UI or the U-Boot emergency "unbrick" routine.
|
||||
|
||||
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.
|
||||
For flashing from an existing Liminix system (we believe that) it
|
||||
is necessary to first boot into a :ref:`system-outputs-kexecboot`
|
||||
system, otherwise you'll be overwriting flash partitions while
|
||||
they're in use - and that might not end well.
|
||||
|
||||
Vendor web page: https://www.gl-inet.com/products/gl-ar750/
|
||||
|
||||
@ -53,16 +52,8 @@
|
||||
|
||||
'';
|
||||
|
||||
module =
|
||||
{
|
||||
pkgs,
|
||||
config,
|
||||
lim,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
module = {pkgs, config, lim, ... }:
|
||||
let
|
||||
inherit (lib) mkIf;
|
||||
openwrt = pkgs.openwrt;
|
||||
firmwareBlobs = pkgs.pkgsBuildBuild.fetchFromGitHub {
|
||||
owner = "kvalo";
|
||||
@ -72,7 +63,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
|
||||
@ -81,10 +72,7 @@
|
||||
'';
|
||||
};
|
||||
mac80211 = pkgs.kmodloader.override {
|
||||
targets = [
|
||||
"ath9k"
|
||||
"ath10k_pci"
|
||||
];
|
||||
targets = ["ath9k" "ath10k_pci"];
|
||||
inherit (config.system.outputs) kernel;
|
||||
dependencies = [ ath10k_cal_data ];
|
||||
};
|
||||
@ -92,8 +80,7 @@
|
||||
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)))
|
||||
@ -102,11 +89,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
|
||||
{
|
||||
inherit (pkgs.liminix.networking) interface;
|
||||
in {
|
||||
imports = [
|
||||
../../modules/network
|
||||
../../modules/arch/mipseb.nix
|
||||
@ -130,37 +117,23 @@
|
||||
rootDevice = "/dev/mtdblock5";
|
||||
dts = {
|
||||
src = "${openwrt.src}/target/linux/ath79/dts/qca9531_glinet_gl-ar750.dts";
|
||||
includePaths = [
|
||||
includes = [
|
||||
"${openwrt.src}/target/linux/ath79/dts"
|
||||
];
|
||||
includes = mkIf config.logging.persistent.enable [
|
||||
./pstore-ramoops.dtsi
|
||||
];
|
||||
};
|
||||
|
||||
networkInterfaces =
|
||||
let
|
||||
inherit (config.system.service.network) link;
|
||||
in
|
||||
{
|
||||
lan = link.build {
|
||||
ifname = "lan";
|
||||
devpath = "/devices/platform/ahb/1a000000.eth";
|
||||
};
|
||||
wan = link.build {
|
||||
ifname = "wan";
|
||||
devpath = "/devices/platform/ahb/19000000.eth";
|
||||
};
|
||||
let inherit (config.system.service.network) link;
|
||||
in {
|
||||
lan = link.build { ifname = "eth0"; };
|
||||
wan = link.build { ifname = "eth1"; };
|
||||
wlan = link.build {
|
||||
ifname = "wlan0";
|
||||
dependencies = [ mac80211 ];
|
||||
};
|
||||
wlan5 = link.build {
|
||||
ifname = "wlan1";
|
||||
dependencies = [
|
||||
ath10k_cal_data
|
||||
mac80211
|
||||
];
|
||||
dependencies = [ ath10k_cal_data mac80211 ];
|
||||
};
|
||||
};
|
||||
};
|
||||
@ -176,9 +149,14 @@
|
||||
};
|
||||
boot.tftp = {
|
||||
loadAddress = lim.parseInt "0x00A00000";
|
||||
appendDTB = true;
|
||||
};
|
||||
kernel = {
|
||||
src = pkgs.pkgsBuildBuild.fetchurl {
|
||||
name = "linux.tar.gz";
|
||||
url = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.137.tar.gz";
|
||||
hash = "sha256-PkdzUKZ0IpBiWe/RS70J76JKnBFzRblWcKlaIFNxnHQ=";
|
||||
};
|
||||
|
||||
# Mainline linux 5.19 doesn't have device-tree support for
|
||||
# this device or even for the SoC, so we use the extensive
|
||||
# OpenWrt kernel patches
|
||||
@ -210,31 +188,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";
|
||||
|
||||
|
@ -1,9 +0,0 @@
|
||||
/ {
|
||||
reserved-memory {
|
||||
ramoops@03f00000 {
|
||||
compatible = "ramoops";
|
||||
reg = <0x03f00000 0x10000>;
|
||||
pmsg-size = <0x10000>;
|
||||
};
|
||||
};
|
||||
};
|
@ -6,7 +6,7 @@
|
||||
config = "mipsel-unknown-linux-musl";
|
||||
gcc = {
|
||||
abi = "32";
|
||||
arch = "mips32"; # maybe mips_24kc-
|
||||
arch = "mips32"; # maybe mips_24kc-
|
||||
};
|
||||
};
|
||||
};
|
||||
@ -32,11 +32,10 @@
|
||||
binary created by :ref:`system-outputs-mtdimage` can be flashed
|
||||
using the vendor web UI or the U-Boot emergency "unbrick" routine.
|
||||
|
||||
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.
|
||||
For flashing from an existing Liminix system (we think) it
|
||||
is necessary to first boot into a :ref:`system-outputs-kexecboot`
|
||||
system, otherwise you'll be overwriting flash partitions while
|
||||
they're in use - and that might not end well.
|
||||
|
||||
Vendor web page: https://www.gl-inet.com/products/gl-mt300a/
|
||||
|
||||
@ -44,22 +43,15 @@
|
||||
|
||||
'';
|
||||
|
||||
module =
|
||||
{
|
||||
pkgs,
|
||||
config,
|
||||
lib,
|
||||
lim,
|
||||
...
|
||||
}:
|
||||
module = { pkgs, config, lib, lim, ...}:
|
||||
let
|
||||
inherit (pkgs.liminix.networking) interface;
|
||||
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
|
||||
@ -90,7 +82,7 @@
|
||||
|
||||
dts = {
|
||||
src = "${openwrt.src}/target/linux/ramips/dts/mt7620a_glinet_gl-mt300a.dts";
|
||||
includePaths = [
|
||||
includes = [
|
||||
"${openwrt.src}/target/linux/ramips/dts"
|
||||
];
|
||||
};
|
||||
@ -98,19 +90,33 @@
|
||||
let
|
||||
inherit (config.system.service.network) link;
|
||||
inherit (config.system.service) vlan;
|
||||
in
|
||||
rec {
|
||||
inherit (pkgs.liminix.services) oneshot;
|
||||
swconfig = oneshot {
|
||||
name = "swconfig";
|
||||
up = ''
|
||||
PATH=${pkgs.swconfig}/bin:$PATH
|
||||
swconfig dev switch0 set reset
|
||||
swconfig dev switch0 set enable_vlan 1
|
||||
swconfig dev switch0 vlan 1 set ports '1 2 3 4 6t'
|
||||
swconfig dev switch0 vlan 2 set ports '0 6t'
|
||||
swconfig dev switch0 set apply
|
||||
'';
|
||||
down = "${pkgs.swconfig}/bin/swconfig dev switch0 set reset";
|
||||
};
|
||||
in rec {
|
||||
eth = link.build { ifname = "eth0"; };
|
||||
# lan and wan ports are both behind a switch on eth0
|
||||
lan = vlan.build {
|
||||
ifname = "eth0.1";
|
||||
primary = eth;
|
||||
vid = "1";
|
||||
dependencies = [swconfig eth];
|
||||
};
|
||||
wan = vlan.build {
|
||||
ifname = "eth0.2";
|
||||
primary = eth;
|
||||
vid = "2";
|
||||
dependencies = [swconfig eth];
|
||||
};
|
||||
wlan = link.build {
|
||||
ifname = "wlan0";
|
||||
@ -120,57 +126,58 @@
|
||||
};
|
||||
boot.tftp = {
|
||||
loadAddress = lim.parseInt "0x00A00000";
|
||||
appendDTB = true;
|
||||
};
|
||||
|
||||
kernel = {
|
||||
src = pkgs.fetchurl {
|
||||
name = "linux.tar.gz";
|
||||
url = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.137.tar.gz";
|
||||
hash = "sha256-PkdzUKZ0IpBiWe/RS70J76JKnBFzRblWcKlaIFNxnHQ=";
|
||||
};
|
||||
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-
|
||||
};
|
||||
};
|
||||
};
|
||||
@ -13,7 +13,7 @@
|
||||
GL.iNet GL-MT300N-v2
|
||||
********************
|
||||
|
||||
The GL-MT300N-v2 "Mango" is is very similar to the :ref:`gl-mt300a`, but is
|
||||
The GL-MT300N-v2 "Mango" is is very similar to the :ref:`MT300A <GL.iNet GL-MT300A>, but is
|
||||
based on the MT7628 chipset instead of MT7620. It's also marginally cheaper
|
||||
and comes in a yellow case not a blue one. Be sure your device is
|
||||
v2 not v1, which is a different animal and has only half as much RAM.
|
||||
@ -25,11 +25,10 @@
|
||||
binary created by :ref:`system-outputs-mtdimage` can be flashed
|
||||
using the vendor web UI or the U-Boot emergency "unbrick" routine.
|
||||
|
||||
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.
|
||||
For flashing from an existing Liminix system (we think) it
|
||||
is necessary to first boot into a :ref:`system-outputs-kexecboot`
|
||||
system, otherwise you'll be overwriting flash partitions while
|
||||
they're in use - and that might not end well.
|
||||
|
||||
Vendor web page: https://www.gl-inet.com/products/gl-mt300n-v2/
|
||||
|
||||
@ -37,29 +36,22 @@
|
||||
|
||||
'';
|
||||
|
||||
module =
|
||||
{
|
||||
pkgs,
|
||||
config,
|
||||
lib,
|
||||
lim,
|
||||
...
|
||||
}:
|
||||
module = { pkgs, config, lib, lim, ...}:
|
||||
let
|
||||
inherit (pkgs.liminix.networking) interface;
|
||||
inherit (pkgs.liminix.services) oneshot;
|
||||
inherit (pkgs.pseudofile) dir symlink;
|
||||
inherit (pkgs) openwrt;
|
||||
|
||||
mac80211 = pkgs.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
|
||||
@ -87,7 +79,7 @@
|
||||
|
||||
dts = {
|
||||
src = "${openwrt.src}/target/linux/ramips/dts/mt7628an_glinet_gl-mt300n-v2.dts";
|
||||
includePaths = [
|
||||
includes = [
|
||||
"${openwrt.src}/target/linux/ramips/dts"
|
||||
];
|
||||
};
|
||||
@ -105,14 +97,10 @@
|
||||
swconfig dev switch0 vlan 2 set ports '0 6t'
|
||||
swconfig dev switch0 set apply
|
||||
'';
|
||||
down = "${pkgs.swconfig}/bin/swconfig dev switch0 set reset";
|
||||
};
|
||||
in
|
||||
rec {
|
||||
eth = link.build {
|
||||
ifname = "eth0";
|
||||
dependencies = [ swconfig ];
|
||||
down = "swconfig dev switch0 set reset";
|
||||
};
|
||||
in rec {
|
||||
eth = link.build { ifname = "eth0"; dependencies = [swconfig]; };
|
||||
# lan and wan ports are both behind a switch on eth0
|
||||
lan = vlan.build {
|
||||
ifname = "eth0.1";
|
||||
@ -134,68 +122,69 @@
|
||||
# 20MB seems to give enough room to uncompress the kernel
|
||||
# without anything getting trodden on. 10MB was too small
|
||||
loadAddress = lim.parseInt "0x1400000";
|
||||
appendDTB = true;
|
||||
};
|
||||
|
||||
kernel = {
|
||||
src = pkgs.fetchurl {
|
||||
name = "linux.tar.gz";
|
||||
url = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.137.tar.gz";
|
||||
hash = "sha256-PkdzUKZ0IpBiWe/RS70J76JKnBFzRblWcKlaIFNxnHQ=";
|
||||
};
|
||||
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";
|
||||
|
@ -1,751 +0,0 @@
|
||||
{
|
||||
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 ];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
@ -19,39 +19,33 @@
|
||||
ARM targets differ from MIPS in that the kernel format expected
|
||||
by QEMU is an "Image" (raw binary file) rather than an ELF
|
||||
file, but this is taken care of by :command:`run.sh`. Check the
|
||||
documentation for the :ref:`qemu` target for more information.
|
||||
documentation for the :ref:`QEMU` (MIPS) target for more information.
|
||||
|
||||
'';
|
||||
|
||||
# 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 = {pkgs, config, lim, ... }: {
|
||||
imports = [
|
||||
../../modules/arch/aarch64.nix
|
||||
../families/qemu.nix
|
||||
];
|
||||
kernel = {
|
||||
config = {
|
||||
VIRTUALIZATION = "y";
|
||||
PCI_HOST_GENERIC="y";
|
||||
|
||||
SERIAL_AMBA_PL011 = "y";
|
||||
SERIAL_AMBA_PL011_CONSOLE = "y";
|
||||
};
|
||||
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,36 +24,30 @@
|
||||
'';
|
||||
installer = "vmroot";
|
||||
|
||||
module =
|
||||
{ config, lim, ... }:
|
||||
{
|
||||
imports = [
|
||||
../../modules/arch/arm.nix
|
||||
../families/qemu.nix
|
||||
];
|
||||
kernel = {
|
||||
config = {
|
||||
PCI_HOST_GENERIC = "y";
|
||||
ARCH_VIRT = "y";
|
||||
module = {pkgs, config, lim, ... }: {
|
||||
imports = [
|
||||
../../modules/arch/arm.nix
|
||||
../families/qemu.nix
|
||||
];
|
||||
kernel = {
|
||||
config = {
|
||||
PCI_HOST_GENERIC = "y";
|
||||
ARCH_VIRT = "y";
|
||||
|
||||
VFP = "y";
|
||||
NEON = "y";
|
||||
AEABI = "y";
|
||||
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;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
config = "mips-unknown-linux-musl";
|
||||
gcc = {
|
||||
abi = "32";
|
||||
arch = "mips32"; # maybe mips_24kc-
|
||||
arch = "mips32"; # maybe mips_24kc-
|
||||
};
|
||||
};
|
||||
};
|
||||
@ -36,50 +36,41 @@
|
||||
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 = {pkgs, 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";
|
||||
};
|
||||
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/"
|
||||
];
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
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";
|
||||
includes = [
|
||||
"${config.system.outputs.kernel.modulesupport}/arch/mips/boot/dts/"
|
||||
];
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -33,25 +33,16 @@
|
||||
};
|
||||
};
|
||||
|
||||
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
|
||||
@ -59,278 +50,281 @@
|
||||
];
|
||||
config = {
|
||||
kernel = {
|
||||
src = pkgs.pkgsBuildBuild.fetchurl {
|
||||
name = "linux.tar.gz";
|
||||
url = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.137.tar.gz";
|
||||
hash = "sha256-PkdzUKZ0IpBiWe/RS70J76JKnBFzRblWcKlaIFNxnHQ=";
|
||||
};
|
||||
extraPatchPhase = ''
|
||||
${pkgs.openwrt.applyPatches.ramips}
|
||||
'';
|
||||
config =
|
||||
{
|
||||
# Initially taken from openwrt's ./target/linux/ramips/mt7621/config-5.15,
|
||||
# then tweaked here and there
|
||||
ARCH_32BIT_OFF_T = "y";
|
||||
ARCH_HIBERNATION_POSSIBLE = "y";
|
||||
ARCH_KEEP_MEMBLOCK = "y";
|
||||
ARCH_MMAP_RND_BITS_MAX = "15";
|
||||
ARCH_MMAP_RND_COMPAT_BITS_MAX = "15";
|
||||
ARCH_SUSPEND_POSSIBLE = "y";
|
||||
AT803X_PHY = "y";
|
||||
BLK_MQ_PCI = "y";
|
||||
BOARD_SCACHE = "y";
|
||||
CEVT_R4K = "y";
|
||||
CLKSRC_MIPS_GIC = "y";
|
||||
CLK_MT7621 = "y";
|
||||
CLOCKSOURCE_WATCHDOG = "y";
|
||||
CLONE_BACKWARDS = "y";
|
||||
CMDLINE_BOOL = "y";
|
||||
COMMON_CLK = "y";
|
||||
COMPAT_32BIT_TIME = "y";
|
||||
CPU_GENERIC_DUMP_TLB = "y";
|
||||
CPU_HAS_DIEI = "y";
|
||||
CPU_HAS_PREFETCH = "y";
|
||||
CPU_HAS_RIXI = "y";
|
||||
CPU_HAS_SYNC = "y";
|
||||
CPU_LITTLE_ENDIAN = "y";
|
||||
CPU_MIPS32 = "y";
|
||||
CPU_MIPS32_R2 = "y";
|
||||
CPU_MIPSR2 = "y";
|
||||
CPU_MIPSR2_IRQ_EI = "y";
|
||||
CPU_MIPSR2_IRQ_VI = "y";
|
||||
CPU_NEEDS_NO_SMARTMIPS_OR_MICROMIPS = "y";
|
||||
CPU_R4K_CACHE_TLB = "y";
|
||||
CPU_RMAP = "y";
|
||||
CPU_SUPPORTS_32BIT_KERNEL = "y";
|
||||
CPU_SUPPORTS_HIGHMEM = "y";
|
||||
CPU_SUPPORTS_MSA = "y";
|
||||
CRC16 = "y";
|
||||
CRYPTO_DEFLATE = "y";
|
||||
CRYPTO_HASH_INFO = "y";
|
||||
CRYPTO_LIB_BLAKE2S_GENERIC = "y";
|
||||
CRYPTO_LIB_POLY1305_RSIZE = "2";
|
||||
CRYPTO_LZO = "y";
|
||||
CRYPTO_ZSTD = "y";
|
||||
CSRC_R4K = "y";
|
||||
DIMLIB = "y";
|
||||
DMA_NONCOHERENT = "y";
|
||||
DTB_RT_NONE = "y";
|
||||
DTC = "y";
|
||||
EARLY_PRINTK = "y";
|
||||
FIXED_PHY = "y";
|
||||
FWNODE_MDIO = "y";
|
||||
FW_LOADER_PAGED_BUF = "y";
|
||||
GENERIC_ATOMIC64 = "y";
|
||||
GENERIC_CLOCKEVENTS = "y";
|
||||
GENERIC_CMOS_UPDATE = "y";
|
||||
GENERIC_CPU_AUTOPROBE = "y";
|
||||
GENERIC_FIND_FIRST_BIT = "y";
|
||||
GENERIC_GETTIMEOFDAY = "y";
|
||||
GENERIC_IOMAP = "y";
|
||||
GENERIC_IRQ_CHIP = "y";
|
||||
GENERIC_IRQ_EFFECTIVE_AFF_MASK = "y";
|
||||
GENERIC_IRQ_SHOW = "y";
|
||||
GENERIC_LIB_ASHLDI3 = "y";
|
||||
GENERIC_LIB_ASHRDI3 = "y";
|
||||
GENERIC_LIB_CMPDI2 = "y";
|
||||
GENERIC_LIB_LSHRDI3 = "y";
|
||||
GENERIC_LIB_UCMPDI2 = "y";
|
||||
GENERIC_PCI_IOMAP = "y";
|
||||
GENERIC_PHY = "y";
|
||||
GENERIC_PINCONF = "y";
|
||||
GENERIC_SCHED_CLOCK = "y";
|
||||
GENERIC_SMP_IDLE_THREAD = "y";
|
||||
GENERIC_TIME_VSYSCALL = "y";
|
||||
GLOB = "y";
|
||||
GPIOLIB_IRQCHIP = "y";
|
||||
GPIO_CDEV = "y";
|
||||
GPIO_GENERIC = "y";
|
||||
GPIO_MT7621 = "y";
|
||||
GRO_CELLS = "y";
|
||||
HANDLE_DOMAIN_IRQ = "y";
|
||||
HARDWARE_WATCHPOINTS = "y";
|
||||
HAS_DMA = "y";
|
||||
HAS_IOMEM = "y";
|
||||
HAS_IOPORT_MAP = "y";
|
||||
I2C = "y";
|
||||
I2C_ALGOBIT = "y";
|
||||
I2C_BOARDINFO = "y";
|
||||
I2C_CHARDEV = "y";
|
||||
I2C_GPIO = "y";
|
||||
I2C_MT7621 = "y";
|
||||
ICPLUS_PHY = "y";
|
||||
IRQCHIP = "y";
|
||||
IRQ_DOMAIN = "y";
|
||||
IRQ_DOMAIN_HIERARCHY = "y";
|
||||
IRQ_FORCED_THREADING = "y";
|
||||
IRQ_MIPS_CPU = "y";
|
||||
IRQ_WORK = "y";
|
||||
LIBFDT = "y";
|
||||
LOCK_DEBUGGING_SUPPORT = "y";
|
||||
LZO_COMPRESS = "y";
|
||||
LZO_DECOMPRESS = "y";
|
||||
MDIO_BUS = "y";
|
||||
MDIO_DEVICE = "y";
|
||||
MDIO_DEVRES = "y";
|
||||
MEDIATEK_GE_PHY = "y";
|
||||
MEMFD_CREATE = "y";
|
||||
MFD_SYSCON = "y";
|
||||
MIGRATION = "y";
|
||||
MIKROTIK = "y";
|
||||
MIKROTIK_RB_SYSFS = "y";
|
||||
MIPS = "y";
|
||||
MIPS_ASID_BITS = "8";
|
||||
MIPS_ASID_SHIFT = "0";
|
||||
MIPS_CLOCK_VSYSCALL = "y";
|
||||
MIPS_CM = "y";
|
||||
MIPS_CPC = "y";
|
||||
MIPS_CPS = "y";
|
||||
MIPS_CPU_SCACHE = "y";
|
||||
MIPS_GIC = "y";
|
||||
MIPS_L1_CACHE_SHIFT = "5";
|
||||
MIPS_LD_CAN_LINK_VDSO = "y";
|
||||
MIPS_MT = "y";
|
||||
MIPS_MT_FPAFF = "y";
|
||||
MIPS_MT_SMP = "y";
|
||||
MIPS_NR_CPU_NR_MAP = "4";
|
||||
MIPS_PERF_SHARED_TC_COUNTERS = "y";
|
||||
MIPS_SPRAM = "y";
|
||||
MODULES_USE_ELF_REL = "y";
|
||||
MTD_CMDLINE_PARTS = "y";
|
||||
MTD_NAND_CORE = "y";
|
||||
MTD_NAND_ECC = "y";
|
||||
MTD_NAND_ECC_SW_HAMMING = "y";
|
||||
MTD_NAND_MT7621 = "y";
|
||||
MTD_NAND_MTK_BMT = "y";
|
||||
MTD_RAW_NAND = "y";
|
||||
MTD_ROUTERBOOT_PARTS = "y";
|
||||
MTD_SERCOMM_PARTS = "y";
|
||||
MTD_SPI_NOR = "y";
|
||||
MTD_SPLIT_FIT_FW = "y";
|
||||
MTD_SPLIT_MINOR_FW = "y";
|
||||
MTD_SPLIT_SEAMA_FW = "y";
|
||||
MTD_SPLIT_TPLINK_FW = "y";
|
||||
MTD_SPLIT_TRX_FW = "y";
|
||||
MTD_SPLIT_UIMAGE_FW = "y";
|
||||
MTD_UBI = "y";
|
||||
MTD_UBI_BEB_LIMIT = "20";
|
||||
MTD_UBI_BLOCK = "y";
|
||||
MTD_UBI_WL_THRESHOLD = "4096";
|
||||
MTD_VIRT_CONCAT = "y";
|
||||
NEED_DMA_MAP_STATE = "y";
|
||||
NET_DEVLINK = "y";
|
||||
NET_DSA = "y";
|
||||
NET_DSA_MT7530 = "y";
|
||||
NET_DSA_MT7530_MDIO = "y";
|
||||
NET_DSA_TAG_MTK = "y";
|
||||
NET_FLOW_LIMIT = "y";
|
||||
NET_MEDIATEK_SOC = "y";
|
||||
NET_SELFTESTS = "y";
|
||||
NET_SWITCHDEV = "y";
|
||||
NET_VENDOR_MEDIATEK = "y";
|
||||
NO_HZ_COMMON = "y";
|
||||
NO_HZ_IDLE = "y";
|
||||
NR_CPUS = "4";
|
||||
NVMEM = "y";
|
||||
OF = "y";
|
||||
OF_ADDRESS = "y";
|
||||
OF_EARLY_FLATTREE = "y";
|
||||
OF_FLATTREE = "y";
|
||||
OF_GPIO = "y";
|
||||
OF_IRQ = "y";
|
||||
OF_KOBJ = "y";
|
||||
OF_MDIO = "y";
|
||||
PAGE_POOL = "y";
|
||||
PAGE_POOL_STATS = "y";
|
||||
PCI = "y";
|
||||
PCIE_MT7621 = "y";
|
||||
PCI_DISABLE_COMMON_QUIRKS = "y";
|
||||
PCI_DOMAINS = "y";
|
||||
PCI_DOMAINS_GENERIC = "y";
|
||||
PCI_DRIVERS_GENERIC = "y";
|
||||
PCS_MTK_LYNXI = "y";
|
||||
PERF_USE_VMALLOC = "y";
|
||||
PGTABLE_LEVELS = "2";
|
||||
PHYLIB = "y";
|
||||
PHYLINK = "y";
|
||||
PHY_MT7621_PCI = "y";
|
||||
PINCTRL = "y";
|
||||
PINCTRL_AW9523 = "y";
|
||||
PINCTRL_MT7621 = "y";
|
||||
PINCTRL_RALINK = "y";
|
||||
PINCTRL_SX150X = "y";
|
||||
POWER_RESET = "y";
|
||||
POWER_RESET_GPIO = "y";
|
||||
POWER_SUPPLY = "y";
|
||||
PTP_1588_CLOCK_OPTIONAL = "y";
|
||||
QUEUED_RWLOCKS = "y";
|
||||
QUEUED_SPINLOCKS = "y";
|
||||
RALINK = "y";
|
||||
RATIONAL = "y";
|
||||
REGMAP = "y";
|
||||
REGMAP_I2C = "y";
|
||||
REGMAP_MMIO = "y";
|
||||
REGULATOR = "y";
|
||||
REGULATOR_FIXED_VOLTAGE = "y";
|
||||
RESET_CONTROLLER = "y";
|
||||
RFS_ACCEL = "y";
|
||||
RPS = "y";
|
||||
RTC_CLASS = "y";
|
||||
RTC_DRV_BQ32K = "y";
|
||||
RTC_DRV_PCF8563 = "y";
|
||||
RTC_I2C_AND_SPI = "y";
|
||||
SCHED_SMT = "y";
|
||||
SERIAL_8250 = "y";
|
||||
SERIAL_8250_CONSOLE = "y";
|
||||
SERIAL_8250_NR_UARTS = "3";
|
||||
SERIAL_8250_RUNTIME_UARTS = "3";
|
||||
SERIAL_MCTRL_GPIO = "y";
|
||||
SERIAL_OF_PLATFORM = "y";
|
||||
SGL_ALLOC = "y";
|
||||
SMP = "y";
|
||||
SMP_UP = "y";
|
||||
SOCK_RX_QUEUE_MAPPING = "y";
|
||||
SOC_BUS = "y";
|
||||
SOC_MT7621 = "y";
|
||||
SPI = "y";
|
||||
SPI_MASTER = "y";
|
||||
SPI_MEM = "y";
|
||||
SPI_MT7621 = "y";
|
||||
SRCU = "y";
|
||||
SWPHY = "y";
|
||||
SYNC_R4K = "y";
|
||||
SYSCTL_EXCEPTION_TRACE = "y";
|
||||
SYS_HAS_CPU_MIPS32_R1 = "y";
|
||||
SYS_HAS_CPU_MIPS32_R2 = "y";
|
||||
SYS_HAS_EARLY_PRINTK = "y";
|
||||
SYS_SUPPORTS_32BIT_KERNEL = "y";
|
||||
SYS_SUPPORTS_ARBIT_HZ = "y";
|
||||
SYS_SUPPORTS_HIGHMEM = "y";
|
||||
SYS_SUPPORTS_HOTPLUG_CPU = "y";
|
||||
SYS_SUPPORTS_LITTLE_ENDIAN = "y";
|
||||
SYS_SUPPORTS_MIPS16 = "y";
|
||||
SYS_SUPPORTS_MIPS_CPS = "y";
|
||||
SYS_SUPPORTS_MULTITHREADING = "y";
|
||||
SYS_SUPPORTS_SCHED_SMT = "y";
|
||||
SYS_SUPPORTS_SMP = "y";
|
||||
SYS_SUPPORTS_ZBOOT = "y";
|
||||
TARGET_ISA_REV = "2";
|
||||
TICK_CPU_ACCOUNTING = "y";
|
||||
TIMER_OF = "y";
|
||||
TIMER_PROBE = "y";
|
||||
TREE_RCU = "y";
|
||||
TREE_SRCU = "y";
|
||||
UBIFS_FS = "y";
|
||||
USB_SUPPORT = "y";
|
||||
USE_OF = "y";
|
||||
WEAK_ORDERING = "y";
|
||||
XPS = "y";
|
||||
XXHASH = "y";
|
||||
ZLIB_DEFLATE = "y";
|
||||
ZLIB_INFLATE = "y";
|
||||
ZSTD_COMPRESS = "y";
|
||||
ZSTD_DECOMPRESS = "y";
|
||||
}
|
||||
// lib.optionalAttrs (config.system.service ? watchdog) {
|
||||
RALINK_WDT = "y"; # watchdog
|
||||
MT7621_WDT = "y"; # or it might be this one
|
||||
};
|
||||
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";
|
||||
@ -351,100 +345,98 @@
|
||||
};
|
||||
};
|
||||
filesystem =
|
||||
let
|
||||
inherit (pkgs.pseudofile) dir symlink;
|
||||
in
|
||||
dir {
|
||||
lib = dir {
|
||||
firmware = dir {
|
||||
mediatek = symlink firmware;
|
||||
};
|
||||
};
|
||||
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;
|
||||
};
|
||||
|
||||
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 ];
|
||||
};
|
||||
};
|
||||
# since this is mentioned in the partition table as well?
|
||||
defaultOutput = "tplink-safeloader";
|
||||
# taken from openwrt sysupgrade image:
|
||||
# openwrt-23.05.2-ramips-mt7621-tplink_archer-ax23-v1-squashfs-sysupgrade.bin: u-boot legacy uImage, MIPS OpenWrt Linux-5.15.137, Linux/MIPS, OS Kernel Image (lzma), 2797386 bytes, Tue Nov 14 13:38:11 2023, Load Address: 0X80001000, Entry Point: 0X80001000, Header CRC: 0X19F74C5B, Data CRC: 0XF685563C
|
||||
loadAddress = lim.parseInt "0x80001000";
|
||||
entryPoint = lim.parseInt "0x80001000";
|
||||
rootDevice = "/dev/mtdblock3";
|
||||
dts = {
|
||||
src = "${openwrt.src}/target/linux/ramips/dts/mt7621_tplink_archer-ax23-v1.dts";
|
||||
includes = [
|
||||
"${openwrt.src}/target/linux/ramips/dts"
|
||||
"${config.system.outputs.kernel.modulesupport}/arch/arm64/boot/dts/mediatek/"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
networkInterfaces =
|
||||
let
|
||||
inherit (config.system.service.network) link;
|
||||
inherit (config.system.service) bridge;
|
||||
in rec {
|
||||
lan1 = link.build { ifname = "lan1"; };
|
||||
lan2 = link.build { ifname = "lan2"; };
|
||||
lan3 = link.build { ifname = "lan3"; };
|
||||
lan4 = link.build { ifname = "lan4"; };
|
||||
wan = link.build { ifname = "wan"; };
|
||||
|
||||
wlan = link.build {
|
||||
ifname = "wlan0";
|
||||
dependencies = [ mac80211 ];
|
||||
};
|
||||
wlan5 = link.build {
|
||||
ifname = "wlan1";
|
||||
dependencies = [ mac80211 ];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -153,18 +153,13 @@
|
||||
};
|
||||
};
|
||||
|
||||
module =
|
||||
{
|
||||
pkgs,
|
||||
config,
|
||||
lib,
|
||||
lim,
|
||||
...
|
||||
}:
|
||||
module = {pkgs, config, lib, lim, ... }:
|
||||
let
|
||||
openwrt = pkgs.openwrt;
|
||||
inherit (lib) mkOption types;
|
||||
inherit (pkgs.liminix.services) oneshot;
|
||||
inherit (pkgs) liminix;
|
||||
mtd_by_name_links = pkgs.liminix.services.oneshot rec {
|
||||
mtd_by_name_links = pkgs.liminix.services.oneshot rec {
|
||||
name = "mtd_by_name_links";
|
||||
up = ''
|
||||
mkdir -p /dev/mtd/by-name
|
||||
@ -174,18 +169,15 @@
|
||||
done
|
||||
'';
|
||||
};
|
||||
in
|
||||
{
|
||||
in {
|
||||
imports = [
|
||||
../../modules/arch/arm.nix
|
||||
../../modules/outputs/tftpboot.nix
|
||||
../../modules/outputs/mbrimage.nix
|
||||
../../modules/outputs/extlinux.nix
|
||||
];
|
||||
|
||||
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 {
|
||||
@ -202,7 +194,7 @@
|
||||
CPU_V7 = "y";
|
||||
ARCH_MULTIPLATFORM = "y";
|
||||
ARCH_MVEBU = "y";
|
||||
ARCH_MULTI_V7 = "y";
|
||||
ARCH_MULTI_V7= "y";
|
||||
PCI_MVEBU = "y";
|
||||
AHCI_MVEBU = "y";
|
||||
|
||||
@ -212,6 +204,7 @@
|
||||
EXPERT = "y";
|
||||
ALLOW_DEV_COREDUMP = "n";
|
||||
|
||||
|
||||
# dts has a compatible for this but dmesg is not
|
||||
# showing it
|
||||
EEPROM_AT24 = "y"; # atmel,24c64
|
||||
@ -222,44 +215,49 @@
|
||||
|
||||
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";
|
||||
PSTORE = "y";
|
||||
PSTORE_RAM = "y";
|
||||
PSTORE_CONSOLE = "y";
|
||||
# PSTORE_DEFLATE_COMPRESS = "n";
|
||||
|
||||
MMC_SDHCI = "y";
|
||||
MMC_SDHCI_PLTFM = "y";
|
||||
MMC_SDHCI_PXAV3 = "y";
|
||||
MMC_MVSDIO = "y";
|
||||
BLOCK = "y";
|
||||
MMC="y";
|
||||
PWRSEQ_EMMC="y"; # ???
|
||||
PWRSEQ_SIMPLE="y"; # ???
|
||||
MMC_BLOCK="y";
|
||||
|
||||
MMC_SDHCI= "y";
|
||||
MMC_SDHCI_PLTFM= "y";
|
||||
MMC_SDHCI_PXAV3= "y";
|
||||
MMC_MVSDIO= "y";
|
||||
|
||||
SERIAL_8250 = "y";
|
||||
SERIAL_8250_CONSOLE = "y";
|
||||
SERIAL_OF_PLATFORM = "y";
|
||||
SERIAL_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";
|
||||
@ -302,7 +300,6 @@
|
||||
};
|
||||
};
|
||||
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
|
||||
@ -313,14 +310,13 @@
|
||||
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;
|
||||
@ -328,12 +324,10 @@
|
||||
};
|
||||
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;
|
||||
};
|
||||
};
|
||||
|
||||
@ -343,81 +337,76 @@
|
||||
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 = "mtdimage";
|
||||
loadAddress = lim.parseInt "0x00800000"; # "0x00008000";
|
||||
entryPoint = lim.parseInt "0x00800000"; # "0x00008000";
|
||||
rootDevice = "/dev/mmcblk0p1";
|
||||
|
||||
dts = {
|
||||
src = "${config.system.outputs.kernel.modulesupport}/arch/arm/boot/dts/marvell/armada-385-turris-omnia.dts";
|
||||
includes = [
|
||||
"${config.system.outputs.kernel.modulesupport}/arch/arm/boot/dts/marvell/"
|
||||
];
|
||||
};
|
||||
flash.eraseBlockSize = 65536; # only used for tftpboot
|
||||
networkInterfaces =
|
||||
let
|
||||
inherit (config.system.service.network) link;
|
||||
inherit (config.system.service) bridge;
|
||||
in rec {
|
||||
en70000 = link.build {
|
||||
# in armada-38x.dtsi this is eth0.
|
||||
# It's connected to port 5 of the 88E6176 switch
|
||||
devpath = "/devices/platform/soc/soc:internal-regs/f1070000.ethernet";
|
||||
# name is unambiguous but not very semantic
|
||||
ifname = "en70000";
|
||||
};
|
||||
en30000 = link.build {
|
||||
# in armada-38x.dtsi this is eth1
|
||||
# It's connected to port 6 of the 88E6176 switch
|
||||
devpath = "/devices/platform/soc/soc:internal-regs/f1030000.ethernet";
|
||||
# name is unambiguous but not very semantic
|
||||
ifname = "en30000";
|
||||
};
|
||||
# the default (from the dts? I'm guessing) behavour for
|
||||
# lan ports on the switch is to attach them to
|
||||
# en30000. It should be possible to do something better,
|
||||
# per
|
||||
# https://www.kernel.org/doc/html/latest/networking/dsa/configuration.html#affinity-of-user-ports-to-cpu-ports
|
||||
# but apparently OpenWrt doesn't either so maybe it's more
|
||||
# complicated than it looks.
|
||||
|
||||
wan = link.build {
|
||||
# in armada-38x.dtsi this is eth2. It may be connected to
|
||||
# an ethernet phy or to the SFP cage, depending on a gpio
|
||||
devpath = "/devices/platform/soc/soc:internal-regs/f1034000.ethernet";
|
||||
ifname = "wan";
|
||||
};
|
||||
|
||||
lan0 = link.build { ifname = "lan0"; };
|
||||
lan1 = link.build { ifname = "lan1"; };
|
||||
lan2 = link.build { ifname = "lan2"; };
|
||||
lan3 = link.build { ifname = "lan3"; };
|
||||
lan4 = link.build { ifname = "lan4"; };
|
||||
lan5 = link.build { ifname = "lan5"; };
|
||||
lan = lan0; # maybe we should build a bridge?
|
||||
|
||||
wlan = link.build {
|
||||
ifname = "wlan0";
|
||||
dependencies = [ mac80211 ];
|
||||
};
|
||||
wlan5 = link.build {
|
||||
ifname = "wlan1";
|
||||
dependencies = [ mac80211 ];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
config = "mipsel-unknown-linux-musl";
|
||||
gcc = {
|
||||
abi = "32";
|
||||
arch = "mips32"; # mips32r2?
|
||||
arch = "mips32"; # mips32r2?
|
||||
};
|
||||
};
|
||||
};
|
||||
@ -101,15 +101,10 @@
|
||||
|
||||
'';
|
||||
|
||||
module =
|
||||
{
|
||||
pkgs,
|
||||
config,
|
||||
lib,
|
||||
lim,
|
||||
...
|
||||
}:
|
||||
module = { pkgs, config, lib, lim, ...}:
|
||||
let
|
||||
inherit (pkgs.liminix.networking) interface;
|
||||
inherit (pkgs.liminix.services) oneshot;
|
||||
inherit (pkgs.pseudofile) dir symlink;
|
||||
inherit (pkgs) openwrt;
|
||||
|
||||
@ -130,9 +125,8 @@
|
||||
url = "https://github.com/openwrt/mt76/raw/1b88dd07f153b202e57fe29734806744ed006b0e/firmware/mt7915_rom_patch.bin";
|
||||
hash = "sha256-ifriAjWzFACrxVWCANZpUaEZgB/0pdbhnTVQytx6ddg=";
|
||||
};
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
in {
|
||||
imports = [
|
||||
# We include it to ensure the bridge functionality
|
||||
# is available on the target kernel.
|
||||
../../modules/bridge
|
||||
@ -192,7 +186,7 @@
|
||||
# Actually, this is not what we want.
|
||||
# This DTS is insufficient.
|
||||
src = ./mt7621_zyxel_nwa50ax.dtsi;
|
||||
includePaths = [
|
||||
includes = [
|
||||
# Here's one weird trick to make `ubi` detection
|
||||
# out of the box.
|
||||
# We will write ubi on /dev/firmware_a:rootfs location
|
||||
@ -209,8 +203,7 @@
|
||||
networkInterfaces =
|
||||
let
|
||||
inherit (config.system.service.network) link;
|
||||
in
|
||||
{
|
||||
in {
|
||||
eth = link.build { ifname = "eth0"; };
|
||||
lan = link.build { ifname = "lan"; };
|
||||
wlan0 = link.build {
|
||||
@ -242,7 +235,7 @@
|
||||
services.zyxel-dual-image = config.boot.zyxel-dual-image.build {
|
||||
ensureActiveImage = "primary";
|
||||
# TODO: use mtd names rather…
|
||||
# primary and secondary are always /dev/mtd3 by virtue of the
|
||||
# primary and secondary are always /dev/mtd3 by virtue of the
|
||||
# dtb being not too wrong…
|
||||
# TODO: remove this hack.
|
||||
primaryMtdPartition = "/dev/mtd3";
|
||||
@ -259,113 +252,116 @@
|
||||
# IMAGE/ramboot-factory.bin := append-kernel | pad-to $$(KERNEL_SIZE) | append-ubi
|
||||
|
||||
kernel = {
|
||||
src = pkgs.fetchurl {
|
||||
name = "linux.tar.gz";
|
||||
url = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.137.tar.gz";
|
||||
hash = "sha256-PkdzUKZ0IpBiWe/RS70J76JKnBFzRblWcKlaIFNxnHQ=";
|
||||
};
|
||||
extraPatchPhase = ''
|
||||
${openwrt.applyPatches.ramips}
|
||||
|
||||
'';
|
||||
config =
|
||||
{
|
||||
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
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
55
doc.nix
55
doc.nix
@ -1,55 +0,0 @@
|
||||
{ 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
|
||||
'';
|
||||
}
|
39
doc/Makefile
39
doc/Makefile
@ -1,23 +1,24 @@
|
||||
DOCS=\
|
||||
admin \
|
||||
configuration \
|
||||
development \
|
||||
index \
|
||||
installation \
|
||||
intro \
|
||||
hardware \
|
||||
module-options-generated.inc \
|
||||
outputs-generated.inc \
|
||||
tutorial
|
||||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
%.adoc: %.rst
|
||||
pandoc -f rst -t asciidoc $< | sed -E -e 's/^(=*) /=\1 /g' > $@
|
||||
# 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
|
||||
|
||||
hardware.adoc: hardware.nix
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
html: Makefile $(patsubst %,%.adoc,$(DOCS))
|
||||
asciidoctor -D _build -d book index.adoc
|
||||
hardware.rst: hardware.nix
|
||||
@rm -f hardware.rst || true
|
||||
@cp $$(nix-build hardware.nix) hardware.rst
|
||||
|
||||
install:
|
||||
mkdir -p $(prefix)/share/doc/liminix
|
||||
cp -a _build/* $(prefix)/share/doc/liminix
|
||||
.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)
|
||||
|
352
doc/admin.adoc
352
doc/admin.adoc
@ -1,352 +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
|
||||
<<_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
|
||||
----
|
198
doc/admin.rst
Normal file
198
doc/admin.rst
Normal file
@ -0,0 +1,198 @@
|
||||
System Administration
|
||||
#####################
|
||||
|
||||
Services on a running system
|
||||
****************************
|
||||
|
||||
* add an s6-rc cheatsheet here
|
||||
|
||||
|
||||
Flashing and updating
|
||||
*********************
|
||||
|
||||
|
||||
|
||||
Flashing from Liminix
|
||||
=====================
|
||||
|
||||
The flash procedure from an existing Liminix-system has two steps.
|
||||
First we reboot the device (using "kexec") into an "ephemeral"
|
||||
RAM-based version of the new configuration, then when we're happy it
|
||||
works we can flash the image - and if it doesn't work we can reboot
|
||||
the device again and it will boot from the old image.
|
||||
|
||||
|
||||
Building the RAM-based image
|
||||
----------------------------
|
||||
|
||||
To create the ephemeral image, build ``outputs.kexecboot`` instead of
|
||||
``outputs.default``. This generates a directory containing the root
|
||||
filesystem image and kernel, along with an executable called `kexec`
|
||||
and a `boot.sh` script that runs it with appropriate arguments.
|
||||
|
||||
For example
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
nix-build -I liminix-config=./examples/arhcive.nix \
|
||||
--arg device "import ./devices/gl-ar750"
|
||||
-A outputs.kexecboot && \
|
||||
(tar chf - result | ssh root@the-device tar -C /run -xvf -)
|
||||
|
||||
and then login to the device and run
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
cd /run/result
|
||||
sh ./boot.sh .
|
||||
|
||||
|
||||
This will load the new kernel and map the root filesystem into a RAM
|
||||
disk, then start executing the new kernel. *This is effectively a
|
||||
reboot - be sure to close all open files and finish anything else
|
||||
you were doing first.*
|
||||
|
||||
If the new system crashes or is rebooted, then the device will revert
|
||||
to the old configuration it finds in flash.
|
||||
|
||||
|
||||
Building the second (permanent) image
|
||||
-------------------------------------
|
||||
|
||||
While running in the kexecboot system, you can build the permanent
|
||||
image and copy it to the device with :command:`ssh`
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
build-machine$ nix-build -I liminix-config=./examples/arhcive.nix \
|
||||
--arg device "import ./devices/gl-ar750"
|
||||
-A outputs.default && \
|
||||
(tar chf - result | ssh root@the-device tar -C /run -xvf -)
|
||||
|
||||
build-machine$ tar chf - result/firmware.bin | \
|
||||
ssh root@the-device tar -C /run -xvf -
|
||||
|
||||
Next you need to connect to the device and locate the "firmware"
|
||||
partition, which you can do with a combination of :command:`dmesg`
|
||||
output and the contents of :file:`/proc/mtd`
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
<5>[ 0.469841] Creating 4 MTD partitions on "spi0.0":
|
||||
<5>[ 0.474837] 0x000000000000-0x000000040000 : "u-boot"
|
||||
<5>[ 0.480796] 0x000000040000-0x000000050000 : "u-boot-env"
|
||||
<5>[ 0.487056] 0x000000050000-0x000000060000 : "art"
|
||||
<5>[ 0.492753] 0x000000060000-0x000001000000 : "firmware"
|
||||
|
||||
# cat /proc/mtd
|
||||
dev: size erasesize name
|
||||
mtd0: 00040000 00001000 "u-boot"
|
||||
mtd1: 00010000 00001000 "u-boot-env"
|
||||
mtd2: 00010000 00001000 "art"
|
||||
mtd3: 00fa0000 00001000 "firmware"
|
||||
mtd4: 002a0000 00001000 "kernel"
|
||||
mtd5: 00d00000 00001000 "rootfs"
|
||||
|
||||
Now run (in this example)
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
flashcp -v firmware.bin /dev/mtd3
|
||||
|
||||
|
||||
"I know my new image is good, can I skip the intermediate step?"
|
||||
----------------------------------------------------------------
|
||||
|
||||
In addition to giving you a chance to see if the new image works, this
|
||||
two-step process ensures that you're not copying the new image over
|
||||
the top of the active root filesystem. Sometimes it works, but you
|
||||
will at least need physical access to the device to power-cycle it
|
||||
because it will be effectively frozen afterwards.
|
||||
|
||||
|
||||
Flashing from the boot monitor
|
||||
==============================
|
||||
|
||||
If you are prepared to open the device and have a TTL serial adaptor
|
||||
of some kind to connect it to, you can probably use U-Boot and a TFTP
|
||||
server to download and flash the image. This is quite
|
||||
hardware-specific, and sometimes involves soldering: please refer
|
||||
to :ref:`serial`.
|
||||
|
||||
|
||||
Flashing from OpenWrt
|
||||
=====================
|
||||
|
||||
.. CAUTION:: Untested! A previous version of these instructions
|
||||
(without the -e flag) led to bricking the device
|
||||
when flashing a jffs2 image. If you are reading
|
||||
this message, nobody has yet reported on whether the
|
||||
new instructions are any better.
|
||||
|
||||
If your device is running OpenWrt then it probably has the
|
||||
:command:`mtd` command installed. After transferring the image onto the
|
||||
device using e.g. :command:`ssh`, you can run it as follows:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
mtd -e -r write /tmp/firmware.bin firmware
|
||||
|
||||
The options to this command are for "erase before writing" and "reboot
|
||||
after writing".
|
||||
|
||||
For more information, please see the `OpenWrt manual <https://openwrt.org/docs/guide-user/installation/sysupgrade.cli>`_ which may also contain (hardware-dependent) instructions on how to flash an image using the vendor firmware - perhaps even from a web interface.
|
||||
|
||||
Updating an installed system (JFFS2)
|
||||
************************************
|
||||
|
||||
|
||||
Adding packages
|
||||
===============
|
||||
|
||||
If your device is running a JFFS2 root filesystem, you can build
|
||||
extra packages for it on your build system and copy them to the
|
||||
device: any package in Nixpkgs or in the Liminix overlay is available
|
||||
with the ``pkgs`` prefix:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
nix-build -I liminix-config=./my-configuration.nix \
|
||||
--arg device "import ./devices/mydevice" -A pkgs.tcpdump
|
||||
|
||||
nix-shell -p min-copy-closure root@the-device result/
|
||||
|
||||
Note that this only copies the package to the device: it doesn't update
|
||||
any profile to add it to ``$PATH``
|
||||
|
||||
|
||||
Rebuilding the system
|
||||
=====================
|
||||
|
||||
:command:`liminix-rebuild` is the Liminix analogue of :command:`nixos-rebuild`, although its operation is a bit different because it expects to run on a build machine and then copy to the host device. Run it with the same ``liminix-config`` and ``device`` parameters as you would run :command:`nix-build`, and it will build any new/changed packages and then copy them to the device using SSH. For example:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
liminix-rebuild root@the-device -I liminix-config=./examples/rotuer.nix --arg device "import ./devices/gl-ar750"
|
||||
|
||||
This will
|
||||
|
||||
* build anything that needs building
|
||||
* copy new or changed packages to the device
|
||||
* reboot the device
|
||||
|
||||
It doesn't delete old packages automatically: to do that run
|
||||
:command:`min-collect-garbage`, which will delete any packages not in
|
||||
the current system closure. Note that Liminix does not have the NixOS
|
||||
concept of environments or generations, and there is no way back from
|
||||
this except for building the previous configuration again.
|
||||
|
||||
|
||||
Caveats
|
||||
-------
|
||||
|
||||
* it needs there to be enough free space on the device for all the new
|
||||
packages in addition to all the packages already on it - which may be
|
||||
a problem if a lot of things have changed (e.g. a new version of
|
||||
nixpkgs).
|
||||
|
||||
* it cannot upgrade the kernel, only userland
|
@ -1,106 +0,0 @@
|
||||
= 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.
|
@ -7,19 +7,19 @@
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
||||
|
||||
project = 'Liminix'
|
||||
copyright = '2023-2024 Daniel Barlow'
|
||||
copyright = '2023, Daniel Barlow'
|
||||
author = 'Daniel Barlow'
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
||||
|
||||
extensions = [
|
||||
# 'sphinx.ext.autosectionlabel'
|
||||
'sphinx.ext.autosectionlabel'
|
||||
]
|
||||
autosectionlabel_prefix_document = True
|
||||
|
||||
templates_path = ['_templates']
|
||||
exclude_patterns = ['*.inc.rst', '_build', 'Thumbs.db', '.DS_Store']
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
|
||||
|
||||
|
||||
|
@ -1,223 +0,0 @@
|
||||
== 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).
|
||||
|
259
doc/configuration.rst
Normal file
259
doc/configuration.rst
Normal file
@ -0,0 +1,259 @@
|
||||
Configuration
|
||||
#############
|
||||
|
||||
Liminix uses the Nix language to provide congruent configuration
|
||||
management. This means that to change anything about the way in
|
||||
which a Liminix system works, you make that change in
|
||||
your :file:`configuration.nix` (or one of the other files it references),
|
||||
and rerun :command:`nix-build` or :command:`liminix-rebuild` to action
|
||||
the change. It is not possible (at least, without shenanigans) to make
|
||||
changes by logging into the device and running imperative commands
|
||||
whose effects may later be overridden: :file:`configuration.nix`
|
||||
always describes the entire system and can be used to recreate that
|
||||
system at any time. You can usefully keep it under version control.
|
||||
|
||||
If you are familiar with NixOS, you will notice some similarities
|
||||
between NixOS and Liminix configuration, and also some
|
||||
differences. Sometimes the differences are due to the
|
||||
resource-constrained devices we deploy onto, sometimes due to
|
||||
differences in the uses these devices are put to.
|
||||
|
||||
|
||||
Configuration taxonomy
|
||||
**********************
|
||||
|
||||
There are many things you can specify in a configuration, but these
|
||||
are the ones you most commonly need to change:
|
||||
|
||||
* which services (processes) to run
|
||||
* what packages to install
|
||||
* permitted users and groups
|
||||
* Linux kernel configuration options
|
||||
* Busybox applets
|
||||
* filesystem layout
|
||||
|
||||
|
||||
Modules
|
||||
*******
|
||||
|
||||
**Modules** are a means of abstraction which allow "bundling"
|
||||
of configuration options related to a common purpose or theme. For
|
||||
example, the ``dnsmasq`` module defines a template for a dnsmasq
|
||||
service, ensures that the dnsmasq package is installed, and provides a
|
||||
dnsmasq user and group for the service to run as. The ``ppp`` module
|
||||
defines a service template and also enables various PPP-related kernel
|
||||
configuration.
|
||||
|
||||
Not all modules are included in the configuration by default, because
|
||||
that would mean that the kernel (and the Busybox binary providing
|
||||
common CLI tools) was compiled with many unnecessary bells and whistles
|
||||
and therefore be bigger than needed. (This is not purely an academic concern
|
||||
if your device has little flash storage). Therefore, specifying a
|
||||
service is usually a two-step process. For example, to add an NTP
|
||||
service you first add :file:`modules/ntp` to your ``imports`` list,
|
||||
then you create a service by calling
|
||||
:code:`config.system.service.ntp.build { .... }` with the appropriate
|
||||
service-dependent configuration parameters.
|
||||
|
||||
.. code-block:: nix
|
||||
|
||||
let svc = config.system.service;
|
||||
in {
|
||||
# ...
|
||||
imports = [
|
||||
./modules/ntp
|
||||
# ....
|
||||
];
|
||||
config.services.ntp = svc.ntp.build {
|
||||
pools = { "pool.ntp.org" = ["iburst"]; };
|
||||
makestep = { threshold = 1.0; limit = 3; };
|
||||
};
|
||||
|
||||
Merely including the module won't define the service on its own: it
|
||||
only creates the template in ``config.system.service.foo`` and you
|
||||
have to create an actual service using the template. This is an
|
||||
intentional choice to allow the creation of multiple
|
||||
differently-configured services based on the same template - perhaps
|
||||
e.g. when you have multiple networks (VPNs etc) in different trust
|
||||
domains, or you want to run two SSH daemons on different ports.
|
||||
(For the background to this, please refer to the :doc:`architecture decision record <adr/module-system>`)
|
||||
|
||||
.. tip:: Liminix modules should be quite familiar (but also different)
|
||||
if you already know how to use NixOS modules. We use the
|
||||
NixOS module infrastructure code, meaning that you should
|
||||
recognise the syntax, the type system, the rules for
|
||||
combining configuration values from different sources. We
|
||||
don't use the NixOS modules themselves, because the
|
||||
underlying system is not similar enough for them to work.
|
||||
|
||||
|
||||
Services
|
||||
********
|
||||
|
||||
We use the `s6-rc service manager <https://www.skarnet.org/software/s6-rc/overview.html>`_ to start/stop/restart services and handle
|
||||
service dependencies. Any attribute in `config.services` will become
|
||||
part of the default set of services that s6-rc will try to bring up on
|
||||
boot.
|
||||
|
||||
For the most part, for common use cases, hopefully the services you
|
||||
need will be defined by modules and you will only have to pass the
|
||||
right parameters to ``build``.
|
||||
|
||||
Should you need to create a custom service of your own devising, use
|
||||
the `oneshot` or `longrun` functions:
|
||||
|
||||
* a "longrun" service is the "normal" service concept: it has a
|
||||
``run`` action which describes the process to start, and it watches
|
||||
that process to restart it if it exits. The process should not
|
||||
attempt to daemonize or "background" itself, otherwise s6-rc will think
|
||||
it died. Whatever it prints to standard output/standard error
|
||||
will be logged.
|
||||
|
||||
.. code-block:: nix
|
||||
|
||||
config.services.cowsayd = pkgs.liminix.services.longrun {
|
||||
name = "cowsayd";
|
||||
run = "${pkgs.cowsayd}/bin/cowsayd --port 3001 --breed hereford";
|
||||
# don't start this until the lan interface is ready
|
||||
dependencies = [ config.services.lan ];
|
||||
}
|
||||
|
||||
|
||||
* a "oneshot" service doesn't have a process attached. It consists of
|
||||
``up`` and ``down`` actions which are bits of shell script that
|
||||
are run at the appropriate points in the service lifecycle
|
||||
|
||||
.. code-block:: nix
|
||||
|
||||
config.services.greenled = pkgs.liminix.services.oneshot {
|
||||
name = "greenled";
|
||||
up = ''
|
||||
echo 17 > /sys/class/gpio/export
|
||||
echo out > /sys/class/gpio/gpio17/direction
|
||||
echo 0 > /sys/class/gpio/gpio17/value
|
||||
'';
|
||||
down = ''
|
||||
echo 0 > /sys/class/gpio/gpio17/value
|
||||
'';
|
||||
}
|
||||
|
||||
Services may have dependencies: as you see above in the ``cowsayd``
|
||||
example, it depends on some service called ``config.services.lan``,
|
||||
meaning that it won't be started until that other service is up.
|
||||
|
||||
..
|
||||
TODO: explain service outputs
|
||||
|
||||
..
|
||||
TODO: outputs that change, and services that poll other services
|
||||
|
||||
Module implementation
|
||||
*********************
|
||||
|
||||
Modules in Liminix conventionally live in
|
||||
:file:`modules/somename/default.nix`. If you want or need to
|
||||
write your own, you may wish to refer to the
|
||||
examples there in conjunction with reading this section.
|
||||
|
||||
A module is a function that accepts ``{lib, pkgs, config, ... }`` and
|
||||
returns an attrset with keys ``imports, options config``.
|
||||
|
||||
* ``imports`` is a list of paths to the other modules required by this one
|
||||
|
||||
* ``options`` is a nested set of option declarations
|
||||
|
||||
* ``config`` is a nested set of option definitions
|
||||
|
||||
The NixOS manual section `Writing NixOS Modules
|
||||
<https://nixos.org/manual/nixos/stable/#sec-writing-modules>`_ is a
|
||||
quite comprehensive reference to writing NixOS modules, which is also
|
||||
mostly applicable to Liminix except that it doesn't cover
|
||||
service templates.
|
||||
|
||||
Service templates
|
||||
=================
|
||||
|
||||
To expose a service template in a module, it needs the following:
|
||||
|
||||
* an option declaration for ``system.service.myservicename`` with the
|
||||
type of ``liminix.lib.types.serviceDefn``
|
||||
|
||||
.. code-block:: nix
|
||||
|
||||
options = {
|
||||
system.service.cowsay = mkOption {
|
||||
type = liminix.lib.types.serviceDefn;
|
||||
};
|
||||
};
|
||||
|
||||
* an option definition for the same key, which specifies where to
|
||||
import the service template from (often :file:`./service.nix`)
|
||||
and the types of its parameters.
|
||||
|
||||
.. code-block:: nix
|
||||
|
||||
config.system.service.cowsay = liminix.callService ./service.nix {
|
||||
address = mkOption {
|
||||
type = types.str;
|
||||
default = "0.0.0.0";
|
||||
description = "Listen on specified address";
|
||||
example = "127.0.0.1";
|
||||
};
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 22;
|
||||
description = "Listen on specified TCP port";
|
||||
};
|
||||
breed = mkOption {
|
||||
type = types.str;
|
||||
default = "British Friesian"
|
||||
description = "Breed of the cow";
|
||||
};
|
||||
};
|
||||
|
||||
Then you need to provide the service template itself, probably in
|
||||
:file:`./service.nix`:
|
||||
|
||||
.. code-block:: nix
|
||||
|
||||
{
|
||||
# any nixpkgs package can be named here
|
||||
liminix
|
||||
, cowsayd
|
||||
, serviceFns
|
||||
, lib
|
||||
}:
|
||||
# these are the parameters declared in the callService invocation
|
||||
{ address, port, breed} :
|
||||
let
|
||||
inherit (liminix.services) longrun;
|
||||
inherit (lib.strings) escapeShellArg;
|
||||
in longrun {
|
||||
name = "cowsayd";
|
||||
run = "${cowsayd}/bin/cowsayd --address ${address} --port ${builtins.toString port} --breed ${escapeShellArg breed}";
|
||||
}
|
||||
|
||||
.. tip::
|
||||
|
||||
Not relevant to module-based services specifically, but a common
|
||||
gotcha when specifiying services is forgetting to transform "rich"
|
||||
parameter values into text when composing a command for the shell
|
||||
to execute. Note here that the port number, an integer, is
|
||||
stringified with ``toString``, and the name of the breed,
|
||||
which may contain spaces, is
|
||||
escaped with ``escapeShellArg``
|
||||
|
||||
Types
|
||||
=====
|
||||
|
||||
All of the NixOS module types are available in Liminix. These
|
||||
Liminix-specific types also exist in ``pkgs.liminix.lib.types``:
|
||||
|
||||
* ``service``: an s6-rc service
|
||||
* ``interface``: an s6-rc service which specifies a network
|
||||
interface
|
||||
* ``serviceDefn``: a service "template" definition
|
||||
|
||||
In the future it is likely that we will extend this to include other
|
||||
useful types in the networking domain: for example; IP address,
|
||||
network prefix or netmask, protocol family and others as we find them.
|
@ -1,637 +0,0 @@
|
||||
= 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]
|
||||
----
|
||||
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 (`+ppp0+`, or `+ppp1+` or `+ppp7+`) as an
|
||||
output so that a dependent service can reference it to set up a route,
|
||||
or to configure firewall rules.
|
||||
|
||||
A service `+myservice+` should write its outputs as files in
|
||||
`+/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 `+in_outputs+` shell function in the `+up+` or `+run+`
|
||||
attributes of the service:
|
||||
|
||||
[source,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 `+output+`
|
||||
to print the value of an output
|
||||
|
||||
[source,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 `+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
|
||||
`+modules/dhcp6c/acquire-wan-address.fnl+`
|
||||
|
||||
[source,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 `+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.
|
||||
|
||||
===== Design considerations for outputs
|
||||
|
||||
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 evel of an advanced shell like Bash, let alone those of a
|
||||
real programming language.
|
||||
|
||||
Note also that the Lua `+svc+` library only reads the first line of each
|
||||
output.
|
||||
|
||||
|
||||
=== Modules
|
||||
|
||||
Modules in Liminix conventionally live in
|
||||
`+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
|
||||
https://nixos.org/manual/nixos/stable/#sec-writing-modules[Writing NixOS
|
||||
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
|
||||
|
||||
Although you can define services "ad hoc" using `longrun` or `oneshot`
|
||||
<<_writing_services,as above>>, this approach has limitations if
|
||||
you're writing code intended for wider use. Services in the
|
||||
modules bundled with Liminix are implemented following a pattern we
|
||||
call "service templates": functions that accept a _type-checked_
|
||||
attrset and return an appropriately configured service that can be
|
||||
assigned by the caller to a key in ``config.services``.
|
||||
|
||||
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+`
|
||||
|
||||
[source,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 `+./service.nix+`) and the types of its
|
||||
parameters.
|
||||
|
||||
[source,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
|
||||
`+./service.nix+`:
|
||||
|
||||
[source,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.
|
||||
|
||||
|
||||
=== Emulated devices
|
||||
|
||||
Unless your changes depend on particular hardware devices, you may
|
||||
want to test your new/changed module with one of the emulated
|
||||
"devices" which runn on your build machine using the free
|
||||
http://www.qemu.org[QEMU machine emulator]. They are
|
||||
|
||||
* `qemu`(MIPS)
|
||||
* `qemu-armv7l`(32 bit ARM)
|
||||
* `qemu-aarch64` (64 bit ARM)
|
||||
|
||||
This means you don't need to keep flashing or messing with U-Boot: it
|
||||
also enables testing against emulated network peers using
|
||||
https://wiki.qemu.org/Documentation/Networking#Socket[QEMU socket
|
||||
networking], which may be preferable to letting Liminix loose on your
|
||||
actual LAN. To build,
|
||||
|
||||
[source,console]
|
||||
----
|
||||
nix-build -I liminix-config=path/to/your/configuration.nix --arg device "import ./devices/qemu" -A outputs.default
|
||||
----
|
||||
|
||||
This creates a `+result/+` directory containing a `+vmlinux+` and a
|
||||
`+rootfs+`, and a shell script `+run.sh+` which invokes QEMU to run
|
||||
that kernel with that filesystem. It connects the Liminix serial console
|
||||
and the https://www.qemu.org/docs/master/system/monitor.html[QEMU
|
||||
monitor] to stdin/stdout. Use `^P` (not `^A`) to switch to the monitor.
|
||||
|
||||
// FIXME should add a `connect.sh` script instead of requiring nix-shell here
|
||||
|
||||
If you run with `+--background /path/to/some/directory+` as the first
|
||||
parameter, it will fork into the background and open Unix sockets in
|
||||
that directory for console and monitor. Use `+nix-shell --run
|
||||
connect-vm+` to connect to either of these sockets, and ^O to
|
||||
disconnect.
|
||||
|
||||
[[qemu-networking]]
|
||||
===== Networking
|
||||
|
||||
VMs can network with each other using QEMU socket networking. We observe
|
||||
these conventions, so that we can run multiple emulated instances and
|
||||
have them wired up to each other in the right way:
|
||||
|
||||
* multicast 230.0.0.1:1234 : access (interconnect between router and
|
||||
"isp")
|
||||
* multicast 230.0.0.1:1235 : lan
|
||||
* multicast 230.0.0.1:1236 : world (the internet)
|
||||
|
||||
Any VM started by a `+run.sh+` script is connected to "lan" and
|
||||
"access". The emulated upstream (see below) runs PPPoE and is
|
||||
connected to "access" and "world".
|
||||
|
||||
===== Upstream connection
|
||||
|
||||
In pkgs/routeros there is a derivation to install and configure
|
||||
https://mikrotik.com/software[Mikrotik RouterOS] as a PPPoE access
|
||||
concentrator connected to the `+access+` and `+world+` networks, so that
|
||||
Liminix PPPoE client support can be tested without actual hardware.
|
||||
|
||||
This is made available as the `+routeros+` command in `+buildEnv+`, so
|
||||
you can do something like:
|
||||
|
||||
....
|
||||
mkdir ros-sockets
|
||||
nix-shell
|
||||
nix-shell$ routeros ros-sockets
|
||||
nix-shell$ connect-vm ./ros-sockets/console
|
||||
....
|
||||
|
||||
to start it and connect to it. Note that by default it runs in the
|
||||
background. It is connected to "access" and "world" virtual networks and
|
||||
runs a PPPoE service on "access" - so a Liminix VM with a PPPOE client
|
||||
can connect to it and thus reach the virtual internet. [ check, but
|
||||
pretty sure this is not the actual internet ]
|
||||
|
||||
[.title-ref]#Liminix does not provide RouterOS licences and it is your
|
||||
own responsibility if you use this to ensure you're compliant with the
|
||||
terms of Mikrotik's licencing. It may be supplemented or replaced in
|
||||
time with configurations for RP-PPPoE and/or Accel PPP.#
|
||||
|
||||
== Hardware hacking/porting to new device
|
||||
|
||||
The steps to port to a new hardware device are largely undocumented at
|
||||
present (although this hasn't stopped people from figuring it out
|
||||
already). As an outline I would recommend
|
||||
|
||||
* choose hardware that OpenWrt already supports, otherwise you will
|
||||
probably spend a lot of time writing kernel code. The OpenWrt kernel
|
||||
supports many network interfaces and other hardware for a lot of
|
||||
hardware boards that might only just about be able to boot Linux on a
|
||||
serial port if you stick to mainline Linux
|
||||
|
||||
* work out how to get a serial console on it. You are unlikely to get
|
||||
working networking on your first go at boulding a kernel
|
||||
|
||||
* find the most similar device in Liminiux and copy
|
||||
`devices/existing-similar-device` to `devices/cool-new-device` as a
|
||||
starting point
|
||||
|
||||
* use the kernel configuration (`/proc/config.gz`) from OpenWrt as a
|
||||
reference for the kernel config you'll need to specify
|
||||
in `devices/cool-new-device/default.nix`
|
||||
|
||||
* break it down into achieveable goals. Your first goal should be
|
||||
something that can TFTP boot the kernel as far as a running
|
||||
userland. Networking is harder, Wifi often much harder - it
|
||||
sometimes also depends on having working flash _even if_ you're TFTP
|
||||
booting because the driver expects to load wifi firmware or
|
||||
calibration data from the flash
|
||||
|
||||
* ask on IRC!
|
||||
|
||||
=== TFTP
|
||||
|
||||
[[tftpserver]]
|
||||
How you get your image onto hardware will vary according to the device,
|
||||
but is likely to involve taking it apart to add wires to serial console
|
||||
pads/headers, then using U-Boot to fetch images over TFTP. The OpenWrt
|
||||
documentation has a
|
||||
https://openwrt.org/docs/techref/hardware/port.serial[good explanation]
|
||||
of what you may expect to find on the device.
|
||||
|
||||
[[tufted]]
|
||||
`tufted` is a rudimentary TFTP server which runs from the command
|
||||
line, has an allowlist for client connections, and follows symlinks,
|
||||
so you can have your device download images direct from the
|
||||
`+./result+` directory without exposing `+/nix/store/+` to the
|
||||
internet or mucking about copying files to `+/tftproot+`. If the
|
||||
permitted device is to be given the IP address 192.168.8.251 you might
|
||||
do something like this:
|
||||
|
||||
[source,console]
|
||||
----
|
||||
nix-shell --run "tufted -a 192.168.8.251 result"
|
||||
----
|
||||
|
||||
Now add the device and server IP addresses to your configuration:
|
||||
|
||||
[source,nix]
|
||||
----
|
||||
boot.tftp = {
|
||||
serverip = "192.168.8.111";
|
||||
ipaddr = "192.168.8.251";
|
||||
};
|
||||
----
|
||||
|
||||
and then build the derivation for `+outputs.default+` or
|
||||
`+outputs.mtdimage+` (for which it will be an alias on any device where
|
||||
this is applicable). You should find it has created
|
||||
|
||||
* `+result/firmware.bin+` which is the file you are going to flash
|
||||
* `+result/flash.scr+` which is a set of instructions to U-Boot to
|
||||
download the image and write it to flash after erasing the appropriate
|
||||
flash partition.
|
||||
|
||||
NOTE: TTL serial connections typically have no form of flow control and so
|
||||
don't always like having massive chunks of text pasted into them - and
|
||||
U-Boot may drop characters while it's busy. So don't necessarily expect
|
||||
to copy-paste the whole of `+boot.scr+` into a terminal emulator and
|
||||
have it work just like that. You may need to paste each line one at a
|
||||
time, or even retype it.
|
||||
|
||||
=== Running from RAM
|
||||
|
||||
For a faster edit-compile-test cycle, you can build a TFTP-bootable
|
||||
image which boots directly from RAM (using phram) instead of needing
|
||||
to be flashed first. In your device configuration add
|
||||
|
||||
[source,nix]
|
||||
----
|
||||
imports = [
|
||||
./modules/tftpboot.nix
|
||||
];
|
||||
----
|
||||
|
||||
and then build `+outputs.tftpboot+`. This creates a file `+result/boot.scr+`, which you can copy and paste into U-Boot to
|
||||
transfer the kernel and filesystem over TFTP and boot the kernel from
|
||||
RAM.
|
||||
|
||||
[[bng]]
|
||||
=== Networking
|
||||
|
||||
You probably don't want to be testing a device that might serve DHCP,
|
||||
DNS and routing protocols on the same LAN as you (or your colleagues,
|
||||
employees, or family) are using for anything else, because it will
|
||||
interfere. You also might want to test the device against an "upstream"
|
||||
connection without having to unplug your regular home router from the
|
||||
internet so you can borrow the cable/fibre/DSL.
|
||||
|
||||
`+bordervm+` is included for this purpose. You will need
|
||||
|
||||
* a Linux machine with a spare (PCI or USB) ethernet device which you
|
||||
can dedicate to Liminix
|
||||
* an L2TP service such as https://www.aa.net.uk/broadband/l2tp-service/
|
||||
|
||||
You need to "hide" the Ethernet device from the host so that QEMU has
|
||||
exclusive use of it. For PCI this means configuring it for VFIO
|
||||
passthru; for USB you need to unload the module(s) it uses. I have
|
||||
this segment in my build machine's `configuration.nix` which you may
|
||||
be able to adapt:
|
||||
|
||||
[source,nix]
|
||||
----
|
||||
boot = {
|
||||
kernelParams = [ "intel_iommu=on" ];
|
||||
kernelModules = [
|
||||
"kvm-intel" "vfio_virqfd" "vfio_pci" "vfio_iommu_type1" "vfio"
|
||||
];
|
||||
|
||||
postBootCommands = ''
|
||||
# modprobe -i vfio-pci
|
||||
# echo vfio-pci > /sys/bus/pci/devices/0000:01:00.0/driver_override
|
||||
'';
|
||||
blacklistedKernelModules = [
|
||||
"r8153_ecm" "cdc_ether"
|
||||
];
|
||||
};
|
||||
services.udev.extraRules = ''
|
||||
SUBSYSTEM=="usb", ATTRS{idVendor}=="0bda", ATTRS{idProduct}=="8153", OWNER="dan"
|
||||
'';
|
||||
----
|
||||
|
||||
Then you can execute `+run-border-vm+` in a `+buildEnv+` shell, which
|
||||
starts up QEMU using the NixOS configuration in
|
||||
`+bordervm-configuration.nix+`.
|
||||
|
||||
Inside the VM
|
||||
|
||||
* your Liminix checkout is mounted under `+/home/liminix/liminix+`
|
||||
* TFTP is listening on the ethernet device and serving
|
||||
`+/home/liminix/liminix+`. The server IP address is 10.0.0.1
|
||||
* a PPPOE-L2TP relay is running on the same ethernet card. When the
|
||||
connected Liminix device makes PPPoE requests, the relay spawns L2TPv2
|
||||
Access Concentrator sessions to your specified L2TP LNS. Note that
|
||||
authentication is expected at the PPP layer not the L2TP layer, so the
|
||||
PAP/CHAP credentials provided by your L2TP service can be configured
|
||||
into your test device - bordervm doesn't need to know about them.
|
||||
|
||||
To configure bordervm, you need a file called `+bordervm.conf.nix+`
|
||||
which you can create by copying and appropriately editing
|
||||
`+bordervm.conf-example.nix+`
|
||||
|
||||
NOTE: If you make changes to the bordervm configuration after executing
|
||||
`+run-border-vm+`, you need to remove the `+border.qcow2+` disk image
|
||||
file otherwise the changes won't get picked up.
|
||||
|
||||
== Contributing
|
||||
|
||||
Patches welcome! Also bug reports, documentation improvements,
|
||||
experience reports/case studies etc etc all equally as welcome.
|
||||
|
||||
* if you have an obvious bug fix, new package, documentation
|
||||
improvement or other uncontroversial small patch, send it straight
|
||||
in.
|
||||
|
||||
* if you have a large new feature or design change in mind, please
|
||||
please _get in touch_ to talk about it before you commit time to
|
||||
implementing it. Perhaps it isn't what we were expecting, almost
|
||||
certainly we will have ideas or advice on what it should do or how
|
||||
it should be done.
|
||||
|
||||
Liminix development is not tied to Github or any other particular
|
||||
forge. How to send changes:
|
||||
|
||||
1. Push your Liminix repo with your changes to a git repository
|
||||
somewhere on the Internet that I can clone from. It can be on Codeberg
|
||||
or Gitlab or Sourcehut or Forgejo or Gitea or Github or a bare repo in
|
||||
your own personal web space or any kind of hosting you like.
|
||||
|
||||
2. Email devel@liminix.org with the URL of the repo and the branch
|
||||
name, and we will take a look.
|
||||
|
||||
If that's not an option, I’m also happy for you to send your changes
|
||||
direct to the list itself, as an incremental git bundle or using git
|
||||
format-patch. We'll work it out somehow.
|
||||
|
||||
The main development repo for Liminix is hosted at
|
||||
<https://gti.telent.net/dan/liminix>, with a read-only mirror at
|
||||
<https://github.com/telent/liminix>. If you're happy to use Github
|
||||
then you can fork from the latter to make your changes, but please use
|
||||
the mailing list one of the approved routes to tell me about your changes because I don't regularly go there to check PRs.
|
||||
|
||||
Remember that the <<_code_of_conduct>> applies to all Liminix spaces,
|
||||
and anyone who violates it may be sanctioned or expelled from these
|
||||
spaces at the discretion of the project leadership.
|
||||
|
||||
=== Nix language style
|
||||
|
||||
This section describes some Nix language style points that we attempt to
|
||||
adhere to in this repo. Some are more aspirational than actual.
|
||||
|
||||
* indentation and style is according to `nixfmt-rfc-style`
|
||||
* favour `+callPackage+` over raw `+import+` for calling derivations or
|
||||
any function that may generate one - any code that might need `+pkgs+`
|
||||
or parts of it.
|
||||
* prefer `+let inherit (quark) up down strange charm+` over
|
||||
`+with quark+`, in any context where the scope is more than a single
|
||||
expression or there is more than one reference to `+up+`, `+down+` etc.
|
||||
`+with pkgs; [ foo bar baz]+` is OK,
|
||||
`+with lib; stdenv.mkDerivation { ... }+` is usually not.
|
||||
* `+<liminix>+` is defined only when running tests, so don't refer to it
|
||||
in "application" code
|
||||
* the parameters to a derivation are sorted alphabetically, except for
|
||||
`+lib+`, `+stdenv+` and maybe other non-package "special cases"
|
||||
* where a `+let+` form defines multiple names, put a newline after the
|
||||
token `+let+`, and indent each name two characters
|
||||
* to decide whether some code should be a package or a module? Packages
|
||||
are self-contained - they live in `+/nix/store/eeeeeee-name+` and don't
|
||||
directly change system behaviour by their presence or absense. modules
|
||||
can add to `+/etc+` or `+/bin+` or other global state, create services,
|
||||
all that side-effecty stuff. Generally it should be a package unless it
|
||||
can't be.
|
||||
|
||||
=== Copyright
|
||||
|
||||
The Nix code in Liminix is MIT-licenced (same as Nixpkgs), but the code
|
||||
it combines from other places (e.g. Linux, OpenWrt) may have a variety
|
||||
of licences. Copyright assignment is not expected:
|
||||
just like when submitting to the Linux kernel you retain the copyright
|
||||
on the code you contribute.
|
||||
|
||||
=== Automated builds
|
||||
|
||||
Automated builds are run on each push to the main branch. This tests
|
||||
that (among other things)
|
||||
|
||||
* every device image builds
|
||||
* the build for the “qemu” target is executed with a fake network upstream to test
|
||||
* PPPoE and DHCP service
|
||||
* hostap (wireless gateway)
|
||||
|
||||
You can view the build output at https://build.liminix.org . The tests
|
||||
are defined in ci.nix.
|
||||
|
||||
Unfortunately there's no (easy) way I can make _my_ CI infrastructure
|
||||
run _your_ code, other than merging it. But see <<_running_tests>>
|
||||
for how to exercise the same code locally on your machine.
|
||||
|
||||
|
||||
== Running tests
|
||||
|
||||
You can run all of the tests by evaluating `+ci.nix+`, which is the
|
||||
input I use in Hydra.
|
||||
|
||||
[source,console]
|
||||
----
|
||||
nix-build -I liminix=`pwd` ci.nix -A pppoe # run one job
|
||||
nix-build -I liminix=`pwd` ci.nix -A all # run all jobs
|
||||
----
|
||||
|
||||
== Troubleshooting
|
||||
|
||||
=== Diagnosing unexpectedly large images
|
||||
|
||||
Sometimes you can add a package and it causes the image size to balloon
|
||||
because it has dependencies on other things you didn't know about. Build
|
||||
the `+outputs.manifest+` attribute, which is a JSON representation of
|
||||
the filesystem, and you can run `+nix-store --query+` on it.
|
||||
|
||||
[source,console]
|
||||
----
|
||||
nix-build -I liminix-config=path/to/your/configuration.nix \
|
||||
--arg device "import ./devices/qemu" -A outputs.manifest \
|
||||
-o manifest
|
||||
nix-store -q --tree manifest
|
||||
----
|
403
doc/development.rst
Normal file
403
doc/development.rst
Normal file
@ -0,0 +1,403 @@
|
||||
Development
|
||||
###########
|
||||
|
||||
As a developer working on Liminix, or implementing a service or
|
||||
module, you probably want to test your changes more conveniently
|
||||
than by building and flashing a new image every time. This section
|
||||
documents various affordances for iteration and experiments.
|
||||
|
||||
In general, packages and tools that run on the "build" machine are
|
||||
available in the ``buildEnv`` derivation and can most easily
|
||||
be added to your environment by running :command:`nix-shell`.
|
||||
|
||||
|
||||
|
||||
Emulated devices
|
||||
****************
|
||||
|
||||
Liminix has a ``qemu`` device, which generates images suitable for
|
||||
running on your build machine using the free `QEMU machine emulator <http://www.qemu.org>`_.
|
||||
This is useful for developing userland without needing to keep
|
||||
flashing or messing with U-Boot: it also enables testing against
|
||||
emulated network peers using `QEMU socket networking <https://wiki.qemu.org/Documentation/Networking#Socket>`_,
|
||||
which may be preferable to letting Liminix loose on your actual LAN.
|
||||
To build it,
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
nix-build -I liminix-config=path/to/your/configuration.nix --arg device "import ./devices/qemu" -A outputs.default
|
||||
|
||||
This creates a :file:`result/` directory containing a :file:`vmlinux`
|
||||
and a :file:`rootfs`, and also a shell script :file:`run.sh` which
|
||||
invokes QEMU to run that kernel with that filesystem. It connects the Liminix
|
||||
serial console and the `QEMU monitor <https://www.qemu.org/docs/master/system/monitor.html>`_ to stdin/stdout. Use ^P (not ^A) to switch to the monitor.
|
||||
|
||||
If you run with ``--background /path/to/some/directory`` as the first
|
||||
parameter, it will fork into the background and open Unix sockets in
|
||||
that directory for console and monitor. Use :command:`nix-shell --run
|
||||
connect-vm` to connect to either of these sockets, and ^O to
|
||||
disconnect.
|
||||
|
||||
.. _qemu-networking:
|
||||
|
||||
Networking
|
||||
==========
|
||||
|
||||
VMs can network with each other using QEMU
|
||||
socket networking. We observe these conventions, so that we can run
|
||||
multiple emulated instances and have them wired up to each other in
|
||||
the right way:
|
||||
|
||||
* multicast 230.0.0.1:1234 : access (interconnect between router and "isp")
|
||||
* multicast 230.0.0.1:1235 : lan
|
||||
* multicast 230.0.0.1:1236 : world (the internet)
|
||||
|
||||
Any VM started by a :command:`run.sh` script is connected to "lan" and
|
||||
"access", and the emulated border network gateway (see below) runs
|
||||
PPPoE and is connected to "access" and "world".
|
||||
|
||||
.. _border-network-gateway:
|
||||
|
||||
Border Network Gateway
|
||||
----------------------
|
||||
|
||||
In pkgs/routeros there is a derivation to install and configure
|
||||
`Mikrotik RouterOS <https://mikrotik.com/software>`_ as a PPPoE access
|
||||
concentrator connected to the ``access`` and ``world`` networks, so that
|
||||
Liminix PPPoE client support can be tested without actual hardware.
|
||||
|
||||
This is made available as the :command:`routeros` command in
|
||||
``buildEnv``, so you can do something like::
|
||||
|
||||
mkdir ros-sockets
|
||||
nix-shell
|
||||
nix-shell$ routeros ros-sockets
|
||||
nix-shell$ connect-vm ./ros-sockets/console
|
||||
|
||||
to start it and connect to it. Note that by default it runs in the
|
||||
background. It is connected to "access" and "world" virtual networks
|
||||
and runs a PPPoE service on "access" - so a Liminix VM with a
|
||||
PPPOE client can connect to it and thus reach the virtual internet.
|
||||
[ check, but pretty sure this is not the actual internet ]
|
||||
|
||||
`Liminix does not provide RouterOS licences and it is your own
|
||||
responsibility if you use this to ensure you're compliant with the
|
||||
terms of Mikrotik's licencing. It may be supplemented or replaced in
|
||||
time with configurations for RP-PPPoE and/or Accel PPP.`
|
||||
|
||||
Hardware devices
|
||||
****************
|
||||
|
||||
.. _serial:
|
||||
|
||||
U-Boot and serial shenanigans
|
||||
=============================
|
||||
|
||||
Every device that we have so far encountered in Liminix uses `U-Boot,
|
||||
the "Universal Boot Loader" <https://docs.u-boot.org/en/latest/>`_ so
|
||||
it's worth knowing a bit about it. "Universal" is in this context a
|
||||
bit of a misnomer, though: encountering *mainline* U-Boot is very rare
|
||||
and often you'll find it is a fork from some version last updated
|
||||
in 2008. Upgrading U-Boot is more or less complicated depending on the
|
||||
device and is outside scope for Liminix.
|
||||
|
||||
To speak to U-Boot on your device you'll usually need a serial
|
||||
connection to it. This is device-specific. Usually it involves
|
||||
opening the box, locating the serial header pins (TX, RX and GND) and
|
||||
connecting a USB TTL converter to them.
|
||||
|
||||
The Rolls Royce of USB/UART cables is the `FTDI cable
|
||||
<https://cpc.farnell.com/ftdi/ttl-232r-rpi/cable-debug-ttl-232-usb-rpi/dp/SC12825?st=usb%20to%20uart%20cable>`_,
|
||||
but there are cheaper alternatives based on the PL2303 and CP2102 chipsets. Or
|
||||
get creative and use the `UART GPIO pins <https://pinout.xyz/>`_ on a Raspberry Pi. Whatever you do, make sure
|
||||
that the voltages are compatible: if your device is 3.3V (this is
|
||||
typical but not universal), you don't want to be sending it 5v or
|
||||
(even worse) 12v.
|
||||
|
||||
Run a terminal emulator such as Minicom on the computer at other end
|
||||
of the link. 115200 8N1 is the typical speed.
|
||||
|
||||
.. NOTE::
|
||||
|
||||
TTL serial connections typically have no form of flow control and
|
||||
so don't always like having massive chunks of text pasted into
|
||||
them - and U-Boot may drop characters while it's busy. So don't
|
||||
necessarily expect to copy-paste large chunks of text into the
|
||||
terminal emulator and have it work just like that.
|
||||
|
||||
If using Minicom, you may find it helps to bring up the "Termimal
|
||||
settings" dialog (C^A T), then configure "Newline tx delay" to
|
||||
some small but non-zero value.
|
||||
|
||||
When you turn the router on you should be greeted with some messages
|
||||
from U-Boot, followed by the instruction to hit some key to stop
|
||||
autoboot. Do this and you will get to the prompt. If you didn't see
|
||||
anything, the strong likelihood is that TX and RX are the wrong way
|
||||
around. If you see garbage, try a different speed.
|
||||
|
||||
Interesting commands to try first in U-Boot are :command:`help` and
|
||||
:command:`printenv`.
|
||||
|
||||
To do anything useful with U-Boot you will probably need a way to get
|
||||
large binary files onto the device, and the usual way to do this is by
|
||||
adding a network connection and using TFTP to download them. It's
|
||||
quite common that the device's U-Boot doesn't speak DHCP so it will
|
||||
need a static LAN address. You might also want to keep it away from
|
||||
your "real" LAN: see :ref:`bng` for some potentially useful tooling
|
||||
to use it on an isolated network.
|
||||
|
||||
|
||||
TFTP
|
||||
====
|
||||
|
||||
.. _tftp server:
|
||||
|
||||
How you get your image onto hardware will vary according to the
|
||||
device, but is likely to involve taking it apart to add wires to
|
||||
serial console pads/headers, then using U-Boot to fetch images over
|
||||
TFTP. The OpenWrt documentation has a `good explanation <https://openwrt.org/docs/techref/hardware/port.serial>`_ of what you may expect to find on
|
||||
the device.
|
||||
|
||||
There is a rudimentary TFTP server bundled with the system which runs
|
||||
from the command line, has an allowlist for client connections, and
|
||||
follows symlinks, so you can have your device download images direct
|
||||
from the :file:`./result` directory without exposing :file:`/nix/store/` to the
|
||||
internet or mucking about copying files to :file:`/tftproot`. If the
|
||||
permitted device is to be given the IP address 192.168.8.251 you might
|
||||
do something like this:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
nix-shell --run "tufted -a 192.168.8.251 result"
|
||||
|
||||
Now add the device and server IP addresses to your configuration:
|
||||
|
||||
.. code-block:: nix
|
||||
|
||||
boot.tftp = {
|
||||
serverip = "192.168.8.111";
|
||||
ipaddr = "192.168.8.251";
|
||||
};
|
||||
|
||||
and then build the derivation for ``outputs.default`` or
|
||||
``outputs.mtdimage`` (for which it will be an alias on any device
|
||||
where this is applicable). You should find it has created
|
||||
|
||||
* :file:`result/firmware.bin` which is the file you are going to flash
|
||||
* :file:`result/flash.scr` which is a set of instructions to U-Boot to
|
||||
download the image and write it to flash after erasing the appropriate
|
||||
flash partition.
|
||||
|
||||
.. NOTE::
|
||||
|
||||
TTL serial connections typically have no form of flow control and
|
||||
so don't always like having massive chunks of text pasted into
|
||||
them - and U-Boot may drop characters while it's busy. So don't
|
||||
necessarily expect to copy-paste the whole of :file:`boot.scr` into
|
||||
a terminal emulator and have it work just like that. You may need
|
||||
to paste each line one at a time, or even retype it.
|
||||
|
||||
|
||||
For a faster edit-compile-test cycle, you can build a TFTP-bootable
|
||||
image instead of flashing. In your device configuration add
|
||||
|
||||
.. code-block:: nix
|
||||
|
||||
imports = [
|
||||
./modules/tftpboot.nix
|
||||
];
|
||||
|
||||
and then build ``outputs.tftpboot``. This creates a file in
|
||||
``result/`` called ``boot.scr``, which you can copy and paste into
|
||||
U-Boot to transfer the kernel and filesystem over TFTP and boot the
|
||||
kernel from RAM.
|
||||
|
||||
|
||||
.. _bng:
|
||||
|
||||
Networking
|
||||
==========
|
||||
|
||||
You probably don't want to be testing a device that might serve DHCP,
|
||||
DNS and routing protocols on the same LAN as you (or your colleagues,
|
||||
employees, or family) are using for anything else, because it will
|
||||
interfere. You also might want to test the device against an
|
||||
"upstream" connection without having to unplug your regular home
|
||||
router from the internet so you can borrow the cable/fibre/DSL.
|
||||
|
||||
``bordervm`` is included for this purpose. You will need
|
||||
|
||||
* a Linux machine with a spare (PCI or USB) ethernet device which you can dedicate to Liminix
|
||||
|
||||
* an L2TP service such as https://www.aa.net.uk/broadband/l2tp-service/
|
||||
|
||||
You need to "hide" the Ethernet device from the host - for PCI this
|
||||
means configuring it for VFIO passthru; for USB you need to unload the
|
||||
module(s) it uses. I have this segment in configuration.nix which you
|
||||
may be able to adapt:
|
||||
|
||||
.. code-block:: nix
|
||||
|
||||
boot = {
|
||||
kernelParams = [ "intel_iommu=on" ];
|
||||
kernelModules = [
|
||||
"kvm-intel" "vfio_virqfd" "vfio_pci" "vfio_iommu_type1" "vfio"
|
||||
];
|
||||
|
||||
postBootCommands = ''
|
||||
# modprobe -i vfio-pci
|
||||
# echo vfio-pci > /sys/bus/pci/devices/0000:01:00.0/driver_override
|
||||
'';
|
||||
blacklistedKernelModules = [
|
||||
"r8153_ecm" "cdc_ether"
|
||||
];
|
||||
};
|
||||
services.udev.extraRules = ''
|
||||
SUBSYSTEM=="usb", ATTRS{idVendor}=="0bda", ATTRS{idProduct}=="8153", OWNER="dan"
|
||||
'';
|
||||
|
||||
Then
|
||||
you can execute :command:`run-border-vm` in a ``buildEnv`` shell,
|
||||
which starts up QEMU using the NixOS configuration in
|
||||
:file:`bordervm-configuration.nix`.
|
||||
|
||||
In this VM
|
||||
|
||||
* your Liminix checkout is mounted under :file:`/home/liminix/liminix`
|
||||
|
||||
* TFTP is listening on the ethernet device and serving
|
||||
:file:`/home/liminix/liminix`. The server IP address is 10.0.0.1
|
||||
|
||||
* a PPPOE-L2TP relay is running on the same ethernet card. When the
|
||||
connected Liminix device makes PPPoE requests, the relay spawns
|
||||
L2TPv2 Access Concentrator sessions to your specified L2TP LNS.
|
||||
Note that authentication is expected at the PPP layer not the L2TP
|
||||
layer, so the PAP/CHAP credentials provided by your L2TP service can
|
||||
be configured into your test device - bordervm doesn't need to know
|
||||
about them.
|
||||
|
||||
To configure bordervm, you need a file called :file:`bordervm.conf.nix`
|
||||
which you can create by copying and appropriately editing :file:`bordervm.conf-example.nix`
|
||||
|
||||
.. note::
|
||||
|
||||
If you make changes to the bordervm configuration after executing
|
||||
:command:`run-border-vm`, you need to remove the :file:`border.qcow2` disk
|
||||
image file otherwise the changes won't get picked up.
|
||||
|
||||
|
||||
Running tests
|
||||
*************
|
||||
|
||||
You can run all of the tests by evaluating :file:`ci.nix`, which is the
|
||||
input I use in Hydra. Note that it expects Nixpkgs stable `and` unstable
|
||||
as inputs, because it builds the qemu device against both.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
nix-build --argstr liminix `pwd` --arg nixpkgs "<nixpkgs>" \
|
||||
--argstr unstable `pwd`/../unstable-nixpkgs/ ci.nix
|
||||
|
||||
To run a single named test, use the ``-A`` flag. For example, ``-A pppoe``
|
||||
|
||||
|
||||
|
||||
|
||||
Troubleshooting
|
||||
***************
|
||||
|
||||
Diagnosing unexpectedly large images
|
||||
====================================
|
||||
|
||||
Sometimes you can add a package and it causes the image size to balloon
|
||||
because it has dependencies on other things you didn't know about. Build the
|
||||
``outputs.manifest`` attribute, which is a JSON representation of the
|
||||
filesystem, and you can run :command:`nix-store --query` on it.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
nix-build -I liminix-config=path/to/your/configuration.nix \
|
||||
--arg device "import ./devices/qemu" -A outputs.manifest \
|
||||
-o manifest
|
||||
nix-store -q --tree manifest
|
||||
|
||||
|
||||
Contributing
|
||||
************
|
||||
|
||||
Contributions are welcome, though in these early days there may be a
|
||||
bit of back and forth involved before patches are merged:
|
||||
Please get in touch somehow `before` you invest a lot of time into a
|
||||
code contribution I haven't asked for. Just so I know it's expected
|
||||
and you're not wasting time doing something I won't accept or have
|
||||
already started on.
|
||||
|
||||
|
||||
Nix language style
|
||||
==================
|
||||
|
||||
This section describes some Nix language style points that we
|
||||
attempt to adhere to in this repo.
|
||||
|
||||
* favour ``callPackage`` over raw ``import`` for calling derivations
|
||||
or any function that may generate one - any code that might need
|
||||
``pkgs`` or parts of it.
|
||||
|
||||
* prefer ``let inherit (quark) up down strange charm`` over
|
||||
``with quark``, in any context where the scope is more than a single
|
||||
expression or there is more than one reference to ``up``, ``down``
|
||||
etc. ``with pkgs; [ foo bar baz]`` is OK,
|
||||
``with lib; stdenv.mkDerivation { ... }`` is usually not.
|
||||
|
||||
* ``<liminix>`` is defined only when running tests, so don't refer to it
|
||||
in "application" code
|
||||
|
||||
* the parameters to a derivation are sorted alphabetically, except for
|
||||
``lib``, ``stdenv`` and maybe other non-package "special cases"
|
||||
|
||||
* indentation is whatever emacs nix-mode says it is.
|
||||
|
||||
* where a ``let`` form defines multiple names, put a newline after the
|
||||
token ``let``, and indent each name two characters
|
||||
|
||||
* to decide whether some code should be a package or a module?
|
||||
Packages are self-contained - they live in ``/nix/store/eeeeeee-name``
|
||||
and don't directly change system behaviour by their presence or
|
||||
absense. modules can add to
|
||||
``/etc`` or ``/bin`` or other global state, create services, all that
|
||||
side-effecty stuff. Generally it should be a package unless it
|
||||
can't be.
|
||||
|
||||
|
||||
|
||||
Copyright
|
||||
=========
|
||||
|
||||
The Nix code in Liminix is MIT-licenced (same as Nixpkgs), but the
|
||||
code it combines from other places (e.g. Linux, OpenWrt) may have a
|
||||
variety of licences. I have no intention of asking for copyright
|
||||
assignment: just like when submitting to the Linux kernel you retain
|
||||
the copyright on the code you contribute.
|
||||
|
||||
|
||||
Code of Conduct
|
||||
===============
|
||||
|
||||
Please govern yourself in Liminix project venues according to the
|
||||
`Code of Conduct <https://gti.telent.net/dan/liminix/src/commit/7bcf6b15c3fdddafeda13f65b3cd4a422dc52cd3/CODE-OF-CONDUCT.md>`_
|
||||
|
||||
|
||||
Where to send patches
|
||||
=====================
|
||||
|
||||
|
||||
Liminix' primary repo is https://gti.telent.net/dan/liminix but you
|
||||
can't send code there directly because it doesn't have open registrations.
|
||||
|
||||
* There's a `mirror on Github <https://github.com/telent/liminix>`_ for
|
||||
convenience and visibility: you can open PRs against that
|
||||
|
||||
* or, you can send me your patch by email using `git send-email <https://git-send-email.io/>`_
|
||||
|
||||
* or in the future, some day, we will have federated Gitea using
|
||||
ActivityPub.
|
@ -1,36 +1,32 @@
|
||||
{
|
||||
eval,
|
||||
lib,
|
||||
pkgs,
|
||||
}:
|
||||
{ eval, lib, pkgs }:
|
||||
let
|
||||
inherit (lib) types;
|
||||
conf = eval.config;
|
||||
rootDir = builtins.toPath ./..;
|
||||
stripAnyPrefixes = lib.flip (lib.fold lib.removePrefix) [ "${rootDir}/" ];
|
||||
optToDoc = name: opt: {
|
||||
stripAnyPrefixes = lib.flip (lib.fold lib.removePrefix)
|
||||
["${rootDir}/"];
|
||||
optToDoc = name: opt : {
|
||||
inherit name;
|
||||
description = opt.description or null;
|
||||
default = opt.default or null;
|
||||
visible = if (opt ? visible && opt.visible == "shallow") then true else opt.visible or true;
|
||||
visible =
|
||||
if (opt ? visible && opt.visible == "shallow")
|
||||
then true
|
||||
else opt.visible or true;
|
||||
readOnly = opt.readOnly or false;
|
||||
type = opt.type.description or "unspecified";
|
||||
};
|
||||
spliceServiceDefn =
|
||||
item:
|
||||
if item.type == "parametrisable s6-rc service definition" then
|
||||
let
|
||||
sd = lib.attrByPath item.loc [ "not found" ] conf;
|
||||
in
|
||||
item
|
||||
// {
|
||||
declarations = map stripAnyPrefixes item.declarations;
|
||||
spliceServiceDefn = item :
|
||||
if item.type == "parametrisable s6-rc service definition"
|
||||
then
|
||||
let sd = lib.attrByPath item.loc ["not found"] conf;
|
||||
in item // {
|
||||
declarations = map stripAnyPrefixes item.declarations;
|
||||
parameters =
|
||||
let
|
||||
x = lib.mapAttrsToList optToDoc sd.parameters;
|
||||
in
|
||||
x;
|
||||
let x = lib.mapAttrsToList optToDoc sd.parameters; in x;
|
||||
}
|
||||
else
|
||||
item // { declarations = map stripAnyPrefixes item.declarations; };
|
||||
item // { declarations = map stripAnyPrefixes item.declarations; };
|
||||
in
|
||||
builtins.map spliceServiceDefn (pkgs.lib.optionAttrSetToDocList eval.options)
|
||||
builtins.map spliceServiceDefn
|
||||
(pkgs.lib.optionAttrSetToDocList eval.options)
|
||||
|
@ -1,24 +1,38 @@
|
||||
with import <nixpkgs> { };
|
||||
with import <nixpkgs> {} ;
|
||||
|
||||
let
|
||||
inherit (builtins) stringLength readDir filter;
|
||||
devices = filter (n: n != "families") (lib.mapAttrsToList (n: t: n) (readDir ../devices));
|
||||
texts = map (
|
||||
n:
|
||||
let
|
||||
d = import ../devices/${n}/default.nix;
|
||||
tag = ".. _${lib.strings.replaceStrings [ " " ] [ "-" ] n}:";
|
||||
d' = {
|
||||
description = ''
|
||||
${n}
|
||||
${substring 0 (stringLength n) "============================"}
|
||||
'';
|
||||
} // d;
|
||||
in
|
||||
"${tag}\n\n${d'.description}"
|
||||
) devices;
|
||||
devices = filter (n: n != "families")
|
||||
(lib.mapAttrsToList (n: t: n) (readDir ../devices));
|
||||
texts = map (n:
|
||||
let d = import ../devices/${n}/default.nix;
|
||||
d' = {
|
||||
description = "${n}\n${substring 0 (stringLength n) "********************************"}\n";
|
||||
} // d;
|
||||
installer =
|
||||
if d ? description && d ? installer
|
||||
then ''
|
||||
|
||||
The default installation route for this device is
|
||||
:ref:`system-outputs-${d.installer}`
|
||||
''
|
||||
else "";
|
||||
in d'.description)
|
||||
devices;
|
||||
in
|
||||
writeText "hwdoc" ''
|
||||
Supported hardware
|
||||
##################
|
||||
|
||||
For development, the `GL.iNet GL-MT300A <https://www.gl-inet.com/products/gl-mt300a/>`_
|
||||
is an attractive choice as it has a builtin "debrick" procedure in the
|
||||
boot monitor and is also comparatively simple to
|
||||
attach serial cables to (soldering not required), so it
|
||||
is lower-risk than some devices.
|
||||
|
||||
For a more powerful device, something with an ath10k would be the safe bet,
|
||||
or the Linksys E8450 which seems popular in the openwrt community.
|
||||
|
||||
${lib.concatStringsSep "\n\n" texts}
|
||||
|
||||
''
|
||||
|
@ -1,57 +0,0 @@
|
||||
= Liminix
|
||||
Daniel Barlow
|
||||
:doctype: book
|
||||
:toc: left
|
||||
|
||||
include::intro.adoc[]
|
||||
|
||||
include::tutorial.adoc[]
|
||||
|
||||
include::installation.adoc[]
|
||||
|
||||
= For Administrators
|
||||
|
||||
include::configuration.adoc[]
|
||||
|
||||
include::admin.adoc[]
|
||||
|
||||
include::development.adoc[]
|
||||
|
||||
include::modules.adoc[]
|
||||
|
||||
include::code-of-conduct.adoc[]
|
||||
|
||||
[appendix]
|
||||
= Supported hardware
|
||||
|
||||
=== Recommended devices
|
||||
|
||||
For development, the supported GL.iNet devices are all good choices if
|
||||
you can find them, as they have a builtin "debrick" procedure in the
|
||||
boot monitor and are also comparatively simple to attach serial cables
|
||||
to (soldering not required), so are lower-risk than some other devices.
|
||||
|
||||
For a more powerful device, something with an ath10k wireless would be
|
||||
the safe bet, or the Linksys E8450 which seems popular in the OpenWrt
|
||||
community.
|
||||
|
||||
include::hardware.adoc[]
|
||||
|
||||
[appendix]
|
||||
= Module and service options
|
||||
|
||||
include::module-options-generated.inc.adoc[]
|
||||
|
||||
[appendix]
|
||||
= Outputs
|
||||
|
||||
*Outputs* are artefacts that can be installed somehow on a
|
||||
target device, or "installers" which run on the target device to perform
|
||||
the installation.
|
||||
|
||||
There are different outputs because different target devices need
|
||||
different artefacts, or have different ways to get that artefact
|
||||
installed. The options available for a particular device are described
|
||||
in the section for that device.
|
||||
|
||||
include::outputs-generated.inc.adoc[]
|
23
doc/index.rst
Normal file
23
doc/index.rst
Normal file
@ -0,0 +1,23 @@
|
||||
Liminix
|
||||
#######
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 3
|
||||
:caption: Contents:
|
||||
|
||||
intro
|
||||
tutorial
|
||||
configuration
|
||||
admin
|
||||
development
|
||||
modules
|
||||
hardware
|
||||
outputs
|
||||
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
@ -1,182 +0,0 @@
|
||||
= Installation
|
||||
|
||||
Hardware devices vary wildly in their affordances for installing new
|
||||
operating systems, so it should be no surprise that the Liminix
|
||||
installation procedure is hardware-dependent. This section contains
|
||||
generic instructions, but please refer to the documentation for your
|
||||
device to find whether and how well they apply.
|
||||
|
||||
Most of the supported devices fall into one of two broad categories:
|
||||
|
||||
* devices we install by preparing a raw flash image and copying it
|
||||
directly onto (some part of) the flash. This is analogous to (though
|
||||
not quite the same as) using
|
||||
https://www.man7.org/linux/man-pages/man1/dd.1.html:dd(1) on a
|
||||
"grown up" computer to copy a raw disk image. Devices in this
|
||||
category are usually smaller, older, and/or less powerful.
|
||||
|
||||
* devices where the vendor provides a "higher level" storage
|
||||
abstraction, such as http://linux-mtd.infradead.org/doc/ubi.html:UBI
|
||||
over raw flash, or a consumer flash such as MMC, or another storage
|
||||
technology entirely. Installation on these devices is less uniform
|
||||
because it depends on exactly what kind of storage abstraction.
|
||||
|
||||
|
||||
== Building a firmware image
|
||||
|
||||
Liminix uses the Nix language to provide congruent configuration
|
||||
management. This means that to change anything about the way in which a
|
||||
Liminix system works, you make that change in your `+configuration.nix+`
|
||||
(or one of the other files it references), and rerun `+nix-build+` to
|
||||
action the change. It is not possible (at least, without shenanigans) to
|
||||
make changes by logging into the device and running imperative commands
|
||||
whose effects may later be overridden: `+configuration.nix+` always
|
||||
describes the entire system and can be used to recreate that system at
|
||||
any time. You can usefully keep it under version control.
|
||||
|
||||
If you are familiar with NixOS, you will notice some similarities
|
||||
between NixOS and Liminix configuration, and also some differences.
|
||||
Sometimes the differences are due to the resource-constrained devices we
|
||||
deploy onto, sometimes due to differences in the uses these devices are
|
||||
put to.
|
||||
|
||||
For a more full description of how to configure Liminix, see
|
||||
<<_configuration>>. Assuming for the moment that you want a typical home
|
||||
wireless gateway/router, the best way to get started is to copy
|
||||
`+examples/rotuer.nix+` and edit it for your requirements.
|
||||
|
||||
[source,console]
|
||||
----
|
||||
$ cp examples/rotuer.nix configuration.nix
|
||||
$ vi configuration.nix # other editors are available
|
||||
$ # adjust this next command for your hardware device
|
||||
$ nix-build -I liminix-config=./configuration.nix \
|
||||
--arg device "import ./devices/gl-mt300a" -A outputs.default
|
||||
----
|
||||
|
||||
For raw flash devices, this will leave you with a file
|
||||
`+result/firmware.bin+` which you now need to write to the flash.
|
||||
For other devices, _please check the device documentation_
|
||||
|
||||
== Flashing from the boot monitor (TFTP install)
|
||||
|
||||
You will need
|
||||
|
||||
* to open the device and attach a TTL serial adaptor of some kind
|
||||
* a TFTP server on the network that the device is plugged into
|
||||
(or can be plugged into for installation)
|
||||
|
||||
Installing via serial connection is quite hardware-specific and
|
||||
depending on the device may even involve soldering. However, it is in
|
||||
some ways the most "reliable" option: if you can see what's happening
|
||||
(or not happening) in early boot, the risk of "bricking" is
|
||||
substantially reduced and you have options for recovering if you
|
||||
misstep or flash a bad image.
|
||||
|
||||
[[serial]]
|
||||
=== Serial connections
|
||||
|
||||
To speak to U-Boot on your device you'll usually need a serial
|
||||
connection to it. This typically involves opening the box, locating the
|
||||
serial header pins (TX, RX and GND) and connecting a USB TTL converter
|
||||
to them.
|
||||
|
||||
The Rolls Royce of USB/UART cables is the
|
||||
https://cpc.farnell.com/ftdi/ttl-232r-rpi/cable-debug-ttl-232-usb-rpi/dp/SC12825?st=usb%20to%20uart%20cable[FTDI
|
||||
cable], but there are cheaper alternatives based on the PL2303 and
|
||||
CP2102 chipsets - or you could even get creative and use the
|
||||
https://pinout.xyz/[UART GPIO pins] on a Raspberry Pi. Whatever you do,
|
||||
make sure that the voltages are compatible: if your device is 3.3V (this
|
||||
is typical but not universal), you don't want to be sending it 5v or
|
||||
(even worse) 12v.
|
||||
|
||||
Run a terminal emulator such as Minicom on the computer at other end of
|
||||
the link. 115200 8N1 is the typical speed.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
TTL serial connections often have no flow control and so don't always
|
||||
like having massive chunks of text pasted into them - and U-Boot may
|
||||
drop characters while it's busy. So don't do that.
|
||||
|
||||
If using Minicom, you may find it helps to bring up the "Termimal
|
||||
settings" dialog (C^A T), then configure "Newline tx delay" to some
|
||||
small but non-zero value.
|
||||
====
|
||||
|
||||
When you turn the router on you should be greeted with some messages
|
||||
from U-Boot, followed by the instruction to hit some key to stop
|
||||
autoboot. Do this and you will get to the prompt. If you didn't see
|
||||
anything, the strong likelihood is that TX and RX are the wrong way
|
||||
around, or your computer is expecting flow control which the 3 wire
|
||||
connection does not provide. If you see garbage, try a different
|
||||
speed.
|
||||
|
||||
Interesting commands to try first in U-Boot are `+help+` and
|
||||
`+printenv+`.
|
||||
|
||||
=== TFTP
|
||||
|
||||
You will also need to configure a TFTP server on a network that's
|
||||
accessible to the device: how you do that will vary according to which
|
||||
TFTP server you're using and so is out of scope for this document.
|
||||
|
||||
HINT: <<tufted>>, a rudimentary TFTP server, is supplied with Liminix
|
||||
for development purposes. It may or may not fit your needs here.
|
||||
|
||||
==== Building and installing the image
|
||||
|
||||
Follow the device-specific instructions for "TFTP install": usually, the
|
||||
steps are
|
||||
|
||||
* build the [.title-ref]#outputs.mtdimage# output
|
||||
* copy `+result/firmware.bin+` to wherever your TFTP server serves files
|
||||
from
|
||||
* execute the commands listed in `+result/flash.scr+` at the
|
||||
U-Boot command line
|
||||
* reset the device
|
||||
|
||||
You should now see messages from U-Boot, then from the Linux kernel and
|
||||
eventually a shell prompt.
|
||||
|
||||
NOTE: Before you reboot, check which networks the device is plugged into, and
|
||||
disconnect as necessary. If you've just installed a DHCP server or
|
||||
anything else that responds to broadcasts, you may not want it to do
|
||||
that on the network that you temporarily connected it to for installing
|
||||
it.
|
||||
|
||||
== Flashing from OpenWrt
|
||||
|
||||
CAUTION: Untested! A previous version of these instructions (without
|
||||
the -e flag) led to soft-bricking the device when flashing a JFFS2
|
||||
image. The current version _should_ work better but if you are reading this
|
||||
message then nobody has yet confirmed it
|
||||
|
||||
If your device is running OpenWrt then it probably has the `+mtd+`
|
||||
command installed. Transfer `+result/firmware.bin+` onto the running
|
||||
device using e.g. `+scp+`. Now flash as follows:
|
||||
|
||||
[source,console]
|
||||
----
|
||||
mtd -e -r write /tmp/firmware.bin firmware
|
||||
----
|
||||
|
||||
The options to this command are for "erase before writing" and "reboot
|
||||
after writing".
|
||||
|
||||
For more information, please see the
|
||||
https://openwrt.org/docs/guide-user/installation/sysupgrade.cli[OpenWrt
|
||||
manual] which may also contain (hardware-dependent) instructions on how
|
||||
to flash an image using the vendor firmware - perhaps even from a web
|
||||
interface.
|
||||
|
||||
== Flashing from Liminix
|
||||
|
||||
If the device is already running Liminix then in general you cannot safely
|
||||
copy a new image over the top of the running system while it is running.
|
||||
|
||||
If the running system was configured with <<levitate>> you can use
|
||||
that to safely flash your new image. Otherwise you may attempt to use
|
||||
`+flashcp+` directly, but bust out the serial leads in preparation for
|
||||
it going horribly wrong.
|
||||
|
@ -1,62 +0,0 @@
|
||||
= Introduction
|
||||
|
||||
== What is Liminix?
|
||||
|
||||
Liminix is a Nix-based collection of software tailored for domestic
|
||||
wifi router or IoT devices, of the kind that OpenWrt or DD-WRT
|
||||
or Gargoyle or Tomato run on. It's not NixOS on your router: we target
|
||||
devices that are underpowered for the full NixOS experience. It uses
|
||||
busybox tools, musl instead of GNU libc, and s6-rc instead of systemd.
|
||||
|
||||
The Liminix name comes from Liminis, in Latin the genitive declension of
|
||||
"limen", or "of the threshold". Your router stands at the threshold of
|
||||
your (online) home and everything you send to/receive from the outside
|
||||
word goes across it.
|
||||
|
||||
== Where to find out more
|
||||
|
||||
=== The Manual
|
||||
|
||||
You are reading it now, and it is available from wherever you
|
||||
are reading it :-) but its canonical location is
|
||||
https://www.liminix.org/doc/
|
||||
|
||||
=== Source code
|
||||
|
||||
Liminix source code is held in git, and hosted at
|
||||
https://gti.telent.net/dan/liminix, with a mirror at
|
||||
https://github.com/telent/liminix. You can clone from either of those
|
||||
repos. For more on this, see <<_contributing>>.
|
||||
|
||||
=== IRC
|
||||
|
||||
There is an IRC channel https://webchat.oftc.net/?nick=&channels=#liminix[#liminix] registered on the https://www.oftc.net/[OFTC] network, which
|
||||
is a good place to ask if you want a quick answer about how to use
|
||||
Liminix or are looking at a new port. Be mindful that other
|
||||
participants may be in different timezones than your own, so do not
|
||||
expect an immediate answer.
|
||||
|
||||
=== Mailing lists
|
||||
|
||||
Three Liminix mailing lists are available: all are quite low volume.
|
||||
To subscribe to any of these lists, send an email to
|
||||
listname+subscribe@liminix.org. You can write anything you want in the
|
||||
subject and message body: only the destination address is important.
|
||||
|
||||
|
||||
* https://lists.liminix.org/announce/maillist.html[`announce@liminix.org`] for infrequent announcements from Liminix maintainers
|
||||
* https://lists.liminix.org/devel/maillist.html[`devel@liminix.org`] for development-related discussion, patches, suggestions etc
|
||||
* https://lists.liminix.org/users/maillist.html[`users@liminix.org`] for help requests and general discussion
|
||||
|
||||
The mailing lists are managed with Mlmmj and archived with MHonArc.
|
||||
|
||||
=== Standards of behaviour
|
||||
|
||||
Liminix is dedicated to providing a harassment-free experience for
|
||||
everyone. We do not tolerate harassment of participants in any form.
|
||||
|
||||
The Liminix <<_code_of_conduct>> applies to all Liminix spaces, including
|
||||
the IRC channel, mailing lists, and any other forums, both online and
|
||||
off. Anyone who violates the code of conduct may be sanctioned or
|
||||
expelled from these spaces at the discretion of the project
|
||||
leadership.
|
15
doc/intro.rst
Normal file
15
doc/intro.rst
Normal file
@ -0,0 +1,15 @@
|
||||
Introduction
|
||||
############
|
||||
|
||||
Liminix is a Nix-based collection of software tailored for domestic
|
||||
wifi router or IoT device devices, of the kind that OpenWrt or DD-WRT
|
||||
or Gargoyle or Tomato run on.
|
||||
|
||||
This is not NixOS-on-your-router: it's aimed at devices that are
|
||||
underpowered for the full NixOS experience. It uses busybox tools,
|
||||
musl instead of GNU libc, and s6-rc instead of systemd.
|
||||
|
||||
The Liminix name comes from Liminis, in Latin the genitive declension
|
||||
of "limen", or "of the threshold". Your router stands at the threshold
|
||||
of your (online) home and everything you send to/receive from the
|
||||
outside word goes across it.
|
@ -1 +0,0 @@
|
||||
== Module options
|
4
doc/modules.rst
Normal file
4
doc/modules.rst
Normal file
@ -0,0 +1,4 @@
|
||||
Module options
|
||||
##############
|
||||
|
||||
.. include:: modules-generated.rst
|
@ -1,12 +1,13 @@
|
||||
== Outputs
|
||||
Outputs
|
||||
#######
|
||||
|
||||
Liminix _outputs_ are artefacts that can be installed somehow on a
|
||||
target device, or "installers" which run on the target device to perform
|
||||
the installation.
|
||||
Liminix *outputs* are artefacts that can be installed somehow on a
|
||||
target device, or "installers" which run on the target device to
|
||||
perform the installation.
|
||||
|
||||
There are different outputs because different target devices need
|
||||
different artefacts, or have different ways to get that artefact
|
||||
installed. The options available for a particular device are described
|
||||
in the section for that device.
|
||||
installed. The options available for a particular device are described in
|
||||
the section for that device.
|
||||
|
||||
include::outputs-generated.inc.adoc[]
|
||||
.. include:: outputs-generated.rst
|
@ -16,4 +16,4 @@
|
||||
(each [_ option (ipairs (sorted-options (yaml.load (io.read "*a"))))]
|
||||
(when (and (output? option) (not option.internal))
|
||||
(print (.. ".. _" (string.gsub option.name "%." "-") ":") "\n")
|
||||
(print option.description "\n")))
|
||||
(print option.description)))
|
||||
|
@ -1,322 +0,0 @@
|
||||
== Tutorial
|
||||
|
||||
Liminix is very configurable, which can make it initially quite daunting
|
||||
- especially if you're learning Nix or Linux or networking concepts at
|
||||
the same time. In this section we build some "worked example" Liminix
|
||||
images to introduce the concepts. If you follow the examples exactly,
|
||||
they should work. If you change things as you go along, they may work
|
||||
differently or not at all, but the experience should be educational
|
||||
either way.
|
||||
|
||||
=== Requirements
|
||||
|
||||
You will need a reasonably powerful computer running Nix. Target devices
|
||||
for Liminix are unlikely to have the CPU power and disk space to be able
|
||||
to build it in situ, so the build process is based around
|
||||
"cross-compilation" from another computer. The build machine can be any
|
||||
reasonably powerful desktop/laptop/server PC running NixOS. Standalone
|
||||
Nixpkgs installations on other Linux distributions - or on MacOS, or
|
||||
even in a Docker container - also ought to work but are untested.
|
||||
|
||||
=== Running in Qemu
|
||||
|
||||
You can try out Liminix without even having a router to play with. Clone
|
||||
the Liminix git repository and change into its directory
|
||||
|
||||
[source,console]
|
||||
----
|
||||
git clone https://gti.telent.net/dan/liminix
|
||||
cd liminix
|
||||
----
|
||||
|
||||
Now build Liminix
|
||||
|
||||
[source,console]
|
||||
----
|
||||
nix-build -I liminix-config=./examples/hello-from-qemu.nix \
|
||||
--arg device "import ./devices/qemu" -A outputs.default
|
||||
----
|
||||
|
||||
In this command `+liminix-config+` points to the desired software
|
||||
configuration (e.g. services, users, filesystem, secrets) and `+device+`
|
||||
describes the hardware (or emulated hardware) to run it on.
|
||||
`+outputs.default+` tells Liminix that we want the default image output
|
||||
for flashing to the device: for the Qemu "hardware" it's an alias for
|
||||
`+outputs.vmbuild+`, which creates a directory containing a root
|
||||
filesystem image and a kernel.
|
||||
|
||||
[TIP]
|
||||
====
|
||||
The first time you run this it may take several hours, because it builds
|
||||
all of the dependencies including a full MIPS gcc and library toolchain.
|
||||
Once those intermediate build products are in the nix store, subsequent
|
||||
builds will be much faster - practically instant, if nothing has
|
||||
changed.
|
||||
====
|
||||
|
||||
Now you can try it:
|
||||
|
||||
[source,console]
|
||||
----
|
||||
./result/run.sh
|
||||
----
|
||||
|
||||
This starts the Qemu emulator with a bunch of useful options, to run the
|
||||
Liminix configuration you just built. It connects the emulated device's
|
||||
serial console and the
|
||||
https://www.qemu.org/docs/master/system/monitor.html[QEMU monitor] to
|
||||
stdin/stdout.
|
||||
|
||||
You should now see Linux boot messages and after a few seconds be
|
||||
presented with a root shell prompt. You can run commands to look at the
|
||||
filesystem, see what processes are running, view log messages (in
|
||||
:file:/run/log/current), etc. To kill the emulator, press ^P (Control P)
|
||||
then c to enter the "QEMU Monitor", then type `+quit+` at the `+(qemu)+`
|
||||
prompt.
|
||||
|
||||
To see that it's running network services we need to connect to its
|
||||
emulated network. Start the machine again, if you had stopped it, and
|
||||
open up a second terminal on your build machine. We're going to run
|
||||
another virtual machine attached to the virtual network, which will
|
||||
request an IP address from our Liminix system and give you a shell you
|
||||
can run ssh from.
|
||||
|
||||
We use https://www.system-rescue.org/[System Rescue] in tty mode (no
|
||||
graphical output) for this example, but if you have some other favourite
|
||||
Linux Live CD ISO - or, for that matter, any other OS image that QEMU
|
||||
can boot - adjust the command to suit.
|
||||
|
||||
Download the System Rescue ISO:
|
||||
|
||||
[source,console]
|
||||
----
|
||||
curl https://fastly-cdn.system-rescue.org/releases/10.01/systemrescue-10.01-amd64.iso -O
|
||||
----
|
||||
|
||||
and run it
|
||||
|
||||
[source,console]
|
||||
----
|
||||
nix-shell -p qemu --run " \
|
||||
qemu-system-x86_64 \
|
||||
-echr 16 \
|
||||
-m 1024 \
|
||||
-cdrom systemrescue-10.01-amd64.iso \
|
||||
-netdev socket,mcast=230.0.0.1:1235,localaddr=127.0.0.1,id=lan \
|
||||
-device virtio-net,disable-legacy=on,disable-modern=off,netdev=lan,mac=ba:ad:3d:ea:21:01 \
|
||||
-display none -serial mon:stdio"
|
||||
----
|
||||
|
||||
System Rescue displays a boot menu at which you should select the
|
||||
"serial console" option, then after a few moments it boots to a root
|
||||
prompt. You can now try things out:
|
||||
|
||||
* run `+ip a+` and see that it's been allocated an IP address in the
|
||||
range 10.3.0.0/16.
|
||||
* run `+ping 10.3.0.1+` to see that the Liminix VM responds
|
||||
* run `+ssh root@10.3.0.1+` to try logging into it.
|
||||
|
||||
Congratulations! You have installed your first Liminix system - albeit
|
||||
it has no practical use and it's not even real. The next step is to try
|
||||
running it on hardware.
|
||||
|
||||
=== Installing on hardware
|
||||
|
||||
For the next example, we're going to install onto an actual hardware
|
||||
device. These steps have been tested using a GL.iNet GL-MT300A, which
|
||||
has been chosen for the purpose because it's cheap and easy to unbrick
|
||||
if necessary.
|
||||
|
||||
[WARNING]
|
||||
====
|
||||
There is always a risk of rendering your device unbootable by flashing
|
||||
it with an image that doesn't work. The GL-MT300A has a builtin
|
||||
"debrick" procedure in the boot monitor and is also comparatively simple
|
||||
to attach serial cables to (soldering not required), so it is lower-risk
|
||||
than some devices. Using some other Liminix-supported MIPS hardware
|
||||
device also _ought_ to work here, but you accept the slightly greater
|
||||
bricking risk if it doesn't.
|
||||
|
||||
See <<_supported_hardware>> for device support status.
|
||||
====
|
||||
|
||||
You may want to read and inwardly digest the section on <<serial>>
|
||||
when you start working with Liminix on real hardware. You
|
||||
won't _need_ serial access for this example, assuming it works, but it
|
||||
allows you to see the boot monitor and kernel messages, and to login
|
||||
directly to the device if for some reason it doesn't bring its network
|
||||
up.
|
||||
|
||||
Now we can build Liminix. Although we could use the same example
|
||||
configuration as we did for Qemu, you might not want to plug a DHCP
|
||||
server into your working LAN because it will compete with the real DHCP
|
||||
service. So we're going to use a different configuration with a DHCP
|
||||
client: this is `+examples/hello-from-mt300.nix+`
|
||||
|
||||
It's instructive to compare the two configurations:
|
||||
|
||||
[source,console]
|
||||
----
|
||||
diff -u examples/hello-from-qemu.nix examples/hello-from-mt300.nix
|
||||
----
|
||||
|
||||
You'll see a new `+boot.tftp+` stanza which you can ignore,
|
||||
`+services.dns+` has been removed, and the static IP address allocation
|
||||
has been replaced by a `+dhcp.client+` service.
|
||||
|
||||
[source,console]
|
||||
----
|
||||
nix-build -I liminix-config=./examples/hello-from-mt300.nix \
|
||||
--arg device "import ./devices/gl-mt300a" -A outputs.default
|
||||
----
|
||||
|
||||
[TIP]
|
||||
====
|
||||
The first time you run this it may take several hours. Again? Yes, even
|
||||
if you ran the previous example. Qemu is set up as a big-endian system
|
||||
whereas the MediaTek SoC on this device is little-endian - so it
|
||||
requires building all of the dependencies including an entirely
|
||||
different MIPS gcc and library toolchain to the other one.
|
||||
====
|
||||
|
||||
This time in `+result/+` you will see a bunch of files. Most of them you
|
||||
can ignore for the moment, but `+result/firmware.bin+` is the firmware
|
||||
image you can flash.
|
||||
|
||||
==== Flashing
|
||||
|
||||
Again, there are a number of different ways you could do this: using
|
||||
TFTP with a serial cable, through the stock firmware's web UI, or using
|
||||
the https://docs.gl-inet.com/router/en/3/tutorials/debrick/[vendor's
|
||||
"debrick" process]. The last of these options has a lot to recommend it
|
||||
for a first attempt:
|
||||
|
||||
* it works no matter what firmware is currently installed
|
||||
* it doesn't require plugging a router into the same network as your
|
||||
build system and potentially messing up your actual upstream
|
||||
* no need to open the device and add cables
|
||||
|
||||
You can read detailed instructions on the vendor site, but the short
|
||||
version is:
|
||||
|
||||
[arabic]
|
||||
. turn the device off
|
||||
. connect it by ethernet cable to a computer
|
||||
. configure the computer to have static ip address 192.168.1.10
|
||||
. while holding down the Reset button, turn the device on
|
||||
. after about five seconds you can release the Reset button
|
||||
. visit http://192.168.1.1/ using a web browser on the connected
|
||||
computer
|
||||
. click on "Browse" and choose `+result/firmware.bin+`
|
||||
. click on "Update firmware"
|
||||
. wait a minute or so while it updates.
|
||||
|
||||
There's no feedback from the web interface when the flashing is
|
||||
finished, but what should happen is that the router reboots and starts
|
||||
running Liminix. Now you need to figure out what address it got from
|
||||
DHCP - e.g. by checking the DHCP server logs, or maybe by pinging
|
||||
`+hello.lan+` or something. Once you've found it on the network you can
|
||||
ping it and ssh to it just like you did the Qemu example, but this time
|
||||
for real.
|
||||
|
||||
[WARNING]
|
||||
====
|
||||
Do not leave the default root password in place on any device exposed to
|
||||
the internet! Although it has no writable storage and no default route,
|
||||
a motivated attacker with some imagination could probably still do
|
||||
something awful using it.
|
||||
====
|
||||
|
||||
Congratulations Part II! You have installed your first Liminix system on
|
||||
actual hardware - albeit that it _still_ has no practical use.
|
||||
|
||||
Exercise for the reader: change the default password by editing
|
||||
`+examples/hello-from-mt300.nix+`, and then create and upload a new
|
||||
image that has it set to something less hopeless.
|
||||
|
||||
=== Routing
|
||||
|
||||
The third example `+examples/demo.nix+` is a fully-functional home "WiFi
|
||||
router" - although you will have to edit it a bit before it will
|
||||
actually work for you. Copy `+examples/demo.nix+` to `+my-router.nix+`
|
||||
(or other name of your choice) and open it in your favourite text
|
||||
editor. Everywhere that the text `+EDIT+` appears is either a place you
|
||||
probably want to change or a place you almost certainly need to change.
|
||||
|
||||
There's a lot going on in this configuration:
|
||||
|
||||
* it provides a wireless access point using the `+hostapd+` service: in
|
||||
this stanza you can change the ssid, the channel, the passphrase etc.
|
||||
* the wireless lan and wired lan are bridged together with the
|
||||
`+bridge+` service, so that your wired and wireless clients appear to be
|
||||
on the same network.
|
||||
|
||||
[TIP]
|
||||
====
|
||||
If you were using a hardware device that provides both 2.4GHz and 5GHz
|
||||
wifi, you'd probably find that it has two wireless devices (often called
|
||||
wlan0 and wlan1). In Liminix we handle this by running two `+hostapd+`
|
||||
services, and adding both of them to the network bridge along with the
|
||||
wired lan. (You can see an example in `+examples/rotuer.nix+`)
|
||||
====
|
||||
|
||||
* we use the combination DNS and DHCP daemon provided by the `+dnsmasq+`
|
||||
service, which you can configure
|
||||
* the upstream network is "PPP over Ethernet", provided by the `+pppoe+`
|
||||
service. Assuming that your ISP uses this standard, they will have
|
||||
provided you with a PPP username and password (sometimes this will be
|
||||
listed as "PAP" or "CHAP") which you can edit into the configuration
|
||||
* this example supports the
|
||||
newfootnote:[https://datatracker.ietf.org/doc/html/rfc1883[RFC1883
|
||||
Internet Protocol, Version 6] was published in 1995, so only "new"
|
||||
when Bill Clinton was US President] Internet Protocol v6 as well as
|
||||
traditional IPv4. Configuring IPv6 seems to vary from one ISP to the
|
||||
next: this example expects them to be providing IP address allocation
|
||||
and "prefix delegation" using DHCP6.
|
||||
|
||||
Build it using the same method as the previous example
|
||||
|
||||
[source,console]
|
||||
----
|
||||
nix-build -I liminix-config=./my-router.nix \
|
||||
--arg device "import ./devices/gl-mt300a" -A outputs.default
|
||||
----
|
||||
|
||||
and then you can flash it to the device.
|
||||
|
||||
==== Bonus: in-place updates
|
||||
|
||||
This configuration uses a writable filesystem (see the line
|
||||
`+rootfsType = "jffs2"+`), which means that once you've flashed it for
|
||||
the first time, you can make further updates over SSH onto the running
|
||||
router. To try this, make a small change (I'd suggest changing the
|
||||
hostname) and then run
|
||||
|
||||
[source,console]
|
||||
----
|
||||
nix-build -I liminix-config=./my-router.nix \
|
||||
--arg device "import ./devices/gl-ar750" \
|
||||
-A outputs.systemConfiguration && \
|
||||
result/install.sh root@address-of-the-device
|
||||
----
|
||||
|
||||
(This requires the device to be network-accessible from your build
|
||||
machine, which for a test/demo system might involve a second network
|
||||
device in your build system - USB ethernet adapters are cheap - or a bit
|
||||
of messing around unplugging cables.)
|
||||
|
||||
For more information about in-place-updates, see the manual section
|
||||
`+Rebuilding the system+`.
|
||||
|
||||
=== Final thoughts
|
||||
|
||||
* These are demonstration configs for pedagogical purposes. If you'd
|
||||
like to see some more realistic uses of Liminix,
|
||||
`+examples/rotuer,arhcive,extneder.nix+` are based on some actual real
|
||||
hosts in my home network.
|
||||
* The technique used here for flashing was chosen mostly because it
|
||||
doesn't need much infrastructure/tooling, but it is a bit of a faff
|
||||
(requires physical access, vendor specific). There are slicker ways to
|
||||
do it that need a bit more setup - we'll talk about that later as well.
|
||||
|
||||
*Footnotes*
|
324
doc/tutorial.rst
Normal file
324
doc/tutorial.rst
Normal file
@ -0,0 +1,324 @@
|
||||
Tutorial
|
||||
########
|
||||
|
||||
Liminix is very configurable, which can make it initially quite
|
||||
daunting - especially if you're learning Nix or Linux or networking
|
||||
concepts at the same time. In this section we build some "worked
|
||||
example" Liminix images to introduce the concepts. If you follow the
|
||||
examples exactly, they should work. If you change things as you go
|
||||
along, they may work differently or not at all, but the experience
|
||||
should be educational either way.
|
||||
|
||||
|
||||
Requirements
|
||||
************
|
||||
|
||||
You will need a reasonably powerful computer running Nix. Target
|
||||
devices for Liminix are unlikely to have the CPU power and disk space
|
||||
to be able to build it in situ, so the build process is based around
|
||||
"cross-compilation" from another computer. The build machine can be
|
||||
any reasonably powerful desktop/laptop/server PC running NixOS.
|
||||
Standalone Nixpkgs installations on other Linux distributions - or on
|
||||
MacOS, or even in a Docker container - also ought to work but are
|
||||
untested.
|
||||
|
||||
|
||||
Running in Qemu
|
||||
***************
|
||||
|
||||
You can try out Liminix without even having a router to play with.
|
||||
Clone the Liminix git repository and change into its directory
|
||||
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
git clone https://gti.telent.net/dan/liminix
|
||||
cd liminix
|
||||
|
||||
Now build Liminix
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
nix-build -I liminix-config=./examples/hello-from-qemu.nix \
|
||||
--arg device "import ./devices/qemu" -A outputs.default
|
||||
|
||||
In this command ``liminix-config`` points to the desired software
|
||||
configuration (e.g. services, users, filesystem, secrets) and
|
||||
``device`` describes the hardware (or emulated hardware) to run it on.
|
||||
``outputs.default`` tells Liminix that we want the default image
|
||||
output for flashing to the device: for the Qemu "hardware" it's an
|
||||
alias for ``outputs.vmbuild``, which creates a directory containing a
|
||||
root filesystem image and a kernel.
|
||||
|
||||
.. tip:: The first time you run this it may take several hours,
|
||||
because it builds all of the dependencies including a full
|
||||
MIPS gcc and library toolchain. Once those intermediate build
|
||||
products are in the nix store, subsequent builds will be much
|
||||
faster - practically instant, if nothing has changed.
|
||||
|
||||
Now you can try it:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
./result/run.sh
|
||||
|
||||
This starts the Qemu emulator with a bunch of useful options, to run
|
||||
the Liminix configuration you just built. It connects the emulated
|
||||
device's serial console and the `QEMU monitor
|
||||
<https://www.qemu.org/docs/master/system/monitor.html>`_ to
|
||||
stdin/stdout.
|
||||
|
||||
You should now see Linux boot messages and after a few seconds be
|
||||
presented with a root shell prompt. You can run commands to look at
|
||||
the filesystem, see what processes are running, view log messages (in
|
||||
:file:/run/uncaught-logs.current), etc. To kill the emulator, press ^P
|
||||
(Control P) then c to enter the "QEMU Monitor", then type ``quit`` at
|
||||
the ``(qemu)`` prompt.
|
||||
|
||||
To see that it's running network services we need to connect to its
|
||||
emulated network. Start the machine again, if you had stopped it, and
|
||||
open up a second terminal on your build machine. We're going to run
|
||||
another virtual machine attached to the virtual network, which will
|
||||
request an IP address from our Liminix system and give you a shell you
|
||||
can run ssh from.
|
||||
|
||||
We use `System Rescue <https://www.system-rescue.org/>`_ in tty
|
||||
mode (no graphical output) for this example, but if you have some
|
||||
other favourite Linux Live CD ISO - or, for that matter, any other OS
|
||||
image that QEMU can boot - adjust the command to suit.
|
||||
|
||||
Download the System Rescue ISO:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
curl https://fastly-cdn.system-rescue.org/releases/10.01/systemrescue-10.01-amd64.iso -O
|
||||
|
||||
and run it
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
nix-shell -p qemu --run " \
|
||||
qemu-system-x86_64 \
|
||||
-echr 16 \
|
||||
-m 1024 \
|
||||
-cdrom systemrescue-10.01-amd64.iso \
|
||||
-netdev socket,mcast=230.0.0.1:1235,localaddr=127.0.0.1,id=lan \
|
||||
-device virtio-net,disable-legacy=on,disable-modern=off,netdev=lan,mac=ba:ad:3d:ea:21:01 \
|
||||
-display none -serial mon:stdio"
|
||||
|
||||
System Rescue displays a boot menu at which you should select the
|
||||
"serial console" option, then after a few moments it boots to a root
|
||||
prompt. You can now try things out:
|
||||
|
||||
* run :command:`ip a` and see that it's been allocated an IP address in the range 10.3.0.0/16.
|
||||
|
||||
* run :command:`ping 10.3.0.1` to see that the Liminix VM responds
|
||||
|
||||
* run :command:`ssh root@10.3.0.1` to try logging into it.
|
||||
|
||||
Congratulations! You have installed your first Liminix system - albeit
|
||||
it has no practical use and it's not even real. The next step is to try
|
||||
running it on hardware.
|
||||
|
||||
Installing on hardware
|
||||
**********************
|
||||
|
||||
For the next example, we're going to install onto an actual hardware
|
||||
device. These steps have been tested using a GL.iNet GL-MT300A, which
|
||||
has been chosen for the purpose because it's cheap and easy to
|
||||
unbrick if necessary.
|
||||
|
||||
.. warning:: There is always a risk of rendering your device
|
||||
unbootable by flashing it with an image that doesn't
|
||||
work. The GL-MT300A has a builtin "debrick" procedure in
|
||||
the boot monitor and is also comparatively simple to
|
||||
attach serial cables to (soldering not required), so it
|
||||
is lower-risk than some devices. Using some other
|
||||
Liminix-supported MIPS hardware device also *ought* to
|
||||
work here, but you accept the slightly greater bricking
|
||||
risk if it doesn't.
|
||||
|
||||
See :doc:`hardware` for device support status.
|
||||
|
||||
You may want to read and inwardly digest the Develoment Manual section
|
||||
:ref:`serial` when you start working with Liminix on real hardware. You
|
||||
won't *need* serial access for this example, assuming it works, but it
|
||||
allows you
|
||||
to see the boot monitor and kernel messages, and to login directly to
|
||||
the device if for some reason it doesn't bring its network up.
|
||||
|
||||
Now we can build Liminix. Although we could use the same example
|
||||
configuration as we did for Qemu, you might not want to plug a DHCP
|
||||
server into your working LAN because it will compete with the real
|
||||
DHCP service. So we're going to use a different configuration with a
|
||||
DHCP client: this is :file:`examples/hello-from-mt300.nix`
|
||||
|
||||
It's instructive to compare the two configurations:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
diff -u examples/hello-from-qemu.nix examples/hello-from-mt300.nix
|
||||
|
||||
You'll see a new ``boot.tftp`` stanza which you can ignore,
|
||||
``services.dns`` has been removed, and the static IP address allocation
|
||||
has been replaced by a ``dhcp.client`` service.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
nix-build -I liminix-config=./examples/hello-from-mt300.nix \
|
||||
--arg device "import ./devices/gl-mt300a" -A outputs.default
|
||||
|
||||
.. tip:: The first time you run this it may take several hours.
|
||||
Again? Yes, even if you ran the previous example. Qemu is
|
||||
set up as a big-endian system whereas the MediaTek SoC
|
||||
on this device is little-endian - so it requires building
|
||||
all of the dependencies including an entirely different
|
||||
MIPS gcc and library toolchain to the other one.
|
||||
|
||||
This time in :file:`result/` you will see a bunch of files. Most of
|
||||
them you can ignore for the moment, but :file:`result/firmware.bin` is
|
||||
the firmware image you can flash.
|
||||
|
||||
|
||||
Flashing
|
||||
========
|
||||
|
||||
Again, there are a number of different ways you could do this: using
|
||||
TFTP with a serial cable, through the stock firmware's web UI, or
|
||||
using the `vendor's "debrick" process
|
||||
<https://docs.gl-inet.com/router/en/3/tutorials/debrick/>`_. The last
|
||||
of these options has a lot to recommend it for a first attempt:
|
||||
|
||||
* it works no matter what firmware is currently installed
|
||||
|
||||
* it doesn't require plugging a router into the same network as your
|
||||
build system and potentially messing up your actual upstream
|
||||
|
||||
* no need to open the device and add cables
|
||||
|
||||
You can read detailed instructions on the vendor site, but the short version is:
|
||||
|
||||
1. turn the device off
|
||||
2. connect it by ethernet cable to a computer
|
||||
3. configure the computer to have static ip address 192.168.1.10
|
||||
4. while holding down the Reset button, turn the device on
|
||||
5. after about five seconds you can release the Reset button
|
||||
6. visit http://192.168.1.1/ using a web browser on the connected computer
|
||||
7. click on "Browse" and choose :file:`result/firmware.bin`
|
||||
8. click on "Update firmware"
|
||||
9. wait a minute or so while it updates.
|
||||
|
||||
There's no feedback from the web interface when the flashing is
|
||||
finished, but what should happen is that the router reboots and
|
||||
starts running Liminix. Now you need to figure out what address it got
|
||||
from DHCP - e.g. by checking the DHCP server logs, or maybe by pinging
|
||||
``hello.lan`` or something. Once you've found it on the
|
||||
network you can ping it and ssh to it just like you did the Qemu
|
||||
example, but this time for real.
|
||||
|
||||
.. warning:: Do not leave the default root password in place on any
|
||||
device exposed to the internet! Although it has no
|
||||
writable storage and no default route, a motivated attacker
|
||||
with some imagination could probably still do something
|
||||
awful using it.
|
||||
|
||||
Congratulations Part II! You have installed your first Liminix system on actual hardware - albeit that it *still* has no practical use.
|
||||
|
||||
Exercise for the reader: change the default password by editing
|
||||
:file:`examples/hello-from-mt300.nix`, and then create and upload a
|
||||
new image that has it set to something less hopeless.
|
||||
|
||||
Routing
|
||||
*******
|
||||
|
||||
The third example :file:`examples/demo.nix` is a fully-functional home
|
||||
"WiFi router" - although you will have to edit it a bit before it will
|
||||
actually work for you. Copy :file:`examples/demo.nix` to
|
||||
:file:`my-router.nix` (or other name of your choice) and open it in
|
||||
your favourite text editor. Everywhere that the text :code:`EDIT`
|
||||
appears is either a place you probably want to change or a place you
|
||||
almost certainly need to change.
|
||||
|
||||
There's a lot going on in this configuration:
|
||||
|
||||
* it provides a wireless access point using the :code:`hostapd`
|
||||
service: in this stanza you can change the ssid, the channel,
|
||||
the passphrase etc.
|
||||
|
||||
* the wireless lan and wired lan are bridged together with the
|
||||
:code:`bridge` service, so that your wired and wireless clients appear
|
||||
to be on the same network.
|
||||
|
||||
.. tip:: If you were using a hardware device that provides both 2.4GHz
|
||||
and 5GHz wifi, you'd probably find that it has two wireless
|
||||
devices (often called wlan0 and wlan1). In Liminix we handle
|
||||
this by running two :code:`hostapd` services, and adding
|
||||
both of them to the network bridge along with the wired lan.
|
||||
(You can see an example in :file:`examples/rotuer.nix`)
|
||||
|
||||
* we use the combination DNS and DHCP daemon provided by the
|
||||
:code:`dnsmasq` service, which you can configure
|
||||
|
||||
* the upstream network is "PPP over Ethernet", provided by the
|
||||
:code:`pppoe` service. Assuming that your ISP uses this standard,
|
||||
they will have provided you with a PPP username and password
|
||||
(sometimes this will be listed as "PAP" or "CHAP") which you can edit
|
||||
into the configuration
|
||||
|
||||
* this example supports the new [#ipv6]_ Internet Protocol v6
|
||||
as well as traditional IPv4. Configuring IPv6 seems to
|
||||
vary from one ISP to the next: this example expects them
|
||||
to be providing IP address allocation and "prefix delegation"
|
||||
using DHCP6.
|
||||
|
||||
Build it using the same method as the previous example
|
||||
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
nix-build -I liminix-config=./my-router.nix \
|
||||
--arg device "import ./devices/gl-mt300a" -A outputs.default
|
||||
|
||||
and then you can flash it to the device.
|
||||
|
||||
|
||||
Bonus: in-place updates
|
||||
=======================
|
||||
|
||||
This configuration uses a writable filesystem (see the line
|
||||
:code:`rootfsType = "jffs2"`), which means that once you've flashed it
|
||||
for the first time, you can make further updates over SSH onto the
|
||||
running router. To try this, make a small change (I'd suggest changing
|
||||
the hostname) and then run
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
nix-shell --run "liminix-rebuild root@address-of-the-device -I liminix-config=./my-router.nix --arg device "import ./devices/gl-ar750""
|
||||
|
||||
(This requires the device to be network-accessible from your build
|
||||
machine, which for a test/demo system might involve a second network
|
||||
device in your build system - USB ethernet adapters are cheap - or
|
||||
a bit of messing around unplugging cables.)
|
||||
|
||||
For more information about :code:`liminix-rebuild`, see the manual section :ref:`admin:Rebuilding the system`.
|
||||
|
||||
|
||||
Final thoughts
|
||||
**************
|
||||
|
||||
* These are demonstration configs for pedagogical purposes. If you'd
|
||||
like to see some more realistic uses of Liminix,
|
||||
:file:`examples/rotuer,arhcive,extneder.nix` are based on some
|
||||
actual real hosts in my home network.
|
||||
|
||||
* The technique used here for flashing was chosen mostly because it
|
||||
doesn't need much infrastructure/tooling, but it is a bit of a faff
|
||||
(requires physical access, vendor specific). There are slicker ways
|
||||
to do it that need a bit more setup - we'll talk about that later as
|
||||
well.
|
||||
|
||||
|
||||
|
||||
.. rubric:: Footnotes
|
||||
|
||||
.. [#ipv6] `RFC1883 Internet Protocol, Version 6 <https://datatracker.ietf.org/doc/html/rfc1883>`_ was published in 1995, so only "new" when Bill Clinton was US President
|
@ -9,19 +9,17 @@
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
}: let
|
||||
secrets = import ./extneder-secrets.nix;
|
||||
inherit (pkgs.liminix.services) oneshot longrun target;
|
||||
inherit (pkgs.liminix.services) oneshot longrun bundle target;
|
||||
inherit (pkgs.pseudofile) dir symlink;
|
||||
inherit (pkgs) writeText serviceFns;
|
||||
inherit (pkgs) writeText dropbear ifwait serviceFns;
|
||||
svc = config.system.service;
|
||||
in
|
||||
rec {
|
||||
in rec {
|
||||
boot = {
|
||||
tftp = {
|
||||
serverip = "10.0.0.1";
|
||||
ipaddr = "10.0.0.8";
|
||||
serverip = "192.168.8.148";
|
||||
ipaddr = "192.168.8.251";
|
||||
};
|
||||
};
|
||||
|
||||
@ -30,17 +28,38 @@ rec {
|
||||
../modules/network
|
||||
../modules/vlan
|
||||
../modules/ssh
|
||||
../modules/usb.nix
|
||||
../modules/watchdog
|
||||
../modules/mount
|
||||
];
|
||||
hostname = "arhcive";
|
||||
|
||||
kernel = {
|
||||
config = {
|
||||
USB = "y";
|
||||
USB_EHCI_HCD = "y";
|
||||
USB_EHCI_HCD_PLATFORM = "y";
|
||||
USB_OHCI_HCD = "y";
|
||||
USB_OHCI_HCD_PLATFORM = "y";
|
||||
USB_SUPPORT = "y";
|
||||
USB_COMMON = "y";
|
||||
USB_STORAGE = "y";
|
||||
USB_STORAGE_DEBUG = "n";
|
||||
USB_UAS = "y";
|
||||
USB_ANNOUNCE_NEW_DEVICES = "y";
|
||||
SCSI = "y";
|
||||
BLK_DEV_SD = "y";
|
||||
USB_PRINTER = "y";
|
||||
MSDOS_PARTITION = "y";
|
||||
EFI_PARTITION = "y";
|
||||
EXT4_FS = "y";
|
||||
EXT4_USE_FOR_EXT2 = "y";
|
||||
FS_ENCRYPTION = "y";
|
||||
};
|
||||
};
|
||||
|
||||
services.dhcpc =
|
||||
let
|
||||
iface = config.hardware.networkInterfaces.lan;
|
||||
in
|
||||
svc.network.dhcp.client.build {
|
||||
let iface = config.hardware.networkInterfaces.lan;
|
||||
in svc.network.dhcp.client.build {
|
||||
interface = iface;
|
||||
dependencies = [ config.services.hostname ];
|
||||
};
|
||||
@ -48,16 +67,14 @@ rec {
|
||||
services.sshd = svc.ssh.build { };
|
||||
|
||||
services.watchdog = svc.watchdog.build {
|
||||
watched = with config.services; [
|
||||
sshd
|
||||
dhcpc
|
||||
];
|
||||
watched = with config.services ; [ sshd dhcpc ];
|
||||
};
|
||||
|
||||
services.resolvconf = oneshot rec {
|
||||
dependencies = [ services.dhcpc ];
|
||||
name = "resolvconf";
|
||||
up = ''
|
||||
. ${serviceFns}
|
||||
( in_outputs ${name}
|
||||
for i in $(output ${services.dhcpc} dns); do
|
||||
echo "nameserver $i" > resolv.conf
|
||||
@ -69,20 +86,17 @@ rec {
|
||||
etc = dir {
|
||||
"resolv.conf" = symlink "${services.resolvconf}/.outputs/resolv.conf";
|
||||
};
|
||||
srv = dir { };
|
||||
srv = dir {};
|
||||
};
|
||||
|
||||
services.defaultroute4 = svc.network.route.build {
|
||||
via = "$(output ${services.dhcpc} router)";
|
||||
target = "default";
|
||||
dependencies = [ services.dhcpc ];
|
||||
dependencies = [services.dhcpc];
|
||||
};
|
||||
|
||||
programs.busybox = {
|
||||
applets = [
|
||||
"lsusb"
|
||||
"tar"
|
||||
];
|
||||
programs.busybox = {
|
||||
applets = ["lsusb" "tar"];
|
||||