OSDev.org

The Place to Start for Operating System Developers
It is currently Thu Mar 28, 2024 7:54 am

All times are UTC - 6 hours




Post new topic Reply to topic  [ 16 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: Programming the APIC to avoid fault when loading IDT (x64)
PostPosted: Tue May 25, 2021 3:21 pm 
Offline
Member
Member
User avatar

Joined: Sun Mar 21, 2021 1:09 pm
Posts: 38
Location: current location
Hello, I have a question. How to reprogram the local APIC, more specifically to enable interrupts. I migrated to x86_64 and I need to port my Idt handling. The PIC has a initialization command for the slave and master, but IIRC the APIC doen't have one. How do I initialize it. Do I have to do it at all? The reason I'm reprogramming it is because the computer crashes at the lidt instruction, as IIRC this only happens in this case, as it would just load a wrong IDT and this does not cause a fault (maybe it is the sti?).
what I got is that I have to disable the PIC, would that solve my problem?

EDIT:
isn't the sti tied to the APIC, if that is the case, that probably causes it

_________________
iustitiae iniustos iudicat


Top
 Profile  
 
 Post subject: Re: Programming the APIC to avoid fault when loading IDT (x6
PostPosted: Tue May 25, 2021 5:17 pm 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5099
acccidiccc wrote:
I migrated to x86_64 and I need to port my Idt handling. The PIC has a initialization command for the slave and master, but IIRC the APIC doen't have one. How do I initialize it. Do I have to do it at all?

Why would migrating to long mode require migrating to APIC? You know those two things are separate, right?

acccidiccc wrote:
The reason I'm reprogramming it is because the computer crashes at the lidt instruction, as IIRC this only happens in this case, as it would just load a wrong IDT and this does not cause a fault (maybe it is the sti?).

The LIDT instruction can cause faults.


Top
 Profile  
 
 Post subject: Re: Programming the APIC to avoid fault when loading IDT (x6
PostPosted: Wed May 26, 2021 3:58 am 
Offline
Member
Member
User avatar

Joined: Sun Mar 21, 2021 1:09 pm
Posts: 38
Location: current location
Hello, sorry, I worded myself wrong. I needed to reprogram the IDT, not the APIC, but as the PIC is obsolete, I figured to support the APIC instead of the obsolete PIC. sorry for the wrong wording. Thanks for the link, it helped me. now maybe it gets cause because the thing that is passed on is a
Code:
uint64_t ptr[2];


Code:
uint64_t idtAddr = (unsigned long long) IDT;
ptr[0] = ((sizeof (struct IDT_entry) * 256) + ((idtAddr & 0xff ff ff ff) << 32)); // the shift is invalid
ptr[1] = idtAddr >> 32;

Code:
uint64_t idtAddr = (unsigned long long) IDT;
ptr[0] = ((sizeof (struct IDT_entry) * 256) + ((idtAddr & 0xff ff ff ff) << 16)); // fixes the first pionter, still faults, even as the PIC is initialized
ptr[1] = idtAddr >> 32;


adding a couple printfs to see the ptr's value reveals that
thanks for the link to the lidt instruction "manual"
idtAddr = FFFFFFFF802051A0;
ptr [0] = 802051A000001000; // that meas that 0x00001000 gets loaded first, but this is a unsigned long (uint32_t) ; that probably cause the crash
ptr [1] = 00000000FFFFFFFF;


this however is besides the point. How do I initialize the APIC. I mean it also is useful for the MP, as they provide a tool to communicate via the I/O APIC. It just seems more useful for modern systems. As I want to make my OS as viable for servers as possible, and as they often feature multiprocessing, APIC support seems mandatory. Correct me if i'm wrong. I can use the PIC code for now, but again it seems more useful to use APIC, 1: because outb is slow, and that puts a cap on the speed of the os, as interrupts need to send through I/O ports. I rather would write to a memory address because of the speed.
by the way, I used the PIC code of the interrupts tutorial. Maybe it doesn't work in long mode, even though I see no reason for that.
by the way I use stivale, it says that interrupts are masked. how do I de-mask them? maybe that causes it?w

_________________
iustitiae iniustos iudicat


Top
 Profile  
 
 Post subject: Re: Programming the APIC to avoid fault when loading IDT (x6
PostPosted: Wed May 26, 2021 5:37 am 
Offline
Member
Member
User avatar

Joined: Sun Mar 21, 2021 1:09 pm
Posts: 38
Location: current location
managed to get bochs working, it said 3rd exception unhandled (13). so it triggers a general protection exception. as it says, on the page you sent me, it gets cause when the address is in a non conanical form. As the CPL is probably 0, it's the supplied address. now my only problem is getting gdb to work with the virtual address as it doesn't want (Cannot access memory at address 0xffffffff80200508). This probably got caused by my lack of knowlegde when it comes to gas.

_________________
iustitiae iniustos iudicat


Top
 Profile  
 
 Post subject: Re: Programming the APIC to avoid fault when loading IDT (x6
PostPosted: Wed May 26, 2021 6:14 am 
Offline
Member
Member
User avatar

Joined: Sun Mar 21, 2021 1:09 pm
Posts: 38
Location: current location
again, made a subroutine that prints the text supplied to the %rax register.

prints semi correct values of the supplied stuff, written in c

Code:
void printhex (uint64_t *k) {
// for arrays
       for (int i = 0; i <= <array size>; i++) {
            printf("as hex %x", k[i]);
       }
}

this prints uint16_t though. maybe this has something to do with the nature of the %rax register.
the order supplied is wrong. I have identifed the issue at hand.

_________________
iustitiae iniustos iudicat


Top
 Profile  
 
 Post subject: Re: Programming the APIC to avoid fault when loading IDT (x6
PostPosted: Wed May 26, 2021 1:09 pm 
Offline
Member
Member

Joined: Wed Aug 30, 2017 8:24 am
Posts: 1593
So, here's how I load the IDT:
Code:
/* void load_idt(const void *base, size_t len); */
.global load_idt
.type load_idt,@function
load_idt:
    addq $-16, %rsp
    decq %rsi
    movq %rdi, 8(%rsp)
    movw %si, 6(%rsp)
    lidtq 6(%rsp)
    addq $16, %rsp
    retq
.size load_idt,.-load_idt
The problem with doing this in C is that you would need a packed structure consisting of a two-byte length mask and an eight-byte pointer. It is possible to create such a thing in C, but cumbersome. But also, I had already resolved to do all the assembler things in external assembler functions, and so here is my function. It works. And if you want to tell me that it is slow: It runs once at startup, and once more on each AP that comes online. I believe performance does not matter under these circumstances.

_________________
Carpe diem!


Top
 Profile  
 
 Post subject: Re: Programming the APIC to avoid fault when loading IDT (x6
PostPosted: Wed May 26, 2021 1:31 pm 
Offline
Member
Member
User avatar

Joined: Sun Mar 21, 2021 1:09 pm
Posts: 38
Location: current location
so your function does
Code:
.global load_idt
.type load_idt,@function
load_idt:
    addq $-16, %rsp # remove 16 from stack register, so you can do stuff as 8(%rsp). presumably
    decq %rsi #decrease source register. to underflow it?
    movq %rdi, 8(%rsp) #move the IDT pointer to rdi
    movw %si, 6(%rsp) #move the len? to the index register
    lidtq 6(%rsp) #load the idt with the limit with the limit first
    addq $16, %rsp # add the 16 back to the rsp
    retq #return
.size load_idt,.-load_idt #get the size. i have no idea why what i found was to set the function size


thanks for your reply! this hopefully can help me. but couldn't you have loaded the idt directly. Not very experienced when it comes to assembly
the si is for indexing, the rsi and rdi for copying, as far as i read. why use them?

EDIT the rsi and rdi are for the ABI right?

_________________
iustitiae iniustos iudicat


Top
 Profile  
 
 Post subject: Re: Programming the APIC to avoid fault when loading IDT (x6
PostPosted: Wed May 26, 2021 3:12 pm 
Offline
Member
Member
User avatar

Joined: Sun Mar 21, 2021 1:09 pm
Posts: 38
Location: current location
ok i solved. I think i am terminally stupid. my solution worked. i used the esp register instead of the rsp register. now it doesn't triple fault anymore. lidt works. meine fresse

... but, it hangs at the lidt instruction for no apparent reason
EDIT:
it only manages to not cause a triple fault with my qemu options, also it doesn't show me the interrupts correctly. still causes 0xd

_________________
iustitiae iniustos iudicat


Top
 Profile  
 
 Post subject: Re: Programming the APIC to avoid fault when loading IDT (x6
PostPosted: Thu May 27, 2021 9:48 am 
Offline
Member
Member

Joined: Wed Aug 30, 2017 8:24 am
Posts: 1593
acccidiccc wrote:
so your function does
Code:
.global load_idt
.type load_idt,@function
load_idt:
    addq $-16, %rsp # remove 16 from stack register, so you can do stuff as 8(%rsp). presumably
    decq %rsi #decrease source register. to underflow it?
    movq %rdi, 8(%rsp) #move the IDT pointer to rdi
    movw %si, 6(%rsp) #move the len? to the index register
    lidtq 6(%rsp) #load the idt with the limit with the limit first
    addq $16, %rsp # add the 16 back to the rsp
    retq #return
.size load_idt,.-load_idt #get the size. i have no idea why what i found was to set the function size


thanks for your reply! this hopefully can help me. but couldn't you have loaded the idt directly. Not very experienced when it comes to assembly
the si is for indexing, the rsi and rdi for copying, as far as i read. why use them?

EDIT the rsi and rdi are for the ABI right?
Yeah, the comment i put above the function was important. That's how the function is defined in C. I decided to take the pointer in RDI (first argument) and the length in RSI (second argument). However, the size must be below 65536 for architectural reasons, so I can just refer to SI instead of RSI, it means the same thing.

And no, the decrement is not to underflow it. It is just because the first word of the IDTR does not contain the length, but the limit. Which in practice always one less than the length in bytes. The objective is to construct an area in memory that is 10 bytes long, in which the first 2 bytes consist of the limit and the remaining 8 bytes consist of the pointer. I think you read the moves the wrong way around: In AT&T syntax, "movq %rdi, 8(%rsp)" moves the contents of RDI to [RSP+8].

You can, in theory, do the same thing in C like this:
Code:
uint16_t idtr[5] = { len - 1, addr, addr >> 16, addr >> 32, addr >> 48};
asm("lidt %0" : "m"(idtr));

However, I have no idea what the right clobbers and the right specifiers are.
acccidiccc wrote:
... but, it hangs at the lidt instruction for no apparent reason
OK, that is weird. LIDT should either succeed or cause an exception. I can only presume you have managed to trigger a bug. Exception 13 is still #GP, and the manual says that happens when the address is non-canonical. Given you already f*ed up the 32-bit/64-bit thing once, is it possible you are only copying 32 bits of the address into the IDTR?

_________________
Carpe diem!


Top
 Profile  
 
 Post subject: Re: Programming the APIC to avoid fault when loading IDT (x6
PostPosted: Fri May 28, 2021 8:37 am 
Offline
Member
Member
User avatar

Joined: Sun Mar 21, 2021 1:09 pm
Posts: 38
Location: current location
thanks for the explanation. now it makes sense. Read the wiki article and they just used the %rdi and %rsi registers as integer storage. Man i feel dumb. you pass the values onto the stack, and then load the contents of the stack into the lidt register, starting with the limit of two byte size, and the 8 byte address.
the code i was using was probably valid for the simpler cdecl calling convention, which was to pop thinks from the stack. however, this is not advisable on a x86_64 system.
the emulator hanged with specific option, using a barebones
Code:
qemu-system-x86_64 -d int /dev/sdb # the usb stick containing the os

showed at first a general protection fault. and now a page fault with the following address in the cr2 address.
Code:
ffff802054e01000
the 1000 seems familiar.
the cpu called the address in the idtr register. which was not a valid address and triggered a page fault. the address of the idt contains garbage and a general protection fault gets caused and doesn't get resolved, double fault, which also doesn't get resolved and boom: triple fault. this is my guess as this is the order of exceptions shown on qemu.

nullplan wrote:
Given you already f*ed up the 32-bit/64-bit thing once, is it possible you are only copying 32 bits of the address into the IDTR?

actually that was the thing i fixed, i was using the %ebp register, now i am using the %rbp register, which kinda fixed things. can i use your solution?

_________________
iustitiae iniustos iudicat


Top
 Profile  
 
 Post subject: Re: Programming the APIC to avoid fault when loading IDT (x6
PostPosted: Fri May 28, 2021 1:21 pm 
Offline
Member
Member

Joined: Wed Aug 30, 2017 8:24 am
Posts: 1593
acccidiccc wrote:
the code i was using was probably valid for the simpler cdecl calling convention, which was to pop thinks from the stack. however, this is not advisable on a x86_64 system.
Yes, you actually have to read your arguments the same way the compiler passes them in.
acccidiccc wrote:
can i use your solution?
Be my guest. That's why I posted it.

_________________
Carpe diem!


Top
 Profile  
 
 Post subject: Re: Programming the APIC to avoid fault when loading IDT (x6
PostPosted: Fri May 28, 2021 1:40 pm 
Offline
Member
Member
User avatar

Joined: Sun Mar 21, 2021 1:09 pm
Posts: 38
Location: current location
thanks a lot! :)

_________________
iustitiae iniustos iudicat


Top
 Profile  
 
 Post subject: Re: Programming the APIC to avoid fault when loading IDT (x6
PostPosted: Tue Jun 01, 2021 6:48 am 
Offline
Member
Member
User avatar

Joined: Sun Mar 21, 2021 1:09 pm
Posts: 38
Location: current location
solved it. the gdt code segment i used was invalid for the gdt setup by limine. for anybody using limine and trying to setup the idt, change the selector from 0x08 to 0x28.

_________________
iustitiae iniustos iudicat


Top
 Profile  
 
 Post subject: Re: Programming the APIC to avoid fault when loading IDT (x6
PostPosted: Tue Jun 01, 2021 12:26 pm 
Offline
Member
Member

Joined: Wed Aug 30, 2017 8:24 am
Posts: 1593
acccidiccc wrote:
solved it. the gdt code segment i used was invalid for the gdt setup by limine. for anybody using limine and trying to setup the idt, change the selector from 0x08 to 0x28.
Better idea: Load your own GDT before loading an IDT. Unless limine is actually documenting the selector values, you should not trust them. And loading your own GDT is required at some point anyway, so may as well do it.

_________________
Carpe diem!


Top
 Profile  
 
 Post subject: Re: Programming the APIC to avoid fault when loading IDT (x6
PostPosted: Tue Jun 01, 2021 1:07 pm 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5099
nullplan wrote:
Unless limine is actually documenting the selector values,

It's documented: version 1 and version two.

But you still need to set up your own GDT, so using the bootloader's selectors in your IDT seems like a poor choice.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 16 posts ]  Go to page 1, 2  Next

All times are UTC - 6 hours


Who is online

Users browsing this forum: Bing [Bot] and 59 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group