Предыдущая реализация ядра не включала механизм таблиц страниц, и ядро работало непосредственно в физическом адресном пространстве. Хотя это было относительно просто, для поддержки нескольких пользовательских процессов и обеспечения изоляции и параллельного выполнения в ядре, мы должны сначала использовать изученные знания о таблицах страниц, чтобы перенести среду выполнения ядра из физического адресного пространства в виртуальное адресное пространство, подготавливая основу для будущих функций.
Более конкретно, мы хотим разместить код ядра в виртуальном адресном пространстве, начиная с адреса 0xffffffff80200000. Это означает, что вся структура ядра, которая ранее начиналась с адреса 0x80200000, теперь смещена на адрес 0xffffffff80200000; отображение определяется как виртуальный адрес минус смещение 0xffffffff00000000, равное исходному физическому адресу. Конечно, линейное смещение не является единственным способом отображения, но на данный момент всё содержимое кода и данных ядра отображается линейно между виртуальным и физическим пространством.
Поэтому необходимо внести некоторые изменения в исходный linker script и параметры управления физической памятью.
/* Linker Script 语法可以参见:http://www.scoberlin.de/content/media/http/informatik/gcc_docs/ld_3.html */
/* 目标架构 */
OUTPUT_ARCH(riscv)
/* 执行入口 */
ENTRY(_start)
/* 数据存放起始地址 */
BASE_ADDRESS = 0xffffffff80200000; /* 修改为虚拟地址 */
SECTIONS
{
/* . 表示当前地址(location counter) */
. = BASE_ADDRESS;
/* start 符号表示全部的开始位置 */
kernel_start = .;
/* 加入对齐 */
. = ALIGN(4K);
text_start = .;
/* .text 字段 */
.text : {
/* 把 entry 函数放在最前面 */
*(.text.entry)
/* 要链接的文件的 .text 字段集中放在这里 */
*(.text .text.*)
}
/* 加入对齐 */
. = ALIGN(4K);
rodata_start = .;
/* .rodata 字段 */
.rodata : {
/* 要链接的文件的 .rodata 字段集中放在这里 */
*(.rodata .rodata.*)
}
/* 加入对齐 */
. = ALIGN(4K);
data_start = .;
/* .data 字段 */
.data : {
/* 要链接的文件的 .data 字段集中放在这里 */
*(.data .data.*)
}
/* 加入对齐 */
. = ALIGN(4K);
bss_start = .;
/* .bss 字段 */
.bss : {
/* 要链接的文件的 .bss 字段集中放在这里 */
*(.sbss .bss .bss.*)
}
/* 结束地址 */
/* 加入对齐 */
. = ALIGN(4K);
kernel_end = .;
}
Во-первых, в linker script мы изменили базовый адрес на виртуальный, а также внесли некоторые другие изменения, такие как выравнивание каждого сегмента данных до 4 КБ. Один виртуальный 4КБ-страничный блок не будет содержать два сегмента, что делает атрибут страницы определённым. Например, без выравнивания сегменты только для чтения, такие как .rodata и .data, могут находиться на одной странице, но таблица страниц должна быть заполнена такими атрибутами, как возможность записи, и их необходимо разделить для маркировки атрибутов.
Соответствующие изменения вносятся в os/src/memory/config.rs
, где KERNEL_END_ADDRESS
изменяется на виртуальный адрес с добавлением смещения:
lazy_static! {
/// 内核代码结束的地址,即可以用来分配的内存起始地址
///
/// 因为 Rust 语言限制,我们只能将其作为一个运行时求值的 static 变量,而不能作为 const
pub static ref KERNEL_END_ADDRESS: VirtualAddress = VirtualAddress(kernel_end as usize);
}
/// 内核使用线性映射的偏移量
pub const KERNEL_MAP_OFFSET: usize = 0xffff_ffff_0000_0000;
Аналогично предыдущей главе, мы также инкапсулировали виртуальный адрес и номер виртуальной страницы, поддерживая такие функции, как VirtualAddress::from(PhysicalAddress)
и преобразование признаков (например, операции добавления и вычитания смещения), которые более ориентированы на синтаксис Rust. Здесь мы не будем подробно описывать реализацию, но вы можете обратиться к os/src/memory/address.rs
для получения дополнительной информации.
Последним шагом является информирование процессора RISC-V о наших изменениях, то есть нам нужно выполнить преобразование из режима доступа к физической памяти в режим доступа к виртуальной памяти во время запуска, одновременно это означает, что нам нужно написать простую таблицу страниц для завершения этого линейного сопоставления:
# 操作系统启动时所需的指令以及字段
#
# 我们在 linker.ld 中将程序入口设置为了 _start,因此在这里我们将填充这个标签
# 它将会执行一些必要操作,然后跳转至我们用 rust 编写的入口函数
#
# 关于 RISC-V 下的汇编语言,可以参考 https://github.com/riscv/riscv-asm-manual/blob/master/riscv-asm.md
# %hi 表示取 [12,32) 位,%lo 表示取 [0,12) 位
.section .text.entry
.globl _start
# 目前 _start 的功能:将预留的栈空间写入 $sp,然后跳转至 rust_main
_start:
# 计算 boot_page_table 的物理页号
lui t0, %hi(boot_page_table)
li t1, 0xffffffff00000000
sub t0, t0, t1
srli t0, t0, 12
# 8 << 60 是 satp 中使用 Sv39 模式的记号
li t1, (8 << 60)
or t0, t0, t1
# 写入 satp 并更新 TLB
csrw satp, t0
sfence.vma
# 加载栈地址
lui sp, %hi(boot_stack_top)
addi sp, sp, %lo(boot_stack_top)
# 跳转至 rust_main
lui t0, %hi(rust_main)
addi t0, t0, %lo(rust_main)
jr t0
# 回忆:bss 段是 ELF 文件中只记录长度,而全部初始化为 0 的一段内存空间
# 这里声明字段 .bss.stack 作为操作系统启动时的栈
.section .bss.stack
.global boot_stack
boot_stack:
# 16K 启动栈大小
.space 4096 * 16
.global boot_stack_top
boot_stack_top:
# 栈结尾
# 初始内核映射所用的页表
.section .data
.align 12
boot_page_table:
.quad 0
.quad 0
# 第 2 项:0x8000_0000 -> 0x8000_0000,0xcf 表示 VRWXAD 均为 1
.quad (0x80000 << 10) | 0xcf
.zero 507 * 8
# 第 510 项:0xffff_ffff_8000_0000 -> 0x8000_0000,0xcf 表示 VRWXAD 均为 1
.quad (0x80000 << 10) | 0xcf
.quad 0
Давайте ещё раз рассмотрим ситуацию после запуска OpenSBI:
satp
установлен в режим Bare, что означает прямой доступ к физической памяти через физические адреса для операций чтения и записи. PC указывает на первую инструкцию ядра по адресу 0x80200000;sp
.Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )