UPDATE 2019:
While initial instructions worked fine for me, it seems that some equipment had issues with syslinux and its configuration. This old instructions have been superseeded with current ones.
Update 2019-11-02: since this post has quite a bit of hits, i cleaned up instructions a bit: there were some unnecessary things used below (e.g. delivery of signed efi file from bios dir) which distracts from important things. Those issues are updated and configuration seems more clean now. I have also removed “old” bios configuration to see which files should go where and added kernel configuration for fedora, both live and network installation media.
Why now changes?
There are actually two reasons:
- I have a Dell Latitude 7440 and this laptop boots fine with old (syslinux) instructions. No issues here.
- However Lenovo Thinkpad t440s does not (it loads syslinux.efi, but then stops). Dnsmasq logs show that the file load was stopped by the client.
In order to solve this, i have replaced syslinux with grub, which seems to work great.
Note: new instructions are here for grub and uefi. Bios systems have a different configuration and has to be set up in a different way to run grub. Since i do not particularly care about bios systems anymore, those instructions have been removed.
BOOT WITH GRUB (2019 update)
Purpose of this update is to use grub instead of syslinux (as initially used).
First, download following file from ubuntu archive. This file will be delivered by dnsmasq.
- download link for the signed efi is -> grubnetx64.efi.signed
- then copy it to
/srv/pxe/grub/
Additionally, to show menus and stuff, you need grub modules. Those you can get from an Ubuntu ISO and copy them to the server:
- use ubuntu dvd and copy files from this location on the dvd ->
/boot/grub/x86_64-efi/*
- to the target location ->
/srv/pxe/grub/x86_64-efi/
The whole goal is to have the following directory structure:
[root@hp:/srv/pxe]# tree
.
├── grub
│ ├── grub.cfg
│ ├── grubnetx64.efi.signed
│ └── x86_64-efi
│ ├── acpi.mod
│ ├── adler32.mod
│ ├── *********** a whole lot of grub modules ***********
│ └── zfscrypt.mod
└── mounts
├── arch
├── fedora
├── fedora-net
└── ubuntu
Contents of /srv/pxe/grub/grub.cfg
should look like this:
function load_video {
insmod efi_gop
insmod efi_uga
insmod video_bochs
insmod video_cirrus
insmod all_video
}
load_video
set gfxpayload=keep
insmod gzio
insmod part_gpt
insmod ext2
insmod http
default vesamenu.c32
menuentry 'Ubuntu' {
linux (tftp,192.168.43.1)/mounts/ubuntu/casper/vmlinuz root=/dev/nfs rw nfsroot=192.168.43.1:/srv/pxe/mounts/ubuntu ip=dhcp preseed/url=http://192.168.43.1/pxe/ubuntu/preseed/ubuntu.seed boot=casper netboot=nfs
initrd (tftp,192.168.43.1)/mounts/ubuntu/casper/initrd
}
menuentry 'Arch' {
linux (tftp,192.168.43.1)/mounts/arch/arch/boot/x86_64/vmlinuz archisobasedir=arch archiso_http_srv=http://192.168.43.1/pxe/arch/ ip=:::::eth0:dhcp -
initrd (tftp,192.168.43.1)/mounts/arch/arch/boot/x86_64/archiso.img
}
menuentry 'Fedora Live' {
linuxefi (tftp,192.168.43.1)/mounts/fedora/images/pxeboot/vmlinuz ip=dhcp root=live:http://192.168.43.1/pxe/fedora/LiveOS/squashfs.img ro rd.live.image
initrdefi (tftp,192.168.43.1)/mounts/fedora/images/pxeboot/initrd.img
}
menuentry 'Fedora Network' {
linuxefi (tftp,192.168.43.1)/mounts/fedora-net/images/pxeboot/vmlinuz ip=dhcp root=live:http://192.168.43.1/pxe/fedora-net/images/install.img ro
initrdefi (tftp,192.168.43.1)/mounts/fedora-net/images/pxeboot/initrd.img
}
Contents of /etc/dnsmaq.conf
should look like this:
#general settings
port=0
interface=eno2
bind-interfaces
#dhcp
dhcp-range=192.168.43.2,192.168.43.254,48h
dhcp-option=3,192.168.43.1
#nameserver
dhcp-option=6,192.168.40.1
#caching dns server to serve lan
server=192.168.40.1
domain-needed
bogus-priv
no-resolv
no-poll
domain=lan
local=/lan/
expand-hosts
cache-size=1000
no-negcache
dnssec
#pxe
enable-tftp
dhcp-boot=boot/bios/pxelinux.0,hp,192.168.43.1
tftp-root=/srv/pxe
tftp-mtu=1500
log-dhcp
dhcp-vendorclass=BIOS,PXEClient:Arch:00000
dhcp-vendorclass=UEFI32,PXEClient:Arch:00006
dhcp-vendorclass=UEFI,PXEClient:Arch:00007
dhcp-vendorclass=UEFI64,PXEClient:Arch:00009
#dhcp-boot=net:UEFI32,boot/uefi/syslinux/efi32/syslinux.efi,,192.168.43.1
#dhcp-boot=net:UEFI,boot/uefi/syslinux.efi,,192.168.43.1
#dhcp-boot=net:UEFI64,boot/uefi/syslinux.efi,,192.168.43.1
#dhcp-boot=net:BIOS,boot/grub/i386-pc/core.0
dhcp-boot=net:UEFI,grub/grubnetx64.efi.signed,,192.168.43.1
dhcp-boot=net:UEFI64,grub/grubnetx64.efi.signed,,192.168.43.1
Done.
BOOT WITH SYSLINUX (Old instructions)
This was done mostly by trial and error, but i managed to do a pxe boot for both old bios systems and new uefi. Not well documented task online, so here is something that could serve as a guide/tutorial. Hope i didn’t leave something out… Anyhow, good luck.
Prereq:
- syslinux binaries – download from kernel.org (don’t install them, just download and extract proper files). These files are needed from the zip archive:
- for bios
- bios/com32/elflink/ldlinux.c32
- bios/com32/libutil/libutil.c32
- bios/com32/menu/menu.c32
- bios/com32/menu/vesamenu.c32
- bios/core/pxelinux.0
- bios/core/lpxelinux.0
- for uefi
- efi64/efi/syslinux.efi
- this file is needed as original and as copy (reason why)
- copy this to bootx64.efi
- copy this to bootx64c.efi
- efi64/com32/elflink/ldlinux/ldlinux.e64
- efi64/com32/libutil/libutil.c32
- efi64/com32/menu/menu.c32
- efi64/com32/menu/vesamenu.c32
- efi64/efi/syslinux.efi
- for bios
- dnsmasq
- nginx
- nfs server
Location used:
/srv/pxe
Let’s start with what is there at the finish line.
Directory tree looks like this:
Note: boot/bios/pxelinux.cfg/default
and boot/uefi/pxelinux.cfg/default
are the same files!
[root@hp:/srv/pxe]# tree
.
├── boot
│ ├── bios
│ │ ├── ldlinux.c32
│ │ ├── libutil.c32
│ │ ├── lpxelinux.0
│ │ ├── menu.c32
│ │ ├── pxelinux.0
│ │ ├── pxelinux.cfg
│ │ │ └── default
│ │ └── vesamenu.c32
│ └── uefi
│ ├── bootx64c.efi
│ ├── bootx64.efi
│ ├── ldlinux.e64
│ ├── libutil.c32
│ ├── menu.c32
│ ├── pxelinux.cfg
│ │ └── default
│ ├── syslinux.efi
│ ├── syslx64.cfg
│ └── vesamenu.c32
├── mounts
│ ├── arch
│ ├── fedora-gnome
│ ├── fedora-kde
│ ├── kubuntu
│ ├── neon
│ └── opensuse
└── syslinux-6.04-pre1.zip
16 directories, 26 files
Contents of the /srv/pxe/boot/bios/pxelinux.cfg/default
should look like this:
DEFAULT menu.c32
#PROMPT 0
MENU TITLE PXE UEFI Boot Menu
#MENU AUTOBOOT Starting Local System in # seconds
PROMPT 0
TIMEOUT 0
LABEL Arch
KERNEL http://192.168.29.99/pxe/arch/arch/boot/x86_64/vmlinuz
APPEND initrd=http://192.168.29.99/pxe/arch/arch/boot/x86_64/archiso.img archisobasedir=arch archiso_http_srv=http://192.168.29.99/pxe/arch/ ip=:::::eth0:dhcp -
EOF
MENU SEPARATOR
LABEL Opensuse Tumbleweed
KERNEL http://192.168.29.99/pxe/opensuse/boot/x86_64/loader/linux
APPEND initrd=http://192.168.29.99/pxe/opensuse/boot/x86_64/loader/initrd showopts acpi=force
EOF
MENU SEPARATOR
LABEL Kubuntu
KERNEL http://192.168.29.99/pxe/kubuntu/casper/vmlinuz.efi
APPEND initrd=http://192.168.29.99/pxe/kubuntu/casper/initrd.lz preseed/url=http://192.168.29.99/pxe/kubuntu/preseed/kubuntu.seed boot=casper netboot=nfs nfsroot=192.168.29.99:/srv/pxe/mounts/kubuntu/
EOF
LABEL Neon
KERNEL http://192.168.29.99/pxe/neon/casper/vmlinuz.efi
APPEND initrd=http://192.168.29.99/pxe/neon/casper/initrd.lz boot=casper netboot=nfs nfsroot=192.168.29.99:/srv/pxe/mounts/neon/
EOF
Contents of /etc/dnsmasq.conf
should look like this:
#general settings
port=0
interface=br0
bind-interfaces
#dhcp
dhcp-range=192.168.29.100,192.168.29.199,48h
dhcp-option=3,192.168.29.1
#nameserver
dhcp-option=6,192.168.29.90
#caching dns server to serve lan
listen-address=127.0.0.1,192.168.29.99
#pxe
enable-tftp
dhcp-boot=boot/bios/pxelinux.0,hp,192.168.29.99
tftp-root=/srv/pxe
log-dhcp
dhcp-vendorclass=BIOS,PXEClient:Arch:00000
dhcp-vendorclass=UEFI32,PXEClient:Arch:00006
dhcp-vendorclass=UEFI,PXEClient:Arch:00007
dhcp-vendorclass=UEFI64,PXEClient:Arch:00009
dhcp-boot=net:UEFI32,boot/uefi/syslinux.efi,,192.168.29.99
dhcp-boot=net:UEFI,boot/uefi/syslinux.efi,,192.168.29.99
dhcp-boot=net:UEFI64,boot/uefi/syslinux.efi,,192.168.29.99
Contents of /etc/nginx/sites-enabled/default
should look like this:
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ =404;
#autoindex on;
#root /var/www/html;
}
location /pxe {
alias /srv/pxe/mounts;
autoindex on;
}
}
Nfs mountpoints in /etc/exports
look like this:
/srv/pxe/mounts 192.168.29.1/24(ro,no_subtree_check,nohide)
/srv/pxe/mounts/arch 192.168.29.1/24(ro,no_subtree_check,nohide)
/srv/pxe/mounts/opensuse 192.168.29.1/24(ro,no_subtree_check,nohide)
/srv/pxe/mounts/fedora-kde 192.168.29.1/24(ro,no_subtree_check,nohide)
/srv/pxe/mounts/fedora-gnome 192.168.29.1/24(ro,no_subtree_check,nohide)
/srv/pxe/mounts/kubuntu 192.168.29.1/24(ro,no_subtree_check,nohide)
/srv/pxe/mounts/neon 192.168.29.1/24(ro,no_subtree_check,nohide)
Let’s start with the explanations.
First and foremost, the pxe server has to also be a dhcp server. For this we’re using dnsmasq which also includes tftp server. PXE relevant config in this case is marked with #pxe part of the file. So, tftp is trivial file transfer protocol and this is used to load syslinux.efi
or pxelinux.0
during boot. Those two binaries load everything else afterwards. The boot process looks like this:
- Dhcp server will provide ip address to client and send the file (e.g.
syslinux.efi
loader) marked in thednsmasq.conf
using tftp protocol. Bios systems usepxelinux.0
loader and uefi systems use syslinux.efi. Dnsmasq will route the request to proper file:- In case of bios, dhcp-boot option will be used
- In case of uefi, we have to route to proper architecture using
dhcp-vendor
anddhcp-boot
options indnsmasq.conf
- Note: there is difference between efi, efi32 and efi64. My example here uses only efi64 because i have no interest in e.g. ia32 architecture that’s why only efi64 is linked in dnsmasq configuration.
- Once loader has been sent, the client searches for other files (i.e.
ldlinux.c32
orlibutil.c32
). Those files are picked up from the same directory where the loader is found. Make sure that other files are coming from proper sources! e.g.libutil.c32
is not the same in bios and uefi versions! Because of this,syslinux.efi
found insyslinux-6.04-pre1.zip
has to be copied with two additional namesbootx64.efi
andbootx64c.efi
. Maybe now it’s even outdated and not needed, but i left it there just in case. Once more, default file inpxelinux.cfg
directory can be, and in my case is, the same file. - Next, the menu gets shown. When option is selected, we have to load the kernel and initrd for that option into memory. Loading of those files can be done with different protocols, but i’ve found that tftp doesn’t work for uefi, but it works for bios. To address this, i’ve set up nginx as http server to send mentioned files to client. Would recommend this for generic setup.
- For some images, it could happen that only nfs is supported. Nfs shares then need to be exported, but usage of this is defined in
/srv/pxe/boot/bios/pxelinux.cfg/default
configuration
In order to use isos, you need to mount them to proper directory. E.g.:
mount /mnt/red/data/isos/linux/neon-useredition-20180104-1018-amd64.iso /srv/pxe/mounts/neon
Done!
Now restart another computer in the same network and choose pxe boot.