LXR | KVM | PM | Time | Interrupt | Systems Performance | Bootup Optimization

QEMU上RISC-V架构[U-Boot-OpenSBI-OPTEE-Linux]:环境搭建、启动流程概述、运行流程概述

1. 编译运行Kernel和OPTEE

参考文档《 OPTEE_00_01 - OP-TEE support - Home - RISE Project Confluence Wiki (atlassian.net)》。

下载和编译代码:

git clone https://gitlab.com/riseproject/riscv-optee/buildroot.git -b dev-optee-mpxy
cd buildroot
make qemu_riscv64_virt_optee_defconfig
make

 基于QEMU运行Linux和OPTEE:

./output/images/start-qemu.sh

也即执行如下命令:

qemu-system-riscv64 -M virt -cpu rv64,zkr=on \--指定CPU类型为 RISC-V 64位,并启用了zkr扩展(一个RISC-V扩展,用于支持Krentix内核)。
  -dtb qemu_rv64_virt_domain.dtb \
  -m 4096 -smp 2 \
  -semihosting-config enable=on,target=native \--启用半主机(semihosting)功能,允许虚拟机调用宿主机(host)的系统服务。
  -serial tcp:127.0.0.1:64320,server \--将虚拟机的串行端口重定向到TCP服务器,监听本地IP地址127.0.0.1的64320端口。
  -bios u-boot-spl \--指定第一阶段引导加载程序(Secondary Program Loader),这里是U-Boot的SPL(小型引导加载程序)。
  -device loader,file=u-boot.itb,addr=0x80200000 \--添加一个虚拟设备,加载U-Boot的ITB(Image Type Blob)文件,并将其映射到虚拟内存地址 `0x80200000`。
  -device virtio-blk-device,drive=hd0 \--添加一个虚拟的VirtIO块设备,用于模拟硬盘驱动器。
  -drive format=raw,file=sdcard.img,id=hd0 \--指定一个虚拟硬盘驱动器,使用原始格式的镜像文件sdcard.img。
  -device virtio-net-pci,netdev=net0 \--添加一个虚拟的VirtIO网络设备。
  -netdev user,id=net0,hostfwd=tcp::2200-:22 \--创建一个用户模式的网络设备,并设置端口转发,将宿主机的2200端口转发到虚拟机的22端口(通常用于SSH)。
  -nographic

 在另一个终端连接到Linux:

# launch another terminal and connect to normal world
telnet localhost 64320

执行xtest测试:

# run 'xtest' or 'optee_example*' in the shell

左边窗口是QEMU shell,显示OPTEE日志;右边窗口Linux shell:

2 启动流程

对于2个Hart的OpenSBI+OPTEE+U-BOot启动流程如下:

RISC-V下SPL->OpenSBI-OPTEE-Linux启动流程如下:

  1. M-Mode:u-boot-spl加载u-boot.itb文件,启动OpenSBI。
  2. M-Mode->S-Mode:OpenSBI通过mret跳转到OPTEE执行,启动TOS。
  3. S-Mode->M-Mode:OPTEE启动完后,通过ecall返回到执行OpenSBI。
  4. M-Mode->S-Mode:OpenSBI通过mre启动u-boot。
  5. S-Mode:u-boot加载Kernel镜像,跳转到Kernel执行。
  6. S-Mode->U-Mode:Kernel加载rootfs,启动rootfs的init进程开启用户空间。

2.1 镜像组成

u-boot-spl作为bios,负责加载并加些u-boot.itb。

u-boot.itb根据binman.dts生成,包括:u-boot-nodtb.bintee.binfw_dynamic.bin

/dts-v1/;

/ {
    timestamp = <0x66da6580>;
    description = "Configuration to load OpenSBI before U-Boot";
    #address-cells = <0x02>;

    images {

        uboot {
            description = "U-Boot";
            type = "standalone";
            os = "U-Boot";
            arch = "riscv";
            compression = "none";
            load = <0x00 0x81200000>;
            data = <...>;
        };

        tee {
            description = "OP-TEE";
            type = "tee";
            arch = "riscv";
            compression = "none";
            os = "tee";
            load = <0x00 0xf1000000>;
            data = [...];
        };

        opensbi {
            description = "OpenSBI fw_dynamic Firmware";
            type = "firmware";
            os = "opensbi";
            arch = "riscv";
            compression = "none";
            load = <0x00 0x80100000>;
            entry = <0x00 0x80100000>;
            data = <...>;
        };
    };

    configurations {
        default = "conf-1";

        conf-1 {
            description = "NAME";
            firmware = "opensbi";
            loadables = "uboot\0tee";
        };
    };
};

