Booting oskit-mach with a serial console ======================================== Author: Igor Khavkine Last Updated: Fri Aug 31 22:15:24 EDT 2001 Copyright (c) 2001 Igor Khavkine. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with no Invariant Sections, with the Front-Cover Texts being Title, Author and Last Updated, and with no Back-Cover Texts. A copy of the license can be obtained from http://www.gnu.org/licenses/fdl.txt Why? ---- Why? because it's covenient. If you have a second computer, but not a second monitor or keyboard, you can connet your second box to your main one using null-modem serial cables. Once that is done, you can configure the GRUB bootloader to use the serial port when starting up and boot oskit-mach without having to switch monitor cables or type blindly at a second keyboard. Also, oskit-mach supports the GDB remote debugging protocol over a serial line. This way it's now possible to debug the running kernel relatively unobtrusively because, the debugger will not be running on the same machine. How? ---- First you need some equipment: two computers, each one should have at least one (two is preferable) free serial ports, as many null-modem serial cables. Then you need to follow the instructions given below. (1) The first step is to get source for oskit-mach and oskit. Currently (2001-07-30) you have to get the sources for oskit from [2] and compile them yourself. You need to get the snapshot oskit-20000202, newer versions either don't work or have not been tested. Hoefuly this situation will change in the future. Now is your chance (if you wish) to compile oskit with debugging symbols. (If you encounter errors while compiling, try removing anything that has to do with `unix' or `examples' from the file modules.x86.pc). Then you need to get the sources for oskit-mach. They are available from the GNU CVS repository[3], you need to check out the `gnumach' module with the flag `-roskit-branch'. Again now is your chance to compile oskit-mach with debugging symbols. To make the compiled kernel image usable, you have to apply a small patch attached in Appendix B. (2) Now you need to setup GRUB on your second box so it would accept input from a serial port while booting up. This is simple to do by adding the following lines to your `menu.lst' file, before any of the menu entries: serial --unit=0 --speed=9600 terminal serial Unit refers to the serial port you wish to use (0 is COM1), and speed is optional. For more information see the GRUB documentation. (3) You need to make sure that your main box has the necessary utilities to communicate with your second box over a serial line. You can use a terminal emulator like minicom(1), or a simple serial communication program cu(1) which comes with the GNU uucp package. Or if you feel really lazy you can use this hack: stty raw cat > /dev/ttyS1 # in one terminal window cat /dev/ttyS1 # in a second terminal window (4) Now you have to make sure your computer has an at least partially setup Hurd partition. You can find instructions how to do that here [4,5]. Copy the oskit-mach kernel binary compressed with gzip to /boot/oskit-mach.gz and use the following command line[1] to boot it from GRUB: /boot/oskit-mach.gz -h CONS_COM=1 -d GDB_COM=2 BAUD=9600 -- root=hd0s2 -h (use serial console) CONS_COM=1 (COM1) CONS_COM=2 (COM2) CONS_COM=3 (COM3) CONS_COM=4 (COM4) -d (enable serial port debugging, optional) GDB_COM=2 (use a different port other then CONS_COM, default is to use the same as CONS_COM) BAUD=9600 (use this baud rate, optional, default is 9600) -- (delimits the arguments passed to the oskit from those to the kernel) root=hd0s2 (tell gnumach which is your root partition, in this case it's hd0s2) (5) Now I suggest that you familiarize yourself with the GDB documentation on remote debugging. If you pass the -d boot flag to oskit-mach, then it will automatically insert a breakpoint at main() and wait for further instructions from GDB over the serial line. Here's a simple example of how to attach GDB to a remote target over a serial line (I assume that you don't cross your serial cables and connect COM1 to COM1 and COM2 to COM2): $ script # record the debugging session $ gdb # assume you're in the oskit-mach build dir. (gdb) file kernel (gdb) target remote /dev/ttyS1 [...gdb attached, blah, blah, blah...] (gdb) break panic (gdb) continue [...] (gdb) quit $ ^D # finish recording the session This way you can catch any kernel panics (except for the really nasty ones and try to debug them). I've noticed that once oskit-mach is running under GDB, pressing C-c from GDB will not suspend it, this makes it hard to set additional breakpoints after the kernel is running. So optinally you can modify oskit-mach to add a dummy system call that will be used only for setting breakpoints, and make a small program that calls it, you can use it whenever you want to pause the kernel and examine something under GDB. An example of how to do this is attached in Appendix A. Now you're all set to do some serious kernel hacking. I hope more people will take advantage of this opportunity. Appendix A ---------- Apply this patch to oskit-mach to add a dummy system call: --- gdb-stub.diff --- Index: kern/syscall_sw.c =================================================================== RCS file: /cvs/gnumach/kern/syscall_sw.c,v retrieving revision 1.1.1.1.2.2 diff -u -r1.1.1.1.2.2 syscall_sw.c --- kern/syscall_sw.c 2001/04/05 06:52:47 1.1.1.1.2.2 +++ kern/syscall_sw.c 2001/07/30 21:45:14 @@ -98,6 +98,8 @@ extern kern_return_t syscall_fipc_recv(); #endif /* FIPC */ +/*XXX*/extern kern_return_t gdb_break_stub (); + mach_trap_t mach_trap_table[] = { MACH_TRAP(kern_invalid, 0), /* 0 */ /* Unix */ MACH_TRAP(kern_invalid, 0), /* 1 */ /* Unix */ @@ -283,7 +285,14 @@ MACH_TRAP(kern_invalid, 0), /* 126 */ MACH_TRAP(kern_invalid, 0), /* 127 */ MACH_TRAP(kern_invalid, 0), /* 128 */ - MACH_TRAP(kern_invalid, 0), /* 129 */ + MACH_TRAP(gdb_break_stub, 1), /* 129 */ }; +volatile int gdb_break_stub (void *addr) /*XXX*/ +{ + void *dummy; + dummy = addr; + return 0; +} + int mach_trap_count = (sizeof(mach_trap_table) / sizeof(mach_trap_table[0])); --- end --- When starting an oskit-mach debug session with GDB set a break poit at `gdb_break_stub'. Then use this program to invoke the system call when desired: --- gdb-break.c --- /* Compile with: gcc -o gdb-break gdb-break.c gdb-break-stub.S */ #include #include #include extern int gdb_break_stub (void *addr); int main () { kern_return_t err; err = gdb_break_stub (&main); printf ("result from syscall: %s\n", strerror(err)); return 0; } --- end --- --- gdb-break-stub.S --- #include kernel_trap(gdb_break_stub,-129,1) --- end --- Appendix B ---------- You have to apply this patch to oskit-mach to avoid a frivolous panic from the kernel. --- oskit-mach-interrupt.diff --- diff -u oskit/osenv_mem.c.orig oskit/osenv_mem.c --- oskit/osenv_mem.c.orig Sat Jul 21 21:57:50 2001 +++ oskit/osenv_mem.c Fri Aug 31 00:04:50 2001 @@ -194,9 +194,13 @@ Always go directly to physical memory. */ flags |= OSENV_VIRT_EQ_PHYS|OSENV_PHYS_WIRED|OSENV_PHYS_CONTIG; +/* XXX The version of oskit we are working with (20000202) does not + * seem to respect requirement stated below */ +#if 0 if (in_oskit_interrupt) /* The oskit documentation says an interrupt caller must set the flag. */ assert (flags & OSENV_NONBLOCKING); +#endif if ((oskit_addr_t) block < phys_mem_max) { --- end --- References ---------- [1] OSKit Reference Manual, section 1.6.3, http://www.cs.utah.edu/flux/oskit/doc.html [2] http://www.cs.utah.edu/flux/oskit/ [3] http://www.gnu.org/software/devel [4] http://www.walfield.org/papers/hurd-installation-guide/ [5] http://www.pick.ucam.org/~mcv21/hurd.html vim:ts=8:tw=72:sw=8: