Help with sending text to COM1

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
User avatar
eryjus
Member
Member
Posts: 286
Joined: Fri Oct 21, 2011 9:47 pm
Libera.chat IRC: eryjus
Location: Tustin, CA USA

Help with sending text to COM1

Post by eryjus »

After lots of debugging, I have found an issue with my code to write debugging details to COM1. When this code is enabled, I end up with a page fault under Bochs (QEMU works just fine).

More to the point, when I disable the in and out lines in the code below, it works (I have to disable both lines for it to work). I used the Serial_Ports wiki sample code as inspiration.

Code: Select all

DbgConsolePutChar:
                push        rbp                     ; save the caller's frame
                mov.q       rbp,rsp                 ; create our own frame
                push        rdx                     ; save rdx

.loop:          mov.q       rdx,(DBG_PORT+5)        ; we want the LSR
                in          al,dx                   ; read the port
                test.b      al,0x20                 ; mask out the transmit buffer flag
                jz          .loop                   ; loop until it is empty

                mov.q       rdx,(DBG_PORT+0)        ; we want the serial port
                mov.q       rax,[rbp+16]            ; get the character to write
                cmp.q       rax,13                  ; is the char a <CR>?
                jne         .put                    ; if not, put it on the serial port

                mov.q       rax,10                  ; make it a LF

.put:           out         dx,al                   ; write the char to the serial port

                pop         rdx                     ; restore rdx
                pop         rbp                     ; restore caller's frame
                ret
My initialization code:

Code: Select all

DbgConsoleInit:
                push        rbp                     ; save the caller's frame
                mov.q       rbp,rsp                 ; create our own frame
                push        rdx                     ; save rdx

                xor.q       rax,rax                 ; clear the entire rax register
                mov.q       rdx,(DBG_PORT+1)        ; we want the IER port
                out         dx,al                   ; disable interrupts

                mov.b       al,0x80                 ; need to setup the baud rate divisor
                mov.q       rdx,(DBG_PORT+3)        ; we want the LCR port
                out         dx,al                   ; enable the DLAB registers

                mov.b       al,0x03                 ; the divisor is 3 for 38.4K baud
                mov.q       rdx,(DBG_PORT+0)        ; we want the base port
                out         dx,al                   ; send the lo byte to the UART

                mov.b       al,0x00                 ; set the upper byte
                mov.q       rdx,(DBG_PORT+1)        ; we want the base port + 1
                out         dx,al                   ; send the hi byte to the UART

                mov.b       al,0x03                 ; disable DLAB, set bits to 8-N-1
                mov.q       rdx,(DBG_PORT+3)        ; we want the LCR port
                out         dx,al                   ; set the line setup

                mov.b       al,0xc7                 ; we don't need a FIFO
                mov.q       rdx,(DBG_PORT+2)        ; we want the FCR port
                out         dx,al                   ; set the FIFO parms

                mov.b       al,0x0b                 ; we don't want interrupts
                mov.q       rdx,(DBG_PORT+4)        ; we want the MCR port
                out         dx,al                   ; No interrupts

                pop         rdx                     ; restore rdx
                pop         rbp                     ; restore caller's frame
                ret
Any help would be greatly appreciated.
Adam

The name is fitting: Century Hobby OS -- At this rate, it's gonna take me that long!
Read about my mistakes and missteps with this iteration: Journal

"Sometimes things just don't make sense until you figure them out." -- Phil Stahlheber
User avatar
xenos
Member
Member
Posts: 1121
Joined: Thu Aug 11, 2005 11:00 pm
Libera.chat IRC: xenos1984
Location: Tartu, Estonia
Contact:

Re: Help with sending text to COM1

Post by xenos »

So which instruction exactly is causing the page fault, and what can you deduce from RIP and CR2 of the page fault?
Programmers' Hardware Database // GitHub user: xenos1984; OS project: NOS
User avatar
eryjus
Member
Member
Posts: 286
Joined: Fri Oct 21, 2011 9:47 pm
Libera.chat IRC: eryjus
Location: Tustin, CA USA

Re: Help with sending text to COM1

Post by eryjus »

Thank you for your reply.

OK, the page fault is happening in my function MmuIsPageMapped(). In particular, with the following line:

Code: Select all

test.q      [rbx+rax],0x01
RBX = 0xffff fff8 0100 0000
RAX = 0x0000 0000 0000 0100
CR2 = 0xffff fff8 0100 0100

