How to make a GDT?

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.
nullplan
Member
Member
Posts: 1789
Joined: Wed Aug 30, 2017 8:24 am

Re: How to make a GDT?

Post by nullplan »

zap8600 wrote:I believe that it is pointing to the 16-bit segment. It should instead point to 0x30, which I believe is the 64-bit segment.
Without the GDT, nobody will be able to tell you. However, segment 0x10 refers to index 2, and 0x30 refers to index 6 (you just have to divide by eight).
zap8600 wrote:If this is the case though, I believe the problem should be fixed by replacing the 5 with 7.
You are loading the TR with 0x2B, which refers to index 5. Your problem is that segment 5 is not initialized correctly, You set the limit (one byte too high, because the limit for some reason is one less than the size of the segment, but that is immaterial here) and the base, but never the segment type. This is why you should read the CPU manuals which detail the structure of the thing.

In 64-bit mode, the TSS is a long segment, i.e. it uses two consecutive GDT entries for the whole base pointer. I have the GDT declared as just an array of 64-bit numbers, and therefore I can just do this initialization:

Code: Select all

static void init_gdt(uint64_t *gdt, const struct tss* tss)
{
    uint64_t tssp = (uint64_t)tss;
    gdt[KCODE_DESC] = 0x00af9a000000ffff;
    gdt[KDATA_DESC] = 0x00af92000000ffff;
    gdt[UDATA_DESC] = 0x00aff2000000ffff;
    gdt[UCODE_DESC] = 0x00affa000000ffff;
    gdt[TSS_DESC] = (tssp << 16) & 0xffffff0000 | (tssp << 32) & 0xff00000000000000 | sizeof (struct tss) - 1 | (uint64_t)0x89 << 40;
    gdt[TSSU_DESC] = tssp >> 32;
}
There. Done. That is all you need. You don't need anything more. Never understood why people love to complicate this stuff with tons of structures.
Carpe diem!
zap8600
Member
Member
Posts: 195
Joined: Tue Nov 02, 2021 11:26 am
Libera.chat IRC: zap8600

Re: How to make a GDT?

Post by zap8600 »

nullplan wrote: You are loading the TR with 0x2B, which refers to index 5. Your problem is that segment 5 is not initialized correctly, You set the limit (one byte too high, because the limit for some reason is one less than the size of the segment, but that is immaterial here) and the base, but never the segment type. This is why you should read the CPU manuals which detail the structure of the thing.

In 64-bit mode, the TSS is a long segment, i.e. it uses two consecutive GDT entries for the whole base pointer. I have the GDT declared as just an array of 64-bit numbers, and therefore I can just do this initialization:

Code: Select all

static void init_gdt(uint64_t *gdt, const struct tss* tss)
{
    uint64_t tssp = (uint64_t)tss;
    gdt[KCODE_DESC] = 0x00af9a000000ffff;
    gdt[KDATA_DESC] = 0x00af92000000ffff;
    gdt[UDATA_DESC] = 0x00aff2000000ffff;
    gdt[UCODE_DESC] = 0x00affa000000ffff;
    gdt[TSS_DESC] = (tssp << 16) & 0xffffff0000 | (tssp << 32) & 0xff00000000000000 | sizeof (struct tss) - 1 | (uint64_t)0x89 << 40;
    gdt[TSSU_DESC] = tssp >> 32;
}
There. Done. That is all you need. You don't need anything more. Never understood why people love to complicate this stuff with tons of structures.
So what does the TR need to be in order to refer to segment 7?
nullplan
Member
Member
Posts: 1789
Joined: Wed Aug 30, 2017 8:24 am

Re: How to make a GDT?

Post by nullplan »

zap8600 wrote:So what does the TR need to be in order to refer to segment 7?
Go the other way around and multiply by eight. Seven times eight is fifty-six, or 0x38 in hexadecimal. You can add another three to retain the original code's RPL of three, but it really doesn't matter unless you were using hardware task switching. Which you are not, seeing as you are in 64-bit mode, which has no support for such.
Carpe diem!
zap8600
Member
Member
Posts: 195
Joined: Tue Nov 02, 2021 11:26 am
Libera.chat IRC: zap8600

Re: How to make a GDT?

Post by zap8600 »

nullplan wrote: Go the other way around and multiply by eight. Seven times eight is fifty-six, or 0x38 in hexadecimal. You can add another three to retain the original code's RPL of three, but it really doesn't matter unless you were using hardware task switching. Which you are not, seeing as you are in 64-bit mode, which has no support for such.
Thank you. Finally, my GDT works, and Limine's terminal function works as well. I also got interrupts working. Although, the interrupts aren't working properly (I think). When running asm volatile ("int $0x3"); in the kernel, it prints the interrupt message three times. I believe that it may have to do with my ISR handler (which is very simple). Here is my ISR handler.

Code: Select all

