Page 16 of 20
Re: How to make a GDT?
Posted: Tue Nov 01, 2022 12:10 pm
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.
Re: How to make a GDT?
Posted: Tue Nov 01, 2022 12:29 pm
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?
Re: How to make a GDT?
Posted: Tue Nov 01, 2022 12:49 pm
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.
Re: How to make a GDT?
Posted: Tue Nov 01, 2022 2:35 pm
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");
}
Re: How to make a GDT?
Posted: Tue Nov 01, 2022 4:40 pm
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?
Re: How to make a GDT?
Posted: Tue Nov 01, 2022 5:05 pm
by Octocontrabass
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.
Re: How to make a GDT?
Posted: Tue Nov 01, 2022 5:45 pm
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
Re: How to make a GDT?
Posted: Tue Nov 01, 2022 9:27 pm
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.
What is the instruction at this address? Which function contains that instruction?
Re: How to make a GDT?
Posted: Wed Nov 02, 2022 9:26 am
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?
Re: How to make a GDT?
Posted: Wed Nov 02, 2022 10:14 am
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.
Re: How to make a GDT?
Posted: Wed Nov 02, 2022 10:48 am
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.
Re: How to make a GDT?
Posted: Wed Nov 02, 2022 11:01 am
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.
Re: How to make a GDT?
Posted: Wed Nov 02, 2022 1:42 pm
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.
Re: How to make a GDT?
Posted: Wed Nov 02, 2022 3:07 pm
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?
Re: How to make a GDT?
Posted: Wed Nov 02, 2022 3:10 pm
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.