最近对网络启动很感兴趣,搜索了下发现 GRUB iPXE 都可以网络启动,还支持自定义界面。GRUB 还有那么多的主题,可是 GRUB 没办法在 efi 下启动 win 的 iso,所以选了 iPXE,可是官网的 ipxe.efi 不支持自定义启动图,想要换启动图只能自己编译了。

编译

  编译好之后不能启动,查了好久原来是新版有 bug ,要换旧版才行。编译旧版又因为源码版本太旧和新编译器各种不兼容。后来看到 ArchLinux 源里的 iPXE 跑得挺好,可还是不支持设置背景图。干脆照着 ArchLinux 的 PKGBUILD 改改吧……

源码:

1
2
curl -Lo ipxe-1.21.1.tar.gz https://github.com/ipxe/ipxe/archive/refs/tags/v1.21.1.tar.gz
tar xvf ipxe-1.21.1.tar.gz

PATCH:

1
2
curl -Lo ipxe-1.21.1-fragmented_handshake.patch https://github.com/ipxe/ipxe/pull/116/commits/ca9f5fc5645c60c00c3ca232d2a492aa1eb29c58.patch
patch -Np1 -d ipxe-1.21.1 -i ../ipxe-1.21.1-fragmented_handshake.patch

接下来是改编译选项:

ipxe-1.21.1/src/config/local/general.h
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
// disable unsafe options
#undef CRYPTO_80211_WEP /* WEP encryption (deprecated and insecure!) */
#undef CRYPTO_80211_WPA /* WPA Personal, authenticating with passphrase */

// enable additional options
#define NET_PROTO_IPV6 /* IPv6 protocol */
#define DOWNLOAD_PROTO_HTTPS /* Secure Hypertext Transfer Protocol */
#define DOWNLOAD_PROTO_NFS /* Network File System Protocol */
#define IMAGE_TRUST_CMD /* Image trust management commands */
#define NEIGHBOUR_CMD /* Neighbour management commands */
#define NTP_CMD /* NTP commands */
#define CERT_CMD /* Certificate management commands */

// 关闭 `Press Ctrl-B for the iPXE command line` 2 秒超时提示。(嵌入自定义脚本后也不会出现
#define BANNER_TIMEOUT 0
// 开启 console 命令以设置背景图。
#define CONSOLE_CMD /* Console command */

#define IMAGE_SCRIPT /* iPXE script image support */
#define IMAGE_EFI /* EFI image support */
#define NSLOOKUP_CMD /* DNS resolving command */
#define TIME_CMD /* Time commands */
#define REBOOT_CMD /* Reboot command */
#define POWEROFF_CMD /* Power off command */
#define PING_CMD /* Ping command */
ipxe-1.21.1/src/config/local/console.h
1
2
// 显示图片用
#define CONSOLE_FRAMEBUFFER /* Graphical framebuffer console */

嵌入脚本:

myscript.ipxe
1
2
3
#!ipxe
ifconf
chain tftp://${next-server}/boot.ipxe

  不嵌入脚本的话,ipxe开机后会显示 2 秒 Press Ctrl-B for the iPXE command line 提示,然后执行 autoboot,之后会请求 option 175,再根据 DHCP 服务器返回的地址抓取配置文件加载。

  嵌入脚本则不会显示2秒提示,会直接加载嵌入的脚本。

  ipxe 的文档说 ifconfdhcp 是一个命令,但 ifconf 在开始自动配置之前不会关闭网络接口,dhcp 则是先 downup,第二条是加载 DHCP Server 返回的 TFTP Server 中的 boot.ipxe 文件。

编译:

1
2
cd ipxe-1.21.1
make -j14 NO_WERROR=1 EMBED="myscript.ipxe" bin-x86_64-efi/ipxe.efi -C src

  编译后的文件在 ipxe-1.21.1/src/bin-x86_64-efi/ipxe.efi,Arch 的 PKGBUILD 把 HTTPS 证书也都编译进去了,但是家里局域网用 HTTPS 很麻烦也没必要,就没放进去。这样要从 HTTPS boot 就需要 crosscert 服务器,可以用官方的,也可以自己镜像。

