Implementing operating system #4

Last week we discussed taking outputs and today we are going to discuss the segmentation part.

Segmentation means accessing the memory through segments. Segments are portions of the address space, possibly overlapping, specified by a base address and a limit.

A 48-bit logical address is used to address a byte in segmented memory: The segment is specified by 16 bits, while the offset inside that segment is specified by 32 bits. The offset is added to the segment’s base address, and the resulting linear address is compared to the segment’s limit.

Translation of logical address to linear address

We have to set up a table that describes each segment to enable segmentation. It is called a segment descriptor table. There are two types of descriptor tables.

  1. Global descriptor tables (GDT)
  2. Local descriptor tables (LDT)

When accessing memory, most of the time there is no need to specify the segment to use directly. Six 16-bit segment registers are available on the processor.

register ss - used whenever accessing the stack (through the stack pointer esp),

register cs -the code segment register and specifies the segment to use when fetching instructions.

register ds -is used for other data accesses.

registers es, gs, fs -OS is free to use these whenever it wants.

For specific portions of memory, the GDT provides base access privileges. We can utilize a GDT entry to generate segment violation exceptions, which allow the kernel to terminate a process that isn’t supposed to be running. To do this, most current operating systems use a memory mode known as “paging.”

GRUB has already installed a GDT for you. If we overwrite that, it will cause a machine reset. So we should set up our own GDT in a place in memory that we know and can access. This involves building our own GDT, telling the processor where it is, and finally loading the processor’s CS, DS, ES, FS, and GS registers with our new entries.

The GDT is a 64-bit long list of entries. These entries specify where the authorized region will begin in memory, as well as the region’s limit and the access privileges associated with it. The NULL descriptor is the first entry in your GDT, entry 0. This is a standard rule. Meanwhile, a General Protection fault will occur if any segment registers are set to 0.

In our tutorial kernel, we will create a GDT with only 3 entries. In our tutorial kernel, we will create a GDT with only 3 entries.

First you should create a header file called memory_segment.h .Inside the file store the following code.

memory_segments.h

Now you have to create a c file and include the above header file.

So you have to create a file called memory_segments.c .Then store the following code segment inside the file.

memroy_segment.c

Now we should create a source file in order to load the Global descriptor table. Use the following code to the source file.

gdt.s

Now that our GDT loading infrastructure is in place, and we compile and link it into our kernel, we need to call,

segments_install_gdt();

in order to actually do our work!

Open ‘kmain.c’ and add ‘segments_install_gdt();’ as the very first line in your main() function. The GDT needs to be one of the very first things that you initialize because as you learned from this section of the tutorial, it’s very important. You can now compile, link, and send our kernel to our floppy disk to test it out. You won’t see any visible changes on the screen: this is an internal change.

That’s it for this article! Have a nice day!!

Software engineering undergraduate