]> rtime.felk.cvut.cz Git - l4.git/blob - kernel/fiasco/src/jdb/ux/jdb-ux.cpp
Inital import
[l4.git] / kernel / fiasco / src / jdb / ux / jdb-ux.cpp
1 INTERFACE [ux]:
2
3 #include "types.h"
4 #include "l4_types.h"
5
6 class Trap_state;
7 class Thread;
8 class Context;
9 class Jdb_entry_frame;
10 class Mem_space;
11
12 EXTENSION class Jdb
13 {
14 public:
15   static const char * const reg_names[];
16
17   typedef enum
18     {
19       s_unknown, s_ipc, s_syscall, s_pagefault, s_fputrap,
20       s_interrupt, s_timer_interrupt, s_slowtrap, s_user_invoke,
21     } Guessed_thread_state;
22
23   enum { MIN_SCREEN_HEIGHT = 20, MIN_SCREEN_WIDTH = 80 };
24
25   template < typename T > static T peek(T const *addr, Address_type user);
26
27   static int (*bp_test_log_only)();
28   static int (*bp_test_break)(char *errbuf, size_t bufsize);
29
30 private:
31   static unsigned short rows, cols;
32
33 };
34
35 IMPLEMENTATION [ux]:
36
37 #include <cstdio>
38 #include <sys/ioctl.h>
39 #include <termios.h>
40 #include <unistd.h>
41
42 #include "globals.h"
43 #include "initcalls.h"
44 #include "jdb_core.h"
45 #include "jdb_dbinfo.h"
46 #include "jdb_screen.h"
47 #include "kernel_console.h"
48 #include "kernel_thread.h"
49 #include "keycodes.h"
50 #include "kmem.h"
51 #include "libc_support.h"
52 #include "logdefs.h"
53 #include "mem_layout.h"
54 #include "push_console.h"
55 #include "simpleio.h"
56 #include "space.h"
57 #include "static_init.h"
58 #include "thread.h"
59 #include "thread_state.h"
60 #include "trap_state.h"
61 #include "usermode.h"
62 #include "vkey.h"
63
64 int (*Jdb::bp_test_log_only)();
65 int (*Jdb::bp_test_break)(char *errbuf, size_t bufsize);
66
67 unsigned short Jdb::rows, Jdb::cols;
68
69 const char * const Jdb::reg_names[] =
70 { "EAX", "EBX", "ECX", "EDX", "EBP", "ESI", "EDI", "EIP", "ESP", "EFL" };
71
72 static Proc::Status jdb_irq_state;
73
74 IMPLEMENT inline
75 void
76 Jdb::enter_trap_handler(unsigned /*cpu*/)
77 {
78   conf_screen();
79
80   // Set terminal raw mode
81   enter_getchar();
82
83   // Flush all output streams
84   fflush (NULL);
85 }
86
87 IMPLEMENT inline
88 void
89 Jdb::leave_trap_handler(unsigned)
90 {
91   // Restore terminal mode
92   leave_getchar();
93
94   // Flush all output streams
95   fflush (NULL);
96
97   if (!::running)
98     signal (SIGIO, SIG_IGN);            // Ignore hardware interrupts
99 }
100
101
102 IMPLEMENT inline
103 bool
104 Jdb::handle_user_request(unsigned /*cpu*/)
105 { return false; }
106
107 IMPLEMENT inline
108 bool
109 Jdb::test_checksums()
110 { return true; }
111
112
113 // disable interrupts before entering the kernel debugger
114 IMPLEMENT
115 void
116 Jdb::save_disable_irqs(unsigned cpu)
117 {
118   assert(cpu == 0);
119   jdb_irq_state = Proc::cli_save();
120 }
121
122 // restore interrupts after leaving the kernel debugger
123 IMPLEMENT
124 void
125 Jdb::restore_irqs(unsigned cpu)
126 {
127   assert(cpu == 0);
128   Proc::sti_restore(jdb_irq_state);
129 }
130
131 STATIC_INITIALIZE_P(Jdb,JDB_INIT_PRIO);
132
133 PUBLIC static FIASCO_INIT
134 void
135 Jdb::init()
136 {
137   // Install JDB handler
138   Trap_state::base_handler = (Trap_state::Handler)enter_jdb;
139
140   // be sure that Push_console comes very first
141   static Push_console c;
142   Kconsole::console()->register_console(&c, 0);
143
144   register_libc_atexit(leave_getchar);
145
146   Thread::set_int3_handler(handle_int3_threadctx);
147 }
148
149 PRIVATE
150 static void
151 Jdb::conf_screen()
152 {
153   struct winsize win;
154   ioctl (fileno (stdin), TIOCGWINSZ, &win);
155   rows = win.ws_row;
156   cols = win.ws_col;
157
158   if (rows < MIN_SCREEN_HEIGHT || cols < MIN_SCREEN_WIDTH)
159     printf ("%sTerminal probably too small, should be at least %dx%d!\033[0m\n",
160             esc_emph, MIN_SCREEN_WIDTH, MIN_SCREEN_HEIGHT);
161
162   Jdb_screen::set_height(rows);
163   Jdb_screen::set_width(cols);
164 }
165
166 /** handle int3 debug extension */
167 PUBLIC static inline NOEXPORT
168 int
169 Jdb::int3_extension()
170 {
171   Jdb_entry_frame *entry_frame = Jdb::entry_frame.cpu(0);
172   Address      addr = entry_frame->ip();
173   Address_type user = (entry_frame->cs() & 3) ? ADDR_USER : ADDR_KERNEL;
174   Unsigned8    todo = peek ((Unsigned8 *) addr, user);
175   Space *space = NULL; //get_task_id(0);
176
177   if (todo == 0x3c && peek ((Unsigned8 *) (addr+1), user) == 13)
178     {
179       enter_getchar();
180       entry_frame->_ax = Vkey::get();
181       Vkey::clear();
182       leave_getchar();
183       return 1;
184     }
185   else if (todo != 0xeb)
186     {
187       snprintf (error_buffer.cpu(0), sizeof (error_buffer.cpu(0)), "INT 3");
188       return 0;
189     }
190
191   // todo == 0xeb => enter_kdebug()
192   Mword i;
193   Mword len = peek ((Unsigned8 *) ++addr, user);
194
195   if (len > 2 &&
196       peek (((Unsigned8 *) addr + 1), user) == '*' &&
197       peek (((Unsigned8 *) addr + 2), user) == '#')
198     {
199       char c = peek (((Unsigned8 *) addr + 3), user);
200
201       if ((c == '#')
202           ? execute_command_ni(space, (char const *) entry_frame->_ax)
203           : execute_command_ni(space, (char const *)(addr + 3), len-2))
204         return 1; // => leave Jdb
205     }
206
207   len = len < sizeof(error_buffer.cpu(0))-1 ? len : sizeof(error_buffer.cpu(0))-1;
208   for (i = 0; i < len; i++)
209     error_buffer.cpu(0)[i] = peek ((Unsigned8 *) ++addr, user);
210   error_buffer.cpu(0)[i] = 0;
211   return 0;
212 }
213
214 static
215 bool
216 Jdb::handle_special_cmds(int)
217 { return 1; }
218
219 IMPLEMENT
220 bool
221 Jdb::handle_debug_traps(unsigned cpu)
222 {
223   switch (entry_frame.cpu(cpu)->_trapno)
224     {
225       case 1:
226         snprintf (error_buffer.cpu(cpu), sizeof (error_buffer.cpu(cpu)), "Interception");
227         break;
228       case 3:
229         if (int3_extension())
230           return false;
231 #ifdef FIXME
232         if (get_thread(cpu)->d_taskno())
233           {
234             if (bp_test_log_only && bp_test_log_only())
235               return false;
236             if (bp_test_break
237                 && bp_test_break(error_buffer.cpu(cpu), sizeof(error_buffer.cpu(cpu))))
238               break;
239           }
240 #endif
241     }
242
243   return true;
244 }
245
246 IMPLEMENT
247 void
248 Jdb::handle_nested_trap(Jdb_entry_frame *e)
249 {
250   printf("Trap in JDB: IP:%08lx\n", e->ip());
251 }
252
253 IMPLEMENT inline
254 bool
255 Jdb::handle_conditional_breakpoint(unsigned /*cpu*/)
256 { return false; }
257
258
259 PUBLIC
260 static int
261 Jdb::get_register(char *reg)
262 {
263   char reg_name[4];
264   int i;
265
266   putchar(reg_name[0] = 'E');
267
268   for (i=1; i<3; i++)
269     {
270       int c = getchar();
271       if (c == KEY_ESC)
272         return false;
273       putchar(reg_name[i] = c & 0xdf);
274     }
275
276   reg_name[3] = '\0';
277
278   for (i=0; i<9; i++)
279     if (*((unsigned*)reg_name) == *((unsigned*)reg_names[i]))
280       break;
281
282   if (i==9)
283     return false;
284
285   *reg = i+1;
286   return true;
287 }
288
289
290 PUBLIC
291 static Space *
292 Jdb::translate_task(Address /*addr*/, Space *task)
293 {
294   // we have no idea if addr belongs to kernel or user space
295   // since kernel and user occupy different address spaces
296   return task;
297 }
298
299 PUBLIC
300 static Address
301 Jdb::virt_to_kvirt(Address virt, Mem_space* space)
302 {
303   Mem_space::Phys_addr phys;
304   Mem_space::Size size;
305
306   if (!space)
307     {
308       // Kernel address.
309       // We can directly access it via virtual addresses if it's kernel code
310       // (which is always mapped, but doesn't appear in the kernel pagetable)
311       //  or if we find a mapping for it in the kernel's master pagetable.
312       return (virt >= (Address)&Mem_layout::load && 
313               virt <  (Kernel_thread::init_done() 
314                                 ? (Address)&Mem_layout::end
315                                 : (Address)&Mem_layout::initcall_end)
316               || Kernel_task::kernel_task()->mem_space()->v_lookup (
317                 Mem_space::Addr::create(virt), 0, 0, 0))
318         ? virt
319         : (Address) -1;
320     }
321   else
322     {
323       // User address.
324       // We can't directly access it because it's in a different host process
325       // but if the task's pagetable has a mapping for it, we can translate
326       // task-virtual -> physical -> kernel-virtual address and then access.
327       return (space->v_lookup (Mem_space::Addr::create(virt), &phys, &size, 0))
328         ? (Address) Kmem::phys_to_virt (phys.value() + (virt & (size.value()-1)))
329         : (Address) -1;
330     }
331 }
332
333 IMPLEMENT inline NEEDS ["space.h"]
334 template <typename T>
335 T
336 Jdb::peek (T const *addr, Address_type user)
337 {
338   return current_mem_space()->peek(addr, user);
339 }
340
341 PUBLIC static
342 int
343 Jdb::peek_task(Address virt, Space *space, void *value, int width)
344 {
345   // make sure we don't cross a page boundary
346   if (virt & (width-1))
347     return -1;
348
349   Address kvirt = virt_to_kvirt(virt, space?space->mem_space():0);
350   if (kvirt == (Address)-1)
351     return -1;
352
353   memcpy(value, (void*)kvirt, width);
354   return 0;
355 }
356
357 PUBLIC static
358 int
359 Jdb::poke_task(Address virt, Space *space, void const *value, int width)
360 {
361   // make sure we don't cross a page boundary
362   if (virt & (width-1))
363     return -1;
364
365   Address kvirt = virt_to_kvirt(virt, space?space->mem_space():0);
366
367   if (kvirt == (Address)-1)
368     return -1;
369
370   memcpy((void*)kvirt, value, width);
371   return 0;
372 }
373
374 PUBLIC
375 static int
376 Jdb::is_adapter_memory(Address /*addr*/, Space * /*task*/)
377 {
378   return 0;
379 }
380
381 #define WEAK __attribute__((weak))
382 extern "C" char in_slowtrap, in_page_fault, in_handle_fputrap;
383 extern "C" char in_interrupt, in_timer_interrupt, in_timer_interrupt_slow;
384 extern "C" char i30_ret_switch WEAK, in_slow_ipc1 WEAK;
385 extern "C" char in_slow_ipc2 WEAK, in_slow_ipc4;
386 extern "C" char in_sc_ipc1 WEAK, in_sc_ipc2 WEAK, in_syscall WEAK;
387 #undef WEAK
388
389 /** Try to guess the thread state of t by walking down the kernel stack and
390  * locking at the first return address we find. */
391 PUBLIC
392 static Jdb::Guessed_thread_state
393 Jdb::guess_thread_state(Thread *t)
394 {
395   Guessed_thread_state state = s_unknown;
396   Mword *ktop = (Mword*)((Mword)context_of(t->get_kernel_sp()) +
397                          Config::thread_block_size);
398
399   for (int i=-1; i>-26; i--)
400     {
401       if (ktop[i] != 0)
402         {
403           if (ktop[i] == (Mword)&in_page_fault)
404             state = s_pagefault;
405           if ((ktop[i] == (Mword)&i30_ret_switch) ||// shortcut.S, int 0x30
406               (ktop[i] == (Mword)&in_slow_ipc1) ||  // shortcut.S, int 0x30
407               (ktop[i] == (Mword)&in_slow_ipc4) ||  // entry.S, int 0x30
408 #if !defined(CONFIG_ASSEMBLER_IPC_SHORTCUT)
409               (ktop[i] == (Mword)&in_sc_ipc1)   ||  // entry.S, int 0x30
410               (ktop[i] == (Mword)&in_sc_ipc2)   ||  // entry.S, sysenter
411 #endif
412              0)
413             state = s_ipc;
414           else if (ktop[i] == (Mword)&in_syscall)
415             state = s_syscall;
416           else if (ktop[i] == (Mword)&Thread::user_invoke)
417             state = s_user_invoke;
418           else if (ktop[i] == (Mword)&in_handle_fputrap)
419             state = s_fputrap;
420           else if (ktop[i] == (Mword)&in_interrupt)
421             state = s_interrupt;
422           else if ((ktop[i] == (Mword)&in_timer_interrupt) ||
423                    (ktop[i] == (Mword)&in_timer_interrupt_slow))
424             state = s_timer_interrupt;
425           else if (ktop[i] == (Mword)&in_slowtrap)
426             state = s_slowtrap;
427           if (state != s_unknown)
428             break;
429         }
430     }
431
432   if (state == s_unknown && (t->state() & Thread_ipc_mask))
433     state = s_ipc;
434
435   return state;
436 }
437
438 // Don't make these members of Jdb else we have to include <termios.h>
439 // into the Jdb interface ...
440 static struct termios raw, new_raw;
441 static int    getchar_entered;
442
443 /** prepare Linux console for raw input */
444 PUBLIC static
445 void
446 Jdb::enter_getchar()
447 {
448   if (!getchar_entered++)
449     {
450       tcgetattr (fileno (stdin), &raw);
451       memcpy(&new_raw, &raw, sizeof(new_raw));
452       new_raw.c_lflag    &= ~(ICANON|ECHO);
453       new_raw.c_cc[VMIN]  = 0;
454       new_raw.c_cc[VTIME] = 1;
455       tcsetattr (fileno (stdin), TCSAFLUSH, &new_raw);
456     }
457 }
458
459 /** restore Linux console. */
460 PUBLIC static
461 void
462 Jdb::leave_getchar()
463 {
464   if (!--getchar_entered)
465     tcsetattr (fileno (stdin), TCSAFLUSH, &raw);
466 }
467
468 //----------------------------------------------------------------------------
469 IMPLEMENTATION [ux && mp]:
470
471 static
472 void
473 Jdb::send_nmi(unsigned /*cpu*/)
474 {
475 }