news 2026/11/4 6:00:55

RISC-V机密计算实战:PMP隔离、安全监视器与CoVE扩展解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
RISC-V机密计算实战:PMP隔离、安全监视器与CoVE扩展解析

1. 先从“机密计算”这个被说烂的词讲起

做 RISC-V 安全相关的工作这些年,我每次对外讲“机密计算”,对方第一反应基本是“和 TrustZone 差不多吧”。这句话其实把两个完全不同的东西混在一起了。传统意义上的安全世界(Secure World)解决的是“别让普通世界碰到我的钱”,而机密计算解决的是“连操作系统本身都别想看到我的数据”。这两者的信任边界、硬件需求、软件栈复杂度完全不在一个量级上。

RISC-V 平台这几年在服务器和云场景里开始频繁露面,机密计算这个词也顺势成了架构师们绕不开的选项。RISC-V 好就好在指令集和特权架构是完全开放的,你能拿到完整的 CPU RTL、完整的启动固件源码、完整的工具链控制权,这意味着你可以从物理层面一点一点把信任边界收紧,而不像闭源平台那样只能对着几个固定接口做文章。这篇文章我想从我自己实际做过的项目出发,把 RISC-V 机密计算落地时最关键的三个环节讲透:PMP 物理内存隔离、M 模式安全监视器、以及正在标准化进程中的 CoVE 机密虚机扩展。适合正在做 RISC-V 固件、TEE、或者想评估 RISC-V 能不能承载云上机密虚机的人参考。

1.1 你以为的“安全隔离”和真正要的“机密计算”差别在哪

先捋一下威胁模型。普通的安全启动、签名固件、权限位检查,解决的是“防止攻击者篡改软件”。但机密计算面对的场景更狠:你的宿主机操作系统、云平台管理员、甚至隔壁虚机的 Root 用户都可能成为对手。虚拟机监视器(Hypervisor)是宿主内核的一部分,如果它能直接读虚机的物理内存,那么虚机里的业务数据就等于裸奔。

机密计算的思路是把可信计算基(TCB)压缩到很小的一块:CPU 核心本身、一小段固件、以及专门负责隔离的内存管理单元。这个 TCB 之外的任何代码,包括宿主 OS,都被当成“不可信”来处理。宿主 OS 可以继续管理 CPU、调度虚机、处理 I/O,但它永远没有权限去访问被标记为“机密”的物理页面。这不仅仅是安全设计的审美偏好,而是等保、隐私合规、以及云服务商对客户承诺“数据不出租户”时硬性的技术保底。

在 x86 上有 TDX/SEV,在 ARM 上有 CCA,RISC-V 上对应的就是 PMP + 安全监视器 + CoVE 这套组合。区别在于,x86 和 ARM 把大部分机制固化在硬件微码和 EL2 固件里,而 RISC-V 给了你一大片可以自主实现的软件空间。一方面这是好事,灵活、可控、能针对真实硬件做裁剪;另一方面这也是难事,很多在 x86 上不用你操心的边界切换、中断路由、物理地址保护,在 RISC-V 上都需要自己在安全监视器里一点点写出来。

1.2 为什么我会在 RISC-V 上做这件事

我最早接触 RISC-V 安全是从一块自研 SoC 开始的,当时项目里要跑 Linux,还要给一个金融场景的应用提供端到端加密隔离。预算有限,买不起商业 TEE 授权,最后只能从 RISC-V 特权规范里翻方案。翻了几天发现,PMP 机制其实已经足够在硬件层面完成“物理内存分区”,关键是上层怎么组织安全监视器和访问策略——这部分完全没有标准答案,全靠自己设计。

后来 CoVE 规范在 RISC-V 基金会里推进,我才算把之前的碎片化思路串起来了。CoVE 想做的事情,是给“机密虚机”提供一套体系化的 RISC-V 架构支持,让监控器、客户虚机、宿主 OS、I/O 设备之间的边界有统一规范。虽然它还很新,但设计方向和我自己在项目里踩坑后总结的经验几乎一致:先靠 PMP 把物理内存隔离成一个个保护区,再由 M 模式安全监视器管理保护区之间的切换,最后让客户虚机运行在受控的虚拟地址空间里。这篇文章的主体,就是沿着这条实践路径展开。

2. PMP 隔离:机密空间的物理级铁丝网

