ATF原生篇(二十):Components-翻译(XLAT)表库

翻译(XLAT)表库

本文档描述了Trusted Firmware-A(TF-A)使用的翻译表库(版本2)的设计。

  • 该库提供了API,用于根据内存布局的描述创建页表
  • 以及设置与内存管理单元(MMU)相关的系统寄存器,
  • 并执行所需的翻译后备缓冲区(TLB)维护操作。

更具体地说,这个库旨在支持的一些用例是:

  • 根据内存布局的描述静态分配转换表并填充它们(在运行时)。存储器布局通常由平台端口提供为存储器区域的列表;
  • 支持生成与库代码执行的异常级别不同的翻译制度相关的翻译表;
  • 支持动态映射和取消映射区域,即使MMU处于打开状态。这可以用于临时映射一些内存区域,并在以后不再需要时取消映射;
  • 支持非一致(non-identity)的虚拟到物理的映射,以压缩虚拟地址空间;
  • 支持在运行时更改内存区域的内存属性

关于版本1、版本2和MPU库

本文档重点介绍库的第2版,其源代码位于lib/xlat_tables_v2目录中。库的版本1仍然可以在“lib/xlat_tables”目录中找到,但它的灵活性较低,不支持动态映射lib/xlat_mpu,它等效地配置Arm的mpu,也在这里进行了说明。

lib/xlat_mpu是实验性的,这意味着它的API可能会改变。目前,它努力实现与xlat_tables_v2的一致性和代码重用。未来的版本可能更特定于MPU(例如,删除所有提到的虚拟地址)。尽管潜在的错误修复将应用于所有版本的xlat_*libs,但未来的功能增强将集中在版本2上,可能不会向后移植到版本1和MPU版本。

因此,建议使用版本2,尤其是对于新的平台端口(除非平台使用MPU)。

然而,请注意,版本2和MPU版本仍在积极开发中,目前还不稳定。因此,兼容性中断可能从这一点开始,除非另有说明,否则本文档将隐式引用库的版本2。

设计概念和接口

本节介绍了翻译表库中使用的一些关键概念和数据结构。

1-mmap regions

“mmap_region”是一种抽象、简洁的方式来表示要映射的内存区域。它是库的关键接口之一。通过以下方式识别:

    • its physical base address;
    • its virtual base address;
    • its size;
    • its attributes;
    • its mapping granularity (optional).(粒度)

请参阅xlat_tables_v2.h``中的struct mmap_region``类型。

用户通常会提供一个要映射的mmap区域列表,并让库将其转换为一组翻译表。因此,库可能会创建新的翻译表,更新或拆分现有的翻译表。

