80386 Microprocessor: Memory Management and Protection
LIDT and SLDT Instructions
SLDT (Store Local Descriptor Table) Instruction:
- The SLDT instruction retrieves information about the Local Descriptor Table (LDT) and stores it in a register or memory location.
- Syntax:
SLDT operand
- The operand can be a register or a 6-byte memory location where the LDT information will be stored.
- Purpose and Functionality: The SLDT instruction enables the program to obtain details about the LDT, which is a data structure used for managing segment descriptors at the local level.
LIDT (Load Interrupt Descriptor Table) Instruction:
- The LIDT instruction loads the Interrupt Descriptor Table (IDT) with a new base address and size limit.
- Syntax:
LIDT operand
- The operand can be a 6-byte memory location containing the new IDT base address and size limit.
- Purpose and Functionality: The LIDT instruction is used in x86 assembly language to set up or modify the IDT, which is used for handling interrupts and exceptions.
GDTR, LDTR, and IDTR
GDTR (Global Descriptor Table Register)
- GDTR is a special register in the 80386 processor that holds the base address and size limit of the Global Descriptor Table (GDT).
- It is used to point to the GDT, which contains segment descriptors for memory segments used by the entire system.
- The GDTR is loaded using the
LGDT
(Load Global Descriptor Table) instruction. - Purpose: GDTR provides the necessary information to locate and access the GDT, enabling memory segmentation and protection at the system level.
LDTR (Local Descriptor Table Register)
- LDTR is a special register in the 80386 processor that holds the base address and size limit of the Local Descriptor Table (LDT).
- It is used to point to the LDT, which contains segment descriptors for memory segments specific to a particular process or task.
- The LDTR is loaded using the
LLDT
(Load Local Descriptor Table) instruction. - Purpose: LDTR facilitates finer-grained memory management and protection by allowing different processes to have separate memory segments defined in their respective LDTs.
IDTR (Interrupt Descriptor Table Register)
- IDTR is a special register in the 80386 processor that holds the base address and size limit of the Interrupt Descriptor Table (IDT).
- It is used to point to the IDT, which contains descriptors for interrupt and exception handling routines.
- The IDTR is loaded using the
LIDT
(Load Interrupt Descriptor Table) instruction. - Purpose: IDTR enables the processor to locate and execute the appropriate interrupt or exception handler routines defined in the IDT when an interrupt or exception occurs.
Selector Format
The selector portion of a logical address identifies a descriptor by specifying a descriptor table and indexing a descriptor within that table. Selectors may be visible to applications programs as a field within a pointer variable, but the values of selectors are usually assigned (fixed up) by linkers or linking loaders.
- Index: Selects one of 8192 descriptors in a descriptor table. The processor simply multiplies this index value by 8 (the length of a descriptor) and adds the result to the base address of the descriptor table in order to access the appropriate segment descriptor in the table.
- Table Indicator: Specifies to which descriptor table the selector refers. A zero indicates the GDT; a one indicates the current LDT.
- Requested Privilege Level (RPL): Used by the protection mechanism. Because the first entry of the GDT is not used by the processor, a selector that has an index of zero and a table indicator of zero.
General Segment Descriptor Format
- The segment descriptor provides the processor with the data it needs to map a logical address into a linear address.
- Descriptors are created by compilers, linkers, loaders, or the operating system, not by applications programmers.
Segment-descriptor fields are:
- BASE: Defines the location of the segment within the 4-gigabyte linear address space. The processor concatenates the three fragments of the base address to form a single 32-bit value.
- LIMIT: Defines the size of the segment. When the processor concatenates the two parts of the limit field, a 20-bit value results. The processor interprets the limit field in one of two ways, depending on the setting of the granularity bit:
- In units of one byte, to define a limit of up to 1 megabyte.
- Granularity bit: Specifies the units with which the LIMIT field is interpreted. When the bit is clear, the limit is interpreted in units of one byte; when set, the limit is interpreted in units of 4 Kilobytes.
- TYPE: Distinguishes between various kinds of descriptors.
Privilege Levels: CPL, RPL, DPL, EPL, and IOPL
- CPL (Current Privilege Level): A 2-bit field in the processor’s registers that indicates the current privilege level of the executing code. The privilege levels range from 0 to 3, with 0 being the highest privilege level and 3 being the lowest. The CPL is used to control access to privileged instructions and data. For example, only code that is running in ring 0 can execute privileged instructions.
- RPL (Requestor’s Privilege Level): A 2-bit field in the processor’s segment descriptors that indicates the privilege level of the code that is accessing the segment. The RPL is used to control access to segments. For example, a program that is running in ring 3 can only access segments that have a DPL of 0 or 3.
- DPL (Descriptor Privilege Level): A 2-bit field in the processor’s segment descriptors that indicates the privilege level of the segment itself. When a program tries to access a segment, the processor checks the RPL of the segment selector against the DPL of the segment descriptor. If the RPL is less than or equal to the DPL, then the access is allowed. If the RPL is greater than the DPL, then the access is denied and a general protection fault is generated.
- EPL (Effective Privilege Level): A dynamic concept that represents the current privilege level of a running application or task within the x86 architecture’s ring protection model. It determines the level of access a program has to certain resources, such as memory, I/O ports, and sensitive instructions. It can change during execution based on system calls, hardware interrupts, or software transitions between protection rings. It’s influenced by both the CPL (Current Privilege Level) of the running code and the IOPL (Input/Output Privilege Level) of the executing task.
- IOPL (Input/Output Privilege Level): A static setting stored in a 2-bit field within the x86 processor’s flags register (EFLAGS). It controls which privilege levels (rings) have unrestricted access to I/O ports and instructions that directly manipulate hardware devices. It’s typically set by the operating system to enforce security and prevent unauthorized programs from directly controlling hardware resources. Programs running at a privilege level equal to or higher than the IOPL have full I/O access.
Protection Mechanisms in 80386
Five aspects of protection in the 80386 microprocessor:
- Type checking: The 80386 checks the type of each memory access to ensure that it is compatible with the access permissions of the segment being accessed. For example, a read access to a code segment is not allowed if the segment is marked as execute-only.
- Limit checking: The 80386 checks the limit of each segment to ensure that the access does not exceed the bounds of the segment. For example, a read access to a segment that is 1MB in size is not allowed if the offset is greater than 1MB.
- Restriction of addressable domain: The 80386 can be configured to restrict the addressable domain of each task. This means that each task can only access a specific range of memory. This can be used to isolate tasks from each other and to prevent them from accessing sensitive data.
- Restriction of procedure entry points: The 80386 can be configured to restrict the procedure entry points that are available to each task. This can be used to prevent tasks from executing privileged instructions or from accessing kernel-mode data.
- Restriction of instruction set: The 80386 can be configured to restrict the instruction set that is available to each task. This can be used to prevent tasks from executing certain instructions, such as those that can be used to modify the operating system or to access hardware directly.
Combining Segment and Page Level Protection
The 80386 processor offers a unique and powerful feature: the ability to combine both segment protection and page-level protection for memory management and security. This dual-layered approach provides several advantages over relying on just one type of protection.
Concept:
- Segment protection: Segment protection defines logical segments of memory with specified sizes, types (code, data, stack), and access permissions. This provides a coarse-grained approach to safeguarding certain memory regions.
- Page-level protection: While segments offer a broader view, pages further divide them into smaller, fixed-size chunks (4KB) in RAM. Each page can have its own protection attributes, offering a more granular and flexible control over individual memory units.
Combining the two: The 80386 utilizes both protection mechanisms in a two-step process:
- Segment translation: During address translation, the processor first uses the segment selector to locate the corresponding segment descriptor in the GDT or LDT. This descriptor defines the base address and limit of the segment in physical memory.
- Page translation (optional): If paging is enabled for the segment, the processor then takes the translated linear address and further breaks it down into a page number and an offset within the page. Using the page number, it consults the appropriate page table to find the physical frame number where the specific page resides.
Virtual 8086 Mode Features
Virtual 8086 mode is used by many operating systems to support legacy applications that were written for real mode. For example, Microsoft Windows uses virtual 8086 mode to run DOS applications.
Virtual 8086 mode also has some security benefits. It can be used to isolate malicious applications from the rest of the system. For example, if a malicious application is running in a virtual machine, it cannot access the operating system or other applications running in other virtual machines.
Here are some of the features of virtual 8086 mode:
- Support for legacy applications: Virtual 8086 mode allows operating systems to support legacy applications that were written for real mode.
Task Switching in 80386
The 80386 microprocessor can perform a task switch in four ways:
-
Using a
JMP
orCALL
instruction: AJMP
orCALL
instruction can refer to a task state segment (TSS) descriptor. When the instruction is executed, the 80386 switches to the task specified by the TSS descriptor. - Using an interrupt or exception: An interrupt or exception can cause a task switch if it vectors to a task gate in the interrupt descriptor table (IDT). A task gate is a special type of gate that allows the 80386 to switch to a task.
-
Using an
IRET
instruction: When anIRET
instruction is executed, the 80386 switches to the task specified by the task register (TR). The TR is a special register that stores the selector of the current task’s TSS. - Using the NT (nested task) flag: When the NT flag is set, the 80386 can switch to a task that was interrupted by another task. This allows the 80386 to implement nested task switching.
When the 80386 performs a task switch, it saves the state of the current task on the stack and loads the state of the new task from the TSS. The state of a task includes the following:
- The values of the general-purpose registers
- The value of the instruction pointer (IP)
- The value of the flags register
- The value of the segment registers
- The value of the task register
The 80386 also updates the task link field in the TSS of the current task to point to the new task. This allows the 80386 to return to the current task if it is interrupted by another task. Task switching is a critical part of multitasking operating systems. It allows the operating system to efficiently manage multiple tasks and ensure that each task has access to the CPU when it needs it.
- Isolation: Virtual 8086 mode can be used to isolate malicious applications from the rest of the system.
- Performance: Virtual 8086 mode is relatively efficient, and it does not significantly impact the performance of the operating system or other applications running on the system.
TSS is a special type of segment, used to manage the task. The 80386 uses TSS like a scratch-pad. It stores everything it needs to know about a task in TSS. This means that task environment (context) is stored in the TSS. TSS is not accessible to the general user program or program even at privilege level 0. The fields within TSS are accessible to only 80386. The fields of a TSS are divided into two sets: Dynamic set and Static set.
Dynamic Set
The 80386 updates dynamic set when it switches from one task to another task. This set includes:
- The general registers (EAX, EBX, ECX, EDX, ESP, EBP, ESI, EDI)
- The segment registers (CS, SS, DS, ES, FS, GS)
- The flag registers (EFLAGS)
- The instruction pointer (EIP)
- Back link
The first four fields (general registers, segment registers/selectors, flags and instruction pointer) save the state of the microprocessor, 80386. Saving EIP guarantees that the task can be restarted at the point at which it was stopped and saving EFLAGs allows 80386 to execute conditional instructions properly, when the task is restarted. The back link is used by the 80386 to keep track of a previous task. By executing a return instruction at the end of the new task, the back link selector for the previous TSS is automatically loaded into task register. This activates the previous task and restores the prior program environment.
Static Set
The 80386 only reads fields from this set. This set includes:
- The selector for the task’s LDT
- The register (PDBR) that contains the base address of the task’s page Directory
- Pointers to the stacks for privilege levels 0-2
- The T-bit (debug trap bit) which causes the 80386 to raise a debug exception when a task switch occurs.
- The I/O map offset.
Entering Virtual 8086 Mode
Virtual 8086 mode (V86 mode) is a compatibility mode for the Intel 80386 and later processors that allows them to run 8086 and 8088 programs. When the CPU is in V86 mode, it executes instructions in the same way as an 8086 or 8088 processor, with the same memory addressing and interrupt handling.
V86 mode can be entered in two ways:
- Through a task switch: When the CPU is in protected mode, it can switch to V86 mode by loading a task descriptor from the task state segment (TSS). The TSS must be for a V86 task, and the VM bit in the EFLAGS register must be set.
- Through an interrupt: When the CPU is in protected mode, it can enter V86 mode by handling an interrupt that is directed to a V86 task. The VM bit in the EFLAGS register must be set before the interrupt handler is called.
When the CPU leaves V86 mode, it returns to the protected mode task that was running before V86 mode was entered. The VM bit in the EFLAGS register is cleared when the CPU leaves V86 mode.
V86 mode was introduced in the Intel 80386 processor to allow it to run 8086 and 8088 programs without requiring them to be modified. V86 mode is also used by some operating systems to provide a way for 8086 and 8088 programs to run alongside protected mode programs.
Limitations of V86 mode:
- Memory limitation: V86 mode programs are limited to a maximum of 1MB of memory.
- Interrupt limitation: V86 mode programs can only use a subset of the interrupts available in protected mode.
- Protection limitation: V86 mode programs are not protected from each other or from the operating system.
Leaving V86 Mode
When a microprocessor leaves V86 mode, it returns to protected mode. This can happen for a number of reasons, such as:
- An interrupt or exception occurs.
- A task switch is performed.
- The microprocessor is reset.
When the microprocessor leaves V86 mode, it saves the state of the V86 task, including the values of the segment registers, the instruction pointer, and the flags register. It then loads the state of the protected-mode task, which includes the values of the segment registers, the instruction pointer, and the flags register.
The microprocessor can only leave V86 mode if it is running in a V86 task. A V86 task is a special type of task that runs in V86 mode. V86 tasks are limited in what they can do, but they can run legacy 8086 programs.
Here are some of the limitations of V86 tasks:
- They cannot access protected-mode memory.
- They cannot use protected-mode interrupts.
- They cannot use protected-mode features such as paging and virtual memory.
Despite these limitations, V86 tasks are a valuable tool for running legacy 8086 programs. They allow these programs to run alongside protected mode programs, which can provide a number of benefits, such as improved performance and security.