PMP(Physical Memory Protection)是 RISC-V 特权架构里的物理内存保护单元,说白了它就是一种每个硬件线程(Hart)自带的 L1 级访问控制门卫。它不看虚拟地址,只认物理地址;它不依赖软件维护页表,而是由硬件在每次访问时直接检查。这意味着即使宿主 OS 的内核被完全攻破,只要它不通过安全监视器,就没办法跨过 PMP 划定的红线。

2.1 PMP 工作机制:每个 Hart 都是你的门卫

PMP 的配置寄存器分为两类:PMPADDR 负责描述区域的物理地址范围,PMPCFG 负责描述区域的权限属性。RISC-V 规范允许每个 hart 最多实现 16 个 PMP 条目(部分低端核可能更少,RVA 配置文档一般要求至少 16 个)。每个条目由 8 位配置信息加上一个可变长的地址字段组成。

以 RV64 为例,PMPCFG0 这个 CSR 的位 0 到 7 对应 pmp0 的配置,位 8 到 15 对应 pmp1,依此类推。配置位里最关键的是 R/W/X 三个权限位,以及 A[1:0] 地址匹配模式和 L 锁定位的组合:

配置位含义说明
R可读1 表示允许读
W可写1 表示允许写
X可执行1 表示允许取指
A[1:0]模式选择OFF / TOR / NA4 / NAPOT
L锁定置 1 后直到复位前该条目不能被软件修改

A 字段里有四种模式,实际用得最多的是 TOR 和 NAPOT。TOR(Top of Range)用两个相邻 PMP 条目的地址值夹出一个区间;NAPOT(Naturally Aligned Power-of-Two)则直接把一个地址字段的高位解释为区域的基址,连续的低位 1 的个数就是区域大小。比如一个 NAPOT 条目里的地址值看起来是 0x8000_0000 到 0x8000_0FFF 对齐、低 12 位全 1,它代表的保护区就是 4KB。

优先级规则也容易记错:如果多个 PMP 条目同时命中同一个访问,编号最小的那个条目说了算。所以你在设计区域时,最好按物理地址从小到大排列条目,并始终预留一个“默认拒绝”兜底条目,避免漏配某块区域时出现静默放行。

2.2 第一个要注意的问题:颗粒度和 M 模式豁免

做 PMP 配置时最容易栽的坑是颗粒度。RISC-V 规范允许实现自定义 PMP 最小颗粒度,常见实现是 4KB 对齐,但也有芯片给出 16 字节甚至 64KB 的颗粒度。如果你的安全区间只打算保护一个 2KB 的敏感结构体,而硬件颗粒度是 4KB,那么 PMP 匹配范围会自动扩大到你没预期的地方,导致相邻内存也被纳入保护或产生额外故障。规范的做法是:在驱动初始化时读取 misa/实现相关寄存器或平台设备树里暴露的 pmp 颗粒度信息,再做一次地址对齐裁剪。一味在软件里假设 4KB 是危险的。

另一个老生常谈但必须强调的点:M 模式默认豁免所有 PMP 检查。也就是说,PMP 主要限制的是 S 模式和 U 模式;但有一条特殊规则,一旦某个条目的 L 位被置 1,那么 M 模式下访问这块区域也必须遵守该条目的权限。这个特性是做安全监视器最关键的武器:把关键固件代码段用 L 位锁定为只读,保证即使 M 模式侧发生内存破坏,攻击者也没办法改写可信代码。

如果你关注 RISC-V 安全扩展的发展,还会看到 Smepmp 这类扩展,它提供 M 模式只读,S 模式按权限访问的灵活配置,适合想进一步收紧 M 模式但又不想完全锁死的场景。小规模芯片上不建议一上来就追新扩展,先把基础 PMP 的锁定语义和颗粒度摸清楚,后面加扩展只是锦上添花。

2.3 link.ld、内存布局和 PMP 的第一次握手

PMP 设计不是一个独立的寄存器配置问题,它必须和固件的链接脚本(link.ld)配合。裸机固件启动时,_start符号所在的物理地址、所有代码段/数据段的位置、栈顶的偏移,这些组成内存布局,而 PMP 的保护区就是围绕布局来划分的。如果你的链接脚本把内核镜像堆在一个物理地址上,但 PMP 保护区只覆盖了前半段,后半段就会变成未受保护的可写区,被宿主进程改掉之后再去执行,后果不用我多说了。

下面是一个常见的最小链接脚本骨架,来源是我在一个 QEMU RISC-V 原型项目里实际用过的版本:

OUTPUT_ARCH(riscv) ENTRY(_start) SECTIONS { . = 0x80000000; /* 固定链接地址,QEMU virt 的 DDR 起点 */ .text : { KEEP(*(.text.init)); *(.text .text.*); } .rodata : { *(.rodata .rodata.*); } .data : ALIGN(4096) { __data_start = .; *(.data .data.*); __data_end = .; } .bss : ALIGN(4096) { __bss_start = .; *(.bss .bss.*); __bss_end = .; } . = ALIGN(4096); __heap_start = .; __heap_end = . + 0x100000; /* 1MB 堆 */ }

这个脚本故意把_start放在 0x80000000,目的是让它落在 QEMU virt 平台的物理内存起点,和平台的内存映射吻合。你在写自己的脚本时,要注意__heap_start和__heap_end的地址必须暴露给 PMP 配置代码,否则你后面设保护区的时候不知道边界在哪。

配套的 PMP 初始化逻辑,在 M 模式固件里大概是这样的思路:先把所有 M 模式核心镜像用的区域配置成读写执行,等固件完成自检和镜像度量之后,立即把代码段改成只读,再把 S 模式启动要用的内存区域单独划成一个窗口,权限只给读和写,不给执行。这样即使 S 模式内核某一页被写坏,它也没法直接跳到那块物理地址去执行恶意代码。

还有一点,锁定的 L 位不是越早置越好。如果固件还要支持 OTA 升级,而你把整个固件区域用 L=1 锁死,后续就无法改写固件分区。我一般的设计是把引导固件(Boot ROM 和 BL1)设为 L=1 只读,把可升级的 BL2/BL3 区域留一个可更新窗口,更新完成后由安全监视器切换 PMP 条目并作重新度量。

3. 安全监视器:藏在 M 模式的“可信管家”

PMP 只负责“拦截”,真正做决策的是安全监视器(Security Monitor)。在 RISC-V 特权架构里,M 模式拥有最终控制权:它能操作所有 CSR、能改 PMP、能接管所有异常和中断。所以安全监视器的正统设计位置是 M 模式,它的职责是作为不可信 S 模式代码与可信资源之间的唯一仲裁点。

3.1 为什么需要一个安全监视器而不是直接在 OS 内做事

RISC-V 的软件运行模式分为 U(用户)、S(操作系统/虚拟机监视器)、M(机器/固件),带虚拟化扩展还有 VS/VU 两层。如果你只是想在普通 Linux 上跑一个加密算法库,直接在用户态应用里做就够了;但如果你要保护一段完整虚机的运行过程,宿主操作系统本身就不可信,那就不能在宿主内核里放任何安全逻辑。你会需要一个独立于宿主内核运行的、自己在内存里留了一片锁死区域的监视器代码,它负责管理宿主和机密虚机之间的切换,而宿主内核没有能力改写它。

这里也解释了一个常见的困惑:为什么不直接让客户虚机运行在 M 模式,这样权限最高,什么都不用怕。答案是 M 模式没有虚拟内存、没有分页、没有中断控制器的高级抽象,直接跑一个完整 OS 在 M 模式下不仅性能灾难,而且一旦 OS 出了漏洞就直接破坏整个机器级安全边界。机密虚机仍然运行在受控的虚拟地址空间里,只不过它的物理页被 PMP 保护起来,并且地址翻译被二次封装——这种设计才符合机密计算的模型。

3.2 监控器必须做好的四件事

我自己做安全监视器的时候,发现它的核心工作可以收敛成四件事:

第一,上下文切换管理。从普通世界切到机密世界时,要把宿主的所有通用寄存器、CSR、活跃状态都压到指定内存,然后把机密虚机的现场恢复出来。这里最容易出错的是 mstatus 的 MPP 位和 mret 行为,切错了直接导致返回地址错误。

第二,异常和中断路由。物理中断来了之后,安全监视器要先判断这个中断属于宿主还是机密虚机,再决定要不要注入。机密虚机的中断不能直接透传给宿主,因为宿主一旦看到虚拟中断号,就可能推断出客户正在进行的敏感操作,所以你要设计一套中断隔离表格。

第三,SBI 服务。RISC-V 的 S 模式通过 ecall 调用 M 模式固件,这套接口标准叫 SBI(Supervisor Binary Interface)。安全监视器会扩展一些自定义的 SBI 功能,比如“申请一段机密内存”、“创建机密虚机上下文”、“切换 VP”。这些功能是宿主 OS 唯一能触及安全监视器的方式,必须做严格入参校验。