Which confirms the address. PML4[255] is recursively mapped and this address is within my Page Table section.

CR3 confirms my PML4 is at physical 0x400000. Dumping the physical memory at 0x400000 reveals that the upper end of the PML4 table is corrupt. So this is the source of my problem. What I don't understand is why this only presents itself when the 2 lines mentioned before are enabled and it does not present itself at all with QEMU and with the lines are disabled in Bochs.

Any tricks from your experience on how I might be able to narrow this down quickly? My only thought is a bunch of Bochs Magic Breakpoints and lots of memory dumps at each breakpoint.
Adam

The name is fitting: Century Hobby OS -- At this rate, it's gonna take me that long!
Read about my mistakes and missteps with this iteration: Journal

"Sometimes things just don't make sense until you figure them out." -- Phil Stahlheber
User avatar
eryjus
Member
Member
Posts: 286
Joined: Fri Oct 21, 2011 9:47 pm
Libera.chat IRC: eryjus
Location: Tustin, CA USA

Re: Help with sending text to COM1

Post by eryjus »

OK, I figured it out, or at least it is not crashing anymore. I had a bad stack setup for my spurious interrupt handler. Apparently, Bochs generates a spurious interrupt running my code when QEMU does not. Any suggestions on how to determine why I am getting a spurious interrupt?

I have my unused -- well I though they were unused -- TSS stacks set to 0. Therefore when I push my first entry on the stack, I would trash my paging tables. Which leads me to another question: would I be better served to use an address that I know will always generate a page fault (with a known value) rather than 0? I would think it would make troubleshooting situations like this easier.... Thoughts?
Adam

The name is fitting: Century Hobby OS -- At this rate, it's gonna take me that long!
Read about my mistakes and missteps with this iteration: Journal

"Sometimes things just don't make sense until you figure them out." -- Phil Stahlheber
Octocontrabass
Member
Member
Posts: 5588
Joined: Mon Mar 25, 2013 7:01 pm

Re: Help with sending text to COM1

Post by Octocontrabass »

eryjus wrote:OK, I figured it out, or at least it is not crashing anymore. I had a bad stack setup for my spurious interrupt handler. Apparently, Bochs generates a spurious interrupt running my code when QEMU does not. Any suggestions on how to determine why I am getting a spurious interrupt?
If it's IRQ7 or IRQ15, you are receiving it because Bochs imitates hardware that generates spurious interrupts.
User avatar
eryjus
Member
Member
Posts: 286
Joined: Fri Oct 21, 2011 9:47 pm
Libera.chat IRC: eryjus
Location: Tustin, CA USA

Re: Help with sending text to COM1

Post by eryjus »

OK, I re-read the portion of the wiki, and I still have questions.

First, does QEMU not emulate spurious interrupts?

Second, I'm under the impression that a spurious interrupt is clearly divided into 2 separate causes: 1) a programming issue such as issuing an EOI when not required, or 2) a performance issues such as not responding to an interrupt in a timely manner. Is my understanding correct?
Adam

The name is fitting: Century Hobby OS -- At this rate, it's gonna take me that long!
Read about my mistakes and missteps with this iteration: Journal

"Sometimes things just don't make sense until you figure them out." -- Phil Stahlheber
Octocontrabass
Member
Member
Posts: 5588
Joined: Mon Mar 25, 2013 7:01 pm

Re: Help with sending text to COM1

Post by Octocontrabass »

eryjus wrote:First, does QEMU not emulate spurious interrupts?
It does, but the conditions necessary to trigger them may be slightly different.
eryjus wrote:Second, I'm under the impression that a spurious interrupt is clearly divided into 2 separate causes: 1) a programming issue such as issuing an EOI when not required, or 2) a performance issues such as not responding to an interrupt in a timely manner. Is my understanding correct?
From what I understand, they usually occur when there's a race condition in the hardware. For example, if you mask interrupts at the CPU, receive an interrupt through the PIC, and then mask that interrupt in the PIC, the CPU still thinks there's an interrupt pending and will attempt to handle it once you unmask interrupts on the CPU. The PIC can't cancel the in-progress interrupt, so you receive a spurious interrupt.

There are a few threads around here with people asking similar questions, so be sure to look over those in case there's something I've missed.
Post Reply