配置

  DHCP Server 用的是 OpenWrt 路由器上的 dnsmasq。

/etc/config/dhcp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
config boot
option filename 'ipxe.efi'
option serveraddress '10.0.0.6'
option force '1'
option servername 'miu.lan'

config match
option networkid 'ipxe'
option match '175'

config boot
option filename 'tag:ipxe,boot.ipxe'
option serveraddress '10.0.0.6'
option servername 'miu.lan'
boot.ipxe
1
2
3
4
5
#!ipxe
set http-url http://${next-server}/pxe
set os-url http://${next-server}/nya/pxe
set crosscert ${http-url}/auto
chain ${http-url}/menu.ipxe
menu.ipxe
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
#!ipxe

# Set background picture
console --picture ${http-url}/bg_custom.png --left 400 --right 840 --top 220 --bottom 220

# Figure out if client is 64-bit capable
cpuid --ext 29 && set arch x64 || set arch x86
cpuid --ext 29 && set archb 64 || set archb 32
cpuid --ext 29 && set archl amd64 || set archl i386

# Some menu defaults
# set menu-timeout 0 if no client-specific settings found
isset ${menu-timeout} || set menu-timeout 0
set submenu-timeout ${menu-timeout}
isset ${menu-default} || set menu-default WinPEISO

###################### MAIN MENU --${platform}--${ip} ##########################

:start
# menu iPXE boot menu for ${manufacturer} ${product} (${archb}bit)
menu iPXE boot menu for ${ip} ${product} (${archb}bit)
item --gap -- ------------------------- Operating Systems -------------------------
item ArchLinux Arch Linux 2024.01.01
item Manjaro Manjaro Linux kde-23.1.2-minimal
item WinPEISO Windows 10 PE 2004 1.22G
item Win11PEISO Windows 11 PE 22631 375M
item USBOX_V7 Windows 11 PE USBOX_V7 1.89G
item --gap -- ------------------- Internet Network Boot Service -------------------
item archbfsu Arch Linux (latest, live, bfsu)
item openSUSEstable openSUSE stable (installation, bfsu)
item ustc USTC Network Boot Service
item --gap -- ------------------------- Advanced Options --------------------------
item memtest_uefi Memtest86+
item --key c config Configure settings
item bootdisk Boot from local disk
item shell iPXE shell
item reboot Reboot
item --key x exit Exit iPXE and continue BIOS boot

choose --timeout ${menu-timeout} --default ${menu-default} selected || goto cancel
set menu-timeout 0
goto ${selected}

:cancel
echo You cancelled the menu, dropping you to a shell

:shell
echo Type 'exit' to get the back to the menu
shell
set menu-timeout 0
set submenu-timeout 0
goto start

:bootdisk
sanboot --no-describe --drive 0x80

:failed
echo Booting failed, dropping to shell
goto shell

:reboot
reboot

:exit
exit

:config
config
goto start

:back
set submenu-timeout 0
clear submenu-default
goto start

############ MAIN MENU ITEMS ############

:failed_download
echo
echo Failed to download a file.
echo Press a key to return to the menu.
prompt
imgfree
goto start

:failed_boot
echo
echo Boot failed.
echo Press a key to return to the menu.
prompt
imgfree
goto start

:WinPEISO
sanboot --no-describe ${os-url}/Windows10PE_2004.iso || goto failed
goto start

:Win11PEISO
sanboot --no-describe ${os-url}/Windows11PE64-22631.2861.iso || goto failed
goto start

:USBOX_V7
sanboot --no-describe ${os-url}/USBOX_V7.iso || goto failed
goto start