区域属性指定内存的类型(例如设备或缓存的正常内存)以及内存访问权限(只读或读写、可执行或不可执行、安全或不安全,等等)。在EL1&0翻译制度的情况下,属性还指定该区域是用户区域(EL0)还是特权区域(EL1)。请参阅xlat_tables_v2.h``中的MT_xxx`定义。请注意,对于EL1&0翻译机制,同时为EL1和EL0设置“从不执行”属性。
粒度控制映射区域时要降低到的转换表级别。例如,假设MMU已配置为使用4KB颗粒大小,库可以使用以下两个选项之一映射2MB内存区域:

扫描二维码关注公众号,回复: 15169491 查看本文章
  • -使用单个二级翻译表条目;
  • -使用到第三级翻译表的第二级中间条目(该表包含512个条目,每个条目映射4KB)。

第一种解决方案可能需要更少的转换表,因此可能需要更少内存。然而,如果这个2MB区域的一部分后来用不同的内存属性重新映射,那么库可能需要拆分现有的页面表来细化映射。如果这里使用了一个二级条目,则需要动态分配一个三级表,并修改二级表以指向这个新的三级表。这在运行时会带来性能成本。

如果用户预先知道可能会发生这样的重新映射操作,那么他们可能从一开始就对这个2MB区域强制执行4KB映射粒度;动态重新映射这些4KB页面中的一些页面就变成了一种轻量级操作。

区域的粒度是一个可选字段;如果没有指定,库将根据自己的意愿选择该区域的映射粒度(更多细节可在下面的“内存映射算法”部分找到)。

MPU库还使用“struct mmap_region”来指定翻译,但MPU的翻译仅限于指定有效地址和访问权限。如果请求的虚拟地址和物理地址不匹配,系统将死机。作为基于寄存器的确定性存储器参考时序,MPU硬件不涉及驻留在存储器中的转换表。

目前,MPU库也仅限于EL2的MPU翻译,而其他EL没有MMU翻译。然而,这些限制有望在未来的库版本中得到克服。

2-翻译上下文

库可以创建或修改与库代码执行的异常级别不同的翻译制度相关的翻译表。

例如,EL3软件(例如BL31)可能会使用该库来创建与S-EL1&0翻译制度相关的翻译表

这种灵活性来自于“翻译上下文”的使用。翻译上下文构成了图书馆用于跟踪给定翻译制度的一组翻译表的状态的信息超集。

库在内部分配一个默认的翻译上下文,该上下文属于当前异常级别的翻译机制。可以使用“REGISTER_XLAT_CONTEXT()”宏显式分配和初始化其他上下文。提供了单独的API来对默认翻译上下文或替代翻译上下文进行操作。
要注册翻译上下文,用户必须向库提供以下信息:

*一个名字。

由此产生的翻译上下文变量将在该名称之后调用,并将“_xlat_ctx”附加到该名称。例如,如果宏名称参数为“foo”,则上下文变量名称将为“foo_xlat_ctx”。

*要映射的最大“mmap”区域数。

应同时考虑静态和动态区域(如果适用)。

*要分配的子转换表的数量。

要为此上下文静态分配的转换表的数量,不包括始终分配的初始查找级别转换表。例如,如果初始查找级别为1,则此参数将指定为此上下文预先分配的2级和3级转换表的数量。

*虚拟地址空间的大小。

要使用此上下文映射的虚拟地址空间的大小(以字节为单位)。这将顺便确定初始查找级别转换表中的条目数量:库将分配映射整个虚拟地址空间所需的条目数量。

*物理地址空间的大小。
要使用此上下文映射的物理地址空间的大小(以字节为单位)。
默认翻译上下文是使用来自特定平台定义的信息(大部分)在内部初始化的:

-名称:硬编码为tf``;因此,默认上下文变量的名称为 ``tf_xlat_ctx``; -mmap区域数:``MAX_mmap_regions``; -子转换表的数量:``MAX_XLAT_tables``; -虚拟地址空间的大小:PLAT_VIRT_ADDR_space_size; -物理地址空间的大小:`PLAT_PHY_ADDR_space_size
有关这些宏的更多详细信息,请参阅:ref:`移植指南’。

3-静态和动态内存区域

该库可选地支持动态内存映射。可以使用“PLAT_XLAT_TABLES_DYNAMIC”平台构建标志启用此功能。
当启用动态内存映射时,库将mmap区域分类为静态动态

-静态区域在系统的使用寿命内是固定的。它们只能在创建和填充转换表之前尽早添加。他们
之后无法移除。

-动态区域可以随时添加或删除。

禁用动态内存映射功能时,仅存在静态区域。

动态存储器映射特征可以用于映射和取消映射瞬态存储器区域。当用户需要在一段固定的时间内访问一些内存时,这是很有用的,在此之后,内存可能会被丢弃和回收。例如,仅在系统初始化时启动时需要的内存区域,或者在正常世界和可信世界之间临时共享内存缓冲区。请
注意,由调用者来确保在添加或删除区域时不会同时访问这些区域。

尽管此功能提供了一定程度的动态内存分配,但这不允许在任意内存位置动态分配任意数量的内存。用户仍然需要在编译时声明这些分配的限制;库将拒绝任何不适合此预先分配的内存池的映射请求。

4-库API

此库公开的外部API在xlat_tables_v2.h头文件中声明并记录。这应该是获取有关此库提供的不同API的使用情况的信息。本节只是提供了一些额外的细节和说明。
尽管“mmap_region”结构是公开可见的类型,但不建议手动填充这些结构。相反,无论API期望在哪里类型为mmap_region_t``的函数参数,这些参数应使用MAP_region*()``辅助宏族构造。这是为了限制如果“mmap_region”结构类型在未来发展,则兼容性将中断。
“MAP_REGION()”和“MAP_REGION_FLAT()”宏不允许指定映射粒度,这使得库实现可以自由选择

但是,在需要特定粒度的情况下,可以使用“MAP_REGION2()”宏。强烈建议仅使用`MAP_REGION_FLAT()``来定义MPU库的区域。

