diff --git a/docs/kernels.md b/docs/kernels.md index 5adf7500d..c7376abef 100644 --- a/docs/kernels.md +++ b/docs/kernels.md @@ -36,6 +36,35 @@ In summary, LinuxKit offers a choice of the following kernels: - [linuxkit/kernel-fedora](https://hub.docker.com/r/linuxkit/kernel-fedora/): Selected Fedora kernels. +## Compiling kernel modules + +The LinuxKit kernel packages include `kernel-dev.tar` which contains +the headers and other files required to compile kernel modules against +the specific version of the kernel. Currently, the headers are not +included in the initial RAM disk, but it is possible to compile custom +modules offline and include then include the modules in the initial +RAM disk. + +There is a [example](../tests/kmod), but basically one can use a +multi-stage build to compile the kernel modules: +``` +FROM linuxkit/kernel:4.9.x AS ksrc +# Extract headers and compile module +FROM linuxkit/kernel-compile:1b396c221af673757703258159ddc8539843b02b@sha256:6b32d205bfc6407568324337b707d195d027328dbfec554428ea93e7b0a8299b AS build +COPY --from=ksrc /kernel-dev.tar / +RUN tar xf kernel-dev.tar + +# copy module source code and compile +``` + +To use the kernel module, we recommend adding a final stage to the +Dockerfile above, which copies the kernel module from the `build` +stage and performs a `insmod` as the entry point. You can add this +package to the `onboot` section in your YAML +file. [kmod.yml](../tests/kmod/kmod.yml) contains an example for the +configuration. + + ## Working with Linux kernel patches for LinuxKit We may apply patches to the Linux kernel used in LinuxKit, primarily to diff --git a/kernel/Dockerfile b/kernel/Dockerfile index 7cbf93542..da1051f27 100644 --- a/kernel/Dockerfile +++ b/kernel/Dockerfile @@ -53,9 +53,13 @@ RUN DVER=$(basename $(find /tmp/kernel-modules/lib/modules/ -mindepth 1 -maxdept dir=/tmp/usr/src/linux-headers-$DVER && \ mkdir -p $dir && \ cp /linux/.config $dir && \ - cd /linux && \ - cp -a include "$dir" && \ - mkdir -p "$dir"/arch/x86 && cp -a arch/x86/include "$dir"/arch/x86/ && \ + cp /linux/Module.symvers $dir && \ + find . -path './include/*' -prune -o \ + -path './arch/*/include' -prune -o \ + -path './scripts/*' -prune -o \ + -type f \( -name 'Makefile*' -o -name 'Kconfig*' -o -name 'Kbuild*' -o \ + -name '*.lds' -o -name '*.pl' -o -name '*.sh' \) | \ + tar cf - -T - | (cd $dir; tar xf -) && \ ( cd /tmp && tar cf /out/kernel-dev.tar usr/src ) RUN printf "KERNEL_SOURCE=${KERNEL_SOURCE}\n" > /out/kernel-source-info diff --git a/test/kmod/Dockerfile b/test/kmod/Dockerfile new file mode 100644 index 000000000..7b45e112a --- /dev/null +++ b/test/kmod/Dockerfile @@ -0,0 +1,21 @@ +# This Dockerfile extracts the kernel headers from the kernel image +# and then compiles a simple hello world kernel module against them. +# In the last stage, it creates a package, which can be used for +# testing. + +FROM linuxkit/kernel:4.9.x AS ksrc + +# Extract headers and compile module +FROM linuxkit/kernel-compile:1b396c221af673757703258159ddc8539843b02b@sha256:6b32d205bfc6407568324337b707d195d027328dbfec554428ea93e7b0a8299b AS build +COPY --from=ksrc /kernel-dev.tar / +RUN tar xf kernel-dev.tar + +WORKDIR /kmod +COPY ./src/* ./ +RUN make all + +# Package +FROM alpine:3.5 +COPY --from=build /kmod/hello_world.ko / +COPY check.sh /check.sh +ENTRYPOINT ["/bin/sh", "/check.sh"] diff --git a/test/kmod/check.sh b/test/kmod/check.sh new file mode 100755 index 000000000..02e491624 --- /dev/null +++ b/test/kmod/check.sh @@ -0,0 +1,15 @@ +#!/bin/sh +function failed { + printf "Kernel module test suite FAILED\n" + /sbin/poweroff -f +} + +uname -a +modinfo hello_world.ko || failed +insmod hello_world.ko || failed +[ -n "$(dmesg | grep -o 'Hello LinuxKit')" ] || failed +rmmod hello_world || failed + +printf "Kernel module test suite PASSED\n" + +/sbin/poweroff -f diff --git a/test/kmod/kmod.yml b/test/kmod/kmod.yml new file mode 100644 index 000000000..ff554c1e5 --- /dev/null +++ b/test/kmod/kmod.yml @@ -0,0 +1,17 @@ +kernel: + image: "linuxkit/kernel:4.9.x" + cmdline: "console=ttyS0" +init: + - linuxkit/init:63eed9ca7a09d2ce4c0c5e7238ac005fa44f564b + - linuxkit/runc:b0fb122e10dbb7e4e45115177a61a3f8d68c19a9 + - linuxkit/containerd:18eaf72f3f4f9a9f29ca1951f66df701f873060b +onboot: + - name: check + image: "kmod-test" + binds: + - /dev:/dev + - /lib/modules:/lib/modules + capabilities: + - all +outputs: + - format: kernel+initrd diff --git a/test/kmod/run_test.sh b/test/kmod/run_test.sh new file mode 100755 index 000000000..f0ffe7f27 --- /dev/null +++ b/test/kmod/run_test.sh @@ -0,0 +1,10 @@ +#! /bin/sh + +# Make sure we have the latest kernel image +docker pull linuxkit/kernel:4.9.x +# Build a package +docker build -t kmod-test . +# Build a LinuxKit image with kernel module (and test script) +moby build kmod +# Run it +linuxkit run kmod diff --git a/test/kmod/src/Makefile b/test/kmod/src/Makefile new file mode 100644 index 000000000..31c8215dd --- /dev/null +++ b/test/kmod/src/Makefile @@ -0,0 +1,6 @@ +obj-m += hello_world.o +KVER=$(shell basename /usr/src/linux-headers-*) +all: + make -C /usr/src/$(KVER) M=$(PWD) modules +clean: + make -C /usr/src/$(KVER) M=$(PWD) clean diff --git a/test/kmod/src/hello_world.c b/test/kmod/src/hello_world.c new file mode 100644 index 000000000..7dd6d3ee2 --- /dev/null +++ b/test/kmod/src/hello_world.c @@ -0,0 +1,22 @@ +/* + * A simple Hello World kernel module + */ +#include +#include + +int init_hello(void) +{ + printk(KERN_INFO "Hello LinuxKit\n"); + return 0; +} + +void exit_hello(void) +{ + printk(KERN_INFO "Goodbye LinuxKit.\n"); +} + +module_init(init_hello); +module_exit(exit_hello); +MODULE_AUTHOR("Rolf Neugebauer "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("A simple Hello World kernel module for testing");