qemu_rv64_virt_domain.dtb是Linux kernel DTB文件。

sdcard.img包括了rootfs,以及存放于boot目录下的extlinux.conf和Image文件。

在rootfs编译完成后:

  • extlinux.conf被overlay到rootfs中的boot目录中。
  • 拷贝Image到rootfs的boot目录中。

然后对rootfs内容生成rootfs.ext2。rootfs.ext2经过genimage.sh根据genimage_sdcard.cfg配置生成sdcard.img。

extlinux.conf中定义了U-Boot启动Kernel的行为:

TIMEOUT 5
DEFAULT qemu_riscv64_optee
LABEL qemu_riscv64_optee
  kernel /boot/Image
  append console=ttyS0,115200n8 earlycon=sbi rootwait root=/dev/vda1 rw

2.2 spl解析OpenSBI/OPTEE/U-Boot

spl中解析u-boot.itb文件:

  • 从FIT中解析出、tee、U-Boot镜像参数。
  • 将OPTEE、U-Boot参数附加到fdt中,所以在OpenSBI中可以获取OPTEE启动数据。
  • 跳转到OpenSBI中执行。
spl_ram_load_image
  spl_load_simple_fit
    spl_simple_fit_read
      spl_ram_load_read--使用特定设备读取手段,将FIT读取到内存中。
    spl_simple_fit_parse
    spl_fit_get_image_node
    load_simple_fit--将FIT中数据读取到FIT中load地址。
      fit_image_verify_with_data--根据需要进行验签。
      gunzip/image_decomp--根据需要进行解压。
    spl_fit_record_loadable--将FIT中解析的内容(arch、os、type、size、entry、load)写入到fit-images下的每个镜像中。

附加的dtb内容如下:

    fit-images {
        tee {
            arch = "riscv";
            os = "tee";
            type = "tee";
            size = <0x000922da>;
            load = <0x00000000 0xf1000000>;
        };
        uboot {
            arch = "riscv";
            os = "U-Boot";
            type = "standalone";
            size = <0x00099760>;
            load = <0x00000000 0x81200000>;
        };
    };

spl启动OpenSBI流程参考《SPL到OpenSBI》。

2.3 OpenSBI启动OPTEE

2.3.1 OpenSBI mpxy ecall处理

Trap处理,以:

_trap_handler
  TRAP_CALL_C_ROUTINE
    sbi_trap_handler
     sbi_ecall_handler
      sbi_ecall_find_extension--根据extension_id找到对应的struct sbi_ecall_extension。
      --调用struct sbi_ecall_extension->handle()函数。
      --将返回值写入struct sbi_trap_context->regs的a0和a1中。
    sbi_trap_set_context

将ecall_mpxy放到sbi_ecall_exts中,当S-Mode发起mpxy ecall即调用sbi_ecall_mpxy_handler:

struct sbi_ecall_extension ecall_mpxy = {
    .extid_start        = SBI_EXT_MPXY,
    .extid_end        = SBI_EXT_MPXY,
    .register_extensions    = sbi_ecall_mpxy_register_extensions,
    .handle            = sbi_ecall_mpxy_handler,
};

sbi_ecall_mpxy_handler处理如下funcid:

sbi_ecall_mpxy_handler
  SBI_EXT_MPXY_SET_SHMEM
    sbi_mpxy_set_shmem
  SBI_EXT_MPXY_READ_ATTRS
    sbi_mpxy_read_attrs
  SBI_EXT_MPXY_WRITE_ATTRS
    sbi_mpxy_write_attrs
  SBI_EXT_MPXY_SEND_MSG_WITH_RESP
    sbi_mpxy_send_message
  SBI_EXT_MPXY_SEND_MSG_NO_RESP
    sbi_mpxy_send_message
  SBI_EXT_MPXY_GET_NOTIFICATION_EVENTS
    sbi_mpxy_get_notification_events

2.3.2 OpenSBI启动流程

OpenSBI平台相关操作函数集:

