Kernel crashes when loading GDT segment selectors

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
LukeyTheKid
Posts: 17
Joined: Fri Jun 12, 2020 7:53 am
Libera.chat IRC: lukeythekid

Kernel crashes when loading GDT segment selectors

Post by LukeyTheKid »

I've been working on setting up my GDT, and though I've validated my entries several times, I seem to be crashing when trying to load any of the data segments. I think my actual entries seem to be correct; I've printed them out and the hex values look correct (and match examples I've seen online). The lgdt instruction is executed, but then my first load fails (e.g. "movw %ax, %ds"). I tried removing all of the data segment set calls, and only doing the longjump, and that does not crash.

I'm not super experienced with QEMU/Bochs so I don't have debug output yet - that's what I'm working on now. But in the meantime, I figured I would post in case I made some obvious boneheaded mistake (always a good bet). Any help would be much appreciated!

Here is my entry struct / gdt

Code: Select all

#include <stdint.h>

struct gdt_entry_struct {

  uint16_t limit_low;
  uint16_t base_low;
  uint8_t base_middle;
  uint8_t access;
  uint8_t lim_high_and_gran;
  uint8_t base_high;
} __attribute__((packed));
typedef struct gdt_entry_struct gdt_entry_t;

struct gdt_ptr_struct {
  uint16_t size;
  uint32_t offset;
} __attribute__((packed));
typedef struct gdt_ptr_struct gdt_ptr_t;

void init_descriptor_tables();
Implementation:

Code: Select all

#include "kernel/descriptor_table.h"


// For accessing assembly function
extern void gdt_flush(uint32_t);
// Internal function
static void init_gdt();
static void gdt_set_gate(int32_t idx, uint32_t base, uint32_t limit, uint8_t access, uint8_t gran);

// We want 5 entries for our GDT: null, kernel code, kernel data, user code, user data
gdt_entry_t gdt_entries[5];
gdt_ptr_t gdt_ptr;


// Implementation
void init_descriptor_tables(){
  init_gdt();
}

static void init_gdt(){

  // Set up the actual ptr
  // The offset is the linear address of the table itself, which means that paging applies.
  // The size is the size of the table subtracted by 1. This is because the maximum value
  // of size is 65535, while the GDT can be up to 65536 bytes (a maximum of 8192 entries). 
  gdt_ptr.size = (sizeof(gdt_entry_t) * 5) - 1;
  gdt_ptr.offset = (uint32_t)&gdt_entries[0];

  // Add entries to the table
  gdt_set_gate(0, 0x0, 0x0, 0x0, 0x0);             // Null entry
  gdt_set_gate(1, 0x0, 0xFFFFFFFF, 0x9A, 0xCF);  // Kernel Code
  gdt_set_gate(2, 0x0, 0xFFFFFFFF, 0x92, 0xCF);  // Kernel data
  gdt_set_gate(3, 0x0, 0xFFFFFFFF, 0xFA, 0xCF);  // User space code
  gdt_set_gate(4, 0x0, 0xFFFFFFFF, 0xF2, 0xCF);  // User space data

  gdt_flush((uint32_t)&gdt_ptr);
}

static void gdt_set_gate(int32_t idx, uint32_t base, uint32_t limit, uint8_t access, uint8_t gran) {

  // First 2 bytes of base, then third byte of base, then fourth
  gdt_entries[idx].base_low    = (base & 0xFFFF);
  gdt_entries[idx].base_middle = (base >> 16) & 0xFF;
  gdt_entries[idx].base_high   = (base >> 24) & 0xFF;

  // Limit low is first 2 bytes
  // Limit high is first 4 bits of third byte
  // Setting lower 4 bits of lim_high_and_gran (the limit high bits)
  gdt_entries[idx].limit_low         = (limit & 0xFFFF);
  gdt_entries[idx].lim_high_and_gran = (limit >> 16) & 0x0F;

  // Set high 4 bits of lim_high_and_gran
  // These are the actual granularity bits
  gdt_entries[idx].lim_high_and_gran |= (gran & 0xF0);

  // Just set access directly
  gdt_entries[idx].access = access;
}

The assembly to load the GDT

Code: Select all


	.section .text

	.globl gdt_flush
	.type gdt_flush, @function

gdt_flush:
	movl 4(%esp), %eax
	lgdt (%eax)

	movw 0x10, %ax
	movw %ax, %ds
	movw %ax, %es
	movw %ax, %fs
	movw %ax, %gs
	movw %ax, %ss
	jmp $0x08,$.flush
.flush:
	ret
And finally, the call to init_descriptor_tables() from kernel main

Code: Select all

#include <stdio.h>
 
#include <kernel/tty.h>
#include <kernel/descriptor_table.h>
 
void kernel_main(void) {
	terminal_initialize();
	printf("Hello, kernel World!\n");
	init_descriptor_tables();
	printf("Initialized descriptor tables\n");
}
If it's easier to read this on GitHub, here's the repo: https://github.com/LukeRobbins2112/myos
Octocontrabass
Member
Member
Posts: 5574
Joined: Mon Mar 25, 2013 7:01 pm

Re: Kernel crashes when loading GDT segment selectors

Post by Octocontrabass »

Code: Select all

	movw 0x10, %ax
In AT&T syntax, immediate values are prefixed with $. Without the prefix, that is a memory reference. I would guess the value in memory at that address is not a valid segment selector.
ITchimp
Member
Member
Posts: 134
Joined: Sat Aug 18, 2018 8:44 pm

Re: Kernel crashes when loading GDT segment selectors

Post by ITchimp »

did you run it on bochs? if so can you post bochsout.txt?
LukeyTheKid
Posts: 17
Joined: Fri Jun 12, 2020 7:53 am
Libera.chat IRC: lukeythekid

Re: Kernel crashes when loading GDT segment selectors

Post by LukeyTheKid »

Octocontrabass wrote:

Code: Select all

	movw 0x10, %ax
In AT&T syntax, immediate values are prefixed with $. Without the prefix, that is a memory reference. I would guess the value in memory at that address is not a valid segment selector.
Yep, that's it alright, thanks for pointing that out - boneheaded mistake indeed. Fixed up the immediate value and it's working.
ITChimp, thanks for your reply. I did run it on bochs but I'm still learning the ropes - reading through the bochsout.txt, I see the garbage %eax value.
Post Reply