第四,策略检查。每次主机想打开新一轮内存窗口,监控器都要校验请求是否来自合法组件、目标地址是否落在白名单内、权限组合是否合理。我见过不少团队把策略写死到代码里,后来改需求还得刷整个固件。合理的做法是把策略做成配置表放在独立区域,用哈希签名保护。

3.3 与 OpenSBI / 自研监控器的选型取舍

大多数 RISC-V 开发板都自带 OpenSBI,它实现了标准 SBI 调用和部分平台初始化。如果只是跑 Linux,直接用 OpenSBI 足够;但如果是做机密计算,OpenSBI 的默认实现就不够了,因为它没有“机密内存管理”和“攻击面最小化”的概念。你可以选择基于 OpenSBI 做二次开发,也可以自己写一个极简监控器,只保留重塑安全边界所需的最小逻辑,然后把其余平台初始化交给上一级固件。

我实际经验是:先跑通 OpenSBI 了解所有调用路径,再决定哪些模块能剪掉。剪掉的原则是,凡是和中断、PMP、虚拟化、内存申请无关的代码,都可以踢出 TCB。最终留下来的安全监视器代码量可能只有几千行,但它的正确性要求比一个全功能 SBI 高一个数量级,因为它是信任根,连内核都没资格怀疑它。

4. CoVE 机密虚机:给云原生加一个可信执行层

PMP 和安全监视器解决的是物理内存隔离的问题,但现代虚机场景还有虚拟化地址翻译、I/O 隔离、中断注入等一大堆新活儿。RISC-V 社区针对这些需求推动了一个扩展叫 CoVE(Confidential VM Extension),目标是让 RISC-V 平台能像 x86 TDX、ARM CCA 一样,在标准虚拟化栈里直接支持机密虚机。

4.1 追溯为什么需要 CoVE

如果你只是在自家板卡上跑一个裸机安全应用,PMP 已经够用;但云端场景不一样,云上跑的是标准 Linux 虚拟化,虚机的生命周期由 Hypervisor(VMM)管理,客户机也是标准 OS。如果把宿主 Hypervisor 拉出 TCB,那么现有虚拟化代码全都要重写。CoVE 的意义就是定义一个标准机制,让现有的 Hypervisor 几乎可以继续跑,只不过它对机密虚机的控制权被安全监视器大幅收回,变成了“只提供资源调度,不触摸客户数据”的角色。

CoVE 规范还在演进,但基本思路已经比较清晰:客户虚机运行在 VS/VU 模式,由安全监视器在 M 模式作为 TEE 管理方,负责物理页面的加密保护、虚拟 CPU 上下文隔离、以及客户内存的搬入搬出。宿主 OS 和普通虚机运行在非机密世界,只能通过规定的 TEE 管理调用接口向安全监视器发起能力受限的请求。

4.2 CoVE 架构里的关键角色划分

我按自己的理解画个角色表,方便你对比:

角色所在模式职责
安全监视器M 模式管理 PMP、执行上下文切换、验证启动镜像、处理 TEE 管理调用
机密虚机(TVM)VS/VU 模式运行机密负载,宿主不可见其内部状态
宿主 HypervisorS 模式调度 CPU、管理 I/O,但不允许读取机密虚机物理页
普通虚机VS/VU 模式按传统虚拟化方式运行,不享受机密保护

机密虚机的物理内存来源是安全监视器掌握的保护区。宿主只能向监视器“申请”内存页,监视器分配后会把对应区域的 PMP 条目切给机密世界,同时用两阶段地址翻译(satp 负责客户虚拟地址到客户物理地址,hgatp 负责客户物理地址到宿主物理地址)把客户物理地址隐藏起来。任何来自设备的中断,也要先经过监视器判断,再决定要不要注入虚拟中断,防止宿主通过中断信号侧信道偷看客户行为。

I/O 这一侧同样重要。RISC-V 配套的还有 IOMMU 扩展,用来管理设备对物理内存的 DMA 访问。只有 DMA 被限制在非机密内存时,PMP 对 CPU 的隔离才真正闭环;否则攻击者可以直接控制一张网卡去 DMA 读机密内存页。这个点在我的项目里是最后补上的,最初只用 PMP 保护 CPU 侧,导致设备侧是一个巨大的安全漏洞。

4.3 与 ARM CCA、x86 TDX 的横向对照

做技术选型时免不了横向比较。我的经验是,x86 TDX 胜在生态成熟、工具链齐全,但硬件黑盒多,安全边界难审计;ARM CCA 有 Realm Management Extension,理念相近,但在中小团队手里封闭度偏高;RISC-V CoVE 最大的优势是开源可审计、硬件和固件你都能拿到源码,劣势也很明显——规范新、硬件支持少、工具链不完善,很多东西要靠自己实现。

