Build and Modify the FreeBSD Kernel

Build and Run the FreeBSD Kernel

Compared to the Linux kernel, it’s relatively easy to build and run the FreeBSD kernel.

Get the Source Code

Typically, the source code is installed along with the operating system in /usr/src. If this directory doesn’t exist, you can obtain the source code like this:

# git clone https://git.freebsd.org/src.git /usr/src
# cd /usr/src
# git checkout release/14.2.0

Nevertheless, if you have this directory, it’s still recommended to clone the repository and move .git to /usr/src so we can perform version control conveniently.

# git clone https://git.freebsd.org/src.git ~/freebsd
# cd ~/freebsd
# git checkout release/14.2.0
# mv ~/freebsd/.git /usr/src
# cd /usr/src
# git status

The above operation will set the directory permissions to be writable only by the root user. If you want normal users to also have write permissions so we can develop in normal user, you can add the user to the wheel user group and set the directory’s group to wheel.

# pw groupmod wheel -m your_username
# chgrp -R wheel /usr/src
# chmod g+s /usr/src
# find /usr/src -type d -exec chmod g+rwx {} \;
# find /usr/src -type f -exec chmod g+rw {} \;

Build and Run the FreeBSD Kernel

Caution

Before continue, be sure to back up the virtual machine to prevent it from being unable to start if the kernel is modified.

Let’s first build and install a kernel that uses the default configuration:

$ su root
# cd /usr/src
# make buildkernel -j$(sysctl -n hw.ncpu)
# make installkernel

Where sysctl -n hw.ncpu returns the value of CPU cores.

After installation, reboot the VM and see if the kernel works.

To generate compile_commands.json, which provides compilation information for language servers, replace the first make commands with the following command:

# intercept-build --append make buildkernel -j$(sysctl -n hw.ncpu)

The intercept-build command is bundled with llvm19 package, which intercepts build process to generate compile_commands.json. The --append flag tells the intercept-build to read an existing compile_commands.json if it exists and append the results to the database, also merge duplicated command keys.

The buildkernel target will use a default configuration named GENERIC, which supports a wide range of hardwares. This configuration file is located in /usr/src/sys/amd64/conf/GENERIC on x86_64 machine [freebsd] ch1: x86_64 conf, and /usr/src/sys/arm64/conf/GENERIC on arm64 machine [freebsd] ch1: arm64 conf.

To use your own customized kernel configuration, copy the generic configuration file and edit it:

# cd /usr/src/sys/amd64/conf
# cp GENERIC MYKERN
# vim MYKERN

The format of the kernel configuration file is simple. Each line contains a keyword that represents a device or subsystem, an argument, and a brief description. Any text after a # is considered a comment and ignored. For the specifications of the kernel configuration, refer to config(5).

After editing your configuration file, save it and execute the following command to build the kernel:

# make buildkernel KERNCONF=MYKERN

Modify the FreeBSD Kernel

Same as the Linux kernel, we are about to print a “Hello world!” string when the kernel starts.

The stuff similar to Linux’s arch/x86/kernel/head_64.S [linux] ch1: entry point x86_64 is sys/amd64/amd64/locore.S [freebsd] ch1: entry point x86_64, the stuff similar to Linux’s start_kernel() [linux] ch1: start_kernel() is mi_startup() defined in sys/kern/init_main.c [freebsd] ch1: mi_startup(), and the stuff similar to Linux’s printk() [linux] ch1: printk() is printf() defined in sys/sys/systm.h [freebsd] ch1: printf().

So all we need to do is adding a printf() to mi_startup() that prints “Hello world!” string. The patch is as follows:

patch.diff
diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c
index 1575287716ee..4523b912d6be 100644
--- a/sys/kern/init_main.c
+++ b/sys/kern/init_main.c
@@ -334,6 +334,7 @@ mi_startup(void)
 		/* Check off the one we're just done */
 		last = sip->subsystem;
 	}
+	printf("Hello world!\n")
 
 	TSEXIT();	/* Here so we don't overlap with start_init. */
 	BOOTTRACE("mi_startup done");

Apply this patch, rebuild and install the kernel, reboot the VM and you can see the effect in dmesg:

$ dmesg | grep Hello
Hello world!

More Resources

Last updated on