OSDev.org

The Place to Start for Operating System Developers
It is currently Tue Apr 23, 2024 1:49 pm

All times are UTC - 6 hours




Post new topic Reply to topic  [ 54 posts ]  Go to page Previous  1, 2, 3, 4  Next
Author Message
 Post subject: Re: IDT Help
PostPosted: Wed Mar 10, 2021 8:18 pm 
Offline
Member
Member

Joined: Tue Mar 09, 2021 9:31 pm
Posts: 25
Octocontrabass wrote:
It's #GP caused by the IDT entry for interrupt 0x20 (32), which should be IRQ0 according to how you've programmed the PICs. Your IDT only has entries for interrupt 0 through 31, so it's not surprising you'll get an exception.

It's immediately followed by #DF because your IDT entry for #GP is invalid. Perhaps use "info idt" in the QEMU monitor to see what QEMU thinks of your IDT. You should probably fix your exception handlers before you worry about IRQs!

How do I get QEMU to give me a prompt? Currently I run qemu and it immediately boots into my OS and then crashes. This is the command I use.
Code:
qemu-system-x86_64 -cdrom dist/x86_64/kernel.iso -no-reboot -d int

I think with the new code I have 48 IRQs, but I could be wrong. How do I fix my exception handlers? I thought that's what the IRQs did, exception and interrupt handling.


Top
 Profile  
 
 Post subject: Re: IDT Help
PostPosted: Wed Mar 10, 2021 9:14 pm 
Offline
Member
Member

Joined: Thu Feb 18, 2021 3:07 am
Posts: 28
As Octocontrabass said, you can't access CS directly. Use the following code to change it (it's in AT&T syntax because I don't know how the operand sizes work in Intel's syntax for these instructions):
Code:
pushq 0x8 # Push the code selector. Pushes 64 bits (although the selector is 16 bits)
pushq cs_reload_lbl # Push the address of the label
retfq # Return Far - This will set RIP to cs_reload_lbl and CS to 0x8
cs_reload_lbl:
# The rest of the code here

isaiah0311 wrote:
How do I fix my exception handlers? I thought that's what the IRQs did, exception and interrupt handling.

You can't handle exceptions and IRQs with the exact same code. Each interrupt serves a different purpose.
In my OS, I have one IRQ handler and one exception handler, both written in C (actually C++ but I don't use any C++ features except classes). These two handlers are called by small assembly handlers, one for each interrupt. The assembly handlers push all the registers and call the handlers, giving them the interrupt's number and an error code (if the exception has one). The C handlers call a thread that handles the exception/IRQ. After the C handler returns, the assembly handler pops all the registers and returns (iret).
For now, I think your exception handler could just print some exception info and then hang ('jmp $').

Also, about your 'long_mode_start' function, I don't understand it. Where and when do you load the GDT? And why do you still set the segment registers to 0?
Besides that, you don't need to push rsp in your handler. The interrupt does it by itself. And since you'll probably push all the registers a few more times in your code, I'd recommend you create a 'pusha' macro.


Top
 Profile  
 
 Post subject: Re: IDT Help
PostPosted: Wed Mar 10, 2021 9:38 pm 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5143
isaiah0311 wrote:
How do I get QEMU to give me a prompt?

The Windows builds of QEMU include a GUI with a convenient menu to switch between them, but the QEMU documentation says you can use keyboard shortcuts to switch to the monitor too.

isaiah0311 wrote:
I think with the new code I have 48 IRQs,

You have 16 IRQs. IRQs are interrupt requests that come from hardware external to the CPU core. The CPU core itself can also generate 32 exceptions. All together, that's 48 interrupts, which requires at least 48 entries in your IDT, and 48 ISRs to handle all of them.

If you get the terminology wrong, you'll definitely be confused.


Top
 Profile  
 
 Post subject: Re: IDT Help
PostPosted: Wed Mar 10, 2021 10:17 pm 
Offline
Member
Member

Joined: Tue Mar 09, 2021 9:31 pm
Posts: 25
SKC wrote:
As Octocontrabass said, you can't access CS directly. Use the following code to change it (it's in AT&T syntax because I don't know how the operand sizes work in Intel's syntax for these instructions):
Code:
pushq 0x8 # Push the code selector. Pushes 64 bits (although the selector is 16 bits)
pushq cs_reload_lbl # Push the address of the label
retfq # Return Far - This will set RIP to cs_reload_lbl and CS to 0x8
cs_reload_lbl:
# The rest of the code here


