lang: core: embedded: provisioner: Add IPXE support

This lets you boot from ipxe. You can run the ipxe shell from their
stock image or the netboot.xyz one. For the latter, press "m", then type
"dhcp" (machine is now pingable!) then type "route" to check the ip.

To boot type:

chain http://192.168.42.1:4280/menu.ipxe

and you're off!

Thanks to frebib for finding the workaround to the VFS bug. The answer
is you need to run the imgfree command to unblock the initrd.
This commit is contained in:
James Shubin
2025-01-18 01:07:19 -05:00
parent 2f3bd72491
commit 483cc22c32
2 changed files with 69 additions and 3 deletions

View File

@@ -0,0 +1,38 @@
#!ipxe
menu iPXE Boot Options
item kickstart Install {{ .distro }} {{ .version }} {{ .arch }} ( kickstart )
item manual Install {{ .distro }} {{ .version }} {{ .arch }} ( manual )
item shell iPXE shell
item exit Exit to BIOS
choose --default kickstart --timeout 10000 option && goto ${option}
:kickstartwip
imgfree
set server_root {{ .server_base }}
kernel ${server_root}{{ .distro }}{{ .version }}-{{ .arch }}/vmlinuz ip=dhcp initrd=initrd.magic inst.repo={{ .inst_repo_base }}releases/{{ .version }}/{{ .flavour }}/{{ .arch }}/os/ inst.text inst.ks={{ .ks }} console=ttyS1,115200n8
initrd ${server_root}{{ .distro }}{{ .version }}-{{ .arch }}/initrd.img
boot
# The imgfree command works around some bug, which makes our initrd not be seen
# properly. We would otherwise get a VFS not syncing error on boot.
:kickstart
imgfree
kernel {{ .server_base }}{{ .distro }}{{ .version }}-{{ .arch }}/vmlinuz ip=dhcp initrd=initrd.img inst.repo={{ .inst_repo_base }}releases/{{ .version }}/{{ .flavour }}/{{ .arch }}/os/ inst.ks={{ .ks }}
initrd {{ .server_base }}{{ .distro }}{{ .version }}-{{ .arch }}/initrd.img
boot
:manual
imgfree
kernel {{ .server_base }}{{ .distro }}{{ .version }}-{{ .arch }}/vmlinuz ip=dhcp inst.repo={{ .inst_repo_base }}releases/{{ .version }}/{{ .flavour }}/{{ .arch }}/os/
initrd {{ .server_base }}{{ .distro }}{{ .version }}-{{ .arch }}/initrd.img
boot
:shell
shell
:exit
exit

View File

