GDT and Interrupts

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
Geometrian
Member
Member
Posts: 77
Joined: Tue Nov 20, 2012 4:45 pm
Contact:

GDT and Interrupts

Post by Geometrian »

Hi,

I have always struggled with the GDT (and IDT). My current understanding:

GRUB boots into 32-bit protected mode, and the documentation seems to imply that interrupts aren't enabled. Just to be safe, I disable them before doing anything with the GDT or IDT.

The GDT describes where certain things are. For my purposes right now, it holds a null record, a kernel code record, a kernel data record, a user code record, and a user data record. All code and data records start at 0x00000000u and are sized 0xFFFFFFFFu, so they are a flat memory model. For now, no IDT is used.

The exact format of the access byte for each GDT entry is problematic, specifically bit 3 (fourth). At here and here the bit is described so that 1 is code/data selector, 0 is system (TSS, LDT, or Gate). However, elsewhere on that second page and also on this one, the bit is said to be 1 for code selector, 0 for data selector. These are contradictory. For my code, I assumed that the 1=code/data, 0=system information is correct. It would be great if someone could clarify.

Since GDT entries are a pain, I made a C++ class that handles it. See here. The five entries are set up and loaded by:

Code: Select all

static EntryGDT gdt_entries[5];
void load_gdt(void) {
	uint32_t base  = (uint32_t)(&gdt_entries); //The linear address of the first gdt_entry_t struct.
	uint16_t limit = sizeof(EntryGDT)*5 - 1;   //The upper 16 bits of all selector limits.  Size of table minus 1.  sizeof(EntryGDT) is 8 (I checked)

	EntryGDT::construct(gdt_entries,   0x00000000u,         0u, EntryGDT::Access::AccessByte::              get_null()); //Null segment
	EntryGDT::construct(gdt_entries+1, 0x00000000u,0xFFFFFFFFu, EntryGDT::Access::AccessByte::get_selector_datacode(0)); //Kernel code segment
	EntryGDT::construct(gdt_entries+2, 0x00000000u,0xFFFFFFFFu, EntryGDT::Access::AccessByte::get_selector_datacode(0)); //Kernel data segment
	EntryGDT::construct(gdt_entries+3, 0x00000000u,0xFFFFFFFFu, EntryGDT::Access::AccessByte::get_selector_datacode(3)); //User code segment
	EntryGDT::construct(gdt_entries+4, 0x00000000u,0xFFFFFFFFu, EntryGDT::Access::AccessByte::get_selector_datacode(3)); //User data segment

	gdt_lgdt(base,limit);
}

Code: Select all

[GLOBAL gdt_lgdt]

gdt_ptr:
	DW 0 ;For limit storage
	DD 0 ;For base storage

gdt_lgdt:
	mov  eax, [esp + 4]
	mov  [gdt_ptr + 2], eax
	mov  ax, [esp + 8]
	mov  [gdt_ptr], ax

	lgdt  [gdt_ptr]

	ret
Then, one needs to reload the segment registers, as mentioned on the GDT Tutorial:

Code: Select all

[GLOBAL reload_segments]

;See http://wiki.osdev.org/Segmentation
;The 0x08 is the offset of the kernel code selector in the GDT (the second 8-byte entry, following the null-entry)
;The 0x10 is the offset of the kernel data selector in the GDT (the third 8-byte entry, following the code selector at 0x08)
reload_segments:
	; Reload CS register containing code selector:
	jmp  0x08:reload_CS
reload_CS:
	; Reload data segment registers:
	mov  ax, 0x10
	mov  ds, ax
	mov  es, ax
	mov  fs, ax
	mov  gs, ax
	mov  ss, ax

	ret
