[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