Added Global Descriptor Table(GDT)...not working yet(as always)

This commit is contained in:
ice-bit 2019-07-04 12:49:53 +02:00
parent 222ab282d9
commit ccd31d8fab
7 changed files with 264 additions and 5 deletions

View File

@ -1,4 +1,4 @@
OBJS = multiboot.asm.o kernel_loader.asm.o ports.asm.o OBJS = multiboot.asm.o kernel_loader.asm.o ports.asm.o gdt.asm.o
ASM = nasm ASM = nasm
ASMFLAGS = -f elf ASMFLAGS = -f elf

15
kernel/cpu/gdt.asm Normal file
View File

@ -0,0 +1,15 @@
global gdt_flush ; for drivers/gdt.c
section .text
gdt_flush:
mov eax, [esp+4] ; get address of gdt_ptr_t
lgdt [eax] ; Load GDT
mov ax, 0x10 ; offset in the GDT of the data segment
mov ds, ax ; Load data segment selectors
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
jmp 0x08:.flush ; offset in the GDT of the code segment
.flush:
ret

View File

@ -1,4 +1,4 @@
OBJS = tty.o OBJS = tty.o gdt.o
CC = i686-elf-gcc # cross-compiler CC = i686-elf-gcc # cross-compiler
CFLAGS = -m32 -fno-stack-protector -ffreestanding -Wall -Wextra -Werror -g -c CFLAGS = -m32 -fno-stack-protector -ffreestanding -Wall -Wextra -Werror -g -c

104
kernel/drivers/gdt.c Normal file
View File

@ -0,0 +1,104 @@
#include "gdt.h"
#include "ports.h"
// Internal method
extern void gdt_flush(uint32_t); // Defined on cpu/gdt.asm
static gdt_entry_t construct_null_entry();
static gdt_entry_t construct_entry(gdt_access_t access);
static void init_gdt();
gdt_entry_t gdt_entries[5];
gdt_ptr_t gdt_ptr;
// This method will be called by the kernel
void gdt_setup() {
init_gdt();
}
static void init_gdt() {
gdt_ptr.limit = (sizeof(gdt_entry_t) * 5) - 1;
gdt_ptr.base = (uint32_t)&gdt_entries;
gdt_entry_t null_segment = construct_null_entry();
gdt_entry_t kernel_mode_code_segment = construct_entry(
(struct gdt_access){
.type = GDT_CODE_TYPE_EXEC_READ,
.dt = GDT_CODE_AND_DATA_DESCRIPTOR,
.dpl = GDT_RING0,
.p = GDT_SEGMENT_PRESENT
}
);
gdt_entry_t kernel_mode_data_segment = construct_entry(
(struct gdt_access){
.type = GDT_DATA_TYPE_READ_WRITE,
.dt = GDT_CODE_AND_DATA_DESCRIPTOR,
.dpl = GDT_RING0,
.p = GDT_SEGMENT_PRESENT
}
);
gdt_entry_t user_mode_code_segment = construct_entry(
(struct gdt_access){
.type = GDT_CODE_TYPE_EXEC_READ,
.dt = GDT_CODE_AND_DATA_DESCRIPTOR,
.dpl = GDT_RING3,
.p = GDT_SEGMENT_PRESENT
}
);
gdt_entry_t user_mode_data_segment = construct_entry(
(struct gdt_access){
.type = GDT_DATA_TYPE_READ_WRITE,
.dt = GDT_CODE_AND_DATA_DESCRIPTOR,
.dpl = GDT_RING3,
.p = GDT_SEGMENT_PRESENT
}
);
gdt_entries[0] = null_segment;
gdt_entries[1] = kernel_mode_code_segment;
gdt_entries[2] = kernel_mode_data_segment;
gdt_entries[3] = user_mode_code_segment;
gdt_entries[4] = user_mode_data_segment;
gdt_flush((uint32_t)&gdt_ptr);
}
static gdt_entry_t construct_entry(gdt_access_t access) {
gdt_entry_t entry = (struct gdt_entry_struct) {
.base_low = GDT_BASE & 0xFFFF,
.base_middle = (GDT_BASE >> 16) % 0xFF,
.base_high = (GDT_BASE >> 24) & 0xFF,
.limit_low = (GDT_LIMIT & 0xFFFF),
.access = access,
.granularity = (struct gdt_granularity) {
.g = GDT_GRANULARITY_4K,
.d = GDT_OPERAND_SIZE_32,
.zero = 0,
.seglen = GDT_SEGMENT_LENGTH
}
};
return entry;
}
// The only difference is in the access
static gdt_entry_t construct_null_entry() {
gdt_entry_t null_entry = (struct gdt_entry_struct) {
.base_low = 0,
.base_middle = 0,
.base_high = 0,
.limit_low = 0,
.access = (struct gdt_access) {
.p = 0,
.dpl = 0,
.dt = 0,
.type = 0
},
.granularity = (struct gdt_granularity) {
.g = 0,
.d = 0,
.zero = 0,
.seglen = 0
}
};
return null_entry;
}

