bochs I/O apic write with len=2 (should be 4)

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.
Post Reply
chez4
Posts: 5
Joined: Sat Feb 23, 2019 1:09 pm
Libera.chat IRC: chez4

bochs I/O apic write with len=2 (should be 4)

Post by chez4 »

When running my OS in qemu, or just hardware, it runs fine, with no problems. However, when I run it in bochs, it prints nothing, and fails, with the error "00243442083p[IOAPIC] >>PANIC<< I/O apic write with len=2 (should be 4) at address 0x0000fec00020".

Image
(Text should be displayed, it does in qemu and hardware)

I stepped through in bochs to see where the error occurs:

Code: Select all

<bochs:866> 
Next at t=213053579
(0) [0x000000101278] 0008:0000000000101278 (unk. ctxt): cmp eax, edx              ; 39d0
<bochs:867> 
Next at t=223053579
(0) [0x000000101270] 0008:0000000000101270 (unk. ctxt): mov word ptr ds:[eax], cx ; 668908
<bochs:868> 
Next at t=233053579
(0) [0x000000101270] 0008:0000000000101270 (unk. ctxt): mov word ptr ds:[eax], cx ; 668908
<bochs:869> 
Next at t=243053579
(0) [0x000000101278] 0008:0000000000101278 (unk. ctxt): cmp eax, edx              ; 39d0
<bochs:870> 
00243442083p[IOAPIC] >>PANIC<< I/O apic write with len=2 (should be 4) at address 0x0000fec00020
It repeats those instructions loads of times, then errors. I'm not sure why, since I'm not even using the I/O APIC for interrupts (unless the APIC can be treated as the PIC until it is enabled). I believe the error is due to sending EOI to the PIC, because ever since I started handling interrupts, bochs stopped working. IRQ 0 is the only interrupt that has function, the others I just ignore.
This is how I send the EOI:

Code: Select all

eoi:
    mov    al, 0x20
    out    0x20, al
    ret
The error also could be due to my ksleep function, as bochs panics whilst executing it (I think from the stepping above). I also made that around the same time bochs stopped working.

You can see any code on my git repository: https://github.com/chez4/burnsOS/tree/master/x86/src Files that are relevant: boot.asm kmain.c system.c
User avatar
bzt
Member
Member
Posts: 1584
Joined: Thu Oct 13, 2016 4:55 pm
Contact:

Re: bochs I/O apic write with len=2 (should be 4)

Post by bzt »

Hi,

I think the message is self explanatory. You're writing a word instead of a dword (IOAPIC registers are 32 bit wide). Just because it's MMIO, doesn't mean you can write to it at any offset with any granularity, you must write to the whole register at once.

If you use C (as I suspect from the disassembly), then you should use

Code: Select all

*((uint32_t*)IOAPIC_regX) = X;
and *not*

Code: Select all

*((uint16_t*)IOAPIC_regX) = X;
If you don't intentionally write to the IOAPIC as you say, then you probably have an out of bound indexing issue. Generate a symbol table text file from your code. If you're using elf, you can use

Code: Select all

