2
0

65 Commits

Author SHA1 Message Date
dan 4ae1edbce7 first boot on armv7 / Turris Omnia 2023-10-29 11:51:49 +00:00
dan 1a5efff3ae in uimage FIT, honour ${arch} 2023-10-29 11:50:23 +00:00
dan 8772e64b94 extract NETDEVICES kconfig to kernel.nix module 2023-10-29 11:49:03 +00:00
dan e6ef4f78bb "ubimage" module contains ubifs image + instructions
Presently either you run this from U-Boot or you figure out for
yourself how to kexecboot into a recovery system :-)
2023-10-21 23:20:53 +01:00
dan d2f517a4e9 preinit.c: reindent 2023-10-19 21:02:18 +01:00
dan 0f38ee0e9c remove PREINIT_USE_LIBC option as it is now the only option 2023-10-19 18:59:02 +01:00
dan 61dc5beca8 preinit: parse rootfstype from kernel command line 2023-10-19 18:56:09 +01:00
dan f3225c2bd5 delete dup outputs.systemConfiguration
perhaps this should go into initramfs.nix not jffs2.nix
2023-10-19 10:09:08 +01:00
dan 8798ee9830 partial fix for timeout handling
1) "Unknown transfer id" message was because the local variable "tid"
is not a transfer id, it is a sequence number  - so the check was
actually comparing expected vs actual acknowledged sequence number,
not TID.  It's still a problem if we get the wrong one, but it
indicates a lost packet (so we should resend) not a packet that was
sent from somewhere else.

2) if the ACK packet has not been received, our retry should involve
_resending_ it, not just trying to wait for it again.

