Compare commits
65 Commits
first-do-no-arm
...
armv7
| Author | SHA1 | Date | |
|---|---|---|---|
| 4ae1edbce7 | |||
| 1a5efff3ae | |||
| 8772e64b94 | |||
| e6ef4f78bb | |||
| d2f517a4e9 | |||
| 0f38ee0e9c | |||
| 61dc5beca8 | |||
| f3225c2bd5 | |||
| 8798ee9830 | |||
| 629914f65e | |||
| 0693cf23d8 | |||
| c341eb46b6 | |||
| 1a369ff3bf | |||
| 364c5faf9e | |||
| 80a09a9a9b | |||
| 3518e2ecca | |||
| fc2eb6ee4d | |||
| bd20f3e419 | |||
| f62ad0e1d7 | |||
| ed792e0dc0 | |||
| d025c33d30 | |||
| a755c9c3c5 | |||
| fdf74fa06b | |||
| b8dea2ed34 | |||
| c18f07f02f | |||
| 1c4412a1f4 | |||
| dbc16edf96 | |||
| 528d619d76 | |||
| 269e972970 | |||
| 2a5669c2cd | |||
| 4df248323c | |||
| b01840fa7f | |||
| a896c4e31c | |||
| caf8e85061 | |||
| 76f03ecf0f | |||
| f57997c605 | |||
| 3c483ebd9a | |||
| a30b658999 | |||
| 1ec7fcc197 | |||
| f9f934b40c | |||
| e3c8ab351e | |||
| 7e13eda490 | |||
| 3a2f074199 | |||
| 74f4e0a2f3 | |||
| 9133475103 | |||
| abd01a7809 | |||
| d0c2b3b274 | |||
| dfe7228b99 | |||
| ab147abd9b | |||
| bca0c9b26b | |||
| c59a228955 | |||
| 1673a71831 | |||
| 94e51db649 | |||
| 3205a38ac9 | |||
| 119d6ad379 | |||
| 8d356890b0 | |||
| be22fbbb0a | |||
| 9f87fd8625 | |||
| 3a3ee8bbbd | |||
| 381730d081 | |||
| 57eb55de58 | |||
| 4389fa15f7 | |||
| 4f29bdd3ed | |||
| f1c04c7979 | |||
| f75995e895 |
+255
@@ -2436,3 +2436,258 @@ Sun Sep 17 16:44:31 BST 2023
|
||||
Can we figure out which bits of the old doc are missing from the new
|
||||
one and just transplant those? Then we can merge it sooner
|
||||
instead of blocking on writig all the new stuff
|
||||
|
||||
Mon Sep 25 16:58:51 BST 2023
|
||||
|
||||
jffs2 on mt300a isn't finding root partition in initramfs, and it
|
||||
seems to be because MTD_SPLIT_UIMAGE_FW isn't working
|
||||
|
||||
[ 0.426792] spi spi0.0: force spi mode3
|
||||
[ 0.431305] spi-nor spi0.0: w25q128 (16384 Kbytes)
|
||||
[ 0.436322] 5 fixed-partitions partitions found on MTD device spi0.0
|
||||
[ 0.442875] OF: Bad cell count for /palmbus@10000000/spi@b00/flash@0/partitions
|
||||
[ 0.450400] OF: Bad cell count for /palmbus@10000000/spi@b00/flash@0/partitions
|
||||
[ 0.458208] OF: Bad cell count for /palmbus@10000000/spi@b00/flash@0/partitions
|
||||
[ 0.465751] OF: Bad cell count for /palmbus@10000000/spi@b00/flash@0/partitions
|
||||
[ 0.473522] Creating 5 MTD partitions on "spi0.0":
|
||||
[ 0.478466] 0x000000000000-0x000000030000 : "u-boot"
|
||||
[ 0.484447] 0x000000030000-0x000000040000 : "u-boot-env"
|
||||
[ 0.490888] 0x000000040000-0x000000050000 : "factory"
|
||||
[ 0.497110] 0x000000050000-0x000000fd0000 : "firmware"
|
||||
[ 0.596423] 0x000000ff0000-0x000001000000 : "art"
|
||||
[ 0.611508] gsw: setting port4 to ephy mode
|
||||
|
||||
with squashfs root it's the same but for the extra split partitions:
|
||||
|
||||
[ 0.468715] Creating 5 MTD partitions on "spi0.0":
|
||||
[ 0.473653] 0x000000000000-0x000000030000 : "u-boot"
|
||||
[ 0.479652] 0x000000030000-0x000000040000 : "u-boot-env"
|
||||
[ 0.486085] 0x000000040000-0x000000050000 : "factory"
|
||||
[ 0.492318] 0x000000050000-0x000000fd0000 : "firmware"
|
||||
[ 0.499304] 2 uimage-fw partitions found on MTD device firmware
|
||||
[ 0.505457] Creating 2 MTD partitions on "firmware":
|
||||
[ 0.510543] 0x000000000000-0x000000260000 : "kernel"
|
||||
[ 0.516616] 0x000000260000-0x000000f80000 : "rootfs"
|
||||
[ 0.522570] mtd: device 5 (rootfs) set to be root filesystem
|
||||
[ 0.528565] 0x000000ff0000-0x000001000000 : "art"
|
||||
|
||||
turns out this is because the device thinks it has 4k erase block size
|
||||
because MTD_SPI_NOR_USE_4K_SECTORS was set, and that was causing
|
||||
mtdsplit to look in the wrong place for a root filesystem
|
||||
|
||||
Mon Sep 25 18:50:05 BST 2023
|
||||
|
||||
No, that wasn't it. Turned out to be an endianness-dependent check for
|
||||
JFFS2 magic in mtdsplit.
|
||||
|
||||
setenv serverip 10.0.0.1
|
||||
setenv ipaddr 10.0.0.8
|
||||
tftp 0xa00000 result/uimage
|
||||
bootm 0xa00000
|
||||
|
||||
Fri Sep 29 20:50:39 BST 2023
|
||||
|
||||
setenv bootargs 'liminix mtdparts=phram0:M(rootfs) phram.phram=phram0,0x40411f28,4194304,65536 memmap=4194304$0x40411f28 root=/dev/mtdblock0 console=ttyAMA0,115200 earlycon'
|
||||
|
||||
setenv serverip 10.0.0.1
|
||||
setenv ipaddr 10.0.0.5
|
||||
setenv bootargs 'liminix console=ttyS0,115200 panic=10 oops=panic earlycon=uart8250,mmio32,0x11002000 root=/dev/mtdblock0'
|
||||
tftp 0x4007ff28 result/uimage ; tftp 0x40432f28 result/rootfs
|
||||
bootm 0x4007ff28
|
||||
|
||||
setenv bootargs 'liminix console=ttyS0,115200 panic=1 oops=panic earlycon root=/dev/mtdblock0'
|
||||
|
||||
md 0x42ff0000
|
||||
|
||||
Cannot find regmap for /infracfg@10000000: -524.
|
||||
|
||||
# "console=ttyAMA0,38400 panic=10 oops=panic init=/bin/init loglevel=8 root=/dev/mtdblock0 rootfstype=squashfs fw_devlink=off"
|
||||
|
||||
|
||||
40812468: 69726553 203a6c61 41424d41 304c5020 Serial: AMBA PL0
|
||||
40812478: 55203131 20545241 76697264 3c0a7265 11 UART driver.<
|
||||
40812488: 61723e33 706f6f6d 61203a73 6165726c 3>ramoops: alrea
|
||||
40812498: 69207964 6974696e 7a696c61 3c0a6465 dy initialized.<
|
||||
408124a8: 61723e34 706f6f6d 70203a73 65626f72 4>ramoops: probe
|
||||
408124b8: 20666f20 66663234 30303030 6d61722e of 42ff0000.ram
|
||||
408124c8: 73706f6f 69616620 2064656c 68746977 oops failed with
|
||||
408124d8: 72726520 2d20726f 3c0a3232 61433e33 error -22.<3>Ca
|
||||
408124e8: 746f6e6e 6e696620 65722064 70616d67 nnot find regmap
|
||||
408124f8: 726f6620 6e692f20 63617266 31406766 for /infracfg@1
|
||||
40812508: 30303030 3a303030 32352d20 333c0a34 0000000: -524.<3
|
||||
40812518: 6e61433e 20746f6e 646e6966 67657220 >Cannot find reg
|
||||
MT7622>
|
||||
40812528: 2070616d 20726f66 666e692f 66636172 map for /infracf
|
||||
40812538: 30314067 30303030 203a3030 3432352d g@10000000: -524
|
||||
40812548: 3e333c0a 6e6e6143 6620746f 20646e69 .<3>Cannot find
|
||||
40812558: 6d676572 66207061 2f20726f 72666e69 regmap for /infr
|
||||
40812568: 67666361 30303140 30303030 2d203a30 acfg@10000000: -
|
||||
40812578: 0a343235 433e333c 6f6e6e61 69662074 524.<3>Cannot fi
|
||||
40812588: 7220646e 616d6765 6f662070 702f2072 nd regmap for /p
|
||||
40812598: 63697265 31406766 32303030 3a303030 ericfg@10002000:
|
||||
408125a8: 32352d20 313c0a34 616e553e 20656c62 -524.<1>Unable
|
||||
408125b8: 68206f74 6c646e61 656b2065 6c656e72 to handle kernel
|
||||
408125c8: 67617020 20676e69 75716572 20747365 paging request
|
||||
408125d8: 76207461 75747269 61206c61 65726464 at virtual addre
|
||||
408125e8: 66207373 66666666 66666666 66666666 ss fffffffffffff
|
||||
408125f8: 0a656666 4d3e313c 61206d65 74726f62 ffe.<1>Mem abort
|
||||
|
||||
|
||||
CONFIG_SERIAL_8250_FSL=y
|
||||
CONFIG_SERIAL_8250_MT6577=y
|
||||
CONFIG_SERIAL_8250_NR_UARTS=3
|
||||
CONFIG_SERIAL_8250_RUNTIME_UARTS=3
|
||||
CONFIG_SERIAL_DEV_BUS=y
|
||||
CONFIG_SERIAL_DEV_CTRL_TTYPORT=y
|
||||
CONFIG_SERIAL_MCTRL_GPIO=y
|
||||
CONFIG_SERIAL_OF_PLATFORM=y
|
||||
|
||||
Mon Oct 2 10:17:04 BST 2023
|
||||
|
||||
We have a bootable aarch64 kernel for the Belkin, but it does not
|
||||
understand the memmap= parameter we're using to protect the
|
||||
phram image from being used as general memory.
|
||||
|
||||
One option is to add a reserved-memory stanza in the device tree,
|
||||
using u-boot "fdt" command, but we don't know the fdt address in u-boot
|
||||
because it doesn't have any commands to parse the image and set
|
||||
variables pointing at the sub-components. (There's iminfo, but it's
|
||||
onyl human-readable)
|
||||
|
||||
Second option is to amend the dtb in the tftpboot module: this
|
||||
would mean regenerating the uimage
|
||||
|
||||
Third option: for tftpboot do we _have_ to use FIT? maybe we could
|
||||
grab the fdt as a separate tftp transaction
|
||||
|
||||
we need to apply kernel patch 9401911f2d9f89035f7acebab16e72d43d1282fb
|
||||
to avoid using ioremap on sysem ram which is not allowed on arm
|
||||
|
||||
|
||||
Tue Oct 3 14:15:38 BST 2023
|
||||
|
||||
Progress on Liminix ARM support. The device I'm starting with is
|
||||
the Belkin RT3200 (also known as [Linksys E8450](https://openwrt.org/toh/linksys/e8450)) which seems to be a featureful piece of kit, and whioch I snagged for a
|
||||
very good price on the Bay of E
|
||||
|
||||
# Where are we right now?
|
||||
|
||||
* we can TFTP boot it to userland
|
||||
* ethernet works
|
||||
|
||||
## What else needs doing?
|
||||
|
||||
* it has dual band wifi with many interesting features. I've built the
|
||||
|
||||
* we're only running in RAM, probably need to add some kernel config
|
||||
to support the flash
|
||||
* initramfs support is not yet implemented
|
||||
* the flash is NAND flash and it's quite large compared with the
|
||||
existing Liminix devices, so we're going to add UBIFS which will
|
||||
use it better than JFFS2 does
|
||||
* all this work is on a branch and needs to be cleaned up a lot before
|
||||
I'm letting it into main
|
||||
|
||||
## What have we found?
|
||||
|
||||
There are some significant differences between this and the MIPS
|
||||
devices (yes, other than an entirely different architecture), mostly
|
||||
to do with "legacy boot" support or the lack thereof. For example:
|
||||
|
||||
* there aren't any options like `MIPS_RAW_APPENDED_DTB` to glom
|
||||
together a kernel and device tree (FDT), because the bootloader is
|
||||
expected to be able to provide a FDT following "standards".
|
||||
U-Boot will do this, provided that we use the newer "FIT" Uimage
|
||||
format which allows a kernel and DTB and initrd to be combined in
|
||||
the same container. (Sadly we can't use FIT everywhere because a
|
||||
lot of MIPS devices use really old forks of U-Boot that don't
|
||||
understand it)
|
||||
|
||||
* for tftpboot, on MIPS we use the `memmap` kernel command line option
|
||||
to reserve some RAM for the root filesystem. On Arm there's no such
|
||||
option, so we have to add a
|
||||
[reserved-memory](https://github.com/torvalds/linux/blob/master/Documentation/devicetree/bindings/reserved-memory/reserved-memory.yaml)
|
||||
node in the device tree instead. Which means, given that we _only_
|
||||
want to do this when tftp booting (the memory is wasted
|
||||
otherwise), we have to rewrite the device tree in that
|
||||
scenario.
|
||||
|
||||
* then it turns out that phram doesn't (didn't) work anyway, because
|
||||
it calls ioremap() and [you can't use ioremap on system memory on ARM](https://lwn.net/Articles/409689/). In newer kernels this is
|
||||
fixed: there is a [conditional](https://github.com/torvalds/linux/blob/master/drivers/mtd/devices/phram.c#L127) here to use whichever of ioremap or
|
||||
memremap is appropriate for the memory passed to phram, but it looks
|
||||
non-trivial to backport so I've gone for a [much less sophisticated approach](https://gti.telent.net/dan/liminix/commit/f7cd9c2b6e6c99a228e066b09e3febcf71c63fa1#diff-d8e355f1b2dcde7378ebb40c92cdd2ce3125753c)
|
||||
|
||||
* we're using [DSA](https://www.kernel.org/doc/Documentation/networking/dsa/dsa.txt) instead of the OpenWrt [swconfig](https://openwrt.org/docs/techref/swconfig) program. There was actually surprisingly little work needed to adjust to this.
|
||||
|
||||
|
||||
Other than that, it was mostly the usual process of "did the kernel
|
||||
crash silently, or has it just been unable to open a console device?".
|
||||
In this regard, *one neat trick*: even though U-Boot on this device
|
||||
doesn't support pstore, we can use it anyway if we don't do compression.
|
||||
Enable
|
||||
|
||||
```
|
||||
PSTORE = "y";
|
||||
PSTORE_RAM = "y";
|
||||
PSTORE_CONSOLE = "y";
|
||||
PSTORE_DEFLATE_COMPRESS = "n";
|
||||
```
|
||||
|
||||
then boot with `panic=3 oops=panic`, then when it resets use the
|
||||
U_Boot `md` command to see what happened:
|
||||
|
||||
```
|
||||
MT7622> md 0x42ff0000
|
||||
42ff0000: 43474244 00000000 00000ff4 3d3d3d3d DBGC........====
|
||||
42ff0010: 39342e31 36373031 500a442d 63696e61 1.491076-D.Panic
|
||||
42ff0020: 50203123 31747261 3e363c0a 20505050 #1 Part1.<6>PPP
|
||||
42ff0030: 20445342 706d6f43 73736572 206e6f69 BSD Compression
|
||||
[....]
|
||||
42ff0ca0: 20676e69 73756e75 6b206465 656e7265 ing unused kerne
|
||||
42ff0cb0: 656d206c 79726f6d 3731203a 0a4b3832 l memory: 1728K.
|
||||
42ff0cc0: 523e363c 2f206e75 74696e69 20736120 <6>Run /init as
|
||||
42ff0cd0: 74696e69 6f727020 73736563 3e373c0a init process.<7>
|
||||
```
|
||||
|
||||
Wed Oct 4 21:08:44 BST 2023
|
||||
|
||||
By randomly including chinks of the openwrt config we have made it
|
||||
find the mt7915e on the pcie bus. I just don't yet know which bit of
|
||||
the openwrt config it was.
|
||||
|
||||
It doesn't actually work yet though. 5GHz wifi gets calibration data
|
||||
from the flash, so it is not going to work unless (1) we reflash the
|
||||
firmware partition, or (2) we find another way to provide it
|
||||
calibration data.
|
||||
|
||||
https://forum.openwrt.org/t/belkin-rt3200-linksys-e8450-wifi-ax-discussion/94302/401
|
||||
|
||||
Sat Oct 7 22:56:40 BST 2023
|
||||
|
||||
We're almost ready to merge the rt3200 support (it's not finished
|
||||
but it mostly won't break mips) except for the uimage module
|
||||
which needs all that FIT stuff, and the tftpboot contortions
|
||||
to amend the dtb for memmap
|
||||
|
||||
For legacy uimage
|
||||
|
||||
1) add commandline params to dtb
|
||||
2) objcopy fdt into vmlinux.elf
|
||||
3) strip to raw image and compress
|
||||
4) mkimage
|
||||
|
||||
|
||||
For FIT uimage
|
||||
|
||||
1) add commandline params to dtb
|
||||
2) strip to raw image and compress
|
||||
3) create its file
|
||||
4) mkimage
|
||||
|
||||
Do we still want to handle the no-dtb case? what about
|
||||
standards-compliant boot, where u-boot is providing the dtb? No option
|
||||
to provide a commandline in that case, but maybe also no need to.
|
||||
|
||||
For tftpboot, am undecided. We could use the dtb rewriting thing
|
||||
everywhere, in the interest of consistency.
|
||||
|
||||
@@ -8,17 +8,21 @@ let
|
||||
pkgs = (import nixpkgs {});
|
||||
borderVmConf = ./bordervm.conf-example.nix;
|
||||
inherit (pkgs.lib.attrsets) genAttrs;
|
||||
devices = [ "qemu" "gl-ar750" "gl-mt300n-v2" "gl-mt300a" ];
|
||||
devices = {
|
||||
virt = [ "qemu" "qemu-aarch64" ];
|
||||
hw = [ "gl-ar750" "gl-mt300n-v2" "gl-mt300a" ];
|
||||
};
|
||||
vanilla = ./vanilla-configuration.nix;
|
||||
for-device = name:
|
||||
for-device = cfg: name:
|
||||
(import liminix {
|
||||
inherit nixpkgs borderVmConf;
|
||||
device = import (liminix + "/devices/${name}");
|
||||
liminix-config = vanilla;
|
||||
liminix-config = cfg;
|
||||
}).outputs.default;
|
||||
tests = import ./tests/ci.nix;
|
||||
jobs =
|
||||
(genAttrs devices (name: for-device name)) //
|
||||
(genAttrs devices.hw (name: for-device ./vanilla-configuration-hw.nix name)) //
|
||||
(genAttrs devices.virt (name: for-device vanilla name)) //
|
||||
tests //
|
||||
{
|
||||
buildEnv = (import liminix {
|
||||
@@ -36,6 +40,7 @@ let
|
||||
buildPhase = ''
|
||||
cat ${(import ./doc/extract-options.nix).doc} > options.json
|
||||
cat options.json | fennel --correlate parse-options.fnl > modules-generated.rst
|
||||
cp ${(import ./doc/hardware.nix)} hardware.rst
|
||||
make html
|
||||
'';
|
||||
installPhase = ''
|
||||
|
||||
+1
-1
@@ -54,7 +54,7 @@ in {
|
||||
tufted
|
||||
routeros.routeros
|
||||
routeros.ros-exec-script
|
||||
mips-vm
|
||||
run-liminix-vm
|
||||
borderVm.build.vm
|
||||
go-l2tp
|
||||
min-copy-closure
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
{
|
||||
description = ''
|
||||
Belkin RT-3200 / Linksys E8450
|
||||
******************************
|
||||
|
||||
This device is based on a 64 bit Mediatek MT7622 ARM platform,
|
||||
and is "work in progress" in Liminix.
|
||||
|
||||
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 "tftpboot",
|
||||
if you want the wireless to work).
|
||||
|
||||
- MediaTek MT7622BV (1350MHz)
|
||||
- 128MB NAND flash
|
||||
- 512MB RAM
|
||||
- b/g/n wireless using MediaTek MT7622BV (MT7615E driver)
|
||||
- a/n/ac/ax wireless using MediaTek MT7915E
|
||||
'';
|
||||
|
||||
system = {
|
||||
crossSystem = {
|
||||
config = "aarch64-unknown-linux-musl";
|
||||
};
|
||||
};
|
||||
|
||||
module = {pkgs, config, lib, ... }:
|
||||
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 ];
|
||||
kernel = {
|
||||
src = pkgs.pkgsBuildBuild.fetchurl {
|
||||
name = "linux.tar.gz";
|
||||
url = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.71.tar.gz";
|
||||
hash = "sha256-yhO2cXIeIgUxkSZf/4aAsF11uxyh+UUZu6D1h92vCD8=";
|
||||
};
|
||||
extraPatchPhase = ''
|
||||
${pkgs.openwrt.applyPatches.mediatek}
|
||||
'';
|
||||
config = {
|
||||
PCI = "y";
|
||||
ARCH_MEDIATEK = "y";
|
||||
# ARM_MEDIATEK_CPUFREQ = "y";
|
||||
|
||||
# needed for "Cannot find regmap for /infracfg@10000000"
|
||||
MFD_SYSCON = "y";
|
||||
MTK_INFRACFG = "y";
|
||||
|
||||
MTK_PMIC_WRAP = "y";
|
||||
MTK_EFUSE="y";
|
||||
# MTK_HSDMA="y";
|
||||
MTK_SCPSYS="y";
|
||||
MTK_SCPSYS_PM_DOMAINS="y";
|
||||
# MTK_THERMAL="y";
|
||||
MTK_TIMER="y";
|
||||
|
||||
COMMON_CLK_MT7622 = "y";
|
||||
COMMON_CLK_MT7622_ETHSYS = "y";
|
||||
COMMON_CLK_MT7622_HIFSYS = "y";
|
||||
COMMON_CLK_MT7622_AUDSYS = "y";
|
||||
PM_CLK="y";
|
||||
|
||||
REGMAP_MMIO = "y";
|
||||
CLKSRC_MMIO = "y";
|
||||
REGMAP = "y";
|
||||
|
||||
MEDIATEK_GE_PHY = "y";
|
||||
# MEDIATEK_MT6577_AUXADC = "y";
|
||||
# MEDIATEK_WATCHDOG = "y";
|
||||
NET_MEDIATEK_SOC = "y";
|
||||
NET_MEDIATEK_SOC_WED = "y";
|
||||
NET_MEDIATEK_STAR_EMAC = "y"; # this enables REGMAP_MMIO
|
||||
NET_VENDOR_MEDIATEK = "y";
|
||||
PCIE_MEDIATEK = "y";
|
||||
|
||||
BLOCK = "y"; # move this to base option
|
||||
|
||||
SPI_MASTER = "y";
|
||||
SPI = "y";
|
||||
SPI_MEM="y";
|
||||
SPI_MTK_NOR="y";
|
||||
SPI_MTK_SNFI = "y";
|
||||
|
||||
MTD = "y";
|
||||
MTD_BLOCK = "y";
|
||||
MTD_RAW_NAND = "y";
|
||||
MTD_NAND_MTK = "y";
|
||||
MTD_NAND_MTK_BMT = "y"; # Bad-block Management Table
|
||||
MTD_NAND_ECC_MEDIATEK= "y";
|
||||
MTD_NAND_ECC_SW_HAMMING= "y";
|
||||
MTD_SPI_NAND= "y";
|
||||
MTD_OF_PARTS = "y";
|
||||
MTD_NAND_CORE= "y";
|
||||
MTD_SPI_NOR= "y";
|
||||
MTD_SPLIT_FIRMWARE= "y";
|
||||
MTD_SPLIT_FIT_FW= "y";
|
||||
|
||||
|
||||
MMC = "y";
|
||||
MMC_BLOCK = "y";
|
||||
MMC_CQHCI = "y";
|
||||
MMC_MTK = "y";
|
||||
|
||||
# Distributed Switch Architecture is needed
|
||||
# to make the ethernet ports visible
|
||||
NET_DSA="y";
|
||||
NET_DSA_MT7530="y";
|
||||
NET_DSA_TAG_MTK="y";
|
||||
|
||||
PSTORE = "y";
|
||||
PSTORE_RAM = "y";
|
||||
PSTORE_CONSOLE = "y";
|
||||
PSTORE_DEFLATE_COMPRESS = "n";
|
||||
|
||||
SERIAL_8250 = "y";
|
||||
SERIAL_8250_CONSOLE = "y";
|
||||
SERIAL_8250_MT6577="y";
|
||||
# SERIAL_8250_NR_UARTS="3";
|
||||
# SERIAL_8250_RUNTIME_UARTS="3";
|
||||
SERIAL_OF_PLATFORM="y";
|
||||
};
|
||||
};
|
||||
boot = {
|
||||
commandLine = [ "console=ttyS0,115200" ];
|
||||
tftp.loadAddress = "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.mac80211.override {
|
||||
drivers = [
|
||||
"mt7615e"
|
||||
"mt7915e"
|
||||
];
|
||||
klibBuild = config.system.outputs.kernel.modulesupport;
|
||||
};
|
||||
in {
|
||||
ubi = {
|
||||
minIOSize = "2048";
|
||||
eraseBlockSize = "126976";
|
||||
maxLEBcount = "1024"; # guessing
|
||||
};
|
||||
|
||||
defaultOutput = "flashimage";
|
||||
# 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 = "0x42000000";
|
||||
entryPoint = "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/"
|
||||
];
|
||||
};
|
||||
|
||||
flash.eraseBlockSize = "65536"; # this is probably wrong
|
||||
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 ];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
}
|
||||
@@ -18,13 +18,22 @@
|
||||
};
|
||||
|
||||
description = ''
|
||||
GL.INet GL-AR750 "Creta" travel router
|
||||
GL.iNet GL-AR750
|
||||
****************
|
||||
|
||||
The GL-AR750 "Creta" travel router features:
|
||||
|
||||
- QCA9531 @650Mhz SoC
|
||||
- dual band wireless: IEEE 802.11a/b/g/n/ac
|
||||
- two 10/100Mbps LAN ports and one WAN
|
||||
- 128MB DDR2 RAM / 16MB NOR Flash
|
||||
- "ath79" soc family
|
||||
https://www.gl-inet.com/products/gl-ar750/
|
||||
- 128MB DDR2 RAM
|
||||
- 16MB NOR Flash
|
||||
- supported in OpenWrt by the "ath79" SoC family
|
||||
|
||||
As with many GL.iNet devices, the stock vendor firmware
|
||||
is a fork of OpenWrt, meaning that the plain binary
|
||||
``firmware.bin`` that Liminix builds can be flashed using the
|
||||
vendor web UI and the U-Boot emergency "unbrick" routine
|
||||
|
||||
The GL-AR750 has two distinct sets of wifi hardware. The 2.4GHz
|
||||
radio is part of the QCA9531 SoC, i.e. it's on the same silicon as
|
||||
@@ -34,8 +43,13 @@
|
||||
other hand, is provided by a QCA9887 PCIe (PCI embedded) WLAN chip:
|
||||
I haven't looked closely at the router innards to see if this is
|
||||
actually physically a separate board that could be unplugged, but
|
||||
as far as the Linux is concerned it behaves as one. This is
|
||||
as far as Linux is concerned it behaves as one. This is
|
||||
supported by the ath10k driver.
|
||||
|
||||
Vendor web page: https://www.gl-inet.com/products/gl-ar750/
|
||||
|
||||
OpenWrt web page: https://openwrt.org/toh/gl.inet/gl-ar750
|
||||
|
||||
'';
|
||||
|
||||
module = {pkgs, config, ... }:
|
||||
@@ -79,7 +93,10 @@
|
||||
inherit (pkgs.pseudofile) dir symlink;
|
||||
inherit (pkgs.liminix.networking) interface;
|
||||
in {
|
||||
imports = [ ../../modules/network];
|
||||
imports = [
|
||||
../../modules/network
|
||||
../../modules/arch/mipseb.nix
|
||||
];
|
||||
|
||||
programs.busybox.options = {
|
||||
FEATURE_DD_IBS_OBS = "y"; # ath10k_cal_data needs skip_bytes,fullblock
|
||||
@@ -106,11 +123,11 @@
|
||||
in {
|
||||
lan = link.build { ifname = "eth0"; };
|
||||
wan = link.build { ifname = "eth1"; };
|
||||
wlan_24 = link.build {
|
||||
wlan = link.build {
|
||||
ifname = "wlan0";
|
||||
dependencies = [ mac80211 ];
|
||||
};
|
||||
wlan_5 = link.build {
|
||||
wlan5 = link.build {
|
||||
ifname = "wlan1";
|
||||
dependencies = [ mac80211 ath10k_cal_data ];
|
||||
};
|
||||
@@ -139,9 +156,6 @@
|
||||
${openwrt.applyPatches.ath79}
|
||||
'';
|
||||
config = {
|
||||
MIPS_ELF_APPENDED_DTB = "y";
|
||||
OF = "y";
|
||||
USE_OF = "y";
|
||||
ATH79 = "y";
|
||||
PCI = "y";
|
||||
PCI_AR724X = "y";
|
||||
@@ -161,7 +175,6 @@
|
||||
CONSOLE_LOGLEVEL_QUIET = "4";
|
||||
|
||||
NET = "y";
|
||||
NETDEVICES = "y";
|
||||
ETHERNET = "y";
|
||||
NET_VENDOR_ATHEROS = "y";
|
||||
AG71XX = "y"; # ethernet (qca,qca9530-eth)
|
||||
@@ -169,7 +182,6 @@
|
||||
AR8216_PHY = "y"; # eth1 is behind a switch
|
||||
|
||||
MTD_SPI_NOR = "y";
|
||||
MTD_SPI_NOR_USE_4K_SECTORS = "n"; # jffs2 needs min 8k erase block
|
||||
|
||||
SPI_ATH79 = "y"; # these are copied from OpenWrt.
|
||||
SPI_MASTER= "y"; # At least one of them is necessary
|
||||
@@ -186,22 +198,17 @@
|
||||
SYSFS = "y";
|
||||
SPI = "y";
|
||||
MTD = "y";
|
||||
MTD_CMDLINE_PARTS = "y";
|
||||
MTD_BLOCK = "y"; # fix undefined ref to register_mtd_blktrans_devs
|
||||
|
||||
WATCHDOG = "y";
|
||||
ATH79_WDT = "y"; # watchdog timer
|
||||
|
||||
CPU_BIG_ENDIAN= "y";
|
||||
|
||||
# this is all copied from nixwrt ath79 config. Clearly not all
|
||||
# of it is device config, some of it is wifi config or
|
||||
# installation method config or ...
|
||||
|
||||
CMDLINE_PARTITION = "y";
|
||||
EARLY_PRINTK = "y";
|
||||
|
||||
PARTITION_ADVANCED = "y";
|
||||
PRINTK_TIME = "y";
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# GL.INet GL-MT300A
|
||||
# GL.iNet GL-MT300A
|
||||
|
||||
{
|
||||
system = {
|
||||
@@ -12,10 +12,47 @@
|
||||
};
|
||||
|
||||
description = ''
|
||||
GL.iNet GL-MT300A
|
||||
********************
|
||||
|
||||
The GL-MT300A is based on a MT7620 chipset.
|
||||
|
||||
The GL.iNet pocket router range makes nice cheap hardware for
|
||||
playing with Liminix or similar projects. The manufacturers seem
|
||||
open to the DIY market, and the devices have a reasonable amount
|
||||
of RAM and are much easier to get serial connections than many
|
||||
COTS routers.
|
||||
|
||||
Wire up the serial connection: this probably involves opening
|
||||
the box, locating the serial header pins (TX, RX and GND) and
|
||||
connecting a USB TTL converter - e.g. a PL2303 based device - to
|
||||
it. The defunct OpenWRT wiki has a guide with some pictures. (If
|
||||
you don't have a USB TTL converter to hand, other options are
|
||||
available. For example, use the GPIO pins on a Raspberry Pi.)
|
||||
|
||||
Run a terminal emulator such as Minicom on whatever is on the
|
||||
other end of the link. I use 115200 8N1 and find it also helps
|
||||
to set "Line tx delay" to 1ms, "backspace sends DEL" and
|
||||
"lineWrap on".
|
||||
|
||||
When you turn the router on you should be greeted with some
|
||||
messages from U-Boot and a little bit of ASCII art, followed by
|
||||
the instruction to hit SPACE to stop autoboot. Do this and you
|
||||
will get a gl-mt300a> prompt.
|
||||
|
||||
For flashing from uboot, the firmware partition is from
|
||||
0xbc050000 to 0xbcfd0000.
|
||||
|
||||
WiFi on this device is provided by the rt2800soc module. It
|
||||
expects firmware to be present in the "factory" MTD partition, so
|
||||
- assuming we want to use the wireless - we need to build MTD
|
||||
support into the kernel even if we're using TFTP root
|
||||
support into the kernel even if we're using TFTP root.
|
||||
|
||||
|
||||
Vendor web page: https://www.gl-inet.com/products/gl-mt300a/
|
||||
|
||||
OpenWrt web page: https://openwrt.org/toh/gl.inet/gl-mt300a
|
||||
|
||||
'';
|
||||
|
||||
module = { pkgs, config, lib, ...}:
|
||||
@@ -26,7 +63,8 @@
|
||||
drivers = ["rt2800soc"];
|
||||
klibBuild = config.system.outputs.kernel.modulesupport;
|
||||
};
|
||||
in {
|
||||
in {
|
||||
imports = [ ../../modules/arch/mipsel.nix ];
|
||||
hardware = {
|
||||
defaultOutput = "flashimage";
|
||||
loadAddress = "0x80000000";
|
||||
@@ -107,9 +145,6 @@
|
||||
${openwrt.applyPatches.ramips}
|
||||
'';
|
||||
config = {
|
||||
MIPS_ELF_APPENDED_DTB = "y";
|
||||
OF = "y";
|
||||
USE_OF = "y";
|
||||
|
||||
RALINK = "y";
|
||||
PCI = "y";
|
||||
@@ -124,7 +159,6 @@
|
||||
CONSOLE_LOGLEVEL_QUIET = "4";
|
||||
|
||||
NET = "y";
|
||||
NETDEVICES = "y";
|
||||
ETHERNET = "y";
|
||||
NET_VENDOR_RALINK = "y";
|
||||
NET_RALINK_MDIO = "y";
|
||||
@@ -140,18 +174,13 @@
|
||||
SPI_MEM= "y";
|
||||
|
||||
MTD = "y";
|
||||
MTD_CMDLINE_PARTS = "y";
|
||||
MTD_BLOCK = "y"; # fix undefined ref to register_mtd_blktrans_devs
|
||||
|
||||
CPU_LITTLE_ENDIAN = "y";
|
||||
|
||||
CMDLINE_PARTITION = "y";
|
||||
EARLY_PRINTK = "y";
|
||||
|
||||
NEW_LEDS = "y";
|
||||
LEDS_CLASS = "y"; # required by rt2x00lib
|
||||
|
||||
PARTITION_ADVANCED = "y";
|
||||
PRINTK_TIME = "y";
|
||||
} // lib.optionalAttrs (config.system.service ? vlan) {
|
||||
SWCONFIG = "y";
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
# GL.INet GL-MT300N v2
|
||||
|
||||
{
|
||||
system = {
|
||||
crossSystem = {
|
||||
@@ -11,6 +9,21 @@
|
||||
};
|
||||
};
|
||||
|
||||
description = ''
|
||||
GL.iNet GL-MT300N-v2
|
||||
********************
|
||||
|
||||
The GL-MT300N-v2 "Mango" is is very similar to the MT300A, but is
|
||||
based on MT7628 instead of MT7620. It's also marginally cheaper
|
||||
and comes in a yellow case not a blue one. It's different again
|
||||
to the v1, which has only half the RAM.
|
||||
|
||||
Vendor web page: https://www.gl-inet.com/products/gl-mt300n-v2/
|
||||
|
||||
OpenWrt web page: https://openwrt.org/toh/gl.inet/gl-mt300n_v2
|
||||
|
||||
'';
|
||||
|
||||
module = { pkgs, config, lib, ...}:
|
||||
let
|
||||
inherit (pkgs.liminix.networking) interface;
|
||||
@@ -27,6 +40,7 @@
|
||||
hash = "sha256:1dkhfznmdz6s50kwc841x3wj0h6zg6icg5g2bim9pvg66as2vmh9";
|
||||
};
|
||||
in {
|
||||
imports = [ ../../modules/arch/mipsel.nix ];
|
||||
filesystem = dir {
|
||||
lib = dir {
|
||||
firmware = dir {
|
||||
@@ -103,14 +117,10 @@
|
||||
${openwrt.applyPatches.ramips}
|
||||
'';
|
||||
config = {
|
||||
MIPS_ELF_APPENDED_DTB = "y";
|
||||
OF = "y";
|
||||
USE_OF = "y";
|
||||
|
||||
RALINK = "y";
|
||||
PCI = "y";
|
||||
SOC_MT7620 = "y";
|
||||
CPU_LITTLE_ENDIAN= "y";
|
||||
|
||||
SERIAL_8250_CONSOLE = "y";
|
||||
SERIAL_8250 = "y";
|
||||
@@ -121,7 +131,6 @@
|
||||
CONSOLE_LOGLEVEL_QUIET = "4";
|
||||
|
||||
MTD = "y";
|
||||
MTD_CMDLINE_PARTS = "y";
|
||||
MTD_BLOCK = "y"; # fix undefined ref to register_mtd_blktrans_dev
|
||||
|
||||
SPI = "y";
|
||||
@@ -134,7 +143,6 @@
|
||||
REGULATOR_FIXED_VOLTAGE = "y";
|
||||
|
||||
NET = "y";
|
||||
NETDEVICES = "y";
|
||||
ETHERNET = "y";
|
||||
|
||||
PHYLIB = "y";
|
||||
@@ -151,10 +159,8 @@
|
||||
|
||||
PHY_RALINK_USB = "y";
|
||||
|
||||
CMDLINE_PARTITION = "y";
|
||||
EARLY_PRINTK = "y";
|
||||
|
||||
PARTITION_ADVANCED = "y";
|
||||
PRINTK_TIME = "y";
|
||||
} // lib.optionalAttrs (config.system.service ? vlan) {
|
||||
SWCONFIG = "y";
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
# This "device" generates images that can be used with the QEMU
|
||||
# emulator. The default output is a directory containing separate
|
||||
# kernel ("Image" format) and root filesystem (squashfs or jffs2)
|
||||
# images
|
||||
{
|
||||
system = {
|
||||
crossSystem = {
|
||||
config = "aarch64-unknown-linux-musl";
|
||||
};
|
||||
};
|
||||
|
||||
# this device is described by the "qemu" device
|
||||
description = "";
|
||||
|
||||
|
||||
module = {pkgs, config, ... }: {
|
||||
imports = [ ../../modules/arch/aarch64.nix ];
|
||||
kernel = {
|
||||
src = pkgs.pkgsBuildBuild.fetchurl {
|
||||
name = "linux.tar.gz";
|
||||
url = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.71.tar.gz";
|
||||
hash = "sha256-yhO2cXIeIgUxkSZf/4aAsF11uxyh+UUZu6D1h92vCD8=";
|
||||
};
|
||||
config = {
|
||||
VIRTUALIZATION = "y";
|
||||
PCI_HOST_GENERIC="y";
|
||||
|
||||
MTD = "y";
|
||||
MTD_BLOCK2MTD = "y";
|
||||
MTD_BLOCK = "y";
|
||||
|
||||
VIRTIO_MENU = "y";
|
||||
PCI = "y";
|
||||
VIRTIO_PCI = "y";
|
||||
BLOCK = "y";
|
||||
VIRTIO_BLK = "y";
|
||||
VIRTIO_NET = "y";
|
||||
|
||||
SERIAL_EARLYCON_ARM_SEMIHOST = "y"; # earlycon=smh
|
||||
SERIAL_AMBA_PL011 = "y";
|
||||
SERIAL_AMBA_PL011_CONSOLE = "y";
|
||||
};
|
||||
};
|
||||
boot.commandLine = [
|
||||
"console=ttyAMA0,38400"
|
||||
];
|
||||
hardware =
|
||||
let
|
||||
mac80211 = pkgs.mac80211.override {
|
||||
drivers = ["mac80211_hwsim"];
|
||||
klibBuild = config.system.outputs.kernel.modulesupport;
|
||||
};
|
||||
in {
|
||||
defaultOutput = "vmroot";
|
||||
loadAddress = "0x0";
|
||||
entryPoint = "0x0";
|
||||
rootDevice = "/dev/mtdblock0";
|
||||
|
||||
flash.eraseBlockSize = "65536"; # c.f. pkgs/mips-vm/mips-vm.sh
|
||||
networkInterfaces =
|
||||
let inherit (config.system.service.network) link;
|
||||
in {
|
||||
wan = link.build { ifname = "eth0"; };
|
||||
lan = link.build { ifname = "eth1"; };
|
||||
|
||||
wlan_24 = link.build {
|
||||
ifname = "wlan0";
|
||||
dependencies = [ mac80211 ];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
}
|
||||
@@ -12,7 +12,35 @@
|
||||
};
|
||||
};
|
||||
|
||||
description = ''
|
||||
QEMU
|
||||
****
|
||||
|
||||
This is not a hardware device. This target produces an image for
|
||||
QEMU, the "generic and open source machine emulator and
|
||||
virtualizer".
|
||||
|
||||
Liminix can build QEMU for both MIPS (:code:`qemu` device) and Aarch64 (:code:`qemu-aarch64` device)
|
||||
|
||||
MIPS QEMU emulates a "Malta" board, which was an ATX form factor
|
||||
evaluation board made by MIPS Technologies, but mostly in Liminix
|
||||
we use paravirtualized devices (Virtio) instead of emulating
|
||||
hardware. For Aarch64 we use the QEMU "virt" board.
|
||||
|
||||
Building an image for QEMU results in a :file:`result/` directory
|
||||
containing ``run.sh`` ``vmlinux``, ``rootfs`` and possibly
|
||||
(architecture-dependent) ``Image``. To invoke the emulator,
|
||||
run ``run.sh``.
|
||||
|
||||
The configuration includes two emulated "hardware" ethernet
|
||||
devices and the kernel :code:`mac80211_hwsim` module to
|
||||
provide an emulated wlan device. To read more about how
|
||||
to connect to this network, refer to :ref:`qemu-networking`
|
||||
in the Development manual.
|
||||
|
||||
'';
|
||||
module = {pkgs, config, ... }: {
|
||||
imports = [ ../../modules/arch/mipseb.nix ];
|
||||
kernel = {
|
||||
src = pkgs.pkgsBuildBuild.fetchurl {
|
||||
name = "linux.tar.gz";
|
||||
@@ -21,13 +49,10 @@
|
||||
};
|
||||
config = {
|
||||
MIPS_MALTA= "y";
|
||||
CPU_LITTLE_ENDIAN= "n";
|
||||
CPU_BIG_ENDIAN= "y";
|
||||
CPU_MIPS32_R2= "y";
|
||||
|
||||
MTD = "y";
|
||||
MTD_BLOCK2MTD = "y";
|
||||
MTD_BLKDEVS = "y";
|
||||
MTD_BLOCK = "y";
|
||||
|
||||
VIRTIO_MENU = "y";
|
||||
@@ -35,7 +60,6 @@
|
||||
VIRTIO_PCI = "y";
|
||||
BLOCK = "y";
|
||||
VIRTIO_BLK = "y";
|
||||
NETDEVICES = "y";
|
||||
VIRTIO_NET = "y";
|
||||
|
||||
SERIAL_8250= "y";
|
||||
@@ -50,7 +74,8 @@
|
||||
};
|
||||
in {
|
||||
defaultOutput = "vmroot";
|
||||
flash.eraseBlockSize = "65536"; # c.f. pkgs/mips-vm/mips-vm.sh
|
||||
rootDevice = "/dev/mtdblock0";
|
||||
flash.eraseBlockSize = "65536"; # c.f. pkgs/run-liminix-vm/run-liminix-vm.sh
|
||||
networkInterfaces =
|
||||
let inherit (config.system.service.network) link;
|
||||
in {
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
{
|
||||
description = ''
|
||||
Turris Omnia
|
||||
************
|
||||
'';
|
||||
|
||||
system = {
|
||||
crossSystem = {
|
||||
config = "armv7l-unknown-linux-musleabihf";
|
||||
};
|
||||
};
|
||||
|
||||
module = {pkgs, config, lib, ... }:
|
||||
let openwrt = pkgs.openwrt; in {
|
||||
imports = [ ../../modules/arch/arm.nix ];
|
||||
kernel = {
|
||||
src = pkgs.pkgsBuildBuild.fetchurl {
|
||||
name = "linux.tar.gz";
|
||||
url = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.71.tar.gz";
|
||||
hash = "sha256-yhO2cXIeIgUxkSZf/4aAsF11uxyh+UUZu6D1h92vCD8=";
|
||||
};
|
||||
extraPatchPhase = ''
|
||||
${pkgs.openwrt.applyPatches.mvebu}
|
||||
'';
|
||||
config = {
|
||||
PCI = "y";
|
||||
OF = "y";
|
||||
MEMORY = "y"; # for MVEBU_DEVBUS
|
||||
DMADEVICES = "y"; # for MV_XOR
|
||||
CPU_V7 = "y";
|
||||
ARCH_MULTIPLATFORM = "y";
|
||||
ARCH_MVEBU = "y";
|
||||
ARCH_MULTI_V7= "y";
|
||||
PCI_MVEBU = "y";
|
||||
AHCI_MVEBU = "y";
|
||||
MACH_ARMADA_38X = "y";
|
||||
SMP = "y";
|
||||
NR_CPUS = "4";
|
||||
VFP = "y";
|
||||
NEON= "y";
|
||||
|
||||
# WARNING: unmet direct dependencies detected for ARCH_WANT_LIBATA_LEDS
|
||||
ATA = "y";
|
||||
|
||||
# switch is DSA
|
||||
# CONFIG_NET_DSA_MV88E6060=y
|
||||
# CONFIG_NET_DSA_MV88E6XXX=y
|
||||
# CONFIG_NET_DSA_MV88E6XXX_GLOBAL2=y
|
||||
|
||||
# CONFIG_REGMAP=y
|
||||
# CONFIG_REGMAP_I2C=y
|
||||
# CONFIG_REGMAP_SPI=y
|
||||
# CONFIG_REGMAP_MMIO=y
|
||||
|
||||
PSTORE = "y";
|
||||
PSTORE_RAM = "y";
|
||||
PSTORE_CONSOLE = "y";
|
||||
PSTORE_DEFLATE_COMPRESS = "n";
|
||||
|
||||
SERIAL_8250 = "y";
|
||||
SERIAL_8250_CONSOLE = "y";
|
||||
SERIAL_OF_PLATFORM="y";
|
||||
SERIAL_MVEBU_UART = "y";
|
||||
SERIAL_MVEBU_CONSOLE = "y";
|
||||
|
||||
SERIAL_8250_DMA= "y";
|
||||
SERIAL_8250_DW= "y";
|
||||
SERIAL_8250_EXTENDED= "y";
|
||||
SERIAL_8250_MANY_PORTS= "y";
|
||||
SERIAL_8250_SHARE_IRQ= "y";
|
||||
OF_ADDRESS= "y";
|
||||
OF_MDIO= "y";
|
||||
|
||||
MVEBU_DEVBUS= "y"; # "Device Bus controller ... flash devices such as NOR, NAND, SRAM, and FPGA"
|
||||
MVMDIO= "y";
|
||||
MVNETA= "y";
|
||||
MVNETA_BM= "y";
|
||||
MVNETA_BM_ENABLE= "y";
|
||||
MVPP2= "y";
|
||||
MV_XOR= "y";
|
||||
};
|
||||
};
|
||||
boot = {
|
||||
commandLine = [ "console=ttyS0,115200" ];
|
||||
imageFormat = "fit";
|
||||
};
|
||||
|
||||
hardware = {
|
||||
defaultOutput = "flashimage";
|
||||
loadAddress = "0x00008000";
|
||||
entryPoint = "0x00008000";
|
||||
rootDevice = "/dev/mtdblock0";
|
||||
dts = {
|
||||
src = "${config.system.outputs.kernel.modulesupport}/arch/arm/boot/dts/armada-385-turris-omnia.dts";
|
||||
includes = [
|
||||
# "${openwrt.src}/target/linux/mediatek/dts"
|
||||
"${config.system.outputs.kernel.modulesupport}/arch/arm/boot/dts/"
|
||||
];
|
||||
};
|
||||
|
||||
networkInterfaces =
|
||||
let
|
||||
inherit (config.system.service.network) link;
|
||||
inherit (config.system.service) bridge;
|
||||
in rec {
|
||||
lan = link.build { ifname = "eth0"; };
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
}
|
||||
+3
-3
@@ -138,8 +138,8 @@ device using e.g. :command:`ssh`, you can run it as follows:
|
||||
mtd -e -r write /tmp/firmware.bin firmware
|
||||
|
||||
The options to this command are for "erase before writing" and "reboot
|
||||
after writing".
|
||||
|
||||
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)
|
||||
@@ -188,7 +188,7 @@ 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
|
||||
|
||||
+12
-1
@@ -13,7 +13,10 @@ author = 'Daniel Barlow'
|
||||
# -- General configuration ---------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
||||
|
||||
extensions = []
|
||||
extensions = [
|
||||
'sphinx.ext.autosectionlabel'
|
||||
]
|
||||
autosectionlabel_prefix_document = True
|
||||
|
||||
templates_path = ['_templates']
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
@@ -25,3 +28,11 @@ exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
|
||||
html_theme = 'alabaster'
|
||||
html_static_path = ['_static']
|
||||
|
||||
html_theme_options = {
|
||||
'logo': '/logo.svg',
|
||||
'globaltoc_collapse': 'false',
|
||||
'page_width': '90%',
|
||||
'body_max_width': '90%',
|
||||
'description': 'A Nix-based OpenWrt-style embedded Linux system for consumer wifi routers'
|
||||
}
|
||||
|
||||
+5
-3
@@ -27,13 +27,13 @@ To build it,
|
||||
|
||||
nix-build -I liminix-config=path/to/your/configuration.nix --arg device "import ./devices/qemu" -A outputs.default
|
||||
|
||||
In a ``buildEnv`` nix-shell, you can use the :command:`mips-vm` command
|
||||
In a ``buildEnv`` nix-shell, you can use the :command:`run-liminix-vm` command
|
||||
to run Qemu with appropriate options. It connects the Liminix
|
||||
serial console and the `QEMU monitor <https://www.qemu.org/docs/master/system/monitor.html>`_ to stdin/stdout. Use ^P (not ^A) to switch to the monitor.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
nix-shell --run "mips-vm result/vmlinux result/squashfs"
|
||||
nix-shell --run "run-liminix-vm result/vmlinux result/squashfs"
|
||||
|
||||
If you run with ``--background /path/to/some/directory`` as the first
|
||||
parameter, it will fork into the background and open Unix sockets in
|
||||
@@ -41,6 +41,8 @@ that directory for console and monitor. Use :command:`connect-vm`
|
||||
(also in the ``buildEnv`` environment) to connect to either of these
|
||||
sockets, and ^O to disconnect.
|
||||
|
||||
.. _qemu-networking:
|
||||
|
||||
Networking
|
||||
==========
|
||||
|
||||
@@ -53,7 +55,7 @@ the right way:
|
||||
* multicast 230.0.0.1:1235 : lan
|
||||
* multicast 230.0.0.1:1236 : world (the internet)
|
||||
|
||||
A VM started with :command:`mips-vm` is connected to "lan" and "access", and
|
||||
A VM started with :command:`run-liminix-vm` is connected to "lan" and "access", and
|
||||
the emulated border network gateway (see below) runs PPPoE and is
|
||||
connected to "access" and "world".
|
||||
|
||||
|
||||
@@ -15,8 +15,9 @@ let
|
||||
builtins.attrNames
|
||||
(lib.filterAttrsRecursive
|
||||
(n: t:
|
||||
(t=="directory") ||
|
||||
((t=="regular") && ((builtins.match ".*\\.nix$" n) != null)))
|
||||
(n != "arch") &&
|
||||
((t=="directory") ||
|
||||
((t=="regular") && ((builtins.match ".*\\.nix$" n) != null))))
|
||||
(builtins.readDir ../modules));
|
||||
modulefiles = builtins.map (n: builtins.toPath "${../modules}/${n}") modulenames;
|
||||
eval = (lib.evalModules {
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
with import <nixpkgs> {} ;
|
||||
|
||||
let
|
||||
devices =
|
||||
builtins.readDir ../devices;
|
||||
texts = lib.mapAttrsToList (n: t:
|
||||
let d = import ../devices/${n}/default.nix;
|
||||
d' = { description = "no description for ${n}"; } // d;
|
||||
in d'.description )
|
||||
devices;
|
||||
in
|
||||
writeText "hwdoc" ''
|
||||
Supported hardware
|
||||
##################
|
||||
|
||||
${lib.concatStringsSep "\n\n" texts}
|
||||
|
||||
''
|
||||
@@ -11,6 +11,7 @@ Liminix
|
||||
admin
|
||||
development
|
||||
modules
|
||||
hardware
|
||||
|
||||
|
||||
Indices and tables
|
||||
|
||||
+84
-10
@@ -60,7 +60,7 @@ Now you can try it:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
nix-shell --run "mips-vm ./result/vmlinux ./result/rootfs"
|
||||
./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
|
||||
@@ -123,9 +123,9 @@ 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
|
||||
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
|
||||
unbrick if necessary.
|
||||
|
||||
.. warning:: There is always a risk of rendering your device
|
||||
unbootable by flashing it with an image that doesn't
|
||||
@@ -227,13 +227,86 @@ example, but this time for real.
|
||||
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.
|
||||
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
|
||||
**************
|
||||
@@ -243,13 +316,14 @@ Final thoughts
|
||||
:file:`examples/rotuer,arhcive,extneder.nix` are based on some
|
||||
actual real hosts in my home network.
|
||||
|
||||
* These example images are not writable. Later we will explain how to
|
||||
generate an image that can be changed after installation, and
|
||||
even use :command:`liminix-rebuild` (analogous to :command:`nixos-rebuild`)
|
||||
to keep it up to date.
|
||||
|
||||
* 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
|
||||
|
||||
@@ -51,7 +51,6 @@ in rec {
|
||||
SCSI = "y";
|
||||
BLK_DEV_SD = "y";
|
||||
USB_PRINTER = "y";
|
||||
PARTITION_ADVANCED = "y";
|
||||
MSDOS_PARTITION = "y";
|
||||
EFI_PARTITION = "y";
|
||||
EXT4_FS = "y";
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
# This is an example configuration for a "typical" small office/home
|
||||
# router and wifi access point.
|
||||
|
||||
# You need to copy it to another filename and change the configuration
|
||||
# wherever the text "EDIT" appears - please consult the tutorial
|
||||
# documentation for details.
|
||||
|
||||
{ config, pkgs, lib, ... } :
|
||||
let
|
||||
inherit (pkgs.liminix.services) bundle oneshot longrun;
|
||||
inherit (pkgs) serviceFns;
|
||||
# EDIT: you can pick your preferred RFC1918 address space
|
||||
# for NATted connections, if you don't like this one.
|
||||
ipv4LocalNet = "10.8.0";
|
||||
svc = config.system.service;
|
||||
|
||||
in rec {
|
||||
boot = {
|
||||
tftp = {
|
||||
freeSpaceBytes = 3 * 1024 * 1024;
|
||||
serverip = "10.0.0.1";
|
||||
ipaddr = "10.0.0.8";
|
||||
};
|
||||
};
|
||||
|
||||
imports = [
|
||||
../modules/bridge
|
||||
../modules/dhcp6c
|
||||
../modules/dnsmasq
|
||||
../modules/firewall
|
||||
../modules/hostapd
|
||||
../modules/network
|
||||
../modules/ntp
|
||||
../modules/ppp
|
||||
../modules/ssh
|
||||
../modules/standard.nix
|
||||
../modules/vlan
|
||||
../modules/wlan.nix
|
||||
];
|
||||
rootfsType = "jffs2";
|
||||
hostname = "the-internet"; # EDIT
|
||||
|
||||
services.hostap = svc.hostapd.build {
|
||||
interface = config.hardware.networkInterfaces.wlan;
|
||||
# EDIT: you will want to change the obvious things
|
||||
# here to values of your choice
|
||||
params = {
|
||||
ssid = "the-internet";
|
||||
channel = "1";
|
||||
country_code = "GB";
|
||||
wpa_passphrase = "not a real wifi password";
|
||||
|
||||
hw_mode="g";
|
||||
ieee80211n = 1;
|
||||
auth_algs = 1; # 1=wpa2, 2=wep, 3=both
|
||||
wpa = 2; # 1=wpa, 2=wpa2, 3=both
|
||||
wpa_key_mgmt = "WPA-PSK";
|
||||
wpa_pairwise = "TKIP CCMP"; # auth for wpa (may not need this?)
|
||||
rsn_pairwise = "CCMP"; # auth for wpa2
|
||||
wmm_enabled = 1;
|
||||
};
|
||||
};
|
||||
|
||||
services.int = svc.network.address.build {
|
||||
interface = svc.bridge.primary.build { ifname = "int"; };
|
||||
family = "inet"; address = "${ipv4LocalNet}.1"; prefixLength = 16;
|
||||
};
|
||||
|
||||
services.bridge = svc.bridge.members.build {
|
||||
primary = services.int;
|
||||
members = with config.hardware.networkInterfaces;
|
||||
[ wlan lan ];
|
||||
};
|
||||
|
||||
services.ntp = svc.ntp.build {
|
||||
pools = { "pool.ntp.org" = ["iburst"]; };
|
||||
makestep = { threshold = 1.0; limit = 3; };
|
||||
};
|
||||
|
||||
services.sshd = svc.ssh.build { };
|
||||
|
||||
users.root = {
|
||||
# EDIT: choose a root password and then use
|
||||
# "mkpasswd -m sha512crypt" to determine the hash.
|
||||
# It should start wirh $6$.
|
||||
passwd = "$6$6HG7WALLQQY1LQDE$428cnouMJ7wVmyK9.dF1uWs7t0z9ztgp3MHvN5bbeo0M4Kqg/u2ThjoSHIjCEJQlnVpDOaEKcOjXAlIClHWN21";
|
||||
openssh.authorizedKeys.keys = [
|
||||
# EDIT: you can add your ssh pubkey here
|
||||
# "ssh-rsa AAAAB3NzaC1....H6hKd user@example.com";
|
||||
];
|
||||
};
|
||||
|
||||
services.dns =
|
||||
let interface = services.int;
|
||||
in svc.dnsmasq.build {
|
||||
resolvconf = services.resolvconf;
|
||||
inherit interface;
|
||||
ranges = [
|
||||
"${ipv4LocalNet}.10,${ipv4LocalNet}.249"
|
||||
# EDIT: ... maybe. In this example we use "ra-stateless",
|
||||
# meaning dnsmasq sends router advertisements with the O and A
|
||||
# bits set, and provides a stateless DHCP service. The client
|
||||
# will use a SLAAC address, and use DHCP for other
|
||||
# configuration information.
|
||||
# If you didn't understand the preceding sentence then
|
||||
# the default is _probably_ fine, but if you need
|
||||
# a DHCP-only IPv6 network or some other different
|
||||
# configuration, this is the place to change it.
|
||||
"::,constructor:$(output ${interface} ifname),ra-stateless"
|
||||
];
|
||||
# EDIT: choose a domain name for the DNS names issued for your
|
||||
# DHCP-issued hosts
|
||||
domain = "lan.example.com";
|
||||
};
|
||||
|
||||
services.wan = svc.pppoe.build {
|
||||
interface = config.hardware.networkInterfaces.wan;
|
||||
ppp-options = [
|
||||
"debug" "+ipv6" "noauth"
|
||||
# EDIT: change the strings "chap-username"
|
||||
# and "chap-secret" to match the username/password
|
||||
# provided by your ISP for PPP logins
|
||||
"name" "chap-username"
|
||||
"password" "chap-secret"
|
||||
];
|
||||
};
|
||||
|
||||
services.resolvconf = oneshot rec {
|
||||
dependencies = [ services.wan ];
|
||||
name = "resolvconf";
|
||||
up = ''
|
||||
. ${serviceFns}
|
||||
( in_outputs ${name}
|
||||
echo "nameserver $(output ${services.wan} ns1)" > resolv.conf
|
||||
echo "nameserver $(output ${services.wan} ns2)" >> resolv.conf
|
||||
chmod 0444 resolv.conf
|
||||
)
|
||||
'';
|
||||
};
|
||||
|
||||
filesystem =
|
||||
let inherit (pkgs.pseudofile) dir symlink;
|
||||
in dir {
|
||||
etc = dir {
|
||||
"resolv.conf" = symlink "${services.resolvconf}/.outputs/resolv.conf";
|
||||
};
|
||||
};
|
||||
|
||||
services.defaultroute4 = svc.network.route.build {
|
||||
via = "$(output ${services.wan} address)";
|
||||
target = "default";
|
||||
dependencies = [ services.wan ];
|
||||
};
|
||||
|
||||
services.defaultroute6 = svc.network.route.build {
|
||||
via = "$(output ${services.wan} ipv6-peer-address)";
|
||||
target = "default";
|
||||
interface = services.wan;
|
||||
};
|
||||
|
||||
services.firewall = svc.firewall.build {
|
||||
ruleset = import ./demo-firewall.nix;
|
||||
};
|
||||
|
||||
services.packet_forwarding = svc.network.forward.build { };
|
||||
|
||||
# We expect the ISP uses DHCP6 to issue IPv6 addresses. There is a
|
||||
# service to request address information in the form of a DHCP
|
||||
# lease, and two dependent services that listen for updates to the
|
||||
# DHCP address information and update the addresses of the WAN and
|
||||
# LAN interfaces respectively.
|
||||
|
||||
services.dhcp6c =
|
||||
let client = svc.dhcp6c.client.build {
|
||||
interface = services.wan;
|
||||
};
|
||||
in bundle {
|
||||
name = "dhcp6c";
|
||||
contents = [
|
||||
(svc.dhcp6c.prefix.build {
|
||||
# if your ISP provides you a real IPv6 prefix for your local
|
||||
# network (usually a /64 or /48 or something in between the
|
||||
# two), this service subscribes to that "prefix delegation"
|
||||
# information, and uses it to assign an address to the LAN
|
||||
# device. dnsmasq will notice this address and use it to
|
||||
# form the addresses it hands out to devices on the lan
|
||||
inherit client;
|
||||
interface = services.int;
|
||||
})
|
||||
(svc.dhcp6c.address.build {
|
||||
# if your ISP provides you a regular global IPv6 address,
|
||||
# this service subscribes to that information and assigns
|
||||
# the address to the WAN device.
|
||||
inherit client;
|
||||
interface = services.wan;
|
||||
})
|
||||
];
|
||||
};
|
||||
|
||||
defaultProfile.packages = with pkgs; [
|
||||
min-collect-garbage
|
||||
];
|
||||
}
|
||||
+24
-33
@@ -9,7 +9,7 @@
|
||||
{ config, pkgs, lib, ... } :
|
||||
let
|
||||
secrets = import ./rotuer-secrets.nix;
|
||||
inherit (pkgs.liminix.services) oneshot longrun;
|
||||
inherit (pkgs.liminix.services) oneshot longrun bundle;
|
||||
inherit (pkgs) serviceFns;
|
||||
svc = config.system.service;
|
||||
wirelessConfig = {
|
||||
@@ -38,6 +38,7 @@ in rec {
|
||||
../modules/network
|
||||
../modules/ppp
|
||||
../modules/dnsmasq
|
||||
../modules/dhcp6c
|
||||
../modules/firewall
|
||||
../modules/hostapd
|
||||
../modules/bridge
|
||||
@@ -48,7 +49,7 @@ in rec {
|
||||
hostname = "rotuer";
|
||||
|
||||
services.hostap = svc.hostapd.build {
|
||||
interface = config.hardware.networkInterfaces.wlan_24;
|
||||
interface = config.hardware.networkInterfaces.wlan;
|
||||
params = {
|
||||
ssid = "liminix";
|
||||
hw_mode="g";
|
||||
@@ -58,7 +59,7 @@ in rec {
|
||||
};
|
||||
|
||||
services.hostap5 = svc.hostapd.build {
|
||||
interface = config.hardware.networkInterfaces.wlan_5;
|
||||
interface = config.hardware.networkInterfaces.wlan5;
|
||||
params = rec {
|
||||
ssid = "liminix_5";
|
||||
hw_mode="a";
|
||||
@@ -78,7 +79,9 @@ in rec {
|
||||
services.bridge = svc.bridge.members.build {
|
||||
primary = services.int;
|
||||
members = with config.hardware.networkInterfaces;
|
||||
[ wlan_24 wlan_5 lan ];
|
||||
[ wlan
|
||||
wlan5
|
||||
lan ];
|
||||
};
|
||||
|
||||
services.ntp = svc.ntp.build {
|
||||
@@ -154,39 +157,27 @@ in rec {
|
||||
};
|
||||
|
||||
services.firewall = svc.firewall.build {
|
||||
ruleset = import ./rotuer-firewall.nix;
|
||||
ruleset = import ./demo-firewall.nix;
|
||||
};
|
||||
|
||||
services.packet_forwarding = svc.network.forward.build { };
|
||||
|
||||
services.dhcp6 =
|
||||
let
|
||||
name = "dhcp6c.wan";
|
||||
in longrun {
|
||||
inherit name;
|
||||
notification-fd = 10;
|
||||
run = ''
|
||||
export SERVICE_STATE=/run/service-state/${name}
|
||||
${pkgs.odhcp6c}/bin/odhcp6c -s ${pkgs.odhcp-script} -e -v -p /run/${name}.pid -P 48 $(output ${services.wan} ifname)
|
||||
)
|
||||
'';
|
||||
dependencies = [ services.wan ];
|
||||
};
|
||||
|
||||
services.acquire-lan-prefix =
|
||||
let script = pkgs.callPackage ./acquire-delegated-prefix.nix { };
|
||||
in longrun {
|
||||
name = "acquire-lan-prefix";
|
||||
run = "${script} /run/service-state/dhcp6c.wan $(output ${services.int} ifname)";
|
||||
dependencies = [ services.dhcp6 ];
|
||||
};
|
||||
|
||||
services.acquire-wan-address =
|
||||
let script = pkgs.callPackage ./acquire-wan-address.nix { };
|
||||
in longrun {
|
||||
name = "acquire-wan-address";
|
||||
run = "${script} /run/service-state/dhcp6c.wan $(output ${services.wan} ifname)";
|
||||
dependencies = [ services.dhcp6 ];
|
||||
services.dhcp6c =
|
||||
let client = svc.dhcp6c.client.build {
|
||||
interface = services.wan;
|
||||
};
|
||||
in bundle {
|
||||
name = "dhcp6c";
|
||||
contents = [
|
||||
(svc.dhcp6c.prefix.build {
|
||||
inherit client;
|
||||
interface = services.int;
|
||||
})
|
||||
(svc.dhcp6c.address.build {
|
||||
inherit client;
|
||||
interface = services.wan;
|
||||
})
|
||||
];
|
||||
};
|
||||
|
||||
defaultProfile.packages = with pkgs; [
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
{
|
||||
lzma
|
||||
, stdenv
|
||||
, ubootTools
|
||||
, dtc
|
||||
} :
|
||||
let
|
||||
objcopy = "${stdenv.cc.bintools.targetPrefix}objcopy";
|
||||
in {
|
||||
kernel
|
||||
, commandLine
|
||||
, entryPoint
|
||||
, extraName ? "" # e.g. socFamily
|
||||
, loadAddress
|
||||
, dtb ? null
|
||||
} :
|
||||
stdenv.mkDerivation {
|
||||
name = "kernel.image";
|
||||
phases = [
|
||||
"preparePhase"
|
||||
(if dtb != null then "dtbPhase" else ":")
|
||||
"buildPhase"
|
||||
"installPhase" ];
|
||||
nativeBuildInputs = [
|
||||
lzma
|
||||
dtc
|
||||
stdenv.cc
|
||||
ubootTools
|
||||
];
|
||||
preparePhase = ''
|
||||
cp ${kernel} vmlinux.elf; chmod +w vmlinux.elf
|
||||
'';
|
||||
dtbPhase = ''
|
||||
dtc -I dtb -O dts -o tmp.dts ${dtb}
|
||||
echo '/{ chosen { bootargs = ${builtins.toJSON commandLine}; }; };' >> tmp.dts
|
||||
dtc -I dts -O dtb -o tmp.dtb tmp.dts
|
||||
${objcopy} --update-section .appended_dtb=tmp.dtb vmlinux.elf || ${objcopy} --add-section .appended_dtb=${dtb} vmlinux.elf
|
||||
'';
|
||||
|
||||
buildPhase = ''
|
||||
${objcopy} -O binary -R .reginfo -R .notes -R .note -R .comment -R .mdebug -R .note.gnu.build-id -S vmlinux.elf vmlinux.bin
|
||||
rm -f vmlinux.bin.lzma ; lzma -k -z vmlinux.bin
|
||||
mkimage -A mips -O linux -T kernel -C lzma -a ${loadAddress} -e ${entryPoint} -n 'MIPS Liminix Linux ${extraName}' -d vmlinux.bin.lzma kernel.uimage
|
||||
'';
|
||||
installPhase = ''
|
||||
cp kernel.uimage $out
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{ lib, pkgs, config, ...}:
|
||||
{
|
||||
config = {
|
||||
kernel.config = {
|
||||
CPU_LITTLE_ENDIAN= "y";
|
||||
CPU_BIG_ENDIAN= "n";
|
||||
# CMDLINE_FROM_BOOTLOADER availability is conditional
|
||||
# on CMDLINE being set to something non-empty
|
||||
CMDLINE="\"empty=false\"";
|
||||
CMDLINE_FROM_BOOTLOADER = "y";
|
||||
|
||||
OF = "y";
|
||||
# USE_OF = "y";
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{ lib, pkgs, config, ...}:
|
||||
{
|
||||
config = {
|
||||
kernel.config = {
|
||||
MIPS_ELF_APPENDED_DTB = "y";
|
||||
MIPS_BOOTLOADER_CMDLINE_REQUIRE_COOKIE = "y";
|
||||
MIPS_BOOTLOADER_CMDLINE_COOKIE = "\"liminix\"";
|
||||
MIPS_CMDLINE_DTB_EXTEND = "y";
|
||||
|
||||
OF = "y";
|
||||
USE_OF = "y";
|
||||
};
|
||||
boot.commandLine = [
|
||||
"console=ttyS0,115200" # true of all mips we've yet encountered
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{ lib, pkgs, config, ...}:
|
||||
{
|
||||
imports = [ ./mips.nix ];
|
||||
config = {
|
||||
kernel.config = {
|
||||
CPU_BIG_ENDIAN = "y";
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{ lib, pkgs, config, ...}:
|
||||
{
|
||||
imports = [ ./mips.nix ];
|
||||
config = {
|
||||
kernel.config = {
|
||||
CPU_LITTLE_ENDIAN = "y";
|
||||
};
|
||||
};
|
||||
}
|
||||
+6
-2
@@ -39,7 +39,7 @@ in {
|
||||
};
|
||||
rootfsType = mkOption {
|
||||
default = "squashfs";
|
||||
type = types.enum ["squashfs" "jffs2"];
|
||||
type = types.enum ["squashfs" "jffs2" "ubifs"];
|
||||
};
|
||||
boot = {
|
||||
commandLine = mkOption {
|
||||
@@ -47,6 +47,10 @@ in {
|
||||
default = [];
|
||||
description = "Kernel command line";
|
||||
};
|
||||
imageFormat = mkOption {
|
||||
type = types.enum ["fit" "uimage"];
|
||||
default = "uimage";
|
||||
};
|
||||
tftp = {
|
||||
loadAddress = mkOption {
|
||||
type = types.str;
|
||||
@@ -81,7 +85,7 @@ in {
|
||||
[ s6 s6-init-bin execline s6-linux-init s6-rc ];
|
||||
|
||||
boot.commandLine = [
|
||||
"console=ttyS0,115200 panic=10 oops=panic init=/bin/init loglevel=8"
|
||||
"panic=10 oops=panic init=/bin/init loglevel=8"
|
||||
"root=${config.hardware.rootDevice}"
|
||||
"rootfstype=${config.rootfsType}"
|
||||
"fw_devlink=off"
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
liminix
|
||||
, lib
|
||||
, callPackage
|
||||
}:
|
||||
{ client, interface } :
|
||||
let
|
||||
inherit (liminix.services) longrun;
|
||||
inherit (lib) mkOption types;
|
||||
name = "dhcp6c.addr.${client.name}.${interface.name}";
|
||||
script = callPackage ./acquire-wan-address.nix { };
|
||||
in longrun {
|
||||
inherit name;
|
||||
run = "${script} /run/service-state/${client.name} $(output ${interface} ifname)";
|
||||
dependencies = [ client interface ];
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
liminix
|
||||
, lib
|
||||
, odhcp6c
|
||||
, odhcp-script
|
||||
}:
|
||||
{ interface } :
|
||||
let
|
||||
inherit (liminix.services) longrun;
|
||||
inherit (lib) mkOption types;
|
||||
name = "dhcp6c.${interface.name}";
|
||||
in longrun {
|
||||
inherit name;
|
||||
notification-fd = 10;
|
||||
run = ''
|
||||
export SERVICE_STATE=/run/service-state/${name}
|
||||
${odhcp6c}/bin/odhcp6c -s ${odhcp-script} -e -v -p /run/${name}.pid -P0 $(output ${interface} ifname)
|
||||
)
|
||||
'';
|
||||
dependencies = [ interface ];
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
## DHCP6 client module
|
||||
## ===================
|
||||
##
|
||||
## This is for use if you have an IPv6-capable upstream that provides
|
||||
## address information and/or prefix delegation using DHCP6. It
|
||||
## provides a service to request address information in the form of a
|
||||
## DHCP lease, and two dependent services that listen for updates
|
||||
## to the DHCP address information and can be used to update
|
||||
## addresses of network interfaces that you want to assign those
|
||||
## prefixes to
|
||||
|
||||
{ lib, pkgs, config, ...}:
|
||||
let
|
||||
inherit (lib) mkOption types;
|
||||
inherit (pkgs.liminix.services) oneshot;
|
||||
inherit (pkgs) liminix;
|
||||
in
|
||||
{
|
||||
options = {
|
||||
system.service.dhcp6c = {
|
||||
client = mkOption { type = liminix.lib.types.serviceDefn; };
|
||||
prefix = mkOption { type = liminix.lib.types.serviceDefn; };
|
||||
address = mkOption { type = liminix.lib.types.serviceDefn; };
|
||||
};
|
||||
};
|
||||
config.system.service.dhcp6c = {
|
||||
client = liminix.callService ./client.nix {
|
||||
interface = mkOption {
|
||||
type = liminix.lib.types.interface;
|
||||
description = "interface (usually WAN) to query for DHCP6";
|
||||
};
|
||||
};
|
||||
address = liminix.callService ./address.nix {
|
||||
client = mkOption {
|
||||
type = types.anything; # liminix.lib.types.service;
|
||||
};
|
||||
interface = mkOption {
|
||||
type = liminix.lib.types.interface;
|
||||
description = "interface to assign the address to";
|
||||
};
|
||||
};
|
||||
prefix = liminix.callService ./prefix.nix {
|
||||
client = mkOption {
|
||||
type = types.anything; # liminix.lib.types.service;
|
||||
};
|
||||
interface = mkOption {
|
||||
type = liminix.lib.types.interface;
|
||||
description = "interface to assign <prefix>::1 to";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
liminix
|
||||
, lib
|
||||
, callPackage
|
||||
}:
|
||||
{ client, interface } :
|
||||
let
|
||||
inherit (liminix.services) longrun;
|
||||
inherit (lib) mkOption types;
|
||||
name = "dhcp6c.prefix.${client.name}.${interface.name}";
|
||||
script = callPackage ./acquire-delegated-prefix.nix { };
|
||||
in longrun {
|
||||
inherit name;
|
||||
run = "${script} /run/service-state/${client.name} $(output ${interface} ifname)";
|
||||
dependencies = [ client interface ];
|
||||
}
|
||||
@@ -38,6 +38,9 @@ in {
|
||||
kernel = {
|
||||
config = {
|
||||
MTD_SPLIT_UIMAGE_FW = "y";
|
||||
} // lib.optionalAttrs (pkgs.stdenv.isMips) {
|
||||
# https://stackoverflow.com/questions/26466470/can-the-logical-erase-block-size-of-an-mtd-device-be-increased
|
||||
MTD_SPI_NOR_USE_4K_SECTORS = "n";
|
||||
};
|
||||
};
|
||||
|
||||
@@ -47,10 +50,12 @@ in {
|
||||
|
||||
system.outputs = {
|
||||
firmware =
|
||||
let o = config.system.outputs; in
|
||||
pkgs.runCommand "firmware" {} ''
|
||||
dd if=${o.uimage} of=$out bs=128k conv=sync
|
||||
dd if=${o.rootfs} of=$out bs=128k conv=sync,nocreat,notrunc oflag=append
|
||||
let
|
||||
o = config.system.outputs;
|
||||
bs = config.hardware.flash.eraseBlockSize;
|
||||
in pkgs.runCommand "firmware" {} ''
|
||||
dd if=${o.uimage} of=$out bs=${bs} conv=sync
|
||||
dd if=${o.rootfs} of=$out bs=${bs} conv=sync,nocreat,notrunc oflag=append
|
||||
'';
|
||||
flashimage =
|
||||
let o = config.system.outputs; in
|
||||
|
||||
+3
-5
@@ -24,7 +24,7 @@ in {
|
||||
extraPatchPhase = mkOption {
|
||||
default = "true";
|
||||
type = types.lines;
|
||||
} ;
|
||||
};
|
||||
config = mkOption {
|
||||
description = ''
|
||||
Kernel config options, as listed in Kconfig* files in the
|
||||
@@ -56,10 +56,6 @@ in {
|
||||
MODULE_SIG = if modular then "y" else "n";
|
||||
DEBUG_FS = "y";
|
||||
|
||||
MIPS_BOOTLOADER_CMDLINE_REQUIRE_COOKIE = "y";
|
||||
MIPS_BOOTLOADER_CMDLINE_COOKIE = "\"liminix\"";
|
||||
MIPS_CMDLINE_DTB_EXTEND = "y";
|
||||
|
||||
# basic networking protocols
|
||||
NET = "y";
|
||||
UNIX = "y";
|
||||
@@ -68,6 +64,8 @@ in {
|
||||
PACKET = "y"; # for ppp, tcpdump ...
|
||||
SYSVIPC= "y";
|
||||
|
||||
NETDEVICES = "y"; # even PPP needs this
|
||||
|
||||
# disabling this option causes the kernel to use an "empty"
|
||||
# initramfs instead: it has a /dev/console node and not much
|
||||
# else. Note that pid 1 is started *before* the root
|
||||
|
||||
+31
-11
@@ -7,6 +7,12 @@
|
||||
let
|
||||
inherit (lib) mkOption types concatStringsSep;
|
||||
inherit (pkgs) liminix callPackage writeText;
|
||||
arch = let s = pkgs.stdenv; in
|
||||
if s.isAarch64
|
||||
then "aarch64"
|
||||
else if s.isMips
|
||||
then "mips"
|
||||
else throw "can't determine arch";
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
@@ -36,7 +42,7 @@ in
|
||||
type = types.package;
|
||||
description = ''
|
||||
Directory containing separate kernel and rootfs image for
|
||||
use with qemu (see mips-vm)
|
||||
use with qemu (see run-liminix-vm)
|
||||
'';
|
||||
};
|
||||
manifest = mkOption {
|
||||
@@ -62,28 +68,42 @@ in
|
||||
kernel = liminix.builders.kernel.override {
|
||||
inherit (config.kernel) config src extraPatchPhase;
|
||||
};
|
||||
dtb = (callPackage ../kernel/dtb.nix {}) {
|
||||
dtb = liminix.builders.dtb {
|
||||
inherit (config.boot) commandLine;
|
||||
dts = config.hardware.dts.src;
|
||||
includes = config.hardware.dts.includes ++ [
|
||||
"${kernel.headers}/include"
|
||||
];
|
||||
};
|
||||
uimage = (callPackage ../kernel/uimage.nix {}) {
|
||||
uimage = liminix.builders.uimage {
|
||||
commandLine = concatStringsSep " " config.boot.commandLine;
|
||||
inherit (config.hardware) loadAddress entryPoint;
|
||||
inherit (config.boot) imageFormat;
|
||||
inherit kernel;
|
||||
inherit dtb;
|
||||
};
|
||||
# could use trivial-builders.linkFarmFromDrvs here?
|
||||
vmroot = pkgs.runCommand "qemu" {} ''
|
||||
mkdir $out
|
||||
cd $out
|
||||
ln -s ${config.system.outputs.rootfs} rootfs
|
||||
ln -s ${kernel} vmlinux
|
||||
ln -s ${manifest} manifest
|
||||
ln -s ${kernel.headers} build
|
||||
'';
|
||||
vmroot =
|
||||
let
|
||||
cmdline = builtins.toJSON (concatStringsSep " " config.boot.commandLine);
|
||||
makeBootableImage = pkgs.runCommandCC "objcopy" {}
|
||||
(if pkgs.stdenv.isAarch64
|
||||
then "${pkgs.stdenv.cc.targetPrefix}objcopy -O binary -S ${kernel} $out"
|
||||
else "cp ${kernel} $out");
|
||||
in pkgs.runCommandCC "vmroot" {} ''
|
||||
mkdir $out
|
||||
cd $out
|
||||
ln -s ${config.system.outputs.rootfs} rootfs
|
||||
ln -s ${kernel} vmlinux
|
||||
ln -s ${manifest} manifest
|
||||
ln -s ${kernel.headers} build
|
||||
echo ${cmdline} > commandline
|
||||
cat > run.sh << EOF
|
||||
#!${pkgs.runtimeShell}
|
||||
CMDLINE=${cmdline} ${pkgs.pkgsBuildBuild.run-liminix-vm}/bin/run-liminix-vm --arch ${arch} \$* ${makeBootableImage} ${config.system.outputs.rootfs}
|
||||
EOF
|
||||
chmod +x run.sh
|
||||
'';
|
||||
|
||||
manifest = writeText "manifest.json" (builtins.toJSON config.filesystem.contents);
|
||||
};
|
||||
|
||||
@@ -7,5 +7,6 @@
|
||||
./kexecboot.nix
|
||||
./flashimage.nix
|
||||
./jffs2.nix
|
||||
./ubifs.nix
|
||||
];
|
||||
}
|
||||
|
||||
+20
-8
@@ -42,27 +42,39 @@ in {
|
||||
ln -s ${o.manifest} manifest
|
||||
ln -s ${o.kernel.headers} build
|
||||
ln -s ${o.uimage} uimage
|
||||
ln -s ${o.boot-scr} boot.scr
|
||||
ln -s ${o.boot-scr}/dtb dtb
|
||||
ln -s ${o.boot-scr}/script boot.scr
|
||||
'';
|
||||
|
||||
boot-scr =
|
||||
let
|
||||
inherit (pkgs.lib.trivial) toHexString;
|
||||
o = config.system.outputs;
|
||||
cmdline = concatStringsSep " " config.boot.commandLine;
|
||||
in
|
||||
pkgs.buildPackages.runCommand "boot-scr" {} ''
|
||||
pkgs.buildPackages.runCommand "boot-scr" { nativeBuildInputs = [ pkgs.pkgsBuildBuild.dtc ]; } ''
|
||||
uimageSize=$(($(stat -L -c %s ${o.uimage}) + 0x1000 &(~0xfff)))
|
||||
rootfsStart=0x$(printf %x $((${cfg.loadAddress} + 0x100000 + $uimageSize)))
|
||||
rootfsStart=0x$(printf %x $((${cfg.loadAddress} + 0x100000 + $uimageSize &(~0xfffff) )))
|
||||
rootfsBytes=$(($(stat -L -c %s ${o.rootfs}) + 0x100000 &(~0xfffff)))
|
||||
rootfsBytes=$(($rootfsBytes + ${toString cfg.freeSpaceBytes} ))
|
||||
cmd="mtdparts=phram0:''${rootfsMb}M(rootfs) phram.phram=phram0,''${rootfsStart},''${rootfsBytes},${config.hardware.flash.eraseBlockSize} memmap=''${rootfsBytes}\$''${rootfsStart} root=/dev/mtdblock0";
|
||||
rootfsMb=$(($rootfsBytes >> 20))
|
||||
cmd="mtdparts=phram0:''${rootfsMb}M(rootfs) phram.phram=phram0,''${rootfsStart},''${rootfsBytes},${config.hardware.flash.eraseBlockSize} root=/dev/mtdblock0";
|
||||
|
||||
cat > $out << EOF
|
||||
dtbStart=$(printf %x $((${cfg.loadAddress} + $rootfsBytes + 0x100000 + $uimageSize )))
|
||||
|
||||
mkdir $out
|
||||
cat ${o.dtb} > $out/dtb
|
||||
fdtput -p -t s $out/dtb /reserved-memory/phram-rootfs compatible phram
|
||||
fdtput -p -t lx $out/dtb /reserved-memory/phram-rootfs reg 0 $rootfsStart 0 $(printf %x $rootfsBytes)
|
||||
|
||||
dtbBytes=$(($(stat -L -c %s $out/dtb) + 0x1000 &(~0xfff)))
|
||||
|
||||
cat > $out/script << EOF
|
||||
setenv serverip ${cfg.serverip}
|
||||
setenv ipaddr ${cfg.ipaddr}
|
||||
setenv bootargs 'liminix $cmd'
|
||||
tftp 0x$(printf %x ${cfg.loadAddress}) result/uimage ; tftp 0x$(printf %x $rootfsStart) result/rootfs
|
||||
bootm 0x$(printf %x ${cfg.loadAddress})
|
||||
setenv bootargs 'liminix ${cmdline} $cmd'
|
||||
tftpboot 0x$(printf %x ${cfg.loadAddress}) result/uimage ; tftpboot 0x$(printf %x $rootfsStart) result/rootfs ; tftpboot 0x$dtbStart result/dtb
|
||||
bootm 0x$(printf %x ${cfg.loadAddress}) - 0x$dtbStart
|
||||
EOF
|
||||
'';
|
||||
};
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
{
|
||||
config
|
||||
, pkgs
|
||||
, lib
|
||||
, ...
|
||||
}:
|
||||
let
|
||||
inherit (lib) mkIf mkOption types;
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
./initramfs.nix
|
||||
];
|
||||
options.hardware.ubi = {
|
||||
minIOSize = mkOption { type = types.str; };
|
||||
eraseBlockSize = mkOption { type = types.str; }; # LEB
|
||||
maxLEBcount = mkOption { type = types.str; }; # LEB
|
||||
};
|
||||
|
||||
config = mkIf (config.rootfsType == "ubifs") {
|
||||
kernel.config = {
|
||||
MTD_UBI="y";
|
||||
|
||||
UBIFS_FS = "y";
|
||||
UBIFS_FS_SECURITY = "n";
|
||||
};
|
||||
boot.initramfs.enable = true;
|
||||
system.outputs = rec {
|
||||
systemConfiguration =
|
||||
pkgs.systemconfig config.filesystem.contents;
|
||||
rootfs =
|
||||
let
|
||||
inherit (pkgs.pkgsBuildBuild) runCommand mtdutils;
|
||||
cfg = config.hardware.ubi;
|
||||
in runCommand "mkfs.ubifs" {
|
||||
depsBuildBuild = [ mtdutils ];
|
||||
} ''
|
||||
mkdir -p $TMPDIR/empty/nix/store/ $TMPDIR/empty/secrets $TMPDIR/empty/boot
|
||||
cp ${systemConfiguration}/bin/activate $TMPDIR/empty/activate
|
||||
ln -s ${pkgs.s6-init-bin}/bin/init $TMPDIR/empty/init
|
||||
cp ${config.system.outputs.uimage} $TMPDIR/empty/boot/uimage
|
||||
mkdir -p $TMPDIR/empty/nix/store
|
||||
for path in $(cat ${systemConfiguration}/etc/nix-store-paths) ; do
|
||||
(cd $TMPDIR/empty && cp -a $path .$path)
|
||||
done
|
||||
mkfs.ubifs -x favor_lzo -c ${cfg.maxLEBcount} -m ${cfg.minIOSize} -e ${cfg.eraseBlockSize} -y -r $TMPDIR/empty --output $out --squash-uids -o $out
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
{
|
||||
config
|
||||
, pkgs
|
||||
, lib
|
||||
, ...
|
||||
}:
|
||||
let
|
||||
inherit (lib) mkIf mkEnableOption mkOption types concatStringsSep;
|
||||
loadaddr = config.boot.tftp.loadAddress;
|
||||
cfg = config.boot.tftp;
|
||||
instructions = pkgs.writeText "INSTALL.md" ''
|
||||
|
||||
# First-time installation
|
||||
|
||||
First-time installation of a UBIFS Liminix system presently can only
|
||||
be done from the U-Boot command line (or from a ramdisk-based recovery
|
||||
system: see "kexecboot" but we don't have detailed instructions for
|
||||
this process yet).
|
||||
|
||||
These steps were tested on a Belkin RT3200 (also known as Linksys
|
||||
E8450). Other devices may be set up differently, so use them as
|
||||
inspiration and don't just paste them blindly. Consult the Liminix
|
||||
manual for how to connect a serial cable to your device and get into
|
||||
U-Boot
|
||||
|
||||
1) determine which MTD device is being used for UBI and the partition name:
|
||||
|
||||
uboot> ubi part
|
||||
Device 0: ubi0, MTD partition ubi
|
||||
|
||||
In this case the important value is `ubi0`
|
||||
|
||||
2) list the available volumes and create a new one on which to install Liminix
|
||||
|
||||
uboot> ubi info l
|
||||
[ copious output scrolls past ]
|
||||
|
||||
Expect there to be existing volumes and for some or all of them to be
|
||||
important. Unless you know what you're doing, don't remove anything
|
||||
whose name suggests it's related to uboot, or any kind of backup or
|
||||
recovery partition. To see how much space is free:
|
||||
|
||||
uboot> ubi info
|
||||
[ ... ]
|
||||
UBI: available PEBs: 823
|
||||
|
||||
Now we can make our new root volume
|
||||
|
||||
uboot> ubi create liminix -
|
||||
|
||||
3) transfer the root filesystem from the build system and write it
|
||||
to the new volume
|
||||
|
||||
uboot> setenv serverip ${cfg.serverip}
|
||||
uboot> setenv ipaddr ${cfg.ipaddr}
|
||||
uboot> tftpboot ${loadaddr} result/rootfs
|
||||
uboot> ubi write ${loadaddr} liminix $filesize
|
||||
|
||||
Now we have the root filesystem installed on the device. You
|
||||
can even mount it and poke around using `ubifsmount ubi0:liminix;
|
||||
ubifsls /`
|
||||
|
||||
4) optional: before you configure the device to boot into Liminix
|
||||
automatically, you can try booting it by hand to see if it works:
|
||||
|
||||
uboot> ubifsmount ubi0:liminix
|
||||
uboot> ubifsload ${loadaddr} boot/uimage
|
||||
uboot> bootm ${loadaddr}
|
||||
|
||||
Once you've done this and you're happy with it, reset the device to
|
||||
U-Boot. You don't need to recreate the volume but you do need to
|
||||
repeat step 3.
|
||||
|
||||
5) Instructions for configuring autoboot are likely to be very
|
||||
device-dependent. On the Linksys E8450/Belkin RT3200, the environment
|
||||
variable `boot_production` governs what happens on a normal boot, so
|
||||
you could do
|
||||
|
||||
uboot> setenv boot_production 'led $bootled_pwr on ; ubifsmount ubi0:liminix; ubifsload ${loadaddr} boot/uimage; bootm ${loadaddr}'
|
||||
|
||||
On other devices, some detective work may be needed. Try running
|
||||
`printenv` and look for likely commands, try looking at the existing
|
||||
boot process, maybe even try looking for documentation for that device.
|
||||
|
||||
6) Now you can reboot the device into Liminix
|
||||
|
||||
uboot> reset
|
||||
|
||||
'';
|
||||
in {
|
||||
options.system.outputs = {
|
||||
ubimage = mkOption {
|
||||
type = types.package;
|
||||
description = ''
|
||||
UBIFS image and instructions to install it
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf (config.rootfsType == "ubifs") {
|
||||
system.outputs = {
|
||||
ubimage =
|
||||
let o = config.system.outputs; in
|
||||
pkgs.runCommand "ubimage" {} ''
|
||||
mkdir $out
|
||||
cd $out
|
||||
ln -s ${o.rootfs} rootfs
|
||||
ln -s ${instructions} INSTALL
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
+3
-1
@@ -16,6 +16,8 @@ in {
|
||||
networking = callPackage ./liminix-tools/networking {};
|
||||
builders = {
|
||||
squashfs = callPackage ./liminix-tools/builders/squashfs.nix {};
|
||||
dtb = callPackage ./kernel/dtb.nix {};
|
||||
uimage = callPackage ./kernel/uimage.nix {};
|
||||
kernel = callPackage ./kernel {};
|
||||
};
|
||||
callService = path : parameters :
|
||||
@@ -57,7 +59,7 @@ in {
|
||||
systemconfig = callPackage ./systemconfig {};
|
||||
s6-init-bin = callPackage ./s6-init-bin {};
|
||||
s6-rc-database = callPackage ./s6-rc-database {};
|
||||
mips-vm = callPackage ./mips-vm {};
|
||||
run-liminix-vm = callPackage ./run-liminix-vm {};
|
||||
ppp = callPackage ./ppp {};
|
||||
pppoe = callPackage ./pppoe {};
|
||||
|
||||
|
||||
@@ -11,6 +11,11 @@
|
||||
}:
|
||||
let
|
||||
writeConfig = import ../kernel/write-kconfig.nix { inherit lib writeText; };
|
||||
arch = if stdenv.isMips
|
||||
then "mips"
|
||||
else if stdenv.isAarch64
|
||||
then "arm64"
|
||||
else throw "unknown arch";
|
||||
in stdenv.mkDerivation {
|
||||
name = "kernel-modules";
|
||||
|
||||
@@ -25,7 +30,7 @@ in stdenv.mkDerivation {
|
||||
HOST_EXTRACFLAGS = with buildPackages.pkgs;
|
||||
"-I${buildPackages.openssl.dev}/include -L${buildPackages.openssl.out}/lib";
|
||||
CROSS_COMPILE = stdenv.cc.bintools.targetPrefix;
|
||||
ARCH = "mips"; # kernel uses "mips" here for both mips and mipsel
|
||||
ARCH = arch;
|
||||
KBUILD_BUILD_HOST = "liminix.builder";
|
||||
|
||||
buildPhase = ''
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
let
|
||||
writeConfig = import ./write-kconfig.nix { inherit lib writeText; };
|
||||
kconfigFile = writeConfig "kconfig" config;
|
||||
arch = stdenv.hostPlatform.linuxArch;
|
||||
inherit lib; in
|
||||
stdenv.mkDerivation rec {
|
||||
name = "kernel";
|
||||
@@ -18,7 +19,7 @@ stdenv.mkDerivation rec {
|
||||
hardeningDisable = ["all"];
|
||||
nativeBuildInputs = [buildPackages.stdenv.cc] ++
|
||||
(with buildPackages.pkgs; [
|
||||
rsync bc bison flex pkgconfig
|
||||
rsync bc bison flex pkg-config
|
||||
openssl ncurses.all perl
|
||||
]);
|
||||
CC = "${stdenv.cc.bintools.targetPrefix}gcc";
|
||||
@@ -28,7 +29,7 @@ stdenv.mkDerivation rec {
|
||||
"-I${openssl.dev}/include -L${openssl.out}/lib -L${ncurses.out}/lib";
|
||||
PKG_CONFIG_PATH = "./pkgconfig";
|
||||
CROSS_COMPILE = stdenv.cc.bintools.targetPrefix;
|
||||
ARCH = "mips"; # kernel uses "mips" here for both mips and mipsel
|
||||
ARCH = arch;
|
||||
KBUILD_BUILD_HOST = "liminix.builder";
|
||||
|
||||
dontStrip = true;
|
||||
@@ -48,6 +49,7 @@ stdenv.mkDerivation rec {
|
||||
|
||||
patches = [
|
||||
./cmdline-cookie.patch
|
||||
./phram-allow-cached-mappings.patch
|
||||
];
|
||||
|
||||
# this is here to work around what I think is a bug in nixpkgs
|
||||
@@ -75,7 +77,7 @@ stdenv.mkDerivation rec {
|
||||
export KBUILD_OUTPUT=`pwd`
|
||||
cp ${kconfigFile} .config
|
||||
cp ${kconfigFile} .config.orig
|
||||
make V=1 olddefconfig
|
||||
make olddefconfig
|
||||
'';
|
||||
|
||||
checkConfigurationPhase = ''
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/dts-v1/;
|
||||
|
||||
// used on arm/aarch64 to provide a U-bootable image that combines
|
||||
// kernel and fdt
|
||||
|
||||
/ {
|
||||
description = "Simple image with single Linux kernel and FDT blob";
|
||||
#address-cells = <1>;
|
||||
|
||||
images {
|
||||
kernel {
|
||||
description = "Vanilla Linux kernel";
|
||||
// data = /incbin/("./vmlinux.bin.gz");
|
||||
type = "kernel";
|
||||
// arch = "arm64";
|
||||
os = "linux";
|
||||
// compression = "gzip";
|
||||
// load = <00000000>;
|
||||
// entry = <00000000>;
|
||||
hash-1 {
|
||||
algo = "crc32";
|
||||
};
|
||||
hash-2 {
|
||||
algo = "sha1";
|
||||
};
|
||||
};
|
||||
fdt-1 {
|
||||
description = "Flattened Device Tree blob";
|
||||
// data = /incbin/("./target.dtb");
|
||||
type = "flat_dt";
|
||||
// arch = "arm64";
|
||||
compression = "none";
|
||||
hash-1 {
|
||||
algo = "crc32";
|
||||
};
|
||||
hash-2 {
|
||||
algo = "sha1";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
configurations {
|
||||
default = "conf-1";
|
||||
conf-1 {
|
||||
description = "Boot Linux kernel with FDT blob";
|
||||
kernel = "kernel";
|
||||
fdt = "fdt-1";
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,47 @@
|
||||
From bb7e7aeb3d832059e33b1e76eb85d4680f77abf2 Mon Sep 17 00:00:00 2001
|
||||
From: Ben Hutchings <ben@decadent.org.uk>
|
||||
Date: Fri, 3 Jun 2016 01:08:36 +0100
|
||||
Subject: [PATCH] phram: Use memremap() to allow mapping of system RAM
|
||||
|
||||
Using memremap() instead of ioremap() allows mapping a disk image in
|
||||
system RAM that has somehow been reserved. It should fall back
|
||||
to ioremap() where necessary.
|
||||
|
||||
Entirely untested, and I'm not convinced this is a good idea at all.
|
||||
---
|
||||
drivers/mtd/devices/phram.c | 7 ++++---
|
||||
1 file changed, 4 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c
|
||||
index 8b66e52ca3cc..0ea254e2ba51 100644
|
||||
--- a/drivers/mtd/devices/phram.c
|
||||
+++ b/drivers/mtd/devices/phram.c
|
||||
@@ -88,7 +88,7 @@ static void unregister_devices(void)
|
||||
|
||||
list_for_each_entry_safe(this, safe, &phram_list, list) {
|
||||
mtd_device_unregister(&this->mtd);
|
||||
- iounmap(this->mtd.priv);
|
||||
+ memunmap(this->mtd.priv);
|
||||
kfree(this->mtd.name);
|
||||
kfree(this);
|
||||
}
|
||||
@@ -104,7 +104,8 @@ static int register_device(char *name, phys_addr_t start, size_t len)
|
||||
goto out0;
|
||||
|
||||
ret = -EIO;
|
||||
- new->mtd.priv = ioremap(start, len);
|
||||
+ new->mtd.priv = memremap(start, len,
|
||||
+ MEMREMAP_WB | MEMREMAP_WT | MEMREMAP_WC);
|
||||
if (!new->mtd.priv) {
|
||||
pr_err("ioremap failed\n");
|
||||
goto out1;
|
||||
@@ -134,7 +135,7 @@ static int register_device(char *name, phys_addr_t start, size_t len)
|
||||
return 0;
|
||||
|
||||
out2:
|
||||
- iounmap(new->mtd.priv);
|
||||
+ memunmap(new->mtd.priv);
|
||||
out1:
|
||||
kfree(new);
|
||||
out0:
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
{
|
||||
lzma
|
||||
, stdenv
|
||||
, ubootTools
|
||||
, dtc
|
||||
, lib
|
||||
} :
|
||||
let
|
||||
objcopy = "${stdenv.cc.bintools.targetPrefix}objcopy";
|
||||
arch = stdenv.hostPlatform.linuxArch;
|
||||
stripAndZip = ''
|
||||
${objcopy} -O binary -R .reginfo -R .notes -R .note -R .comment -R .mdebug -R .note.gnu.build-id -S vmlinux.elf vmlinux.bin
|
||||
rm -f vmlinux.bin.lzma ; lzma -k -z vmlinux.bin
|
||||
'';
|
||||
in {
|
||||
kernel
|
||||
, commandLine
|
||||
, entryPoint
|
||||
, extraName ? "" # e.g. socFamily
|
||||
, loadAddress
|
||||
, imageFormat
|
||||
, dtb ? null
|
||||
} : stdenv.mkDerivation {
|
||||
name = "kernel.image";
|
||||
phases = [
|
||||
"preparePhase"
|
||||
(if commandLine != null then assert dtb != null; "mungeDtbPhase" else ":")
|
||||
(if imageFormat == "fit" then "buildPhaseFIT" else "buildPhaseUImage")
|
||||
"installPhase"
|
||||
];
|
||||
nativeBuildInputs = [
|
||||
lzma
|
||||
dtc
|
||||
stdenv.cc
|
||||
ubootTools
|
||||
];
|
||||
preparePhase = ''
|
||||
cp ${kernel} vmlinux.elf; chmod +w vmlinux.elf
|
||||
'';
|
||||
mungeDtbPhase = ''
|
||||
dtc -I dtb -O dts -o tmp.dts ${dtb}
|
||||
echo '/{ chosen { bootargs = ${builtins.toJSON commandLine}; }; };' >> tmp.dts
|
||||
dtc -I dts -O dtb -o tmp.dtb tmp.dts
|
||||
'';
|
||||
|
||||
buildPhaseUImage = ''
|
||||
test -f tmp.dtb && ${objcopy} --update-section .appended_dtb=tmp.dtb vmlinux.elf || ${objcopy} --add-section .appended_dtb=tmp.dtb vmlinux.elf
|
||||
${stripAndZip}
|
||||
mkimage -A ${arch} -O linux -T kernel -C lzma -a ${loadAddress} -e ${entryPoint} -n '${lib.toUpper arch} Liminix Linux ${extraName}' -d vmlinux.bin.lzma kernel.uimage
|
||||
'';
|
||||
|
||||
buildPhaseFIT = ''
|
||||
${stripAndZip}
|
||||
cat ${./kernel_fdt.its} > mkimage.its
|
||||
cat << _VARS >> mkimage.its
|
||||
/ {
|
||||
images {
|
||||
kernel {
|
||||
data = /incbin/("./vmlinux.bin.lzma");
|
||||
load = <${loadAddress}>;
|
||||
entry = <${entryPoint}>;
|
||||
arch = "${arch}";
|
||||
compression = "lzma";
|
||||
};
|
||||
fdt-1 {
|
||||
data = /incbin/("./tmp.dtb");
|
||||
arch = "${arch}";
|
||||
};
|
||||
};
|
||||
};
|
||||
_VARS
|
||||
mkimage -f mkimage.its kernel.uimage
|
||||
mkimage -l kernel.uimage
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
cp kernel.uimage $out
|
||||
'';
|
||||
}
|
||||
@@ -14,6 +14,11 @@
|
||||
, lib
|
||||
}:
|
||||
let
|
||||
arch = if stdenv.isMips
|
||||
then "mips"
|
||||
else if stdenv.isAarch64
|
||||
then "arm64"
|
||||
else throw "unknown arch";
|
||||
openwrtSrc = fetchFromGitHub {
|
||||
name = "openwrt-source";
|
||||
repo = "openwrt";
|
||||
@@ -52,6 +57,14 @@ let
|
||||
WLAN_VENDOR_MEDIATEK = "y";
|
||||
MT7603E = "y";
|
||||
};
|
||||
|
||||
mt7915e = {
|
||||
MT7915E = "m";
|
||||
};
|
||||
mt7615e = {
|
||||
MT7615E = "m";
|
||||
MT7622_WMAC = "y";
|
||||
};
|
||||
mac80211_hwsim = {
|
||||
MAC80211_HWSIM = "y";
|
||||
};
|
||||
@@ -94,11 +107,11 @@ let
|
||||
hardeningDisable = ["all"];
|
||||
nativeBuildInputs = [buildPackages.stdenv.cc] ++
|
||||
(with buildPackages.pkgs;
|
||||
[bc bison flex pkgconfig openssl
|
||||
[bc bison flex pkg-config openssl
|
||||
which kmod cpio
|
||||
]);
|
||||
inherit CC CROSS_COMPILE;
|
||||
ARCH = "mips"; # kernel uses "mips" here for both mips and mipsel
|
||||
ARCH = arch;
|
||||
dontStrip = true;
|
||||
dontPatchELF = true;
|
||||
phases = [
|
||||
|
||||
@@ -14,6 +14,7 @@ let
|
||||
cp -av ${src}/target/linux/generic/files/* .
|
||||
chmod -R u+w .
|
||||
cp -av ${src}/target/linux/${family}/files/* .
|
||||
test -d ${src}/target/linux/${family}/files-5.15/ && cp -av ${src}/target/linux/${family}/files-5.15/* .
|
||||
chmod -R u+w .
|
||||
patches() {
|
||||
for i in $* ; do patch --batch --forward -p1 < $i ;done
|
||||
@@ -26,9 +27,12 @@ let
|
||||
patch --batch -p1 --reverse < ${src}/target/linux/generic/pending-5.15/330-MIPS-kexec-Accept-command-line-parameters-from-users.patch
|
||||
patches ${src}/target/linux/generic/hack-5.15/*.patch
|
||||
patches ${src}/target/linux/${family}/patches-5.15/*.patch
|
||||
patches ${./make-mtdsplit-jffs2-endian-agnostic.patch}
|
||||
'';
|
||||
in {
|
||||
inherit src;
|
||||
applyPatches.ath79 = doPatch "ath79";
|
||||
applyPatches.ramips = doPatch "ramips";
|
||||
applyPatches.mediatek = doPatch "mediatek"; # aarch64
|
||||
applyPatches.mvebu = doPatch "mvebu"; # arm
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
From 12345790 Tue Sep 26 18:30:44 2023
|
||||
From: Daniel Barlow <dan@telent.net>
|
||||
Date: Tue Sep 26 18:29:47 BST 2023
|
||||
Subject: mtdsplit: find magic of little-endian JFFS2
|
||||
|
||||
On a little-endian CPU, the little-endian JFFS2 magic
|
||||
appears to be word-swapped.
|
||||
|
||||
There is probably a better way to implement this; if you are reading
|
||||
this patch you are probably well-qualified to do that and upstream it
|
||||
to OpenWrt
|
||||
|
||||
|
||||
diff --git a/drivers/mtd/mtdsplit/mtdsplit.c b/drivers/mtd/mtdsplit/mtdsplit.c
|
||||
index b2e51dcfc6..533af0298e 100644
|
||||
--- a/drivers/mtd/mtdsplit/mtdsplit.c
|
||||
+++ b/drivers/mtd/mtdsplit/mtdsplit.c
|
||||
@@ -92,6 +93,6 @@
|
||||
*type = MTDSPLIT_PART_TYPE_SQUASHFS;
|
||||
return 0;
|
||||
- } else if (magic == 0x19852003) {
|
||||
+ } else if ((magic == 0x19852003) || (magic == 0x20031985)) {
|
||||
if (type)
|
||||
*type = MTDSPLIT_PART_TYPE_JFFS2;
|
||||
return 0;
|
||||
@@ -0,0 +1,4 @@
|
||||
CFLAGS+=-Os -fno-stack-protector -fpic -fPIC
|
||||
LDFLAGS=-static
|
||||
|
||||
preinit: preinit.o parseopts.o
|
||||
@@ -15,7 +15,6 @@ stdenv.mkDerivation {
|
||||
|
||||
# NIX_DEBUG=2;
|
||||
hardeningDisable = [ "all" ];
|
||||
CFLAGS = "-Os -static -DPREINIT_USE_LIBC -fno-stack-protector -fpic -fPIC -I ./ -I ${kernel}/tools/include/nolibc";
|
||||
|
||||
postBuild = ''
|
||||
$STRIP --remove-section=.note --remove-section=.comment preinit
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static int begins_with(char * str, char * prefix)
|
||||
{
|
||||
while(*prefix) {
|
||||
if(*str == '\0') return 0;
|
||||
if(*str != *prefix) return 0;
|
||||
str++;
|
||||
prefix++;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
void parseopts(char * cmdline, char **root, char **rootfstype) {
|
||||
*root = 0;
|
||||
*rootfstype = 0;
|
||||
|
||||
for(char *p = cmdline; *p; p++) {
|
||||
if(begins_with(p, "root=")) {
|
||||
*root = p + strlen("root=");
|
||||
while(*p && (*p != ' ')) p++;
|
||||
|
||||
if(*p) {
|
||||
*p = '\0';
|
||||
p++;
|
||||
};
|
||||
};
|
||||
if(begins_with(p, "rootfstype=")) {
|
||||
*rootfstype = p + strlen("rootfstype=");
|
||||
while(*p && (*p != ' ')) p++;
|
||||
if(*p) {
|
||||
*p = '\0';
|
||||
p++;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
#ifdef TESTS
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define die(fmt, ...) do { printf(fmt, __VA_ARGS__); exit(1); } while(0)
|
||||
#define S(x) #x
|
||||
#define expect_equal(actual, expected) \
|
||||
if(!actual || strcmp(actual, expected)) die("%d: expected \"%s\", got \"%s\"", __LINE__, expected, actual);
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
char * root = "/dev/hda1";
|
||||
char * rootfstype = "xiafs";
|
||||
char *buf;
|
||||
|
||||
// finds root= and rootfstype= options
|
||||
buf = strdup("liminix console=ttyS0,115200 panic=10 oops=panic init=/bin/init loglevel=8 root=/dev/ubi0_4 rootfstype=ubifs fw_devlink=off mtdparts=phram0:18M(rootfs) phram.phram=phram0,0x40400000,18874368,65536 root=/dev/mtdblock0 foo");
|
||||
parseopts(buf, &root, &rootfstype);
|
||||
expect_equal(root, "/dev/mtdblock0");
|
||||
expect_equal(rootfstype, "ubifs");
|
||||
|
||||
// in case of duplicates, chooses the latter
|
||||
// also: works if the option is end of string
|
||||
buf = strdup("liminix console=ttyS0,115200 panic=10 oops=panic init=/bin/init loglevel=8 root=/dev/ubi0_4 rootfstype=ubifs fw_devlink=off mtdparts=phram0:18M(rootfs) phram.phram=phram0,0x40400000,18874368,65536 root=/dev/mtdblock0");
|
||||
parseopts(buf, &root, &rootfstype);
|
||||
expect_equal(root, "/dev/mtdblock0");
|
||||
expect_equal(rootfstype, "ubifs");
|
||||
|
||||
// options may appear in either order
|
||||
buf = strdup("liminix fw_devlink=off root=/dev/hda1 rootfstype=ubifs foo");
|
||||
parseopts(buf, &root, &rootfstype);
|
||||
expect_equal(root, "/dev/hda1");
|
||||
expect_equal(rootfstype, "ubifs");
|
||||
|
||||
buf = strdup("liminix rootfstype=ubifs fw_devlink=off root=/dev/hda1 foo");
|
||||
parseopts(buf, &root, &rootfstype);
|
||||
expect_equal(rootfstype, "ubifs");
|
||||
expect_equal(root, "/dev/hda1");
|
||||
|
||||
// provides NULL for missing options
|
||||
buf = strdup("liminix rufustype=ubifs fw_devlink=off foot=/dev/hda1");
|
||||
parseopts(buf, &root, &rootfstype);
|
||||
if(rootfstype) die("expected null rootfstype, got \"%s\"", rootfstype);
|
||||
if(root) die("expected null root, got \"%s\"", root);
|
||||
|
||||
// provides empty strings for empty options
|
||||
buf = strdup("liminix rootfstype= fw_devlink=off root= /dev/hda1");
|
||||
parseopts(buf, &root, &rootfstype);
|
||||
if(strlen(rootfstype)) die("expected empty rootfstype, got \"%s\"", rootfstype);
|
||||
if(strlen(root)) die("expected null root, got \"%s\"", root);
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
+51
-59
@@ -1,37 +1,36 @@
|
||||
#ifdef PREINIT_USE_LIBC
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/wait.h>
|
||||
#include <string.h>
|
||||
#else
|
||||
#include <nolibc.h>
|
||||
#endif
|
||||
#include <asm/setup.h>
|
||||
|
||||
#include <asm/setup.h> /* for COMMAND_LINE_SIZE */
|
||||
|
||||
void parseopts(char * cmdline, char **root, char **rootfstype);
|
||||
|
||||
#define ERR(x) write(2, x, strlen(x))
|
||||
#define AVER(c) do { if(c < 0) ERR("failed: " #c); } while(0)
|
||||
|
||||
static int begins_with(char * str, char * prefix)
|
||||
{
|
||||
while(*prefix) {
|
||||
if(*str == '\0') return 0;
|
||||
if(*str != *prefix) return 0;
|
||||
str++;
|
||||
prefix++;
|
||||
}
|
||||
return 1;
|
||||
while(*prefix) {
|
||||
if(*str == '\0') return 0;
|
||||
if(*str != *prefix) return 0;
|
||||
str++;
|
||||
prefix++;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void fork_exec(char * command, char *args[])
|
||||
static int fork_exec(char * command, char *args[])
|
||||
{
|
||||
int fork_pid = fork();
|
||||
AVER(fork_pid);
|
||||
if(fork_pid > 0)
|
||||
wait(NULL);
|
||||
else
|
||||
AVER(execve(command, args, NULL));
|
||||
int fork_pid = fork();
|
||||
AVER(fork_pid);
|
||||
if(fork_pid > 0)
|
||||
wait(NULL);
|
||||
else
|
||||
return execve(command, args, NULL);
|
||||
}
|
||||
|
||||
char banner[] = "Running pre-init...\n";
|
||||
@@ -39,52 +38,45 @@ char buf[COMMAND_LINE_SIZE];
|
||||
|
||||
int main(int argc, char *argv[], char *envp[])
|
||||
{
|
||||
#ifndef PREINIT_USE_LIBC
|
||||
asm("la $gp, _gp\nsw $gp,16($sp)");
|
||||
#endif
|
||||
char *rootdevice = 0;
|
||||
char *p = buf;
|
||||
write(1, banner, strlen(banner));
|
||||
char *rootdevice = 0;
|
||||
char *rootfstype = 0;
|
||||
char *p = buf;
|
||||
write(1, banner, strlen(banner));
|
||||
|
||||
mount("none", "/proc", "proc", 0, NULL);
|
||||
mount("none", "/proc", "proc", 0, NULL);
|
||||
|
||||
int cmdline = open("/proc/cmdline", O_RDONLY, 0);
|
||||
int cmdline = open("/proc/cmdline", O_RDONLY, 0);
|
||||
|
||||
if(cmdline>=0) {
|
||||
int len = read(cmdline, buf, sizeof buf - 1);
|
||||
buf[len]='\0';
|
||||
write(1, "cmdline ", 8);
|
||||
write(1, buf, len);
|
||||
};
|
||||
if(cmdline>=0) {
|
||||
int len = read(cmdline, buf, sizeof buf - 1);
|
||||
buf[len]='\0';
|
||||
write(1, "cmdline ", 8);
|
||||
write(1, buf, len);
|
||||
};
|
||||
|
||||
while(*p) {
|
||||
if(begins_with(p, "root=")) {
|
||||
rootdevice = p + 5;
|
||||
while(*p && (*p != ' ')) p++;
|
||||
*p= '\0';
|
||||
}
|
||||
while(*p && (*p != ' ')) p++;
|
||||
p++;
|
||||
}
|
||||
parseopts(buf, &rootdevice, &rootfstype);
|
||||
|
||||
if(rootdevice) {
|
||||
if(!rootfstype) rootfstype = "jffs2"; /* backward compatibility */
|
||||
write(1, "rootdevice ", 11);
|
||||
write(1, rootdevice, strlen(rootdevice));
|
||||
write(1, " (", 2);
|
||||
write(1, rootfstype, strlen(rootfstype));
|
||||
write(1, ")\n", 1);
|
||||
|
||||
if(rootdevice) {
|
||||
write(1, "rootdevice ", 11);
|
||||
write(1, rootdevice, strlen(rootdevice));
|
||||
write(1, "\n", 1);
|
||||
AVER(mount(rootdevice, "/target/persist", rootfstype, 0, NULL));
|
||||
AVER(mount("/target/persist/nix", "/target/nix",
|
||||
"bind", MS_BIND, NULL));
|
||||
|
||||
AVER(mount(rootdevice, "/target/persist", "jffs2", 0, NULL));
|
||||
AVER(mount("/target/persist/nix", "/target/nix",
|
||||
"bind", MS_BIND, NULL));
|
||||
char *exec_args[] = { "activate", "/target", NULL };
|
||||
AVER(fork_exec("/target/persist/activate", exec_args));
|
||||
AVER(chdir("/target"));
|
||||
|
||||
char *exec_args[] = { "activate", "/target", NULL };
|
||||
fork_exec("/target/persist/activate", exec_args);
|
||||
AVER(chdir("/target"));
|
||||
AVER(mount("/target", "/", "bind", MS_BIND | MS_REC, NULL));
|
||||
AVER(chroot("."));
|
||||
|
||||
AVER(mount("/target", "/", "bind", MS_BIND | MS_REC, NULL));
|
||||
AVER(chroot("."));
|
||||
argv[0] = "init";
|
||||
argv[1] = NULL;
|
||||
|
||||
AVER(execve("/persist/init", argv, envp));
|
||||
}
|
||||
argv[0] = "init";
|
||||
argv[1] = NULL;
|
||||
AVER(execve("/persist/init", argv, envp));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
with import <nixpkgs> {};
|
||||
mkShell {
|
||||
name = "preinit-env";
|
||||
src = ./.;
|
||||
}
|
||||
@@ -5,15 +5,15 @@
|
||||
, symlinkJoin
|
||||
, lib
|
||||
}: let
|
||||
mips-vm = writeShellScriptBin "mips-vm" ''
|
||||
run-liminix-vm = writeShellScriptBin "run-liminix-vm" ''
|
||||
export PATH="${lib.makeBinPath [qemu]}:$PATH"
|
||||
${builtins.readFile ./mips-vm.sh}
|
||||
${builtins.readFile ./run-liminix-vm.sh}
|
||||
'';
|
||||
connect = writeShellScriptBin "connect-vm" ''
|
||||
export PATH="${lib.makeBinPath [socat]}:$PATH"
|
||||
socat -,raw,echo=0,icanon=0,isig=0,icrnl=0,escape=0x0f unix-connect:$1
|
||||
'';
|
||||
in symlinkJoin {
|
||||
name = "mips-vm";
|
||||
paths = [ mips-vm connect ];
|
||||
name = "run-liminix-vm";
|
||||
paths = [ run-liminix-vm connect ];
|
||||
}
|
||||
@@ -12,6 +12,12 @@ usage(){
|
||||
exit 1
|
||||
}
|
||||
|
||||
arch="mips"
|
||||
if test "$1" = "--arch" ; then
|
||||
arch=$2
|
||||
shift;shift
|
||||
fi
|
||||
|
||||
if test "$1" = "--background" ; then
|
||||
statedir=$2
|
||||
if test -z "$statedir" || ! test -d $statedir ; then
|
||||
@@ -31,7 +37,7 @@ test -n "$2" || usage
|
||||
|
||||
lan=${LAN-"socket,mcast=230.0.0.1:1235,localaddr=127.0.0.1"}
|
||||
|
||||
rootfs=$(mktemp mips-vm-fs-XXXXXX)
|
||||
rootfs=$(mktemp run-liminix-vm-fs-XXXXXX)
|
||||
dd if=/dev/zero of=$rootfs bs=1M count=16 conv=sync
|
||||
dd if=$2 of=$rootfs bs=65536 conv=sync,nocreat,notrunc
|
||||
|
||||
@@ -39,13 +45,21 @@ if test -n "$3"; then
|
||||
initramfs="-initrd $3"
|
||||
fi
|
||||
|
||||
case "$arch" in
|
||||
mips)
|
||||
QEMU="qemu-system-mips -M malta"
|
||||
;;
|
||||
aarch64)
|
||||
QEMU="qemu-system-aarch64 -M virt -semihosting -cpu cortex-a72"
|
||||
;;
|
||||
esac
|
||||
|
||||
INIT=${INIT-/bin/init}
|
||||
echo $QEMU_OPTIONS
|
||||
qemu-system-mips \
|
||||
-M malta -m 256 \
|
||||
set -x
|
||||
$QEMU \
|
||||
-m 256 \
|
||||
-echr 16 \
|
||||
-append "liminix default console=ttyS0,38400n8 panic=10 oops=panic init=$INIT loglevel=8 root=/dev/mtdblock0 block2mtd.block2mtd=/dev/vda,65536" \
|
||||
-append "$CMDLINE liminix root=/dev/mtdblock0 block2mtd.block2mtd=/dev/vda,65536" \
|
||||
-drive file=$rootfs,format=raw,readonly=off,if=virtio,index=0 \
|
||||
${initramfs} \
|
||||
-netdev socket,id=access,mcast=230.0.0.1:1234,localaddr=127.0.0.1 \
|
||||
@@ -12,7 +12,7 @@ in stdenv.mkDerivation {
|
||||
sha256 = "0hi2rj1a1fbvr5n1090q1zzigjyxmn643jzrwngw4ij0g82za3al";
|
||||
};
|
||||
name = "swconfig";
|
||||
buildInputs = [ buildPackages.pkgconfig ];
|
||||
buildInputs = [ buildPackages.pkg-config ];
|
||||
nativeBuildInputs = [ libnl ];
|
||||
CFLAGS="-O2 -Ifrom_kernel -I${libnl.dev}/include/libnl3";
|
||||
LDFLAGS="-L${libnl.out}/lib";
|
||||
|
||||
@@ -68,7 +68,7 @@ in attrset:
|
||||
src = ./.;
|
||||
|
||||
CFLAGS = "-Os";
|
||||
LDFLAGS = "-static";
|
||||
LDFLAGS = "-static -Xlinker -static";
|
||||
|
||||
postConfigure = ''
|
||||
cp ${makedevs} makedevs.c
|
||||
|
||||
+22
-11
@@ -195,19 +195,22 @@ function tftp:handle_RRQ(socket, host, port, source, options)
|
||||
]]
|
||||
yield(false, true)
|
||||
end
|
||||
socket:sendto(self.DATA(data, tid), host, port)
|
||||
--[[ Now check for an ACK.
|
||||
RFC1350 requires that for every packet sent, an ACK is received
|
||||
before the next packet can be sent.
|
||||
]]
|
||||
|
||||
local acked
|
||||
local retried = 0
|
||||
local timeout = time() + timeout_secs
|
||||
local timedout = false
|
||||
repeat
|
||||
socket:sendto(self.DATA(data, tid), host, port)
|
||||
--[[ Now check for an ACK.
|
||||
RFC1350 requires that for every packet sent, an ACK is received
|
||||
before the next packet can be sent.
|
||||
]]
|
||||
yield(true, false) -- we need to wait until the socket is readable again
|
||||
local ack, ackhost, ackport = socket:recvfrom(ACKSIZE)
|
||||
if ackhost ~= host or ackport ~= port or self.parse_ACK(ack) ~= tid then
|
||||
local ack_sequence = self.parse_ACK(ack)
|
||||
|
||||
if ackhost ~= host or ackport ~= port then
|
||||
--[[https://tools.ietf.org/html/rfc1350#page-5
|
||||
"If a source TID does not match, the packet should be
|
||||
discarded as erroneously sent from somewhere else.
|
||||
@@ -216,13 +219,21 @@ function tftp:handle_RRQ(socket, host, port, source, options)
|
||||
]]
|
||||
socket:sendto(err(ERR_UNKNOWN_ID), ackhost, ackport)
|
||||
yield(true, false)
|
||||
else
|
||||
acked = true
|
||||
end
|
||||
elseif ack_sequence ~= tid then
|
||||
-- this looks confusing, but the local variable
|
||||
-- "tid" here is actually block number (aka
|
||||
-- sequence number), not tid at all.
|
||||
log(("ack received for old block %d (expecting %d)"):format(ack_sequence, tid))
|
||||
acked = false
|
||||
else
|
||||
acked = true
|
||||
end
|
||||
|
||||
if not acked then log("resending") end
|
||||
retried = retried + 1
|
||||
timedout = time() > timeout
|
||||
until acked or retried > ACK_RETRIES or timedout
|
||||
if timedout or retried > ACK_RETRIES then
|
||||
until acked or retried > ACK_RETRIES
|
||||
if retried > ACK_RETRIES then
|
||||
--There doesn't seem to be a standard error for timeout.
|
||||
socket:sendto(err("Ack timeout"), host, port)
|
||||
error("Timeout waiting for ACK")
|
||||
|
||||
@@ -7,15 +7,13 @@ let img = (import liminix {
|
||||
liminix-config = ./configuration.nix;
|
||||
}).outputs.vmroot;
|
||||
pkgs = import <nixpkgs> { overlays = [(import ../../overlay.nix)]; };
|
||||
inherit (pkgs.pkgsBuildBuild) routeros mips-vm;
|
||||
in pkgs.runCommand "check" {
|
||||
nativeBuildInputs = with pkgs; [
|
||||
mips-vm
|
||||
expect
|
||||
socat
|
||||
] ;
|
||||
} ''
|
||||
mkdir vm
|
||||
mips-vm --background ./vm ${img}/vmlinux ${img}/rootfs
|
||||
${img}/run.sh --background ./vm
|
||||
expect ${./script.expect} >$out
|
||||
''
|
||||
|
||||
@@ -9,11 +9,11 @@ let lmx = (import liminix {
|
||||
rogue = lmx.pkgs.rogue;
|
||||
img = lmx.outputs.vmroot;
|
||||
pkgs = import <nixpkgs> { overlays = [(import ../../overlay.nix)]; };
|
||||
inherit (pkgs.pkgsBuildBuild) mips-vm;
|
||||
inherit (pkgs.pkgsBuildBuild) run-liminix-vm;
|
||||
in pkgs.runCommand "check" {
|
||||
nativeBuildInputs = with pkgs; [
|
||||
expect
|
||||
mips-vm
|
||||
run-liminix-vm
|
||||
socat
|
||||
min-copy-closure
|
||||
rogue
|
||||
@@ -22,7 +22,7 @@ in pkgs.runCommand "check" {
|
||||
. ${../test-helpers.sh}
|
||||
|
||||
mkdir vm
|
||||
LAN=user,hostfwd=tcp::2022-:22 mips-vm --background ./vm ${img}/vmlinux ${img}/rootfs
|
||||
LAN=user,hostfwd=tcp::2022-:22 run-liminix-vm --background ./vm ${img}/vmlinux ${img}/rootfs
|
||||
expect ${./wait-until-ready.expect}
|
||||
export SSH_COMMAND="ssh -o StrictHostKeyChecking=no -p 2022 -i ${./id}"
|
||||
$SSH_COMMAND root@localhost echo ready
|
||||
|
||||
@@ -7,7 +7,7 @@ let img = (import liminix {
|
||||
liminix-config = ./configuration.nix;
|
||||
}).outputs.default;
|
||||
pkgs = import <nixpkgs> { overlays = [(import ../../overlay.nix)]; };
|
||||
inherit (pkgs.pkgsBuildBuild) routeros mips-vm;
|
||||
inherit (pkgs.pkgsBuildBuild) routeros run-liminix-vm;
|
||||
in pkgs.runCommand "check" {
|
||||
nativeBuildInputs = with pkgs; [
|
||||
python3Packages.scapy
|
||||
@@ -15,7 +15,6 @@ in pkgs.runCommand "check" {
|
||||
jq
|
||||
socat
|
||||
routeros.routeros
|
||||
mips-vm
|
||||
] ;
|
||||
} ''
|
||||
serverstatedir=$(mktemp -d -t routeros-XXXXXX)
|
||||
@@ -27,7 +26,7 @@ export MPLCONFIGDIR=$(mktemp -d -t routeros-XXXXXX)
|
||||
|
||||
routeros $serverstatedir
|
||||
mkdir vm
|
||||
mips-vm --background ./vm ${img}/vmlinux ${img}/rootfs
|
||||
${img}/run.sh --background ./vm
|
||||
expect ${./getaddress.expect}
|
||||
|
||||
set -o pipefail
|
||||
|
||||
+3
-5
@@ -7,17 +7,15 @@ let img = (import liminix {
|
||||
liminix-config = ./configuration.nix;
|
||||
}).outputs.default;
|
||||
pkgs = import <nixpkgs> { overlays = [(import ../../overlay.nix)]; };
|
||||
inherit (pkgs.pkgsBuildBuild) mips-vm;
|
||||
inherit (pkgs.pkgsBuildBuild) run-liminix-vm;
|
||||
in pkgs.runCommand "check" {
|
||||
nativeBuildInputs = with pkgs; [
|
||||
expect
|
||||
mips-vm
|
||||
socat
|
||||
expect socat
|
||||
] ;
|
||||
} ''
|
||||
. ${../test-helpers.sh}
|
||||
|
||||
mkdir vm
|
||||
mips-vm --background ./vm ${img}/vmlinux ${img}/rootfs
|
||||
${img}/run.sh --background ./vm
|
||||
expect ${./wait-for-wlan.expect} |tee output && mv output $out
|
||||
''
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{ ... }:
|
||||
{
|
||||
imports = [
|
||||
./vanilla-configuration.nix
|
||||
./modules/standard.nix
|
||||
];
|
||||
}
|
||||
Reference in New Issue
Block a user