From 735282b978b76a6b2e4c5d3ebc60caf16c5964d2 Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Mon, 10 Jul 2023 13:46:57 +0000 Subject: [PATCH 01/68] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20SBI=20Spec=202.0-rc1?= =?UTF-8?q?=20=E7=BF=BB=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../20230710-sbi-specification-translation.md | 1728 +++++++++++++++++ .../sbi-specification-translation/fig1.jpg | Bin 0 -> 9376 bytes .../sbi-specification-translation/fig2.jpg | Bin 0 -> 15500 bytes .../sbi-specification-translation/fig3.jpg | Bin 0 -> 21174 bytes 4 files changed, 1728 insertions(+) create mode 100644 articles/20230710-sbi-specification-translation.md create mode 100644 articles/images/sbi-specification-translation/fig1.jpg create mode 100644 articles/images/sbi-specification-translation/fig2.jpg create mode 100644 articles/images/sbi-specification-translation/fig3.jpg diff --git a/articles/20230710-sbi-specification-translation.md b/articles/20230710-sbi-specification-translation.md new file mode 100644 index 0000000..7995530 --- /dev/null +++ b/articles/20230710-sbi-specification-translation.md @@ -0,0 +1,1728 @@ +> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [tables]`
` +> Acknowledgements: 山东大学智能创新研究院 [RISC-V SBI-1.0.0 版本 中文](https://zhuanlan.zhihu.com/p/634337322) + +# RISC-V SBI 翻译 + +## 更改日志 + +### 版本 2.0-rc1 + +- 添加了共享内存物理地址范围参数的通用描述 +- 添加了 SBI 调试控制台扩展 +- 放宽了 SBI PMU 固件计数器的计数器宽度要求 +- 在 SBI PMU 扩展中添加了 sbi_pmu_counter_fw_read_hi() +- 为 SBI 实现特定固件事件保留空间 +- 添加了 SBI 系统挂起扩展 +- 添加了 SBI CPPC 扩展 +- 阐明了 SBI 扩展只有在定义发现已实现 SBI 的机制时才能部分实现函数 +- 添加了错误代码 SBI_ERR_NO_SHMEM +- 添加了 SBI 嵌套加速扩展 +- 添加了虚拟 HART 的通用描述 +- 添加了 SBI 隐身时间记帐扩展 +- 添加了 SBI PMU 快照扩展 + +### 版本 1.0.0 + +- 更新批准版本 + +### 版本 1.0-rc3 + +- 更新调用约定 +- 修复了 PMU 扩展名中的拼写错误 +- 添加缩写表 + +### 版本 1.0-rc2 + +- 更新 RISC-V 格式 +- 修改介绍部分 +- 删除了所有 RV32 引用 + +### 版本 1.0-rc1 + +- 错别字修正 + 版本 0.3.0 + +### 版本 0.3.0 + +- 少量错别字修正 +- 文本更新 LICENSE 部分的超链接 + +### 版本 0.3-rc1 + +- 改进文档样式和命名约定 +- 添加 SBI 系统重置扩展 +- 修改 SBI 介绍部分 +- 修改 SBI hart 状态管理扩展文档 +- 为 SBI hart 状态管理扩展添加挂起 +- 添加性能监控单元扩展 +- 阐明一个不应局部实现的 SBI 扩展 + +### 版本 0.2 + +- 整个 v0.1 SBI 已移动至旧版扩展,现已是可选扩展。 + +从技术上讲,这是一个向后不兼容的更改,因为遗留扩展是可选的,并且 SBI 的 v0.1 不允许探测,但我们已经尽力了。 + +## 章节 1. 介绍 + +本规范描述了 RISC-V Supervisor Binary Interface,简称为 SBI。SBI 允许在所有 RISC-V 实现上,通过定义平台(或虚拟化管理程序)特定功能的抽象,使监管者模式(S 模式或 VS 模式)的软件具备可移植性。SBI 的设计遵循 RISC-V 的一般原则,即核心部分小而精简,同时具备一组可选的模块化扩展功能。 +SBI 扩展作为整体是可选的,但不允许部分实现。如果 sbi_probe_extension() 表明某个扩展可用,那么 sbi_get_spec_version() 报告的 SBI 版本中的所有函数必须符合该版本的 SBI 规范。 +提供给监管模式软件的更高特权级软件称为 SBI 实现或 Supervisor Execution Environment(SEE)。SBI 实现(或 SEE)可以是在机器模式(M-mode)下执行的平台运行时固件(参见下图 1),也可以是在超级监管模式(HS-mode)下执行的某个虚拟化监管程序(参见下图 2)。 + +![fig1.jpg](images/sbi-specification-translation/fig1.jpg) + +图 1.无 H 扩展 RISC-V 系统 + +![fig2.jpg](images/sbi-specification-translation/fig2.jpg) + +图 2.带 H 扩展 RISC-V 系统 + +SBI 规范不指定硬件发现的任何方法。监督者软件必须依赖其他行业标准的硬件发现方法(例如设备树或 ACPI)来实现。 + +## 章节 2. 术语和缩写 + +本规范使用了下列术语及缩写: + +| 术语 | 含义 | +| :--- | --------------------------------------------------------------- | +| ACPI | 高级配置和电源接口 (Advanced Configuration and Power Interface) | +| ASID | 地址空间标识符 (Address Space Identifier) | +| BMC | 底板管理控制器 (Baseboard Management Controller) | +| CPPC | 协处理器性能控制 (Collaborative Processor Performance Control) | +| EID | 扩展号 (Extension ID) | +| FID | 函数号 (Function ID) | +| HSM | 核心状态管理 (Hart State Management) | +| IPI | 处理器核间中断 (Inter Processor Interrupt) | +| PMU | 性能监控单元 (Performance Monitoring Unit) | +| SBI | 监管二进制接口 (Supervisor Binary Interface) | +| SEE | 监管执行环境 (Supervisor Execution Environment) | +| VMID | 虚拟机标识符 (Virtual Machine Identifier) | + +## 章节 3. 二进制编码 + +所有的 SBI 函数共享一种二进制编码方式,这有助于混合使用 SBI 扩展功能。SBI 规范遵循以下调用约定。 + +- 在监管者和 SEE 之间,使用 ECALL 作为控制传输指令。 +- a7 编码 SBI 扩展 ID(EID) +- a6 编码 SBI 函数 ID(FID),对于任何在 a7 中编码的 SBI 扩展,其定义在 SBI v0.2 之后。 +- 在 SBI 调用期间,除了 a0 和 a1 寄存器外,所有寄存器都必须由被调用方保留。 +- SBI 函数必须在 a0 和 a1 中返回一对值,其中 a0 返回错误代码。类似于返回 C 结构体。 + +``` +struct sbiret + { + long error; + long value; +}; +``` + +为了保持兼容性,SBI 扩展 ID(EID)和 SBI 函数 ID(FID)被编码为有符号的 32 位整数。当以寄存器形式传递时,遵循上述标准的调用约定规则。 +表 1 列出了标准 SBI 错误代码。 +_表 1.标准 SBI 错误_ + +| 错误类型 | 值 | 描述 | +| :------------------------ | -- | :------------- | +| SBI_SUCCESS | 0 | 顺利完成 | +| SBI_ERR_FAILED | -1 | 失败 | +| SBI_ERR_NOT_SUPPORTED | -2 | 不支持操作 | +| SBI_ERR_INVALID_PARAM | -3 | 非法参数 | +| SBI_ERR_DENIED | -4 | 拒绝 | +| SBI_ERR_INVALID_ADDRESS | -5 | 非法地址 | +| SBI_ERR_ALREADY_AVAILABLE | -6 | (资源)已可用 | +| SBI_ERR_ALREADY_STARTED | -7 | (操作)已启动 | +| SBI_ERR_ALREADY_STOPPED | -8 | (操作)已停止 | +| SBI_ERR_NO_SHMEM | -9 | 共享内存不可用 | + +不支持的 SBI 扩展 ID(EID)或 SBI 函数 ID(FID)的 ECALL 必须返回错误代码 SBI_ERR_NOT_SUPPORTED。 +每个 SBI 函数应该优先选择无符号长整型 unsigned long 作为数据类型。这使得规范简单且易于适应所有 RISC-V ISA 类型。如果数据被定义为 32 位宽度,则更高权限的软件必须确保只使用 32 位数据。 + +### 3.1 HART 列表参数 + +如果 SBI 函数需要将一组 hart 传递给更高权限模式,它必须使用如下所定义的 hart 掩码。这适用于在 v0.2 版本之后定义的任何扩展。 +任何需要 hart 掩码的函数需要传递以下两个参数。 + +- unsigned long hart_mask 是一个包含 hartid 的标量位向量。 +- unsigned long hart_mask_base 是计算位向量的起始 hartid。 + +在单个 SBI 函数调用中,可以设置的最大 hart 数量始终为 XLEN。如果较低特权模式需要传递超过 XLEN 个 hart 的信息,它应该调用多个 SBI 函数调用的实例。hart_mask_base 可以设置为-1,表示可以忽略 hart_mask 并考虑所有可用的 harts。 +使用 hart 掩码的任何函数可能返回表 2 中列出的错误值,这些错误值是针对特定函数的错误值的补充。 +_表 2. HART 掩码错误_ + +| 错误代码 | 描述 | +| :-------------------- | :----------------------------------------------------------------------------------------------- | +| SBI_ERR_INVALID_PARAM | hart_mask_base 或 hart_mask 中任何一个 hartid 无效,即该 hartid 未被平台启用或对监管程序不可用。 | + +### 3.2 共享内存物理地址范围参数 + +如果 SBI 功能需要将共享内存物理地址范围传递给 SBI 实现(或更高特权模式),则该物理内存地址范围必须满足以下要求: + +- SBI 实现必须检查监管模式软件是否允许使用请求的访问类型(读和/或写)访问指定的物理内存范围。 +- SBI 实现必须使用 PMA 属性访问指定的物理内存范围。 + +> 注意:如果监督者模式的软件使用与 PMA 不同的内存类型访问相同的物理内存范围,就可能发生一致性丢失或意外的内存排序。调用的软件应遵循 RISC-V Svpbmt 规范中定义的规则和顺序,以防止一致性丢失和内存排序问题的发生。 + +- 在共享内存中的数据必须遵循小端字节顺序。 + +建议将传递给 SBI 函数的内存物理地址至少使用两个无符号长参数,以支持具有大于 XLEN 位的内存物理地址的平台。 + +## 章节 4. 基本扩展 (EID #0x10) + +基本扩展旨在尽可能简洁。因此,它仅包含用于探测可用的 SBI 扩展以及查询 SBI 版本的功能。基本扩展中的所有函数必须由所有 SBI 实现支持,因此没有定义错误返回值。 + +### 4.1.函数:获取 SBI 规范版本 (FID #0) + +``` +struct sbiret sbi_get_spec_version(void); +``` + +返回当前的 SBI 规范版本。此函数必定成功。SBI 规范的次版本号编码在低 24 位中,主版本号编码在接下来的 7 位中。第 31 位必须为 0,保留用于未来扩展 RISC-V-SBI-1.0.0-Chinese + +### 4.2.函数:获取 SBI 实现标识符 (FID #1) + +``` +struct sbiret sbi_get_impl_id(void); +``` + +返回当前 SBI 实现的标识符,每个 SBI 实现的标识符都是不同的。这个标识符旨在让软件探测 SBI 实现的特殊问题或特点。 + +### 4.3.函数:获取 SBI 实现版本 (FID #2) + +``` +struct sbiret sbi_get_impl_version(void); +``` + +返回当前 SBI 实现的版本。该版本号的编码是特定于 SBI 实现的。 + +### 4.4.函数:探测 SBI 扩展功能 (FID #3) + +``` +struct sbiret sbi_probe_extension(long extension_id); +``` + +如果给定的 SBI 扩展 ID (EID) 不可用,则返回 0;如果可用,且实现定义为其他非零值则返回 1。 + +### 4.5.函数:获取机器供应商标识符 (FID #4) + +``` +struct sbiret sbi_get_mvendorid(void); +``` + +返回一个合法的 mvendorid CSR 值,其中 0 总是一个合法的值。mvendorid CSR 是一个用于标识机器供应商的控制状态寄存器,它用于表示底层硬件的供应商或制造商。 + +### 4.6.函数:获取机器体系结构标识符 (FID #5) + +``` +struct sbiret sbi_get_marchid(void); +``` + +返回一个在 marchid CSR 中合法的值,其中 0 总是合法的值。marchid CSR 是 RISC-V 架构中的一个控制状态寄存器,用于标识机器体系结构。 + +### 4.7.函数:获取机器实现标识符 ID (FID #6) + +``` +struct sbiret sbi_get_mimpid(void); +``` + +返回一个在 mimpid CSR 中合法的值,而且对于该 CSR,0 始终是一个合法的值。 + +### 4.8.函数列表 + +_表 3.基础函数列表_ + +| 函数名 | SBI 版本 | FID | EID | +| :----------------------- | -------- | --- | ---- | +| sbi_get_sbi_spec_version | 0.2 | 0 | 0x10 | +| sbi_get_sbi_impl_id | 0.2 | 1 | 0x10 | +| sbi_get_sbi_impl_version | 0.2 | 2 | 0x10 | +| sbi_probe_extension | 0.2 | 3 | 0x10 | +| sbi_get_mvendorid | 0.2 | 4 | 0x10 | +| sbi_get_marchid | 0.2 | 5 | 0x10 | +| sbi_get_mimpid | 0.2 | 6 | 0x10 | + +### 4.9.SBI 实现标识符 + +_表 4. SBI 实现 IDs_ + +| SBI 实现 ID | 名称 | +| :---------- | -------------------------- | +| 0 | Berkeley Boot Loader (BBL) | +| 1 | OpenSBI | +| 2 | Xvisor | +| 3 | KVM | +| 4 | RustSBI | +| 5 | Diosix | +| 6 | Coffer | +| 7 | Xen Project | + +## 章节 5. 旧版扩展 (EIDs #0x00- #0x0F) + +传统的 SBI 扩展与 SBI v0.2(或更高版本)规范相比,遵循略微不同的调用约定,其中: + +- a6 寄存器中的 SBI 函数 ID 字段被忽略,因为这些被编码为多个 SBI 扩展 ID。 +- a1 寄存器中不返回任何值。 +- 在 SBI 调用期间,除 a0 寄存器外的所有寄存器都必须由被调用者保留。 +- a0 寄存器中返回的值是特定于 SBI 传统扩展的。 +- SBI 实现在监管者访问内存时发生的页面和访问故障会被重定向回监管者,并且 sepc 寄存器指向故障的 ECALL 指令。 + +传统 SBI 扩展已被以下扩展所取代。传统的控制台 SBI 函数(sbi_console_getchar() 和 sbi_console_putchar())预计将被弃用;它们没有替代方案。 + +### 5.1.扩展:设置时钟 (EID #0x00) + +``` +long sbi_set_timer(uint64_t stime_value) +``` + +设置时钟,在 stime_value 时间之后进行下一次事件。此功能还清除待处理的计时器中断位。 +如果监管程序希望清除计时器中断但不安排下一个计时器事件,则可以将计时器中断请求设置为无限远(即(uint64_t)-1),或者通过清除 sie.STIE 寄存器位来屏蔽计时器中断。 +此 SBI 调用在成功时返回 0,否则返回实现特定的负错误代码。 + +### 5.2.扩展:控制台字符输出 (EID #0x01) + +``` +long sbi_console_putchar(int ch) +``` + +将**ch**中的数据写入控制台。 +与 sbi_console_getchar() 不同的是,如果仍有任何待传输的字符或接收终端还没有准备好接收字节,则此 SBI 调用将阻塞。但是,如果控制台根本不存在,则字符将被丢弃。 +此 SBI 调用在成功时返回 0,否则返回实现特定的负错误代码。 + +### 5.3.扩展:控制台字符输入 (EID #0x02) + +``` +long sbi_console_getchar(void) +``` + +从调试控制台中读一个字符。 +此 SBI 调用在成功时返回 0,否则返回实现特定的负错误代码。 + +### 5.4.扩展:清除 IPI (EID #0x03) + +``` +long sbi_clear_ipi(void) +``` + +清除任何挂起的 IPI(处理器核间中断)。这个 SBI 调用只会清除被调用的 hart(硬件线程),其他的 hart 不受影响。 +sbi_clear_ipi() 已经被弃用,因为 S 模式代码可以直接清除 sip.SSIP 寄存器位。. +这个 SBI 调用会返回 0,如果没有 IPI 被挂起,则返回一个实现特定的正值,如果有 IPI 被挂起。 + +### 5.5.扩展:发送 IPI (EID #0x04) + +``` +long sbi_send_ipi(const unsigned long *hart_mask) +``` + +向 hart_mask 指定的所有 hart 发送跨处理器中断。跨处理器中断在接收的 hart 上表现为 Supervisor 模式软件中断。 +hart_mask 是指向 hart 位向量的虚拟地址。该位向量表示为 unsigned long 无符号长整型序列,其长度等于系统中 hart 的数量除以 unsigned long 中的位数,向上取整到下一个整数。 +此 SBI 调用在成功时返回 0,或返回一个实现特定的负错误代码。 + +### 5.6.扩展:远程 FENCE.I (EID #0x05) + +``` +long sbi_remote_fence_i(const unsigned long *hart_mask) +``` + +该函数用于指示远程处理器执行 FENCE.I 指令。 +此 SBI 调用在成功时返回 0,或返回一个实现特定的负错误代码。 + +### 5.7.扩展:远程 SFENCE.VMA (EID #0x06) + +``` +long sbi_remote_sfence_vma(const unsigned long *hart_mask,unsigned long start, unsigned long size) +``` + +指示远程 harts 执行一个或多个 SFENCE.VMA 指令,涵盖从 start 到 size 的虚拟地址范围。其中,hart_mask 参数与 sbi_send_ipi() 函数中描述的一样。 +此 SBI 调用在成功时返回 0,或返回一个实现特定的负错误代码。 + +### 5.8.扩展:远程 SFENCE.VMA(指定地址空间标识符)(EID #0x07) + +``` +long sbi_remote_sfence_vma_asid(const unsigned long *hart_mask,unsigned long start, unsigned long size, unsigned long asid) +``` + +指示远程 hart 执行一个或多个 SFENCE.VMA 指令,覆盖 start 和 size 之间的虚拟地址范围。此操作仅涵盖给定的 ASID。 +此 SBI 调用在成功时返回 0,或返回一个实现特定的负错误代码。 + +### 5.9.扩展:系统关闭 (EID #0x08) + +``` +void sbi_shutdown(void) +``` + +将所有 hart 从监管者模式的角度置于关闭状态。 +此 SBI 调用在成功时返回 0,或返回一个实现特定的负错误代码。 + +### 5.10.函数列表 + +_表 5. 旧版函数列表_ + +| 函数名 | SBI 版本 | FID | EID | 替代 EID | +| :------------------------- | -------- | --- | --------- | ---------- | +| sbi_set_timer | 0.1 | 0 | 0x00 | 0x54494D45 | +| sbi_console_putchar | 0.1 | 0 | 0x01 | N/A | +| sbi_console_getchar | 0.1 | 0 | 0x02 | N/A | +| sbi_clear_ipi | 0.1 | 0 | 0x03 | N/A | +| sbi_send_ipi | 0.1 | 0 | 0x04 | 0x735049 | +| sbi_remote_fence_i | 0.1 | 0 | 0x05 | 0x52464E43 | +| sbi_remote_sfence_vma | 0.1 | 0 | 0x06 | 0x52464E43 | +| sbi_remote_sfence_vma_asid | 0.1 | 0 | 0x07 | 0x52464E43 | +| sbi_shutdown | 0.1 | 0 | 0x08 | 0x53525354 | +| 保留 | | | 0x09-0x0F | | + +## 章节 6. 时钟扩展 (EID #0x54494D45 "TIME") + +这个替代扩展(EID #0x00)替代了传统的计时器扩展。它遵循在 v0.2 中定义的新的调用约定。 + +### 6.1.函数:时钟设定 (FID #0) + +``` +struct sbiret sbi_set_timer(uint64_t stime_value) +``` + +在 stime_value 时间之后,为下一个事件设置时钟。stime_value 以绝对时间表示。此函数还必须清除挂起的计时器中断位。 +如果监管者希望在不安排下一个计时器事件的情况下清除计时器中断,可以请求一个无限远的计时器中断(即 (uint64_t)-1),或者通过清除 sie.STIE 寄存器位来屏蔽计时器中断。 + +### 6.2.函数列表 + +_表 6. 时钟函数列表_ + +| 函数名 | SBI 版本 | FID | EID | +| :------------ | -------- | --- | ---------- | +| sbi_set_timer | 0.2 | 0 | 0x54494D45 | + +## 章节 7. IPI 扩展 (EID #0x735049 "sPI: s-mode IPI") + +该扩展替代了传统的扩展 (EID #0x04)。其他与 IPI 相关的传统扩展(0x3)现已不推荐使用。该扩展中的所有函数都遵循二进制编码部分中定义的 hart_mask. + +### 7.1.函数:发送 IPI (FID #0) + +``` +struct sbiret sbi_send_ipi(unsigned long hart_mask,unsigned long hart_mask_base) +``` + +向 hart_mask 中定义的所有 hart 发送跨处理器中断。接收 hart 上的处理器间中断将在其上表现为超级处理器软件中断。 +sbiret.error 返回的可能错误代码如表 7 所示。 +_表 7. IPI 发送错误_ + +| 错误代码 | 描述 | +| :--------------- | -------------------------------- | +| SBI_SUCCESS 成功 | IPI 被成功发送至所有目标 harts。 | + +### 7.2.函数列表 + +_表 8. IPI 函数列表_ + +| 函数名 | SBI 版本 | FID | EID | +| :----------- | -------- | --- | -------- | +| sbi_send_ipi | 0.2 | 0 | 0x735049 | + +## 章节 8. RFENCE 扩展 (EID #0x52464E43 "RFNC") + +该扩展定义了所有与远程屏障相关的函数,并替代了旧的扩展(EID #0x05 - #0x07)。所有的函数都遵循二进制编码部分中定义的 hart_mask。任何希望使用地址范围(即 start_addr 和 size)的函数,必须遵守以下对范围参数的约束条件。 +如果以下条件满足,则远程屏障函数充当完全 TLB 刷新的作用: + +- start_addr 和 size 都为 0 +- size 等于 2^XLEN-1 + +### 8.1.函数:远程 FENCE.I 指令 (FID #0) + +``` +struct sbiret sbi_remote_fence_i(unsigned long hart_mask,unsigned long hart_mask_base) +``` + +远程指示 harts 执行 FENCE.I 指令。 +sbiret.error 返回的可能错误代码如表 9 所示。 +_表 9. RFENCE 远程 FENCE.I 指令错误_ + +| 错误代码 | 描述 | +| :--------------- | -------------------------------- | +| SBI_SUCCESS 成功 | IPI 被成功发送至所有目标 harts。 | + +### 8.2.函数:远程 SFENCE.VMA 指令 (FID #1) + +``` +struct sbiret sbi_remote_sfence_vma(unsigned long hart_mask, +unsigned long hart_mask_base, unsigned long start_addr, unsigned long size) +``` + +指示远程 hart 执行一个或多个 SFENCE.VMA 指令,覆盖从 start 到 size 的虚拟地址范围内的地址。 +sbiret.error 返回的可能错误代码如表 10 所示。 +_表 10. RFENCE 远程 SFENCE.VMA 错误_ + +| 错误代码 | 描述 | +| :------------------------------- | -------------------------------- | +| SBI_SUCCESS 成功 | IPI 被成功发送至所有目标 harts。 | +| SBI_ERR_INVALID_ADDRESS 非法地址 | start_addr 或 size 非法 | + +### 8.3.函数:远程 SFENCE.VMA 指定 ASID (FID #2) + +``` +struct sbiret sbi_remote_sfence_vma_asid(unsigned long hart_mask, +unsigned long hart_mask_base, unsigned long start_addr, unsigned long size, +unsigned long asid) +``` + +指示远程 hart 执行一个或多个 SFENCE.VMA 指令,覆盖从 start 到 size 的虚拟地址范围内的地址。这仅涵盖给定的 ASID。 +sbiret.error 返回的可能错误代码如表 11 所示。 +_表 11. RFENCE 远程 SFENCE.VMA 指定 ASID 错误_ + +| 错误代码 | 描述 | +| :------------------------------- | -------------------------------- | +| SBI_SUCCESS 成功 | IPI 被成功发送至所有目标 harts。 | +| SBI_ERR_INVALID_ADDRESS 非法地址 | start_addr 或 size 非法 | + +### 8.4.函数:远程 HFENCE.GVMA 指定 VMID (FID #3) + +``` +struct sbiret sbi_remote_hfence_gvma_vmid(unsigned long hart_mask, +unsigned long hart_mask_base, unsigned long start_addr, unsigned long size, +unsigned long vmid) +``` + +指示远程 hart 执行一个或多个 HFENCE.GVMA 指令,仅覆盖给定 VMID(虚拟机标识符)的 start 到 size 之间的客户机物理地址范围。此函数调用仅适用于实现了虚拟化扩展的 harts。 +sbiret.error 返回的可能错误代码如表 12 所示。 +_表 12. RFENCE 远程 HFENCE.GVMA 指定 VMID 错误_ + +| 错误代码 | 描述 | +| :---------------------------------- | ---------------------------------------------------------------- | +| SBI_SUCCESS 成功 | IPI 被成功发送至所有目标 harts。 | +| SBI_ERR_NOT_SUPPORTED(操作)不支持 | 由于未被实现或目标 hart 中不错支持虚拟化扩展,则该操作不受支持。 | +| SBI_ERR_INVALID_ADDRESS 非法地址 | start_addr 或 size 非法 | + +### 8.5.函数:远程 HFENCE.GVMA (FID #4) + +``` +struct sbiret sbi_remote_hfence_gvma(unsigned long hart_mask, +unsigned long hart_mask_base, unsigned long start_addr, unsigned long size) +``` + +指示远程 harts 执行一个或多个 HFENCE.GVMA 指令,覆盖从 start 到 size 之间的所有客户机的客户机物理地址范围。此函数调用仅适用于实现虚拟化扩展的 harts。 +sbiret.error 返回的可能错误代码如表 13 所示。 +_表 13. RFENCE 远程 HFENCE.GVMA 错误_ + +| 错误代码 | 描述 | +| :---------------------------------- | ---------------------------------------------------------------- | +| SBI_SUCCESS 成功 | IPI 被成功发送至所有目标 harts。 | +| SBI_ERR_NOT_SUPPORTED(操作)不支持 | 由于未被实现或目标 hart 中不错支持虚拟化扩展,则该操作不受支持。 | +| SBI_ERR_INVALID_ADDRESS 非法地址 | start_addr 或 size 非法 | + +### 8.6.函数:远程 HFENCE.VVMA 指定 ASID (FID #5) + +``` +struct sbiret sbi_remote_hfence_vvma_asid(unsigned long hart_mask, +unsigned long hart_mask_base, unsigned long start_addr, unsigned long size, +unsigned long asid) +``` + +远程指示 harts 执行一个或多个 HFENCE.VVMA 指令,覆盖从 start 至 size 之间,指定的 ASID 与 +VMID(hgatp 寄存器)下所有客户机物理地址范围。此函数调用仅适用于实现虚拟化扩展的 harts。 +sbiret.error 返回的可能错误代码如表 14 所示。 +_表 14. RFENCE 远程 HFENCE.VVMA 指定 ASID 错误_ + +| 错误代码 | 描述 | +| :---------------------------------- | ---------------------------------------------------------------- | +| SBI_SUCCESS 成功 | IPI 被成功发送至所有目标 harts。 | +| SBI_ERR_NOT_SUPPORTED(操作)不支持 | 由于未被实现或目标 hart 中不错支持虚拟化扩展,则该操作不受支持。 | +| SBI_ERR_INVALID_ADDRESS 非法地址 | start_addr 或 size 非法 | + +### 8.7.函数:远程 HFENCE.VVMA (FID #6) + +``` +struct sbiret sbi_remote_hfence_vvma(unsigned long hart_mask, +unsigned long hart_mask_base, unsigned long start_addr, unsigned long size) +``` + +指示远程 HART 执行一个或多个 HFENCE.VVMA 指令,覆盖当前调用 HART 的 VMID(在 hgatp 寄存器中)下的 start 和 size 之间的客户虚拟地址范围。此函数调用仅适用于实现了虚拟化扩展的 harts。 +sbiret.error 返回的可能错误代码如表 15 所示。 +_表 15.RFENCE 远程 HFENCE.VVMA 错误_ + +| 错误代码 | 描述 | +| :---------------------------------- | ---------------------------------------------------------------- | +| SBI_SUCCESS 成功 | IPI 被成功发送至所有目标 harts。 | +| SBI_ERR_NOT_SUPPORTED(操作)不支持 | 由于未被实现或目标 hart 中不错支持虚拟化扩展,则该操作不受支持。 | +| SBI_ERR_INVALID_ADDRESS 非法地址 | start_addr 或 size 非法 | + +### 8.8.函数列表 + +_表 16. RFENCE 函数列表_ + +| 函数名 | SBI 版本 | FID | EID | +| :-------------------------- | -------- | --- | ---------- | +| sbi_remote_fence_i | 0.2 | 0 | 0x52464E43 | +| sbi_remote_sfence_vma | 0.2 | 1 | 0x52464E43 | +| sbi_remote_sfence_vma_asid | 0.2 | 2 | 0x52464E43 | +| sbi_remote_hfence_gvma_vmid | 0.2 | 3 | 0x52464E43 | +| sbi_remote_hfence_gvma | 0.2 | 4 | 0x52464E43 | +| sbi_remote_hfence_vvma_asid | 0.2 | 5 | 0x52464E43 | +| sbi_remote_hfence_vvma | 0.2 | 6 | 0x52464E43 | + +## 章节 9. Hart State Management 状态管理扩展 (EID #0x48534D "HSM") + +Hart State Management (HSM) 扩展引入了一组 Hart 状态和一组函数,允许 S-mode 监管者模式下的软件请求 Hart 状态变更。 +下面的表 17 描述了所有可能的 HSM 状态以及每个状态的唯一 HSM 状态标识符。: +_表 17. HSM Hart 状态_ + +| 状态 ID | 状态名 | 描述 | +| :------ | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------- | +| 0 | STARTED 启动 | 该 hart 处于物理上电状态,并正常执行。 | +| 1 | STOPPED 结束 | 该 hart 没有在监管者模式 S-mode 或任何更低特权模式下执行。如果底层平台具有关闭 hart 的物理机制,那么它可能被 SBI 实现关闭了电源。 | +| 2 | START_PENDING 等待被启动 | 另一个 hart 请求将该 hart 从 STOPPED 状态启动(或上电),而 SBI 实现仍在努力将该 hart 转换为 STARTED 状态。 | +| 3 | STOP_PENDING 结束等待 | 该 hart 在 STARTED 状态下请求停止(或关机),而 SBI 实现仍在努力将该 hart 转变为 STOPPED 状态。 | +| 4 | SUSPENDED 挂起 | 该 hart 处于特定于平台的暂停(或低功耗)状态。 | +| 5 | SUSPEND_PENDING 等待挂起 | 该 hart 从 STARTED 状态请求将自身置于特定于平台的低功耗状态,并且 SBI 实现正在努力将该 hart 转换为特定于平台的 SUSPENDED 状态。 | +| 6 | RESUME_PENDING 等待恢复 | 中断或特定于平台的硬件事件导致 hart 从 SUSPENDED 状态恢复正常执行,而 SBI 实现正在努力将该 hart 转换为 STARTED 状态。 | + +在任何时刻,hart 应该处于上述提到的 hart 状态之一。SBI 实现对 hart 状态的转换应遵循图 3 所示的状态机。 + +![fig3.jpg](images/sbi-specification-translation/fig3.jpg) +图 3. SBI HSM 状态机 +一个平台可以有多个 hart,这些 hart 被分组成层次拓扑组(如核心、集群、节点等),每个层次组都有独立的平台特定低功耗状态。这些层次拓扑组的平台特定低功耗状态可以表示为一个 hart 的平台特定挂起状态。SBI 实现可以利用较高层次拓扑组的挂起状态,使用以下一种方法之一: + +1. Platform-coordinated **平台协调**: In this approach, when a hart becomes idle the supervisor-mode power- managment software will request deepest suspend state for the hart and higher topology groups. An SBI implementation should choose a suspend state at higher topology group which is: 在这种方法中,当一个 hart 变为空闲时,监管模式 S-mode 的功耗管理软件将请求该 hart 和较高层次组的最深挂起状态。SBI 实现应选择一个较高层次组的挂起状态,满足以下条件: + 1. 不比指定的挂起状态更深 + 2. 唤醒延迟不高于指定挂起状态的唤醒延迟 +2. OS-inititated **操作系统发起**: 在这种方法中,监管模式 S-mode 的功耗管理软件将在最后一个 hart 变为空闲后,直接请求较高层次组的挂起状态。当一个 hart 变为空闲时,监管模式的功耗管理软件总是选择该 hart 的挂起状态,但仅在该 hart 是组内最后一个运行的 hart 时,才为较高层次组选择一个挂起状态。SBI 实现应满足以下条件: + 1. 永远不要选择与指定暂停状态不同的较高层次组的挂起状态 + 2. 总是优先选择最近请求的较高层次组的挂起状态。 + +### 9.1.函数:HART 启动 (FID #0) + +``` +struct sbiret sbi_hart_start(unsigned long hartid, +unsigned long start_addr, unsigned long opaque) +``` + +SBI 实现以特定的寄存器值在指定的 start_addr 地址处启动执行目标 hart 的超级模式。具体的寄存器值如表 18 所述。 +_表 18. HSM Hart 启动的寄存器状态_ + +| 寄存器名称 | 寄存器值 | +| :--------------------------- | ----------- | +| satp | 0 | +| sstatus.SIE | 0 | +| a0 | hartid | +| a1 | opaque 参数 | +| 其他寄存器仍处于未定义状态。 | | + +此调用是异步的,具体来说,sbi_hart_start() 函数可以在目标 hart 开始执行之前返回,只要 SBI 实现能够确保返回的结果准确。如果 SBI 实现是在机器模式(M-mode)下执行的平台运行时固件,则必须在将控制权转移给 S-mode 模式软件之前配置 PMP 和其他 M-mode 状态。 +hartid 参数指定要启动的目标 hart。 +start_addr 参数指向一个运行时指定的物理地址,该 hart 可以在 S-mode 模式下开始执行。 +opaque 参数是一个 XLEN 位的值,当 hart 在 start_addr 处开始执行时 opaque 参数被设置在 a1 寄存器中。 +sbiret.error 中可能返回的错误代码在下表 19 中显示。 +_表 19. HSM Hart 启动错误_ + +| 错误代码 | 描述 | +| :-------------------------------- | -------------------------------------------------------------------------------- | +| SBI_SUCCESS 成功 | Hart 之前处于停止状态。它将从 start_addr 开始执行 | +| SBI_ERR_INVALID_ADDRESS 非法地址 | start_addr 无效,原因可能如下:无效物理地址。该地址被 PMP 禁止在 S-mode 下执行。 | +| SBI_ERR_INVALID_PARAM 非法参数 | Hartid 是无效的物理地址,因为其相应的 hart 不能以 S-mode 启动。 | +| SBI_ERR_ALREADY_AVAILAB LE 已存在 | 给定 hartid 已启动。 | +| SBI_ERR_FAILED 失败 | 未知原因造成的启动失败。 | + +### 9.2.函数:HART 停止 (FID #1) + +``` +struct sbiret sbi_hart_stop(void) +``` + +要求 SBI 实现停止以 S-mode 模式执行调用 hart,并将其所有权返回给 SBI 实现。在正常情况下,不希望此调用返回。sbi_hart_stop() 必须在禁用 S-mode 模式中断时调用。 +sbiret.error 可能返回的错误代码如下表 20 所示。 +_表 20. HSM Hart 停止错误_ + +| 错误代码 | 描述 | +| :------------------ | ---------------------- | +| SBI_ERR_FAILED 失败 | 当前 hart 停止执行失败 | + +### 9.3.函数:HART 获取状态 (FID #2) + +``` +struct sbiret sbi_hart_get_status(unsigned long hartid) +``` + +通过 sbiret.value 获取给定 hart 的当前状态(或 HSM 状态 ID),或通过 sbiret.error 获取错误信息。 +hartid 参数指定需要查询状态的目标 hart。 +sbiret.value 中可能返回的状态(或 HSM 状态 ID)值在表 17 中进行了描述。 +sbiret.error 可能返回的错误代码如下表 21 所示。 +_表 21. HSM Hart 获取状态错误_ + +| 错误代码 | 描述 | +| :----------------------------- | ------------------ | +| SBI_ERR_INVALID_PARAM 无效参数 | 给定的 hartid 无效 | + +由于任何并发的 sbi_hart_start()、sbi_hart_stop() 或 sbi_hart_suspend() 调用,harts 可能随时转换 HSM 状态,因此该函数的返回值可能不代表返回值验证时 hart 的实际状态。 + +### 9.4.函数:HART 挂起 (FID #3) + +``` +struct sbiret sbi_hart_suspend(uint32_t suspend_type, +unsigned long resume_addr, unsigned long opaque) +``` + +SBI 实现将调用的 hart 置于由 suspend_type 参数指定的平台特定的挂起(或低功耗)状态。当收到中断或平台特定的硬件事件时,hart 将自动退出挂起状态并恢复正常执行。 +一个 hart 的平台特定挂起状态可以是保持性的或非保持性的。保持性挂起状态将保留所有特权模式下 hart 的寄存器和 CSR 值,而非保持性挂起状态将不保留 hart 的寄存器和 CSR 值。 +从保持性挂起状态恢复是比较简单的,S-mode 模式的软件将看到 SBI 挂起调用返回而无需任何失败。在保持性挂起期间,resume_addr 参数未使用。 +从非保持性挂起状态恢复相对更复杂,需要软件还原各种特权模式下的 hart 寄存器和 CSR。从非保持性挂起状态恢复后,hart 将跳转到由 resume_addr 指定的 S-mode 模式地址,具体寄存器的值在表 22 中描述。 +_表 22. HSM Hart 恢复寄存器状态_ + +| 寄存器名称 | 寄存器值 | +| :------------------------- | ----------- | +| satp | 0 | +| sstatus.SIE | 0 | +| a0 | hartid | +| a1 | Opaque 参数 | +| 其他寄存器保持未定义状态。 | | + +suspend_type 参数是 32 位宽,可能的值如表 23 所示。 +_表 23. HSM Hart 挂起类型_ + +| 值 | 描述 | +| :---------------------- | -------------------- | +| 0x00000000 | 默认保持性挂起 | +| 0x00000001 - 0x0FFFFFFF | 保留供未来使用 | +| 0x10000000 - 0x7FFFFFFF | 平台特定保持性挂起 | +| 0x80000000 | 默认非保持性挂起 | +| 0x80000001 - 0x8FFFFFFF | 保留供未来使用 | +| 0x90000000 -0xFFFFFFFF | 平台特定非保持性挂起 | +| > 0xFFFFFFFF | 保留 | + +resume_addr 参数指向一个在非保持性挂起后,hart 可以在 S-mode 模式下恢复执行的运行时指定的物理地址。 +opaque 参数是一个 XLEN 位的值,当 hart 在非保留挂起后在 resume_addr 处恢复执行时,会将该值设置在 a1 寄存器中。 +sbiret.error 中可能返回的错误代码如表 24 所示。 +_表 24. HSM Hart 挂起错误_ + +| 错误代码 | 描述 | +| :------------------------------- | --------------------------------------------------------------------------------------------- | +| SBI_SUCCESS 成功 | Hart 已成功地从保持性挂起状态中恢复。 | +| SBI_ERR_INVALID_PARAM 无效参数 | suspend_type 无效 | +| SBI_ERR_NOT_SUPPORTED 不支持 | suspend_type 有效但未实现。 | +| SBI_ERR_INVALID_ADDRESS 无效地址 | resume_addr 无效,可能是由于以下原因:无效的物理地址。该地址被 PMP 禁止在 S-mode 模式下运行。 | +| SBI_ERR_FAILED 失败 | 挂起请求因未知原因而失败。 | + +### 9.5. 函数列表 + +_表 25. HSM 函数列表_ + +| 函数名 | SBI 版本 | FID | EID | +| :--------------------------- | -------- | --- | -------- | +| sbi_hart_start 启动 | 0.2 | 0 | 0x48534D | +| sbi_hart_stop 停止 | 0.2 | 1 | 0x48534D | +| sbi_hart_get_status 获取状态 | 0.2 | 2 | 0x48534D | +| sbi_hart_suspend 挂起 | 0.3 | 3 | 0x48534D | + +## 章节 10. 系统复位扩展 (EID #0x53525354 "SRST") + +系统复位扩展提供了一个函数,允许 S-mode 模式下的软件请求系统级的重启或关闭操作。"系统"指的是 S-mode 模式下的软件的视角,底层的 SBI 实现可以是机器模式 M-mode 固件或虚拟化管理程序。 + +### 10.1.函数:系统复位 (FID #0) + +``` +struct sbiret sbi_system_reset(uint32_t reset_type, uint32_t reset_reason) +``` + +根据提供的类型 reset_type 和原因 reset_reason 重置系统。这是一个同步调用,如果成功将不会返回。 +reset_type 参数的宽度为 32 位,其可能的取值在下面的表 26 中列出。 +_表 26. SRST 系统复位类型_ + +| 值 | 描述 | +| :--------------------- | -------------------------- | +| 0x00000000 | 关机 | +| 0x00000001 | 冷启动 | +| 0x00000002 | 热启动 | +| 0x00000003 -0xEFFFFFFF | 保留以便未来使用 | +| 0xF0000000 -0xFFFFFFFF | 供应商或平台特定的复位类型 | +| > 0xFFFFFFFF | 保留 | + +reset_reason 是一个可选参数,表示系统复位的原因。该参数是 32 位宽,可能的取值如下所示: +_表 27. SRST 系统复位原因_ + +| 值 | 描述 | +| :--------------------- | -------------------------- | +| 0x00000000 | 没有原因 | +| 0x00000001 | 系统失败 | +| 0x00000002 -0xDFFFFFFF | 保留以便未来使用 | +| 0xE0000000 -0xEFFFFFFF | SBI 实现特定的复位原因 | +| 0xF0000000 -0xFFFFFFFF | 供应商或平台特定的复位类型 | +| > 0xFFFFFFFF | 保留 | + +当 S-mode 下的软件在本地运行时,SBI 实现属于机器模式下的固件。在这种情况下,关机等同于整个系统的物理断电,冷启动等同于整个系统的物理断电循环(并重新上电)。此外,热重启等同于对主处理器和系统的一部分进行断电循环,而不是整个系统。例如,在具有 BMC(主板管理控制器)的服务器级系统上,热重启不会对 BMC 进行断电循环,而冷重启则肯定会对 BMC 进行断电循环。 +当 S-mode 模式下的软件在虚拟机内运行时,SBI 实现是一个虚拟化管理器。关机、冷重启和热重启在功能上与本机情况相同,但可能不会导致任何物理电源变化。 +sbiret.error 中可能返回的错误代码如表 28 所示。 +_表 28. SRST 系统复位错误 s_ + +| 错误代码 | 描述 | +| :----------------------------- | ------------------------------- | +| SBI_ERR_INVALID_PARAM 无效参数 | reset_type 或 reset_reason 无效 | +| SBI_ERR_NOT_SUPPORTED 不支持 | reset_type 有效但未被设定 | +| SBI_ERR_FAILED 失败 | 未知原因的复位请求失败 | + +### 10.2.函数列表 + +_表 29. SRST 函数列表_ + +| Function Name | SBI Version | FID | EID | +| :--------------- | ----------- | --- | ---------- | +| sbi_system_reset | 0.3 | 0 | 0x53525354 | + +## 章节 11. 性能监控单元扩展 (EID #0x504D55 "PMU") + +RISC-V 硬件性能计数器(如 mcycle、minstret 和 mhpmcounterX CSRs)可以以只读方式从监管者模式 S-mode 使用 cycle、instret 和 hpmcounterX CSRs 进行访问。SBI 性能监控单元(PMU)扩展是一个接口,供监管者模式 S-mode 使用,以便借助机器模式(或虚拟化模式)配置和使用 RISC-V 硬件性能计数器。这些硬件性能计数器只能从机器模式使用 mcountinhibit 和 mhpmeventX CSRs 启动、停止或配置。因此,如果 RISC-V 平台未实现 mhpmcounterX CSR,机器模式的 SBI 实现可能选择禁止 SBI PMU 扩展。 +一般情况下,RISC-V 平台支持使用有限数量的硬件性能计数器(最多 64 位)来监控各种硬件事件。此外,SBI 实现还可以提供固件性能计数器,用于监控固件事件,例如不对齐的加载/存储指令数量、RFENCE 数量、IPI 数量等。固件计数器始终为 64 位。 +SBI PMU 扩展提供以下功能: + +1. 监管者模式软件发现和配置每个 HART 的硬件/固件计数器的接口 +2. 具有典型 perf 兼容接口的硬件/固件性能计数器和事件 +3. 完全访问微体系结构的原始事件编码 + +为了定义 SBI PMU 扩展调用,我们首先定义了重要的实体 counter_idx、event_idx 和 event_data。counter_idx 是分配给每个硬件/固件计数器的逻辑编号。event_idx 表示硬件(或固件)事件,而 event_idx 为 64 位宽,表示硬件(或固件)事件的额外配置(或参数)。 +event_idx 是一个 20 位宽的数字,编码如下: + +``` +event_idx[19:16]= type +event_idx[15:0] = code +``` + +### 11.1.事件:硬件通用事件 (Type #0) + +event_idx.type(即事件类型)对于所有硬件通用事件应为 0x0,并且每个硬件通用事件由一个唯一的 event_idx.code(即事件代码)标识,如下所示的表格 30 中所述。 +_表 30. PMU 硬件事件_ + +| 通用事件名称 | 代码 | 描述 | +| :--------------------------------- | ---- | ------------------------------------- | +| SBI_PMU_HW_NO_EVENT | 0 | 未使用的事件,因为 event_idx 不能为零 | +| SBI_PMU_HW_CPU_CYCLES | 1 | 每个 CPU 周期的事件 | +| SBI_PMU_HW_INSTRUCTIONS | 2 | 每个完成的指令的事件 | +| SBI_PMU_HW_CACHE_REFERENCES | 3 | 缓存命中的事件 | +| SBI_PMU_HW_CACHE_MISSES | 4 | 缓存未命中的事件 | +| SBI_PMU_HW_BRANCH_INSTRUCTIONS | 5 | 分支指令的事件 | +| SBI_PMU_HW_BRANCH_MISSES | 6 | 分支预测错误的事件 | +| SBI_PMU_HW_BUS_CYCLES | 7 | 每个 BUS 周期的事件 | +| SBI_PMU_HW_STALLED_CYCLES_FRONTEND | 8 | 微架构前端阻塞周期的事件 | +| SBI_PMU_HW_STALLED_CYCLES_BACKEND | 9 | 微架构后端阻塞周期的事件 | +| SBI_PMU_HW_REF_CPU_CYCLES | 10 | 每个参考 CPU 周期的事件 | + +> 注意:对于硬件通用事件,event_data(即事件数据)未使用,event_data 的所有非零值都保留供将来使用。 + +> 注意:当 RISC-V 平台使用 WFI 指令进入 WAIT 状态或使用 SBI HSM HART 挂起调用进入平台特定的挂起状态时,CPU 时钟可能会停止。 + +> 注意:SBI_PMU_HW_CPU_CYCLES 事件通过 cycle CSR 计数 CPU 时钟周期。这些周期可能是可变频率的周期,在 CPU 时钟停止时不计数。 + +> 注意:SBI_PMU_HW_REF_CPU_CYCLES 在 CPU 时钟未停止时计数固定频率的时钟周期。计数的固定频率可以是例如时间 CSR 计数的频率。 +> 注意:SBI_PMU_HW_BUS_CYCLES 计数固定频率的时钟周期。计数的固定频率可以是例如时间 CSR 计数的频率,或者可以是 HART(及其私有缓存)与系统其他部分之间的时钟边界的频率。 + +### 11.2.事件:硬件缓存事件 (Type #1) + +对于所有硬件缓存事件,event_idx.type(即事件类型)应为 0x1,并且每个硬件缓存事件由唯一的 event_idx.code(即事件代码)标识,其编码如下: + +``` +event_idx.code[15:3] = cache_id +event_idx.code[2:1] = op_id +event_idx.code[0:0] = result_id +``` + +下表显示了以下标识符可能的值:event_idx.code.cache_id(即缓存事件 ID),event_idx.code.op_id(即缓存操作 ID)和 event_idx.code.result_id(即缓存结果 ID)。 +_表 31. PMU 缓存事件 ID_ + +| 缓存事件名称 | 事件 ID | 描述 | +| :-------------------- | ------- | ----------------- | +| SBI_PMU_HW_CACHE_L1D | 0 | 一级数据缓存事件 | +| SBI_PMU_HW_CACHE_L1I | 1 | 一级指令缓存事件 | +| SBI_PMU_HW_CACHE_LL | 2 | 最后一级缓存事件 | +| SBI_PMU_HW_CACHE_DTLB | 3 | 数据 TLB 事件 | +| SBI_PMU_HW_CACHE_ITLB | 4 | 指令 TLB 事件 | +| SBI_PMU_HW_CACHE_BPU | 5 | 分支预测单元事件 | +| SBI_PMU_HW_CACHE_NODE | 6 | NUMA 节点缓存事件 | + +_表 32. PMU 缓存操作 ID_ + +| 缓存操作名称 | 操作 ID | 描述 | +| :--------------------------- | ------- | ---------- | +| SBI_PMU_HW_CACHE_OP_READ | 0 | 读取缓存行 | +| SBI_PMU_HW_CACHE_OP_WRITE | 1 | 写入缓存行 | +| SBI_PMU_HW_CACHE_OP_PREFETCH | 2 | 预取缓存行 | + +_表 33. PMU 缓存操作结果 ID_ + +| 缓存结果名称 | 结果 ID | 描述 | +| :----------------------------- | ------- | ---------- | +| SBI_PMU_HW_CACHE_RESULT_ACCESS | 0 | 缓存访问 | +| SBI_PMU_HW_CACHE_RESULT_MISS | 1 | 缓存未命中 | + +> 注意:对于硬件缓存事件,event_data(即事件数据)未使用,所有非零值的事件数据均保留用于将来使用。 + +### 11.3.事件:硬件原始事件 (Type #2) + +硬件原始事件类型的 event_idx.type (i.e. event type) 值应为 0x2,而 event_idx.code(i.e. event code) 值应为零。 +对于具有 32 位宽度的 mhpmeventX CSRs 的 RISC-V 平台,event_data 配置(或参数)应具有要编程到 mhpmeventX CSR 中的 32 位值。 +对于具有 64 位宽度的 mhpmeventX CSRs 的 RISC-V 平台,event_data 配置(或参数)应具有要编程到 mhpmeventX CSR 的低 48 位值,而 SBI 实现将确定要编程到 mhpmeventX CSR 的高 16 位值。 + +> 注意:RISC-V 平台的硬件实现可能选择定义要写入 mhpmeventX CSR 的硬件事件的预期值。对于硬件常规/缓存事件,为了简化起见,RISC-V 平台的硬件实现可能使用零扩展的 event_idx 作为预期值。 + +### 11.4.事件:固件事件 (Type #15) + +所有固件事件的 event_idx.type(即事件类型)应为 0xf,并且每个固件事件都由唯一的 event_idx.code(即事件代码)标识,其描述如下所示的 表 34 中。 +_表 34. PMU 固件事件_ + +| 固件事件名称 | 编码 | 描述 | +| :------------------------------------ | ---- | ----------------------------------------------------- | +| SBI_PMU_FW_MISALIGNED_LOAD | 0 | 对齐错误加载陷入事件 | +| SBI_PMU_FW_MISALIGNED_STORE | 1 | 对齐错误存储陷入事件 | +| SBI_PMU_FW_ACCESS_LOAD | 2 | 加载访问陷入事件 | +| SBI_PMU_FW_ACCESS_STORE | 3 | 存储访问陷入事件 | +| SBI_PMU_FW_ILLEGAL_INSN | 4 | 非法指令陷入事件 | +| SBI_PMU_FW_SET_TIMER | 5 | 设置定时器事件 | +| SBI_PMU_FW_IPI_SENT | 6 | 发送 IPI 给其他 HART 事件 | +| SBI_PMU_FW_IPI_RECEIVED | 7 | 接收来自其他 HART 的 IPI 事件 | +| SBI_PMU_FW_FENCE_I_SENT | 8 | 发送 FENCE.I 请求给其他 HART 事件 | +| SBI_PMU_FW_FENCE_I_RECEIVED | 9 | 接收来自其他 HART 的 FENCE.I 请求事件 | +| SBI_PMU_FW_SFENCE_VMA_SENT | 10 | 发送 SFENCE.VMA 请求给其他 HART 事件 | +| SBI_PMU_FW_SFENCE_VMA_RECEIVED | 11 | 接收来自其他 HART 的 SFENCE.VMA 请求事件 | +| SBI_PMU_FW_SFENCE_VMA_ASID_SENT | 12 | 发送带有 ASID 的 SFENCE.VMA 请求给其他 HART 事件 | +| SBI_PMU_FW_SFENCE_VMA_ASID_RECEIVE D | 13 | 接收来自其他 HART 的带有 ASID 的 SFENCE.VMA 请求事件 | +| SBI_PMU_FW_HFENCE_GVMA_SENT | 14 | 发送 HFENCE.GVMA 请求给其他 HART 事件 | +| SBI_PMU_FW_HFENCE_GVMA_RECEIVED | 15 | 接收来自其他 HART 的 HFENCE.GVMA 请求事件 | +| SBI_PMU_FW_HFENCE_GVMA_VMID_SENT | 16 | 发送带有 VMID 的 HFENCE.GVMA 请求给其他 HART 事件 | +| SBI_PMU_FW_HFENCE_GVMA_VMID_RECEI VED | 17 | 接收来自其他 HART 的带有 VMID 的 HFENCE.GVMA 请求事件 | +| SBI_PMU_FW_HFENCE_VVMA_SENT | 18 | 发送 HFENCE.VVMA 请求给其他 HART 事件 | +| SBI_PMU_FW_HFENCE_VVMA_RECEIVED | 19 | 接收来自其他 HART 的 HFENCE.VVMA 请求事件 | +| SBI_PMU_FW_HFENCE_VVMA_ASID_SENT | 20 | 向其他 HART 发送带有 ASID 的 HFENCE.VVMA 请求事件 | +| SBI_PMU_FW_HFENCE_VVMA_ASID_RECEIV ED | 21 | R 从其他 HART 接收带有 ASID 的 HFENCE.VVMA 请求事件 | + +> 注意:对于固件事件,event_data(即事件数据)未使用,所有非零的 event_data 值都保留供将来使用。 + +### 11.5. 函数:获取可用计数器的数量 (FID #0) + +``` +struct sbiret sbi_pmu_num_counters() +``` + +**返回**可用计数器的数量到 sbiret.value 中,并始终在 sbiret.error 中返回 SBI_SUCCESS。 + +### 11.6. 函数:获取特定计数器的详细信息 (FID #1) + +``` +struct sbiret sbi_pmu_counter_get_info(unsigned long counter_idx) +``` + +获取有关指定计数器的详细信息,例如底层 CSR 编号、计数器的宽度、计数器的类型(硬件/固件)等。 +此 SBI 调用返回的 counter_info 信息编码如下: + +``` +counter_info[11:0] = CSR (12bit CSR number) +counter_info[17:12] = Width (One less than number of bits in CSR) +counter_info[XLEN-2:18] = Reserved for future use counter_info[XLEN-1] = Type (0 = hardware and 1 = firmware) +``` + +若 counter_info.type == 1,那么 counter_info.csr 与 counter_info.width 应该被忽略。 +在 sbiret.value 中返回上述描述的 counter_info。 +sbiret.error 中可能返回的错误代码如下表 35 所示。 +_表 35. PMU 计数器获取信息错误_ + +| 错误代码 | 描述 | +| :-------------------- | ------------------------------ | +| SBI_SUCCESS | 成功读取 counter_info | +| SBI_ERR_INVALID_PARAM | counter_idx 指向无效的计数器。 | + +### 11.7. 函数:查找并配置匹配计数器 (FID #2) + +``` +struct sbiret sbi_pmu_counter_config_matching(unsigned long counter_idx_base, +unsigned long counter_idx_mask, unsigned long config_flags, unsigned long event_idx, uint64_t event_data) +``` + +在一组计数器中查找并配置一个尚未启动(或已启用)且可以监视指定事件的计数器。其中,counter_idx_base 和 counter_idx_mask 参数表示计数器集合,event_idx 表示要监视的事件,event_data 表示任何附加事件配置。 +config_flags 参数表示额外的计数器配置和过滤标志。config_flags 参数的位定义如下所示: +_表 36. PMU 计数器配置匹配标志_ + +| 标志名称 | 位 | 描述 | +| :--------------------------- | ---------- | ------------------------------------ | +| SBI_PMU_CFG_FLAG_SKIP_MATCH | 0:0 | 跳过计数器匹配 | +| SBI_PMU_CFG_FLAG_CLEAR_VALUE | 1:1 | 在计数器配置中清零(或置零)计数器值 | +| SBI_PMU_CFG_FLAG_AUTO_START | 2:2 | 配置匹配计数器后自动启动计数器 | +| SBI_PMU_CFG_FLAG_SET_VUINH | 3:3 | VU 模式下禁止事件计数 | +| SBI_PMU_CFG_FLAG_SET_VSINH | 4:4 | VS 模式下禁止事件计数 | +| SBI_PMU_CFG_FLAG_SET_UINH | 5:5 | U 模式下禁止事件计数 | +| SBI_PMU_CFG_FLAG_SET_SINH | 6:6 | S 模式下禁止事件计数 | +| SBI_PMU_CFG_FLAG_SET_MINH | 7:7 | M 模式下禁止事件计数 | +| RESERVED | 8:(XLEN-1) | 所有非零值保留供将来使用 | + +> 注意:当在 config_flags 中设置了 SBI_PMU_CFG_FLAG_SKIP_MATCH 标志时,SBI 实现将无条件地从由 counter_idx_base 和 counter_idx_mask 指定的计数器集合中选择第一个计数器。 + +> 注意:config_flags 中的 SBI_PMU_CFG_FLAG_AUTO_START 标志对计数器值没有影响。 + +> 注意:config_flags[3:7] 位是事件过滤提示,因此在安全性方面或由于底层 RISC-V 平台缺乏事件过滤支持时,SBI 实现可能会忽略或覆盖这些提示。 +> 成功时,在 sbiret.value 中返回 counter_idx。 +> 如果操作失败,在 sbiret.error 中可能返回的错误代码如下表 37 所示: +> _表 37. PMU 计数器配置匹配错误_ + +| 错误代码 | 描述 | +| :-------------------- | ---------------------------------- | +| SBI_SUCCESS | 计数器已成功找到并配置。 | +| SBI_ERR_INVALID_PARAM | 计数器集合中存在无效的计数器。 | +| SBI_ERR_NOT_SUPPORTED | 没有任何计数器能够监视指定的事件。 | + +### 11.8. 函数:启动一组计数器 (FID #3) + +``` +struct sbiret sbi_pmu_counter_start(unsigned long counter_idx_base, unsigned long counter_idx_mask, +unsigned long start_flags, uint64_t initial_value) +``` + +启动或启用一组计数器,并设置指定的初始值。counter_idx_base 和 counter_idx_mask 参数表示计数器集合,initial_value 参数指定计数器的初始值。 +start_flags 参数的位定义如下表 38 所示: +_表 38. PMU 计数器启动标志_ + +| 标志名称 | 位 | 描述 | +| :--------------------------- | ---------- | ----------------------------------------------- | +| SBI_PMU_START_SET_INIT_VALUE | 0:0 | parameter 根据 initial_value 参数设置计数器的值 | +| RESERVED | 1:(XLEN-1) | 所有非零值保留供将来使用 | + +> 注意:当 start_flags 中未设置 SBI_PMU_START_SET_INIT_VALUE 时,计数器值不会被修改,并且事件计数将从当前计数器值开始。 +> sbiret.error 中可能返回的错误代码在下表表 39 中列出。 +> _表 39. PMU 计数器启动错误_ + +| 错误代码 | 描述 | +| :---------------------- | -------------------------------- | +| SBI_SUCCESS | 计数器启动成功 | +| SBI_ERR_INVALID_PARAM | 参数中指定的一些计数器无效。 | +| SBI_ERR_ALREADY_STARTED | 参数中指定的一些计数器已被启动。 | + +### 11.9. 函数:停止或禁用一组计数器 (FID #4) + +``` +struct sbiret sbi_pmu_counter_stop(unsigned long counter_idx_base, +unsigned long counter_idx_mask, unsigned long stop_flags) +``` + +停止或禁用调用 HART 上的一组计数器。counter_idx_base 和 counter_idx_mask 参数表示计数器的集合。stop_flags 参数的位定义如下表 40 所示。 +_表 40. PMU 计数器停止禁用标志_ + +| 标志名称 | 位 | 描述 | +| :---------------------- | ---------- | ---------------------------- | +| SBI_PMU_STOP_FLAG_RESET | 0:0 | 重置计数器到事件的映射关系。 | +| RESERVED | 1:(XLEN-1) | 所有非零值都保留供将来使用。 | + +返回在 sbiret.error 中可能出现的错误代码如下表 41 所示。 +_表 41. PMU 计数器停止禁用错误_ + +| 错误代码 | 描述 | +| :---------------------- | -------------------------- | +| SBI_SUCCESS | 计数器禁用成功。 | +| SBI_ERR_INVALID_PARAM | 指定的某些计数器无效。 | +| SBI_ERR_ALREADY_STOPPED | 指定的某些计数器已经停止。 | + +### 11.10. 函数:读取固件计数器 (FID #5) + +``` +struct sbiret sbi_pmu_counter_fw_read(unsigned long counter_idx) +``` + +在 sbiret.value 中提供固件计数器的当前值。 +sbiret.error 中返回的可能错误代码如下表 42 所示。 +_表 42. PMU 读取固件计数错误_ + +| 错误代码 | 描述 | +| :-------------------- | ---------------------------------------------- | +| SBI_SUCCESS | 成功读取固件计数器的值。 | +| SBI_ERR_INVALID_PARAM | counter_idx 指向一个硬件计数器或无效的计数器。 | + +### 11.11. 函数:读取固件计数器的高位 (FID #6) + +``` +struct sbiret sbi_pmu_counter_fw_read_hi(unsigned long counter_idx) +``` + +在 sbiret.value 中提供当前固件计数器值的前 32 位。对于 RV64(或更高)系统,此函数在 sbiret.value 中总是返回 0。 + +在 sbiret.error 中返回的可能的错误代码显示在下面的表 43 中。 + +*表 43. PMU 计数器固件读高错误* + +| 错误代码 | 描述 | +| :-------------------- | :------------------------------------------------- | +| SBI_SUCCESS | 固件计数器读取成功。 | +| SBI_ERR_INVALID_PARAM | counter_idx 指向一个硬件计数器或一个无效的计数器。 | + +### 11.12. 函数:启用 PMU 快照功能 (FID #7) + +``` +struct sbiret sbi_pmu_snapshot_set_shmem(unsigned long shmem_phys_lo, unsigned long shmem_phys_hi) +``` + +为 PMU 状态快照设置共享内存区域。shmem_phys_lo 指定共享内存物理地址的低 XLEN 位,shmem_phys_hi 指定共享内存物理地址的高 XLEN 位。shmem_phys_lo 必须是 4096 字节(即页面)对齐。共享内存的大小必须是 4096 字节。共享内存的布局在表 44 中描述。 + +*表 44. SBI PMU 快照共享内存布局* + +| 名称 | 偏移量 | 大小 | 描述 | +| :---------------------- | :----- | :--- | :----------------------------------------------------------------------------------------- | +| counter_overflow_bitmap | 0x0000 | 8 | 一个所有逻辑溢出计数器的位图。只有在 Sscofpmf ISA 扩展可用时,它才有效。否则,它必须为零。 | +| counter_values | 0x0008 | 512 | 一个 64 位逻辑计数器的数组,每个索引代表与硬件/固件相关的每个逻辑计数器的值。 | +| Reserved | 0x0208 | 3576 | 保留给未来使用 | + +今后对这一结构的任何修订都应以向后兼容的方式进行,并将与 SBI 的一个版本相关。 + +这个函数应该在启动时每个哈特只被调用一次。一旦配置好,当 sbi_pmu_counter_stop 被调用并设置了 SBI_PMU_STOP_FLAG_TAKE_SNAPSHOT 标志时,SBI 实现可以对共享内存进行读/写访问。当 sbi_pmu_counter_start 被调用并设置了 SBI_PMU_START_FLAG_INIT_SNAPSHOT 标记时,SBI 实现有只读访问权。SBI 实现不得在其他时间访问该内存。 + +在 sbiret.error 中返回的可能的错误代码如下表 45 所示。 + +*表 45. PMU 设置快照区的错误* + +| 错误代码 | 描述 | +| ----------------------- | -------------------------------------------------------------------------------------------- | +| SBI_SUCCESS | 固件计数器读取成功。 | +| SBI_ERR_INVALID_ADDRESS | shmem_phys_lo 和 shmem_phys_hi 参数所指向的共享内存是不可写的,或者不满足 3.2 节的其他要求。 | + +### 11.13. 函数列表 + +_表 43. PMU 函数列表_ + +| 函数名 | SBI 版本 | FID | EID | +| :------------------------------ | -------- | --- | -------- | +| sbi_pmu_num_counters | 0.3 | 0 | 0x504D55 | +| sbi_pmu_counter_get_info | 0.3 | 1 | 0x504D55 | +| sbi_pmu_counter_config_matching | 0.3 | 2 | 0x504D55 | +| sbi_pmu_counter_start | 0.3 | 3 | 0x504D55 | +| sbi_pmu_counter_stop | 0.3 | 4 | 0x504D55 | +| sbi_pmu_counter_fw_read | 0.3 | 5 | 0x504D55 | +| sbi_pmu_counter_fw_read_hi | 2.0 | 6 | 0x504D55 | +| sbi_pmu_snapshot_set_shmem | 2.0 | 7 | 0x504D55 | + +## 章节 12. 调试控制台扩展 (EID #0x4442434E "DBCN") + +调试控制台扩展定义了一种通用机制,用于在监管模式软件中进行调试以及引导过程中的早期打印输出。 + +这个扩展取代了传统的控制台 putchar(EID #0x01)和控制台 getchar(EID #0x02)扩展。调试控制台扩展允许监督者模式的软件在一次 SBI 调用中写入或读取多个字节。 + +如果底层物理控制台有额外的位用于错误检查(或纠正),那么这些额外的位应该由 SBI 实现来处理。 + +> 注意:建议使用调试控制台扩展发送/接收的字节遵循 UTF-8 字符编码。 + +### 12.1. 函数:控制台写入 (FID #0) + +``` +struct sbiret sbi_debug_console_write(unsigned long num_bytes, unsigned long base_addr_lo, unsigned long base_addr_hi) +``` + +从输入存储器向调试控制台写入字节。 + +num_bytes 参数指定了输入存储器中的字节数。输入存储器的物理基址由两个 XLEN 位宽的参数表示。base_addr_lo 参数指定输入存储器物理基址的低 XLEN 位,base_addr_hi 参数指定输入存储器的高 XLEN 位。 + +这是一个非阻塞的 SBI 调用,如果调试台不能接受更多的字节,它可能会做部分/不写。 + +写入的字节数在 sbiret.value 中返回,可能的错误代码在 sbiret.error 中返回,如下表 47 所示。 + +*表 47. 调试控制台写入错误* + +| 错误代码 | 描述 | +| :-------------------- | :------------------------------------------------------------------------------------ | +| SBI_SUCCESS | 成功写入的字节数。 | +| SBI_ERR_INVALID_PARAM | num_bytes、base_addr_lo 和 base_addr_hi 参数所指向的内存不满足第 3.2 节中描述的要求。 | +| SBI_ERR_FAILED | 由于 I/O 错误,写入失败。 | + +### 12.2. 函数:控制台读取 (FID #1) + +``` +struct sbiret sbi_debug_console_read(unsigned long num_bytes, unsigned long base_addr_lo, unsigned long base_addr_hi) +``` + +从调试控制台读取字节到一个输出存储器。 + +num_bytes 参数规定了可以写入输出存储器的最大字节数。输出存储器的物理基址由两个 XLEN 位宽的参数表示。base_addr_lo 参数指定输出存储器物理基址的低 XLEN 位,base_addr_hi 参数指定输出存储器的高 XLEN 位。 + +这是一个非阻塞的 SBI 调用,如果在调试控制台上没有要读的字节,它就不会向输出内存写任何东西。 + +读取的字节数在 sbiret.value 中返回,可能的错误代码在 sbiret.error 中返回,如下表 48 所示。 + +*表 48. 调试控制台读取错误* + +| 错误代码 | 描述 | +| :-------------------- | :------------------------------------------------------------------------------------ | +| SBI_SUCCESS | 成功读取的字节数。 | +| SBI_ERR_INVALID_PARAM | num_bytes、base_addr_lo 和 base_addr_hi 参数所指向的内存不满足第 3.2 节中描述的要求。 | +| SBI_ERR_FAILED | 由于 I/O 错误,读取失败。 | + +### 12.3. 函数:控制台写字节 (FID #2) + +``` +struct sbiret sbi_debug_console_write_byte(uint8_t byte) +``` + +写一个单字节到调试控制台。 + +这是一个阻塞的 SBI 调用,它只在将指定的字节写入调试控制台后返回。如果有 I/O 错误,它也会以 SBI_ERR_FAILED 返回。 + +sbiret.value 被设置为 0,在 sbiret.error 中返回的可能的错误代码如下表 49 所示。 + +*表 49. 调试控制台写入字节的错误* + +| 错误代码 | 描述 | +| :------------- | --------------------------- | +| SBI_SUCCESS | 字节写入成功。 | +| SBI_ERR_FAILED | 由于 I/O 错误,写字节失败。 | + +### 12.4. 函数列表 + +*表 50. DBCN 事件列表* + +| 函数名 | SBI 版本 | FID | EID | +| :--------------------------- | :------- | :-- | :--------- | +| sbi_debug_console_write | 2.0 | 0 | 0x4442434E | +| sbi_debug_console_read | 2.0 | 1 | 0x4442434E | +| sbi_debug_console_write_byte | 2.0 | 2 | 0x4442434E | + +## 章节 13. 系统挂起扩展 (EID #0x53555350 "SUSP") + +系统挂起扩展定义了一组系统级睡眠状态和一个允许监督者模式软件请求系统过渡到睡眠状态的功能。睡眠状态由 32 位宽的标识符(sleep_type)来识别。这些标识符的可能值如表 51 所示。 + +术语 "系统 "指的是监管者软件的世界观。底层的 SBI 实现可以由机器模式的固件或管理程序提供。 + +系统挂起扩展没有为支持的睡眠类型提供任何探测方法。平台应该在其硬件描述中指定其支持的系统睡眠类型和每个类型的唤醒设备。SUSPEND_TO_RAM 睡眠类型是一个例外,它的存在是通过扩展来暗示的。 + +*表 51. SUSP 系统睡眠类型* + +| 类型 | 名字 | 描述 | +| :-------------------- | :------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 0 | SUSPEND_TO_RAM | 这是一个 "暂停到 RAM "的睡眠类型,类似于 ACPI 的 S2 或 S3。进入时要求除了调用的 hart 以外的所有 hart 都处于 HSM STOPPED 状态,并且所有 hart 寄存器和 CSR 都保存在 RAM 中。 | +| 0x00000001 0x7fffffff | | 保留给未来使用 | +| 0x80000000 0xffffffff | | 平台特有的系统睡眠类型 | +| > 0xffffffff | | 保留 | + +### 13.1. 函数:系统挂起 (FID #0) + +``` +struct sbiret sbi_system_suspend(uint32_t sleep_type, unsigned long resume_addr, unsigned long opaque) +``` + +sbi_system_suspend() 调用的返回意味着一个错误,表 53 中的错误代码将出现在 sbiret.error 中。一个成功的挂起和唤醒,会导致启动挂起的 hart 从 STOPPED 状态恢复。为了恢复,hart 将跳转到监督者模式,地址由 resume_addr 指定,具体寄存器值见表 52。 + +*表 52. SUSP 系统恢复寄存器状态* + +| 寄存器名称 | 寄存器值 | +| :------------------------------------- | :--------------- | +| satp | 0 | +| sstatus.SIE | 0 | +| a0 | hartid | +| a1 | opaque parameter | +| 所有其他的寄存器仍然处于未定义的状态。 | | + +> 注意:一个无符号的长参数对 resume_addr 来说就足够了,因为在 MMU 关闭的情况下,hart 将以监督者模式恢复执行,因此 resume_addr 必须小于 XLEN 位宽。 + +resume_addr 参数指向一个运行时指定的物理地址,hart 可以在系统暂停后以监督者模式恢复执行。 + +参数 opaque 是一个 XLEN 位的值,当系统暂停后,hart 在 resume_addr 恢复执行时,这个值将被设置在 a1 寄存器中。 + +除了确保所选睡眠类型的所有进入标准得到满足,例如确保其他 harts 处于 STOPPED 状态,调用者必须确保所有电源单元和域处于与所选睡眠类型兼容的状态。用于从系统睡眠状态恢复的电源单元、电源域和唤醒设备的准备工作是平台特定的,超出了本规范的范围。 + +当主管软件在虚拟机内运行时,SBI 的实现是由管理程序提供的。系统暂停在功能上的表现与本地情况相同,但可能不会导致任何物理功率的变化。 + +sbiret.error 中可能返回的错误代码见表 53。 + +*表 53. SUSP 系统挂起的错误* + +| 错误代码 | 描述 | +| :---------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------- | +| SBI_SUCCESS | 系统已暂停并成功恢复。 | +| SBI_ERR_INVALID_PARAM | sleep_type 是保留的,或者是平台特定的,未实现的。 | +| SBI_ERR_NOT_SUPPORTED | sleep_type 没有被保留,并且已经实现,但由于一个或多个依赖项缺失,平台不支持它。 | +| SBI_ERR_INVALID_ADDRESS | resume_addr 是无效的,可能是由于以下原因:1. 它不是一个有效的物理地址。2. 对该地址的可执行访问被物理内存保护机制或监督者模式的 H-扩展 G-阶段所禁止。 | +| SBI_ERR_FAILED | 暂停请求因未指明或未知的其他原因而失败。 | + +### 13.2. 函数列表 + +*表 54. SUSP 事件列表* + +| 函数名 | SBI 版本 | FID | EID | +| :----------------- | :------- | :-- | :--------- | +| sbi_system_suspend | 2.0 | 0 | 0x53555350 | + +## 章节 14. CPPC 扩展 (EID #0x43505043 "CPPC") + +ACPI 定义了协作处理器性能控制(CPPC)机制,这是一个抽象而灵活的机制,用于监管者模式的电源管理软件与平台中的实体协作,管理处理器的性能。 + +SBI CPPC 扩展提供了一个抽象,通过 SBI 调用访问 CPPC 寄存器。CPPC 寄存器可以是与一个单独的平台实体(如 BMC)共享的内存位置。即使 CPPC 被定义在 ACPI 规范中,也可能实现一个基于 Device Tree 的 CPPC 驱动。 + +表 55 定义了由 SBI CPPC 功能使用的所有 CPPC 寄存器的 32 位标识。32 位寄存器空间的前半部分与 ACPI 规范所定义的寄存器相对应。后半部分提供了 ACPI 规范中没有定义的信息,但也是监管者模式电源管理软件额外需要的。 + +*表 55. CPPC 寄存器* + +| 寄存器 ID | 寄存去 | 位宽 | 属性 | 描述 | +| :-------------------- | :------------------------------------ | :------ | :----------------------- | ---------------------------------------------------- | +| 0x00000000 | HighestPerformance | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.1 | +| 0x00000001 | NominalPerformance | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.2 | +| 0x00000002 | LowestNonlinearPerformance | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.4 | +| 0x00000003 | LowestPerformance | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.5 | +| 0x00000004 | GuaranteedPerformanceRegister | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.6 | +| 0x00000005 | DesiredPerformanceRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.2.3 | +| 0x00000006 | MinimumPerformanceRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.2.2 | +| 0x00000007 | MaximumPerformanceRegister | 32 | Read / WriteRead / Write | ACPI Spec 6.5: 8.4.6.1.2.1 | +| 0x00000008 | PerformanceReductionToleranceRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.2.4 | +| 0x00000009 | TimeWindowRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.2.5 | +| 0x0000000A | CounterWraparoundTime | 32 / 64 | Read-only | ACPI Spec 6.5: 8.4.6.1.3.1 | +| 0x0000000B | ReferencePerformanceCounterRegister | 32 / 64 | Read-only | ACPI Spec 6.5: 8.4.6.1.3.1 | +| 0x0000000C | DeliveredPerformanceCounterRegister | 32 / 64 | Read-only | ACPI Spec 6.5: 8.4.6.1.3.1 | +| 0x0000000D | PerformanceLimitedRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.3.2 | +| 0x0000000E | CPPCEnableRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.4 | +| 0x0000000F | AutonomousSelectionEnable | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.5 | +| 0x00000010 | AutonomousActivityWindowRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.6 | +| 0x00000011 | EnergyPerformancePreferenceRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.7 | +| 0x00000012 | ReferencePerformance | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.3 | +| 0x00000013 | LowestFrequency | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.7 | +| 0x00000014 | NominalFrequency | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.7 | +| 0x00000015-0x7FFFFFFF | | 32 | | 保留给未来使用。 | +| 0x80000000 | TransitionLatency | 32 | Read-only | 提供以纳秒为单位的最大(最坏情况)性能状态转换延迟。 | +| 0x800000010xFFFFFFFF | | 32 | | 保留给未来使用。 | + +### 14.1. 函数:探测 CPPC 寄存器 (FID #0) + +``` +struct sbiret sbi_cppc_probe(uint32_t cppc_reg_id) +``` + +探测由 cppc_reg_id 参数指定的 CPPC 寄存器是否被平台实现。 + +如果寄存器被实现,sbiret.value 将包含寄存器的宽度。如果寄存器没有实现,sbiret.value 将被设置为 0。 + +在 sbiret.error 中返回的可能的错误代码如表 56 所示。 + +*表 56. CPPC 探测错误* + +| 错误代码 | 描述 | +| :-------------------- | :----------------------------------------- | +| SBI_SUCCESS | 探测成功完成。 | +| SBI_ERR_INVALID_PARAM | cppc_reg_id 被保留。 | +| SBI_ERR_FAILED | 探测请求因未指明的或未知的其他原因而失败。 | + +### 14.2. 函数:读 CPPC 寄存器 (FID #1) + +``` +struct sbiret sbi_cppc_read(uint32_t cppc_reg_id) +``` + +读取 cppc_reg_id 参数中指定的寄存器,并在 sbiret.value 中返回数值。当监督者模式 XLEN 为 32 时,sbiret.value 将只包含 CPPC 寄存器值的低 32 位。 + +sbiret.error 中可能返回的错误代码见表 57。 + +*表 57. CPPC 读错误* + +| 错误代码 | 描述 | +| :-------------------- | :----------------------------------------- | +| SBI_SUCCESS | 读取已成功完成。 | +| SBI_ERR_INVALID_PARAM | cppc_reg_id 被保留。 | +| SBI_ERR_NOT_SUPPORTED | cppc_reg_id 没有被平台实现。 | +| SBI_ERR_DENIED | cppc_reg_id 是一个只写的寄存器。 | +| SBI_ERR_FAILED | 读取请求因未指定的或未知的其他原因而失败。 | + +### 14.3. 函数:读取 CPPC 寄存器高位 (FID #2) + +``` +struct sbiret sbi_cppc_read_hi(uint32_t cppc_reg_id) +``` + +读取参数 cppc_reg_id 中指定的寄存器的高 32 位的值,并在 sbiret.value 中返回该值。当主管模式 XLEN 为 64 或更高时,该函数在 sbiret.value 中总是返回 0。 + +sbiret.error 中可能返回的错误代码见表 58。 + +*表 58. CPPC 读高位错误* + +| 错误代码 | 描述 | +| :-------------------- | :----------------------------------------- | +| SBI_SUCCESS | 读取已成功完成。 | +| SBI_ERR_INVALID_PARAM | cppc_reg_id 被保留。 | +| SBI_ERR_NOT_SUPPORTED | cppc_reg_id 没有被平台实现。 | +| SBI_ERR_DENIED | cppc_reg_id 是一个只写的寄存器。 | +| SBI_ERR_FAILED | 读取请求因未指定的或未知的其他原因而失败。 | + +### 14.4. 函数:写 CPPC 寄存器 (FID #3) + +``` +struct sbiret sbi_cppc_write(uint32_t cppc_reg_id, uint64_t val) +``` + +将参数 val 中传递的值写到 cppc_reg_id 参数指定的寄存器中。 + +sbiret.error 中可能返回的错误代码见表 59。 + +*表 59. CPPC 写错误* + +| 错误代码 | 描述 | +| :-------------------- | :----------------------------------------- | +| SBI_SUCCESS | 写入已成功完成。 | +| SBI_ERR_INVALID_PARAM | cppc_reg_id 被保留。 | +| SBI_ERR_NOT_SUPPORTED | cppc_reg_id 没有被平台实现。 | +| SBI_ERR_DENIED | cppc_reg_id 是一个只读的寄存器。 | +| SBI_ERR_FAILED | 读取请求因未指定的或未知的其他原因而失败。 | + +### 14.5. 函数列表 + +*表 60. CPPC 函数列表* + +| 函数名 | SBI 版本 | FID | EID | +| :--------------- | :------- | :-- | :--------- | +| sbi_cppc_probe | 2.0 | 0 | 0x43505043 | +| sbi_cppc_read | 2.0 | 1 | 0x43505043 | +| sbi_cppc_read_hi | 2.0 | 2 | 0x43505043 | +| sbi_cppc_write | 2.0 | 3 | 0x43505043 | + +## 章节 15. 嵌套加速扩展 (EID #0x4E41434C "NACL") + +嵌套虚拟化是指一个虚拟化监控程序能够作为宿主客户机来运行另一个虚拟化监控程序的能力。RISC-V 嵌套虚拟化需要一个 L0 虚拟化监控程序(以虚拟化监控模式运行)来捕获并模拟 RISC-V H 扩展功能(如 CSR 访问、HFENCE 指令、HLV/HSV 指令等),以供 L1 虚拟化监控程序(以虚拟化监督者模式运行)使用。 + +SBI 嵌套加速扩展定义了 SBI 实现(或 L0 虚拟化监控程序)与监督者模式软件(或 L1 虚拟化监控程序)之间基于共享内存的接口,可以让两者协作减少 L0 虚拟化监控程序用于模拟 RISC-V H 扩展功能的陷阱。嵌套加速共享内存允许 L1 虚拟化监控程序批量处理多个 RISC-V H 扩展 CSR 访问和 HFENCE 请求,然后通过显式同步的 SBI 调用由 L0 虚拟化监控程序进行模拟。 + +> 注意:如果底层平台已经在硬件中实现了 RISC-V H 扩展,M 模式固件不应该实现 SBI 嵌套加速扩展。 + +该 SBI 扩展定义了一些可选的特性,必须由监督者模式软件(或 L1 虚拟化监控程序)在使用相应的 SBI 函数之前进行发现。每个嵌套加速的特性都被分配一个唯一的 ID,该 ID 是一个无符号 32 位整数。下面的表 61 列出了所有的嵌套加速特性。 + +*表 61. 嵌套加速功能* + +| 特性 ID | 特性名称 | 描述 | +| :---------- | :------------------------- | :------------- | +| 0x00000000 | SBI_NACL_FEAT_SYNC_CSR | 同步 CSR | +| 0x00000001 | SBI_NACL_FEAT_SYNC_HFENCE | 同步 HFENCE | +| 0x00000002 | SBI_NACL_FEAT_SYNC_SRET | 同步 SRET | +| 0x00000003 | SBI_NACL_FEAT_AUTOSWAP_CSR | 自动交换 CSR | +| >0x00000003 | RESERVED | 保留给未来使用 | + +为了使用 SBI 嵌套加速扩展,监督者模式软件(或 L1 虚拟化监控程序)在启动时必须为每个虚拟 hart 设置嵌套加速共享内存的物理地址。嵌套加速共享内存的物理基地址必须是 4096 字节(即一页)对齐,而嵌套加速共享内存的大小假设为 4096 + (1024 * (XLEN / 8)) 字节。下面的表 62 展示了嵌套加速共享内存的布局。 + +*表 62. 嵌套加速的共享内存布局* + +| 名称 | 偏移量 | 大小(byte) | 描述 | +| :------------ | :--------- | ------------ | :--------------------------------------------------------------------------------------------------------------------------- | +| Scratch space | 0x00000000 | 4096 | 嵌套加速特性的具体数据。 | +| CSR space | 0x00001000 | XLEN * 128 | 一个由 1024 个 XLEN 位字组成的数组,每个字对应于 RISC-V 特权规范 [priv_v1.12] 表 2.1 中定义的可能的 RISC-V H-extension CSR。 | + +上面表格 62 中所示的暂存空间的内容对于每个嵌套加速特性是单独定义的。 + +上面表格 62 中 CSR 空间的内容是 RISC-V H 扩展 CSR 值的数组,其中 CSR 寄存器 `` 在索引 `` = ((`` & 0xc00) >> 2) | (`` & 0xff) 处。除非某些嵌套加速特性定义了不同的行为,否则 SBI 实现(或 L0 虚拟化监控程序)在任何 RISC-V H 扩展 CSR 状态改变时必须更新 CSR 空间。下面的表格 63 显示了所有可能的 1024 个 RISC-V H 扩展 CSR 的 CSR 空间索引范围。 + +*表 63. 嵌套加速 H-扩展 CSR 指数范围* + +> TODO + +### 15.1. 特性:同步 CSR (ID #0) + +同步 CSR 特性描述的是 SBI 实现(或 L0 虚拟化监控程序)允许监督者模式软件(或 L1 虚拟化监控程序)使用 CSR 空间来编写 RISC-V H 扩展 CSR 的能力。 + +嵌套加速特性将范围为 0x0F80 - 0x0FFF(128 字节)的空闲空间定义为嵌套 CSR 脏位图。嵌套 CSR 脏位图为每个可能的 RISC-V H 扩展 CSR 包含了 1 位。 + +要在嵌套加速共享内存中写入 CSR,监督者模式软件(或 L1 虚拟化监控程序)必须执行以下操作: + +1. 计算 `` = ((`` & 0xc00) >> 2) | (`` & 0xff) +2. 在 CSR 空间中的索引为 `` 的字位置写入新的 CSR 值。 +3. 设置嵌套 CSR 脏位图中的第 `` 位。 + +要同步 CSR ``,SBI 实现(或 L0 虚拟化监控程序)必须执行以下操作: + +1. 计算 `` = ((`` & 0xc00) >> 2) | (`` & 0xff) +2. 如果嵌套 CSR 脏位图中的第 `` 位未被设置,则跳转到步骤 5。 +3. 使用 CSR 空间中索引为 `` 的字中的新 CSR 值,来模拟写入 CSR ``。 +4. 在嵌套的 CSR 脏位图中清除 `` 位。 +5. 将 CSR `` 的最新值写回到 CSR 空间中索引为 `` 的单词中。 + +在同步多个 CSR 时,如果 CSR `` 的值取决于其他 CSR `` 的值,则 SBI 实现(或 L0 虚拟化监视程序)必须在 CSR `` 之前同步 CSR ``。例如,CSR hip 的值取决于 CSR hvip 的值,这意味着首先模拟和写入 hvip,然后再写入 hip。 + +### 15.2. 特性:同步 HFENCE (ID #1) + +对于 synchronize HFENCE 特性的描述,它说明了 SBI 实现(或 L0 虚拟化监控程序)允许监督者软件(或 L1 虚拟化监控程序)通过临时空间发出 HFENCE 指令的能力。 + +该嵌套加速特性将临时空间偏移范围 0x0800 - 0x0F7F(1920 字节)定义为嵌套 HFENCE 条目的数组。嵌套 HFENCE 条目的总数为 3840 / XLEN,其中每个嵌套 HFENCE 条目由四个 XLEN 位的字组成。 + +嵌套的 HFENCE 条目相当于在一定范围的客户机地址上进行的 HFENCE 操作。下表 64 显示了嵌套 HFENCE 条目的格式,而下表 65 提供了嵌套 HFENCE 条目类型的列表。在监督者软件(或 L1 虚拟化监控程序)显式发出同步 HFENCE 请求时,SBI 实现(或 L0 虚拟化监控程序)将处理具有 Config.Pending 位设置的嵌套 HFENCE 条目。在处理挂起的嵌套 HFENCE 条目后,SBI 实现(或 L0 虚拟化监控程序)将清除这些条目的 Config.Pending 位。 + +*表 64. 嵌套的 HFENCE 条目格式* + +| 字 | 名称 | 编码 | +| :- | :---------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 0 | Config | 关于嵌套 HFENCE 条目的配置信息
BIT[XLEN-1:XLEN-1] - Pending BIT[XLEN-2:XLEN-4] - Reserved and must be zero BIT[XLEN-5:XLEN-8] - Type BIT[XLEN-9:XLEN-9] - Reserved and must be zero BIT[XLEN-10:XLEN-16] - Order if XLEN == 32 then BIT[15:9] - VMID BIT[8:0] - ASID else BIT[29:16] - VMID BIT[15:0] - ASID
假设失效的页面大小为 1 << (Config.Order + 12) 字节。 | +| 1 | Page_Number | 页地址右移 Config.Order + 12 位。 | +| 2 | Reserved | 保留用于将来使用,必须为零。 | +| 3 | Page_Count | 需要使无效的页面数量 | + +*表 65. 嵌套的 HFENCE 条目类型* + +| 类型 | 名称 | 描述 | +| :--- | :------------ | :------------------------------------------------------------------------------------------------------------------------------------------------ | +| 0 | GVMA | 在所有 VMID 中使一个客户机物理地址范围失效。配置字的 VMID 和 ASID 字段将被忽略且必须为零。 | +| 1 | GVMA_ALL | 使所有 VMID 中的所有客户机物理地址失效。配置字的 Order、VMID 和 ASID 字段将被忽略且必须为零。Page_Number 和 Page_Count 两个字将被忽略且必须为零。 | +| 2 | GVMA_VMID | 使特定 VMID 的客户机物理地址范围失效。配置字的 ASID 字段将被忽略且必须为零。 | +| 3 | GVMA_VMID_ALL | 使特定 VMID 的所有客户机物理地址失效。配置字的 Order 和 ASID 字段将被忽略且必须为零。Page_Number 和 Page_Count 两个字将被忽略且必须为零。 | +| 4 | VVMA | 使特定 VMID 的客户机虚拟地址范围失效。配置字的 ASID 字段将被忽略且必须为零。 | +| 5 | VVMA_ALL | 使特定 VMID 的所有客户机虚拟地址失效。配置字的 Order 和 ASID 字段将被忽略且必须为零。Page_Number 和 Page_Count 两个字将被忽略且必须为零。 | +| 6 | VVMA_ASID | 使特定 VMID 和 ASID 的客户机虚拟地址范围失效。 | +| 7 | VVMA_ASID_ALL | 使特定 VMID 和 ASID 的所有客户机虚拟地址失效。配置字的 Order 字段将被忽略且必须为零。Page_Number 和 Page_Count 两个字将被忽略且必须为零。 | +| >7 | Reserved | 保留以备将来使用。 | + +要添加一个嵌套的 HFENCE 条目,监督者软件(或 L1 虚拟化监控程序)必须执行以下操作: + +1. 找到一个未使用的嵌套 HFENCE 条目,其中 Config.Pending == 0。 +2. 更新嵌套的 HFENCE 条目中的 Page_Number 和 Page_Count 两个字。 +3. 更新嵌套 HFENCE 条目中的配置字,以设置 Config.Pending 位。 + +若要同步一个嵌套的 HFENCE 条目,SBI 实现(或 L0 虚拟化监控程序)必须执行以下操作: + +1. 如果 Config.Pending == 0,则不执行任何操作并跳过下面的步骤。 +2. 根据嵌套 HFENCE 条目中的详细信息处理 HFENCE。 +3. 清除嵌套 HFENCE 条目中的 Config.Pending 位。 + +### 15.3. 特性:同步 SRET (ID #2) + +同步 SRET 特性描述了 SBI 实现(或 L0 虚拟化监控程序)在嵌套加速共享内存中对监督者软件(或 L1 虚拟化监控程序)进行 CSR 和 HFENCE 同步,并进行 SRET 模拟的能力。 + +这个嵌套加速特性将临时空间的偏移范围定义为 0x0000 - 0x01FF(512 个字节),用作嵌套的 SRET 上下文。下表 66 展示了嵌套 SRET 上下文的内容。 + +*表 66. 嵌套的 SRET 上下文* + +| 偏移 | 名称 | 编码 | +| :-------------- | :------- | :--------------------------- | +| 0 * (XLEN / 8) | Reserved | 保留以备将来使用,必须为零。 | +| 1 * (XLEN / 8) | X1 | 在 GPR X1 中要恢复的值。 | +| 2 * (XLEN / 8) | X2 | 在 GPR X2 中要恢复的值。 | +| 3 * (XLEN / 8) | X3 | 在 GPR X3 中要恢复的值。 | +| 4 * (XLEN / 8) | X4 | 在 GPR X4 中要恢复的值。 | +| 5 * (XLEN / 8) | X5 | 在 GPR X5 中要恢复的值。 | +| 6 * (XLEN / 8) | X6 | 在 GPR X6 中要恢复的值。 | +| 7 * (XLEN / 8) | X7 | 在 GPR X7 中要恢复的值。 | +| 8 * (XLEN / 8) | X8 | 在 GPR X8 中要恢复的值。 | +| 9 * (XLEN / 8) | X9 | 在 GPR X9 中要恢复的值。 | +| 10 * (XLEN / 8) | X10 | 在 GPR X10 中要恢复的值。 | +| 11 * (XLEN / 8) | X11 | 在 GPR X11 中要恢复的值。 | +| 12 * (XLEN / 8) | X12 | 在 GPR X12 中要恢复的值。 | +| 13 * (XLEN / 8) | X13 | 在 GPR X13 中要恢复的值。 | +| 14 * (XLEN / 8) | X14 | 在 GPR X14 中要恢复的值。 | +| 15 * (XLEN / 8) | X15 | 在 GPR X15 中要恢复的值。 | +| 16 * (XLEN / 8) | X16 | 在 GPR X16 中要恢复的值。 | +| 17 * (XLEN / 8) | X17 | 在 GPR X17 中要恢复的值。 | +| 18 * (XLEN / 8) | X18 | 在 GPR X18 中要恢复的值。 | +| 19 * (XLEN / 8) | X19 | 在 GPR X19 中要恢复的值。 | +| 20 * (XLEN / 8) | X20 | 在 GPR X20 中要恢复的值。 | +| 21 * (XLEN / 8) | X21 | 在 GPR X21 中要恢复的值。 | +| 22 * (XLEN / 8) | X22 | 在 GPR X22 中要恢复的值。 | +| 23 * (XLEN / 8) | X23 | 在 GPR X23 中要恢复的值。 | +| 24 * (XLEN / 8) | X24 | 在 GPR X24 中要恢复的值。 | +| 25 * (XLEN / 8) | X25 | 在 GPR X25 中要恢复的值。 | +| 26 * (XLEN / 8) | X26 | 在 GPR X26 中要恢复的值。 | +| 27 * (XLEN / 8) | X27 | 在 GPR X27 中要恢复的值。 | +| 28 * (XLEN / 8) | X28 | 在 GPR X28 中要恢复的值。 | +| 29 * (XLEN / 8) | X29 | 在 GPR X29 中要恢复的值。 | +| 30 * (XLEN / 8) | X30 | 在 GPR X10 中要恢复的值。 | +| 31 * (XLEN / 8) | X31 | 在 GPR X31 中要恢复的值。 | +| 32 * (XLEN / 8) | X32 | 在 GPR X32 中要恢复的值。 | + +在发送同步 SRET 请求给 SBI 实现(或 L0 虚拟化监控程序)之前,监督者软件(或 L1 虚拟化监控程序)必须将要在嵌套 SRET 上下文的偏移量 `` * (XLEN / 8) 处恢复的 GPR X `` 值写入。 + +当监督者软件(或 L1 虚拟化监控程序)发出同步 SRET 请求时,SBI 实现(或 L0 虚拟化监控程序)必须执行以下操作: + +1. 如果 SBI_NACL_FEAT_SYNC_CSR 特性可用,则 + 1. 所有由 SBI 实现(或 L0 虚拟化监控程序)实现的 RISC-V H 扩展 CSR 按照第 15.1 节所描述的方式进行同步。这相当于调用 SBI 的 sbi_nacl_sync_csr(-1UL) 函数。 +2. 如果 SBI_NACL_FEAT_SYNC_HFENCE 特性可用,则 + 1. 所有嵌套的 HFENCE 条目按照第 15.2 节所描述的方式进行同步。这相当于调用 SBI 的 sbi_nacl_sync_hfence(-1UL) 函数。 +3. 从嵌套的 SRET 上下文中恢复通用寄存器 X `` 的值。 +4. 按照 RISC-V 特权规范 [priv_v1.12] 中定义的内容,模拟执行 SRET 指令。 + +### 15.4. 特性:自动交换 CSR (ID #3) + +自动交换 CSR 特性描述了 SBI 实现(或 L0 虚拟化监控程序)在以下情况下自动交换特定的 RISC-V H 扩展 CSR 值,这些 CSR 值位于嵌套的加速共享内存中: + +- 在为来自监督者软件(或 L1 虚拟化监控程序)的同步的 SRET 请求模拟执行 SRET 指令之前。 +- 在监督者(或 L1)虚拟化状态从 ON 变为 OFF 之后。 + +> 注意:监督者软件(或 L1 虚拟化监控程序)应该将 autoswap CSR 特性与同步 SRET 特性结合使用。 + +这个嵌套加速特性将 0x0200 - 0x027F(128 字节)的空间偏移范围定义为嵌套的自动交换上下文。下面的表格 67 展示了嵌套的自动交换上下文的内容。 + +*表 67. 嵌套的自动交换上下文* + +| 偏移量 | 名称 | 编码 | +| :-------------------- | :------------- | :------------------------------------------------------------------------------------- | +| 0 * (XLEN / 8) | Autoswap_Flags | 动交换标志位
BIT[XLEN-1:1] - 保留用于将来使用,必须为零
BIT[0:0] - HSTATUS | +| 1 * (XLEN / 8) | HSTATUS | 要与 HSTATUS CSR 交换的值 | +| 2 * (XLEN / 8) - 0x7F | Reserved | 保留以供将来使用。 | + +要启用从嵌套的自动交换上下文中自动交换 CSRs,监督者软件(或 L1 虚拟化监控程序)必须执行以下操作: + +1. 在嵌套的自动交换上下文中写入 HSTATUS 交换值。 +2. 在嵌套的自动交换上下文中设置 Autoswap_Flags.HSTATUS 位。 + +要从嵌套的自动交换上下文中交换 CSRs,SBI 实现(或 L0 虚拟化监控程序)必须执行以下操作: + +1. 如果在嵌套的自动交换上下文中设置了 Autoswap_Flags.HSTATUS 位,则将监督者的 HSTATUS CSR 值与嵌套的自动交换上下文中的 HSTATUS 值进行交换。 + +### 15.5. 函数:探头嵌套加速功能 (FID #0) + +``` +struct sbiret sbi_nacl_probe_feature(uint32_t feature_id) +``` + +探测一个嵌套加速特性。这是 SBI 嵌套加速扩展的一个必须函数。feature_id 参数指定要探测的嵌套加速特性。表格 61 提供了可能的特性 ID 列表。该函数在 sbiret.error 中始终返回 SBI_SUCCESS。如果给定的 feature_id 不可用,则在 sbiret.value 中返回 0,如果可用,则返回 1。 + +### 15.6. 函数:设置嵌套加速的共享内存 (FID #1) + +``` +struct sbiret sbi_nacl_set_shmem(unsigned long shmem_phys_lo, + unsigned long shmem_phys_hi, + unsigned long flags) +``` + +在调用的 hart 上设置并启用嵌套加速的共享内存。这是 SBI 嵌套加速扩展的强制功能。 + +如果 shmem_phys_lo 和 shmem_phys_hi 两个参数的位无法组成全 1,则 shmem_phys_lo 指定了共享内存物理基址的低 XLEN 位,而 shmem_phys_hi 指定了共享内存物理基址的高 XLEN 位。shmem_phys_lo 必须是 4096 字节(即一页)对齐,而共享内存的大小假定为 4096+(XLEN * 128) 字节。 + +如果 shmem_phys_lo 和 shmem_phys_hi 两个参数的位全部为 1,则嵌套加速功能被禁用。 + +flags 参数保留供将来使用,必须为零。 + +在 sbiret.error 中返回的可能错误代码在表 68 中显示。 + +*表 68. NACL 设置共享内存错误* + +| 错误代码 | 描述 | +| :---------------------- | :-------------------------------------------------------------------------------- | +| SBI_SUCCESS | 共享内存被成功地设置或清除。 | +| SBI_ERR_INVALID_PARAM | flags 参数不为零,或者 shmem_phys_lo 参数不是 4096 字节对齐的。 | +| SBI_ERR_INVALID_ADDRESS | 由 shmem_phys_lo 和 shmem_phys_hi 参数指定的共享内存不符合第 3.2 节中描述的要求。 | + +### 15.7. 函数:同步共享内存 CSR (FID #2) + +``` +struct sbiret sbi_nacl_sync_csr(unsigned long csr_num) +``` + +在嵌套加速共享内存中同步 CSR 寄存器。这是一个可选功能,仅在支持 SBI_NACL_FEAT_SYNC_CSR 功能时可用。参数 csr_num 指定要同步的 RISC-V H 扩展 CSR 寄存器集合。 + +如果 csr_num 的位全部为 1,则根据第 15.1 节的描述,同步 SBI 实现(或 L0 虚拟化监控程序)实现的所有 RISC-V H 扩展 CSR 寄存器。 + +如果 (csr_num & 0x300) == 0x200 并且 csr_num < 0x1000,则根据第 15.1 节的描述,仅同步由 csr_num 参数指定的单个 RISC-V H 扩展 CSR 寄存器。 + +在 sbiret.error 中返回的可能错误代码在表 69 中显示。 + +*表 69. NACL 同步 CSR 错误* + +| 错误代码 | 描述 | +| :-------------------- | :------------------------------------------------------------------------------------------------------------------------------------ | +| SBI_SUCCESS | CSRs 同步成功。 | +| SBI_ERR_NOT_SUPPORTED | SBI_NACL_FEAT_SYNC_CSR 特性不可用。 | +| SBI_ERR_INVALID_PARAM | csr_num 不是全部的位数,并且要么:
* (csr_num & 0x300) != 0x200 或
* csr_num >= 0x1000 或
* csr_num 未被 SBI 实现。 | +| SBI_ERR_NO_SHMEM | 嵌套加速共享内存不可用。 | + +### 15.8. 函数:同步共享内存 HFENCEs (FID #3) + +``` +struct sbiret sbi_nacl_sync_hfence(unsigned long entry_index) +``` + +在嵌套加速共享内存中同步 HFENCE 指令。这是一个可选功能,仅在支持 SBI_NACL_FEAT_SYNC_HFENCE 功能时可用。参数 entry_index 指定要同步的嵌套 HFENCE 指令集合。 + +如果 entry_index 的位全部为 1,根据第 15.2 节的描述,将同步所有嵌套 HFENCE 指令。 + +如果 entry_index < (3840 / XLEN),则根据第 15.2 节的描述,仅同步由 entry_index 参数指定的单个嵌套 HFENCE 指令。 + +在 sbiret.error 中返回的可能错误代码在表 70 中显示。 + +*表 70. NACL 同步 HFENCE 错误* + +| 错误代码 | 描述 | +| :-------------------- | :--------------------------------------------------------------- | +| SBI_SUCCESS | HFENCEs 同步成功 | +| SBI_ERR_NOT_SUPPORTED | SBI_NACL_FEAT_SYNC_HFENCE 特性不可用 | +| SBI_ERR_INVALID_PARAM | entry_index 不是全 1 位比特,并且 entry_index >= (3840 / XLEN)。 | +| SBI_ERR_NO_SHMEM | 嵌套加速共享内存不可用。 | + +### 15.9. 函数:同步共享内存并模拟 SRET 指令 (FID #4) + +``` +struct sbiret sbi_nacl_sync_sret(void) +``` + +同步嵌套加速共享内存中的 CSR 寄存器和 HFENCE 指令,并模拟 SRET 指令。这是一个可选功能,仅在支持 SBI_NACL_FEAT_SYNC_SRET 特性时可用。 + +监督者软件(或 L1 虚拟化监控程序)使用此函数进行同步 SRET 请求,并且 SBI 实现(或 L0 虚拟化监控程序)必须按第 15.3 节中的描述进行处理。 + +该函数在成功时不返回任何值,而在失败时 sbiret.error 可能会返回表 71 中所示的错误代码。 + +*表 71. NACL 同步 SRET 错误* + +| 错误代码 | 描述 | +| :-------------------- | :--------------------------------- | +| SBI_ERR_NOT_SUPPORTED | SBI_NACL_FEAT_SYNC_SRET 特性不可用 | +| SBI_ERR_NO_SHMEM | 嵌套加速共享内存不可用。 | + +### 15.10. 函数列表 + +*表 72. NACL 函数列表* + +| 函数名 | SBI 版本 | FID | EID | +| :--------------------- | :------- | :-- | :--------- | +| sbi_nacl_probe_feature | 2.0 | 0 | 0x4E41434C | +| sbi_nacl_set_shmem | 2.0 | 1 | 0x4E41434C | +| sbi_nacl_sync_csr | 2.0 | 2 | 0x4E41434C | +| sbi_nacl_sync_hfence | 2.0 | 3 | 0x4E41434C | +| sbi_nacl_sync_sret | 2.0 | 4 | 0x4E41434C | + +## 章节 16. 偷窃时间的核算扩展 (EID #0x535441 "STA") + +SBI 实现可能会遇到虚拟 HART 准备就绪但无法运行的情况。例如,当多个 SBI 领域共享处理器,或者 SBI 实现是一个虚拟机监视器,客户环境和其他客户环境或主机任务共享处理器时,可能会出现这些情况。当虚拟 HART 有时无法运行时,虚拟 HART 上下文中的观察者可能需要一种方式来解释比预期少的进展。虚拟 HART 准备好但必须等待的时间称为“被窃取的时间”,并且对其进行跟踪被称为窃取时间核算。窃取时间核算(STA)扩展定义了一种机制,使得 SBI 实现能够为每个虚拟 HART 向监督模式软件提供窃取时间和抢占信息。 + +SBI 嵌套加速扩展在 SBI 实现(或 L0 监督者模式程序)和主管软件(或 L1 管理程序)之间定义了一个基于共享内存的接口,允许两者合作减少 L0 管理程序为模拟 RISC-V H-扩展功能而采取的陷阱。嵌套的加速共享内存允许 L1 管理程序批量处理多个 RISC-V H-extension CSR 访问和 HFENCE 请求,然后由 L0 管理程序在明确的同步 SBI 调用时进行模拟。 + +### 16.1. 函数 设置窃取时间共享内存地址 + +``` +struct sbiret sbi_steal_time_set_shmem(unsigned long shmem_phys_lo, unsigned long shmem_phys_hi, uint32_t flags) +``` + +设置共享内存物理基址,用于调用虚拟 HART 的窃取时间核算,并启用 SBI 实现的窃取时间信息报告。 + +如果 shmem_phys_lo 和 shmem_phys_hi 不是全一的位数,那么 shmem_phys_lo 指定共享内存物理基址的低 XLEN 位,shmem_phys_hi 指定共享内存物理基址的高 XLEN 位。shmem_phys_lo 必须是 64 字节对齐的。共享内存的大小被假定为至少 64 字节。在从 SBI 调用返回之前,所有字节必须被 SBI 实现设置为零。 + +如果 shmem_phys_lo 和 shmem_phys_hi 是全一的位数,SBI 实现将停止报告虚拟 HART 的偷窃时间信息。 + +flags 必须被设置为零。 + +当共享内存被用于窃取时间核算时,预计共享内存不会被监督者模式的软件写入。然而,如果监督者模式软件的写入发生,SBI 实现必须不会表现失常,然而,在这种情况下,它可能会使共享内存充满不一致的数据。 + +SBI 实现必须在系统复位时停止对共享内存的写入。 + +共享内存布局的定义见表 73。 + +*表 73. STA 共享内存结构* + +| 名称 | 偏移量 | 大小 | 描述 | +| :-------- | :----- | :--- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| sequence | 0 | 4 | SBI 实现必须在写入窃取字段之前将该字段递增为奇数值,并在写入窃取后再次递增为偶数值(即,奇数序列号表示正在进行的更新)。SBI 实现应确保序列字段只在很短的时间内保持奇数。主管模式软件必须在读取窃取字段之前和之后检查这个字段,如果它是不同的或奇数的,则重复读取。这个序列字段使在 32 位环境下执行的监督者模式软件能够读取窃取字段的值。 | +| flags | 4 | 4 | 始终为零。未来对 SBI 调用的扩展可能允许监督者模式的软件写到共享内存的一些字段。只要 SBI 调用的 flags 参数使用零值,这种扩展就不会被启用。 | +| steal | 8 | 8 | 该虚拟 HART 没有空闲和安排外出的时间,单位是纳秒。虚拟 HART 空闲的时间将不被报告为偷窃时间。 | +| preempted | 16 | 1 | 一个指示标志,指示注册了此结构的虚拟 HART 是否正在运行或停止。如果虚拟 HART 被抢占(即窃取字段在增加),SBI 实现可能会写入非零值,而在虚拟 HART 重新开始运行之前,必须写入零值。例如,监督模式软件可以使用这个标志来检查锁的持有者是否已被抢占,并在这种情况下禁用 optimistic spinning。 | +| pad | 17 | 47 | 用零填充到 64 字节的边界。 | + +sbiret.value 被设置为 0,在 sbiret.error 中可能返回的错误代码如下表 74 所示。 + +*表 74. STA 设置窃取时间共享内存地址错误* + +| 错误代码 | 描述 | +| :---------------------- | :------------------------------------------------------------------------------------------- | +| SBI_SUCCESS | 偷取时间共享内存物理基址被成功设置或清除。 | +| SBI_ERR_INVALID_PARAM | flags 参数不为零或 shmem_phys_lo 不是 64 字节对齐的。 | +| SBI_ERR_INVALID_ADDRESS | shmem_phys_lo 和 shmem_phys_hi 参数所指向的共享内存是不可写的,或者不满足 3.2 节的其他要求。 | +| SBI_ERR_FAILED | 该请求因未指明的或未知的其他原因而失败。 | + +### 16.2. 函数列表 + +*表 75. STA 函数列表* + +| 函数名 | SBI 版本 | FID | EID | +| :----------------------- | :------- | :-- | :------- | +| sbi_steal_time_set_shmem | 2.0 | 0 | 0x535441 | + +## 章节 17. 实验性 SBI 扩展空间 (EIDs #0x08000000 - #0x08FFFFFF) + +未安排。 + +## 章节 18. 供应商特定 SBI 扩展空间 (EIDs #0x09000000 - #0x09FFFFFF) + +从 mvendorid 开始的低位。 + +## 章节 19. 固件特定 SBI 扩展空间 (EIDs #0x0A000000 - #0x0AFFFFFF) + +低位是 SBI 实现的 ID。固件特定的 SBI 扩展适用于 SBI 实现。它提供了在外部固件规范中定义的特定固件的 SBI 功能。 + +## 参考资料 + +▪ [priv_v1.12] The RISC-V Instruction Set Manual, Volume II: Privileged Architecture, Document Version 20211203, URL: github.com/riscv/riscv-isa-manual/releases/tag/Priv-v1.12 diff --git a/articles/images/sbi-specification-translation/fig1.jpg b/articles/images/sbi-specification-translation/fig1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..29606bbf423e27cca4b072435cbac049859327fd GIT binary patch literal 9376 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 literal 0 HcmV?d00001 diff --git a/articles/images/sbi-specification-translation/fig2.jpg b/articles/images/sbi-specification-translation/fig2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..77eff48319bbcc6b5078d149da9a9f91f03677d2 GIT binary patch literal 15500 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 literal 0 HcmV?d00001 diff --git a/articles/images/sbi-specification-translation/fig3.jpg b/articles/images/sbi-specification-translation/fig3.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6be45f79976d8ec1f0be02c2cc443da2b26cc92b GIT binary patch literal 21174 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 literal 0 HcmV?d00001 -- Gitee From 4ca52eec40896da846013e4318a08b576a0ffc66 Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Wed, 12 Jul 2023 08:09:27 +0000 Subject: [PATCH 02/68] =?UTF-8?q?1.=20=E6=B7=BB=E5=8A=A0=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E5=A4=B4=202.=20=E6=B7=BB=E5=8A=A0=E7=AE=80=E4=BB=8B=20=091.?= =?UTF-8?q?=20=E8=83=8C=E6=99=AF=20=092.=20=E4=BB=8B=E7=BB=8D=E7=9A=84?= =?UTF-8?q?=E4=B9=8B=E5=89=8D=E5=85=B6=E4=BB=96=E4=BA=BA=E7=9A=84=E5=B7=A5?= =?UTF-8?q?=E4=BD=9C=EF=BC=8C=E4=BB=A5=E5=8F=8A=E6=B2=9F=E9=80=9A=E7=9A=84?= =?UTF-8?q?=E6=83=85=E5=86=B5=20=093.=20=E7=BF=BB=E8=AF=91=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E7=9A=84=E4=B8=BB=E8=A6=81=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../20230710-sbi-specification-translation.md | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/articles/20230710-sbi-specification-translation.md b/articles/20230710-sbi-specification-translation.md index 7995530..ac0d241 100644 --- a/articles/20230710-sbi-specification-translation.md +++ b/articles/20230710-sbi-specification-translation.md @@ -1,8 +1,22 @@ -> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [tables]`
` -> Acknowledgements: 山东大学智能创新研究院 [RISC-V SBI-1.0.0 版本 中文](https://zhuanlan.zhihu.com/p/634337322) +> Title: [RISC-V SBI specification](https://github.com/riscv-non-isa/riscv-sbi-doc/releases/download/v2.0-rc1/riscv-sbi.pdf)`
` +> Author: riscv.org `
` +> Translator: 刘澳 <1219671600@qq.com>`
` +> Date: 2022/07/10`
` +> Revisor: Falcon `
` +> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux)`
` +> Acknowledgements: 山东大学 山东大学智能创新研究院 [RISC-V SBI-1.0.0 版本 中文](https://zhuanlan.zhihu.com/p/634337322) +> Sponsor: PLCT Lab, ISCAS # RISC-V SBI 翻译 +## 简介 + +本次翻译工作的背景为我正在调研 RISC-V SBI 相关知识,发现 SBI Spec 正在快速更新,目前已经开始更新 2.0 版本。于此同时,我发现国内已经有了 SBI Spec 的翻译工作,不过可能因为某些原因,并没有机构对 SBI Spec 2.0-rc1 进行翻译。我认为这对于我来说是一个很好的了解 SBI Spec 的机会,对于国内想要学学习 RISC-V SBI 的研究人员可能也会有些帮助,所以就开始了 SBI Spec 2.0-rc1 的翻译工作。 + +在这里非常感谢山东大学以及山东大学智能创新研究院,他们之前翻译了 SBI Spec 1.0 版本,我的工作是在他们的基础上完成的。在与原翻译人员通过邮件取得沟通后,他们非常慷慨的同意了我使用他们的工作做为基础来进行我的工作。 + +本次翻译的相较于前一版本,主要新增了 SBI Spec 2.0-rc1 版本的新增内容(在更改日志中提及),以及对原翻译的格式进行了一些调整,对一些术语进行了统一。 + ## 更改日志 ### 版本 2.0-rc1 -- Gitee From 887b102e1d3f2019dcc99b9f2e6a3e71c9235353 Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Wed, 12 Jul 2023 08:55:04 +0000 Subject: [PATCH 03/68] =?UTF-8?q?1.=20=E5=8E=BB=E6=8E=89=E5=87=A0=E5=A4=84?= =?UTF-8?q?=E8=8B=B1=E6=96=87=E5=8E=9F=E6=96=87=202.=20=E5=88=A0=E5=8E=BB?= =?UTF-8?q?=E5=87=A0=E5=A4=84=E5=9B=BE=E8=A1=A8=E7=BC=BA=E5=B0=91=E6=8D=A2?= =?UTF-8?q?=E8=A1=8C=E7=9A=84=E6=83=85=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../20230710-sbi-specification-translation.md | 112 ++++++++++++++++-- 1 file changed, 99 insertions(+), 13 deletions(-) diff --git a/articles/20230710-sbi-specification-translation.md b/articles/20230710-sbi-specification-translation.md index ac0d241..57a97d2 100644 --- a/articles/20230710-sbi-specification-translation.md +++ b/articles/20230710-sbi-specification-translation.md @@ -1,9 +1,9 @@ -> Title: [RISC-V SBI specification](https://github.com/riscv-non-isa/riscv-sbi-doc/releases/download/v2.0-rc1/riscv-sbi.pdf)`
` +> Title: [RISC-V SBI specification](https://github.com/riscv-non-isa/riscv-sbi-doc/releases/download/v2.0-rc1/riscv-sbi.pdf) `
` > Author: riscv.org `
` > Translator: 刘澳 <1219671600@qq.com>`
` -> Date: 2022/07/10`
` +> Date: 2022/07/10 `
` > Revisor: Falcon `
` -> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux)`
` +> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux) `
` > Acknowledgements: 山东大学 山东大学智能创新研究院 [RISC-V SBI-1.0.0 版本 中文](https://zhuanlan.zhihu.com/p/634337322) > Sponsor: PLCT Lab, ISCAS @@ -588,13 +588,15 @@ _表 17. HSM Hart 状态_ 在任何时刻,hart 应该处于上述提到的 hart 状态之一。SBI 实现对 hart 状态的转换应遵循图 3 所示的状态机。 ![fig3.jpg](images/sbi-specification-translation/fig3.jpg) + 图 3. SBI HSM 状态机 + 一个平台可以有多个 hart,这些 hart 被分组成层次拓扑组(如核心、集群、节点等),每个层次组都有独立的平台特定低功耗状态。这些层次拓扑组的平台特定低功耗状态可以表示为一个 hart 的平台特定挂起状态。SBI 实现可以利用较高层次拓扑组的挂起状态,使用以下一种方法之一: -1. Platform-coordinated **平台协调**: In this approach, when a hart becomes idle the supervisor-mode power- managment software will request deepest suspend state for the hart and higher topology groups. An SBI implementation should choose a suspend state at higher topology group which is: 在这种方法中,当一个 hart 变为空闲时,监管模式 S-mode 的功耗管理软件将请求该 hart 和较高层次组的最深挂起状态。SBI 实现应选择一个较高层次组的挂起状态,满足以下条件: - 1. 不比指定的挂起状态更深 +1. **平台协调**: 在这种方法中,当一个 hart 变为空闲时,监管模式 S-mode 的功耗管理软件将请求该 hart 和较高层次组的最深挂起状态。SBI 实现应选择一个较高层次组的挂起状态,满足以下条件: + 1. 不比指定的挂起状态更 2. 唤醒延迟不高于指定挂起状态的唤醒延迟 -2. OS-inititated **操作系统发起**: 在这种方法中,监管模式 S-mode 的功耗管理软件将在最后一个 hart 变为空闲后,直接请求较高层次组的挂起状态。当一个 hart 变为空闲时,监管模式的功耗管理软件总是选择该 hart 的挂起状态,但仅在该 hart 是组内最后一个运行的 hart 时,才为较高层次组选择一个挂起状态。SBI 实现应满足以下条件: +2. O**操作系统发起**: 在这种方法中,监管模式 S-mode 的功耗管理软件将在最后一个 hart 变为空闲后,直接请求较高层次组的挂起状态。当一个 hart 变为空闲时,监管模式的功耗管理软件总是选择该 hart 的挂起状态,但仅在该 hart 是组内最后一个运行的 hart 时,才为较高层次组选择一个挂起状态。SBI 实现应满足以下条件: 1. 永远不要选择与指定暂停状态不同的较高层次组的挂起状态 2. 总是优先选择最近请求的较高层次组的挂起状态。 @@ -1409,7 +1411,91 @@ SBI 嵌套加速扩展定义了 SBI 实现(或 L0 虚拟化监控程序)与 *表 63. 嵌套加速 H-扩展 CSR 指数范围* -> TODO + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
H-extension CSR addressSBI NACL CSR space index
[11:10][9:8][7:4]Hex RangeHex Range
0010xxxx0x200 - 0x2ff0x000 - 0x0ff
01100xxx0x600 - 0x67f0x100 - 0x17f
011010xx0x680 - 0x6bf0x180 - 0x1bf
011011xx0x6c0 - 0x6ff0x1c0 - 0x1ff
10100xxx0xa00 - 0xa7f0x200 - 0x27f
101010xx0xa80 - 0xabf0x280 - 0x2bf
101011xx0xac0 - 0xaff0x2c0 - 0x2ff
11100xxx0xe00 - 0xe7f0x300 - 0x37f
111010xx0xe80 - 0xebf0x380 - 0x3bf
111011xx0xec0 - 0xeff0x3c0 - 0x3ff
### 15.1. 特性:同步 CSR (ID #0) @@ -1443,12 +1529,12 @@ SBI 嵌套加速扩展定义了 SBI 实现(或 L0 虚拟化监控程序)与 *表 64. 嵌套的 HFENCE 条目格式* -| 字 | 名称 | 编码 | -| :- | :---------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| 0 | Config | 关于嵌套 HFENCE 条目的配置信息
BIT[XLEN-1:XLEN-1] - Pending BIT[XLEN-2:XLEN-4] - Reserved and must be zero BIT[XLEN-5:XLEN-8] - Type BIT[XLEN-9:XLEN-9] - Reserved and must be zero BIT[XLEN-10:XLEN-16] - Order if XLEN == 32 then BIT[15:9] - VMID BIT[8:0] - ASID else BIT[29:16] - VMID BIT[15:0] - ASID
假设失效的页面大小为 1 << (Config.Order + 12) 字节。 | -| 1 | Page_Number | 页地址右移 Config.Order + 12 位。 | -| 2 | Reserved | 保留用于将来使用,必须为零。 | -| 3 | Page_Count | 需要使无效的页面数量 | +| 字 | 名称 | 编码 | +| :- | :---------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 0 | Config | 关于嵌套 HFENCE 条目的配置信息
BIT[XLEN-1:XLEN-1] - 待定
BIT[XLEN-2:XLEN-4] - 保留,必须为零
BIT[XLEN-5:XLEN-8] - 类型
BIT[XLEN-9:XLEN-9] - 保留,必须为零
BIT[XLEN-10:XLEN-16] - 顺序
if XLEN == 32 then
    BIT[15:9] - VMID
    BIT[8:0] - ASID
else
    BIT[29:16] - VMID
    BIT[15:0] - ASID

假设失效的页面大小为 1 << (Config.Order + 12) 字节。 | +| 1 | Page_Number | 页地址右移 Config.Order + 12 位。 | +| 2 | Reserved | 保留用于将来使用,必须为零。 | +| 3 | Page_Count | 需要使无效的页面数量 | *表 65. 嵌套的 HFENCE 条目类型* -- Gitee From 814c5aed0437e81cd657eda655a2f8b71a519eef Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Wed, 12 Jul 2023 09:03:51 +0000 Subject: [PATCH 04/68] =?UTF-8?q?tico=20=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../20230710-sbi-specification-translation.md | 573 +++++++++--------- 1 file changed, 287 insertions(+), 286 deletions(-) diff --git a/articles/20230710-sbi-specification-translation.md b/articles/20230710-sbi-specification-translation.md index 57a97d2..6f574b9 100644 --- a/articles/20230710-sbi-specification-translation.md +++ b/articles/20230710-sbi-specification-translation.md @@ -1,11 +1,12 @@ -> Title: [RISC-V SBI specification](https://github.com/riscv-non-isa/riscv-sbi-doc/releases/download/v2.0-rc1/riscv-sbi.pdf) `
` -> Author: riscv.org `
` -> Translator: 刘澳 <1219671600@qq.com>`
` -> Date: 2022/07/10 `
` -> Revisor: Falcon `
` -> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux) `
` -> Acknowledgements: 山东大学 山东大学智能创新研究院 [RISC-V SBI-1.0.0 版本 中文](https://zhuanlan.zhihu.com/p/634337322) -> Sponsor: PLCT Lab, ISCAS +> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [spaces header tables]
+> Title: [RISC-V SBI specification](https://github.com/riscv-non-isa/riscv-sbi-doc/releases/download/v2.0-rc1/riscv-sbi.pdf)
+> Author: riscv.org
+> Translator: 刘澳 <1219671600@qq.com>
+> Date: 2022/07/10
+> Revisor: Falcon
+> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux)
+> Acknowledgements: 山东大学 山东大学智能创新研究院 [RISC-V SBI-1.0.0 版本 中文](https://zhuanlan.zhihu.com/p/634337322)
+> Sponsor: PLCT Lab, ISCAS # RISC-V SBI 翻译 @@ -98,7 +99,7 @@ SBI 规范不指定硬件发现的任何方法。监督者软件必须依赖其 本规范使用了下列术语及缩写: | 术语 | 含义 | -| :--- | --------------------------------------------------------------- | +|:-----|---------------------------------------------------------------| | ACPI | 高级配置和电源接口 (Advanced Configuration and Power Interface) | | ASID | 地址空间标识符 (Address Space Identifier) | | BMC | 底板管理控制器 (Baseboard Management Controller) | @@ -135,16 +136,16 @@ struct sbiret _表 1.标准 SBI 错误_ | 错误类型 | 值 | 描述 | -| :------------------------ | -- | :------------- | +|:--------------------------|----|:-------------| | SBI_SUCCESS | 0 | 顺利完成 | | SBI_ERR_FAILED | -1 | 失败 | | SBI_ERR_NOT_SUPPORTED | -2 | 不支持操作 | | SBI_ERR_INVALID_PARAM | -3 | 非法参数 | | SBI_ERR_DENIED | -4 | 拒绝 | | SBI_ERR_INVALID_ADDRESS | -5 | 非法地址 | -| SBI_ERR_ALREADY_AVAILABLE | -6 | (资源)已可用 | -| SBI_ERR_ALREADY_STARTED | -7 | (操作)已启动 | -| SBI_ERR_ALREADY_STOPPED | -8 | (操作)已停止 | +| SBI_ERR_ALREADY_AVAILABLE | -6 | (资源)已可用 | +| SBI_ERR_ALREADY_STARTED | -7 | (操作)已启动 | +| SBI_ERR_ALREADY_STOPPED | -8 | (操作)已停止 | | SBI_ERR_NO_SHMEM | -9 | 共享内存不可用 | 不支持的 SBI 扩展 ID(EID)或 SBI 函数 ID(FID)的 ECALL 必须返回错误代码 SBI_ERR_NOT_SUPPORTED。 @@ -162,8 +163,8 @@ _表 1.标准 SBI 错误_ 使用 hart 掩码的任何函数可能返回表 2 中列出的错误值,这些错误值是针对特定函数的错误值的补充。 _表 2. HART 掩码错误_ -| 错误代码 | 描述 | -| :-------------------- | :----------------------------------------------------------------------------------------------- | +| 错误代码 | 描述 | +|:----------------------|:---------------------------------------------------------------------------------------------| | SBI_ERR_INVALID_PARAM | hart_mask_base 或 hart_mask 中任何一个 hartid 无效,即该 hartid 未被平台启用或对监管程序不可用。 | ### 3.2 共享内存物理地址范围参数 @@ -244,7 +245,7 @@ struct sbiret sbi_get_mimpid(void); _表 3.基础函数列表_ | 函数名 | SBI 版本 | FID | EID | -| :----------------------- | -------- | --- | ---- | +|:-------------------------|----------|-----|------| | sbi_get_sbi_spec_version | 0.2 | 0 | 0x10 | | sbi_get_sbi_impl_id | 0.2 | 1 | 0x10 | | sbi_get_sbi_impl_version | 0.2 | 2 | 0x10 | @@ -258,7 +259,7 @@ _表 3.基础函数列表_ _表 4. SBI 实现 IDs_ | SBI 实现 ID | 名称 | -| :---------- | -------------------------- | +|:------------|----------------------------| | 0 | Berkeley Boot Loader (BBL) | | 1 | OpenSBI | | 2 | Xvisor | @@ -370,7 +371,7 @@ void sbi_shutdown(void) _表 5. 旧版函数列表_ | 函数名 | SBI 版本 | FID | EID | 替代 EID | -| :------------------------- | -------- | --- | --------- | ---------- | +|:---------------------------|----------|-----|-----------|------------| | sbi_set_timer | 0.1 | 0 | 0x00 | 0x54494D45 | | sbi_console_putchar | 0.1 | 0 | 0x01 | N/A | | sbi_console_getchar | 0.1 | 0 | 0x02 | N/A | @@ -400,7 +401,7 @@ struct sbiret sbi_set_timer(uint64_t stime_value) _表 6. 时钟函数列表_ | 函数名 | SBI 版本 | FID | EID | -| :------------ | -------- | --- | ---------- | +|:--------------|----------|-----|------------| | sbi_set_timer | 0.2 | 0 | 0x54494D45 | ## 章节 7. IPI 扩展 (EID #0x735049 "sPI: s-mode IPI") @@ -417,8 +418,8 @@ struct sbiret sbi_send_ipi(unsigned long hart_mask,unsigned long hart_mask_base) sbiret.error 返回的可能错误代码如表 7 所示。 _表 7. IPI 发送错误_ -| 错误代码 | 描述 | -| :--------------- | -------------------------------- | +| 错误代码 | 描述 | +|:---------------|-------------------------------| | SBI_SUCCESS 成功 | IPI 被成功发送至所有目标 harts。 | ### 7.2.函数列表 @@ -426,7 +427,7 @@ _表 7. IPI 发送错误_ _表 8. IPI 函数列表_ | 函数名 | SBI 版本 | FID | EID | -| :----------- | -------- | --- | -------- | +|:-------------|----------|-----|----------| | sbi_send_ipi | 0.2 | 0 | 0x735049 | ## 章节 8. RFENCE 扩展 (EID #0x52464E43 "RFNC") @@ -447,8 +448,8 @@ struct sbiret sbi_remote_fence_i(unsigned long hart_mask,unsigned long hart_mask sbiret.error 返回的可能错误代码如表 9 所示。 _表 9. RFENCE 远程 FENCE.I 指令错误_ -| 错误代码 | 描述 | -| :--------------- | -------------------------------- | +| 错误代码 | 描述 | +|:---------------|-------------------------------| | SBI_SUCCESS 成功 | IPI 被成功发送至所有目标 harts。 | ### 8.2.函数:远程 SFENCE.VMA 指令 (FID #1) @@ -462,10 +463,10 @@ unsigned long hart_mask_base, unsigned long start_addr, unsigned long size) sbiret.error 返回的可能错误代码如表 10 所示。 _表 10. RFENCE 远程 SFENCE.VMA 错误_ -| 错误代码 | 描述 | -| :------------------------------- | -------------------------------- | +| 错误代码 | 描述 | +|:-------------------------------|-------------------------------| | SBI_SUCCESS 成功 | IPI 被成功发送至所有目标 harts。 | -| SBI_ERR_INVALID_ADDRESS 非法地址 | start_addr 或 size 非法 | +| SBI_ERR_INVALID_ADDRESS 非法地址 | start_addr 或 size 非法 | ### 8.3.函数:远程 SFENCE.VMA 指定 ASID (FID #2) @@ -479,10 +480,10 @@ unsigned long asid) sbiret.error 返回的可能错误代码如表 11 所示。 _表 11. RFENCE 远程 SFENCE.VMA 指定 ASID 错误_ -| 错误代码 | 描述 | -| :------------------------------- | -------------------------------- | +| 错误代码 | 描述 | +|:-------------------------------|-------------------------------| | SBI_SUCCESS 成功 | IPI 被成功发送至所有目标 harts。 | -| SBI_ERR_INVALID_ADDRESS 非法地址 | start_addr 或 size 非法 | +| SBI_ERR_INVALID_ADDRESS 非法地址 | start_addr 或 size 非法 | ### 8.4.函数:远程 HFENCE.GVMA 指定 VMID (FID #3) @@ -496,11 +497,11 @@ unsigned long vmid) sbiret.error 返回的可能错误代码如表 12 所示。 _表 12. RFENCE 远程 HFENCE.GVMA 指定 VMID 错误_ -| 错误代码 | 描述 | -| :---------------------------------- | ---------------------------------------------------------------- | -| SBI_SUCCESS 成功 | IPI 被成功发送至所有目标 harts。 | +| 错误代码 | 描述 | +|:--------------------------------|--------------------------------------------------------------| +| SBI_SUCCESS 成功 | IPI 被成功发送至所有目标 harts。 | | SBI_ERR_NOT_SUPPORTED(操作)不支持 | 由于未被实现或目标 hart 中不错支持虚拟化扩展,则该操作不受支持。 | -| SBI_ERR_INVALID_ADDRESS 非法地址 | start_addr 或 size 非法 | +| SBI_ERR_INVALID_ADDRESS 非法地址 | start_addr 或 size 非法 | ### 8.5.函数:远程 HFENCE.GVMA (FID #4) @@ -513,11 +514,11 @@ unsigned long hart_mask_base, unsigned long start_addr, unsigned long size) sbiret.error 返回的可能错误代码如表 13 所示。 _表 13. RFENCE 远程 HFENCE.GVMA 错误_ -| 错误代码 | 描述 | -| :---------------------------------- | ---------------------------------------------------------------- | -| SBI_SUCCESS 成功 | IPI 被成功发送至所有目标 harts。 | +| 错误代码 | 描述 | +|:--------------------------------|--------------------------------------------------------------| +| SBI_SUCCESS 成功 | IPI 被成功发送至所有目标 harts。 | | SBI_ERR_NOT_SUPPORTED(操作)不支持 | 由于未被实现或目标 hart 中不错支持虚拟化扩展,则该操作不受支持。 | -| SBI_ERR_INVALID_ADDRESS 非法地址 | start_addr 或 size 非法 | +| SBI_ERR_INVALID_ADDRESS 非法地址 | start_addr 或 size 非法 | ### 8.6.函数:远程 HFENCE.VVMA 指定 ASID (FID #5) @@ -532,11 +533,11 @@ VMID(hgatp 寄存器)下所有客户机物理地址范围。此函数调用 sbiret.error 返回的可能错误代码如表 14 所示。 _表 14. RFENCE 远程 HFENCE.VVMA 指定 ASID 错误_ -| 错误代码 | 描述 | -| :---------------------------------- | ---------------------------------------------------------------- | -| SBI_SUCCESS 成功 | IPI 被成功发送至所有目标 harts。 | +| 错误代码 | 描述 | +|:--------------------------------|--------------------------------------------------------------| +| SBI_SUCCESS 成功 | IPI 被成功发送至所有目标 harts。 | | SBI_ERR_NOT_SUPPORTED(操作)不支持 | 由于未被实现或目标 hart 中不错支持虚拟化扩展,则该操作不受支持。 | -| SBI_ERR_INVALID_ADDRESS 非法地址 | start_addr 或 size 非法 | +| SBI_ERR_INVALID_ADDRESS 非法地址 | start_addr 或 size 非法 | ### 8.7.函数:远程 HFENCE.VVMA (FID #6) @@ -549,18 +550,18 @@ unsigned long hart_mask_base, unsigned long start_addr, unsigned long size) sbiret.error 返回的可能错误代码如表 15 所示。 _表 15.RFENCE 远程 HFENCE.VVMA 错误_ -| 错误代码 | 描述 | -| :---------------------------------- | ---------------------------------------------------------------- | -| SBI_SUCCESS 成功 | IPI 被成功发送至所有目标 harts。 | +| 错误代码 | 描述 | +|:--------------------------------|--------------------------------------------------------------| +| SBI_SUCCESS 成功 | IPI 被成功发送至所有目标 harts。 | | SBI_ERR_NOT_SUPPORTED(操作)不支持 | 由于未被实现或目标 hart 中不错支持虚拟化扩展,则该操作不受支持。 | -| SBI_ERR_INVALID_ADDRESS 非法地址 | start_addr 或 size 非法 | +| SBI_ERR_INVALID_ADDRESS 非法地址 | start_addr 或 size 非法 | ### 8.8.函数列表 _表 16. RFENCE 函数列表_ | 函数名 | SBI 版本 | FID | EID | -| :-------------------------- | -------- | --- | ---------- | +|:----------------------------|----------|-----|------------| | sbi_remote_fence_i | 0.2 | 0 | 0x52464E43 | | sbi_remote_sfence_vma | 0.2 | 1 | 0x52464E43 | | sbi_remote_sfence_vma_asid | 0.2 | 2 | 0x52464E43 | @@ -575,15 +576,15 @@ Hart State Management (HSM) 扩展引入了一组 Hart 状态和一组函数, 下面的表 17 描述了所有可能的 HSM 状态以及每个状态的唯一 HSM 状态标识符。: _表 17. HSM Hart 状态_ -| 状态 ID | 状态名 | 描述 | -| :------ | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------- | -| 0 | STARTED 启动 | 该 hart 处于物理上电状态,并正常执行。 | +| 状态 ID | 状态名 | 描述 | +|:--------|------------------------|------------------------------------------------------------------------------------------------------------------------------| +| 0 | STARTED 启动 | 该 hart 处于物理上电状态,并正常执行。 | | 1 | STOPPED 结束 | 该 hart 没有在监管者模式 S-mode 或任何更低特权模式下执行。如果底层平台具有关闭 hart 的物理机制,那么它可能被 SBI 实现关闭了电源。 | -| 2 | START_PENDING 等待被启动 | 另一个 hart 请求将该 hart 从 STOPPED 状态启动(或上电),而 SBI 实现仍在努力将该 hart 转换为 STARTED 状态。 | -| 3 | STOP_PENDING 结束等待 | 该 hart 在 STARTED 状态下请求停止(或关机),而 SBI 实现仍在努力将该 hart 转变为 STOPPED 状态。 | +| 2 | START_PENDING 等待被启动 | 另一个 hart 请求将该 hart 从 STOPPED 状态启动(或上电),而 SBI 实现仍在努力将该 hart 转换为 STARTED 状态。 | +| 3 | STOP_PENDING 结束等待 | 该 hart 在 STARTED 状态下请求停止(或关机),而 SBI 实现仍在努力将该 hart 转变为 STOPPED 状态。 | | 4 | SUSPENDED 挂起 | 该 hart 处于特定于平台的暂停(或低功耗)状态。 | -| 5 | SUSPEND_PENDING 等待挂起 | 该 hart 从 STARTED 状态请求将自身置于特定于平台的低功耗状态,并且 SBI 实现正在努力将该 hart 转换为特定于平台的 SUSPENDED 状态。 | -| 6 | RESUME_PENDING 等待恢复 | 中断或特定于平台的硬件事件导致 hart 从 SUSPENDED 状态恢复正常执行,而 SBI 实现正在努力将该 hart 转换为 STARTED 状态。 | +| 5 | SUSPEND_PENDING 等待挂起 | 该 hart 从 STARTED 状态请求将自身置于特定于平台的低功耗状态,并且 SBI 实现正在努力将该 hart 转换为特定于平台的 SUSPENDED 状态。 | +| 6 | RESUME_PENDING 等待恢复 | 中断或特定于平台的硬件事件导致 hart 从 SUSPENDED 状态恢复正常执行,而 SBI 实现正在努力将该 hart 转换为 STARTED 状态。 | 在任何时刻,hart 应该处于上述提到的 hart 状态之一。SBI 实现对 hart 状态的转换应遵循图 3 所示的状态机。 @@ -610,12 +611,12 @@ unsigned long start_addr, unsigned long opaque) SBI 实现以特定的寄存器值在指定的 start_addr 地址处启动执行目标 hart 的超级模式。具体的寄存器值如表 18 所述。 _表 18. HSM Hart 启动的寄存器状态_ -| 寄存器名称 | 寄存器值 | -| :--------------------------- | ----------- | -| satp | 0 | -| sstatus.SIE | 0 | -| a0 | hartid | -| a1 | opaque 参数 | +| 寄存器名称 | 寄存器值 | +|:----------------------------|-------------| +| satp | 0 | +| sstatus.SIE | 0 | +| a0 | hartid | +| a1 | opaque 参数 | | 其他寄存器仍处于未定义状态。 | | 此调用是异步的,具体来说,sbi_hart_start() 函数可以在目标 hart 开始执行之前返回,只要 SBI 实现能够确保返回的结果准确。如果 SBI 实现是在机器模式(M-mode)下执行的平台运行时固件,则必须在将控制权转移给 S-mode 模式软件之前配置 PMP 和其他 M-mode 状态。 @@ -625,13 +626,13 @@ opaque 参数是一个 XLEN 位的值,当 hart 在 start_addr 处开始执行 sbiret.error 中可能返回的错误代码在下表 19 中显示。 _表 19. HSM Hart 启动错误_ -| 错误代码 | 描述 | -| :-------------------------------- | -------------------------------------------------------------------------------- | -| SBI_SUCCESS 成功 | Hart 之前处于停止状态。它将从 start_addr 开始执行 | +| 错误代码 | 描述 | +|:--------------------------------|----------------------------------------------------------------------------| +| SBI_SUCCESS 成功 | Hart 之前处于停止状态。它将从 start_addr 开始执行 | | SBI_ERR_INVALID_ADDRESS 非法地址 | start_addr 无效,原因可能如下:无效物理地址。该地址被 PMP 禁止在 S-mode 下执行。 | -| SBI_ERR_INVALID_PARAM 非法参数 | Hartid 是无效的物理地址,因为其相应的 hart 不能以 S-mode 启动。 | -| SBI_ERR_ALREADY_AVAILAB LE 已存在 | 给定 hartid 已启动。 | -| SBI_ERR_FAILED 失败 | 未知原因造成的启动失败。 | +| SBI_ERR_INVALID_PARAM 非法参数 | Hartid 是无效的物理地址,因为其相应的 hart 不能以 S-mode 启动。 | +| SBI_ERR_ALREADY_AVAILAB LE 已存在 | 给定 hartid 已启动。 | +| SBI_ERR_FAILED 失败 | 未知原因造成的启动失败。 | ### 9.2.函数:HART 停止 (FID #1) @@ -644,7 +645,7 @@ sbiret.error 可能返回的错误代码如下表 20 所示。 _表 20. HSM Hart 停止错误_ | 错误代码 | 描述 | -| :------------------ | ---------------------- | +|:------------------|----------------------| | SBI_ERR_FAILED 失败 | 当前 hart 停止执行失败 | ### 9.3.函数:HART 获取状态 (FID #2) @@ -660,7 +661,7 @@ sbiret.error 可能返回的错误代码如下表 21 所示。 _表 21. HSM Hart 获取状态错误_ | 错误代码 | 描述 | -| :----------------------------- | ------------------ | +|:---------------------------|------------------| | SBI_ERR_INVALID_PARAM 无效参数 | 给定的 hartid 无效 | 由于任何并发的 sbi_hart_start()、sbi_hart_stop() 或 sbi_hart_suspend() 调用,harts 可能随时转换 HSM 状态,因此该函数的返回值可能不代表返回值验证时 hart 的实际状态。 @@ -678,19 +679,19 @@ SBI 实现将调用的 hart 置于由 suspend_type 参数指定的平台特定 从非保持性挂起状态恢复相对更复杂,需要软件还原各种特权模式下的 hart 寄存器和 CSR。从非保持性挂起状态恢复后,hart 将跳转到由 resume_addr 指定的 S-mode 模式地址,具体寄存器的值在表 22 中描述。 _表 22. HSM Hart 恢复寄存器状态_ -| 寄存器名称 | 寄存器值 | -| :------------------------- | ----------- | -| satp | 0 | -| sstatus.SIE | 0 | -| a0 | hartid | -| a1 | Opaque 参数 | +| 寄存器名称 | 寄存器值 | +|:--------------------------|-------------| +| satp | 0 | +| sstatus.SIE | 0 | +| a0 | hartid | +| a1 | Opaque 参数 | | 其他寄存器保持未定义状态。 | | suspend_type 参数是 32 位宽,可能的值如表 23 所示。 _表 23. HSM Hart 挂起类型_ | 值 | 描述 | -| :---------------------- | -------------------- | +|:------------------------|--------------------| | 0x00000000 | 默认保持性挂起 | | 0x00000001 - 0x0FFFFFFF | 保留供未来使用 | | 0x10000000 - 0x7FFFFFFF | 平台特定保持性挂起 | @@ -704,20 +705,20 @@ opaque 参数是一个 XLEN 位的值,当 hart 在非保留挂起后在 resume sbiret.error 中可能返回的错误代码如表 24 所示。 _表 24. HSM Hart 挂起错误_ -| 错误代码 | 描述 | -| :------------------------------- | --------------------------------------------------------------------------------------------- | -| SBI_SUCCESS 成功 | Hart 已成功地从保持性挂起状态中恢复。 | -| SBI_ERR_INVALID_PARAM 无效参数 | suspend_type 无效 | -| SBI_ERR_NOT_SUPPORTED 不支持 | suspend_type 有效但未实现。 | +| 错误代码 | 描述 | +|:-------------------------------|-----------------------------------------------------------------------------------------| +| SBI_SUCCESS 成功 | Hart 已成功地从保持性挂起状态中恢复。 | +| SBI_ERR_INVALID_PARAM 无效参数 | suspend_type 无效 | +| SBI_ERR_NOT_SUPPORTED 不支持 | suspend_type 有效但未实现。 | | SBI_ERR_INVALID_ADDRESS 无效地址 | resume_addr 无效,可能是由于以下原因:无效的物理地址。该地址被 PMP 禁止在 S-mode 模式下运行。 | -| SBI_ERR_FAILED 失败 | 挂起请求因未知原因而失败。 | +| SBI_ERR_FAILED 失败 | 挂起请求因未知原因而失败。 | ### 9.5. 函数列表 _表 25. HSM 函数列表_ | 函数名 | SBI 版本 | FID | EID | -| :--------------------------- | -------- | --- | -------- | +|:---------------------------|----------|-----|----------| | sbi_hart_start 启动 | 0.2 | 0 | 0x48534D | | sbi_hart_stop 停止 | 0.2 | 1 | 0x48534D | | sbi_hart_get_status 获取状态 | 0.2 | 2 | 0x48534D | @@ -738,7 +739,7 @@ reset_type 参数的宽度为 32 位,其可能的取值在下面的表 26 中 _表 26. SRST 系统复位类型_ | 值 | 描述 | -| :--------------------- | -------------------------- | +|:-----------------------|--------------------------| | 0x00000000 | 关机 | | 0x00000001 | 冷启动 | | 0x00000002 | 热启动 | @@ -750,7 +751,7 @@ reset_reason 是一个可选参数,表示系统复位的原因。该参数是 _表 27. SRST 系统复位原因_ | 值 | 描述 | -| :--------------------- | -------------------------- | +|:-----------------------|--------------------------| | 0x00000000 | 没有原因 | | 0x00000001 | 系统失败 | | 0x00000002 -0xDFFFFFFF | 保留以便未来使用 | @@ -764,7 +765,7 @@ sbiret.error 中可能返回的错误代码如表 28 所示。 _表 28. SRST 系统复位错误 s_ | 错误代码 | 描述 | -| :----------------------------- | ------------------------------- | +|:-----------------------------|-------------------------------| | SBI_ERR_INVALID_PARAM 无效参数 | reset_type 或 reset_reason 无效 | | SBI_ERR_NOT_SUPPORTED 不支持 | reset_type 有效但未被设定 | | SBI_ERR_FAILED 失败 | 未知原因的复位请求失败 | @@ -774,7 +775,7 @@ _表 28. SRST 系统复位错误 s_ _表 29. SRST 函数列表_ | Function Name | SBI Version | FID | EID | -| :--------------- | ----------- | --- | ---------- | +|:-----------------|-------------|-----|------------| | sbi_system_reset | 0.3 | 0 | 0x53525354 | ## 章节 11. 性能监控单元扩展 (EID #0x504D55 "PMU") @@ -800,19 +801,19 @@ event_idx[15:0] = code event_idx.type(即事件类型)对于所有硬件通用事件应为 0x0,并且每个硬件通用事件由一个唯一的 event_idx.code(即事件代码)标识,如下所示的表格 30 中所述。 _表 30. PMU 硬件事件_ -| 通用事件名称 | 代码 | 描述 | -| :--------------------------------- | ---- | ------------------------------------- | +| 通用事件名称 | 代码 | 描述 | +|:-----------------------------------|------|------------------------------------| | SBI_PMU_HW_NO_EVENT | 0 | 未使用的事件,因为 event_idx 不能为零 | -| SBI_PMU_HW_CPU_CYCLES | 1 | 每个 CPU 周期的事件 | -| SBI_PMU_HW_INSTRUCTIONS | 2 | 每个完成的指令的事件 | -| SBI_PMU_HW_CACHE_REFERENCES | 3 | 缓存命中的事件 | -| SBI_PMU_HW_CACHE_MISSES | 4 | 缓存未命中的事件 | -| SBI_PMU_HW_BRANCH_INSTRUCTIONS | 5 | 分支指令的事件 | -| SBI_PMU_HW_BRANCH_MISSES | 6 | 分支预测错误的事件 | -| SBI_PMU_HW_BUS_CYCLES | 7 | 每个 BUS 周期的事件 | -| SBI_PMU_HW_STALLED_CYCLES_FRONTEND | 8 | 微架构前端阻塞周期的事件 | -| SBI_PMU_HW_STALLED_CYCLES_BACKEND | 9 | 微架构后端阻塞周期的事件 | -| SBI_PMU_HW_REF_CPU_CYCLES | 10 | 每个参考 CPU 周期的事件 | +| SBI_PMU_HW_CPU_CYCLES | 1 | 每个 CPU 周期的事件 | +| SBI_PMU_HW_INSTRUCTIONS | 2 | 每个完成的指令的事件 | +| SBI_PMU_HW_CACHE_REFERENCES | 3 | 缓存命中的事件 | +| SBI_PMU_HW_CACHE_MISSES | 4 | 缓存未命中的事件 | +| SBI_PMU_HW_BRANCH_INSTRUCTIONS | 5 | 分支指令的事件 | +| SBI_PMU_HW_BRANCH_MISSES | 6 | 分支预测错误的事件 | +| SBI_PMU_HW_BUS_CYCLES | 7 | 每个 BUS 周期的事件 | +| SBI_PMU_HW_STALLED_CYCLES_FRONTEND | 8 | 微架构前端阻塞周期的事件 | +| SBI_PMU_HW_STALLED_CYCLES_BACKEND | 9 | 微架构后端阻塞周期的事件 | +| SBI_PMU_HW_REF_CPU_CYCLES | 10 | 每个参考 CPU 周期的事件 | > 注意:对于硬件通用事件,event_data(即事件数据)未使用,event_data 的所有非零值都保留供将来使用。 @@ -837,7 +838,7 @@ event_idx.code[0:0] = result_id _表 31. PMU 缓存事件 ID_ | 缓存事件名称 | 事件 ID | 描述 | -| :-------------------- | ------- | ----------------- | +|:----------------------|---------|-----------------| | SBI_PMU_HW_CACHE_L1D | 0 | 一级数据缓存事件 | | SBI_PMU_HW_CACHE_L1I | 1 | 一级指令缓存事件 | | SBI_PMU_HW_CACHE_LL | 2 | 最后一级缓存事件 | @@ -849,7 +850,7 @@ _表 31. PMU 缓存事件 ID_ _表 32. PMU 缓存操作 ID_ | 缓存操作名称 | 操作 ID | 描述 | -| :--------------------------- | ------- | ---------- | +|:-----------------------------|---------|----------| | SBI_PMU_HW_CACHE_OP_READ | 0 | 读取缓存行 | | SBI_PMU_HW_CACHE_OP_WRITE | 1 | 写入缓存行 | | SBI_PMU_HW_CACHE_OP_PREFETCH | 2 | 预取缓存行 | @@ -857,7 +858,7 @@ _表 32. PMU 缓存操作 ID_ _表 33. PMU 缓存操作结果 ID_ | 缓存结果名称 | 结果 ID | 描述 | -| :----------------------------- | ------- | ---------- | +|:-------------------------------|---------|----------| | SBI_PMU_HW_CACHE_RESULT_ACCESS | 0 | 缓存访问 | | SBI_PMU_HW_CACHE_RESULT_MISS | 1 | 缓存未命中 | @@ -877,7 +878,7 @@ _表 33. PMU 缓存操作结果 ID_ _表 34. PMU 固件事件_ | 固件事件名称 | 编码 | 描述 | -| :------------------------------------ | ---- | ----------------------------------------------------- | +|:--------------------------------------|------|-----------------------------------------------------| | SBI_PMU_FW_MISALIGNED_LOAD | 0 | 对齐错误加载陷入事件 | | SBI_PMU_FW_MISALIGNED_STORE | 1 | 对齐错误存储陷入事件 | | SBI_PMU_FW_ACCESS_LOAD | 2 | 加载访问陷入事件 | @@ -931,9 +932,9 @@ counter_info[XLEN-2:18] = Reserved for future use counter_info[XLEN-1] = Type (0 sbiret.error 中可能返回的错误代码如下表 35 所示。 _表 35. PMU 计数器获取信息错误_ -| 错误代码 | 描述 | -| :-------------------- | ------------------------------ | -| SBI_SUCCESS | 成功读取 counter_info | +| 错误代码 | 描述 | +|:----------------------|-----------------------------| +| SBI_SUCCESS | 成功读取 counter_info | | SBI_ERR_INVALID_PARAM | counter_idx 指向无效的计数器。 | ### 11.7. 函数:查找并配置匹配计数器 (FID #2) @@ -947,17 +948,17 @@ unsigned long counter_idx_mask, unsigned long config_flags, unsigned long event config_flags 参数表示额外的计数器配置和过滤标志。config_flags 参数的位定义如下所示: _表 36. PMU 计数器配置匹配标志_ -| 标志名称 | 位 | 描述 | -| :--------------------------- | ---------- | ------------------------------------ | -| SBI_PMU_CFG_FLAG_SKIP_MATCH | 0:0 | 跳过计数器匹配 | +| 标志名称 | 位 | 描述 | +|:-----------------------------|------------|----------------------------------| +| SBI_PMU_CFG_FLAG_SKIP_MATCH | 0:0 | 跳过计数器匹配 | | SBI_PMU_CFG_FLAG_CLEAR_VALUE | 1:1 | 在计数器配置中清零(或置零)计数器值 | -| SBI_PMU_CFG_FLAG_AUTO_START | 2:2 | 配置匹配计数器后自动启动计数器 | -| SBI_PMU_CFG_FLAG_SET_VUINH | 3:3 | VU 模式下禁止事件计数 | -| SBI_PMU_CFG_FLAG_SET_VSINH | 4:4 | VS 模式下禁止事件计数 | -| SBI_PMU_CFG_FLAG_SET_UINH | 5:5 | U 模式下禁止事件计数 | -| SBI_PMU_CFG_FLAG_SET_SINH | 6:6 | S 模式下禁止事件计数 | -| SBI_PMU_CFG_FLAG_SET_MINH | 7:7 | M 模式下禁止事件计数 | -| RESERVED | 8:(XLEN-1) | 所有非零值保留供将来使用 | +| SBI_PMU_CFG_FLAG_AUTO_START | 2:2 | 配置匹配计数器后自动启动计数器 | +| SBI_PMU_CFG_FLAG_SET_VUINH | 3:3 | VU 模式下禁止事件计数 | +| SBI_PMU_CFG_FLAG_SET_VSINH | 4:4 | VS 模式下禁止事件计数 | +| SBI_PMU_CFG_FLAG_SET_UINH | 5:5 | U 模式下禁止事件计数 | +| SBI_PMU_CFG_FLAG_SET_SINH | 6:6 | S 模式下禁止事件计数 | +| SBI_PMU_CFG_FLAG_SET_MINH | 7:7 | M 模式下禁止事件计数 | +| RESERVED | 8:(XLEN-1) | 所有非零值保留供将来使用 | > 注意:当在 config_flags 中设置了 SBI_PMU_CFG_FLAG_SKIP_MATCH 标志时,SBI 实现将无条件地从由 counter_idx_base 和 counter_idx_mask 指定的计数器集合中选择第一个计数器。 @@ -968,8 +969,8 @@ _表 36. PMU 计数器配置匹配标志_ > 如果操作失败,在 sbiret.error 中可能返回的错误代码如下表 37 所示: > _表 37. PMU 计数器配置匹配错误_ -| 错误代码 | 描述 | -| :-------------------- | ---------------------------------- | +| 错误代码 | 描述 | +|:----------------------|---------------------------------| | SBI_SUCCESS | 计数器已成功找到并配置。 | | SBI_ERR_INVALID_PARAM | 计数器集合中存在无效的计数器。 | | SBI_ERR_NOT_SUPPORTED | 没有任何计数器能够监视指定的事件。 | @@ -986,7 +987,7 @@ start_flags 参数的位定义如下表 38 所示: _表 38. PMU 计数器启动标志_ | 标志名称 | 位 | 描述 | -| :--------------------------- | ---------- | ----------------------------------------------- | +|:-----------------------------|------------|-----------------------------------------------| | SBI_PMU_START_SET_INIT_VALUE | 0:0 | parameter 根据 initial_value 参数设置计数器的值 | | RESERVED | 1:(XLEN-1) | 所有非零值保留供将来使用 | @@ -994,9 +995,9 @@ _表 38. PMU 计数器启动标志_ > sbiret.error 中可能返回的错误代码在下表表 39 中列出。 > _表 39. PMU 计数器启动错误_ -| 错误代码 | 描述 | -| :---------------------- | -------------------------------- | -| SBI_SUCCESS | 计数器启动成功 | +| 错误代码 | 描述 | +|:------------------------|-------------------------------| +| SBI_SUCCESS | 计数器启动成功 | | SBI_ERR_INVALID_PARAM | 参数中指定的一些计数器无效。 | | SBI_ERR_ALREADY_STARTED | 参数中指定的一些计数器已被启动。 | @@ -1010,16 +1011,16 @@ unsigned long counter_idx_mask, unsigned long stop_flags) 停止或禁用调用 HART 上的一组计数器。counter_idx_base 和 counter_idx_mask 参数表示计数器的集合。stop_flags 参数的位定义如下表 40 所示。 _表 40. PMU 计数器停止禁用标志_ -| 标志名称 | 位 | 描述 | -| :---------------------- | ---------- | ---------------------------- | +| 标志名称 | 位 | 描述 | +|:------------------------|------------|---------------------------| | SBI_PMU_STOP_FLAG_RESET | 0:0 | 重置计数器到事件的映射关系。 | | RESERVED | 1:(XLEN-1) | 所有非零值都保留供将来使用。 | 返回在 sbiret.error 中可能出现的错误代码如下表 41 所示。 _表 41. PMU 计数器停止禁用错误_ -| 错误代码 | 描述 | -| :---------------------- | -------------------------- | +| 错误代码 | 描述 | +|:------------------------|-------------------------| | SBI_SUCCESS | 计数器禁用成功。 | | SBI_ERR_INVALID_PARAM | 指定的某些计数器无效。 | | SBI_ERR_ALREADY_STOPPED | 指定的某些计数器已经停止。 | @@ -1034,8 +1035,8 @@ struct sbiret sbi_pmu_counter_fw_read(unsigned long counter_idx) sbiret.error 中返回的可能错误代码如下表 42 所示。 _表 42. PMU 读取固件计数错误_ -| 错误代码 | 描述 | -| :-------------------- | ---------------------------------------------- | +| 错误代码 | 描述 | +|:----------------------|---------------------------------------------| | SBI_SUCCESS | 成功读取固件计数器的值。 | | SBI_ERR_INVALID_PARAM | counter_idx 指向一个硬件计数器或无效的计数器。 | @@ -1051,8 +1052,8 @@ struct sbiret sbi_pmu_counter_fw_read_hi(unsigned long counter_idx) *表 43. PMU 计数器固件读高错误* -| 错误代码 | 描述 | -| :-------------------- | :------------------------------------------------- | +| 错误代码 | 描述 | +|:----------------------|:------------------------------------------------| | SBI_SUCCESS | 固件计数器读取成功。 | | SBI_ERR_INVALID_PARAM | counter_idx 指向一个硬件计数器或一个无效的计数器。 | @@ -1066,11 +1067,11 @@ struct sbiret sbi_pmu_snapshot_set_shmem(unsigned long shmem_phys_lo, unsigned *表 44. SBI PMU 快照共享内存布局* -| 名称 | 偏移量 | 大小 | 描述 | -| :---------------------- | :----- | :--- | :----------------------------------------------------------------------------------------- | +| 名称 | 偏移量 | 大小 | 描述 | +|:------------------------|:-------|:-----|:------------------------------------------------------------------------------------| | counter_overflow_bitmap | 0x0000 | 8 | 一个所有逻辑溢出计数器的位图。只有在 Sscofpmf ISA 扩展可用时,它才有效。否则,它必须为零。 | -| counter_values | 0x0008 | 512 | 一个 64 位逻辑计数器的数组,每个索引代表与硬件/固件相关的每个逻辑计数器的值。 | -| Reserved | 0x0208 | 3576 | 保留给未来使用 | +| counter_values | 0x0008 | 512 | 一个 64 位逻辑计数器的数组,每个索引代表与硬件/固件相关的每个逻辑计数器的值。 | +| Reserved | 0x0208 | 3576 | 保留给未来使用 | 今后对这一结构的任何修订都应以向后兼容的方式进行,并将与 SBI 的一个版本相关。 @@ -1080,9 +1081,9 @@ struct sbiret sbi_pmu_snapshot_set_shmem(unsigned long shmem_phys_lo, unsigned *表 45. PMU 设置快照区的错误* -| 错误代码 | 描述 | -| ----------------------- | -------------------------------------------------------------------------------------------- | -| SBI_SUCCESS | 固件计数器读取成功。 | +| 错误代码 | 描述 | +|-------------------------|------------------------------------------------------------------------------------------| +| SBI_SUCCESS | 固件计数器读取成功。 | | SBI_ERR_INVALID_ADDRESS | shmem_phys_lo 和 shmem_phys_hi 参数所指向的共享内存是不可写的,或者不满足 3.2 节的其他要求。 | ### 11.13. 函数列表 @@ -1090,7 +1091,7 @@ struct sbiret sbi_pmu_snapshot_set_shmem(unsigned long shmem_phys_lo, unsigned _表 43. PMU 函数列表_ | 函数名 | SBI 版本 | FID | EID | -| :------------------------------ | -------- | --- | -------- | +|:--------------------------------|----------|-----|----------| | sbi_pmu_num_counters | 0.3 | 0 | 0x504D55 | | sbi_pmu_counter_get_info | 0.3 | 1 | 0x504D55 | | sbi_pmu_counter_config_matching | 0.3 | 2 | 0x504D55 | @@ -1126,9 +1127,9 @@ num_bytes 参数指定了输入存储器中的字节数。输入存储器的物 *表 47. 调试控制台写入错误* -| 错误代码 | 描述 | -| :-------------------- | :------------------------------------------------------------------------------------ | -| SBI_SUCCESS | 成功写入的字节数。 | +| 错误代码 | 描述 | +|:----------------------|:----------------------------------------------------------------------------------| +| SBI_SUCCESS | 成功写入的字节数。 | | SBI_ERR_INVALID_PARAM | num_bytes、base_addr_lo 和 base_addr_hi 参数所指向的内存不满足第 3.2 节中描述的要求。 | | SBI_ERR_FAILED | 由于 I/O 错误,写入失败。 | @@ -1148,9 +1149,9 @@ num_bytes 参数规定了可以写入输出存储器的最大字节数。输出 *表 48. 调试控制台读取错误* -| 错误代码 | 描述 | -| :-------------------- | :------------------------------------------------------------------------------------ | -| SBI_SUCCESS | 成功读取的字节数。 | +| 错误代码 | 描述 | +|:----------------------|:----------------------------------------------------------------------------------| +| SBI_SUCCESS | 成功读取的字节数。 | | SBI_ERR_INVALID_PARAM | num_bytes、base_addr_lo 和 base_addr_hi 参数所指向的内存不满足第 3.2 节中描述的要求。 | | SBI_ERR_FAILED | 由于 I/O 错误,读取失败。 | @@ -1168,9 +1169,9 @@ sbiret.value 被设置为 0,在 sbiret.error 中返回的可能的错误代码 *表 49. 调试控制台写入字节的错误* -| 错误代码 | 描述 | -| :------------- | --------------------------- | -| SBI_SUCCESS | 字节写入成功。 | +| 错误代码 | 描述 | +|:---------------|-------------------------| +| SBI_SUCCESS | 字节写入成功。 | | SBI_ERR_FAILED | 由于 I/O 错误,写字节失败。 | ### 12.4. 函数列表 @@ -1178,7 +1179,7 @@ sbiret.value 被设置为 0,在 sbiret.error 中返回的可能的错误代码 *表 50. DBCN 事件列表* | 函数名 | SBI 版本 | FID | EID | -| :--------------------------- | :------- | :-- | :--------- | +|:-----------------------------|:---------|:----|:-----------| | sbi_debug_console_write | 2.0 | 0 | 0x4442434E | | sbi_debug_console_read | 2.0 | 1 | 0x4442434E | | sbi_debug_console_write_byte | 2.0 | 2 | 0x4442434E | @@ -1193,12 +1194,12 @@ sbiret.value 被设置为 0,在 sbiret.error 中返回的可能的错误代码 *表 51. SUSP 系统睡眠类型* -| 类型 | 名字 | 描述 | -| :-------------------- | :------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 类型 | 名字 | 描述 | +|:----------------------|:---------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 0 | SUSPEND_TO_RAM | 这是一个 "暂停到 RAM "的睡眠类型,类似于 ACPI 的 S2 或 S3。进入时要求除了调用的 hart 以外的所有 hart 都处于 HSM STOPPED 状态,并且所有 hart 寄存器和 CSR 都保存在 RAM 中。 | -| 0x00000001 0x7fffffff | | 保留给未来使用 | -| 0x80000000 0xffffffff | | 平台特有的系统睡眠类型 | -| > 0xffffffff | | 保留 | +| 0x00000001 0x7fffffff | | 保留给未来使用 | +| 0x80000000 0xffffffff | | 平台特有的系统睡眠类型 | +| > 0xffffffff | | 保留 | ### 13.1. 函数:系统挂起 (FID #0) @@ -1210,12 +1211,12 @@ sbi_system_suspend() 调用的返回意味着一个错误,表 53 中的错误 *表 52. SUSP 系统恢复寄存器状态* -| 寄存器名称 | 寄存器值 | -| :------------------------------------- | :--------------- | -| satp | 0 | -| sstatus.SIE | 0 | -| a0 | hartid | -| a1 | opaque parameter | +| 寄存器名称 | 寄存器值 | +|:--------------------------------------|:-----------------| +| satp | 0 | +| sstatus.SIE | 0 | +| a0 | hartid | +| a1 | opaque parameter | | 所有其他的寄存器仍然处于未定义的状态。 | | > 注意:一个无符号的长参数对 resume_addr 来说就足够了,因为在 MMU 关闭的情况下,hart 将以监督者模式恢复执行,因此 resume_addr 必须小于 XLEN 位宽。 @@ -1232,20 +1233,20 @@ sbiret.error 中可能返回的错误代码见表 53。 *表 53. SUSP 系统挂起的错误* -| 错误代码 | 描述 | -| :---------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------- | -| SBI_SUCCESS | 系统已暂停并成功恢复。 | -| SBI_ERR_INVALID_PARAM | sleep_type 是保留的,或者是平台特定的,未实现的。 | +| 错误代码 | 描述 | +|:------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------| +| SBI_SUCCESS | 系统已暂停并成功恢复。 | +| SBI_ERR_INVALID_PARAM | sleep_type 是保留的,或者是平台特定的,未实现的。 | | SBI_ERR_NOT_SUPPORTED | sleep_type 没有被保留,并且已经实现,但由于一个或多个依赖项缺失,平台不支持它。 | | SBI_ERR_INVALID_ADDRESS | resume_addr 是无效的,可能是由于以下原因:1. 它不是一个有效的物理地址。2. 对该地址的可执行访问被物理内存保护机制或监督者模式的 H-扩展 G-阶段所禁止。 | -| SBI_ERR_FAILED | 暂停请求因未指明或未知的其他原因而失败。 | +| SBI_ERR_FAILED | 暂停请求因未指明或未知的其他原因而失败。 | ### 13.2. 函数列表 *表 54. SUSP 事件列表* | 函数名 | SBI 版本 | FID | EID | -| :----------------- | :------- | :-- | :--------- | +|:-------------------|:---------|:----|:-----------| | sbi_system_suspend | 2.0 | 0 | 0x53555350 | ## 章节 14. CPPC 扩展 (EID #0x43505043 "CPPC") @@ -1258,32 +1259,32 @@ SBI CPPC 扩展提供了一个抽象,通过 SBI 调用访问 CPPC 寄存器。 *表 55. CPPC 寄存器* -| 寄存器 ID | 寄存去 | 位宽 | 属性 | 描述 | -| :-------------------- | :------------------------------------ | :------ | :----------------------- | ---------------------------------------------------- | -| 0x00000000 | HighestPerformance | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.1 | -| 0x00000001 | NominalPerformance | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.2 | -| 0x00000002 | LowestNonlinearPerformance | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.4 | -| 0x00000003 | LowestPerformance | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.5 | -| 0x00000004 | GuaranteedPerformanceRegister | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.6 | -| 0x00000005 | DesiredPerformanceRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.2.3 | -| 0x00000006 | MinimumPerformanceRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.2.2 | -| 0x00000007 | MaximumPerformanceRegister | 32 | Read / WriteRead / Write | ACPI Spec 6.5: 8.4.6.1.2.1 | -| 0x00000008 | PerformanceReductionToleranceRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.2.4 | -| 0x00000009 | TimeWindowRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.2.5 | -| 0x0000000A | CounterWraparoundTime | 32 / 64 | Read-only | ACPI Spec 6.5: 8.4.6.1.3.1 | -| 0x0000000B | ReferencePerformanceCounterRegister | 32 / 64 | Read-only | ACPI Spec 6.5: 8.4.6.1.3.1 | -| 0x0000000C | DeliveredPerformanceCounterRegister | 32 / 64 | Read-only | ACPI Spec 6.5: 8.4.6.1.3.1 | -| 0x0000000D | PerformanceLimitedRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.3.2 | -| 0x0000000E | CPPCEnableRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.4 | -| 0x0000000F | AutonomousSelectionEnable | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.5 | -| 0x00000010 | AutonomousActivityWindowRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.6 | -| 0x00000011 | EnergyPerformancePreferenceRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.7 | -| 0x00000012 | ReferencePerformance | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.3 | -| 0x00000013 | LowestFrequency | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.7 | -| 0x00000014 | NominalFrequency | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.7 | -| 0x00000015-0x7FFFFFFF | | 32 | | 保留给未来使用。 | +| 寄存器 ID | 寄存去 | 位宽 | 属性 | 描述 | +|:----------------------|:--------------------------------------|:--------|:-------------------------|---------------------------------------------------| +| 0x00000000 | HighestPerformance | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.1 | +| 0x00000001 | NominalPerformance | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.2 | +| 0x00000002 | LowestNonlinearPerformance | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.4 | +| 0x00000003 | LowestPerformance | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.5 | +| 0x00000004 | GuaranteedPerformanceRegister | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.6 | +| 0x00000005 | DesiredPerformanceRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.2.3 | +| 0x00000006 | MinimumPerformanceRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.2.2 | +| 0x00000007 | MaximumPerformanceRegister | 32 | Read / WriteRead / Write | ACPI Spec 6.5: 8.4.6.1.2.1 | +| 0x00000008 | PerformanceReductionToleranceRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.2.4 | +| 0x00000009 | TimeWindowRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.2.5 | +| 0x0000000A | CounterWraparoundTime | 32 / 64 | Read-only | ACPI Spec 6.5: 8.4.6.1.3.1 | +| 0x0000000B | ReferencePerformanceCounterRegister | 32 / 64 | Read-only | ACPI Spec 6.5: 8.4.6.1.3.1 | +| 0x0000000C | DeliveredPerformanceCounterRegister | 32 / 64 | Read-only | ACPI Spec 6.5: 8.4.6.1.3.1 | +| 0x0000000D | PerformanceLimitedRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.3.2 | +| 0x0000000E | CPPCEnableRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.4 | +| 0x0000000F | AutonomousSelectionEnable | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.5 | +| 0x00000010 | AutonomousActivityWindowRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.6 | +| 0x00000011 | EnergyPerformancePreferenceRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.7 | +| 0x00000012 | ReferencePerformance | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.3 | +| 0x00000013 | LowestFrequency | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.7 | +| 0x00000014 | NominalFrequency | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.7 | +| 0x00000015-0x7FFFFFFF | | 32 | | 保留给未来使用。 | | 0x80000000 | TransitionLatency | 32 | Read-only | 提供以纳秒为单位的最大(最坏情况)性能状态转换延迟。 | -| 0x800000010xFFFFFFFF | | 32 | | 保留给未来使用。 | +| 0x800000010xFFFFFFFF | | 32 | | 保留给未来使用。 | ### 14.1. 函数:探测 CPPC 寄存器 (FID #0) @@ -1299,8 +1300,8 @@ struct sbiret sbi_cppc_probe(uint32_t cppc_reg_id) *表 56. CPPC 探测错误* -| 错误代码 | 描述 | -| :-------------------- | :----------------------------------------- | +| 错误代码 | 描述 | +|:----------------------|:----------------------------------------| | SBI_SUCCESS | 探测成功完成。 | | SBI_ERR_INVALID_PARAM | cppc_reg_id 被保留。 | | SBI_ERR_FAILED | 探测请求因未指明的或未知的其他原因而失败。 | @@ -1317,8 +1318,8 @@ sbiret.error 中可能返回的错误代码见表 57。 *表 57. CPPC 读错误* -| 错误代码 | 描述 | -| :-------------------- | :----------------------------------------- | +| 错误代码 | 描述 | +|:----------------------|:----------------------------------------| | SBI_SUCCESS | 读取已成功完成。 | | SBI_ERR_INVALID_PARAM | cppc_reg_id 被保留。 | | SBI_ERR_NOT_SUPPORTED | cppc_reg_id 没有被平台实现。 | @@ -1337,8 +1338,8 @@ sbiret.error 中可能返回的错误代码见表 58。 *表 58. CPPC 读高位错误* -| 错误代码 | 描述 | -| :-------------------- | :----------------------------------------- | +| 错误代码 | 描述 | +|:----------------------|:----------------------------------------| | SBI_SUCCESS | 读取已成功完成。 | | SBI_ERR_INVALID_PARAM | cppc_reg_id 被保留。 | | SBI_ERR_NOT_SUPPORTED | cppc_reg_id 没有被平台实现。 | @@ -1357,8 +1358,8 @@ sbiret.error 中可能返回的错误代码见表 59。 *表 59. CPPC 写错误* -| 错误代码 | 描述 | -| :-------------------- | :----------------------------------------- | +| 错误代码 | 描述 | +|:----------------------|:----------------------------------------| | SBI_SUCCESS | 写入已成功完成。 | | SBI_ERR_INVALID_PARAM | cppc_reg_id 被保留。 | | SBI_ERR_NOT_SUPPORTED | cppc_reg_id 没有被平台实现。 | @@ -1370,7 +1371,7 @@ sbiret.error 中可能返回的错误代码见表 59。 *表 60. CPPC 函数列表* | 函数名 | SBI 版本 | FID | EID | -| :--------------- | :------- | :-- | :--------- | +|:-----------------|:---------|:----|:-----------| | sbi_cppc_probe | 2.0 | 0 | 0x43505043 | | sbi_cppc_read | 2.0 | 1 | 0x43505043 | | sbi_cppc_read_hi | 2.0 | 2 | 0x43505043 | @@ -1389,7 +1390,7 @@ SBI 嵌套加速扩展定义了 SBI 实现(或 L0 虚拟化监控程序)与 *表 61. 嵌套加速功能* | 特性 ID | 特性名称 | 描述 | -| :---------- | :------------------------- | :------------- | +|:------------|:---------------------------|:-------------| | 0x00000000 | SBI_NACL_FEAT_SYNC_CSR | 同步 CSR | | 0x00000001 | SBI_NACL_FEAT_SYNC_HFENCE | 同步 HFENCE | | 0x00000002 | SBI_NACL_FEAT_SYNC_SRET | 同步 SRET | @@ -1400,10 +1401,10 @@ SBI 嵌套加速扩展定义了 SBI 实现(或 L0 虚拟化监控程序)与 *表 62. 嵌套加速的共享内存布局* -| 名称 | 偏移量 | 大小(byte) | 描述 | -| :------------ | :--------- | ------------ | :--------------------------------------------------------------------------------------------------------------------------- | -| Scratch space | 0x00000000 | 4096 | 嵌套加速特性的具体数据。 | -| CSR space | 0x00001000 | XLEN * 128 | 一个由 1024 个 XLEN 位字组成的数组,每个字对应于 RISC-V 特权规范 [priv_v1.12] 表 2.1 中定义的可能的 RISC-V H-extension CSR。 | +| 名称 | 偏移量 | 大小(byte) | 描述 | +|:--------------|:-----------|------------|:-------------------------------------------------------------------------------------------------------------------------| +| Scratch space | 0x00000000 | 4096 | 嵌套加速特性的具体数据。 | +| CSR space | 0x00001000 | XLEN * 128 | 一个由 1024 个 XLEN 位字组成的数组,每个字对应于 RISC-V 特权规范 [priv_v1.12] 表 2.1 中定义的可能的 RISC-V H-extension CSR。 | 上面表格 62 中所示的暂存空间的内容对于每个嵌套加速特性是单独定义的。 @@ -1414,8 +1415,8 @@ SBI 嵌套加速扩展定义了 SBI 实现(或 L0 虚拟化监控程序)与 - - + + @@ -1529,26 +1530,26 @@ SBI 嵌套加速扩展定义了 SBI 实现(或 L0 虚拟化监控程序)与 *表 64. 嵌套的 HFENCE 条目格式* -| 字 | 名称 | 编码 | -| :- | :---------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| 0 | Config | 关于嵌套 HFENCE 条目的配置信息
BIT[XLEN-1:XLEN-1] - 待定
BIT[XLEN-2:XLEN-4] - 保留,必须为零
BIT[XLEN-5:XLEN-8] - 类型
BIT[XLEN-9:XLEN-9] - 保留,必须为零
BIT[XLEN-10:XLEN-16] - 顺序
if XLEN == 32 then
    BIT[15:9] - VMID
    BIT[8:0] - ASID
else
    BIT[29:16] - VMID
    BIT[15:0] - ASID

假设失效的页面大小为 1 << (Config.Order + 12) 字节。 | -| 1 | Page_Number | 页地址右移 Config.Order + 12 位。 | -| 2 | Reserved | 保留用于将来使用,必须为零。 | -| 3 | Page_Count | 需要使无效的页面数量 | +| 字 | 名称 | 编码 | +|:---|:------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| 0 | Config | 关于嵌套 HFENCE 条目的配置信息
BIT[XLEN-1:XLEN-1] - 待定
BIT[XLEN-2:XLEN-4] - 保留,必须为零
BIT[XLEN-5:XLEN-8] - 类型
BIT[XLEN-9:XLEN-9] - 保留,必须为零
BIT[XLEN-10:XLEN-16] - 顺序
if XLEN == 32 then
BIT[15:9] - VMID
BIT[8:0] - ASID
else
BIT[29:16] - VMID
BIT[15:0] - ASID

假设失效的页面大小为 1 << (Config.Order + 12) 字节。 | +| 1 | Page_Number | 页地址右移 Config.Order + 12 位。 | +| 2 | Reserved | 保留用于将来使用,必须为零。 | +| 3 | Page_Count | 需要使无效的页面数量 | *表 65. 嵌套的 HFENCE 条目类型* -| 类型 | 名称 | 描述 | -| :--- | :------------ | :------------------------------------------------------------------------------------------------------------------------------------------------ | -| 0 | GVMA | 在所有 VMID 中使一个客户机物理地址范围失效。配置字的 VMID 和 ASID 字段将被忽略且必须为零。 | +| 类型 | 名称 | 描述 | +|:-----|:--------------|:--------------------------------------------------------------------------------------------------------------------------------------------| +| 0 | GVMA | 在所有 VMID 中使一个客户机物理地址范围失效。配置字的 VMID 和 ASID 字段将被忽略且必须为零。 | | 1 | GVMA_ALL | 使所有 VMID 中的所有客户机物理地址失效。配置字的 Order、VMID 和 ASID 字段将被忽略且必须为零。Page_Number 和 Page_Count 两个字将被忽略且必须为零。 | -| 2 | GVMA_VMID | 使特定 VMID 的客户机物理地址范围失效。配置字的 ASID 字段将被忽略且必须为零。 | -| 3 | GVMA_VMID_ALL | 使特定 VMID 的所有客户机物理地址失效。配置字的 Order 和 ASID 字段将被忽略且必须为零。Page_Number 和 Page_Count 两个字将被忽略且必须为零。 | -| 4 | VVMA | 使特定 VMID 的客户机虚拟地址范围失效。配置字的 ASID 字段将被忽略且必须为零。 | -| 5 | VVMA_ALL | 使特定 VMID 的所有客户机虚拟地址失效。配置字的 Order 和 ASID 字段将被忽略且必须为零。Page_Number 和 Page_Count 两个字将被忽略且必须为零。 | -| 6 | VVMA_ASID | 使特定 VMID 和 ASID 的客户机虚拟地址范围失效。 | -| 7 | VVMA_ASID_ALL | 使特定 VMID 和 ASID 的所有客户机虚拟地址失效。配置字的 Order 字段将被忽略且必须为零。Page_Number 和 Page_Count 两个字将被忽略且必须为零。 | -| >7 | Reserved | 保留以备将来使用。 | +| 2 | GVMA_VMID | 使特定 VMID 的客户机物理地址范围失效。配置字的 ASID 字段将被忽略且必须为零。 | +| 3 | GVMA_VMID_ALL | 使特定 VMID 的所有客户机物理地址失效。配置字的 Order 和 ASID 字段将被忽略且必须为零。Page_Number 和 Page_Count 两个字将被忽略且必须为零。 | +| 4 | VVMA | 使特定 VMID 的客户机虚拟地址范围失效。配置字的 ASID 字段将被忽略且必须为零。 | +| 5 | VVMA_ALL | 使特定 VMID 的所有客户机虚拟地址失效。配置字的 Order 和 ASID 字段将被忽略且必须为零。Page_Number 和 Page_Count 两个字将被忽略且必须为零。 | +| 6 | VVMA_ASID | 使特定 VMID 和 ASID 的客户机虚拟地址范围失效。 | +| 7 | VVMA_ASID_ALL | 使特定 VMID 和 ASID 的所有客户机虚拟地址失效。配置字的 Order 字段将被忽略且必须为零。Page_Number 和 Page_Count 两个字将被忽略且必须为零。 | +| >7 | Reserved | 保留以备将来使用。 | 要添加一个嵌套的 HFENCE 条目,监督者软件(或 L1 虚拟化监控程序)必须执行以下操作: @@ -1570,41 +1571,41 @@ SBI 嵌套加速扩展定义了 SBI 实现(或 L0 虚拟化监控程序)与 *表 66. 嵌套的 SRET 上下文* -| 偏移 | 名称 | 编码 | -| :-------------- | :------- | :--------------------------- | +| 偏移 | 名称 | 编码 | +|:----------------|:---------|:-------------------------| | 0 * (XLEN / 8) | Reserved | 保留以备将来使用,必须为零。 | -| 1 * (XLEN / 8) | X1 | 在 GPR X1 中要恢复的值。 | -| 2 * (XLEN / 8) | X2 | 在 GPR X2 中要恢复的值。 | -| 3 * (XLEN / 8) | X3 | 在 GPR X3 中要恢复的值。 | -| 4 * (XLEN / 8) | X4 | 在 GPR X4 中要恢复的值。 | -| 5 * (XLEN / 8) | X5 | 在 GPR X5 中要恢复的值。 | -| 6 * (XLEN / 8) | X6 | 在 GPR X6 中要恢复的值。 | -| 7 * (XLEN / 8) | X7 | 在 GPR X7 中要恢复的值。 | -| 8 * (XLEN / 8) | X8 | 在 GPR X8 中要恢复的值。 | -| 9 * (XLEN / 8) | X9 | 在 GPR X9 中要恢复的值。 | -| 10 * (XLEN / 8) | X10 | 在 GPR X10 中要恢复的值。 | -| 11 * (XLEN / 8) | X11 | 在 GPR X11 中要恢复的值。 | -| 12 * (XLEN / 8) | X12 | 在 GPR X12 中要恢复的值。 | -| 13 * (XLEN / 8) | X13 | 在 GPR X13 中要恢复的值。 | -| 14 * (XLEN / 8) | X14 | 在 GPR X14 中要恢复的值。 | -| 15 * (XLEN / 8) | X15 | 在 GPR X15 中要恢复的值。 | -| 16 * (XLEN / 8) | X16 | 在 GPR X16 中要恢复的值。 | -| 17 * (XLEN / 8) | X17 | 在 GPR X17 中要恢复的值。 | -| 18 * (XLEN / 8) | X18 | 在 GPR X18 中要恢复的值。 | -| 19 * (XLEN / 8) | X19 | 在 GPR X19 中要恢复的值。 | -| 20 * (XLEN / 8) | X20 | 在 GPR X20 中要恢复的值。 | -| 21 * (XLEN / 8) | X21 | 在 GPR X21 中要恢复的值。 | -| 22 * (XLEN / 8) | X22 | 在 GPR X22 中要恢复的值。 | -| 23 * (XLEN / 8) | X23 | 在 GPR X23 中要恢复的值。 | -| 24 * (XLEN / 8) | X24 | 在 GPR X24 中要恢复的值。 | -| 25 * (XLEN / 8) | X25 | 在 GPR X25 中要恢复的值。 | -| 26 * (XLEN / 8) | X26 | 在 GPR X26 中要恢复的值。 | -| 27 * (XLEN / 8) | X27 | 在 GPR X27 中要恢复的值。 | -| 28 * (XLEN / 8) | X28 | 在 GPR X28 中要恢复的值。 | -| 29 * (XLEN / 8) | X29 | 在 GPR X29 中要恢复的值。 | -| 30 * (XLEN / 8) | X30 | 在 GPR X10 中要恢复的值。 | -| 31 * (XLEN / 8) | X31 | 在 GPR X31 中要恢复的值。 | -| 32 * (XLEN / 8) | X32 | 在 GPR X32 中要恢复的值。 | +| 1 * (XLEN / 8) | X1 | 在 GPR X1 中要恢复的值。 | +| 2 * (XLEN / 8) | X2 | 在 GPR X2 中要恢复的值。 | +| 3 * (XLEN / 8) | X3 | 在 GPR X3 中要恢复的值。 | +| 4 * (XLEN / 8) | X4 | 在 GPR X4 中要恢复的值。 | +| 5 * (XLEN / 8) | X5 | 在 GPR X5 中要恢复的值。 | +| 6 * (XLEN / 8) | X6 | 在 GPR X6 中要恢复的值。 | +| 7 * (XLEN / 8) | X7 | 在 GPR X7 中要恢复的值。 | +| 8 * (XLEN / 8) | X8 | 在 GPR X8 中要恢复的值。 | +| 9 * (XLEN / 8) | X9 | 在 GPR X9 中要恢复的值。 | +| 10 * (XLEN / 8) | X10 | 在 GPR X10 中要恢复的值。 | +| 11 * (XLEN / 8) | X11 | 在 GPR X11 中要恢复的值。 | +| 12 * (XLEN / 8) | X12 | 在 GPR X12 中要恢复的值。 | +| 13 * (XLEN / 8) | X13 | 在 GPR X13 中要恢复的值。 | +| 14 * (XLEN / 8) | X14 | 在 GPR X14 中要恢复的值。 | +| 15 * (XLEN / 8) | X15 | 在 GPR X15 中要恢复的值。 | +| 16 * (XLEN / 8) | X16 | 在 GPR X16 中要恢复的值。 | +| 17 * (XLEN / 8) | X17 | 在 GPR X17 中要恢复的值。 | +| 18 * (XLEN / 8) | X18 | 在 GPR X18 中要恢复的值。 | +| 19 * (XLEN / 8) | X19 | 在 GPR X19 中要恢复的值。 | +| 20 * (XLEN / 8) | X20 | 在 GPR X20 中要恢复的值。 | +| 21 * (XLEN / 8) | X21 | 在 GPR X21 中要恢复的值。 | +| 22 * (XLEN / 8) | X22 | 在 GPR X22 中要恢复的值。 | +| 23 * (XLEN / 8) | X23 | 在 GPR X23 中要恢复的值。 | +| 24 * (XLEN / 8) | X24 | 在 GPR X24 中要恢复的值。 | +| 25 * (XLEN / 8) | X25 | 在 GPR X25 中要恢复的值。 | +| 26 * (XLEN / 8) | X26 | 在 GPR X26 中要恢复的值。 | +| 27 * (XLEN / 8) | X27 | 在 GPR X27 中要恢复的值。 | +| 28 * (XLEN / 8) | X28 | 在 GPR X28 中要恢复的值。 | +| 29 * (XLEN / 8) | X29 | 在 GPR X29 中要恢复的值。 | +| 30 * (XLEN / 8) | X30 | 在 GPR X10 中要恢复的值。 | +| 31 * (XLEN / 8) | X31 | 在 GPR X31 中要恢复的值。 | +| 32 * (XLEN / 8) | X32 | 在 GPR X32 中要恢复的值。 | 在发送同步 SRET 请求给 SBI 实现(或 L0 虚拟化监控程序)之前,监督者软件(或 L1 虚拟化监控程序)必须将要在嵌套 SRET 上下文的偏移量 `` * (XLEN / 8) 处恢复的 GPR X `` 值写入。 @@ -1630,10 +1631,10 @@ SBI 嵌套加速扩展定义了 SBI 实现(或 L0 虚拟化监控程序)与 *表 67. 嵌套的自动交换上下文* -| 偏移量 | 名称 | 编码 | -| :-------------------- | :------------- | :------------------------------------------------------------------------------------- | +| 偏移量 | 名称 | 编码 | +|:----------------------|:---------------|:------------------------------------------------------------------------------------| | 0 * (XLEN / 8) | Autoswap_Flags | 动交换标志位
BIT[XLEN-1:1] - 保留用于将来使用,必须为零
BIT[0:0] - HSTATUS | -| 1 * (XLEN / 8) | HSTATUS | 要与 HSTATUS CSR 交换的值 | +| 1 * (XLEN / 8) | HSTATUS | 要与 HSTATUS CSR 交换的值 | | 2 * (XLEN / 8) - 0x7F | Reserved | 保留以供将来使用。 | 要启用从嵌套的自动交换上下文中自动交换 CSRs,监督者软件(或 L1 虚拟化监控程序)必须执行以下操作: @@ -1673,10 +1674,10 @@ flags 参数保留供将来使用,必须为零。 *表 68. NACL 设置共享内存错误* -| 错误代码 | 描述 | -| :---------------------- | :-------------------------------------------------------------------------------- | +| 错误代码 | 描述 | +|:------------------------|:-------------------------------------------------------------------------------| | SBI_SUCCESS | 共享内存被成功地设置或清除。 | -| SBI_ERR_INVALID_PARAM | flags 参数不为零,或者 shmem_phys_lo 参数不是 4096 字节对齐的。 | +| SBI_ERR_INVALID_PARAM | flags 参数不为零,或者 shmem_phys_lo 参数不是 4096 字节对齐的。 | | SBI_ERR_INVALID_ADDRESS | 由 shmem_phys_lo 和 shmem_phys_hi 参数指定的共享内存不符合第 3.2 节中描述的要求。 | ### 15.7. 函数:同步共享内存 CSR (FID #2) @@ -1695,12 +1696,12 @@ struct sbiret sbi_nacl_sync_csr(unsigned long csr_num) *表 69. NACL 同步 CSR 错误* -| 错误代码 | 描述 | -| :-------------------- | :------------------------------------------------------------------------------------------------------------------------------------ | -| SBI_SUCCESS | CSRs 同步成功。 | -| SBI_ERR_NOT_SUPPORTED | SBI_NACL_FEAT_SYNC_CSR 特性不可用。 | +| 错误代码 | 描述 | +|:----------------------|:---------------------------------------------------------------------------------------------------------------------------------| +| SBI_SUCCESS | CSRs 同步成功。 | +| SBI_ERR_NOT_SUPPORTED | SBI_NACL_FEAT_SYNC_CSR 特性不可用。 | | SBI_ERR_INVALID_PARAM | csr_num 不是全部的位数,并且要么:
* (csr_num & 0x300) != 0x200 或
* csr_num >= 0x1000 或
* csr_num 未被 SBI 实现。 | -| SBI_ERR_NO_SHMEM | 嵌套加速共享内存不可用。 | +| SBI_ERR_NO_SHMEM | 嵌套加速共享内存不可用。 | ### 15.8. 函数:同步共享内存 HFENCEs (FID #3) @@ -1718,12 +1719,12 @@ struct sbiret sbi_nacl_sync_hfence(unsigned long entry_index) *表 70. NACL 同步 HFENCE 错误* -| 错误代码 | 描述 | -| :-------------------- | :--------------------------------------------------------------- | -| SBI_SUCCESS | HFENCEs 同步成功 | -| SBI_ERR_NOT_SUPPORTED | SBI_NACL_FEAT_SYNC_HFENCE 特性不可用 | +| 错误代码 | 描述 | +|:----------------------|:-------------------------------------------------------------| +| SBI_SUCCESS | HFENCEs 同步成功 | +| SBI_ERR_NOT_SUPPORTED | SBI_NACL_FEAT_SYNC_HFENCE 特性不可用 | | SBI_ERR_INVALID_PARAM | entry_index 不是全 1 位比特,并且 entry_index >= (3840 / XLEN)。 | -| SBI_ERR_NO_SHMEM | 嵌套加速共享内存不可用。 | +| SBI_ERR_NO_SHMEM | 嵌套加速共享内存不可用。 | ### 15.9. 函数:同步共享内存并模拟 SRET 指令 (FID #4) @@ -1740,16 +1741,16 @@ struct sbiret sbi_nacl_sync_sret(void) *表 71. NACL 同步 SRET 错误* | 错误代码 | 描述 | -| :-------------------- | :--------------------------------- | +|:----------------------|:---------------------------------| | SBI_ERR_NOT_SUPPORTED | SBI_NACL_FEAT_SYNC_SRET 特性不可用 | -| SBI_ERR_NO_SHMEM | 嵌套加速共享内存不可用。 | +| SBI_ERR_NO_SHMEM | 嵌套加速共享内存不可用。 | ### 15.10. 函数列表 *表 72. NACL 函数列表* | 函数名 | SBI 版本 | FID | EID | -| :--------------------- | :------- | :-- | :--------- | +|:-----------------------|:---------|:----|:-----------| | sbi_nacl_probe_feature | 2.0 | 0 | 0x4E41434C | | sbi_nacl_set_shmem | 2.0 | 1 | 0x4E41434C | | sbi_nacl_sync_csr | 2.0 | 2 | 0x4E41434C | @@ -1784,31 +1785,31 @@ SBI 实现必须在系统复位时停止对共享内存的写入。 *表 73. STA 共享内存结构* -| 名称 | 偏移量 | 大小 | 描述 | -| :-------- | :----- | :--- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 名称 | 偏移量 | 大小 | 描述 | +|:----------|:-------|:-----|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | sequence | 0 | 4 | SBI 实现必须在写入窃取字段之前将该字段递增为奇数值,并在写入窃取后再次递增为偶数值(即,奇数序列号表示正在进行的更新)。SBI 实现应确保序列字段只在很短的时间内保持奇数。主管模式软件必须在读取窃取字段之前和之后检查这个字段,如果它是不同的或奇数的,则重复读取。这个序列字段使在 32 位环境下执行的监督者模式软件能够读取窃取字段的值。 | -| flags | 4 | 4 | 始终为零。未来对 SBI 调用的扩展可能允许监督者模式的软件写到共享内存的一些字段。只要 SBI 调用的 flags 参数使用零值,这种扩展就不会被启用。 | -| steal | 8 | 8 | 该虚拟 HART 没有空闲和安排外出的时间,单位是纳秒。虚拟 HART 空闲的时间将不被报告为偷窃时间。 | -| preempted | 16 | 1 | 一个指示标志,指示注册了此结构的虚拟 HART 是否正在运行或停止。如果虚拟 HART 被抢占(即窃取字段在增加),SBI 实现可能会写入非零值,而在虚拟 HART 重新开始运行之前,必须写入零值。例如,监督模式软件可以使用这个标志来检查锁的持有者是否已被抢占,并在这种情况下禁用 optimistic spinning。 | -| pad | 17 | 47 | 用零填充到 64 字节的边界。 | +| flags | 4 | 4 | 始终为零。未来对 SBI 调用的扩展可能允许监督者模式的软件写到共享内存的一些字段。只要 SBI 调用的 flags 参数使用零值,这种扩展就不会被启用。 | +| steal | 8 | 8 | 该虚拟 HART 没有空闲和安排外出的时间,单位是纳秒。虚拟 HART 空闲的时间将不被报告为偷窃时间。 | +| preempted | 16 | 1 | 一个指示标志,指示注册了此结构的虚拟 HART 是否正在运行或停止。如果虚拟 HART 被抢占(即窃取字段在增加),SBI 实现可能会写入非零值,而在虚拟 HART 重新开始运行之前,必须写入零值。例如,监督模式软件可以使用这个标志来检查锁的持有者是否已被抢占,并在这种情况下禁用 optimistic spinning。 | +| pad | 17 | 47 | 用零填充到 64 字节的边界。 | sbiret.value 被设置为 0,在 sbiret.error 中可能返回的错误代码如下表 74 所示。 *表 74. STA 设置窃取时间共享内存地址错误* -| 错误代码 | 描述 | -| :---------------------- | :------------------------------------------------------------------------------------------- | -| SBI_SUCCESS | 偷取时间共享内存物理基址被成功设置或清除。 | -| SBI_ERR_INVALID_PARAM | flags 参数不为零或 shmem_phys_lo 不是 64 字节对齐的。 | +| 错误代码 | 描述 | +|:------------------------|:-----------------------------------------------------------------------------------------| +| SBI_SUCCESS | 偷取时间共享内存物理基址被成功设置或清除。 | +| SBI_ERR_INVALID_PARAM | flags 参数不为零或 shmem_phys_lo 不是 64 字节对齐的。 | | SBI_ERR_INVALID_ADDRESS | shmem_phys_lo 和 shmem_phys_hi 参数所指向的共享内存是不可写的,或者不满足 3.2 节的其他要求。 | -| SBI_ERR_FAILED | 该请求因未指明的或未知的其他原因而失败。 | +| SBI_ERR_FAILED | 该请求因未指明的或未知的其他原因而失败。 | ### 16.2. 函数列表 *表 75. STA 函数列表* | 函数名 | SBI 版本 | FID | EID | -| :----------------------- | :------- | :-- | :------- | +|:-------------------------|:---------|:----|:---------| | sbi_steal_time_set_shmem | 2.0 | 0 | 0x535441 | ## 章节 17. 实验性 SBI 扩展空间 (EIDs #0x08000000 - #0x08FFFFFF) -- Gitee From f4aba26bccea38c851ba6e2f252b66e55a9de542 Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Sat, 29 Jul 2023 08:36:36 +0000 Subject: [PATCH 05/68] =?UTF-8?q?=E6=8C=89=E7=85=A7=E8=92=99=E8=80=81?= =?UTF-8?q?=E5=B8=88=E7=9A=84=E4=BF=AE=E6=94=B9=E6=84=8F=E8=A7=81=E8=BF=9B?= =?UTF-8?q?=E8=A1=8C=E4=BA=86=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../20230710-sbi-specification-translation.md | 867 +++++++++--------- 1 file changed, 450 insertions(+), 417 deletions(-) diff --git a/articles/20230710-sbi-specification-translation.md b/articles/20230710-sbi-specification-translation.md index 6f574b9..e470892 100644 --- a/articles/20230710-sbi-specification-translation.md +++ b/articles/20230710-sbi-specification-translation.md @@ -1,22 +1,24 @@ -> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [spaces header tables]
-> Title: [RISC-V SBI specification](https://github.com/riscv-non-isa/riscv-sbi-doc/releases/download/v2.0-rc1/riscv-sbi.pdf)
-> Author: riscv.org
-> Translator: 刘澳 <1219671600@qq.com>
-> Date: 2022/07/10
-> Revisor: Falcon
-> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux)
-> Acknowledgements: 山东大学 山东大学智能创新研究院 [RISC-V SBI-1.0.0 版本 中文](https://zhuanlan.zhihu.com/p/634337322)
+> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [spaces header tables]`
` +> Title: [RISC-V SBI specification](https://github.com/riscv-non-isa/riscv-sbi-doc/releases/download/v2.0-rc1/riscv-sbi.pdf) `
` +> Author: riscv.org `
` +> Translator: 刘澳 <1219671600@qq.com>`
` +> Date: 2022/07/10 `
` +> Revisor: Falcon `
` +> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux) `
` +> Acknowledgements: 山东大学 山东大学智能创新研究院 [RISC-V SBI-1.0.0 版本 中文](https://zhuanlan.zhihu.com/p/634337322) `
` > Sponsor: PLCT Lab, ISCAS # RISC-V SBI 翻译 ## 简介 -本次翻译工作的背景为我正在调研 RISC-V SBI 相关知识,发现 SBI Spec 正在快速更新,目前已经开始更新 2.0 版本。于此同时,我发现国内已经有了 SBI Spec 的翻译工作,不过可能因为某些原因,并没有机构对 SBI Spec 2.0-rc1 进行翻译。我认为这对于我来说是一个很好的了解 SBI Spec 的机会,对于国内想要学学习 RISC-V SBI 的研究人员可能也会有些帮助,所以就开始了 SBI Spec 2.0-rc1 的翻译工作。 +本次翻译工作的背景为我正在调研 RISC-V SBI 相关知识,发现 SBI Spec 正在快速更新,目前已经开始更新 2.0 版本。于此同时,我发现国内已经有了 SBI Spec 的翻译工作,不过可能因为某些原因,并没有机构对 SBI Spec 2.0-rc1 进行翻译。我认为这对于我来说是一个很好的了解 SBI Spec 的机会,对于国内想要学习 RISC-V SBI 的研究人员可能也会有些帮助,所以就开始了 SBI Spec 2.0-rc1 的翻译工作。 + +在这里非常感谢山东大学以及山东大学智能创新研究院,他们之前翻译了 SBI Spec 1.0 版本,我的这次翻译是在他们的基础上完成的。在与原翻译人员通过邮件取得沟通后,他们非常慷慨地同意了我使用他们的翻译成果做为基础来进行我的工作。他们的翻译成果发表在 [RISC-V SBI-1.0.0 版本 中文](https://zhuanlan.zhihu.com/p/634337322)。 在这里非常感谢山东大学以及山东大学智能创新研究院,他们之前翻译了 SBI Spec 1.0 版本,我的工作是在他们的基础上完成的。在与原翻译人员通过邮件取得沟通后,他们非常慷慨的同意了我使用他们的工作做为基础来进行我的工作。 -本次翻译的相较于前一版本,主要新增了 SBI Spec 2.0-rc1 版本的新增内容(在更改日志中提及),以及对原翻译的格式进行了一些调整,对一些术语进行了统一。 +本次翻译相较于前一版本,主要对 SBI Spec 2.0-rc1 版本的新增内容(见“更改日志”一节)进行了翻译,以及对原翻译的格式进行了一些调整,对一些术语进行了统一。 ## 更改日志 @@ -25,7 +27,7 @@ - 添加了共享内存物理地址范围参数的通用描述 - 添加了 SBI 调试控制台扩展 - 放宽了 SBI PMU 固件计数器的计数器宽度要求 -- 在 SBI PMU 扩展中添加了 sbi_pmu_counter_fw_read_hi() +- 在 SBI PMU 扩展中添加了 `sbi_pmu_counter_fw_read_hi()` - 为 SBI 实现特定固件事件保留空间 - 添加了 SBI 系统挂起扩展 - 添加了 SBI CPPC 扩展 @@ -80,7 +82,7 @@ ## 章节 1. 介绍 -本规范描述了 RISC-V Supervisor Binary Interface,简称为 SBI。SBI 允许在所有 RISC-V 实现上,通过定义平台(或虚拟化管理程序)特定功能的抽象,使监管者模式(S 模式或 VS 模式)的软件具备可移植性。SBI 的设计遵循 RISC-V 的一般原则,即核心部分小而精简,同时具备一组可选的模块化扩展功能。 +本规范描述了 RISC-V Supervisor Binary Interface,简称为 SBI。SBI 允许在所有 RISC-V 实现上,通过定义平台(或虚拟化管理程序)特定功能的抽象,使监督者模式(S 模式或 VS 模式)的软件具备可移植性。SBI 的设计遵循 RISC-V 的一般原则,即核心部分小而精简,同时具备一组可选的模块化扩展功能。 SBI 扩展作为整体是可选的,但不允许部分实现。如果 sbi_probe_extension() 表明某个扩展可用,那么 sbi_get_spec_version() 报告的 SBI 版本中的所有函数必须符合该版本的 SBI 规范。 提供给监管模式软件的更高特权级软件称为 SBI 实现或 Supervisor Execution Environment(SEE)。SBI 实现(或 SEE)可以是在机器模式(M-mode)下执行的平台运行时固件(参见下图 1),也可以是在超级监管模式(HS-mode)下执行的某个虚拟化监管程序(参见下图 2)。 @@ -99,7 +101,7 @@ SBI 规范不指定硬件发现的任何方法。监督者软件必须依赖其 本规范使用了下列术语及缩写: | 术语 | 含义 | -|:-----|---------------------------------------------------------------| +| :--- | --------------------------------------------------------------- | | ACPI | 高级配置和电源接口 (Advanced Configuration and Power Interface) | | ASID | 地址空间标识符 (Address Space Identifier) | | BMC | 底板管理控制器 (Baseboard Management Controller) | @@ -117,7 +119,7 @@ SBI 规范不指定硬件发现的任何方法。监督者软件必须依赖其 所有的 SBI 函数共享一种二进制编码方式,这有助于混合使用 SBI 扩展功能。SBI 规范遵循以下调用约定。 -- 在监管者和 SEE 之间,使用 ECALL 作为控制传输指令。 +- 在监督者和 SEE 之间,使用 ECALL 作为控制传输指令。 - a7 编码 SBI 扩展 ID(EID) - a6 编码 SBI 函数 ID(FID),对于任何在 a7 中编码的 SBI 扩展,其定义在 SBI v0.2 之后。 - 在 SBI 调用期间,除了 a0 和 a1 寄存器外,所有寄存器都必须由被调用方保留。 @@ -125,27 +127,28 @@ SBI 规范不指定硬件发现的任何方法。监督者软件必须依赖其 ``` struct sbiret - { - long error; - long value; +{ + long error; + long value; }; ``` 为了保持兼容性,SBI 扩展 ID(EID)和 SBI 函数 ID(FID)被编码为有符号的 32 位整数。当以寄存器形式传递时,遵循上述标准的调用约定规则。 表 1 列出了标准 SBI 错误代码。 + _表 1.标准 SBI 错误_ | 错误类型 | 值 | 描述 | -|:--------------------------|----|:-------------| +| :------------------------ | -- | :------------- | | SBI_SUCCESS | 0 | 顺利完成 | | SBI_ERR_FAILED | -1 | 失败 | | SBI_ERR_NOT_SUPPORTED | -2 | 不支持操作 | | SBI_ERR_INVALID_PARAM | -3 | 非法参数 | | SBI_ERR_DENIED | -4 | 拒绝 | | SBI_ERR_INVALID_ADDRESS | -5 | 非法地址 | -| SBI_ERR_ALREADY_AVAILABLE | -6 | (资源)已可用 | -| SBI_ERR_ALREADY_STARTED | -7 | (操作)已启动 | -| SBI_ERR_ALREADY_STOPPED | -8 | (操作)已停止 | +| SBI_ERR_ALREADY_AVAILABLE | -6 | (资源)已可用 | +| SBI_ERR_ALREADY_STARTED | -7 | (操作)已启动 | +| SBI_ERR_ALREADY_STOPPED | -8 | (操作)已停止 | | SBI_ERR_NO_SHMEM | -9 | 共享内存不可用 | 不支持的 SBI 扩展 ID(EID)或 SBI 函数 ID(FID)的 ECALL 必须返回错误代码 SBI_ERR_NOT_SUPPORTED。 @@ -161,10 +164,11 @@ _表 1.标准 SBI 错误_ 在单个 SBI 函数调用中,可以设置的最大 hart 数量始终为 XLEN。如果较低特权模式需要传递超过 XLEN 个 hart 的信息,它应该调用多个 SBI 函数调用的实例。hart_mask_base 可以设置为-1,表示可以忽略 hart_mask 并考虑所有可用的 harts。 使用 hart 掩码的任何函数可能返回表 2 中列出的错误值,这些错误值是针对特定函数的错误值的补充。 + _表 2. HART 掩码错误_ -| 错误代码 | 描述 | -|:----------------------|:---------------------------------------------------------------------------------------------| +| 错误代码 | 描述 | +| :-------------------- | :----------------------------------------------------------------------------------------------- | | SBI_ERR_INVALID_PARAM | hart_mask_base 或 hart_mask 中任何一个 hartid 无效,即该 hartid 未被平台启用或对监管程序不可用。 | ### 3.2 共享内存物理地址范围参数 @@ -178,21 +182,21 @@ _表 2. HART 掩码错误_ - 在共享内存中的数据必须遵循小端字节顺序。 -建议将传递给 SBI 函数的内存物理地址至少使用两个无符号长参数,以支持具有大于 XLEN 位的内存物理地址的平台。 +建议将传递给 SBI 函数的内存物理地址至少无符号长整形参数,以支持具有大于 XLEN 位的内存物理地址的平台。 ## 章节 4. 基本扩展 (EID #0x10) 基本扩展旨在尽可能简洁。因此,它仅包含用于探测可用的 SBI 扩展以及查询 SBI 版本的功能。基本扩展中的所有函数必须由所有 SBI 实现支持,因此没有定义错误返回值。 -### 4.1.函数:获取 SBI 规范版本 (FID #0) +### 4.1 函数:获取 SBI 规范版本 (FID #0) ``` struct sbiret sbi_get_spec_version(void); ``` -返回当前的 SBI 规范版本。此函数必定成功。SBI 规范的次版本号编码在低 24 位中,主版本号编码在接下来的 7 位中。第 31 位必须为 0,保留用于未来扩展 RISC-V-SBI-1.0.0-Chinese +返回当前的 SBI 规范版本。此函数必定成功。SBI 规范的次版本号编码在低 24 位中,主版本号编码在接下来的 7 位中。第 31 位必须为 0,保留用于未来扩展。 -### 4.2.函数:获取 SBI 实现标识符 (FID #1) +### 4.2 函数:获取 SBI 实现标识符 (FID #1) ``` struct sbiret sbi_get_impl_id(void); @@ -200,7 +204,7 @@ struct sbiret sbi_get_impl_id(void); 返回当前 SBI 实现的标识符,每个 SBI 实现的标识符都是不同的。这个标识符旨在让软件探测 SBI 实现的特殊问题或特点。 -### 4.3.函数:获取 SBI 实现版本 (FID #2) +### 4.3 函数:获取 SBI 实现版本 (FID #2) ``` struct sbiret sbi_get_impl_version(void); @@ -208,15 +212,15 @@ struct sbiret sbi_get_impl_version(void); 返回当前 SBI 实现的版本。该版本号的编码是特定于 SBI 实现的。 -### 4.4.函数:探测 SBI 扩展功能 (FID #3) +### 4.4 函数:探测 SBI 扩展功能 (FID #3) ``` struct sbiret sbi_probe_extension(long extension_id); ``` -如果给定的 SBI 扩展 ID (EID) 不可用,则返回 0;如果可用,且实现定义为其他非零值则返回 1。 +如果给定的 SBI 扩展 ID (EID) 不可用,则返回 0;如果可用,返回值应为 1,或为特定 SBI 实现定义的其他非 0 值。 -### 4.5.函数:获取机器供应商标识符 (FID #4) +### 4.5 函数:获取机器供应商标识符 (FID #4) ``` struct sbiret sbi_get_mvendorid(void); @@ -224,7 +228,7 @@ struct sbiret sbi_get_mvendorid(void); 返回一个合法的 mvendorid CSR 值,其中 0 总是一个合法的值。mvendorid CSR 是一个用于标识机器供应商的控制状态寄存器,它用于表示底层硬件的供应商或制造商。 -### 4.6.函数:获取机器体系结构标识符 (FID #5) +### 4.6 函数:获取机器体系结构标识符 (FID #5) ``` struct sbiret sbi_get_marchid(void); @@ -232,7 +236,7 @@ struct sbiret sbi_get_marchid(void); 返回一个在 marchid CSR 中合法的值,其中 0 总是合法的值。marchid CSR 是 RISC-V 架构中的一个控制状态寄存器,用于标识机器体系结构。 -### 4.7.函数:获取机器实现标识符 ID (FID #6) +### 4.7 函数:获取机器实现标识符 ID (FID #6) ``` struct sbiret sbi_get_mimpid(void); @@ -245,7 +249,7 @@ struct sbiret sbi_get_mimpid(void); _表 3.基础函数列表_ | 函数名 | SBI 版本 | FID | EID | -|:-------------------------|----------|-----|------| +| :----------------------- | -------- | --- | ---- | | sbi_get_sbi_spec_version | 0.2 | 0 | 0x10 | | sbi_get_sbi_impl_id | 0.2 | 1 | 0x10 | | sbi_get_sbi_impl_version | 0.2 | 2 | 0x10 | @@ -254,12 +258,12 @@ _表 3.基础函数列表_ | sbi_get_marchid | 0.2 | 5 | 0x10 | | sbi_get_mimpid | 0.2 | 6 | 0x10 | -### 4.9.SBI 实现标识符 +### 4.9 SBI 实现标识符 _表 4. SBI 实现 IDs_ | SBI 实现 ID | 名称 | -|:------------|----------------------------| +| :---------- | -------------------------- | | 0 | Berkeley Boot Loader (BBL) | | 1 | OpenSBI | | 2 | Xvisor | @@ -277,11 +281,12 @@ _表 4. SBI 实现 IDs_ - a1 寄存器中不返回任何值。 - 在 SBI 调用期间,除 a0 寄存器外的所有寄存器都必须由被调用者保留。 - a0 寄存器中返回的值是特定于 SBI 传统扩展的。 -- SBI 实现在监管者访问内存时发生的页面和访问故障会被重定向回监管者,并且 sepc 寄存器指向故障的 ECALL 指令。 + +SBI 实现在监督者访问内存时发生的页面和访问故障会被重定向回监督者,并且 sepc 寄存器指向故障的 ECALL 指令。 传统 SBI 扩展已被以下扩展所取代。传统的控制台 SBI 函数(sbi_console_getchar() 和 sbi_console_putchar())预计将被弃用;它们没有替代方案。 -### 5.1.扩展:设置时钟 (EID #0x00) +### 5.1 扩展:设置时钟 (EID #0x00) ``` long sbi_set_timer(uint64_t stime_value) @@ -291,17 +296,17 @@ long sbi_set_timer(uint64_t stime_value) 如果监管程序希望清除计时器中断但不安排下一个计时器事件,则可以将计时器中断请求设置为无限远(即(uint64_t)-1),或者通过清除 sie.STIE 寄存器位来屏蔽计时器中断。 此 SBI 调用在成功时返回 0,否则返回实现特定的负错误代码。 -### 5.2.扩展:控制台字符输出 (EID #0x01) +### 5.2 扩展:控制台字符输出 (EID #0x01) ``` long sbi_console_putchar(int ch) ``` -将**ch**中的数据写入控制台。 +将 **ch** 中的数据写入控制台。 与 sbi_console_getchar() 不同的是,如果仍有任何待传输的字符或接收终端还没有准备好接收字节,则此 SBI 调用将阻塞。但是,如果控制台根本不存在,则字符将被丢弃。 此 SBI 调用在成功时返回 0,否则返回实现特定的负错误代码。 -### 5.3.扩展:控制台字符输入 (EID #0x02) +### 5.3 扩展:控制台字符输入 (EID #0x02) ``` long sbi_console_getchar(void) @@ -310,7 +315,7 @@ long sbi_console_getchar(void) 从调试控制台中读一个字符。 此 SBI 调用在成功时返回 0,否则返回实现特定的负错误代码。 -### 5.4.扩展:清除 IPI (EID #0x03) +### 5.4 扩展:清除 IPI (EID #0x03) ``` long sbi_clear_ipi(void) @@ -318,19 +323,19 @@ long sbi_clear_ipi(void) 清除任何挂起的 IPI(处理器核间中断)。这个 SBI 调用只会清除被调用的 hart(硬件线程),其他的 hart 不受影响。 sbi_clear_ipi() 已经被弃用,因为 S 模式代码可以直接清除 sip.SSIP 寄存器位。. -这个 SBI 调用会返回 0,如果没有 IPI 被挂起,则返回一个实现特定的正值,如果有 IPI 被挂起。 +如果没有 IPI 被挂起,这个 SBI 调用返回 0;如果有 IPI 被挂起,则返回一个实现特定的正值。 -### 5.5.扩展:发送 IPI (EID #0x04) +### 5.5 扩展:发送 IPI (EID #0x04) ``` long sbi_send_ipi(const unsigned long *hart_mask) ``` -向 hart_mask 指定的所有 hart 发送跨处理器中断。跨处理器中断在接收的 hart 上表现为 Supervisor 模式软件中断。 -hart_mask 是指向 hart 位向量的虚拟地址。该位向量表示为 unsigned long 无符号长整型序列,其长度等于系统中 hart 的数量除以 unsigned long 中的位数,向上取整到下一个整数。 +向 hart_mask 指定的所有 hart 发送跨处理器中断。跨处理器中断在接收的 hart 上表现为监督者模式软件中断。 +hart_mask 是指向 hart 位向量的虚拟地址。该位向量表示为无符号长整型序列,其长度等于系统中 hart 的数量除以无符号长整型中的位数,向上取整到下一个整数。 此 SBI 调用在成功时返回 0,或返回一个实现特定的负错误代码。 -### 5.6.扩展:远程 FENCE.I (EID #0x05) +### 5.6 扩展:远程 FENCE.I (EID #0x05) ``` long sbi_remote_fence_i(const unsigned long *hart_mask) @@ -339,7 +344,7 @@ long sbi_remote_fence_i(const unsigned long *hart_mask) 该函数用于指示远程处理器执行 FENCE.I 指令。 此 SBI 调用在成功时返回 0,或返回一个实现特定的负错误代码。 -### 5.7.扩展:远程 SFENCE.VMA (EID #0x06) +### 5.7 扩展:远程 SFENCE.VMA (EID #0x06) ``` long sbi_remote_sfence_vma(const unsigned long *hart_mask,unsigned long start, unsigned long size) @@ -348,7 +353,7 @@ long sbi_remote_sfence_vma(const unsigned long *hart_mask,unsigned long start, u 指示远程 harts 执行一个或多个 SFENCE.VMA 指令,涵盖从 start 到 size 的虚拟地址范围。其中,hart_mask 参数与 sbi_send_ipi() 函数中描述的一样。 此 SBI 调用在成功时返回 0,或返回一个实现特定的负错误代码。 -### 5.8.扩展:远程 SFENCE.VMA(指定地址空间标识符)(EID #0x07) +### 5.8 扩展:远程 SFENCE.VMA(指定地址空间标识符)(EID #0x07) ``` long sbi_remote_sfence_vma_asid(const unsigned long *hart_mask,unsigned long start, unsigned long size, unsigned long asid) @@ -357,21 +362,21 @@ long sbi_remote_sfence_vma_asid(const unsigned long *hart_mask,unsigned long sta 指示远程 hart 执行一个或多个 SFENCE.VMA 指令,覆盖 start 和 size 之间的虚拟地址范围。此操作仅涵盖给定的 ASID。 此 SBI 调用在成功时返回 0,或返回一个实现特定的负错误代码。 -### 5.9.扩展:系统关闭 (EID #0x08) +### 5.9 扩展:系统关闭 (EID #0x08) ``` void sbi_shutdown(void) ``` -将所有 hart 从监管者模式的角度置于关闭状态。 +将所有 hart 从监督者模式的角度置于关闭状态。 此 SBI 调用在成功时返回 0,或返回一个实现特定的负错误代码。 -### 5.10.函数列表 +### 5.10 函数列表 _表 5. 旧版函数列表_ | 函数名 | SBI 版本 | FID | EID | 替代 EID | -|:---------------------------|----------|-----|-----------|------------| +| :------------------------- | -------- | --- | --------- | ---------- | | sbi_set_timer | 0.1 | 0 | 0x00 | 0x54494D45 | | sbi_console_putchar | 0.1 | 0 | 0x01 | N/A | | sbi_console_getchar | 0.1 | 0 | 0x02 | N/A | @@ -387,47 +392,48 @@ _表 5. 旧版函数列表_ 这个替代扩展(EID #0x00)替代了传统的计时器扩展。它遵循在 v0.2 中定义的新的调用约定。 -### 6.1.函数:时钟设定 (FID #0) +### 6.1 函数:时钟设定 (FID #0) ``` struct sbiret sbi_set_timer(uint64_t stime_value) ``` 在 stime_value 时间之后,为下一个事件设置时钟。stime_value 以绝对时间表示。此函数还必须清除挂起的计时器中断位。 -如果监管者希望在不安排下一个计时器事件的情况下清除计时器中断,可以请求一个无限远的计时器中断(即 (uint64_t)-1),或者通过清除 sie.STIE 寄存器位来屏蔽计时器中断。 +如果监督者希望在不安排下一个计时器事件的情况下清除计时器中断,可以请求一个无限远的计时器中断(即 (uint64_t)-1),或者通过清除 sie.STIE 寄存器位来屏蔽计时器中断。 -### 6.2.函数列表 +### 6.2 函数列表 _表 6. 时钟函数列表_ | 函数名 | SBI 版本 | FID | EID | -|:--------------|----------|-----|------------| +| :------------ | -------- | --- | ---------- | | sbi_set_timer | 0.2 | 0 | 0x54494D45 | ## 章节 7. IPI 扩展 (EID #0x735049 "sPI: s-mode IPI") 该扩展替代了传统的扩展 (EID #0x04)。其他与 IPI 相关的传统扩展(0x3)现已不推荐使用。该扩展中的所有函数都遵循二进制编码部分中定义的 hart_mask. -### 7.1.函数:发送 IPI (FID #0) +### 7.1 函数:发送 IPI (FID #0) ``` struct sbiret sbi_send_ipi(unsigned long hart_mask,unsigned long hart_mask_base) ``` -向 hart_mask 中定义的所有 hart 发送跨处理器中断。接收 hart 上的处理器间中断将在其上表现为超级处理器软件中断。 +向 hart_mask 中定义的所有 hart 发送跨处理器中断。接收 hart 上的处理器间中断将在其上表现为监督者软件中断。 sbiret.error 返回的可能错误代码如表 7 所示。 + _表 7. IPI 发送错误_ -| 错误代码 | 描述 | -|:---------------|-------------------------------| -| SBI_SUCCESS 成功 | IPI 被成功发送至所有目标 harts。 | +| 错误代码 | 描述 | +| :--------------- | ------------------------------ | +| SBI_SUCCESS 成功 | IPI 被成功发送至所有目标 harts | -### 7.2.函数列表 +### 7.2 函数列表 _表 8. IPI 函数列表_ | 函数名 | SBI 版本 | FID | EID | -|:-------------|----------|-----|----------| +| :----------- | -------- | --- | -------- | | sbi_send_ipi | 0.2 | 0 | 0x735049 | ## 章节 8. RFENCE 扩展 (EID #0x52464E43 "RFNC") @@ -438,7 +444,7 @@ _表 8. IPI 函数列表_ - start_addr 和 size 都为 0 - size 等于 2^XLEN-1 -### 8.1.函数:远程 FENCE.I 指令 (FID #0) +### 8.1 函数:远程 FENCE.I 指令 (FID #0) ``` struct sbiret sbi_remote_fence_i(unsigned long hart_mask,unsigned long hart_mask_base) @@ -446,13 +452,14 @@ struct sbiret sbi_remote_fence_i(unsigned long hart_mask,unsigned long hart_mask 远程指示 harts 执行 FENCE.I 指令。 sbiret.error 返回的可能错误代码如表 9 所示。 + _表 9. RFENCE 远程 FENCE.I 指令错误_ -| 错误代码 | 描述 | -|:---------------|-------------------------------| +| 错误代码 | 描述 | +| :--------------- | -------------------------------- | | SBI_SUCCESS 成功 | IPI 被成功发送至所有目标 harts。 | -### 8.2.函数:远程 SFENCE.VMA 指令 (FID #1) +### 8.2 函数:远程 SFENCE.VMA 指令 (FID #1) ``` struct sbiret sbi_remote_sfence_vma(unsigned long hart_mask, @@ -461,14 +468,15 @@ unsigned long hart_mask_base, unsigned long start_addr, unsigned long size) 指示远程 hart 执行一个或多个 SFENCE.VMA 指令,覆盖从 start 到 size 的虚拟地址范围内的地址。 sbiret.error 返回的可能错误代码如表 10 所示。 + _表 10. RFENCE 远程 SFENCE.VMA 错误_ -| 错误代码 | 描述 | -|:-------------------------------|-------------------------------| +| 错误代码 | 描述 | +| :------------------------------- | -------------------------------- | | SBI_SUCCESS 成功 | IPI 被成功发送至所有目标 harts。 | -| SBI_ERR_INVALID_ADDRESS 非法地址 | start_addr 或 size 非法 | +| SBI_ERR_INVALID_ADDRESS 非法地址 | start_addr 或 size 非法 | -### 8.3.函数:远程 SFENCE.VMA 指定 ASID (FID #2) +### 8.3 函数:远程 SFENCE.VMA 指定 ASID (FID #2) ``` struct sbiret sbi_remote_sfence_vma_asid(unsigned long hart_mask, @@ -478,14 +486,15 @@ unsigned long asid) 指示远程 hart 执行一个或多个 SFENCE.VMA 指令,覆盖从 start 到 size 的虚拟地址范围内的地址。这仅涵盖给定的 ASID。 sbiret.error 返回的可能错误代码如表 11 所示。 + _表 11. RFENCE 远程 SFENCE.VMA 指定 ASID 错误_ -| 错误代码 | 描述 | -|:-------------------------------|-------------------------------| +| 错误代码 | 描述 | +| :------------------------------- | -------------------------------- | | SBI_SUCCESS 成功 | IPI 被成功发送至所有目标 harts。 | -| SBI_ERR_INVALID_ADDRESS 非法地址 | start_addr 或 size 非法 | +| SBI_ERR_INVALID_ADDRESS 非法地址 | start_addr 或 size 非法 | -### 8.4.函数:远程 HFENCE.GVMA 指定 VMID (FID #3) +### 8.4 函数:远程 HFENCE.GVMA 指定 VMID (FID #3) ``` struct sbiret sbi_remote_hfence_gvma_vmid(unsigned long hart_mask, @@ -495,15 +504,16 @@ unsigned long vmid) 指示远程 hart 执行一个或多个 HFENCE.GVMA 指令,仅覆盖给定 VMID(虚拟机标识符)的 start 到 size 之间的客户机物理地址范围。此函数调用仅适用于实现了虚拟化扩展的 harts。 sbiret.error 返回的可能错误代码如表 12 所示。 + _表 12. RFENCE 远程 HFENCE.GVMA 指定 VMID 错误_ -| 错误代码 | 描述 | -|:--------------------------------|--------------------------------------------------------------| -| SBI_SUCCESS 成功 | IPI 被成功发送至所有目标 harts。 | +| 错误代码 | 描述 | +| :---------------------------------- | ---------------------------------------------------------------- | +| SBI_SUCCESS 成功 | IPI 被成功发送至所有目标 harts。 | | SBI_ERR_NOT_SUPPORTED(操作)不支持 | 由于未被实现或目标 hart 中不错支持虚拟化扩展,则该操作不受支持。 | -| SBI_ERR_INVALID_ADDRESS 非法地址 | start_addr 或 size 非法 | +| SBI_ERR_INVALID_ADDRESS 非法地址 | start_addr 或 size 非法 | -### 8.5.函数:远程 HFENCE.GVMA (FID #4) +### 8.5 函数:远程 HFENCE.GVMA (FID #4) ``` struct sbiret sbi_remote_hfence_gvma(unsigned long hart_mask, @@ -512,15 +522,16 @@ unsigned long hart_mask_base, unsigned long start_addr, unsigned long size) 指示远程 harts 执行一个或多个 HFENCE.GVMA 指令,覆盖从 start 到 size 之间的所有客户机的客户机物理地址范围。此函数调用仅适用于实现虚拟化扩展的 harts。 sbiret.error 返回的可能错误代码如表 13 所示。 + _表 13. RFENCE 远程 HFENCE.GVMA 错误_ -| 错误代码 | 描述 | -|:--------------------------------|--------------------------------------------------------------| -| SBI_SUCCESS 成功 | IPI 被成功发送至所有目标 harts。 | +| 错误代码 | 描述 | +| :---------------------------------- | ---------------------------------------------------------------- | +| SBI_SUCCESS 成功 | IPI 被成功发送至所有目标 harts。 | | SBI_ERR_NOT_SUPPORTED(操作)不支持 | 由于未被实现或目标 hart 中不错支持虚拟化扩展,则该操作不受支持。 | -| SBI_ERR_INVALID_ADDRESS 非法地址 | start_addr 或 size 非法 | +| SBI_ERR_INVALID_ADDRESS 非法地址 | start_addr 或 size 非法 | -### 8.6.函数:远程 HFENCE.VVMA 指定 ASID (FID #5) +### 8.6 函数:远程 HFENCE.VVMA 指定 ASID (FID #5) ``` struct sbiret sbi_remote_hfence_vvma_asid(unsigned long hart_mask, @@ -531,15 +542,16 @@ unsigned long asid) 远程指示 harts 执行一个或多个 HFENCE.VVMA 指令,覆盖从 start 至 size 之间,指定的 ASID 与 VMID(hgatp 寄存器)下所有客户机物理地址范围。此函数调用仅适用于实现虚拟化扩展的 harts。 sbiret.error 返回的可能错误代码如表 14 所示。 + _表 14. RFENCE 远程 HFENCE.VVMA 指定 ASID 错误_ -| 错误代码 | 描述 | -|:--------------------------------|--------------------------------------------------------------| -| SBI_SUCCESS 成功 | IPI 被成功发送至所有目标 harts。 | +| 错误代码 | 描述 | +| :---------------------------------- | ---------------------------------------------------------------- | +| SBI_SUCCESS 成功 | IPI 被成功发送至所有目标 harts。 | | SBI_ERR_NOT_SUPPORTED(操作)不支持 | 由于未被实现或目标 hart 中不错支持虚拟化扩展,则该操作不受支持。 | -| SBI_ERR_INVALID_ADDRESS 非法地址 | start_addr 或 size 非法 | +| SBI_ERR_INVALID_ADDRESS 非法地址 | start_addr 或 size 非法 | -### 8.7.函数:远程 HFENCE.VVMA (FID #6) +### 8.7 函数:远程 HFENCE.VVMA (FID #6) ``` struct sbiret sbi_remote_hfence_vvma(unsigned long hart_mask, @@ -548,20 +560,21 @@ unsigned long hart_mask_base, unsigned long start_addr, unsigned long size) 指示远程 HART 执行一个或多个 HFENCE.VVMA 指令,覆盖当前调用 HART 的 VMID(在 hgatp 寄存器中)下的 start 和 size 之间的客户虚拟地址范围。此函数调用仅适用于实现了虚拟化扩展的 harts。 sbiret.error 返回的可能错误代码如表 15 所示。 + _表 15.RFENCE 远程 HFENCE.VVMA 错误_ -| 错误代码 | 描述 | -|:--------------------------------|--------------------------------------------------------------| -| SBI_SUCCESS 成功 | IPI 被成功发送至所有目标 harts。 | +| 错误代码 | 描述 | +| :---------------------------------- | ---------------------------------------------------------------- | +| SBI_SUCCESS 成功 | IPI 被成功发送至所有目标 harts。 | | SBI_ERR_NOT_SUPPORTED(操作)不支持 | 由于未被实现或目标 hart 中不错支持虚拟化扩展,则该操作不受支持。 | -| SBI_ERR_INVALID_ADDRESS 非法地址 | start_addr 或 size 非法 | +| SBI_ERR_INVALID_ADDRESS 非法地址 | start_addr 或 size 非法 | -### 8.8.函数列表 +### 8.8 函数列表 _表 16. RFENCE 函数列表_ | 函数名 | SBI 版本 | FID | EID | -|:----------------------------|----------|-----|------------| +| :-------------------------- | -------- | --- | ---------- | | sbi_remote_fence_i | 0.2 | 0 | 0x52464E43 | | sbi_remote_sfence_vma | 0.2 | 1 | 0x52464E43 | | sbi_remote_sfence_vma_asid | 0.2 | 2 | 0x52464E43 | @@ -572,19 +585,20 @@ _表 16. RFENCE 函数列表_ ## 章节 9. Hart State Management 状态管理扩展 (EID #0x48534D "HSM") -Hart State Management (HSM) 扩展引入了一组 Hart 状态和一组函数,允许 S-mode 监管者模式下的软件请求 Hart 状态变更。 +Hart State Management (HSM) 扩展引入了一组 Hart 状态和一组函数,允许监督者模式下的软件请求 Hart 状态变更。 下面的表 17 描述了所有可能的 HSM 状态以及每个状态的唯一 HSM 状态标识符。: + _表 17. HSM Hart 状态_ -| 状态 ID | 状态名 | 描述 | -|:--------|------------------------|------------------------------------------------------------------------------------------------------------------------------| -| 0 | STARTED 启动 | 该 hart 处于物理上电状态,并正常执行。 | -| 1 | STOPPED 结束 | 该 hart 没有在监管者模式 S-mode 或任何更低特权模式下执行。如果底层平台具有关闭 hart 的物理机制,那么它可能被 SBI 实现关闭了电源。 | -| 2 | START_PENDING 等待被启动 | 另一个 hart 请求将该 hart 从 STOPPED 状态启动(或上电),而 SBI 实现仍在努力将该 hart 转换为 STARTED 状态。 | -| 3 | STOP_PENDING 结束等待 | 该 hart 在 STARTED 状态下请求停止(或关机),而 SBI 实现仍在努力将该 hart 转变为 STOPPED 状态。 | -| 4 | SUSPENDED 挂起 | 该 hart 处于特定于平台的暂停(或低功耗)状态。 | -| 5 | SUSPEND_PENDING 等待挂起 | 该 hart 从 STARTED 状态请求将自身置于特定于平台的低功耗状态,并且 SBI 实现正在努力将该 hart 转换为特定于平台的 SUSPENDED 状态。 | -| 6 | RESUME_PENDING 等待恢复 | 中断或特定于平台的硬件事件导致 hart 从 SUSPENDED 状态恢复正常执行,而 SBI 实现正在努力将该 hart 转换为 STARTED 状态。 | +| 状态 ID | 状态名 | 描述 | +| :------ | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | +| 0 | 启动 (STARTED) | 该 hart 处于物理上电状态,并正常执行。 | +| 1 | 结束 (STOPPED) | 该 hart 没有在监督者模式或任何更低特权模式下执行。如果底层平台具有关闭 hart 的物理机制,那么它可能被 SBI 实现关闭了电源。 | +| 2 | 等待被启动 (START_PENDING) | 另一个 hart 请求将该 hart 从 STOPPED 状态启动(或上电),而 SBI 实现仍在努力将该 hart 转换为 STARTED 状态。 | +| 3 | 结束等待 (STOP_PENDING) | 该 hart 在 STARTED 状态下请求停止(或关机),而 SBI 实现仍在努力将该 hart 转变为 STOPPED 状态。 | +| 4 | 挂起 (SUSPENDED) | 该 hart 处于特定于平台的暂停(或低功耗)状态。 | +| 5 | 等待挂起 (SUSPEND_PENDING) | 该 hart 从 STARTED 状态请求将自身置于特定于平台的低功耗状态,并且 SBI 实现正在努力将该 hart 转换为特定于平台的 SUSPENDED 状态。 | +| 6 | 等待恢复 (RESUME_PENDING) | 中断或特定于平台的硬件事件导致 hart 从 SUSPENDED 状态恢复正常执行,而 SBI 实现正在努力将该 hart 转换为 STARTED 状态。 | 在任何时刻,hart 应该处于上述提到的 hart 状态之一。SBI 实现对 hart 状态的转换应遵循图 3 所示的状态机。 @@ -594,61 +608,64 @@ _表 17. HSM Hart 状态_ 一个平台可以有多个 hart,这些 hart 被分组成层次拓扑组(如核心、集群、节点等),每个层次组都有独立的平台特定低功耗状态。这些层次拓扑组的平台特定低功耗状态可以表示为一个 hart 的平台特定挂起状态。SBI 实现可以利用较高层次拓扑组的挂起状态,使用以下一种方法之一: -1. **平台协调**: 在这种方法中,当一个 hart 变为空闲时,监管模式 S-mode 的功耗管理软件将请求该 hart 和较高层次组的最深挂起状态。SBI 实现应选择一个较高层次组的挂起状态,满足以下条件: - 1. 不比指定的挂起状态更 +1. **平台协调**: 在这种方法中,当一个 hart 变为空闲时,监督者模式的功耗管理软件将请求该 hart 和较高层次组的最深挂起状态。SBI 实现应选择一个较高层次组的挂起状态,满足以下条件: + 1. 不比指定的挂起状态更深 2. 唤醒延迟不高于指定挂起状态的唤醒延迟 -2. O**操作系统发起**: 在这种方法中,监管模式 S-mode 的功耗管理软件将在最后一个 hart 变为空闲后,直接请求较高层次组的挂起状态。当一个 hart 变为空闲时,监管模式的功耗管理软件总是选择该 hart 的挂起状态,但仅在该 hart 是组内最后一个运行的 hart 时,才为较高层次组选择一个挂起状态。SBI 实现应满足以下条件: +2. **操作系统发起**: 在这种方法中,监督者模式的功耗管理软件将在最后一个 hart 变为空闲后,直接请求较高层次组的挂起状态。当一个 hart 变为空闲时,监管模式的功耗管理软件总是选择该 hart 的挂起状态,但仅在该 hart 是组内最后一个运行的 hart 时,才为较高层次组选择一个挂起状态。SBI 实现应满足以下条件: 1. 永远不要选择与指定暂停状态不同的较高层次组的挂起状态 2. 总是优先选择最近请求的较高层次组的挂起状态。 -### 9.1.函数:HART 启动 (FID #0) +### 9.1 函数:HART 启动 (FID #0) ``` struct sbiret sbi_hart_start(unsigned long hartid, unsigned long start_addr, unsigned long opaque) ``` -SBI 实现以特定的寄存器值在指定的 start_addr 地址处启动执行目标 hart 的超级模式。具体的寄存器值如表 18 所述。 +SBI 实现以特定的寄存器值在指定的 start_addr 地址处启动执行目标 hart 的监督者模式。具体的寄存器值如表 18 所述。 + _表 18. HSM Hart 启动的寄存器状态_ -| 寄存器名称 | 寄存器值 | -|:----------------------------|-------------| -| satp | 0 | -| sstatus.SIE | 0 | -| a0 | hartid | -| a1 | opaque 参数 | +| 寄存器名称 | 寄存器值 | +| :--------------------------- | ----------- | +| satp | 0 | +| sstatus.SIE | 0 | +| a0 | hartid | +| a1 | opaque 参数 | | 其他寄存器仍处于未定义状态。 | | -此调用是异步的,具体来说,sbi_hart_start() 函数可以在目标 hart 开始执行之前返回,只要 SBI 实现能够确保返回的结果准确。如果 SBI 实现是在机器模式(M-mode)下执行的平台运行时固件,则必须在将控制权转移给 S-mode 模式软件之前配置 PMP 和其他 M-mode 状态。 +此调用是异步的,具体来说,sbi_hart_start() 函数可以在目标 hart 开始执行之前返回,只要 SBI 实现能够确保返回的结果准确。如果 SBI 实现是在机器模式(M-mode)下执行的平台运行时固件,则必须在将控制权转移给监督者模式软件之前配置 PMP 和其他 M-mode 状态。 hartid 参数指定要启动的目标 hart。 -start_addr 参数指向一个运行时指定的物理地址,该 hart 可以在 S-mode 模式下开始执行。 +start_addr 参数指向一个运行时指定的物理地址,该 hart 可以在监督者模式下开始执行。 opaque 参数是一个 XLEN 位的值,当 hart 在 start_addr 处开始执行时 opaque 参数被设置在 a1 寄存器中。 sbiret.error 中可能返回的错误代码在下表 19 中显示。 + _表 19. HSM Hart 启动错误_ -| 错误代码 | 描述 | -|:--------------------------------|----------------------------------------------------------------------------| -| SBI_SUCCESS 成功 | Hart 之前处于停止状态。它将从 start_addr 开始执行 | -| SBI_ERR_INVALID_ADDRESS 非法地址 | start_addr 无效,原因可能如下:无效物理地址。该地址被 PMP 禁止在 S-mode 下执行。 | -| SBI_ERR_INVALID_PARAM 非法参数 | Hartid 是无效的物理地址,因为其相应的 hart 不能以 S-mode 启动。 | -| SBI_ERR_ALREADY_AVAILAB LE 已存在 | 给定 hartid 已启动。 | -| SBI_ERR_FAILED 失败 | 未知原因造成的启动失败。 | +| 错误代码 | 描述 | +| :-------------------------------- | ---------------------------------------------------------------------------------- | +| SBI_SUCCESS 成功 | Hart 之前处于停止状态。它将从 start_addr 开始执行 | +| SBI_ERR_INVALID_ADDRESS 非法地址 | start_addr 无效,原因可能如下:无效物理地址。该地址被 PMP 禁止在监督者模式下执行。 | +| SBI_ERR_INVALID_PARAM 非法参数 | 无效的 Hart,因为其相应的 hart 不能以监督者模式启动。 | +| SBI_ERR_ALREADY_AVAILAB LE 已存在 | 给定 hartid 已启动。 | +| SBI_ERR_FAILED 失败 | 未知原因造成的启动失败。 | -### 9.2.函数:HART 停止 (FID #1) +### 9.2 函数:HART 停止 (FID #1) ``` struct sbiret sbi_hart_stop(void) ``` -要求 SBI 实现停止以 S-mode 模式执行调用 hart,并将其所有权返回给 SBI 实现。在正常情况下,不希望此调用返回。sbi_hart_stop() 必须在禁用 S-mode 模式中断时调用。 +要求 SBI 实现停止以监督者模式执行调用 hart,并将其所有权返回给 SBI 实现。在正常情况下,不希望此调用返回。sbi_hart_stop() 必须在禁用监督者模式中断时调用。 sbiret.error 可能返回的错误代码如下表 20 所示。 + _表 20. HSM Hart 停止错误_ | 错误代码 | 描述 | -|:------------------|----------------------| +| :------------------ | ---------------------- | | SBI_ERR_FAILED 失败 | 当前 hart 停止执行失败 | -### 9.3.函数:HART 获取状态 (FID #2) +### 9.3 函数:HART 获取状态 (FID #2) ``` struct sbiret sbi_hart_get_status(unsigned long hartid) @@ -658,15 +675,16 @@ struct sbiret sbi_hart_get_status(unsigned long hartid) hartid 参数指定需要查询状态的目标 hart。 sbiret.value 中可能返回的状态(或 HSM 状态 ID)值在表 17 中进行了描述。 sbiret.error 可能返回的错误代码如下表 21 所示。 + _表 21. HSM Hart 获取状态错误_ | 错误代码 | 描述 | -|:---------------------------|------------------| +| :----------------------------- | ------------------ | | SBI_ERR_INVALID_PARAM 无效参数 | 给定的 hartid 无效 | 由于任何并发的 sbi_hart_start()、sbi_hart_stop() 或 sbi_hart_suspend() 调用,harts 可能随时转换 HSM 状态,因此该函数的返回值可能不代表返回值验证时 hart 的实际状态。 -### 9.4.函数:HART 挂起 (FID #3) +### 9.4 函数:HART 挂起 (FID #3) ``` struct sbiret sbi_hart_suspend(uint32_t suspend_type, @@ -675,50 +693,53 @@ unsigned long resume_addr, unsigned long opaque) SBI 实现将调用的 hart 置于由 suspend_type 参数指定的平台特定的挂起(或低功耗)状态。当收到中断或平台特定的硬件事件时,hart 将自动退出挂起状态并恢复正常执行。 一个 hart 的平台特定挂起状态可以是保持性的或非保持性的。保持性挂起状态将保留所有特权模式下 hart 的寄存器和 CSR 值,而非保持性挂起状态将不保留 hart 的寄存器和 CSR 值。 -从保持性挂起状态恢复是比较简单的,S-mode 模式的软件将看到 SBI 挂起调用返回而无需任何失败。在保持性挂起期间,resume_addr 参数未使用。 -从非保持性挂起状态恢复相对更复杂,需要软件还原各种特权模式下的 hart 寄存器和 CSR。从非保持性挂起状态恢复后,hart 将跳转到由 resume_addr 指定的 S-mode 模式地址,具体寄存器的值在表 22 中描述。 +从保持性挂起状态恢复是比较简单的,监督者模式的软件将看到 SBI 挂起调用返回而无需任何失败。在保持性挂起期间,resume_addr 参数未使用。 +从非保持性挂起状态恢复相对更复杂,需要软件还原各种特权模式下的 hart 寄存器和 CSR。从非保持性挂起状态恢复后,hart 将跳转到由 resume_addr 指定的监督者模式地址,具体寄存器的值在表 22 中描述。 + _表 22. HSM Hart 恢复寄存器状态_ -| 寄存器名称 | 寄存器值 | -|:--------------------------|-------------| -| satp | 0 | -| sstatus.SIE | 0 | -| a0 | hartid | -| a1 | Opaque 参数 | +| 寄存器名称 | 寄存器值 | +| :------------------------- | ----------- | +| satp | 0 | +| sstatus.SIE | 0 | +| a0 | hartid | +| a1 | Opaque 参数 | | 其他寄存器保持未定义状态。 | | suspend_type 参数是 32 位宽,可能的值如表 23 所示。 + _表 23. HSM Hart 挂起类型_ | 值 | 描述 | -|:------------------------|--------------------| +| :---------------------- | -------------------- | | 0x00000000 | 默认保持性挂起 | | 0x00000001 - 0x0FFFFFFF | 保留供未来使用 | | 0x10000000 - 0x7FFFFFFF | 平台特定保持性挂起 | | 0x80000000 | 默认非保持性挂起 | | 0x80000001 - 0x8FFFFFFF | 保留供未来使用 | -| 0x90000000 -0xFFFFFFFF | 平台特定非保持性挂起 | +| 0x90000000 - 0xFFFFFFFF | 平台特定非保持性挂起 | | > 0xFFFFFFFF | 保留 | -resume_addr 参数指向一个在非保持性挂起后,hart 可以在 S-mode 模式下恢复执行的运行时指定的物理地址。 +resume_addr 参数指向一个在非保持性挂起后,hart 可以在监督者模式下恢复执行的运行时指定的物理地址。 opaque 参数是一个 XLEN 位的值,当 hart 在非保留挂起后在 resume_addr 处恢复执行时,会将该值设置在 a1 寄存器中。 sbiret.error 中可能返回的错误代码如表 24 所示。 + _表 24. HSM Hart 挂起错误_ -| 错误代码 | 描述 | -|:-------------------------------|-----------------------------------------------------------------------------------------| -| SBI_SUCCESS 成功 | Hart 已成功地从保持性挂起状态中恢复。 | -| SBI_ERR_INVALID_PARAM 无效参数 | suspend_type 无效 | -| SBI_ERR_NOT_SUPPORTED 不支持 | suspend_type 有效但未实现。 | -| SBI_ERR_INVALID_ADDRESS 无效地址 | resume_addr 无效,可能是由于以下原因:无效的物理地址。该地址被 PMP 禁止在 S-mode 模式下运行。 | -| SBI_ERR_FAILED 失败 | 挂起请求因未知原因而失败。 | +| 错误代码 | 描述 | +| :------------------------------- | ------------------------------------------------------------------------------------------- | +| SBI_SUCCESS 成功 | Hart 已成功地从保持性挂起状态中恢复。 | +| SBI_ERR_INVALID_PARAM 无效参数 | suspend_type 无效 | +| SBI_ERR_NOT_SUPPORTED 不支持 | suspend_type 有效但未实现。 | +| SBI_ERR_INVALID_ADDRESS 无效地址 | resume_addr 无效,可能是由于以下原因:无效的物理地址或该地址被 PMP 禁止在监督者模式下运行。 | +| SBI_ERR_FAILED 失败 | 挂起请求因未知原因而失败。 | -### 9.5. 函数列表 +### 9.5 函数列表 _表 25. HSM 函数列表_ | 函数名 | SBI 版本 | FID | EID | -|:---------------------------|----------|-----|----------| +| :--------------------------- | -------- | --- | -------- | | sbi_hart_start 启动 | 0.2 | 0 | 0x48534D | | sbi_hart_stop 停止 | 0.2 | 1 | 0x48534D | | sbi_hart_get_status 获取状态 | 0.2 | 2 | 0x48534D | @@ -726,9 +747,9 @@ _表 25. HSM 函数列表_ ## 章节 10. 系统复位扩展 (EID #0x53525354 "SRST") -系统复位扩展提供了一个函数,允许 S-mode 模式下的软件请求系统级的重启或关闭操作。"系统"指的是 S-mode 模式下的软件的视角,底层的 SBI 实现可以是机器模式 M-mode 固件或虚拟化管理程序。 +系统复位扩展提供了一个函数,允许监督者模式下的软件请求系统级的重启或关闭操作。"系统"指的是监督者模式下的软件的视角,底层的 SBI 实现可以是机器模式 M-mode 固件或虚拟化管理程序。 -### 10.1.函数:系统复位 (FID #0) +### 10.1 函数:系统复位 (FID #0) ``` struct sbiret sbi_system_reset(uint32_t reset_type, uint32_t reset_reason) @@ -736,22 +757,24 @@ struct sbiret sbi_system_reset(uint32_t reset_type, uint32_t reset_reason) 根据提供的类型 reset_type 和原因 reset_reason 重置系统。这是一个同步调用,如果成功将不会返回。 reset_type 参数的宽度为 32 位,其可能的取值在下面的表 26 中列出。 + _表 26. SRST 系统复位类型_ -| 值 | 描述 | -|:-----------------------|--------------------------| -| 0x00000000 | 关机 | -| 0x00000001 | 冷启动 | -| 0x00000002 | 热启动 | -| 0x00000003 -0xEFFFFFFF | 保留以便未来使用 | -| 0xF0000000 -0xFFFFFFFF | 供应商或平台特定的复位类型 | -| > 0xFFFFFFFF | 保留 | +| 值 | 描述 | +| :---------------------- | -------------------------- | +| 0x00000000 | 关机 | +| 0x00000001 | 冷启动 | +| 0x00000002 | 热启动 | +| 0x00000003 - 0xEFFFFFFF | 保留以便未来使用 | +| 0xF0000000 - 0xFFFFFFFF | 供应商或平台特定的复位类型 | +| > 0xFFFFFFFF | 保留 | reset_reason 是一个可选参数,表示系统复位的原因。该参数是 32 位宽,可能的取值如下所示: + _表 27. SRST 系统复位原因_ | 值 | 描述 | -|:-----------------------|--------------------------| +| :--------------------- | -------------------------- | | 0x00000000 | 没有原因 | | 0x00000001 | 系统失败 | | 0x00000002 -0xDFFFFFFF | 保留以便未来使用 | @@ -759,32 +782,33 @@ _表 27. SRST 系统复位原因_ | 0xF0000000 -0xFFFFFFFF | 供应商或平台特定的复位类型 | | > 0xFFFFFFFF | 保留 | -当 S-mode 下的软件在本地运行时,SBI 实现属于机器模式下的固件。在这种情况下,关机等同于整个系统的物理断电,冷启动等同于整个系统的物理断电循环(并重新上电)。此外,热重启等同于对主处理器和系统的一部分进行断电循环,而不是整个系统。例如,在具有 BMC(主板管理控制器)的服务器级系统上,热重启不会对 BMC 进行断电循环,而冷重启则肯定会对 BMC 进行断电循环。 -当 S-mode 模式下的软件在虚拟机内运行时,SBI 实现是一个虚拟化管理器。关机、冷重启和热重启在功能上与本机情况相同,但可能不会导致任何物理电源变化。 +当监督者下的软件在本地运行时,SBI 实现属于机器模式下的固件。在这种情况下,关机等同于整个系统的物理断电,冷启动等同于整个系统的物理断电循环(并重新上电)。此外,热重启等同于对主处理器和系统的一部分进行断电循环,而不是整个系统。例如,在具有 BMC(主板管理控制器)的服务器级系统上,热重启不会对 BMC 进行断电循环,而冷重启则肯定会对 BMC 进行断电循环。 +当监督者模式下的软件在虚拟机内运行时,SBI 实现是一个虚拟化管理器。关机、冷重启和热重启在功能上与本机情况相同,但可能不会导致任何物理电源变化。 sbiret.error 中可能返回的错误代码如表 28 所示。 + _表 28. SRST 系统复位错误 s_ | 错误代码 | 描述 | -|:-----------------------------|-------------------------------| +| :----------------------------- | ------------------------------- | | SBI_ERR_INVALID_PARAM 无效参数 | reset_type 或 reset_reason 无效 | | SBI_ERR_NOT_SUPPORTED 不支持 | reset_type 有效但未被设定 | | SBI_ERR_FAILED 失败 | 未知原因的复位请求失败 | -### 10.2.函数列表 +### 10.2 函数列表 _表 29. SRST 函数列表_ | Function Name | SBI Version | FID | EID | -|:-----------------|-------------|-----|------------| +| :--------------- | ----------- | --- | ---------- | | sbi_system_reset | 0.3 | 0 | 0x53525354 | ## 章节 11. 性能监控单元扩展 (EID #0x504D55 "PMU") -RISC-V 硬件性能计数器(如 mcycle、minstret 和 mhpmcounterX CSRs)可以以只读方式从监管者模式 S-mode 使用 cycle、instret 和 hpmcounterX CSRs 进行访问。SBI 性能监控单元(PMU)扩展是一个接口,供监管者模式 S-mode 使用,以便借助机器模式(或虚拟化模式)配置和使用 RISC-V 硬件性能计数器。这些硬件性能计数器只能从机器模式使用 mcountinhibit 和 mhpmeventX CSRs 启动、停止或配置。因此,如果 RISC-V 平台未实现 mhpmcounterX CSR,机器模式的 SBI 实现可能选择禁止 SBI PMU 扩展。 +RISC-V 硬件性能计数器(如 mcycle、minstret 和 mhpmcounterX CSRs)可以以只读方式从监督者模式使用 cycle、instret 和 hpmcounterX CSRs 进行访问。SBI 性能监控单元(PMU)扩展是一个接口,供监督者模式使用,以便借助机器模式(或虚拟化模式)配置和使用 RISC-V 硬件性能计数器。这些硬件性能计数器只能从机器模式使用 mcountinhibit 和 mhpmeventX CSRs 启动、停止或配置。因此,如果 RISC-V 平台未实现 mhpmcounterX CSR,机器模式的 SBI 实现可能选择禁止 SBI PMU 扩展。 一般情况下,RISC-V 平台支持使用有限数量的硬件性能计数器(最多 64 位)来监控各种硬件事件。此外,SBI 实现还可以提供固件性能计数器,用于监控固件事件,例如不对齐的加载/存储指令数量、RFENCE 数量、IPI 数量等。固件计数器始终为 64 位。 SBI PMU 扩展提供以下功能: -1. 监管者模式软件发现和配置每个 HART 的硬件/固件计数器的接口 +1. 监督者模式软件发现和配置每个 HART 的硬件/固件计数器的接口 2. 具有典型 perf 兼容接口的硬件/固件性能计数器和事件 3. 完全访问微体系结构的原始事件编码 @@ -796,24 +820,25 @@ event_idx[19:16]= type event_idx[15:0] = code ``` -### 11.1.事件:硬件通用事件 (Type #0) +### 11.1 事件:硬件通用事件 (Type #0) event_idx.type(即事件类型)对于所有硬件通用事件应为 0x0,并且每个硬件通用事件由一个唯一的 event_idx.code(即事件代码)标识,如下所示的表格 30 中所述。 + _表 30. PMU 硬件事件_ -| 通用事件名称 | 代码 | 描述 | -|:-----------------------------------|------|------------------------------------| +| 通用事件名称 | 代码 | 描述 | +| :--------------------------------- | ---- | ------------------------------------- | | SBI_PMU_HW_NO_EVENT | 0 | 未使用的事件,因为 event_idx 不能为零 | -| SBI_PMU_HW_CPU_CYCLES | 1 | 每个 CPU 周期的事件 | -| SBI_PMU_HW_INSTRUCTIONS | 2 | 每个完成的指令的事件 | -| SBI_PMU_HW_CACHE_REFERENCES | 3 | 缓存命中的事件 | -| SBI_PMU_HW_CACHE_MISSES | 4 | 缓存未命中的事件 | -| SBI_PMU_HW_BRANCH_INSTRUCTIONS | 5 | 分支指令的事件 | -| SBI_PMU_HW_BRANCH_MISSES | 6 | 分支预测错误的事件 | -| SBI_PMU_HW_BUS_CYCLES | 7 | 每个 BUS 周期的事件 | -| SBI_PMU_HW_STALLED_CYCLES_FRONTEND | 8 | 微架构前端阻塞周期的事件 | -| SBI_PMU_HW_STALLED_CYCLES_BACKEND | 9 | 微架构后端阻塞周期的事件 | -| SBI_PMU_HW_REF_CPU_CYCLES | 10 | 每个参考 CPU 周期的事件 | +| SBI_PMU_HW_CPU_CYCLES | 1 | 每个 CPU 周期的事件 | +| SBI_PMU_HW_INSTRUCTIONS | 2 | 每个完成的指令的事件 | +| SBI_PMU_HW_CACHE_REFERENCES | 3 | 缓存命中的事件 | +| SBI_PMU_HW_CACHE_MISSES | 4 | 缓存未命中的事件 | +| SBI_PMU_HW_BRANCH_INSTRUCTIONS | 5 | 分支指令的事件 | +| SBI_PMU_HW_BRANCH_MISSES | 6 | 分支预测错误的事件 | +| SBI_PMU_HW_BUS_CYCLES | 7 | 每个 BUS 周期的事件 | +| SBI_PMU_HW_STALLED_CYCLES_FRONTEND | 8 | 微架构前端阻塞周期的事件 | +| SBI_PMU_HW_STALLED_CYCLES_BACKEND | 9 | 微架构后端阻塞周期的事件 | +| SBI_PMU_HW_REF_CPU_CYCLES | 10 | 每个参考 CPU 周期的事件 | > 注意:对于硬件通用事件,event_data(即事件数据)未使用,event_data 的所有非零值都保留供将来使用。 @@ -824,7 +849,7 @@ _表 30. PMU 硬件事件_ > 注意:SBI_PMU_HW_REF_CPU_CYCLES 在 CPU 时钟未停止时计数固定频率的时钟周期。计数的固定频率可以是例如时间 CSR 计数的频率。 > 注意:SBI_PMU_HW_BUS_CYCLES 计数固定频率的时钟周期。计数的固定频率可以是例如时间 CSR 计数的频率,或者可以是 HART(及其私有缓存)与系统其他部分之间的时钟边界的频率。 -### 11.2.事件:硬件缓存事件 (Type #1) +### 11.2 事件:硬件缓存事件 (Type #1) 对于所有硬件缓存事件,event_idx.type(即事件类型)应为 0x1,并且每个硬件缓存事件由唯一的 event_idx.code(即事件代码)标识,其编码如下: @@ -835,10 +860,11 @@ event_idx.code[0:0] = result_id ``` 下表显示了以下标识符可能的值:event_idx.code.cache_id(即缓存事件 ID),event_idx.code.op_id(即缓存操作 ID)和 event_idx.code.result_id(即缓存结果 ID)。 + _表 31. PMU 缓存事件 ID_ | 缓存事件名称 | 事件 ID | 描述 | -|:----------------------|---------|-----------------| +| :-------------------- | ------- | ----------------- | | SBI_PMU_HW_CACHE_L1D | 0 | 一级数据缓存事件 | | SBI_PMU_HW_CACHE_L1I | 1 | 一级指令缓存事件 | | SBI_PMU_HW_CACHE_LL | 2 | 最后一级缓存事件 | @@ -850,7 +876,7 @@ _表 31. PMU 缓存事件 ID_ _表 32. PMU 缓存操作 ID_ | 缓存操作名称 | 操作 ID | 描述 | -|:-----------------------------|---------|----------| +| :--------------------------- | ------- | ---------- | | SBI_PMU_HW_CACHE_OP_READ | 0 | 读取缓存行 | | SBI_PMU_HW_CACHE_OP_WRITE | 1 | 写入缓存行 | | SBI_PMU_HW_CACHE_OP_PREFETCH | 2 | 预取缓存行 | @@ -858,27 +884,28 @@ _表 32. PMU 缓存操作 ID_ _表 33. PMU 缓存操作结果 ID_ | 缓存结果名称 | 结果 ID | 描述 | -|:-------------------------------|---------|----------| +| :----------------------------- | ------- | ---------- | | SBI_PMU_HW_CACHE_RESULT_ACCESS | 0 | 缓存访问 | | SBI_PMU_HW_CACHE_RESULT_MISS | 1 | 缓存未命中 | > 注意:对于硬件缓存事件,event_data(即事件数据)未使用,所有非零值的事件数据均保留用于将来使用。 -### 11.3.事件:硬件原始事件 (Type #2) +### 11.3 事件:硬件原始事件 (Type #2) 硬件原始事件类型的 event_idx.type (i.e. event type) 值应为 0x2,而 event_idx.code(i.e. event code) 值应为零。 -对于具有 32 位宽度的 mhpmeventX CSRs 的 RISC-V 平台,event_data 配置(或参数)应具有要编程到 mhpmeventX CSR 中的 32 位值。 -对于具有 64 位宽度的 mhpmeventX CSRs 的 RISC-V 平台,event_data 配置(或参数)应具有要编程到 mhpmeventX CSR 的低 48 位值,而 SBI 实现将确定要编程到 mhpmeventX CSR 的高 16 位值。 +对于具有 32 位宽度的 mhpmeventX CSRs 的 RISC-V 平台,event_data 配置(或参数)应包含要编程到 mhpmeventX CSR 中的 32 位值。 +对于具有 64 位宽度的 mhpmeventX CSRs 的 RISC-V 平台,event_data 配置(或参数)应包含要编程到 mhpmeventX CSR 的低 48 位值,而 SBI 实现将确定要编程到 mhpmeventX CSR 的高 16 位值。 > 注意:RISC-V 平台的硬件实现可能选择定义要写入 mhpmeventX CSR 的硬件事件的预期值。对于硬件常规/缓存事件,为了简化起见,RISC-V 平台的硬件实现可能使用零扩展的 event_idx 作为预期值。 -### 11.4.事件:固件事件 (Type #15) +### 11.4 事件:固件事件 (Type #15) 所有固件事件的 event_idx.type(即事件类型)应为 0xf,并且每个固件事件都由唯一的 event_idx.code(即事件代码)标识,其描述如下所示的 表 34 中。 + _表 34. PMU 固件事件_ | 固件事件名称 | 编码 | 描述 | -|:--------------------------------------|------|-----------------------------------------------------| +| :------------------------------------ | ---- | ----------------------------------------------------- | | SBI_PMU_FW_MISALIGNED_LOAD | 0 | 对齐错误加载陷入事件 | | SBI_PMU_FW_MISALIGNED_STORE | 1 | 对齐错误存储陷入事件 | | SBI_PMU_FW_ACCESS_LOAD | 2 | 加载访问陷入事件 | @@ -904,7 +931,7 @@ _表 34. PMU 固件事件_ > 注意:对于固件事件,event_data(即事件数据)未使用,所有非零的 event_data 值都保留供将来使用。 -### 11.5. 函数:获取可用计数器的数量 (FID #0) +### 11.5 函数:获取可用计数器的数量 (FID #0) ``` struct sbiret sbi_pmu_num_counters() @@ -912,7 +939,7 @@ struct sbiret sbi_pmu_num_counters() **返回**可用计数器的数量到 sbiret.value 中,并始终在 sbiret.error 中返回 SBI_SUCCESS。 -### 11.6. 函数:获取特定计数器的详细信息 (FID #1) +### 11.6 函数:获取特定计数器的详细信息 (FID #1) ``` struct sbiret sbi_pmu_counter_get_info(unsigned long counter_idx) @@ -930,14 +957,15 @@ counter_info[XLEN-2:18] = Reserved for future use counter_info[XLEN-1] = Type (0 若 counter_info.type == 1,那么 counter_info.csr 与 counter_info.width 应该被忽略。 在 sbiret.value 中返回上述描述的 counter_info。 sbiret.error 中可能返回的错误代码如下表 35 所示。 + _表 35. PMU 计数器获取信息错误_ -| 错误代码 | 描述 | -|:----------------------|-----------------------------| -| SBI_SUCCESS | 成功读取 counter_info | +| 错误代码 | 描述 | +| :-------------------- | ------------------------------ | +| SBI_SUCCESS | 成功读取 counter_info | | SBI_ERR_INVALID_PARAM | counter_idx 指向无效的计数器。 | -### 11.7. 函数:查找并配置匹配计数器 (FID #2) +### 11.7 函数:查找并配置匹配计数器 (FID #2) ``` struct sbiret sbi_pmu_counter_config_matching(unsigned long counter_idx_base, @@ -946,19 +974,20 @@ unsigned long counter_idx_mask, unsigned long config_flags, unsigned long event 在一组计数器中查找并配置一个尚未启动(或已启用)且可以监视指定事件的计数器。其中,counter_idx_base 和 counter_idx_mask 参数表示计数器集合,event_idx 表示要监视的事件,event_data 表示任何附加事件配置。 config_flags 参数表示额外的计数器配置和过滤标志。config_flags 参数的位定义如下所示: + _表 36. PMU 计数器配置匹配标志_ -| 标志名称 | 位 | 描述 | -|:-----------------------------|------------|----------------------------------| -| SBI_PMU_CFG_FLAG_SKIP_MATCH | 0:0 | 跳过计数器匹配 | +| 标志名称 | 位 | 描述 | +| :--------------------------- | ---------- | ------------------------------------ | +| SBI_PMU_CFG_FLAG_SKIP_MATCH | 0:0 | 跳过计数器匹配 | | SBI_PMU_CFG_FLAG_CLEAR_VALUE | 1:1 | 在计数器配置中清零(或置零)计数器值 | -| SBI_PMU_CFG_FLAG_AUTO_START | 2:2 | 配置匹配计数器后自动启动计数器 | -| SBI_PMU_CFG_FLAG_SET_VUINH | 3:3 | VU 模式下禁止事件计数 | -| SBI_PMU_CFG_FLAG_SET_VSINH | 4:4 | VS 模式下禁止事件计数 | -| SBI_PMU_CFG_FLAG_SET_UINH | 5:5 | U 模式下禁止事件计数 | -| SBI_PMU_CFG_FLAG_SET_SINH | 6:6 | S 模式下禁止事件计数 | -| SBI_PMU_CFG_FLAG_SET_MINH | 7:7 | M 模式下禁止事件计数 | -| RESERVED | 8:(XLEN-1) | 所有非零值保留供将来使用 | +| SBI_PMU_CFG_FLAG_AUTO_START | 2:2 | 配置匹配计数器后自动启动计数器 | +| SBI_PMU_CFG_FLAG_SET_VUINH | 3:3 | VU 模式下禁止事件计数 | +| SBI_PMU_CFG_FLAG_SET_VSINH | 4:4 | VS 模式下禁止事件计数 | +| SBI_PMU_CFG_FLAG_SET_UINH | 5:5 | U 模式下禁止事件计数 | +| SBI_PMU_CFG_FLAG_SET_SINH | 6:6 | S 模式下禁止事件计数 | +| SBI_PMU_CFG_FLAG_SET_MINH | 7:7 | M 模式下禁止事件计数 | +| RESERVED | 8:(XLEN-1) | 所有非零值保留供将来使用 | > 注意:当在 config_flags 中设置了 SBI_PMU_CFG_FLAG_SKIP_MATCH 标志时,SBI 实现将无条件地从由 counter_idx_base 和 counter_idx_mask 指定的计数器集合中选择第一个计数器。 @@ -967,15 +996,16 @@ _表 36. PMU 计数器配置匹配标志_ > 注意:config_flags[3:7] 位是事件过滤提示,因此在安全性方面或由于底层 RISC-V 平台缺乏事件过滤支持时,SBI 实现可能会忽略或覆盖这些提示。 > 成功时,在 sbiret.value 中返回 counter_idx。 > 如果操作失败,在 sbiret.error 中可能返回的错误代码如下表 37 所示: -> _表 37. PMU 计数器配置匹配错误_ -| 错误代码 | 描述 | -|:----------------------|---------------------------------| +_表 37. PMU 计数器配置匹配错误_ + +| 错误代码 | 描述 | +| :-------------------- | ---------------------------------- | | SBI_SUCCESS | 计数器已成功找到并配置。 | | SBI_ERR_INVALID_PARAM | 计数器集合中存在无效的计数器。 | | SBI_ERR_NOT_SUPPORTED | 没有任何计数器能够监视指定的事件。 | -### 11.8. 函数:启动一组计数器 (FID #3) +### 11.8 函数:启动一组计数器 (FID #3) ``` struct sbiret sbi_pmu_counter_start(unsigned long counter_idx_base, unsigned long counter_idx_mask, @@ -984,24 +1014,26 @@ unsigned long start_flags, uint64_t initial_value) 启动或启用一组计数器,并设置指定的初始值。counter_idx_base 和 counter_idx_mask 参数表示计数器集合,initial_value 参数指定计数器的初始值。 start_flags 参数的位定义如下表 38 所示: + _表 38. PMU 计数器启动标志_ | 标志名称 | 位 | 描述 | -|:-----------------------------|------------|-----------------------------------------------| +| :--------------------------- | ---------- | ----------------------------------------------- | | SBI_PMU_START_SET_INIT_VALUE | 0:0 | parameter 根据 initial_value 参数设置计数器的值 | | RESERVED | 1:(XLEN-1) | 所有非零值保留供将来使用 | > 注意:当 start_flags 中未设置 SBI_PMU_START_SET_INIT_VALUE 时,计数器值不会被修改,并且事件计数将从当前计数器值开始。 > sbiret.error 中可能返回的错误代码在下表表 39 中列出。 -> _表 39. PMU 计数器启动错误_ -| 错误代码 | 描述 | -|:------------------------|-------------------------------| -| SBI_SUCCESS | 计数器启动成功 | +_表 39. PMU 计数器启动错误_ + +| 错误代码 | 描述 | +| :---------------------- | -------------------------------- | +| SBI_SUCCESS | 计数器启动成功 | | SBI_ERR_INVALID_PARAM | 参数中指定的一些计数器无效。 | | SBI_ERR_ALREADY_STARTED | 参数中指定的一些计数器已被启动。 | -### 11.9. 函数:停止或禁用一组计数器 (FID #4) +### 11.9 函数:停止或禁用一组计数器 (FID #4) ``` struct sbiret sbi_pmu_counter_stop(unsigned long counter_idx_base, @@ -1009,23 +1041,25 @@ unsigned long counter_idx_mask, unsigned long stop_flags) ``` 停止或禁用调用 HART 上的一组计数器。counter_idx_base 和 counter_idx_mask 参数表示计数器的集合。stop_flags 参数的位定义如下表 40 所示。 + _表 40. PMU 计数器停止禁用标志_ -| 标志名称 | 位 | 描述 | -|:------------------------|------------|---------------------------| +| 标志名称 | 位 | 描述 | +| :---------------------- | ---------- | ---------------------------- | | SBI_PMU_STOP_FLAG_RESET | 0:0 | 重置计数器到事件的映射关系。 | | RESERVED | 1:(XLEN-1) | 所有非零值都保留供将来使用。 | 返回在 sbiret.error 中可能出现的错误代码如下表 41 所示。 + _表 41. PMU 计数器停止禁用错误_ -| 错误代码 | 描述 | -|:------------------------|-------------------------| +| 错误代码 | 描述 | +| :---------------------- | -------------------------- | | SBI_SUCCESS | 计数器禁用成功。 | | SBI_ERR_INVALID_PARAM | 指定的某些计数器无效。 | | SBI_ERR_ALREADY_STOPPED | 指定的某些计数器已经停止。 | -### 11.10. 函数:读取固件计数器 (FID #5) +### 11.10 函数:读取固件计数器 (FID #5) ``` struct sbiret sbi_pmu_counter_fw_read(unsigned long counter_idx) @@ -1033,14 +1067,15 @@ struct sbiret sbi_pmu_counter_fw_read(unsigned long counter_idx) 在 sbiret.value 中提供固件计数器的当前值。 sbiret.error 中返回的可能错误代码如下表 42 所示。 + _表 42. PMU 读取固件计数错误_ -| 错误代码 | 描述 | -|:----------------------|---------------------------------------------| +| 错误代码 | 描述 | +| :-------------------- | ---------------------------------------------- | | SBI_SUCCESS | 成功读取固件计数器的值。 | | SBI_ERR_INVALID_PARAM | counter_idx 指向一个硬件计数器或无效的计数器。 | -### 11.11. 函数:读取固件计数器的高位 (FID #6) +### 11.11 函数:读取固件计数器的高位 (FID #6) ``` struct sbiret sbi_pmu_counter_fw_read_hi(unsigned long counter_idx) @@ -1052,12 +1087,12 @@ struct sbiret sbi_pmu_counter_fw_read_hi(unsigned long counter_idx) *表 43. PMU 计数器固件读高错误* -| 错误代码 | 描述 | -|:----------------------|:------------------------------------------------| +| 错误代码 | 描述 | +| :-------------------- | :------------------------------------------------- | | SBI_SUCCESS | 固件计数器读取成功。 | | SBI_ERR_INVALID_PARAM | counter_idx 指向一个硬件计数器或一个无效的计数器。 | -### 11.12. 函数:启用 PMU 快照功能 (FID #7) +### 11.12 函数:启用 PMU 快照功能 (FID #7) ``` struct sbiret sbi_pmu_snapshot_set_shmem(unsigned long shmem_phys_lo, unsigned long shmem_phys_hi) @@ -1067,31 +1102,31 @@ struct sbiret sbi_pmu_snapshot_set_shmem(unsigned long shmem_phys_lo, unsigned *表 44. SBI PMU 快照共享内存布局* -| 名称 | 偏移量 | 大小 | 描述 | -|:------------------------|:-------|:-----|:------------------------------------------------------------------------------------| +| 名称 | 偏移量 | 大小 | 描述 | +| :---------------------- | :----- | :--- | :----------------------------------------------------------------------------------------- | | counter_overflow_bitmap | 0x0000 | 8 | 一个所有逻辑溢出计数器的位图。只有在 Sscofpmf ISA 扩展可用时,它才有效。否则,它必须为零。 | -| counter_values | 0x0008 | 512 | 一个 64 位逻辑计数器的数组,每个索引代表与硬件/固件相关的每个逻辑计数器的值。 | -| Reserved | 0x0208 | 3576 | 保留给未来使用 | +| counter_values | 0x0008 | 512 | 一个 64 位逻辑计数器的数组,每个索引代表与硬件/固件相关的每个逻辑计数器的值。 | +| Reserved | 0x0208 | 3576 | 保留给未来使用 | 今后对这一结构的任何修订都应以向后兼容的方式进行,并将与 SBI 的一个版本相关。 -这个函数应该在启动时每个哈特只被调用一次。一旦配置好,当 sbi_pmu_counter_stop 被调用并设置了 SBI_PMU_STOP_FLAG_TAKE_SNAPSHOT 标志时,SBI 实现可以对共享内存进行读/写访问。当 sbi_pmu_counter_start 被调用并设置了 SBI_PMU_START_FLAG_INIT_SNAPSHOT 标记时,SBI 实现有只读访问权。SBI 实现不得在其他时间访问该内存。 +这个函数应该在启动时每个 Hart 只被调用一次。一旦配置好,当 sbi_pmu_counter_stop 被调用并设置了 SBI_PMU_STOP_FLAG_TAKE_SNAPSHOT 标志时,SBI 实现可以对共享内存进行读/写访问。当 sbi_pmu_counter_start 被调用并设置了 SBI_PMU_START_FLAG_INIT_SNAPSHOT 标记时,SBI 实现有只读访问权。SBI 实现不得在其他时间访问该内存。 在 sbiret.error 中返回的可能的错误代码如下表 45 所示。 *表 45. PMU 设置快照区的错误* -| 错误代码 | 描述 | -|-------------------------|------------------------------------------------------------------------------------------| -| SBI_SUCCESS | 固件计数器读取成功。 | +| 错误代码 | 描述 | +| ----------------------- | -------------------------------------------------------------------------------------------- | +| SBI_SUCCESS | 固件计数器读取成功。 | | SBI_ERR_INVALID_ADDRESS | shmem_phys_lo 和 shmem_phys_hi 参数所指向的共享内存是不可写的,或者不满足 3.2 节的其他要求。 | -### 11.13. 函数列表 +### 11.13 函数列表 _表 43. PMU 函数列表_ | 函数名 | SBI 版本 | FID | EID | -|:--------------------------------|----------|-----|----------| +| :------------------------------ | -------- | --- | -------- | | sbi_pmu_num_counters | 0.3 | 0 | 0x504D55 | | sbi_pmu_counter_get_info | 0.3 | 1 | 0x504D55 | | sbi_pmu_counter_config_matching | 0.3 | 2 | 0x504D55 | @@ -1111,7 +1146,7 @@ _表 43. PMU 函数列表_ > 注意:建议使用调试控制台扩展发送/接收的字节遵循 UTF-8 字符编码。 -### 12.1. 函数:控制台写入 (FID #0) +### 12.1 函数:控制台写入 (FID #0) ``` struct sbiret sbi_debug_console_write(unsigned long num_bytes, unsigned long base_addr_lo, unsigned long base_addr_hi) @@ -1127,13 +1162,13 @@ num_bytes 参数指定了输入存储器中的字节数。输入存储器的物 *表 47. 调试控制台写入错误* -| 错误代码 | 描述 | -|:----------------------|:----------------------------------------------------------------------------------| -| SBI_SUCCESS | 成功写入的字节数。 | +| 错误代码 | 描述 | +| :-------------------- | :------------------------------------------------------------------------------------ | +| SBI_SUCCESS | 成功写入的字节数。 | | SBI_ERR_INVALID_PARAM | num_bytes、base_addr_lo 和 base_addr_hi 参数所指向的内存不满足第 3.2 节中描述的要求。 | | SBI_ERR_FAILED | 由于 I/O 错误,写入失败。 | -### 12.2. 函数:控制台读取 (FID #1) +### 12.2 函数:控制台读取 (FID #1) ``` struct sbiret sbi_debug_console_read(unsigned long num_bytes, unsigned long base_addr_lo, unsigned long base_addr_hi) @@ -1149,13 +1184,13 @@ num_bytes 参数规定了可以写入输出存储器的最大字节数。输出 *表 48. 调试控制台读取错误* -| 错误代码 | 描述 | -|:----------------------|:----------------------------------------------------------------------------------| -| SBI_SUCCESS | 成功读取的字节数。 | +| 错误代码 | 描述 | +| :-------------------- | :------------------------------------------------------------------------------------ | +| SBI_SUCCESS | 成功读取的字节数。 | | SBI_ERR_INVALID_PARAM | num_bytes、base_addr_lo 和 base_addr_hi 参数所指向的内存不满足第 3.2 节中描述的要求。 | | SBI_ERR_FAILED | 由于 I/O 错误,读取失败。 | -### 12.3. 函数:控制台写字节 (FID #2) +### 12.3 函数:控制台写字节 (FID #2) ``` struct sbiret sbi_debug_console_write_byte(uint8_t byte) @@ -1169,17 +1204,17 @@ sbiret.value 被设置为 0,在 sbiret.error 中返回的可能的错误代码 *表 49. 调试控制台写入字节的错误* -| 错误代码 | 描述 | -|:---------------|-------------------------| -| SBI_SUCCESS | 字节写入成功。 | +| 错误代码 | 描述 | +| :------------- | --------------------------- | +| SBI_SUCCESS | 字节写入成功。 | | SBI_ERR_FAILED | 由于 I/O 错误,写字节失败。 | -### 12.4. 函数列表 +### 12.4 函数列表 *表 50. DBCN 事件列表* | 函数名 | SBI 版本 | FID | EID | -|:-----------------------------|:---------|:----|:-----------| +| :--------------------------- | :------- | :-- | :--------- | | sbi_debug_console_write | 2.0 | 0 | 0x4442434E | | sbi_debug_console_read | 2.0 | 1 | 0x4442434E | | sbi_debug_console_write_byte | 2.0 | 2 | 0x4442434E | @@ -1188,20 +1223,20 @@ sbiret.value 被设置为 0,在 sbiret.error 中返回的可能的错误代码 系统挂起扩展定义了一组系统级睡眠状态和一个允许监督者模式软件请求系统过渡到睡眠状态的功能。睡眠状态由 32 位宽的标识符(sleep_type)来识别。这些标识符的可能值如表 51 所示。 -术语 "系统 "指的是监管者软件的世界观。底层的 SBI 实现可以由机器模式的固件或管理程序提供。 +术语 "系统 "指的是监督者软件的世界观。底层的 SBI 实现可以由机器模式的固件或管理程序提供。 系统挂起扩展没有为支持的睡眠类型提供任何探测方法。平台应该在其硬件描述中指定其支持的系统睡眠类型和每个类型的唤醒设备。SUSPEND_TO_RAM 睡眠类型是一个例外,它的存在是通过扩展来暗示的。 *表 51. SUSP 系统睡眠类型* -| 类型 | 名字 | 描述 | -|:----------------------|:---------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| 0 | SUSPEND_TO_RAM | 这是一个 "暂停到 RAM "的睡眠类型,类似于 ACPI 的 S2 或 S3。进入时要求除了调用的 hart 以外的所有 hart 都处于 HSM STOPPED 状态,并且所有 hart 寄存器和 CSR 都保存在 RAM 中。 | -| 0x00000001 0x7fffffff | | 保留给未来使用 | -| 0x80000000 0xffffffff | | 平台特有的系统睡眠类型 | -| > 0xffffffff | | 保留 | +| 类型 | 名字 | 描述 | +| :-------------------- | :------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 0 | SUSPEND_TO_RAM | 这是一个 "休眠到内存 "的睡眠类型,类似于 ACPI 的 S2 或 S3。进入时要求除了调用的 hart 以外的所有 hart 都处于 HSM STOPPED 状态,并且所有 hart 寄存器和 CSR 都保存在 RAM 中。 | +| 0x00000001 0x7fffffff | | 保留给未来使用 | +| 0x80000000 0xffffffff | | 平台特有的系统睡眠类型 | +| > 0xffffffff | | 保留 | -### 13.1. 函数:系统挂起 (FID #0) +### 13.1 函数:系统挂起 (FID #0) ``` struct sbiret sbi_system_suspend(uint32_t sleep_type, unsigned long resume_addr, unsigned long opaque) @@ -1211,12 +1246,12 @@ sbi_system_suspend() 调用的返回意味着一个错误,表 53 中的错误 *表 52. SUSP 系统恢复寄存器状态* -| 寄存器名称 | 寄存器值 | -|:--------------------------------------|:-----------------| -| satp | 0 | -| sstatus.SIE | 0 | -| a0 | hartid | -| a1 | opaque parameter | +| 寄存器名称 | 寄存器值 | +| :------------------------------------- | :--------------- | +| satp | 0 | +| sstatus.SIE | 0 | +| a0 | hartid | +| a1 | opaque parameter | | 所有其他的寄存器仍然处于未定义的状态。 | | > 注意:一个无符号的长参数对 resume_addr 来说就足够了,因为在 MMU 关闭的情况下,hart 将以监督者模式恢复执行,因此 resume_addr 必须小于 XLEN 位宽。 @@ -1233,58 +1268,58 @@ sbiret.error 中可能返回的错误代码见表 53。 *表 53. SUSP 系统挂起的错误* -| 错误代码 | 描述 | -|:------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------| -| SBI_SUCCESS | 系统已暂停并成功恢复。 | -| SBI_ERR_INVALID_PARAM | sleep_type 是保留的,或者是平台特定的,未实现的。 | +| 错误代码 | 描述 | +| :---------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------- | +| SBI_SUCCESS | 系统已暂停并成功恢复。 | +| SBI_ERR_INVALID_PARAM | sleep_type 是保留的,或者是平台特定的,未实现的。 | | SBI_ERR_NOT_SUPPORTED | sleep_type 没有被保留,并且已经实现,但由于一个或多个依赖项缺失,平台不支持它。 | | SBI_ERR_INVALID_ADDRESS | resume_addr 是无效的,可能是由于以下原因:1. 它不是一个有效的物理地址。2. 对该地址的可执行访问被物理内存保护机制或监督者模式的 H-扩展 G-阶段所禁止。 | -| SBI_ERR_FAILED | 暂停请求因未指明或未知的其他原因而失败。 | +| SBI_ERR_FAILED | 暂停请求因未指明或未知的其他原因而失败。 | -### 13.2. 函数列表 +### 13.2 函数列表 *表 54. SUSP 事件列表* | 函数名 | SBI 版本 | FID | EID | -|:-------------------|:---------|:----|:-----------| +| :----------------- | :------- | :-- | :--------- | | sbi_system_suspend | 2.0 | 0 | 0x53555350 | ## 章节 14. CPPC 扩展 (EID #0x43505043 "CPPC") -ACPI 定义了协作处理器性能控制(CPPC)机制,这是一个抽象而灵活的机制,用于监管者模式的电源管理软件与平台中的实体协作,管理处理器的性能。 +ACPI 定义了协作处理器性能控制(CPPC)机制,这是一个抽象而灵活的机制,用于监督者模式的电源管理软件与平台中的实体协作,管理处理器的性能。 SBI CPPC 扩展提供了一个抽象,通过 SBI 调用访问 CPPC 寄存器。CPPC 寄存器可以是与一个单独的平台实体(如 BMC)共享的内存位置。即使 CPPC 被定义在 ACPI 规范中,也可能实现一个基于 Device Tree 的 CPPC 驱动。 -表 55 定义了由 SBI CPPC 功能使用的所有 CPPC 寄存器的 32 位标识。32 位寄存器空间的前半部分与 ACPI 规范所定义的寄存器相对应。后半部分提供了 ACPI 规范中没有定义的信息,但也是监管者模式电源管理软件额外需要的。 +表 55 定义了由 SBI CPPC 功能使用的所有 CPPC 寄存器的 32 位标识。32 位寄存器空间的前半部分与 ACPI 规范所定义的寄存器相对应。后半部分提供了 ACPI 规范中没有定义的信息,但也是监督者模式电源管理软件额外需要的。 *表 55. CPPC 寄存器* -| 寄存器 ID | 寄存去 | 位宽 | 属性 | 描述 | -|:----------------------|:--------------------------------------|:--------|:-------------------------|---------------------------------------------------| -| 0x00000000 | HighestPerformance | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.1 | -| 0x00000001 | NominalPerformance | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.2 | -| 0x00000002 | LowestNonlinearPerformance | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.4 | -| 0x00000003 | LowestPerformance | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.5 | -| 0x00000004 | GuaranteedPerformanceRegister | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.6 | -| 0x00000005 | DesiredPerformanceRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.2.3 | -| 0x00000006 | MinimumPerformanceRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.2.2 | -| 0x00000007 | MaximumPerformanceRegister | 32 | Read / WriteRead / Write | ACPI Spec 6.5: 8.4.6.1.2.1 | -| 0x00000008 | PerformanceReductionToleranceRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.2.4 | -| 0x00000009 | TimeWindowRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.2.5 | -| 0x0000000A | CounterWraparoundTime | 32 / 64 | Read-only | ACPI Spec 6.5: 8.4.6.1.3.1 | -| 0x0000000B | ReferencePerformanceCounterRegister | 32 / 64 | Read-only | ACPI Spec 6.5: 8.4.6.1.3.1 | -| 0x0000000C | DeliveredPerformanceCounterRegister | 32 / 64 | Read-only | ACPI Spec 6.5: 8.4.6.1.3.1 | -| 0x0000000D | PerformanceLimitedRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.3.2 | -| 0x0000000E | CPPCEnableRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.4 | -| 0x0000000F | AutonomousSelectionEnable | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.5 | -| 0x00000010 | AutonomousActivityWindowRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.6 | -| 0x00000011 | EnergyPerformancePreferenceRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.7 | -| 0x00000012 | ReferencePerformance | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.3 | -| 0x00000013 | LowestFrequency | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.7 | -| 0x00000014 | NominalFrequency | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.7 | -| 0x00000015-0x7FFFFFFF | | 32 | | 保留给未来使用。 | -| 0x80000000 | TransitionLatency | 32 | Read-only | 提供以纳秒为单位的最大(最坏情况)性能状态转换延迟。 | -| 0x800000010xFFFFFFFF | | 32 | | 保留给未来使用。 | +| 寄存器 ID | 寄存去 | 位宽 | 属性 | 描述 | +| :---------------------- | :------------------------------------ | :------ | :----------------------- | ---------------------------------------------------- | +| 0x00000000 | HighestPerformance | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.1 | +| 0x00000001 | NominalPerformance | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.2 | +| 0x00000002 | LowestNonlinearPerformance | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.4 | +| 0x00000003 | LowestPerformance | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.5 | +| 0x00000004 | GuaranteedPerformanceRegister | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.6 | +| 0x00000005 | DesiredPerformanceRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.2.3 | +| 0x00000006 | MinimumPerformanceRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.2.2 | +| 0x00000007 | MaximumPerformanceRegister | 32 | Read / WriteRead / Write | ACPI Spec 6.5: 8.4.6.1.2.1 | +| 0x00000008 | PerformanceReductionToleranceRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.2.4 | +| 0x00000009 | TimeWindowRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.2.5 | +| 0x0000000A | CounterWraparoundTime | 32 / 64 | Read-only | ACPI Spec 6.5: 8.4.6.1.3.1 | +| 0x0000000B | ReferencePerformanceCounterRegister | 32 / 64 | Read-only | ACPI Spec 6.5: 8.4.6.1.3.1 | +| 0x0000000C | DeliveredPerformanceCounterRegister | 32 / 64 | Read-only | ACPI Spec 6.5: 8.4.6.1.3.1 | +| 0x0000000D | PerformanceLimitedRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.3.2 | +| 0x0000000E | CPPCEnableRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.4 | +| 0x0000000F | AutonomousSelectionEnable | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.5 | +| 0x00000010 | AutonomousActivityWindowRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.6 | +| 0x00000011 | EnergyPerformancePreferenceRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.7 | +| 0x00000012 | ReferencePerformance | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.3 | +| 0x00000013 | LowestFrequency | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.7 | +| 0x00000014 | NominalFrequency | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.7 | +| 0x00000015-0x7FFFFFFF | | 32 | | 保留给未来使用。 | +| 0x80000000 | TransitionLatency | 32 | Read-only | 提供以纳秒为单位的最大(最坏情况)性能状态转换延迟。 | +| 0x80000001 - 0xFFFFFFFF | | 32 | | 保留给未来使用。 | ### 14.1. 函数:探测 CPPC 寄存器 (FID #0) @@ -1300,13 +1335,13 @@ struct sbiret sbi_cppc_probe(uint32_t cppc_reg_id) *表 56. CPPC 探测错误* -| 错误代码 | 描述 | -|:----------------------|:----------------------------------------| +| 错误代码 | 描述 | +| :-------------------- | :----------------------------------------- | | SBI_SUCCESS | 探测成功完成。 | | SBI_ERR_INVALID_PARAM | cppc_reg_id 被保留。 | | SBI_ERR_FAILED | 探测请求因未指明的或未知的其他原因而失败。 | -### 14.2. 函数:读 CPPC 寄存器 (FID #1) +### 14.2 函数:读 CPPC 寄存器 (FID #1) ``` struct sbiret sbi_cppc_read(uint32_t cppc_reg_id) @@ -1318,15 +1353,15 @@ sbiret.error 中可能返回的错误代码见表 57。 *表 57. CPPC 读错误* -| 错误代码 | 描述 | -|:----------------------|:----------------------------------------| +| 错误代码 | 描述 | +| :-------------------- | :----------------------------------------- | | SBI_SUCCESS | 读取已成功完成。 | | SBI_ERR_INVALID_PARAM | cppc_reg_id 被保留。 | | SBI_ERR_NOT_SUPPORTED | cppc_reg_id 没有被平台实现。 | | SBI_ERR_DENIED | cppc_reg_id 是一个只写的寄存器。 | | SBI_ERR_FAILED | 读取请求因未指定的或未知的其他原因而失败。 | -### 14.3. 函数:读取 CPPC 寄存器高位 (FID #2) +### 14.3 函数:读取 CPPC 寄存器高位 (FID #2) ``` struct sbiret sbi_cppc_read_hi(uint32_t cppc_reg_id) @@ -1338,15 +1373,15 @@ sbiret.error 中可能返回的错误代码见表 58。 *表 58. CPPC 读高位错误* -| 错误代码 | 描述 | -|:----------------------|:----------------------------------------| +| 错误代码 | 描述 | +| :-------------------- | :----------------------------------------- | | SBI_SUCCESS | 读取已成功完成。 | | SBI_ERR_INVALID_PARAM | cppc_reg_id 被保留。 | | SBI_ERR_NOT_SUPPORTED | cppc_reg_id 没有被平台实现。 | | SBI_ERR_DENIED | cppc_reg_id 是一个只写的寄存器。 | | SBI_ERR_FAILED | 读取请求因未指定的或未知的其他原因而失败。 | -### 14.4. 函数:写 CPPC 寄存器 (FID #3) +### 14.4 函数:写 CPPC 寄存器 (FID #3) ``` struct sbiret sbi_cppc_write(uint32_t cppc_reg_id, uint64_t val) @@ -1358,20 +1393,20 @@ sbiret.error 中可能返回的错误代码见表 59。 *表 59. CPPC 写错误* -| 错误代码 | 描述 | -|:----------------------|:----------------------------------------| +| 错误代码 | 描述 | +| :-------------------- | :----------------------------------------- | | SBI_SUCCESS | 写入已成功完成。 | | SBI_ERR_INVALID_PARAM | cppc_reg_id 被保留。 | | SBI_ERR_NOT_SUPPORTED | cppc_reg_id 没有被平台实现。 | | SBI_ERR_DENIED | cppc_reg_id 是一个只读的寄存器。 | | SBI_ERR_FAILED | 读取请求因未指定的或未知的其他原因而失败。 | -### 14.5. 函数列表 +### 14.5 函数列表 *表 60. CPPC 函数列表* | 函数名 | SBI 版本 | FID | EID | -|:-----------------|:---------|:----|:-----------| +| :--------------- | :------- | :-- | :--------- | | sbi_cppc_probe | 2.0 | 0 | 0x43505043 | | sbi_cppc_read | 2.0 | 1 | 0x43505043 | | sbi_cppc_read_hi | 2.0 | 2 | 0x43505043 | @@ -1381,7 +1416,7 @@ sbiret.error 中可能返回的错误代码见表 59。 嵌套虚拟化是指一个虚拟化监控程序能够作为宿主客户机来运行另一个虚拟化监控程序的能力。RISC-V 嵌套虚拟化需要一个 L0 虚拟化监控程序(以虚拟化监控模式运行)来捕获并模拟 RISC-V H 扩展功能(如 CSR 访问、HFENCE 指令、HLV/HSV 指令等),以供 L1 虚拟化监控程序(以虚拟化监督者模式运行)使用。 -SBI 嵌套加速扩展定义了 SBI 实现(或 L0 虚拟化监控程序)与监督者模式软件(或 L1 虚拟化监控程序)之间基于共享内存的接口,可以让两者协作减少 L0 虚拟化监控程序用于模拟 RISC-V H 扩展功能的陷阱。嵌套加速共享内存允许 L1 虚拟化监控程序批量处理多个 RISC-V H 扩展 CSR 访问和 HFENCE 请求,然后通过显式同步的 SBI 调用由 L0 虚拟化监控程序进行模拟。 +SBI 嵌套加速扩展定义了 SBI 实现(或 L0 虚拟化监控程序)与监督者模式软件(或 L1 虚拟化监控程序)之间基于共享内存的接口,可以让两者协作减少 L0 虚拟化监控程序用于模拟 RISC-V H 扩展功能的陷入。嵌套加速共享内存允许 L1 虚拟化监控程序批量处理多个 RISC-V H 扩展 CSR 访问和 HFENCE 请求,然后通过显式同步的 SBI 调用由 L0 虚拟化监控程序进行模拟。 > 注意:如果底层平台已经在硬件中实现了 RISC-V H 扩展,M 模式固件不应该实现 SBI 嵌套加速扩展。 @@ -1390,7 +1425,7 @@ SBI 嵌套加速扩展定义了 SBI 实现(或 L0 虚拟化监控程序)与 *表 61. 嵌套加速功能* | 特性 ID | 特性名称 | 描述 | -|:------------|:---------------------------|:-------------| +| :---------- | :------------------------- | :------------- | | 0x00000000 | SBI_NACL_FEAT_SYNC_CSR | 同步 CSR | | 0x00000001 | SBI_NACL_FEAT_SYNC_HFENCE | 同步 HFENCE | | 0x00000002 | SBI_NACL_FEAT_SYNC_SRET | 同步 SRET | @@ -1401,10 +1436,10 @@ SBI 嵌套加速扩展定义了 SBI 实现(或 L0 虚拟化监控程序)与 *表 62. 嵌套加速的共享内存布局* -| 名称 | 偏移量 | 大小(byte) | 描述 | -|:--------------|:-----------|------------|:-------------------------------------------------------------------------------------------------------------------------| -| Scratch space | 0x00000000 | 4096 | 嵌套加速特性的具体数据。 | -| CSR space | 0x00001000 | XLEN * 128 | 一个由 1024 个 XLEN 位字组成的数组,每个字对应于 RISC-V 特权规范 [priv_v1.12] 表 2.1 中定义的可能的 RISC-V H-extension CSR。 | +| 名称 | 偏移量 | 大小(byte) | 描述 | +| :------------ | :--------- | ------------ | :--------------------------------------------------------------------------------------------------------------------------- | +| Scratch space | 0x00000000 | 4096 | 嵌套加速特性的具体数据。 | +| CSR space | 0x00001000 | XLEN * 128 | 一个由 1024 个 XLEN 位字组成的数组,每个字对应于 RISC-V 特权规范 [priv_v1.12] 表 2.1 中定义的可能的 RISC-V H-extension CSR。 | 上面表格 62 中所示的暂存空间的内容对于每个嵌套加速特性是单独定义的。 @@ -1498,7 +1533,7 @@ SBI 嵌套加速扩展定义了 SBI 实现(或 L0 虚拟化监控程序)与
H-extension CSR addressSBI NACL CSR space indexH-extension CSR addressSBI NACL CSR space index
[11:10]
-### 15.1. 特性:同步 CSR (ID #0) +### 15.1 特性:同步 CSR (ID #0) 同步 CSR 特性描述的是 SBI 实现(或 L0 虚拟化监控程序)允许监督者模式软件(或 L1 虚拟化监控程序)使用 CSR 空间来编写 RISC-V H 扩展 CSR 的能力。 @@ -1520,7 +1555,7 @@ SBI 嵌套加速扩展定义了 SBI 实现(或 L0 虚拟化监控程序)与 在同步多个 CSR 时,如果 CSR `` 的值取决于其他 CSR `` 的值,则 SBI 实现(或 L0 虚拟化监视程序)必须在 CSR `` 之前同步 CSR ``。例如,CSR hip 的值取决于 CSR hvip 的值,这意味着首先模拟和写入 hvip,然后再写入 hip。 -### 15.2. 特性:同步 HFENCE (ID #1) +### 15.2 特性:同步 HFENCE (ID #1) 对于 synchronize HFENCE 特性的描述,它说明了 SBI 实现(或 L0 虚拟化监控程序)允许监督者软件(或 L1 虚拟化监控程序)通过临时空间发出 HFENCE 指令的能力。 @@ -1530,26 +1565,26 @@ SBI 嵌套加速扩展定义了 SBI 实现(或 L0 虚拟化监控程序)与 *表 64. 嵌套的 HFENCE 条目格式* -| 字 | 名称 | 编码 | -|:---|:------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| 0 | Config | 关于嵌套 HFENCE 条目的配置信息
BIT[XLEN-1:XLEN-1] - 待定
BIT[XLEN-2:XLEN-4] - 保留,必须为零
BIT[XLEN-5:XLEN-8] - 类型
BIT[XLEN-9:XLEN-9] - 保留,必须为零
BIT[XLEN-10:XLEN-16] - 顺序
if XLEN == 32 then
BIT[15:9] - VMID
BIT[8:0] - ASID
else
BIT[29:16] - VMID
BIT[15:0] - ASID

假设失效的页面大小为 1 << (Config.Order + 12) 字节。 | -| 1 | Page_Number | 页地址右移 Config.Order + 12 位。 | -| 2 | Reserved | 保留用于将来使用,必须为零。 | -| 3 | Page_Count | 需要使无效的页面数量 | +| 字 | 名称 | 编码 | +| :- | :---------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 0 | Config | 关于嵌套 HFENCE 条目的配置信息
BIT[XLEN-1:XLEN-1] - 待定
BIT[XLEN-2:XLEN-4] - 保留,必须为零
BIT[XLEN-5:XLEN-8] - 类型
BIT[XLEN-9:XLEN-9] - 保留,必须为零
BIT[XLEN-10:XLEN-16] - 顺序
if XLEN == 32 then
    BIT[15:9] - VMID
    BIT[8:0] - ASID
else
    BIT[29:16] - VMID
    BIT[15:0] - ASID

假设失效的页面大小为 1 << (Config.Order + 12) 字节。 | +| 1 | Page_Number | 页地址右移 Config.Order + 12 位。 | +| 2 | Reserved | 保留用于将来使用,必须为零。 | +| 3 | Page_Count | 需要使无效的页面数量 | *表 65. 嵌套的 HFENCE 条目类型* -| 类型 | 名称 | 描述 | -|:-----|:--------------|:--------------------------------------------------------------------------------------------------------------------------------------------| -| 0 | GVMA | 在所有 VMID 中使一个客户机物理地址范围失效。配置字的 VMID 和 ASID 字段将被忽略且必须为零。 | +| 类型 | 名称 | 描述 | +| :--- | :------------ | :------------------------------------------------------------------------------------------------------------------------------------------------ | +| 0 | GVMA | 在所有 VMID 中使一个客户机物理地址范围失效。配置字的 VMID 和 ASID 字段将被忽略且必须为零。 | | 1 | GVMA_ALL | 使所有 VMID 中的所有客户机物理地址失效。配置字的 Order、VMID 和 ASID 字段将被忽略且必须为零。Page_Number 和 Page_Count 两个字将被忽略且必须为零。 | -| 2 | GVMA_VMID | 使特定 VMID 的客户机物理地址范围失效。配置字的 ASID 字段将被忽略且必须为零。 | -| 3 | GVMA_VMID_ALL | 使特定 VMID 的所有客户机物理地址失效。配置字的 Order 和 ASID 字段将被忽略且必须为零。Page_Number 和 Page_Count 两个字将被忽略且必须为零。 | -| 4 | VVMA | 使特定 VMID 的客户机虚拟地址范围失效。配置字的 ASID 字段将被忽略且必须为零。 | -| 5 | VVMA_ALL | 使特定 VMID 的所有客户机虚拟地址失效。配置字的 Order 和 ASID 字段将被忽略且必须为零。Page_Number 和 Page_Count 两个字将被忽略且必须为零。 | -| 6 | VVMA_ASID | 使特定 VMID 和 ASID 的客户机虚拟地址范围失效。 | -| 7 | VVMA_ASID_ALL | 使特定 VMID 和 ASID 的所有客户机虚拟地址失效。配置字的 Order 字段将被忽略且必须为零。Page_Number 和 Page_Count 两个字将被忽略且必须为零。 | -| >7 | Reserved | 保留以备将来使用。 | +| 2 | GVMA_VMID | 使特定 VMID 的客户机物理地址范围失效。配置字的 ASID 字段将被忽略且必须为零。 | +| 3 | GVMA_VMID_ALL | 使特定 VMID 的所有客户机物理地址失效。配置字的 Order 和 ASID 字段将被忽略且必须为零。Page_Number 和 Page_Count 两个字将被忽略且必须为零。 | +| 4 | VVMA | 使特定 VMID 的客户机虚拟地址范围失效。配置字的 ASID 字段将被忽略且必须为零。 | +| 5 | VVMA_ALL | 使特定 VMID 的所有客户机虚拟地址失效。配置字的 Order 和 ASID 字段将被忽略且必须为零。Page_Number 和 Page_Count 两个字将被忽略且必须为零。 | +| 6 | VVMA_ASID | 使特定 VMID 和 ASID 的客户机虚拟地址范围失效。 | +| 7 | VVMA_ASID_ALL | 使特定 VMID 和 ASID 的所有客户机虚拟地址失效。配置字的 Order 字段将被忽略且必须为零。Page_Number 和 Page_Count 两个字将被忽略且必须为零。 | +| >7 | Reserved | 保留以备将来使用。 | 要添加一个嵌套的 HFENCE 条目,监督者软件(或 L1 虚拟化监控程序)必须执行以下操作: @@ -1563,7 +1598,7 @@ SBI 嵌套加速扩展定义了 SBI 实现(或 L0 虚拟化监控程序)与 2. 根据嵌套 HFENCE 条目中的详细信息处理 HFENCE。 3. 清除嵌套 HFENCE 条目中的 Config.Pending 位。 -### 15.3. 特性:同步 SRET (ID #2) +### 15.3 特性:同步 SRET (ID #2) 同步 SRET 特性描述了 SBI 实现(或 L0 虚拟化监控程序)在嵌套加速共享内存中对监督者软件(或 L1 虚拟化监控程序)进行 CSR 和 HFENCE 同步,并进行 SRET 模拟的能力。 @@ -1571,41 +1606,41 @@ SBI 嵌套加速扩展定义了 SBI 实现(或 L0 虚拟化监控程序)与 *表 66. 嵌套的 SRET 上下文* -| 偏移 | 名称 | 编码 | -|:----------------|:---------|:-------------------------| +| 偏移 | 名称 | 编码 | +| :-------------- | :------- | :--------------------------- | | 0 * (XLEN / 8) | Reserved | 保留以备将来使用,必须为零。 | -| 1 * (XLEN / 8) | X1 | 在 GPR X1 中要恢复的值。 | -| 2 * (XLEN / 8) | X2 | 在 GPR X2 中要恢复的值。 | -| 3 * (XLEN / 8) | X3 | 在 GPR X3 中要恢复的值。 | -| 4 * (XLEN / 8) | X4 | 在 GPR X4 中要恢复的值。 | -| 5 * (XLEN / 8) | X5 | 在 GPR X5 中要恢复的值。 | -| 6 * (XLEN / 8) | X6 | 在 GPR X6 中要恢复的值。 | -| 7 * (XLEN / 8) | X7 | 在 GPR X7 中要恢复的值。 | -| 8 * (XLEN / 8) | X8 | 在 GPR X8 中要恢复的值。 | -| 9 * (XLEN / 8) | X9 | 在 GPR X9 中要恢复的值。 | -| 10 * (XLEN / 8) | X10 | 在 GPR X10 中要恢复的值。 | -| 11 * (XLEN / 8) | X11 | 在 GPR X11 中要恢复的值。 | -| 12 * (XLEN / 8) | X12 | 在 GPR X12 中要恢复的值。 | -| 13 * (XLEN / 8) | X13 | 在 GPR X13 中要恢复的值。 | -| 14 * (XLEN / 8) | X14 | 在 GPR X14 中要恢复的值。 | -| 15 * (XLEN / 8) | X15 | 在 GPR X15 中要恢复的值。 | -| 16 * (XLEN / 8) | X16 | 在 GPR X16 中要恢复的值。 | -| 17 * (XLEN / 8) | X17 | 在 GPR X17 中要恢复的值。 | -| 18 * (XLEN / 8) | X18 | 在 GPR X18 中要恢复的值。 | -| 19 * (XLEN / 8) | X19 | 在 GPR X19 中要恢复的值。 | -| 20 * (XLEN / 8) | X20 | 在 GPR X20 中要恢复的值。 | -| 21 * (XLEN / 8) | X21 | 在 GPR X21 中要恢复的值。 | -| 22 * (XLEN / 8) | X22 | 在 GPR X22 中要恢复的值。 | -| 23 * (XLEN / 8) | X23 | 在 GPR X23 中要恢复的值。 | -| 24 * (XLEN / 8) | X24 | 在 GPR X24 中要恢复的值。 | -| 25 * (XLEN / 8) | X25 | 在 GPR X25 中要恢复的值。 | -| 26 * (XLEN / 8) | X26 | 在 GPR X26 中要恢复的值。 | -| 27 * (XLEN / 8) | X27 | 在 GPR X27 中要恢复的值。 | -| 28 * (XLEN / 8) | X28 | 在 GPR X28 中要恢复的值。 | -| 29 * (XLEN / 8) | X29 | 在 GPR X29 中要恢复的值。 | -| 30 * (XLEN / 8) | X30 | 在 GPR X10 中要恢复的值。 | -| 31 * (XLEN / 8) | X31 | 在 GPR X31 中要恢复的值。 | -| 32 * (XLEN / 8) | X32 | 在 GPR X32 中要恢复的值。 | +| 1 * (XLEN / 8) | X1 | 在 GPR X1 中要恢复的值。 | +| 2 * (XLEN / 8) | X2 | 在 GPR X2 中要恢复的值。 | +| 3 * (XLEN / 8) | X3 | 在 GPR X3 中要恢复的值。 | +| 4 * (XLEN / 8) | X4 | 在 GPR X4 中要恢复的值。 | +| 5 * (XLEN / 8) | X5 | 在 GPR X5 中要恢复的值。 | +| 6 * (XLEN / 8) | X6 | 在 GPR X6 中要恢复的值。 | +| 7 * (XLEN / 8) | X7 | 在 GPR X7 中要恢复的值。 | +| 8 * (XLEN / 8) | X8 | 在 GPR X8 中要恢复的值。 | +| 9 * (XLEN / 8) | X9 | 在 GPR X9 中要恢复的值。 | +| 10 * (XLEN / 8) | X10 | 在 GPR X10 中要恢复的值。 | +| 11 * (XLEN / 8) | X11 | 在 GPR X11 中要恢复的值。 | +| 12 * (XLEN / 8) | X12 | 在 GPR X12 中要恢复的值。 | +| 13 * (XLEN / 8) | X13 | 在 GPR X13 中要恢复的值。 | +| 14 * (XLEN / 8) | X14 | 在 GPR X14 中要恢复的值。 | +| 15 * (XLEN / 8) | X15 | 在 GPR X15 中要恢复的值。 | +| 16 * (XLEN / 8) | X16 | 在 GPR X16 中要恢复的值。 | +| 17 * (XLEN / 8) | X17 | 在 GPR X17 中要恢复的值。 | +| 18 * (XLEN / 8) | X18 | 在 GPR X18 中要恢复的值。 | +| 19 * (XLEN / 8) | X19 | 在 GPR X19 中要恢复的值。 | +| 20 * (XLEN / 8) | X20 | 在 GPR X20 中要恢复的值。 | +| 21 * (XLEN / 8) | X21 | 在 GPR X21 中要恢复的值。 | +| 22 * (XLEN / 8) | X22 | 在 GPR X22 中要恢复的值。 | +| 23 * (XLEN / 8) | X23 | 在 GPR X23 中要恢复的值。 | +| 24 * (XLEN / 8) | X24 | 在 GPR X24 中要恢复的值。 | +| 25 * (XLEN / 8) | X25 | 在 GPR X25 中要恢复的值。 | +| 26 * (XLEN / 8) | X26 | 在 GPR X26 中要恢复的值。 | +| 27 * (XLEN / 8) | X27 | 在 GPR X27 中要恢复的值。 | +| 28 * (XLEN / 8) | X28 | 在 GPR X28 中要恢复的值。 | +| 29 * (XLEN / 8) | X29 | 在 GPR X29 中要恢复的值。 | +| 30 * (XLEN / 8) | X30 | 在 GPR X10 中要恢复的值。 | +| 31 * (XLEN / 8) | X31 | 在 GPR X31 中要恢复的值。 | +| 32 * (XLEN / 8) | 保存 | 保留以后使用 | 在发送同步 SRET 请求给 SBI 实现(或 L0 虚拟化监控程序)之前,监督者软件(或 L1 虚拟化监控程序)必须将要在嵌套 SRET 上下文的偏移量 `` * (XLEN / 8) 处恢复的 GPR X `` 值写入。 @@ -1618,7 +1653,7 @@ SBI 嵌套加速扩展定义了 SBI 实现(或 L0 虚拟化监控程序)与 3. 从嵌套的 SRET 上下文中恢复通用寄存器 X `` 的值。 4. 按照 RISC-V 特权规范 [priv_v1.12] 中定义的内容,模拟执行 SRET 指令。 -### 15.4. 特性:自动交换 CSR (ID #3) +### 15.4 特性:自动交换 CSR (ID #3) 自动交换 CSR 特性描述了 SBI 实现(或 L0 虚拟化监控程序)在以下情况下自动交换特定的 RISC-V H 扩展 CSR 值,这些 CSR 值位于嵌套的加速共享内存中: @@ -1631,10 +1666,10 @@ SBI 嵌套加速扩展定义了 SBI 实现(或 L0 虚拟化监控程序)与 *表 67. 嵌套的自动交换上下文* -| 偏移量 | 名称 | 编码 | -|:----------------------|:---------------|:------------------------------------------------------------------------------------| +| 偏移量 | 名称 | 编码 | +| :-------------------- | :------------- | :------------------------------------------------------------------------------------- | | 0 * (XLEN / 8) | Autoswap_Flags | 动交换标志位
BIT[XLEN-1:1] - 保留用于将来使用,必须为零
BIT[0:0] - HSTATUS | -| 1 * (XLEN / 8) | HSTATUS | 要与 HSTATUS CSR 交换的值 | +| 1 * (XLEN / 8) | HSTATUS | 要与 HSTATUS CSR 交换的值 | | 2 * (XLEN / 8) - 0x7F | Reserved | 保留以供将来使用。 | 要启用从嵌套的自动交换上下文中自动交换 CSRs,监督者软件(或 L1 虚拟化监控程序)必须执行以下操作: @@ -1646,7 +1681,7 @@ SBI 嵌套加速扩展定义了 SBI 实现(或 L0 虚拟化监控程序)与 1. 如果在嵌套的自动交换上下文中设置了 Autoswap_Flags.HSTATUS 位,则将监督者的 HSTATUS CSR 值与嵌套的自动交换上下文中的 HSTATUS 值进行交换。 -### 15.5. 函数:探头嵌套加速功能 (FID #0) +### 15.5 函数:探头嵌套加速功能 (FID #0) ``` struct sbiret sbi_nacl_probe_feature(uint32_t feature_id) @@ -1654,7 +1689,7 @@ struct sbiret sbi_nacl_probe_feature(uint32_t feature_id) 探测一个嵌套加速特性。这是 SBI 嵌套加速扩展的一个必须函数。feature_id 参数指定要探测的嵌套加速特性。表格 61 提供了可能的特性 ID 列表。该函数在 sbiret.error 中始终返回 SBI_SUCCESS。如果给定的 feature_id 不可用,则在 sbiret.value 中返回 0,如果可用,则返回 1。 -### 15.6. 函数:设置嵌套加速的共享内存 (FID #1) +### 15.6 函数:设置嵌套加速的共享内存 (FID #1) ``` struct sbiret sbi_nacl_set_shmem(unsigned long shmem_phys_lo, @@ -1674,13 +1709,13 @@ flags 参数保留供将来使用,必须为零。 *表 68. NACL 设置共享内存错误* -| 错误代码 | 描述 | -|:------------------------|:-------------------------------------------------------------------------------| +| 错误代码 | 描述 | +| :---------------------- | :-------------------------------------------------------------------------------- | | SBI_SUCCESS | 共享内存被成功地设置或清除。 | -| SBI_ERR_INVALID_PARAM | flags 参数不为零,或者 shmem_phys_lo 参数不是 4096 字节对齐的。 | +| SBI_ERR_INVALID_PARAM | flags 参数不为零,或者 shmem_phys_lo 参数不是 4096 字节对齐的。 | | SBI_ERR_INVALID_ADDRESS | 由 shmem_phys_lo 和 shmem_phys_hi 参数指定的共享内存不符合第 3.2 节中描述的要求。 | -### 15.7. 函数:同步共享内存 CSR (FID #2) +### 15.7 函数:同步共享内存 CSR (FID #2) ``` struct sbiret sbi_nacl_sync_csr(unsigned long csr_num) @@ -1696,14 +1731,14 @@ struct sbiret sbi_nacl_sync_csr(unsigned long csr_num) *表 69. NACL 同步 CSR 错误* -| 错误代码 | 描述 | -|:----------------------|:---------------------------------------------------------------------------------------------------------------------------------| -| SBI_SUCCESS | CSRs 同步成功。 | -| SBI_ERR_NOT_SUPPORTED | SBI_NACL_FEAT_SYNC_CSR 特性不可用。 | +| 错误代码 | 描述 | +| :-------------------- | :------------------------------------------------------------------------------------------------------------------------------------ | +| SBI_SUCCESS | CSRs 同步成功。 | +| SBI_ERR_NOT_SUPPORTED | SBI_NACL_FEAT_SYNC_CSR 特性不可用。 | | SBI_ERR_INVALID_PARAM | csr_num 不是全部的位数,并且要么:
* (csr_num & 0x300) != 0x200 或
* csr_num >= 0x1000 或
* csr_num 未被 SBI 实现。 | -| SBI_ERR_NO_SHMEM | 嵌套加速共享内存不可用。 | +| SBI_ERR_NO_SHMEM | 嵌套加速共享内存不可用。 | -### 15.8. 函数:同步共享内存 HFENCEs (FID #3) +### 15.8 函数:同步共享内存 HFENCEs (FID #3) ``` struct sbiret sbi_nacl_sync_hfence(unsigned long entry_index) @@ -1719,14 +1754,14 @@ struct sbiret sbi_nacl_sync_hfence(unsigned long entry_index) *表 70. NACL 同步 HFENCE 错误* -| 错误代码 | 描述 | -|:----------------------|:-------------------------------------------------------------| -| SBI_SUCCESS | HFENCEs 同步成功 | -| SBI_ERR_NOT_SUPPORTED | SBI_NACL_FEAT_SYNC_HFENCE 特性不可用 | +| 错误代码 | 描述 | +| :-------------------- | :--------------------------------------------------------------- | +| SBI_SUCCESS | HFENCEs 同步成功 | +| SBI_ERR_NOT_SUPPORTED | SBI_NACL_FEAT_SYNC_HFENCE 特性不可用 | | SBI_ERR_INVALID_PARAM | entry_index 不是全 1 位比特,并且 entry_index >= (3840 / XLEN)。 | -| SBI_ERR_NO_SHMEM | 嵌套加速共享内存不可用。 | +| SBI_ERR_NO_SHMEM | 嵌套加速共享内存不可用。 | -### 15.9. 函数:同步共享内存并模拟 SRET 指令 (FID #4) +### 15.9 函数:同步共享内存并模拟 SRET 指令 (FID #4) ``` struct sbiret sbi_nacl_sync_sret(void) @@ -1741,16 +1776,16 @@ struct sbiret sbi_nacl_sync_sret(void) *表 71. NACL 同步 SRET 错误* | 错误代码 | 描述 | -|:----------------------|:---------------------------------| +| :-------------------- | :--------------------------------- | | SBI_ERR_NOT_SUPPORTED | SBI_NACL_FEAT_SYNC_SRET 特性不可用 | -| SBI_ERR_NO_SHMEM | 嵌套加速共享内存不可用。 | +| SBI_ERR_NO_SHMEM | 嵌套加速共享内存不可用。 | -### 15.10. 函数列表 +### 15.10 函数列表 *表 72. NACL 函数列表* | 函数名 | SBI 版本 | FID | EID | -|:-----------------------|:---------|:----|:-----------| +| :--------------------- | :------- | :-- | :--------- | | sbi_nacl_probe_feature | 2.0 | 0 | 0x4E41434C | | sbi_nacl_set_shmem | 2.0 | 1 | 0x4E41434C | | sbi_nacl_sync_csr | 2.0 | 2 | 0x4E41434C | @@ -1761,9 +1796,7 @@ struct sbiret sbi_nacl_sync_sret(void) SBI 实现可能会遇到虚拟 HART 准备就绪但无法运行的情况。例如,当多个 SBI 领域共享处理器,或者 SBI 实现是一个虚拟机监视器,客户环境和其他客户环境或主机任务共享处理器时,可能会出现这些情况。当虚拟 HART 有时无法运行时,虚拟 HART 上下文中的观察者可能需要一种方式来解释比预期少的进展。虚拟 HART 准备好但必须等待的时间称为“被窃取的时间”,并且对其进行跟踪被称为窃取时间核算。窃取时间核算(STA)扩展定义了一种机制,使得 SBI 实现能够为每个虚拟 HART 向监督模式软件提供窃取时间和抢占信息。 -SBI 嵌套加速扩展在 SBI 实现(或 L0 监督者模式程序)和主管软件(或 L1 管理程序)之间定义了一个基于共享内存的接口,允许两者合作减少 L0 管理程序为模拟 RISC-V H-扩展功能而采取的陷阱。嵌套的加速共享内存允许 L1 管理程序批量处理多个 RISC-V H-extension CSR 访问和 HFENCE 请求,然后由 L0 管理程序在明确的同步 SBI 调用时进行模拟。 - -### 16.1. 函数 设置窃取时间共享内存地址 +### 16.1 函数 设置窃取时间共享内存地址 ``` struct sbiret sbi_steal_time_set_shmem(unsigned long shmem_phys_lo, unsigned long shmem_phys_hi, uint32_t flags) @@ -1785,31 +1818,31 @@ SBI 实现必须在系统复位时停止对共享内存的写入。 *表 73. STA 共享内存结构* -| 名称 | 偏移量 | 大小 | 描述 | -|:----------|:-------|:-----|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| 名称 | 偏移量 | 大小 | 描述 | +| :-------- | :----- | :--- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | sequence | 0 | 4 | SBI 实现必须在写入窃取字段之前将该字段递增为奇数值,并在写入窃取后再次递增为偶数值(即,奇数序列号表示正在进行的更新)。SBI 实现应确保序列字段只在很短的时间内保持奇数。主管模式软件必须在读取窃取字段之前和之后检查这个字段,如果它是不同的或奇数的,则重复读取。这个序列字段使在 32 位环境下执行的监督者模式软件能够读取窃取字段的值。 | -| flags | 4 | 4 | 始终为零。未来对 SBI 调用的扩展可能允许监督者模式的软件写到共享内存的一些字段。只要 SBI 调用的 flags 参数使用零值,这种扩展就不会被启用。 | -| steal | 8 | 8 | 该虚拟 HART 没有空闲和安排外出的时间,单位是纳秒。虚拟 HART 空闲的时间将不被报告为偷窃时间。 | -| preempted | 16 | 1 | 一个指示标志,指示注册了此结构的虚拟 HART 是否正在运行或停止。如果虚拟 HART 被抢占(即窃取字段在增加),SBI 实现可能会写入非零值,而在虚拟 HART 重新开始运行之前,必须写入零值。例如,监督模式软件可以使用这个标志来检查锁的持有者是否已被抢占,并在这种情况下禁用 optimistic spinning。 | -| pad | 17 | 47 | 用零填充到 64 字节的边界。 | +| flags | 4 | 4 | 始终为零。未来对 SBI 调用的扩展可能允许监督者模式的软件写到共享内存的一些字段。只要 SBI 调用的 flags 参数使用零值,这种扩展就不会被启用。 | +| steal | 8 | 8 | 该虚拟 HART 没有空闲和安排外出的时间,单位是纳秒。虚拟 HART 空闲的时间将不被报告为偷窃时间。 | +| preempted | 16 | 1 | 一个指示标志,指示注册了此结构的虚拟 HART 是否正在运行或停止。如果虚拟 HART 被抢占(即窃取字段在增加),SBI 实现可能会写入非零值,而在虚拟 HART 重新开始运行之前,必须写入零值。例如,监督模式软件可以使用这个标志来检查锁的持有者是否已被抢占,并在这种情况下禁用 optimistic spinning。 | +| pad | 17 | 47 | 用零填充到 64 字节的边界。 | sbiret.value 被设置为 0,在 sbiret.error 中可能返回的错误代码如下表 74 所示。 *表 74. STA 设置窃取时间共享内存地址错误* -| 错误代码 | 描述 | -|:------------------------|:-----------------------------------------------------------------------------------------| -| SBI_SUCCESS | 偷取时间共享内存物理基址被成功设置或清除。 | -| SBI_ERR_INVALID_PARAM | flags 参数不为零或 shmem_phys_lo 不是 64 字节对齐的。 | +| 错误代码 | 描述 | +| :---------------------- | :------------------------------------------------------------------------------------------- | +| SBI_SUCCESS | 偷取时间共享内存物理基址被成功设置或清除。 | +| SBI_ERR_INVALID_PARAM | flags 参数不为零或 shmem_phys_lo 不是 64 字节对齐的。 | | SBI_ERR_INVALID_ADDRESS | shmem_phys_lo 和 shmem_phys_hi 参数所指向的共享内存是不可写的,或者不满足 3.2 节的其他要求。 | -| SBI_ERR_FAILED | 该请求因未指明的或未知的其他原因而失败。 | +| SBI_ERR_FAILED | 该请求因未指明的或未知的其他原因而失败。 | -### 16.2. 函数列表 +### 16.2 函数列表 *表 75. STA 函数列表* | 函数名 | SBI 版本 | FID | EID | -|:-------------------------|:---------|:----|:---------| +| :----------------------- | :------- | :-- | :------- | | sbi_steal_time_set_shmem | 2.0 | 0 | 0x535441 | ## 章节 17. 实验性 SBI 扩展空间 (EIDs #0x08000000 - #0x08FFFFFF) -- Gitee From 0252c757227799f1f2c66705590f3021910b05fd Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Sat, 29 Jul 2023 08:39:52 +0000 Subject: [PATCH 06/68] sbi-specification-translation.md: commit correct result of tinycorrect-spaces Signed-off-by: Groot <1219671600@qq.com> --- .../20230710-sbi-specification-translation.md | 82 +++++++++---------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/articles/20230710-sbi-specification-translation.md b/articles/20230710-sbi-specification-translation.md index e470892..d55759c 100644 --- a/articles/20230710-sbi-specification-translation.md +++ b/articles/20230710-sbi-specification-translation.md @@ -1,11 +1,11 @@ -> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [spaces header tables]`
` -> Title: [RISC-V SBI specification](https://github.com/riscv-non-isa/riscv-sbi-doc/releases/download/v2.0-rc1/riscv-sbi.pdf) `
` -> Author: riscv.org `
` -> Translator: 刘澳 <1219671600@qq.com>`
` -> Date: 2022/07/10 `
` -> Revisor: Falcon `
` -> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux) `
` -> Acknowledgements: 山东大学 山东大学智能创新研究院 [RISC-V SBI-1.0.0 版本 中文](https://zhuanlan.zhihu.com/p/634337322) `
` +> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [spaces]
+> Title: [RISC-V SBI specification](https://github.com/riscv-non-isa/riscv-sbi-doc/releases/download/v2.0-rc1/riscv-sbi.pdf)
+> Author: riscv.org
+> Translator: 刘澳 <1219671600@qq.com>
+> Date: 2022/07/10
+> Revisor: Falcon
+> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux)
+> Acknowledgements: 山东大学 山东大学智能创新研究院 [RISC-V SBI-1.0.0 版本 中文](https://zhuanlan.zhihu.com/p/634337322)
> Sponsor: PLCT Lab, ISCAS # RISC-V SBI 翻译 @@ -734,7 +734,7 @@ _表 24. HSM Hart 挂起错误_ | SBI_ERR_INVALID_ADDRESS 无效地址 | resume_addr 无效,可能是由于以下原因:无效的物理地址或该地址被 PMP 禁止在监督者模式下运行。 | | SBI_ERR_FAILED 失败 | 挂起请求因未知原因而失败。 | -### 9.5 函数列表 +### 9.5 函数列表 _表 25. HSM 函数列表_ @@ -931,7 +931,7 @@ _表 34. PMU 固件事件_ > 注意:对于固件事件,event_data(即事件数据)未使用,所有非零的 event_data 值都保留供将来使用。 -### 11.5 函数:获取可用计数器的数量 (FID #0) +### 11.5 函数:获取可用计数器的数量 (FID #0) ``` struct sbiret sbi_pmu_num_counters() @@ -939,7 +939,7 @@ struct sbiret sbi_pmu_num_counters() **返回**可用计数器的数量到 sbiret.value 中,并始终在 sbiret.error 中返回 SBI_SUCCESS。 -### 11.6 函数:获取特定计数器的详细信息 (FID #1) +### 11.6 函数:获取特定计数器的详细信息 (FID #1) ``` struct sbiret sbi_pmu_counter_get_info(unsigned long counter_idx) @@ -965,7 +965,7 @@ _表 35. PMU 计数器获取信息错误_ | SBI_SUCCESS | 成功读取 counter_info | | SBI_ERR_INVALID_PARAM | counter_idx 指向无效的计数器。 | -### 11.7 函数:查找并配置匹配计数器 (FID #2) +### 11.7 函数:查找并配置匹配计数器 (FID #2) ``` struct sbiret sbi_pmu_counter_config_matching(unsigned long counter_idx_base, @@ -1005,7 +1005,7 @@ _表 37. PMU 计数器配置匹配错误_ | SBI_ERR_INVALID_PARAM | 计数器集合中存在无效的计数器。 | | SBI_ERR_NOT_SUPPORTED | 没有任何计数器能够监视指定的事件。 | -### 11.8 函数:启动一组计数器 (FID #3) +### 11.8 函数:启动一组计数器 (FID #3) ``` struct sbiret sbi_pmu_counter_start(unsigned long counter_idx_base, unsigned long counter_idx_mask, @@ -1033,7 +1033,7 @@ _表 39. PMU 计数器启动错误_ | SBI_ERR_INVALID_PARAM | 参数中指定的一些计数器无效。 | | SBI_ERR_ALREADY_STARTED | 参数中指定的一些计数器已被启动。 | -### 11.9 函数:停止或禁用一组计数器 (FID #4) +### 11.9 函数:停止或禁用一组计数器 (FID #4) ``` struct sbiret sbi_pmu_counter_stop(unsigned long counter_idx_base, @@ -1059,7 +1059,7 @@ _表 41. PMU 计数器停止禁用错误_ | SBI_ERR_INVALID_PARAM | 指定的某些计数器无效。 | | SBI_ERR_ALREADY_STOPPED | 指定的某些计数器已经停止。 | -### 11.10 函数:读取固件计数器 (FID #5) +### 11.10 函数:读取固件计数器 (FID #5) ``` struct sbiret sbi_pmu_counter_fw_read(unsigned long counter_idx) @@ -1075,7 +1075,7 @@ _表 42. PMU 读取固件计数错误_ | SBI_SUCCESS | 成功读取固件计数器的值。 | | SBI_ERR_INVALID_PARAM | counter_idx 指向一个硬件计数器或无效的计数器。 | -### 11.11 函数:读取固件计数器的高位 (FID #6) +### 11.11 函数:读取固件计数器的高位 (FID #6) ``` struct sbiret sbi_pmu_counter_fw_read_hi(unsigned long counter_idx) @@ -1092,7 +1092,7 @@ struct sbiret sbi_pmu_counter_fw_read_hi(unsigned long counter_idx) | SBI_SUCCESS | 固件计数器读取成功。 | | SBI_ERR_INVALID_PARAM | counter_idx 指向一个硬件计数器或一个无效的计数器。 | -### 11.12 函数:启用 PMU 快照功能 (FID #7) +### 11.12 函数:启用 PMU 快照功能 (FID #7) ``` struct sbiret sbi_pmu_snapshot_set_shmem(unsigned long shmem_phys_lo, unsigned long shmem_phys_hi) @@ -1121,7 +1121,7 @@ struct sbiret sbi_pmu_snapshot_set_shmem(unsigned long shmem_phys_lo, unsigned | SBI_SUCCESS | 固件计数器读取成功。 | | SBI_ERR_INVALID_ADDRESS | shmem_phys_lo 和 shmem_phys_hi 参数所指向的共享内存是不可写的,或者不满足 3.2 节的其他要求。 | -### 11.13 函数列表 +### 11.13 函数列表 _表 43. PMU 函数列表_ @@ -1146,7 +1146,7 @@ _表 43. PMU 函数列表_ > 注意:建议使用调试控制台扩展发送/接收的字节遵循 UTF-8 字符编码。 -### 12.1 函数:控制台写入 (FID #0) +### 12.1 函数:控制台写入 (FID #0) ``` struct sbiret sbi_debug_console_write(unsigned long num_bytes, unsigned long base_addr_lo, unsigned long base_addr_hi) @@ -1168,7 +1168,7 @@ num_bytes 参数指定了输入存储器中的字节数。输入存储器的物 | SBI_ERR_INVALID_PARAM | num_bytes、base_addr_lo 和 base_addr_hi 参数所指向的内存不满足第 3.2 节中描述的要求。 | | SBI_ERR_FAILED | 由于 I/O 错误,写入失败。 | -### 12.2 函数:控制台读取 (FID #1) +### 12.2 函数:控制台读取 (FID #1) ``` struct sbiret sbi_debug_console_read(unsigned long num_bytes, unsigned long base_addr_lo, unsigned long base_addr_hi) @@ -1190,7 +1190,7 @@ num_bytes 参数规定了可以写入输出存储器的最大字节数。输出 | SBI_ERR_INVALID_PARAM | num_bytes、base_addr_lo 和 base_addr_hi 参数所指向的内存不满足第 3.2 节中描述的要求。 | | SBI_ERR_FAILED | 由于 I/O 错误,读取失败。 | -### 12.3 函数:控制台写字节 (FID #2) +### 12.3 函数:控制台写字节 (FID #2) ``` struct sbiret sbi_debug_console_write_byte(uint8_t byte) @@ -1209,7 +1209,7 @@ sbiret.value 被设置为 0,在 sbiret.error 中返回的可能的错误代码 | SBI_SUCCESS | 字节写入成功。 | | SBI_ERR_FAILED | 由于 I/O 错误,写字节失败。 | -### 12.4 函数列表 +### 12.4 函数列表 *表 50. DBCN 事件列表* @@ -1236,7 +1236,7 @@ sbiret.value 被设置为 0,在 sbiret.error 中返回的可能的错误代码 | 0x80000000 0xffffffff | | 平台特有的系统睡眠类型 | | > 0xffffffff | | 保留 | -### 13.1 函数:系统挂起 (FID #0) +### 13.1 函数:系统挂起 (FID #0) ``` struct sbiret sbi_system_suspend(uint32_t sleep_type, unsigned long resume_addr, unsigned long opaque) @@ -1276,7 +1276,7 @@ sbiret.error 中可能返回的错误代码见表 53。 | SBI_ERR_INVALID_ADDRESS | resume_addr 是无效的,可能是由于以下原因:1. 它不是一个有效的物理地址。2. 对该地址的可执行访问被物理内存保护机制或监督者模式的 H-扩展 G-阶段所禁止。 | | SBI_ERR_FAILED | 暂停请求因未指明或未知的其他原因而失败。 | -### 13.2 函数列表 +### 13.2 函数列表 *表 54. SUSP 事件列表* @@ -1341,7 +1341,7 @@ struct sbiret sbi_cppc_probe(uint32_t cppc_reg_id) | SBI_ERR_INVALID_PARAM | cppc_reg_id 被保留。 | | SBI_ERR_FAILED | 探测请求因未指明的或未知的其他原因而失败。 | -### 14.2 函数:读 CPPC 寄存器 (FID #1) +### 14.2 函数:读 CPPC 寄存器 (FID #1) ``` struct sbiret sbi_cppc_read(uint32_t cppc_reg_id) @@ -1361,7 +1361,7 @@ sbiret.error 中可能返回的错误代码见表 57。 | SBI_ERR_DENIED | cppc_reg_id 是一个只写的寄存器。 | | SBI_ERR_FAILED | 读取请求因未指定的或未知的其他原因而失败。 | -### 14.3 函数:读取 CPPC 寄存器高位 (FID #2) +### 14.3 函数:读取 CPPC 寄存器高位 (FID #2) ``` struct sbiret sbi_cppc_read_hi(uint32_t cppc_reg_id) @@ -1381,7 +1381,7 @@ sbiret.error 中可能返回的错误代码见表 58。 | SBI_ERR_DENIED | cppc_reg_id 是一个只写的寄存器。 | | SBI_ERR_FAILED | 读取请求因未指定的或未知的其他原因而失败。 | -### 14.4 函数:写 CPPC 寄存器 (FID #3) +### 14.4 函数:写 CPPC 寄存器 (FID #3) ``` struct sbiret sbi_cppc_write(uint32_t cppc_reg_id, uint64_t val) @@ -1401,7 +1401,7 @@ sbiret.error 中可能返回的错误代码见表 59。 | SBI_ERR_DENIED | cppc_reg_id 是一个只读的寄存器。 | | SBI_ERR_FAILED | 读取请求因未指定的或未知的其他原因而失败。 | -### 14.5 函数列表 +### 14.5 函数列表 *表 60. CPPC 函数列表* @@ -1533,7 +1533,7 @@ SBI 嵌套加速扩展定义了 SBI 实现(或 L0 虚拟化监控程序)与 -### 15.1 特性:同步 CSR (ID #0) +### 15.1 特性:同步 CSR (ID #0) 同步 CSR 特性描述的是 SBI 实现(或 L0 虚拟化监控程序)允许监督者模式软件(或 L1 虚拟化监控程序)使用 CSR 空间来编写 RISC-V H 扩展 CSR 的能力。 @@ -1555,7 +1555,7 @@ SBI 嵌套加速扩展定义了 SBI 实现(或 L0 虚拟化监控程序)与 在同步多个 CSR 时,如果 CSR `` 的值取决于其他 CSR `` 的值,则 SBI 实现(或 L0 虚拟化监视程序)必须在 CSR `` 之前同步 CSR ``。例如,CSR hip 的值取决于 CSR hvip 的值,这意味着首先模拟和写入 hvip,然后再写入 hip。 -### 15.2 特性:同步 HFENCE (ID #1) +### 15.2 特性:同步 HFENCE (ID #1) 对于 synchronize HFENCE 特性的描述,它说明了 SBI 实现(或 L0 虚拟化监控程序)允许监督者软件(或 L1 虚拟化监控程序)通过临时空间发出 HFENCE 指令的能力。 @@ -1567,7 +1567,7 @@ SBI 嵌套加速扩展定义了 SBI 实现(或 L0 虚拟化监控程序)与 | 字 | 名称 | 编码 | | :- | :---------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| 0 | Config | 关于嵌套 HFENCE 条目的配置信息
BIT[XLEN-1:XLEN-1] - 待定
BIT[XLEN-2:XLEN-4] - 保留,必须为零
BIT[XLEN-5:XLEN-8] - 类型
BIT[XLEN-9:XLEN-9] - 保留,必须为零
BIT[XLEN-10:XLEN-16] - 顺序
if XLEN == 32 then
    BIT[15:9] - VMID
    BIT[8:0] - ASID
else
    BIT[29:16] - VMID
    BIT[15:0] - ASID

假设失效的页面大小为 1 << (Config.Order + 12) 字节。 | +| 0 | Config | 关于嵌套 HFENCE 条目的配置信息
BIT[XLEN-1:XLEN-1] - 待定
BIT[XLEN-2:XLEN-4] - 保留,必须为零
BIT[XLEN-5:XLEN-8] - 类型
BIT[XLEN-9:XLEN-9] - 保留,必须为零
BIT[XLEN-10:XLEN-16] - 顺序
if XLEN == 32 then
BIT[15:9] - VMID
BIT[8:0] - ASID
else
BIT[29:16] - VMID
BIT[15:0] - ASID

假设失效的页面大小为 1 << (Config.Order + 12) 字节。 | | 1 | Page_Number | 页地址右移 Config.Order + 12 位。 | | 2 | Reserved | 保留用于将来使用,必须为零。 | | 3 | Page_Count | 需要使无效的页面数量 | @@ -1598,7 +1598,7 @@ SBI 嵌套加速扩展定义了 SBI 实现(或 L0 虚拟化监控程序)与 2. 根据嵌套 HFENCE 条目中的详细信息处理 HFENCE。 3. 清除嵌套 HFENCE 条目中的 Config.Pending 位。 -### 15.3 特性:同步 SRET (ID #2) +### 15.3 特性:同步 SRET (ID #2) 同步 SRET 特性描述了 SBI 实现(或 L0 虚拟化监控程序)在嵌套加速共享内存中对监督者软件(或 L1 虚拟化监控程序)进行 CSR 和 HFENCE 同步,并进行 SRET 模拟的能力。 @@ -1653,7 +1653,7 @@ SBI 嵌套加速扩展定义了 SBI 实现(或 L0 虚拟化监控程序)与 3. 从嵌套的 SRET 上下文中恢复通用寄存器 X `` 的值。 4. 按照 RISC-V 特权规范 [priv_v1.12] 中定义的内容,模拟执行 SRET 指令。 -### 15.4 特性:自动交换 CSR (ID #3) +### 15.4 特性:自动交换 CSR (ID #3) 自动交换 CSR 特性描述了 SBI 实现(或 L0 虚拟化监控程序)在以下情况下自动交换特定的 RISC-V H 扩展 CSR 值,这些 CSR 值位于嵌套的加速共享内存中: @@ -1681,7 +1681,7 @@ SBI 嵌套加速扩展定义了 SBI 实现(或 L0 虚拟化监控程序)与 1. 如果在嵌套的自动交换上下文中设置了 Autoswap_Flags.HSTATUS 位,则将监督者的 HSTATUS CSR 值与嵌套的自动交换上下文中的 HSTATUS 值进行交换。 -### 15.5 函数:探头嵌套加速功能 (FID #0) +### 15.5 函数:探头嵌套加速功能 (FID #0) ``` struct sbiret sbi_nacl_probe_feature(uint32_t feature_id) @@ -1689,7 +1689,7 @@ struct sbiret sbi_nacl_probe_feature(uint32_t feature_id) 探测一个嵌套加速特性。这是 SBI 嵌套加速扩展的一个必须函数。feature_id 参数指定要探测的嵌套加速特性。表格 61 提供了可能的特性 ID 列表。该函数在 sbiret.error 中始终返回 SBI_SUCCESS。如果给定的 feature_id 不可用,则在 sbiret.value 中返回 0,如果可用,则返回 1。 -### 15.6 函数:设置嵌套加速的共享内存 (FID #1) +### 15.6 函数:设置嵌套加速的共享内存 (FID #1) ``` struct sbiret sbi_nacl_set_shmem(unsigned long shmem_phys_lo, @@ -1715,7 +1715,7 @@ flags 参数保留供将来使用,必须为零。 | SBI_ERR_INVALID_PARAM | flags 参数不为零,或者 shmem_phys_lo 参数不是 4096 字节对齐的。 | | SBI_ERR_INVALID_ADDRESS | 由 shmem_phys_lo 和 shmem_phys_hi 参数指定的共享内存不符合第 3.2 节中描述的要求。 | -### 15.7 函数:同步共享内存 CSR (FID #2) +### 15.7 函数:同步共享内存 CSR (FID #2) ``` struct sbiret sbi_nacl_sync_csr(unsigned long csr_num) @@ -1738,7 +1738,7 @@ struct sbiret sbi_nacl_sync_csr(unsigned long csr_num) | SBI_ERR_INVALID_PARAM | csr_num 不是全部的位数,并且要么:
* (csr_num & 0x300) != 0x200 或
* csr_num >= 0x1000 或
* csr_num 未被 SBI 实现。 | | SBI_ERR_NO_SHMEM | 嵌套加速共享内存不可用。 | -### 15.8 函数:同步共享内存 HFENCEs (FID #3) +### 15.8 函数:同步共享内存 HFENCEs (FID #3) ``` struct sbiret sbi_nacl_sync_hfence(unsigned long entry_index) @@ -1761,7 +1761,7 @@ struct sbiret sbi_nacl_sync_hfence(unsigned long entry_index) | SBI_ERR_INVALID_PARAM | entry_index 不是全 1 位比特,并且 entry_index >= (3840 / XLEN)。 | | SBI_ERR_NO_SHMEM | 嵌套加速共享内存不可用。 | -### 15.9 函数:同步共享内存并模拟 SRET 指令 (FID #4) +### 15.9 函数:同步共享内存并模拟 SRET 指令 (FID #4) ``` struct sbiret sbi_nacl_sync_sret(void) @@ -1780,7 +1780,7 @@ struct sbiret sbi_nacl_sync_sret(void) | SBI_ERR_NOT_SUPPORTED | SBI_NACL_FEAT_SYNC_SRET 特性不可用 | | SBI_ERR_NO_SHMEM | 嵌套加速共享内存不可用。 | -### 15.10 函数列表 +### 15.10 函数列表 *表 72. NACL 函数列表* @@ -1796,7 +1796,7 @@ struct sbiret sbi_nacl_sync_sret(void) SBI 实现可能会遇到虚拟 HART 准备就绪但无法运行的情况。例如,当多个 SBI 领域共享处理器,或者 SBI 实现是一个虚拟机监视器,客户环境和其他客户环境或主机任务共享处理器时,可能会出现这些情况。当虚拟 HART 有时无法运行时,虚拟 HART 上下文中的观察者可能需要一种方式来解释比预期少的进展。虚拟 HART 准备好但必须等待的时间称为“被窃取的时间”,并且对其进行跟踪被称为窃取时间核算。窃取时间核算(STA)扩展定义了一种机制,使得 SBI 实现能够为每个虚拟 HART 向监督模式软件提供窃取时间和抢占信息。 -### 16.1 函数 设置窃取时间共享内存地址 +### 16.1 函数 设置窃取时间共享内存地址 ``` struct sbiret sbi_steal_time_set_shmem(unsigned long shmem_phys_lo, unsigned long shmem_phys_hi, uint32_t flags) @@ -1837,7 +1837,7 @@ sbiret.value 被设置为 0,在 sbiret.error 中可能返回的错误代码如 | SBI_ERR_INVALID_ADDRESS | shmem_phys_lo 和 shmem_phys_hi 参数所指向的共享内存是不可写的,或者不满足 3.2 节的其他要求。 | | SBI_ERR_FAILED | 该请求因未指明的或未知的其他原因而失败。 | -### 16.2 函数列表 +### 16.2 函数列表 *表 75. STA 函数列表* -- Gitee From 8ce0b814167a86726c13b3c076440aeea634f9e0 Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Wed, 2 Aug 2023 02:55:59 +0000 Subject: [PATCH 07/68] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../20230710-sbi-specification-translation.md | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/articles/20230710-sbi-specification-translation.md b/articles/20230710-sbi-specification-translation.md index d55759c..ed6985c 100644 --- a/articles/20230710-sbi-specification-translation.md +++ b/articles/20230710-sbi-specification-translation.md @@ -1,11 +1,11 @@ -> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [spaces]
-> Title: [RISC-V SBI specification](https://github.com/riscv-non-isa/riscv-sbi-doc/releases/download/v2.0-rc1/riscv-sbi.pdf)
-> Author: riscv.org
-> Translator: 刘澳 <1219671600@qq.com>
-> Date: 2022/07/10
-> Revisor: Falcon
-> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux)
-> Acknowledgements: 山东大学 山东大学智能创新研究院 [RISC-V SBI-1.0.0 版本 中文](https://zhuanlan.zhihu.com/p/634337322)
+> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [spaces]`
` +> Title: [RISC-V SBI specification](https://github.com/riscv-non-isa/riscv-sbi-doc/releases/download/v2.0-rc1/riscv-sbi.pdf)`
` +> Author: riscv.org`
` +> Translator: 刘澳 <1219671600@qq.com>`
` +> Date: 2022/07/10`
` +> Revisor: Falcon `
` +> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux)`
` +> Acknowledgements: 山东大学 山东大学智能创新研究院 [RISC-V SBI-1.0.0 版本 中文](https://zhuanlan.zhihu.com/p/634337322)`
` > Sponsor: PLCT Lab, ISCAS # RISC-V SBI 翻译 @@ -16,8 +16,6 @@ 在这里非常感谢山东大学以及山东大学智能创新研究院,他们之前翻译了 SBI Spec 1.0 版本,我的这次翻译是在他们的基础上完成的。在与原翻译人员通过邮件取得沟通后,他们非常慷慨地同意了我使用他们的翻译成果做为基础来进行我的工作。他们的翻译成果发表在 [RISC-V SBI-1.0.0 版本 中文](https://zhuanlan.zhihu.com/p/634337322)。 -在这里非常感谢山东大学以及山东大学智能创新研究院,他们之前翻译了 SBI Spec 1.0 版本,我的工作是在他们的基础上完成的。在与原翻译人员通过邮件取得沟通后,他们非常慷慨的同意了我使用他们的工作做为基础来进行我的工作。 - 本次翻译相较于前一版本,主要对 SBI Spec 2.0-rc1 版本的新增内容(见“更改日志”一节)进行了翻译,以及对原翻译的格式进行了一些调整,对一些术语进行了统一。 ## 更改日志 @@ -1123,7 +1121,7 @@ struct sbiret sbi_pmu_snapshot_set_shmem(unsigned long shmem_phys_lo, unsigned ### 11.13 函数列表 -_表 43. PMU 函数列表_ +_表 46. PMU 函数列表_ | 函数名 | SBI 版本 | FID | EID | | :------------------------------ | -------- | --- | -------- | @@ -1565,12 +1563,16 @@ SBI 嵌套加速扩展定义了 SBI 实现(或 L0 虚拟化监控程序)与 *表 64. 嵌套的 HFENCE 条目格式* -| 字 | 名称 | 编码 | -| :- | :---------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| 0 | Config | 关于嵌套 HFENCE 条目的配置信息
BIT[XLEN-1:XLEN-1] - 待定
BIT[XLEN-2:XLEN-4] - 保留,必须为零
BIT[XLEN-5:XLEN-8] - 类型
BIT[XLEN-9:XLEN-9] - 保留,必须为零
BIT[XLEN-10:XLEN-16] - 顺序
if XLEN == 32 then
BIT[15:9] - VMID
BIT[8:0] - ASID
else
BIT[29:16] - VMID
BIT[15:0] - ASID

假设失效的页面大小为 1 << (Config.Order + 12) 字节。 | -| 1 | Page_Number | 页地址右移 Config.Order + 12 位。 | -| 2 | Reserved | 保留用于将来使用,必须为零。 | -| 3 | Page_Count | 需要使无效的页面数量 | +| 字 | 名称 | 编码 | +| :- | :---------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 0 | Config | 关于嵌套 HFENCE 条目的配置信息
BIT[XLEN-1:XLEN-1] - 待定
BIT[XLEN-2:XLEN-4] - 保留,必须为零
BIT[XLEN-5:XLEN-8] - 类型
BIT[XLEN-9:XLEN-9] - 保留,必须为零
BIT[XLEN-10:XLEN-16] - 顺序
if XLEN == 32 then
+ BIT[15:9] - VMID
+ BIT[8:0] - ASID
else
+ BIT[29:16] - VMID
+ BIT[15:0] - ASID

假设失效的页面大小为 1 << (Config.Order + 12) 字节。 | +| 1 | Page_Number | 页地址右移 Config.Order + 12 位。 | +| 2 | Reserved | 保留用于将来使用,必须为零。 | +| 3 | Page_Count | 需要使无效的页面数量 | *表 65. 嵌套的 HFENCE 条目类型* -- Gitee From 120f37b97e1612c7886376caa6c19b90720e0ade Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Wed, 2 Aug 2023 02:56:36 +0000 Subject: [PATCH 08/68] sbi-specification-translation.md: commit correct result of tinycorrect-spaces Signed-off-by: Groot <1219671600@qq.com> --- .../20230710-sbi-specification-translation.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/articles/20230710-sbi-specification-translation.md b/articles/20230710-sbi-specification-translation.md index ed6985c..ef47efc 100644 --- a/articles/20230710-sbi-specification-translation.md +++ b/articles/20230710-sbi-specification-translation.md @@ -1,11 +1,11 @@ -> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [spaces]`
` -> Title: [RISC-V SBI specification](https://github.com/riscv-non-isa/riscv-sbi-doc/releases/download/v2.0-rc1/riscv-sbi.pdf)`
` -> Author: riscv.org`
` -> Translator: 刘澳 <1219671600@qq.com>`
` -> Date: 2022/07/10`
` -> Revisor: Falcon `
` -> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux)`
` -> Acknowledgements: 山东大学 山东大学智能创新研究院 [RISC-V SBI-1.0.0 版本 中文](https://zhuanlan.zhihu.com/p/634337322)`
` +> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [spaces]
+> Title: [RISC-V SBI specification](https://github.com/riscv-non-isa/riscv-sbi-doc/releases/download/v2.0-rc1/riscv-sbi.pdf)
+> Author: riscv.org
+> Translator: 刘澳 <1219671600@qq.com>
+> Date: 2022/07/10
+> Revisor: Falcon
+> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux)
+> Acknowledgements: 山东大学 山东大学智能创新研究院 [RISC-V SBI-1.0.0 版本 中文](https://zhuanlan.zhihu.com/p/634337322)
> Sponsor: PLCT Lab, ISCAS # RISC-V SBI 翻译 -- Gitee From 1f5cff191c10441bca753802d9cedfcb3788daa0 Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Wed, 2 Aug 2023 06:50:43 +0000 Subject: [PATCH 09/68] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20SBI=20=E5=9B=BA?= =?UTF-8?q?=E4=BB=B6=E5=88=86=E6=9E=90=E4=BB=A3=E7=A0=81=EF=BC=88=E4=B8=80?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- articles/20230728-sbi-firemware-analyze-1.md | 1068 ++++++++++++++++++ 1 file changed, 1068 insertions(+) create mode 100644 articles/20230728-sbi-firemware-analyze-1.md diff --git a/articles/20230728-sbi-firemware-analyze-1.md b/articles/20230728-sbi-firemware-analyze-1.md new file mode 100644 index 0000000..8ec6407 --- /dev/null +++ b/articles/20230728-sbi-firemware-analyze-1.md @@ -0,0 +1,1068 @@ +> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.1 - [spaces tables]`
` +> Author: groot `
` +> Date: 2023/07/28 `
` +> Revisor: Falcon [falcon@tinylab.org](mailto:falcon@tinylab.org) `
` +> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux) `
` +> Proposal: [RISC-V Linux 内核 SBI 调用技术分析](https://gitee.com/tinylab/riscv-linux/issues/I64YC4) `
` +> Sponsor: PLCT Lab, ISCAS + +# OpenSBI 固件代码分析(一) + +## 前言 + +之前的文章中给大家介绍了一下 SBI 和 OpenSBI,不过并没有特别深入的分析 OpenSBI 的整个流程。接下来为大家,我将带领读者从源码开始剖析,分析 OpenSBI 的启动流程,逐渐深入的学习 OpenSBI。 + +该篇文章首先带领读者阅读 OpenSBI 的固件源码部分,我们将从 `firmware/fw_base.S` 开始,探索 OpenSBI 的世界。 + +## 三种固件类型 + +该内容链接到[《QEMU 启动方式分析(4): OpenSBI 固件分析与 SBI 规范的 HSM 扩展》](https://tinylab.org/opensbi-firmware-and-sbi-hsm/)的第四部分,这里不做过多的赘述。 + +简而言之就是下面的内容: + +* FW_PAYLOAD 类型固件打包了二进制文件和固件,适用于无法同时加载 OpenSBI 和 Runtime 的下一引导阶段。 +* FW_JUMP 类型固件能够跳转到下一引导阶段的入口,需要在编译时指定下一引导阶段要加载的地址。 +* FW_DYNAMIC 类型固件可以从上一引导阶段获得 Runtime 的下一个入口地址,无需在编译时指定。使用 struct fw_dynamic_info 结构体提供信息。 + +如果想指定某个启动方式,可以在编译的时候使用 `PLATFORM` 参数进行指定。 + +```bash +make PLATFORM= +``` + +如果使用 `FW_PAYLOAD` 方式,可以在编译的时候使用 `FW_PAYLOAD_PATH` 进行指定。 + +```shell +make PLATFORM= FW_PAYLOAD_PATH= +``` + +同时,选择 `FW_OPTIONS` 来控制 `OpenSBI` 在运行时是否输出,具体的 `options` 支持可以在文件 `include/sbi/sbi_scratch.h` 代码 `enum sbi_scratch_options` 中查看。 + +```bash +make PLATFORM= FW_OPTIONS= +``` + +## RISC-V 汇编预知识 + +### 通用寄存器 + +risc-v 有 32 个通用寄存器(简写 reg),标号为 `x0` - `x31` + +| 寄存器 | 编程接口名称 (ABI) | 描述 | 使用 | +| ------ | --------------------- | -------------------------------- | ------------------------------------ | +| x0 | zero | Hard-wired zero | 硬件零 | +| x1 | ra | Return address | 常用于保存(函数的)返回地址 | +| x2 | sp | Stack pointer | 栈顶指针 | +| x3 | gp | Global pointer | — | +| x4 | tp | Thread pointer | — | +| x5-7 | t0-2 | Temporary | 临时寄存器 | +| x8 | s0/fp | Saved Register/ Frame pointer | (函数调用时)保存的寄存器和栈顶指针 | +| x9 | s1 | Saved register | (函数调用时)保存的寄存器 | +| x10-11 | a0-1 | Function argument/ return value | (函数调用时)的参数/函数的返回值 | +| x12-17 | a2-7 | Function argument | (函数调用时)的参数 | +| x18-27 | s2-11 | Saved register | (函数调用时)保存的寄存器 | +| x28-31 | t3-6 | Temporary | 临时寄存器 | + +### 指令格式 + +RISC-V 指令具有六种基本指令格式: + +* R 类型指令:用于寄存器 - 寄存器操作; +* I 类型指令:用于短立即数和访存 load 操作; +* S 类型指令:用于访存 store 操作; +* B 类型指令:用于条件跳转操作; +* U 类型指令:用于长立即数操作; +* J 类型指令:用于无条件操作; + +![RISC-V 指令格式](http://imgtec.eetrend.com/files/2021-09/%E5%8D%9A%E5%AE%A2/100553533-219776-x1.png) + +### 常见指令 + +这里不再花费篇幅给大家介绍常见指令,大家可以通过互联网学习相关内容。 + +对于初学者,我推荐可以先从[《rvbook》](https://github.com/Lingrui98/RISC-V-book/blob/master/rvbook.pdf)开始学起。 + +### 汇编指示符 + +RISC-V 的汇编指示符和作用如下 + +| 指示符 | 作用 | +| :--------------- | :------------------------------------------------------------------------------------ | +| .text | 代码段,之后跟的符号都在.text内 | +| .data | 数据段,之后跟的符号都在.data内 | +| .bss | 未初始化数据段,之后跟的符号都在.bss中 | +| .section .foo | 自定义段,之后跟的符号都在.foo段中,.foo段名可以做修改 | +| .align n | 按2的n次幂字节对齐 | +| .balign n | 按n字节对齐 | +| .globl sym | 声明sym未全局符号,其它文件可以访问 | +| .string “str” | 将字符串str放入内存 | +| .byte b1,…,bn | 在内存中连续存储n个单字节 | +| .half w1,…,wn | 在内存中连续存储n个半字(2字节) | +| .word w1,…,wn | 在内存中连续存储n个字(4字节) | +| .dword w1,…,wn | 在内存中连续存储n个双字(8字节) | +| .float f1,…,fn | 在内存中连续存储n个单精度浮点数 | +| .double d1,…,dn | 在内存中连续存储n个双精度浮点数 | +| .option rvc | 使用压缩指令(risc-v c) | +| .option norvc | 不压缩指令 | +| .option relax | 允许链接器松弛(linker relaxation,链接时多次扫描代码,尽可能将跳转两条指令替换为一条) | +| .option norelax | 不允许链接松弛 | +| .option pic | 与位置无关代码段 | +| .option nopic | 与位置有关代码段 | +| .option push | 将所有.option设置存入栈 | +| .option pop | 从栈中弹出上次存入的.option设置 | + +## fw_base + +该文件中的代码是整个 OpenSBI 的起点,我们理解 OpenSBI 的代码就从这里开始吧! + +首先给大家介绍文件中的一部分代码,将这些代码的作用从宏观上给大家介绍一下, 然后再深入代码,将注释打在代码中,方便大家理解。 + +### 代码意义 + +该段代码首先执行启动函数,在这个过程中通过之前规定的启动方式(fw_payload, fw_jump, fw_dynamic)找到一个启动核。如果规定了 `FW_PIC = y`,意味着将生成位置无关的固件映像,代码将进行一些重定位工作。 + +```asm +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel + */ + +#include +#include +#include +#include +#include +#include + +#define BOOT_STATUS_RELOCATE_DONE 1 +#define BOOT_STATUS_BOOT_HART_DONE 2 + +.macro MOV_3R __d0, __s0, __d1, __s1, __d2, __s2 + add \__d0, \__s0, zero + add \__d1, \__s1, zero + add \__d2, \__s2, zero +.endm + +.macro MOV_5R __d0, __s0, __d1, __s1, __d2, __s2, __d3, __s3, __d4, __s4 + add \__d0, \__s0, zero + add \__d1, \__s1, zero + add \__d2, \__s2, zero + add \__d3, \__s3, zero + add \__d4, \__s4, zero +.endm + +/* + * If __start_reg <= __check_reg and __check_reg < __end_reg then + * jump to __pass + */ +.macro BRANGE __start_reg, __end_reg, __check_reg, __jump_lable + blt \__check_reg, \__start_reg, 999f + bge \__check_reg, \__end_reg, 999f + j \__jump_lable +999: +.endm + + .section .entry, "ax", %progbits + .align 3 + .globl _start + .globl _start_warm + +// 开始 +_start: + /* Find preferred boot HART id */ +# 伪代码,见第 26 行 + MOV_3R s0, a0, s1, a1, s2, a2 +# 调用 fw_boot_hart 函数 +# fw_payload 和 fw_jump 返回 -1 +# fw_danymic 返回指定数字,设置为指定核心启动 + call fw_boot_hart + add a6, a0, zero +# 伪代码,见第 20 行 + MOV_3R a0, s0, a1, s1, a2, s2 +# 下面的 66 - 70 行代码以多核的角度看 +# 每个核都要执行这些所有代码,没有符合条件的核心进入 _wait_relocate_copy_done 等待 +# 判断 a6 是否是 -1 +# 如果是 -1,调用 _try_lottery 函数随机产生一个启动核心 + li a7, -1 + beq a6, a7, _try_lottery + /* Jump to relocation wait loop if we are not boot hart */ +# 如果不是 -1 意味着指定了启动核心, + bne a0, a6, _wait_relocate_copy_done + +# ,前面的 _try_lottery 只能有一个核心获得 lottery,其他没有获取的 +_try_lottery: + /* Jump to relocation wait loop if we don't get relocation lottery */ + lla a6, _relocate_lottery + li a7, 1 +# 原子操作 +# a6指向的地址上的值(_relocate_lottery的地址)做原子加1,_relocate_lottery的老值写入a6。 + amoadd.w a6, a7, (a6) +# _relocate_lottery不等于0,就跳到boot hart做完重定位的地方。 +# 如果多个核一起启动执行,只有最先执行上面原子加指令的核的a6(_relocate_lottery初始值)是0, +# 所以,后续执行到这里的核都是从核,直接跳到重定位完成的地址。 + bnez a6, _wait_relocate_copy_done + + /* Save load address */ + lla t0, _load_start + lla t1, _fw_start + REG_S t1, 0(t0) +# "FW_PIC=y" 生成位置无关的可执行固件映像。 +# OpenSBI 可以在任意地址上以适当的对齐方式运行。 +# 因此,原始的重定位机制("FW_PIC=n")将被跳过。 +# 换句话说,OpenSBI 将直接在加载地址上运行,而不需要进行任何代码移动。 +# 这个选项需要支持 PIE(位置无关执行)的工具链,并且默认情况下开启。 +# 下面的代码一直到 #endif 都不需要特别关注 +#ifdef FW_PIC + /* relocate the global table content */ + lla t0, _link_start + REG_L t0, 0(t0) + /* t1 shall has the address of _fw_start */ + sub t2, t1, t0 + lla t3, _runtime_offset + REG_S t2, (t3) + lla t0, __rel_dyn_start + lla t1, __rel_dyn_end + beq t0, t1, _relocate_done +2: + REG_L t5, REGBYTES(t0) /* t5 <-- relocation info:type */ + li t3, R_RISCV_RELATIVE /* reloc type R_RISCV_RELATIVE */ + bne t5, t3, 3f + REG_L t3, 0(t0) + REG_L t5, (REGBYTES * 2)(t0) /* t5 <-- addend */ + add t5, t5, t2 + add t3, t3, t2 + REG_S t5, 0(t3) /* store runtime address to the GOT entry */ + j 5f + +3: + lla t4, __dyn_sym_start + +4: + srli t6, t5, SYM_INDEX /* t6 <--- sym table index */ + andi t5, t5, 0xFF /* t5 <--- relocation type */ + li t3, RELOC_TYPE + bne t5, t3, 5f + + /* address R_RISCV_64 or R_RISCV_32 cases*/ + REG_L t3, 0(t0) + li t5, SYM_SIZE + mul t6, t6, t5 + add s5, t4, t6 + REG_L t6, (REGBYTES * 2)(t0) /* t0 <-- addend */ + REG_L t5, REGBYTES(s5) + add t5, t5, t6 + add t5, t5, t2 /* t5 <-- location to fix up in RAM */ + add t3, t3, t2 /* t3 <-- location to fix up in RAM */ + REG_S t5, 0(t3) /* store runtime address to the variable */ + +5: + addi t0, t0, (REGBYTES * 3) + blt t0, t1, 2b + j _relocate_done +_wait_relocate_copy_done: + j _wait_for_boot_hart +#else + /* Relocate if load address != link address */ +_relocate: + lla t0, _link_start + REG_L t0, 0(t0) + lla t1, _link_end + REG_L t1, 0(t1) + lla t2, _load_start + REG_L t2, 0(t2) + beq t0, t2, _relocate_done + sub t3, t1, t0 + add t3, t3, t2 + lla t4, _relocate_done + sub t4, t4, t2 + add t4, t4, t0 + blt t2, t0, _relocate_copy_to_upper +_relocate_copy_to_lower: + ble t1, t2, _relocate_copy_to_lower_loop + lla t3, _relocate_lottery + BRANGE t2, t1, t3, _start_hang + lla t3, _boot_status + BRANGE t2, t1, t3, _start_hang + lla t3, _relocate + lla t5, _relocate_done + BRANGE t2, t1, t3, _start_hang + BRANGE t2, t1, t5, _start_hang + BRANGE t3, t5, t2, _start_hang +_relocate_copy_to_lower_loop: + REG_L t3, 0(t2) + REG_S t3, 0(t0) + add t0, t0, __SIZEOF_POINTER__ + add t2, t2, __SIZEOF_POINTER__ + blt t0, t1, _relocate_copy_to_lower_loop + jr t4 +_relocate_copy_to_upper: + ble t3, t0, _relocate_copy_to_upper_loop + lla t2, _relocate_lottery + BRANGE t0, t3, t2, _start_hang + lla t2, _boot_status + BRANGE t0, t3, t2, _start_hang + lla t2, _relocate + lla t5, _relocate_done + BRANGE t0, t3, t2, _start_hang + BRANGE t0, t3, t5, _start_hang + BRANGE t2, t5, t0, _start_hang +_relocate_copy_to_upper_loop: + add t3, t3, -__SIZEOF_POINTER__ + add t1, t1, -__SIZEOF_POINTER__ + REG_L t2, 0(t3) + REG_S t2, 0(t1) + blt t0, t1, _relocate_copy_to_upper_loop + jr t4 +_wait_relocate_copy_done: + lla t0, _fw_start + lla t1, _link_start + REG_L t1, 0(t1) + beq t0, t1, _wait_for_boot_hart + lla t2, _boot_status + lla t3, _wait_for_boot_hart + sub t3, t3, t0 + add t3, t3, t1 +1: + /* waitting for relocate copy done (_boot_status == 1) */ + li t4, BOOT_STATUS_RELOCATE_DONE + REG_L t5, 0(t2) + /* Reduce the bus traffic so that boot hart may proceed faster */ + nop + nop + nop + bgt t4, t5, 1b + jr t3 +#endif +# 重定位结束 +_relocate_done: + + /* + * Mark relocate copy done + * Use _boot_status copy relative to the load address + */ +# 加载 _boot_status 的地址到 t0 + lla t0, _boot_status +#ifndef FW_PIC + lla t1, _link_start + REG_L t1, 0(t1) + lla t2, _load_start + REG_L t2, 0(t2) + sub t0, t0, t1 + add t0, t0, t2 +#endif +# 改变 t0 为 BOOT_STATUS_RELOCATE_DONE,这个是个宏,被定义为 1 + li t1, BOOT_STATUS_RELOCATE_DONE + REG_S t1, 0(t0) +# 确保以上的访存操作已经做完 + fence rw, rw + + /* At this point we are running from link address */ + + /* Reset all registers for boot HART */ + li ra, 0 +# 将所有寄存器清零 + call _reset_regs + +# 将 _bss_start 和 _bss_end 分别加载到 s4 和 s5 + /* Zero-out BSS */ + lla s4, _bss_start + lla s5, _bss_end +# 循环将所有 bss 段内的内容清零 +_bss_zero: +# 向 s4 寄存器所指的内存中写 0,也就是清零 s4 寄存器所指内存的值 + REG_S zero, (s4) +# s4 所指的地址 +4,指向下一个地址 + add s4, s4, __SIZEOF_POINTER__ +# 如果 s4 的值小于 s5,也就是还没到 _bss_end ,跳至 _bss_zero + blt s4, s5, _bss_zero + +# 设置一些临时使用的中断 + /* Setup temporary trap handler */ + lla s4, _start_hang + csrw CSR_MTVEC, s4 +# 设置一些临时使用的中断栈 + /* Setup temporary stack */ + lla s4, _fw_end + li s5, (SBI_SCRATCH_SIZE * 2) + add sp, s4, s5 + +# 保存一些信息,不同的启动类型有不同的操作 + /* Allow main firmware to save info */ + MOV_5R s0, a0, s1, a1, s2, a2, s3, a3, s4, a4 + call fw_save_info + MOV_5R a0, s0, a1, s1, a2, s2, a3, s3, a4, s4 + +# 如果定义了设备树的地址,将它加载进来 +#ifdef FW_FDT_PATH + /* Override previous arg1 */ + lla a1, fw_fdt_bin +#endif + +# 针对特定平台,进行初始化 +# 如果没有在 fw_platform_init 函数中对 FDT 进行修改 +# 返回值不变 + /* + * Initialize platform + * Note: The a0 to a4 registers passed to the + * firmware are parameters to this function. + */ + MOV_5R s0, a0, s1, a1, s2, a2, s3, a3, s4, a4 + call fw_platform_init + add t0, a0, zero + MOV_5R a0, s0, a1, s1, a2, s2, a3, s3, a4, s4 + add a1, t0, zero + + /* Preload HART details + * s7 -> HART Count + * s8 -> HART Stack Size + * s9 -> Heap Size + * s10 -> Heap Offset + */ +# 将平台相关的数据结构地址加载进 a4 寄存器 + lla a4, platform +# 根据寄存器 a4 将平台相关的数据加载进来 +#if __riscv_xlen > 32 + lwu s7, SBI_PLATFORM_HART_COUNT_OFFSET(a4) + lwu s8, SBI_PLATFORM_HART_STACK_SIZE_OFFSET(a4) + lwu s9, SBI_PLATFORM_HEAP_SIZE_OFFSET(a4) +#else + lw s7, SBI_PLATFORM_HART_COUNT_OFFSET(a4) + lw s8, SBI_PLATFORM_HART_STACK_SIZE_OFFSET(a4) + lw s9, SBI_PLATFORM_HEAP_SIZE_OFFSET(a4) +#endif + +# tp 是 RISC-V 中的一个特殊寄存器,用于指向临时工作区域(scratch space)。 +# 将 _fw_end 地址加载进 tp, 在用 s7,s8 计算出 scratch space, 再加上 tp, 调整 scratch space 的位置 + /* Setup scratch space for all the HARTs*/ + lla tp, _fw_end + mul a5, s7, s8 + add tp, tp, a5 +# 原理同上 +# 调整 heap 基址 + /* Setup heap base address */ + lla s10, _fw_start + sub s10, tp, s10 + add tp, tp, s9 + /* Keep a copy of tp */ + add t3, tp, zero + /* Counter */ + li t2, 1 + /* hartid 0 is mandated by ISA */ + li t1, 0 +_scratch_init: + /* + * The following registers hold values that are computed before + * entering this block, and should remain unchanged. + * + * t3 -> the firmware end address + * s7 -> HART count + * s8 -> HART stack size + * s9 -> Heap Size + * s10 -> Heap Offset + */ +# 找到 scratch space 的基址 + add tp, t3, zero + sub tp, tp, s9 +# t1 首次是 0,计算出来的 a5 也等于 0, +# 这个 t1 是 hart 的编号,s8 是每个核的栈大小 +# 所以,a5是每个hart的栈的偏移。 + mul a5, s8, t1 + sub tp, tp, a5 + li a5, SBI_SCRATCH_SIZE + sub tp, tp, a5 + + /* Initialize scratch space */ + /* Store fw_start and fw_size in scratch space */ + lla a4, _fw_start + sub a5, t3, a4 + REG_S a4, SBI_SCRATCH_FW_START_OFFSET(tp) + REG_S a5, SBI_SCRATCH_FW_SIZE_OFFSET(tp) + + /* Store R/W section's offset in scratch space */ + lla a4, __fw_rw_offset + REG_L a5, 0(a4) + REG_S a5, SBI_SCRATCH_FW_RW_OFFSET(tp) + + /* Store fw_heap_offset and fw_heap_size in scratch space */ + REG_S s10, SBI_SCRATCH_FW_HEAP_OFFSET(tp) + REG_S s9, SBI_SCRATCH_FW_HEAP_SIZE_OFFSET(tp) + +# 设置函数:加载下一个阶段的参数1 的地址 + /* Store next arg1 in scratch space */ + MOV_3R s0, a0, s1, a1, s2, a2 + call fw_next_arg1 + REG_S a0, SBI_SCRATCH_NEXT_ARG1_OFFSET(tp) + MOV_3R a0, s0, a1, s1, a2, s2 +# 设置函数:加载下一个阶段的可执行文件的地址 的地址 + /* Store next address in scratch space */ + MOV_3R s0, a0, s1, a1, s2, a2 + call fw_next_addr + REG_S a0, SBI_SCRATCH_NEXT_ADDR_OFFSET(tp) + MOV_3R a0, s0, a1, s1, a2, s2 +# 设置函数:设置下一个阶段的特权等级 的地址 + /* Store next mode in scratch space */ + MOV_3R s0, a0, s1, a1, s2, a2 + call fw_next_mode + REG_S a0, SBI_SCRATCH_NEXT_MODE_OFFSET(tp) + MOV_3R a0, s0, a1, s1, a2, s2 +# 设置启动函数地址 + /* Store warm_boot address in scratch space */ + lla a4, _start_warm + REG_S a4, SBI_SCRATCH_WARMBOOT_ADDR_OFFSET(tp) + +# 将特定平台的数据结构加载进来 + /* Store platform address in scratch space */ + lla a4, platform + REG_S a4, SBI_SCRATCH_PLATFORM_ADDR_OFFSET(tp) + +# 将 hartid-to-scratch 函数的地址存入 scratch space + /* Store hartid-to-scratch function address in scratch space */ + lla a4, _hartid_to_scratch + REG_S a4, SBI_SCRATCH_HARTID_TO_SCRATCH_OFFSET(tp) + +# 将 trap-exit 函数的地址存入 scratch space + /* Store trap-exit function address in scratch space */ + lla a4, _trap_exit + REG_S a4, SBI_SCRATCH_TRAP_EXIT_OFFSET(tp) + /* Clear tmp0 in scratch space */ + REG_S zero, SBI_SCRATCH_TMP0_OFFSET(tp) + /* Store firmware options in scratch space */ + MOV_3R s0, a0, s1, a1, s2, a2 + +# FW_OPTIONS 禁用 OpenSBI 启动时打印信息 +#ifdef FW_OPTIONS + li a0, FW_OPTIONS +#else + call fw_options +#endif + REG_S a0, SBI_SCRATCH_OPTIONS_OFFSET(tp) + MOV_3R a0, s0, a1, s1, a2, s2 + /* Move to next scratch space */ +# 再将 t1 + 1,检查 t1 是否小于 s7(HART_COUNT) +# 如果小于,说明还有其他核的 scratch_space 没有初始化完成 +# 继续进行其他核心的初始化工作 + add t1, t1, t2 + blt t1, s7, _scratch_init + + /* + * Relocate Flatened Device Tree (FDT) + * source FDT address = previous arg1 + * destination FDT address = next arg1 + * + * Note: We will preserve a0 and a1 passed by + * previous booting stage. + */ +# a1 = 0,意味着不需要进行 _fdt_reloc +# a1 的值见 +# 279: lla a1, fw_fdt_bin + beqz a1, _fdt_reloc_done + /* Mask values in a4 */ + li a4, 0xff + /* t1 = destination FDT start address */ + MOV_3R s0, a0, s1, a1, s2, a2 +# 加载下一个阶段的参数 1 + call fw_next_arg1 + add t1, a0, zero + MOV_3R a0, s0, a1, s1, a2, s2 + beqz t1, _fdt_reloc_done + beq t1, a1, _fdt_reloc_done + /* t0 = source FDT start address */ + add t0, a1, zero + /* t2 = source FDT size in big-endian */ +#if __riscv_xlen == 64 + lwu t2, 4(t0) +#else + lw t2, 4(t0) +#endif + /* t3 = bit[15:8] of FDT size */ + add t3, t2, zero + srli t3, t3, 16 + and t3, t3, a4 + slli t3, t3, 8 + /* t4 = bit[23:16] of FDT size */ + add t4, t2, zero + srli t4, t4, 8 + and t4, t4, a4 + slli t4, t4, 16 + /* t5 = bit[31:24] of FDT size */ + add t5, t2, zero + and t5, t5, a4 + slli t5, t5, 24 + /* t2 = bit[7:0] of FDT size */ + srli t2, t2, 24 + and t2, t2, a4 + /* t2 = FDT size in little-endian */ + or t2, t2, t3 + or t2, t2, t4 + or t2, t2, t5 + /* t2 = destination FDT end address */ + add t2, t1, t2 + /* FDT copy loop */ + ble t2, t1, _fdt_reloc_done +_fdt_reloc_again: + REG_L t3, 0(t0) + REG_S t3, 0(t1) + add t0, t0, __SIZEOF_POINTER__ + add t1, t1, __SIZEOF_POINTER__ + blt t1, t2, _fdt_reloc_again +_fdt_reloc_done: + +# 启动核表明自己启动完成 + /* mark boot hart done */ + li t0, BOOT_STATUS_BOOT_HART_DONE + lla t1, _boot_status + REG_S t0, 0(t1) + fence rw, rw + j _start_warm + +# 非启动核等待启动核热启动完成 + /* waiting for boot hart to be done (_boot_status == 2) */ +_wait_for_boot_hart: + li t0, BOOT_STATUS_BOOT_HART_DONE + lla t1, _boot_status + REG_L t1, 0(t1) + /* Reduce the bus traffic so that boot hart may proceed faster */ + nop + nop + nop + bne t0, t1, _wait_for_boot_hart + +# 热启动 +# 主从核全部要跑下面的代码 +# 无非是从核先在_wait_for_boot_hart那里等待, +# 直到主核继续执行完一些公共的处理逻辑,比如上面的bss清理,scratch内存初始化,dtb重定位等。 +_start_warm: + /* Reset all registers for non-boot HARTs */ + li ra, 0 + call _reset_regs + + /* Disable all interrupts */ + csrw CSR_MIE, zero + + /* Find HART count and HART stack size */ + lla a4, platform +#if __riscv_xlen == 64 + lwu s7, SBI_PLATFORM_HART_COUNT_OFFSET(a4) + lwu s8, SBI_PLATFORM_HART_STACK_SIZE_OFFSET(a4) +#else + lw s7, SBI_PLATFORM_HART_COUNT_OFFSET(a4) + lw s8, SBI_PLATFORM_HART_STACK_SIZE_OFFSET(a4) +#endif + REG_L s9, SBI_PLATFORM_HART_INDEX2ID_OFFSET(a4) + +# 使用 CSR(Control and Status Register)指令,将机器模式下的 HART ID(处理器 ID)读取到 s6 寄存器中。 +# CSR_MHARTID 是一个特定的寄存器控制编码,用于获取当前 HART 的 ID。 + /* Find HART id */ + csrr s6, CSR_MHARTID + + /* Find HART index */ + beqz s9, 3f + li a4, 0 +1: +#if __riscv_xlen == 64 + lwu a5, (s9) +#else + lw a5, (s9) +#endif + beq a5, s6, 2f + add s9, s9, 4 + add a4, a4, 1 + blt a4, s7, 1b + li a4, -1 +2: add s6, a4, zero +3: bge s6, s7, _start_hang + +# 经过下面的操作,可以找到符合上面条件的 HART 的 scratch space 的 end 位置 + /* Find the scratch space based on HART index */ + lla tp, _fw_end + mul a5, s7, s8 + add tp, tp, a5 + mul a5, s8, s6 + sub tp, tp, a5 + li a5, SBI_SCRATCH_SIZE + sub tp, tp, a5 + +# 将上面的值写入 CSR_MSCRATCH 寄存器 + /* update the mscratch */ + csrw CSR_MSCRATCH, tp + + /* Setup stack */ + add sp, tp, zero + + /* Setup trap handler */ + lla a4, _trap_handler +# 如果架构是 32 位的,做一些特殊操作 +#if __riscv_xlen == 32 + csrr a5, CSR_MISA + srli a5, a5, ('H' - 'A') + andi a5, a5, 0x1 + beq a5, zero, _skip_trap_handler_rv32_hyp + lla a4, _trap_handler_rv32_hyp +_skip_trap_handler_rv32_hyp: +#endif + csrw CSR_MTVEC, a4 + +#if __riscv_xlen == 32 + /* Override trap exit for H-extension */ + csrr a5, CSR_MISA + srli a5, a5, ('H' - 'A') + andi a5, a5, 0x1 + beq a5, zero, _skip_trap_exit_rv32_hyp + lla a4, _trap_exit_rv32_hyp + csrr a5, CSR_MSCRATCH + REG_S a4, SBI_SCRATCH_TRAP_EXIT_OFFSET(a5) +_skip_trap_exit_rv32_hyp: +#endif + +# 正式进入 SBI 运行时环境 + /* Initialize SBI runtime */ + csrr a0, CSR_MSCRATCH + call sbi_init + + /* We don't expect to reach here hence just hang */ + j _start_hang + + .data + .align 3 +#ifdef FW_PIC +_runtime_offset: + RISCV_PTR 0 +#endif +_relocate_lottery: + RISCV_PTR 0 +_boot_status: + RISCV_PTR 0 +_load_start: + RISCV_PTR _fw_start +_link_start: + RISCV_PTR FW_TEXT_START +_link_end: + RISCV_PTR _fw_reloc_end +__fw_rw_offset: + RISCV_PTR _fw_rw_start - _fw_start + + .section .entry, "ax", %progbits + .align 3 + .globl _hartid_to_scratch +_hartid_to_scratch: + /* + * a0 -> HART ID (passed by caller) + * a1 -> HART Index (passed by caller) + * t0 -> HART Stack Size + * t1 -> HART Stack End + * t2 -> Temporary + */ + lla t2, platform +#if __riscv_xlen == 64 + lwu t0, SBI_PLATFORM_HART_STACK_SIZE_OFFSET(t2) + lwu t2, SBI_PLATFORM_HART_COUNT_OFFSET(t2) +#else + lw t0, SBI_PLATFORM_HART_STACK_SIZE_OFFSET(t2) + lw t2, SBI_PLATFORM_HART_COUNT_OFFSET(t2) +#endif + sub t2, t2, a1 + mul t2, t2, t0 + lla t1, _fw_end + add t1, t1, t2 + li t2, SBI_SCRATCH_SIZE + sub a0, t1, t2 + ret + + .section .entry, "ax", %progbits + .align 3 + .globl _start_hang +_start_hang: + wfi + j _start_hang + + .section .entry, "ax", %progbits + .align 3 + .weak fw_platform_init +fw_platform_init: + add a0, a1, zero + ret + + /* Map implicit memcpy() added by compiler to sbi_memcpy() */ + .section .text + .align 3 + .globl memcpy + +# 下面许多都是类似 c 语言中的函数,这里不再赘述 +memcpy: + tail sbi_memcpy + + /* Map implicit memset() added by compiler to sbi_memset() */ + .section .text + .align 3 + .globl memset +memset: + tail sbi_memset + + /* Map implicit memmove() added by compiler to sbi_memmove() */ + .section .text + .align 3 + .globl memmove +memmove: + tail sbi_memmove + + /* Map implicit memcmp() added by compiler to sbi_memcmp() */ + .section .text + .align 3 + .globl memcmp +memcmp: + tail sbi_memcmp + +.macro TRAP_SAVE_AND_SETUP_SP_T0 + /* Swap TP and MSCRATCH */ + csrrw tp, CSR_MSCRATCH, tp + + /* Save T0 in scratch space */ + REG_S t0, SBI_SCRATCH_TMP0_OFFSET(tp) + + /* + * Set T0 to appropriate exception stack + * + * Came_From_M_Mode = ((MSTATUS.MPP < PRV_M) ? 1 : 0) - 1; + * Exception_Stack = TP ^ (Came_From_M_Mode & (SP ^ TP)) + * + * Came_From_M_Mode = 0 ==> Exception_Stack = TP + * Came_From_M_Mode = -1 ==> Exception_Stack = SP + */ + csrr t0, CSR_MSTATUS + srl t0, t0, MSTATUS_MPP_SHIFT + and t0, t0, PRV_M + slti t0, t0, PRV_M + add t0, t0, -1 + xor sp, sp, tp + and t0, t0, sp + xor sp, sp, tp + xor t0, tp, t0 + + /* Save original SP on exception stack */ + REG_S sp, (SBI_TRAP_REGS_OFFSET(sp) - SBI_TRAP_REGS_SIZE)(t0) + + /* Set SP to exception stack and make room for trap registers */ + add sp, t0, -(SBI_TRAP_REGS_SIZE) + + /* Restore T0 from scratch space */ + REG_L t0, SBI_SCRATCH_TMP0_OFFSET(tp) + + /* Save T0 on stack */ + REG_S t0, SBI_TRAP_REGS_OFFSET(t0)(sp) + + /* Swap TP and MSCRATCH */ + csrrw tp, CSR_MSCRATCH, tp +.endm + +.macro TRAP_SAVE_MEPC_MSTATUS have_mstatush + /* Save MEPC and MSTATUS CSRs */ + csrr t0, CSR_MEPC + REG_S t0, SBI_TRAP_REGS_OFFSET(mepc)(sp) + csrr t0, CSR_MSTATUS + REG_S t0, SBI_TRAP_REGS_OFFSET(mstatus)(sp) + .if \have_mstatush + csrr t0, CSR_MSTATUSH + REG_S t0, SBI_TRAP_REGS_OFFSET(mstatusH)(sp) + .else + REG_S zero, SBI_TRAP_REGS_OFFSET(mstatusH)(sp) + .endif +.endm + +.macro TRAP_SAVE_GENERAL_REGS_EXCEPT_SP_T0 + /* Save all general regisers except SP and T0 */ + REG_S zero, SBI_TRAP_REGS_OFFSET(zero)(sp) + REG_S ra, SBI_TRAP_REGS_OFFSET(ra)(sp) + REG_S gp, SBI_TRAP_REGS_OFFSET(gp)(sp) + REG_S tp, SBI_TRAP_REGS_OFFSET(tp)(sp) + REG_S t1, SBI_TRAP_REGS_OFFSET(t1)(sp) + REG_S t2, SBI_TRAP_REGS_OFFSET(t2)(sp) + REG_S s0, SBI_TRAP_REGS_OFFSET(s0)(sp) + REG_S s1, SBI_TRAP_REGS_OFFSET(s1)(sp) + REG_S a0, SBI_TRAP_REGS_OFFSET(a0)(sp) + REG_S a1, SBI_TRAP_REGS_OFFSET(a1)(sp) + REG_S a2, SBI_TRAP_REGS_OFFSET(a2)(sp) + REG_S a3, SBI_TRAP_REGS_OFFSET(a3)(sp) + REG_S a4, SBI_TRAP_REGS_OFFSET(a4)(sp) + REG_S a5, SBI_TRAP_REGS_OFFSET(a5)(sp) + REG_S a6, SBI_TRAP_REGS_OFFSET(a6)(sp) + REG_S a7, SBI_TRAP_REGS_OFFSET(a7)(sp) + REG_S s2, SBI_TRAP_REGS_OFFSET(s2)(sp) + REG_S s3, SBI_TRAP_REGS_OFFSET(s3)(sp) + REG_S s4, SBI_TRAP_REGS_OFFSET(s4)(sp) + REG_S s5, SBI_TRAP_REGS_OFFSET(s5)(sp) + REG_S s6, SBI_TRAP_REGS_OFFSET(s6)(sp) + REG_S s7, SBI_TRAP_REGS_OFFSET(s7)(sp) + REG_S s8, SBI_TRAP_REGS_OFFSET(s8)(sp) + REG_S s9, SBI_TRAP_REGS_OFFSET(s9)(sp) + REG_S s10, SBI_TRAP_REGS_OFFSET(s10)(sp) + REG_S s11, SBI_TRAP_REGS_OFFSET(s11)(sp) + REG_S t3, SBI_TRAP_REGS_OFFSET(t3)(sp) + REG_S t4, SBI_TRAP_REGS_OFFSET(t4)(sp) + REG_S t5, SBI_TRAP_REGS_OFFSET(t5)(sp) + REG_S t6, SBI_TRAP_REGS_OFFSET(t6)(sp) +.endm + +.macro TRAP_CALL_C_ROUTINE + /* Call C routine */ + add a0, sp, zero + call sbi_trap_handler +.endm + +.macro TRAP_RESTORE_GENERAL_REGS_EXCEPT_A0_T0 + /* Restore all general regisers except A0 and T0 */ + REG_L ra, SBI_TRAP_REGS_OFFSET(ra)(a0) + REG_L sp, SBI_TRAP_REGS_OFFSET(sp)(a0) + REG_L gp, SBI_TRAP_REGS_OFFSET(gp)(a0) + REG_L tp, SBI_TRAP_REGS_OFFSET(tp)(a0) + REG_L t1, SBI_TRAP_REGS_OFFSET(t1)(a0) + REG_L t2, SBI_TRAP_REGS_OFFSET(t2)(a0) + REG_L s0, SBI_TRAP_REGS_OFFSET(s0)(a0) + REG_L s1, SBI_TRAP_REGS_OFFSET(s1)(a0) + REG_L a1, SBI_TRAP_REGS_OFFSET(a1)(a0) + REG_L a2, SBI_TRAP_REGS_OFFSET(a2)(a0) + REG_L a3, SBI_TRAP_REGS_OFFSET(a3)(a0) + REG_L a4, SBI_TRAP_REGS_OFFSET(a4)(a0) + REG_L a5, SBI_TRAP_REGS_OFFSET(a5)(a0) + REG_L a6, SBI_TRAP_REGS_OFFSET(a6)(a0) + REG_L a7, SBI_TRAP_REGS_OFFSET(a7)(a0) + REG_L s2, SBI_TRAP_REGS_OFFSET(s2)(a0) + REG_L s3, SBI_TRAP_REGS_OFFSET(s3)(a0) + REG_L s4, SBI_TRAP_REGS_OFFSET(s4)(a0) + REG_L s5, SBI_TRAP_REGS_OFFSET(s5)(a0) + REG_L s6, SBI_TRAP_REGS_OFFSET(s6)(a0) + REG_L s7, SBI_TRAP_REGS_OFFSET(s7)(a0) + REG_L s8, SBI_TRAP_REGS_OFFSET(s8)(a0) + REG_L s9, SBI_TRAP_REGS_OFFSET(s9)(a0) + REG_L s10, SBI_TRAP_REGS_OFFSET(s10)(a0) + REG_L s11, SBI_TRAP_REGS_OFFSET(s11)(a0) + REG_L t3, SBI_TRAP_REGS_OFFSET(t3)(a0) + REG_L t4, SBI_TRAP_REGS_OFFSET(t4)(a0) + REG_L t5, SBI_TRAP_REGS_OFFSET(t5)(a0) + REG_L t6, SBI_TRAP_REGS_OFFSET(t6)(a0) +.endm + +.macro TRAP_RESTORE_MEPC_MSTATUS have_mstatush + /* Restore MEPC and MSTATUS CSRs */ + REG_L t0, SBI_TRAP_REGS_OFFSET(mepc)(a0) + csrw CSR_MEPC, t0 + REG_L t0, SBI_TRAP_REGS_OFFSET(mstatus)(a0) + csrw CSR_MSTATUS, t0 + .if \have_mstatush + REG_L t0, SBI_TRAP_REGS_OFFSET(mstatusH)(a0) + csrw CSR_MSTATUSH, t0 + .endif +.endm + +.macro TRAP_RESTORE_A0_T0 + /* Restore T0 */ + REG_L t0, SBI_TRAP_REGS_OFFSET(t0)(a0) + + /* Restore A0 */ + REG_L a0, SBI_TRAP_REGS_OFFSET(a0)(a0) +.endm + + .section .entry, "ax", %progbits + .align 3 + .globl _trap_handler + .globl _trap_exit +_trap_handler: + TRAP_SAVE_AND_SETUP_SP_T0 + + TRAP_SAVE_MEPC_MSTATUS 0 + + TRAP_SAVE_GENERAL_REGS_EXCEPT_SP_T0 + + TRAP_CALL_C_ROUTINE + +_trap_exit: + TRAP_RESTORE_GENERAL_REGS_EXCEPT_A0_T0 + + TRAP_RESTORE_MEPC_MSTATUS 0 + + TRAP_RESTORE_A0_T0 + + mret + +#if __riscv_xlen == 32 + .section .entry, "ax", %progbits + .align 3 + .globl _trap_handler_rv32_hyp + .globl _trap_exit_rv32_hyp +_trap_handler_rv32_hyp: + TRAP_SAVE_AND_SETUP_SP_T0 + + TRAP_SAVE_MEPC_MSTATUS 1 + + TRAP_SAVE_GENERAL_REGS_EXCEPT_SP_T0 + + TRAP_CALL_C_ROUTINE + +_trap_exit_rv32_hyp: + TRAP_RESTORE_GENERAL_REGS_EXCEPT_A0_T0 + + TRAP_RESTORE_MEPC_MSTATUS 1 + + TRAP_RESTORE_A0_T0 + + mret +#endif + + .section .entry, "ax", %progbits + .align 3 + .globl _reset_regs +_reset_regs: + + /* flush the instruction cache */ + fence.i + /* Reset all registers except ra, a0, a1 and a2 */ + li sp, 0 + li gp, 0 + li tp, 0 + li t0, 0 + li t1, 0 + li t2, 0 + li s0, 0 + li s1, 0 + li a3, 0 + li a4, 0 + li a5, 0 + li a6, 0 + li a7, 0 + li s2, 0 + li s3, 0 + li s4, 0 + li s5, 0 + li s6, 0 + li s7, 0 + li s8, 0 + li s9, 0 + li s10, 0 + li s11, 0 + li t3, 0 + li t4, 0 + li t5, 0 + li t6, 0 + csrw CSR_MSCRATCH, 0 + + ret + +#ifdef FW_FDT_PATH + .section .rodata + .align 4 + .globl fw_fdt_bin +fw_fdt_bin: + .incbin FW_FDT_PATH +#ifdef FW_FDT_PADDING + .fill FW_FDT_PADDING, 1, 0 +#endif +#endif + +``` + +## 小结 + +通过分析 `fw_base.S` 中的源码,我们已经知道了 OpenSBI 的启动流程的最前面一段是如何完成的。之后我们会对 `fw_payload.S`、`fw_jump.S` 和 `fw_dynamic.S` 这三个文件进行解析,带领读者体会其中的差异。 -- Gitee From ec0d0e391f8048d70158fc4cbea1952e19cf69db Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Wed, 2 Aug 2023 06:56:28 +0000 Subject: [PATCH 10/68] sbi-firemware-analyze-1.md: commit correct result of tinycorrect-spaces Signed-off-by: Groot <1219671600@qq.com> --- articles/20230728-sbi-firemware-analyze-1.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/articles/20230728-sbi-firemware-analyze-1.md b/articles/20230728-sbi-firemware-analyze-1.md index 8ec6407..151690b 100644 --- a/articles/20230728-sbi-firemware-analyze-1.md +++ b/articles/20230728-sbi-firemware-analyze-1.md @@ -1,8 +1,8 @@ -> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.1 - [spaces tables]`
` -> Author: groot `
` -> Date: 2023/07/28 `
` -> Revisor: Falcon [falcon@tinylab.org](mailto:falcon@tinylab.org) `
` -> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux) `
` +> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [spaces]
+> Author: groot
+> Date: 2023/07/28
+> Revisor: Falcon [falcon@tinylab.org](mailto:falcon@tinylab.org)
+> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux)
> Proposal: [RISC-V Linux 内核 SBI 调用技术分析](https://gitee.com/tinylab/riscv-linux/issues/I64YC4) `
` > Sponsor: PLCT Lab, ISCAS @@ -115,7 +115,7 @@ RISC-V 的汇编指示符和作用如下 该文件中的代码是整个 OpenSBI 的起点,我们理解 OpenSBI 的代码就从这里开始吧! -首先给大家介绍文件中的一部分代码,将这些代码的作用从宏观上给大家介绍一下, 然后再深入代码,将注释打在代码中,方便大家理解。 +首先给大家介绍文件中的一部分代码,将这些代码的作用从宏观上给大家介绍一下,然后再深入代码,将注释打在代码中,方便大家理解。 ### 代码意义 @@ -174,7 +174,7 @@ RISC-V 的汇编指示符和作用如下 // 开始 _start: /* Find preferred boot HART id */ -# 伪代码,见第 26 行 +# 伪代码,见第 26 行 MOV_3R s0, a0, s1, a1, s2, a2 # 调用 fw_boot_hart 函数 # fw_payload 和 fw_jump 返回 -1 -- Gitee From 1b0229881b002514f8f663ce51b96a5b4d080565 Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Wed, 2 Aug 2023 06:59:33 +0000 Subject: [PATCH 11/68] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8F=82=E8=80=83?= =?UTF-8?q?=E8=B5=84=E6=96=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- articles/20230728-sbi-firemware-analyze-1.md | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/articles/20230728-sbi-firemware-analyze-1.md b/articles/20230728-sbi-firemware-analyze-1.md index 151690b..fe990b7 100644 --- a/articles/20230728-sbi-firemware-analyze-1.md +++ b/articles/20230728-sbi-firemware-analyze-1.md @@ -1,8 +1,8 @@ -> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [spaces]
-> Author: groot
-> Date: 2023/07/28
-> Revisor: Falcon [falcon@tinylab.org](mailto:falcon@tinylab.org)
-> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux)
+> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.1 - [spaces tables]`
` +> Author: groot `
` +> Date: 2023/07/28 `
` +> Revisor: Falcon [falcon@tinylab.org](mailto:falcon@tinylab.org) `
` +> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux) `
` > Proposal: [RISC-V Linux 内核 SBI 调用技术分析](https://gitee.com/tinylab/riscv-linux/issues/I64YC4) `
` > Sponsor: PLCT Lab, ISCAS @@ -115,7 +115,7 @@ RISC-V 的汇编指示符和作用如下 该文件中的代码是整个 OpenSBI 的起点,我们理解 OpenSBI 的代码就从这里开始吧! -首先给大家介绍文件中的一部分代码,将这些代码的作用从宏观上给大家介绍一下,然后再深入代码,将注释打在代码中,方便大家理解。 +首先给大家介绍文件中的一部分代码,将这些代码的作用从宏观上给大家介绍一下, 然后再深入代码,将注释打在代码中,方便大家理解。 ### 代码意义 @@ -174,7 +174,7 @@ RISC-V 的汇编指示符和作用如下 // 开始 _start: /* Find preferred boot HART id */ -# 伪代码,见第 26 行 +# 伪代码,见第 26 行 MOV_3R s0, a0, s1, a1, s2, a2 # 调用 fw_boot_hart 函数 # fw_payload 和 fw_jump 返回 -1 @@ -1066,3 +1066,9 @@ fw_fdt_bin: ## 小结 通过分析 `fw_base.S` 中的源码,我们已经知道了 OpenSBI 的启动流程的最前面一段是如何完成的。之后我们会对 `fw_payload.S`、`fw_jump.S` 和 `fw_dynamic.S` 这三个文件进行解析,带领读者体会其中的差异。 + +## 参考资料 + +- RISC-V 手册 +- [OpenSBI 官方仓库固件部分文档](https://github.com/riscv-software-src/opensbi/tree/master/docs/firmware) +- OpenSBI 源代码 -- Gitee From 9ca3d78a2baf77fb95ee58349a4a6ed909d79705 Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Wed, 2 Aug 2023 06:59:52 +0000 Subject: [PATCH 12/68] sbi-firemware-analyze-1.md: commit correct result of tinycorrect-spaces Signed-off-by: Groot <1219671600@qq.com> --- articles/20230728-sbi-firemware-analyze-1.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/articles/20230728-sbi-firemware-analyze-1.md b/articles/20230728-sbi-firemware-analyze-1.md index fe990b7..2a29c9c 100644 --- a/articles/20230728-sbi-firemware-analyze-1.md +++ b/articles/20230728-sbi-firemware-analyze-1.md @@ -1,8 +1,8 @@ -> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.1 - [spaces tables]`
` -> Author: groot `
` -> Date: 2023/07/28 `
` -> Revisor: Falcon [falcon@tinylab.org](mailto:falcon@tinylab.org) `
` -> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux) `
` +> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [spaces]
+> Author: groot
+> Date: 2023/07/28
+> Revisor: Falcon [falcon@tinylab.org](mailto:falcon@tinylab.org)
+> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux)
> Proposal: [RISC-V Linux 内核 SBI 调用技术分析](https://gitee.com/tinylab/riscv-linux/issues/I64YC4) `
` > Sponsor: PLCT Lab, ISCAS @@ -115,7 +115,7 @@ RISC-V 的汇编指示符和作用如下 该文件中的代码是整个 OpenSBI 的起点,我们理解 OpenSBI 的代码就从这里开始吧! -首先给大家介绍文件中的一部分代码,将这些代码的作用从宏观上给大家介绍一下, 然后再深入代码,将注释打在代码中,方便大家理解。 +首先给大家介绍文件中的一部分代码,将这些代码的作用从宏观上给大家介绍一下,然后再深入代码,将注释打在代码中,方便大家理解。 ### 代码意义 @@ -174,7 +174,7 @@ RISC-V 的汇编指示符和作用如下 // 开始 _start: /* Find preferred boot HART id */ -# 伪代码,见第 26 行 +# 伪代码,见第 26 行 MOV_3R s0, a0, s1, a1, s2, a2 # 调用 fw_boot_hart 函数 # fw_payload 和 fw_jump 返回 -1 -- Gitee From 1abeb3355da4f5ddc01e1c1eaf7820a319ec1111 Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Wed, 2 Aug 2023 07:00:13 +0000 Subject: [PATCH 13/68] sbi-firemware-analyze-1.md: commit correct result of tinycorrect-comments Signed-off-by: Groot <1219671600@qq.com> --- articles/20230728-sbi-firemware-analyze-1.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/articles/20230728-sbi-firemware-analyze-1.md b/articles/20230728-sbi-firemware-analyze-1.md index 2a29c9c..b46f400 100644 --- a/articles/20230728-sbi-firemware-analyze-1.md +++ b/articles/20230728-sbi-firemware-analyze-1.md @@ -1,4 +1,4 @@ -> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [spaces]
+> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [spaces comments]
> Author: groot
> Date: 2023/07/28
> Revisor: Falcon [falcon@tinylab.org](mailto:falcon@tinylab.org)
@@ -247,7 +247,7 @@ _try_lottery: li t3, RELOC_TYPE bne t5, t3, 5f - /* address R_RISCV_64 or R_RISCV_32 cases*/ + /* address R_RISCV_64 or R_RISCV_32 cases */ REG_L t3, 0(t0) li t5, SYM_SIZE mul t6, t6, t5 @@ -437,7 +437,7 @@ _bss_zero: # tp 是 RISC-V 中的一个特殊寄存器,用于指向临时工作区域(scratch space)。 # 将 _fw_end 地址加载进 tp, 在用 s7,s8 计算出 scratch space, 再加上 tp, 调整 scratch space 的位置 - /* Setup scratch space for all the HARTs*/ + /* Setup scratch space for all the HARTs */ lla tp, _fw_end mul a5, s7, s8 add tp, tp, a5 -- Gitee From c1f72719e4f9a8fab4f0a4897efd1c7d2b7534d6 Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Wed, 2 Aug 2023 07:00:29 +0000 Subject: [PATCH 14/68] sbi-firemware-analyze-1.md: commit correct result of tinycorrect-tables Signed-off-by: Groot <1219671600@qq.com> --- articles/20230728-sbi-firemware-analyze-1.md | 78 ++++++++++---------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/articles/20230728-sbi-firemware-analyze-1.md b/articles/20230728-sbi-firemware-analyze-1.md index b46f400..f30c5c3 100644 --- a/articles/20230728-sbi-firemware-analyze-1.md +++ b/articles/20230728-sbi-firemware-analyze-1.md @@ -1,4 +1,4 @@ -> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [spaces comments]
+> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [spaces comments tables]
> Author: groot
> Date: 2023/07/28
> Revisor: Falcon [falcon@tinylab.org](mailto:falcon@tinylab.org)
@@ -48,20 +48,20 @@ make PLATFORM= FW_OPTIONS= risc-v 有 32 个通用寄存器(简写 reg),标号为 `x0` - `x31` -| 寄存器 | 编程接口名称 (ABI) | 描述 | 使用 | -| ------ | --------------------- | -------------------------------- | ------------------------------------ | -| x0 | zero | Hard-wired zero | 硬件零 | -| x1 | ra | Return address | 常用于保存(函数的)返回地址 | -| x2 | sp | Stack pointer | 栈顶指针 | -| x3 | gp | Global pointer | — | -| x4 | tp | Thread pointer | — | -| x5-7 | t0-2 | Temporary | 临时寄存器 | -| x8 | s0/fp | Saved Register/ Frame pointer | (函数调用时)保存的寄存器和栈顶指针 | -| x9 | s1 | Saved register | (函数调用时)保存的寄存器 | -| x10-11 | a0-1 | Function argument/ return value | (函数调用时)的参数/函数的返回值 | -| x12-17 | a2-7 | Function argument | (函数调用时)的参数 | -| x18-27 | s2-11 | Saved register | (函数调用时)保存的寄存器 | -| x28-31 | t3-6 | Temporary | 临时寄存器 | +| 寄存器 | 编程接口名称(ABI) | 描述 | 使用 | +|--------|-------------------|---------------------------------|------------------------------------| +| x0 | zero | Hard-wired zero | 硬件零 | +| x1 | ra | Return address | 常用于保存(函数的)返回地址 | +| x2 | sp | Stack pointer | 栈顶指针 | +| x3 | gp | Global pointer | — | +| x4 | tp | Thread pointer | — | +| x5-7 | t0-2 | Temporary | 临时寄存器 | +| x8 | s0/fp | Saved Register/ Frame pointer | (函数调用时)保存的寄存器和栈顶指针 | +| x9 | s1 | Saved register | (函数调用时)保存的寄存器 | +| x10-11 | a0-1 | Function argument/ return value | (函数调用时)的参数/函数的返回值 | +| x12-17 | a2-7 | Function argument | (函数调用时)的参数 | +| x18-27 | s2-11 | Saved register | (函数调用时)保存的寄存器 | +| x28-31 | t3-6 | Temporary | 临时寄存器 | ### 指令格式 @@ -86,30 +86,30 @@ RISC-V 指令具有六种基本指令格式: RISC-V 的汇编指示符和作用如下 -| 指示符 | 作用 | -| :--------------- | :------------------------------------------------------------------------------------ | -| .text | 代码段,之后跟的符号都在.text内 | -| .data | 数据段,之后跟的符号都在.data内 | -| .bss | 未初始化数据段,之后跟的符号都在.bss中 | -| .section .foo | 自定义段,之后跟的符号都在.foo段中,.foo段名可以做修改 | -| .align n | 按2的n次幂字节对齐 | -| .balign n | 按n字节对齐 | -| .globl sym | 声明sym未全局符号,其它文件可以访问 | -| .string “str” | 将字符串str放入内存 | -| .byte b1,…,bn | 在内存中连续存储n个单字节 | -| .half w1,…,wn | 在内存中连续存储n个半字(2字节) | -| .word w1,…,wn | 在内存中连续存储n个字(4字节) | -| .dword w1,…,wn | 在内存中连续存储n个双字(8字节) | -| .float f1,…,fn | 在内存中连续存储n个单精度浮点数 | -| .double d1,…,dn | 在内存中连续存储n个双精度浮点数 | -| .option rvc | 使用压缩指令(risc-v c) | -| .option norvc | 不压缩指令 | -| .option relax | 允许链接器松弛(linker relaxation,链接时多次扫描代码,尽可能将跳转两条指令替换为一条) | -| .option norelax | 不允许链接松弛 | -| .option pic | 与位置无关代码段 | -| .option nopic | 与位置有关代码段 | -| .option push | 将所有.option设置存入栈 | -| .option pop | 从栈中弹出上次存入的.option设置 | +| 指示符 | 作用 | +|:----------------|:----------------------------------------------------------------------------------| +| .text | 代码段,之后跟的符号都在.text内 | +| .data | 数据段,之后跟的符号都在.data内 | +| .bss | 未初始化数据段,之后跟的符号都在.bss中 | +| .section .foo | 自定义段,之后跟的符号都在.foo段中,.foo段名可以做修改 | +| .align n | 按2的n次幂字节对齐 | +| .balign n | 按n字节对齐 | +| .globl sym | 声明sym未全局符号,其它文件可以访问 | +| .string “str” | 将字符串str放入内存 | +| .byte b1,…,bn | 在内存中连续存储n个单字节 | +| .half w1,…,wn | 在内存中连续存储n个半字(2字节) | +| .word w1,…,wn | 在内存中连续存储n个字(4字节) | +| .dword w1,…,wn | 在内存中连续存储n个双字(8字节) | +| .float f1,…,fn | 在内存中连续存储n个单精度浮点数 | +| .double d1,…,dn | 在内存中连续存储n个双精度浮点数 | +| .option rvc | 使用压缩指令(risc-v c) | +| .option norvc | 不压缩指令 | +| .option relax | 允许链接器松弛(linker relaxation,链接时多次扫描代码,尽可能将跳转两条指令替换为一条) | +| .option norelax | 不允许链接松弛 | +| .option pic | 与位置无关代码段 | +| .option nopic | 与位置有关代码段 | +| .option push | 将所有.option设置存入栈 | +| .option pop | 从栈中弹出上次存入的.option设置 | ## fw_base -- Gitee From 12d9e89e3bf0f0cfe5d0984a3133366b1ca49a54 Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Wed, 2 Aug 2023 07:09:11 +0000 Subject: [PATCH 15/68] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=9B=BE=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- articles/20230728-sbi-firemware-analyze-1.md | 90 +++++++++--------- .../images/sbi-firmware/instruction-spec.png | Bin 0 -> 111317 bytes 2 files changed, 44 insertions(+), 46 deletions(-) create mode 100644 articles/images/sbi-firmware/instruction-spec.png diff --git a/articles/20230728-sbi-firemware-analyze-1.md b/articles/20230728-sbi-firemware-analyze-1.md index f30c5c3..ce94883 100644 --- a/articles/20230728-sbi-firemware-analyze-1.md +++ b/articles/20230728-sbi-firemware-analyze-1.md @@ -1,8 +1,8 @@ -> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [spaces comments tables]
-> Author: groot
-> Date: 2023/07/28
-> Revisor: Falcon [falcon@tinylab.org](mailto:falcon@tinylab.org)
-> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux)
+> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [spaces comments tables]`
` +> Author: groot `
` +> Date: 2023/07/28`
` +> Revisor: Falcon [falcon@tinylab.org](mailto:falcon@tinylab.org)`
` +> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux)`
` > Proposal: [RISC-V Linux 内核 SBI 调用技术分析](https://gitee.com/tinylab/riscv-linux/issues/I64YC4) `
` > Sponsor: PLCT Lab, ISCAS @@ -48,20 +48,20 @@ make PLATFORM= FW_OPTIONS= risc-v 有 32 个通用寄存器(简写 reg),标号为 `x0` - `x31` -| 寄存器 | 编程接口名称(ABI) | 描述 | 使用 | -|--------|-------------------|---------------------------------|------------------------------------| -| x0 | zero | Hard-wired zero | 硬件零 | -| x1 | ra | Return address | 常用于保存(函数的)返回地址 | -| x2 | sp | Stack pointer | 栈顶指针 | -| x3 | gp | Global pointer | — | -| x4 | tp | Thread pointer | — | -| x5-7 | t0-2 | Temporary | 临时寄存器 | -| x8 | s0/fp | Saved Register/ Frame pointer | (函数调用时)保存的寄存器和栈顶指针 | -| x9 | s1 | Saved register | (函数调用时)保存的寄存器 | -| x10-11 | a0-1 | Function argument/ return value | (函数调用时)的参数/函数的返回值 | -| x12-17 | a2-7 | Function argument | (函数调用时)的参数 | -| x18-27 | s2-11 | Saved register | (函数调用时)保存的寄存器 | -| x28-31 | t3-6 | Temporary | 临时寄存器 | +| 寄存器 | 编程接口名称(ABI) | 描述 | 使用 | +| ------ | ------------------- | ------------------------------- | ------------------------------------ | +| x0 | zero | Hard-wired zero | 硬件零 | +| x1 | ra | Return address | 常用于保存(函数的)返回地址 | +| x2 | sp | Stack pointer | 栈顶指针 | +| x3 | gp | Global pointer | — | +| x4 | tp | Thread pointer | — | +| x5-7 | t0-2 | Temporary | 临时寄存器 | +| x8 | s0/fp | Saved Register/ Frame pointer | (函数调用时)保存的寄存器和栈顶指针 | +| x9 | s1 | Saved register | (函数调用时)保存的寄存器 | +| x10-11 | a0-1 | Function argument/ return value | (函数调用时)的参数/函数的返回值 | +| x12-17 | a2-7 | Function argument | (函数调用时)的参数 | +| x18-27 | s2-11 | Saved register | (函数调用时)保存的寄存器 | +| x28-31 | t3-6 | Temporary | 临时寄存器 | ### 指令格式 @@ -72,9 +72,7 @@ RISC-V 指令具有六种基本指令格式: * S 类型指令:用于访存 store 操作; * B 类型指令:用于条件跳转操作; * U 类型指令:用于长立即数操作; -* J 类型指令:用于无条件操作; - -![RISC-V 指令格式](http://imgtec.eetrend.com/files/2021-09/%E5%8D%9A%E5%AE%A2/100553533-219776-x1.png) +* J 类型指令:用于无条件操作;![RISC-V 指令格式](images/sbi-firmware/instruction-spec.png) ### 常见指令 @@ -86,30 +84,30 @@ RISC-V 指令具有六种基本指令格式: RISC-V 的汇编指示符和作用如下 -| 指示符 | 作用 | -|:----------------|:----------------------------------------------------------------------------------| -| .text | 代码段,之后跟的符号都在.text内 | -| .data | 数据段,之后跟的符号都在.data内 | -| .bss | 未初始化数据段,之后跟的符号都在.bss中 | -| .section .foo | 自定义段,之后跟的符号都在.foo段中,.foo段名可以做修改 | -| .align n | 按2的n次幂字节对齐 | -| .balign n | 按n字节对齐 | -| .globl sym | 声明sym未全局符号,其它文件可以访问 | -| .string “str” | 将字符串str放入内存 | -| .byte b1,…,bn | 在内存中连续存储n个单字节 | -| .half w1,…,wn | 在内存中连续存储n个半字(2字节) | -| .word w1,…,wn | 在内存中连续存储n个字(4字节) | -| .dword w1,…,wn | 在内存中连续存储n个双字(8字节) | -| .float f1,…,fn | 在内存中连续存储n个单精度浮点数 | -| .double d1,…,dn | 在内存中连续存储n个双精度浮点数 | -| .option rvc | 使用压缩指令(risc-v c) | -| .option norvc | 不压缩指令 | -| .option relax | 允许链接器松弛(linker relaxation,链接时多次扫描代码,尽可能将跳转两条指令替换为一条) | -| .option norelax | 不允许链接松弛 | -| .option pic | 与位置无关代码段 | -| .option nopic | 与位置有关代码段 | -| .option push | 将所有.option设置存入栈 | -| .option pop | 从栈中弹出上次存入的.option设置 | +| 指示符 | 作用 | +| :--------------- | :------------------------------------------------------------------------------------ | +| .text | 代码段,之后跟的符号都在.text内 | +| .data | 数据段,之后跟的符号都在.data内 | +| .bss | 未初始化数据段,之后跟的符号都在.bss中 | +| .section .foo | 自定义段,之后跟的符号都在.foo段中,.foo段名可以做修改 | +| .align n | 按2的n次幂字节对齐 | +| .balign n | 按n字节对齐 | +| .globl sym | 声明sym未全局符号,其它文件可以访问 | +| .string “str” | 将字符串str放入内存 | +| .byte b1,…,bn | 在内存中连续存储n个单字节 | +| .half w1,…,wn | 在内存中连续存储n个半字(2字节) | +| .word w1,…,wn | 在内存中连续存储n个字(4字节) | +| .dword w1,…,wn | 在内存中连续存储n个双字(8字节) | +| .float f1,…,fn | 在内存中连续存储n个单精度浮点数 | +| .double d1,…,dn | 在内存中连续存储n个双精度浮点数 | +| .option rvc | 使用压缩指令(risc-v c) | +| .option norvc | 不压缩指令 | +| .option relax | 允许链接器松弛(linker relaxation,链接时多次扫描代码,尽可能将跳转两条指令替换为一条) | +| .option norelax | 不允许链接松弛 | +| .option pic | 与位置无关代码段 | +| .option nopic | 与位置有关代码段 | +| .option push | 将所有.option设置存入栈 | +| .option pop | 从栈中弹出上次存入的.option设置 | ## fw_base diff --git a/articles/images/sbi-firmware/instruction-spec.png b/articles/images/sbi-firmware/instruction-spec.png new file mode 100644 index 0000000000000000000000000000000000000000..e8adbab57331f39254f798ca05f72691b99742d7 GIT binary patch literal 111317 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 GIYMAKb#+yAv)h{Zvhbt?BG4P7%E@@H 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 UMbfZF5@2XP(7j)-W*hPU0L)>9#sB~S literal 0 HcmV?d00001 -- Gitee From 4edeb5dc552ffd817074f8483183ee326096ac75 Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Wed, 2 Aug 2023 07:09:23 +0000 Subject: [PATCH 16/68] sbi-firemware-analyze-1.md: commit correct result of tinycorrect-spaces Signed-off-by: Groot <1219671600@qq.com> --- articles/20230728-sbi-firemware-analyze-1.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/articles/20230728-sbi-firemware-analyze-1.md b/articles/20230728-sbi-firemware-analyze-1.md index ce94883..34f8cf8 100644 --- a/articles/20230728-sbi-firemware-analyze-1.md +++ b/articles/20230728-sbi-firemware-analyze-1.md @@ -1,8 +1,8 @@ -> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [spaces comments tables]`
` -> Author: groot `
` -> Date: 2023/07/28`
` -> Revisor: Falcon [falcon@tinylab.org](mailto:falcon@tinylab.org)`
` -> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux)`
` +> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [spaces]
+> Author: groot
+> Date: 2023/07/28
+> Revisor: Falcon [falcon@tinylab.org](mailto:falcon@tinylab.org)
+> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux)
> Proposal: [RISC-V Linux 内核 SBI 调用技术分析](https://gitee.com/tinylab/riscv-linux/issues/I64YC4) `
` > Sponsor: PLCT Lab, ISCAS -- Gitee From 9dc6ab1fbe51586a7eb6ac17f8b3dc0b0ccc9601 Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Wed, 2 Aug 2023 07:09:28 +0000 Subject: [PATCH 17/68] sbi-firemware-analyze-1.md: commit correct result of tinycorrect-tables Signed-off-by: Groot <1219671600@qq.com> --- articles/20230728-sbi-firemware-analyze-1.md | 78 ++++++++++---------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/articles/20230728-sbi-firemware-analyze-1.md b/articles/20230728-sbi-firemware-analyze-1.md index 34f8cf8..309f0ac 100644 --- a/articles/20230728-sbi-firemware-analyze-1.md +++ b/articles/20230728-sbi-firemware-analyze-1.md @@ -1,4 +1,4 @@ -> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [spaces]
+> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [spaces tables]
> Author: groot
> Date: 2023/07/28
> Revisor: Falcon [falcon@tinylab.org](mailto:falcon@tinylab.org)
@@ -48,20 +48,20 @@ make PLATFORM= FW_OPTIONS= risc-v 有 32 个通用寄存器(简写 reg),标号为 `x0` - `x31` -| 寄存器 | 编程接口名称(ABI) | 描述 | 使用 | -| ------ | ------------------- | ------------------------------- | ------------------------------------ | -| x0 | zero | Hard-wired zero | 硬件零 | -| x1 | ra | Return address | 常用于保存(函数的)返回地址 | -| x2 | sp | Stack pointer | 栈顶指针 | -| x3 | gp | Global pointer | — | -| x4 | tp | Thread pointer | — | -| x5-7 | t0-2 | Temporary | 临时寄存器 | -| x8 | s0/fp | Saved Register/ Frame pointer | (函数调用时)保存的寄存器和栈顶指针 | -| x9 | s1 | Saved register | (函数调用时)保存的寄存器 | -| x10-11 | a0-1 | Function argument/ return value | (函数调用时)的参数/函数的返回值 | -| x12-17 | a2-7 | Function argument | (函数调用时)的参数 | -| x18-27 | s2-11 | Saved register | (函数调用时)保存的寄存器 | -| x28-31 | t3-6 | Temporary | 临时寄存器 | +| 寄存器 | 编程接口名称(ABI) | 描述 | 使用 | +|--------|-------------------|---------------------------------|------------------------------------| +| x0 | zero | Hard-wired zero | 硬件零 | +| x1 | ra | Return address | 常用于保存(函数的)返回地址 | +| x2 | sp | Stack pointer | 栈顶指针 | +| x3 | gp | Global pointer | — | +| x4 | tp | Thread pointer | — | +| x5-7 | t0-2 | Temporary | 临时寄存器 | +| x8 | s0/fp | Saved Register/ Frame pointer | (函数调用时)保存的寄存器和栈顶指针 | +| x9 | s1 | Saved register | (函数调用时)保存的寄存器 | +| x10-11 | a0-1 | Function argument/ return value | (函数调用时)的参数/函数的返回值 | +| x12-17 | a2-7 | Function argument | (函数调用时)的参数 | +| x18-27 | s2-11 | Saved register | (函数调用时)保存的寄存器 | +| x28-31 | t3-6 | Temporary | 临时寄存器 | ### 指令格式 @@ -84,30 +84,30 @@ RISC-V 指令具有六种基本指令格式: RISC-V 的汇编指示符和作用如下 -| 指示符 | 作用 | -| :--------------- | :------------------------------------------------------------------------------------ | -| .text | 代码段,之后跟的符号都在.text内 | -| .data | 数据段,之后跟的符号都在.data内 | -| .bss | 未初始化数据段,之后跟的符号都在.bss中 | -| .section .foo | 自定义段,之后跟的符号都在.foo段中,.foo段名可以做修改 | -| .align n | 按2的n次幂字节对齐 | -| .balign n | 按n字节对齐 | -| .globl sym | 声明sym未全局符号,其它文件可以访问 | -| .string “str” | 将字符串str放入内存 | -| .byte b1,…,bn | 在内存中连续存储n个单字节 | -| .half w1,…,wn | 在内存中连续存储n个半字(2字节) | -| .word w1,…,wn | 在内存中连续存储n个字(4字节) | -| .dword w1,…,wn | 在内存中连续存储n个双字(8字节) | -| .float f1,…,fn | 在内存中连续存储n个单精度浮点数 | -| .double d1,…,dn | 在内存中连续存储n个双精度浮点数 | -| .option rvc | 使用压缩指令(risc-v c) | -| .option norvc | 不压缩指令 | -| .option relax | 允许链接器松弛(linker relaxation,链接时多次扫描代码,尽可能将跳转两条指令替换为一条) | -| .option norelax | 不允许链接松弛 | -| .option pic | 与位置无关代码段 | -| .option nopic | 与位置有关代码段 | -| .option push | 将所有.option设置存入栈 | -| .option pop | 从栈中弹出上次存入的.option设置 | +| 指示符 | 作用 | +|:----------------|:----------------------------------------------------------------------------------| +| .text | 代码段,之后跟的符号都在.text内 | +| .data | 数据段,之后跟的符号都在.data内 | +| .bss | 未初始化数据段,之后跟的符号都在.bss中 | +| .section .foo | 自定义段,之后跟的符号都在.foo段中,.foo段名可以做修改 | +| .align n | 按2的n次幂字节对齐 | +| .balign n | 按n字节对齐 | +| .globl sym | 声明sym未全局符号,其它文件可以访问 | +| .string “str” | 将字符串str放入内存 | +| .byte b1,…,bn | 在内存中连续存储n个单字节 | +| .half w1,…,wn | 在内存中连续存储n个半字(2字节) | +| .word w1,…,wn | 在内存中连续存储n个字(4字节) | +| .dword w1,…,wn | 在内存中连续存储n个双字(8字节) | +| .float f1,…,fn | 在内存中连续存储n个单精度浮点数 | +| .double d1,…,dn | 在内存中连续存储n个双精度浮点数 | +| .option rvc | 使用压缩指令(risc-v c) | +| .option norvc | 不压缩指令 | +| .option relax | 允许链接器松弛(linker relaxation,链接时多次扫描代码,尽可能将跳转两条指令替换为一条) | +| .option norelax | 不允许链接松弛 | +| .option pic | 与位置无关代码段 | +| .option nopic | 与位置有关代码段 | +| .option push | 将所有.option设置存入栈 | +| .option pop | 从栈中弹出上次存入的.option设置 | ## fw_base -- Gitee From 29f5370c5e395a991efae673ac1238405660f002 Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Wed, 2 Aug 2023 07:09:39 +0000 Subject: [PATCH 18/68] sbi-firemware-analyze-1.md: commit correct result of tinycorrect-urls Signed-off-by: Groot <1219671600@qq.com> --- articles/20230728-sbi-firemware-analyze-1.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/articles/20230728-sbi-firemware-analyze-1.md b/articles/20230728-sbi-firemware-analyze-1.md index 309f0ac..c53bf43 100644 --- a/articles/20230728-sbi-firemware-analyze-1.md +++ b/articles/20230728-sbi-firemware-analyze-1.md @@ -1,4 +1,4 @@ -> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [spaces tables]
+> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [spaces tables urls]
> Author: groot
> Date: 2023/07/28
> Revisor: Falcon [falcon@tinylab.org](mailto:falcon@tinylab.org)
@@ -16,7 +16,7 @@ ## 三种固件类型 -该内容链接到[《QEMU 启动方式分析(4): OpenSBI 固件分析与 SBI 规范的 HSM 扩展》](https://tinylab.org/opensbi-firmware-and-sbi-hsm/)的第四部分,这里不做过多的赘述。 +该内容链接到[《QEMU 启动方式分析(4): OpenSBI 固件分析与 SBI 规范的 HSM 扩展》][003]的第四部分,这里不做过多的赘述。 简而言之就是下面的内容: @@ -78,7 +78,7 @@ RISC-V 指令具有六种基本指令格式: 这里不再花费篇幅给大家介绍常见指令,大家可以通过互联网学习相关内容。 -对于初学者,我推荐可以先从[《rvbook》](https://github.com/Lingrui98/RISC-V-book/blob/master/rvbook.pdf)开始学起。 +对于初学者,我推荐可以先从[《rvbook》][001]开始学起。 ### 汇编指示符 @@ -1068,5 +1068,9 @@ fw_fdt_bin: ## 参考资料 - RISC-V 手册 -- [OpenSBI 官方仓库固件部分文档](https://github.com/riscv-software-src/opensbi/tree/master/docs/firmware) +- [OpenSBI 官方仓库固件部分文档][002] - OpenSBI 源代码 + +[001]: https://github.com/Lingrui98/RISC-V-book/blob/master/rvbook.pdf +[002]: https://github.com/riscv-software-src/opensbi/tree/master/docs/firmware +[003]: https://tinylab.org/opensbi-firmware-and-sbi-hsm/ -- Gitee From cfdc2dfc5fb068169b67842daf59637b2a5ffb93 Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Wed, 2 Aug 2023 07:09:49 +0000 Subject: [PATCH 19/68] sbi-firemware-analyze-1.md: commit correct result of tinycorrect-refs Signed-off-by: Groot <1219671600@qq.com> --- articles/20230728-sbi-firemware-analyze-1.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/articles/20230728-sbi-firemware-analyze-1.md b/articles/20230728-sbi-firemware-analyze-1.md index c53bf43..dbe2788 100644 --- a/articles/20230728-sbi-firemware-analyze-1.md +++ b/articles/20230728-sbi-firemware-analyze-1.md @@ -1,4 +1,4 @@ -> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [spaces tables urls]
+> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [spaces tables urls refs]
> Author: groot
> Date: 2023/07/28
> Revisor: Falcon [falcon@tinylab.org](mailto:falcon@tinylab.org)
@@ -1067,9 +1067,11 @@ fw_fdt_bin: ## 参考资料 +- OpenSBI 源代码 - RISC-V 手册 +- [Lingrui98/RISC-V-book/blob/master/rvbook.pdf][001] - [OpenSBI 官方仓库固件部分文档][002] -- OpenSBI 源代码 +- [opensbi-firmware-and-sbi-hsm][003] [001]: https://github.com/Lingrui98/RISC-V-book/blob/master/rvbook.pdf [002]: https://github.com/riscv-software-src/opensbi/tree/master/docs/firmware -- Gitee From 41b5450369f4cb9d1153874f0baaa2323022a502 Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Wed, 2 Aug 2023 07:10:11 +0000 Subject: [PATCH 20/68] sbi-firemware-analyze-1.md: commit correct result of tinycorrect-pangu Signed-off-by: Groot <1219671600@qq.com> --- articles/20230728-sbi-firemware-analyze-1.md | 50 ++++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/articles/20230728-sbi-firemware-analyze-1.md b/articles/20230728-sbi-firemware-analyze-1.md index dbe2788..2da1df7 100644 --- a/articles/20230728-sbi-firemware-analyze-1.md +++ b/articles/20230728-sbi-firemware-analyze-1.md @@ -1,4 +1,4 @@ -> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [spaces tables urls refs]
+> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [spaces tables urls refs pangu]
> Author: groot
> Date: 2023/07/28
> Revisor: Falcon [falcon@tinylab.org](mailto:falcon@tinylab.org)
@@ -86,28 +86,28 @@ RISC-V 的汇编指示符和作用如下 | 指示符 | 作用 | |:----------------|:----------------------------------------------------------------------------------| -| .text | 代码段,之后跟的符号都在.text内 | -| .data | 数据段,之后跟的符号都在.data内 | -| .bss | 未初始化数据段,之后跟的符号都在.bss中 | -| .section .foo | 自定义段,之后跟的符号都在.foo段中,.foo段名可以做修改 | -| .align n | 按2的n次幂字节对齐 | -| .balign n | 按n字节对齐 | -| .globl sym | 声明sym未全局符号,其它文件可以访问 | -| .string “str” | 将字符串str放入内存 | -| .byte b1,…,bn | 在内存中连续存储n个单字节 | -| .half w1,…,wn | 在内存中连续存储n个半字(2字节) | -| .word w1,…,wn | 在内存中连续存储n个字(4字节) | -| .dword w1,…,wn | 在内存中连续存储n个双字(8字节) | -| .float f1,…,fn | 在内存中连续存储n个单精度浮点数 | -| .double d1,…,dn | 在内存中连续存储n个双精度浮点数 | +| .text | 代码段,之后跟的符号都在.text 内 | +| .data | 数据段,之后跟的符号都在.data 内 | +| .bss | 未初始化数据段,之后跟的符号都在.bss 中 | +| .section .foo | 自定义段,之后跟的符号都在.foo 段中,.foo 段名可以做修改 | +| .align n | 按 2 的 n 次幂字节对齐 | +| .balign n | 按 n 字节对齐 | +| .globl sym | 声明 sym 未全局符号,其它文件可以访问 | +| .string “str” | 将字符串 str 放入内存 | +| .byte b1,…,bn | 在内存中连续存储 n 个单字节 | +| .half w1,…,wn | 在内存中连续存储 n 个半字(2 字节) | +| .word w1,…,wn | 在内存中连续存储 n 个字(4 字节) | +| .dword w1,…,wn | 在内存中连续存储 n 个双字(8 字节) | +| .float f1,…,fn | 在内存中连续存储 n 个单精度浮点数 | +| .double d1,…,dn | 在内存中连续存储 n 个双精度浮点数 | | .option rvc | 使用压缩指令(risc-v c) | | .option norvc | 不压缩指令 | -| .option relax | 允许链接器松弛(linker relaxation,链接时多次扫描代码,尽可能将跳转两条指令替换为一条) | +| .option relax | 允许链接器松弛(linker relaxation,链接时多次扫描代码,尽可能将跳转两条指令替换为一条) | | .option norelax | 不允许链接松弛 | | .option pic | 与位置无关代码段 | | .option nopic | 与位置有关代码段 | -| .option push | 将所有.option设置存入栈 | -| .option pop | 从栈中弹出上次存入的.option设置 | +| .option push | 将所有.option 设置存入栈 | +| .option pop | 从栈中弹出上次存入的.option 设置 | ## fw_base @@ -197,10 +197,10 @@ _try_lottery: lla a6, _relocate_lottery li a7, 1 # 原子操作 -# a6指向的地址上的值(_relocate_lottery的地址)做原子加1,_relocate_lottery的老值写入a6。 +# a6 指向的地址上的值(_relocate_lottery 的地址)做原子加 1,_relocate_lottery 的老值写入 a6。 amoadd.w a6, a7, (a6) -# _relocate_lottery不等于0,就跳到boot hart做完重定位的地方。 -# 如果多个核一起启动执行,只有最先执行上面原子加指令的核的a6(_relocate_lottery初始值)是0, +# _relocate_lottery 不等于 0,就跳到 boot hart 做完重定位的地方。 +# 如果多个核一起启动执行,只有最先执行上面原子加指令的核的 a6(_relocate_lottery 初始值)是 0, # 所以,后续执行到这里的核都是从核,直接跳到重定位完成的地址。 bnez a6, _wait_relocate_copy_done @@ -467,7 +467,7 @@ _scratch_init: sub tp, tp, s9 # t1 首次是 0,计算出来的 a5 也等于 0, # 这个 t1 是 hart 的编号,s8 是每个核的栈大小 -# 所以,a5是每个hart的栈的偏移。 +# 所以,a5 是每个 hart 的栈的偏移。 mul a5, s8, t1 sub tp, tp, a5 li a5, SBI_SCRATCH_SIZE @@ -489,7 +489,7 @@ _scratch_init: REG_S s10, SBI_SCRATCH_FW_HEAP_OFFSET(tp) REG_S s9, SBI_SCRATCH_FW_HEAP_SIZE_OFFSET(tp) -# 设置函数:加载下一个阶段的参数1 的地址 +# 设置函数:加载下一个阶段的参数 1 的地址 /* Store next arg1 in scratch space */ MOV_3R s0, a0, s1, a1, s2, a2 call fw_next_arg1 @@ -631,8 +631,8 @@ _wait_for_boot_hart: # 热启动 # 主从核全部要跑下面的代码 -# 无非是从核先在_wait_for_boot_hart那里等待, -# 直到主核继续执行完一些公共的处理逻辑,比如上面的bss清理,scratch内存初始化,dtb重定位等。 +# 无非是从核先在_wait_for_boot_hart 那里等待, +# 直到主核继续执行完一些公共的处理逻辑,比如上面的 bss 清理,scratch 内存初始化,dtb 重定位等。 _start_warm: /* Reset all registers for non-boot HARTs */ li ra, 0 -- Gitee From dea6de4cf1f3fec433e66ddca88899785bcc69a9 Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Wed, 2 Aug 2023 07:10:25 +0000 Subject: [PATCH 21/68] sbi-firemware-analyze-1.md: commit correct result of tinycorrect-autocorrect Signed-off-by: Groot <1219671600@qq.com> --- articles/20230728-sbi-firemware-analyze-1.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/articles/20230728-sbi-firemware-analyze-1.md b/articles/20230728-sbi-firemware-analyze-1.md index 2da1df7..d7ab078 100644 --- a/articles/20230728-sbi-firemware-analyze-1.md +++ b/articles/20230728-sbi-firemware-analyze-1.md @@ -1,4 +1,4 @@ -> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [spaces tables urls refs pangu]
+> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [spaces tables urls refs pangu autocorrect]
> Author: groot
> Date: 2023/07/28
> Revisor: Falcon [falcon@tinylab.org](mailto:falcon@tinylab.org)
@@ -16,7 +16,7 @@ ## 三种固件类型 -该内容链接到[《QEMU 启动方式分析(4): OpenSBI 固件分析与 SBI 规范的 HSM 扩展》][003]的第四部分,这里不做过多的赘述。 +该内容链接到 [《QEMU 启动方式分析(4): OpenSBI 固件分析与 SBI 规范的 HSM 扩展》][003] 的第四部分,这里不做过多的赘述。 简而言之就是下面的内容: @@ -78,7 +78,7 @@ RISC-V 指令具有六种基本指令格式: 这里不再花费篇幅给大家介绍常见指令,大家可以通过互联网学习相关内容。 -对于初学者,我推荐可以先从[《rvbook》][001]开始学起。 +对于初学者,我推荐可以先从 [《rvbook》][001] 开始学起。 ### 汇编指示符 @@ -100,7 +100,7 @@ RISC-V 的汇编指示符和作用如下 | .dword w1,…,wn | 在内存中连续存储 n 个双字(8 字节) | | .float f1,…,fn | 在内存中连续存储 n 个单精度浮点数 | | .double d1,…,dn | 在内存中连续存储 n 个双精度浮点数 | -| .option rvc | 使用压缩指令(risc-v c) | +| .option rvc | 使用压缩指令 (risc-v c) | | .option norvc | 不压缩指令 | | .option relax | 允许链接器松弛(linker relaxation,链接时多次扫描代码,尽可能将跳转两条指令替换为一条) | | .option norelax | 不允许链接松弛 | -- Gitee From 083531068c1d69b0f4998a7685d5ebdd61fa3d8b Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Wed, 2 Aug 2023 07:12:10 +0000 Subject: [PATCH 22/68] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=B0=8F=E9=94=99?= =?UTF-8?q?=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- articles/20230728-sbi-firemware-analyze-1.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/articles/20230728-sbi-firemware-analyze-1.md b/articles/20230728-sbi-firemware-analyze-1.md index d7ab078..26a5042 100644 --- a/articles/20230728-sbi-firemware-analyze-1.md +++ b/articles/20230728-sbi-firemware-analyze-1.md @@ -72,7 +72,8 @@ RISC-V 指令具有六种基本指令格式: * S 类型指令:用于访存 store 操作; * B 类型指令:用于条件跳转操作; * U 类型指令:用于长立即数操作; -* J 类型指令:用于无条件操作;![RISC-V 指令格式](images/sbi-firmware/instruction-spec.png) +* J 类型指令:用于无条件操作; +![RISC-V 指令格式](images/sbi-firmware/instruction-spec.png) ### 常见指令 -- Gitee From 43c97af2eb7348be5ae4124aaad382e1bdff44fa Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Wed, 2 Aug 2023 07:33:24 +0000 Subject: [PATCH 23/68] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=83=A8=E5=88=86?= =?UTF-8?q?=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- articles/20230728-sbi-firemware-analyze-1.md | 35 +++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/articles/20230728-sbi-firemware-analyze-1.md b/articles/20230728-sbi-firemware-analyze-1.md index 26a5042..59f5305 100644 --- a/articles/20230728-sbi-firemware-analyze-1.md +++ b/articles/20230728-sbi-firemware-analyze-1.md @@ -118,7 +118,35 @@ RISC-V 的汇编指示符和作用如下 ### 代码意义 -该段代码首先执行启动函数,在这个过程中通过之前规定的启动方式(fw_payload, fw_jump, fw_dynamic)找到一个启动核。如果规定了 `FW_PIC = y`,意味着将生成位置无关的固件映像,代码将进行一些重定位工作。 +该段代码首先执行启动函数,在这个过程中通过之前规定的启动方式(fw_payload, fw_jump, fw_dynamic)找到一个启动核。如果规定了 `FW_PIC = y`,意味着将生成位置无关的固件映像,代码将进行一些重定位工作。这一部分读者暂且先不用关心。 + +该文件RISC-V处理器的引导程序,用于启动操作系统或其他固件。 + +代码以汇编语言和宏定义为主,用于初始化处理器,设置运行时环境,并将控制权转移到操作系统或其他固件。下面将解释代码的主要部分: + +- 起始部分:该部分定义了代码的许可证、版权信息和作者。 + +- 宏定义:在这部分,定义了一些宏指令,用于简化代码中的重复操作,例如MOV_3R和MOV_5R宏用于将寄存器中的值复制到另一个寄存器。 + +- _start:这是代码的主要入口点。它开始执行时会查找首选的启动核心(preferred boot HART id)。然后,它调用fw_boot_hart函数,该函数会尝试选择要启动的核心,如果未指定,它将选择一个核心来执行。启动核心将进行一系列初始化操作,然后根据指定的启动参数跳转到适当的代码段。 + +- _try_lottery:这段代码在多核情况下使用。在多个核同时启动时,只有最先执行原子加指令的核心会获得"lottery",其他核心将等待启动核心完成初始化。 + +- _relocate:如果启动核心的加载地址和链接地址不同,将进行重定位。它将把代码从加载地址复制到链接地址,以便在链接地址上运行。这部分代码是用于位置无关执行(FW_PIC=y)的情况,使OpenSBI能够在不同的地址上正确运行。 + +- _relocate_done:在重定位完成后,将在这里标记重定位完成。这对于其他核心等待重定位完成非常重要。 + +- _scratch_init:在这里设置了每个HART(核心)的scratch空间,该空间用于保存处理器的运行时信息。 + +- _start_warm:在这里进行非启动核心的初始化,等待启动核心完成初始化。非启动核心会等待启动核心在主要初始化工作完成后,再进行自己的初始化。 + +- _hartid_to_scratch:用于将HART ID映射到scratch空间的函数。 + +- 其他一些功能:还包括处理中断、初始化FDT(Flattened Device Tree)等功能。 + +这段代码是一个用于RISC-V平台的引导程序,用于初始化处理器和运行时环境,并最终将控制权转移到操作系统或其他固件。它支持多核处理器,并确保每个核心都能正确初始化和运行。 + +### 代码解析 ```asm /* @@ -630,10 +658,9 @@ _wait_for_boot_hart: nop bne t0, t1, _wait_for_boot_hart + # 热启动 -# 主从核全部要跑下面的代码 -# 无非是从核先在_wait_for_boot_hart 那里等待, -# 直到主核继续执行完一些公共的处理逻辑,比如上面的 bss 清理,scratch 内存初始化,dtb 重定位等。 +# 在这里进行非启动核心的初始化,等待启动核心完成初始化。非启动核心会等待启动核心在主要初始化工作完成后,再进行自己的初始化。 _start_warm: /* Reset all registers for non-boot HARTs */ li ra, 0 -- Gitee From bee2fcda2168516511b73013f6953a7174b70265 Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Wed, 2 Aug 2023 07:33:48 +0000 Subject: [PATCH 24/68] sbi-firemware-analyze-1.md: commit correct result of tinycorrect-spaces Signed-off-by: Groot <1219671600@qq.com> --- articles/20230728-sbi-firemware-analyze-1.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/articles/20230728-sbi-firemware-analyze-1.md b/articles/20230728-sbi-firemware-analyze-1.md index 59f5305..c0a7d46 100644 --- a/articles/20230728-sbi-firemware-analyze-1.md +++ b/articles/20230728-sbi-firemware-analyze-1.md @@ -1,4 +1,4 @@ -> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [spaces tables urls refs pangu autocorrect]
+> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [spaces]
> Author: groot
> Date: 2023/07/28
> Revisor: Falcon [falcon@tinylab.org](mailto:falcon@tinylab.org)
@@ -658,7 +658,6 @@ _wait_for_boot_hart: nop bne t0, t1, _wait_for_boot_hart - # 热启动 # 在这里进行非启动核心的初始化,等待启动核心完成初始化。非启动核心会等待启动核心在主要初始化工作完成后,再进行自己的初始化。 _start_warm: -- Gitee From d03d1bf4abe709d9a368d30ac88aa31407c3ee36 Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Wed, 2 Aug 2023 07:33:50 +0000 Subject: [PATCH 25/68] sbi-firemware-analyze-1.md: commit correct result of tinycorrect-tables Signed-off-by: Groot <1219671600@qq.com> --- articles/20230728-sbi-firemware-analyze-1.md | 36 ++++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/articles/20230728-sbi-firemware-analyze-1.md b/articles/20230728-sbi-firemware-analyze-1.md index c0a7d46..eb63ca7 100644 --- a/articles/20230728-sbi-firemware-analyze-1.md +++ b/articles/20230728-sbi-firemware-analyze-1.md @@ -1,4 +1,4 @@ -> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [spaces]
+> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [spaces tables]
> Author: groot
> Date: 2023/07/28
> Revisor: Falcon [falcon@tinylab.org](mailto:falcon@tinylab.org)
@@ -87,28 +87,28 @@ RISC-V 的汇编指示符和作用如下 | 指示符 | 作用 | |:----------------|:----------------------------------------------------------------------------------| -| .text | 代码段,之后跟的符号都在.text 内 | -| .data | 数据段,之后跟的符号都在.data 内 | -| .bss | 未初始化数据段,之后跟的符号都在.bss 中 | -| .section .foo | 自定义段,之后跟的符号都在.foo 段中,.foo 段名可以做修改 | -| .align n | 按 2 的 n 次幂字节对齐 | -| .balign n | 按 n 字节对齐 | -| .globl sym | 声明 sym 未全局符号,其它文件可以访问 | -| .string “str” | 将字符串 str 放入内存 | -| .byte b1,…,bn | 在内存中连续存储 n 个单字节 | -| .half w1,…,wn | 在内存中连续存储 n 个半字(2 字节) | -| .word w1,…,wn | 在内存中连续存储 n 个字(4 字节) | -| .dword w1,…,wn | 在内存中连续存储 n 个双字(8 字节) | -| .float f1,…,fn | 在内存中连续存储 n 个单精度浮点数 | -| .double d1,…,dn | 在内存中连续存储 n 个双精度浮点数 | -| .option rvc | 使用压缩指令 (risc-v c) | +| .text | 代码段,之后跟的符号都在.text 内 | +| .data | 数据段,之后跟的符号都在.data 内 | +| .bss | 未初始化数据段,之后跟的符号都在.bss 中 | +| .section .foo | 自定义段,之后跟的符号都在.foo 段中,.foo 段名可以做修改 | +| .align n | 按 2 的 n 次幂字节对齐 | +| .balign n | 按 n 字节对齐 | +| .globl sym | 声明 sym 未全局符号,其它文件可以访问 | +| .string “str” | 将字符串 str 放入内存 | +| .byte b1,…,bn | 在内存中连续存储 n 个单字节 | +| .half w1,…,wn | 在内存中连续存储 n 个半字(2 字节) | +| .word w1,…,wn | 在内存中连续存储 n 个字(4 字节) | +| .dword w1,…,wn | 在内存中连续存储 n 个双字(8 字节) | +| .float f1,…,fn | 在内存中连续存储 n 个单精度浮点数 | +| .double d1,…,dn | 在内存中连续存储 n 个双精度浮点数 | +| .option rvc | 使用压缩指令 (risc-v c) | | .option norvc | 不压缩指令 | | .option relax | 允许链接器松弛(linker relaxation,链接时多次扫描代码,尽可能将跳转两条指令替换为一条) | | .option norelax | 不允许链接松弛 | | .option pic | 与位置无关代码段 | | .option nopic | 与位置有关代码段 | -| .option push | 将所有.option 设置存入栈 | -| .option pop | 从栈中弹出上次存入的.option 设置 | +| .option push | 将所有.option 设置存入栈 | +| .option pop | 从栈中弹出上次存入的.option 设置 | ## fw_base -- Gitee From 88c45a774501a0f3713f3912af40bb44f8f6c201 Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Wed, 2 Aug 2023 07:34:01 +0000 Subject: [PATCH 26/68] sbi-firemware-analyze-1.md: commit correct result of tinycorrect-pangu Signed-off-by: Groot <1219671600@qq.com> --- articles/20230728-sbi-firemware-analyze-1.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/articles/20230728-sbi-firemware-analyze-1.md b/articles/20230728-sbi-firemware-analyze-1.md index eb63ca7..6b31559 100644 --- a/articles/20230728-sbi-firemware-analyze-1.md +++ b/articles/20230728-sbi-firemware-analyze-1.md @@ -1,4 +1,4 @@ -> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [spaces tables]
+> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [spaces tables pangu]
> Author: groot
> Date: 2023/07/28
> Revisor: Falcon [falcon@tinylab.org](mailto:falcon@tinylab.org)
@@ -120,31 +120,31 @@ RISC-V 的汇编指示符和作用如下 该段代码首先执行启动函数,在这个过程中通过之前规定的启动方式(fw_payload, fw_jump, fw_dynamic)找到一个启动核。如果规定了 `FW_PIC = y`,意味着将生成位置无关的固件映像,代码将进行一些重定位工作。这一部分读者暂且先不用关心。 -该文件RISC-V处理器的引导程序,用于启动操作系统或其他固件。 +该文件 RISC-V 处理器的引导程序,用于启动操作系统或其他固件。 代码以汇编语言和宏定义为主,用于初始化处理器,设置运行时环境,并将控制权转移到操作系统或其他固件。下面将解释代码的主要部分: - 起始部分:该部分定义了代码的许可证、版权信息和作者。 -- 宏定义:在这部分,定义了一些宏指令,用于简化代码中的重复操作,例如MOV_3R和MOV_5R宏用于将寄存器中的值复制到另一个寄存器。 +- 宏定义:在这部分,定义了一些宏指令,用于简化代码中的重复操作,例如 MOV_3R 和 MOV_5R 宏用于将寄存器中的值复制到另一个寄存器。 -- _start:这是代码的主要入口点。它开始执行时会查找首选的启动核心(preferred boot HART id)。然后,它调用fw_boot_hart函数,该函数会尝试选择要启动的核心,如果未指定,它将选择一个核心来执行。启动核心将进行一系列初始化操作,然后根据指定的启动参数跳转到适当的代码段。 +- _start:这是代码的主要入口点。它开始执行时会查找首选的启动核心(preferred boot HART id)。然后,它调用 fw_boot_hart 函数,该函数会尝试选择要启动的核心,如果未指定,它将选择一个核心来执行。启动核心将进行一系列初始化操作,然后根据指定的启动参数跳转到适当的代码段。 - _try_lottery:这段代码在多核情况下使用。在多个核同时启动时,只有最先执行原子加指令的核心会获得"lottery",其他核心将等待启动核心完成初始化。 -- _relocate:如果启动核心的加载地址和链接地址不同,将进行重定位。它将把代码从加载地址复制到链接地址,以便在链接地址上运行。这部分代码是用于位置无关执行(FW_PIC=y)的情况,使OpenSBI能够在不同的地址上正确运行。 +- _relocate:如果启动核心的加载地址和链接地址不同,将进行重定位。它将把代码从加载地址复制到链接地址,以便在链接地址上运行。这部分代码是用于位置无关执行(FW_PIC=y)的情况,使 OpenSBI 能够在不同的地址上正确运行。 - _relocate_done:在重定位完成后,将在这里标记重定位完成。这对于其他核心等待重定位完成非常重要。 -- _scratch_init:在这里设置了每个HART(核心)的scratch空间,该空间用于保存处理器的运行时信息。 +- _scratch_init:在这里设置了每个 HART(核心)的 scratch 空间,该空间用于保存处理器的运行时信息。 - _start_warm:在这里进行非启动核心的初始化,等待启动核心完成初始化。非启动核心会等待启动核心在主要初始化工作完成后,再进行自己的初始化。 -- _hartid_to_scratch:用于将HART ID映射到scratch空间的函数。 +- _hartid_to_scratch:用于将 HART ID 映射到 scratch 空间的函数。 -- 其他一些功能:还包括处理中断、初始化FDT(Flattened Device Tree)等功能。 +- 其他一些功能:还包括处理中断、初始化 FDT(Flattened Device Tree)等功能。 -这段代码是一个用于RISC-V平台的引导程序,用于初始化处理器和运行时环境,并最终将控制权转移到操作系统或其他固件。它支持多核处理器,并确保每个核心都能正确初始化和运行。 +这段代码是一个用于 RISC-V 平台的引导程序,用于初始化处理器和运行时环境,并最终将控制权转移到操作系统或其他固件。它支持多核处理器,并确保每个核心都能正确初始化和运行。 ### 代码解析 -- Gitee From 142c994558d93c105aeffa3ff4748e010aed23c2 Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Thu, 3 Aug 2023 01:54:05 +0000 Subject: [PATCH 27/68] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=96=87=E4=BB=B6=20ar?= =?UTF-8?q?ticles/20230710-sbi-specification-translation.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../20230710-sbi-specification-translation.md | 1864 ----------------- 1 file changed, 1864 deletions(-) delete mode 100644 articles/20230710-sbi-specification-translation.md diff --git a/articles/20230710-sbi-specification-translation.md b/articles/20230710-sbi-specification-translation.md deleted file mode 100644 index ef47efc..0000000 --- a/articles/20230710-sbi-specification-translation.md +++ /dev/null @@ -1,1864 +0,0 @@ -> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [spaces]
-> Title: [RISC-V SBI specification](https://github.com/riscv-non-isa/riscv-sbi-doc/releases/download/v2.0-rc1/riscv-sbi.pdf)
-> Author: riscv.org
-> Translator: 刘澳 <1219671600@qq.com>
-> Date: 2022/07/10
-> Revisor: Falcon
-> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux)
-> Acknowledgements: 山东大学 山东大学智能创新研究院 [RISC-V SBI-1.0.0 版本 中文](https://zhuanlan.zhihu.com/p/634337322)
-> Sponsor: PLCT Lab, ISCAS - -# RISC-V SBI 翻译 - -## 简介 - -本次翻译工作的背景为我正在调研 RISC-V SBI 相关知识,发现 SBI Spec 正在快速更新,目前已经开始更新 2.0 版本。于此同时,我发现国内已经有了 SBI Spec 的翻译工作,不过可能因为某些原因,并没有机构对 SBI Spec 2.0-rc1 进行翻译。我认为这对于我来说是一个很好的了解 SBI Spec 的机会,对于国内想要学习 RISC-V SBI 的研究人员可能也会有些帮助,所以就开始了 SBI Spec 2.0-rc1 的翻译工作。 - -在这里非常感谢山东大学以及山东大学智能创新研究院,他们之前翻译了 SBI Spec 1.0 版本,我的这次翻译是在他们的基础上完成的。在与原翻译人员通过邮件取得沟通后,他们非常慷慨地同意了我使用他们的翻译成果做为基础来进行我的工作。他们的翻译成果发表在 [RISC-V SBI-1.0.0 版本 中文](https://zhuanlan.zhihu.com/p/634337322)。 - -本次翻译相较于前一版本,主要对 SBI Spec 2.0-rc1 版本的新增内容(见“更改日志”一节)进行了翻译,以及对原翻译的格式进行了一些调整,对一些术语进行了统一。 - -## 更改日志 - -### 版本 2.0-rc1 - -- 添加了共享内存物理地址范围参数的通用描述 -- 添加了 SBI 调试控制台扩展 -- 放宽了 SBI PMU 固件计数器的计数器宽度要求 -- 在 SBI PMU 扩展中添加了 `sbi_pmu_counter_fw_read_hi()` -- 为 SBI 实现特定固件事件保留空间 -- 添加了 SBI 系统挂起扩展 -- 添加了 SBI CPPC 扩展 -- 阐明了 SBI 扩展只有在定义发现已实现 SBI 的机制时才能部分实现函数 -- 添加了错误代码 SBI_ERR_NO_SHMEM -- 添加了 SBI 嵌套加速扩展 -- 添加了虚拟 HART 的通用描述 -- 添加了 SBI 隐身时间记帐扩展 -- 添加了 SBI PMU 快照扩展 - -### 版本 1.0.0 - -- 更新批准版本 - -### 版本 1.0-rc3 - -- 更新调用约定 -- 修复了 PMU 扩展名中的拼写错误 -- 添加缩写表 - -### 版本 1.0-rc2 - -- 更新 RISC-V 格式 -- 修改介绍部分 -- 删除了所有 RV32 引用 - -### 版本 1.0-rc1 - -- 错别字修正 - 版本 0.3.0 - -### 版本 0.3.0 - -- 少量错别字修正 -- 文本更新 LICENSE 部分的超链接 - -### 版本 0.3-rc1 - -- 改进文档样式和命名约定 -- 添加 SBI 系统重置扩展 -- 修改 SBI 介绍部分 -- 修改 SBI hart 状态管理扩展文档 -- 为 SBI hart 状态管理扩展添加挂起 -- 添加性能监控单元扩展 -- 阐明一个不应局部实现的 SBI 扩展 - -### 版本 0.2 - -- 整个 v0.1 SBI 已移动至旧版扩展,现已是可选扩展。 - -从技术上讲,这是一个向后不兼容的更改,因为遗留扩展是可选的,并且 SBI 的 v0.1 不允许探测,但我们已经尽力了。 - -## 章节 1. 介绍 - -本规范描述了 RISC-V Supervisor Binary Interface,简称为 SBI。SBI 允许在所有 RISC-V 实现上,通过定义平台(或虚拟化管理程序)特定功能的抽象,使监督者模式(S 模式或 VS 模式)的软件具备可移植性。SBI 的设计遵循 RISC-V 的一般原则,即核心部分小而精简,同时具备一组可选的模块化扩展功能。 -SBI 扩展作为整体是可选的,但不允许部分实现。如果 sbi_probe_extension() 表明某个扩展可用,那么 sbi_get_spec_version() 报告的 SBI 版本中的所有函数必须符合该版本的 SBI 规范。 -提供给监管模式软件的更高特权级软件称为 SBI 实现或 Supervisor Execution Environment(SEE)。SBI 实现(或 SEE)可以是在机器模式(M-mode)下执行的平台运行时固件(参见下图 1),也可以是在超级监管模式(HS-mode)下执行的某个虚拟化监管程序(参见下图 2)。 - -![fig1.jpg](images/sbi-specification-translation/fig1.jpg) - -图 1.无 H 扩展 RISC-V 系统 - -![fig2.jpg](images/sbi-specification-translation/fig2.jpg) - -图 2.带 H 扩展 RISC-V 系统 - -SBI 规范不指定硬件发现的任何方法。监督者软件必须依赖其他行业标准的硬件发现方法(例如设备树或 ACPI)来实现。 - -## 章节 2. 术语和缩写 - -本规范使用了下列术语及缩写: - -| 术语 | 含义 | -| :--- | --------------------------------------------------------------- | -| ACPI | 高级配置和电源接口 (Advanced Configuration and Power Interface) | -| ASID | 地址空间标识符 (Address Space Identifier) | -| BMC | 底板管理控制器 (Baseboard Management Controller) | -| CPPC | 协处理器性能控制 (Collaborative Processor Performance Control) | -| EID | 扩展号 (Extension ID) | -| FID | 函数号 (Function ID) | -| HSM | 核心状态管理 (Hart State Management) | -| IPI | 处理器核间中断 (Inter Processor Interrupt) | -| PMU | 性能监控单元 (Performance Monitoring Unit) | -| SBI | 监管二进制接口 (Supervisor Binary Interface) | -| SEE | 监管执行环境 (Supervisor Execution Environment) | -| VMID | 虚拟机标识符 (Virtual Machine Identifier) | - -## 章节 3. 二进制编码 - -所有的 SBI 函数共享一种二进制编码方式,这有助于混合使用 SBI 扩展功能。SBI 规范遵循以下调用约定。 - -- 在监督者和 SEE 之间,使用 ECALL 作为控制传输指令。 -- a7 编码 SBI 扩展 ID(EID) -- a6 编码 SBI 函数 ID(FID),对于任何在 a7 中编码的 SBI 扩展,其定义在 SBI v0.2 之后。 -- 在 SBI 调用期间,除了 a0 和 a1 寄存器外,所有寄存器都必须由被调用方保留。 -- SBI 函数必须在 a0 和 a1 中返回一对值,其中 a0 返回错误代码。类似于返回 C 结构体。 - -``` -struct sbiret -{ - long error; - long value; -}; -``` - -为了保持兼容性,SBI 扩展 ID(EID)和 SBI 函数 ID(FID)被编码为有符号的 32 位整数。当以寄存器形式传递时,遵循上述标准的调用约定规则。 -表 1 列出了标准 SBI 错误代码。 - -_表 1.标准 SBI 错误_ - -| 错误类型 | 值 | 描述 | -| :------------------------ | -- | :------------- | -| SBI_SUCCESS | 0 | 顺利完成 | -| SBI_ERR_FAILED | -1 | 失败 | -| SBI_ERR_NOT_SUPPORTED | -2 | 不支持操作 | -| SBI_ERR_INVALID_PARAM | -3 | 非法参数 | -| SBI_ERR_DENIED | -4 | 拒绝 | -| SBI_ERR_INVALID_ADDRESS | -5 | 非法地址 | -| SBI_ERR_ALREADY_AVAILABLE | -6 | (资源)已可用 | -| SBI_ERR_ALREADY_STARTED | -7 | (操作)已启动 | -| SBI_ERR_ALREADY_STOPPED | -8 | (操作)已停止 | -| SBI_ERR_NO_SHMEM | -9 | 共享内存不可用 | - -不支持的 SBI 扩展 ID(EID)或 SBI 函数 ID(FID)的 ECALL 必须返回错误代码 SBI_ERR_NOT_SUPPORTED。 -每个 SBI 函数应该优先选择无符号长整型 unsigned long 作为数据类型。这使得规范简单且易于适应所有 RISC-V ISA 类型。如果数据被定义为 32 位宽度,则更高权限的软件必须确保只使用 32 位数据。 - -### 3.1 HART 列表参数 - -如果 SBI 函数需要将一组 hart 传递给更高权限模式,它必须使用如下所定义的 hart 掩码。这适用于在 v0.2 版本之后定义的任何扩展。 -任何需要 hart 掩码的函数需要传递以下两个参数。 - -- unsigned long hart_mask 是一个包含 hartid 的标量位向量。 -- unsigned long hart_mask_base 是计算位向量的起始 hartid。 - -在单个 SBI 函数调用中,可以设置的最大 hart 数量始终为 XLEN。如果较低特权模式需要传递超过 XLEN 个 hart 的信息,它应该调用多个 SBI 函数调用的实例。hart_mask_base 可以设置为-1,表示可以忽略 hart_mask 并考虑所有可用的 harts。 -使用 hart 掩码的任何函数可能返回表 2 中列出的错误值,这些错误值是针对特定函数的错误值的补充。 - -_表 2. HART 掩码错误_ - -| 错误代码 | 描述 | -| :-------------------- | :----------------------------------------------------------------------------------------------- | -| SBI_ERR_INVALID_PARAM | hart_mask_base 或 hart_mask 中任何一个 hartid 无效,即该 hartid 未被平台启用或对监管程序不可用。 | - -### 3.2 共享内存物理地址范围参数 - -如果 SBI 功能需要将共享内存物理地址范围传递给 SBI 实现(或更高特权模式),则该物理内存地址范围必须满足以下要求: - -- SBI 实现必须检查监管模式软件是否允许使用请求的访问类型(读和/或写)访问指定的物理内存范围。 -- SBI 实现必须使用 PMA 属性访问指定的物理内存范围。 - -> 注意:如果监督者模式的软件使用与 PMA 不同的内存类型访问相同的物理内存范围,就可能发生一致性丢失或意外的内存排序。调用的软件应遵循 RISC-V Svpbmt 规范中定义的规则和顺序,以防止一致性丢失和内存排序问题的发生。 - -- 在共享内存中的数据必须遵循小端字节顺序。 - -建议将传递给 SBI 函数的内存物理地址至少无符号长整形参数,以支持具有大于 XLEN 位的内存物理地址的平台。 - -## 章节 4. 基本扩展 (EID #0x10) - -基本扩展旨在尽可能简洁。因此,它仅包含用于探测可用的 SBI 扩展以及查询 SBI 版本的功能。基本扩展中的所有函数必须由所有 SBI 实现支持,因此没有定义错误返回值。 - -### 4.1 函数:获取 SBI 规范版本 (FID #0) - -``` -struct sbiret sbi_get_spec_version(void); -``` - -返回当前的 SBI 规范版本。此函数必定成功。SBI 规范的次版本号编码在低 24 位中,主版本号编码在接下来的 7 位中。第 31 位必须为 0,保留用于未来扩展。 - -### 4.2 函数:获取 SBI 实现标识符 (FID #1) - -``` -struct sbiret sbi_get_impl_id(void); -``` - -返回当前 SBI 实现的标识符,每个 SBI 实现的标识符都是不同的。这个标识符旨在让软件探测 SBI 实现的特殊问题或特点。 - -### 4.3 函数:获取 SBI 实现版本 (FID #2) - -``` -struct sbiret sbi_get_impl_version(void); -``` - -返回当前 SBI 实现的版本。该版本号的编码是特定于 SBI 实现的。 - -### 4.4 函数:探测 SBI 扩展功能 (FID #3) - -``` -struct sbiret sbi_probe_extension(long extension_id); -``` - -如果给定的 SBI 扩展 ID (EID) 不可用,则返回 0;如果可用,返回值应为 1,或为特定 SBI 实现定义的其他非 0 值。 - -### 4.5 函数:获取机器供应商标识符 (FID #4) - -``` -struct sbiret sbi_get_mvendorid(void); -``` - -返回一个合法的 mvendorid CSR 值,其中 0 总是一个合法的值。mvendorid CSR 是一个用于标识机器供应商的控制状态寄存器,它用于表示底层硬件的供应商或制造商。 - -### 4.6 函数:获取机器体系结构标识符 (FID #5) - -``` -struct sbiret sbi_get_marchid(void); -``` - -返回一个在 marchid CSR 中合法的值,其中 0 总是合法的值。marchid CSR 是 RISC-V 架构中的一个控制状态寄存器,用于标识机器体系结构。 - -### 4.7 函数:获取机器实现标识符 ID (FID #6) - -``` -struct sbiret sbi_get_mimpid(void); -``` - -返回一个在 mimpid CSR 中合法的值,而且对于该 CSR,0 始终是一个合法的值。 - -### 4.8.函数列表 - -_表 3.基础函数列表_ - -| 函数名 | SBI 版本 | FID | EID | -| :----------------------- | -------- | --- | ---- | -| sbi_get_sbi_spec_version | 0.2 | 0 | 0x10 | -| sbi_get_sbi_impl_id | 0.2 | 1 | 0x10 | -| sbi_get_sbi_impl_version | 0.2 | 2 | 0x10 | -| sbi_probe_extension | 0.2 | 3 | 0x10 | -| sbi_get_mvendorid | 0.2 | 4 | 0x10 | -| sbi_get_marchid | 0.2 | 5 | 0x10 | -| sbi_get_mimpid | 0.2 | 6 | 0x10 | - -### 4.9 SBI 实现标识符 - -_表 4. SBI 实现 IDs_ - -| SBI 实现 ID | 名称 | -| :---------- | -------------------------- | -| 0 | Berkeley Boot Loader (BBL) | -| 1 | OpenSBI | -| 2 | Xvisor | -| 3 | KVM | -| 4 | RustSBI | -| 5 | Diosix | -| 6 | Coffer | -| 7 | Xen Project | - -## 章节 5. 旧版扩展 (EIDs #0x00- #0x0F) - -传统的 SBI 扩展与 SBI v0.2(或更高版本)规范相比,遵循略微不同的调用约定,其中: - -- a6 寄存器中的 SBI 函数 ID 字段被忽略,因为这些被编码为多个 SBI 扩展 ID。 -- a1 寄存器中不返回任何值。 -- 在 SBI 调用期间,除 a0 寄存器外的所有寄存器都必须由被调用者保留。 -- a0 寄存器中返回的值是特定于 SBI 传统扩展的。 - -SBI 实现在监督者访问内存时发生的页面和访问故障会被重定向回监督者,并且 sepc 寄存器指向故障的 ECALL 指令。 - -传统 SBI 扩展已被以下扩展所取代。传统的控制台 SBI 函数(sbi_console_getchar() 和 sbi_console_putchar())预计将被弃用;它们没有替代方案。 - -### 5.1 扩展:设置时钟 (EID #0x00) - -``` -long sbi_set_timer(uint64_t stime_value) -``` - -设置时钟,在 stime_value 时间之后进行下一次事件。此功能还清除待处理的计时器中断位。 -如果监管程序希望清除计时器中断但不安排下一个计时器事件,则可以将计时器中断请求设置为无限远(即(uint64_t)-1),或者通过清除 sie.STIE 寄存器位来屏蔽计时器中断。 -此 SBI 调用在成功时返回 0,否则返回实现特定的负错误代码。 - -### 5.2 扩展:控制台字符输出 (EID #0x01) - -``` -long sbi_console_putchar(int ch) -``` - -将 **ch** 中的数据写入控制台。 -与 sbi_console_getchar() 不同的是,如果仍有任何待传输的字符或接收终端还没有准备好接收字节,则此 SBI 调用将阻塞。但是,如果控制台根本不存在,则字符将被丢弃。 -此 SBI 调用在成功时返回 0,否则返回实现特定的负错误代码。 - -### 5.3 扩展:控制台字符输入 (EID #0x02) - -``` -long sbi_console_getchar(void) -``` - -从调试控制台中读一个字符。 -此 SBI 调用在成功时返回 0,否则返回实现特定的负错误代码。 - -### 5.4 扩展:清除 IPI (EID #0x03) - -``` -long sbi_clear_ipi(void) -``` - -清除任何挂起的 IPI(处理器核间中断)。这个 SBI 调用只会清除被调用的 hart(硬件线程),其他的 hart 不受影响。 -sbi_clear_ipi() 已经被弃用,因为 S 模式代码可以直接清除 sip.SSIP 寄存器位。. -如果没有 IPI 被挂起,这个 SBI 调用返回 0;如果有 IPI 被挂起,则返回一个实现特定的正值。 - -### 5.5 扩展:发送 IPI (EID #0x04) - -``` -long sbi_send_ipi(const unsigned long *hart_mask) -``` - -向 hart_mask 指定的所有 hart 发送跨处理器中断。跨处理器中断在接收的 hart 上表现为监督者模式软件中断。 -hart_mask 是指向 hart 位向量的虚拟地址。该位向量表示为无符号长整型序列,其长度等于系统中 hart 的数量除以无符号长整型中的位数,向上取整到下一个整数。 -此 SBI 调用在成功时返回 0,或返回一个实现特定的负错误代码。 - -### 5.6 扩展:远程 FENCE.I (EID #0x05) - -``` -long sbi_remote_fence_i(const unsigned long *hart_mask) -``` - -该函数用于指示远程处理器执行 FENCE.I 指令。 -此 SBI 调用在成功时返回 0,或返回一个实现特定的负错误代码。 - -### 5.7 扩展:远程 SFENCE.VMA (EID #0x06) - -``` -long sbi_remote_sfence_vma(const unsigned long *hart_mask,unsigned long start, unsigned long size) -``` - -指示远程 harts 执行一个或多个 SFENCE.VMA 指令,涵盖从 start 到 size 的虚拟地址范围。其中,hart_mask 参数与 sbi_send_ipi() 函数中描述的一样。 -此 SBI 调用在成功时返回 0,或返回一个实现特定的负错误代码。 - -### 5.8 扩展:远程 SFENCE.VMA(指定地址空间标识符)(EID #0x07) - -``` -long sbi_remote_sfence_vma_asid(const unsigned long *hart_mask,unsigned long start, unsigned long size, unsigned long asid) -``` - -指示远程 hart 执行一个或多个 SFENCE.VMA 指令,覆盖 start 和 size 之间的虚拟地址范围。此操作仅涵盖给定的 ASID。 -此 SBI 调用在成功时返回 0,或返回一个实现特定的负错误代码。 - -### 5.9 扩展:系统关闭 (EID #0x08) - -``` -void sbi_shutdown(void) -``` - -将所有 hart 从监督者模式的角度置于关闭状态。 -此 SBI 调用在成功时返回 0,或返回一个实现特定的负错误代码。 - -### 5.10 函数列表 - -_表 5. 旧版函数列表_ - -| 函数名 | SBI 版本 | FID | EID | 替代 EID | -| :------------------------- | -------- | --- | --------- | ---------- | -| sbi_set_timer | 0.1 | 0 | 0x00 | 0x54494D45 | -| sbi_console_putchar | 0.1 | 0 | 0x01 | N/A | -| sbi_console_getchar | 0.1 | 0 | 0x02 | N/A | -| sbi_clear_ipi | 0.1 | 0 | 0x03 | N/A | -| sbi_send_ipi | 0.1 | 0 | 0x04 | 0x735049 | -| sbi_remote_fence_i | 0.1 | 0 | 0x05 | 0x52464E43 | -| sbi_remote_sfence_vma | 0.1 | 0 | 0x06 | 0x52464E43 | -| sbi_remote_sfence_vma_asid | 0.1 | 0 | 0x07 | 0x52464E43 | -| sbi_shutdown | 0.1 | 0 | 0x08 | 0x53525354 | -| 保留 | | | 0x09-0x0F | | - -## 章节 6. 时钟扩展 (EID #0x54494D45 "TIME") - -这个替代扩展(EID #0x00)替代了传统的计时器扩展。它遵循在 v0.2 中定义的新的调用约定。 - -### 6.1 函数:时钟设定 (FID #0) - -``` -struct sbiret sbi_set_timer(uint64_t stime_value) -``` - -在 stime_value 时间之后,为下一个事件设置时钟。stime_value 以绝对时间表示。此函数还必须清除挂起的计时器中断位。 -如果监督者希望在不安排下一个计时器事件的情况下清除计时器中断,可以请求一个无限远的计时器中断(即 (uint64_t)-1),或者通过清除 sie.STIE 寄存器位来屏蔽计时器中断。 - -### 6.2 函数列表 - -_表 6. 时钟函数列表_ - -| 函数名 | SBI 版本 | FID | EID | -| :------------ | -------- | --- | ---------- | -| sbi_set_timer | 0.2 | 0 | 0x54494D45 | - -## 章节 7. IPI 扩展 (EID #0x735049 "sPI: s-mode IPI") - -该扩展替代了传统的扩展 (EID #0x04)。其他与 IPI 相关的传统扩展(0x3)现已不推荐使用。该扩展中的所有函数都遵循二进制编码部分中定义的 hart_mask. - -### 7.1 函数:发送 IPI (FID #0) - -``` -struct sbiret sbi_send_ipi(unsigned long hart_mask,unsigned long hart_mask_base) -``` - -向 hart_mask 中定义的所有 hart 发送跨处理器中断。接收 hart 上的处理器间中断将在其上表现为监督者软件中断。 -sbiret.error 返回的可能错误代码如表 7 所示。 - -_表 7. IPI 发送错误_ - -| 错误代码 | 描述 | -| :--------------- | ------------------------------ | -| SBI_SUCCESS 成功 | IPI 被成功发送至所有目标 harts | - -### 7.2 函数列表 - -_表 8. IPI 函数列表_ - -| 函数名 | SBI 版本 | FID | EID | -| :----------- | -------- | --- | -------- | -| sbi_send_ipi | 0.2 | 0 | 0x735049 | - -## 章节 8. RFENCE 扩展 (EID #0x52464E43 "RFNC") - -该扩展定义了所有与远程屏障相关的函数,并替代了旧的扩展(EID #0x05 - #0x07)。所有的函数都遵循二进制编码部分中定义的 hart_mask。任何希望使用地址范围(即 start_addr 和 size)的函数,必须遵守以下对范围参数的约束条件。 -如果以下条件满足,则远程屏障函数充当完全 TLB 刷新的作用: - -- start_addr 和 size 都为 0 -- size 等于 2^XLEN-1 - -### 8.1 函数:远程 FENCE.I 指令 (FID #0) - -``` -struct sbiret sbi_remote_fence_i(unsigned long hart_mask,unsigned long hart_mask_base) -``` - -远程指示 harts 执行 FENCE.I 指令。 -sbiret.error 返回的可能错误代码如表 9 所示。 - -_表 9. RFENCE 远程 FENCE.I 指令错误_ - -| 错误代码 | 描述 | -| :--------------- | -------------------------------- | -| SBI_SUCCESS 成功 | IPI 被成功发送至所有目标 harts。 | - -### 8.2 函数:远程 SFENCE.VMA 指令 (FID #1) - -``` -struct sbiret sbi_remote_sfence_vma(unsigned long hart_mask, -unsigned long hart_mask_base, unsigned long start_addr, unsigned long size) -``` - -指示远程 hart 执行一个或多个 SFENCE.VMA 指令,覆盖从 start 到 size 的虚拟地址范围内的地址。 -sbiret.error 返回的可能错误代码如表 10 所示。 - -_表 10. RFENCE 远程 SFENCE.VMA 错误_ - -| 错误代码 | 描述 | -| :------------------------------- | -------------------------------- | -| SBI_SUCCESS 成功 | IPI 被成功发送至所有目标 harts。 | -| SBI_ERR_INVALID_ADDRESS 非法地址 | start_addr 或 size 非法 | - -### 8.3 函数:远程 SFENCE.VMA 指定 ASID (FID #2) - -``` -struct sbiret sbi_remote_sfence_vma_asid(unsigned long hart_mask, -unsigned long hart_mask_base, unsigned long start_addr, unsigned long size, -unsigned long asid) -``` - -指示远程 hart 执行一个或多个 SFENCE.VMA 指令,覆盖从 start 到 size 的虚拟地址范围内的地址。这仅涵盖给定的 ASID。 -sbiret.error 返回的可能错误代码如表 11 所示。 - -_表 11. RFENCE 远程 SFENCE.VMA 指定 ASID 错误_ - -| 错误代码 | 描述 | -| :------------------------------- | -------------------------------- | -| SBI_SUCCESS 成功 | IPI 被成功发送至所有目标 harts。 | -| SBI_ERR_INVALID_ADDRESS 非法地址 | start_addr 或 size 非法 | - -### 8.4 函数:远程 HFENCE.GVMA 指定 VMID (FID #3) - -``` -struct sbiret sbi_remote_hfence_gvma_vmid(unsigned long hart_mask, -unsigned long hart_mask_base, unsigned long start_addr, unsigned long size, -unsigned long vmid) -``` - -指示远程 hart 执行一个或多个 HFENCE.GVMA 指令,仅覆盖给定 VMID(虚拟机标识符)的 start 到 size 之间的客户机物理地址范围。此函数调用仅适用于实现了虚拟化扩展的 harts。 -sbiret.error 返回的可能错误代码如表 12 所示。 - -_表 12. RFENCE 远程 HFENCE.GVMA 指定 VMID 错误_ - -| 错误代码 | 描述 | -| :---------------------------------- | ---------------------------------------------------------------- | -| SBI_SUCCESS 成功 | IPI 被成功发送至所有目标 harts。 | -| SBI_ERR_NOT_SUPPORTED(操作)不支持 | 由于未被实现或目标 hart 中不错支持虚拟化扩展,则该操作不受支持。 | -| SBI_ERR_INVALID_ADDRESS 非法地址 | start_addr 或 size 非法 | - -### 8.5 函数:远程 HFENCE.GVMA (FID #4) - -``` -struct sbiret sbi_remote_hfence_gvma(unsigned long hart_mask, -unsigned long hart_mask_base, unsigned long start_addr, unsigned long size) -``` - -指示远程 harts 执行一个或多个 HFENCE.GVMA 指令,覆盖从 start 到 size 之间的所有客户机的客户机物理地址范围。此函数调用仅适用于实现虚拟化扩展的 harts。 -sbiret.error 返回的可能错误代码如表 13 所示。 - -_表 13. RFENCE 远程 HFENCE.GVMA 错误_ - -| 错误代码 | 描述 | -| :---------------------------------- | ---------------------------------------------------------------- | -| SBI_SUCCESS 成功 | IPI 被成功发送至所有目标 harts。 | -| SBI_ERR_NOT_SUPPORTED(操作)不支持 | 由于未被实现或目标 hart 中不错支持虚拟化扩展,则该操作不受支持。 | -| SBI_ERR_INVALID_ADDRESS 非法地址 | start_addr 或 size 非法 | - -### 8.6 函数:远程 HFENCE.VVMA 指定 ASID (FID #5) - -``` -struct sbiret sbi_remote_hfence_vvma_asid(unsigned long hart_mask, -unsigned long hart_mask_base, unsigned long start_addr, unsigned long size, -unsigned long asid) -``` - -远程指示 harts 执行一个或多个 HFENCE.VVMA 指令,覆盖从 start 至 size 之间,指定的 ASID 与 -VMID(hgatp 寄存器)下所有客户机物理地址范围。此函数调用仅适用于实现虚拟化扩展的 harts。 -sbiret.error 返回的可能错误代码如表 14 所示。 - -_表 14. RFENCE 远程 HFENCE.VVMA 指定 ASID 错误_ - -| 错误代码 | 描述 | -| :---------------------------------- | ---------------------------------------------------------------- | -| SBI_SUCCESS 成功 | IPI 被成功发送至所有目标 harts。 | -| SBI_ERR_NOT_SUPPORTED(操作)不支持 | 由于未被实现或目标 hart 中不错支持虚拟化扩展,则该操作不受支持。 | -| SBI_ERR_INVALID_ADDRESS 非法地址 | start_addr 或 size 非法 | - -### 8.7 函数:远程 HFENCE.VVMA (FID #6) - -``` -struct sbiret sbi_remote_hfence_vvma(unsigned long hart_mask, -unsigned long hart_mask_base, unsigned long start_addr, unsigned long size) -``` - -指示远程 HART 执行一个或多个 HFENCE.VVMA 指令,覆盖当前调用 HART 的 VMID(在 hgatp 寄存器中)下的 start 和 size 之间的客户虚拟地址范围。此函数调用仅适用于实现了虚拟化扩展的 harts。 -sbiret.error 返回的可能错误代码如表 15 所示。 - -_表 15.RFENCE 远程 HFENCE.VVMA 错误_ - -| 错误代码 | 描述 | -| :---------------------------------- | ---------------------------------------------------------------- | -| SBI_SUCCESS 成功 | IPI 被成功发送至所有目标 harts。 | -| SBI_ERR_NOT_SUPPORTED(操作)不支持 | 由于未被实现或目标 hart 中不错支持虚拟化扩展,则该操作不受支持。 | -| SBI_ERR_INVALID_ADDRESS 非法地址 | start_addr 或 size 非法 | - -### 8.8 函数列表 - -_表 16. RFENCE 函数列表_ - -| 函数名 | SBI 版本 | FID | EID | -| :-------------------------- | -------- | --- | ---------- | -| sbi_remote_fence_i | 0.2 | 0 | 0x52464E43 | -| sbi_remote_sfence_vma | 0.2 | 1 | 0x52464E43 | -| sbi_remote_sfence_vma_asid | 0.2 | 2 | 0x52464E43 | -| sbi_remote_hfence_gvma_vmid | 0.2 | 3 | 0x52464E43 | -| sbi_remote_hfence_gvma | 0.2 | 4 | 0x52464E43 | -| sbi_remote_hfence_vvma_asid | 0.2 | 5 | 0x52464E43 | -| sbi_remote_hfence_vvma | 0.2 | 6 | 0x52464E43 | - -## 章节 9. Hart State Management 状态管理扩展 (EID #0x48534D "HSM") - -Hart State Management (HSM) 扩展引入了一组 Hart 状态和一组函数,允许监督者模式下的软件请求 Hart 状态变更。 -下面的表 17 描述了所有可能的 HSM 状态以及每个状态的唯一 HSM 状态标识符。: - -_表 17. HSM Hart 状态_ - -| 状态 ID | 状态名 | 描述 | -| :------ | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | -| 0 | 启动 (STARTED) | 该 hart 处于物理上电状态,并正常执行。 | -| 1 | 结束 (STOPPED) | 该 hart 没有在监督者模式或任何更低特权模式下执行。如果底层平台具有关闭 hart 的物理机制,那么它可能被 SBI 实现关闭了电源。 | -| 2 | 等待被启动 (START_PENDING) | 另一个 hart 请求将该 hart 从 STOPPED 状态启动(或上电),而 SBI 实现仍在努力将该 hart 转换为 STARTED 状态。 | -| 3 | 结束等待 (STOP_PENDING) | 该 hart 在 STARTED 状态下请求停止(或关机),而 SBI 实现仍在努力将该 hart 转变为 STOPPED 状态。 | -| 4 | 挂起 (SUSPENDED) | 该 hart 处于特定于平台的暂停(或低功耗)状态。 | -| 5 | 等待挂起 (SUSPEND_PENDING) | 该 hart 从 STARTED 状态请求将自身置于特定于平台的低功耗状态,并且 SBI 实现正在努力将该 hart 转换为特定于平台的 SUSPENDED 状态。 | -| 6 | 等待恢复 (RESUME_PENDING) | 中断或特定于平台的硬件事件导致 hart 从 SUSPENDED 状态恢复正常执行,而 SBI 实现正在努力将该 hart 转换为 STARTED 状态。 | - -在任何时刻,hart 应该处于上述提到的 hart 状态之一。SBI 实现对 hart 状态的转换应遵循图 3 所示的状态机。 - -![fig3.jpg](images/sbi-specification-translation/fig3.jpg) - -图 3. SBI HSM 状态机 - -一个平台可以有多个 hart,这些 hart 被分组成层次拓扑组(如核心、集群、节点等),每个层次组都有独立的平台特定低功耗状态。这些层次拓扑组的平台特定低功耗状态可以表示为一个 hart 的平台特定挂起状态。SBI 实现可以利用较高层次拓扑组的挂起状态,使用以下一种方法之一: - -1. **平台协调**: 在这种方法中,当一个 hart 变为空闲时,监督者模式的功耗管理软件将请求该 hart 和较高层次组的最深挂起状态。SBI 实现应选择一个较高层次组的挂起状态,满足以下条件: - 1. 不比指定的挂起状态更深 - 2. 唤醒延迟不高于指定挂起状态的唤醒延迟 -2. **操作系统发起**: 在这种方法中,监督者模式的功耗管理软件将在最后一个 hart 变为空闲后,直接请求较高层次组的挂起状态。当一个 hart 变为空闲时,监管模式的功耗管理软件总是选择该 hart 的挂起状态,但仅在该 hart 是组内最后一个运行的 hart 时,才为较高层次组选择一个挂起状态。SBI 实现应满足以下条件: - 1. 永远不要选择与指定暂停状态不同的较高层次组的挂起状态 - 2. 总是优先选择最近请求的较高层次组的挂起状态。 - -### 9.1 函数:HART 启动 (FID #0) - -``` -struct sbiret sbi_hart_start(unsigned long hartid, -unsigned long start_addr, unsigned long opaque) -``` - -SBI 实现以特定的寄存器值在指定的 start_addr 地址处启动执行目标 hart 的监督者模式。具体的寄存器值如表 18 所述。 - -_表 18. HSM Hart 启动的寄存器状态_ - -| 寄存器名称 | 寄存器值 | -| :--------------------------- | ----------- | -| satp | 0 | -| sstatus.SIE | 0 | -| a0 | hartid | -| a1 | opaque 参数 | -| 其他寄存器仍处于未定义状态。 | | - -此调用是异步的,具体来说,sbi_hart_start() 函数可以在目标 hart 开始执行之前返回,只要 SBI 实现能够确保返回的结果准确。如果 SBI 实现是在机器模式(M-mode)下执行的平台运行时固件,则必须在将控制权转移给监督者模式软件之前配置 PMP 和其他 M-mode 状态。 -hartid 参数指定要启动的目标 hart。 -start_addr 参数指向一个运行时指定的物理地址,该 hart 可以在监督者模式下开始执行。 -opaque 参数是一个 XLEN 位的值,当 hart 在 start_addr 处开始执行时 opaque 参数被设置在 a1 寄存器中。 -sbiret.error 中可能返回的错误代码在下表 19 中显示。 - -_表 19. HSM Hart 启动错误_ - -| 错误代码 | 描述 | -| :-------------------------------- | ---------------------------------------------------------------------------------- | -| SBI_SUCCESS 成功 | Hart 之前处于停止状态。它将从 start_addr 开始执行 | -| SBI_ERR_INVALID_ADDRESS 非法地址 | start_addr 无效,原因可能如下:无效物理地址。该地址被 PMP 禁止在监督者模式下执行。 | -| SBI_ERR_INVALID_PARAM 非法参数 | 无效的 Hart,因为其相应的 hart 不能以监督者模式启动。 | -| SBI_ERR_ALREADY_AVAILAB LE 已存在 | 给定 hartid 已启动。 | -| SBI_ERR_FAILED 失败 | 未知原因造成的启动失败。 | - -### 9.2 函数:HART 停止 (FID #1) - -``` -struct sbiret sbi_hart_stop(void) -``` - -要求 SBI 实现停止以监督者模式执行调用 hart,并将其所有权返回给 SBI 实现。在正常情况下,不希望此调用返回。sbi_hart_stop() 必须在禁用监督者模式中断时调用。 -sbiret.error 可能返回的错误代码如下表 20 所示。 - -_表 20. HSM Hart 停止错误_ - -| 错误代码 | 描述 | -| :------------------ | ---------------------- | -| SBI_ERR_FAILED 失败 | 当前 hart 停止执行失败 | - -### 9.3 函数:HART 获取状态 (FID #2) - -``` -struct sbiret sbi_hart_get_status(unsigned long hartid) -``` - -通过 sbiret.value 获取给定 hart 的当前状态(或 HSM 状态 ID),或通过 sbiret.error 获取错误信息。 -hartid 参数指定需要查询状态的目标 hart。 -sbiret.value 中可能返回的状态(或 HSM 状态 ID)值在表 17 中进行了描述。 -sbiret.error 可能返回的错误代码如下表 21 所示。 - -_表 21. HSM Hart 获取状态错误_ - -| 错误代码 | 描述 | -| :----------------------------- | ------------------ | -| SBI_ERR_INVALID_PARAM 无效参数 | 给定的 hartid 无效 | - -由于任何并发的 sbi_hart_start()、sbi_hart_stop() 或 sbi_hart_suspend() 调用,harts 可能随时转换 HSM 状态,因此该函数的返回值可能不代表返回值验证时 hart 的实际状态。 - -### 9.4 函数:HART 挂起 (FID #3) - -``` -struct sbiret sbi_hart_suspend(uint32_t suspend_type, -unsigned long resume_addr, unsigned long opaque) -``` - -SBI 实现将调用的 hart 置于由 suspend_type 参数指定的平台特定的挂起(或低功耗)状态。当收到中断或平台特定的硬件事件时,hart 将自动退出挂起状态并恢复正常执行。 -一个 hart 的平台特定挂起状态可以是保持性的或非保持性的。保持性挂起状态将保留所有特权模式下 hart 的寄存器和 CSR 值,而非保持性挂起状态将不保留 hart 的寄存器和 CSR 值。 -从保持性挂起状态恢复是比较简单的,监督者模式的软件将看到 SBI 挂起调用返回而无需任何失败。在保持性挂起期间,resume_addr 参数未使用。 -从非保持性挂起状态恢复相对更复杂,需要软件还原各种特权模式下的 hart 寄存器和 CSR。从非保持性挂起状态恢复后,hart 将跳转到由 resume_addr 指定的监督者模式地址,具体寄存器的值在表 22 中描述。 - -_表 22. HSM Hart 恢复寄存器状态_ - -| 寄存器名称 | 寄存器值 | -| :------------------------- | ----------- | -| satp | 0 | -| sstatus.SIE | 0 | -| a0 | hartid | -| a1 | Opaque 参数 | -| 其他寄存器保持未定义状态。 | | - -suspend_type 参数是 32 位宽,可能的值如表 23 所示。 - -_表 23. HSM Hart 挂起类型_ - -| 值 | 描述 | -| :---------------------- | -------------------- | -| 0x00000000 | 默认保持性挂起 | -| 0x00000001 - 0x0FFFFFFF | 保留供未来使用 | -| 0x10000000 - 0x7FFFFFFF | 平台特定保持性挂起 | -| 0x80000000 | 默认非保持性挂起 | -| 0x80000001 - 0x8FFFFFFF | 保留供未来使用 | -| 0x90000000 - 0xFFFFFFFF | 平台特定非保持性挂起 | -| > 0xFFFFFFFF | 保留 | - -resume_addr 参数指向一个在非保持性挂起后,hart 可以在监督者模式下恢复执行的运行时指定的物理地址。 -opaque 参数是一个 XLEN 位的值,当 hart 在非保留挂起后在 resume_addr 处恢复执行时,会将该值设置在 a1 寄存器中。 -sbiret.error 中可能返回的错误代码如表 24 所示。 - -_表 24. HSM Hart 挂起错误_ - -| 错误代码 | 描述 | -| :------------------------------- | ------------------------------------------------------------------------------------------- | -| SBI_SUCCESS 成功 | Hart 已成功地从保持性挂起状态中恢复。 | -| SBI_ERR_INVALID_PARAM 无效参数 | suspend_type 无效 | -| SBI_ERR_NOT_SUPPORTED 不支持 | suspend_type 有效但未实现。 | -| SBI_ERR_INVALID_ADDRESS 无效地址 | resume_addr 无效,可能是由于以下原因:无效的物理地址或该地址被 PMP 禁止在监督者模式下运行。 | -| SBI_ERR_FAILED 失败 | 挂起请求因未知原因而失败。 | - -### 9.5 函数列表 - -_表 25. HSM 函数列表_ - -| 函数名 | SBI 版本 | FID | EID | -| :--------------------------- | -------- | --- | -------- | -| sbi_hart_start 启动 | 0.2 | 0 | 0x48534D | -| sbi_hart_stop 停止 | 0.2 | 1 | 0x48534D | -| sbi_hart_get_status 获取状态 | 0.2 | 2 | 0x48534D | -| sbi_hart_suspend 挂起 | 0.3 | 3 | 0x48534D | - -## 章节 10. 系统复位扩展 (EID #0x53525354 "SRST") - -系统复位扩展提供了一个函数,允许监督者模式下的软件请求系统级的重启或关闭操作。"系统"指的是监督者模式下的软件的视角,底层的 SBI 实现可以是机器模式 M-mode 固件或虚拟化管理程序。 - -### 10.1 函数:系统复位 (FID #0) - -``` -struct sbiret sbi_system_reset(uint32_t reset_type, uint32_t reset_reason) -``` - -根据提供的类型 reset_type 和原因 reset_reason 重置系统。这是一个同步调用,如果成功将不会返回。 -reset_type 参数的宽度为 32 位,其可能的取值在下面的表 26 中列出。 - -_表 26. SRST 系统复位类型_ - -| 值 | 描述 | -| :---------------------- | -------------------------- | -| 0x00000000 | 关机 | -| 0x00000001 | 冷启动 | -| 0x00000002 | 热启动 | -| 0x00000003 - 0xEFFFFFFF | 保留以便未来使用 | -| 0xF0000000 - 0xFFFFFFFF | 供应商或平台特定的复位类型 | -| > 0xFFFFFFFF | 保留 | - -reset_reason 是一个可选参数,表示系统复位的原因。该参数是 32 位宽,可能的取值如下所示: - -_表 27. SRST 系统复位原因_ - -| 值 | 描述 | -| :--------------------- | -------------------------- | -| 0x00000000 | 没有原因 | -| 0x00000001 | 系统失败 | -| 0x00000002 -0xDFFFFFFF | 保留以便未来使用 | -| 0xE0000000 -0xEFFFFFFF | SBI 实现特定的复位原因 | -| 0xF0000000 -0xFFFFFFFF | 供应商或平台特定的复位类型 | -| > 0xFFFFFFFF | 保留 | - -当监督者下的软件在本地运行时,SBI 实现属于机器模式下的固件。在这种情况下,关机等同于整个系统的物理断电,冷启动等同于整个系统的物理断电循环(并重新上电)。此外,热重启等同于对主处理器和系统的一部分进行断电循环,而不是整个系统。例如,在具有 BMC(主板管理控制器)的服务器级系统上,热重启不会对 BMC 进行断电循环,而冷重启则肯定会对 BMC 进行断电循环。 -当监督者模式下的软件在虚拟机内运行时,SBI 实现是一个虚拟化管理器。关机、冷重启和热重启在功能上与本机情况相同,但可能不会导致任何物理电源变化。 -sbiret.error 中可能返回的错误代码如表 28 所示。 - -_表 28. SRST 系统复位错误 s_ - -| 错误代码 | 描述 | -| :----------------------------- | ------------------------------- | -| SBI_ERR_INVALID_PARAM 无效参数 | reset_type 或 reset_reason 无效 | -| SBI_ERR_NOT_SUPPORTED 不支持 | reset_type 有效但未被设定 | -| SBI_ERR_FAILED 失败 | 未知原因的复位请求失败 | - -### 10.2 函数列表 - -_表 29. SRST 函数列表_ - -| Function Name | SBI Version | FID | EID | -| :--------------- | ----------- | --- | ---------- | -| sbi_system_reset | 0.3 | 0 | 0x53525354 | - -## 章节 11. 性能监控单元扩展 (EID #0x504D55 "PMU") - -RISC-V 硬件性能计数器(如 mcycle、minstret 和 mhpmcounterX CSRs)可以以只读方式从监督者模式使用 cycle、instret 和 hpmcounterX CSRs 进行访问。SBI 性能监控单元(PMU)扩展是一个接口,供监督者模式使用,以便借助机器模式(或虚拟化模式)配置和使用 RISC-V 硬件性能计数器。这些硬件性能计数器只能从机器模式使用 mcountinhibit 和 mhpmeventX CSRs 启动、停止或配置。因此,如果 RISC-V 平台未实现 mhpmcounterX CSR,机器模式的 SBI 实现可能选择禁止 SBI PMU 扩展。 -一般情况下,RISC-V 平台支持使用有限数量的硬件性能计数器(最多 64 位)来监控各种硬件事件。此外,SBI 实现还可以提供固件性能计数器,用于监控固件事件,例如不对齐的加载/存储指令数量、RFENCE 数量、IPI 数量等。固件计数器始终为 64 位。 -SBI PMU 扩展提供以下功能: - -1. 监督者模式软件发现和配置每个 HART 的硬件/固件计数器的接口 -2. 具有典型 perf 兼容接口的硬件/固件性能计数器和事件 -3. 完全访问微体系结构的原始事件编码 - -为了定义 SBI PMU 扩展调用,我们首先定义了重要的实体 counter_idx、event_idx 和 event_data。counter_idx 是分配给每个硬件/固件计数器的逻辑编号。event_idx 表示硬件(或固件)事件,而 event_idx 为 64 位宽,表示硬件(或固件)事件的额外配置(或参数)。 -event_idx 是一个 20 位宽的数字,编码如下: - -``` -event_idx[19:16]= type -event_idx[15:0] = code -``` - -### 11.1 事件:硬件通用事件 (Type #0) - -event_idx.type(即事件类型)对于所有硬件通用事件应为 0x0,并且每个硬件通用事件由一个唯一的 event_idx.code(即事件代码)标识,如下所示的表格 30 中所述。 - -_表 30. PMU 硬件事件_ - -| 通用事件名称 | 代码 | 描述 | -| :--------------------------------- | ---- | ------------------------------------- | -| SBI_PMU_HW_NO_EVENT | 0 | 未使用的事件,因为 event_idx 不能为零 | -| SBI_PMU_HW_CPU_CYCLES | 1 | 每个 CPU 周期的事件 | -| SBI_PMU_HW_INSTRUCTIONS | 2 | 每个完成的指令的事件 | -| SBI_PMU_HW_CACHE_REFERENCES | 3 | 缓存命中的事件 | -| SBI_PMU_HW_CACHE_MISSES | 4 | 缓存未命中的事件 | -| SBI_PMU_HW_BRANCH_INSTRUCTIONS | 5 | 分支指令的事件 | -| SBI_PMU_HW_BRANCH_MISSES | 6 | 分支预测错误的事件 | -| SBI_PMU_HW_BUS_CYCLES | 7 | 每个 BUS 周期的事件 | -| SBI_PMU_HW_STALLED_CYCLES_FRONTEND | 8 | 微架构前端阻塞周期的事件 | -| SBI_PMU_HW_STALLED_CYCLES_BACKEND | 9 | 微架构后端阻塞周期的事件 | -| SBI_PMU_HW_REF_CPU_CYCLES | 10 | 每个参考 CPU 周期的事件 | - -> 注意:对于硬件通用事件,event_data(即事件数据)未使用,event_data 的所有非零值都保留供将来使用。 - -> 注意:当 RISC-V 平台使用 WFI 指令进入 WAIT 状态或使用 SBI HSM HART 挂起调用进入平台特定的挂起状态时,CPU 时钟可能会停止。 - -> 注意:SBI_PMU_HW_CPU_CYCLES 事件通过 cycle CSR 计数 CPU 时钟周期。这些周期可能是可变频率的周期,在 CPU 时钟停止时不计数。 - -> 注意:SBI_PMU_HW_REF_CPU_CYCLES 在 CPU 时钟未停止时计数固定频率的时钟周期。计数的固定频率可以是例如时间 CSR 计数的频率。 -> 注意:SBI_PMU_HW_BUS_CYCLES 计数固定频率的时钟周期。计数的固定频率可以是例如时间 CSR 计数的频率,或者可以是 HART(及其私有缓存)与系统其他部分之间的时钟边界的频率。 - -### 11.2 事件:硬件缓存事件 (Type #1) - -对于所有硬件缓存事件,event_idx.type(即事件类型)应为 0x1,并且每个硬件缓存事件由唯一的 event_idx.code(即事件代码)标识,其编码如下: - -``` -event_idx.code[15:3] = cache_id -event_idx.code[2:1] = op_id -event_idx.code[0:0] = result_id -``` - -下表显示了以下标识符可能的值:event_idx.code.cache_id(即缓存事件 ID),event_idx.code.op_id(即缓存操作 ID)和 event_idx.code.result_id(即缓存结果 ID)。 - -_表 31. PMU 缓存事件 ID_ - -| 缓存事件名称 | 事件 ID | 描述 | -| :-------------------- | ------- | ----------------- | -| SBI_PMU_HW_CACHE_L1D | 0 | 一级数据缓存事件 | -| SBI_PMU_HW_CACHE_L1I | 1 | 一级指令缓存事件 | -| SBI_PMU_HW_CACHE_LL | 2 | 最后一级缓存事件 | -| SBI_PMU_HW_CACHE_DTLB | 3 | 数据 TLB 事件 | -| SBI_PMU_HW_CACHE_ITLB | 4 | 指令 TLB 事件 | -| SBI_PMU_HW_CACHE_BPU | 5 | 分支预测单元事件 | -| SBI_PMU_HW_CACHE_NODE | 6 | NUMA 节点缓存事件 | - -_表 32. PMU 缓存操作 ID_ - -| 缓存操作名称 | 操作 ID | 描述 | -| :--------------------------- | ------- | ---------- | -| SBI_PMU_HW_CACHE_OP_READ | 0 | 读取缓存行 | -| SBI_PMU_HW_CACHE_OP_WRITE | 1 | 写入缓存行 | -| SBI_PMU_HW_CACHE_OP_PREFETCH | 2 | 预取缓存行 | - -_表 33. PMU 缓存操作结果 ID_ - -| 缓存结果名称 | 结果 ID | 描述 | -| :----------------------------- | ------- | ---------- | -| SBI_PMU_HW_CACHE_RESULT_ACCESS | 0 | 缓存访问 | -| SBI_PMU_HW_CACHE_RESULT_MISS | 1 | 缓存未命中 | - -> 注意:对于硬件缓存事件,event_data(即事件数据)未使用,所有非零值的事件数据均保留用于将来使用。 - -### 11.3 事件:硬件原始事件 (Type #2) - -硬件原始事件类型的 event_idx.type (i.e. event type) 值应为 0x2,而 event_idx.code(i.e. event code) 值应为零。 -对于具有 32 位宽度的 mhpmeventX CSRs 的 RISC-V 平台,event_data 配置(或参数)应包含要编程到 mhpmeventX CSR 中的 32 位值。 -对于具有 64 位宽度的 mhpmeventX CSRs 的 RISC-V 平台,event_data 配置(或参数)应包含要编程到 mhpmeventX CSR 的低 48 位值,而 SBI 实现将确定要编程到 mhpmeventX CSR 的高 16 位值。 - -> 注意:RISC-V 平台的硬件实现可能选择定义要写入 mhpmeventX CSR 的硬件事件的预期值。对于硬件常规/缓存事件,为了简化起见,RISC-V 平台的硬件实现可能使用零扩展的 event_idx 作为预期值。 - -### 11.4 事件:固件事件 (Type #15) - -所有固件事件的 event_idx.type(即事件类型)应为 0xf,并且每个固件事件都由唯一的 event_idx.code(即事件代码)标识,其描述如下所示的 表 34 中。 - -_表 34. PMU 固件事件_ - -| 固件事件名称 | 编码 | 描述 | -| :------------------------------------ | ---- | ----------------------------------------------------- | -| SBI_PMU_FW_MISALIGNED_LOAD | 0 | 对齐错误加载陷入事件 | -| SBI_PMU_FW_MISALIGNED_STORE | 1 | 对齐错误存储陷入事件 | -| SBI_PMU_FW_ACCESS_LOAD | 2 | 加载访问陷入事件 | -| SBI_PMU_FW_ACCESS_STORE | 3 | 存储访问陷入事件 | -| SBI_PMU_FW_ILLEGAL_INSN | 4 | 非法指令陷入事件 | -| SBI_PMU_FW_SET_TIMER | 5 | 设置定时器事件 | -| SBI_PMU_FW_IPI_SENT | 6 | 发送 IPI 给其他 HART 事件 | -| SBI_PMU_FW_IPI_RECEIVED | 7 | 接收来自其他 HART 的 IPI 事件 | -| SBI_PMU_FW_FENCE_I_SENT | 8 | 发送 FENCE.I 请求给其他 HART 事件 | -| SBI_PMU_FW_FENCE_I_RECEIVED | 9 | 接收来自其他 HART 的 FENCE.I 请求事件 | -| SBI_PMU_FW_SFENCE_VMA_SENT | 10 | 发送 SFENCE.VMA 请求给其他 HART 事件 | -| SBI_PMU_FW_SFENCE_VMA_RECEIVED | 11 | 接收来自其他 HART 的 SFENCE.VMA 请求事件 | -| SBI_PMU_FW_SFENCE_VMA_ASID_SENT | 12 | 发送带有 ASID 的 SFENCE.VMA 请求给其他 HART 事件 | -| SBI_PMU_FW_SFENCE_VMA_ASID_RECEIVE D | 13 | 接收来自其他 HART 的带有 ASID 的 SFENCE.VMA 请求事件 | -| SBI_PMU_FW_HFENCE_GVMA_SENT | 14 | 发送 HFENCE.GVMA 请求给其他 HART 事件 | -| SBI_PMU_FW_HFENCE_GVMA_RECEIVED | 15 | 接收来自其他 HART 的 HFENCE.GVMA 请求事件 | -| SBI_PMU_FW_HFENCE_GVMA_VMID_SENT | 16 | 发送带有 VMID 的 HFENCE.GVMA 请求给其他 HART 事件 | -| SBI_PMU_FW_HFENCE_GVMA_VMID_RECEI VED | 17 | 接收来自其他 HART 的带有 VMID 的 HFENCE.GVMA 请求事件 | -| SBI_PMU_FW_HFENCE_VVMA_SENT | 18 | 发送 HFENCE.VVMA 请求给其他 HART 事件 | -| SBI_PMU_FW_HFENCE_VVMA_RECEIVED | 19 | 接收来自其他 HART 的 HFENCE.VVMA 请求事件 | -| SBI_PMU_FW_HFENCE_VVMA_ASID_SENT | 20 | 向其他 HART 发送带有 ASID 的 HFENCE.VVMA 请求事件 | -| SBI_PMU_FW_HFENCE_VVMA_ASID_RECEIV ED | 21 | R 从其他 HART 接收带有 ASID 的 HFENCE.VVMA 请求事件 | - -> 注意:对于固件事件,event_data(即事件数据)未使用,所有非零的 event_data 值都保留供将来使用。 - -### 11.5 函数:获取可用计数器的数量 (FID #0) - -``` -struct sbiret sbi_pmu_num_counters() -``` - -**返回**可用计数器的数量到 sbiret.value 中,并始终在 sbiret.error 中返回 SBI_SUCCESS。 - -### 11.6 函数:获取特定计数器的详细信息 (FID #1) - -``` -struct sbiret sbi_pmu_counter_get_info(unsigned long counter_idx) -``` - -获取有关指定计数器的详细信息,例如底层 CSR 编号、计数器的宽度、计数器的类型(硬件/固件)等。 -此 SBI 调用返回的 counter_info 信息编码如下: - -``` -counter_info[11:0] = CSR (12bit CSR number) -counter_info[17:12] = Width (One less than number of bits in CSR) -counter_info[XLEN-2:18] = Reserved for future use counter_info[XLEN-1] = Type (0 = hardware and 1 = firmware) -``` - -若 counter_info.type == 1,那么 counter_info.csr 与 counter_info.width 应该被忽略。 -在 sbiret.value 中返回上述描述的 counter_info。 -sbiret.error 中可能返回的错误代码如下表 35 所示。 - -_表 35. PMU 计数器获取信息错误_ - -| 错误代码 | 描述 | -| :-------------------- | ------------------------------ | -| SBI_SUCCESS | 成功读取 counter_info | -| SBI_ERR_INVALID_PARAM | counter_idx 指向无效的计数器。 | - -### 11.7 函数:查找并配置匹配计数器 (FID #2) - -``` -struct sbiret sbi_pmu_counter_config_matching(unsigned long counter_idx_base, -unsigned long counter_idx_mask, unsigned long config_flags, unsigned long event_idx, uint64_t event_data) -``` - -在一组计数器中查找并配置一个尚未启动(或已启用)且可以监视指定事件的计数器。其中,counter_idx_base 和 counter_idx_mask 参数表示计数器集合,event_idx 表示要监视的事件,event_data 表示任何附加事件配置。 -config_flags 参数表示额外的计数器配置和过滤标志。config_flags 参数的位定义如下所示: - -_表 36. PMU 计数器配置匹配标志_ - -| 标志名称 | 位 | 描述 | -| :--------------------------- | ---------- | ------------------------------------ | -| SBI_PMU_CFG_FLAG_SKIP_MATCH | 0:0 | 跳过计数器匹配 | -| SBI_PMU_CFG_FLAG_CLEAR_VALUE | 1:1 | 在计数器配置中清零(或置零)计数器值 | -| SBI_PMU_CFG_FLAG_AUTO_START | 2:2 | 配置匹配计数器后自动启动计数器 | -| SBI_PMU_CFG_FLAG_SET_VUINH | 3:3 | VU 模式下禁止事件计数 | -| SBI_PMU_CFG_FLAG_SET_VSINH | 4:4 | VS 模式下禁止事件计数 | -| SBI_PMU_CFG_FLAG_SET_UINH | 5:5 | U 模式下禁止事件计数 | -| SBI_PMU_CFG_FLAG_SET_SINH | 6:6 | S 模式下禁止事件计数 | -| SBI_PMU_CFG_FLAG_SET_MINH | 7:7 | M 模式下禁止事件计数 | -| RESERVED | 8:(XLEN-1) | 所有非零值保留供将来使用 | - -> 注意:当在 config_flags 中设置了 SBI_PMU_CFG_FLAG_SKIP_MATCH 标志时,SBI 实现将无条件地从由 counter_idx_base 和 counter_idx_mask 指定的计数器集合中选择第一个计数器。 - -> 注意:config_flags 中的 SBI_PMU_CFG_FLAG_AUTO_START 标志对计数器值没有影响。 - -> 注意:config_flags[3:7] 位是事件过滤提示,因此在安全性方面或由于底层 RISC-V 平台缺乏事件过滤支持时,SBI 实现可能会忽略或覆盖这些提示。 -> 成功时,在 sbiret.value 中返回 counter_idx。 -> 如果操作失败,在 sbiret.error 中可能返回的错误代码如下表 37 所示: - -_表 37. PMU 计数器配置匹配错误_ - -| 错误代码 | 描述 | -| :-------------------- | ---------------------------------- | -| SBI_SUCCESS | 计数器已成功找到并配置。 | -| SBI_ERR_INVALID_PARAM | 计数器集合中存在无效的计数器。 | -| SBI_ERR_NOT_SUPPORTED | 没有任何计数器能够监视指定的事件。 | - -### 11.8 函数:启动一组计数器 (FID #3) - -``` -struct sbiret sbi_pmu_counter_start(unsigned long counter_idx_base, unsigned long counter_idx_mask, -unsigned long start_flags, uint64_t initial_value) -``` - -启动或启用一组计数器,并设置指定的初始值。counter_idx_base 和 counter_idx_mask 参数表示计数器集合,initial_value 参数指定计数器的初始值。 -start_flags 参数的位定义如下表 38 所示: - -_表 38. PMU 计数器启动标志_ - -| 标志名称 | 位 | 描述 | -| :--------------------------- | ---------- | ----------------------------------------------- | -| SBI_PMU_START_SET_INIT_VALUE | 0:0 | parameter 根据 initial_value 参数设置计数器的值 | -| RESERVED | 1:(XLEN-1) | 所有非零值保留供将来使用 | - -> 注意:当 start_flags 中未设置 SBI_PMU_START_SET_INIT_VALUE 时,计数器值不会被修改,并且事件计数将从当前计数器值开始。 -> sbiret.error 中可能返回的错误代码在下表表 39 中列出。 - -_表 39. PMU 计数器启动错误_ - -| 错误代码 | 描述 | -| :---------------------- | -------------------------------- | -| SBI_SUCCESS | 计数器启动成功 | -| SBI_ERR_INVALID_PARAM | 参数中指定的一些计数器无效。 | -| SBI_ERR_ALREADY_STARTED | 参数中指定的一些计数器已被启动。 | - -### 11.9 函数:停止或禁用一组计数器 (FID #4) - -``` -struct sbiret sbi_pmu_counter_stop(unsigned long counter_idx_base, -unsigned long counter_idx_mask, unsigned long stop_flags) -``` - -停止或禁用调用 HART 上的一组计数器。counter_idx_base 和 counter_idx_mask 参数表示计数器的集合。stop_flags 参数的位定义如下表 40 所示。 - -_表 40. PMU 计数器停止禁用标志_ - -| 标志名称 | 位 | 描述 | -| :---------------------- | ---------- | ---------------------------- | -| SBI_PMU_STOP_FLAG_RESET | 0:0 | 重置计数器到事件的映射关系。 | -| RESERVED | 1:(XLEN-1) | 所有非零值都保留供将来使用。 | - -返回在 sbiret.error 中可能出现的错误代码如下表 41 所示。 - -_表 41. PMU 计数器停止禁用错误_ - -| 错误代码 | 描述 | -| :---------------------- | -------------------------- | -| SBI_SUCCESS | 计数器禁用成功。 | -| SBI_ERR_INVALID_PARAM | 指定的某些计数器无效。 | -| SBI_ERR_ALREADY_STOPPED | 指定的某些计数器已经停止。 | - -### 11.10 函数:读取固件计数器 (FID #5) - -``` -struct sbiret sbi_pmu_counter_fw_read(unsigned long counter_idx) -``` - -在 sbiret.value 中提供固件计数器的当前值。 -sbiret.error 中返回的可能错误代码如下表 42 所示。 - -_表 42. PMU 读取固件计数错误_ - -| 错误代码 | 描述 | -| :-------------------- | ---------------------------------------------- | -| SBI_SUCCESS | 成功读取固件计数器的值。 | -| SBI_ERR_INVALID_PARAM | counter_idx 指向一个硬件计数器或无效的计数器。 | - -### 11.11 函数:读取固件计数器的高位 (FID #6) - -``` -struct sbiret sbi_pmu_counter_fw_read_hi(unsigned long counter_idx) -``` - -在 sbiret.value 中提供当前固件计数器值的前 32 位。对于 RV64(或更高)系统,此函数在 sbiret.value 中总是返回 0。 - -在 sbiret.error 中返回的可能的错误代码显示在下面的表 43 中。 - -*表 43. PMU 计数器固件读高错误* - -| 错误代码 | 描述 | -| :-------------------- | :------------------------------------------------- | -| SBI_SUCCESS | 固件计数器读取成功。 | -| SBI_ERR_INVALID_PARAM | counter_idx 指向一个硬件计数器或一个无效的计数器。 | - -### 11.12 函数:启用 PMU 快照功能 (FID #7) - -``` -struct sbiret sbi_pmu_snapshot_set_shmem(unsigned long shmem_phys_lo, unsigned long shmem_phys_hi) -``` - -为 PMU 状态快照设置共享内存区域。shmem_phys_lo 指定共享内存物理地址的低 XLEN 位,shmem_phys_hi 指定共享内存物理地址的高 XLEN 位。shmem_phys_lo 必须是 4096 字节(即页面)对齐。共享内存的大小必须是 4096 字节。共享内存的布局在表 44 中描述。 - -*表 44. SBI PMU 快照共享内存布局* - -| 名称 | 偏移量 | 大小 | 描述 | -| :---------------------- | :----- | :--- | :----------------------------------------------------------------------------------------- | -| counter_overflow_bitmap | 0x0000 | 8 | 一个所有逻辑溢出计数器的位图。只有在 Sscofpmf ISA 扩展可用时,它才有效。否则,它必须为零。 | -| counter_values | 0x0008 | 512 | 一个 64 位逻辑计数器的数组,每个索引代表与硬件/固件相关的每个逻辑计数器的值。 | -| Reserved | 0x0208 | 3576 | 保留给未来使用 | - -今后对这一结构的任何修订都应以向后兼容的方式进行,并将与 SBI 的一个版本相关。 - -这个函数应该在启动时每个 Hart 只被调用一次。一旦配置好,当 sbi_pmu_counter_stop 被调用并设置了 SBI_PMU_STOP_FLAG_TAKE_SNAPSHOT 标志时,SBI 实现可以对共享内存进行读/写访问。当 sbi_pmu_counter_start 被调用并设置了 SBI_PMU_START_FLAG_INIT_SNAPSHOT 标记时,SBI 实现有只读访问权。SBI 实现不得在其他时间访问该内存。 - -在 sbiret.error 中返回的可能的错误代码如下表 45 所示。 - -*表 45. PMU 设置快照区的错误* - -| 错误代码 | 描述 | -| ----------------------- | -------------------------------------------------------------------------------------------- | -| SBI_SUCCESS | 固件计数器读取成功。 | -| SBI_ERR_INVALID_ADDRESS | shmem_phys_lo 和 shmem_phys_hi 参数所指向的共享内存是不可写的,或者不满足 3.2 节的其他要求。 | - -### 11.13 函数列表 - -_表 46. PMU 函数列表_ - -| 函数名 | SBI 版本 | FID | EID | -| :------------------------------ | -------- | --- | -------- | -| sbi_pmu_num_counters | 0.3 | 0 | 0x504D55 | -| sbi_pmu_counter_get_info | 0.3 | 1 | 0x504D55 | -| sbi_pmu_counter_config_matching | 0.3 | 2 | 0x504D55 | -| sbi_pmu_counter_start | 0.3 | 3 | 0x504D55 | -| sbi_pmu_counter_stop | 0.3 | 4 | 0x504D55 | -| sbi_pmu_counter_fw_read | 0.3 | 5 | 0x504D55 | -| sbi_pmu_counter_fw_read_hi | 2.0 | 6 | 0x504D55 | -| sbi_pmu_snapshot_set_shmem | 2.0 | 7 | 0x504D55 | - -## 章节 12. 调试控制台扩展 (EID #0x4442434E "DBCN") - -调试控制台扩展定义了一种通用机制,用于在监管模式软件中进行调试以及引导过程中的早期打印输出。 - -这个扩展取代了传统的控制台 putchar(EID #0x01)和控制台 getchar(EID #0x02)扩展。调试控制台扩展允许监督者模式的软件在一次 SBI 调用中写入或读取多个字节。 - -如果底层物理控制台有额外的位用于错误检查(或纠正),那么这些额外的位应该由 SBI 实现来处理。 - -> 注意:建议使用调试控制台扩展发送/接收的字节遵循 UTF-8 字符编码。 - -### 12.1 函数:控制台写入 (FID #0) - -``` -struct sbiret sbi_debug_console_write(unsigned long num_bytes, unsigned long base_addr_lo, unsigned long base_addr_hi) -``` - -从输入存储器向调试控制台写入字节。 - -num_bytes 参数指定了输入存储器中的字节数。输入存储器的物理基址由两个 XLEN 位宽的参数表示。base_addr_lo 参数指定输入存储器物理基址的低 XLEN 位,base_addr_hi 参数指定输入存储器的高 XLEN 位。 - -这是一个非阻塞的 SBI 调用,如果调试台不能接受更多的字节,它可能会做部分/不写。 - -写入的字节数在 sbiret.value 中返回,可能的错误代码在 sbiret.error 中返回,如下表 47 所示。 - -*表 47. 调试控制台写入错误* - -| 错误代码 | 描述 | -| :-------------------- | :------------------------------------------------------------------------------------ | -| SBI_SUCCESS | 成功写入的字节数。 | -| SBI_ERR_INVALID_PARAM | num_bytes、base_addr_lo 和 base_addr_hi 参数所指向的内存不满足第 3.2 节中描述的要求。 | -| SBI_ERR_FAILED | 由于 I/O 错误,写入失败。 | - -### 12.2 函数:控制台读取 (FID #1) - -``` -struct sbiret sbi_debug_console_read(unsigned long num_bytes, unsigned long base_addr_lo, unsigned long base_addr_hi) -``` - -从调试控制台读取字节到一个输出存储器。 - -num_bytes 参数规定了可以写入输出存储器的最大字节数。输出存储器的物理基址由两个 XLEN 位宽的参数表示。base_addr_lo 参数指定输出存储器物理基址的低 XLEN 位,base_addr_hi 参数指定输出存储器的高 XLEN 位。 - -这是一个非阻塞的 SBI 调用,如果在调试控制台上没有要读的字节,它就不会向输出内存写任何东西。 - -读取的字节数在 sbiret.value 中返回,可能的错误代码在 sbiret.error 中返回,如下表 48 所示。 - -*表 48. 调试控制台读取错误* - -| 错误代码 | 描述 | -| :-------------------- | :------------------------------------------------------------------------------------ | -| SBI_SUCCESS | 成功读取的字节数。 | -| SBI_ERR_INVALID_PARAM | num_bytes、base_addr_lo 和 base_addr_hi 参数所指向的内存不满足第 3.2 节中描述的要求。 | -| SBI_ERR_FAILED | 由于 I/O 错误,读取失败。 | - -### 12.3 函数:控制台写字节 (FID #2) - -``` -struct sbiret sbi_debug_console_write_byte(uint8_t byte) -``` - -写一个单字节到调试控制台。 - -这是一个阻塞的 SBI 调用,它只在将指定的字节写入调试控制台后返回。如果有 I/O 错误,它也会以 SBI_ERR_FAILED 返回。 - -sbiret.value 被设置为 0,在 sbiret.error 中返回的可能的错误代码如下表 49 所示。 - -*表 49. 调试控制台写入字节的错误* - -| 错误代码 | 描述 | -| :------------- | --------------------------- | -| SBI_SUCCESS | 字节写入成功。 | -| SBI_ERR_FAILED | 由于 I/O 错误,写字节失败。 | - -### 12.4 函数列表 - -*表 50. DBCN 事件列表* - -| 函数名 | SBI 版本 | FID | EID | -| :--------------------------- | :------- | :-- | :--------- | -| sbi_debug_console_write | 2.0 | 0 | 0x4442434E | -| sbi_debug_console_read | 2.0 | 1 | 0x4442434E | -| sbi_debug_console_write_byte | 2.0 | 2 | 0x4442434E | - -## 章节 13. 系统挂起扩展 (EID #0x53555350 "SUSP") - -系统挂起扩展定义了一组系统级睡眠状态和一个允许监督者模式软件请求系统过渡到睡眠状态的功能。睡眠状态由 32 位宽的标识符(sleep_type)来识别。这些标识符的可能值如表 51 所示。 - -术语 "系统 "指的是监督者软件的世界观。底层的 SBI 实现可以由机器模式的固件或管理程序提供。 - -系统挂起扩展没有为支持的睡眠类型提供任何探测方法。平台应该在其硬件描述中指定其支持的系统睡眠类型和每个类型的唤醒设备。SUSPEND_TO_RAM 睡眠类型是一个例外,它的存在是通过扩展来暗示的。 - -*表 51. SUSP 系统睡眠类型* - -| 类型 | 名字 | 描述 | -| :-------------------- | :------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| 0 | SUSPEND_TO_RAM | 这是一个 "休眠到内存 "的睡眠类型,类似于 ACPI 的 S2 或 S3。进入时要求除了调用的 hart 以外的所有 hart 都处于 HSM STOPPED 状态,并且所有 hart 寄存器和 CSR 都保存在 RAM 中。 | -| 0x00000001 0x7fffffff | | 保留给未来使用 | -| 0x80000000 0xffffffff | | 平台特有的系统睡眠类型 | -| > 0xffffffff | | 保留 | - -### 13.1 函数:系统挂起 (FID #0) - -``` -struct sbiret sbi_system_suspend(uint32_t sleep_type, unsigned long resume_addr, unsigned long opaque) -``` - -sbi_system_suspend() 调用的返回意味着一个错误,表 53 中的错误代码将出现在 sbiret.error 中。一个成功的挂起和唤醒,会导致启动挂起的 hart 从 STOPPED 状态恢复。为了恢复,hart 将跳转到监督者模式,地址由 resume_addr 指定,具体寄存器值见表 52。 - -*表 52. SUSP 系统恢复寄存器状态* - -| 寄存器名称 | 寄存器值 | -| :------------------------------------- | :--------------- | -| satp | 0 | -| sstatus.SIE | 0 | -| a0 | hartid | -| a1 | opaque parameter | -| 所有其他的寄存器仍然处于未定义的状态。 | | - -> 注意:一个无符号的长参数对 resume_addr 来说就足够了,因为在 MMU 关闭的情况下,hart 将以监督者模式恢复执行,因此 resume_addr 必须小于 XLEN 位宽。 - -resume_addr 参数指向一个运行时指定的物理地址,hart 可以在系统暂停后以监督者模式恢复执行。 - -参数 opaque 是一个 XLEN 位的值,当系统暂停后,hart 在 resume_addr 恢复执行时,这个值将被设置在 a1 寄存器中。 - -除了确保所选睡眠类型的所有进入标准得到满足,例如确保其他 harts 处于 STOPPED 状态,调用者必须确保所有电源单元和域处于与所选睡眠类型兼容的状态。用于从系统睡眠状态恢复的电源单元、电源域和唤醒设备的准备工作是平台特定的,超出了本规范的范围。 - -当主管软件在虚拟机内运行时,SBI 的实现是由管理程序提供的。系统暂停在功能上的表现与本地情况相同,但可能不会导致任何物理功率的变化。 - -sbiret.error 中可能返回的错误代码见表 53。 - -*表 53. SUSP 系统挂起的错误* - -| 错误代码 | 描述 | -| :---------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------- | -| SBI_SUCCESS | 系统已暂停并成功恢复。 | -| SBI_ERR_INVALID_PARAM | sleep_type 是保留的,或者是平台特定的,未实现的。 | -| SBI_ERR_NOT_SUPPORTED | sleep_type 没有被保留,并且已经实现,但由于一个或多个依赖项缺失,平台不支持它。 | -| SBI_ERR_INVALID_ADDRESS | resume_addr 是无效的,可能是由于以下原因:1. 它不是一个有效的物理地址。2. 对该地址的可执行访问被物理内存保护机制或监督者模式的 H-扩展 G-阶段所禁止。 | -| SBI_ERR_FAILED | 暂停请求因未指明或未知的其他原因而失败。 | - -### 13.2 函数列表 - -*表 54. SUSP 事件列表* - -| 函数名 | SBI 版本 | FID | EID | -| :----------------- | :------- | :-- | :--------- | -| sbi_system_suspend | 2.0 | 0 | 0x53555350 | - -## 章节 14. CPPC 扩展 (EID #0x43505043 "CPPC") - -ACPI 定义了协作处理器性能控制(CPPC)机制,这是一个抽象而灵活的机制,用于监督者模式的电源管理软件与平台中的实体协作,管理处理器的性能。 - -SBI CPPC 扩展提供了一个抽象,通过 SBI 调用访问 CPPC 寄存器。CPPC 寄存器可以是与一个单独的平台实体(如 BMC)共享的内存位置。即使 CPPC 被定义在 ACPI 规范中,也可能实现一个基于 Device Tree 的 CPPC 驱动。 - -表 55 定义了由 SBI CPPC 功能使用的所有 CPPC 寄存器的 32 位标识。32 位寄存器空间的前半部分与 ACPI 规范所定义的寄存器相对应。后半部分提供了 ACPI 规范中没有定义的信息,但也是监督者模式电源管理软件额外需要的。 - -*表 55. CPPC 寄存器* - -| 寄存器 ID | 寄存去 | 位宽 | 属性 | 描述 | -| :---------------------- | :------------------------------------ | :------ | :----------------------- | ---------------------------------------------------- | -| 0x00000000 | HighestPerformance | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.1 | -| 0x00000001 | NominalPerformance | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.2 | -| 0x00000002 | LowestNonlinearPerformance | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.4 | -| 0x00000003 | LowestPerformance | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.5 | -| 0x00000004 | GuaranteedPerformanceRegister | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.6 | -| 0x00000005 | DesiredPerformanceRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.2.3 | -| 0x00000006 | MinimumPerformanceRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.2.2 | -| 0x00000007 | MaximumPerformanceRegister | 32 | Read / WriteRead / Write | ACPI Spec 6.5: 8.4.6.1.2.1 | -| 0x00000008 | PerformanceReductionToleranceRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.2.4 | -| 0x00000009 | TimeWindowRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.2.5 | -| 0x0000000A | CounterWraparoundTime | 32 / 64 | Read-only | ACPI Spec 6.5: 8.4.6.1.3.1 | -| 0x0000000B | ReferencePerformanceCounterRegister | 32 / 64 | Read-only | ACPI Spec 6.5: 8.4.6.1.3.1 | -| 0x0000000C | DeliveredPerformanceCounterRegister | 32 / 64 | Read-only | ACPI Spec 6.5: 8.4.6.1.3.1 | -| 0x0000000D | PerformanceLimitedRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.3.2 | -| 0x0000000E | CPPCEnableRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.4 | -| 0x0000000F | AutonomousSelectionEnable | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.5 | -| 0x00000010 | AutonomousActivityWindowRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.6 | -| 0x00000011 | EnergyPerformancePreferenceRegister | 32 | Read / Write | ACPI Spec 6.5: 8.4.6.1.7 | -| 0x00000012 | ReferencePerformance | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.3 | -| 0x00000013 | LowestFrequency | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.7 | -| 0x00000014 | NominalFrequency | 32 | Read-only | ACPI Spec 6.5: 8.4.6.1.1.7 | -| 0x00000015-0x7FFFFFFF | | 32 | | 保留给未来使用。 | -| 0x80000000 | TransitionLatency | 32 | Read-only | 提供以纳秒为单位的最大(最坏情况)性能状态转换延迟。 | -| 0x80000001 - 0xFFFFFFFF | | 32 | | 保留给未来使用。 | - -### 14.1. 函数:探测 CPPC 寄存器 (FID #0) - -``` -struct sbiret sbi_cppc_probe(uint32_t cppc_reg_id) -``` - -探测由 cppc_reg_id 参数指定的 CPPC 寄存器是否被平台实现。 - -如果寄存器被实现,sbiret.value 将包含寄存器的宽度。如果寄存器没有实现,sbiret.value 将被设置为 0。 - -在 sbiret.error 中返回的可能的错误代码如表 56 所示。 - -*表 56. CPPC 探测错误* - -| 错误代码 | 描述 | -| :-------------------- | :----------------------------------------- | -| SBI_SUCCESS | 探测成功完成。 | -| SBI_ERR_INVALID_PARAM | cppc_reg_id 被保留。 | -| SBI_ERR_FAILED | 探测请求因未指明的或未知的其他原因而失败。 | - -### 14.2 函数:读 CPPC 寄存器 (FID #1) - -``` -struct sbiret sbi_cppc_read(uint32_t cppc_reg_id) -``` - -读取 cppc_reg_id 参数中指定的寄存器,并在 sbiret.value 中返回数值。当监督者模式 XLEN 为 32 时,sbiret.value 将只包含 CPPC 寄存器值的低 32 位。 - -sbiret.error 中可能返回的错误代码见表 57。 - -*表 57. CPPC 读错误* - -| 错误代码 | 描述 | -| :-------------------- | :----------------------------------------- | -| SBI_SUCCESS | 读取已成功完成。 | -| SBI_ERR_INVALID_PARAM | cppc_reg_id 被保留。 | -| SBI_ERR_NOT_SUPPORTED | cppc_reg_id 没有被平台实现。 | -| SBI_ERR_DENIED | cppc_reg_id 是一个只写的寄存器。 | -| SBI_ERR_FAILED | 读取请求因未指定的或未知的其他原因而失败。 | - -### 14.3 函数:读取 CPPC 寄存器高位 (FID #2) - -``` -struct sbiret sbi_cppc_read_hi(uint32_t cppc_reg_id) -``` - -读取参数 cppc_reg_id 中指定的寄存器的高 32 位的值,并在 sbiret.value 中返回该值。当主管模式 XLEN 为 64 或更高时,该函数在 sbiret.value 中总是返回 0。 - -sbiret.error 中可能返回的错误代码见表 58。 - -*表 58. CPPC 读高位错误* - -| 错误代码 | 描述 | -| :-------------------- | :----------------------------------------- | -| SBI_SUCCESS | 读取已成功完成。 | -| SBI_ERR_INVALID_PARAM | cppc_reg_id 被保留。 | -| SBI_ERR_NOT_SUPPORTED | cppc_reg_id 没有被平台实现。 | -| SBI_ERR_DENIED | cppc_reg_id 是一个只写的寄存器。 | -| SBI_ERR_FAILED | 读取请求因未指定的或未知的其他原因而失败。 | - -### 14.4 函数:写 CPPC 寄存器 (FID #3) - -``` -struct sbiret sbi_cppc_write(uint32_t cppc_reg_id, uint64_t val) -``` - -将参数 val 中传递的值写到 cppc_reg_id 参数指定的寄存器中。 - -sbiret.error 中可能返回的错误代码见表 59。 - -*表 59. CPPC 写错误* - -| 错误代码 | 描述 | -| :-------------------- | :----------------------------------------- | -| SBI_SUCCESS | 写入已成功完成。 | -| SBI_ERR_INVALID_PARAM | cppc_reg_id 被保留。 | -| SBI_ERR_NOT_SUPPORTED | cppc_reg_id 没有被平台实现。 | -| SBI_ERR_DENIED | cppc_reg_id 是一个只读的寄存器。 | -| SBI_ERR_FAILED | 读取请求因未指定的或未知的其他原因而失败。 | - -### 14.5 函数列表 - -*表 60. CPPC 函数列表* - -| 函数名 | SBI 版本 | FID | EID | -| :--------------- | :------- | :-- | :--------- | -| sbi_cppc_probe | 2.0 | 0 | 0x43505043 | -| sbi_cppc_read | 2.0 | 1 | 0x43505043 | -| sbi_cppc_read_hi | 2.0 | 2 | 0x43505043 | -| sbi_cppc_write | 2.0 | 3 | 0x43505043 | - -## 章节 15. 嵌套加速扩展 (EID #0x4E41434C "NACL") - -嵌套虚拟化是指一个虚拟化监控程序能够作为宿主客户机来运行另一个虚拟化监控程序的能力。RISC-V 嵌套虚拟化需要一个 L0 虚拟化监控程序(以虚拟化监控模式运行)来捕获并模拟 RISC-V H 扩展功能(如 CSR 访问、HFENCE 指令、HLV/HSV 指令等),以供 L1 虚拟化监控程序(以虚拟化监督者模式运行)使用。 - -SBI 嵌套加速扩展定义了 SBI 实现(或 L0 虚拟化监控程序)与监督者模式软件(或 L1 虚拟化监控程序)之间基于共享内存的接口,可以让两者协作减少 L0 虚拟化监控程序用于模拟 RISC-V H 扩展功能的陷入。嵌套加速共享内存允许 L1 虚拟化监控程序批量处理多个 RISC-V H 扩展 CSR 访问和 HFENCE 请求,然后通过显式同步的 SBI 调用由 L0 虚拟化监控程序进行模拟。 - -> 注意:如果底层平台已经在硬件中实现了 RISC-V H 扩展,M 模式固件不应该实现 SBI 嵌套加速扩展。 - -该 SBI 扩展定义了一些可选的特性,必须由监督者模式软件(或 L1 虚拟化监控程序)在使用相应的 SBI 函数之前进行发现。每个嵌套加速的特性都被分配一个唯一的 ID,该 ID 是一个无符号 32 位整数。下面的表 61 列出了所有的嵌套加速特性。 - -*表 61. 嵌套加速功能* - -| 特性 ID | 特性名称 | 描述 | -| :---------- | :------------------------- | :------------- | -| 0x00000000 | SBI_NACL_FEAT_SYNC_CSR | 同步 CSR | -| 0x00000001 | SBI_NACL_FEAT_SYNC_HFENCE | 同步 HFENCE | -| 0x00000002 | SBI_NACL_FEAT_SYNC_SRET | 同步 SRET | -| 0x00000003 | SBI_NACL_FEAT_AUTOSWAP_CSR | 自动交换 CSR | -| >0x00000003 | RESERVED | 保留给未来使用 | - -为了使用 SBI 嵌套加速扩展,监督者模式软件(或 L1 虚拟化监控程序)在启动时必须为每个虚拟 hart 设置嵌套加速共享内存的物理地址。嵌套加速共享内存的物理基地址必须是 4096 字节(即一页)对齐,而嵌套加速共享内存的大小假设为 4096 + (1024 * (XLEN / 8)) 字节。下面的表 62 展示了嵌套加速共享内存的布局。 - -*表 62. 嵌套加速的共享内存布局* - -| 名称 | 偏移量 | 大小(byte) | 描述 | -| :------------ | :--------- | ------------ | :--------------------------------------------------------------------------------------------------------------------------- | -| Scratch space | 0x00000000 | 4096 | 嵌套加速特性的具体数据。 | -| CSR space | 0x00001000 | XLEN * 128 | 一个由 1024 个 XLEN 位字组成的数组,每个字对应于 RISC-V 特权规范 [priv_v1.12] 表 2.1 中定义的可能的 RISC-V H-extension CSR。 | - -上面表格 62 中所示的暂存空间的内容对于每个嵌套加速特性是单独定义的。 - -上面表格 62 中 CSR 空间的内容是 RISC-V H 扩展 CSR 值的数组,其中 CSR 寄存器 `` 在索引 `` = ((`` & 0xc00) >> 2) | (`` & 0xff) 处。除非某些嵌套加速特性定义了不同的行为,否则 SBI 实现(或 L0 虚拟化监控程序)在任何 RISC-V H 扩展 CSR 状态改变时必须更新 CSR 空间。下面的表格 63 显示了所有可能的 1024 个 RISC-V H 扩展 CSR 的 CSR 空间索引范围。 - -*表 63. 嵌套加速 H-扩展 CSR 指数范围* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
H-extension CSR addressSBI NACL CSR space index
[11:10][9:8][7:4]Hex RangeHex Range
0010xxxx0x200 - 0x2ff0x000 - 0x0ff
01100xxx0x600 - 0x67f0x100 - 0x17f
011010xx0x680 - 0x6bf0x180 - 0x1bf
011011xx0x6c0 - 0x6ff0x1c0 - 0x1ff
10100xxx0xa00 - 0xa7f0x200 - 0x27f
101010xx0xa80 - 0xabf0x280 - 0x2bf
101011xx0xac0 - 0xaff0x2c0 - 0x2ff
11100xxx0xe00 - 0xe7f0x300 - 0x37f
111010xx0xe80 - 0xebf0x380 - 0x3bf
111011xx0xec0 - 0xeff0x3c0 - 0x3ff
- -### 15.1 特性:同步 CSR (ID #0) - -同步 CSR 特性描述的是 SBI 实现(或 L0 虚拟化监控程序)允许监督者模式软件(或 L1 虚拟化监控程序)使用 CSR 空间来编写 RISC-V H 扩展 CSR 的能力。 - -嵌套加速特性将范围为 0x0F80 - 0x0FFF(128 字节)的空闲空间定义为嵌套 CSR 脏位图。嵌套 CSR 脏位图为每个可能的 RISC-V H 扩展 CSR 包含了 1 位。 - -要在嵌套加速共享内存中写入 CSR,监督者模式软件(或 L1 虚拟化监控程序)必须执行以下操作: - -1. 计算 `` = ((`` & 0xc00) >> 2) | (`` & 0xff) -2. 在 CSR 空间中的索引为 `` 的字位置写入新的 CSR 值。 -3. 设置嵌套 CSR 脏位图中的第 `` 位。 - -要同步 CSR ``,SBI 实现(或 L0 虚拟化监控程序)必须执行以下操作: - -1. 计算 `` = ((`` & 0xc00) >> 2) | (`` & 0xff) -2. 如果嵌套 CSR 脏位图中的第 `` 位未被设置,则跳转到步骤 5。 -3. 使用 CSR 空间中索引为 `` 的字中的新 CSR 值,来模拟写入 CSR ``。 -4. 在嵌套的 CSR 脏位图中清除 `` 位。 -5. 将 CSR `` 的最新值写回到 CSR 空间中索引为 `` 的单词中。 - -在同步多个 CSR 时,如果 CSR `` 的值取决于其他 CSR `` 的值,则 SBI 实现(或 L0 虚拟化监视程序)必须在 CSR `` 之前同步 CSR ``。例如,CSR hip 的值取决于 CSR hvip 的值,这意味着首先模拟和写入 hvip,然后再写入 hip。 - -### 15.2 特性:同步 HFENCE (ID #1) - -对于 synchronize HFENCE 特性的描述,它说明了 SBI 实现(或 L0 虚拟化监控程序)允许监督者软件(或 L1 虚拟化监控程序)通过临时空间发出 HFENCE 指令的能力。 - -该嵌套加速特性将临时空间偏移范围 0x0800 - 0x0F7F(1920 字节)定义为嵌套 HFENCE 条目的数组。嵌套 HFENCE 条目的总数为 3840 / XLEN,其中每个嵌套 HFENCE 条目由四个 XLEN 位的字组成。 - -嵌套的 HFENCE 条目相当于在一定范围的客户机地址上进行的 HFENCE 操作。下表 64 显示了嵌套 HFENCE 条目的格式,而下表 65 提供了嵌套 HFENCE 条目类型的列表。在监督者软件(或 L1 虚拟化监控程序)显式发出同步 HFENCE 请求时,SBI 实现(或 L0 虚拟化监控程序)将处理具有 Config.Pending 位设置的嵌套 HFENCE 条目。在处理挂起的嵌套 HFENCE 条目后,SBI 实现(或 L0 虚拟化监控程序)将清除这些条目的 Config.Pending 位。 - -*表 64. 嵌套的 HFENCE 条目格式* - -| 字 | 名称 | 编码 | -| :- | :---------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| 0 | Config | 关于嵌套 HFENCE 条目的配置信息
BIT[XLEN-1:XLEN-1] - 待定
BIT[XLEN-2:XLEN-4] - 保留,必须为零
BIT[XLEN-5:XLEN-8] - 类型
BIT[XLEN-9:XLEN-9] - 保留,必须为零
BIT[XLEN-10:XLEN-16] - 顺序
if XLEN == 32 then
- BIT[15:9] - VMID
- BIT[8:0] - ASID
else
- BIT[29:16] - VMID
- BIT[15:0] - ASID

假设失效的页面大小为 1 << (Config.Order + 12) 字节。 | -| 1 | Page_Number | 页地址右移 Config.Order + 12 位。 | -| 2 | Reserved | 保留用于将来使用,必须为零。 | -| 3 | Page_Count | 需要使无效的页面数量 | - -*表 65. 嵌套的 HFENCE 条目类型* - -| 类型 | 名称 | 描述 | -| :--- | :------------ | :------------------------------------------------------------------------------------------------------------------------------------------------ | -| 0 | GVMA | 在所有 VMID 中使一个客户机物理地址范围失效。配置字的 VMID 和 ASID 字段将被忽略且必须为零。 | -| 1 | GVMA_ALL | 使所有 VMID 中的所有客户机物理地址失效。配置字的 Order、VMID 和 ASID 字段将被忽略且必须为零。Page_Number 和 Page_Count 两个字将被忽略且必须为零。 | -| 2 | GVMA_VMID | 使特定 VMID 的客户机物理地址范围失效。配置字的 ASID 字段将被忽略且必须为零。 | -| 3 | GVMA_VMID_ALL | 使特定 VMID 的所有客户机物理地址失效。配置字的 Order 和 ASID 字段将被忽略且必须为零。Page_Number 和 Page_Count 两个字将被忽略且必须为零。 | -| 4 | VVMA | 使特定 VMID 的客户机虚拟地址范围失效。配置字的 ASID 字段将被忽略且必须为零。 | -| 5 | VVMA_ALL | 使特定 VMID 的所有客户机虚拟地址失效。配置字的 Order 和 ASID 字段将被忽略且必须为零。Page_Number 和 Page_Count 两个字将被忽略且必须为零。 | -| 6 | VVMA_ASID | 使特定 VMID 和 ASID 的客户机虚拟地址范围失效。 | -| 7 | VVMA_ASID_ALL | 使特定 VMID 和 ASID 的所有客户机虚拟地址失效。配置字的 Order 字段将被忽略且必须为零。Page_Number 和 Page_Count 两个字将被忽略且必须为零。 | -| >7 | Reserved | 保留以备将来使用。 | - -要添加一个嵌套的 HFENCE 条目,监督者软件(或 L1 虚拟化监控程序)必须执行以下操作: - -1. 找到一个未使用的嵌套 HFENCE 条目,其中 Config.Pending == 0。 -2. 更新嵌套的 HFENCE 条目中的 Page_Number 和 Page_Count 两个字。 -3. 更新嵌套 HFENCE 条目中的配置字,以设置 Config.Pending 位。 - -若要同步一个嵌套的 HFENCE 条目,SBI 实现(或 L0 虚拟化监控程序)必须执行以下操作: - -1. 如果 Config.Pending == 0,则不执行任何操作并跳过下面的步骤。 -2. 根据嵌套 HFENCE 条目中的详细信息处理 HFENCE。 -3. 清除嵌套 HFENCE 条目中的 Config.Pending 位。 - -### 15.3 特性:同步 SRET (ID #2) - -同步 SRET 特性描述了 SBI 实现(或 L0 虚拟化监控程序)在嵌套加速共享内存中对监督者软件(或 L1 虚拟化监控程序)进行 CSR 和 HFENCE 同步,并进行 SRET 模拟的能力。 - -这个嵌套加速特性将临时空间的偏移范围定义为 0x0000 - 0x01FF(512 个字节),用作嵌套的 SRET 上下文。下表 66 展示了嵌套 SRET 上下文的内容。 - -*表 66. 嵌套的 SRET 上下文* - -| 偏移 | 名称 | 编码 | -| :-------------- | :------- | :--------------------------- | -| 0 * (XLEN / 8) | Reserved | 保留以备将来使用,必须为零。 | -| 1 * (XLEN / 8) | X1 | 在 GPR X1 中要恢复的值。 | -| 2 * (XLEN / 8) | X2 | 在 GPR X2 中要恢复的值。 | -| 3 * (XLEN / 8) | X3 | 在 GPR X3 中要恢复的值。 | -| 4 * (XLEN / 8) | X4 | 在 GPR X4 中要恢复的值。 | -| 5 * (XLEN / 8) | X5 | 在 GPR X5 中要恢复的值。 | -| 6 * (XLEN / 8) | X6 | 在 GPR X6 中要恢复的值。 | -| 7 * (XLEN / 8) | X7 | 在 GPR X7 中要恢复的值。 | -| 8 * (XLEN / 8) | X8 | 在 GPR X8 中要恢复的值。 | -| 9 * (XLEN / 8) | X9 | 在 GPR X9 中要恢复的值。 | -| 10 * (XLEN / 8) | X10 | 在 GPR X10 中要恢复的值。 | -| 11 * (XLEN / 8) | X11 | 在 GPR X11 中要恢复的值。 | -| 12 * (XLEN / 8) | X12 | 在 GPR X12 中要恢复的值。 | -| 13 * (XLEN / 8) | X13 | 在 GPR X13 中要恢复的值。 | -| 14 * (XLEN / 8) | X14 | 在 GPR X14 中要恢复的值。 | -| 15 * (XLEN / 8) | X15 | 在 GPR X15 中要恢复的值。 | -| 16 * (XLEN / 8) | X16 | 在 GPR X16 中要恢复的值。 | -| 17 * (XLEN / 8) | X17 | 在 GPR X17 中要恢复的值。 | -| 18 * (XLEN / 8) | X18 | 在 GPR X18 中要恢复的值。 | -| 19 * (XLEN / 8) | X19 | 在 GPR X19 中要恢复的值。 | -| 20 * (XLEN / 8) | X20 | 在 GPR X20 中要恢复的值。 | -| 21 * (XLEN / 8) | X21 | 在 GPR X21 中要恢复的值。 | -| 22 * (XLEN / 8) | X22 | 在 GPR X22 中要恢复的值。 | -| 23 * (XLEN / 8) | X23 | 在 GPR X23 中要恢复的值。 | -| 24 * (XLEN / 8) | X24 | 在 GPR X24 中要恢复的值。 | -| 25 * (XLEN / 8) | X25 | 在 GPR X25 中要恢复的值。 | -| 26 * (XLEN / 8) | X26 | 在 GPR X26 中要恢复的值。 | -| 27 * (XLEN / 8) | X27 | 在 GPR X27 中要恢复的值。 | -| 28 * (XLEN / 8) | X28 | 在 GPR X28 中要恢复的值。 | -| 29 * (XLEN / 8) | X29 | 在 GPR X29 中要恢复的值。 | -| 30 * (XLEN / 8) | X30 | 在 GPR X10 中要恢复的值。 | -| 31 * (XLEN / 8) | X31 | 在 GPR X31 中要恢复的值。 | -| 32 * (XLEN / 8) | 保存 | 保留以后使用 | - -在发送同步 SRET 请求给 SBI 实现(或 L0 虚拟化监控程序)之前,监督者软件(或 L1 虚拟化监控程序)必须将要在嵌套 SRET 上下文的偏移量 `` * (XLEN / 8) 处恢复的 GPR X `` 值写入。 - -当监督者软件(或 L1 虚拟化监控程序)发出同步 SRET 请求时,SBI 实现(或 L0 虚拟化监控程序)必须执行以下操作: - -1. 如果 SBI_NACL_FEAT_SYNC_CSR 特性可用,则 - 1. 所有由 SBI 实现(或 L0 虚拟化监控程序)实现的 RISC-V H 扩展 CSR 按照第 15.1 节所描述的方式进行同步。这相当于调用 SBI 的 sbi_nacl_sync_csr(-1UL) 函数。 -2. 如果 SBI_NACL_FEAT_SYNC_HFENCE 特性可用,则 - 1. 所有嵌套的 HFENCE 条目按照第 15.2 节所描述的方式进行同步。这相当于调用 SBI 的 sbi_nacl_sync_hfence(-1UL) 函数。 -3. 从嵌套的 SRET 上下文中恢复通用寄存器 X `` 的值。 -4. 按照 RISC-V 特权规范 [priv_v1.12] 中定义的内容,模拟执行 SRET 指令。 - -### 15.4 特性:自动交换 CSR (ID #3) - -自动交换 CSR 特性描述了 SBI 实现(或 L0 虚拟化监控程序)在以下情况下自动交换特定的 RISC-V H 扩展 CSR 值,这些 CSR 值位于嵌套的加速共享内存中: - -- 在为来自监督者软件(或 L1 虚拟化监控程序)的同步的 SRET 请求模拟执行 SRET 指令之前。 -- 在监督者(或 L1)虚拟化状态从 ON 变为 OFF 之后。 - -> 注意:监督者软件(或 L1 虚拟化监控程序)应该将 autoswap CSR 特性与同步 SRET 特性结合使用。 - -这个嵌套加速特性将 0x0200 - 0x027F(128 字节)的空间偏移范围定义为嵌套的自动交换上下文。下面的表格 67 展示了嵌套的自动交换上下文的内容。 - -*表 67. 嵌套的自动交换上下文* - -| 偏移量 | 名称 | 编码 | -| :-------------------- | :------------- | :------------------------------------------------------------------------------------- | -| 0 * (XLEN / 8) | Autoswap_Flags | 动交换标志位
BIT[XLEN-1:1] - 保留用于将来使用,必须为零
BIT[0:0] - HSTATUS | -| 1 * (XLEN / 8) | HSTATUS | 要与 HSTATUS CSR 交换的值 | -| 2 * (XLEN / 8) - 0x7F | Reserved | 保留以供将来使用。 | - -要启用从嵌套的自动交换上下文中自动交换 CSRs,监督者软件(或 L1 虚拟化监控程序)必须执行以下操作: - -1. 在嵌套的自动交换上下文中写入 HSTATUS 交换值。 -2. 在嵌套的自动交换上下文中设置 Autoswap_Flags.HSTATUS 位。 - -要从嵌套的自动交换上下文中交换 CSRs,SBI 实现(或 L0 虚拟化监控程序)必须执行以下操作: - -1. 如果在嵌套的自动交换上下文中设置了 Autoswap_Flags.HSTATUS 位,则将监督者的 HSTATUS CSR 值与嵌套的自动交换上下文中的 HSTATUS 值进行交换。 - -### 15.5 函数:探头嵌套加速功能 (FID #0) - -``` -struct sbiret sbi_nacl_probe_feature(uint32_t feature_id) -``` - -探测一个嵌套加速特性。这是 SBI 嵌套加速扩展的一个必须函数。feature_id 参数指定要探测的嵌套加速特性。表格 61 提供了可能的特性 ID 列表。该函数在 sbiret.error 中始终返回 SBI_SUCCESS。如果给定的 feature_id 不可用,则在 sbiret.value 中返回 0,如果可用,则返回 1。 - -### 15.6 函数:设置嵌套加速的共享内存 (FID #1) - -``` -struct sbiret sbi_nacl_set_shmem(unsigned long shmem_phys_lo, - unsigned long shmem_phys_hi, - unsigned long flags) -``` - -在调用的 hart 上设置并启用嵌套加速的共享内存。这是 SBI 嵌套加速扩展的强制功能。 - -如果 shmem_phys_lo 和 shmem_phys_hi 两个参数的位无法组成全 1,则 shmem_phys_lo 指定了共享内存物理基址的低 XLEN 位,而 shmem_phys_hi 指定了共享内存物理基址的高 XLEN 位。shmem_phys_lo 必须是 4096 字节(即一页)对齐,而共享内存的大小假定为 4096+(XLEN * 128) 字节。 - -如果 shmem_phys_lo 和 shmem_phys_hi 两个参数的位全部为 1,则嵌套加速功能被禁用。 - -flags 参数保留供将来使用,必须为零。 - -在 sbiret.error 中返回的可能错误代码在表 68 中显示。 - -*表 68. NACL 设置共享内存错误* - -| 错误代码 | 描述 | -| :---------------------- | :-------------------------------------------------------------------------------- | -| SBI_SUCCESS | 共享内存被成功地设置或清除。 | -| SBI_ERR_INVALID_PARAM | flags 参数不为零,或者 shmem_phys_lo 参数不是 4096 字节对齐的。 | -| SBI_ERR_INVALID_ADDRESS | 由 shmem_phys_lo 和 shmem_phys_hi 参数指定的共享内存不符合第 3.2 节中描述的要求。 | - -### 15.7 函数:同步共享内存 CSR (FID #2) - -``` -struct sbiret sbi_nacl_sync_csr(unsigned long csr_num) -``` - -在嵌套加速共享内存中同步 CSR 寄存器。这是一个可选功能,仅在支持 SBI_NACL_FEAT_SYNC_CSR 功能时可用。参数 csr_num 指定要同步的 RISC-V H 扩展 CSR 寄存器集合。 - -如果 csr_num 的位全部为 1,则根据第 15.1 节的描述,同步 SBI 实现(或 L0 虚拟化监控程序)实现的所有 RISC-V H 扩展 CSR 寄存器。 - -如果 (csr_num & 0x300) == 0x200 并且 csr_num < 0x1000,则根据第 15.1 节的描述,仅同步由 csr_num 参数指定的单个 RISC-V H 扩展 CSR 寄存器。 - -在 sbiret.error 中返回的可能错误代码在表 69 中显示。 - -*表 69. NACL 同步 CSR 错误* - -| 错误代码 | 描述 | -| :-------------------- | :------------------------------------------------------------------------------------------------------------------------------------ | -| SBI_SUCCESS | CSRs 同步成功。 | -| SBI_ERR_NOT_SUPPORTED | SBI_NACL_FEAT_SYNC_CSR 特性不可用。 | -| SBI_ERR_INVALID_PARAM | csr_num 不是全部的位数,并且要么:
* (csr_num & 0x300) != 0x200 或
* csr_num >= 0x1000 或
* csr_num 未被 SBI 实现。 | -| SBI_ERR_NO_SHMEM | 嵌套加速共享内存不可用。 | - -### 15.8 函数:同步共享内存 HFENCEs (FID #3) - -``` -struct sbiret sbi_nacl_sync_hfence(unsigned long entry_index) -``` - -在嵌套加速共享内存中同步 HFENCE 指令。这是一个可选功能,仅在支持 SBI_NACL_FEAT_SYNC_HFENCE 功能时可用。参数 entry_index 指定要同步的嵌套 HFENCE 指令集合。 - -如果 entry_index 的位全部为 1,根据第 15.2 节的描述,将同步所有嵌套 HFENCE 指令。 - -如果 entry_index < (3840 / XLEN),则根据第 15.2 节的描述,仅同步由 entry_index 参数指定的单个嵌套 HFENCE 指令。 - -在 sbiret.error 中返回的可能错误代码在表 70 中显示。 - -*表 70. NACL 同步 HFENCE 错误* - -| 错误代码 | 描述 | -| :-------------------- | :--------------------------------------------------------------- | -| SBI_SUCCESS | HFENCEs 同步成功 | -| SBI_ERR_NOT_SUPPORTED | SBI_NACL_FEAT_SYNC_HFENCE 特性不可用 | -| SBI_ERR_INVALID_PARAM | entry_index 不是全 1 位比特,并且 entry_index >= (3840 / XLEN)。 | -| SBI_ERR_NO_SHMEM | 嵌套加速共享内存不可用。 | - -### 15.9 函数:同步共享内存并模拟 SRET 指令 (FID #4) - -``` -struct sbiret sbi_nacl_sync_sret(void) -``` - -同步嵌套加速共享内存中的 CSR 寄存器和 HFENCE 指令,并模拟 SRET 指令。这是一个可选功能,仅在支持 SBI_NACL_FEAT_SYNC_SRET 特性时可用。 - -监督者软件(或 L1 虚拟化监控程序)使用此函数进行同步 SRET 请求,并且 SBI 实现(或 L0 虚拟化监控程序)必须按第 15.3 节中的描述进行处理。 - -该函数在成功时不返回任何值,而在失败时 sbiret.error 可能会返回表 71 中所示的错误代码。 - -*表 71. NACL 同步 SRET 错误* - -| 错误代码 | 描述 | -| :-------------------- | :--------------------------------- | -| SBI_ERR_NOT_SUPPORTED | SBI_NACL_FEAT_SYNC_SRET 特性不可用 | -| SBI_ERR_NO_SHMEM | 嵌套加速共享内存不可用。 | - -### 15.10 函数列表 - -*表 72. NACL 函数列表* - -| 函数名 | SBI 版本 | FID | EID | -| :--------------------- | :------- | :-- | :--------- | -| sbi_nacl_probe_feature | 2.0 | 0 | 0x4E41434C | -| sbi_nacl_set_shmem | 2.0 | 1 | 0x4E41434C | -| sbi_nacl_sync_csr | 2.0 | 2 | 0x4E41434C | -| sbi_nacl_sync_hfence | 2.0 | 3 | 0x4E41434C | -| sbi_nacl_sync_sret | 2.0 | 4 | 0x4E41434C | - -## 章节 16. 偷窃时间的核算扩展 (EID #0x535441 "STA") - -SBI 实现可能会遇到虚拟 HART 准备就绪但无法运行的情况。例如,当多个 SBI 领域共享处理器,或者 SBI 实现是一个虚拟机监视器,客户环境和其他客户环境或主机任务共享处理器时,可能会出现这些情况。当虚拟 HART 有时无法运行时,虚拟 HART 上下文中的观察者可能需要一种方式来解释比预期少的进展。虚拟 HART 准备好但必须等待的时间称为“被窃取的时间”,并且对其进行跟踪被称为窃取时间核算。窃取时间核算(STA)扩展定义了一种机制,使得 SBI 实现能够为每个虚拟 HART 向监督模式软件提供窃取时间和抢占信息。 - -### 16.1 函数 设置窃取时间共享内存地址 - -``` -struct sbiret sbi_steal_time_set_shmem(unsigned long shmem_phys_lo, unsigned long shmem_phys_hi, uint32_t flags) -``` - -设置共享内存物理基址,用于调用虚拟 HART 的窃取时间核算,并启用 SBI 实现的窃取时间信息报告。 - -如果 shmem_phys_lo 和 shmem_phys_hi 不是全一的位数,那么 shmem_phys_lo 指定共享内存物理基址的低 XLEN 位,shmem_phys_hi 指定共享内存物理基址的高 XLEN 位。shmem_phys_lo 必须是 64 字节对齐的。共享内存的大小被假定为至少 64 字节。在从 SBI 调用返回之前,所有字节必须被 SBI 实现设置为零。 - -如果 shmem_phys_lo 和 shmem_phys_hi 是全一的位数,SBI 实现将停止报告虚拟 HART 的偷窃时间信息。 - -flags 必须被设置为零。 - -当共享内存被用于窃取时间核算时,预计共享内存不会被监督者模式的软件写入。然而,如果监督者模式软件的写入发生,SBI 实现必须不会表现失常,然而,在这种情况下,它可能会使共享内存充满不一致的数据。 - -SBI 实现必须在系统复位时停止对共享内存的写入。 - -共享内存布局的定义见表 73。 - -*表 73. STA 共享内存结构* - -| 名称 | 偏移量 | 大小 | 描述 | -| :-------- | :----- | :--- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| sequence | 0 | 4 | SBI 实现必须在写入窃取字段之前将该字段递增为奇数值,并在写入窃取后再次递增为偶数值(即,奇数序列号表示正在进行的更新)。SBI 实现应确保序列字段只在很短的时间内保持奇数。主管模式软件必须在读取窃取字段之前和之后检查这个字段,如果它是不同的或奇数的,则重复读取。这个序列字段使在 32 位环境下执行的监督者模式软件能够读取窃取字段的值。 | -| flags | 4 | 4 | 始终为零。未来对 SBI 调用的扩展可能允许监督者模式的软件写到共享内存的一些字段。只要 SBI 调用的 flags 参数使用零值,这种扩展就不会被启用。 | -| steal | 8 | 8 | 该虚拟 HART 没有空闲和安排外出的时间,单位是纳秒。虚拟 HART 空闲的时间将不被报告为偷窃时间。 | -| preempted | 16 | 1 | 一个指示标志,指示注册了此结构的虚拟 HART 是否正在运行或停止。如果虚拟 HART 被抢占(即窃取字段在增加),SBI 实现可能会写入非零值,而在虚拟 HART 重新开始运行之前,必须写入零值。例如,监督模式软件可以使用这个标志来检查锁的持有者是否已被抢占,并在这种情况下禁用 optimistic spinning。 | -| pad | 17 | 47 | 用零填充到 64 字节的边界。 | - -sbiret.value 被设置为 0,在 sbiret.error 中可能返回的错误代码如下表 74 所示。 - -*表 74. STA 设置窃取时间共享内存地址错误* - -| 错误代码 | 描述 | -| :---------------------- | :------------------------------------------------------------------------------------------- | -| SBI_SUCCESS | 偷取时间共享内存物理基址被成功设置或清除。 | -| SBI_ERR_INVALID_PARAM | flags 参数不为零或 shmem_phys_lo 不是 64 字节对齐的。 | -| SBI_ERR_INVALID_ADDRESS | shmem_phys_lo 和 shmem_phys_hi 参数所指向的共享内存是不可写的,或者不满足 3.2 节的其他要求。 | -| SBI_ERR_FAILED | 该请求因未指明的或未知的其他原因而失败。 | - -### 16.2 函数列表 - -*表 75. STA 函数列表* - -| 函数名 | SBI 版本 | FID | EID | -| :----------------------- | :------- | :-- | :------- | -| sbi_steal_time_set_shmem | 2.0 | 0 | 0x535441 | - -## 章节 17. 实验性 SBI 扩展空间 (EIDs #0x08000000 - #0x08FFFFFF) - -未安排。 - -## 章节 18. 供应商特定 SBI 扩展空间 (EIDs #0x09000000 - #0x09FFFFFF) - -从 mvendorid 开始的低位。 - -## 章节 19. 固件特定 SBI 扩展空间 (EIDs #0x0A000000 - #0x0AFFFFFF) - -低位是 SBI 实现的 ID。固件特定的 SBI 扩展适用于 SBI 实现。它提供了在外部固件规范中定义的特定固件的 SBI 功能。 - -## 参考资料 - -▪ [priv_v1.12] The RISC-V Instruction Set Manual, Volume II: Privileged Architecture, Document Version 20211203, URL: github.com/riscv/riscv-isa-manual/releases/tag/Priv-v1.12 -- Gitee From 569a558c43284dd723d229dee4694e3bbcade202 Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Thu, 3 Aug 2023 01:54:25 +0000 Subject: [PATCH 28/68] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=96=87=E4=BB=B6=20ar?= =?UTF-8?q?ticles/images/sbi-firmware?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../images/sbi-firmware/instruction-spec.png | Bin 111317 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 articles/images/sbi-firmware/instruction-spec.png diff --git a/articles/images/sbi-firmware/instruction-spec.png b/articles/images/sbi-firmware/instruction-spec.png deleted file mode 100644 index e8adbab57331f39254f798ca05f72691b99742d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 111317 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 GIYMAKb#+yAv)h{Zvhbt?BG4P7%E@@H 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 UMbfZF5@2XP(7j)-W*hPU0L)>9#sB~S -- Gitee From 762f36b341f79c7ef91af5cae0b35ba6e874a364 Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Thu, 3 Aug 2023 01:55:18 +0000 Subject: [PATCH 29/68] =?UTF-8?q?Revert=20"=E5=88=A0=E9=99=A4=E6=96=87?= =?UTF-8?q?=E4=BB=B6=20articles/images/sbi-firmware"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 569a558c43284dd723d229dee4694e3bbcade202. --- .../images/sbi-firmware/instruction-spec.png | Bin 0 -> 111317 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 articles/images/sbi-firmware/instruction-spec.png diff --git a/articles/images/sbi-firmware/instruction-spec.png b/articles/images/sbi-firmware/instruction-spec.png new file mode 100644 index 0000000000000000000000000000000000000000..e8adbab57331f39254f798ca05f72691b99742d7 GIT binary patch literal 111317 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 GIYMAKb#+yAv)h{Zvhbt?BG4P7%E@@H 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 UMbfZF5@2XP(7j)-W*hPU0L)>9#sB~S literal 0 HcmV?d00001 -- Gitee From fc1b0a1fdad7b4c94fecd6e529e85b128f79b891 Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Thu, 3 Aug 2023 01:55:29 +0000 Subject: [PATCH 30/68] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=96=87=E4=BB=B6=20ar?= =?UTF-8?q?ticles/images/sbi-specification-translation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sbi-specification-translation/fig1.jpg | Bin 9376 -> 0 bytes .../sbi-specification-translation/fig2.jpg | Bin 15500 -> 0 bytes .../sbi-specification-translation/fig3.jpg | Bin 21174 -> 0 bytes 3 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 articles/images/sbi-specification-translation/fig1.jpg delete mode 100644 articles/images/sbi-specification-translation/fig2.jpg delete mode 100644 articles/images/sbi-specification-translation/fig3.jpg diff --git a/articles/images/sbi-specification-translation/fig1.jpg b/articles/images/sbi-specification-translation/fig1.jpg deleted file mode 100644 index 29606bbf423e27cca4b072435cbac049859327fd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9376 zcmZ8`RZtuZ&?HW95AKTxg1bv_cPF?9g6l$XcXxMp4~x6IyZZ)*z3;!eyN8>Hu6doV zo|>K+HQ66O(g9FV+EU^wS}MF0(f{R6`A|8~jB+q=(83O!Y0|~iRFnW5AKn182*phn zZd#`3F!bb&{|z1@{Rn$V{Yv+2tkd^GHqf^M*?EHj3PGSDJ1b^S@_S5gGaoZsK+nFX z9mq$32;^4Rv+RZEfaj0MS|2sg-7n-_;$Wx#Q{fW~NqBbhH9qXq>7Dqvc$9vZzA768 z&i1hY2fV3oi(czgjE5nyK#%v~`;4o)If&_pXpixW^2_%Nk$sU%hzS_&BkF;8v#x6A zs^_mr0Z; z;$!+j{+{=~1J3I<7898PmP2YjcRr0F9pGMQ3@4szol?(hid$dZl68wEDJ)Lo=lx z5-}8T*vMDB+GRyInZiREDU>aMW*X$rpQbNa-}{bPFB5ToeI;!#^0Jh78>Xbm&u`&P z6NH!Do(W_t%t`zP_fG$^mb{6W3+{rD0O`M&(5^c@E?f>NcYP(Y2P+32cpOPTQ>CmU z%1&5a*te-dGsar7UMBF4GpqERiUAvjUZhmiDGOv7TXOb=+bhhU?gK($97+NSG*62I8JdksWa&~Z(HJ{8@~Tlhc8oDo&x*PDX@9Ac@-uwFAvhO&udT4 zd3JOVq=qph_xV}9&M7kL(4e&$=E*0GkNEbxd-?y7mSC2a!WFVQXr=ry|1r7jHCRuZ zr_pIa`v>%|RoOXg%p;;T+so}IvKqz^$#3)K9FXarA=Y6IwJn`OK-z0&H)@7$u%;tv zH|Zh415Gln!Pslo*;M-`KTqxqp)n^PI$d6?M{o~t$+L|@?=5*_Bj4OvdTFkuYtn)z zV*(Theo{H`Ht!?02tPvt@gil9L#m6}py*@Rc9AC7q!#`>*_y^rtcjaqVhmo};Nsj| zM{T9dncqqdexq|4%AA*q>J`m!Coc6-uS#pPO@>;;&iZ!7@&!v1ryL5z*exz1#OkZ5 zL*zNA7L65UeQbt4YR>X3QwDl|e@B+b^zAvPD8JYqxvy4A0tA=ypJsNz9=sI%A60#q z7aEcEp|q*MSgss175S&t4hQ!wQaf|mYv_M7^*@*>mfH4^Yf_{({#%yB@&C%uT%i3* zybM6QN45~h_H(Z2rn*YV=hXK7tI&HsWk8nAK*K8q^|KzvBP!MXinoA%#^XeRGyb!; zNwn{Dc((Ui4~W1Jm8N#fF|IH$ue#@(I)91i)Crwuzm|q~+I=5DP4pa^ZaR{iL?-cT zYk)onJqLPD;pS?V{qh#Nli8uCk$3w&SWc~fHviK};y#iAI+z<%a_uN`L|kUZmJ(df zqOS+z+q0<>3m*D1Yp`E%+N2E{O}#B~c)a#VbCGxlG7g?$5TZLwp{p85r-EQDO5J1=>C7y z{GV=Z{>lmk^$C&r3k3y*QcKUR@s^Qp_0C?lISI-L;Xo}}R3QI$rC!0XoqdULBD1@V z`tjp$e4+No#VZ|K0t+tGfXXlW9%$H5p4{2Vo)1{;@>k%jn^2D`Uj}-jjNifQ`7DWc zp)w{sc=Q5wd2!*6r(JOL{%N&o(OwQa0(psX(gFT7y$^O2?CexAq24i$DKXvItOi4H z^@@gS!@a;ij{I;g5}88SS{YPpwMGzRin`sZV%*0{Ph)Q=}Ypc5kg3xc1CtUrSZrs^(c)X&q47I?De`h)#^roOO}lH zs_<|+&;6~(?~(Kb^41nUdmnyTzxG@)mghK9yw5}ZqKdt5FnZad*qgrXfl!L4Fo|Aa zOlblYs>R`_g)tmwt`5)^Vu6DXISE312EH8xzavz{M0dH3$`Bk0j^~NoWaUoJ9ewoM zq8DD)ivkn0zv)rZWH}P(=xhq zfov$DKbI41q+OHqEZ)~tjeb0sA*^Q`2pQ54or)?ADo0OAS>Kd>;5+g$eN_-)rend~ zTYauC0v>IasCjO%`;b)iBgZe%0tsLs-7ojL;XN#YqwL z`tq1Qikb9?L)>pm9{V^;U~TzOQ@E4+`^@KL?YxCz_SW_tp_k#8=k)w4CR)dKp}F<5 zBW5>?%2G>>cCVe6I5h>omtS z<=Z5*B1YFk%C21Q2JEv7v1d_b+(U;+GXo(IXqGMK&ED_*yZ%fP+8+Ml!bvNce_Kbc z49eETs!7l+=@pB+>KH%1hVehzpQdE3Bc&~v=x}lZ`meOc7uG7|2R?>_&DL*SV0^B@ zQGn}^pjZ9c>BSaqT|$UE?0e)k(4AGm2~tbB-Bx^G>Yg*3!B)(p$E12n!4s_I+gbQm zP48L_u^20f#J+;r2l=)%9by5;cTW+9%1`C)z(33Dr$AJ2*HX?kok$oAKO%|Rh2>?{3%WfSH28>4vC zfY)eIVmxgo>=;Qtm1<;GiKcZRkPo6(I^tS1MbLmerM8-K7*X}A$-T_j(h{t2l||!} zF_eI&U)vi<82LEkR@k4)?H1;1RABYYEN~Wq#FGf_>4)h7LKW4mbjGpx)F3=7y*oMl z-h_oqK8GBY_~r>Qw}hGF9xx%>lDt`)Tm9<4Dl7OhHsvy>mDU_744aTmN|g@cfMwz> zv`{#A4bVY>Mw6*(*m;@-Xl%WZQv>vzrEzsNF*@}!Lx-H^P51WDDR#r)lI>M@q=dsy zrLL)fL%=92;`z-;xErPrud$)X@zWXqQ&>t02Ei_3@frZhrlwEJW4 z#_1^r{UXA4s_fZU(>MG9A1Ap$U9Z>^{?>PZ7?$I%v|Sy(HUd4kF$EydVEwRi)s>Xh z0*2>$twI}AewfEr>8qQ8pUIaCtzl%WJ2$k`8j_^rDP=yEh^mAs_i|p99CYTv5GQY{ zGi}(6@3^QsL8^AfyhVn!F!j&9*l!fe&v)ABVB8TC9_HRO_F)UdjFL5Tx;XCG1@xdx~ly)ofmD+Bc0CO|h^cG}sa{WCv!Fgh7ET zcH<4+&%a@x%p(S(Yb#OBU3KzU+M8Z6?wv+WGK&1Rh* zTy$jUbe7pWpfBm{bI;!Flek~maYkB1&V8aUKr0)iBOZjItheheX2Yyl5}P`dU_*g;N}FEiRxBLL zO8w4!4s~X!pzVMKV)m>~cRbb?3C$f)fcs7Rz}Ti?ZQw};WmvR2>$htaJZ6^An<@{r zpqLhC6x|#1 zd~AFTSfu?^MIV)++4)5iau{Z*PuT*?KTQlmMWCRdQK7#&ctVNPT-v`ktpyQuh#I^E zwGq+t9Qb~GB+Z>d?`y*<+ghs7FaD?KuU-}oPWofFP|*!ET}yn^5lpUo(Y|(GU!7|k zUeW=R3Ujh_5!G_KjSmN*&Cl1gCN_HVxgC3@D7FLpzgbc&u&_cVx)(IyWM`#zZ@>huA)$-=s>J2ppI5Y z$byXGMsNX=o8O*%p@2lo`{Z=LYoDAJeT{I>-tINz zwn5oZ>~FiHN$mVA*-s>nfyXdP4F@>hYoR*w7A-0W3I!n+ffZeEdndLrGmhxrxPArj z;r?0foFAy}wt|!-;V20!>0pp7aQTNg@46}!72gTPd>x1(ZH)c2n1X5=9-7H8_MvpCvPpmYQKXNxi5;8H^zwYAt2O~8?AAKL8fGV13JRf{0`znUByy+!H{%{ zurR3qS-)e1MPPygzFhQoG7``fjSWyDQO2<-r7LK}6Ev8w;A|-_#y3;l-!kGu5gjp} zw#V+?_bL3x_5!hS39+#d+>0VINvu4JAyYngcFNJhJJQ;sbr(R)j>g(CR*- zvJr7|c{}nyq>swg2i25-NZ>MjHbt{KP%66hA2HU+=zG$!;`l|Ox$7-dGrMGJF?VQh zs=6!HrYTo z&QGZ!k{l*}?+f3{r~3HC(-%J-uQLv6AA=%V5uIrP&Ec11(<%IAWxI-H;veO?=Q8Zu zDQF@#8Dzf^jo#GTDHS`nZAo7Zv~&ofQbEl=dLj4ht2*h|3n=W2BHJ2e1d-z_RVX4# zAV^m;?{h+hlD+*4(Qta7n`?=R%)wmy{p)J?f^DrUWHCx8iWS;fk9WAHB>1knJr@ou zAQ})PUgNI2h2y|Oj^#Fmy(U$?WUDIjNwx2K9FKeB*Geuoa0K$Ug7vCr>1Jp=dHD3l zS*pl60g%qLsG0jkxHNfF2uS1bsMn?OKI5W(8M3gIkVcD3>vPf^;?$@GU;lbhNZ!$w zpoWp*2%$q++89N8@$N<2ENdUxiW+@q$D1hDcj<;TWhtW$!>=iHNaWfeN!ya# z;L^(RAV=X{LBBhe`i2;&`M46^BpWj{`Lym{MT$xDX7*1`2g3~THH$;)JNfd#cGLTI zeBbBdI1G25gbL;lAgSQ6dSLFEL%YZZa`>N)NAVqWxvkJ|U&Rv-=iovl$B6{f>0Pk* zOe#BbNHC#%sw)UV=Z}qQ{}hy&2hbn-u0$yCC^o3;bmMo^>Jrt{5*PRFEQ4ZZo-TPn zQ~9~3k#RB>{;_W;RM7c{Jvb6!E*>^*1tpm^c5#}XdN52`6;srz*sIo|%?bA*Q$sP% z+rm%_I^)eItHLeB*MD{6c~`?@7HTicf)IN=|dXfR%wjY1>gmV8+`y?!W&E%DkOKyKRbTG>DCFfCQj0)=b+ z&`J>!DDH+2xU?|(%`EwP>cReJ2{|g#v(peL zha4zE8z-?(JsF1PIH%L_+C7N_RG=}#3sj0?4SL4HCnCY4B44uNy5|@;a8ruQgsq(H zGb6r?SMr`R{w8|VLtC1EQEs=-rfVKM74B;3Zu;P@IWajd-nBc|uw{=w&ZfL<@0HzgFy!8Mne>RCqYG=_Vz$ zW?~?QclX&Pg78L!BQIHsy+r3>tHJ`=6UG+9AR8?1f39jO0yS?FU zzJ4_EIZ%U)6s=Lai0X|6$Fq=pkDHo zd;dh#h+UnX2)o)Bj2)_Bb+T+dRO?T&PsBEG5a?@}^TCYj&(PMFO z*p0~89j742sot>}+dIVIt9ukvFwi|%SJ+#Jno*&pBdMV~@a`{D4z%Xl-pz1z+#7I} z%rDl<-8FsXcy+ac703P5&+p=1zuC`-d8RH<6$x-+x2@=s+`qL2SVT?#C#_}2wH3LbT7bYVRco<_KS{@-N_wlidaJ4mypWJpb#bs^-##LtK z%u)7F18FJ#7Q@M8Y6TsBp8!Ct{UL>hqGk4?dLv8gjFDFAfSo|i81}M>V8^^rq;gY+ z-ZB5*?;vdJE{BWV@fNsvwB0%gj^O1lL~YmSJ5=FHzRWKff^v*h3;b{sJxLQ=kitR;A8v{T_B(!+h>f{F7VU zzWaD{*s;`J;NBSudLpMv<4^ACru$1$S*DsQPR=;$gDhKc=ZWRMLisBdu`c)wq}%XD ztW~_Im61D&;Oi`?AmV7q z)SGm!6!vMh_SzYoxhEYHbk7@fE5)s*74AUAk$@ns#tqnN;rT14-G&qJvZ>jr?IYmy zpF_KeK>1RoG4$mdhK^YHa4(h&IS$=J4;kfoC#h6wgCR@M;fn+@jg*_`NpH$$kjMN< zABRkY#bj&b_g<~9eONoe;#lm+T3YvGDSJ6Ck3fb5z1Ua5Teg1JAZ0i|ayeL9Pc3}C zwa@@4Qno`8lj{JUs)-tp@7%s0B^t)tLV>{=eEamH6$!bK0exKD z49*J~$8KYXRcV{cy0RZ0COXAQl+L*O`sTdhJEgMvmaFe+wlTT@GH9=+U6-dMNcl-H z@GONuPRKq(2~4}*<4XZtFp-oVtCh(ud*A8tS<#CO#aWElcY9_@)LsOgQcu{APo)73 z{8^rd?m2D@TP+u_0lNF0?gyZb*Zh+-$R zC0=WwJe-1)SzxhXK@48+&*O$W?aG^ zDDhPXSkY;RQ^IvGmlT)RYqY_VsX%j8MD3s<@_j2cq% zs?cpY7_}1T!#1Bba(x`}JI4gDJh|e=0%p#{n(x)4vnrE^f9DnYel}dSDxbtP^{=JW zNj%rXK=qOPU?>S-zn$2m7Ko+4ETJlPRZQv~M8nH|4K01nzOG6>Jzq_EYZWHEY3Jk# z@2_vAwJUGlXQZ{0Y^J>s6iL`Uic_^1usqH^NtR%9mP9NdyzSp7SZ{iGjL%BXT*IQR zA(*_h=MAOMJ2*lACMY_go{kw+LHF!^DQey@=c>GZAH%t10vJ3nbN;g$atFi6*S-{( z*T+4pm2I7pyRMpYdz34I$d3X&Dh?0svPH`j40QHx`-eyu z5dxGXA-r54fy_yXv7~tSfWI6X(NPUBlU-3^3+*0f`m48TAaYphcK;C#Q;-yjJ)$@j zLLyrBZ(S9`lKT>=dSx>cn+doy-}`mFN1%nOTA`CI`@51O{5K=gD}zmQ!}4g&WGb^P zq_Slxu(q>rZ4TLz#IcmO+&sV%qmSo14ry{sW|AFIvtSr1bBFazga}Vx$~gC{?{0s%U2A24q?5^Py#x&R2u;^0jU1Y^ zCVd80$+WluNEG+f^GhP^Gn54VRO(r4p^#8*Sj?@+5Y|r{!aUg_o9S&9yYs!SKWAK{ zy7Cz0%q=RVQ$%mPA@Oip#cZv$@!bw5JGoaCYl#0U{)w`?h;;m(?8l~@bF&Q*!1&W3 z5RRX}YAy@koxOf-z87ia+;sB2ZUaG-qo!2DC-_sZb3s`8ogP6}b5JH|N~U4H3a}qn z5RDQXW?VD(g?ss@6~BTm9RrU9ZU~o>mmBc{EEhsgOSethYkIEO8gbfcp|M8z)I54} zVzB{KC^%$i!+BU1uVMdh5b3e|gr$Zj@E~Qx-^W`!Un3U2hvrxU(PT>IBgFR^+M95t0L-gK(EF1f11Z5t>olO9^bkRM#T*ex$PQ323;u@Z(2NjKfe@D zl^qN#Wl&E$RYrMOau_uF$>BW|x&Y$yx3yJ>96y(?t5mcc($7cB8!>9<>kJiqLu{h( z%)Y~!Z|jHm7ZvcI+Q-!yxnKXjFj@+-SJ9NmpmY%jz1h-!B-`?YmHH;P$A9mZFj{y zLX%KB`2?I3?3|38S{-T+{PR4j|3uc*!-bb$g3fevoP!ipa~e$3x1>FeEIJM_I*Fh< z?+T$kP^1c>pO>4d?DDIsx!);UjFN&HEX+CWG3Ij7kP)xG+brE?kNa?64qF+dMrJ_D zF#@#}6Y6hqVKKq_lkSD?U+xmxG8g#H#3gFBaD);J=B6;aehDW57n>mjjq;e~ZzLa6 zPm!Y>^}iwQL1*I)e2o)9R2i-H`{T8Ex&PXrWw2UzLyNZh)C*F7=A-X1nwBV++(Y7Z z&s_w4uOgn{{6Tq+@o$c>>orIqbwB=f-HPJoo&N@_N4a1fn1ysTnp+sN-fY~{wkZut zy!IxGLRuY{B)|O`$kFDz)-nO6QED8Cw}*N);mwn~rH#MNY4bb(-q+FS`_)mZric_; zqiyc2?YppoW>?Go^ztvc_I|eL?5n?K(lG2)S6}pr$fIu8Tk=ne4GCj6HY!@CUaOB6 z|B3F8@*wZH{D>80*(gV&T$4Kri|YG934E!<kcKJeLlE4mN*PNUA=h1_6#0tCABK-JeFA^* zjQ6O9ajy2uLI2*9#0HmkUfDp8EcUKdQ4mYvoaOVC?3m7;A9O1j;4S9w@K?A@sOG?+Q6V!da&Ha z1{lRy88&QqJLj5mf00i~W>vI^Aypw=N`dI>m%XgBLDa+J`&k1SJ`cl11+yG>Pqq0} zrvAIq`U^7;x~nND@;=q?0F}MChF~StTMjWbF;UxyPG%d^ng7y&$DWr;&H2os3o`=H zaTu$royWd3_`{7?KZPi==- diff --git a/articles/images/sbi-specification-translation/fig2.jpg b/articles/images/sbi-specification-translation/fig2.jpg deleted file mode 100644 index 77eff48319bbcc6b5078d149da9a9f91f03677d2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15500 zcmajFV~{2=7bV=b-P5-1Y1`JcZQHhO+cu_cP1{d%+P3@IdEecw-Tm>^mmjHAC6%O3 z&bi6GH!9K+5}|w`Ae!PLN*YSs#Ge1!%#fgYptMxr!Jq<3VtKL@B_$*!m_PR#(O^yN zKV@;L`rj~G)2DSSUi(VEu#W;dG-+N0?*Y>HV5jze4=LZ+-@C5?&H)AdfqY}{nBUW< z@NWihf}0I){Z9qW{wKhOfP$~QR}dilm*8r}mtf=C%b(Z$fq;fx;Fm9ds^B2-=qU=A z2L!Hz1A&lK!N3fE0U+BW)tkeU;k
}96& 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 diff --git a/articles/images/sbi-specification-translation/fig3.jpg b/articles/images/sbi-specification-translation/fig3.jpg deleted file mode 100644 index 6be45f79976d8ec1f0be02c2cc443da2b26cc92b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21174 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 -- Gitee From 8220e7567d00a45295542ac461164710d4d116e4 Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Thu, 3 Aug 2023 01:56:35 +0000 Subject: [PATCH 31/68] update articles/20230728-sbi-firemware-analyze-1.md. Signed-off-by: Groot <1219671600@qq.com> --- articles/20230728-sbi-firemware-analyze-1.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/articles/20230728-sbi-firemware-analyze-1.md b/articles/20230728-sbi-firemware-analyze-1.md index 6b31559..6420950 100644 --- a/articles/20230728-sbi-firemware-analyze-1.md +++ b/articles/20230728-sbi-firemware-analyze-1.md @@ -3,7 +3,7 @@ > Date: 2023/07/28
> Revisor: Falcon [falcon@tinylab.org](mailto:falcon@tinylab.org)
> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux)
-> Proposal: [RISC-V Linux 内核 SBI 调用技术分析](https://gitee.com/tinylab/riscv-linux/issues/I64YC4) `
` +> Proposal: [RISC-V Linux 内核 SBI 调用技术分析](https://gitee.com/tinylab/riscv-linux/issues/I64YC4)
> Sponsor: PLCT Lab, ISCAS # OpenSBI 固件代码分析(一) -- Gitee From da611d2df0286e3b39e030ef182f10e09fa5e960 Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Thu, 3 Aug 2023 08:49:01 +0000 Subject: [PATCH 32/68] =?UTF-8?q?=E9=87=8D=E5=91=BD=E5=90=8D=20articles/20?= =?UTF-8?q?230728-sbi-firemware-analyze-1.md=20=E4=B8=BA=20articles/202307?= =?UTF-8?q?28-sbi-firmware-analyze-1.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...-firemware-analyze-1.md => 20230728-sbi-firmware-analyze-1.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename articles/{20230728-sbi-firemware-analyze-1.md => 20230728-sbi-firmware-analyze-1.md} (100%) diff --git a/articles/20230728-sbi-firemware-analyze-1.md b/articles/20230728-sbi-firmware-analyze-1.md similarity index 100% rename from articles/20230728-sbi-firemware-analyze-1.md rename to articles/20230728-sbi-firmware-analyze-1.md -- Gitee From 348dcf04ae0267c886b26f95c6c70bc77f0f612d Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Mon, 14 Aug 2023 02:58:26 +0000 Subject: [PATCH 33/68] =?UTF-8?q?=E9=87=8D=E5=86=99=E4=BA=86=E8=AF=A5?= =?UTF-8?q?=E7=AF=87=E6=96=87=E7=AB=A0=EF=BC=8C=E5=B0=86=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E6=94=BE=E7=BD=AE=E5=9C=A8=E5=90=8E=E9=9D=A2=E7=9A=84=E6=96=87?= =?UTF-8?q?=E7=AB=A0=E4=B8=AD=EF=BC=8C=E9=99=8D=E4=BD=8E=E9=98=85=E8=AF=BB?= =?UTF-8?q?=E6=96=87=E7=AB=A0=E7=9A=84=E5=9B=B0=E9=9A=BE=E7=A8=8B=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- articles/20230728-sbi-firmware-analyze-1.md | 1099 +++---------------- 1 file changed, 155 insertions(+), 944 deletions(-) diff --git a/articles/20230728-sbi-firmware-analyze-1.md b/articles/20230728-sbi-firmware-analyze-1.md index 6420950..df17f84 100644 --- a/articles/20230728-sbi-firmware-analyze-1.md +++ b/articles/20230728-sbi-firmware-analyze-1.md @@ -1,9 +1,9 @@ -> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [spaces tables pangu]
+> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [refs pangu autocorrect epw]
> Author: groot
> Date: 2023/07/28
> Revisor: Falcon [falcon@tinylab.org](mailto:falcon@tinylab.org)
> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux)
-> Proposal: [RISC-V Linux 内核 SBI 调用技术分析](https://gitee.com/tinylab/riscv-linux/issues/I64YC4)
+> Proposal: [RISC-V Linux 内核 SBI 调用技术分析](https://gitee.com/tinylab/riscv-linux/issues/I64YC4)
> Sponsor: PLCT Lab, ISCAS # OpenSBI 固件代码分析(一) @@ -18,7 +18,7 @@ 该内容链接到 [《QEMU 启动方式分析(4): OpenSBI 固件分析与 SBI 规范的 HSM 扩展》][003] 的第四部分,这里不做过多的赘述。 -简而言之就是下面的内容: +如果作者对这方面并不感兴趣,这里给出相关内容的简略概述: * FW_PAYLOAD 类型固件打包了二进制文件和固件,适用于无法同时加载 OpenSBI 和 Runtime 的下一引导阶段。 * FW_JUMP 类型固件能够跳转到下一引导阶段的入口,需要在编译时指定下一引导阶段要加载的地址。 @@ -116,7 +116,7 @@ RISC-V 的汇编指示符和作用如下 首先给大家介绍文件中的一部分代码,将这些代码的作用从宏观上给大家介绍一下,然后再深入代码,将注释打在代码中,方便大家理解。 -### 代码意义 +### 代码分析 该段代码首先执行启动函数,在这个过程中通过之前规定的启动方式(fw_payload, fw_jump, fw_dynamic)找到一个启动核。如果规定了 `FW_PIC = y`,意味着将生成位置无关的固件映像,代码将进行一些重定位工作。这一部分读者暂且先不用关心。 @@ -146,951 +146,162 @@ RISC-V 的汇编指示符和作用如下 这段代码是一个用于 RISC-V 平台的引导程序,用于初始化处理器和运行时环境,并最终将控制权转移到操作系统或其他固件。它支持多核处理器,并确保每个核心都能正确初始化和运行。 -### 代码解析 - -```asm -/* - * SPDX-License-Identifier: BSD-2-Clause - * - * Copyright (c) 2019 Western Digital Corporation or its affiliates. - * - * Authors: - * Anup Patel - */ - -#include -#include -#include -#include -#include -#include - -#define BOOT_STATUS_RELOCATE_DONE 1 -#define BOOT_STATUS_BOOT_HART_DONE 2 - -.macro MOV_3R __d0, __s0, __d1, __s1, __d2, __s2 - add \__d0, \__s0, zero - add \__d1, \__s1, zero - add \__d2, \__s2, zero -.endm - -.macro MOV_5R __d0, __s0, __d1, __s1, __d2, __s2, __d3, __s3, __d4, __s4 - add \__d0, \__s0, zero - add \__d1, \__s1, zero - add \__d2, \__s2, zero - add \__d3, \__s3, zero - add \__d4, \__s4, zero -.endm - -/* - * If __start_reg <= __check_reg and __check_reg < __end_reg then - * jump to __pass - */ -.macro BRANGE __start_reg, __end_reg, __check_reg, __jump_lable - blt \__check_reg, \__start_reg, 999f - bge \__check_reg, \__end_reg, 999f - j \__jump_lable -999: -.endm - - .section .entry, "ax", %progbits - .align 3 - .globl _start - .globl _start_warm - -// 开始 -_start: - /* Find preferred boot HART id */ -# 伪代码,见第 26 行 - MOV_3R s0, a0, s1, a1, s2, a2 -# 调用 fw_boot_hart 函数 -# fw_payload 和 fw_jump 返回 -1 -# fw_danymic 返回指定数字,设置为指定核心启动 - call fw_boot_hart - add a6, a0, zero -# 伪代码,见第 20 行 - MOV_3R a0, s0, a1, s1, a2, s2 -# 下面的 66 - 70 行代码以多核的角度看 -# 每个核都要执行这些所有代码,没有符合条件的核心进入 _wait_relocate_copy_done 等待 -# 判断 a6 是否是 -1 -# 如果是 -1,调用 _try_lottery 函数随机产生一个启动核心 - li a7, -1 - beq a6, a7, _try_lottery - /* Jump to relocation wait loop if we are not boot hart */ -# 如果不是 -1 意味着指定了启动核心, - bne a0, a6, _wait_relocate_copy_done - -# ,前面的 _try_lottery 只能有一个核心获得 lottery,其他没有获取的 -_try_lottery: - /* Jump to relocation wait loop if we don't get relocation lottery */ - lla a6, _relocate_lottery - li a7, 1 -# 原子操作 -# a6 指向的地址上的值(_relocate_lottery 的地址)做原子加 1,_relocate_lottery 的老值写入 a6。 - amoadd.w a6, a7, (a6) -# _relocate_lottery 不等于 0,就跳到 boot hart 做完重定位的地方。 -# 如果多个核一起启动执行,只有最先执行上面原子加指令的核的 a6(_relocate_lottery 初始值)是 0, -# 所以,后续执行到这里的核都是从核,直接跳到重定位完成的地址。 - bnez a6, _wait_relocate_copy_done - - /* Save load address */ - lla t0, _load_start - lla t1, _fw_start - REG_S t1, 0(t0) -# "FW_PIC=y" 生成位置无关的可执行固件映像。 -# OpenSBI 可以在任意地址上以适当的对齐方式运行。 -# 因此,原始的重定位机制("FW_PIC=n")将被跳过。 -# 换句话说,OpenSBI 将直接在加载地址上运行,而不需要进行任何代码移动。 -# 这个选项需要支持 PIE(位置无关执行)的工具链,并且默认情况下开启。 -# 下面的代码一直到 #endif 都不需要特别关注 -#ifdef FW_PIC - /* relocate the global table content */ - lla t0, _link_start - REG_L t0, 0(t0) - /* t1 shall has the address of _fw_start */ - sub t2, t1, t0 - lla t3, _runtime_offset - REG_S t2, (t3) - lla t0, __rel_dyn_start - lla t1, __rel_dyn_end - beq t0, t1, _relocate_done -2: - REG_L t5, REGBYTES(t0) /* t5 <-- relocation info:type */ - li t3, R_RISCV_RELATIVE /* reloc type R_RISCV_RELATIVE */ - bne t5, t3, 3f - REG_L t3, 0(t0) - REG_L t5, (REGBYTES * 2)(t0) /* t5 <-- addend */ - add t5, t5, t2 - add t3, t3, t2 - REG_S t5, 0(t3) /* store runtime address to the GOT entry */ - j 5f - -3: - lla t4, __dyn_sym_start - -4: - srli t6, t5, SYM_INDEX /* t6 <--- sym table index */ - andi t5, t5, 0xFF /* t5 <--- relocation type */ - li t3, RELOC_TYPE - bne t5, t3, 5f - - /* address R_RISCV_64 or R_RISCV_32 cases */ - REG_L t3, 0(t0) - li t5, SYM_SIZE - mul t6, t6, t5 - add s5, t4, t6 - REG_L t6, (REGBYTES * 2)(t0) /* t0 <-- addend */ - REG_L t5, REGBYTES(s5) - add t5, t5, t6 - add t5, t5, t2 /* t5 <-- location to fix up in RAM */ - add t3, t3, t2 /* t3 <-- location to fix up in RAM */ - REG_S t5, 0(t3) /* store runtime address to the variable */ - -5: - addi t0, t0, (REGBYTES * 3) - blt t0, t1, 2b - j _relocate_done -_wait_relocate_copy_done: - j _wait_for_boot_hart -#else - /* Relocate if load address != link address */ -_relocate: - lla t0, _link_start - REG_L t0, 0(t0) - lla t1, _link_end - REG_L t1, 0(t1) - lla t2, _load_start - REG_L t2, 0(t2) - beq t0, t2, _relocate_done - sub t3, t1, t0 - add t3, t3, t2 - lla t4, _relocate_done - sub t4, t4, t2 - add t4, t4, t0 - blt t2, t0, _relocate_copy_to_upper -_relocate_copy_to_lower: - ble t1, t2, _relocate_copy_to_lower_loop - lla t3, _relocate_lottery - BRANGE t2, t1, t3, _start_hang - lla t3, _boot_status - BRANGE t2, t1, t3, _start_hang - lla t3, _relocate - lla t5, _relocate_done - BRANGE t2, t1, t3, _start_hang - BRANGE t2, t1, t5, _start_hang - BRANGE t3, t5, t2, _start_hang -_relocate_copy_to_lower_loop: - REG_L t3, 0(t2) - REG_S t3, 0(t0) - add t0, t0, __SIZEOF_POINTER__ - add t2, t2, __SIZEOF_POINTER__ - blt t0, t1, _relocate_copy_to_lower_loop - jr t4 -_relocate_copy_to_upper: - ble t3, t0, _relocate_copy_to_upper_loop - lla t2, _relocate_lottery - BRANGE t0, t3, t2, _start_hang - lla t2, _boot_status - BRANGE t0, t3, t2, _start_hang - lla t2, _relocate - lla t5, _relocate_done - BRANGE t0, t3, t2, _start_hang - BRANGE t0, t3, t5, _start_hang - BRANGE t2, t5, t0, _start_hang -_relocate_copy_to_upper_loop: - add t3, t3, -__SIZEOF_POINTER__ - add t1, t1, -__SIZEOF_POINTER__ - REG_L t2, 0(t3) - REG_S t2, 0(t1) - blt t0, t1, _relocate_copy_to_upper_loop - jr t4 -_wait_relocate_copy_done: - lla t0, _fw_start - lla t1, _link_start - REG_L t1, 0(t1) - beq t0, t1, _wait_for_boot_hart - lla t2, _boot_status - lla t3, _wait_for_boot_hart - sub t3, t3, t0 - add t3, t3, t1 -1: - /* waitting for relocate copy done (_boot_status == 1) */ - li t4, BOOT_STATUS_RELOCATE_DONE - REG_L t5, 0(t2) - /* Reduce the bus traffic so that boot hart may proceed faster */ - nop - nop - nop - bgt t4, t5, 1b - jr t3 -#endif -# 重定位结束 -_relocate_done: - - /* - * Mark relocate copy done - * Use _boot_status copy relative to the load address - */ -# 加载 _boot_status 的地址到 t0 - lla t0, _boot_status -#ifndef FW_PIC - lla t1, _link_start - REG_L t1, 0(t1) - lla t2, _load_start - REG_L t2, 0(t2) - sub t0, t0, t1 - add t0, t0, t2 -#endif -# 改变 t0 为 BOOT_STATUS_RELOCATE_DONE,这个是个宏,被定义为 1 - li t1, BOOT_STATUS_RELOCATE_DONE - REG_S t1, 0(t0) -# 确保以上的访存操作已经做完 - fence rw, rw - - /* At this point we are running from link address */ - - /* Reset all registers for boot HART */ - li ra, 0 -# 将所有寄存器清零 - call _reset_regs - -# 将 _bss_start 和 _bss_end 分别加载到 s4 和 s5 - /* Zero-out BSS */ - lla s4, _bss_start - lla s5, _bss_end -# 循环将所有 bss 段内的内容清零 -_bss_zero: -# 向 s4 寄存器所指的内存中写 0,也就是清零 s4 寄存器所指内存的值 - REG_S zero, (s4) -# s4 所指的地址 +4,指向下一个地址 - add s4, s4, __SIZEOF_POINTER__ -# 如果 s4 的值小于 s5,也就是还没到 _bss_end ,跳至 _bss_zero - blt s4, s5, _bss_zero - -# 设置一些临时使用的中断 - /* Setup temporary trap handler */ - lla s4, _start_hang - csrw CSR_MTVEC, s4 -# 设置一些临时使用的中断栈 - /* Setup temporary stack */ - lla s4, _fw_end - li s5, (SBI_SCRATCH_SIZE * 2) - add sp, s4, s5 - -# 保存一些信息,不同的启动类型有不同的操作 - /* Allow main firmware to save info */ - MOV_5R s0, a0, s1, a1, s2, a2, s3, a3, s4, a4 - call fw_save_info - MOV_5R a0, s0, a1, s1, a2, s2, a3, s3, a4, s4 - -# 如果定义了设备树的地址,将它加载进来 -#ifdef FW_FDT_PATH - /* Override previous arg1 */ - lla a1, fw_fdt_bin -#endif - -# 针对特定平台,进行初始化 -# 如果没有在 fw_platform_init 函数中对 FDT 进行修改 -# 返回值不变 - /* - * Initialize platform - * Note: The a0 to a4 registers passed to the - * firmware are parameters to this function. - */ - MOV_5R s0, a0, s1, a1, s2, a2, s3, a3, s4, a4 - call fw_platform_init - add t0, a0, zero - MOV_5R a0, s0, a1, s1, a2, s2, a3, s3, a4, s4 - add a1, t0, zero - - /* Preload HART details - * s7 -> HART Count - * s8 -> HART Stack Size - * s9 -> Heap Size - * s10 -> Heap Offset - */ -# 将平台相关的数据结构地址加载进 a4 寄存器 - lla a4, platform -# 根据寄存器 a4 将平台相关的数据加载进来 -#if __riscv_xlen > 32 - lwu s7, SBI_PLATFORM_HART_COUNT_OFFSET(a4) - lwu s8, SBI_PLATFORM_HART_STACK_SIZE_OFFSET(a4) - lwu s9, SBI_PLATFORM_HEAP_SIZE_OFFSET(a4) -#else - lw s7, SBI_PLATFORM_HART_COUNT_OFFSET(a4) - lw s8, SBI_PLATFORM_HART_STACK_SIZE_OFFSET(a4) - lw s9, SBI_PLATFORM_HEAP_SIZE_OFFSET(a4) -#endif - -# tp 是 RISC-V 中的一个特殊寄存器,用于指向临时工作区域(scratch space)。 -# 将 _fw_end 地址加载进 tp, 在用 s7,s8 计算出 scratch space, 再加上 tp, 调整 scratch space 的位置 - /* Setup scratch space for all the HARTs */ - lla tp, _fw_end - mul a5, s7, s8 - add tp, tp, a5 -# 原理同上 -# 调整 heap 基址 - /* Setup heap base address */ - lla s10, _fw_start - sub s10, tp, s10 - add tp, tp, s9 - /* Keep a copy of tp */ - add t3, tp, zero - /* Counter */ - li t2, 1 - /* hartid 0 is mandated by ISA */ - li t1, 0 -_scratch_init: - /* - * The following registers hold values that are computed before - * entering this block, and should remain unchanged. - * - * t3 -> the firmware end address - * s7 -> HART count - * s8 -> HART stack size - * s9 -> Heap Size - * s10 -> Heap Offset - */ -# 找到 scratch space 的基址 - add tp, t3, zero - sub tp, tp, s9 -# t1 首次是 0,计算出来的 a5 也等于 0, -# 这个 t1 是 hart 的编号,s8 是每个核的栈大小 -# 所以,a5 是每个 hart 的栈的偏移。 - mul a5, s8, t1 - sub tp, tp, a5 - li a5, SBI_SCRATCH_SIZE - sub tp, tp, a5 - - /* Initialize scratch space */ - /* Store fw_start and fw_size in scratch space */ - lla a4, _fw_start - sub a5, t3, a4 - REG_S a4, SBI_SCRATCH_FW_START_OFFSET(tp) - REG_S a5, SBI_SCRATCH_FW_SIZE_OFFSET(tp) - - /* Store R/W section's offset in scratch space */ - lla a4, __fw_rw_offset - REG_L a5, 0(a4) - REG_S a5, SBI_SCRATCH_FW_RW_OFFSET(tp) - - /* Store fw_heap_offset and fw_heap_size in scratch space */ - REG_S s10, SBI_SCRATCH_FW_HEAP_OFFSET(tp) - REG_S s9, SBI_SCRATCH_FW_HEAP_SIZE_OFFSET(tp) - -# 设置函数:加载下一个阶段的参数 1 的地址 - /* Store next arg1 in scratch space */ - MOV_3R s0, a0, s1, a1, s2, a2 - call fw_next_arg1 - REG_S a0, SBI_SCRATCH_NEXT_ARG1_OFFSET(tp) - MOV_3R a0, s0, a1, s1, a2, s2 -# 设置函数:加载下一个阶段的可执行文件的地址 的地址 - /* Store next address in scratch space */ - MOV_3R s0, a0, s1, a1, s2, a2 - call fw_next_addr - REG_S a0, SBI_SCRATCH_NEXT_ADDR_OFFSET(tp) - MOV_3R a0, s0, a1, s1, a2, s2 -# 设置函数:设置下一个阶段的特权等级 的地址 - /* Store next mode in scratch space */ - MOV_3R s0, a0, s1, a1, s2, a2 - call fw_next_mode - REG_S a0, SBI_SCRATCH_NEXT_MODE_OFFSET(tp) - MOV_3R a0, s0, a1, s1, a2, s2 -# 设置启动函数地址 - /* Store warm_boot address in scratch space */ - lla a4, _start_warm - REG_S a4, SBI_SCRATCH_WARMBOOT_ADDR_OFFSET(tp) - -# 将特定平台的数据结构加载进来 - /* Store platform address in scratch space */ - lla a4, platform - REG_S a4, SBI_SCRATCH_PLATFORM_ADDR_OFFSET(tp) - -# 将 hartid-to-scratch 函数的地址存入 scratch space - /* Store hartid-to-scratch function address in scratch space */ - lla a4, _hartid_to_scratch - REG_S a4, SBI_SCRATCH_HARTID_TO_SCRATCH_OFFSET(tp) - -# 将 trap-exit 函数的地址存入 scratch space - /* Store trap-exit function address in scratch space */ - lla a4, _trap_exit - REG_S a4, SBI_SCRATCH_TRAP_EXIT_OFFSET(tp) - /* Clear tmp0 in scratch space */ - REG_S zero, SBI_SCRATCH_TMP0_OFFSET(tp) - /* Store firmware options in scratch space */ - MOV_3R s0, a0, s1, a1, s2, a2 - -# FW_OPTIONS 禁用 OpenSBI 启动时打印信息 -#ifdef FW_OPTIONS - li a0, FW_OPTIONS -#else - call fw_options -#endif - REG_S a0, SBI_SCRATCH_OPTIONS_OFFSET(tp) - MOV_3R a0, s0, a1, s1, a2, s2 - /* Move to next scratch space */ -# 再将 t1 + 1,检查 t1 是否小于 s7(HART_COUNT) -# 如果小于,说明还有其他核的 scratch_space 没有初始化完成 -# 继续进行其他核心的初始化工作 - add t1, t1, t2 - blt t1, s7, _scratch_init - - /* - * Relocate Flatened Device Tree (FDT) - * source FDT address = previous arg1 - * destination FDT address = next arg1 - * - * Note: We will preserve a0 and a1 passed by - * previous booting stage. - */ -# a1 = 0,意味着不需要进行 _fdt_reloc -# a1 的值见 -# 279: lla a1, fw_fdt_bin - beqz a1, _fdt_reloc_done - /* Mask values in a4 */ - li a4, 0xff - /* t1 = destination FDT start address */ - MOV_3R s0, a0, s1, a1, s2, a2 -# 加载下一个阶段的参数 1 - call fw_next_arg1 - add t1, a0, zero - MOV_3R a0, s0, a1, s1, a2, s2 - beqz t1, _fdt_reloc_done - beq t1, a1, _fdt_reloc_done - /* t0 = source FDT start address */ - add t0, a1, zero - /* t2 = source FDT size in big-endian */ -#if __riscv_xlen == 64 - lwu t2, 4(t0) -#else - lw t2, 4(t0) -#endif - /* t3 = bit[15:8] of FDT size */ - add t3, t2, zero - srli t3, t3, 16 - and t3, t3, a4 - slli t3, t3, 8 - /* t4 = bit[23:16] of FDT size */ - add t4, t2, zero - srli t4, t4, 8 - and t4, t4, a4 - slli t4, t4, 16 - /* t5 = bit[31:24] of FDT size */ - add t5, t2, zero - and t5, t5, a4 - slli t5, t5, 24 - /* t2 = bit[7:0] of FDT size */ - srli t2, t2, 24 - and t2, t2, a4 - /* t2 = FDT size in little-endian */ - or t2, t2, t3 - or t2, t2, t4 - or t2, t2, t5 - /* t2 = destination FDT end address */ - add t2, t1, t2 - /* FDT copy loop */ - ble t2, t1, _fdt_reloc_done -_fdt_reloc_again: - REG_L t3, 0(t0) - REG_S t3, 0(t1) - add t0, t0, __SIZEOF_POINTER__ - add t1, t1, __SIZEOF_POINTER__ - blt t1, t2, _fdt_reloc_again -_fdt_reloc_done: - -# 启动核表明自己启动完成 - /* mark boot hart done */ - li t0, BOOT_STATUS_BOOT_HART_DONE - lla t1, _boot_status - REG_S t0, 0(t1) - fence rw, rw - j _start_warm - -# 非启动核等待启动核热启动完成 - /* waiting for boot hart to be done (_boot_status == 2) */ -_wait_for_boot_hart: - li t0, BOOT_STATUS_BOOT_HART_DONE - lla t1, _boot_status - REG_L t1, 0(t1) - /* Reduce the bus traffic so that boot hart may proceed faster */ - nop - nop - nop - bne t0, t1, _wait_for_boot_hart - -# 热启动 -# 在这里进行非启动核心的初始化,等待启动核心完成初始化。非启动核心会等待启动核心在主要初始化工作完成后,再进行自己的初始化。 -_start_warm: - /* Reset all registers for non-boot HARTs */ - li ra, 0 - call _reset_regs - - /* Disable all interrupts */ - csrw CSR_MIE, zero - - /* Find HART count and HART stack size */ - lla a4, platform -#if __riscv_xlen == 64 - lwu s7, SBI_PLATFORM_HART_COUNT_OFFSET(a4) - lwu s8, SBI_PLATFORM_HART_STACK_SIZE_OFFSET(a4) -#else - lw s7, SBI_PLATFORM_HART_COUNT_OFFSET(a4) - lw s8, SBI_PLATFORM_HART_STACK_SIZE_OFFSET(a4) -#endif - REG_L s9, SBI_PLATFORM_HART_INDEX2ID_OFFSET(a4) - -# 使用 CSR(Control and Status Register)指令,将机器模式下的 HART ID(处理器 ID)读取到 s6 寄存器中。 -# CSR_MHARTID 是一个特定的寄存器控制编码,用于获取当前 HART 的 ID。 - /* Find HART id */ - csrr s6, CSR_MHARTID - - /* Find HART index */ - beqz s9, 3f - li a4, 0 -1: -#if __riscv_xlen == 64 - lwu a5, (s9) -#else - lw a5, (s9) -#endif - beq a5, s6, 2f - add s9, s9, 4 - add a4, a4, 1 - blt a4, s7, 1b - li a4, -1 -2: add s6, a4, zero -3: bge s6, s7, _start_hang - -# 经过下面的操作,可以找到符合上面条件的 HART 的 scratch space 的 end 位置 - /* Find the scratch space based on HART index */ - lla tp, _fw_end - mul a5, s7, s8 - add tp, tp, a5 - mul a5, s8, s6 - sub tp, tp, a5 - li a5, SBI_SCRATCH_SIZE - sub tp, tp, a5 - -# 将上面的值写入 CSR_MSCRATCH 寄存器 - /* update the mscratch */ - csrw CSR_MSCRATCH, tp - - /* Setup stack */ - add sp, tp, zero - - /* Setup trap handler */ - lla a4, _trap_handler -# 如果架构是 32 位的,做一些特殊操作 -#if __riscv_xlen == 32 - csrr a5, CSR_MISA - srli a5, a5, ('H' - 'A') - andi a5, a5, 0x1 - beq a5, zero, _skip_trap_handler_rv32_hyp - lla a4, _trap_handler_rv32_hyp -_skip_trap_handler_rv32_hyp: -#endif - csrw CSR_MTVEC, a4 - -#if __riscv_xlen == 32 - /* Override trap exit for H-extension */ - csrr a5, CSR_MISA - srli a5, a5, ('H' - 'A') - andi a5, a5, 0x1 - beq a5, zero, _skip_trap_exit_rv32_hyp - lla a4, _trap_exit_rv32_hyp - csrr a5, CSR_MSCRATCH - REG_S a4, SBI_SCRATCH_TRAP_EXIT_OFFSET(a5) -_skip_trap_exit_rv32_hyp: -#endif - -# 正式进入 SBI 运行时环境 - /* Initialize SBI runtime */ - csrr a0, CSR_MSCRATCH - call sbi_init - - /* We don't expect to reach here hence just hang */ - j _start_hang - - .data - .align 3 -#ifdef FW_PIC -_runtime_offset: - RISCV_PTR 0 -#endif -_relocate_lottery: - RISCV_PTR 0 -_boot_status: - RISCV_PTR 0 -_load_start: - RISCV_PTR _fw_start -_link_start: - RISCV_PTR FW_TEXT_START -_link_end: - RISCV_PTR _fw_reloc_end -__fw_rw_offset: - RISCV_PTR _fw_rw_start - _fw_start - - .section .entry, "ax", %progbits - .align 3 - .globl _hartid_to_scratch -_hartid_to_scratch: - /* - * a0 -> HART ID (passed by caller) - * a1 -> HART Index (passed by caller) - * t0 -> HART Stack Size - * t1 -> HART Stack End - * t2 -> Temporary - */ - lla t2, platform -#if __riscv_xlen == 64 - lwu t0, SBI_PLATFORM_HART_STACK_SIZE_OFFSET(t2) - lwu t2, SBI_PLATFORM_HART_COUNT_OFFSET(t2) -#else - lw t0, SBI_PLATFORM_HART_STACK_SIZE_OFFSET(t2) - lw t2, SBI_PLATFORM_HART_COUNT_OFFSET(t2) -#endif - sub t2, t2, a1 - mul t2, t2, t0 - lla t1, _fw_end - add t1, t1, t2 - li t2, SBI_SCRATCH_SIZE - sub a0, t1, t2 - ret - - .section .entry, "ax", %progbits - .align 3 - .globl _start_hang -_start_hang: - wfi - j _start_hang - - .section .entry, "ax", %progbits - .align 3 - .weak fw_platform_init -fw_platform_init: - add a0, a1, zero - ret - - /* Map implicit memcpy() added by compiler to sbi_memcpy() */ - .section .text - .align 3 - .globl memcpy - -# 下面许多都是类似 c 语言中的函数,这里不再赘述 -memcpy: - tail sbi_memcpy - - /* Map implicit memset() added by compiler to sbi_memset() */ - .section .text - .align 3 - .globl memset -memset: - tail sbi_memset - - /* Map implicit memmove() added by compiler to sbi_memmove() */ - .section .text - .align 3 - .globl memmove -memmove: - tail sbi_memmove - - /* Map implicit memcmp() added by compiler to sbi_memcmp() */ - .section .text - .align 3 - .globl memcmp -memcmp: - tail sbi_memcmp - -.macro TRAP_SAVE_AND_SETUP_SP_T0 - /* Swap TP and MSCRATCH */ - csrrw tp, CSR_MSCRATCH, tp - - /* Save T0 in scratch space */ - REG_S t0, SBI_SCRATCH_TMP0_OFFSET(tp) - - /* - * Set T0 to appropriate exception stack - * - * Came_From_M_Mode = ((MSTATUS.MPP < PRV_M) ? 1 : 0) - 1; - * Exception_Stack = TP ^ (Came_From_M_Mode & (SP ^ TP)) - * - * Came_From_M_Mode = 0 ==> Exception_Stack = TP - * Came_From_M_Mode = -1 ==> Exception_Stack = SP - */ - csrr t0, CSR_MSTATUS - srl t0, t0, MSTATUS_MPP_SHIFT - and t0, t0, PRV_M - slti t0, t0, PRV_M - add t0, t0, -1 - xor sp, sp, tp - and t0, t0, sp - xor sp, sp, tp - xor t0, tp, t0 - - /* Save original SP on exception stack */ - REG_S sp, (SBI_TRAP_REGS_OFFSET(sp) - SBI_TRAP_REGS_SIZE)(t0) - - /* Set SP to exception stack and make room for trap registers */ - add sp, t0, -(SBI_TRAP_REGS_SIZE) - - /* Restore T0 from scratch space */ - REG_L t0, SBI_SCRATCH_TMP0_OFFSET(tp) - - /* Save T0 on stack */ - REG_S t0, SBI_TRAP_REGS_OFFSET(t0)(sp) - - /* Swap TP and MSCRATCH */ - csrrw tp, CSR_MSCRATCH, tp -.endm - -.macro TRAP_SAVE_MEPC_MSTATUS have_mstatush - /* Save MEPC and MSTATUS CSRs */ - csrr t0, CSR_MEPC - REG_S t0, SBI_TRAP_REGS_OFFSET(mepc)(sp) - csrr t0, CSR_MSTATUS - REG_S t0, SBI_TRAP_REGS_OFFSET(mstatus)(sp) - .if \have_mstatush - csrr t0, CSR_MSTATUSH - REG_S t0, SBI_TRAP_REGS_OFFSET(mstatusH)(sp) - .else - REG_S zero, SBI_TRAP_REGS_OFFSET(mstatusH)(sp) - .endif -.endm - -.macro TRAP_SAVE_GENERAL_REGS_EXCEPT_SP_T0 - /* Save all general regisers except SP and T0 */ - REG_S zero, SBI_TRAP_REGS_OFFSET(zero)(sp) - REG_S ra, SBI_TRAP_REGS_OFFSET(ra)(sp) - REG_S gp, SBI_TRAP_REGS_OFFSET(gp)(sp) - REG_S tp, SBI_TRAP_REGS_OFFSET(tp)(sp) - REG_S t1, SBI_TRAP_REGS_OFFSET(t1)(sp) - REG_S t2, SBI_TRAP_REGS_OFFSET(t2)(sp) - REG_S s0, SBI_TRAP_REGS_OFFSET(s0)(sp) - REG_S s1, SBI_TRAP_REGS_OFFSET(s1)(sp) - REG_S a0, SBI_TRAP_REGS_OFFSET(a0)(sp) - REG_S a1, SBI_TRAP_REGS_OFFSET(a1)(sp) - REG_S a2, SBI_TRAP_REGS_OFFSET(a2)(sp) - REG_S a3, SBI_TRAP_REGS_OFFSET(a3)(sp) - REG_S a4, SBI_TRAP_REGS_OFFSET(a4)(sp) - REG_S a5, SBI_TRAP_REGS_OFFSET(a5)(sp) - REG_S a6, SBI_TRAP_REGS_OFFSET(a6)(sp) - REG_S a7, SBI_TRAP_REGS_OFFSET(a7)(sp) - REG_S s2, SBI_TRAP_REGS_OFFSET(s2)(sp) - REG_S s3, SBI_TRAP_REGS_OFFSET(s3)(sp) - REG_S s4, SBI_TRAP_REGS_OFFSET(s4)(sp) - REG_S s5, SBI_TRAP_REGS_OFFSET(s5)(sp) - REG_S s6, SBI_TRAP_REGS_OFFSET(s6)(sp) - REG_S s7, SBI_TRAP_REGS_OFFSET(s7)(sp) - REG_S s8, SBI_TRAP_REGS_OFFSET(s8)(sp) - REG_S s9, SBI_TRAP_REGS_OFFSET(s9)(sp) - REG_S s10, SBI_TRAP_REGS_OFFSET(s10)(sp) - REG_S s11, SBI_TRAP_REGS_OFFSET(s11)(sp) - REG_S t3, SBI_TRAP_REGS_OFFSET(t3)(sp) - REG_S t4, SBI_TRAP_REGS_OFFSET(t4)(sp) - REG_S t5, SBI_TRAP_REGS_OFFSET(t5)(sp) - REG_S t6, SBI_TRAP_REGS_OFFSET(t6)(sp) -.endm - -.macro TRAP_CALL_C_ROUTINE - /* Call C routine */ - add a0, sp, zero - call sbi_trap_handler -.endm - -.macro TRAP_RESTORE_GENERAL_REGS_EXCEPT_A0_T0 - /* Restore all general regisers except A0 and T0 */ - REG_L ra, SBI_TRAP_REGS_OFFSET(ra)(a0) - REG_L sp, SBI_TRAP_REGS_OFFSET(sp)(a0) - REG_L gp, SBI_TRAP_REGS_OFFSET(gp)(a0) - REG_L tp, SBI_TRAP_REGS_OFFSET(tp)(a0) - REG_L t1, SBI_TRAP_REGS_OFFSET(t1)(a0) - REG_L t2, SBI_TRAP_REGS_OFFSET(t2)(a0) - REG_L s0, SBI_TRAP_REGS_OFFSET(s0)(a0) - REG_L s1, SBI_TRAP_REGS_OFFSET(s1)(a0) - REG_L a1, SBI_TRAP_REGS_OFFSET(a1)(a0) - REG_L a2, SBI_TRAP_REGS_OFFSET(a2)(a0) - REG_L a3, SBI_TRAP_REGS_OFFSET(a3)(a0) - REG_L a4, SBI_TRAP_REGS_OFFSET(a4)(a0) - REG_L a5, SBI_TRAP_REGS_OFFSET(a5)(a0) - REG_L a6, SBI_TRAP_REGS_OFFSET(a6)(a0) - REG_L a7, SBI_TRAP_REGS_OFFSET(a7)(a0) - REG_L s2, SBI_TRAP_REGS_OFFSET(s2)(a0) - REG_L s3, SBI_TRAP_REGS_OFFSET(s3)(a0) - REG_L s4, SBI_TRAP_REGS_OFFSET(s4)(a0) - REG_L s5, SBI_TRAP_REGS_OFFSET(s5)(a0) - REG_L s6, SBI_TRAP_REGS_OFFSET(s6)(a0) - REG_L s7, SBI_TRAP_REGS_OFFSET(s7)(a0) - REG_L s8, SBI_TRAP_REGS_OFFSET(s8)(a0) - REG_L s9, SBI_TRAP_REGS_OFFSET(s9)(a0) - REG_L s10, SBI_TRAP_REGS_OFFSET(s10)(a0) - REG_L s11, SBI_TRAP_REGS_OFFSET(s11)(a0) - REG_L t3, SBI_TRAP_REGS_OFFSET(t3)(a0) - REG_L t4, SBI_TRAP_REGS_OFFSET(t4)(a0) - REG_L t5, SBI_TRAP_REGS_OFFSET(t5)(a0) - REG_L t6, SBI_TRAP_REGS_OFFSET(t6)(a0) -.endm - -.macro TRAP_RESTORE_MEPC_MSTATUS have_mstatush - /* Restore MEPC and MSTATUS CSRs */ - REG_L t0, SBI_TRAP_REGS_OFFSET(mepc)(a0) - csrw CSR_MEPC, t0 - REG_L t0, SBI_TRAP_REGS_OFFSET(mstatus)(a0) - csrw CSR_MSTATUS, t0 - .if \have_mstatush - REG_L t0, SBI_TRAP_REGS_OFFSET(mstatusH)(a0) - csrw CSR_MSTATUSH, t0 - .endif -.endm - -.macro TRAP_RESTORE_A0_T0 - /* Restore T0 */ - REG_L t0, SBI_TRAP_REGS_OFFSET(t0)(a0) - - /* Restore A0 */ - REG_L a0, SBI_TRAP_REGS_OFFSET(a0)(a0) -.endm - - .section .entry, "ax", %progbits - .align 3 - .globl _trap_handler - .globl _trap_exit -_trap_handler: - TRAP_SAVE_AND_SETUP_SP_T0 - - TRAP_SAVE_MEPC_MSTATUS 0 - - TRAP_SAVE_GENERAL_REGS_EXCEPT_SP_T0 - - TRAP_CALL_C_ROUTINE - -_trap_exit: - TRAP_RESTORE_GENERAL_REGS_EXCEPT_A0_T0 - - TRAP_RESTORE_MEPC_MSTATUS 0 - - TRAP_RESTORE_A0_T0 - - mret - -#if __riscv_xlen == 32 - .section .entry, "ax", %progbits - .align 3 - .globl _trap_handler_rv32_hyp - .globl _trap_exit_rv32_hyp -_trap_handler_rv32_hyp: - TRAP_SAVE_AND_SETUP_SP_T0 - - TRAP_SAVE_MEPC_MSTATUS 1 - - TRAP_SAVE_GENERAL_REGS_EXCEPT_SP_T0 - - TRAP_CALL_C_ROUTINE - -_trap_exit_rv32_hyp: - TRAP_RESTORE_GENERAL_REGS_EXCEPT_A0_T0 - - TRAP_RESTORE_MEPC_MSTATUS 1 - - TRAP_RESTORE_A0_T0 - - mret -#endif - - .section .entry, "ax", %progbits - .align 3 - .globl _reset_regs -_reset_regs: - - /* flush the instruction cache */ - fence.i - /* Reset all registers except ra, a0, a1 and a2 */ - li sp, 0 - li gp, 0 - li tp, 0 - li t0, 0 - li t1, 0 - li t2, 0 - li s0, 0 - li s1, 0 - li a3, 0 - li a4, 0 - li a5, 0 - li a6, 0 - li a7, 0 - li s2, 0 - li s3, 0 - li s4, 0 - li s5, 0 - li s6, 0 - li s7, 0 - li s8, 0 - li s9, 0 - li s10, 0 - li s11, 0 - li t3, 0 - li t4, 0 - li t5, 0 - li t6, 0 - csrw CSR_MSCRATCH, 0 - - ret - -#ifdef FW_FDT_PATH - .section .rodata - .align 4 - .globl fw_fdt_bin -fw_fdt_bin: - .incbin FW_FDT_PATH -#ifdef FW_FDT_PADDING - .fill FW_FDT_PADDING, 1, 0 -#endif -#endif +### 代码流程分析 + +1. 初始化: + - 代码以宏和包含必要的头文件开始。 + - 定义了在不同寄存器之间移动寄存器值的宏。 + - 定义了基于范围进行条件分支的宏。 + +2. 开始和引导 HART 识别: + - 定义了_start 标签,引导过程从此开始。 + - 代码使用一个函数(fw_boot_hart)来确定首选引导 HART(硬件线程)ID。 + - 结果存储在寄存器 a6 中。 + - 如果结果为 -1,则表示使用_try_lottery 机制随机选择引导 HART。 + +3. 重定位和初始化: + - 如果定义了 `FW_PIC` 并且编译器启用了位置无关可执行文件(英文:position-independent executable,缩写为 PIE),代码执行重定位。 + - 设置了 BSS 段,初始化临时陷阱处理程序、堆栈和其他临时临时空间。 + - 为不同的 HART 初始化了各种临是空间的值。 + +4. 设备树重定位: + - 如果提供了设备树,代码将对其进行重定位。 + +5. 引导状态和等待: + - 标记引导 HART 为完成状态,并等待所有 HART 完成重定位和初始化。 + +6. 非引导 HART 的热启动: + - 非引导 HART 进行热启动初始化,包括设置它们的临时空间、堆栈、陷阱处理程序和其他运行时数据。 + +7. 进入 SBI 运行时: + - 非引导 HART 初始化 SBI(监管程序二进制接口)运行时环境。 + +8. 非引导 HART 的循环: + - 非引导 HART 进入循环,不断执行 wfi(等待中断)指令,基本上处于空闲状态。 + +另外,固件中还实现了一些基础的内存复制函数: +- 内存复制(memcpy)和内存置位(memset)函数。 + +需要注意的是,这里只涉及了启动代码的一部分,还有其他的功能和细节可能没有在这个代码片段中展示出来。 + +整个 OpenSBI 启动过程涵盖了从硬件复位到 SBI 初始化和传递控制权给操作系统的完整流程。 + +将流程整理一下,以字符画的形式绘制出来 OpenSBI 的启动流程大致分为以下几个步骤: + +``` + +-------------------+ + | | + | _start | + | | + +-------------------+ + | + v + +-------------------+ + | | + | Find preferred | + | boot HART | + | | + +-------------------+ + | + v + +-------------------+ + | | + | Call fw_boot_hart| + | | + +-------------------+ + | + v + +-------------------+ + | | + | Handle Boot HART| + | | + +-------------------+ + / | \ + v | v + +-----------------+ | +-----------------+ + | | | | | + | Handle lottery | | | Wait for Boot | + | and relocation | | | HART Done | + | | | | | + +-----------------+ | +-----------------+ + \ | / + v | v + +----------------------+----------------------+ + | | + | Boot HART Done | + | | + +----------------------+----------------------+ + | + v + +------------------------+ + | | + | Initialize | + | for Boot HART | + | | + +------------------------+ + | + v + +-------------------------------+ + | | + | Mark relocate copy done | + | | + +-------------------------------+ + | + v + +-------------------------------+ + | | + | Clear BSS, Set Stack, etc. | + | | + +-------------------------------+ + | + v + +---------------------------------+ + | | + | Store Information in Scratch | + | | + +---------------------------------+ + | + v + +--------------------------------+ + | | + | Relocate Flatened Device Tree | + | | + +--------------------------------+ + | + v + +-------------------------------+ + | | + | Mark boot hart done | + | | + +-------------------------------+ + | + v + +-------------------------------+ + | | + | Warm Boot Start | + | | + +-------------------------------+ + | + v + +----------------------------------+ + | | + | Initialize for Non-Boot | + | HART | + | | + +----------------------------------+ + | + v + +-------------------------------+ + | | + | sbi_init | + | | + +-------------------------------+ ``` ## 小结 -通过分析 `fw_base.S` 中的源码,我们已经知道了 OpenSBI 的启动流程的最前面一段是如何完成的。之后我们会对 `fw_payload.S`、`fw_jump.S` 和 `fw_dynamic.S` 这三个文件进行解析,带领读者体会其中的差异。 +通过分析 `fw_base.S` 中的源码,我们已经知道了 OpenSBI 的启动流程的最前面一段的逻辑内容。之后我们会对其中的代码进行具体的解析,将上面的内容与具体的代码对应起来,加深读者对 OpenSBI 固件代码的理解。 ## 参考资料 -- Gitee From b494801e8b2f774076e0a2d768f3828a61ee2a3e Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Mon, 14 Aug 2023 03:02:35 +0000 Subject: [PATCH 34/68] update articles/20230728-sbi-firmware-analyze-1.md. Signed-off-by: Groot <1219671600@qq.com> --- articles/20230728-sbi-firmware-analyze-1.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/articles/20230728-sbi-firmware-analyze-1.md b/articles/20230728-sbi-firmware-analyze-1.md index df17f84..1af5d3c 100644 --- a/articles/20230728-sbi-firmware-analyze-1.md +++ b/articles/20230728-sbi-firmware-analyze-1.md @@ -114,8 +114,6 @@ RISC-V 的汇编指示符和作用如下 该文件中的代码是整个 OpenSBI 的起点,我们理解 OpenSBI 的代码就从这里开始吧! -首先给大家介绍文件中的一部分代码,将这些代码的作用从宏观上给大家介绍一下,然后再深入代码,将注释打在代码中,方便大家理解。 - ### 代码分析 该段代码首先执行启动函数,在这个过程中通过之前规定的启动方式(fw_payload, fw_jump, fw_dynamic)找到一个启动核。如果规定了 `FW_PIC = y`,意味着将生成位置无关的固件映像,代码将进行一些重定位工作。这一部分读者暂且先不用关心。 -- Gitee From 5bc34d797f0464ef3632b277826b53d332b8ea3a Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Mon, 14 Aug 2023 03:03:47 +0000 Subject: [PATCH 35/68] update articles/20230728-sbi-firmware-analyze-1.md. Signed-off-by: Groot <1219671600@qq.com> --- articles/20230728-sbi-firmware-analyze-1.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/articles/20230728-sbi-firmware-analyze-1.md b/articles/20230728-sbi-firmware-analyze-1.md index 1af5d3c..e9aa834 100644 --- a/articles/20230728-sbi-firmware-analyze-1.md +++ b/articles/20230728-sbi-firmware-analyze-1.md @@ -219,14 +219,14 @@ RISC-V 的汇编指示符和作用如下 +-----------------+ | +-----------------+ | | | | | | Handle lottery | | | Wait for Boot | - | and relocation | | | HART Done | + | and relocation | | | HART Done | | | | | | +-----------------+ | +-----------------+ \ | / v | v +----------------------+----------------------+ | | - | Boot HART Done | + | Boot HART Done | | | +----------------------+----------------------+ | -- Gitee From 4a79cb16b3628f91f58ee5e92e9f6f4a443424d4 Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Mon, 14 Aug 2023 03:09:30 +0000 Subject: [PATCH 36/68] update articles/20230728-sbi-firmware-analyze-1.md. Signed-off-by: Groot <1219671600@qq.com> --- articles/20230728-sbi-firmware-analyze-1.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/articles/20230728-sbi-firmware-analyze-1.md b/articles/20230728-sbi-firmware-analyze-1.md index e9aa834..251c69c 100644 --- a/articles/20230728-sbi-firmware-analyze-1.md +++ b/articles/20230728-sbi-firmware-analyze-1.md @@ -87,9 +87,9 @@ RISC-V 的汇编指示符和作用如下 | 指示符 | 作用 | |:----------------|:----------------------------------------------------------------------------------| -| .text | 代码段,之后跟的符号都在.text 内 | -| .data | 数据段,之后跟的符号都在.data 内 | -| .bss | 未初始化数据段,之后跟的符号都在.bss 中 | +| .text | 代码段,之后跟的符号都在 .text 内 | +| .data | 数据段,之后跟的符号都在 .data 内 | +| .bss | 未初始化数据段,之后跟的符号都在 .bss 中 | | .section .foo | 自定义段,之后跟的符号都在.foo 段中,.foo 段名可以做修改 | | .align n | 按 2 的 n 次幂字节对齐 | | .balign n | 按 n 字节对齐 | @@ -107,8 +107,8 @@ RISC-V 的汇编指示符和作用如下 | .option norelax | 不允许链接松弛 | | .option pic | 与位置无关代码段 | | .option nopic | 与位置有关代码段 | -| .option push | 将所有.option 设置存入栈 | -| .option pop | 从栈中弹出上次存入的.option 设置 | +| .option push | 将所有 .option 设置存入栈 | +| .option pop | 从栈中弹出上次存入的 .option 设置 | ## fw_base @@ -128,7 +128,7 @@ RISC-V 的汇编指示符和作用如下 - _start:这是代码的主要入口点。它开始执行时会查找首选的启动核心(preferred boot HART id)。然后,它调用 fw_boot_hart 函数,该函数会尝试选择要启动的核心,如果未指定,它将选择一个核心来执行。启动核心将进行一系列初始化操作,然后根据指定的启动参数跳转到适当的代码段。 -- _try_lottery:这段代码在多核情况下使用。在多个核同时启动时,只有最先执行原子加指令的核心会获得"lottery",其他核心将等待启动核心完成初始化。 +- _try_lottery:这段代码在多核情况下使用。在多个核同时启动时,只有最先执行原子加指令的核心会获得 "lottery",其他核心将等待启动核心完成初始化。 - _relocate:如果启动核心的加载地址和链接地址不同,将进行重定位。它将把代码从加载地址复制到链接地址,以便在链接地址上运行。这部分代码是用于位置无关执行(FW_PIC=y)的情况,使 OpenSBI 能够在不同的地址上正确运行。 @@ -142,7 +142,7 @@ RISC-V 的汇编指示符和作用如下 - 其他一些功能:还包括处理中断、初始化 FDT(Flattened Device Tree)等功能。 -这段代码是一个用于 RISC-V 平台的引导程序,用于初始化处理器和运行时环境,并最终将控制权转移到操作系统或其他固件。它支持多核处理器,并确保每个核心都能正确初始化和运行。 +firmware 部分是一个用于 RISC-V 平台的引导程序,用于初始化处理器和运行时环境,并最终将控制权转移到操作系统或其他固件。它支持多核处理器,并确保每个核心都能正确初始化和运行。 ### 代码流程分析 @@ -152,10 +152,10 @@ RISC-V 的汇编指示符和作用如下 - 定义了基于范围进行条件分支的宏。 2. 开始和引导 HART 识别: - - 定义了_start 标签,引导过程从此开始。 + - 定义了 _start 标签,引导过程从此开始。 - 代码使用一个函数(fw_boot_hart)来确定首选引导 HART(硬件线程)ID。 - 结果存储在寄存器 a6 中。 - - 如果结果为 -1,则表示使用_try_lottery 机制随机选择引导 HART。 + - 如果结果为 -1,则表示使用 _try_lottery 机制随机选择引导 HART。 3. 重定位和初始化: - 如果定义了 `FW_PIC` 并且编译器启用了位置无关可执行文件(英文:position-independent executable,缩写为 PIE),代码执行重定位。 @@ -172,7 +172,7 @@ RISC-V 的汇编指示符和作用如下 - 非引导 HART 进行热启动初始化,包括设置它们的临时空间、堆栈、陷阱处理程序和其他运行时数据。 7. 进入 SBI 运行时: - - 非引导 HART 初始化 SBI(监管程序二进制接口)运行时环境。 + - 非引导 HART 初始化 SBI 运行时环境。 8. 非引导 HART 的循环: - 非引导 HART 进入循环,不断执行 wfi(等待中断)指令,基本上处于空闲状态。 -- Gitee From 5310fe2c09a837cd140b6e5c124426996d22e74a Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Mon, 14 Aug 2023 03:59:50 +0000 Subject: [PATCH 37/68] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20sbi-firemware-analyz?= =?UTF-8?q?e-2=20=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- articles/20230728-sbi-firmware-analyze-2.md | 951 ++++++++++++++++++++ 1 file changed, 951 insertions(+) create mode 100644 articles/20230728-sbi-firmware-analyze-2.md diff --git a/articles/20230728-sbi-firmware-analyze-2.md b/articles/20230728-sbi-firmware-analyze-2.md new file mode 100644 index 0000000..1aba4b2 --- /dev/null +++ b/articles/20230728-sbi-firmware-analyze-2.md @@ -0,0 +1,951 @@ +> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [spaces refs pangu]
+> Author: groot
+> Date: 2023/07/28
+> Revisor: Falcon [falcon@tinylab.org](mailto:falcon@tinylab.org)
+> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux)
+> Proposal: [RISC-V Linux 内核 SBI 调用技术分析](https://gitee.com/tinylab/riscv-linux/issues/I64YC4)
+> Sponsor: PLCT Lab, ISCAS + +# OpenSBI 固件代码分析(二) + +## 前言 + +在上一篇文章中,我们在逻辑层面讲解了 fw_base.S 这个文件,不过并没有对具体的代码进行分析。在这篇文章中,我们将代码与文章 [sbi-firemware-analyze-1](sbi-firemware-analyze-1.md) 结合起来,为读者进行更深层次的讲解,加深读者对 OpenSBI 固件代码的理解。 + +## 代码解析 + +将代码内容分割成小段进行解析,并在每段前用有序列表将其主要功能进行归纳。 + +### 初始化 + +1. 导入头文件: + * 通过 `#include` 指令导入了一系列的头文件,包括用于 RISC-V 汇编的宏定义和功能。 +2. 定义宏和常量: + * 定义了两个常量 `BOOT_STATUS_RELOCATE_DONE` 和 `BOOT_STATUS_BOOT_HART_DONE`,用于表示引导状态。 + * 定义了一系列宏,例如 `MOV_3R` 和 `MOV_5R`,用于将寄存器值复制到其他寄存器。 + * 定义了宏 `BRANGE`,用于根据一组寄存器值的范围条件进行条件跳转。 +3. 进入代码段: + * 使用 `.section` 指令将代码放置在 `.entry` 段中,指定代码段属性为 "ax"(可执行且分配内存)。 + * 使用 `.align` 指令将代码对齐到 2^3 = 8 字节的边界。 + * 使用 `.globl` 指令声明全局可见的 `_start` 和 `_start_warm` 函数。 + +这段代码主要是一些预处理和准备工作,定义了常量、宏以及入口函数,并为接下来的代码部分做了一些准备。通常,这样的初始化和准备阶段在程序中会出现在代码的开头。在此之后,该文件应该会继续定义和执行更多的代码来实现特定的功能。 + +```asm +// opensbi/firmware/fw_base.S: 1 + +/* + + * SPDX-License-Identifier: BSD-2-Clause + + * + + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + + * + + * Authors: + + * Anup Patel + + */ + +#include + +#include + +#include + +#include + +#include + +#include + +#define BOOT_STATUS_RELOCATE_DONE 1 + +#define BOOT_STATUS_BOOT_HART_DONE 2 + +.macro MOV_3R __d0, __s0, __d1, __s1, __d2, __s2 + + add \__d0, \__s0, zero + + add \__d1, \__s1, zero + + add \__d2, \__s2, zero + +.endm + +.macro MOV_5R __d0, __s0, __d1, __s1, __d2, __s2, __d3, __s3, __d4, __s4 + + add \__d0, \__s0, zero + + add \__d1, \__s1, zero + + add \__d2, \__s2, zero + + add \__d3, \__s3, zero + + add \__d4, \__s4, zero + +.endm + +/* + + * If __start_reg <= __check_reg and __check_reg < __end_reg then + + * jump to __pass + + */ + +.macro BRANGE __start_reg, __end_reg, __check_reg, __jump_lable + + blt \__check_reg, \__start_reg, 999f + + bge \__check_reg, \__end_reg, 999f + + j \__jump_lable + +999: + +.endm + + .section .entry, "ax", %progbits + + .align 3 + + .globl _start + + .globl _start_warm +``` + +### 开始和引导 HART 识别 + +1. `_start` 函数开始,表示系统启动的入口点。 +2. 调用 `fw_boot_hart` 函数,用于确定首选的启动核心(HART),函数的返回值存储在寄存器 `a0` 中。 +3. 将函数的返回值存储在寄存器 `a6` 中,这个值代表着首选的启动核心的编号。如果返回值是 `-1`,表示没有特定的首选核心。 + + - 判断 `a6` 的值是否等于 `-1`,如果等于 `-1`,表示没有指定特定的启动核心,需要进行随机选择。 + - 如果 `a6` 不等于 `-1`,说明已经指定了启动核心。通过比较 `a0` 和 `a6` 的值,如果不相等,表示当前核心不是指定的启动核心,应该跳转到 `_wait_relocate_copy_done` 函数处。 +4. 如果到达 `_try_lottery` 函数,表示需要进行抽签来选择启动核心。这里使用原子操作(`amoadd.w`)在地址 `_relocate_lottery` 所指向的位置执行原子加 1 操作,得到的结果存储在 `a6` 中。 +5. 根据 `_relocate_lottery` 的值,判断是否是第一个核心执行到抽签操作的,如果不是(`a6` 不等于 0),则直接跳转到 `_wait_relocate_copy_done`。 +6. 如果到达这里,说明是第一个核心执行抽签操作,即抽中了抽签,将会进入下一阶段的代码。 + +这段代码的主要目的是确定首选的启动核心,并在多核情况下确保只有一个核心执行到 `_try_lottery` 部分,并将启动核心的信息保存到特定地址。对于其他核心,如果不是启动核心,则会跳转到 `_wait_relocate_copy_done` 处等待。 + +``` +// opensbi/firmware/fw_base.S: 49 + +// 开始 +_start: + + /* Find preferred boot HART id */ + +# 伪代码,见第 26 行 + + MOV_3R s0, a0, s1, a1, s2, a2 + +# 调用 fw_boot_hart 函数 + +# fw_payload 和 fw_jump 返回 -1 + +# fw_danymic 返回指定数字,设置为指定核心启动 + + call fw_boot_hart + + add a6, a0, zero + +# 伪代码,见第 20 行 + + MOV_3R a0, s0, a1, s1, a2, s2 + +# 下面的 66 - 70 行代码以多核的角度看 + +# 每个核都要执行这些所有代码,没有符合条件的核心进入 _wait_relocate_copy_done 等待 + +# 判断 a6 是否是 -1 + +# 如果是 -1,调用 _try_lottery 函数随机产生一个启动核心 + + li a7, -1 + + beq a6, a7, _try_lottery + + /* Jump to relocation wait loop if we are not boot hart */ + +# 如果不是 -1 意味着指定了启动核心, + + bne a0, a6, _wait_relocate_copy_done + +# ,前面的 _try_lottery 只能有一个核心获得 lottery,其他没有获取的 + +_try_lottery: + + /* Jump to relocation wait loop if we don't get relocation lottery */ + + lla a6, _relocate_lottery + + li a7, 1 + +# 原子操作 + +# a6 指向的地址上的值(_relocate_lottery 的地址)做原子加 1,_relocate_lottery 的老值写入 a6。 + + amoadd.w a6, a7, (a6) + +# _relocate_lottery 不等于 0,就跳到 boot hart 做完重定位的地方。 + +# 如果多个核一起启动执行,只有最先执行上面原子加指令的核的 a6(_relocate_lottery 初始值)是 0, + +# 所以,后续执行到这里的核都是从核,直接跳到重定位完成的地址。 + + bnez a6, _wait_relocate_copy_done +``` + +### 重定位和初始化 + +重定位过程这里暂不展示,不妨碍读者理解代码逻辑,有兴趣可以自行阅读。 + +这里只展示 BSS 段的设置以及针对不同 HART 的初始化操作。 + +1. 如果没有启用位置无关代码,计算并更新 `t0` 为相对于加载地址的 `_boot_status` 的地址。 +2. 使用 `fence` 指令确保内存访问操作完成。 +3. 设置中断处理例程为 `_start_hang`。 +4. 设置临时中断栈的位置。 + +这段代码在完成重定位后进行了一系列初始化和设置操作,包括清零 BSS 段、设置中断处理例程和临时中断栈。这些操作为系统启动后的主固件提供了一个干净的环境和所需的信息。 + +``` +// opensbi/firmware/fw_base.S: 194 + /* + + * Mark relocate copy done + + * Use _boot_status copy relative to the load address + + */ + +# 加载 _boot_status 的地址到 t0 + + lla t0, _boot_status + +#ifndef FW_PIC + + lla t1, _link_start + + REG_L t1, 0(t1) + + lla t2, _load_start + + REG_L t2, 0(t2) + + sub t0, t0, t1 + + add t0, t0, t2 + +#endif + +# 改变 t0 为 BOOT_STATUS_RELOCATE_DONE,这个是个宏,被定义为 1 + + li t1, BOOT_STATUS_RELOCATE_DONE + + REG_S t1, 0(t0) + +# 确保以上的访存操作已经做完 + + fence rw, rw + + /* At this point we are running from link address */ + + /* Reset all registers for boot HART */ + + li ra, 0 + +# 将所有寄存器清零 + + call _reset_regs + +# 将 _bss_start 和 _bss_end 分别加载到 s4 和 s5 + + /* Zero-out BSS */ + + lla s4, _bss_start + + lla s5, _bss_end + +# 循环将所有 bss 段内的内容清零 + +_bss_zero: + +# 向 s4 寄存器所指的内存中写 0,也就是清零 s4 寄存器所指内存的值 + + REG_S zero, (s4) + +# s4 所指的地址 +4,指向下一个地址 + + add s4, s4, __SIZEOF_POINTER__ + +# 如果 s4 的值小于 s5,也就是还没到 _bss_end ,跳至 _bss_zero + + blt s4, s5, _bss_zero + +# 设置一些临时使用的中断 + + /* Setup temporary trap handler */ + + lla s4, _start_hang + + csrw CSR_MTVEC, s4 + +# 设置一些临时使用的中断栈 + + /* Setup temporary stack */ + + lla s4, _fw_end + + li s5, (SBI_SCRATCH_SIZE * 2) + + add sp, s4, s5 +``` + +### 设备树重定位 + +``` +// opensbi/firmware/fw_base.S: 239 + +# 如果定义了设备树的地址,将它加载进来 + +#ifdef FW_FDT_PATH + + /* Override previous arg1 */ + + lla a1, fw_fdt_bin + +#endif + +``` + +### 针对特定平台进行相关初始化 + +1. 使用 `MOV_5R` 宏将参数传递给 `fw_platform_init` 函数。 +2. 调用 `fw_platform_init` 函数来初始化平台。 + +``` +// opensbi/firmware/fw_base.S: 245 +/* + + * Initialize platform + + * Note: The a0 to a4 registers passed to the + + * firmware are parameters to this function. + + */ + + MOV_5R s0, a0, s1, a1, s2, a2, s3, a3, s4, a4 + + call fw_platform_init + + add t0, a0, zero + + MOV_5R a0, s0, a1, s1, a2, s2, a3, s3, a4, s4 + + add a1, t0, zero + + /* Preload HART details + + * s7 -> HART Count + + * s8 -> HART Stack Size + + * s9 -> Heap Size + + * s10 -> Heap Offset + + */ + +# 将平台相关的数据结构地址加载进 a4 寄存器 + + lla a4, platform + +# 根据寄存器 a4 将平台相关的数据加载进来 + +#if __riscv_xlen > 32 + + lwu s7, SBI_PLATFORM_HART_COUNT_OFFSET(a4) + + lwu s8, SBI_PLATFORM_HART_STACK_SIZE_OFFSET(a4) + + lwu s9, SBI_PLATFORM_HEAP_SIZE_OFFSET(a4) + +#else + + lw s7, SBI_PLATFORM_HART_COUNT_OFFSET(a4) + + lw s8, SBI_PLATFORM_HART_STACK_SIZE_OFFSET(a4) + + lw s9, SBI_PLATFORM_HEAP_SIZE_OFFSET(a4) + +#endif +``` + +### 启动核启动 + +1. 初始化 Scratch Space(临时工作区域): +2. 初始化 Scratch Space 内容: + * 初始化 scratch space,包括存储 fw_start、fw_size、R/W section 偏移、fw_heap_offset、fw_heap_size 等信息。 + * 加载下一个阶段的参数、可执行文件地址、特权等级、启动函数地址、平台数据结构地址、hartid-to-scratch 函数地址、trap-exit 函数地址等,并存储到 scratch space。 +3. 初始化所有核心的 Scratch Space: + * 通过循环初始化所有核心的 scratch space,直到所有核心的 scratch space 都被初始化。 +4. 重新定位 Flattened Device Tree (FDT): + * 如果存在需要重新定位的 FDT,则将先前和下一个阶段的参数传递给 `_fdt_reloc` 函数,将 FDT 从源地址复制到目标地址。 +5. 标记启动核心完成: + * 启动核心将状态标记为已完成,以通知其他核心。 +6. 非启动核心等待启动核心完成: + * 非启动核心等待启动核心标记为已完成,以确保启动核心完成初始化后才继续执行。 + +这段汇编代码执行了 RISC-V 系统的早期初始化和引导任务,包括初始化 scratch space、存储关键信息和参数、重新定位 FDT,以及核心间的同步。 + +``` +# tp 是 RISC-V 中的一个特殊寄存器,用于指向临时工作区域(scratch space)。 + +# 将 _fw_end 地址加载进 tp, 在用 s7,s8 计算出 scratch space, 再加上 tp, 调整 scratch space 的位置 + + /* Setup scratch space for all the HARTs */ + + lla tp, _fw_end + + mul a5, s7, s8 + + add tp, tp, a5 + +# 原理同上 + +# 调整 heap 基址 + + /* Setup heap base address */ + + lla s10, _fw_start + + sub s10, tp, s10 + + add tp, tp, s9 + + /* Keep a copy of tp */ + + add t3, tp, zero + + /* Counter */ + + li t2, 1 + + /* hartid 0 is mandated by ISA */ + + li t1, 0 + +_scratch_init: + + /* + + * The following registers hold values that are computed before + + * entering this block, and should remain unchanged. + + * + + * t3 -> the firmware end address + + * s7 -> HART count + + * s8 -> HART stack size + + * s9 -> Heap Size + + * s10 -> Heap Offset + + */ + +# 找到 scratch space 的基址 + + add tp, t3, zero + + sub tp, tp, s9 + +# t1 首次是 0,计算出来的 a5 也等于 0, + +# 这个 t1 是 hart 的编号,s8 是每个核的栈大小 + +# 所以,a5 是每个 hart 的栈的偏移。 + + mul a5, s8, t1 + + sub tp, tp, a5 + + li a5, SBI_SCRATCH_SIZE + + sub tp, tp, a5 + + /* Initialize scratch space */ + + /* Store fw_start and fw_size in scratch space */ + + lla a4, _fw_start + + sub a5, t3, a4 + + REG_S a4, SBI_SCRATCH_FW_START_OFFSET(tp) + + REG_S a5, SBI_SCRATCH_FW_SIZE_OFFSET(tp) + + /* Store R/W section's offset in scratch space */ + + lla a4, __fw_rw_offset + + REG_L a5, 0(a4) + + REG_S a5, SBI_SCRATCH_FW_RW_OFFSET(tp) + + /* Store fw_heap_offset and fw_heap_size in scratch space */ + + REG_S s10, SBI_SCRATCH_FW_HEAP_OFFSET(tp) + + REG_S s9, SBI_SCRATCH_FW_HEAP_SIZE_OFFSET(tp) + +# 设置函数:加载下一个阶段的参数 1 的地址 + + /* Store next arg1 in scratch space */ + + MOV_3R s0, a0, s1, a1, s2, a2 + + call fw_next_arg1 + + REG_S a0, SBI_SCRATCH_NEXT_ARG1_OFFSET(tp) + + MOV_3R a0, s0, a1, s1, a2, s2 + +# 设置函数:加载下一个阶段的可执行文件的地址 的地址 + + /* Store next address in scratch space */ + + MOV_3R s0, a0, s1, a1, s2, a2 + + call fw_next_addr + + REG_S a0, SBI_SCRATCH_NEXT_ADDR_OFFSET(tp) + + MOV_3R a0, s0, a1, s1, a2, s2 + +# 设置函数:设置下一个阶段的特权等级 的地址 + + /* Store next mode in scratch space */ + + MOV_3R s0, a0, s1, a1, s2, a2 + + call fw_next_mode + + REG_S a0, SBI_SCRATCH_NEXT_MODE_OFFSET(tp) + + MOV_3R a0, s0, a1, s1, a2, s2 + +# 设置启动函数地址 + + /* Store warm_boot address in scratch space */ + + lla a4, _start_warm + + REG_S a4, SBI_SCRATCH_WARMBOOT_ADDR_OFFSET(tp) + +# 将特定平台的数据结构加载进来 + + /* Store platform address in scratch space */ + + lla a4, platform + + REG_S a4, SBI_SCRATCH_PLATFORM_ADDR_OFFSET(tp) + +# 将 hartid-to-scratch 函数的地址存入 scratch space + + /* Store hartid-to-scratch function address in scratch space */ + + lla a4, _hartid_to_scratch + + REG_S a4, SBI_SCRATCH_HARTID_TO_SCRATCH_OFFSET(tp) + +# 将 trap-exit 函数的地址存入 scratch space + + /* Store trap-exit function address in scratch space */ + + lla a4, _trap_exit + + REG_S a4, SBI_SCRATCH_TRAP_EXIT_OFFSET(tp) + + /* Clear tmp0 in scratch space */ + + REG_S zero, SBI_SCRATCH_TMP0_OFFSET(tp) + + /* Store firmware options in scratch space */ + + MOV_3R s0, a0, s1, a1, s2, a2 + +# FW_OPTIONS 禁用 OpenSBI 启动时打印信息 + +#ifdef FW_OPTIONS + + li a0, FW_OPTIONS + +#else + + call fw_options + +#endif + + REG_S a0, SBI_SCRATCH_OPTIONS_OFFSET(tp) + + MOV_3R a0, s0, a1, s1, a2, s2 + + /* Move to next scratch space */ + +# 再将 t1 + 1,检查 t1 是否小于 s7(HART_COUNT) + +# 如果小于,说明还有其他核的 scratch_space 没有初始化完成 + +# 继续进行其他核心的初始化工作 + + add t1, t1, t2 + + blt t1, s7, _scratch_init + + /* + + * Relocate Flatened Device Tree (FDT) + + * source FDT address = previous arg1 + + * destination FDT address = next arg1 + + * + + * Note: We will preserve a0 and a1 passed by + + * previous booting stage. + + */ + +# a1 = 0,意味着不需要进行 _fdt_reloc + +# a1 的值见 + +# 279: lla a1, fw_fdt_bin + + beqz a1, _fdt_reloc_done + + /* Mask values in a4 */ + + li a4, 0xff + + /* t1 = destination FDT start address */ + + MOV_3R s0, a0, s1, a1, s2, a2 + +# 加载下一个阶段的参数 1 + + call fw_next_arg1 + + add t1, a0, zero + + MOV_3R a0, s0, a1, s1, a2, s2 + + beqz t1, _fdt_reloc_done + + beq t1, a1, _fdt_reloc_done + + /* t0 = source FDT start address */ + + add t0, a1, zero + + /* t2 = source FDT size in big-endian */ + +#if __riscv_xlen == 64 + + lwu t2, 4(t0) + +#else + + lw t2, 4(t0) + +#endif + + /* t3 = bit[15:8] of FDT size */ + + add t3, t2, zero + + srli t3, t3, 16 + + and t3, t3, a4 + + slli t3, t3, 8 + + /* t4 = bit[23:16] of FDT size */ + + add t4, t2, zero + + srli t4, t4, 8 + + and t4, t4, a4 + + slli t4, t4, 16 + + /* t5 = bit[31:24] of FDT size */ + + add t5, t2, zero + + and t5, t5, a4 + + slli t5, t5, 24 + + /* t2 = bit[7:0] of FDT size */ + + srli t2, t2, 24 + + and t2, t2, a4 + + /* t2 = FDT size in little-endian */ + + or t2, t2, t3 + + or t2, t2, t4 + + or t2, t2, t5 + + /* t2 = destination FDT end address */ + + add t2, t1, t2 + + /* FDT copy loop */ + + ble t2, t1, _fdt_reloc_done + +_fdt_reloc_again: + + REG_L t3, 0(t0) + + REG_S t3, 0(t1) + + add t0, t0, __SIZEOF_POINTER__ + + add t1, t1, __SIZEOF_POINTER__ + + blt t1, t2, _fdt_reloc_again + +_fdt_reloc_done: + +# 启动核表明自己启动完成 + + /* mark boot hart done */ + + li t0, BOOT_STATUS_BOOT_HART_DONE + + lla t1, _boot_status + + REG_S t0, 0(t1) + + fence rw, rw + + j _start_warm +``` + +### 非引导 HART 的热启动 + +1. 使用 `_reset_regs` 函数重置所有寄存器的值,为非启动核心做准备。 +2. 使用 `CSR_MIE` 控制寄存器,将机器中断使能位清零,禁用所有中断。 +3. 从平台数据结构中加载 HART 的数量和 HART 栈大小。 +4. 从机器模式下的 `CSR_MHARTID` 寄存器中读取当前非启动核心的 HART ID(处理器 ID)到寄存器 `s6`。 +5. 如果 HART index 在平台数据结构中可用,将 `s6` 设置为对应的 HART index。 +6. 计算非启动核心的 scratch space 的地址范围,并将其存储到 `CSR_MSCRATCH` 寄存器中。这个寄存器是每个 HART 独有的,用于存储核心的临时数据。 +7. 设置栈的位置为 scratch space 的地址,即为 `sp` 寄存器赋值。 +8. 配置陷阱(trap)处理程序,将 `_trap_handler` 函数的地址加载到 `CSR_MTVEC` 寄存器中,用于处理中断和异常。 + +这段代码负责为非启动核心进行初始化,设置其运行环境,栈,陷阱处理程序等,以及可能的特定于架构的设置。 + +``` +// opensbi/firmware/fw_base.S: 439 + +# 热启动 + +# 在这里进行非启动核心的初始化,等待启动核心完成初始化。非启动核心会等待启动核心在主要初始化工作完成后,再进行自己的初始化。 + +_start_warm: + + /* Reset all registers for non-boot HARTs */ + + li ra, 0 + + call _reset_regs + + /* Disable all interrupts */ + + csrw CSR_MIE, zero + + /* Find HART count and HART stack size */ + + lla a4, platform + +#if __riscv_xlen == 64 + + lwu s7, SBI_PLATFORM_HART_COUNT_OFFSET(a4) + + lwu s8, SBI_PLATFORM_HART_STACK_SIZE_OFFSET(a4) + +#else + + lw s7, SBI_PLATFORM_HART_COUNT_OFFSET(a4) + + lw s8, SBI_PLATFORM_HART_STACK_SIZE_OFFSET(a4) + +#endif + + REG_L s9, SBI_PLATFORM_HART_INDEX2ID_OFFSET(a4) + +# 使用 CSR(Control and Status Register)指令,将机器模式下的 HART ID(处理器 ID)读取到 s6 寄存器中。 + +# CSR_MHARTID 是一个特定的寄存器控制编码,用于获取当前 HART 的 ID。 + + /* Find HART id */ + + csrr s6, CSR_MHARTID + + /* Find HART index */ + + beqz s9, 3f + + li a4, 0 + +1: + +#if __riscv_xlen == 64 + + lwu a5, (s9) + +#else + + lw a5, (s9) + +#endif + + beq a5, s6, 2f + + add s9, s9, 4 + + add a4, a4, 1 + + blt a4, s7, 1b + + li a4, -1 + +2: add s6, a4, zero + +3: bge s6, s7, _start_hang + +# 经过下面的操作,可以找到符合上面条件的 HART 的 scratch space 的 end 位置 + + /* Find the scratch space based on HART index */ + + lla tp, _fw_end + + mul a5, s7, s8 + + add tp, tp, a5 + + mul a5, s8, s6 + + sub tp, tp, a5 + + li a5, SBI_SCRATCH_SIZE + + sub tp, tp, a5 + +# 将上面的值写入 CSR_MSCRATCH 寄存器 + + /* update the mscratch */ + + csrw CSR_MSCRATCH, tp + + /* Setup stack */ + + add sp, tp, zero + + /* Setup trap handler */ + + lla a4, _trap_handler + +# 如果架构是 32 位的,做一些特殊操作 + +#if __riscv_xlen == 32 + + csrr a5, CSR_MISA + + srli a5, a5, ('H' - 'A') + + andi a5, a5, 0x1 + + beq a5, zero, _skip_trap_handler_rv32_hyp + + lla a4, _trap_handler_rv32_hyp + +_skip_trap_handler_rv32_hyp: + +#endif + + csrw CSR_MTVEC, a4 + +#if __riscv_xlen == 32 + + /* Override trap exit for H-extension */ + + csrr a5, CSR_MISA + + srli a5, a5, ('H' - 'A') + + andi a5, a5, 0x1 + + beq a5, zero, _skip_trap_exit_rv32_hyp + + lla a4, _trap_exit_rv32_hyp + + csrr a5, CSR_MSCRATCH + + REG_S a4, SBI_SCRATCH_TRAP_EXIT_OFFSET(a5) + +_skip_trap_exit_rv32_hyp: + +#endif +``` + +### 进入 SBI 运行时 + +这段代码将非启动核心引导到 SBI 的运行时环境,并进入一个死循环,表示在这个点上程序不应该继续执行。 + +``` +// opensbi/firmware/fw_base.S: 516 + +# 正式进入 SBI 运行时环境 + + /* Initialize SBI runtime */ + + csrr a0, CSR_MSCRATCH + + call sbi_init + + /* We don't expect to reach here hence just hang */ + + j _start_hang +``` + +## 小结 + +本篇文章从上一篇文章的逻辑层面进入到实际的代码层面,为读者梳理好整个汇编文件的内容,并且分割出不同的小模块,将各个模块的主要作用整理成有序列表,并且在模块内部的代码块中尽力做到逐段注释,提高可阅读性。 + +## 参考文献 + +- OpenSBI 源代码 +- RISC-V 手册 -- Gitee From 70ad3bc87d260b55add2845c93cb44e8b2af19f5 Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Tue, 15 Aug 2023 01:47:44 +0000 Subject: [PATCH 38/68] update articles/20230728-sbi-firmware-analyze-1.md. Signed-off-by: Groot <1219671600@qq.com> --- articles/20230728-sbi-firmware-analyze-1.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/articles/20230728-sbi-firmware-analyze-1.md b/articles/20230728-sbi-firmware-analyze-1.md index 251c69c..493e6c8 100644 --- a/articles/20230728-sbi-firmware-analyze-1.md +++ b/articles/20230728-sbi-firmware-analyze-1.md @@ -18,7 +18,7 @@ 该内容链接到 [《QEMU 启动方式分析(4): OpenSBI 固件分析与 SBI 规范的 HSM 扩展》][003] 的第四部分,这里不做过多的赘述。 -如果作者对这方面并不感兴趣,这里给出相关内容的简略概述: +如果读者对这方面并不感兴趣,这里给出相关内容的简略概述: * FW_PAYLOAD 类型固件打包了二进制文件和固件,适用于无法同时加载 OpenSBI 和 Runtime 的下一引导阶段。 * FW_JUMP 类型固件能够跳转到下一引导阶段的入口,需要在编译时指定下一引导阶段要加载的地址。 @@ -64,8 +64,8 @@ risc-v 有 32 个通用寄存器(简写 reg),标号为 `x0` - `x31` | x28-31 | t3-6 | Temporary | 临时寄存器 | ### 指令格式 - -RISC-V 指令具有六种基本指令格式: + +RISC-V 基本整数指令集("I")有六种指令格式: * R 类型指令:用于寄存器 - 寄存器操作; * I 类型指令:用于短立即数和访存 load 操作; -- Gitee From 3d483f58a42df20d4c0f79d956435078dfd3e404 Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Tue, 15 Aug 2023 01:48:32 +0000 Subject: [PATCH 39/68] update articles/20230728-sbi-firmware-analyze-2.md. Signed-off-by: Groot <1219671600@qq.com> --- articles/20230728-sbi-firmware-analyze-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/articles/20230728-sbi-firmware-analyze-2.md b/articles/20230728-sbi-firmware-analyze-2.md index 1aba4b2..d119423 100644 --- a/articles/20230728-sbi-firmware-analyze-2.md +++ b/articles/20230728-sbi-firmware-analyze-2.md @@ -10,7 +10,7 @@ ## 前言 -在上一篇文章中,我们在逻辑层面讲解了 fw_base.S 这个文件,不过并没有对具体的代码进行分析。在这篇文章中,我们将代码与文章 [sbi-firemware-analyze-1](sbi-firemware-analyze-1.md) 结合起来,为读者进行更深层次的讲解,加深读者对 OpenSBI 固件代码的理解。 +在上一篇文章中,我们在逻辑层面讲解了 fw_base.S 这个文件,不过并没有对具体的代码进行分析。在这篇文章中,我们将代码与文章 [sbi-firemware-analyze-1](20230728-sbi-firemware-analyze-1.md) 结合起来,为读者进行更深层次的讲解,加深读者对 OpenSBI 固件代码的理解。 ## 代码解析 -- Gitee From 156437a01dc250ebfe5d6d1d4356ed53138d6251 Mon Sep 17 00:00:00 2001 From: falcon Date: Wed, 16 Aug 2023 01:29:46 +0000 Subject: [PATCH 40/68] Update articles/20230728-sbi-firmware-analyze-1.md --- articles/20230728-sbi-firmware-analyze-1.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/articles/20230728-sbi-firmware-analyze-1.md b/articles/20230728-sbi-firmware-analyze-1.md index 493e6c8..44e14a5 100644 --- a/articles/20230728-sbi-firmware-analyze-1.md +++ b/articles/20230728-sbi-firmware-analyze-1.md @@ -93,7 +93,7 @@ RISC-V 的汇编指示符和作用如下 | .section .foo | 自定义段,之后跟的符号都在.foo 段中,.foo 段名可以做修改 | | .align n | 按 2 的 n 次幂字节对齐 | | .balign n | 按 n 字节对齐 | -| .globl sym | 声明 sym 未全局符号,其它文件可以访问 | +| .globl sym | 声明 sym 为全局符号,其它文件可以访问 | | .string “str” | 将字符串 str 放入内存 | | .byte b1,…,bn | 在内存中连续存储 n 个单字节 | | .half w1,…,wn | 在内存中连续存储 n 个半字(2 字节) | -- Gitee From d3482efffe745bf6c8dc45e214086428135ca1e3 Mon Sep 17 00:00:00 2001 From: falcon Date: Wed, 16 Aug 2023 01:29:56 +0000 Subject: [PATCH 41/68] Update articles/20230728-sbi-firmware-analyze-1.md --- articles/20230728-sbi-firmware-analyze-1.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/articles/20230728-sbi-firmware-analyze-1.md b/articles/20230728-sbi-firmware-analyze-1.md index 44e14a5..ffc5fcc 100644 --- a/articles/20230728-sbi-firmware-analyze-1.md +++ b/articles/20230728-sbi-firmware-analyze-1.md @@ -10,7 +10,7 @@ ## 前言 -之前的文章中给大家介绍了一下 SBI 和 OpenSBI,不过并没有特别深入的分析 OpenSBI 的整个流程。接下来为大家,我将带领读者从源码开始剖析,分析 OpenSBI 的启动流程,逐渐深入的学习 OpenSBI。 +之前的文章给大家介绍了一下 SBI 和 OpenSBI,不过并没有特别深入地分析 OpenSBI 的整个流程。接下来我将带领读者从源码开始剖析,分析 OpenSBI 的启动流程,逐渐深入地学习 OpenSBI。 该篇文章首先带领读者阅读 OpenSBI 的固件源码部分,我们将从 `firmware/fw_base.S` 开始,探索 OpenSBI 的世界。 -- Gitee From 754dc7657220e09d3830c4a4a3e9a8a752656734 Mon Sep 17 00:00:00 2001 From: falcon Date: Wed, 16 Aug 2023 01:30:10 +0000 Subject: [PATCH 42/68] Update articles/20230728-sbi-firmware-analyze-1.md --- articles/20230728-sbi-firmware-analyze-1.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/articles/20230728-sbi-firmware-analyze-1.md b/articles/20230728-sbi-firmware-analyze-1.md index ffc5fcc..0ceee4b 100644 --- a/articles/20230728-sbi-firmware-analyze-1.md +++ b/articles/20230728-sbi-firmware-analyze-1.md @@ -118,7 +118,7 @@ RISC-V 的汇编指示符和作用如下 该段代码首先执行启动函数,在这个过程中通过之前规定的启动方式(fw_payload, fw_jump, fw_dynamic)找到一个启动核。如果规定了 `FW_PIC = y`,意味着将生成位置无关的固件映像,代码将进行一些重定位工作。这一部分读者暂且先不用关心。 -该文件 RISC-V 处理器的引导程序,用于启动操作系统或其他固件。 +该文件是 RISC-V 处理器的引导程序,用于启动操作系统或其他固件。 代码以汇编语言和宏定义为主,用于初始化处理器,设置运行时环境,并将控制权转移到操作系统或其他固件。下面将解释代码的主要部分: -- Gitee From 4a66f8f5d3e05c675c589cf330900db838bc6150 Mon Sep 17 00:00:00 2001 From: falcon Date: Wed, 16 Aug 2023 01:30:35 +0000 Subject: [PATCH 43/68] Update articles/20230728-sbi-firmware-analyze-1.md --- articles/20230728-sbi-firmware-analyze-1.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/articles/20230728-sbi-firmware-analyze-1.md b/articles/20230728-sbi-firmware-analyze-1.md index 0ceee4b..48f98ab 100644 --- a/articles/20230728-sbi-firmware-analyze-1.md +++ b/articles/20230728-sbi-firmware-analyze-1.md @@ -159,7 +159,7 @@ firmware 部分是一个用于 RISC-V 平台的引导程序,用于初始化处 3. 重定位和初始化: - 如果定义了 `FW_PIC` 并且编译器启用了位置无关可执行文件(英文:position-independent executable,缩写为 PIE),代码执行重定位。 - - 设置了 BSS 段,初始化临时陷阱处理程序、堆栈和其他临时临时空间。 + - 设置了 BSS 段,初始化临时陷阱处理程序、堆栈和其他临时空间。 - 为不同的 HART 初始化了各种临是空间的值。 4. 设备树重定位: -- Gitee From d957c1376ecd14ee9a82af671c42b957e2532201 Mon Sep 17 00:00:00 2001 From: falcon Date: Wed, 16 Aug 2023 01:31:02 +0000 Subject: [PATCH 44/68] Update articles/20230728-sbi-firmware-analyze-1.md --- articles/20230728-sbi-firmware-analyze-1.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/articles/20230728-sbi-firmware-analyze-1.md b/articles/20230728-sbi-firmware-analyze-1.md index 48f98ab..d57eedc 100644 --- a/articles/20230728-sbi-firmware-analyze-1.md +++ b/articles/20230728-sbi-firmware-analyze-1.md @@ -211,7 +211,7 @@ firmware 部分是一个用于 RISC-V 平台的引导程序,用于初始化处 v +-------------------+ | | - | Handle Boot HART| + | Handle Boot HART | | | +-------------------+ / | \ -- Gitee From 293c101979bdf1ba3b2f0ea26a043bb38529d076 Mon Sep 17 00:00:00 2001 From: falcon Date: Wed, 16 Aug 2023 01:31:15 +0000 Subject: [PATCH 45/68] Update articles/20230728-sbi-firmware-analyze-1.md --- articles/20230728-sbi-firmware-analyze-1.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/articles/20230728-sbi-firmware-analyze-1.md b/articles/20230728-sbi-firmware-analyze-1.md index d57eedc..6979abc 100644 --- a/articles/20230728-sbi-firmware-analyze-1.md +++ b/articles/20230728-sbi-firmware-analyze-1.md @@ -160,7 +160,7 @@ firmware 部分是一个用于 RISC-V 平台的引导程序,用于初始化处 3. 重定位和初始化: - 如果定义了 `FW_PIC` 并且编译器启用了位置无关可执行文件(英文:position-independent executable,缩写为 PIE),代码执行重定位。 - 设置了 BSS 段,初始化临时陷阱处理程序、堆栈和其他临时空间。 - - 为不同的 HART 初始化了各种临是空间的值。 + - 为不同的 HART 初始化了各种临时空间的值。 4. 设备树重定位: - 如果提供了设备树,代码将对其进行重定位。 -- Gitee From 07cfbbae00ebed3e74d5bb65db377aeb9ad8ee97 Mon Sep 17 00:00:00 2001 From: falcon Date: Wed, 16 Aug 2023 01:31:30 +0000 Subject: [PATCH 46/68] Update articles/20230728-sbi-firmware-analyze-1.md --- articles/20230728-sbi-firmware-analyze-1.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/articles/20230728-sbi-firmware-analyze-1.md b/articles/20230728-sbi-firmware-analyze-1.md index 6979abc..97796ea 100644 --- a/articles/20230728-sbi-firmware-analyze-1.md +++ b/articles/20230728-sbi-firmware-analyze-1.md @@ -204,7 +204,7 @@ firmware 部分是一个用于 RISC-V 平台的引导程序,用于初始化处 v +-------------------+ | | - | Call fw_boot_hart| + | Call fw_boot_hart | | | +-------------------+ | -- Gitee From e5cf467d00648c468f87c8f56c97e45c905b0941 Mon Sep 17 00:00:00 2001 From: falcon Date: Wed, 16 Aug 2023 01:31:45 +0000 Subject: [PATCH 47/68] Update articles/20230728-sbi-firmware-analyze-1.md --- articles/20230728-sbi-firmware-analyze-1.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/articles/20230728-sbi-firmware-analyze-1.md b/articles/20230728-sbi-firmware-analyze-1.md index 97796ea..442beab 100644 --- a/articles/20230728-sbi-firmware-analyze-1.md +++ b/articles/20230728-sbi-firmware-analyze-1.md @@ -289,7 +289,7 @@ firmware 部分是一个用于 RISC-V 平台的引导程序,用于初始化处 +----------------------------------+ | v - +-------------------------------+ + +-------------------------------+ | | | sbi_init | | | -- Gitee From ba6931e877864e82ca73fd63ffe88528ba0112ee Mon Sep 17 00:00:00 2001 From: falcon Date: Wed, 16 Aug 2023 01:31:58 +0000 Subject: [PATCH 48/68] Update articles/20230728-sbi-firmware-analyze-1.md --- articles/20230728-sbi-firmware-analyze-1.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/articles/20230728-sbi-firmware-analyze-1.md b/articles/20230728-sbi-firmware-analyze-1.md index 442beab..675ed70 100644 --- a/articles/20230728-sbi-firmware-analyze-1.md +++ b/articles/20230728-sbi-firmware-analyze-1.md @@ -291,7 +291,7 @@ firmware 部分是一个用于 RISC-V 平台的引导程序,用于初始化处 v +-------------------------------+ | | - | sbi_init | + | sbi_init | | | +-------------------------------+ -- Gitee From dd9ba50415e1de1c77dc42f2078eb615be1a66c4 Mon Sep 17 00:00:00 2001 From: falcon Date: Wed, 16 Aug 2023 01:33:09 +0000 Subject: [PATCH 49/68] Update articles/20230728-sbi-firmware-analyze-2.md --- articles/20230728-sbi-firmware-analyze-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/articles/20230728-sbi-firmware-analyze-2.md b/articles/20230728-sbi-firmware-analyze-2.md index d119423..1f53405 100644 --- a/articles/20230728-sbi-firmware-analyze-2.md +++ b/articles/20230728-sbi-firmware-analyze-2.md @@ -26,7 +26,7 @@ * 定义了宏 `BRANGE`,用于根据一组寄存器值的范围条件进行条件跳转。 3. 进入代码段: * 使用 `.section` 指令将代码放置在 `.entry` 段中,指定代码段属性为 "ax"(可执行且分配内存)。 - * 使用 `.align` 指令将代码对齐到 2^3 = 8 字节的边界。 + * 使用 `.align` 指令将代码对齐到 `2^3 = 8` 字节的边界。 * 使用 `.globl` 指令声明全局可见的 `_start` 和 `_start_warm` 函数。 这段代码主要是一些预处理和准备工作,定义了常量、宏以及入口函数,并为接下来的代码部分做了一些准备。通常,这样的初始化和准备阶段在程序中会出现在代码的开头。在此之后,该文件应该会继续定义和执行更多的代码来实现特定的功能。 -- Gitee From 3696bc7f43b72e50c17db42e9d4573fe41956f80 Mon Sep 17 00:00:00 2001 From: falcon Date: Wed, 16 Aug 2023 01:33:30 +0000 Subject: [PATCH 50/68] Update articles/20230728-sbi-firmware-analyze-2.md --- articles/20230728-sbi-firmware-analyze-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/articles/20230728-sbi-firmware-analyze-2.md b/articles/20230728-sbi-firmware-analyze-2.md index 1f53405..b3f9289 100644 --- a/articles/20230728-sbi-firmware-analyze-2.md +++ b/articles/20230728-sbi-firmware-analyze-2.md @@ -245,7 +245,7 @@ _try_lottery: #endif -# 改变 t0 为 BOOT_STATUS_RELOCATE_DONE,这个是个宏,被定义为 1 +# 改变 t0 为 BOOT_STATUS_RELOCATE_DONE,这是个宏,被定义为 1 li t1, BOOT_STATUS_RELOCATE_DONE -- Gitee From a568519801c1bd8e2f2de8bbed5778d9c1cb171f Mon Sep 17 00:00:00 2001 From: falcon Date: Wed, 16 Aug 2023 01:33:45 +0000 Subject: [PATCH 51/68] Update articles/20230728-sbi-firmware-analyze-2.md --- articles/20230728-sbi-firmware-analyze-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/articles/20230728-sbi-firmware-analyze-2.md b/articles/20230728-sbi-firmware-analyze-2.md index b3f9289..62066a3 100644 --- a/articles/20230728-sbi-firmware-analyze-2.md +++ b/articles/20230728-sbi-firmware-analyze-2.md @@ -535,7 +535,7 @@ _scratch_init: MOV_3R a0, s0, a1, s1, a2, s2 -# 设置函数:设置下一个阶段的特权等级 的地址 +# 设置函数:设置下一个阶段的特权等级的地址 /* Store next mode in scratch space */ -- Gitee From bfd50ef3672a9be0004a3cc01e0678fa9a9d1414 Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Wed, 16 Aug 2023 01:48:03 +0000 Subject: [PATCH 52/68] =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- articles/20230728-sbi-firmware-analyze-1.md | 121 ++++---- articles/20230728-sbi-firmware-analyze-2.md | 326 +------------------- 2 files changed, 58 insertions(+), 389 deletions(-) diff --git a/articles/20230728-sbi-firmware-analyze-1.md b/articles/20230728-sbi-firmware-analyze-1.md index 675ed70..5eb9a77 100644 --- a/articles/20230728-sbi-firmware-analyze-1.md +++ b/articles/20230728-sbi-firmware-analyze-1.md @@ -12,7 +12,7 @@ 之前的文章给大家介绍了一下 SBI 和 OpenSBI,不过并没有特别深入地分析 OpenSBI 的整个流程。接下来我将带领读者从源码开始剖析,分析 OpenSBI 的启动流程,逐渐深入地学习 OpenSBI。 -该篇文章首先带领读者阅读 OpenSBI 的固件源码部分,我们将从 `firmware/fw_base.S` 开始,探索 OpenSBI 的世界。 +因为 OpenSBI 的启动过程从 `firmware/fw_base.S` 这个文件开始,所以我将从这个文件开始带领读者阅读 OpenSBI 的固件源码部分,探索 OpenSBI 的世界。 ## 三种固件类型 @@ -48,23 +48,23 @@ make PLATFORM= FW_OPTIONS= risc-v 有 32 个通用寄存器(简写 reg),标号为 `x0` - `x31` -| 寄存器 | 编程接口名称(ABI) | 描述 | 使用 | -|--------|-------------------|---------------------------------|------------------------------------| -| x0 | zero | Hard-wired zero | 硬件零 | -| x1 | ra | Return address | 常用于保存(函数的)返回地址 | -| x2 | sp | Stack pointer | 栈顶指针 | -| x3 | gp | Global pointer | — | -| x4 | tp | Thread pointer | — | -| x5-7 | t0-2 | Temporary | 临时寄存器 | -| x8 | s0/fp | Saved Register/ Frame pointer | (函数调用时)保存的寄存器和栈顶指针 | -| x9 | s1 | Saved register | (函数调用时)保存的寄存器 | -| x10-11 | a0-1 | Function argument/ return value | (函数调用时)的参数/函数的返回值 | -| x12-17 | a2-7 | Function argument | (函数调用时)的参数 | -| x18-27 | s2-11 | Saved register | (函数调用时)保存的寄存器 | -| x28-31 | t3-6 | Temporary | 临时寄存器 | +| 寄存器 | 编程接口名称(ABI) | 描述 | 使用 | +| ------ | ------------------- | ------------------------------- | ------------------------------------ | +| x0 | zero | Hard-wired zero | 硬件零 | +| x1 | ra | Return address | 常用于保存(函数的)返回地址 | +| x2 | sp | Stack pointer | 栈顶指针 | +| x3 | gp | Global pointer | — | +| x4 | tp | Thread pointer | — | +| x5-7 | t0-2 | Temporary | 临时寄存器 | +| x8 | s0/fp | Saved Register/ Frame pointer | (函数调用时)保存的寄存器和栈顶指针 | +| x9 | s1 | Saved register | (函数调用时)保存的寄存器 | +| x10-11 | a0-1 | Function argument/ return value | (函数调用时)的参数/函数的返回值 | +| x12-17 | a2-7 | Function argument | (函数调用时)的参数 | +| x18-27 | s2-11 | Saved register | (函数调用时)保存的寄存器 | +| x28-31 | t3-6 | Temporary | 临时寄存器 | ### 指令格式 - + RISC-V 基本整数指令集("I")有六种指令格式: * R 类型指令:用于寄存器 - 寄存器操作; @@ -73,46 +73,46 @@ RISC-V 基本整数指令集("I")有六种指令格式: * B 类型指令:用于条件跳转操作; * U 类型指令:用于长立即数操作; * J 类型指令:用于无条件操作; -![RISC-V 指令格式](images/sbi-firmware/instruction-spec.png) + ![RISC-V 指令格式](images/sbi-firmware/instruction-spec.png) ### 常见指令 这里不再花费篇幅给大家介绍常见指令,大家可以通过互联网学习相关内容。 -对于初学者,我推荐可以先从 [《rvbook》][001] 开始学起。 +对于初学者,我推荐可以先从 [《rvbook》][001] 和 [《RISC-V Assembly Programmer's Manual》](https://github.com/riscv-non-isa/riscv-asm-manual/blob/master/riscv-asm.md)开始学起。 ### 汇编指示符 RISC-V 的汇编指示符和作用如下 -| 指示符 | 作用 | -|:----------------|:----------------------------------------------------------------------------------| -| .text | 代码段,之后跟的符号都在 .text 内 | -| .data | 数据段,之后跟的符号都在 .data 内 | -| .bss | 未初始化数据段,之后跟的符号都在 .bss 中 | -| .section .foo | 自定义段,之后跟的符号都在.foo 段中,.foo 段名可以做修改 | -| .align n | 按 2 的 n 次幂字节对齐 | -| .balign n | 按 n 字节对齐 | -| .globl sym | 声明 sym 为全局符号,其它文件可以访问 | -| .string “str” | 将字符串 str 放入内存 | -| .byte b1,…,bn | 在内存中连续存储 n 个单字节 | -| .half w1,…,wn | 在内存中连续存储 n 个半字(2 字节) | -| .word w1,…,wn | 在内存中连续存储 n 个字(4 字节) | -| .dword w1,…,wn | 在内存中连续存储 n 个双字(8 字节) | -| .float f1,…,fn | 在内存中连续存储 n 个单精度浮点数 | -| .double d1,…,dn | 在内存中连续存储 n 个双精度浮点数 | -| .option rvc | 使用压缩指令 (risc-v c) | -| .option norvc | 不压缩指令 | -| .option relax | 允许链接器松弛(linker relaxation,链接时多次扫描代码,尽可能将跳转两条指令替换为一条) | -| .option norelax | 不允许链接松弛 | -| .option pic | 与位置无关代码段 | -| .option nopic | 与位置有关代码段 | -| .option push | 将所有 .option 设置存入栈 | -| .option pop | 从栈中弹出上次存入的 .option 设置 | +| 指示符 | 作用 | +| :--------------- | :-------------------------------------------------------------------------------------- | +| .text | 代码段,之后跟的符号都在 .text 内 | +| .data | 数据段,之后跟的符号都在 .data 内 | +| .bss | 未初始化数据段,之后跟的符号都在 .bss 中 | +| .section .foo | 自定义段,之后跟的符号都在.foo 段中,.foo 段名可以做修改 | +| .align n | 按 2 的 n 次幂字节对齐 | +| .balign n | 按 n 字节对齐 | +| .globl sym | 声明 sym 为全局符号,其它文件可以访问 | +| .string “str” | 将字符串 str 放入内存 | +| .byte b1,…,bn | 在内存中连续存储 n 个单字节 | +| .half w1,…,wn | 在内存中连续存储 n 个半字(2 字节) | +| .word w1,…,wn | 在内存中连续存储 n 个字(4 字节) | +| .dword w1,…,wn | 在内存中连续存储 n 个双字(8 字节) | +| .float f1,…,fn | 在内存中连续存储 n 个单精度浮点数 | +| .double d1,…,dn | 在内存中连续存储 n 个双精度浮点数 | +| .option rvc | 使用压缩指令 (risc-v c) | +| .option norvc | 不压缩指令 | +| .option relax | 允许链接器松弛(linker relaxation,链接时多次扫描代码,尽可能将跳转两条指令替换为一条) | +| .option norelax | 不允许链接松弛 | +| .option pic | 与位置无关代码段 | +| .option nopic | 与位置有关代码段 | +| .option push | 将所有 .option 设置存入栈 | +| .option pop | 从栈中弹出上次存入的 .option 设置 | ## fw_base -该文件中的代码是整个 OpenSBI 的起点,我们理解 OpenSBI 的代码就从这里开始吧! +`firmware/fw_base.S` 文件中的代码是整个 OpenSBI 的起点,我们理解 OpenSBI 的代码就从这里开始吧! ### 代码分析 @@ -123,23 +123,14 @@ RISC-V 的汇编指示符和作用如下 代码以汇编语言和宏定义为主,用于初始化处理器,设置运行时环境,并将控制权转移到操作系统或其他固件。下面将解释代码的主要部分: - 起始部分:该部分定义了代码的许可证、版权信息和作者。 - - 宏定义:在这部分,定义了一些宏指令,用于简化代码中的重复操作,例如 MOV_3R 和 MOV_5R 宏用于将寄存器中的值复制到另一个寄存器。 - - _start:这是代码的主要入口点。它开始执行时会查找首选的启动核心(preferred boot HART id)。然后,它调用 fw_boot_hart 函数,该函数会尝试选择要启动的核心,如果未指定,它将选择一个核心来执行。启动核心将进行一系列初始化操作,然后根据指定的启动参数跳转到适当的代码段。 - - _try_lottery:这段代码在多核情况下使用。在多个核同时启动时,只有最先执行原子加指令的核心会获得 "lottery",其他核心将等待启动核心完成初始化。 - - _relocate:如果启动核心的加载地址和链接地址不同,将进行重定位。它将把代码从加载地址复制到链接地址,以便在链接地址上运行。这部分代码是用于位置无关执行(FW_PIC=y)的情况,使 OpenSBI 能够在不同的地址上正确运行。 - - _relocate_done:在重定位完成后,将在这里标记重定位完成。这对于其他核心等待重定位完成非常重要。 - - _scratch_init:在这里设置了每个 HART(核心)的 scratch 空间,该空间用于保存处理器的运行时信息。 - - _start_warm:在这里进行非启动核心的初始化,等待启动核心完成初始化。非启动核心会等待启动核心在主要初始化工作完成后,再进行自己的初始化。 - - _hartid_to_scratch:用于将 HART ID 映射到 scratch 空间的函数。 - - 其他一些功能:还包括处理中断、初始化 FDT(Flattened Device Tree)等功能。 firmware 部分是一个用于 RISC-V 平台的引导程序,用于初始化处理器和运行时环境,并最终将控制权转移到操作系统或其他固件。它支持多核处理器,并确保每个核心都能正确初始化和运行。 @@ -147,37 +138,39 @@ firmware 部分是一个用于 RISC-V 平台的引导程序,用于初始化处 ### 代码流程分析 1. 初始化: + - 代码以宏和包含必要的头文件开始。 - 定义了在不同寄存器之间移动寄存器值的宏。 - 定义了基于范围进行条件分支的宏。 - 2. 开始和引导 HART 识别: + - 定义了 _start 标签,引导过程从此开始。 - 代码使用一个函数(fw_boot_hart)来确定首选引导 HART(硬件线程)ID。 - 结果存储在寄存器 a6 中。 - 如果结果为 -1,则表示使用 _try_lottery 机制随机选择引导 HART。 - 3. 重定位和初始化: + - 如果定义了 `FW_PIC` 并且编译器启用了位置无关可执行文件(英文:position-independent executable,缩写为 PIE),代码执行重定位。 - 设置了 BSS 段,初始化临时陷阱处理程序、堆栈和其他临时空间。 - 为不同的 HART 初始化了各种临时空间的值。 - 4. 设备树重定位: - - 如果提供了设备树,代码将对其进行重定位。 + - 如果提供了设备树,代码将对其进行重定位。 5. 引导状态和等待: - - 标记引导 HART 为完成状态,并等待所有 HART 完成重定位和初始化。 + - 标记引导 HART 为完成状态,并等待所有 HART 完成重定位和初始化。 6. 非引导 HART 的热启动: - - 非引导 HART 进行热启动初始化,包括设置它们的临时空间、堆栈、陷阱处理程序和其他运行时数据。 + - 非引导 HART 进行热启动初始化,包括设置它们的临时空间、堆栈、陷阱处理程序和其他运行时数据。 7. 进入 SBI 运行时: - - 非引导 HART 初始化 SBI 运行时环境。 + - 非引导 HART 初始化 SBI 运行时环境。 8. 非引导 HART 的循环: + - 非引导 HART 进入循环,不断执行 wfi(等待中断)指令,基本上处于空闲状态。 另外,固件中还实现了一些基础的内存复制函数: + - 内存复制(memcpy)和内存置位(memset)函数。 需要注意的是,这里只涉及了启动代码的一部分,还有其他的功能和细节可能没有在这个代码片段中展示出来。 @@ -260,11 +253,11 @@ firmware 部分是一个用于 RISC-V 平台的引导程序,用于初始化处 +---------------------------------+ | v - +--------------------------------+ - | | - | Relocate Flatened Device Tree | - | | - +--------------------------------+ + +---------------------------------+ + | | + | Relocate Flattened Device Tree | + | | + +---------------------------------+ | v +-------------------------------+ diff --git a/articles/20230728-sbi-firmware-analyze-2.md b/articles/20230728-sbi-firmware-analyze-2.md index 62066a3..be5f211 100644 --- a/articles/20230728-sbi-firmware-analyze-2.md +++ b/articles/20230728-sbi-firmware-analyze-2.md @@ -35,19 +35,12 @@ // opensbi/firmware/fw_base.S: 1 /* - * SPDX-License-Identifier: BSD-2-Clause - * - * Copyright (c) 2019 Western Digital Corporation or its affiliates. - * - * Authors: - * Anup Patel - */ #include @@ -67,55 +60,36 @@ #define BOOT_STATUS_BOOT_HART_DONE 2 .macro MOV_3R __d0, __s0, __d1, __s1, __d2, __s2 - add \__d0, \__s0, zero - add \__d1, \__s1, zero - add \__d2, \__s2, zero - .endm .macro MOV_5R __d0, __s0, __d1, __s1, __d2, __s2, __d3, __s3, __d4, __s4 - add \__d0, \__s0, zero - add \__d1, \__s1, zero - add \__d2, \__s2, zero - add \__d3, \__s3, zero - add \__d4, \__s4, zero .endm /* - * If __start_reg <= __check_reg and __check_reg < __end_reg then - * jump to __pass - */ .macro BRANGE __start_reg, __end_reg, __check_reg, __jump_lable - blt \__check_reg, \__start_reg, 999f - bge \__check_reg, \__end_reg, 999f - j \__jump_lable 999: .endm - .section .entry, "ax", %progbits - .align 3 - .globl _start - .globl _start_warm ``` @@ -142,63 +116,41 @@ _start: /* Find preferred boot HART id */ # 伪代码,见第 26 行 - MOV_3R s0, a0, s1, a1, s2, a2 # 调用 fw_boot_hart 函数 - # fw_payload 和 fw_jump 返回 -1 - # fw_danymic 返回指定数字,设置为指定核心启动 - call fw_boot_hart - add a6, a0, zero # 伪代码,见第 20 行 - MOV_3R a0, s0, a1, s1, a2, s2 # 下面的 66 - 70 行代码以多核的角度看 - # 每个核都要执行这些所有代码,没有符合条件的核心进入 _wait_relocate_copy_done 等待 - # 判断 a6 是否是 -1 - # 如果是 -1,调用 _try_lottery 函数随机产生一个启动核心 - li a7, -1 - beq a6, a7, _try_lottery - /* Jump to relocation wait loop if we are not boot hart */ # 如果不是 -1 意味着指定了启动核心, - bne a0, a6, _wait_relocate_copy_done # ,前面的 _try_lottery 只能有一个核心获得 lottery,其他没有获取的 - _try_lottery: - /* Jump to relocation wait loop if we don't get relocation lottery */ - lla a6, _relocate_lottery - li a7, 1 # 原子操作 - # a6 指向的地址上的值(_relocate_lottery 的地址)做原子加 1,_relocate_lottery 的老值写入 a6。 - amoadd.w a6, a7, (a6) # _relocate_lottery 不等于 0,就跳到 boot hart 做完重定位的地方。 - # 如果多个核一起启动执行,只有最先执行上面原子加指令的核的 a6(_relocate_lottery 初始值)是 0, - # 所以,后续执行到这里的核都是从核,直接跳到重定位完成的地址。 - bnez a6, _wait_relocate_copy_done ``` @@ -218,93 +170,60 @@ _try_lottery: ``` // opensbi/firmware/fw_base.S: 194 /* - * Mark relocate copy done - * Use _boot_status copy relative to the load address - */ # 加载 _boot_status 的地址到 t0 - lla t0, _boot_status #ifndef FW_PIC - lla t1, _link_start - REG_L t1, 0(t1) - lla t2, _load_start - REG_L t2, 0(t2) - sub t0, t0, t1 - add t0, t0, t2 #endif # 改变 t0 为 BOOT_STATUS_RELOCATE_DONE,这是个宏,被定义为 1 - li t1, BOOT_STATUS_RELOCATE_DONE - REG_S t1, 0(t0) # 确保以上的访存操作已经做完 - fence rw, rw - /* At this point we are running from link address */ - /* Reset all registers for boot HART */ - li ra, 0 # 将所有寄存器清零 - call _reset_regs # 将 _bss_start 和 _bss_end 分别加载到 s4 和 s5 - /* Zero-out BSS */ - lla s4, _bss_start - lla s5, _bss_end # 循环将所有 bss 段内的内容清零 - _bss_zero: - # 向 s4 寄存器所指的内存中写 0,也就是清零 s4 寄存器所指内存的值 - REG_S zero, (s4) - # s4 所指的地址 +4,指向下一个地址 - add s4, s4, __SIZEOF_POINTER__ # 如果 s4 的值小于 s5,也就是还没到 _bss_end ,跳至 _bss_zero - blt s4, s5, _bss_zero # 设置一些临时使用的中断 - /* Setup temporary trap handler */ - lla s4, _start_hang - csrw CSR_MTVEC, s4 # 设置一些临时使用的中断栈 - /* Setup temporary stack */ - lla s4, _fw_end - li s5, (SBI_SCRATCH_SIZE * 2) - add sp, s4, s5 ``` @@ -314,11 +233,8 @@ _bss_zero: // opensbi/firmware/fw_base.S: 239 # 如果定义了设备树的地址,将它加载进来 - #ifdef FW_FDT_PATH - /* Override previous arg1 */ - lla a1, fw_fdt_bin #endif @@ -333,57 +249,35 @@ _bss_zero: ``` // opensbi/firmware/fw_base.S: 245 /* - * Initialize platform - * Note: The a0 to a4 registers passed to the - * firmware are parameters to this function. - */ MOV_5R s0, a0, s1, a1, s2, a2, s3, a3, s4, a4 - call fw_platform_init - add t0, a0, zero - MOV_5R a0, s0, a1, s1, a2, s2, a3, s3, a4, s4 - add a1, t0, zero - /* Preload HART details - * s7 -> HART Count - * s8 -> HART Stack Size - * s9 -> Heap Size - * s10 -> Heap Offset - */ # 将平台相关的数据结构地址加载进 a4 寄存器 - lla a4, platform # 根据寄存器 a4 将平台相关的数据加载进来 - #if __riscv_xlen > 32 - lwu s7, SBI_PLATFORM_HART_COUNT_OFFSET(a4) - lwu s8, SBI_PLATFORM_HART_STACK_SIZE_OFFSET(a4) - lwu s9, SBI_PLATFORM_HEAP_SIZE_OFFSET(a4) #else - lw s7, SBI_PLATFORM_HART_COUNT_OFFSET(a4) - lw s8, SBI_PLATFORM_HART_STACK_SIZE_OFFSET(a4) - lw s9, SBI_PLATFORM_HEAP_SIZE_OFFSET(a4) #endif @@ -410,25 +304,17 @@ _bss_zero: # tp 是 RISC-V 中的一个特殊寄存器,用于指向临时工作区域(scratch space)。 # 将 _fw_end 地址加载进 tp, 在用 s7,s8 计算出 scratch space, 再加上 tp, 调整 scratch space 的位置 - /* Setup scratch space for all the HARTs */ - lla tp, _fw_end - mul a5, s7, s8 - add tp, tp, a5 # 原理同上 # 调整 heap 基址 - /* Setup heap base address */ - lla s10, _fw_start - sub s10, tp, s10 - add tp, tp, s9 /* Keep a copy of tp */ @@ -436,7 +322,6 @@ _bss_zero: add t3, tp, zero /* Counter */ - li t2, 1 /* hartid 0 is mandated by ISA */ @@ -444,313 +329,174 @@ _bss_zero: li t1, 0 _scratch_init: - /* - * The following registers hold values that are computed before - * entering this block, and should remain unchanged. - * - * t3 -> the firmware end address - * s7 -> HART count - * s8 -> HART stack size - * s9 -> Heap Size - * s10 -> Heap Offset - */ # 找到 scratch space 的基址 - add tp, t3, zero - sub tp, tp, s9 # t1 首次是 0,计算出来的 a5 也等于 0, - # 这个 t1 是 hart 的编号,s8 是每个核的栈大小 - # 所以,a5 是每个 hart 的栈的偏移。 - mul a5, s8, t1 - sub tp, tp, a5 - li a5, SBI_SCRATCH_SIZE - sub tp, tp, a5 - /* Initialize scratch space */ - /* Store fw_start and fw_size in scratch space */ - lla a4, _fw_start - sub a5, t3, a4 - REG_S a4, SBI_SCRATCH_FW_START_OFFSET(tp) - REG_S a5, SBI_SCRATCH_FW_SIZE_OFFSET(tp) - /* Store R/W section's offset in scratch space */ - lla a4, __fw_rw_offset - REG_L a5, 0(a4) - REG_S a5, SBI_SCRATCH_FW_RW_OFFSET(tp) - /* Store fw_heap_offset and fw_heap_size in scratch space */ - REG_S s10, SBI_SCRATCH_FW_HEAP_OFFSET(tp) - REG_S s9, SBI_SCRATCH_FW_HEAP_SIZE_OFFSET(tp) # 设置函数:加载下一个阶段的参数 1 的地址 - /* Store next arg1 in scratch space */ - MOV_3R s0, a0, s1, a1, s2, a2 - call fw_next_arg1 - REG_S a0, SBI_SCRATCH_NEXT_ARG1_OFFSET(tp) - MOV_3R a0, s0, a1, s1, a2, s2 # 设置函数:加载下一个阶段的可执行文件的地址 的地址 - /* Store next address in scratch space */ - MOV_3R s0, a0, s1, a1, s2, a2 - call fw_next_addr - REG_S a0, SBI_SCRATCH_NEXT_ADDR_OFFSET(tp) - MOV_3R a0, s0, a1, s1, a2, s2 # 设置函数:设置下一个阶段的特权等级的地址 - /* Store next mode in scratch space */ - MOV_3R s0, a0, s1, a1, s2, a2 - call fw_next_mode - REG_S a0, SBI_SCRATCH_NEXT_MODE_OFFSET(tp) - MOV_3R a0, s0, a1, s1, a2, s2 # 设置启动函数地址 - /* Store warm_boot address in scratch space */ - lla a4, _start_warm - REG_S a4, SBI_SCRATCH_WARMBOOT_ADDR_OFFSET(tp) # 将特定平台的数据结构加载进来 - /* Store platform address in scratch space */ - lla a4, platform - REG_S a4, SBI_SCRATCH_PLATFORM_ADDR_OFFSET(tp) # 将 hartid-to-scratch 函数的地址存入 scratch space - /* Store hartid-to-scratch function address in scratch space */ - lla a4, _hartid_to_scratch - REG_S a4, SBI_SCRATCH_HARTID_TO_SCRATCH_OFFSET(tp) # 将 trap-exit 函数的地址存入 scratch space - /* Store trap-exit function address in scratch space */ - lla a4, _trap_exit - REG_S a4, SBI_SCRATCH_TRAP_EXIT_OFFSET(tp) - - /* Clear tmp0 in scratch space */ - + /* Clear tmp0 in scratch space * REG_S zero, SBI_SCRATCH_TMP0_OFFSET(tp) - /* Store firmware options in scratch space */ - MOV_3R s0, a0, s1, a1, s2, a2 # FW_OPTIONS 禁用 OpenSBI 启动时打印信息 - #ifdef FW_OPTIONS - li a0, FW_OPTIONS - #else - call fw_options - #endif - REG_S a0, SBI_SCRATCH_OPTIONS_OFFSET(tp) - MOV_3R a0, s0, a1, s1, a2, s2 - /* Move to next scratch space */ # 再将 t1 + 1,检查 t1 是否小于 s7(HART_COUNT) - # 如果小于,说明还有其他核的 scratch_space 没有初始化完成 - # 继续进行其他核心的初始化工作 - add t1, t1, t2 - blt t1, s7, _scratch_init - /* - * Relocate Flatened Device Tree (FDT) - * source FDT address = previous arg1 - * destination FDT address = next arg1 - * - * Note: We will preserve a0 and a1 passed by - * previous booting stage. - */ # a1 = 0,意味着不需要进行 _fdt_reloc - # a1 的值见 - # 279: lla a1, fw_fdt_bin - beqz a1, _fdt_reloc_done - /* Mask values in a4 */ - li a4, 0xff - /* t1 = destination FDT start address */ - MOV_3R s0, a0, s1, a1, s2, a2 # 加载下一个阶段的参数 1 - call fw_next_arg1 - add t1, a0, zero - MOV_3R a0, s0, a1, s1, a2, s2 - beqz t1, _fdt_reloc_done - beq t1, a1, _fdt_reloc_done - /* t0 = source FDT start address */ - add t0, a1, zero - /* t2 = source FDT size in big-endian */ - #if __riscv_xlen == 64 - lwu t2, 4(t0) - #else - lw t2, 4(t0) - #endif - /* t3 = bit[15:8] of FDT size */ - add t3, t2, zero - srli t3, t3, 16 - and t3, t3, a4 - slli t3, t3, 8 - /* t4 = bit[23:16] of FDT size */ - add t4, t2, zero - srli t4, t4, 8 - and t4, t4, a4 - slli t4, t4, 16 - /* t5 = bit[31:24] of FDT size */ - add t5, t2, zero - and t5, t5, a4 - slli t5, t5, 24 - /* t2 = bit[7:0] of FDT size */ - srli t2, t2, 24 - and t2, t2, a4 - /* t2 = FDT size in little-endian */ - or t2, t2, t3 - or t2, t2, t4 - or t2, t2, t5 - /* t2 = destination FDT end address */ - add t2, t1, t2 - /* FDT copy loop */ - ble t2, t1, _fdt_reloc_done _fdt_reloc_again: - REG_L t3, 0(t0) - REG_S t3, 0(t1) - add t0, t0, __SIZEOF_POINTER__ - add t1, t1, __SIZEOF_POINTER__ - blt t1, t2, _fdt_reloc_again _fdt_reloc_done: - # 启动核表明自己启动完成 - /* mark boot hart done */ - li t0, BOOT_STATUS_BOOT_HART_DONE - lla t1, _boot_status - REG_S t0, 0(t1) - fence rw, rw - j _start_warm ``` @@ -771,75 +517,41 @@ _fdt_reloc_done: // opensbi/firmware/fw_base.S: 439 # 热启动 - # 在这里进行非启动核心的初始化,等待启动核心完成初始化。非启动核心会等待启动核心在主要初始化工作完成后,再进行自己的初始化。 - _start_warm: - /* Reset all registers for non-boot HARTs */ - li ra, 0 - call _reset_regs - /* Disable all interrupts */ - csrw CSR_MIE, zero - /* Find HART count and HART stack size */ - lla a4, platform - #if __riscv_xlen == 64 - lwu s7, SBI_PLATFORM_HART_COUNT_OFFSET(a4) - lwu s8, SBI_PLATFORM_HART_STACK_SIZE_OFFSET(a4) - #else - lw s7, SBI_PLATFORM_HART_COUNT_OFFSET(a4) - lw s8, SBI_PLATFORM_HART_STACK_SIZE_OFFSET(a4) - #endif - REG_L s9, SBI_PLATFORM_HART_INDEX2ID_OFFSET(a4) # 使用 CSR(Control and Status Register)指令,将机器模式下的 HART ID(处理器 ID)读取到 s6 寄存器中。 - # CSR_MHARTID 是一个特定的寄存器控制编码,用于获取当前 HART 的 ID。 - /* Find HART id */ - csrr s6, CSR_MHARTID - /* Find HART index */ - beqz s9, 3f - li a4, 0 - 1: - #if __riscv_xlen == 64 - lwu a5, (s9) - #else - lw a5, (s9) - #endif - beq a5, s6, 2f - add s9, s9, 4 - add a4, a4, 1 - blt a4, s7, 1b - li a4, -1 2: add s6, a4, zero @@ -847,77 +559,46 @@ _start_warm: 3: bge s6, s7, _start_hang # 经过下面的操作,可以找到符合上面条件的 HART 的 scratch space 的 end 位置 - /* Find the scratch space based on HART index */ - lla tp, _fw_end - mul a5, s7, s8 - add tp, tp, a5 - mul a5, s8, s6 - sub tp, tp, a5 - li a5, SBI_SCRATCH_SIZE - sub tp, tp, a5 # 将上面的值写入 CSR_MSCRATCH 寄存器 - /* update the mscratch */ - csrw CSR_MSCRATCH, tp - /* Setup stack */ - add sp, tp, zero - /* Setup trap handler */ - lla a4, _trap_handler # 如果架构是 32 位的,做一些特殊操作 - #if __riscv_xlen == 32 - csrr a5, CSR_MISA - srli a5, a5, ('H' - 'A') - andi a5, a5, 0x1 - beq a5, zero, _skip_trap_handler_rv32_hyp - lla a4, _trap_handler_rv32_hyp _skip_trap_handler_rv32_hyp: - #endif - csrw CSR_MTVEC, a4 #if __riscv_xlen == 32 - /* Override trap exit for H-extension */ - csrr a5, CSR_MISA - srli a5, a5, ('H' - 'A') - andi a5, a5, 0x1 - beq a5, zero, _skip_trap_exit_rv32_hyp - lla a4, _trap_exit_rv32_hyp - csrr a5, CSR_MSCRATCH - REG_S a4, SBI_SCRATCH_TRAP_EXIT_OFFSET(a5) _skip_trap_exit_rv32_hyp: - #endif ``` @@ -929,15 +610,10 @@ _skip_trap_exit_rv32_hyp: // opensbi/firmware/fw_base.S: 516 # 正式进入 SBI 运行时环境 - /* Initialize SBI runtime */ - csrr a0, CSR_MSCRATCH - call sbi_init - /* We don't expect to reach here hence just hang */ - j _start_hang ``` -- Gitee From e5f63c125515f12aa95c06edc3ae86c76d75d206 Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Wed, 16 Aug 2023 01:49:05 +0000 Subject: [PATCH 53/68] sbi-firmware-analyze-1.md: commit correct result of tinycorrect-tables Signed-off-by: Groot <1219671600@qq.com> --- articles/20230728-sbi-firmware-analyze-1.md | 78 ++++++++++----------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/articles/20230728-sbi-firmware-analyze-1.md b/articles/20230728-sbi-firmware-analyze-1.md index 5eb9a77..ede7e4f 100644 --- a/articles/20230728-sbi-firmware-analyze-1.md +++ b/articles/20230728-sbi-firmware-analyze-1.md @@ -1,4 +1,4 @@ -> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [refs pangu autocorrect epw]
+> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [tables]
> Author: groot
> Date: 2023/07/28
> Revisor: Falcon [falcon@tinylab.org](mailto:falcon@tinylab.org)
@@ -48,20 +48,20 @@ make PLATFORM= FW_OPTIONS= risc-v 有 32 个通用寄存器(简写 reg),标号为 `x0` - `x31` -| 寄存器 | 编程接口名称(ABI) | 描述 | 使用 | -| ------ | ------------------- | ------------------------------- | ------------------------------------ | -| x0 | zero | Hard-wired zero | 硬件零 | -| x1 | ra | Return address | 常用于保存(函数的)返回地址 | -| x2 | sp | Stack pointer | 栈顶指针 | -| x3 | gp | Global pointer | — | -| x4 | tp | Thread pointer | — | -| x5-7 | t0-2 | Temporary | 临时寄存器 | -| x8 | s0/fp | Saved Register/ Frame pointer | (函数调用时)保存的寄存器和栈顶指针 | -| x9 | s1 | Saved register | (函数调用时)保存的寄存器 | -| x10-11 | a0-1 | Function argument/ return value | (函数调用时)的参数/函数的返回值 | -| x12-17 | a2-7 | Function argument | (函数调用时)的参数 | -| x18-27 | s2-11 | Saved register | (函数调用时)保存的寄存器 | -| x28-31 | t3-6 | Temporary | 临时寄存器 | +| 寄存器 | 编程接口名称(ABI) | 描述 | 使用 | +|--------|-------------------|---------------------------------|------------------------------------| +| x0 | zero | Hard-wired zero | 硬件零 | +| x1 | ra | Return address | 常用于保存(函数的)返回地址 | +| x2 | sp | Stack pointer | 栈顶指针 | +| x3 | gp | Global pointer | — | +| x4 | tp | Thread pointer | — | +| x5-7 | t0-2 | Temporary | 临时寄存器 | +| x8 | s0/fp | Saved Register/ Frame pointer | (函数调用时)保存的寄存器和栈顶指针 | +| x9 | s1 | Saved register | (函数调用时)保存的寄存器 | +| x10-11 | a0-1 | Function argument/ return value | (函数调用时)的参数/函数的返回值 | +| x12-17 | a2-7 | Function argument | (函数调用时)的参数 | +| x18-27 | s2-11 | Saved register | (函数调用时)保存的寄存器 | +| x28-31 | t3-6 | Temporary | 临时寄存器 | ### 指令格式 @@ -85,30 +85,30 @@ RISC-V 基本整数指令集("I")有六种指令格式: RISC-V 的汇编指示符和作用如下 -| 指示符 | 作用 | -| :--------------- | :-------------------------------------------------------------------------------------- | -| .text | 代码段,之后跟的符号都在 .text 内 | -| .data | 数据段,之后跟的符号都在 .data 内 | -| .bss | 未初始化数据段,之后跟的符号都在 .bss 中 | -| .section .foo | 自定义段,之后跟的符号都在.foo 段中,.foo 段名可以做修改 | -| .align n | 按 2 的 n 次幂字节对齐 | -| .balign n | 按 n 字节对齐 | -| .globl sym | 声明 sym 为全局符号,其它文件可以访问 | -| .string “str” | 将字符串 str 放入内存 | -| .byte b1,…,bn | 在内存中连续存储 n 个单字节 | -| .half w1,…,wn | 在内存中连续存储 n 个半字(2 字节) | -| .word w1,…,wn | 在内存中连续存储 n 个字(4 字节) | -| .dword w1,…,wn | 在内存中连续存储 n 个双字(8 字节) | -| .float f1,…,fn | 在内存中连续存储 n 个单精度浮点数 | -| .double d1,…,dn | 在内存中连续存储 n 个双精度浮点数 | -| .option rvc | 使用压缩指令 (risc-v c) | -| .option norvc | 不压缩指令 | -| .option relax | 允许链接器松弛(linker relaxation,链接时多次扫描代码,尽可能将跳转两条指令替换为一条) | -| .option norelax | 不允许链接松弛 | -| .option pic | 与位置无关代码段 | -| .option nopic | 与位置有关代码段 | -| .option push | 将所有 .option 设置存入栈 | -| .option pop | 从栈中弹出上次存入的 .option 设置 | +| 指示符 | 作用 | +|:----------------|:----------------------------------------------------------------------------------| +| .text | 代码段,之后跟的符号都在 .text 内 | +| .data | 数据段,之后跟的符号都在 .data 内 | +| .bss | 未初始化数据段,之后跟的符号都在 .bss 中 | +| .section .foo | 自定义段,之后跟的符号都在.foo 段中,.foo 段名可以做修改 | +| .align n | 按 2 的 n 次幂字节对齐 | +| .balign n | 按 n 字节对齐 | +| .globl sym | 声明 sym 为全局符号,其它文件可以访问 | +| .string “str” | 将字符串 str 放入内存 | +| .byte b1,…,bn | 在内存中连续存储 n 个单字节 | +| .half w1,…,wn | 在内存中连续存储 n 个半字(2 字节) | +| .word w1,…,wn | 在内存中连续存储 n 个字(4 字节) | +| .dword w1,…,wn | 在内存中连续存储 n 个双字(8 字节) | +| .float f1,…,fn | 在内存中连续存储 n 个单精度浮点数 | +| .double d1,…,dn | 在内存中连续存储 n 个双精度浮点数 | +| .option rvc | 使用压缩指令 (risc-v c) | +| .option norvc | 不压缩指令 | +| .option relax | 允许链接器松弛(linker relaxation,链接时多次扫描代码,尽可能将跳转两条指令替换为一条) | +| .option norelax | 不允许链接松弛 | +| .option pic | 与位置无关代码段 | +| .option nopic | 与位置有关代码段 | +| .option push | 将所有 .option 设置存入栈 | +| .option pop | 从栈中弹出上次存入的 .option 设置 | ## fw_base -- Gitee From fa85fe7d4c98d63c63dd4e89bd02618f76aa439a Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Wed, 16 Aug 2023 01:49:21 +0000 Subject: [PATCH 54/68] sbi-firmware-analyze-1.md: commit correct result of tinycorrect-urls Signed-off-by: Groot <1219671600@qq.com> --- articles/20230728-sbi-firmware-analyze-1.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/articles/20230728-sbi-firmware-analyze-1.md b/articles/20230728-sbi-firmware-analyze-1.md index ede7e4f..9c67ba9 100644 --- a/articles/20230728-sbi-firmware-analyze-1.md +++ b/articles/20230728-sbi-firmware-analyze-1.md @@ -1,4 +1,4 @@ -> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [tables]
+> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [tables urls]
> Author: groot
> Date: 2023/07/28
> Revisor: Falcon [falcon@tinylab.org](mailto:falcon@tinylab.org)
@@ -79,7 +79,7 @@ RISC-V 基本整数指令集("I")有六种指令格式: 这里不再花费篇幅给大家介绍常见指令,大家可以通过互联网学习相关内容。 -对于初学者,我推荐可以先从 [《rvbook》][001] 和 [《RISC-V Assembly Programmer's Manual》](https://github.com/riscv-non-isa/riscv-asm-manual/blob/master/riscv-asm.md)开始学起。 +对于初学者,我推荐可以先从 [《rvbook》][001] 和 [《RISC-V Assembly Programmer's Manual》][004]开始学起。 ### 汇编指示符 @@ -305,3 +305,4 @@ firmware 部分是一个用于 RISC-V 平台的引导程序,用于初始化处 [001]: https://github.com/Lingrui98/RISC-V-book/blob/master/rvbook.pdf [002]: https://github.com/riscv-software-src/opensbi/tree/master/docs/firmware [003]: https://tinylab.org/opensbi-firmware-and-sbi-hsm/ +[004]: https://github.com/riscv-non-isa/riscv-asm-manual/blob/master/riscv-asm.md -- Gitee From 1ddfe95bfa915adbd47e37c32dca12163a3b4146 Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Wed, 16 Aug 2023 01:49:23 +0000 Subject: [PATCH 55/68] sbi-firmware-analyze-1.md: commit correct result of tinycorrect-refs Signed-off-by: Groot <1219671600@qq.com> --- articles/20230728-sbi-firmware-analyze-1.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/articles/20230728-sbi-firmware-analyze-1.md b/articles/20230728-sbi-firmware-analyze-1.md index 9c67ba9..0a85e1d 100644 --- a/articles/20230728-sbi-firmware-analyze-1.md +++ b/articles/20230728-sbi-firmware-analyze-1.md @@ -1,4 +1,4 @@ -> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [tables urls]
+> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [tables urls refs]
> Author: groot
> Date: 2023/07/28
> Revisor: Falcon [falcon@tinylab.org](mailto:falcon@tinylab.org)
@@ -301,6 +301,7 @@ firmware 部分是一个用于 RISC-V 平台的引导程序,用于初始化处 - [Lingrui98/RISC-V-book/blob/master/rvbook.pdf][001] - [OpenSBI 官方仓库固件部分文档][002] - [opensbi-firmware-and-sbi-hsm][003] +- [riscv-non-isa/riscv-asm-manual/blob/master/riscv-asm.md][004] [001]: https://github.com/Lingrui98/RISC-V-book/blob/master/rvbook.pdf [002]: https://github.com/riscv-software-src/opensbi/tree/master/docs/firmware -- Gitee From a83410372b7f6476c7e40ac3292e71f97c2e133f Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Wed, 16 Aug 2023 01:49:37 +0000 Subject: [PATCH 56/68] sbi-firmware-analyze-1.md: commit correct result of tinycorrect-autocorrect Signed-off-by: Groot <1219671600@qq.com> --- articles/20230728-sbi-firmware-analyze-1.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/articles/20230728-sbi-firmware-analyze-1.md b/articles/20230728-sbi-firmware-analyze-1.md index 0a85e1d..fda8117 100644 --- a/articles/20230728-sbi-firmware-analyze-1.md +++ b/articles/20230728-sbi-firmware-analyze-1.md @@ -1,4 +1,4 @@ -> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [tables urls refs]
+> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [tables urls refs autocorrect]
> Author: groot
> Date: 2023/07/28
> Revisor: Falcon [falcon@tinylab.org](mailto:falcon@tinylab.org)
@@ -79,7 +79,7 @@ RISC-V 基本整数指令集("I")有六种指令格式: 这里不再花费篇幅给大家介绍常见指令,大家可以通过互联网学习相关内容。 -对于初学者,我推荐可以先从 [《rvbook》][001] 和 [《RISC-V Assembly Programmer's Manual》][004]开始学起。 +对于初学者,我推荐可以先从 [《rvbook》][001] 和 [《RISC-V Assembly Programmer's Manual》][004] 开始学起。 ### 汇编指示符 -- Gitee From 5465428e5fcd0cbf991eb30d90ba0729b17da6dc Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Wed, 16 Aug 2023 01:49:42 +0000 Subject: [PATCH 57/68] sbi-firmware-analyze-1.md: commit correct result of tinycorrect-epw Signed-off-by: Groot <1219671600@qq.com> --- articles/20230728-sbi-firmware-analyze-1.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/articles/20230728-sbi-firmware-analyze-1.md b/articles/20230728-sbi-firmware-analyze-1.md index fda8117..c45ece9 100644 --- a/articles/20230728-sbi-firmware-analyze-1.md +++ b/articles/20230728-sbi-firmware-analyze-1.md @@ -1,4 +1,4 @@ -> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [tables urls refs autocorrect]
+> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [tables urls refs autocorrect epw]
> Author: groot
> Date: 2023/07/28
> Revisor: Falcon [falcon@tinylab.org](mailto:falcon@tinylab.org)
@@ -152,7 +152,7 @@ firmware 部分是一个用于 RISC-V 平台的引导程序,用于初始化处 - 如果定义了 `FW_PIC` 并且编译器启用了位置无关可执行文件(英文:position-independent executable,缩写为 PIE),代码执行重定位。 - 设置了 BSS 段,初始化临时陷阱处理程序、堆栈和其他临时空间。 - - 为不同的 HART 初始化了各种临时空间的值。 + - 为不同的 HART 初始化了各种临是空间的值。 4. 设备树重定位: - 如果提供了设备树,代码将对其进行重定位。 -- Gitee From b1f0b3e3c8927b4317e017f5fa13fbd6e739069f Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Wed, 16 Aug 2023 01:54:58 +0000 Subject: [PATCH 58/68] =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- articles/20230728-sbi-firmware-analyze-1.md | 46 ++++++++++----------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/articles/20230728-sbi-firmware-analyze-1.md b/articles/20230728-sbi-firmware-analyze-1.md index c45ece9..6e504c4 100644 --- a/articles/20230728-sbi-firmware-analyze-1.md +++ b/articles/20230728-sbi-firmware-analyze-1.md @@ -260,33 +260,33 @@ firmware 部分是一个用于 RISC-V 平台的引导程序,用于初始化处 +---------------------------------+ | v - +-------------------------------+ - | | - | Mark boot hart done | - | | - +-------------------------------+ + +-------------------------------+ + | | + | Mark boot hart done | + | | + +-------------------------------+ | v - +-------------------------------+ - | | - | Warm Boot Start | - | | - +-------------------------------+ + +-------------------------------+ + | | + | Warm Boot Start | + | | + +-------------------------------+ | v - +----------------------------------+ - | | - | Initialize for Non-Boot | - | HART | - | | - +----------------------------------+ - | - v - +-------------------------------+ - | | - | sbi_init | - | | - +-------------------------------+ + +----------------------------------+ + | | + | Initialize for Non-Boot | + | HART | + | | + +----------------------------------+ + | + v + +-------------------------------+ + | | + | sbi_init | + | | + +-------------------------------+ ``` -- Gitee From c0f266ed064b479b95309a4c458c85b15beb1ea2 Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Wed, 16 Aug 2023 01:56:03 +0000 Subject: [PATCH 59/68] =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- articles/20230728-sbi-firmware-analyze-1.md | 50 ++++++++++----------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/articles/20230728-sbi-firmware-analyze-1.md b/articles/20230728-sbi-firmware-analyze-1.md index 6e504c4..5a81fb2 100644 --- a/articles/20230728-sbi-firmware-analyze-1.md +++ b/articles/20230728-sbi-firmware-analyze-1.md @@ -260,33 +260,33 @@ firmware 部分是一个用于 RISC-V 平台的引导程序,用于初始化处 +---------------------------------+ | v - +-------------------------------+ - | | - | Mark boot hart done | - | | - +-------------------------------+ - | - v - +-------------------------------+ - | | - | Warm Boot Start | - | | - +-------------------------------+ + +-------------------------------+ + | | + | Mark boot hart done | + | | + +-------------------------------+ + | + v + +-------------------------------+ + | | + | Warm Boot Start | + | | + +-------------------------------+ + | + v + +----------------------------------+ + | | + | Initialize for Non-Boot | + | HART | + | | + +----------------------------------+ | v - +----------------------------------+ - | | - | Initialize for Non-Boot | - | HART | - | | - +----------------------------------+ - | - v - +-------------------------------+ - | | - | sbi_init | - | | - +-------------------------------+ + +-------------------------------+ + | | + | sbi_init | + | | + +-------------------------------+ ``` -- Gitee From c33e9ac974c5c0982536fe8befa0dda0d0f7d331 Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Wed, 16 Aug 2023 01:58:34 +0000 Subject: [PATCH 60/68] =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- articles/20230728-sbi-firmware-analyze-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/articles/20230728-sbi-firmware-analyze-2.md b/articles/20230728-sbi-firmware-analyze-2.md index be5f211..0851ac6 100644 --- a/articles/20230728-sbi-firmware-analyze-2.md +++ b/articles/20230728-sbi-firmware-analyze-2.md @@ -621,7 +621,7 @@ _skip_trap_exit_rv32_hyp: 本篇文章从上一篇文章的逻辑层面进入到实际的代码层面,为读者梳理好整个汇编文件的内容,并且分割出不同的小模块,将各个模块的主要作用整理成有序列表,并且在模块内部的代码块中尽力做到逐段注释,提高可阅读性。 -## 参考文献 +## 参考资料 - OpenSBI 源代码 - RISC-V 手册 -- Gitee From c26624ab75bbc8b46e2c35886184357aee523a43 Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Wed, 16 Aug 2023 01:59:14 +0000 Subject: [PATCH 61/68] sbi-firmware-analyze-2.md: commit correct result of tinycorrect-urls Signed-off-by: Groot <1219671600@qq.com> --- articles/20230728-sbi-firmware-analyze-2.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/articles/20230728-sbi-firmware-analyze-2.md b/articles/20230728-sbi-firmware-analyze-2.md index 0851ac6..ba1f127 100644 --- a/articles/20230728-sbi-firmware-analyze-2.md +++ b/articles/20230728-sbi-firmware-analyze-2.md @@ -1,4 +1,4 @@ -> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [spaces refs pangu]
+> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [urls]
> Author: groot
> Date: 2023/07/28
> Revisor: Falcon [falcon@tinylab.org](mailto:falcon@tinylab.org)
@@ -10,7 +10,7 @@ ## 前言 -在上一篇文章中,我们在逻辑层面讲解了 fw_base.S 这个文件,不过并没有对具体的代码进行分析。在这篇文章中,我们将代码与文章 [sbi-firemware-analyze-1](20230728-sbi-firemware-analyze-1.md) 结合起来,为读者进行更深层次的讲解,加深读者对 OpenSBI 固件代码的理解。 +在上一篇文章中,我们在逻辑层面讲解了 fw_base.S 这个文件,不过并没有对具体的代码进行分析。在这篇文章中,我们将代码与文章 [sbi-firemware-analyze-1][001] 结合起来,为读者进行更深层次的讲解,加深读者对 OpenSBI 固件代码的理解。 ## 代码解析 @@ -625,3 +625,5 @@ _skip_trap_exit_rv32_hyp: - OpenSBI 源代码 - RISC-V 手册 + +[001]: 20230728-sbi-firemware-analyze-1.md -- Gitee From f834cd9d43881667d7877ce52e09fad73ad290b2 Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Wed, 16 Aug 2023 02:00:39 +0000 Subject: [PATCH 62/68] =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- articles/20230728-sbi-firmware-analyze-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/articles/20230728-sbi-firmware-analyze-2.md b/articles/20230728-sbi-firmware-analyze-2.md index ba1f127..37624e8 100644 --- a/articles/20230728-sbi-firmware-analyze-2.md +++ b/articles/20230728-sbi-firmware-analyze-2.md @@ -626,4 +626,4 @@ _skip_trap_exit_rv32_hyp: - OpenSBI 源代码 - RISC-V 手册 -[001]: 20230728-sbi-firemware-analyze-1.md +[001]: ./20230728-sbi-firmware-analyze-1.md -- Gitee From 2d87aa89f370423326bc94b0a99aeb0a1d0f547a Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Wed, 16 Aug 2023 02:01:00 +0000 Subject: [PATCH 63/68] sbi-firmware-analyze-2.md: commit correct result of tinycorrect-urls Signed-off-by: Groot <1219671600@qq.com> --- articles/20230728-sbi-firmware-analyze-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/articles/20230728-sbi-firmware-analyze-2.md b/articles/20230728-sbi-firmware-analyze-2.md index 37624e8..b73bb20 100644 --- a/articles/20230728-sbi-firmware-analyze-2.md +++ b/articles/20230728-sbi-firmware-analyze-2.md @@ -626,4 +626,4 @@ _skip_trap_exit_rv32_hyp: - OpenSBI 源代码 - RISC-V 手册 -[001]: ./20230728-sbi-firmware-analyze-1.md +[001]: 20230728-sbi-firmware-analyze-1.md -- Gitee From 78bb58bfbbf494e5014919d3c0e332b0f7f12180 Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Wed, 16 Aug 2023 02:01:06 +0000 Subject: [PATCH 64/68] sbi-firmware-analyze-2.md: commit correct result of tinycorrect-refs Signed-off-by: Groot <1219671600@qq.com> --- articles/20230728-sbi-firmware-analyze-2.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/articles/20230728-sbi-firmware-analyze-2.md b/articles/20230728-sbi-firmware-analyze-2.md index b73bb20..f0e2818 100644 --- a/articles/20230728-sbi-firmware-analyze-2.md +++ b/articles/20230728-sbi-firmware-analyze-2.md @@ -1,4 +1,4 @@ -> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [urls]
+> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc1 - [urls refs]
> Author: groot
> Date: 2023/07/28
> Revisor: Falcon [falcon@tinylab.org](mailto:falcon@tinylab.org)
@@ -625,5 +625,6 @@ _skip_trap_exit_rv32_hyp: - OpenSBI 源代码 - RISC-V 手册 +- [OpenSBI 固件代码分析(一)][001] [001]: 20230728-sbi-firmware-analyze-1.md -- Gitee From 07ae3a6efa2f04ae04effb9cb4ee3c27fe22f456 Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Wed, 16 Aug 2023 02:02:51 +0000 Subject: [PATCH 65/68] =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- articles/20230728-sbi-firmware-analyze-2.md | 1 - 1 file changed, 1 deletion(-) diff --git a/articles/20230728-sbi-firmware-analyze-2.md b/articles/20230728-sbi-firmware-analyze-2.md index f0e2818..eb2cbbb 100644 --- a/articles/20230728-sbi-firmware-analyze-2.md +++ b/articles/20230728-sbi-firmware-analyze-2.md @@ -238,7 +238,6 @@ _bss_zero: lla a1, fw_fdt_bin #endif - ``` ### 针对特定平台进行相关初始化 -- Gitee From b4675070d88e8a221e58eb2ec9e18ca9bcfa3371 Mon Sep 17 00:00:00 2001 From: Groot <1219671600@qq.com> Date: Wed, 16 Aug 2023 02:06:27 +0000 Subject: [PATCH 66/68] update articles/20230728-sbi-firmware-analyze-2.md. Signed-off-by: Groot <1219671600@qq.com> --- articles/20230728-sbi-firmware-analyze-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/articles/20230728-sbi-firmware-analyze-2.md b/articles/20230728-sbi-firmware-analyze-2.md index eb2cbbb..6fbdf8f 100644 --- a/articles/20230728-sbi-firmware-analyze-2.md +++ b/articles/20230728-sbi-firmware-analyze-2.md @@ -103,7 +103,7 @@ - 如果 `a6` 不等于 `-1`,说明已经指定了启动核心。通过比较 `a0` 和 `a6` 的值,如果不相等,表示当前核心不是指定的启动核心,应该跳转到 `_wait_relocate_copy_done` 函数处。 4. 如果到达 `_try_lottery` 函数,表示需要进行抽签来选择启动核心。这里使用原子操作(`amoadd.w`)在地址 `_relocate_lottery` 所指向的位置执行原子加 1 操作,得到的结果存储在 `a6` 中。 5. 根据 `_relocate_lottery` 的值,判断是否是第一个核心执行到抽签操作的,如果不是(`a6` 不等于 0),则直接跳转到 `_wait_relocate_copy_done`。 -6. 如果到达这里,说明是第一个核心执行抽签操作,即抽中了抽签,将会进入下一阶段的代码。 +6. 如果到达这里,说明是第一个核心执行抽签操作,即抽中了签,将会进入下一阶段的代码。 这段代码的主要目的是确定首选的启动核心,并在多核情况下确保只有一个核心执行到 `_try_lottery` 部分,并将启动核心的信息保存到特定地址。对于其他核心,如果不是启动核心,则会跳转到 `_wait_relocate_copy_done` 处等待。 -- Gitee From 2d1417156313609d2c5b17a8d31530f2fd178114 Mon Sep 17 00:00:00 2001 From: falcon Date: Wed, 16 Aug 2023 06:28:40 +0000 Subject: [PATCH 67/68] Update articles/20230728-sbi-firmware-analyze-1.md --- articles/20230728-sbi-firmware-analyze-1.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/articles/20230728-sbi-firmware-analyze-1.md b/articles/20230728-sbi-firmware-analyze-1.md index 5a81fb2..3171c14 100644 --- a/articles/20230728-sbi-firmware-analyze-1.md +++ b/articles/20230728-sbi-firmware-analyze-1.md @@ -6,7 +6,7 @@ > Proposal: [RISC-V Linux 内核 SBI 调用技术分析](https://gitee.com/tinylab/riscv-linux/issues/I64YC4)
> Sponsor: PLCT Lab, ISCAS -# OpenSBI 固件代码分析(一) +# OpenSBI 固件代码分析(1):启动流程 ## 前言 -- Gitee From a0e2572cf932c543365911bf9953247840575a96 Mon Sep 17 00:00:00 2001 From: falcon Date: Wed, 16 Aug 2023 06:28:54 +0000 Subject: [PATCH 68/68] Update articles/20230728-sbi-firmware-analyze-2.md --- articles/20230728-sbi-firmware-analyze-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/articles/20230728-sbi-firmware-analyze-2.md b/articles/20230728-sbi-firmware-analyze-2.md index 6fbdf8f..1f6bc52 100644 --- a/articles/20230728-sbi-firmware-analyze-2.md +++ b/articles/20230728-sbi-firmware-analyze-2.md @@ -6,7 +6,7 @@ > Proposal: [RISC-V Linux 内核 SBI 调用技术分析](https://gitee.com/tinylab/riscv-linux/issues/I64YC4)
> Sponsor: PLCT Lab, ISCAS -# OpenSBI 固件代码分析(二) +# OpenSBI 固件代码分析(二):fw_base.S 源码分析 ## 前言 -- Gitee