2019-05-31 08:41:28 +02:00
// Zig version: 0.4.0
const arch = @import ( " arch.zig " ) ;
2019-06-22 11:00:57 +02:00
const log = @import ( " ../../log.zig " ) ;
2019-05-31 08:41:28 +02:00
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
2019-06-02 18:14:02 +02:00
const EXECUTABLE_BIT = 0x08 ; // 00001000
const DESCRIPTOR_BIT = 0x10 ; // 00010000
2019-05-31 08:41:28 +02:00
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 ,
} ;
2019-06-02 18:14:02 +02:00
///
/// The TSS entry structure
///
2019-05-31 08:41:28 +02:00
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 ) ,
} ;
2019-06-02 18:14:02 +02:00
/// The GDT pointer that the CPU is loaded with that contains the base address of the GDT and the
/// size.
2019-05-31 08:41:28 +02:00
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 ) ,
} ;
2019-06-02 18:14:02 +02:00
///
/// Set the stack pointer in the TSS entry
///
/// Arguments:
/// IN esp0: u32 - The stack pointer
///
2019-05-31 08:41:28 +02:00
pub fn setTssStack ( esp0 : u32 ) void {
tss . esp0 = esp0 ;
}
2019-06-02 18:14:02 +02:00
///
/// Initialise the Global Descriptor table
///
2019-05-31 08:41:28 +02:00
pub fn init ( ) void {
2019-06-22 11:00:57 +02:00
log . logInfo ( " Init gdt \n " ) ;
2019-05-31 08:41:28 +02:00
// 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 " ) ;
2019-05-31 08:41:28 +02:00
}