l4kd -- The L4 Kernel Debugger
Jochen Liedtke's L4 microkernel comes with a minimalistic kernel
debugger, L4KD, which is primarily intended for debugging the µ-kernel
but also allows examining the kernel and user state of user tasks.
L4KD is not a symbolic debugger. The programmer using it should have
a good understanding of the x86 architecture and should have an
assembler or disassembler listing of whatever he needs to debug next
to him to make sense of what's displayed.
When invoked, L4KD presents the user with its prompt, "L4KD:",
and then waits for the user to enter single-character commands (see
the section on commands).
L4KD is automatically invoked upon each boot of the L4 µ-kernel to
give the user a chance to set any initial breakpoints, etc. Normally,
the user has to enter an explicit "g" command (see below) to
get the system going.
Once the system is running, there are several ways to invoke L4KD:
- The CPU runs into a machine breakpoint previously set using L4KD.
- A thread executing in kernel mode catches an unhandled exception.
(This suggests a bug in the L4 µ-kernel.)
- A privileged user thread catches an unhandled exception. (User
threads can handle exceptions by installing a private LDT.)
(See also the Bugs section.)
- The µ-kernel's timer interrupt handler can monitor the keyboard
and/or a serial port. If the monitoring is enabled (see the
description of the ``I'' command below) and an Escape character
is detected, L4KD is entered.
L4KD can be controlled using the system's video console or using a
serial terminal - see the description of the "V" command
L4KD commands are single or double-character mnemonics; the case is
significant. Commands are invoked automatically once all required
arguments have been typed in; commands expecting no arguments are invoked
immediately. There is no command line editing.
Typing in arguments can be finished by typing Return or Space.
In the following list of commands, the following argument types are
used (all numbers must be given in hexadecimal representation):
- a virtual (or I/O) address.
- width of a memory access in bytes; can be 1, 2, or 4.
- an internal 2-byte (<=4 hex digits) thread reference
- an external 4-byte (>4 hex digits) thread
reference number; this is the low word of the external 8-byte thread
- a task number (0-1ff).
- a thread number within a single task
- a physical page frame number.
- an exception number.
- an exception error code.
Now for the list of commands itself:
- Show a short one-screen help.
- Resume execution.
- Reboot the machine. (Only works on the system console,
not via a serial line.)
- a [ addr ]
- Show absolute base addresses of the L4
kernel modules. Without an argument, shows all modules. With an
4-digit address, shows the module containing the code located at the
The module base addresses can be useful for setting a breakpoint base
address using ``bb'' when debugging a particular kernel module;
however, please note that the memory is mapped in at kernel virtual
address 0xf0000000, so this value needs to be added to the module base
- Show currently set machine breakpoint. (Please always
use ``b Return'' instead of ``b Space'' - see the Bugs
- Delete a currently set machine breakpoint, and reset all
- bb [ baseaddr ]
- Set a base address for future
breakpoints, or (when invoked without an argument) clear a previously
base address. See also ``a''.
- bi addr
- Set an instruction breakpint at virtual
address addr, or baseaddr+addr, if a base address has
previously been set.
- ba size addr
- Set an read or write access
breakpoint for access to the virtual address addr (or
baseaddr+addr, if a base address has previously been set) with
access width size (must be one of 1, 2, 4).
- bw size addr
- As ``ba'', but only for write
- bt [ threadid ]
- Restrict a previously set breakpoint
to a particular thread, or (when not given an argument) remove such a
- bx size addr lower_bound higher_bound
a previously set breakpoint. L4KD is
only entered if addr contains a value of size size that is
within the interval of [lower_bound, higher_bound]. If
lower_bound is greater then higher_bound L4KD is only
entered if value is not within the interval (higher_bound,
- t [ threadid | long_threadid | tasknr.threadnr ]
TCBs (thread control blocks), the state associated with threads. The
information displayed includes the thread's current kernel state,
register set, and kernel stack.
When given without an argument, display the current thread's TCB.
Otherwise display the given thread's TCB.
While displaying TCBs, the following additional commands are
- Cursor Up/Down/Left/Right
- Modify the displayed kernel stack pointer.
(This does not really modify the thread's stack pointer but is
intended for use with the next command.)
- Recalculate the displayed register set (taken from the
kernel stack) based on a new kernel stack pointer adjusted with the
previous commands. L4KD assumes to find a pusha like sequence
plus an additional error code on the stack (the page fault handler
seems to be the only one that generates this sequence). This can be
used to evaluate the state of a thread that raised a page fault. Move
the stack pointer up until the error code (0xfe00000?) is gone and
- d [ addr ]
- Dump (display) memory at the current thread's
virtual address addr (0 if none given). By default, the memory
is displayed in hexadecimal form using 4-byte integers.
While dumping memory, the following additional commands are
- Cursor Up/Down/Left/Right
- Change cursor position.
- Scroll the display down/up one screen.
- Change the displayed data format. This command cycles
between the following formats:
- hexadecimal display of 4-byte integers (default when invoking ``d''),
- hexadecimal display of bytes (1-byte integers),
- x86 page table.
- Interpret the 4-byte integer the cursor is positioned at as
a pointer and start dumping memory at that address.
- Return to the address of the pointer last dereferenced with
Return. (L4KD doesn't keep a history of all previous Returns, only
the last one.)
- p [ tasknr | addr ]
- Dump x86 page table or page
directory for task tasknr, or interpret memory at addr as
a page table and dump it.
The same commands as with ``d'' are available here, too, with two
- It's impossible to scroll out of a single page using the scrolling
- The Home key can un-dereference twice: back from a data page to a
page table, and back from a page table to a page directory.
- m [ framenr ]
- Display mappings from the mapping
database for physical page frame framenr (i.e., for physical
The mapping database represents mappings using a tree data
structure. The display's last three colums represent the links within
this tree. You can use the Cursor Down/Left/Right keys to traverse the
tree. You can follow a link as long as the last bit of the link is
zero. If you follow a link to the left/right with a least significant
bit set to one it will lead you back to the previous level. If you
follow such a link down, you will stay at the same point.
- v addr tasknr
- Display the virtual-to-physical
address mapping for virtual address addr in task taskno.
- Display global kernel state. This includes root pointers
for various thread queues, the system clock, and the page directory
- i size addr
- Input a value of width size
from I/O address addr and display it. size must be one of
1, 2, 4.
- i [ a | i ] addr
- Input a value from the
APIC (``ia'') resp. the I/O APIC (``ii'') at address
addr and display it.
- o [ size | a | i ] addr value
the corresponding form of ``i'', but output the value given.
- Halt the current thread. (See the bugs section)
- T [ + | - ]
- Turn on and off single-stepping.
- P [ + | * | - ]
- Monitor all page
faults in the system (``P+'' and ``P*''), or turn off
``P+'' stops the system upon each page fault and gives the user
the option of entering the kernel debugger (press ``i'' when a
page fault message turns up) or simply resuming execution (any other
``P*'' just displays all page fault messages and doesn't offer
entering L4KD at page fault time.
- X [ + excpnr [ errcode_low errcode_high ] | - ]
L4KD upon catching an excpnr exception with an error code
in the given range. (The error code range is only prompted for for
exceptions delivering error codes.)
``X-'' turns of exception monitoring.
- I [ + | - ]
- The command ``I+'' starts
monitoring the keyboard (and a serial line, if initialized using
``VT'') in the timer interrupt for a press of the Escape key. If
such an event occurs, L4KD is entered. Also, the timer interrupt will
output a progress indicator on the video console (not on a serial
terminal!) to make it easier to detect a hung system.
``I-'' disables this monitoring.
- V [ a | c | m | h | T]
a video mode for interaction with L4KD. ``Vc'' selects the CGA
output, and ``Vh'' or ``Vm'' select MGA (or Hercules)
output. ``Va'' makes an atomatic choice between the first few
options (it uses the default video mode of the machine).
``VT'' additionally enables output via a serial terminal
(attached to COM2; 115.2kBit/s, 8N1). Input from the terminal can
also be enabled by typing the character ``+'' on the terminal.
Once this initialization has been completed, the terminal can also be
used to send Escape characters to enter L4KD (see ``I+'').
Serial I/O and video console I/O can be used to control L4KD at the
same time: Any character typed on any of the input media will appear
on the other console device, too.
4.1 Serious Bugs
The "H" command (halt kernel thread) currently doesn't work.
In all current versions of L4, all user tasks are privileged in the
sense that it is trivial for them to invoke L4KD, e.g. using the enter_kdebug interface.
4.2 Minor Bugs
"b Space" doesn't show the current breakpoint (it displays
nothing) - use "b Return".
(Pentium version only:) Due to machine limitations, instruction
pointer breakpoints in the virtual address space of tasks which have
been put into an L4 ``small address space'' have to be specified with
the instruction's linear address which is its virtual address plus the
small address space's segment's start address. Segments for small
address spaces start at linear address 0xC0000000.
When displaying on a serial terminal, the last (80th) column is not
displayed, and the display cannot be adjusted
to 24 instead of 25 lines. In practice, this seldom hurts.
When using a serial terminal, the PageUp/PageDown keys are reverted,
and the Home key is unavailable; also, there is no easy way to reboot
the machine (Shift-Num doesn't work).
There is no way to browse the memory of a thread different from the
current one in a "d"-like way. The only way to inspect memory
of a different task is to lookup single pages of memory through the
task's page directory using "p".
When browsing page directories using "p", dereferencing
pointers to super (4 MB) pages (by pressing Return) doesn't work
There is no way to modify memory, even though there is a way to in/out
bytes through I/O memory ("i"/"o").
There is currently no direct way to circumvent the initial invocation
of L4KD during boot. However, this can be accomplished indirectly
In this manual page, the description of the "m" command is too
5 See Also
l4kd_api , rmgr
Michael Hohmuth, Jean Wolter