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