aboutsummaryrefslogtreecommitdiff
path: root/server.nix
blob: fc5241024acf0d2920d8bfecc68126b966f516fa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
{
  fetchurl,
  writeText,
  libguestfs-with-appliance-nix,
  pkgsCross,
  qemu,
  runCommand,
  cpio,
  writeShellApplication,
  sshPassword,
  sshPort,
  httpPort,
  writeScript,
}:
let
  updateName = "remarkable-production-image-3.27.1.0-rm2-public";

  updateArchive = fetchurl {
    url =
      # TODO: this is not an official source. might be worth authenticating the updates
      "https://remarkable-software.s3.us-east-2.amazonaws.com/${updateName}.swu";
    hash = "sha256-QPS2n/S1RqkXe0Tg8bAODnvcG5aUOYsAtPvKYwEYGsI=";
  };

  xochitlConfig = writeText "xochitl.conf" ''
    [General]
    DeveloperPassword=${sshPassword}
    AirplaneMode=true
    WebInterfaceEnabled=true
    IdleSuspendDelay=0
    SuspendPowerOffDelay=0
  '';

  usbMacAddr = "52:54:00:12:34:56";

  ifnameUdevRule = writeText "70-persistent-net.rules" ''
    SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", \
      ATTR{address}=="${usbMacAddr}", \
      ATTR{type}=="1", KERNEL=="eth*", NAME="usb0"
  '';

  mmcSymlinkUdevRule = writeText "" ''
    ACTION=="add", KERNEL=="vda", SYMLINK+="mmcblk2"
    ACTION=="add", KERNEL=="vda[0-9]", SYMLINK+="mmcblk2p%n"
  '';

  usbNetworkConfig = writeText "10-usb.network" ''
    [Match]
    Name=usb*

    [Network]
    DHCP=yes
  '';

  usbWaitOnlineService = writeText "usb0-wait-online.service" ''
    [Unit]
    Description=Wait for usb0 to be routable
    Before=network-online.target

    [Service]
    Type=oneshot
    ExecStart=/bin/sh -c 'until ip -4 addr show usb0 | grep -q "inet "; do sleep 1; done'
    RemainAfterExit=yes

    [Install]
    WantedBy=network-online.target
  '';

  xochitlService = writeText "xochitl.service" ''
    [Unit]
    Description=reMarkable main application
    DefaultDependencies=no
    Conflicts=shutdown.target
    Before=shutdown.target
    # add network-online.target to the original dependencies so that xochitl
    # can bind to the usb0 interface when it starts
    Wants=rm-sync.service network-online.target
    After=var-lib-uboot.mount dbus.socket network-online.target
    Requires=dbus.socket

    [Service]
    ExecStart=/usr/bin/xochitl --system
    Restart=on-failure
    NotifyAccess=all
    # required to make xochitl run
    Environment=LD_PRELOAD=/usr/lib/libfakefbdev.so

    [Install]
    WantedBy=multi-user.target
  '';
  
  fakefbdev = pkgsCross.armv7l-hf-multiplatform.stdenv.mkDerivation {
    name = "fakefbdev";
    src = ./fakefbdev;

    installFlags = [ "DESTDIR=${placeholder "out"}" ];
  };

  doNothing = writeScript "do-nothing" ''
    #!/bin/sh
  '';

  kernel = pkgsCross.armv7l-hf-multiplatform.linux;

  rootfsImage = runCommand "rm-rootfs.ext4" { nativeBuildInputs = [ cpio ]; } ''
    cpio -i --file ${updateArchive}
     gzip -dc ${updateName}.ext4.gz > $out
  '';

  diskImage =
    runCommand "rm-disk.qcow2"
      {
        nativeBuildInputs = [
          qemu
          libguestfs-with-appliance-nix
        ];
      }
      ''
        qemu-img create -f qcow2 $out 8G

        guestfish --rw --blocksize=512 --add $out <<EOF
        run

        # not the original partition table of the RM2, sizes differ
        part-init /dev/sda gpt
        part-add /dev/sda p     2048  2099199
        part-add /dev/sda p  2099200 10487807
        part-add /dev/sda p 10487808 12584959
        part-add /dev/sda p 12584960 16775167

        mkfs vfat /dev/sda1
        upload ${rootfsImage} /dev/sda2
        resize2fs /dev/sda2
        # we disable the orphan_file feature to avoid the error
        # 'unsupported feature(s): FEATURE_C12' when e2fsck runs at boot
        mkfs ext4 /dev/sda4 features:^orphan_file

        mount /dev/sda2 /

        download /etc/fstab fstab
        ! sed -i /unknown/d fstab
        upload fstab /etc/fstab

        copy-in ${kernel.modules}/lib/modules /usr/lib

        upload ${ifnameUdevRule} /etc/udev/rules.d/70-persistent-net.rules
        upload ${mmcSymlinkUdevRule} /etc/udev/rules.d/70-mmc-symlinks.rules
        upload ${usbNetworkConfig} /etc/systemd/network/10-usb.network
        upload ${xochitlService} /usr/lib/systemd/system/xochitl.service

        upload ${usbWaitOnlineService} /etc/systemd/system/usb0-wait-online.service
        mkdir-p /etc/systemd/system/network-online.target.wants
        ln-s /etc/systemd/system/usb0-wait-online.service /etc/systemd/system/network-online.target.wants/usb0-wait-online.service

        # delete any systemd units that hinder or delay startup
        rm /usr/lib/systemd/system/wacom_flash.service
        rm /usr/lib/systemd/system/memfaultd.service
        rm /usr/lib/systemd/system/memfault-attributes.service
        rm /usr/lib/systemd/system/swupdate.service
        rm /usr/lib/systemd/system/swupdate.socket
        rm /usr/lib/systemd/system/system-hardening.service
        rm /usr/lib/systemd/system/wpa_supplicant.service

        # stub the usb-ether-once script
        upload ${doNothing} /usr/sbin/usb-ether-once
        chmod 0755 /usr/sbin/usb-ether-once

        upload ${fakefbdev}/lib/libfakefbdev.so /usr/lib/libfakefbdev.so
        chmod 0755 /usr/lib/libfakefbdev.so

        mount /dev/sda4 /home
        cp-a /etc/skel /home/root

        mkdir-p /home/root/.local/share/remarkable/xochitl
        mkdir-p /home/root/.config/remarkable
        upload ${xochitlConfig} /home/root/.config/remarkable/xochitl.conf

        EOF
      '';

in
writeShellApplication {
  name = "reMder-server";

  runtimeInputs = [ qemu ];

  text = ''
    rm -f ./reMder.qcow2
    qemu-img create -b ${diskImage} -F qcow2 -f qcow2 ./reMder.qcow2

    qemu-system-aarch64 \
        -machine virt \
        -m 2048 \
        -kernel ${kernel}/zImage \
        -drive if=none,file=./reMder.qcow2,format=qcow2,id=hd \
        -device virtio-blk-device,drive=hd \
        -netdev user,net=10.11.99.0/27,host=10.11.99.2,dhcpstart=10.11.99.1,hostfwd=tcp::${toString sshPort}-:22,hostfwd=tcp::${toString httpPort}-:80,id=net \
        -device virtio-net-device,netdev=net,mac=${usbMacAddr} \
        -append "console=ttyAMA0 rootfstype=ext4 root=/dev/vda2 rw rootwait init=/sbin/init" \
        -serial mon:stdio \
        -nographic
  '';
}