<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Alexey Denisov</title>
    <description>The latest articles on DEV Community by Alexey Denisov (@alexeyden).</description>
    <link>https://dev.to/alexeyden</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F360136%2Fec13af2b-e95c-4251-a355-b246e6699c69.jpeg</url>
      <title>DEV Community: Alexey Denisov</title>
      <link>https://dev.to/alexeyden</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alexeyden"/>
    <language>en</language>
    <item>
      <title>Quick QEMU setup for Linux kernel module debugging</title>
      <dc:creator>Alexey Denisov</dc:creator>
      <pubDate>Tue, 19 Jan 2021 10:46:41 +0000</pubDate>
      <link>https://dev.to/alexeyden/quick-qemu-setup-for-linux-kernel-module-debugging-2nde</link>
      <guid>https://dev.to/alexeyden/quick-qemu-setup-for-linux-kernel-module-debugging-2nde</guid>
      <description>&lt;p&gt;As my first post here on dev.to, I have decided to share my little note on how to quickly setup up an environment for linux kernel module debugging in QEMU.&lt;/p&gt;

&lt;h1&gt;
  
  
  Step 1: Building linux kernel and busybox userland
&lt;/h1&gt;

&lt;p&gt;Download and extract linux kernel and busybox sources.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.6.tar.xz
$ tar xvf linux-5.6.tar.xz
$ wget https://busybox.net/downloads/busybox-1.31.1.tar.bz2
$ tar xvf busybox-1.31.1.tar.bz2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configure and build linux kernel as usual. A couple of points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can safely start with &lt;code&gt;defconfig&lt;/code&gt;, enabling options your module depends on.&lt;/li&gt;
&lt;li&gt;Make sure that all options you will need during debugging are compiled-in and not built as modules, so you don't have to cram them into the initramfs.&lt;/li&gt;
&lt;li&gt;Enable at least these options if you want to use gdb (see "Kernel hacking" section in &lt;code&gt;menuconfig&lt;/code&gt;):

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;CONFIG_DEBUG_KERNEL=y&lt;/code&gt; (enables kernel debugging facilities)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;CONFIG_DEBUG_INFO=y&lt;/code&gt; (includes debug symbols)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;CONFIG_KGDB=y&lt;/code&gt; (enables kernel GDB backend over serial line)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;CONFIG_PROVE_LOCKING=y&lt;/code&gt; (enable lock dependency checker; not needed for gdb to work, but comes handy for deadlock debugging).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can quickly check if your kernel boots at all:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;qemu-system-x86_64 -kernel linux-5.6.2/arch/x86_64/boot/bzImage \
-nographic \
-append "console=ttyS0"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, it is time to build our busybox userland. Again, &lt;code&gt;defconfig&lt;/code&gt; will do just fine, with a couple of points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Busybox must be linked statically: set &lt;code&gt;CONFIG_STATIC=y&lt;/code&gt; (found in "Settings" - "Build Options" section)&lt;/li&gt;
&lt;li&gt;Expect surprises when building busybox on a system with latest glibc versions. In the case of Archlinux, I've got the following:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;netstat.c:(.text.ip_port_str+0x50): warning: Using 'getservbyport' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
...
rdate.c:(.text.rdate_main+0xf8): undefined reference to `stime'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can get around both problems by building busybox with musl instead if glibc:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Archlinux
sudo pacman -S musl kernel-headers-musl
make CC=musl-gcc

# Ubuntu
sudo apt install musl musl-dev musl-tools
CFLAGS="-I../linux-5.6/usr/include" make CC=musl-gcc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If using kernel headers from the kernel tarball, you have to install them first locally (&lt;code&gt;make headers&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Alternatively, you can try to proceed with glibc by applying &lt;a href="https://git.busybox.net/busybox/patch/?id=d3539be8f27b8cbfdfee460fe08299158f08bcd9"&gt;this patch&lt;/a&gt; to fix stime calls (of course, this wont fix glibc broken static linking warnings).&lt;/p&gt;

&lt;h1&gt;
  
  
  Step 2: Building minimal initramfs
&lt;/h1&gt;

&lt;p&gt;Busybox binary and ab init script are pretty much all we need for our simple initramfs. Let's put busybox in to /bin and create a /bin/sh symlink, so kernel can look up the shell for our init script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir bin
cp ../busybox-1.31.1/busybox bin/
ln -s /bin/busybox bin/sh
touch init &amp;amp;&amp;amp; chmod 777 init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our init script should at least do the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;mount procfs and sysfs;&lt;/li&gt;
&lt;li&gt;populate /dev using information provided by kernel at sysfs (busybox's mdev can nicely do it for us);&lt;/li&gt;
&lt;li&gt;do your initialization (load a module for debugging, run a test program, you name it); the snippet below also contains an example on how to run some code on per-VM instance basis, which is needed when you are debugging some module that has to communicate to its remote counterpart on another machine;&lt;/li&gt;
&lt;li&gt;drop to interactive shell.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's the init script template I use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="c"&gt;# Install busybox applets as symlinks&lt;/span&gt;
/bin/busybox &lt;span class="nt"&gt;--install&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; /bin

&lt;span class="c"&gt;# Mount procfs &amp;amp; sysfs, populate /dev&lt;/span&gt;
&lt;span class="nb"&gt;mkdir &lt;/span&gt;proc sys
mount &lt;span class="nt"&gt;-t&lt;/span&gt; proc none /proc
mount &lt;span class="nt"&gt;-t&lt;/span&gt; sysfs none /sys
mdev &lt;span class="nt"&gt;-s&lt;/span&gt;

&lt;span class="c"&gt;# Optional dynamic device creation:&lt;/span&gt;
&lt;span class="c"&gt;# 1. Old method using mdev as an uevent helper&lt;/span&gt;
&lt;span class="c"&gt;# echo /bin/mdev &amp;gt; /proc/sys/kernel/hotplug &lt;/span&gt;
&lt;span class="c"&gt;# 2. New method using Netlink (requires busybox 1.31 or newer)&lt;/span&gt;
&lt;span class="c"&gt;# mdev -d&lt;/span&gt;

&lt;span class="c"&gt;# Get virtual machine instance number passed as a kernel argument&lt;/span&gt;
&lt;span class="nv"&gt;inst&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /proc/cmdline | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-Eo&lt;/span&gt; &lt;span class="s1"&gt;'vm_inst=[^ ]+'&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'='&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; 2&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Do your common initialization here (insert debugged module, run some test script, etc)&lt;/span&gt;
&lt;span class="nb"&gt;echo &lt;/span&gt;the init is running

&lt;span class="c"&gt;# Do your per-vm instance initialization here (assign an IP address, etc)&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$inst&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; 1 &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo &lt;/span&gt;first instance init
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$inst&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; 2 &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo &lt;/span&gt;second instance init

&lt;span class="c"&gt;# Drop to the shell&lt;/span&gt;
&lt;span class="nb"&gt;exec &lt;/span&gt;setsid cttyhack sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A couple of notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automatic device creation will obviously not work with this simplistic setup, so when, say, you create a character device in your driver, you have to start &lt;code&gt;mdev -s&lt;/code&gt; again manually.&lt;/li&gt;
&lt;li&gt;In order to enable hotplugging, you have to either use busybox 1.31 or later, witch introduced netlink interface support for mdev (&lt;code&gt;mdev -d&lt;/code&gt; option).
Alternatively, you can enable legacy &lt;code&gt;CONFIG_UEVENT_HELPER&lt;/code&gt; option in Linux (disabled by default) and set hotplug executable to mdev: &lt;code&gt;echo /bin/mdev &amp;gt; /proc/sys/kernel/hotplug&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;It is not very convenient to just drop to shell using &lt;code&gt;exec /bin/sh&lt;/code&gt; because at startup the &lt;code&gt;/dev/console&lt;/code&gt; is used by default as a tty.
The problem is &lt;code&gt;/dev/console&lt;/code&gt; can't be a controlling terminal, thus rendering the job control stuff broken (usually indicated by &lt;code&gt;can't access tty; job control turned off&lt;/code&gt; message).
This can be easily worked around with &lt;code&gt;setsid cttyhack sh&lt;/code&gt;. &lt;code&gt;setsid&lt;/code&gt; will exec a cttyhack as a session leader, which will enable job control, and &lt;code&gt;cttyhack&lt;/code&gt; will find a real tty device (usually ttyS0) and reopen stdin/stdout.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, let's generate the initramfs image (assuming &lt;code&gt;initramfs&lt;/code&gt; is your initramfs root containing &lt;code&gt;/init&lt;/code&gt; script and the &lt;code&gt;/bin&lt;/code&gt; directory):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd initramfs                                                                                                                            
find . -print0 | cpio --null -ov -H newc | gzip -9 &amp;gt; ../initramfs.tgz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Step 3: Running QEMU
&lt;/h1&gt;

&lt;p&gt;Running QEMU is as easy as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;qemu-system-x86_64 \
-enable-kvm -cpu host -smp 1 \
-m 256M \
-nographic \
-kernel linux-5.6.2/arch/x86_64/boot/bzImage \
-initrd initramfs.tgz \
-chardev socket,id=gdb,port=1234,server,nowait,host=0.0.0.0 \
-device pci-serial,chardev=gdb \
-append "console=ttyS0 vm_inst=1 kgdbwait kgdboc=ttyS1,115200"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;-enable-kvm -cpu host -smp 2&lt;/code&gt; enables KVM acceleration and sets emulated CPU model to the host CPU; &lt;code&gt;-smp&lt;/code&gt; sets the number of CPU cores the guest can use (1 by default);&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-m 256M&lt;/code&gt; specifies the amount of memory guest can use (128M by default);&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-nographic&lt;/code&gt; disables graphical output and instead emulates a serial port (&lt;code&gt;ttyS0&lt;/code&gt;) attached to stdout/stdin;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-kernel&lt;/code&gt; and &lt;code&gt;-initrd&lt;/code&gt; are quite self explanatory;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-chardev socket,id=gdb,port=1234,server,nowait,host=0.0.0.0&lt;/code&gt; creates a socket listening for GDB frontend connections on TCP port 1234; this socket is later bound to the virtual serial port, which will appear as &lt;code&gt;ttyS1&lt;/code&gt; in the VM;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-device pci-serial,chardev=gdb&lt;/code&gt; creates an emulated PCI serial port device, redirecting its IO to the TCP socket;&lt;/li&gt;
&lt;li&gt;-append "console=ttyS0 vm_inst=1 kgdbwait kgdboc=ttyS1,115200" specifies the kernel command line; remove &lt;code&gt;kgdbwait&lt;/code&gt; to let the kernel boot without waiting for GDB connection.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Attaching the gdb to the running kernel is also easy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cd linux-5.6
$ gdb vmlinux

(gdb) target remote :1234
(gdb) c
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll probably need to explicitly allow gdb to load linux kernel debugging helper scripts. GDB will nicely warn about this if auto-loading is declined:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;echo 'add-auto-load-safe-path /home/dav/Dev/qemu/linux-5.6/scripts/gdb/vmlinux-gdb.py' &amp;gt;&amp;gt; ~/.gdbinit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A bit more evolved example: running two machine instances communicating over emulated null modem.&lt;/p&gt;

&lt;p&gt;Instance 1:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;qemu-system-x86_64 \
    -enable-kvm \
    -cpu host \
    -smp 2 \
    -nographic \
    -kernel linux-5.6.2/arch/x86_64/boot/bzImage \
    -initrd initram.tgz \
    -chardev socket,id=comm,port=8909,host=127.0.0.1 \
    -device pci-serial,chardev=comm \
    -chardev socket,id=gdb,port=1234,server,nowait,host=0.0.0.0 \
    -device pci-serial,chardev=gdb \
    -append "console=ttyS0 vm_inst=1 kgdbwait kgdboc=ttyS2,115200"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instance 2:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;qemu-system-x86_64 \
    -enable-kvm \
    -cpu host \
    -smp 2 \
    -nographic \
    -kernel linux-5.6.2/arch/x86_64/boot/bzImage \
    -initrd initram.tgz \
    -chardev socket,port=8909,server,id=comm,host=0.0.0.0 \
    -device pci-serial,chardev=comm \
    -chardev socket,id=gdb,port=1235,server,nowait,host=0.0.0.0 \
    -device pci-serial,chardev=gdb \
    -append "console=ttyS0 vm_inst=2 kgdbwait kgdboc=ttyS2,115200"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's all for now :) Hope you'll find this useful.&lt;/p&gt;

</description>
      <category>linux</category>
    </item>
  </channel>
</rss>