const struct sbi_platform_operations platform_ops = {
    .cold_boot_allowed    = generic_cold_boot_allowed,
...
    .mpxy_init        = fdt_mpxy_init,
    .vendor_ext_check    = generic_vendor_ext_check,
    .vendor_ext_provider    = generic_vendor_ext_provider,
};

struct sbi_platform platform = {
    .opensbi_version    = OPENSBI_VERSION,
    .platform_version    =
        SBI_PLATFORM_VERSION(CONFIG_PLATFORM_GENERIC_MAJOR_VER,
                     CONFIG_PLATFORM_GENERIC_MINOR_VER),
...
    .platform_ops_addr    = (unsigned long)&platform_ops
};

OpenSBI启动流程中mpxy处理包括:

  • 初始化mpxy。
  • 注册mpxy相关ecall。
sbi_init
  init_coldboot
    sbi_domain_init
      sbi_domain_register--注册root Domain。
    sbi_domain_finalize--
      sbi_platform_domains_init
        generic_domains_init--调用平台domains_init()函数。
          fdt_domains_populate--根据fdt初始化Domain。
            fdt_iterate_each_domain--找到opensbi-domain节点,解析其中的Domain并注册。
              __fdt_parse_domain
                sbi_domain_register--注册trusted-domain和untrusted-domain分别对应OPTEE和U-Boot。
      --如果当前Hart是Cold Hart,那么切换scratch的next_addr/next_mode/next_arg1参数为trusted-domain参数。后面会优先启动OPTEE。
    sbi_mpxy_init
      sbi_platform_mpxy_init
        --调用struct sbi_platform的mpxy_init函数。
          fdt_mpxy_init--遍历fdt_mpxy_drivers,匹配后执行init初始化函数。
            mpxy_opteed_init--调用fdt_mpxy_opteed的init函数

    sbi_ecall_init--遍历sbi_ecall_exts,并调用其register_extensions()函数。
    sbi_boot_print_general--显示Platform、Firmware、SBI信息。
    sbi_boot_print_domains--显示所有Domain信息。
    sbi_boot_print_hart--显示当前Hart信息。
    sbi_hsm_hart_start_finish
      sbi_hart_switch_mode
        mret--返回到OPTEE。

在init_coldboot中打印的Platform、Firmware、SBI、Domain、Hart信息如下:

Platform Name             : riscv-virtio,qemu--表明这是一个使用QEMU模拟的RISC-V平台,具有虚拟I/O设备。
Platform Features         : medeleg--支持中间异常委托(medeleg属性)。
Platform HART Count       : 2--平台有2个硬件线程(HART)。
Platform IPI Device       : aclint-mswi--使用`aclint-mswi`设备处理中断处理器间的中断(IPI)。
Platform Timer Device     : aclint-mtimer @ 10000000Hz--使用`aclint-mtimer`设备作为计时器,频率为10MHz。
Platform Console Device   : uart8250--控制台设备使用的是`uart8250`UART控制器。
Platform HSM Device       : ---
Platform PMU Device       : ---
Platform Reboot Device    : syscon-reboot--使用`syscon-reboot`设备进行系统重启。
Platform Shutdown Device  : syscon-poweroff--使用`syscon-poweroff`设备进行系统关闭。
Platform Suspend Device   : ---
Platform CPPC Device      : ---
Platform RAS Device       : ---
Firmware Base             : 0x80100000--固件基地址。
Firmware Size             : 349 KB--固件大小。
Firmware RW Offset        : 0x40000--固件可读写区域的偏移量。
Firmware RW Size          : 93 KB--固件可读写区域的大小。
Firmware Heap Offset      : 0x4e000--固件堆的偏移量。
Firmware Heap Size        : 37 KB (total), 2 KB (reserved), 17 KB (used), 17 KB (free)--固件堆的总大小、保留大小、已使用大小和空闲大小。
Firmware Scratch Size     : 4096 B (total), 416 B (used), 3680 B (free)--固件临时存储区的总大小、已使用大小和空闲大小。
Runtime SBI Version       : 2.0--SBI(SupervisorBinaryInterface)版本。

