guix-patches
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[bug#41011] [PATCH] gnu: grub: Support for network boot via tftp/nfs.


From: Stefan
Subject: [bug#41011] [PATCH] gnu: grub: Support for network boot via tftp/nfs.
Date: Sun, 24 May 2020 02:22:47 +0200

Hi Mathieu!

> Am 23.05.2020 um 10:10 schrieb Mathieu Othacehe <address@hidden>:
> 
>> +    #~(lambda (bootloader target mount-point)
>> +        "Install GRUB as e.g. \"bootx64.efi\" or \"bootarm64.efi\" \"into
>> +EFI-SUBDIR, which is usually \"efi/boot\" or \"efi/Guix\" below the 
>> directory
> 
> Is that TARGET or EFI-SUBDIR?

I really mean with EFI-SUBDIR the argument from the enclosing 
install-grub-efi-net function, and with TARGET the argument of the lambda 
function. The prototype of the lambda function is fixed by some other guix 
machinery, it is not possible to pass an EFI-SUBDIR argument directly.

Take for example this path, the default: "/mnt/boot/efi/boot". Then MOUNT-POINT 
is "/mnt" (like in 'guix system init config.scm /mnt'), TARGET is "/boot" 
(which you also have to specify inside (operating-system (bootloader (target 
"/boot") …) …), and EFI-SUBDIR is the remaining "efi/boot".

In my personal case I need MOUNT-POINT as "/" (well, 'guix system init …' was 
only necessary once), TARGET as "boot-nfs" (my special setup), and EFI-SUBDIR 
as "efi/boot" (because the U-Boot is using this path by default).

For a TFFP server this TARGET with /boot (or /boot-nfs in my case) is the 
directory whose content has to be served. It can’t be /boot/efi (like for the 
normal grub-efi bootloader), as the TFTP server needs to serve several firmware 
files and the U-Boot.¹ When the U-Boot takes control, by default it tries to 
load /efi/boot/bootaa64.efi via TFTP.

It may be possible to configure the U-Boot to look for files below a /efi/Guix 
directory or any other path, I’m not really sure about this yet. At least 
/efi/boot is the documented last resort for UEFI systems to look for some 
“removable madia” to boot over TFTP.

Next GRUB takes control and looks for its grub.cfg and all its other files via 
TFTP at the path specified during 'grub-mknetdir --subdir=…'. As the TFTP root 
is /boot (where U-Boot and other stuff may live), this now has to be efi/boot. 
So you get all GRUB files like /boot/efi/boot/grub.cfg served via TFTP from the 
directory /boot/efi/boot.

For the usual grub-efi-bootloader this is a bit different. Here the normal path 
is /mnt/boot/efi/Guix and for the 'grub-install' command these parameters are 
required: '—-boot-directory /mnt/boot --bootloader-id Guix --efi-directory 
/boot/efi'. Here the "boot" part to '--boot-directory' is hard coded, as well 
as "Guix". So the only part you are free to chose is "efi" in (operating-system 
(bootloader (target "boot/efi") …) …). Additionally the usual 
grub-efi-bootloader looks for /boot/grub.cfg which thus is not residing inside 
the efi partition like all its other files.


>> +TARGET for the system whose root is mounted at MOUNT-POINT."
>> +        (let* ((mount-point-list (delete "" (string-split mount-point #\/)))
>> +               (target-list (delete "" (string-split target #\/)))
>> +               (net-dir
>> +                (string-append "/" (string-join (append
>> +                                                 mount-point-list
>> +                                                 target-list)
>> +                                                "/")))
> 
> I think you can use something like "(in-vicinity mount-point target)"
> to do the same job.

MOUNT-POINT may be just "/", TARGET is "/boot". I’m avoiding double slashes, 
like "//boot", also for all the other paths. In an earlier version I had the 
“problem” that GRUB searched files with the prefix "//efi/boot" via TFTP. This 
confused me on one side, but on the other side I feared that another TFTP 
server might get problems with this.

Avoiding double slashes seems not to be something that in-vicinity is taking 
care about. And TARGET with its "/boot" looks like an absolute path, making it 
an improper argument to in-vicinity: “in-vicinity should allow filename to 
override vicinity when filename is an absolute pathname and vicinity is equal 
to the value of (user-vicinity). The behavior of in-vicinity when filename is 
absolute and vicinity is not equal to the value of (user-vicinity) is 
unspecified.”

A bit further down I need to count the directory levels up to the "/gnu" store 
to construct a proper link, so I need the result of a string-split anyway. 

So all in all – although it looks a bit complicated – I’d like to keep this.

Hm, but I wasn’t that strict with the EFI-SUBDIR argument. And potentially 
MOUNT-POINT may be a relative path, may it? Do you know? In that case there is 
a bug in prepending the "/" to net-dir, turning a relative into an absolute 
path.

> 
>> +          ;; Tell 'grub-install' that there might be a LUKS-encrypted /boot 
>> or
>> +          ;; root partition.
>> +          (setenv "GRUB_ENABLE_CRYPTODISK" "y")
>> +          (invoke/quiet (string-append bootloader "/bin/grub-mknetdir")
>> +                        (string-append "--net-directory=" net-dir)
>> +                        (string-append "--subdir=" subdir))
>> +          (false-if-exception
>> +            (delete-file efi-bootloader-link))
>> +          (symlink #$efi-bootloader
>> +                   efi-bootloader-link)
>> +          (false-if-exception
>> +            (delete-file store-link))
>> +          (symlink store
>> +                   store-link)))))
> 
> What's the purpose of those two symlinks, isn't grub-mknetdir taking
> care of all this?

No, it doesn’t. The final .efi file after calling 'grub-mknetdir' is 
/boot/efi/boot/arm64-efi/core.efi. I guess when using PXE there is a way to 
point to this efi file, so GRUB doesn’t care. But without this the U-Boot looks 
for the standardised /efi/boot/bootaa64.efi file via TFTP. So the first symlink 
fixes this gap without removing other possibilities.

The second symlink is for GRUB itself. Inside /boot/efi/boot/grub.cfg there are 
commands to specify the root device, usually via a file system label or a UUID. 
But in this case the root device specification is just ”set root=(tftp)”. 
Following lines look up files by e.g. 
/gnu/store/…-grub-2.04/share/grub/unicode.pf2, which GRUB will then try to 
access via TFTP. But the root for the TFTP server is /boot from the TARGET 
argument, and not /. So GRUB trying to access /gnu/store/… results in an access 
to /boot/gnu/store/…. Therefore the symlink from /boot/gnu to ../gnu is needed. 
And as TARGET can freely be chosen the number of up-levels needs to be 
calculated.

Where the usual grub-efi-bootloader is able to find and access the real root 
device directly, the grub-efi-net-bootloader can only use TFTP. Therefore we 
have to make the real “root device” accessible via TFPT. I’m not sure if this 
is wise from a security point of view. But otherwise all files listed in the 
grub.cfg would need to be copied. This would be an overhead and leads to more 
complications, when deleting system generations; then obsolete copies need 
determined and removed again. 

> Creating a system test for this may be a bit difficult, but if you could
> add a section in the documentation describing how to setup a
> 'grub-efi-net-bootloader, that would be great!

I have some difficulties with this – despite not having knowledge with texi 
yet. So help is very welcome.

You need to make use of an NFS server, serving your / directory. 

You need a TFTP server set up to serve the files of our /boot directory with 
access to your /gnu store via the /boot/gnu symlink as well. 

You need to setup your DNS server to send some boot-options. 

I use a “DiskStation” with its UI for all this but needed to fiddle around with 
dnsmasq for the DNS. So this is nothing for a guix documentation. However, this 
might be even out of scope. But ideally there would be examples of how to 
achieve all this with guix itself. I have no experience using guix for anything 
of this.

Then I use all this to boot a Raspberry Pi 3b in aarch64 mode. This requires a 
none free firmware, additional configuration files for the firmware, the 
(modified) U-Boot. And there is even more. I compiled the kernel linux 
(currently from the mainline) with a Raspberry specific defconfig-package as an 
input for the linux package and an additional function to modify the defconfig 
on the fly.

There are more patches to follow for at least parts of all this.

But what I actually want to say with this: Even to reproduce my setup, still a 
lot other stuff is missing. And I have no other computer for tinkering. I can’t 
try out this patch on a usual x86…64 machine. I can't really tell, if this will 
work right out of the box for other people on different computers.

Before documenting this for the public, I would wish that at least someone else 
with access to an x86_64 UEFI system gives it a try. I'm pretty sure it will 
work.

Besides all the server stuff with NFS, TFTP and DNS, there are only few 
specialities to know:

(operating-system
  (file-systems (cons (file-system
                        (mount-point "/")
                        (type "nfs")
                        (device ":/your/servers/path/to/guix-root")
                        (options "addr=10.11.12.2,vers=4.1"))
                       %base-file-systems)) 
  (bootloader 
    (target "/boot")
    (bootloader-configuration (bootloader grub-efi-net-bootloader)
    …))
  …)

The IP address of your server needs to be specified via an “addr=” option. To 
make use of the defaults of the new grub-efi-net-bootloader, the target field 
has to be "/boot".


Bye

Stefan


¹ Well, actually – as for grub-efi-net-bootloader you are now free to use any 
path for target – you can use "/boot/efi". But then you need to make this your 
TFTP server’s root, and you need to use the make-grub-efi-net-bootloader macro 
with "/boot/efi" as well, and you will end up in having double efi folders as 
in /boot/efi/efi/boot/grub.cfg etc. So this doesn’t make sense.




reply via email to

[Prev in Thread] Current Thread [Next in Thread]