void isr_handler()
{
    terminal_write("Received ISR!\n");
}
zap8600
Member
Member
Posts: 195
Joined: Tue Nov 02, 2021 11:26 am
Libera.chat IRC: zap8600

Re: How to make a GDT?

Post by zap8600 »

I have updated my ISR handler, and now I know that something is going wrong. When running asm volatile ("int $0x3");, it prints Received ISR: Breakpoint, which is what it is supposed to do, but then it prints Received ISR: General Protection Fault, which it shouldn't do. It tries to print Received ISR: General Protection Fault again, but it replaces characters in the string with random junk data. Nothing is printed to the screen after that, and I'm not sure if my ISR handler returns or not. Here is my ISR handler.

Code: Select all

void isr_handler(registers_t r)
{
    terminal_writestring("Received ISR: ");
    terminal_writestring(exception_messages[r.int_no]);
    terminal_writestring("\n");
}
What is going wrong?
Octocontrabass
Member
Member
Posts: 5560
Joined: Mon Mar 25, 2013 7:01 pm

Re: How to make a GDT?

Post by Octocontrabass »

zap8600 wrote:

Code: Select all

void isr_handler(registers_t r)
Are you sure that's the correct type for the argument to this function?
zap8600 wrote:What is going wrong?
What is the address of the instruction causing the exception? What is the exception error code? What are the contents of the CPU registers when it happens?

You can get all of this information by printing the contents of your registers struct or by checking the QEMU "-d int" log.
zap8600
Member
Member
Posts: 195
Joined: Tue Nov 02, 2021 11:26 am
Libera.chat IRC: zap8600

Re: How to make a GDT?

Post by zap8600 »

Octocontrabass wrote: Are you sure that's the correct type for the argument to this function?
I believe so. If not, what would it be? Here is registers_t.

Code: Select all

typedef struct {
	uintptr_t r15, r14, r13, r12;
	uintptr_t r11, r10, r9, r8;
	uintptr_t rbp, rdi, rsi, rdx, rcx, rbx, rax;

	uintptr_t int_no, err_code;

	uintptr_t rip, cs, rflags, rsp, ss;
} registers_t;
Octocontrabass wrote: What is the address of the instruction causing the exception? What is the exception error code? What are the contents of the CPU registers when it happens?

You can get all of this information by printing the contents of your registers struct or by checking the QEMU "-d int" log.
Well, it appears to repeat the same thing over and over. It repeats it so fast that I can't get the entire log. Here it is.

Code: Select all

check_exception old: 0xffffffff new 0xd
   546: v=0d e=e190 i=0 cpl=0 IP=0028:ffffffff800002a9 pc=ffffffff800002a9 SP=0030:0000000007f80088 env->regs[R_EAX]=0000000007f80088
RAX=0000000007f80088 RBX=0000000000000006 RCX=0000000000000028 RDX=ffffffff800002a9
RSI=000000000000e190 RDI=000000000000000d RBP=0000000007f80088 RSP=0000000007f80088
R8 =0000000000000006 R9 =0000000000000028 R10=ffffffff800002a9 R11=000000000000e190
R12=000000000000000e R13=0000000007f80088 R14=0000000000000000 R15=0000000000000028
RIP=ffffffff800002a9 RFL=00000006 [-----P-] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0030 0000000000000000 ffffffff 00af9300 DPL=0 DS   [-WA]
CS =0028 0000000000000000 ffffffff 00af9a00 DPL=0 CS64 [-R-]
SS =0030 0000000000000000 ffffffff 00af9300 DPL=0 DS   [-WA]
DS =0030 0000000000000000 ffffffff 00af9300 DPL=0 DS   [-WA]
FS =0030 0000000000000000 00000000 00009300 DPL=0 DS   [-WA]
GS =0030 0000000000000000 00000000 00009300 DPL=0 DS   [-WA]
LDT=0000 0000000000000000 00000000 00008200 DPL=0 LDT
TR =0038 ffffffff80400e92 00000068 0000e900 DPL=3 TSS64-avl
GDT=     ffffffff80400e40 00000047
IDT=     ffffffff80406740 00001000
CR0=80010011 CR2=0000000000000000 CR3=0000000007617000 CR4=00000020
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000 
DR6=00000000ffff0ff0 DR7=0000000000000400
CCS=0000000000000010 CCD=0000000007f80088 CCO=ADDQ
EFER=0000000000000d00
check_exception old: 0xffffffff new 0xd
Octocontrabass
Member
Member
Posts: 5560
Joined: Mon Mar 25, 2013 7:01 pm

Re: How to make a GDT?

Post by Octocontrabass »

zap8600 wrote:If not, what would it be?
Usually it would be "registers_t *" instead of "registers_t". Otherwise, you can't modify the stack frame before returning, and the compiler may clobber your stack frame.
zap8600 wrote:

Code: Select all