Domain0 Name              : root--域0的名称,运行OpenSBI镜像。
Domain0 Boot HART         : 1--域0启动时使用的HART编号。
Domain0 HARTs             : H1 0H1 ,1H1--域0中包含的HART编号。
Domain0 Region00          : 0x0000000000100000-0x0000000000100fff H1 M: H1 (IH1 ,RH1 ,WH1 ) H1 S/U: H1 (RH1 ,WH1 )--描述域0中的内存区域,包括起始地址、结束地址、权限和访问控制。
Domain0 Region01          : 0x0000000010000000-0x0000000010000fff H1 M: H1 (IH1 ,RH1 ,WH1 ) H1 S/U: H1 (RH1 ,WH1 )
Domain0 Region02          : 0x0000000002000000-0x000000000200ffff H1 M: H1 (IH1 ,RH1 ,WH1 ) H1 S/U: H1 ()
Domain0 Region03          : 0x0000000080140000-0x000000008015ffff H1 M: H1 (RH1 ,WH1 ) H1 S/U: H1 ()
Domain0 Region04          : 0x0000000080100000-0x000000008013ffff H1 M: H1 (RH1 ,XH1 ) H1 S/U: H1 ()
Domain0 Region05          : 0x000000000c400000-0x000000000c5fffff H1 M: H1 (IH1 ,RH1 ,WH1 ) H1 S/U: H1 (RH1 ,WH1 )
Domain0 Region06          : 0x000000000c000000-0x000000000c3fffff H1 M: H1 (IH1 ,RH1 ,WH1 ) H1 S/U: H1 (RH1 ,WH1 )
Domain0 Region07          : 0x0000000000000000-0xffffffffffffffff H1 M: H1 () H1 S/U: H1 (RH1 ,WH1 ,XH1 )
Domain0 Next Address      : 0x0000000081200000--域0下一个执行地址,即U-Boot地址。
Domain0 Next Arg1         : 0x000000008129d048--域0下一个执行参数地址。
Domain0 Next Mode         : H1 S-mode--域0下一个执行模式(S-mode,即Supervisor模式)。
Domain0 SysReset          : yes--域0支持系统重置。
Domain0 SysSuspend        : yes--域0支持系统挂起。

Domain1 Name              : trusted-domain--运行OPTEE镜像。
Domain1 Boot HART         : 1
Domain1 HARTs             : H1 0*H1 ,1*H1 
Domain1 Region00          : 0x0000000002000000-0x000000000200ffff H1 M: H1 (IH1 ,RH1 ,WH1 ) H1 S/U: H1 ()
Domain1 Region01          : 0x0000000080140000-0x000000008015ffff H1 M: H1 (RH1 ,WH1 ) H1 S/U: H1 ()
Domain1 Region02          : 0x0000000080100000-0x000000008013ffff H1 M: H1 (RH1 ,XH1 ) H1 S/U: H1 ()
Domain1 Region03          : 0x0000000000000000-0xffffffffffffffff H1 M: H1 (RH1 ,WH1 ,XH1 ) H1 S/U: H1 (RH1 ,WH1 ,XH1 )
Domain1 Next Address      : 0x00000000f1000000--域1下一个执行地址,即OPTEE地址。
Domain1 Next Arg1         : 0x000000008129d048
Domain1 Next Mode         : H1 S-mode
Domain1 SysReset          : no
Domain1 SysSuspend        : no

Domain2 Name              : untrusted-domain--运行U-Boot镜像。
Domain2 Boot HART         : 0
Domain2 HARTs             : H1 0H1 ,1H1 
Domain2 Region00          : 0x0000000002000000-0x000000000200ffff H1 M: H1 (IH1 ,RH1 ,WH1 ) H1 S/U: H1 ()
Domain2 Region01          : 0x0000000080140000-0x000000008015ffff H1 M: H1 (RH1 ,WH1 ) H1 S/U: H1 ()
Domain2 Region02          : 0x0000000080100000-0x000000008013ffff H1 M: H1 (RH1 ,XH1 ) H1 S/U: H1 ()
Domain2 Region03          : 0x00000000f1000000-0x00000000f1ffffff H1 M: H1 () H1 S/U: H1 ()
Domain2 Region04          : 0x0000000000000000-0xffffffffffffffff H1 M: H1 (RH1 ,WH1 ,XH1 ) H1 S/U: H1 (RH1 ,WH1 ,XH1 )
Domain2 Next Address      : 0x0000000081200000--域2下一个执行地址,即U-Boot地址。
Domain2 Next Arg1         : 0x000000008129d048
Domain2 Next Mode         : H1 S-mode
Domain2 SysReset          : no
Domain2 SysSuspend        : no

