1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/rcore-os-rCore-Tutorial

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
part-2.md 9 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
gitlife-traslator Отправлено 29.11.2024 21:14 7f8a4fd

Модификация ядра

Предыдущая реализация ядра не включала механизм таблиц страниц, и ядро работало непосредственно в физическом адресном пространстве. Хотя это было относительно просто, для поддержки нескольких пользовательских процессов и обеспечения изоляции и параллельного выполнения в ядре, мы должны сначала использовать изученные знания о таблицах страниц, чтобы перенести среду выполнения ядра из физического адресного пространства в виртуальное адресное пространство, подготавливая основу для будущих функций.

Более конкретно, мы хотим разместить код ядра в виртуальном адресном пространстве, начиная с адреса 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:

  • В состоянии физической памяти код OpenSBI находится в диапазоне [0x80000000,0x80200000), а код ядра — в непрерывном блоке физической памяти, начинающемся с 0x80200000;
  • Состояние процессора: находится в режиме S, регистр satp установлен в режим Bare, что означает прямой доступ к физической памяти через физические адреса для операций чтения и записи. PC указывает на первую инструкцию ядра по адресу 0x80200000;
  • Указатель стека sp.

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/rcore-os-rCore-Tutorial.git
git@api.gitlife.ru:oschina-mirror/rcore-os-rCore-Tutorial.git
oschina-mirror
rcore-os-rCore-Tutorial
rcore-os-rCore-Tutorial
master