default.nix (14531B)
1 { 2 description = '' 3 == Turris Omnia 4 5 This is a 32 bit ARMv7 MVEBU device, which is usually shipped with 6 TurrisOS, an OpenWrt-based system. Rather than reformatting the builtin 7 storage, we install Liminix on to the existing btrfs filesystem so that 8 the vendor snapshot/recovery system continues to work (and provides you 9 an easy rollback if you decide you don't like Liminix after all). 10 11 The install process has two stages, and is intended that you should not 12 need to open the device and add a serial console (although it may be 13 handy for visibility, and in case anything goes wrong). First we build a 14 minimal installation/recovery system, then we reboot into that recovery 15 image to prepare the device for the full target install. 16 17 === Installation using a USB stick 18 19 First, build the image for the USB stick. Review 20 `+examples/recovery.nix+` in order to change the default root password 21 (which is `+secret+`) and/or the SSH keys, then build it with 22 23 [source,console] 24 ---- 25 $ nix-build -I liminix-config=./examples/recovery.nix \ 26 --arg device "import ./devices/turris-omnia" \ 27 -A outputs.mbrimage -o mbrimage 28 $ file -L mbrimage 29 mbrimage: DOS/MBR boot sector; partition 1 : ID=0x83, active, start-CHS (0x0,0,5), end-CHS (0x6,130,26), startsector 4, 104602 sectors 30 ---- 31 32 Next, copy the image from your build machine to a USB storage medium 33 using `+dd+` or your other most favoured file copying tool, which might 34 be a comand something like this: 35 36 [source,console] 37 ---- 38 $ dd if=mbrimage of=/dev/path/to/the/usb/stick \ 39 bs=1M conv=fdatasync status=progress 40 ---- 41 42 The Omnia's default boot order only checks USB after it has failed to 43 boot from eMMC, which is not ideal for our purpose. Unless you have a 44 serial cable, the easiest way to change this is by booting to TurrisOS 45 and logging in with ssh: 46 47 [source,console] 48 ---- 49 root@turris:/# fw_printenv boot_targets 50 boot_targets=mmc0 nvme0 scsi0 usb0 pxe dhcp 51 root@turris:/# fw_setenv boot_targets usb0 mmc0 52 root@turris:/# fw_printenv boot_targets 53 boot_targets=usb0 mmc0 54 root@turris:/# reboot -f 55 ---- 56 57 It should now boot into the recovery image. It expects a network cable 58 to be plugged into LAN2 with something on the other end of it that 59 serves DHCP requests. Check your DHCP server logs for a request from a 60 `+liminix-recovery+` host and figure out what IP address was assigned. 61 62 [source,console] 63 ---- 64 $ ssh liminix-recovery.lan 65 ---- 66 67 You should get a "Busybox" banner and a root prompt. Now you can start 68 preparing the device to install Liminix on it. First we'll mount the 69 root filesystem and take a snapshot: 70 71 [source,console] 72 ---- 73 # mkdir /dest && mount /dev/mmcblk0p1 /dest 74 # schnapps -d /dest create "pre liminix" 75 # schnapps -d /dest list 76 ERROR: not a valid btrfs filesystem: / 77 # | Type | Size | Date | Description 78 ------+-----------+-------------+---------------------------+------------------------------------ 79 1 | single | 16.00KiB | 1970-01-01 00:11:49 +0000 | pre liminix 80 ---- 81 82 (`+not a valid btrfs filesystem: /+` is not a real error) 83 84 then we can remove all the files 85 86 [source,console] 87 ---- 88 # rm -r /dest/@/* 89 ---- 90 91 and then it's ready to install the real Liminix system onto. On your 92 build system, create the Liminix configuration you wish to install: here 93 we'll use the `+rotuer+` example. 94 95 [source,console] 96 ---- 97 build$ nix-build -I liminix-config=./examples/rotuer.nix \ 98 --arg device "import ./devices/turris-omnia" \ 99 -A outputs.systemConfiguration 100 ---- 101 102 and then use `+min-copy-closure+` to copy it to the device. 103 104 [source,console] 105 ---- 106 build$ nix-shell --run \ 107 "min-copy-closure -r /dest/@ root@liminix-recovery.lan result" 108 ---- 109 110 and activate it 111 112 [source,console] 113 ---- 114 build$ ssh root@liminix-recovery.lan \ 115 "/dest/@/$(readlink result)/bin/install /dest/@" 116 ---- 117 118 The final steps are performed directly on the device again: add a 119 symlink so U-Boot can find `+/boot+`, then restore the default boot 120 order and reboot into the new configuration. 121 122 [source,console] 123 ---- 124 # cd /dest && ln -s @/boot . 125 # fw_setenv boot_targets "mmc0 nvme0 scsi0 usb0 pxe dhcp" 126 # cd / ; umount /dest 127 # reboot 128 ---- 129 130 === Installation using a TFTP server and serial console 131 132 If you have a <<serial,serial console connection>> and a TFTP server, and would 133 rather use them than fiddling with USB sticks, the 134 `+examples/recovery.nix+` configuration also works using the 135 `+tftpboot+` output. So you can do 136 137 [source,console] 138 ---- 139 build$ nix-build -I liminix-config=./examples/recovery.nix \ 140 --arg device "import ./devices/turris-omnia" \ 141 -A outputs.tftpboot 142 ---- 143 144 and then paste the generated `+result/boot.scr+` into U-Boot, and you 145 will end up with the same system as you would have had after booting 146 from USB. If you don't have a serial console connection you could 147 probably even get clever with elaborate use of `+fw_setenv+`, but that 148 is left as an exercise for the reader. 149 150 ''; 151 152 system = { 153 crossSystem = { 154 config = "armv7l-unknown-linux-musleabihf"; 155 }; 156 }; 157 158 module = 159 { 160 pkgs, 161 config, 162 lib, 163 lim, 164 ... 165 }: 166 let 167 inherit (pkgs.liminix.services) oneshot; 168 inherit (pkgs) liminix; 169 mtd_by_name_links = pkgs.liminix.services.oneshot rec { 170 name = "mtd_by_name_links"; 171 up = '' 172 mkdir -p /dev/mtd/by-name 173 cd /dev/mtd/by-name 174 for i in /sys/class/mtd/mtd*[0-9]; do 175 ln -s ../../$(basename $i) $(cat $i/name) 176 done 177 ''; 178 }; 179 in 180 { 181 imports = [ 182 ../../modules/arch/arm.nix 183 ../../modules/outputs/tftpboot.nix 184 ../../modules/outputs/mbrimage.nix 185 ]; 186 187 config = { 188 rootfsType = lib.mkDefault "btrfs"; # override this if you are building tftpboot 189 rootOptions = lib.mkDefault "subvol=@"; 190 191 services.mtd-name-links = mtd_by_name_links; 192 kernel = { 193 src = pkgs.pkgsBuildBuild.fetchurl { 194 name = "linux.tar.gz"; 195 url = "https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.18.54.tar.gz"; 196 hash = "sha256-rHSOttt5N+piPpXCEP6aELvvOzXX+kmoBCkcoP1kOvk="; 197 }; 198 version = "6.18.54"; 199 config = { 200 PCI = "y"; 201 OF = "y"; 202 MEMORY = "y"; # for MVEBU_DEVBUS 203 DMADEVICES = "y"; # for MV_XOR 204 CPU_V7 = "y"; 205 ARCH_MULTIPLATFORM = "y"; 206 ARCH_MVEBU = "y"; 207 ARCH_MULTI_V7 = "y"; 208 PCI_MVEBU = "y"; 209 AHCI_MVEBU = "y"; 210 211 RTC_CLASS = "y"; 212 RTC_DRV_ARMADA38X = "y"; # this may be useful anyway? 213 214 EXPERT = "y"; 215 ALLOW_DEV_COREDUMP = "n"; 216 217 # dts has a compatible for this but dmesg is not 218 # showing it 219 EEPROM_AT24 = "y"; # atmel,24c64 220 221 I2C = "y"; 222 I2C_MUX = "y"; 223 I2C_MUX_PCA954x = "y"; 224 225 MACH_ARMADA_38X = "y"; 226 SMP = "y"; 227 # this is disabled for the moment because it relies on a 228 # GCC plugin that requires gmp.h to build, and I can't see 229 # right now how to confgure it to find gmp 230 STACKPROTECTOR_PER_TASK = "n"; 231 NR_CPUS = "4"; 232 VFP = "y"; 233 NEON = "y"; 234 235 # WARNING: unmet direct dependencies detected for ARCH_WANT_LIBATA_LEDS 236 ATA = "y"; 237 238 BLOCK = "y"; 239 MMC = "y"; 240 PWRSEQ_EMMC = "y"; # ??? 241 PWRSEQ_SIMPLE = "y"; # ??? 242 MMC_BLOCK = "y"; 243 244 MMC_SDHCI = "y"; 245 MMC_SDHCI_PLTFM = "y"; 246 MMC_SDHCI_PXAV3 = "y"; 247 MMC_MVSDIO = "y"; 248 249 SERIAL_8250 = "y"; 250 SERIAL_8250_CONSOLE = "y"; 251 SERIAL_OF_PLATFORM = "y"; 252 SERIAL_MVEBU_UART = "y"; 253 SERIAL_MVEBU_CONSOLE = "y"; 254 255 SERIAL_8250_DMA = "y"; 256 SERIAL_8250_DW = "y"; 257 SERIAL_8250_EXTENDED = "y"; 258 SERIAL_8250_MANY_PORTS = "y"; 259 SERIAL_8250_SHARE_IRQ = "y"; 260 OF_ADDRESS = "y"; 261 OF_MDIO = "y"; 262 263 WATCHDOG = "y"; # watchdog is enabled by u-boot 264 ORION_WATCHDOG = "y"; # so is non-optional to keep feeding 265 266 MVEBU_DEVBUS = "y"; # "Device Bus controller ... flash devices such as NOR, NAND, SRAM, and FPGA" 267 MVMDIO = "y"; 268 MVNETA = "y"; 269 MVNETA_BM = "y"; 270 MVNETA_BM_ENABLE = "y"; 271 SRAM = "y"; # mmio-sram is "compatible" for bm_bppi reqd by BM 272 PHY_MVEBU_A38X_COMPHY = "y"; # for eth2 273 MARVELL_PHY = "y"; 274 275 MVPP2 = "y"; 276 MV_XOR = "y"; 277 278 # there is NOR flash on this device, which is used for U-Boot 279 # and the rescue system (which we don't interfere with) but 280 # also for the U-Boot environment variables (which we might 281 # need to meddle with) 282 MTD_SPI_NOR = "y"; 283 SPI = "y"; 284 SPI_MASTER = "y"; 285 SPI_ORION = "y"; 286 287 NET_DSA = "y"; 288 NET_DSA_MV88E6XXX = "y"; # depends on PTP_1588_CLOCK_OPTIONAL 289 }; 290 conditionalConfig = { 291 USB = { 292 USB_XHCI_MVEBU = "y"; 293 USB_XHCI_HCD = "y"; 294 }; 295 WLAN = { 296 WLAN_VENDOR_ATH = "y"; 297 ATH_COMMON = "m"; 298 ATH9K = "m"; 299 ATH9K_PCI = "y"; 300 ATH10K = "m"; 301 ATH10K_PCI = "m"; 302 ATH10K_DEBUG = "y"; 303 }; 304 }; 305 }; 306 boot = { 307 loader.extlinux.enable = lib.mkDefault true; # override this if you are building tftpboot 308 commandLine = [ 309 "console=ttyS0,115200" 310 "pcie_aspm=off" # ath9k pci incompatible with PCIe ASPM 311 ]; 312 }; 313 filesystem = 314 let 315 inherit (pkgs.pseudofile) dir symlink; 316 firmware = pkgs.stdenv.mkDerivation { 317 name = "wlan-firmware"; 318 phases = [ "installPhase" ]; 319 installPhase = '' 320 mkdir $out 321 cp -r ${pkgs.linux-firmware}/lib/firmware/ath10k/QCA988X $out 322 ''; 323 }; 324 in 325 dir { 326 lib = dir { 327 firmware = dir { 328 ath10k = symlink firmware; 329 }; 330 }; 331 etc = dir { 332 "fw_env.config" = 333 let 334 f = pkgs.writeText "fw_env.config" '' 335 /dev/mtd/by-name/u-boot-env 0x0 0x10000 0x10000 336 ''; 337 in 338 symlink f; 339 }; 340 }; 341 342 boot.tftp = { 343 loadAddress = lim.parseInt "0x1700000"; 344 kernelFormat = "zimage"; 345 compressRoot = true; 346 }; 347 348 hardware = 349 let 350 mac80211 = pkgs.kmodloader.override { 351 inherit (config.system.outputs) kernel; 352 targets = [ 353 "ath9k" 354 "ath10k_pci" 355 ]; 356 }; 357 in 358 { 359 defaultOutput = "updater"; 360 loadAddress = lim.parseInt "0x00800000"; # "0x00008000"; 361 entryPoint = lim.parseInt "0x00800000"; # "0x00008000"; 362 rootDevice = "/dev/mmcblk0p1"; 363 364 dts = { 365 src = "${config.system.outputs.kernel.modulesupport}/arch/arm/boot/dts/marvell/armada-385-turris-omnia.dts"; 366 includePaths = [ 367 "${config.system.outputs.kernel.modulesupport}/arch/arm/boot/dts/marvell/" 368 ]; 369 }; 370 flash.eraseBlockSize = 65536; # only used for tftpboot 371 networkInterfaces = 372 let 373 inherit (config.system.service.network) link; 374 in 375 rec { 376 en70000 = link.build { 377 # in armada-38x.dtsi this is eth0. 378 # It's connected to port 5 of the 88E6176 switch 379 devpath = "/devices/platform/soc/soc:internal-regs/f1070000.ethernet"; 380 # name is unambiguous but not very semantic 381 ifname = "en70000"; 382 }; 383 en30000 = link.build { 384 # in armada-38x.dtsi this is eth1 385 # It's connected to port 6 of the 88E6176 switch 386 devpath = "/devices/platform/soc/soc:internal-regs/f1030000.ethernet"; 387 # name is unambiguous but not very semantic 388 ifname = "en30000"; 389 }; 390 # the default (from the dts? I'm guessing) behavour for 391 # lan ports on the switch is to attach them to 392 # en30000. It should be possible to do something better, 393 # per 394 # https://www.kernel.org/doc/html/latest/networking/dsa/configuration.html#affinity-of-user-ports-to-cpu-ports 395 # but apparently OpenWrt doesn't either so maybe it's more 396 # complicated than it looks. 397 398 wan = link.build { 399 # in armada-38x.dtsi this is eth2. It may be connected to 400 # an ethernet phy or to the SFP cage, depending on a gpio 401 devpath = "/devices/platform/soc/soc:internal-regs/f1034000.ethernet"; 402 ifname = "wan"; 403 }; 404 405 lan0 = link.build { ifname = "lan0"; }; 406 lan1 = link.build { ifname = "lan1"; }; 407 lan2 = link.build { ifname = "lan2"; }; 408 lan3 = link.build { ifname = "lan3"; }; 409 lan4 = link.build { ifname = "lan4"; }; 410 lan5 = link.build { ifname = "lan5"; }; 411 lan = lan0; # maybe we should build a bridge? 412 413 wlan = link.build { 414 ifname = "wlan0"; 415 dependencies = [ mac80211 ]; 416 }; 417 wlan5 = link.build { 418 ifname = "wlan1"; 419 dependencies = [ mac80211 ]; 420 }; 421 }; 422 }; 423 }; 424 }; 425 }