【发布时间】:2016-11-20 15:17:19
【问题描述】:
我正在尝试在 Qemu 上启用 x86 架构上的分页。
但是这段代码会导致三重错误。
此代码段在保护模式下执行。
代码:https://github.com/mridulv/simpleOperatingSystem
这是内核入口文件
[bits 32]
[extern main]
[extern main2]
[extern page_table]
call main
lea ECX, [page_table - 0xC0000000]
mov CR3, ECX
mov ECX, CR0
or ECX, 0x80000000
mov CR0, ECX
;lea ECX, [StartInHigherHalf]
;jmp ECX
;StartInHigherHalf:
; call main2
jmp $
这是创建页面目录表的内核文件。
unsigned int page_table[1024] __attribute__((aligned(4096)));;
void set_page_tables() {
unsigned int pages_entry[1024 * 1024] __attribute__((aligned(4096)));
unsigned int KERNEL_VIRTUAL_OFFSET = 0xC0000000;
unsigned int KERNEL_FIRST_VIRTUAL_ADDRESS = 0xC0000000 >> 12;
int numPageTables = 4;
int numPagesInPageTable = 1024;
int numPageTableEntriesInPageDirectory = 1024;
int totalPages = numPageTables * numPagesInPageTable;
unsigned int index = 0;
unsigned int *pages_ptr = (unsigned int *)(pages_entry - KERNEL_VIRTUAL_OFFSET);
unsigned int *page_table_ptr = (unsigned int *)(pages_entry - KERNEL_VIRTUAL_OFFSET);
unsigned int positionAndFlags = 7;
while (index < totalPages) {
pages_ptr[index] = positionAndFlags;
index = index + 1;
positionAndFlags += 4096;
}
positionAndFlags = 7;
index = KERNEL_FIRST_VIRTUAL_ADDRESS;
unsigned int totalPagesLeft = KERNEL_FIRST_VIRTUAL_ADDRESS + totalPages;
while (index < totalPagesLeft) {
pages_ptr[index] = positionAndFlags;
index = index + 1;
positionAndFlags += 4096;
}
positionAndFlags = (unsigned int)&pages_ptr[0];
positionAndFlags = positionAndFlags | 7;
index = 0;
while (index < numPageTableEntriesInPageDirectory) {
page_table_ptr[index] = positionAndFlags;
index = index + 1;
positionAndFlags += 4096;
}
}
void main() {
char* video_memory = (char*) 0xb8000;
*video_memory = 'X';
set_page_tables();
}
void main2() {
char* video_memory = (char*) 0xb8004;
*video_memory = 'Y';
// __asm__
// (
// "leal (page_table, ), %ecx\n\t" // 0xC0000000 = KERNEL_VIRTUAL_BASE
// "movl %cr3, %ecx"
// );
}
链接器.ld
SECTIONS {
. = 0xC0100000;
.text : AT(ADDR(.text) - 0xC0000000) {
*(.text);
}
. = ALIGN(0x1000);
.bss : AT(ADDR(.bss) - 0xC0000000) {
*(.text);
}
. = ALIGN(0x1000);
.data : AT(ADDR(.data) - 0xC0000000) {
*(.text);
}
}
【问题讨论】:
-
一些显而易见的事情。我可以告诉您,您没有启用A20 line。在
boot.asm中,您保存DL(启动驱动器),然后立即放置数据(PROTECTED_MODE_STR和BEGIN_STR)。 CPU 将尝试执行数据。将您的数据放在所有代码之后(尝试将它们移动到引导签名之前)。另一个大问题是您将内核加载到内存中的 0x1000。您的链接器脚本使用0xC0100000的基础。这假设您的内核在 0x100000 加载,但您的内核在 0x1000。所以在你的情况下你可能需要0xC0001000 -
我什至还没有看到其他内容。但是,在尝试启用分页的代码之前,您已经遇到了很多问题。
-
我还建议将
lea ECX, [page_table - 0xC0000000]更改为mov ECX, page_table - 0xC0000000 -
由于您的代码最初在内存地址 0x1000 上运行,为了在启用分页后执行 0x1000 到 0x1FFF 区域中的代码,您需要标识映射页面。这意味着将物理地址为 0x1000 的页面映射到 0x1000 的虚拟内存地址。不这样做会使启用分页后的代码失败
-
@MichaelPetch 我在 4kb (0x1000) 处对齐不同的部分,这与在 0x100000 处加载不同