0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

如何在KVM上使用CCA RME虚拟机?

冬至子 来源:修志龙_ZenonXiu 作者:修志龙_ZenonXiu 2023-09-11 14:59 次阅读

之前介绍了CCA设计的一个目的是让hypervisor可以创建和调度RME虚拟机和vCPU,并且给它分配内存等资源,但是一旦这些资源分配给RME虚拟机之后,hypervisor就失去了访问这些RME所有的内存内容的权限。这是通过EL3的monitor软件修改GPT表项实现的。从而实现虚拟机的机密计算。

Arm和开源社区提供一个参考设计,其中包括包括:

1. Linux kernel KVM模块的支持:

  • 支持由RME通过RMI接口创建,运行虚拟机vCPU,可以服务RME虚拟的RIS host call
  • 增加了KVM UABI用于管理Realms
  • 增加了Linux kernel作为CCA的Host, 也可以做为CCA的Guest(运行于RME虚拟机,支持RSI接口)

目前运行hypervisor的host Linux运行在non secure EL2, 并且运行于VHE模式。

2. 开源的RMM实现:TF-RMM,主要是服务host hypervisor发过来的RMI请求,创建在Realm状态下对应host Linux KVM里struct kvm的虚拟机的描述和状态,和对应host Linux vCPU的虚拟CPU状态和上下文的结构,并且负责Realm中vCPU的context的save/restore. RMM还负责通过Realm的stage 2实现多个Realm虚拟机间的隔离。

3. 扩展现有的 Arm Trusted Firmware,使它支持RME引入的4个secure状态和它们间切换时的context swithing,增加了RMI调用的支持和GPT页表的管理。

image.png

TF-RMM采用了类似Hafnium, OPTEE的工作方式,主要目的是将原来在host Linux kernel做的部分事情(如和vCPU context,stage 2页表配置相关的)通过RMI调用转到Realm world实现,这部分事情放在host Linux中的话,可以导致host可以获得修改VM的信息和控制VM的执行。但是TF-RMM和Hafnium, OPTEE一样没有调度和复杂的内存管理功能,TF-RMM的执行主要是服务Host Linux的RMI调用和Guest Linux的RSI调用。

概况

image.png

Host Linux支持创建普通的虚拟机和Realm虚拟机,现有的KVM API还是可以使用。Host Linux通过KVM_CAP_ARM_RME告诉user space的VMM,平台是否支持RME的KVM。如果要创建Realm虚拟机,VMM可以通过kvm module的ioctl KVM_ENABLE_CAP调用并传入.cap = KVM_CAP_ARM_RME参数。Host Linux kernel为之创建的struct kvm结构中的kvm->arch.is_realm记录,后面kernel通过kvm_is_realm(kvm)检查是否为Realm VM. KVM_CAP_ARM_RME会导致KVM调用kvm_create_realm->realm_create_rd->rmi_realm_create 这会引起RMI Realm.Create的SMC调用,ATF会将这个调用dispatch给TF-RMM处理,TF-RMM会为之创建Realm Descriptor (RD,struct rd)的结构,对应于host Linux kernel的struct kvm结构。TF-RMM的rmm_handler通过一下handler来服务这些RMI调用:

