pob*_*oby 3 assembly caching x86-64 osdev apic
我正在虚拟机中编写自定义操作系统,并且无法从IOAPIC mmio寄存器中成功编写和读取.即它似乎忽略了索引寄存器写入.加载后R8
与IOAPIC基地址(从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
Run Code Online (Sandbox Code Playgroud)
但是ioapic_read将始终返回写入的最后一个值(通过ioapic_write),而不管使用的索引如何.我有身份分页设置使用0x9B我认为应该禁用缓存.
我试过pause
在每个mov
s 之后使用.没有帮助.在mfence
s之间试过mov
.没有帮助.
我已确认0xFEC00000
地址已成功标识映射.
看起来还有一些缓存正在进行中.我错过了什么?
编辑
我发现这不是一个缓存问题,而是一些很奇怪的东西 - 至少对我无知的大脑而言.我的身份分页,按需工作,以便页面错误将在表中生成正确的物理页面.
这似乎有效,但在IOAPIC mmio寄存器的情况下,我需要通过在尝试使用它之前对0xFEC00000地址执行虚拟读取或写入来导致页面错误.更奇怪的是,我需要先做这个虚拟读取足够的指令,否则它不起作用.例如
这个工作!
mov eax, [os_IOAPICAddress]
mov dword[rax], 0
mov r8, rax
.
.
.
call ioapic_read
Run Code Online (Sandbox Code Playgroud)
......这不!
mov eax, [os_IOAPICAddress]
mov r8, rax
mov dword[rax], 0
.
.
.
call ioapic_read
Run Code Online (Sandbox Code Playgroud)
我怀疑是一个流水线/序列化的问题,但我真的很想知道为什么我需要在将地址写入表之前将其写入表中,然后才能在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
Run Code Online (Sandbox Code Playgroud)
鉴于原始代码,它看起来好像是正确设置页面目录条目位以标记MMIO区域不可访问.我确信还有其他一些问题.随后的编辑,您向我们展示了您的页面错误处理程序pageFault_identity_0x0
:
pageFault_identity_0x0E:
pop r8
push rsi rdi rax rcx rdx r9
Run Code Online (Sandbox Code Playgroud)
当处理器将控制转移到此页面错误异常处理程序时,它将作为参数传递堆栈顶部的错误代码.问题是您使用错误号替换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
Run Code Online (Sandbox Code Playgroud)