Alright I changed the code and now load CS properly, thank you.
SKC wrote:
You can't handle exceptions and IRQs with the exact same code. Each interrupt serves a different purpose.
In my OS, I have one IRQ handler and one exception handler, both written in C (actually C++ but I don't use any C++ features except classes). These two handlers are called by small assembly handlers, one for each interrupt. The assembly handlers push all the registers and call the handlers, giving them the interrupt's number and an error code (if the exception has one). The C handlers call a thread that handles the exception/IRQ. After the C handler returns, the assembly handler pops all the registers and returns (iret).
For now, I think your exception handler could just print some exception info and then hang ('jmp $').

So I understand what you're saying, but I just don't know how to implement it. Right now I use the loop that you recommended in a previous answer.
SKC wrote:
Code:
mov rbx,idt
mov rax,isr_wrapper.handler ; Assuming you use the same handler for all the interrupts. It's fine for now, but you'll need to change it.
mov rcx,48 ; rcx should be the number of entries in your IDT
.idt_loop: ; This loop sets the offset for each IDT entry
    mov word [rbx],ax ; offset1
    shr rax,16
    mov word [rbx+6],ax ; offset2
    shr rax,16
    mov dword [rbx+8],eax ; offset3
    add rbx,16 ; Go to the next entry
    dec rcx
    jne .idt_loop ; 'dec' will set ZF to 1 if the result is 0


So this should make all 48 entries in my IDT? If this is correct, what am I missing? I apologize ahead of time for my lack of knowledge.
Here is the whole main64.asm file.
Code:
global long_mode_start
extern kernel_main
extern interrupt_handler

section .text
bits 64
long_mode_start:
   ; push 0x8 into cs
   push 0x8
   push cs_reload
   retfq

cs_reload:
   call load_idt
   lidt [idt.pointer]
   sti
   
   call kernel_main
   mov rax, 60
   mov rdi, 2
   syscall

load_idt:
   mov rbx, idt
   mov rax, isr_wrapper.handler
   mov rcx, 48
.loop:
   mov word [rbx], ax
   shr rax, 16
   mov word [rbx + 6], ax
   shr rax, 16
   mov dword [rbx + 8], eax
   add rbx, 16
   dec rcx
   jne .loop
   ret

idt:
.irq:
   dw 0x0000
   dw 0x8000
   db 0x00
   db 10101110b
   dw 0x0000
   dd 0x00000000
   dd 0x00000000
.pointer:
        dw $ - idt - 1
        dq idt

isr_wrapper:
.handler:
   push rax
   push rcx
   push rdx
   push rbx
   push rbp
   push rsi
   push rdi
   push r8
   push r9
   push r10
   push r11
   push r12
   push r13
   push r14
   push r15

        cld                     ; C code following the System V ABI requires DF to be clear on function entry
        call interrupt_handler
   
   pop r15
   pop r14
   pop r13
   pop r12
   pop r11
   pop r10
   pop r9
   pop r8
   pop rdi
   pop rsi
   pop rbp
   pop rbx
   pop rdx
   pop rcx
   pop rax
        iretq

SKC wrote:
Also, about your 'long_mode_start' function, I don't understand it. Where and when do you load the GDT? And why do you still set the segment registers to 0?
Besides that, you don't need to push rsp in your handler. The interrupt does it by itself. And since you'll probably push all the registers a few more times in your code, I'd recommend you create a 'pusha' macro.

My GDT is loaded in main.asm when the OS is still in protected mode. I then make a long jump in order to reach long_mode_start after switching to long mode. The segment registers were set to 0 because that is how it was explained to me in a tutorial, I have no other reason. I've taken out that part as of now. Here is the GDT code from the other file.
Code:
section .rodata
gdt:
    dq 0 ; null descriptor
.code: equ $ - gdt
    dq 0x00209a0000000000 ; 64-bit code descriptor (exec/read)
    dq 0x0009200000000000 ; 64-bit data descriptor (read/write)
align 4
    dw 0
.pointer:
    dw $ - gdt - 1
    dq gdt

And the GDT is loaded like this in the start function:
Code:
lgdt [gtd.pointer]
jmp gdt.code:long_mode_start

Octocontrabass wrote:
The Windows builds of QEMU include a GUI with a convenient menu to switch between them, but the QEMU documentation says you can use keyboard shortcuts to switch to the monitor too.

Thank you, I finally got the prompt.


Top
 Profile  
 
 Post subject: Re: IDT Help
PostPosted: Wed Mar 10, 2021 10:53 pm 
Offline
Member
Member

Joined: Thu Feb 18, 2021 3:07 am
Posts: 28
isaiah0311 wrote:
So I understand what you're saying, but I just don't know how to implement it. Right now I use the loop that you recommended in a previous answer.
...
So this should make all 48 entries in my IDT? If this is correct, what am I missing?

The code will set all entries to a specific handler. You can use it now for your exceptions, but you'll need to give each interrupt its own handler at some point. And here the disadvantage of my method reveals itself - writing a handler for each interrupt and then configuring each interrupt separately requires lots of code. I use a simple python script to generate the code, but still, it's very messy and can be confusing. Maybe someone else has a better idea.

Now to your code. Since you create and load the GDT in protected mode, you don't need to change CS (CS is changed when you switch to long mode). However, you do need to change the data segment registers. It looks like you don't fully understand the segment selectors, so I'll explain them:
Each selector is made out of 3 parts: the privilege, the table index, and the descriptor index. In a C struct, the selector would look like this:
Code:
struct Selector
{
    uint16_t privilege:2;
    uint16_t table:1; // 0 for GDT, 1 for LDT
    uint16_t index:13;
};

So, for your data segment descriptors, use 0x10 (index 2, table 0, privilege 0). You need to set them right after you enter long mode.
And in your IDT, you need to use 0x8 (index 1, table 0, privilege 0) and not 0x8000 (index 4096, table 0, privilege 0) for the code selector.


Top
 Profile  
 
 Post subject: Re: IDT Help
PostPosted: Wed Mar 10, 2021 11:19 pm 
Offline
Member
Member

Joined: Tue Mar 09, 2021 9:31 pm
Posts: 25
SKC wrote:
The code will set all entries to a specific handler. You can use it now for your exceptions, but you'll need to give each interrupt its own handler at some point. And here the disadvantage of my method reveals itself - writing a handler for each interrupt and then configuring each interrupt separately requires lots of code. I use a simple python script to generate the code, but still, it's very messy and can be confusing. Maybe someone else has a better idea.

I think I'll try adding a handler for each entry. The code still breaks whenever I turn on interrupts. Unless there is something I need the handler to do in order to make it work? Right now it calls an function in C that does nothing and then hangs with jmp $.
SKC wrote:
It looks like you don't fully understand the segment selectors, so I'll explain them:
Each selector is made out of 3 parts: the privilege, the table index, and the descriptor index. In a C struct, the selector would look like this:
Code:
struct Selector
{
    uint16_t privilege:2;
    uint16_t table:1; // 0 for GDT, 1 for LDT
    uint16_t index:13;
};

So, for your data segment descriptors, use 0x10 (index 2, table 0, privilege 0). You need to set them right after you enter long mode.
And in your IDT, you need to use 0x8 (index 1, table 0, privilege 0) and not 0x8000 (index 4096, table 0, privilege 0) for the code selector.

This makes a lot more sense now, thank you!


Top
 Profile  
 
 Post subject: Re: IDT Help
PostPosted: Thu Mar 11, 2021 1:41 am 
Offline
Member
Member
User avatar

Joined: Thu Oct 13, 2016 4:55 pm
Posts: 1584
isaiah0311 wrote:
I got it working, I installed the bochs-x library. Now I get an error saying /usr/share/qemu/bios.bin does not exist. I'm guessing you were being general and meant just the path to whatever bios.bin file qemu uses, that's my bad.
That's a progress :-) Well, yes, I meant in general, however that's where it actually is on my distro.

Try bochsbios, that also contains the qemu BIOS at /usr/share/bochs/BIOS-qemu-latest. And about vgabios, that should be installed at /usr/share/vgabios/vgabios.bin (both packages should be already installed on your computer as bochs package depends on them. If not, just install them). Man, Ubuntu packages are insane :-D

Cheers,
bzt


Top
 Profile  
 
 Post subject: Re: IDT Help
PostPosted: Thu Mar 11, 2021 10:20 am 
Offline
Member
Member

Joined: Tue Mar 09, 2021 9:31 pm
Posts: 25
bzt wrote:
Try bochsbios, that also contains the qemu BIOS at /usr/share/bochs/BIOS-qemu-latest. And about vgabios, that should be installed at /usr/share/vgabios/vgabios.bin (both packages should be already installed on your computer as bochs package depends on them. If not, just install them). Man, Ubuntu packages are insane :-D

I got Bochs working, thank you so much! So it boots up, but just gives me a black screen and a terminal. Is there a command I need to use to boot into the OS?


Top
 Profile  
 
 Post subject: Re: IDT Help
PostPosted: Thu Mar 11, 2021 10:47 am 
Offline
Member
Member
User avatar

Joined: Thu Oct 13, 2016 4:55 pm
Posts: 1584
isaiah0311 wrote:
I got Bochs working, thank you so much! So it boots up, but just gives me a black screen and a terminal. Is there a command I need to use to boot into the OS?
Yes, same as with gdb and qemu -Ss: the machine is stopped right before any code is executed. This is to allow you to set up break points. Just continue execution with a "c" command.
Read this and this to get you started. Here's a list of available commands (not all of them, use "help" and "help info" commands at the debugger prompt to list them).

Some of the features that will make your life easier:
1. use xchg bx,bx instruction in your code if you want to invoke the debugger (exchanging a value with itself does nothing in other VMs) In contrast to int 3, this won't change the stack nor the VM's state in any way and works without a correct IDT too ;-)
2. use out 0e9h, al to print a character to the terminal (very handy and a single instruction is a lot simpler than sending a character to the serial port)
3. learn its debugger commands, for example "page" command will debug the page table walk for you, I've found that feature extremely useful (and no other debugger can do that)
4. using "info idt" will display the decoded IDT, also very useful
5. you can dump peripheral's registers too, like "info pic" or "info pit" (you'll see later how useful those are when you're debugging IRQs)

