我在virtualbox中编写了一个自定义os,无法从IOAPIC mmio寄存器成功写入和读取。即似乎忽略了索引寄存器的写操作。在使用IOAPIC基地址(由ACPI枚举确定为0xFEC00000)加载R8之后,我使用以下例程进行读取/写入:

; -----------------------------------------------------------------------------
; IN :  RAX = ioapic address, EBX = index register
; OUT:  ECX = return value
ioapic_read:
    mov [r8], ebx
    mov ecx, [r8 + 0x10]
    ret
; -----------------------------------------------------------------------------
; IN :  RAX = ioapic address, EBX = index register, ECX = value
; OUT:  -
ioapic_write:
    mov [r8], ebx
    mov [r8 + 0x10], ecx
    ret


但是ioapic_read将始终返回最后写入的值(通过ioapic_write),而与所使用的索引无关。我具有使用0x9B的身份分页设置,我认为应禁用缓存。

我尝试在每个pause之后使用mov。没帮助在mfence之间尝试了。没帮助

我已经确认mov地址已成功进行身份映射。

似乎仍在进行某些缓存。我想念什么?

编辑

我发现这不是缓存问题,而是很多陌生的东西-至少对于我无知的大脑而言。我的身份分页按需工作,因此页面错误将在表中生成正确的物理页面。

这似乎可行,但是对于IOAPIC mmio寄存器,在尝试使用0xFEC00000地址之前,我需要通过对0xFEC00000地址进行虚拟读取或写入来引起页错误。甚至更奇怪的是,我需要做这个假人事先阅读足够的指令,否则它不起作用。例如

这个工作!

 mov eax, [os_IOAPICAddress]
 mov dword[rax], 0
 mov r8, rax
 .
 .
 .
 call ioapic_read


...这不是!

 mov eax, [os_IOAPICAddress]
 mov r8, rax
 mov dword[rax], 0
 .
 .
 .
 call ioapic_read


我怀疑存在流水线/序列化问题,但是我真的很想了解为什么我需要在MMIO寄存器中使用该地址之前将错误的地址分页到表中,以及为什么我需要事先做得足够多。在后一种情况下,如何修复它以便序列化,这样我就不必担心。

我的身份分页例程:

pageFault_identity_0x0E:
    pop r8
    push rsi rdi rax rcx rdx r9

    test r8, 1
    jnz exception_gate_14
    mov rdx, cr2                                   ; faulting address
    shr rdx, 39
    and rdx, 0x1FF                                 ; get 9 bit index

    mov rdi, cr3
    lea rsi, [rdi + rdx*8]
    mov rdi, [rsi]
    test rdi, 1
    jnz @f
    call set_new_page_table
@@:
    shr rdi, 12                                     ; get rid of flags
    shl rdi, 12

    mov rdx, cr2
    shr rdx, 30                                     ; get 9 bit index
    and rdx, 0x1FF

    lea rsi, [rdi + rdx*8]
    mov rdi, [rsi]
    test rdi, 1
    jnz @f
    call set_new_page_table
@@:
    shr rdi, 12                                     ; get rid of flags
    shl rdi, 12

    mov rdx, cr2
    shr rdx, 21
    mov rax, rdx
    and rdx, 0x1FF                                  ; get 9 bit index
    lea rsi, [rdi + rdx*8]

    shl rax, 21
    or rax, 0x83
    mov [rsi], rax

    shr rax, 21
    shl rax, 21

    pop r9 rdx rcx rax rdi rsi
    iretq
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;
; IN:   rsi = address of blank entry
; OUT:  rdi = base address of new table, changes rax & rcx
;
set_new_page_table:                                ; make table, get it, zero it, insert base into previous table
    movzx rdi, [page_table_count]
    shl rdi, 12
    add rdi, NEW_PAGE_TABLES

    CLEAR_BLOCK rdi, 0x200                     ; clears 4096 bytes in rdi, returns rdi + 4096

    sub rdi, 0x1000
    lea rax, [rdi + 0x3]                              ; table base address
    mov [rsi], rax
    inc [page_table_count]
    ret

最佳答案

在给出原始代码的情况下,好像您已正确设置页面目录入口位以将MMIO区域标记为不可缓存。我确信还有其他问题。在随后的编辑中,您向我们展示了页面错误处理程序pageFault_identity_0x0

pageFault_identity_0x0E:
    pop r8
    push rsi rdi rax rcx rdx r9


当处理器将控制权转移到此页面错误异常处理程序时,它将在堆栈的顶部传递错误代码作为参数。问题是您用错误号替换了R8的内容,而没有保存然后恢复寄存器。

您必须修改异常处理程序以保留R8,将内容从错误编号所在的正确堆栈偏移中移到R8中。只要记住确保错误号不再在IRETQ之前位于堆栈的顶部即可。

从页面错误返回后,您得到的奇怪行为可能与R8无法正确还原直接相关。



可能有效的解决方案是:

pageFault_identity_0x0E:
    push rsi
    push rdi
    push rax
    push rcx
    push rdx
    push r9
    push r8

    mov r8, [rsp+7*8]    ; Error Code is at offset RSP+7*8 after all the pushes
    ; Do exception handling work here

    pop r8
    pop r9
    pop rdx
    pop rcx
    pop rax
    pop rdi
    pop rsi

    add rsp, 8           ; Remove the error code
    iretq

09-10 12:23
查看更多