El 80386: cómo nació la protección de memoria

Fuentes: 80386 Protection

Este artículo del blog de Nand, un desarrollador que construye un núcleo compatible con el 80386 en SystemVerilog, explora el sistema de protección del 80386, un componente crucial para el funcionamiento de sistemas operativos modernos como Windows 3.0, OS/2 y Linux. Originalmente, el modo protegido del 80286 era problemático, careciendo de paging y sin una forma sencilla de volver al modo real. El 80386 solucionó esto, introduciendo paging, un espacio de direcciones de 32 bits, control de usuario/supervisor por página y el modo virtual 8086, permitiendo la ejecución de programas DOS dentro de un entorno multitarea protegido.

El sistema de protección del 80386 es complejo, involucrando anillos de privilegio (4 en total, aunque la mayoría de los sistemas operativos usan 2: kernel en el anillo 0 y programas de usuario en el anillo 3), segmentación y paging. La segmentación mapea direcciones lógicas a lineales, mientras que el paging traduce estas direcciones lineales a físicas, añadiendo capas adicionales de protección. La complejidad de estas comprobaciones de privilegio, si se implementaran en microcódigo, ralentizarían significativamente las operaciones. Para evitar esto, Intel diseñó una Unidad de Prueba de Protección (Protection Test Unit - PTU) dedicada.

La PTU utiliza un Array de Lógica Programable (PLA) para evaluar en paralelo las reglas de protección, en lugar de ejecutarlas secuencialmente en microcódigo. El PLA recibe información como atributos del descriptor (Type, DPL, S, Present), el nivel de privilegio actual (CPL, RPL) y una constante de prueba que indica el tipo de comprobación a realizar. Esta información se combina en un vector de estado de 16 bits, y el PLA produce una salida de 18 bits que indica si la prueba pasó (continuar), si hay un fallo (fault) o si debe redirigirse a un manejador de puerta (gate handler). El PLA utiliza 33 constantes de prueba diferentes, cada una activando un subconjunto específico de las 148 reglas de protección.

Para hacer el sistema más genérico y eficiente, se utiliza un mecanismo de 'inyección de dependencia' a nivel de hardware. La microcódigo guarda la constante de prueba deseada en un registro (PTSAV) antes de llamar a la subrutina LD_DESCRIPTOR (que carga el descriptor de segmento). Luego, PTOVRR recupera y ejecuta esta constante, permitiendo que la misma subrutina realice diferentes validaciones según el contexto (por ejemplo, una carga de registro de segmento o una llamada FAR). El uso de 'slots de retardo' de un ciclo en el microprocesador también contribuye a la velocidad de ejecución.