For example, you could do
Code:
xchg bx,bx
sti
and then at the bochs debugger prompt issue "s" command multiple times and you'll see step-by-step what's happening inside the CPU when you enable the interrupts flag, and what instructions lead to the triple fault.

Cheers,
bzt


Top
 Profile  
 
 Post subject: Re: IDT Help
PostPosted: Thu Mar 11, 2021 1:20 pm 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5143
isaiah0311 wrote:
Code:
idt:
.irq:
   dw 0x0000
   dw 0x8000
   db 0x00
   db 10101110b
   dw 0x0000
   dd 0x00000000
   dd 0x00000000
.pointer:
        dw $ - idt - 1
        dq idt

Well, now your IDT has only one entry, so that's not going to work. You need at least 48 entries. You also still have a bad CS selector and the DPL is set to 1 instead of 0.


Top
 Profile  
 
 Post subject: Re: IDT Help
PostPosted: Thu Mar 11, 2021 1:58 pm 
Offline
Member
Member

Joined: Tue Mar 09, 2021 9:31 pm
Posts: 25
Octocontrabass wrote:
Well, now your IDT has only one entry, so that's not going to work. You need at least 48 entries. You also still have a bad CS selector and the DPL is set to 1 instead of 0.

Thank you for pointing that out, I fixed the code so there is 48 IRQs now.
SKC said my CS selector should be good because it is set when I switch into long mode. Is there something else I am missing?
Also, I don't know what a DPL is, I apologize.