138
kernel/drivers/gdt.h Normal file
View File

@ -0,0 +1,138 @@
/**************************************
* iceOS Kernel *
* Developed by Marco 'icebit' Cetica *
* (c) 2019 *
* Released under GPLv3 *
* https://github.com/ice-bit/iceOS *
***************************************/
#ifndef _GDT_H_
#define _GDT_H_
/*
* First a bit of theory:
* GDT(Global Descriptor Table) is a complex data structure used in x86 systems
* to define memory areas.
* Technically speaking GDT is formed by an array of 8-bytes segment descriptors,
* the first descriptor of the GDT is always a NULL one and CANNNOT be used to allocate
* memory; so we need at least two descriptors(plus the null descriptor) to successfully allocate
* data on our memory.
* In x86 architecture there're two methods to provide virtual memory: Segmentation and Paging:
* With the first one every memory access is done within his own segment, so each address(of a process)
* is added to the segment's base address and checked against the segment's length.
* With paging, however, the address space is split into blocks(usually of 4KiB) called pages.
* Each page can be mapped to physical memory or it can be unmapped(to create virtual memory).
* Segmentation is a built-in functionality of x86 architecture, so to get around this we need to
* define our own GDT. A cool thing segmentation can do for us is to set Ring Level:
* a privilege level to allow our process to run in a 'unprivileged' mode(called user mode, ring 3)
* and to allow drivers(or kernel related stuff) to run in a 'supervisor-mode'(called kernel mode, ring 0).
* Usually bootloader(such as GRUB) sets up GDT for us, the problem is that we cannot known where it is or
* if it is has been overwritten during some other tasks. So it's a good idea to implement
* a new GDT ourself.
*/
#include <stdint.h>
/* Those values were taken from Intel's developer manual */
// GDT fields
#define GDT_BASE 0x00000000
#define GDT_LIMIT 0xFFFFFFFF
// GDT granularity
#define GDT_SEGMENT_LENGTH 0xf
#define GDT_OPERAND_SIZE_16 0
#define GDT_OPERAND_SIZE_32 1
#define GDT_GRANULARITY_1K 0
#define GDT_GRANULARITY_4K 1
// GDT access type fields
#define GDT_DATA_TYPE_READ_ONLY 0x0
#define GDT_DATA_TYPE_READ_ONLY_ACCESSED 0x1
#define GDT_DATA_TYPE_READ_WRITE 0x2
#define GDT_DATA_TYPE_READ_WRITE_ACCESSED 0x3
#define GDT_DATA_TYPE_READ_ONLY_EXPAND_DOWN 0x4
#define GDT_DATA_TYPE_READ_ONLY_EXPAND_DOWN_ACCESSED 0x5
#define GDT_DATA_TYPE_EXEC_ONLY 0x8
#define GDT_CODE_TYPE_EXEC_ONLY_ACCESSED 0x9
#define GDT_CODE_TYPE_EXEC_READ 0xA
#define GDT_CODE_TYPE_EXEC_READ_ACCESSED 0xB
#define GDT_CODE_TYPE_EXEC_CONFORMING 0xC
#define GDT_CODE_TYPE_EXEC_CONFORMING_ACCESSED 0xD
#define GDT_CODE_TYPE_EXEC_READ_CONFORMING 0xE
#define GDT_CODE_TYPE_EXEC_READ_CONFORMING_ACCESSED 0xF
// Descriptor type fields
#define GDT_SYSTEM_DESCRIPTOR 0
#define GDT_CODE_AND_DATA_DESCRIPTOR 1
// GDT Ring number
#define GDT_RING0 0
#define GDT_RING1 1
#define GDT_RING2 2
#define GDT_RING3 3
// 'Present' field
#define GDT_SEGMENT_NOT_PRESENT 0
#define GDT_SEGMENT_PRESENT 1
/* Global Descriptor Table (GDT) implementation */
/* gdt_access is used to access portion of the GDT
* | 0 - 3 | 4 | 3 - 6 | 7 |
* | Type | DT | DPL | P |
* Type: Which type
* DT: descriptor type
* DPL: Kernel ring(0-3)
* P: is segment present? (bool)
*/
struct gdt_access {
uint8_t type:4; // 4 Byte
uint8_t dt:1; // 1 Byte
uint8_t dpl:2; // 2 Byte
uint8_t p:1; // 1 Byte
}__attribute__((packed));
typedef struct gdt_access gdt_access_t;
/* gdt_granularity is used to get portion of GDT entry
* | 0 - 3 | 4 | 5 | 6 | 7 |
* | seglen | 0 | D | G |
* seglen: segment length
* 0: Always zero
* D: Operand size (0 = 16 bit, 1 = 32 bit)
* G: granularity (0 = 1 Byte, 1 = 4KiB)
*/
struct gdt_granularity {
uint8_t seglen:4;
uint8_t zero:2;
uint8_t d:1;
uint8_t g:1;
}__attribute__((packed));
typedef struct gdt_granularity gdt_gran_t;
/* gdt_entry_struct contains the value of a single GDT entry
* Each slice is 64 bits.
* | 0 - 15 | 16 - 31 | 32 - 39 | 40 - 47 | 48 - 55 | 56 - 63 |
* | lim low| base low|base mid | access | gran | base hg |
* lim low: Lower 16 bits of the limit
* base low: Lower 16 bits of the base
* base mid: Next 8 bits of the base
* access: access flag, e.g. which ring this segment can be used in.
* gran.
*/
struct gdt_entry_struct {
uint16_t limit_low;
uint16_t base_low;
uint8_t base_middle;
uint8_t base_high;
gdt_access_t access;
gdt_gran_t granularity;
}__attribute__((packed));
typedef struct gdt_entry_struct gdt_entry_t;
/* Also we have to define a pointer to the data structure
* This is needed to locate it later */
struct gdt_ptr {
uint16_t limit;
uint16_t base;
}__attribute__((packed));
typedef struct gdt_ptr gdt_ptr_t;
/* GDT Kernel API */
void gdt_setup();
#endif

