diff --git a/README.md b/README.md index f7874ae..13c8579 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Linux Kernel Module Cheat -Run one command, get into QEMU Buildroot BusyBox with several minimal Linux kernel 4.9 module example tutorials with GDB debug support. Tested in Ubuntu 14.04 - 16.10 hosts. +Run one command, get into QEMU Buildroot BusyBox with several minimal Linux kernel 4.9 module example tutorials with GDB debug support. x86 and ARM guests supported. Tested in Ubuntu 14.04 - 16.10 hosts. Usage: @@ -176,6 +176,37 @@ And then tell GDB where the module was loaded with: Ctrl + C add-symbol-file ../kernel_module-1.0/fops.ko 0xfffffffa00000000 +## ARM + +The portability of the kernel and toolchains is amazing. + +If you already have an x86 build present, first: + + cd buildroot + mv output output.x86~ + +First ARM build: + + ./run -a arm + +Run without build: + + ./runqemu -a arm + +Debug: + + ./runqemu -a arm -d + # On another terminal. + ./rungdb -a arm + +ARM TODOs: + +- only managed to run in the terminal interface (but weirdly an blank QEMU window is still opened) +- Ctrl + C kills the emulator, not sent to guest. See: + - + - +- `fops.ko`, `printf a >fops` crashes with `BUG: recent printk recursion!` + ## Table of contents 1. [Introduction](introduction.md) diff --git a/kernel_module/external.mk b/kernel_module/external.mk index 3dc5aad..878bd1e 100644 --- a/kernel_module/external.mk +++ b/kernel_module/external.mk @@ -9,7 +9,14 @@ KERNEL_MODULE_SITE = $(BR2_EXTERNAL_KERNEL_MODULE_PATH) KERNEL_MODULE_SITE_METHOD = local define KERNEL_MODULE_BUILD_CMDS - $(MAKE) -C '$(@D)' LINUX_DIR='$(LINUX_DIR)' PWD='$(@D)' CC='$(TARGET_CC)' LD='$(TARGET_LD)' + $(MAKE) \ + -C '$(@D)' \ + ARCH='$(KERNEL_ARCH)' \ + CC='$(TARGET_CC)' \ + CROSS_COMPILE='$(TARGET_CROSS)' \ + LD='$(TARGET_LD)' \ + LINUX_DIR='$(LINUX_DIR)' \ + PWD='$(@D)' endef define KERNEL_MODULE_INSTALL_TARGET_CMDS diff --git a/kernel_module/test/myrmmod.c b/kernel_module/test/myrmmod.c index 1224cc4..3bf7179 100644 --- a/kernel_module/test/myrmmod.c +++ b/kernel_module/test/myrmmod.c @@ -15,7 +15,7 @@ int main(int argc, char **argv) { return EXIT_FAILURE; } if (delete_module(argv[1], O_NONBLOCK) != 0) { - perror("delete_modul"); + perror("delete_module"); return EXIT_FAILURE; } return EXIT_SUCCESS; diff --git a/run b/run index 6ed3844..1c19438 100755 --- a/run +++ b/run @@ -1,7 +1,25 @@ #!/usr/bin/env bash set -e cd buildroot -make BR2_EXTERNAL="$(pwd)/../kernel_module" qemu_x86_64_defconfig +arch='x86_64' +while getopts a: OPT; do + case "$OPT" in + a) + arch=$OPTARG + ;; + esac +done +case "$arch" in + x86_64) + defconfig=qemu_x86_64_defconfig + ;; + arm) + # qemu_arm_vexpress_defconfig required a newer QEMU than 2.0.0 on a Ubuntu host. + # so let's stick to versatile for now. + defconfig=qemu_arm_versatile_defconfig + ;; +esac +make BR2_EXTERNAL="$(pwd)/../kernel_module" "$defconfig" # Can't get rid of this for now. # http://stackoverflow.com/questions/44078245/is-it-possible-to-use-config-fragments-with-buildroots-config cat ../buildroot_config_fragment >> .config diff --git a/rungdb b/rungdb index c0547cb..438a2bc 100755 --- a/rungdb +++ b/rungdb @@ -1,22 +1,51 @@ #!/usr/bin/env bash + +set -e + +arch=x86_64 +while getopts a: OPT; do + case "$OPT" in + a) + arch=$OPTARG + ;; + esac +done +shift "$(($OPTIND - 1))" if [ "$#" -gt 0 ]; then brk="-ex 'break $1'" else brk="" fi -gdb="$(pwd)/buildroot/output/host/usr/bin/x86_64-linux-gdb" + +gdb="$(pwd)/buildroot/output/host/usr/bin/${arch}-linux-gdb" cd buildroot/output/build/linux-*.*.*/ -cmd="$gdb \ - -q \ - -ex 'add-auto-load-safe-path $(pwd)' \ - -ex 'file vmlinux' \ - -ex 'set arch i386:x86-64:intel' \ - -ex 'target remote localhost:1234' \ - $brk \ - -ex 'continue' \ - -ex 'disconnect' \ - -ex 'set arch i386:x86-64' \ - -ex 'target remote localhost:1234' \ - -ex 'lx-symbols ../kernel_module-1.0/' -" +case "$arch" in + x86_64) + # http://stackoverflow.com/questions/11408041/how-to-debug-the-linux-kernel-with-gdb-and-qemu/33203642#33203642 + # http://stackoverflow.com/questions/4943857/linux-kernel-live-debugging-how-its-done-and-what-tools-are-used/42316607#42316607 + # http://stackoverflow.com/questions/28607538/how-to-debug-linux-kernel-modules-with-qemu/44095831#44095831 + cmd="$gdb \ + -q \ + -ex 'add-auto-load-safe-path $(pwd)' \ + -ex 'file vmlinux' \ + -ex 'set arch i386:x86-64:intel' \ + -ex 'target remote localhost:1234' \ + $brk \ + -ex 'continue' \ + -ex 'disconnect' \ + -ex 'set arch i386:x86-64' \ + -ex 'target remote localhost:1234' \ + -ex 'lx-symbols ../kernel_module-1.0/' + " + ;; + arm) + cmd="$gdb \ + -q \ + -ex 'add-auto-load-safe-path $(pwd)' \ + -ex 'file vmlinux' \ + -ex 'target remote localhost:1234' \ + -ex 'lx-symbols ../kernel_module-1.0/' + " + ;; +esac eval "$cmd" diff --git a/runqemu b/runqemu index 7606a9b..69c1eb9 100755 --- a/runqemu +++ b/runqemu @@ -3,12 +3,16 @@ set -e # CLI handling. +arch=x86_64 debug=false nographic=false extra_append='' extra_flags='' -while getopts dn OPT; do +while getopts a:dn OPT; do case "$OPT" in + a) + arch=$OPTARG + ;; d) debug=true extra_flags="$extra_flags -S -s" @@ -21,23 +25,41 @@ while getopts dn OPT; do esac done -# If we turn on buildroot host QEMU some day. -#cmd="./buildroot/output/host/usr/bin/qemu-system-x86_64 \ -cmd="qemu-system-x86_64 \ - -M pc \ - -append 'root=/dev/vda $extra_append' \ - -drive file=buildroot/output/images/rootfs.ext2,if=virtio,format=raw \ - -kernel buildroot/output/images/bzImage \ - -m 128M \ - -net nic,model=virtio \ - -net user \ - -smp 1 \ - $extra_flags \ -; -" +images_dir='buildroot/output/images' +case "$arch" in + x86_64) + # If we turn on buildroot host QEMU some day. + #cmd="./buildroot/output/host/usr/bin/qemu-system-x86_64 \ + cmd="qemu-system-x86_64 \ + -M pc \ + -append 'root=/dev/vda $extra_append' \ + -drive file=${images_dir}/rootfs.ext2,if=virtio,format=raw \ + -kernel ${images_dir}/bzImage \ + -m 128M \ + -net nic,model=virtio \ + -net user \ + -smp 1 \ + $extra_flags + " + ;; + arm) + cmd="qemu-system-arm \ + -M versatilepb \ + -append 'root=/dev/sda console=ttyAMA0,115200' \ + -drive file=${images_dir}/rootfs.ext2,if=scsi,format=raw \ + -dtb ${images_dir}/versatile-pb.dtb \ + -kernel ${images_dir}/zImage \ + -m 128M \ + -net nic,model=rtl8139 \ + -net user \ + -serial stdio \ + -smp 1 \ + $extra_flags" + ;; +esac -if $debug && ! $nographic; then - eval nohup "$cmd" &>/dev/null & +if "$debug" && ! "$nographic" && [ ! "$arch" = 'arm' ]; then + eval "$cmd" &>/dev/null & # TODO: Ctrl +C gets sent to QEMU? Why? Does not happen if I run # ./rungdb manually from outside this script!!! But why?!?! # eval has nothing to do with it, minimized example with explicit