【发布时间】:2016-10-02 23:34:21
【问题描述】:
我正在 virtualbox 中编写自定义操作系统,但在从 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,我认为应该禁用缓存。
我尝试在每个movs 之后使用pause。没有帮助。在movs 之间尝试了mfences。没有帮助。
我已确认0xFEC00000 地址已成功进行身份映射。
看起来仍有一些缓存正在进行。我错过了什么?
编辑
我发现这不是缓存问题,而是一些很陌生的东西——至少对我无知的大脑来说是这样。我的身份分页按需工作,因此页面错误将在表中生成正确的物理页面。
这似乎有效,但在 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
【问题讨论】:
-
这些函数
ioapic_read和ioapic_write是从C调用的吗? -
不,整件事情都在组装中
-
如果您在写入寄存器之前读取它们,您会得到合理的值吗?大多数 IOAPIC 寄存器都是完全可写的,您是否尝试过编写像 IOAPICVER 或 IOAPICARB 这样的 RO 寄存器?
-
我知道您要么有错误,要么您使用的文件大于 4kb。你的问题并没有说它只是说 0x9b 没有真正的上下文。
-
查看您的页面错误处理程序。我是不是弄错了,但你是在破坏 R8 的内容而不恢复它吗?您将错误代码弹出到 R8 中,但这意味着 R8 的先前值已被丢弃。当您的故障处理程序终止时,R8 很可能不再具有原始值。
标签: caching assembly x86-64 osdev apic