I have a header that defines some packed structs in the same way as MINIX, a source file that sets up the data structures, and an assembly function that loads the GDT and sets up the special registers. I'm pretty sure the problem is with the assembly function that actually loads the GDT, but I've included everything just in case.
How do we know that the correct segment selector to call flush_segments from is 0x08, and why are we setting all other segments to 0x10 now?
Code: Select all
.intel_syntax noprefix
.global x86_lgdt
.type x86_lgdt, @function
x86_lgdt:
lgdt [esp+4]
jmp 0x08:flush_segments
flush_segments:
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
ret
Code: Select all
#ifndef _GDT_KERNEL_H
#define _GDT_KERNEL_H
#include <stdint.h>
struct GdtEntry {
uint16_t limit_low;
uint16_t base_low;
uint8_t base_middle;
uint8_t access; /* |P|Pr|1|X|D|W|A| 1 indicates that this is code/data */
uint8_t gran; /* |G|S|0|0|LMIT| */
uint8_t base_high;
}__attribute__((packed));
/*
P - Present bit
Pr - Privilege bits, 0-3
X - Executable bit
D - Direction bit for data, 0 grows up, 1 grows down
- Conforming bit for code, 0 can only be executed by priv set in Pr.
W - Read/Write. 1 enables reading for code segments and write access for data.
A - Access bit used by CPU. Set it to 0.
G - Granularity bit. 0 for limit in 1B blocks. 1 if limit in 4KB blocks (pages)
S - Size bit. 0 for 16-bit mode, 1 for 32-bit mode
*/
struct GdtR {
uint16_t limit;
uint32_t base;
}__attribute__((packed));
void setup_gdt(void);
#endif
Code: Select all
#include <kernel/gdt.h>
#include <kernel/gdt_asm.h>
#include <string.h>
#include <stdio.h>
#define TYPE_CODE 14
#define TYPE_DATA 6
#define PRIV_KERN 0
#define PRIV_USER 3
struct GdtEntry gdt_entries[5];
struct GdtR gdt;
uint8_t createAccess(uint8_t privilege, uint8_t type) {
/* Entry will always be present. 0x90 is 1001 0000b */
return (uint8_t) ((0x90) | (privilege << 5) | (type));
}
void setEntry(uint32_t base, uint32_t limit, uint8_t access, struct GdtEntry* entry) {
entry->base_low = base & 0xFFFF; //lower 16 bits
entry->base_middle = (base << 8) >> 24;
entry->base_high = base >> 24;
entry->limit_low = limit & 0xFFFF;
entry->gran = (12 << 4) | (uint8_t)(limit << 12 >> 28);
/* 12 in highest 4 bits sets granularity bit (setting 4KB blocks)
and Size bit (set 32-bit mode) */
entry->access = access;
}
void setup_gdt(void) {
printf("BEGIN GDT SETUP\n");
memset(gdt_entries, 0, sizeof(gdt_entries));
gdt.base = (uint32_t) &gdt_entries;
gdt.limit = sizeof(gdt_entries) - 1;
printf("Addr: 0x%x\nBase: 0x%x\nLim: 0x%x\n", &gdt, gdt.base, gdt.limit);
uint8_t kern_code = createAccess(PRIV_KERN, TYPE_CODE);
uint8_t kern_data = createAccess(PRIV_KERN, TYPE_DATA);
uint8_t user_code = createAccess(PRIV_USER, TYPE_CODE);
uint8_t user_data = createAccess(PRIV_USER, TYPE_DATA);
//setEntry(0, 0, 0, &gdt_entries[0]);//<--this should be zero'd from memset
setEntry(0, 0x000FFFFF, kern_code, &gdt_entries[1]);
setEntry(0, 0x000FFFFF, kern_data, &gdt_entries[2]);
setEntry(0, 0x000FFFFF, user_code, &gdt_entries[3]);
setEntry(0, 0x000FFFFF, user_data, &gdt_entries[4]);
printf("---Entry---\n");
printf("base: 0x%x\n",(uint32_t)(
(uint32_t)gdt_entries[1].base_high << 24 |
(uint32_t)gdt_entries[1].base_middle) << 16 |
(uint32_t)gdt_entries[1].base_low);
printf("limit: 0x%x\n", (uint32_t)( (uint32_t)gdt_entries[1].gran << 16
| (uint32_t)gdt_entries[1].limit_low) & 0xFFFFF );
for (int i = 1; i < 5; i++) {
struct GdtEntry debugp = gdt_entries[i];
printf("----Entry%d---\n", i);
printf("access: 0x%x\n", (uint32_t)debugp.access);
printf("gran: 0x%x\n", (uint32_t)(debugp.gran >> 4));
}
// x86_lgdt((uint32_t) &gdt);
}
Code: Select all
BEGIN GDT SETUP
Addr: 0x102000
Base: 0x102020
Lim: 0x27
---Entry---
base: 0x0
limit: 0xfffff
----Entry1---
access: 0x9e
gran: 0xc
----Entry2---
access: 0x96
gran: 0xc
----Entry3---
access: 0xfe
gran: 0xc
----Entry4---
access: 0xf6
gran: 0xc