I recently set up a bhyve vm on my workstation for kernel development and figured others might be able to make use of my notes as well, because it's really quite easy.
(thanks for the feedback+fix grahamperrin! originally I had reversed the order of kernel and world)
One side note: I went into this and thought I might be able to set arbitrary breakpoints in kernel methods with this - that did not work for me. I can react and debug dumps this way, but halting the system based on debugger conditions would not work this way. If anyone has any insight or ideas on this (aside from putting dumps into the code), I'd love to get feedback and comments.
VM setup
- Download a FreeBSD disk image, i.e. 13.2-RELEASE if you're running 13.2. This version should match whatever kernel file you have compiled on your host system for debugging purposes.
- On the host system, compile world and kernel as you usually would and
pkg install kgdb
. - Extract that image using
unxz FreeBSD-13.2-RELEASE-amd64.raw.xz
. - Resize the disk image to 40GB:
truncate -s 40G FreeBSD-13.2-RELEASE-amd64.raw
- Create a shell script that will start the new VM:
touch vm.sh && chmod 755 vm.sh && ee vm.sh
- Put relevant VM startup code into this script, for example (you can also use tools like
vm-bhyve
orvmrun.sh
to do this):
Code:
#!/bin/sh
#
# Kernel development setup
#
# Enable ip forwarding
sysctl net.inet.ip.forwarding=1
DISKIMG=FreeBSD-13.2-RELEASE-amd64.raw
MEMORY=4G
# load bhyve image
bhyveload \
-m ${MEMORY} \
-d ${DISKIMG} \
-c /dev/nmdm1B \
kerndev
bhyve \
-A \
-H \
-P \
-c 4 \
-D \
-m ${MEMORY} \
-l com1,/dev/nmdm1B \
-l com2,/dev/nmdm2B \
-s 0,hostbridge \
-s 1,virtio-blk,${DISKIMG} \
-s 2,virtio-net,tap0 \
-s 31,lpc \
kerndev &
PID=$!
sleep 1
ifconfig tap0 inet 192.168.0.1 netmask 255.255.255.252
wait ${PID}
# when we're done, we're destroying the vm
bhyvectl --vm=kerndev --destroy
Networking
- Replace the IP address in the script with ones that work for your local network
- Either make sure to attach that tap0 interface to a bridge or implement nat, i.e. via /etc/pf.conf.
Setting up kernel
- Once inside the VM, modify /usr/src/amd64/conf/GENERIC (or whatever matches your architecture) to include
options GDB
. - Use the opportuntity to clean up /etc/rc.conf from any growfs or other statements that you don't need, disable sendmail, set up IP address and routing, etc. and do adjustments as you see fit.
- Then cd to /usr/src and enter the following command, replacing <NCPU> with the number of CPUs in your system.
Code:
make clean && make -j<NCPU> buildworld && make -j<NCPU> kernel && make -j<NCPU> installworld
Setting up serial console
- Fix /boot/device.hints for uart0 to be flags 0x80
- add
debug.kdb.current=gdb
in /etc/sysctl.conf - change /etc/ttys to include
ttyu1 "/usr/libexec/getty 3wire" vt100 onifexists secure
(mark the "onifexists"!) - reboot
Entering debug mode
Connect to the host on /dev/nmdm2A (i.e. via cu -l /dev/nmdm2A
), because /dev/nmdm1A will be occupied by the debug session now.- On guest
sysctl debug.kdb.enter=1
- On host
cd /usr/obj/usr/src/amd64.amd64/sys/GENERIC/
kgdb kernel
target remote /dev/nmdm1A (or whatever console port of bhyve is used)
One side note: I went into this and thought I might be able to set arbitrary breakpoints in kernel methods with this - that did not work for me. I can react and debug dumps this way, but halting the system based on debugger conditions would not work this way. If anyone has any insight or ideas on this (aside from putting dumps into the code), I'd love to get feedback and comments.
Further Resources
Last edited: