Caching 写入mmio寄存器时如何避免缓存?

Caching 写入mmio寄存器时如何避免缓存?,caching,assembly,x86-64,osdev,apic,Caching,Assembly,X86 64,Osdev,Apic,我正在virtualbox中编写自定义操作系统,在从IOAPIC mmio寄存器成功写入和读取时遇到问题。i、 它似乎忽略了索引寄存器写入。在使用IOAPIC基址加载R8后(根据ACPI枚举确定为0xFEC00000),我使用以下例程进行读/写: ; ----------------------------------------------------------------------------- ; IN : RAX = ioapic address, EBX = index regi

我正在virtualbox中编写自定义操作系统,在从IOAPIC mmio寄存器成功写入和读取时遇到问题。i、 它似乎忽略了索引寄存器写入。在使用IOAPIC基址加载
R8
后(根据ACPI枚举确定为0xFEC00000),我使用以下例程进行读/写:

; -----------------------------------------------------------------------------
; 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_读取将始终返回(通过ioapic_写入)写入的最后一个值,而与所使用的索引无关。我有使用0x9B的身份分页设置,我认为应该禁用缓存

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

我已确认
0xFEC00000
地址已成功进行身份映射

看起来仍有一些缓存在进行。我错过了什么

编辑

我发现这不是缓存问题,而是一个非常奇怪的问题——至少对我无知的大脑来说是如此。我的身份分页,按需工作,这样页面错误将在表中生成正确的物理页面

这似乎是可行的,但在IOAPIC mmio寄存器的情况下,我需要在尝试使用0xFEC000000地址之前,通过对其进行虚拟读取或写入来导致页面错误。更奇怪的是,我需要做这个假人,在之前阅读足够的指令,否则它就不能工作。e、 g

这管用

 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\u identity\u 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

Michael解决了这个问题,但为了完整起见,我将发布我的最终实现

pageFault_identity_0x0E:
    test qword[rsp], 1
    jnz exception_gate_14
    add rsp, 8

    push rsi rdi rax rcx rdx
    mov rdx, cr2                                   ; faulting address
    .
    .
    .
    pop rdx rcx rax rdi rsi
    iretq

编辑:已编辑以删除
xchg

这些函数是从C调用的
ioapic\u read
ioapic\u write
吗?不,整个过程都在汇编中如果在写入寄存器之前读取它们,会得到合理的值吗?大多数IOAPIC寄存器都是完全可写的,您是否尝试过编写像IOAPICVER或IOAPICARB这样的RO寄存器?我知道您可能有bug,或者使用了大于4kb的寄存器。你的问题没有说它只是说0x9b没有真正的上下文。看看你的页面错误处理程序。我弄错了,但你是在破坏R8的内容而不恢复它吗?您将错误代码弹出到R8中,但这意味着R8的先前值已被破坏。当您的错误处理程序终止R8时,R8很可能不再具有原始值。我浪费了2天的时间试图找到它。我彻底地调试了那个分页例程,我确信它不是罪魁祸首。但不知怎的,我错过了流行音乐R8。这是我的一个负担:)没问题,我真的很高兴你添加了页面错误处理程序代码。我怀疑这就是问题所在。很高兴现在对你有用。内核开发和调试可能是一项棘手的工作。您可能只是需要一组新的眼睛来观察它。我避免使用带有内存操作数的XCHG,主要是因为它将导致隐式锁,从而导致相应缓存线的独占所有权和性能损失。不过,带有两个寄存器的XCHG没有这个问题。《英特尔优化指南》有这样的建议:“尽量减少在内存位置上使用xchg指令”欢呼。我不知道。我已经修好了。