[//]: # (
pandoc -f markdown -t html5 -s -c /path/to/style/sheet/file.css \
--self-contained -o /path/to/output/file.html /path/to/this/file.md
)
## Create qemu-kvm guest
### Synopsis
On an internet-connected x86\_64 host running CentOS Linux 7.9 with qemu-kvm
and dependencies installed (see _Set up qemu-kvm for CentOS 7_):
- Retrieve an iso installation image.
- Create and configure a qemu-kvm guest VM.
- Test run guest VM and modify its configuration.
- Save guest VM image and runtime config files for re-use.
### Preliminary requirements
To create and run VMs as a non-root user your USER account must be a member
of the kvm and qemu groups.
Create a private workspace:
```
> mkdir -p ${HOME}/qemu/{cfg,efi,img,pki,run}
```
#### Guest VM network options
##### Private network `-netdev user`
This is the default; each VM is in its own private network with non-routable ip
address(es) assigned to the guest's NIC(s) by the built-in qemu DHCP server: \
Default network: 10.0.2.0/24 \
Gateway (host) address: 10.0.2.2 \
DNS server address: 10.0.2.3 \
Guest address range: 10.0.2.15 to 10.0.2.31
Set alternative and additional params at the `-netdev` command line. You must
add the 'net=', 'host=', 'dhcpstart=' and 'dns=' params if using a pre-set
\$MACADDR for the NIC.
Reference:
[Qemu Documentation/Networking](https://wiki.qemu.org/Documentation/Networking)
##### Public network `-netdev tap`
For a VM to have full internet connectivity with a public ip address your USER
account must have permission to attach the VM's NIC to a tap device (e.g.
tap\_\$USER connected to the host bridge (e.g. br0). Note that the following
will _not_ persist between reboots:
```
> sudo ip tuntap add dev tap_${USER} mode tap user ${USER}
> sudo ip link set dev tap_${USER} up
> sudo ip link set tap_${USER} master br0
```
Reference:
[Hosting QEMU VMs with Public IP Addresses using TAP Interfaces](https://blog.stefan-koch.name/2020/10/25/qemu-public-ip-vm-with-tap)
### Create guest VM from iso file
Retrieve a Linux installation iso image from a repository; we selected a Debian
11.5 ("Bullseye") iso image; it contains non-free firmware required to set up
UEFI boot inside the VM:
```
> ISO_URL='https://cdimage.debian.org/cdimage/unofficial/non-free/cd-including-firmware/11.5.0-live+nonfree/amd64/iso-hybrid/'
> ISO_FILE='debian-live-11.5.0-amd64-standard+nonfree.iso'
> curl -k -L -s -o ${HOME}/qemu/img/${ISO_FILE} ${ISO_URL}/${ISO_FILE}
```
Set a name for the guest VM:
```
> VM_NAME="debian11"
```
Copy OVMF UEFI boot config overlay file to private workspace:
```
> cp /usr/share/OVMF/OVMF_VARS.fd ${HOME}/qemu/efi/${VM_NAME}.fd
```
Set vars for UEFI file paths:
```
EFI_CODE="/usr/share/OVMF/OVMF_CODE.secboot.fd"
EFI_VARS="${HOME}/qemu/efi/${VM_NAME}.fd"
EFI_SHELL="/usr/share/OVMF/UefiShell.iso"
```
Create an empty (sparse) 10G disk image in qcow2 format:
```
> qemu-img create -f qcow2 ${HOME}/qemu/img/${VM_NAME}.qcow2 10G
```
#### Set qemu-kvm command line params
Set name and assign full host cpu capabilities with 2G RAM to guest VM:
```
-name ${VM_NAME} -cpu host -smp $(nproc) -m 2048M
```
Emulate Intel Q35 Express chipset, enable kvm and boot with OVMF UEFI images;
EFI\_CODE must be declared before EFI\_VARS; suppress iPXE boot with
`-net none`; for ancient OSes use Intel 440fx chipset (does not support UEFI,
legacy BIOS only):
```
-machine type=q35,accel=kvm -enable-kvm
-drive if=pflash,index=0,format=raw,read-only=on,file=${EFI_CODE}
-drive if=pflash,index=1,format=raw,file=${EFI_VARS}
-net none
```
Set hard disk and cdrom drives; do _not_ use virtio drivers (Debian installer
will default to scsi) else an EFI system partition (ESP) will not be created in
the guest:
```
-cdrom ${HOME}/qemu/img/${ISO_FILE}
-hda ${HOME}/qemu/img/${VM_NAME}.qcow2
```
Output guest VM display to qxl-vga (enhanced VGA) device using qemu's built-in
vnc server, listen for connections on port 6001 of all interfaces, no password,
use US English keyboard; enable USB tablet to align mouse pointer with vnc
display:
```
-device qxl-vga -display vnc=0.0.0.0:101 -k en-us
-device qemu-xhci -device usb-tablet
```
Reference:
[Qemu USB emulation](https://qemu.readthedocs.io/en/latest/system/devices/usb.html)
Create qemu monitor and serial port Unix sockets:
```
-chardev socket,id=console,path=${HOME}/qemu/run/${VM_NAME}.console,server,nowait
-serial chardev:console
-chardev socket,id=monitor,path=${HOME}/qemu/run/${VM_NAME}.monitor,server,nowait
-monitor chardev:monitor
```
References: \
[Set up a serial terminal/console in RHEL](https://access.redhat.com/articles/3166931) \
[QEMU - Send serial data from host to guest](https://unix.stackexchange.com/questions/667045/)
Set up guest VM network; generate a unique MAC address; create guest NIC; do
not use virtio driver for ancient OSes:
```
for COUNT in {1..6}; do
OCTET="$(tr -cd 1234567890ABCDEF /usr/libexec/qemu-kvm -name ${VM_NAME} ... -daemonize
```
#### Install Debian 11 from iso image
Connect to the guest VM display:
```
> vncviewer localhost::6001 2>/dev/null &
```
The vnc server runs on the host _not_ the guest. To connect to the guest VM
display from a different computer replace localhost with the host's actual ip
address; the host's firewall must permit connections on port 6001.
- If prompted for a choice of boot device select cdrom.
- From GNU GRUB menu select Debian Installer (non-graphical).
- Follow prompts to select language, country and keyboard.
- Installer will attempt to auto-configure network. If auto-config fails or you
need to set network params manually, select Go Back. Follow prompts to enter
ip address, netmask, gateway, name servers, hostname and domain.
- Continue the installation.
- When prompted to partition the hard disk, select Guided partitioning.
- When automatic partitioning is complete, verify that the ATA Qemu hard disk
contains a partition of type ESP (EFI system partition) formatted as a FAT32
filesystem with the bootable flag set on.
- Continue the installation.
- When complete, reboot the VM.
- From GNU GRUB menu select Debian GNU/Linux; VM will boot from virtual hard
drive; login as root to verify and continue installation.
#### Connect to VM serial port and Qemu monitor
Use socat for an interactive connection to the VM serial port and Qemu monitor
socket. The following will connect to the monitor socket, disable the default
^C and ^D quit/exit escape sequences and enable the telnet ^] quit/exit escape
sequence:
```
> socat stdio,rawer,escape=0x1d unix-connect:${HOME}/qemu/run/${VM_NAME}.monitor
```
Reference: [Socat, Telnet and Unix sockets](https://ixday.github.io/post/socat_telnet/)
Use netcat or socat for scripted connections. The following will shut down the
VM from the command line using netcat (nc) or socat:
```
> printf '%b' 'system_powerdown\n' | nc -U ${HOME}/qemu/run/${VM_NAME}.monitor
> printf '%b' 'system_powerdown\n' | socat stdio unix-connect:${HOME}/qemu/run/${VM_NAME}.monitor
```