SimpleOS

LXR

Navigation



Site hébergé par : enix

The LXR Cross Referencer for SOS

source navigation ]
diff markup ]
identifier search ]
general search ]
 
 
Article:1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ] [ 6 ] [ 6.5 ] [ 7 ] [ 7.5 ] [ 8 ] [ 9 ] [ 9.5 ]

001 /* Copyright (C) 2005  David Decotigny
002    Copyright (C) 2000-2004, The KOS team
003 
004    This program is free software; you can redistribute it and/or
005    modify it under the terms of the GNU General Public License
006    as published by the Free Software Foundation; either version 2
007    of the License, or (at your option) any later version.
008    
009    This program is distributed in the hope that it will be useful,
010    but WITHOUT ANY WARRANTY; without even the implied warranty of
011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
012    GNU General Public License for more details.
013    
014    You should have received a copy of the GNU General Public License
015    along with this program; if not, write to the Free Software
016    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
017    USA. 
018 */
019 
020 
021 #include <sos/assert.h>
022 #include <sos/klibc.h>
023 #include <drivers/bochs.h>
024 #include <drivers/x86_videomem.h>
025 #include <hwcore/segment.h>
026 #include <hwcore/gdt.h>
027 #include <sos/uaccess.h>
028 
029 #include "cpu_context.h"
030 
031 
032 /**
033  * Here is the definition of a CPU context for IA32 processors. This
034  * is a SOS convention, not a specification given by the IA32
035  * spec. However there is a strong constraint related to the x86
036  * interrupt handling specification: the top of the stack MUST be
037  * compatible with the 'iret' instruction, ie there must be the
038  * err_code (might be 0), eip, cs and eflags of the destination
039  * context in that order (see Intel x86 specs vol 3, figure 5-4).
040  *
041  * @note IMPORTANT: This definition MUST be consistent with the way
042  * the registers are stored on the stack in
043  * irq_wrappers.S/exception_wrappers.S !!! Hence the constraint above.
044  */
045 struct sos_cpu_state {
046   /* (Lower addresses) */
047 
048   /* These are SOS convention */
049   sos_ui16_t  gs;
050   sos_ui16_t  fs;
051   sos_ui16_t  es;
052   sos_ui16_t  ds;
053   sos_ui16_t  cpl0_ss; /* This is ALWAYS the Stack Segment of the
054                           Kernel context (CPL0) of the interrupted
055                           thread, even for a user thread */
056   sos_ui16_t  alignment_padding; /* unused */
057   sos_ui32_t  eax;
058   sos_ui32_t  ebx;
059   sos_ui32_t  ecx;
060   sos_ui32_t  edx;
061   sos_ui32_t  esi;
062   sos_ui32_t  edi;
063   sos_ui32_t  ebp;
064 
065   /* MUST NEVER CHANGE (dependent on the IA32 iret instruction) */
066   sos_ui32_t  error_code;
067   sos_vaddr_t eip;
068   sos_ui32_t  cs; /* 32bits according to the specs ! However, the CS
069                      register is really 16bits long */
070   sos_ui32_t  eflags;
071 
072   /* (Higher addresses) */
073 } __attribute__((packed));
074 
075 
076 /**
077  * The CS value pushed on the stack by the CPU upon interrupt, and
078  * needed by the iret instruction, is 32bits long while the real CPU
079  * CS register is 16bits only: this macro simply retrieves the CPU
080  * "CS" register value from the CS value pushed on the stack by the
081  * CPU upon interrupt.
082  *
083  * The remaining 16bits pushed by the CPU should be considered
084  * "reserved" and architecture dependent. IMHO, the specs don't say
085  * anything about them. Considering that some architectures generate
086  * non-zero values for these 16bits (at least Cyrix), we'd better
087  * ignore them.
088  */
089 #define GET_CPU_CS_REGISTER_VALUE(pushed_ui32_cs_value) \
090   ( (pushed_ui32_cs_value) & 0xffff )
091 
092 
093 /**
094  * Structure of an interrupted Kernel thread's context
095  */
096 struct sos_cpu_kstate
097 {
098   struct sos_cpu_state regs;
099 } __attribute__((packed));
100 
101 
102 /**
103  * Structure of an interrupted User thread's context. This is almost
104  * the same as a kernel context, except that 2 additional values are
105  * pushed on the stack before the eflags/cs/eip of the interrupted
106  * context: the stack configuration of the interrupted user context.
107  *
108  * @see Section 6.4.1 of Intel x86 vol 1
109  */
110 struct sos_cpu_ustate
111 {
112   struct sos_cpu_state regs;
113   struct
114   {
115     sos_ui32_t cpl3_esp;
116     sos_ui16_t cpl3_ss;
117   };
118 } __attribute__((packed));
119 
120 
121 /*
122  * Structure of a Task State Segment on the x86 Architecture.
123  *
124  * @see Intel x86 spec vol 3, figure 6-2
125  *
126  * @note Such a data structure should not cross any page boundary (see
127  * end of section 6.2.1 of Intel spec vol 3). This is the reason why
128  * we tell gcc to align it on a 128B boundary (its size is 104B, which
129  * is <= 128).
130  */
131 struct x86_tss {
132 
133   /**
134    * Intel provides a way for a task to switch to another in an
135    * automatic way (call gates). In this case, the back_link field
136    * stores the source TSS of the context switch. This allows to
137    * easily implement coroutines, task backtracking, ... In SOS we
138    * don't use TSS for the context switch purpouse, so we always
139    * ignore this field.
140    * (+0)
141    */
142   sos_ui16_t back_link;
143 
144   sos_ui16_t reserved1;
145 
146   /* CPL0 saved context. (+4) */
147   sos_vaddr_t esp0;
148   sos_ui16_t ss0;
149 
150   sos_ui16_t reserved2;
151 
152   /* CPL1 saved context. (+12) */
153   sos_vaddr_t esp1;
154   sos_ui16_t ss1;
155 
156   sos_ui16_t reserved3;
157 
158   /* CPL2 saved context. (+20) */
159   sos_vaddr_t esp2;
160   sos_ui16_t ss2;
161 
162   sos_ui16_t reserved4;
163 
164   /* Interrupted context's saved registers. (+28) */
165   sos_vaddr_t cr3;
166   sos_vaddr_t eip;
167   sos_ui32_t eflags;
168   sos_ui32_t eax;
169   sos_ui32_t ecx;
170   sos_ui32_t edx;
171   sos_ui32_t ebx;
172   sos_ui32_t esp;
173   sos_ui32_t ebp;
174   sos_ui32_t esi;
175   sos_ui32_t edi;
176 
177   /* +72 */
178   sos_ui16_t es;
179   sos_ui16_t reserved5;
180 
181   /* +76 */
182   sos_ui16_t cs;
183   sos_ui16_t reserved6;
184 
185   /* +80 */
186   sos_ui16_t ss;
187   sos_ui16_t reserved7;
188 
189   /* +84 */
190   sos_ui16_t ds;
191   sos_ui16_t reserved8;
192 
193   /* +88 */
194   sos_ui16_t fs;
195   sos_ui16_t reserved9;
196 
197   /* +92 */
198   sos_ui16_t gs;
199   sos_ui16_t reserved10;
200 
201   /* +96 */
202   sos_ui16_t ldtr;
203   sos_ui16_t reserved11;
204 
205   /* +100 */
206   sos_ui16_t debug_trap_flag :1;
207   sos_ui16_t reserved12      :15;
208   sos_ui16_t iomap_base_addr;
209 
210   /* 104 */
211 } __attribute__((packed, aligned(128)));
212 
213 
214 static struct x86_tss kernel_tss;
215 
216 
217 sos_ret_t sos_cpu_context_subsystem_setup()
218 {
219   /* Reset the kernel TSS */
220   memset(&kernel_tss, 0x0, sizeof(kernel_tss));
221 
222   /**
223    * Now setup the kernel TSS.
224    *
225    * Considering the privilege change method we choose (cpl3 -> cpl0
226    * through a software interrupt), we don't need to initialize a
227    * full-fledged TSS. See section 6.4.1 of Intel x86 vol 1. Actually,
228    * only a correct value for the kernel esp and ss are required (aka
229    * "ss0" and "esp0" fields). Since the esp0 will have to be updated
230    * at privilege change time, we don't have to set it up now.
231    */
232   kernel_tss.ss0 = SOS_BUILD_SEGMENT_REG_VALUE(0, FALSE, SOS_SEG_KDATA);
233 
234   /* Register this TSS into the gdt */
235   sos_gdt_register_kernel_tss((sos_vaddr_t) &kernel_tss);
236 
237   return SOS_OK;
238 }
239 
240 
241 /**
242  * THE main operation of a kernel thread. This routine calls the
243  * kernel thread function start_func and calls exit_func when
244  * start_func returns.
245  */
246 static void core_routine (sos_cpu_kstate_function_arg1_t *start_func,
247                           sos_ui32_t start_arg,
248                           sos_cpu_kstate_function_arg1_t *exit_func,
249                           sos_ui32_t exit_arg)
250      __attribute__((noreturn));
251 
252 static void core_routine (sos_cpu_kstate_function_arg1_t *start_func,
253                           sos_ui32_t start_arg,
254                           sos_cpu_kstate_function_arg1_t *exit_func,
255                           sos_ui32_t exit_arg)
256 {
257   start_func(start_arg);
258   exit_func(exit_arg);
259 
260   SOS_ASSERT_FATAL(! "The exit function of the thread should NOT return !");
261   for(;;);
262 }
263 
264 
265 sos_ret_t sos_cpu_kstate_init(struct sos_cpu_state **ctxt,
266                               sos_cpu_kstate_function_arg1_t *start_func,
267                               sos_ui32_t  start_arg,
268                               sos_vaddr_t stack_bottom,
269                               sos_size_t  stack_size,
270                               sos_cpu_kstate_function_arg1_t *exit_func,
271                               sos_ui32_t  exit_arg)
272 {
273   /* We are initializing a Kernel thread's context */
274   struct sos_cpu_kstate *kctxt;
275 
276   /* This is a critical internal function, so that it is assumed that
277      the caller knows what he does: we legitimally assume that values
278      for ctxt, start_func, stack_* and exit_func are allways VALID ! */
279 
280   /* Setup the stack.
281    *
282    * On x86, the stack goes downward. Each frame is configured this
283    * way (higher addresses first):
284    *
285    *  - (optional unused space. As of gcc 3.3, this space is 24 bytes)
286    *  - arg n
287    *  - arg n-1
288    *  - ...
289    *  - arg 1
290    *  - return instruction address: The address the function returns to
291    *    once finished
292    *  - local variables
293    *
294    * The remaining of the code should be read from the end upward to
295    * understand how the processor will handle it.
296    */
297 
298   sos_vaddr_t tmp_vaddr = stack_bottom + stack_size;
299   sos_ui32_t *stack = (sos_ui32_t*)tmp_vaddr;
300 
301   /* If needed, poison the stack */
302 #ifdef SOS_CPU_STATE_DETECT_UNINIT_KERNEL_VARS
303   memset((void*)stack_bottom, SOS_CPU_STATE_STACK_POISON, stack_size);
304 #elif defined(SOS_CPU_STATE_DETECT_KERNEL_STACK_OVERFLOW)
305   sos_cpu_state_prepare_detect_kernel_stack_overflow(stack_bottom, stack_size);
306 #endif
307 
308   /* Simulate a call to the core_routine() function: prepare its
309      arguments */
310   *(--stack) = exit_arg;
311   *(--stack) = (sos_ui32_t)exit_func;
312   *(--stack) = start_arg;
313   *(--stack) = (sos_ui32_t)start_func;
314   *(--stack) = 0; /* Return address of core_routine => force page fault */
315 
316   /*
317    * Setup the initial context structure, so that the CPU will execute
318    * the function core_routine() once this new context has been
319    * restored on CPU
320    */
321 
322   /* Compute the base address of the structure, which must be located
323      below the previous elements */
324   tmp_vaddr  = ((sos_vaddr_t)stack) - sizeof(struct sos_cpu_kstate);
325   kctxt = (struct sos_cpu_kstate*)tmp_vaddr;
326 
327   /* Initialize the CPU context structure */
328   memset(kctxt, 0x0, sizeof(struct sos_cpu_kstate));
329 
330   /* Tell the CPU context structure that the first instruction to
331      execute will be that of the core_routine() function */
332   kctxt->regs.eip = (sos_ui32_t)core_routine;
333 
334   /* Setup the segment registers */
335   kctxt->regs.cs
336     = SOS_BUILD_SEGMENT_REG_VALUE(0, FALSE, SOS_SEG_KCODE); /* Code */
337   kctxt->regs.ds
338     = SOS_BUILD_SEGMENT_REG_VALUE(0, FALSE, SOS_SEG_KDATA); /* Data */
339   kctxt->regs.es
340     = SOS_BUILD_SEGMENT_REG_VALUE(0, FALSE, SOS_SEG_KDATA); /* Data */
341   kctxt->regs.cpl0_ss
342     = SOS_BUILD_SEGMENT_REG_VALUE(0, FALSE, SOS_SEG_KDATA); /* Stack */
343   /* fs and gs unused for the moment. */
344 
345   /* The newly created context is initially interruptible */
346   kctxt->regs.eflags = (1 << 9); /* set IF bit */
347 
348   /* Finally, update the generic kernel/user thread context */
349   *ctxt = (struct sos_cpu_state*) kctxt;
350 
351   return SOS_OK;
352 }
353 
354 
355 /**
356  * Helper function to create a new user thread context.  When
357  * model_uctxt is NON NULL, the new user context is the copy of
358  * model_uctxt, otherwise the SP/PC registers are initialized to the
359  * user_initial_SP/PC arguments
360  */
361 static sos_ret_t cpu_ustate_init(struct sos_cpu_state **ctxt,
362                                  const struct sos_cpu_state *model_uctxt,
363                                  sos_uaddr_t  user_start_PC,
364                                  sos_ui32_t   user_start_arg1,
365                                  sos_ui32_t   user_start_arg2,
366                                  sos_uaddr_t  user_initial_SP,
367                                  sos_vaddr_t  kernel_stack_bottom,
368                                  sos_size_t   kernel_stack_size)
369 {
370   /* We are initializing a User thread's context */
371   struct sos_cpu_ustate *uctxt;
372 
373   /* This is a critical internal function, so that it is assumed that
374      the caller knows what he does: we legitimally assume that values
375      for ctxt, etc. are allways VALID ! */
376 
377   /* Compute the address of the CPU state to restore on CPU when
378      switching to this new user thread */
379   sos_vaddr_t uctxt_vaddr = kernel_stack_bottom
380                              + kernel_stack_size
381                              - sizeof(struct sos_cpu_ustate);
382   uctxt = (struct sos_cpu_ustate*)uctxt_vaddr;
383 
384   if (model_uctxt && !sos_cpu_context_is_in_user_mode(model_uctxt))
385     return -SOS_EINVAL;
386 
387   /* If needed, poison the kernel stack */
388 #ifdef SOS_CPU_STATE_DETECT_UNINIT_KERNEL_VARS
389   memset((void*)kernel_stack_bottom,
390          SOS_CPU_STATE_STACK_POISON,
391          kernel_stack_size);
392 #elif defined(SOS_CPU_STATE_DETECT_KERNEL_STACK_OVERFLOW)
393   sos_cpu_state_prepare_detect_kernel_stack_overflow(kernel_stack_bottom,
394                                                      kernel_stack_size);
395 #endif
396 
397   /*
398    * Setup the initial context structure, so that the CPU will restore
399    * the initial registers' value for the user thread. The
400    * user thread argument is passed in the EAX register.
401    */
402 
403   /* Initialize the CPU context structure */
404   if (! model_uctxt)
405     {
406       memset(uctxt, 0x0, sizeof(struct sos_cpu_ustate));
407 
408       /* Tell the CPU context structure that the first instruction to
409          execute will be located at user_start_PC (in user space) */
410       uctxt->regs.eip = (sos_ui32_t)user_start_PC;
411       
412       /* Tell the CPU where will be the user stack */
413       uctxt->cpl3_esp = user_initial_SP;
414     }
415   else
416     memcpy(uctxt, model_uctxt, sizeof(struct sos_cpu_ustate));
417 
418   /* The parameter to the start function is not passed by the stack to
419      avoid a possible page fault */
420   uctxt->regs.eax = user_start_arg1;
421 
422   /* Optional additional argument for non-duplicated threads */
423   if (! model_uctxt)
424     uctxt->regs.ebx = user_start_arg2;
425 
426   /* Setup the segment registers */
427   uctxt->regs.cs
428     = SOS_BUILD_SEGMENT_REG_VALUE(3, FALSE, SOS_SEG_UCODE); /* Code */
429   uctxt->regs.ds
430     = SOS_BUILD_SEGMENT_REG_VALUE(3, FALSE, SOS_SEG_UDATA); /* Data */
431   uctxt->regs.es
432     = SOS_BUILD_SEGMENT_REG_VALUE(3, FALSE, SOS_SEG_UDATA); /* Data */
433   uctxt->cpl3_ss
434     = SOS_BUILD_SEGMENT_REG_VALUE(3, FALSE, SOS_SEG_UDATA); /* User Stack */
435 
436   /* We need also to update the segment for the kernel stack
437      segment. It will be used when this context will be restored on
438      CPU: initially it will be executing in kernel mode and will
439      switch immediatly to user mode */
440   uctxt->regs.cpl0_ss
441     = SOS_BUILD_SEGMENT_REG_VALUE(0, FALSE, SOS_SEG_KDATA); /* Kernel Stack */
442 
443   /* fs and gs unused for the moment. */
444 
445   /* The newly created context is initially interruptible */
446   uctxt->regs.eflags = (1 << 9); /* set IF bit */
447 
448   /* Finally, update the generic kernel/user thread context */
449   *ctxt = (struct sos_cpu_state*) uctxt;
450 
451   return SOS_OK;
452 }
453 
454 
455 sos_ret_t sos_cpu_ustate_init(struct sos_cpu_state **ctxt,
456                               sos_uaddr_t  user_start_PC,
457                               sos_ui32_t   user_start_arg1,
458                               sos_ui32_t   user_start_arg2,
459                               sos_uaddr_t  user_initial_SP,
460                               sos_vaddr_t  kernel_stack_bottom,
461                               sos_size_t   kernel_stack_size)
462 {
463   return cpu_ustate_init(ctxt, NULL,
464                          user_start_PC,
465                          user_start_arg1, user_start_arg2,
466                          user_initial_SP,
467                          kernel_stack_bottom, kernel_stack_size);
468 }
469 
470 
471 sos_ret_t sos_cpu_ustate_duplicate(struct sos_cpu_state **ctxt,
472                                    const struct sos_cpu_state *model_uctxt,
473                                    sos_ui32_t   user_retval,
474                                    sos_vaddr_t  kernel_stack_bottom,
475                                    sos_size_t   kernel_stack_size)
476 {
477   return cpu_ustate_init(ctxt, model_uctxt,
478                          /* ignored */0,
479                          user_retval, /* ignored */0,
480                          /* ignored */0,
481                          kernel_stack_bottom, kernel_stack_size);
482 }
483 
484 
485 sos_ret_t
486 sos_cpu_context_is_in_user_mode(const struct sos_cpu_state *ctxt)
487 {
488   /* An interrupted user thread has its CS register set to that of the
489      User code segment */
490   switch (GET_CPU_CS_REGISTER_VALUE(ctxt->cs))
491     {
492     case SOS_BUILD_SEGMENT_REG_VALUE(3, FALSE, SOS_SEG_UCODE):
493       return TRUE;
494       break;
495 
496     case SOS_BUILD_SEGMENT_REG_VALUE(0, FALSE, SOS_SEG_KCODE):
497       return FALSE;
498       break;
499 
500     default:
501       SOS_FATAL_ERROR("Invalid saved context Code segment register: 0x%x (k=%x, u=%x) !",
502                       (unsigned) GET_CPU_CS_REGISTER_VALUE(ctxt->cs),
503                       SOS_BUILD_SEGMENT_REG_VALUE(0, FALSE, SOS_SEG_KCODE),
504                       SOS_BUILD_SEGMENT_REG_VALUE(3, FALSE, SOS_SEG_UCODE));
505       break;
506     }
507 
508   /* Should never get here */
509   return -SOS_EFATAL;
510 }
511 
512 
513 #if defined(SOS_CPU_STATE_DETECT_KERNEL_STACK_OVERFLOW)
514 void
515 sos_cpu_state_prepare_detect_kernel_stack_overflow(const struct sos_cpu_state *ctxt,
516                                                    sos_vaddr_t stack_bottom,
517                                                    sos_size_t stack_size)
518 {
519   sos_size_t poison_size = SOS_CPU_STATE_DETECT_KERNEL_STACK_OVERFLOW;
520   if (poison_size > stack_size)
521     poison_size = stack_size;
522 
523   memset((void*)stack_bottom, SOS_CPU_STATE_STACK_POISON, poison_size);
524 }
525 
526 
527 void
528 sos_cpu_state_detect_kernel_stack_overflow(const struct sos_cpu_state *ctxt,
529                                            sos_vaddr_t stack_bottom,
530                                            sos_size_t stack_size)
531 {
532   unsigned char *c;
533   int i;
534 
535   /* On SOS, "ctxt" corresponds to the address of the esp register of
536      the saved context in Kernel mode (always, even for the interrupted
537      context of a user thread). Here we make sure that this stack
538      pointer is within the allowed stack area */
539   SOS_ASSERT_FATAL(((sos_vaddr_t)ctxt) >= stack_bottom);
540   SOS_ASSERT_FATAL(((sos_vaddr_t)ctxt) + sizeof(struct sos_cpu_kstate)
541                    <= stack_bottom + stack_size);
542 
543   /* Check that the bottom of the stack has not been altered */
544   for (c = (unsigned char*) stack_bottom, i = 0 ;
545        (i < SOS_CPU_STATE_DETECT_KERNEL_STACK_OVERFLOW) && (i < stack_size) ;
546        c++, i++)
547     {
548       SOS_ASSERT_FATAL(SOS_CPU_STATE_STACK_POISON == *c);
549     }
550 }
551 #endif
552 
553 
554 /* =======================================================================
555  * Public Accessor functions
556  */
557 
558 
559 sos_vaddr_t sos_cpu_context_get_PC(const struct sos_cpu_state *ctxt)
560 {
561   SOS_ASSERT_FATAL(NULL != ctxt);
562 
563   /* This is the PC of the interrupted context (ie kernel or user
564      context). */
565   return ctxt->eip;
566 }
567 
568 
569 sos_vaddr_t sos_cpu_context_get_SP(const struct sos_cpu_state *ctxt)
570 {
571   SOS_ASSERT_FATAL(NULL != ctxt);
572 
573   /* 'ctxt' corresponds to the SP of the interrupted context, in Kernel
574      mode. We have to test whether the original interrupted context
575      was that of a kernel or user thread */
576   if (TRUE == sos_cpu_context_is_in_user_mode(ctxt))
577     {
578       struct sos_cpu_ustate * uctxt = (struct sos_cpu_ustate*)ctxt;
579       return uctxt->cpl3_esp;
580     }
581 
582   /* On SOS, "ctxt" corresponds to the address of the esp register of
583      the saved context in Kernel mode (always, even for the interrupted
584      context of a user thread). */
585   return (sos_vaddr_t)ctxt;
586 }
587 
588 
589 sos_ret_t
590 sos_cpu_context_set_EX_return_address(struct sos_cpu_state *ctxt,
591                                       sos_vaddr_t ret_vaddr)
592 {
593   ctxt->eip = ret_vaddr;
594   return SOS_OK;
595 }
596 
597 
598 void sos_cpu_context_dump(const struct sos_cpu_state *ctxt)
599 {
600   char buf[128];
601 
602   snprintf(buf, sizeof(buf),
603            "CPU: eip=%x esp0=%x eflags=%x cs=%x ds=%x ss0=%x err=%x",
604            (unsigned)ctxt->eip, (unsigned)ctxt, (unsigned)ctxt->eflags,
605            (unsigned)GET_CPU_CS_REGISTER_VALUE(ctxt->cs), (unsigned)ctxt->ds,
606            (unsigned)ctxt->cpl0_ss,
607            (unsigned)ctxt->error_code);
608   if (TRUE == sos_cpu_context_is_in_user_mode(ctxt))
609     {
610       struct sos_cpu_ustate * uctxt = (struct sos_cpu_ustate*)ctxt;
611       snprintf(buf, sizeof(buf),
612                "%s esp3=%x ss3=%x",
613                buf, (unsigned)uctxt->cpl3_esp, (unsigned)uctxt->cpl3_ss);
614     }
615   else
616     snprintf(buf, sizeof(buf), "%s [KERNEL MODE]", buf);
617 
618   sos_bochs_putstring(buf); sos_bochs_putstring("\n");
619   sos_x86_videomem_putstring(23, 0,
620                              SOS_X86_VIDEO_FG_BLACK | SOS_X86_VIDEO_BG_LTGRAY,
621                              buf);
622 }
623 
624 
625 /* =======================================================================
626  * Public Accessor functions TO BE USED ONLY BY Exception handlers
627  */
628 
629 
630 sos_ui32_t sos_cpu_context_get_EX_info(const struct sos_cpu_state *ctxt)
631 {
632   SOS_ASSERT_FATAL(NULL != ctxt);
633   return ctxt->error_code;
634 }
635 
636 
637 sos_vaddr_t
638 sos_cpu_context_get_EX_faulting_vaddr(const struct sos_cpu_state *ctxt)
639 {
640   sos_ui32_t cr2;
641 
642   /*
643    * See Intel Vol 3 (section 5.14): the address of the faulting
644    * virtual address of a page fault is stored in the cr2
645    * register.
646    *
647    * Actually, we do not store the cr2 register in a saved
648    * kernel thread's context. So we retrieve the cr2's value directly
649    * from the processor. The value we retrieve in an exception handler
650    * is actually the correct one because an exception is synchronous
651    * with the code causing the fault, and cannot be interrupted since
652    * the IDT entries in SOS are "interrupt gates" (ie IRQ are
653    * disabled).
654    */
655   asm volatile ("movl %%cr2, %0"
656                 :"=r"(cr2)
657                 : );
658 
659   return cr2;
660 }
661 
662 
663 /* =======================================================================
664  * Public Accessor functions TO BE USED ONLY BY the SYSCALL handler
665  */
666 
667 
668 /*
669  * By convention, the USER SOS programs always pass 4 arguments to the
670  * kernel syscall handler: in eax/../edx. For less arguments, the
671  * unused registers are filled with 0s. For more arguments, the 4th
672  * syscall parameter gives the address of the array containing the
673  * remaining arguments. In any case, eax corresponds to the syscall
674  * IDentifier.
675  */
676 
677 
678 inline
679 sos_ret_t sos_syscall_get3args(const struct sos_cpu_state *user_ctxt,
680                                /* out */unsigned int *arg1,
681                                /* out */unsigned int *arg2,
682                                /* out */unsigned int *arg3)
683 {
684   *arg1 = user_ctxt->ebx;
685   *arg2 = user_ctxt->ecx;
686   *arg3 = user_ctxt->edx; 
687   return SOS_OK;
688 }
689 
690 
691 sos_ret_t sos_syscall_get1arg(const struct sos_cpu_state *user_ctxt,
692                               /* out */unsigned int *arg1)
693 {
694   unsigned int unused;
695   return sos_syscall_get3args(user_ctxt, arg1, & unused, & unused);
696 }
697 
698 
699 sos_ret_t sos_syscall_get2args(const struct sos_cpu_state *user_ctxt,
700                                /* out */unsigned int *arg1,
701                                /* out */unsigned int *arg2)
702 {
703   unsigned int unused;
704   return sos_syscall_get3args(user_ctxt, arg1, arg2, & unused);
705 }
706 
707 
708 /*
709  * sos_syscall_get3args() is defined in cpu_context.c because it needs
710  * to know the structure of a struct spu_state
711  */
712 
713 sos_ret_t sos_syscall_get4args(const struct sos_cpu_state *user_ctxt,
714                                /* out */unsigned int *arg1,
715                                /* out */unsigned int *arg2,
716                                /* out */unsigned int *arg3,
717                                /* out */unsigned int *arg4)
718 {
719   sos_uaddr_t  uaddr_other_args;
720   unsigned int other_args[2];
721   sos_ret_t    retval;
722 
723   /* Retrieve the 3 arguments. The last one is an array containing the
724      remaining arguments */
725   retval = sos_syscall_get3args(user_ctxt, arg1, arg2,
726                                 (unsigned int *)& uaddr_other_args);
727   if (SOS_OK != retval)
728     return retval;
729   
730   /* Copy the array containing the remaining arguments from user
731      space */
732   retval = sos_memcpy_from_user((sos_vaddr_t)other_args,
733                                 (sos_uaddr_t)uaddr_other_args,
734                                 sizeof(other_args));
735   if (sizeof(other_args) != retval)
736     return -SOS_EFAULT;
737 
738   *arg3 = other_args[0];
739   *arg4 = other_args[1];
740   return SOS_OK;
741 }
742 
743 
744 sos_ret_t sos_syscall_get5args(const struct sos_cpu_state *user_ctxt,
745                                /* out */unsigned int *arg1,
746                                /* out */unsigned int *arg2,
747                                /* out */unsigned int *arg3,
748                                /* out */unsigned int *arg4,
749                                /* out */unsigned int *arg5)
750 {
751   sos_uaddr_t  uaddr_other_args;
752   unsigned int other_args[3];
753   sos_ret_t    retval;
754 
755   /* Retrieve the 3 arguments. The last one is an array containing the
756      remaining arguments */
757   retval = sos_syscall_get3args(user_ctxt, arg1, arg2,
758                                 (unsigned int *)& uaddr_other_args);
759   if (SOS_OK != retval)
760     return retval;
761   
762   /* Copy the array containing the remaining arguments from user
763      space */
764   retval = sos_memcpy_from_user((sos_vaddr_t)other_args,
765                                 (sos_uaddr_t)uaddr_other_args,
766                                 sizeof(other_args));
767   if (sizeof(other_args) != retval)
768     return -SOS_EFAULT;
769 
770   *arg3 = other_args[0];
771   *arg4 = other_args[1];
772   *arg5 = other_args[2];
773   return SOS_OK;
774 }
775 
776 
777 sos_ret_t sos_syscall_get6args(const struct sos_cpu_state *user_ctxt,
778                                /* out */unsigned int *arg1,
779                                /* out */unsigned int *arg2,
780                                /* out */unsigned int *arg3,
781                                /* out */unsigned int *arg4,
782                                /* out */unsigned int *arg5,
783                                /* out */unsigned int *arg6)
784 {
785   sos_uaddr_t  uaddr_other_args;
786   unsigned int other_args[4];
787   sos_ret_t    retval;
788 
789   /* Retrieve the 3 arguments. The last one is an array containing the
790      remaining arguments */
791   retval = sos_syscall_get3args(user_ctxt, arg1, arg2,
792                                 (unsigned int *)& uaddr_other_args);
793   if (SOS_OK != retval)
794     return retval;
795   
796   /* Copy the array containing the remaining arguments from user
797      space */
798   retval = sos_memcpy_from_user((sos_vaddr_t)other_args,
799                                 (sos_uaddr_t)uaddr_other_args,
800                                 sizeof(other_args));
801   if (sizeof(other_args) != retval)
802     return -SOS_EFAULT;
803 
804   *arg3 = other_args[0];
805   *arg4 = other_args[1];
806   *arg5 = other_args[2];
807   *arg6 = other_args[3];
808   return SOS_OK;
809 }
810 
811 
812 sos_ret_t sos_syscall_get7args(const struct sos_cpu_state *user_ctxt,
813                                /* out */unsigned int *arg1,
814                                /* out */unsigned int *arg2,
815                                /* out */unsigned int *arg3,
816                                /* out */unsigned int *arg4,
817                                /* out */unsigned int *arg5,
818                                /* out */unsigned int *arg6,
819                                /* out */unsigned int *arg7)
820 {
821   sos_uaddr_t  uaddr_other_args;
822   unsigned int other_args[5];
823   sos_ret_t    retval;
824 
825   /* Retrieve the 3 arguments. The last one is an array containing the
826      remaining arguments */
827   retval = sos_syscall_get3args(user_ctxt, arg1, arg2,
828                                 (unsigned int *)& uaddr_other_args);
829   if (SOS_OK != retval)
830     return retval;
831   
832   /* Copy the array containing the remaining arguments from user
833      space */
834   retval = sos_memcpy_from_user((sos_vaddr_t)other_args,
835                                 (sos_uaddr_t)uaddr_other_args,
836                                 sizeof(other_args));
837   if (sizeof(other_args) != retval)
838     return -SOS_EFAULT;
839 
840   *arg3 = other_args[0];
841   *arg4 = other_args[1];
842   *arg5 = other_args[2];
843   *arg6 = other_args[3];
844   *arg7 = other_args[4];
845   return SOS_OK;
846 }
847 
848 
849 sos_ret_t sos_syscall_get8args(const struct sos_cpu_state *user_ctxt,
850                                /* out */unsigned int *arg1,
851                                /* out */unsigned int *arg2,
852                                /* out */unsigned int *arg3,
853                                /* out */unsigned int *arg4,
854                                /* out */unsigned int *arg5,
855                                /* out */unsigned int *arg6,
856                                /* out */unsigned int *arg7,
857                                /* out */unsigned int *arg8)
858 {
859   sos_uaddr_t  uaddr_other_args;
860   unsigned int other_args[6];
861   sos_ret_t    retval;
862 
863   /* Retrieve the 3 arguments. The last one is an array containing the
864      remaining arguments */
865   retval = sos_syscall_get3args(user_ctxt, arg1, arg2,
866                                 (unsigned int *)& uaddr_other_args);
867   if (SOS_OK != retval)
868     return retval;
869   
870   /* Copy the array containing the remaining arguments from user
871      space */
872   retval = sos_memcpy_from_user((sos_vaddr_t)other_args,
873                                 (sos_uaddr_t)uaddr_other_args,
874                                 sizeof(other_args));
875   if (sizeof(other_args) != retval)
876     return -SOS_EFAULT;
877 
878   *arg3 = other_args[0];
879   *arg4 = other_args[1];
880   *arg5 = other_args[2];
881   *arg6 = other_args[3];
882   *arg7 = other_args[4];
883   *arg8 = other_args[5];
884   return SOS_OK;
885 }
886 
887 
888 /* =======================================================================
889  * Backtrace facility. To be used for DEBUGging purpose ONLY.
890  */
891 
892 
893 sos_ui32_t sos_backtrace(const struct sos_cpu_state *cpu_state,
894                          sos_ui32_t max_depth,
895                          sos_vaddr_t stack_bottom,
896                          sos_size_t stack_size,
897                          sos_backtrace_callback_t * backtracer,
898                          void *custom_arg)
899 {
900   int depth;
901   sos_vaddr_t callee_PC, caller_frame;
902 
903   /* Cannot backtrace an interrupted user thread ! */
904   if ((NULL != cpu_state)
905       &&
906       (TRUE == sos_cpu_context_is_in_user_mode(cpu_state)))
907     {
908       return 0;
909     }
910   
911   /*
912    * Layout of a frame on the x86 (compiler=gcc):
913    *
914    * funcA calls funcB calls funcC
915    *
916    *         ....
917    *         funcB Argument 2
918    *         funcB Argument 1
919    *         funcA Return eip
920    * frameB: funcA ebp (ie previous stack frame)
921    *         ....
922    *         (funcB local variables)
923    *         ....
924    *         funcC Argument 2
925    *         funcC Argument 1
926    *         funcB Return eip
927    * frameC: funcB ebp (ie previous stack frame == A0) <---- a frame address
928    *         ....
929    *         (funcC local variables)
930    *         ....
931    *
932    * The presence of "ebp" on the stack depends on 2 things:
933    *   + the compiler is gcc
934    *   + the source is compiled WITHOUT the -fomit-frame-pointer option
935    * In the absence of "ebp", chances are high that the value pushed
936    * at that address is outside the stack boundaries, meaning that the
937    * function will return -SOS_ENOSUP.
938    */
939 
940   if (cpu_state)
941     {
942       callee_PC    = cpu_state->eip;
943       caller_frame = cpu_state->ebp;
944     }
945   else
946     {
947       /* Skip the sos_backtrace() frame */
948       callee_PC    = (sos_vaddr_t)__builtin_return_address(0);
949       caller_frame = (sos_vaddr_t)__builtin_frame_address(1);
950     }
951 
952   for(depth=0 ; depth < max_depth ; depth ++)
953     {
954       /* Call the callback */
955       backtracer(callee_PC, caller_frame + 8, depth, custom_arg);
956 
957       /* If the frame address is funky, don't go further */
958       if ( (caller_frame < stack_bottom)
959            || (caller_frame + 4 >= stack_bottom + stack_size) )
960         return depth;
961 
962       /* Go to caller frame */
963       callee_PC    = *((sos_vaddr_t*) (caller_frame + 4));
964       caller_frame = *((sos_vaddr_t*) caller_frame);
965     }
966   
967   return depth;
968 }
969 
970 
971 /* *************************************************************
972  * Function to manage the TSS.  This function is not really "public":
973  * it is reserved to the assembler routines defined in
974  * cpu_context_switch.S
975  *
976  * Update the kernel stack address so that the IRQ, syscalls and
977  * exception return in a correct stack location when coming back into
978  * kernel mode.
979  */
980 void
981 sos_cpu_context_update_kernel_tss(struct sos_cpu_state *next_ctxt)
982 {
983   /* next_ctxt corresponds to an interrupted user thread ? */
984   if (sos_cpu_context_is_in_user_mode(next_ctxt))
985     {
986       /*
987        * Yes: "next_ctxt" is an interrupted user thread => we are
988        * going to switch to user mode ! Setup the stack address so
989        * that the user thread "next_ctxt" can come back to the correct
990        * stack location when returning in kernel mode.
991        *
992        * This stack location corresponds to the SP of the next user
993        * thread once its context has been transferred on the CPU, ie
994        * once the CPU has executed all the pop/iret instruction of the
995        * context switch with privilege change.
996        */
997       kernel_tss.esp0 = ((sos_vaddr_t)next_ctxt)
998                         + sizeof(struct sos_cpu_ustate);
999       /* Note: no need to protect this agains IRQ because IRQs are not
1000          allowed to update it by themselves, and they are not allowed
1001          to block */
1002     }
1003   else
1004     {
1005       /* No: No need to update kernel TSS when we stay in kernel
1006          mode */
1007     }
1008 }

source navigation ] diff markup ] identifier search ] general search ]