pluto/src/kernel/arch/x86/gdt.zig

310 lines
8.4 KiB
Zig
Raw Normal View History

// Zig version: 0.4.0
const arch = @import("arch.zig");
2019-06-22 11:00:57 +02:00
const log = @import("../../log.zig");
const NUMBER_OF_ENTRIES: u16 = 0x06;
const TABLE_SIZE: u16 = @sizeOf(GdtEntry) * NUMBER_OF_ENTRIES - 1;
// The indexes into the GDT where each segment resides.
/// The index of the NULL GDT entry.
const NULL_INDEX: u16 = 0x00;
/// The index of the kernel code GDT entry.
const KERNEL_CODE_INDEX: u16 = 0x01;
/// The index of the kernel data GDT entry.
const KERNEL_DATA_INDEX: u16 = 0x02;
/// The index of the user code GDT entry.
const USER_CODE_INDEX: u16 = 0x03;
/// The index of the user data GDT entry.
const USER_DATA_INDEX: u16 = 0x04;
/// The index of the task state segment GDT entry.
const TSS_INDEX: u16 = 0x05;
// The offsets into the GDT where each segment resides.
/// The offset of the NULL GDT entry.
pub const NULL_OFFSET: u16 = 0x00;
/// The offset of the kernel code GDT entry.
pub const KERNEL_CODE_OFFSET: u16 = 0x08;
/// The offset of the kernel data GDT entry.
pub const KERNEL_DATA_OFFSET: u16 = 0x10;
/// The offset of the user code GDT entry.
pub const USER_CODE_OFFSET: u16 = 0x18;
/// The offset of the user data GDT entry.
pub const USER_DATA_OFFSET: u16 = 0x20;
/// The offset of the TTS GDT entry.
pub const TSS_OFFSET: u16 = 0x28;
// The access bits
const ACCESSED_BIT = 0x01; // 00000001
const WRITABLE_BIT = 0x02; // 00000010
const DIRECTION_CONFORMING_BIT = 0x04; // 00000100
const EXECUTABLE_BIT = 0x08; // 00001000
const DESCRIPTOR_BIT = 0x10; // 00010000
const PRIVILEGE_RING_0 = 0x00; // 00000000
const PRIVILEGE_RING_1 = 0x20; // 00100000
const PRIVILEGE_RING_2 = 0x40; // 01000000
const PRIVILEGE_RING_3 = 0x60; // 01100000
const PRESENT_BIT = 0x80; // 10000000
const KERNEL_SEGMENT = PRESENT_BIT | PRIVILEGE_RING_0 | DESCRIPTOR_BIT;
const USER_SEGMENT = PRESENT_BIT | PRIVILEGE_RING_3 | DESCRIPTOR_BIT;
const CODE_SEGMENT = EXECUTABLE_BIT | WRITABLE_BIT;
const DATA_SEGMENT = WRITABLE_BIT;
const TSS_SEGMENT = PRESENT_BIT | EXECUTABLE_BIT | ACCESSED_BIT;
// The flag bits
const IS_64_BIT = 0x02; // 0010
const IS_32_BIT = 0x04; // 0100
const IS_LIMIT_4K_BIT = 0x08; // 1000
/// The structure that contains all the information that each GDT entry needs.
const GdtEntry = packed struct {
/// The lower 16 bits of the limit address. Describes the size of memory that can be addressed.
limit_low: u16,
/// The lower 24 bits of the base address. Describes the start of memory for the entry.
base_low: u24,
/// Bit 0 : accessed - The CPU will set this when the GDT entry is accessed.
/// Bit 1 : writable - The writable bit to say if the memory region is writable. If set, then memory region is readable and writable. If not set, then the memory region is just readable.
/// Bit 2 : direction_conforming - For a code segment: if set (1), then the code segment can be executed from a lower ring level. If unset (0), then the code segment can only be executed from the same ring level in the privilege flag. For the data segment: if set (1), then the data segment grows downwards. If unset (0), then the data segment grows upwards.
/// Bit 3 : executable - The execution bit to say that the memory region is executable.
/// Bit 4 : descriptor_bit - The descriptor bit.
/// Bit 5-6 : privilege - The ring level of the memory region.
/// Bit 7 : present - The present bit to tell that this GDT entry is present.
access: u8,
/// The upper 4 bits of the limit address. Describes the size of memory that can be addressed.
limit_high: u4,
/// Bit 0 : reserved_zero - This must always be zero.
/// Bit 1 : is_64bits - Whether this is a 64 bit system.
/// Bit 2 : is_32bits - Whether this is a 32 bit system.
/// Bit 3 : is_limit_4K - Whether paging is turned on, and each address is addressed as if it is a page number not physical/logical linear address.
flags: u4,
/// The upper 8 bits of the base address. Describes the start of memory for the entry.
base_high: u8,
};
/// The GDT pointer structure that contains the pointer to the beginning of the GDT and the number
/// of the table (minus 1). Used to load the GDT with LGDT instruction.
pub const GdtPtr = packed struct {
/// 16bit entry for the number of entries (minus 1).
limit: u16,
/// 32bit entry for the base address for the GDT.
base: *GdtEntry,
};
///
/// The TSS entry structure
///
const TtsEntry = packed struct {
/// Pointer to the previous TSS entry
prev_tss: u32,
/// Ring 0 32 bit stack pointer.
esp0: u32,
/// Ring 0 32 bit stack pointer.
ss0: u32,
/// Ring 1 32 bit stack pointer.
esp1: u32,
/// Ring 1 32 bit stack pointer.
ss1: u32,
/// Ring 2 32 bit stack pointer.
esp2: u32,
/// Ring 2 32 bit stack pointer.
ss2: u32,
/// The CR3 control register 3.
cr3: u32,
/// 32 bit instruction pointer.
eip: u32,
/// 32 bit flags register.
eflags: u32,
/// 32 bit accumulator register.
eax: u32,
/// 32 bit counter register.
ecx: u32,
/// 32 bit data register.
edx: u32,
/// 32 bit base register.
ebx: u32,
/// 32 bit stack pointer register.
esp: u32,
/// 32 bit base pointer register.
ebp: u32,
/// 32 bit source register.
esi: u32,
/// 32 bit destination register.
edi: u32,
/// The extra segment.
es: u32,
/// The code segment.
cs: u32,
/// The stack segment.
ss: u32,
/// The data segment.
ds: u32,
/// A extra segment FS.
fs: u32,
/// A extra segment GS.
gs: u32,
/// The local descriptor table register.
ldtr: u32,
/// ?
trap: u16,
/// A pointer to a I/O port bitmap for the current task which specifies individual ports the program should have access to.
io_permissions_base_offset: u16,
};
///
/// Make a GDT entry.
///
/// Arguments:
/// IN access: u8 - The access bits for the descriptor.
/// IN flags: u4 - The flag bits for the descriptor.
///
/// Return:
/// A new GDT entry with the give access and flag bits set with the base at 0x00000000 and limit at 0xFFFFF.
///
fn makeEntry(base: u32, limit: u20, access: u8, flags: u4) GdtEntry {
return GdtEntry {
.limit_low = @truncate(u16, limit),
.base_low = @truncate(u24, base),
.access = access,
.limit_high = @truncate(u4, limit >> 16),
.flags = flags,
.base_high = @truncate(u8, base >> 24),
};
}
/// The GDT entry table of NUMBER_OF_ENTRIES entries.
var gdt_entries: [NUMBER_OF_ENTRIES]GdtEntry = []GdtEntry {
// Null descriptor
makeEntry(0, 0, 0, 0),
// Kernel Code
makeEntry(0, 0xFFFFF, KERNEL_SEGMENT | CODE_SEGMENT, IS_32_BIT | IS_LIMIT_4K_BIT),
// Kernel Data
makeEntry(0, 0xFFFFF, KERNEL_SEGMENT | DATA_SEGMENT, IS_32_BIT | IS_LIMIT_4K_BIT),
// User Code
makeEntry(0, 0xFFFFF, USER_SEGMENT | CODE_SEGMENT, IS_32_BIT | IS_LIMIT_4K_BIT),
// User Data
makeEntry(0, 0xFFFFF, USER_SEGMENT | DATA_SEGMENT, IS_32_BIT | IS_LIMIT_4K_BIT),
// Fill in TSS at runtime
makeEntry(0, 0, 0, 0),
};
/// The GDT pointer that the CPU is loaded with that contains the base address of the GDT and the
/// size.
const gdt_ptr: GdtPtr = GdtPtr {
.limit = TABLE_SIZE,
.base = &gdt_entries[0],
};
/// The task state segment entry.
var tss: TtsEntry = TtsEntry {
.prev_tss = 0,
.esp0 = undefined,
.ss0 = KERNEL_DATA_OFFSET,
.esp1 = 0,
.ss1 = 0,
.esp2 = 0,
.ss2 = 0,
.cr3 = 0,
.eip = 0,
.eflags = 0,
.eax = 0,
.ecx = 0,
.edx = 0,
.ebx = 0,
.esp = 0,
.ebp = 0,
.esi = 0,
.edi = 0,
.es = 0,
.cs = 0,
.ss = 0,
.ds = 0,
.fs = 0,
.gs = 0,
.ldtr = 0,
.trap = 0,
.io_permissions_base_offset = @sizeOf(TtsEntry),
};
///
/// Set the stack pointer in the TSS entry
///
/// Arguments:
/// IN esp0: u32 - The stack pointer
///
pub fn setTssStack(esp0: u32) void {
tss.esp0 = esp0;
}
///
/// Initialise the Global Descriptor table
///
pub fn init() void {
2019-06-22 11:00:57 +02:00
log.logInfo("Init gdt\n");
// Initiate TSS
gdt_entries[TSS_INDEX] = makeEntry(@ptrToInt(&tss), @sizeOf(TtsEntry) - 1, TSS_SEGMENT, 0);
// Load the GDT
arch.lgdt(&gdt_ptr);
// Load the TSS
arch.ltr();
2019-06-30 00:50:44 +02:00
log.logInfo("Done\n");
}