default.nix (14714B)
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 = 157 { 158 pkgs, 159 config, 160 lib, 161 lim, 162 ... 163 }: 164 let 165 inherit (pkgs.liminix.services) oneshot; 166 inherit (pkgs) liminix; 167 mtd_by_name_links = pkgs.liminix.services.oneshot rec { 168 name = "mtd_by_name_links"; 169 up = '' 170 mkdir -p /dev/mtd/by-name 171 cd /dev/mtd/by-name 172 for i in /sys/class/mtd/mtd*[0-9]; do 173 ln -s ../../$(basename $i) $(cat $i/name) 174 done 175 ''; 176 }; 177 in 178 { 179 imports = [ 180 ../../modules/arch/arm.nix 181 ../../modules/outputs/tftpboot.nix 182 ../../modules/outputs/mbrimage.nix 183 ]; 184 185 config = { 186 rootfsType = lib.mkDefault "btrfs"; # override this if you are building tftpboot 187 rootOptions = lib.mkDefault "subvol=@"; 188 189 services.mtd-name-links = mtd_by_name_links; 190 kernel = { 191 src = pkgs.pkgsBuildBuild.fetchurl { 192 name = "linux.tar.gz"; 193 url = "https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.7.4.tar.gz"; 194 hash = "sha256-wIrmL0BS63nRwWfm4nw+dRNVPUzGh9M4X7LaHzAn5tU="; 195 }; 196 version = "6.7.4"; 197 config = { 198 PCI = "y"; 199 OF = "y"; 200 MEMORY = "y"; # for MVEBU_DEVBUS 201 DMADEVICES = "y"; # for MV_XOR 202 CPU_V7 = "y"; 203 ARCH_MULTIPLATFORM = "y"; 204 ARCH_MVEBU = "y"; 205 ARCH_MULTI_V7 = "y"; 206 PCI_MVEBU = "y"; 207 AHCI_MVEBU = "y"; 208 209 RTC_CLASS = "y"; 210 RTC_DRV_ARMADA38X = "y"; # this may be useful anyway? 211 212 EXPERT = "y"; 213 ALLOW_DEV_COREDUMP = "n"; 214 215 # dts has a compatible for this but dmesg is not 216 # showing it 217 EEPROM_AT24 = "y"; # atmel,24c64 218 219 I2C = "y"; 220 I2C_MUX = "y"; 221 I2C_MUX_PCA954x = "y"; 222 223 MACH_ARMADA_38X = "y"; 224 SMP = "y"; 225 # this is disabled for the moment because it relies on a 226 # GCC plugin that requires gmp.h to build, and I can't see 227 # right now how to confgure it to find gmp 228 STACKPROTECTOR_PER_TASK = "n"; 229 NR_CPUS = "4"; 230 VFP = "y"; 231 NEON = "y"; 232 233 # WARNING: unmet direct dependencies detected for ARCH_WANT_LIBATA_LEDS 234 ATA = "y"; 235 236 BLOCK = "y"; 237 MMC = "y"; 238 PWRSEQ_EMMC = "y"; # ??? 239 PWRSEQ_SIMPLE = "y"; # ??? 240 MMC_BLOCK = "y"; 241 242 MMC_SDHCI = "y"; 243 MMC_SDHCI_PLTFM = "y"; 244 MMC_SDHCI_PXAV3 = "y"; 245 MMC_MVSDIO = "y"; 246 247 SERIAL_8250 = "y"; 248 SERIAL_8250_CONSOLE = "y"; 249 SERIAL_OF_PLATFORM = "y"; 250 SERIAL_MVEBU_UART = "y"; 251 SERIAL_MVEBU_CONSOLE = "y"; 252 253 SERIAL_8250_DMA = "y"; 254 SERIAL_8250_DW = "y"; 255 SERIAL_8250_EXTENDED = "y"; 256 SERIAL_8250_MANY_PORTS = "y"; 257 SERIAL_8250_SHARE_IRQ = "y"; 258 OF_ADDRESS = "y"; 259 OF_MDIO = "y"; 260 261 WATCHDOG = "y"; # watchdog is enabled by u-boot 262 ORION_WATCHDOG = "y"; # so is non-optional to keep feeding 263 264 MVEBU_DEVBUS = "y"; # "Device Bus controller ... flash devices such as NOR, NAND, SRAM, and FPGA" 265 MVMDIO = "y"; 266 MVNETA = "y"; 267 MVNETA_BM = "y"; 268 MVNETA_BM_ENABLE = "y"; 269 SRAM = "y"; # mmio-sram is "compatible" for bm_bppi reqd by BM 270 PHY_MVEBU_A38X_COMPHY = "y"; # for eth2 271 MARVELL_PHY = "y"; 272 273 MVPP2 = "y"; 274 MV_XOR = "y"; 275 276 # there is NOR flash on this device, which is used for U-Boot 277 # and the rescue system (which we don't interfere with) but 278 # also for the U-Boot environment variables (which we might 279 # need to meddle with) 280 MTD_SPI_NOR = "y"; 281 SPI = "y"; 282 SPI_MASTER = "y"; 283 SPI_ORION = "y"; 284 285 NET_DSA = "y"; 286 NET_DSA_MV88E6XXX = "y"; # depends on PTP_1588_CLOCK_OPTIONAL 287 }; 288 conditionalConfig = { 289 USB = { 290 USB_XHCI_MVEBU = "y"; 291 USB_XHCI_HCD = "y"; 292 }; 293 WLAN = { 294 WLAN_VENDOR_ATH = "y"; 295 ATH_COMMON = "m"; 296 ATH9K = "m"; 297 ATH9K_PCI = "y"; 298 ATH10K = "m"; 299 ATH10K_PCI = "m"; 300 ATH10K_DEBUG = "y"; 301 }; 302 }; 303 }; 304 boot = { 305 loader.extlinux.enable = lib.mkDefault true; # override this if you are building tftpboot 306 commandLine = [ 307 "console=ttyS0,115200" 308 "pcie_aspm=off" # ath9k pci incompatible with PCIe ASPM 309 ]; 310 }; 311 filesystem = 312 let 313 inherit (pkgs.pseudofile) dir symlink; 314 firmware = pkgs.stdenv.mkDerivation { 315 name = "wlan-firmware"; 316 phases = [ "installPhase" ]; 317 installPhase = '' 318 mkdir $out 319 cp -r ${pkgs.linux-firmware}/lib/firmware/ath10k/QCA988X $out 320 ''; 321 }; 322 in 323 dir { 324 lib = dir { 325 firmware = dir { 326 ath10k = symlink firmware; 327 }; 328 }; 329 etc = dir { 330 "fw_env.config" = 331 let 332 f = pkgs.writeText "fw_env.config" '' 333 /dev/mtd/by-name/u-boot-env 0x0 0x10000 0x10000 334 ''; 335 in 336 symlink f; 337 }; 338 }; 339 340 boot.tftp = { 341 loadAddress = lim.parseInt "0x1700000"; 342 kernelFormat = "zimage"; 343 compressRoot = true; 344 }; 345 346 hardware = 347 let 348 mac80211 = pkgs.kmodloader.override { 349 inherit (config.system.outputs) kernel; 350 targets = [ 351 "ath9k" 352 "ath10k_pci" 353 ]; 354 }; 355 in 356 { 357 defaultOutput = "updater"; 358 loadAddress = lim.parseInt "0x00800000"; # "0x00008000"; 359 entryPoint = lim.parseInt "0x00800000"; # "0x00008000"; 360 rootDevice = "/dev/mmcblk0p1"; 361 362 dts = { 363 src = "${config.system.outputs.kernel.modulesupport}/arch/arm/boot/dts/marvell/armada-385-turris-omnia.dts"; 364 includePaths = [ 365 "${config.system.outputs.kernel.modulesupport}/arch/arm/boot/dts/marvell/" 366 ]; 367 }; 368 flash.eraseBlockSize = 65536; # only used for tftpboot 369 networkInterfaces = 370 let 371 inherit (config.system.service.network) link; 372 in 373 rec { 374 en70000 = link.build { 375 # in armada-38x.dtsi this is eth0. 376 # It's connected to port 5 of the 88E6176 switch 377 devpath = "/devices/platform/soc/soc:internal-regs/f1070000.ethernet"; 378 # name is unambiguous but not very semantic 379 ifname = "en70000"; 380 }; 381 en30000 = link.build { 382 # in armada-38x.dtsi this is eth1 383 # It's connected to port 6 of the 88E6176 switch 384 devpath = "/devices/platform/soc/soc:internal-regs/f1030000.ethernet"; 385 # name is unambiguous but not very semantic 386 ifname = "en30000"; 387 }; 388 # the default (from the dts? I'm guessing) behavour for 389 # lan ports on the switch is to attach them to 390 # en30000. It should be possible to do something better, 391 # per 392 # https://www.kernel.org/doc/html/latest/networking/dsa/configuration.html#affinity-of-user-ports-to-cpu-ports 393 # but apparently OpenWrt doesn't either so maybe it's more 394 # complicated than it looks. 395 396 wan = link.build { 397 # in armada-38x.dtsi this is eth2. It may be connected to 398 # an ethernet phy or to the SFP cage, depending on a gpio 399 devpath = "/devices/platform/soc/soc:internal-regs/f1034000.ethernet"; 400 ifname = "wan"; 401 }; 402 403 lan0 = link.build { ifname = "lan0"; }; 404 lan1 = link.build { ifname = "lan1"; }; 405 lan2 = link.build { ifname = "lan2"; }; 406 lan3 = link.build { ifname = "lan3"; }; 407 lan4 = link.build { ifname = "lan4"; }; 408 lan5 = link.build { ifname = "lan5"; }; 409 lan = lan0; # maybe we should build a bridge? 410 411 wlan = link.build { 412 ifname = "wlan0"; 413 dependencies = [ mac80211 ]; 414 }; 415 wlan5 = link.build { 416 ifname = "wlan1"; 417 dependencies = [ mac80211 ]; 418 }; 419 }; 420 }; 421 }; 422 }; 423 }