Top
 Profile  
 
 Post subject: Re: IDT Help
PostPosted: Thu Mar 11, 2021 2:14 pm 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5143
isaiah0311 wrote:
Thank you for pointing that out, I fixed the code so there is 48 IRQs now.

IDT descriptors are not IRQs. You should use the correct name, otherwise you might get confused!

isaiah0311 wrote:
SKC said my CS selector should be good because it is set when I switch into long mode. Is there something else I am missing?

Aren't you using 0x8 for your CS selector when you switch to long mode? You wrote 0x8000 in the IDT descriptor.

isaiah0311 wrote:
Also, I don't know what a DPL is, I apologize.

Descriptor Privilege Level. It's one of the fields in the IDT descriptor. It determines which privilege level is allowed to call each ISR using the INT instruction. For now, you should set the DPL to 0. You can always change it later if you decide less-privileged code needs to use the INT instruction.


Top
 Profile  
 
 Post subject: Re: IDT Help
PostPosted: Thu Mar 11, 2021 10:30 pm 
Offline
Member
Member

Joined: Tue Mar 09, 2021 9:31 pm
Posts: 25
Octocontrabass wrote:
isaiah0311 wrote:
Thank you for pointing that out, I fixed the code so there is 48 IRQs now.

IDT descriptors are not IRQs. You should use the correct name, otherwise you might get confused!

