Added Global Descriptor Table(GDT)...not working yet(as always)
This commit is contained in:
parent
222ab282d9
commit
ccd31d8fab
@ -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
15
kernel/cpu/gdt.asm
Normal 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
|
@ -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
104
kernel/drivers/gdt.c
Normal 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
138
kernel/drivers/gdt.h
Normal 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
|
@ -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() {
|
||||||
|
@ -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!");
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user