驱动开发注意事项
本节主要介绍 ECS 容器版本的 BSP 编程注意事项。
开发须知
开发者应当参照如下表格的内容,针对不同的 CPU 架构来完成与之相适配的地址映射操作。
名称 | 架构 |
---|---|
VAPP 地址映射 | 全平台 |
KMOD 地址映射 | 全平台 |
Kernel Mirror 映射 | AARCH64、MIPS、CSKY、RISC-V、x86、x86-64、LoongArch64 |
PAGETAB 地址映射 | AARCH64、MIPS、CSKY、RISC-V、x86、x86-64、LoongArch64 |
地址映射
VAPP 地址映射
容器版本的应用程序虚拟起始地址不得小于 LW_CFG_VMM_PGD_SIZE
,建议设定为 LW_CFG_VMM_PGD_SIZE
。
KMOD 地址映射
容器版本的 BSP 中,供内核模块使用的物理地址空间需要单独划分区域,如下所示,增加了 KMOD 物理内存区域。
LW_MMU_PHYSICAL_DESC _G_physicalDesc[] = {
#if LW_CFG_CONTAINER_EN > 0
{ /* KMOD 内存 */
BSP_CFG_KMOD_BASE,
BSP_CFG_KMOD_BASE,
BSP_CFG_KMOD_SIZE,
LW_PHYSICAL_MEM_KMOD
},
#endif /* LW_CFG_CONTAINER_EN */
Kernel Mirror 映射
对于 AARCH64、MIPS、CSKY、RISC-V、x86、x86-64、LoongArch64 平台,以及当 ARM 使能 LPAE 功能时,需要增加 Kernel Mirror 区域的虚拟空间映射。因为在这些处理器平台中,MMU 页表的权限配置不能有效区分内核 DATA 段在内核态可读写,但在用户态只读的场景,因此需要单独映射 Kernel Mirror 段为用户态进程提供对内核 DATA 段的只读权限。
Kernel Mirror 实际是对 Kernel 的 Text 与 Data 段分别在宿主页表和容器页表内进行映射,但二者映射时的权限不同。增加方式如下所示:
- 不使用设备树的修改方法
在 BSP 的 bspmap.h 中增加 Kernel Mirror 区域的虚拟空间映射。
LW_MMU_VIRTUAL_DESC _G_virtualDesc[] = {
#if LW_CFG_CONTAINER_EN > 0
#if LW_CFG_KERN_MIRROR_MAP_EN > 0
{ /* 内核平板映射虚拟空间 */
BSP_CFG_TEXT_BASE,
BSP_CFG_TEXT_SIZE + BSP_CFG_DATA_SIZE,
LW_VIRTUAL_MEM_KERN_FLAT
},
{ /* 内核镜像映射虚拟空间 */
BSP_CFG_VAPP_START + BSP_CFG_VAPP_SIZE,
BSP_CFG_TEXT_SIZE + BSP_CFG_DATA_SIZE,
LW_VIRTUAL_MEM_KERN_MIRROR
},
#endif /* LW_CFG_KERN_MIRROR_MAP_EN */
#endif /* LW_CFG_CONTAINER_EN */
注意:
- 如果 BSP 中有多个 DATA 段,则需要为每个 DATA 段添加 Kernel Mirror 映射,且偏移需要保持一致。
- 例如存在两个 DATA 段分别为 DATA1 与 DATA2, kernel Mirror 映射时虚拟 DATA2 与虚拟 DATA1 的偏移应等于 DATA2 相对 DATA1 的偏移,即
VDATA2_BASE - VDATA1_BASE = DATA2_BASE - DATA1_BASE
。- 内核平板映射虚拟空间地址应为 TEXT 段基地址,空间大小为 TEXT 段和 DATA 段大小总和。
- VAPP_START 为虚拟空间 APP 起始地址。
- 使用设备树的修改方法
在设备树的 memory
节点中增加子节点 virtual_kern_flat
和 virtual_kern_mirror
用于表示 Kernel Mirror 区域虚拟空间映射,该子节点包含虚拟空间地址和虚拟空间大小。
以 AARCH64 平台为例:
虚拟空间映射地址与长度的配置与非设备树版本相同,需要根据宏定义计算出具体的地址与大小。例如内核平板映射虚拟空间地址 BSP_CFG_TEXT_BASE
为 0x9500000,虚拟空间大小 BSP_CFG_TEXT_SIZE + BSP_CFG_DATA_SIZE
为 0x21000000。内核镜像映射虚拟空间地址 BSP_CFG_VAPP_START + BSP_CFG_VAPP_SIZE
为 0x404 00000000,虚拟空间大小 BSP_CFG_TEXT_SIZE + BSP_CFG_DATA_SIZE
为 0x21000000。
{
#address-cells = <2>;
#size-cells = <2>;
...
memory {
device_type = "memory";
...
virtual_kern_flat = <0x00000000 0x09500000 0x00000000 0x21000000>;
virtual_kern_mirror = <0x00000404 0x00000000 0x00000000 0x21000000>;
};
}
注意:
在
reg
属性中,地址和大小应按照#address-cells
和#size-cells
的设置进行编码。在上述示例中使用了 2 个地址单元和 2 个大小单元(均为 32 位),因此 64 位虚拟空间地址和虚拟空间大小被拆分为两个 32 位的值进行表示。
PAGETAB 地址映射
为了支持运行更多的容器,BSP 可以单独创建页表堆用于分配容器页表空间。
以内存大小为 16GB 的 x86_64 设备为例,对 BSP 增加页表堆机制,主要修改如下:
- bspx64/config.h 文件中增加 2.2GB 页表堆配置。
#define BSP_CFG_PAGE_TAB_SIZE ((2LU * 1024 + 200LU) * 1024 * 1024)
- bspInit.c 中增加页表堆全局变量。
#if LW_CFG_CONTAINER_EN > 0 PVOID _G_pvPagetabHeapMem = LW_NULL; size_t _G_stPagetabHeapSize = 0; #endif /* LW_CFG_CONTAINER_EN */
- bspMultiBootParse() 中在 DATA 段后增加页表段。
/* * HEAP */ i++; _G_physicalDesc[i].PHYD_ulPhyAddr = (addr_t)&__e_kernel; _G_physicalDesc[i].PHYD_ulVirMap = (addr_t)&__e_kernel; _G_physicalDesc[i].PHYD_stSize = stHeapMemSize; _G_physicalDesc[i].PHYD_uiType = LW_PHYSICAL_MEM_DATA; *ppvKernelHeapMem = (PVOID)_G_physicalDesc[i].PHYD_ulPhyAddr; *pstKernelHeapSize = (size_t)_G_physicalDesc[i].PHYD_stSize; /* * PAGE Table */ i++; _G_physicalDesc[i].PHYD_ulPhyAddr = (addr_t)&__e_kernel + stHeapMemSize; _G_physicalDesc[i].PHYD_ulVirMap = (addr_t)&__e_kernel + stHeapMemSize; _G_physicalDesc[i].PHYD_stSize = BSP_CFG_PAGE_TAB_SIZE; _G_physicalDesc[i].PHYD_uiType = LW_PHYSICAL_MEM_PAGETAB; _G_pvPagetabHeapMem = (PVOID)_G_physicalDesc[i].PHYD_ulPhyAddr; _G_stPagetabHeapSize = (size_t)_G_physicalDesc[i].PHYD_stSize;
以 ARM64 设备为例,对 BSP 增加页表堆机制,主要修改如下
- bsp/config.h 文件中增加 1GB 页表堆配置和页表基地址配置。
#define BSP_CFG_PAGETAB_SIZE (1LU * 1024 * 1024 * 1024) #define BSP_CFG_PAGETAB_BASE (0x40000000)
- 在 bsp/bspMap.h 中的 _G_physicalDesc 变量中增加如下页表段配置:
#if LW_CFG_CONTAINER_EN > 0 { /* 容器版本页表内存 */ BSP_CFG_PAGETAB_BASE, BSP_CFG_PAGETAB_BASE, BSP_CFG_PAGETAB_SIZE, LW_PHYSICAL_MEM_PAGETAB }, #endif
- bspInit.c 中增加页表堆全局变量。
#if LW_CFG_CONTAINER_EN > 0 PVOID _G_pvPagetabHeapMem = (PVOID)BSP_CFG_PAGETAB_BASE; size_t _G_stPagetabHeapSize = (size_t)BSP_CFG_PAGETAB_SIZE; #endif /* LW_CFG_CONTAINER_EN */
以 mips64 设备为例, mips64 设备不同于 x86 和 ARM ,其内存空间设置需要满足固定地址区间,对 BSP 增加页表堆机制主要修改参考如下:
- bsp/config.h 文件中增加 xkphys 段。
#define BSP_CFG_XKPHYS_BASE 0x9800000000000000
- bsp/config.h 文件中调整 APP 空间大小,预留出 PAGETABLE 空间,如下示例,PAGETABLE 空间大小为 256MB,即 0x10000000。
#define BSP_CFG_APP_SIZE (0x00000001d0000000 - 0x10000000)
- bsp/config.h 文件中增加页表堆配置和页表基地址配置。
#define BSP_CFG_PAGETAB_SIZE (1LU * 256 * 1024 * 1024) #define BSP_CFG_PAGETAB_BASE (BSP_CFG_APP_BASE + BSP_CFG_APP_SIZE)
- bsp/bspInit.c 文件中增加页表堆全局变量。
#if LW_CFG_CONTAINER_EN > 0 PVOID _G_pvPagetabHeapMem = (PVOID)(BSP_CFG_XKPHYS_BASE + BSP_CFG_PAGETAB_BASE); size_t _G_stPagetabHeapSize = (size_t)BSP_CFG_PAGETAB_SIZE; #endif /* LW_CFG_CONTAINER_EN */
- bsp/bsMap.h 文件中的 _G_physicalDesc 变量中增加如下页表段配置。
#if LW_CFG_CONTAINER_EN > 0 { /* 容器版本页表内存 */ BSP_CFG_PAGETAB_BASE, BSP_CFG_XKPHYS_BASE + BSP_CFG_PAGETAB_BASE, BSP_CFG_PAGETAB_SIZE, LW_PHYSICAL_MEM_PAGETAB }, #endif
注意:
- BSP 中若没有配置页表堆,页表空间将直接使用内核堆。
- 增加页表段后,DMA、APP 段空间需要根据实际情况重新计算。
- 地址重新调整后需要注意当前 TEXT、DATA、DMA 段地址范围不能与虚拟地址空间产生冲突,若有重叠部分需要进一步调整。
配置成功后,通过 free
命令可显示 PAGETABLE 空间配置信息,如图。
内存分配
因考虑到云原生环境中需要运行 JSRE,此时需要注意系统堆的分配,不应将系统堆分配的过小,一般应在总内存的 25% 以上。
注意事项
在中断中不能访问应用空间内存
因为在系统陷入中断时,不能确认是否已经进行过容器的切换。若进行过容器的切换,则中断再访问切换前的容器内应用地址空间时,会出现访问错误。所以驱动开发时不应将应用中的地址空间直接传递到中断服务函数中使用。
驱动创建的线程增加全局对象属性
驱动内创建的线程应含
LW_OPTION_OBJECT_GLOBAL
属性,否则可能出现如下错误情形:线程的创建是由容器内进程调用的。当容器内进程消亡时,非
LW_OPTION_OBJECT_GLOBAL
属性的线程会在消亡过程中被回收,此回收过程在内核中进行,内核中因无法访问此进程所在的容器上下文,导致错误。