RIP=ffffffff800002a9
What is the instruction at this address? Which function contains that instruction?
zap8600
Member
Member
Posts: 195
Joined: Tue Nov 02, 2021 11:26 am
Libera.chat IRC: zap8600

Re: How to make a GDT?

Post by zap8600 »

Octocontrabass wrote: Usually it would be "registers_t *" instead of "registers_t". Otherwise, you can't modify the stack frame before returning, and the compiler may clobber your stack frame.
I fixed this, but it had no effect.
Octocontrabass wrote: What is the instruction at this address? Which function contains that instruction?
I'm not sure. How do I check what function is at that address?
Octocontrabass
Member
Member
Posts: 5560
Joined: Mon Mar 25, 2013 7:01 pm

Re: How to make a GDT?

Post by Octocontrabass »

zap8600 wrote:How do I check what function is at that address?
You can use addr2line or objdump. I prefer objdump since I usually want to examine the nearby code to get a better idea of what's wrong.
zap8600
Member
Member
Posts: 195
Joined: Tue Nov 02, 2021 11:26 am
Libera.chat IRC: zap8600

Re: How to make a GDT?

Post by zap8600 »

Octocontrabass wrote: You can use addr2line or objdump. I prefer objdump since I usually want to examine the nearby code to get a better idea of what's wrong.
I believe I might do this by running objdump -d myos.elf. I'm not exactly familiar with objdump. I plan on integrating a mini debugger, so that may help.
User avatar
iansjack
Member
Member
Posts: 4703
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: How to make a GDT?

Post by iansjack »

You should consider running your OS under a full debugger. Gdb works very well in conjunction with qemu and has a host of useful features.
zap8600
Member
Member
Posts: 195
Joined: Tue Nov 02, 2021 11:26 am
Libera.chat IRC: zap8600

Re: How to make a GDT?

Post by zap8600 »

What command(s) do I need to run to find the function I'm looking for? I think it is objdump -d myos.elf, but I'm probably wrong.
zap8600
Member
Member
Posts: 195
Joined: Tue Nov 02, 2021 11:26 am
Libera.chat IRC: zap8600

Re: How to make a GDT?

Post by zap8600 »

I added this mini debugger to my OS, but I wasn't able to get it to work, so I'll worry about it later. Anyways, I found the instruction at ffffffff80000209 (it changed from ffffffff800002a9 to ffffffff80000209). It's in the isr_common function, near the bottom.

Code: Select all

ffffffff800001cb <isr_common>:
ffffffff800001cb:       50                      push   %rax
ffffffff800001cc:       53                      push   %rbx
ffffffff800001cd:       51                      push   %rcx
ffffffff800001ce:       52                      push   %rdx
ffffffff800001cf:       56                      push   %rsi
ffffffff800001d0:       57                      push   %rdi
ffffffff800001d1:       55                      push   %rbp
ffffffff800001d2:       41 50                   push   %r8
ffffffff800001d4:       41 51                   push   %r9
ffffffff800001d6:       41 52                   push   %r10
ffffffff800001d8:       41 53                   push   %r11
ffffffff800001da:       41 54                   push   %r12
ffffffff800001dc:       41 55                   push   %r13
ffffffff800001de:       41 56                   push   %r14
ffffffff800001e0:       41 57                   push   %r15
ffffffff800001e2:       fc                      cld
ffffffff800001e3:       48 89 e7                mov    %rsp,%rdi
ffffffff800001e6:       e8 25 00 00 00          call   ffffffff80000210 <isr_handler>
ffffffff800001eb:       48 89 c4                mov    %rax,%rsp
ffffffff800001ee:       41 5f                   pop    %r15
ffffffff800001f0:       41 5e                   pop    %r14
ffffffff800001f2:       41 5d                   pop    %r13
ffffffff800001f4:       41 5c                   pop    %r12
ffffffff800001f6:       41 5b                   pop    %r11
ffffffff800001f8:       41 5a                   pop    %r10
ffffffff800001fa:       41 59                   pop    %r9
ffffffff800001fc:       41 58                   pop    %r8
ffffffff800001fe:       5d                      pop    %rbp
ffffffff800001ff:       5f                      pop    %rdi
ffffffff80000200:       5e                      pop    %rsi
ffffffff80000201:       5a                      pop    %rdx
ffffffff80000202:       59                      pop    %rcx
ffffffff80000203:       5b                      pop    %rbx
ffffffff80000204:       58                      pop    %rax
ffffffff80000205:       48 83 c4 10             add    $0x10,%rsp
ffffffff80000209:       48 cf                   iretq
ffffffff8000020b:       0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)
Is iretq causing problems?
Octocontrabass
Member
Member
Posts: 5560
Joined: Mon Mar 25, 2013 7:01 pm

Re: How to make a GDT?

Post by Octocontrabass »

The exception is happening at the IRETQ instruction, but usually that means there's a problem somewhere else that causes stack corruption.

For example, the instruction at ffffffff800001eb might be corrupting the stack pointer.
Post Reply