I have gotten into the mood of writing a kernel again. My annoyance always has been that I needed a separate file for assembly operations like ‘lgdt’ or ‘lidt’. This post I will be handling the ‘lgdt’ instruction with inline gcc assembly. This will keep your ‘assembly’ file small and it will only define operations to call your kernel’s entry point. Also it makes your code more portable, because you’ll only need to exclude your ‘gdt.c’ file when compiling for other architectures. Rewriting your ‘assembly’ file becomes more easy then.
We need the following structure:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| /*! \struct GDTPointer
*\brief GDTPointer
*
* This struct defines the GDT Pointer type
*/
struct GDTPointer {
/*! The limit i.e number of descriptors in GDT table */
unsigned short limit;
/*! Base address of the GDT Table */
unsigned int base;
} __attribute__((packed)); |
I will not discuss with you howto create a GDT table or how to fill this structure. It’s only there to show how to use it together with ‘lgdt’.
Let’s create ourself a new GDT pointer:
1
2
3
| struct GDTPointer gdtPointer;
// fill it with the correct data |
OK, good, now that we’ve got that, let’s do the fun part.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // load GDT pointer
asm volatile ("lgdt %0" : "=m" (gdtPointer));
// jump to new segment (0x08 in my case)
asm volatile("ljmp $(0x08), $reload_segments");
// we need to set al non-code segments to 0x10 (in my case)
asm volatile("reload_segments:");
asm volatile("movl $0x10, %eax");
asm volatile("movl %eax, %ds");
asm volatile("movl %eax, %es");
asm volatile("movl %eax, %fs");
asm volatile("movl %eax, %gs");
asm volatile("movl %eax, %ss"); |
If you have something like:
1
| struct GDTPointer* gdtPointer = malloc(sizeof(struct GDTPointer)); |
You’ll need to dereference the variable like:
1
| asm volatile ("lgdt %0" : "=m" (*gdtPointer)); |
You could use the same inline assembly to load an IDT pointer:
1
| asm volatile ("lidt %0" : "=m" (idtPointer)); |