Page 4 of 4

Re: IDT Help

Posted: Fri Mar 12, 2021 11:23 pm
by nullplan
isaiah0311 wrote:Here is how the IRQs are handled:

Code: Select all

isr_handler:
.pic1
   mov ax, 0x20
   out 0x20, ax
   iretq
.pic2
   mov ax, 0x20
   out 0xa0, ax
   iretq
I know this won't read the keyboard input, but I didn't except it to cause a reboot. I figured it would just do nothing.
The ISRs are clobbering ax. You need to save ax and restore it, otherwise the main program will suddenly find itself with a changed AX. Also, the PIC2 handler needs to send EOI to PIC 1 as well. So at least this much is needed:

Code: Select all

isr_handler:
.pic1:
  push rax
  mov ax,0x20
  out 0x20, ax
  pop rax
  iretq

.pic2:
  push rax
  mov ax,0x20
  out 0xa0, ax
  out 0x20, ax
  pop rax
  iretq
For the keyboard interrupt, you at least need to read out the keyboard controller, even if you just discard the byte. Otherwise the keyboard controller will just keep interrupting the CPU in an endless loop, and the CPU will never get to do anything.

Re: IDT Help

Posted: Fri Mar 12, 2021 11:57 pm
by isaiah0311
SKC wrote: Read this.
And this.
So after reading these I came up with a little function to handle keyboard input. The C function it calls just prints "input read" to let me know it worked.
It works when I press a key the first time but then stops. It doesn't seem to be crashing because QEMU isn't rebooting like it used to. So I'm not sure why it stops working.

Code: Select all

isr_handler:
.keyboard:
   push rax
   in ax, 0x21
   cld
   call read_input
   mov ax, 0x20
   out 0x20, ax
   pop rax
   iretq
nullplan wrote: The ISRs are clobbering ax. You need to save ax and restore it, otherwise the main program will suddenly find itself with a changed AX. Also, the PIC2 handler needs to send EOI to PIC 1 as well.
Thank you, I fixed my ISR.

Re: IDT Help

Posted: Sat Mar 13, 2021 12:25 am
by Octocontrabass
isaiah0311 wrote:

Code: Select all

   in ax, 0x21
What's this for?
isaiah0311 wrote:

Code: Select all

   call read_input
If you call a function written in C, you need to save all of the registers C function calls are allowed to clobber. You might want to check the AMD64 psABI for the whole list.
isaiah0311 wrote:

Code: Select all

   out 0x20, ax
That should be AL instead of AX.

Re: IDT Help

Posted: Sat Mar 13, 2021 12:41 am
by isaiah0311
Octocontrabass wrote:
isaiah0311 wrote:

Code: Select all

   in ax, 0x21
What's this for?
Reads the key code from the PIC and puts it into ax (now al).
Octocontrabass wrote: If you call a function written in C, you need to save all of the registers C function calls are allowed to clobber. You might want to check the AMD64 psABI for the whole list.
Can I use my pushall and popall macros here or is it bad to preserve registers that aren't getting clobbered?

Re: IDT Help

Posted: Sat Mar 13, 2021 1:07 am
by Octocontrabass
isaiah0311 wrote:Reads the key code from the PIC and puts it into ax (now al).
The PIC doesn't know anything about key codes. Perhaps you're looking for the keyboard controller?

If you want to pass it as a uint8_t parameter to the C function, you need to use MOVZX to zero-extend AL into EDI. (The AMD64 psABI isn't clear about whether this is necessary, but Clang assumes you will do this.)
isaiah0311 wrote:Can I use my pushall and popall macros here or is it bad to preserve registers that aren't getting clobbered?
It takes a bit more space on the stack, but otherwise it won't hurt.

Speaking of the stack, be careful to keep the stack properly 16-byte aligned when you call a C function. (If your pushall macro pushes 15 registers, the stack will be correctly aligned. Each value you push moves the stack by 8 bytes, and the interrupt itself pushes five values before running your handler code, for a total of (15+5)*8=160 bytes.)

Re: IDT Help

Posted: Sat Mar 13, 2021 8:58 am
by sj95126
Octocontrabass wrote:Speaking of the stack, be careful to keep the stack properly 16-byte aligned when you call a C function. (If your pushall macro pushes 15 registers, the stack will be correctly aligned. Each value you push moves the stack by 8 bytes, and the interrupt itself pushes five values before running your handler code, for a total of (15+5)*8=160 bytes.)
We're going a bit off topic but I'd like to get some clarification on this particular point.

The only reference to 16-byte stack alignment I could find in the x86_64 ABI specifically mentioned 16-byte alignment for the initial %rsp value of a new process (section 3.4.1 of the ABI). It's not unreasonable to extrapolate that to mean that any function entry point should have a 16-byte stack alignment, so let's use that assumption.

Any compiled code (C, etc.) should do this automatically. But if I have assembly code in my kernel that calls a C function, I should actually align the stack as 8-byte aligned, but NOT 16-byte aligned, so that the 'call' instruction pushing the return address on the stack will result in the C function entry point getting a 16-byte aligned stack.

In other words, I should wrap all my asm->C calls with either a macro or a wrapper function that makes an extra adjustment to the stack (if necessary) and restores it after the call. (update: alternatively, I could use the logic the compiler uses, which is that I should know at all times how the stack is aligned, because I generated the instructions that manipulate it, so I may only need realignment in a few very specific places).

Do I have that right?

Re: IDT Help

Posted: Sat Mar 13, 2021 9:48 am
by Octocontrabass
sj95126 wrote:The only reference to 16-byte stack alignment I could find in the x86_64 ABI specifically mentioned 16-byte alignment for the initial %rsp value of a new process (section 3.4.1 of the ABI).
The end of the input argument area shall be aligned on a 16 (32 or 64, if __m256 or __m512 is passed on stack) byte boundary.
Section 3.2.2 of the psABI. They say it a bit differently, but it means the stack needs to be aligned for all function calls.
sj95126 wrote:But if I have assembly code in my kernel that calls a C function, I should actually align the stack as 8-byte aligned, but NOT 16-byte aligned, so that the 'call' instruction pushing the return address on the stack will result in the C function entry point getting a 16-byte aligned stack.
No, the stack must be 16-byte aligned before the CALL instruction.
sj95126 wrote:In other words, I should wrap all my asm->C calls with either a macro or a wrapper function that makes an extra adjustment to the stack (if necessary) and restores it after the call. (update: alternatively, I could use the logic the compiler uses, which is that I should know at all times how the stack is aligned, because I generated the instructions that manipulate it, so I may only need realignment in a few very specific places).
Yep, that works.

Re: IDT Help

Posted: Sat Mar 13, 2021 10:16 am
by sj95126
Octocontrabass wrote:No, the stack must be 16-byte aligned before the CALL instruction.
Oh, good, that's actually easier. So I can use this:

Code: Select all

#define CALL_ALIGN(x)           pushq   %r15 ; \
                                movq    %rsp, %r15 ; \
                                andw    $0xfff0, %sp ; \
                                call    x ; \
                                movq    %r15, %rsp ; \
                                popq    %r15

Re: IDT Help

Posted: Sat Mar 13, 2021 10:17 am
by isaiah0311
My IDT is fully functional and I am able to process keyboard input now. Thank you so much to everyone!