[SOS] changement de privileges

David Decotigny david.decotigny at free.fr
Dim 6 Mar 12:47:24 CET 2005


Bonjour,

Normalement le code est sur le CD. Malheureusement je n'ai pas pu 
verifier car je n'ai pas pu me procurer le numéro de Mars. Je pense 
qu'avec le code sous les yeux ça ira beaucoup mieux.

Quelqu'un peut confirmer que le code n'est pas sur le CD ?

stephane duverger wrote:
> Comment faites-vous pour lancer le premier programme utilisateur en ce
> qui concerne la gestion des privileges ?

Dans le code, on met en place la pile d'un nouveau thread user avec un 
CS pointant vers un segment user :
   uctxt->regs.cs
     = SOS_BUILD_SEGMENT_REG_VALUE(3, FALSE, SOS_SEG_UCODE); /* Code */
(cf hwcore/cpu_context.c:sos_cpu_ustate_init)

Pour rappel, quand on ecrit ça, ca correspond a initialiser le contenu 
de la pile utilisateur. Le contenu de cette pile, et tout 
particulierement cet emplacement, est pris en charge par les 
instructions ret/iret. On utilise precisement cette caracteristique pour 
changer de niveau de privilege : quand on fera iret, c'est cette valeur 
de CS qui sera chargee dans le processeur, entrainant un chgt de 
privilege. Et voila. Point c'est tout.

> "The cpl of the calling procedure must be equal to the dpl of the
> destination code segment" dans le cas de segment non-conformes.

Pour l'acces aux segments de code/donnee, c'est surtout le "RPL" qui 
intervient, lui meme contraint par le CPL. Voir doc intel, paragraphe 
4.8.1.1.

Mais ce paragraphe concerne bien l'*acces* a des segments dont le niveau 
de privilege est different du CPL. Il n'est pas question de *changement* 
de privilege, puisque le CPL ne change pas.

Les changements de privilege sont expliques sections 4.8.5 et 4.8.6 de 
la doc Intel vol 3. La figure explique la configuration de la pile utile 
pour le changement de privilege. Cette configuration de la pile explique 
d'ailleurs la difference entre les structures sos_cpu_kstate et 
sos_cpu_ustate de hwcore/cpu_context.c

> Or le probleme avec les TSS c'est que la syntaxe at&t de la commande
> "ljmp" ne prend que des valeurs immediates, donc difficile a gerer
> dynamiquement sans modifier le code a la volee. Apparement elle peut

   ljmp $SEG_VAL, $offset

Pour etablir SEG_VAL, voir macro SOS_BUILD_SEGMENT_REG_VALUE dans 
hwcore/segment.h

> Alors je me suis dit que le mecanisme des interruptions passait outre
> cette gestion de privilege bien corriace.

C'est une solution. Tu parles surement des interruptions logicielles. 
Dans la solution que tu proposes plus loin, tu parles d'utiliser les 
interruptions materielles : c'est a mon avis trop complique, pas elegant 
et ca introduit une latence inutile.

> J'ai bon :) ? Vous ne le mentionnez pas et je n'ai pas pu voir votre
> code, j'aimerais savoir comment vous avez fait, ca m'illuminerait :)

Mieux vaudrait voir le code, la solution a base d'IRQ timer me parait 
machine_a_café.

> En fait le probleme est apparu lorsque je me suis mis à lire
> "Understanding the linux Kernel" et que j'ai vu qu'un thread noyau
...
> - l'interruption d'horloge finit par arriver.
...
> - comment basculer vers le monde utilisateur ? On peut pas appeler le
> point d'entrée du programme simplement car nous aurions #GP en regard de
> ce qu'explique la doc intel.

Comme dans SOS : c'est iret qui fait le travail, execute en mode kernel 
par le handler de l'IRQ. Puisque le thread interrompu etait en mode 
user, il y a eu chgt de privilege vers kernel, donc le CPU a empile 
automatiquement le necessaire sur la pile noyau pour que iret puisse 
revenir en mode user. Voir ref intel precedente.

Un peu d'histoire de sos maintenant, pour l'anecdote. Si on prend la 
philosophie inverse, si on veut passer "a la main" du mode kernel vers 
le mode user, alors il suffit de trafiquer la pile noyau pour qu'un 
simple iret s'occupe du chgt de privilege. Pour la petite histoire, au 
debut c'est comme ca que SOS devait fonctionner pour creer des threads 
user :
  - au debut on cree un thread noyau, en passant l'adresse d'une 
fonction a executer,
  - la fonction en question fait ce qu'elle veut, y compris mettre en 
place un nouvel espace d'adressage, y copier le prog utilisateur, mais 
est toujours en mode noyau.
  - la fonction en question alloue une pile utilisateur en espace 
utilisateur
  - la fonction en question appelle une fonction speciale chargee 
d'effectuer le changement de privilege :

La fonction de chgt de privilege etait la suivante :
----------------------------------------------------------------------------
.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. In our 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(0, 0, SOS_SEG_UCODE) // ==> esp += 4

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

   /* No error code */
   pushl $0

   /*
    * 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

   /* Now switch to user context ! */
   iret
----------------------------------------------------------------------------

Son prototype etait le suivant :
----------------------------------------------------------------------------
/**
  * The function that switches the current kernel thread to the given
  * user thread.
  *
  * @note This is a non-returning function because once in user mode,
  * we never return in this function. Actually, when the user thread
  * will return back in kernel context, this will take place inside an
  * interrupt handler (ie IRQ, syscall or exception), not inside the
  * following function.
  */
void
sos_cpu_context_switch_to_user_mode(sos_uaddr_t user_initial_PC,
                                     sos_uaddr_t user_initial_SP)
      __attribute__((noreturn));
----------------------------------------------------------------------------

> D'autre part j'avais mis un mail le mois dernier en ce qui concernait
> les rencontres. Est-ce tombé à l'eau ? Ca ferait plaisir de vous voir et
> de pouvoir discuter avec vous et les personnes interessées par le
> développement de noyaux.

Ce n'est pas tombé a l'eau. Au debut, c'est Thomas qui lancait l'idée, 
je n'etais pas specialement chaud. Maintenant je suis plutot decide a 
participer aussi, mais ce qui bloque c'est nos emplois du temps. On vous 
tiendra au courant. Probable que ca intervienne plutot vers la fin de la 
serie.

Bonne journee,


Plus d'informations sur la liste de diffusion Sos