正如本文前面所解释的,当禁用动态映射功能时,不存在动态区域的概念。从概念上讲,只有静态区域。出于这个原因(为了保持与库的版本1的向后兼容性),映射静态区域的API不嵌入与动态区域API(例如“mmap_add_dynamic_region()”)形成对比的是,它们的函数名称中的单词static

尽管静态和动态区域的定义不是基于MMU的状态,但两者在某种程度上仍然是相关的。静态区域只能在调用init_xlat_tables()``之前添加,并且必须在MMU仍处于关闭状态时调用initx_lat_tables()因此,一旦启用MMU,就无法添加静态区域。可以在MMU打开或关闭的情况下添加动态区域。在实践中,通常的呼叫流程如下所示:
#. MMU最初处于关闭状态。
#. 添加一些静态区域,添加一些动态区域。
#. 根据mmap区域列表初始化转换表(使用`init_xlat_tables*()``API之一)。
#. 此时,已无法再添加静态区域。动态区域仍然可以添加或删除。
#. 启用MMU。
#. 可以继续添加或删除动态区域。
由于静态区域是在引导时早期添加的,并且都在平台初始化代码的控制下,因此“mmap_add*()”系列API
预计不会失败。它们不会返回任何错误代码。

尽管如此,这些API将在更新翻译上下文结构之前预先检查是否可以成功添加区域。如果库检测到内存不足,无法满足请求,或者新区域将以无效方式与另一个区域重叠,或者遇到任何其他意外错误,它们将在UART上打印错误消息。

此外,当启用断言时(通常在调试构建中),将触发断言。否则,函数调用将直接返回,而不会添加有问题的内存区域。

5-库限制

动态区域不允许相互重叠。只要其中一个区域完全包含在另一个区域内,就允许静态区域重叠。这允许与库的版本1中以前的行为向后兼容。

实施细则

1-代码结构

图书馆分为4个模块:
-核心模块
提供库的主要功能,例如初始化转换表上下文和映射/取消映射内存区域。本模块提供了诸如“mmap_add_region_ctx”之类的函数,让调用者指定受其影响的翻译表上下文。
请参见`xlat_tables_core.c ``。

-活动上下文模块
实例化当前BL映像所使用的上下文,并提供助手对其进行操作,将其从代码的其余部分中抽象出来。
该模块提供诸如“mmap_add_region”之类的功能,这些功能直接影响使用它们的BL图像。
请参见`xlat_tables_context.c``。

