[SOS] changement de privileges + interrupt manager

David Decotigny david.decotigny at free.fr
Dim 6 Mar 18:29:25 CET 2005


stephane duverger wrote:
> J'ai cependant une derniere question. En regardant l'algo d'iret (intel
> volume 2) je n'ai pas vu (bien que ce soit spécifié dans le volume 3 au
> chapitre des interruptions) que l'instruction remettait les infos de la
> pile user (ss et esp) dans les registres lors du depilement.

Je viens de jeter un coup d'oeil, et effectivement on dirait qu'ils ont 
"oublié" ce detail. En tout cas c'est pas tres clair. Pour la recup de 
SS, c'est presque sous-entendu au debut du pseudo code de 
RETURN-TO-OUTER-PRIVILGE-LEVEL ("Read return segment selector ... "), 
ils oublient juste de mettre a jour ss apres toutes les verifications. 
Par contre, pour esp, la c'est carrement "oublié". Ceci dit, on devine 
qu'il y a un "esp" dans le coup parce que le debut du pseudo-code dit 
qu'on verifie que la pile doit posseder 8 octets supplementaires dans le 
segment de pile actuel (ie SP [étendu a 32 bits] + ESP). Autre chose, a 
aucun moment on ne fait Pop() de ss et esp. Bref, ce pseudo-code est faux.

La doc de reference correcte est celle du 5.12.1 dans la doc intel vol 3 
(Exception- or Interrupt-Handler Procedures). En tous cas c'est le 
comportement observe dans bochs, qemu, AMD k7 800, Cyrix 133, Intel PIV 
2.4 ;)

BTW, le code de qemu peut servir de reference plus "sure" que la doc 
Intel : target-i386/{op,helper}.c
Ou voit clairement la chose :

    if (rpl == cpl) {
       ....
     } else {
         /* return to different priviledge level */
         if (shift == 1) {
             /* 32 bits */
             POPL(ssp, sp, sp_mask, new_esp);
             POPL(ssp, sp, sp_mask, new_ss);
             new_ss &= 0xffff;
         } else {
           ...
         }
     ...
     }

> J'ai vu dans ton code de la version "au debut sos devait fonctionner
> comme ca" que tu preparais bien la pile avec ses infos en plus des
> flags, cs et eip. Cela justifie bien que l'iret changera de pile au
> retour. Je voulais juste avoir confirmation.

Voui, voir code bochs/qemu + Intel 5.12.1 (figure 5-4) dans le vol 3

> Tu places aussi un code d'erreur (push $0) et fait l'iret, hors n'est-il
> pas a la charge du programmeur de supprimer ce fameux code d'erreur
> avant un iret comme l'on fait durant le traitement d'une interruption
> "normale" ?

C'etait en effet completement faux (iret n'a jamais eu a dependre d'un 
code d'erreur empilé), c'etait pour voir si vous suiviez.

Je me suis plante de version dans le cvs, il fallait lire le code suivant :
--------------------------------------------------------------------
.globl sos_cpu_context_switch_to_user_mode
.type sos_cpu_context_switch_to_user_mode, @function
sos_cpu_context_switch_to_user_mode:
         // arg2= user_initial_SP -- esp+8
         // arg1= user_initial_PC -- esp+4
         // caller ip             -- esp

   /*
    * Store the kernel SP where the CPU will have to return to when
    * returning from user to kernel mode. That SP is the value of
    * the SP before the call to this function
    */
   movl  %esp, %eax
   addl  $12,  %eax /* SP before function call is 12B above current SP */
   pushl %eax
   call  sos_cpu_context_update_kernel_tss_switch_to_user
   addl  $4, %esp

   /*
    * Setup the stack so as to suit the iret instruction for a "return"
    * with privilege change. See figure 6-5 of Intel x86 vol 1 (error
    * code is skipped). We could have used the "far ret" instruction,
    * the only difference would have been that the IF flag of the
    * EFLAGS register would not have been setup so that the IRQ inside
    * the user thread are allowed
    */

   /* Push SS register of the user context */
   pushl $SOS_BUILD_SEGMENT_REG_VALUE(3, 0, SOS_SEG_UDATA) // ==> esp += 4

   /* Push the initial user SP */
   pushl 12(%esp) // ==> esp += 4

   /* Push the initial user EFLAGS register */
   pushl $(1 << 9) // IF flag set ==> esp += 4

   /* Push the CS register of the user context */
   pushl $SOS_BUILD_SEGMENT_REG_VALUE(3, 0, SOS_SEG_UCODE) // ==> esp += 4

   /* Push the PC of the user context */
   pushl 20(%esp)

   /*
    * Reset the general purpose user segment registers
    */
   /* Build the contents of these registers */
   movw $SOS_BUILD_SEGMENT_REG_VALUE(3, 0, SOS_SEG_UDATA), %ax
   pushw %ax ; popw %ds
   pushw %ax ; popw %es
   pushw %ax ; popw %fs
   pushw %ax ; popw %gs

   /*
    * Reset the general purpose registers
    */
   movl $0, %eax
   movl $0, %ebx
   movl $0, %ecx
   movl $0, %edx
   movl $0, %ebp
   movl $0, %esi
   movl $0, %edi

   /* Now switch to user context ! */
   iret
--------------------------------------------------------------------
Ce code-la a ete testé et approuvé.

Bonne journee,


Plus d'informations sur la liste de diffusion Sos