diff --git a/01_hello/Makefile b/01_hello/Makefile new file mode 100644 index 0000000..abb2716 --- /dev/null +++ b/01_hello/Makefile @@ -0,0 +1,10 @@ +SRCS=hello.c +KMOD=hello + +# Uncomment the following lines to cross compile to ARM64 architecture for the Raspberry Pi +#CC=clang --target=aarch64-unknown-freebsd +#MACHINE_ARCH=aarch64 +#MACHINE=arm64 + +.include + diff --git a/01_hello/README.md b/01_hello/README.md new file mode 100644 index 0000000..6009f4f --- /dev/null +++ b/01_hello/README.md @@ -0,0 +1,42 @@ +# 01_hello (FreeBSD version) + +A simple hello world style FreeBSD kernel module. + +This example can be compiled and run on a Raspberry Pi running FreeBSD or on an x86 computer. + +## Commands to manage the module + +Follow the kernel's log for new lines (recommended to do so in a separate terminal or tmux) +```shell +$ tail -F /var/log/messages +``` + +Loading the kernel module +```shell +# kldload ./hello.ko +``` + +Unloading the kernel module +```shell +# kldunload ./hello.ko +``` + +## Cross Compiling From x86 FreeBSD to AArch64 FreeBSD + +It is possible to cross compile the kernel module from an x86 version of FreeBSD to the Raspberry Pi version. Edit your Makefile as follows: + +```Make +KMOD=hello +SRCS=hello.c + +CC=clang --target=aarch64-unknown-freebsd + +CFLAGS+= -I/usr/src/sys/arm64/include + +MACHINE_ARCH=aarch64 +MACHINE=arm64 + +.include +``` + +The file hello.ko can then be copied to the Raspberry Pi and loaded into the kernel with kldload command. diff --git a/01_hello/hello.c b/01_hello/hello.c new file mode 100644 index 0000000..9659b44 --- /dev/null +++ b/01_hello/hello.c @@ -0,0 +1,38 @@ +/* + * Copyright [2025] John Holloway + * Learning exercise. Use at your own risk + */ + +#include +#include +#include +#include +#include +#include + +static int loader(struct module *module, int what, void *arg) { + int error = 0; + + switch (what) { + case MOD_LOAD: + printf("Module loaded into FreeBSD kernel\n"); + break; + case MOD_UNLOAD: + printf("Module removed from the FreeBSD kernel\n"); + break; + default: + error = EOPNOTSUPP; + break; + } + return (error); +} + +static moduledata_t mod = { + "hello", + loader, + NULL +}; + +DECLARE_MODULE(hello, mod, SI_SUB_KLD, SI_ORDER_ANY +); + diff --git a/02_better_hello/Makefile b/02_better_hello/Makefile new file mode 100644 index 0000000..196c155 --- /dev/null +++ b/02_better_hello/Makefile @@ -0,0 +1,10 @@ +SRCS=better_hello.c +KMOD=better_hello + +# Uncomment the following lines to cross compile to ARM64 architecture for the Raspberry Pi +#CC=clang --target=aarch64-unknown-freebsd +#MACHINE_ARCH=aarch64 +#MACHINE=arm64 + +.include + diff --git a/02_better_hello/README.md b/02_better_hello/README.md new file mode 100644 index 0000000..2ec9a76 --- /dev/null +++ b/02_better_hello/README.md @@ -0,0 +1,57 @@ +# 02 better_hello (FreeBSD version) + +A simple hello world style FreeBSD kernel module. + +This example can be compiled and run on a Raspberry Pi running FreeBSD or on an x86 computer. + +## Commands to manage the module + +Follow the kernel's log for new lines (recommended to do so in a separate terminal or tmux) +```shell +$ tail -F /var/log/messages +``` + +Loading the kernel module +```shell +# kldload ./better_hello.ko +``` + +To get the metadata from the sysctrl use the following commands: + +```shell +$ sysctl kern.better_hello +kern.better_hello.version: 1.0 +kern.better_hello.license: BSD +kern.better_hello.description: A simple Hello World kernel module +kern.better_hello.author: John Holloway +``` + +To get more specific information, you can enter the child node in the sysctl command. +```shell +$ sysctl kern.better_hello.author +kern.better_hello.author: John Holloway +``` + +Unloading the kernel module +```shell +# kldunload better_hello +``` + +## Cross Compiling From x86 FreeBSD to AArch64 FreeBSD + +It is possible to cross compile the kernel module from an x86 version of FreeBSD to the Raspberry Pi version. Edit your Makefile as follows: + +```Make +KMOD=better_hello +SRCS=better_hello.c + +CC=clang --target=aarch64-unknown-freebsd +CFLAGS+= -I/usr/src/sys/arm64/include + +MACHINE_ARCH=aarch64 +MACHINE=arm64 + +.include +``` + +The file hello.ko can then be copied to the Raspberry Pi and loaded into the kernel with kldload command. diff --git a/02_better_hello/better_hello.c b/02_better_hello/better_hello.c new file mode 100644 index 0000000..fc4c997 --- /dev/null +++ b/02_better_hello/better_hello.c @@ -0,0 +1,88 @@ +/* + * Copyright [2025] John Holloway + * Learning exercise. Use at your own risk + */ + +#include +#include +#include +#include +#include +#include +#include // required for the metadata + +static struct sysctl_ctx_list sysctl_ctx; +static struct sysctl_oid *sysctl_tree; + +static int load_metadata(void) { + // Add a sysctrl context and object identifier to the system control tree + if (sysctl_ctx_init(&sysctl_ctx)) { + return ENOMEM; + } + + sysctl_tree = SYSCTL_ADD_NODE(&sysctl_ctx, + SYSCTL_STATIC_CHILDREN(_kern), + OID_AUTO, + "better_hello", + CTLFLAG_RW, + 0, + "better_hello metadata"); + + // Add metadata entries + SYSCTL_ADD_STRING(&sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), + OID_AUTO, "author", CTLFLAG_RD, + "John Holloway", 0, "Module author"); + + SYSCTL_ADD_STRING(&sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), + OID_AUTO, "description", CTLFLAG_RD, + "A simple Hello World kernel module", + 0, "Module description"); + + SYSCTL_ADD_STRING(&sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), + OID_AUTO, "license", CTLFLAG_RD, + "GPL", 0, "Module license"); + + SYSCTL_ADD_STRING(&sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), + OID_AUTO, "version", CTLFLAG_RD, + "1.0", 0, "Module version"); + return 0; +} + +static int loader(struct module *module, int what, void *arg) { + int error = 0; + + switch (what) { + case MOD_LOAD: + printf("Module loaded into FreeBSD kernel\n" + "Use sysctl to get the kernel metadata\n"); + error = load_metadata(); + break; + + case MOD_UNLOAD: + printf("Module removed from the FreeBSD kernel\n"); + + // If we do not free the system call context + // we end up with dangling pointers in the kernel + if (sysctl_ctx_free(&sysctl_ctx)) { + printf("Can't free the system call context\n"); + error = ENOTEMPTY; + } else { + printf("Metadata has been removed from sysctl tree\n"); + } + + break; + default: + error = EOPNOTSUPP; + break; + } + return(error); +} + +static moduledata_t mod = { + "better_hello", + loader, + NULL +}; + +DECLARE_MODULE(hello, mod, SI_SUB_KLD, SI_ORDER_ANY); + diff --git a/03_gpioctrl/Makefile b/03_gpioctrl/Makefile new file mode 100644 index 0000000..3830ee1 --- /dev/null +++ b/03_gpioctrl/Makefile @@ -0,0 +1,11 @@ +SRCS=gpioctrl.c +KMOD=gpioctrl + +CFLAGS+= -I/usr/obj/usr/src/arm64.aarch64/sys/GENERIC + +# Uncomment the following lines to cross compile to ARM64 architecture for the Raspberry Pi +#CC=clang --target=aarch64-unknown-freebsd +#MACHINE_ARCH=aarch64 +#MACHINE=arm64 + +.include diff --git a/03_gpioctrl/README.md b/03_gpioctrl/README.md new file mode 100644 index 0000000..b9a80c7 --- /dev/null +++ b/03_gpioctrl/README.md @@ -0,0 +1,73 @@ +# 03_gpioctrl (FreeBSD version) + +A style FreeBSD kernel module that can both turn a GPIO pin ON and read from another pin. It will be using the [gpiobus -- GPIO bus system](https://man.freebsd.org/cgi/man.cgi?gpiobus(4)) kernel interface. + +This example can be compiled and run on a Raspberry Pi running FreeBSD or on an x86 computer. + +Unlike the 01_hello, this kernel module requires building the kernel headers and artifacts for FreeBSD. If you have not already done so, you can build them with the following commands: +```shell +# cd /usr/src +# make -j$(sysctl -n hw.ncpu) buildkernel +``` + +## Hardware Setup +![Raspberry Pi Wiring](led_button_Steckplatine.png) + +## Commands to manage the module + +Follow the kernel's log for new lines (recommended to do so in a separate terminal or tmux) +```shell +$ tail -F /var/log/messages +``` + +Loading the kernel module +```shell +# kldload ./gpioctrl.ko +``` + +Unloading the kernel module +```shell +# kldunload gpioctrl +``` + +## Finding the Correct GPIO Numbers +Check your pins at [pinout.xyz](https://www.pinout.xyz) + +Using the `devinfo` command to find information regarding the gpio chips on the board: + +```shell +$ devinfo -rv | grep gpio + gpio0 pnpinfo name=gpio@7e200000 compat=brcm,bcm2711-gpio + gpiobus0 + gpioc0 + gpio1 pnpinfo name=gpio compat=raspberrypi,firmware-gpio + gpiobus1 + gpioc1 + unknown pnpinfo name=gpiomem compat=brcm,bcm2835-gpiomem + gpioled0 pnpinfo name=leds compat=gpio-leds + gpioregulator0 pnpinfo name=sd_io_1v8_reg compat=regulator-gpio +``` + +We know that _bcm2711-gpio_ is the main GPIO controller on the Raspberry Pi, so we will use the GPIO with Offset 0. + +## Cross Compiling From x86 FreeBSD to AArch64 FreeBSD + +It is possible to cross compile the kernel module from an x86 version of FreeBSD to the Raspberry Pi version. Edit your Makefile as follows: + +```shell +KMOD=gpioctrl + +SRCS=gpioctrl.c device_if.h bus_if.h + +CC=clang --target=aarch64-unknown-freebsd + +CFLAGS+= -I/usr/src/sys/arm64/include + +MACHINE_ARCH=aarch64 + +MACHINE=arm64 + +.include +``` + +The file gpio.ko can then be copied to the Raspberry Pi and loaded into the kernel with kldload command. \ No newline at end of file diff --git a/03_gpioctrl/gpioctrl.c b/03_gpioctrl/gpioctrl.c new file mode 100644 index 0000000..6272e5c --- /dev/null +++ b/03_gpioctrl/gpioctrl.c @@ -0,0 +1,160 @@ +/* + * Copyright [2025] John Holloway + * Learning exercise. Use at your own risk + */ + +#include +#include +#include +#include +#include +#include + +#include // for gpio flags + +#include +#include + +static gpio_pin_t led_pin = NULL; +static gpio_pin_t button_pin = NULL; + +static int load_metadata(void) { + // Add a sysctrl context and object identifier to the system control tree + if (sysctl_ctx_init(&sysctl_ctx)) { + return ENOMEM; + } + + sysctl_tree = SYSCTL_ADD_NODE(&sysctl_ctx, + SYSCTL_STATIC_CHILDREN(_kern), + OID_AUTO, + "gpioctrl", + CTLFLAG_RW, + 0, + "gpioctrl metadata"); + + // Add metadata entries + SYSCTL_ADD_STRING(&sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), + OID_AUTO, "author", CTLFLAG_RD, + "John Holloway", 0, "Module author"); + + SYSCTL_ADD_STRING(&sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), + OID_AUTO, "description", CTLFLAG_RD, + "A simple module to turn on an LED and read from a pin", + 0, "Module description"); + + SYSCTL_ADD_STRING(&sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), + OID_AUTO, "license", CTLFLAG_RD, + "GPL", 0, "Module license"); + + SYSCTL_ADD_STRING(&sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), + OID_AUTO, "version", CTLFLAG_RD, + "1.0", 0, "Module version"); + return 0; +} + +static int init_module(void) { + int error = 0; + uint32_t led_number = 21; + uint32_t button_number = 20; + device_t gpio_dev; + + gpio_dev = devclass_get_device(devclass_find("gpiobus"), 0); + + if (!gpio_dev) { + printf("no GPIO device found.\n"); + return ENXIO; + } else { + printf("GPIO device found: %s\n", + device_get_nameunit(gpio_dev)); + + + if ((error = gpio_pin_get_by_bus_pinnum( + gpio_dev, + led_number, + &led_pin)) == 0) { + gpio_pin_setflags(led_pin, GPIO_PIN_OUTPUT); + + gpio_pin_set_active(led_pin, TRUE); + } else { + printf("error: %d\n", error); + return error; + } + + if ((error = gpio_pin_get_by_bus_pinnum( + gpio_dev, + button_number, + &button_pin)) == 0) { + gpio_pin_setflags(button_pin, GPIO_PIN_INPUT); + + bool active; + if (gpio_pin_is_active(button_pin, &active) == 0) { + printf("Button is %spressed", active ? "" : "not "); + } else { + printf("Failed to read button state\n"); + } + + + } else { + printf("error: %d\n", error); + return error; + } + } + + return error; +} + +static int loader(struct module *module, int what, void *arg) { + int error = 0; + + + switch (what) { + case MOD_LOAD: + printf("Module loaded into FreeBSD kernel\n"); + + error = load_metadata(); + if (error != 0) { + break; + } + + error = init_module(); + + break; + case MOD_UNLOAD: + printf("Module removed from the FreeBSD kernel\n"); + + if (led_pin) { + printf("releasing GPIO pin\n"); + gpio_pin_set_active(led_pin, FALSE); + gpio_pin_release(led_pin); + printf("pin released\n"); + } + + if (button_pin) { + printf("releasting button pin\n"); + gpio_pin_release(button_pin); + printf("pin released\n"); + } + + if (sysctl_ctx_free(&sysctl_ctx)) { + printf("Can't free the system call context\n"); + error = ENOTEMPTY; + } else { + printf("Metadata has been removed from sysctl tree\n"); + } + + break; + default: + error = EOPNOTSUPP; + break; + } + return (error); +} + +static moduledata_t mod = { + "gpioctrl", + loader, + NULL +}; + +DECLARE_MODULE(gpioctrl, mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE +); diff --git a/03_gpioctrl/led_button_Steckplatine.png b/03_gpioctrl/led_button_Steckplatine.png new file mode 100644 index 0000000..b2b4a71 Binary files /dev/null and b/03_gpioctrl/led_button_Steckplatine.png differ diff --git a/04_devicefiles/README.md b/04_devicefiles/README.md new file mode 100644 index 0000000..79c3d43 --- /dev/null +++ b/04_devicefiles/README.md @@ -0,0 +1,146 @@ +# 04_devicefiles (FreeBSD version) + +## Device Files + +Device files on FreeBSD are used as an interface between userland and a kernel module. Like on other \*nix systems, they can be found in the `/dev` directory. Device files are virtual files created by the _device file system (devfs)_ that represent physical or virtual devices on the system. + +If we run the command `ls -l` in the `/dev` directory, we can see the device files indicated by the letter **c** in front of the permissions: + +```shell +$ ls -la /dev/gpio* /dev/cuau0 +crw-rw---- 1 uucp dialer 0x31 Mar 31 15:16 /dev/cuau0 +crw------- 1 root wheel 0x2d Mar 31 15:03 /dev/gpioc0 +crw------- 1 root wheel 0x34 Mar 31 15:03 /dev/gpioc1 +``` + +The letter **c** at the beginning of the of the permissions indicated that the devices are _character_ devices. The number 1 is the link count for the file. The next two columns are the owner and the group that owns the device. In the case of `/dev/cuau0` the owner is "uucp" and the group is "dialer". The hex number is the Device ID. Next we have the modification dates and the file name. + +## Character Devices + +FreeBSD only uses character devices, having removed block devices by FreeBSD 5.0. The decision to do so was because the developers found block devices redundant as buffering and caching logic could be handled by the kernel. Leaving block devices in the system would have caused relability problems, as explained by the FreeBSD handbook: + +``` +Other UNIX® systems may support a second type of disk device known as block devices. Block devices are disk devices for which the kernel provides caching. This caching makes block-devices almost unusable, or at least dangerously unreliable. The caching will reorder the sequence of write operations, depriving the application of the ability to know the exact disk contents at any one instant in time[^1]. +``` + +## Device Numbers + +Device numbers are less important on FreeBSD than they are in Linux. On FreeBSD, the devfs system manages the devices nodes automatically in kernel space. The nodes are automatically created when the devfs service starts, and major and minor numbers are assigned to device. Unlike Linux, which breaks the Device ID into major and minor numbers, FreeBSD combines the two into a hex value. + +If we want to separate the major and minor number we can use the `stat -f` with command to format the number. + +```shell +$ stat -f "Major: %Hr, Minor: %Lr" /dev/cuau0 +Major: 0, Minor: 49 +``` + +The "%Hr" and "%Lr" in the escape sequence tell stat to format the response, with the higher part of the hex code being formatted into the string after the word "Major: " and the lower part of the hex formatted after "Minor: ". + +FreeBSD does not have the file /proc/device files. Instead if we want to get the major and minor number of a device we need to use a small shell script utilizing the stat command: + +```shell +$ find /dev -type c -exec stat -f "%N Major: %Hr Minor: %Lr" {} \; +/dev/reroot/reroot Major: 0 Minor: 3 +/dev/random Major: 0 Minor: 4 +/dev/sndstat Major: 0 Minor: 6 +/dev/devctl2 Major: 0 Minor: 7 +/dev/console Major: 0 Minor: 8 +/dev/geom.ctl Major: 0 Minor: 9 +/dev/devctl Major: 0 Minor: 10 +/dev/uinput Major: 0 Minor: 11 +/dev/input/event0 Major: 0 Minor: 12 +/dev/input/event1 Major: 0 Minor: 29 +/dev/kbdmux0 Major: 0 Minor: 13 +/dev/mem Major: 0 Minor: 15 +/dev/kmem Major: 0 Minor: 16 +/dev/netmap Major: 0 Minor: 17 +/dev/full Major: 0 Minor: 18 +/dev/null Major: 0 Minor: 19 +/dev/zero Major: 0 Minor: 20 +... +``` + +## Creating Device files + +### mknod + +FreeBSD has the [mknod](https://man.freebsd.org/cgi/man.cgi?query=mknod&sektion=8) command, but it is depreciated and not recommended. + +If we get the device number for /dev/mmcsd0s1, 0x59, and convert it form hex to decimal we get the major number 0 and minor number 89. We can then use the `mknod` command to create a node with the arguments _c_ for character device, and _0_ and _89_ for the major and minor numbers respectively. The file will be created in the location specified (in this case the current working directory was home) + +```shell +jholloway@BSD-PI4:~ $ ls -l /dev/mmcsd0s1 +crw-r----- 1 root operator 0x59 Mar 31 15:03 /dev/mmcsd0s1 + +jholloway@BSD-PI4:~ $ sudo mknod mymmc c 0 89 + +jholloway@BSD-PI4:~ $ ls +01_hello 03_gpioctrl gpioctrl-cross.ko mymmc +jholloway@BSD-PI4:~ $ ls -l mymmc +crw-r--r-- 1 root jholloway 0x59 Mar 31 15:39 mymmc +``` + +Using the _ls_ command we can see that the newly create node ~/mymmc has the same device number as the file /dev/mmcsd0s1. However, if we try to read from the file we will get an error: + +```shell +jholloway@BSD-PI4:~ $ hexdump mymmc | head +hexdump: mymmc: Operation not supported +``` + +Because mknod has been depreciated, as of FreeBSD 6.0 mknod can create nodes, but the nodes cannot be used to access devices. Instead we must use the devfs utility to create nodes. + +### devfs + +The [devfs](https://man.freebsd.org/cgi/man.cgi?query=devfs&apropos=0&sektion=8) utility in FreeBSD manages device file system mounts, which provide access to kernel-managed devices in userland through the /dev directory. The device file system (devfs) is a virtual filesystem that dynamically creates and manages device nodes corresponding to hardware and pseudo-devices recognized by the kernel + +We can copy an existing node using the devfs utility. First we can edit `/etc/devfs.conf` and add the following line to create a new node called mymmc that links to mmcsd0s1: + +```make +$ ls -l /dev/mmcsd0s1 /dev/mymmc +crw-r----- 1 root operator 0x59 Mar 31 15:03 /dev/mmcsd0s1 +lrwxr-xr-x 1 root wheel 8 Mar 31 16:06 /dev/mymmc -> mmcsd0s1 +``` + +Next we need to restart devfs service to force the system to recreate the device nodes: + +```shell +# service devfs restart +``` + +In addition to _mmcsd0s1_ inside of /dev, we will also see a new node in the directory called _mymmc_ + +```shell +$ ls -l /dev/mmcsd0s1 /dev/mymmc +crw-r----- 1 root operator 0x59 Mar 31 15:03 /dev/mmcsd0s1 +lrwxr-xr-x 1 root wheel 8 Mar 31 16:06 /dev/mymmc -> mmcsd0s1 +``` + +If we compare the original node /dev/mmcsd0s1 with that of /dev/mymmc we will see that it has a different device number, but it will specify that mymmc is a link to mmcsd0s1. This differs from the node created with the `mknode` command, which had the same device id as the original node. Unlike the node we made with the previous method, we can read and write to this device as illustrated with using hexdump: + +```shell +$ sudo hexdump /dev/mmcsd0s1 | head +0000000 3ceb 4290 4453 2e34 2034 0020 0802 0001 +0000010 0002 0002 f000 0032 003f 00ff 0000 0000 +0000020 9000 0001 0000 0729 cb0f 4526 4946 2020 +0000030 2020 2020 2020 4146 3154 2036 2020 31fa +0000040 8ec0 bcd0 7c00 8efb e8d8 0000 835e 19c6 +0000050 07bb fc00 84ac 74c0 b406 cd0e eb10 30f5 +0000060 cde4 cd16 0d19 4e0a 6e6f 732d 7379 6574 +0000070 206d 6964 6b73 0a0d 7250 7365 2073 6e61 +0000080 2079 656b 2079 6f74 7220 6265 6f6f 0d74 +0000090 000a 0000 0000 0000 0000 0000 0000 0000 + +$ sudo hexdump /dev/mymmc | head +0000000 3ceb 4290 4453 2e34 2034 0020 0802 0001 +0000010 0002 0002 f000 0032 003f 00ff 0000 0000 +0000020 9000 0001 0000 0729 cb0f 4526 4946 2020 +0000030 2020 2020 2020 4146 3154 2036 2020 31fa +0000040 8ec0 bcd0 7c00 8efb e8d8 0000 835e 19c6 +0000050 07bb fc00 84ac 74c0 b406 cd0e eb10 30f5 +0000060 cde4 cd16 0d19 4e0a 6e6f 732d 7379 6574 +0000070 206d 6964 6b73 0a0d 7250 7365 2073 6e61 +0000080 2079 656b 2079 6f74 7220 6265 6f6f 0d74 +0000090 000a 0000 0000 0000 0000 0000 0000 0000 +``` + +[^1] : https://www.freebsd.org/doc/en/books/arch-handbook/driverbasics-block.html diff --git a/README.md b/README.md new file mode 100644 index 0000000..83a7a30 --- /dev/null +++ b/README.md @@ -0,0 +1,57 @@ +# FreeBSD Driver Tutorial + +This is a fork of [Johannes4Linux](https://github.com/Johannes4Linux) tutorial series [Linux Kernel Modules and Linux Drivers](https://github.com/Johannes4Linux/Linux_Driver_Tutorial), but instead for creating driver modules for FreeBSD rather than linux. The goal is to recreate the lessons that Johannes created for Linux, but on FreeBSD. + +I am writing more detailed explanations of these kernel modules with step by step in commentary on my blog: [jholloway.dev](https://jholloway.dev). Please check there for more information regarding my journey in FreeBSD driver development. + +## Preparation + +I used a Raspberry Pi 4 Rev 4 with FreeBSD 14.2 installed to develop and test my modules and drivers. To compile them, you need to ensure that you have installed the FreeBSD source tree to /usr/src. If you have not, follow these steps on your Raspberry Pi installation of FreeBSD. + +Note: the _#_ indicates that the commands are be run as root. FreeBSD does not come with sudo installed by default, and instead to run the commands as a non-root user you will need to install either [sudo](https://www.freshports.org/security/sudo/) or [doas](https://www.freshports.org/security/doas/). + +```bash +# git clone --branch releng/14.2 https://git.FreeBSD.org/src.git /usr/src +``` +Some kernel modules will require the kernel headers and artifacts from the build process of building the kernel. To build the kernel on your Raspberry Pi clone the src to /usr/src and run the following command: + +```bash +# cd /usr/src +# make -j$(sysctl -n hw.ncpu) buildkernel +``` +This will build the toolchain, header files, artifacts, and a generic FreeBSD kernel for your Pi in the directory `/usr/obj/usr/src/arm64.aarch64/sys/GENERIC`.This will create the required kernel headers and artifacts For modules which require kernel artifacts, you will need to include this folder in your Makefile. This process can take time with the limited hardware that is on the Pi. For that reason, it can be faster to cross compile from a more powerful laptop or desktop and copy the kernel modules to your Pi. + + +### Cross Compiling to the Raspberry Pi + +In order to cross compile you will need to first make a kernel for the Raspberry Pi. Official FreeBSD images for the Pi use a generic ARM64 kernel, so we will compile a ARM64 generic kernel as well. + +```bash +# git clone --branch releng/14.2 https://git.FreeBSD.org/src.git /usr/src +# -j$(sysctl -n hw.ncpu) TARGET=arm64 TARGET_ARCH=aarch64 KERNCONF=GENERIC buildkernel + +``` + +The MakeFile for each module will need to be updated to include the following lines specifying the architecture: + +```make +CC=clang --target=aarch64-unknown-freebsd +MACHINE_ARCH=aarch64 +MACHINE=arm64 +``` + +For more information on getting FreeBSD setup on a Raspberry Pi, please see the post [First Kernel Modules](https://jholloway.dev/posts/first-kernel-modules/) + +## Content + +In this repo you can find examples for: +1. Simple Hello World kernel Module remade for FreeBSD +2. A better Hello World kernel module remade for FreeBSD that provides metadata for the kernel module. +3. An example for using GPIOs to read from a pin or turn on a pin in a kernel module. + +## More Information + +For more information about Johannes4Linux tutorial series please see his [tutorial playlist](https://www.youtube.com/watch?v=x1Y203vH-Dc&list=PLCGpd0Do5-I3b5TtyqeF1UdyD4C-S-dMa) +I also recommend supporting Johannes4Linux and buying him a coffee [buymeacoffee.com/johannes4linux](https://www.buymeacoffee.com/johannes4linux). + +All credit on this series goes to him as he was the one who got the ball rolling. This branch is merely a recreation of the work he has already done.