实际上,现阶段在 RISC-V 上部署“完整 CoVE”还是超前的事。更多团队的做法是参照 CoVE 的角色划分,先用安全监视器和 PMP 搭一个最小可用的机密执行环境,等芯片 IP 跟进 CoVE 之后再平滑升级。这个路径我在项目里走过一遍,可行。

5. 手把手:最小可信执行原型的搭建路径

理论讲得再多,不如亲手跑一个最小系统。我给出一个可以在 QEMU 上完整实验的路径,你不需要一块真实开发板就能理解整个框架。目标很简单:让 S 模式内核访问一块被 PMP 保护的“机密内存”,访问失败并且安全监视器把这个失败事件记录到共享内存里。

5.1 平台与工具链选择

推荐用 QEMU 的 RISC-V virt 机器,它模拟了足够完整的特权架构和 PLIC/CLINT,不需要额外硬件。工具链用riscv64-unknown-elf-系列,交叉编译裸机固件。如果不想从零折腾启动,可以先用opensbi跑通 SBI 启动路径,再替换成你自定义的安全监视器。

编译时注意-march参数,建议用rv64gc_zicsr_zifencei,只启用基础扩展和 CSR/栅栏指令。不要无脑开rv64gcv,浮点和向量扩展如果在固件里没有处理状态切换,会在 trap 时把现场搞乱。

5.2 第一步:建立一个可度量的启动链

安全系统的第一条铁律是:信任必须从真实固件开始度量。启动链一般分为几级:第一级是 Boot ROM,放在 SoC 内部的只读存储里,不可更改;它的职责是加载第二级固件到固定地址,计算哈希,和 OTP(一次性可编程区)里预置的期望值比较,不一致就停在这里。

二级固件再加载安全监视器和客户镜像,继续做度量。每一级的哈希测量对象都包括代码和数据,代码地址由链接脚本固定。我的经验是,链接脚本里最好把所有段的起始地址都以 4KB 对齐,这样既能满足 PMP 颗粒度,也为后续哈希计算提供对齐的块边界。

5.3 第二步:在 trap 处理里观察 PMP 生效

下面的代码演示了安全监视器在 M 模式 trap 处理的一小段逻辑。它先读mcause,如果是加载或存储访问故障,就把mtval(出问题的物理地址)记录到一块共享内存,方便宿主内核查看:

.section .text.trap .globl mtrap_handler mtrap_handler: csrr t0, mcause li t1, 5 # 0x5 = Load access fault beq t0, t1, save_fault li t1, 7 # 0x7 = Store access fault beq t0, t1, save_fault # 其他异常统一走默认逻辑 j default_handler save_fault: csrr t0, mtval la t1, __fault_log sd t0, 0(t1) li t2, 1 sd t2, 8(t1) default_handler: # 恢复 mstatus / mepc,返回 mret

这段代码虽然朴素,但它验证了 PMP 拦截是否生效。你可以在 S 模式写一段代码,故意去读那块受保护地址,然后回来查看__fault_log里记录的物理地址是不是你想保护的那个。真正的安全监视器当然不能这么简单,但这个最小验证能帮你快速确认 PMP 配置、异常路由、CSR 读写三条链路都没问题。

5.4 第三步:一个可以实验的安全内存隔离实例

在 QEMU 上做实验时,我建议分三步配置 PMP:

第一步,启动阶段所有内存都放行,保证 Boot ROM 可以把镜像搬到 DDR。第二步,安全监视器初始化完成后,把包含监视器自身代码和数据的物理区间设为 R+X(或 R+W+X,取决于是否允许监视器用栈),并设置 L=1。第三步,定义一个独立的机密区间,S 模式对它一律拒绝。

下面是一个极简 C 风格的 PMP 配置思路,不同硬件平台会有寄存器布局差异,但思路通用:

// 伪代码:把 pmp_idx 配置成 TOR 模式,保护区间 [start, end) void pmp_set_tor(int idx, uintptr_t start, uintptr_t end, int rw) { // 依赖前一条 pmpaddr 作为 start,因此要按地址递增顺序设置 csr_write(CSR_PMPADDR(idx - 1), start >> 2); // 实际写前一条的结束地址 csr_write(CSR_PMPADDR(idx), end >> 2); uint8_t cfg = (rw & 1) ? (PMP_R | PMP_W) : PMP_R; cfg |= (PMP_A_TOR | PMP_L); csr_write(CSR_PMPCFG(idx / 4), 读旧值 | ((uint64_t)cfg << ((idx % 4) * 8))); }