@@ -80,6 +80,7 @@ class base($config) {
# eg: equivalent of: https://download.fedoraproject.org/pub/fedora/linux/ # eg: equivalent of: https://download.fedoraproject.org/pub/fedora/linux/
$inst_repo_base = "http://${router_ip}:${http_port_str}/fedora/" # private lan online, no https! $inst_repo_base = "http://${router_ip}:${http_port_str}/fedora/" # private lan online, no https!
$server_base = "http://${router_ip}:${http_port_str}/" # private lan online, no https!
$syslinux_root = "/usr/share/syslinux/" $syslinux_root = "/usr/share/syslinux/"
@@ -360,11 +361,17 @@ class base:repo($config) {
$uefi_root = "${uefi_extract_dir}/boot/efi/EFI/fedora/" $uefi_root = "${uefi_extract_dir}/boot/efi/EFI/fedora/"
$uefi_shim = "${uefi_root}shim.efi" $uefi_shim = "${uefi_root}shim.efi"
$uefi_shimx64 = "${uefi_root}shimx64.efi"
tftp:file "/uefi/shim.efi" { # needs leading slash tftp:file "/uefi/shim.efi" { # needs leading slash
path => $uefi_shim, # TODO: add autoedges path => $uefi_shim, # TODO: add autoedges
Depend => Exec["uefi-extract-${uid}"], Depend => Exec["uefi-extract-${uid}"],
} }
tftp:file "/uefi/shimx64.efi" { # needs leading slash
path => $uefi_shimx64, # TODO: add autoedges
Depend => Exec["uefi-extract-${uid}"],
}
tftp:file "/uefi/grubx64.efi" { # sometimes used? tftp:file "/uefi/grubx64.efi" { # sometimes used?
path => "${uefi_root}grubx64.efi", # TODO: add autoedges path => "${uefi_root}grubx64.efi", # TODO: add autoedges
@@ -399,6 +406,11 @@ class base:repo($config) {
#Depend => Pkg[$pkgs], #Depend => Pkg[$pkgs],
} }
http:file "/${uid}/vmlinuz" { # when using ipxe
path => $vmlinuz_file, # TODO: add autoedges
#Depend => Pkg[$pkgs],
}
$initrd_file = "${distroarch_tftp_prefix}initrd.img" $initrd_file = "${distroarch_tftp_prefix}initrd.img"
exec "initrd-${uid}" { exec "initrd-${uid}" {
@@ -421,6 +433,11 @@ class base:repo($config) {
#Depend => Pkg[$pkgs], #Depend => Pkg[$pkgs],
} }
http:file "/${uid}/initrd.img" { # when using ipxe
path => $initrd_file, # TODO: add autoedges
#Depend => Pkg[$pkgs],
}
# this file resource serves the entire rsync directory over http # this file resource serves the entire rsync directory over http
if $mirror == "" { # and $rsync != "" if $mirror == "" { # and $rsync != ""
@@ -626,7 +643,7 @@ class base:host($name, $config) {
} }
} }
$tftp_menu_template = struct{ $menu_template = struct{
distro => $distro, distro => $distro,
version => $version, # 39 for fedora 39 version => $version, # 39 for fedora 39
arch => $arch, # could also be aarch64 arch => $arch, # could also be aarch64
@@ -634,6 +651,7 @@ class base:host($name, $config) {
ks => "http://${router_ip}:${http_port_str}/fedora/kickstart/${hkey}.ks", # usually $mac or `default` ks => "http://${router_ip}:${http_port_str}/fedora/kickstart/${hkey}.ks", # usually $mac or `default`
inst_repo_base => $inst_repo_base, inst_repo_base => $inst_repo_base,
server_base => $server_base,
} }
# #
@@ -661,20 +679,30 @@ class base:host($name, $config) {
"/uefi/grub.cfg-01-${old_mac}" "/uefi/grub.cfg-01-${old_mac}"
} }
$ipxe_menu = if $mac == "" {
"menu.ipxe"
} else {
"${old_mac}.ipxe"
}
if $bios { if $bios {
tftp:file "${bios_menu}" { # for bios tftp:file "${bios_menu}" { # for bios
data => golang.template(deploy.readfile("/files/bios-menu.tmpl"), $tftp_menu_template), data => golang.template(deploy.readfile("/files/bios-menu.tmpl"), $menu_template),
} }
} else { } else {
tftp:file "${uefi_menu}" { # for uefi tftp:file "${uefi_menu}" { # for uefi
# XXX: linuxefi & initrdefi VS. kernel & append ? # XXX: linuxefi & initrdefi VS. kernel & append ?
data => golang.template(deploy.readfile("/files/uefi-menu.tmpl"), $tftp_menu_template), data => golang.template(deploy.readfile("/files/uefi-menu.tmpl"), $menu_template),
#Depend => Pkg[$pkgs_uefi], #Depend => Pkg[$pkgs_uefi],
#Depend => Exec["uefi-extract"], #Depend => Exec["uefi-extract"],
} }
} }
http:file "/${ipxe_menu}" { # for ipxe
data => golang.template(deploy.readfile("/files/ipxe-menu.tmpl"), $menu_template),
}
# If it's a dir we don't need a suffix, otherwise return the last chunk. # If it's a dir we don't need a suffix, otherwise return the last chunk.
$handoff_code_chunk = if strings.has_suffix($prefix, "/") { $handoff_code_chunk = if strings.has_suffix($prefix, "/") {
"" ""