¡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?
Utilizar “int 30h” para bajar el código de nivel cero a tres.
La paginación permanece invariable.
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.
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