The k project

Protected mode

When your brand new computer boots, it’s still using the old 16bit mode, called “real mode”. One of the first things done by an operating system is to switch to protected mode, which enables 32-bit memory access.

The GDT

The GDT (Global Descriptor Table) is represented as an array of descriptors. Each entry describes a memory segment.

gdte

The DPL field describes the privilege level at which the segment is accessible. Usually the kernel runs at privilege level 0, and the userland at privilege level 3.

The Granularity indicates if the limit is interpreted as a byte number (the flag is cleared) or if it’s interpreted as a number of pages (a page is 4KB length).

The first segment is the null segment, it has the same goal as the NULL pointer in C. The GDT entry should be filled with zero.

The TYPE field should be filled with:

gdte_type

Once you filled your GDT, you must inform the processor about it. This is done with the gdtr register which can be changed with the lgdt assembly instruction:

gdtr_idtr

Segments selectors

The segmentation, set with the GDT, can be used with the segmentation registers:

The format is the following:

selector

The segments can only be changed when the RPL and the CPL (which is contained in the cs register) are smaller than the DPL of the requested segment.

Switching to protected mode

The first thing to do is to load a GDT into the gdtr register with the lgdt instruction. Next, you should set the PE (Protection Enable) bit in the cr0 register:

cr0

Since you can’t modify cr0, you should use a temporary register:

movl $0x12, %cr0 /* It _won't_ assemble */

movl $0x12, %eax
movl %eax, %cr0 /* OK */

Lastly, you should reload all the segment selectors with good values. Like cr0, all the selectors except cs should be set with a temporary register.

cs is special, you can only alter it with an intersegment jump (ljmp), or with an intersegment return (lret):

pushl $0x42 /* push %cs on the stack */
pushl $1f /* push %eip on the stack */
lret /* far return */
1: /* After the lret you will get here, with cs set to 0x42 */