static const struct smc_handler smc_handlers[] = {
    HANDLER(VERSION,        0, 0, smc_version,         true, true),
    HANDLER(FEATURES,        1, 1, smc_read_feature_register, true,  true),
    HANDLER(GRANULE_DELEGATE,    1, 0, smc_granule_delegate,     false, true),
    HANDLER(GRANULE_UNDELEGATE,    1, 0, smc_granule_undelegate,     false, true),
    HANDLER(REALM_CREATE,        2, 0, smc_realm_create,         true,  true),
    HANDLER(REALM_DESTROY,        1, 0, smc_realm_destroy,     true,  true),
    HANDLER(REALM_ACTIVATE,        1, 0, smc_realm_activate,     true,  true),
    HANDLER(REC_CREATE,        3, 0, smc_rec_create,         true,  true),
    HANDLER(REC_DESTROY,        1, 0, smc_rec_destroy,         true,  true),
    HANDLER(REC_ENTER,        2, 0, smc_rec_enter,         false, true),
    HANDLER(DATA_CREATE,        5, 0, smc_data_create,         false, false),
    HANDLER(DATA_CREATE_UNKNOWN,    3, 0, smc_data_create_unknown,     false, false),
    HANDLER(DATA_DESTROY,        2, 0, smc_data_destroy,         false, true),
    HANDLER(RTT_CREATE,        4, 0, smc_rtt_create,         false, true),
    HANDLER(RTT_DESTROY,        4, 0, smc_rtt_destroy,         false, true),
    HANDLER(RTT_FOLD,        4, 0, smc_rtt_fold,         false, true),
    HANDLER(RTT_MAP_UNPROTECTED,    4, 0, smc_rtt_map_unprotected,     false, false),
    HANDLER(RTT_UNMAP_UNPROTECTED,    3, 0, smc_rtt_unmap_unprotected, false, false),
    HANDLER(RTT_READ_ENTRY,        3, 4, smc_rtt_read_entry,     false, true),
    HANDLER(PSCI_COMPLETE,        2, 0, smc_psci_complete,     true,  true),
    HANDLER(REC_AUX_COUNT,        1, 1, smc_rec_aux_count,     true,  true),
    HANDLER(RTT_INIT_RIPAS,        3, 0, smc_rtt_init_ripas,     false, true),
    HANDLER(RTT_SET_RIPAS,        5, 0, smc_rtt_set_ripas,     false, true)

然后VMM通过KVM_CREATE_VCPU的kvm ioctl调用kvm_vm_ioctl_create_vcpu ->kvm_arch_vcpu_create创建了vCPU之后,对于Realm VMh还需要通过KVM_ARM_VCPU_FINALIZE kvm_arch_vcpu_ioctl系统调用, kvm_arm_vcpu_finalize kvm_create_rec->rmi_rec_create, 通过RMI REC_CREATE调用(通过SMC指令)到ATF,ATF再将此调用dispatch到TF-RMM,TF-RMM通过smc_rec_create在Realm状态创建对应于host Linux KVM struct kvm_vcpu的struct rec结构体。Host kernel KVM通过vcpu_is_rec()检查vCPU是否运行在Realm中。

struct rec {
    struct granule *g_rec;    /* the granule in which this REC lives */
    unsigned long rec_idx;    /* which REC is this */
    bool runnable;

    unsigned long regs[31];

    /*
     * PAuth state of Realm.
     * Note that we do not need to save NS state as EL3 will save this as part of world switch.
     */
    struct pauth_state pauth;

    unsigned long pc;
    unsigned long pstate;

    struct sysreg_state sysregs;
    struct common_sysreg_state common_sysregs;

    struct {
        unsigned long start;
        unsigned long end;
        unsigned long addr;
        enum ripas ripas;
    } set_ripas;
    /*
     * Common values across all RECs in a Realm.
     */
    struct {
        unsigned long ipa_bits;
        int s2_starting_level;
        struct granule *g_rtt;
        struct granule *g_rd;
        bool pmu_enabled;
        unsigned int pmu_num_cnts;
        bool sve_enabled;
        uint8_t sve_vq;
    } realm_info;

    struct {
        /*
         * The contents of the *_EL2 system registers at the last time
         * the REC exited to the host due to a synchronous exception.
         * These are the unsanitized register values which may differ
         * from the value returned to the host in rec_exit structure.
         */
        unsigned long esr;
        unsigned long hpfar;
        unsigned long far;
    } last_run_info;

    /* Pointer to per-cpu non-secure state */
    struct ns_state *ns;

    struct {
        /*
         * Set to 'true' when there is a pending PSCI
         * command that must be resolved by the host.
         * The command is encoded in rec- >regs[0].
         *
         * A REC with pending PSCI is not schedulable.
         */
        bool pending;
    } psci_info;

    /* Number of auxiliary granules */
    unsigned int num_rec_aux;

    /* Addresses of auxiliary granules */
    struct granule *g_aux[MAX_REC_AUX_GRANULES];
    struct rec_aux_data aux_data;

    unsigned char rmm_realm_token_buf[SZ_1K];
    size_t rmm_realm_token_len;

    struct token_sign_ctx token_sign_ctx;

    /* Buffer allocation info used for heap init and management */
    struct {
        struct buffer_alloc_ctx ctx;
        bool ctx_initialised;
    } alloc_info;

    struct {
        unsigned long vsesr_el2;
        bool inject;
    } serror_info;

    /* True if host call is pending */
    bool host_call;
};

然后VMM通过KVM_RUN 的ioctl运行vCPU,对于Realm VM, 这是通过kvm_arch_vcpu_ioctl_run ->kvm_rec_enter->rmi_rec_enter,rmi_rec_enter通过RMI REC_ENTER的调用(通过SMC指令)到ATF,ATF将它dispatch到TF-RMM,TF-RMM调用smc_rec_enter,然后通过rec_run_loop将物理CPU的执行转交给Realm EL0&EL1的Realm VM。

完整的vCPU运转图如下:

image.png

由此看出,对于host Linux KVM, 与正常的KVM VM, pKVM VM类似,Realm虚拟机的vCPU的创建是由host Linux上的VMM管理的,每个Realm vCPU对于host Linux来说也只是VM进程的一个线程,也是由host Linux负责调度的。只是Realm vCPU运行于Realm EL0&EL1状态。但是Realm VM和Realm vCPU的信息host Linux是看不到的,这点与正常的KVM VM不同。

从上图也可以看出,Realm vCPU执行的进入和退出需要经过更长的路径,如进入vCPU的执行需要经过host Linux KVM->ATF->TF-RMM->Realm VM,每个步骤都涉及到context save/restore. 但根据初步的研究发现,这个过程中带来的overhead并不是很明显。

image.png

Stage 2 table的管理

RMM的stage 2用于Realm VM之间的隔离。在TF-RMM中维护一个RTT,相当于Normal KVM VM中的stage 2页表,对于Realm VM, 这是由TF-RMM管理的RTT.
Realm VM的IPA空间包括一个受保护的地址范围(PAR),RMM确保只能将其映射到Realm PAS。

对于在PAR内的访问,RMM保证给Realm的机密性和完整性;在PAR之外,hypervisor可以自由地映射NS PAS内存页或模拟访问。这为在Realm VM上运行的OS提供了一个可靠的机制来确定它是访问自己的私有内存,还是可以与不受信任的其他部分共享的内存。

在Realm创建期间,hypervisor可以将一个内存页分配给Realm,在PAR内的特定IPA处,并从NS内存页复制数据到它。IPA和数据经过加密哈希,哈希包含在Realm的认证令牌中。认证令牌允许Realm的所有者推理其初始状态和内容。一旦Realm被激活,只能将内存添加到其他未使用的IPA。

hypervisor可以随时从Realm回收内存。RMM在回收内存页并将其返回给hypervisor之前将此内存页清零。后续访问Realm回收内存的IPA会导致RMM的stage 2异常,这会阻止Realm进一步执行并保持Arm CCA的完整性保证。

在Host Linux KVM中它可以请求map或是unmap Realm VM的stage 2映射,让RMM管理Realm stage 2页表,但它不能获取修改到Realm RMM的stage 2页表内容。例如Host Linux KVM可以通过unmap_stage2_range来请求unmap一些stage 2映射,这个过程是:

unmap_stage2_range- >__unmap_stage2_range- >kvm_realm_unmap_range- >realm_tear_down_rtt_range,

在这里会通过RMI的RTT_DESTROY,RTT_CREAT调用(通过SMC指令)到ATF, ATF再将它dispatch到TF-RMM的smc_rtt_create,smc_rtt_create进行RMM RTT页表操作。

中断处理

目前没有GIC没有专门对Realm VM的虚拟中断支持,因此Realm VM的中断需要在host Linux kernel中通过其vgic中断产生虚拟中断。

由代码可知,RMM在rec_run_loop中准备运行Realm时(参见前面Realm vCPU运行流程图),它调用restore_realm_state

static void restore_realm_state(struct rec *rec)
{
    write_cnthctl_el2(rec- >sysregs.cnthctl_el2);
    isb();

    restore_sysreg_state(&rec- >sysregs);

    write_elr_el2(rec- >pc);
    write_spsr_el2(rec- >pstate);
    write_hcr_el2(rec- >sysregs.hcr_el2);

设置Realm EL2的HCR_EL2寄存器的值为

HCR_FLAGS     (HCR_FWB | HCR_E2H | HCR_RW | HCR_TSC | HCR_AMO |HCR_BSU_IS | HCR_IMO | HCR_FMO | HCR_PTW | HCR_SWIO | HCR_VM | HCR_TID3 | HCR_TEA | HCR_API | HCR_APK)

这个设置中HCR_IMO, HCR_FMO让当运行在Realm EL0&EL1 (Realm VM)时产生的IRQ和FIQ会route到Realm EL2 (RMM)中处理。
RMM对于这些route到Realm EL2的处理为

el2_irq_lel:
    stp    x0, x1, [sp, #-16]!
    mov    x0, #ARM_EXCEPTION_IRQ_LEL
    b    realm_exit
ENDPROC(el2_sync_lel)

el2_fiq_lel:
    stp    x0, x1, [sp, #-16]!
    mov    x0, #ARM_EXCEPTION_FIQ_LEL
    b    realm_exit
ENDPROC(el2_sync_lel)

realm_exit使执行回到handle_realm_exit,然后继续退回到ATF,ATF继续退回到Host Linux KVM, 再在host Linux kernel里进行这个物理中断的处理,如果要产生一个虚拟中断给Realm VM,host Linux kernel利用其GIC driver的vGIC产生一个虚拟中断,然后通过ATF进入到RMM,RMM再回到Realm VM的执行,这是Realm VM就能处理这个pending的虚拟中断。

image.png

ATF的支持

ATF对CCA支持主要包括:
1. 加载RMM Image和启动RMM

image.png

2. 负责Root,Non Secure,Secure和Realm四个secure world的切换,增加了RMM的dispatcher

image.png

image.png

3. 管理GPT页表

Host Linux KVM可以通过alloc_delegated_page ,free_delegated_page 等函数来请求ATF改变一个物理内存页在GPT页表中的PAS, 这是通过host Linux kvm调用rmi_granule_delegate, rmi_granule_undelegate也就是RMI GRANULE_UNDELEGATE ,RMI GRANULE_UNDELEGATE的RMI调用(通过SMC指令),这会进入到ATF的rmmd_rmi_handler进行处理,它将这个RMI请求forward到RMM中处理,RMM处理之后,RMM可以调用RMM_GTSI_DELEGATE或SMC_RMM_GTSI_UNDELEGATE调用(通过SMC)回到ATF,在ATF中的rmmd_rmm_el3_handler中调用gpt_delegate_pas或是gpt_undelegate_pas来修改GPT页表,最终修改了此物理页对应的PAS。

image.png

不受信任的hypervisor总能停止调度一个Realm,并总是可以回收分配给Realm的内存,但在任何情况下,它都不能访问Realm的CPU或内存状态。这是通过一个简单但功能强大delegate(分批/委托)来实现的。hypervisor将内存delegate给Realm world,和回收到non secure world。所有由Realms使用的内存首先必须由hypervisor delegate;RMM本身不管理Realm的内存池。

一旦内存被delegate给Realm world,hypervisor可以请求RMM将其用于各种目的,例如保存Realm的元数据或数据。当一个内存页被delegate给Realm world但未被RMM使用时,RMM确保该内存页清零,从而降低了内存页被重用或回收时意外信息泄露风险。

RMM为hypervisor提供了一个Realm管理接口(RMI),要求RMM delegate内存、创建Realms、执行Realms和为Realms分配内存。每一个RMI命令都是作为一个SMC实现的,所以当hypervisor调用该命令时,它会陷入到EL3 Monitor,然后切换执行到Realm world的RMM来处理命令。在RMI命令完成后,RMM向EL3 Monitor发出一个SMC,将执行切换回NS world的hypervisor。

为了维护Realms的安全保证,RMM必须知道系统上每个内存页的状态,它通过维护自己的内存页状态表(GST)来实现,以跟踪每个内存页的delegate状态和使用情况。当hypervisor delegate一个内存页时,RMM检查其GST,以确认内存页尚未被delegate,RMM然后向EL3 Monitor发出一个SMC请求更改Realm PAS。

EL3 Monitor检查该内存页当前是否位于NS PAS,然后更新GPT将其移至Realm PAS。最后,RMM更新其GST以记录该内存页已被delegate。如果hypervisor试图delegate已被delegate的内存页,或回收RMM正在使用的内存页,RMM会返回一个错误代码给不受信任的hypervisor。与GPT不同,GST不是由硬件检查的,而只是一个软件记录机制。

目前的限制

  1. 目前没有GIC没有专门对Realm VM的虚拟中断支持,因此Realm VM的中断需要在host Linux kernel中通过其vgic中断产生虚拟中断。未来的GIC会支持Realm虚拟中断的注入
  2. 暂时没有 Device Assignment的支持,随着SMMU的进化和软件的开放,后面会支持
  3. 目前在arm构架上的KVM有四种不同的支持方式:armv7-a/armv8.0-a的KVM支持方式,自armv8.1-a开始支持的VHE方式,Google推的pKVM方式,Realm支持的方式。软件构架上需要提供更clear的实现。
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 寄存器
    +关注

    关注

    31

    文章

    5336

    浏览量

    120224
  • ARM处理器
    +关注

    关注

    6

    文章

    360

    浏览量

    41719
  • 虚拟机
    +关注

    关注

    1

    文章

    914

    浏览量

    28158
  • LINUX内核
    +关注

    关注

    1

    文章

    316

    浏览量

    21644
  • RTThread
    +关注

    关注

    8

    文章

    132

    浏览量

    40858
收藏 人收藏

    评论

    相关推荐

    何在Arm计算平台中实现机密计算

    内容不熟悉,请阅读我们的AArch64 异常模型和AArch64 内存管理文章。Arm 机密计算架构 (CCA) 中的有些操作涉及到虚拟机虚拟化。若您对这些概念不熟悉,请参考 AArch64
    发表于 08-05 14:40

    Arm的领域管理扩展(RME)介绍

    的软件或TrustZone固件不能访问。由于这个地址空间,Arm CCA构造了受保护的执行环境,称为领域。 领域允许保护权限较低的软件,如应用程序或虚拟机(VM)其内容。领域还可以防止使用以更高权限运行
    发表于 08-02 11:40

    ARM领域管理扩展(RME)系统架构介绍

    硬件功能和属性CCA架构。 Arm机密计算架构(Arm CCA)能够构建受保护的执行称为Realms的环境。领域允许特权较低的软件,如应用程序或虚拟机保护其内容和执行免受诸如操作系统或系统管理程序之类
    发表于 08-09 07:52

    Java虚拟机向ARM平台的移植

    介绍Java 虚拟机的概念,以常用的KVM 为例,讲述了将Java 虚拟机移植到ARM 平台的方法,重点介绍了移植过程中的编译选项。
    发表于 09-23 11:54 38次下载

    Java虚拟机向ARM平台的移植

    介绍Java 虚拟机的概念,以常用的KVM 为例,讲述了将Java 虚拟机移植到ARM 平台的方法,重点介绍了移植过程中的编译选项。
    发表于 09-23 11:54 22次下载

    KVM虚拟机管理和基本使用

    KVM — 全称是基于内核的虚拟机(Kernel-based Virtual Machine)是一个开源软件,基于内核的虚拟化技术,实际是嵌入系统的一个虚拟化模块,通过优化内核来使用
    的头像 发表于 02-07 09:20 1281次阅读

    误删除KVM虚拟机的数据恢复案例

    服务器KVM虚拟机被误操作删除,每台虚拟机包含一个qcow2格式的磁盘文件和一个raw格式的磁盘文件,需要恢复raw格式的磁盘文件,虚拟机
    的头像 发表于 04-12 13:56 713次阅读
    误删除<b class='flag-5'>KVM</b><b class='flag-5'>虚拟机</b>的数据恢复案例

    Linux下误删除KVM虚拟机的数据恢复案例

    Linux操作系统服务器,EXT4文件系统。服务器上部署3台KVM虚拟机
    的头像 发表于 06-27 13:36 546次阅读
    Linux下误删除<b class='flag-5'>KVM</b><b class='flag-5'>虚拟机</b>的数据恢复案例

    linux虚拟机系统基本指令

    virtualbox"来安装VirtualBox虚拟机软件。 使用命令"sudo apt-get install qemu-kvm"或"sudo yum install qemu-kvm"来安装QEMU
    的头像 发表于 11-17 10:04 1615次阅读

    虚拟机linux怎么编写程序

    虚拟机Linux编写程序是一项非常常见且有用的任务。无论是开发一个简单的脚本、一个Web应用程序还是一个复杂的软件项目,Linux提供了强大的工具和环境来帮助你进行编程。本文将详细介绍如何在
    的头像 发表于 11-17 10:08 1782次阅读

    虚拟机数据恢复—EXT4文件系统下KVM虚拟机数据恢复案例

    发行版本中。KVM使用Linux自身的调度器进行管理。 本案例中的服务器操作系统为Linux,文件系统为EXT4。操作系统的部署的几台KVM虚拟机被删除,每台
    的头像 发表于 04-17 14:22 375次阅读
    <b class='flag-5'>虚拟机</b>数据恢复—EXT4文件系统下<b class='flag-5'>KVM</b><b class='flag-5'>虚拟机</b>数据恢复案例

    服务器数据恢复—KVM虚拟机raw格式磁盘文件数据恢复案例

    服务器数据恢复环境: 一台服务器安装Linux操作系统+EXT4文件系统。服务器运行数台KVM虚拟机,每台虚拟机包含一个qcow2格式的磁盘文件和一个raw格式的磁盘文件。
    的头像 发表于 05-17 13:33 439次阅读
    服务器数据恢复—<b class='flag-5'>KVM</b><b class='flag-5'>虚拟机</b>raw格式磁盘文件数据恢复案例

    服务器数据恢复—误删除KVM虚拟机的数据恢复案例

    1台服务器,Linux操作系统+EXT4文件系统,部署了数台KVM虚拟机,每台虚拟机包含一个qcow2格式的磁盘文件,和一个raw格式的磁盘文件。 工作人员操作失误删除了3台服务器
    的头像 发表于 06-17 15:10 351次阅读
    服务器数据恢复—误删除<b class='flag-5'>KVM</b><b class='flag-5'>虚拟机</b>的数据恢复案例

    虚拟机数据恢复—KVM虚拟机被误删除的数据恢复案例

    虚拟机数据恢复环境: Linux操作系统服务器,EXT4文件系统。服务器中有数台KVM虚拟机虚拟机1:主数据库服务器 虚拟磁盘
    的头像 发表于 08-07 13:33 447次阅读
    <b class='flag-5'>虚拟机</b>数据恢复—<b class='flag-5'>KVM</b><b class='flag-5'>虚拟机</b>被误删除的数据恢复案例

    Ubuntu系统的虚拟机安装步骤 如何在Ubuntu系统编译软件

    Ubuntu系统安装虚拟机的步骤 在Ubuntu系统安装虚拟机是一个相对简单的过程,通常可以通过安装VirtualBox或KVM
    的头像 发表于 12-12 14:36 202次阅读