To my understanding, the loop SKC wrote for me in an earlier reply should put 48 entries into the IDT. I think those are the descriptors. I also have 48 IRQs (I'm not sure how many of these I'm supposed to have).

Octocontrabass wrote:
isaiah0311 wrote:
SKC said my CS selector should be good because it is set when I switch into long mode. Is there something else I am missing?

Aren't you using 0x8 for your CS selector when you switch to long mode? You wrote 0x8000 in the IDT descriptor.

I updated this after SKC pointed this out earlier.

Octocontrabass wrote:
isaiah0311 wrote:
Also, I don't know what a DPL is, I apologize.

Descriptor Privilege Level. It's one of the fields in the IDT descriptor. It determines which privilege level is allowed to call each ISR using the INT instruction. For now, you should set the DPL to 0. You can always change it later if you decide less-privileged code needs to use the INT instruction.

Do I set that in the IDT.pointer section? How do I do that?


Top
 Profile  
 
 Post subject: Re: IDT Help
PostPosted: Thu Mar 11, 2021 10:49 pm 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5143
isaiah0311 wrote:
I also have 48 IRQs (I'm not sure how many of these I'm supposed to have).

You're using the PICs, so you have 16 IRQs. You have 48 of something else. ISRs? IDT descriptors?

isaiah0311 wrote:
Do I set that in the IDT.pointer section? How do I do that?

The DPL is bits 5 and 6 of this byte in the descriptor:
Code:
db 10101110b

Change it to 10001110b to set the DPL to 0.


Top
 Profile  
 
 Post subject: Re: IDT Help
PostPosted: Thu Mar 11, 2021 11:01 pm 
Offline
Member
Member

Joined: Thu Feb 18, 2021 3:07 am
Posts: 28
isaiah0311 wrote:
To my understanding, the loop SKC wrote for me in an earlier reply should put 48 entries into the IDT.

Well yes, but actually no. The loop sets the entries to the same handler. If you use it for the entire IDT, every interrupt/exception/IRQ will call the same code. Since you're in a very early stage, you can set the exception entries (0-31) to the same handler. However, as I said before, you will need to give each interrupt its own handler at some point. But for now, setting all the exceptions (only exceptions, not IRQs) to a dummy handler that just hangs is fine.

Oh, about your confusion with the naming, read this. I hope it helps :)


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

All times are UTC - 6 hours


Who is online

Users browsing this forum: Google [Bot] and 101 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