Boot HART ID              : 1--启动时使用的HART编号。
Boot HART Domain          : trusted-domain--启动时HART所在的域。
Boot HART Priv Version    : v1.12--启动时HART的私有版本。
Boot HART Base ISA        : rv64imafdch--启动时HART支持的基础ISA扩展集。
Boot HART ISA Extensions  : sstc,zicntr,zihpm,zkr,zicboz,zicbom,sdtrig,svadu--启动时HART支持的ISA扩展。
Boot HART PMP Count       : 16--启动时HART支持的物理内存保护(PMP)条目数。
Boot HART PMP Granularity : 2 bits--启动时HARTPMP的粒度。
Boot HART PMP Address Bits: 54--启动时HARTPMP支持的地址位数。
Boot HART MHPM Info       : 16 (0x0007fff8)--启动时HART支持的机器性能监控器(MHPM)计数器数量和位掩码。
Boot HART Debug Triggers  : 2 triggers--启动时HART支持的调试触发器数量。
Boot HART MIDELEG         : 0x0000000000001666--启动时HART支持的中间异常委托位掩码。
Boot HART MEDELEG         : 0x0000000000f0b509--启动时HART支持的机器异常委托位掩码。

mpxy初始化

fdt中包含如下mpxy数据:

    sbi-mpxy-opteed {
        opensbi-domain-instance = <0x00000002>;
        riscv,sbi-mpxy-channel-id = <0x00000002>;
        compatible = "riscv,sbi-mpxy-opteed";
    };

fdt_mpxy_drivers中的fdt_mpxy_opteed为:

static const struct fdt_match mpxy_opteed_match[] = {
    { .compatible = "riscv,sbi-mpxy-opteed", .data = NULL },
    {},
};

struct fdt_mpxy fdt_mpxy_opteed = {
    .match_table = mpxy_opteed_match,
    .init = mpxy_opteed_init,
};

所以匹配fdt_mpxy_drivers中的fdt_mpxy_opteed,执行mpxy_opteed_init():

mpxy_opteed_init
  opteed_domain_setup
  sbi_mpxy_register_channel--获取channel id后,初始化struct sbi_mpxy_channel,然后注册。
    mpxy_opteed_send_message--OPTEE调用ecall通过channel发送消息,OpenSBI接收处理。
      OPTEED_MSG_COMMUNICATE
        sbi_ecall_tee_domain_enter
          sbi_domain_context_enter--进入trusted-domain。
      OPTEED_MSG_COMPLETE
        sbi_ecall_tee_domain_exit
          sbi_domain_context_exit--进入untrusted-domain。

2.4 OPTEE启动流程

optee启动流程:

_start
    reset_primary
    thread_init_thread_core_local
    plat_primary_init_early
    console_init
    core_init_mmu_map
    boot_init_primary_early
    boot_init_primary_late
      init_external_dt
      discover_nsec_memory
      update_external_dt
      boot_primary_init_intc
      init_tee_runtime
      call_finalcalls
    wait_secondary
    thread_clr_boot_thread
    thread_return_to_udomain
      thread_return_to_udomain_by_mpxy
        sbi_mpxy_send_message_withresp
          sbi_ecall--发起extension id为SBI_EXT_MPXY,func id为SBI_EXT_MPXY_SEND_MSG_WITH_RESP,msg id为OPTEED_MSG_COMPLETE的ecall。进入OpenSBI调用sbi_ecall_tee_domain_exit()函数跳转到untrusted domain。
  reset_secondary
    boot_init_secondary
      init_secondary_helper
        

2.5 OpenSBI启动U-Boot流程

OpenSBI收到OPTEE的ecall调用进入OPTEED_MSG_COMPLETE处理流程:

sbi_ecall_tee_domain_exit
  sbi_domain_context_exit--找到当前Hart的struct sbi_context,然后配置下一个Domain的struct sbi_context。
    switch_to_next_domain_context--保存当前CSR等信息到struct sbi_context中;根据target_dom的boot_hartid/next_arg1/next_addr/next_mode跳转。
      sbi_hart_switch_mode
        mret--根据设置的next_addr跳转到指定Mode,比如进入U-Boot。

2.5 U-Boot启动Kernel流程

参考《u-boot到linux》。

3 Linux安全应用和OPTEE交互流程

 Linux下OPTEE安全应用和OPTEE TA交互流程如下:

 

posted on 2024-09-27 23:59  ArnoldLu  阅读(109)  评论(0编辑  收藏  举报

导航