View File

@ -12,7 +12,7 @@ static uint32_t fb_row = 0; // Y
void write_cell(int16_t i, uint8_t c, uint8_t fg, uint8_t bg) { void write_cell(int16_t i, uint8_t c, uint8_t fg, uint8_t bg) {
uint8_t *fb = VGA_PTR; uint8_t *fb = VGA_PTR;
fb[i*2] = c; fb[i*2] = c;
fb[i*2 + 1] = ((bg & 0x0F) << 4) | (fg | 0x0F); fb[i*2 + 1] = ((bg & 0x0F) << 4) | (fg & 0x0F);
} }
void move_cursor(uint16_t pos) { void move_cursor(uint16_t pos) {
@ -71,7 +71,7 @@ void kprint(uint8_t *buf) {
void init_prompt() { void init_prompt() {
uint8_t *prompt = (uint8_t*)"\nuser@iceOS-$ "; uint8_t *prompt = (uint8_t*)"\nuser@iceOS-$ ";
kprint_c(prompt, strlen(prompt), GREEN, BLACK); kprint_c(prompt, strlen(prompt), LIGHT_GREEN, BLACK);
} }
void clear_prompt() { void clear_prompt() {

View File

@ -1,9 +1,11 @@
#include "drivers/tty.h" #include "drivers/tty.h"
#include "drivers/gdt.h"
#include "libc/stdio.h" #include "libc/stdio.h"
void kernel_main() { void kernel_main() {
clear_prompt(); clear_prompt();
init_prompt(); init_prompt(); // Initialize frame buffer
gdt_setup(); // Setup Global Descriptor Table
puts("Hello World!"); puts("Hello World!");
} }