However, VirtualBox hangs after calling "reload_segments();" and Bochs produces:
00018064222i[BIOS ] Booting from 07c0:0000
00018072558i[MEM0 ] allocate_block: block=0x1 used 0x3 of 0x20
00034919314i[MEM0 ] allocate_block: block=0x1e used 0x4 of 0x20
00055800305e[CPU0 ] branch_far64: RIP > limit
00055800305e[CPU0 ] interrupt(): gate descriptor is not valid sys seg (vector=0x0d)
00055800305e[CPU0 ] interrupt(): gate descriptor is not valid sys seg (vector=0x08)
00055800305i[CPU0 ] CPU is in protected mode (active)
00055800305i[CPU0 ] CS.mode = 32 bit
00055800305i[CPU0 ] SS.mode = 32 bit
00055800305i[CPU0 ] EFER = 0x00000000
00055800305i[CPU0 ] | EAX=00000000 EBX=00184120 ECX=00000002 EDX=00000003
00055800305i[CPU0 ] | ESP=0018410c EBP=00000000 ESI=00010000 EDI=00000000
00055800305i[CPU0 ] | IOPL=0 ID vip vif ac vm RF nt of df if tf sf zf AF pf cf
00055800305i[CPU0 ] | SEG sltr(index|ti|rpl) base limit G D
00055800305i[CPU0 ] | CS:0010( 0002| 0| 0) 00000000 ffffffff 1 1
00055800305i[CPU0 ] | DS:0018( 0003| 0| 0) 00000000 ffffffff 1 1
00055800305i[CPU0 ] | SS:0018( 0003| 0| 0) 00000000 ffffffff 1 1
00055800305i[CPU0 ] | ES:0018( 0003| 0| 0) 00000000 ffffffff 1 1
00055800305i[CPU0 ] | FS:0018( 0003| 0| 0) 00000000 ffffffff 1 1
00055800305i[CPU0 ] | GS:0018( 0003| 0| 0) 00000000 ffffffff 1 1
00055800305i[CPU0 ] | EIP=001005a4 (001005a4)
00055800305i[CPU0 ] | CR0=0x60000011 CR2=0x00000000
00055800305i[CPU0 ] | CR3=0x00000000 CR4=0x00000000
00055800305i[CPU0 ] 0x00000000001005a4>> jmp far 0008:001005ab : EAAB0510000800
00055800305e[CPU0 ] exception(): 3rd (13) exception with no resolution, shutdown status is 00h, resetting
00055800305i[SYS ] bx_pc_system_c::Reset(HARDWARE) called
00055800305i[CPU0 ] cpu hardware reset
I don't know what the "branch_far64" line means, but I find the interrupt lines immediately after bizarre, since interrupts weren't enabled! What's going on? What are the problems with my understanding?

Thanks,
Last edited by Geometrian on Wed Jan 31, 2024 5:37 pm, edited 1 time in total.
gerryg400
Member
Member
Posts: 1801
Joined: Thu Mar 25, 2010 11:26 pm
Location: Melbourne, Australia

Re: GDT and Interrupts

Post by gerryg400 »

You haven't shown enough code to allow someone to debug your problem.
If a trainstation is where trains stop, what is a workstation ?
Geometrian
Member
Member
Posts: 77
Joined: Tue Nov 20, 2012 4:45 pm
Contact:

Re: GDT and Interrupts

Post by Geometrian »

That is all the code. After GRUB, it loads the GDT, reloads the segments, and then fails, all with that code there.

If you want every last line of things that aren't enabled, the print statements, the #defines, the linker scripts, etc., I've just updated my OS's SVN (in my signature).
Geometrian
Member
Member
Posts: 77
Joined: Tue Nov 20, 2012 4:45 pm
Contact:

Re: GDT and Interrupts

Post by Geometrian »

Geometrian wrote:The exact format of the access byte for each GDT entry is problematic, specifically bit 3 (fourth). At here and here the bit is described so that 1 is code/data selector, 0 is system (TSS, LDT, or Gate). However, elsewhere on that second page and also on this one, the bit is said to be 1 for code selector, 0 for data selector. These are contradictory. For my code, I assumed that the 1=code/data, 0=system information is correct. It would be great if someone could clarify.
Another resource also suggested the 1=code, 0=data version. Changing the code to reflect that (specifically making the data segment's bit be 0) seems to fix the problem.
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: GDT and Interrupts

Post by Combuster »

Code: Select all

   get_selector_datacode(0)); //Kernel code segment
   get_selector_datacode(0)); //Kernel data segment
Code and data entries are not supposed to be identical.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
Post Reply