:ArchLinux
set release 2024.01.01
set SP_ARCH_MIRROR https://mirrors.ustc.edu.cn/archlinux
set extrabootoptions ip=dhcp net.ifnames=0 BOOTIF=01-${netX/mac} mirror=${SP_ARCH_MIRROR}
kernel ${os-url}/archlinux/${release}/arch/boot/x86_64/vmlinuz-linux || goto failed_download
initrd ${os-url}/archlinux/${release}/arch/boot/amd-ucode.img || goto failed_download
initrd ${os-url}/archlinux/${release}/arch/boot/intel-ucode.img || goto failed_download
initrd ${os-url}/archlinux/${release}/arch/boot/x86_64/initramfs-linux.img || goto failed_download
imgargs vmlinuz-linux initrd=amd-ucode.img initrd=intel-ucode.img initrd=initramfs-linux.img archiso_http_srv=${os-url}/archlinux/${release}/ archisobasedir=arch cms_verify=y ${extrabootoptions}
boot || goto failed_boot

:archbfsu
set release latest
set mirror http://mirror.bfsu.edu.cn/archlinux
set extrabootoptions ip=dhcp net.ifnames=0 BOOTIF=01-${netX/mac} mirror=${mirror}
kernel ${mirror}/iso/${release}/arch/boot/x86_64/vmlinuz-linux || goto failed_download
initrd ${mirror}/iso/${release}/arch/boot/amd-ucode.img || goto failed_download
initrd ${mirror}/iso/${release}/arch/boot/intel-ucode.img || goto failed_download
initrd ${mirror}/iso/${release}/arch/boot/x86_64/initramfs-linux.img || goto failed_download
imgargs vmlinuz-linux initrd=amd-ucode.img initrd=intel-ucode.img initrd=initramfs-linux.img archiso_http_srv=${mirror}/iso/${release}/ archisobasedir=arch cms_verify=y ${extrabootoptions}
boot || goto failed_boot

:Manjaro
set murl ${os-url}/manjaro-kde-23.1.2-minimal-240102-linux66
set SP_ARCH_MIRROR https://mirrors.ustc.edu.cn/manjaro
set extrabootoptions ip=dhcp net.ifnames=0 BOOTIF=01-${netX/mac} mirror=${SP_ARCH_MIRROR} driver=nonfree nouveau.modeset=0 i915.modeset=1 radeon.modeset=1
kernel ${murl}/boot/vmlinuz-x86_64 || goto failed_download
initrd ${murl}/boot/amd_ucode.img || goto failed_download
initrd ${murl}/boot/intel_ucode.img || goto failed_download
initrd ${murl}/boot/initramfs-x86_64.img || goto failed_download
imgargs vmlinuz-x86_64 initrd=amd_ucode.img initrd=intel_ucode.img initrd=initramfs-x86_64.img miso_http_srv=${murl}/ ${extrabootoptions}
boot || goto failed_boot

:openSUSElive
set release 2024.01.01
set os openSUSE-Leap-15.5-KDE-Live-x86_64-Media
kernel ${os-url}/${os}/boot/x86_64/loader/linux
initrd ${os-url}/${os}/boot/x86_64/loader/initrd
imgargs linux initrd=initrd ip=dhcp splash=silent quiet systemd.show_status=yes root=live:${os-url}/${os}/LiveOS/squashfs.img rd.kiwi.live.pxe rd.live.overlay.persistent rd.live.overlay.cowfs=ext4
boot || goto failed_boot

:openSUSEcurrent
set mirror http://mirror.bfsu.edu.cn/opensuse
set base-url ${mirror}/distribution/openSUSE-current/repo/oss
kernel ${base-url}/boot/x86_64/loader/linux || goto failed_download
initrd ${base-url}/boot/x86_64/loader/initrd || goto failed_download
imgargs linux initrd=initrd install=${base-url} hostip=${net0/ip} netmask=${net0/netmask} gateway=${net0/gateway} nameserver=${dns}
boot || goto failed_boot

:openSUSEstable
set mirror http://mirror.bfsu.edu.cn/opensuse
set base-url ${mirror}/distribution/openSUSE-stable/repo/oss
kernel ${base-url}/boot/x86_64/loader/linux || goto failed_download
initrd ${base-url}/boot/x86_64/loader/initrd || goto failed_download
imgargs linux initrd=initrd install=${base-url} hostip=${net0/ip} netmask=${net0/netmask} gateway=${net0/gateway} nameserver=${dns}
boot || goto failed_boot

:ustc
chain ${http-url}/ustc.efi

另外还有配置中用到的背景图:

bg_custom.png