-实用程序模块
提供其他功能,如翻译表的当前状态的调试打印,以及查询内存属性和修改内存属性的助手。
请参见`xlat_tables_utils.c``。

-建筑模块
提供依赖于当前执行状态(AArch32/AArch64)的函数,例如用于TLB无效的函数,设置MMU或计算物理地址空间大小。他们不需要翻译上下文。
请参见aarch32/xlat_tables_arch.c``和aarch64/xlat_tables_archi.c``。

2-从mmap区域到翻译表

翻译上下文包含一个“mmap_region_t”列表,该列表保存在任何给定时间映射的所有区域的信息。每当有映射(resp.unmap)内存区域的请求时,它都会被添加到“mmap_region_t”列表中(resp.removed from)。

mmap区域列表是表示内存布局的一种概念方式。在某个时刻,库必须将这些信息转换为实际的翻译表,以便编程到MMU中。

在调用init_xlat_tables() API之前,库仅作用于mmap区域列表。此时通过mmap_add*()API之一添加静态或动态区域不会以任何方式影响转换表,它们只会在内部mmap区域列表中注册。只有当用户调用“init_xlat_tables()”时,才会根据迄今为止注册的mmap区域列表在内存中填充转换表。这是一种优化,允许一次性创建初始翻译表集,而不必在禁用MMU时每次都对其进行编辑。

调用init_xlat_tables() API后,只能添加动态区域。对翻译表(以及mmap区域列表)的更改将立即生效。

3-内存映射算法

映射函数被实现为递归算法。然而,它受到翻译表的深度级别的限制(Armv8-A架构最多允许4个查找级别)。
默认情况下[#granularity]_,算法将尝试最小化为满足用户请求而创建的转换表的数量。它将倾向于使用尽可能大的块来映射一个区域,只有在绝对必要的情况下才会创建一个子表。这是为了减少固件的内存占用。
需要子表的最常见原因是特定映射需要更精细的粒度。错位区域还需要比用户最初预期的粒度更细的粒度,使用的内存比预期的要多得多。原因是所有级别的翻译都被限制为与该级别的块大小相同粒度的地址翻译。例如,对于4 KiB的页面大小,2级块条目只能转换到2 MiB的粒度。如果物理地址没有与2 MiB对齐,那么还需要额外的3级表。
请注意,并不是每个翻译级别都允许使用任何类型的描述符。根据页面大小的不同,转换的0级和1级可能只允许使用表描述符。如果块条目能够描述翻译,但该级别不允许块描述符,则必须使用表描述符,以及下一级别的附加表。
|路线示例|
mmap区域的排序方式简化了映射它们的代码。即使这种排序只对重叠的静态区域严格需要,但它也必须应用于动态区域,以始终保持所有区域的一致顺序。在映射每个新区域时,将检查转换表中的现有条目以确保一致性。有关正在使用的排序算法的更多详细信息,请参阅核心模块源代码中的注释。
这种映射算法不适用于MPU库,因为MPU硬件通过“基本”和“限制”(底部和顶部)地址直接映射区域。

4-TLB维护操作

库负责在需要时执行TLB维护操作。例如,当用户请求移除动态区域时,库会使与该区域相关联的所有TLB条目无效,以确保这些更改对后续执行可见,包括推测执行,

使用更改后的翻译表条目。

一个反例是转换表的初始化。在这种情况下,不需要明确的TLB维护。Armv8-A体系结构保证所有tlb都被禁止重置,并且它们的内容在重置时不会影响地址转换[#tlb reset ref]_。因此,TLB的失效被推迟到“enable_mmu*()”函数族,就在mmu打开之前。

关于启用和禁用内存管理,对于MPU库,为了减少混淆,启用或禁用MPU的调用在其名称中使用“MPU”代替“mmu”。例如,“enable_mmu_el2()”调用被更改为“enable_mpu_el2(()”。

添加动态区域时也不需要TLB失效。动态区域不允许与现有内存区域重叠。因此,如果动态映射请求被认为是合法的,它会自动涉及在该转换机制中未映射的内存,并且库将其相应的转换表条目初始化为无效描述符。考虑到tlb在体系结构上不允许保存任何无效的转换表条目[#tlb no invalid entry]_,这意味着该映射不能缓存在tlb中。

猜你喜欢

转载自blog.csdn.net/weixin_45264425/article/details/130716419