Implementation of an Operating System-Step 4-Segmentation
Hi everyone!
This is the 4th article in this series. Today I’m going to explain about the segmentation.
What is segmentation in OS
Segmentation in x86 means accessing the memory through segments. Segments are portions of the address space, possibly overlapping, specified by a base address and a limit.
To address a byte in segmented memory you use a 48-bit logical address: 16 bits that specifies the segment and 32-bits that specifies what offset within that segment you want.
The offset is added to the base address of the segment, and the resulting linear address is checked against the segment’s limit — see the figure below.
To enable segmentation you need to set up a table that describes each segment a segment descriptor table. In x86, there are two types of descriptor tables: the Global Descriptor Table (GDT) and Local Descriptor Tables (LDT).
An LDT is set up and managed by user-space processes, and all processes have their own LDT. LDTs can be used if a more complex segmentation model is desired — we won’t use it. The GDT is shared by everyone — it’s global.
Create a c file called memory_seg.c ;
#include “memory_seg.h”
#define SEGMENT_DESCRIPTOR_COUNT 3
#define SEGMENT_BASE 0
#define SEGMENT_LIMIT 0xFFFFF
#define SEGMENT_CODE_TYPE 0x9A
#define SEGMENT_DATA_TYPE 0x92
#define SEGMENT_FLAGS_PART 0x0C
static struct GDTDescriptor gdt_descriptors[SEGMENT_DESCRIPTOR_COUNT];
void segments_init_descriptor(int index, unsigned int base_address, unsigned int limit, unsigned char access_byte, unsigned char flags)
{
gdt_descriptors[index].base_low = base_address & 0xFFFF;
gdt_descriptors[index].base_middle = (base_address >> 16) & 0xFF;
gdt_descriptors[index].base_high = (base_address >> 24) & 0xFF;
gdt_descriptors[index].limit_low = limit & 0xFFFF;
gdt_descriptors[index].limit_and_flags = (limit >> 16) & 0xF;
gdt_descriptors[index].limit_and_flags |= (flags << 4) & 0xF0;
gdt_descriptors[index].access_byte = access_byte;
}
void segments_install_gdt()
{
gdt_descriptors[0].base_low = 0;
gdt_descriptors[0].base_middle = 0;
gdt_descriptors[0].base_high = 0;
gdt_descriptors[0].limit_low = 0;
gdt_descriptors[0].access_byte = 0;
gdt_descriptors[0].limit_and_flags = 0;
struct GDT* gdt_ptr = (struct GDT*)gdt_descriptors;
gdt_ptr->address = (unsigned int)gdt_descriptors;
gdt_ptr->size = (sizeof(struct GDTDescriptor) * SEGMENT_DESCRIPTOR_COUNT) — 1;
// See http://wiki.osdev.org/GDT_Tutorial
segments_init_descriptor(1, SEGMENT_BASE, SEGMENT_LIMIT, SEGMENT_CODE_TYPE, SEGMENT_FLAGS_PART);
segments_init_descriptor(2, SEGMENT_BASE, SEGMENT_LIMIT, SEGMENT_DATA_TYPE, SEGMENT_FLAGS_PART);
segments_load_gdt(*gdt_ptr);
segments_load_registers();
}
Now create the header file called memory_seg.h ;
#ifndef INCLUDE_MEMORY_SEGMENTS
#define INCLUDE_MEMORY_SEGMENTS
struct GDT
{
unsigned short size;
unsigned int address;
} __attribute__((packed));
struct GDTDescriptor
{
unsigned short limit_low;
unsigned short base_low;
unsigned char base_middle;
unsigned char access_byte;
unsigned char limit_and_flags;
unsigned char base_high;
} __attribute__((packed));
void segments_init_descriptor(int index, unsigned int base_address, unsigned int limit, unsigned char access_byte, unsigned char flags);
void segments_install_gdt();
// Wrappers around ASM.
void segments_load_gdt(struct GDT gdt);
void segments_load_registers();
#endif /* INCLUDE_MEMORY_SEGMENTS */
The Global Descriptor Table (GDT)
A GDT is an array of 8-byte segment descriptors. The first descriptor in the GDT is always a null descriptor and can never be used to access memory.
now create gdt.s file ;
global segments_load_gdt
global segments_load_registers
segments_load_gdt:
lgdt [esp + 4]
ret
segments_load_registers:
mov ax, 0x10
mov ds, ax ; 0x10 — an offset into GDT for the third (kernel data segment) record.
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax
jmp 0x08:flush_cs ; 0x08 — an offset into GDT for the second (kernel code segment) record.
flush_cs:
ret
Now insert following lines to Makefile as below :
OBJECTS = loader.o kmain.o io.o gdt.o memory_seg.o
CC = gcc
then edit kmain.c file as below ;
/* The C function */
#include “frame_buffer.h”
#include “serial_port.h”
#include “memory_seg.h”
int main(){
char ptr2[] = “Hello Asitha “;
serial_write(0x3F8, ptr2, 14);
fb_write(ptr2, 14);
segments_install_gdt();
}
Now type make run in terminal;
Hope you have successfully integrated segmentation to your OS and hope to catch you in the next article.
Thank you!