¡Esta es una revisión vieja del documento!
Tabla de Contenidos
Cambiar de nivel cero a tres en IA-32e
Objeto del documento
Brindar recomendaciones para el pasaje de nivel de privilegio cero a tres en IA-32e. A partir del ejemplo de programa en IA-32e aportado por el Ing. Dario Alpern.
Punto de partida
El programa long.asm (disponible haciendo click aquí) propone lo siguiente:
; Hacer un programa que muestre la cantidad de decimas de segundo que estuvo ; corriendo en modo largo (64 bits) y la cantidad de teclas presionadas. ; Utilizar para ello las interrupciones en modo protegido 8 (reloj) y 9 (teclado). ; El cuerpo principal de ejecución se detiene con hlt ; ; Hecho por Dario Alejandro Alpern el 11 de marzo de 2008. ;
Que dice "info gdt"
- Entrada nula
- Segmento de código de 64 bits con DPL=0 (cs_sel_64)
- Segmento de código de 32 bits con DPL=0 (cs_sel_32)
- Segmento de datos de 32 bits (ds_sel)
Que dice "info idt"
- Puerta de interrupción en 8 con DPL=0 (cs_sel_64:int8han)
- Puerta de interrupción en 9 con DPL=0 (cs_sel_64:int9han)
Con esto podemos deducir que el PIC no está siendo reubicado.
Que dice "info tab"
<bochs:4> info tab
cr3: 0x00090000
0x00000000-0x001fffff → 0x00000000-0x001fffff
<bochs:5>
Esto nos dice que tenemos una página de dos MB con identity mapping
¿Cual es la idea?
La paginación permanece invariable.
Utilizar “int 30h” para bajar el código de nivel cero a tres.
Luego el timer tick se usa para actualizar pantalla ejecutando código de nivel 0 como antes.
Lo mismo para el manejo del teclado.
Probar “int 80h” para acceder a nivel cero desde el tres.
Finalmente el programa para detenerse debe utilizar “jmp $” en vez de “halt” (Está claro que esto consume CPU, es sólo porque está en nivel tres).
Manos a la obra
- En la GDT declaramos tres segmentos nuevos. Uno para código de 64 bits con DPL=0, otro de datos de 32 bits también con DPL=3 y otro para TSS (Task State Segment) también de 64 bits.
- En la IDT agregamos una puerta de interrupción en 30h con DPL=0, que será para hacer el cambio de privilegio (de cero a tres). También agregamos otra puerta de interrupción en 80h, esta será para acceso a servicios del kernel por código que corre en nivel tres.
- Dado que describimos una TSS en la GDT debemos reservar espacio en memoria a tal efecto.
Esto se logra con: TSS: times tss64_struc_size db 0
La estructura de TSS de 64 bits está disponible mas abajo.
- Asegurarse que la página esté accesible a nivel USER.
- Definir pila de nivel cero (porque no me gusta como está en long.asm)
mov rsp,pila_0
mov ax,ds_sel
mov ss,ax
Donde pila_0 de define como:
align 256
times 256 db 0
pila_0:
- Definir pila de nivel tres.
- Inicializar la base del descriptor de TSS en la GDT
mov eax,TSS
mov [TSS_sel+gdt+2],ax
- Guardar rsp de nivel 0 en la TSS
mov [TSS+4],rsp
- Cargar TSS en TR
mov eax,TSS_sel
ltr ax
- Invocar INT 30h para pasar de nivel cero a tres
La magia está en int 30h
Al ejecutar la interrupción se guarda en la pila de nivel 0 lo siguiente: RIP, CS, RFLAGS, RSP y SS.
La idea es alterar CS, RSP y SS de la pila. Para que luego al hacer IRETQ se cambie de nivel.
push rbp
mov rbp,rsp
push rax ; para trabajar
mov rax,cs_sel3_64
mov [rbp+16],rax
mov rax,pila_3
mov [rbp+32],rax
mov rax,ds_sel3
mov [rbp+40],rax
pop rax
pop rbp
iretq
Estructura de TS de 64 bits
struc tss64_struc
resd 1 ; Reservada \\
.reg_RSP0 resq 1 \\
.reg_RSP1 resq 1 \\
.reg_RSP2 resq 1 \\
resq 1 ; Reservada \\
.reg_IST1 resq 1 \\
.reg_IST2 resq 1 \\
.reg_IST3 resq 1 \\
.reg_IST4 resq 1 \\
.reg_IST5 resq 1 \\
.reg_IST6 resq 1 \\
.reg_IST7 resq 1 \\
resd 1 ; Reservada \\
resd 1 ; Reservada \\
resw 1 ; Reservada \\
.reg_IOMAP resw 1 \\
endstruc
Agradecimientos
A Dario Alpern por el aporte de long.asm. A Alejandro Furfaro por meternos en este baile. A Mariano Gonzalez y Juan Carlos Cuttitta que colaboran con ideas todo el tiempo. Al cuerpo docente de la cátedra de TDIII.
Se aceptan críticas y comentarios.
— Marcelo Doallo 2012/07/05 21:03