nm kernel.elf | grep -ve '^ ' | cut -d ' ' -f 1,3 >kernel.sym
(note there's a space in both strings). Otherwise it's just a simple text file, with hex addresses (without the "0x" prefix), a space, and a symbol name in each line. Example:

Code: Select all

0000000000007C00 bootsector
0000000000100080 _start
0000000000100100 pic_eoi
0000000000100200 ksleep
...
Then load that file into bochs with

Code: Select all

<bochs:0> ldsym global "kernel.sym"
After that bochs disassembler will tell you exactly in which function the IOAPIC write occurs (address 0000000000101278 looks like inside your kernel, or if not, then there's your problem: you're executing random garbage in memory).

Alternatively you could do

Code: Select all

objdump -d kernel.elf | less
search for the address "1278:", and scroll up to see the function name.

Btw, IOAPIC is not the same as APIC, and no PIC cannot be used as APIC (nor as IOAPIC), but they can be used as PIC if emulation is enabled (which is usually the case for compatiblity).

Cheers,
bzt
chez4
Posts: 5
Joined: Sat Feb 23, 2019 1:09 pm
Libera.chat IRC: chez4

Re: bochs I/O apic write with len=2 (should be 4)

Post by chez4 »

Thanks for the reply

After running objdump like you said, I could see that the 1278: address was in the vga_fill function, and by not calling it fixes the problem.
Here is the code for vga_fill:

Code: Select all

void vga_fill(unsigned char colour)
{
    for (x = 0; x < 80; x++) {
        for (y = 0; y < 25; y++) {
            vga_text[y * 80 + x] = ' ' | ((colour | (colour << 4)) << 8);
        }
    }
}
I'm guessing i've done this bit wrong: vga_text[y * 80 + x], and y * 80 + x writes to some random address which so happens to be one of the IOAPIC registers.

I also noticed that none of the sleep functions I call seem to work in bochs.
nullplan
Member
Member
Posts: 1801
Joined: Wed Aug 30, 2017 8:24 am

Re: bochs I/O apic write with len=2 (should be 4)

Post by nullplan »

With the given code, you can at most access 4000 bytes beyond the start of the vga_text array. Which means vga_text is the most likely culprit, probably not initialized. By my calculation, it must have been at least 0xfebff0c0 for you to get the error from the OP. Forgot to map the VGA memory?
Carpe diem!
User avatar
bzt
Member
Member
Posts: 1584
Joined: Thu Oct 13, 2016 4:55 pm
Contact:

Re: bochs I/O apic write with len=2 (should be 4)

Post by bzt »

chez4 wrote:After running objdump like you said, I could see that the 1278: address was in the vga_fill function, and by not calling it fixes the problem.
There you go, you can be sure now that the problem is in vga_fill. Good job!

I would recommend to generate the symbol map from your makefile anyway, it'll came very handy later. It's really helping when bochs debugger shows your function name. You can make bochs to automatically load it by adding this line to bochsrc

Code: Select all

debug_symbols: file=kernel.sym, offset=0
chez4 wrote:I also noticed that none of the sleep functions I call seem to work in bochs.
Bochs can be configured to "drop" clock cycles (so to speak) for performance reasons. I use

Code: Select all

clock: sync=realtime, time0=local, rtc_sync=1
in the bochrc file which works for me (well, PIT is still not accurate, but close).

Cheers,
bzt
chez4
Posts: 5
Joined: Sat Feb 23, 2019 1:09 pm
Libera.chat IRC: chez4

Re: bochs I/O apic write with len=2 (should be 4)

Post by chez4 »

bzt wrote:Bochs can be configured to "drop" clock cycles (so to speak) for performance reasons. I use

Code: Select all

clock: sync=realtime, time0=local, rtc_sync=1
in the bochrc file which works for me (well, PIT is still not accurate, but close).
Thanks, this fixed the sleep function :D , although sleeping is a bit weird and laggy, probably due to the PIT still not being accurate like you said.
It also for some reason fixed the vga_fill function. Not sure why.
Octocontrabass
Member
Member
Posts: 5586
Joined: Mon Mar 25, 2013 7:01 pm

Re: bochs I/O apic write with len=2 (should be 4)

Post by Octocontrabass »

chez4 wrote:It also for some reason fixed the vga_fill function. Not sure why.
Sounds like it's something dependent on the speed of the emulated hardware. The first thing that comes to mind is IRQ handlers - they can change the CPU state when they run, and they'll have different timing depending on your emulator's configuration.

In fact...

Code: Select all

global isr32
isr32:
    inc    dword [pit_clock]
    call   eoi
    iret
This ISR for handling IRQ0 doesn't save any registers. Will that call to "eoi" change any of them?

Code: Select all

global eoi
eoi:
    mov    al, 0x20
    out    0x20, al
    ret
Oops!
Post Reply