3) I have removed the timeout condition for terminating the resend
loop, because in practice (assuming both ends have the same timeout
setting) all it did was ensure that the loop only ran once. The
timeout is supposed to regulate how long we wait for before retrying
(it doesn't do this, we wait indefinitely), not how long we wait for
before giving up.
2023-10-18 23:35:23 +01:00
dan 629914f65e initial support for ubifs 2023-10-16 19:55:17 +01:00
dan 0693cf23d8 preinit: improve error logging for fork_exec 2023-10-12 19:00:57 +01:00
dan c341eb46b6 use hostPlatform.linuxArch in kernel derivation 2023-10-12 18:59:45 +01:00
dan 1a369ff3bf preinit: remove no-longer-used mips assembly 2023-10-12 18:57:54 +01:00
dan 364c5faf9e tftpboot: fix errors in phram partition size calc 2023-10-10 20:26:27 +01:00
dan 80a09a9a9b rt3200: move the entryPoint 2023-10-10 20:25:42 +01:00
dan 3518e2ecca Merge branch 'hark-how-all-the-belkin-rings' 2023-10-09 19:56:48 +01:00
dan fc2eb6ee4d rt3200 mumble 2023-10-09 19:56:22 +01:00
dan bd20f3e419 uimage: make fit optional 2023-10-09 19:47:57 +01:00
dan f62ad0e1d7 use "tftpboot" instead of "tftp" in u-boot commands
openwrt's u-boot installation doesn't accept the short form
2023-10-09 19:47:57 +01:00
dan ed792e0dc0 rt3200: swap wireless driver load order
mt7515e loads first, so that wlan0 is 2.4GHz
mt7515e loads after, so that 5GHz gets wlan1
2023-10-09 19:47:57 +01:00
dan d025c33d30 rt3200: enable flash/mtd 2023-10-09 19:47:57 +01:00
dan a755c9c3c5 delete some inapplicable kconfig 2023-10-09 19:47:57 +01:00
dan fdf74fa06b add mt7915, 7615 wifi modules
7915 won't work until we have working MTD, because it needs to
read calibration data from flash
2023-10-09 19:47:57 +01:00
dan b8dea2ed34 rt3200: add DSA
this creates a bunch of network interfaces {lan[1234],wan}@eth0
2023-10-09 19:47:57 +01:00
dan c18f07f02f aarch64: make tftpboot work
- patch dtb to add reserved-memory stanza for the phram device to use
  (aarch64 does not accept memmap= command line option)

- patch phram driver to use memremap() instead of ioremap() as
  ioremap can't be used for system ram on arm devices
2023-10-09 19:47:57 +01:00
dan 1c4412a1f4 rt3200: enable serial console 2023-10-09 19:47:57 +01:00
dan dbc16edf96 don't use ttyAMA0 console on all aarch64, just qemu 2023-10-09 19:47:57 +01:00
dan 528d619d76 WIP kernel config for belkin rt3200 2023-10-09 19:47:57 +01:00
dan 269e972970 use FIT images not appended DTB for aarch64 uimage 2023-10-07 22:52:09 +01:00
dan 2a5669c2cd enable openwrt mediatek family patches 2023-10-07 22:42:31 +01:00
dan 4df248323c use MTD_SPI_NOR_USE_4K_SECTORS only on MIPS 2023-10-07 22:41:15 +01:00
dan b01840fa7f add support for mt7915, mt7615 in mac80211 package
mt7915 additionally requires working MTD, so that it can
read calibration data from flash
2023-10-07 22:29:38 +01:00
dan a896c4e31c rename wlan services for devices with > 1 radio
let's standardise on having 2.4GHz radio be "wlan", and
5GHz as "wlan5"
2023-10-07 22:28:57 +01:00
dan caf8e85061 remove USE_OF from aarch64 2023-10-02 22:43:13 +01:00
dan 76f03ecf0f move OF and USE_OF to arch modules 2023-09-30 21:52:13 +01:00
dan f57997c605 remove explicit MTD_BLKDEVS, is selected by MTD_BLOCK 2023-09-30 21:45:50 +01:00
dan 3c483ebd9a set PARTITION_ADVANCED only in ramdisk module 2023-09-30 21:29:12 +01:00
dan a30b658999 remove CMDLINE_PARTITION, we don't need it
(at least, I think we don't)
2023-09-30 21:20:29 +01:00
dan 1ec7fcc197 remove MTD_CMDLINE_PARTS from per-device config
it's enabled in modules/ramdisk, which is required by
flashimage and tftpboot
2023-09-29 21:00:25 +01:00
dan f9f934b40c spell GL.iNet consistently in docs 2023-09-28 23:22:54 +01:00
dan e3c8ab351e fix sphinx warning 2023-09-28 12:19:48 +01:00
dan 7e13eda490 add hardware device descriptions to doc
most of the text is recycled and follows no particular format
2023-09-28 12:17:30 +01:00
dan 3a2f074199 disable 4k flash erase blocks everywhere 2023-09-27 22:10:17 +01:00
dan 74f4e0a2f3 fiddle with manual layout 2023-09-27 13:44:26 +01:00
dan 9133475103 update tutorial discussion of liminix-rebuild 2023-09-27 11:05:06 +01:00
dan abd01a7809 apply mtdpslit patch only for openwrt kernel
it doesn't apply to mainline because there's no mtdsplit
in mainline
2023-09-26 18:40:22 +01:00
dan d0c2b3b274 explain the "demo" example 2023-09-26 18:24:40 +01:00
dan dfe7228b99 fix jffs2 mtd partition splitting on little-endian CPU 2023-09-26 16:43:03 +01:00
dan ab147abd9b less padding in firmware.bin
we only need to align to erase block size, which may be
less than the 128k previously hard-coded
2023-09-26 16:43:03 +01:00
dan bca0c9b26b gl-mt300a use regular-sized flash erase blocks 2023-09-26 16:43:03 +01:00
dan c59a228955 this is the dhcp6c service we want 2023-09-26 16:43:03 +01:00
dan 1673a71831 WIP third example 2023-09-24 23:11:28 +01:00
dan 94e51db649 tests: use run.sh instead of calling run-liminix-vm directly
this makes it easier to run tests on aarch64 where
qemu wants an Image file instead of a vmlinux
2023-09-24 00:24:48 +01:00
dan 3205a38ac9 mac80211 use correct arch 2023-09-24 00:17:32 +01:00
dan 119d6ad379 pkgconfig is now pkg-config 2023-09-21 12:25:35 +01:00
dan 8d356890b0 add aarch64 to CI 2023-09-21 00:00:21 +01:00
dan be22fbbb0a bootable aarch64 liminux with qemu
I may have broken the run-liminix-vm command a bit for MIPS due to
necessary changes in how we pass the command line.  If CI isn't green
for this commit and youre trying the worked examples, I suggest
reverting to the commit before this one.
2023-09-20 22:55:51 +01:00
dan 9f87fd8625 import arch in qemu device config 2023-09-20 21:31:38 +01:00
dan 3a3ee8bbbd fix CI doc error 2023-09-20 21:31:38 +01:00
dan 381730d081 fix CI flashimage error 2023-09-20 21:31:38 +01:00
dan 57eb55de58 found another mips-vm to rename 2023-09-20 19:13:04 +01:00
dan 4389fa15f7 rename mips-vm as run-liminix-vm 2023-09-20 18:33:20 +01:00
dan 4f29bdd3ed detect arch in kernel and uimage
also move kernel builder to pkgs/

FIXME we need to straighten out the mess in calling
dtb.nix/uimage.nix
2023-09-20 18:26:33 +01:00
dan f1c04c7979 extract mips kernel options to module 2023-09-20 17:50:21 +01:00
dan f75995e895 introduce modules/arch/{mipsel,mipseb}.nix
for settings that are common to all mipse[lb] but would not be shared
with e.g. aarch64 or x86
2023-09-20 17:30:05 +01:00
68 changed files with 1962 additions and 284 deletions
+255
View File
@@ -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.
+9 -4
View File
@@ -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
View File
@@ -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
+205
View File
@@ -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 ];
};
};
};
};
}
+25 -18
View File
@@ -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";
};
};
+41 -12
View File
@@ -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";
+16 -10
View File
@@ -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";
+74
View File
@@ -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 ];
};
};
};
};
}
+30 -5
View File
@@ -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 {
+111
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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".
+3 -2
View File
@@ -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 {
+18
View File
@@ -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}
''
+1
View File
@@ -11,6 +11,7 @@ Liminix
admin
development
modules
hardware
Indices and tables
+84 -10
View File
@@ -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
-1
View File
@@ -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";
+203
View File
@@ -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
View File
@@ -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; [
-48
View File
@@ -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
'';
}
+16
View File
@@ -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";
};
};
}
+17
View File
@@ -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
];
};
}
+9
View File
@@ -0,0 +1,9 @@
{ lib, pkgs, config, ...}:
{
imports = [ ./mips.nix ];
config = {
kernel.config = {
CPU_BIG_ENDIAN = "y";
};
};
}
+9
View File
@@ -0,0 +1,9 @@
{ lib, pkgs, config, ...}:
{
imports = [ ./mips.nix ];
config = {
kernel.config = {
CPU_LITTLE_ENDIAN = "y";
};
};
}
+6 -2
View File
@@ -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"
+16
View File
@@ -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 ];
}
+21
View File
@@ -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 ];
}
+52
View File
@@ -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";
};
};
};
}
+16
View File
@@ -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 ];
}
+9 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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);
};
+1
View File
@@ -7,5 +7,6 @@
./kexecboot.nix
./flashimage.nix
./jffs2.nix
./ubifs.nix
];
}
+20 -8
View File
@@ -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
'';
};
+50
View File
@@ -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
'';
};
};
}
+112
View File
@@ -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
View File
@@ -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 {};
+6 -1
View File
@@ -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 = ''
+5 -3
View File
@@ -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 = ''
+50
View File
@@ -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:
+79
View File
@@ -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
'';
}
+15 -2
View File
@@ -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 = [
+4
View File
@@ -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;
+4
View File
@@ -0,0 +1,4 @@
CFLAGS+=-Os -fno-stack-protector -fpic -fPIC
LDFLAGS=-static
preinit: preinit.o parseopts.o
-1
View File
@@ -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
+97
View File
@@ -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
View File
@@ -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));
}
}
+5
View File
@@ -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 \
+1 -1
View File
@@ -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";
+1 -1
View File
@@ -68,7 +68,7 @@ in attrset:
src = ./.;
CFLAGS = "-Os";
LDFLAGS = "-static";
LDFLAGS = "-static -Xlinker -static";
postConfigure = ''
cp ${makedevs} makedevs.c
+22 -11
View File
@@ -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")
+1 -3
View File
@@ -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
''
+3 -3
View File
@@ -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
+2 -3
View File
@@ -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
View File
@@ -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
''
+7
View File
@@ -0,0 +1,7 @@
{ ... }:
{
imports = [
./vanilla-configuration.nix
./modules/standard.nix
];
}