跑通这个实例之后,你可以再做几个扩展实验:锁死固件区域后尝试写它,看是否触发异常;把机密内存从 PMP 列表里临时移除,再验证安全监视器能否恢复;模拟设备 DMA 访问机密内存,配合 IOMMU 看是否被拦截。这些实验会逼你注意很多实现细节,比如 CSR 读写时旧值不能直接覆盖,csrrw和csrs的语义差别等。

6. 常见问题与排查思路

从自己的开发记录里翻了一些高频问题,整理成一张速查表,覆盖了 PMP、安全监视器、CoVE 原型三个阶段比较典型的坑。

现象可能原因排查方向
启动后 S 模式访问任意内存都触发故障PMP 默认拒绝策略生效,但你没有给 S 模式分配合法区域检查是否存在一个覆盖整个非安全内存的放行 PMP 条目
设置 PMP 后 M 模式自己也访问失败有 L=1 条目且权限不足看 L 位是否误置;确认要不要在初始化时临时跳过 L
固件更新后 PMP 配置被重置锁定的 PMP 在复位后失效,更新流程用了未经锁定的代码矢量把更新入口放在 Boot ROM 里,而不是依赖可被覆盖的 SRAM 代码
trap 进入监视器后现场恢复崩溃mstatus.MPP 保存错误或没有先做寄存器保存检查 ecall 进来时的 mstatus 快照和返回时 MPP 还原
interrupt 总被宿主抢占中断路由表里机密中断没有被安全监视器接管确认 PLIC 的中断优先级和监视器的中断注入逻辑
CoVE 原型机上无法加载标准 LinuxVMM 没有适配 TEE 管理调用接口,或内存申请失败先用 OpenSBI 默认接口跑通最小 Linux,再逐层加 TEE 管理调用

PMP 配置失败是我见过最多的坑,尤其是 TOR 模式下“前后两条 PMP 地址必须递增”这个约束。很多人把两条地址写反,结果区域完全错乱。另一个容易出问题的地方是 64 位下 PMPCFG 的字段偏移,pmp0cfg 在字节 0、pmp1cfg 在字节 1,不是按位排列的,写代码时一定要按字节索引计算,而不是简单左移一位。

调试安全监视器时,建议从一开始就把日志写到一段独立于栈和堆的物理区域,并且这段区域不在 PMP 保护范围之外被改写。遇到 trap 崩溃时,先看mcause的数字:5 是 load 访问故障,7 是 store 访问故障,1 是指令访问故障,2 是非法指令。这三个基本能覆盖 80% 的 PMP 配置问题。

最后再分享一个我个人的经验:不要一开始就追求把安全监视器的代码压到最小。保持它的模块化,把上下文切换、PMP 配置、TEE 管理调用、中断路由分开写,然后在每一层之间加校验。等测试用例跑透了,再考虑合并函数、压缩体积。机密计算这种系统里,代码越少越容易审计,但如果没有清晰的模块边界,代码少反而意味着错误集中,后期修复更痛苦。后来我把这套模块化的监视器设计延展到了 CoVE 原型的实验里,发现它和 CoVE 规定的 TEE 管理调用天然能对上,迁移成本低得多。整个过程中最值钱的资产其实是 QEMU 上那套最小验证环境,它能让你在还没有真实硬件支持 CoVE 的情况下,先把隔离、切换、度量这些底层机制全部跑通,为后续芯片选型和企业级落地留出足够提前量。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/11/4 6:00:52

TM4C1299+ADS122U04工业温度采集方案:从选型到校准实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/11/4 5:59:00

2026年9月20日:把普通周日变成高效时间锚点的规划指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/11/4 5:59:00

安防协议栈全链路发布:ONVIF与GB28181设备侧接入深度解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/11/4 5:58:55

[数字图像处理]灰度变换——直方图处理

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/11/4 5:58:31

AI如何重塑科研全流程:从文献挖掘到实验优化

1. 项目背景与核心价值实验室里的显微镜旁堆着三摞实验记录本&#xff0c;这是我十年前刚入行时的常态。如今再看同事的工位&#xff0c;取而代之的是一块实时显示实验数据的屏幕和几个正在自动运行的算法模型。这个转变背后&#xff0c;正是AI技术对自然科学研究的重塑——它不…

作者头像 李华