Trong quá trình nghiên cứu hàm LOS_ArchMmuMap(), chúng ta đã nắm được cách thức ánh xạ bộ nhớ ảo sang bộ nhớ vật lý. Khi thực hiện ánh xạ, tham số UINT 32 flags cho phép xác định các thuộc tính đánh dấu. Bài viết này sẽ đi sâu vào các thuộc tính đánh dấu này, bắt đầu từ các thuộc tính của MMU, sau đó xem xét thông tin ánh xạ trong các phân đoạn bộ nhớ, và cuối cùng là các hàm chuyển đổi thuộc tính.
Thuộc tính đánh dấu MMU
Khi ánh xạ, các trang bộ nhớ có thể được gán các thuộc tính như quyền truy cập, loại bộ nhớ, chiến lược bộ nhớ đệm, v.v. Thông tin chi tiết có thể tham khảo tài liệu chính thức của ARM. Dưới đây là tóm tắt các thông tin quan trọng liên quan đến định dạng các mục bảng trang L1 và các macro định nghĩa thuộc tính bộ nhớ liên quan đến MMU L1 trong tệp arch\arm\arm\include\los_mmu_descriptor_v6.h:
/* TEX CB */
#define MMU_DESCRIPTOR_L1_TEX_SHIFT 12 /* type extension field shift */
#define MMU_DESCRIPTOR_L1_TEX(x) \
((x) << MMU_DESCRIPTOR_L1_TEX_SHIFT) /* type extension */
#define MMU_DESCRIPTOR_L1_TYPE_STRONGLY_ORDERED \
(MMU_DESCRIPTOR_L1_TEX(MMU_DESCRIPTOR_TEX_0) | MMU_DESCRIPTOR_NON_CACHEABLE)
#define MMU_DESCRIPTOR_L1_TYPE_NORMAL_NOCACHE \
(MMU_DESCRIPTOR_L1_TEX(MMU_DESCRIPTOR_TEX_1) | MMU_DESCRIPTOR_NON_CACHEABLE)
#define MMU_DESCRIPTOR_L1_TYPE_DEVICE_SHARED \
(MMU_DESCRIPTOR_L1_TEX(MMU_DESCRIPTOR_TEX_0) | MMU_DESCRIPTOR_WRITE_BACK_ALLOCATE)
#define MMU_DESCRIPTOR_L1_TYPE_DEVICE_NON_SHARED \
(MMU_DESCRIPTOR_L1_TEX(MMU_DESCRIPTOR_TEX_2) | MMU_DESCRIPTOR_NON_CACHEABLE)
#define MMU_DESCRIPTOR_L1_TYPE_NORMAL_WRITE_BACK_ALLOCATE \
(MMU_DESCRIPTOR_L1_TEX(MMU_DESCRIPTOR_TEX_1) | MMU_DESCRIPTOR_WRITE_BACK_NO_ALLOCATE)
#define MMU_DESCRIPTOR_L1_TEX_TYPE_MASK \
(MMU_DESCRIPTOR_L1_TEX(MMU_DESCRIPTOR_TEX_MASK) | MMU_DESCRIPTOR_WRITE_BACK_NO_ALLOCATE)
#define MMU_DESCRIPTOR_L1_AP2_SHIFT 15
#define MMU_DESCRIPTOR_L1_AP2(x) ((x) << MMU_DESCRIPTOR_L1_AP2_SHIFT)
#define MMU_DESCRIPTOR_L1_AP2_0 (MMU_DESCRIPTOR_L1_AP2(0))
#define MMU_DESCRIPTOR_L1_AP2_1 (MMU_DESCRIPTOR_L1_AP2(1))
#define MMU_DESCRIPTOR_L1_AP01_SHIFT 10
#define MMU_DESCRIPTOR_L1_AP01(x) ((x) << MMU_DESCRIPTOR_L1_AP01_SHIFT)
#define MMU_DESCRIPTOR_L1_AP01_0 (MMU_DESCRIPTOR_L1_AP01(0))
#define MMU_DESCRIPTOR_L1_AP01_1 (MMU_DESCRIPTOR_L1_AP01(1))
#define MMU_DESCRIPTOR_L1_AP01_3 (MMU_DESCRIPTOR_L1_AP01(3))
#define MMU_DESCRIPTOR_L1_AP_P_NA_U_NA (MMU_DESCRIPTOR_L1_AP2_0 | MMU_DESCRIPTOR_L1_AP01_0)
#define MMU_DESCRIPTOR_L1_AP_P_RW_U_RW (MMU_DESCRIPTOR_L1_AP2_0 | MMU_DESCRIPTOR_L1_AP01_3)
#define MMU_DESCRIPTOR_L1_AP_P_RW_U_NA (MMU_DESCRIPTOR_L1_AP2_0 | MMU_DESCRIPTOR_L1_AP01_1)
#define MMU_DESCRIPTOR_L1_AP_P_RO_U_RO (MMU_DESCRIPTOR_L1_AP2_1 | MMU_DESCRIPTOR_L1_AP01_3)
#define MMU_DESCRIPTOR_L1_AP_P_RO_U_NA (MMU_DESCRIPTOR_L1_AP2_1 | MMU_DESCRIPTOR_L1_AP01_1)
#define MMU_DESCRIPTOR_L1_AP_MASK (MMU_DESCRIPTOR_L1_AP2_1 | MMU_DESCRIPTOR_L1_AP01_3)
Tương tự, đối với các mục bảng trang L2, định dạng và các macro liên quan được định nghĩa trong tệp arch\arm\arm\include\los_mmu_descriptor_v6.h:
#define MMU_DESCRIPTOR_L2_TEX_SHIFT 6 /* type extension field shift */
#define MMU_DESCRIPTOR_L2_TEX(x) \
((x) << MMU_DESCRIPTOR_L2_TEX_SHIFT) /* type extension */
#define MMU_DESCRIPTOR_L2_TYPE_STRONGLY_ORDERED \
(MMU_DESCRIPTOR_L2_TEX(MMU_DESCRIPTOR_TEX_0) | MMU_DESCRIPTOR_NON_CACHEABLE)
#define MMU_DESCRIPTOR_L2_TYPE_NORMAL_NOCACHE \
(MMU_DESCRIPTOR_L2_TEX(MMU_DESCRIPTOR_TEX_1) | MMU_DESCRIPTOR_NON_CACHEABLE)
#define MMU_DESCRIPTOR_L2_TYPE_DEVICE_SHARED \
(MMU_DESCRIPTOR_L2_TEX(MMU_DESCRIPTOR_TEX_0) | MMU_DESCRIPTOR_WRITE_BACK_ALLOCATE)
#define MMU_DESCRIPTOR_L2_TYPE_DEVICE_NON_SHARED \
(MMU_DESCRIPTOR_L2_TEX(MMU_DESCRIPTOR_TEX_2) | MMU_DESCRIPTOR_NON_CACHEABLE)
#define MMU_DESCRIPTOR_L2_TYPE_NORMAL_WRITE_BACK_ALLOCATE \
(MMU_DESCRIPTOR_L2_TEX(MMU_DESCRIPTOR_TEX_1) | MMU_DESCRIPTOR_WRITE_BACK_NO_ALLOCATE)
#define MMU_DESCRIPTOR_L2_TEX_TYPE_MASK \
(MMU_DESCRIPTOR_L2_TEX(MMU_DESCRIPTOR_TEX_MASK) | MMU_DESCRIPTOR_WRITE_BACK_NO_ALLOCATE)
#define MMU_DESCRIPTOR_L2_AP2_SHIFT 9
#define MMU_DESCRIPTOR_L2_AP2(x) ((x) << MMU_DESCRIPTOR_L2_AP2_SHIFT)
#define MMU_DESCRIPTOR_L2_AP2_0 (MMU_DESCRIPTOR_L2_AP2(0))
#define MMU_DESCRIPTOR_L2_AP2_1 (MMU_DESCRIPTOR_L2_AP2(1))
#define MMU_DESCRIPTOR_L2_AP01_SHIFT 4
#define MMU_DESCRIPTOR_L2_AP01(x) ((x) << MMU_DESCRIPTOR_L2_AP01_SHIFT)
#define MMU_DESCRIPTOR_L2_AP01_0 (MMU_DESCRIPTOR_L2_AP01(0))
#define MMU_DESCRIPTOR_L2_AP01_1 (MMU_DESCRIPTOR_L2_AP01(1))
#define MMU_DESCRIPTOR_L2_AP01_3 (MMU_DESCRIPTOR_L2_AP01(3))
#define MMU_DESCRIPTOR_L2_AP_P_NA_U_NA (MMU_DESCRIPTOR_L2_AP2_0 | MMU_DESCRIPTOR_L2_AP01_0)
#define MMU_DESCRIPTOR_L2_AP_P_RW_U_RW (MMU_DESCRIPTOR_L2_AP2_0 | MMU_DESCRIPTOR_L2_AP01_3)
#define MMU_DESCRIPTOR_L2_AP_P_RW_U_NA (MMU_DESCRIPTOR_L2_AP2_0 | MMU_DESCRIPTOR_L2_AP01_1)
#define MMU_DESCRIPTOR_L2_AP_P_RO_U_RO (MMU_DESCRIPTOR_L2_AP2_1 | MMU_DESCRIPTOR_L2_AP01_3)
#define MMU_DESCRIPTOR_L2_AP_P_RO_U_NA (MMU_DESCRIPTOR_L2_AP2_1 | MMU_DESCRIPTOR_L2_AP01_1)
#define MMU_DESCRIPTOR_L2_AP_MASK (MMU_DESCRIPTOR_L2_AP2_1 | MMU_DESCRIPTOR_L2_AP01_3)
Thuộc tính đánh dấu phân đoạn địa chỉ ánh xạ
Tệp kernel\base\include\los_vm_map.h định nghĩa các thuộc tính đánh dấu cho việc ánh xạ phân đoạn địa chỉ. Các thuộc tính này được chia thành 4 loại chính:
- 2 bit đầu dùng để đánh dấu thiết bị lưu trữ cache.
- Các bit từ 2 đến 5 dùng để đánh dấu quyền truy cập.
- Các bit từ 6 đến 8 dùng để đánh dấu thông tin chia sẻ/riêng tư.
- Các bit từ 9 đến 19 dùng để đánh dấu các thuộc tính như stack, heap, data, text, bss, vdso, mmap, shm, fixed, fixed_noreplace.
- Các bit từ 20 đến 23 chưa được sử dụng.
- 8 bit cao nhất (24-31) được dành riêng cho bộ nhớ chia sẻ SHM.
/* the high 8 bits(24~31) should reserved, shm will use it */ #define VM_MAP_REGION_FLAG_CACHED (0<<0) #define VM_MAP_REGION_FLAG_UNCACHED (1<<0) #define VM_MAP_REGION_FLAG_UNCACHED_DEVICE (2<<0) /* only exists on some arches, otherwise UNCACHED */ #define VM_MAP_REGION_FLAG_STRONGLY_ORDERED (3<<0) /* only exists on some arches, otherwise UNCACHED */ #define VM_MAP_REGION_FLAG_CACHE_MASK (3<<0) #define VM_MAP_REGION_FLAG_PERM_USER (1<<2) #define VM_MAP_REGION_FLAG_PERM_READ (1<<3) #define VM_MAP_REGION_FLAG_PERM_WRITE (1<<4) #define VM_MAP_REGION_FLAG_PERM_EXECUTE (1<<5) #define VM_MAP_REGION_FLAG_PROT_MASK (0xF<<2) #define VM_MAP_REGION_FLAG_NS (1<<6) /* NON-SECURE */ #define VM_MAP_REGION_FLAG_SHARED (1<<7) #define VM_MAP_REGION_FLAG_PRIVATE (1<<8) #define VM_MAP_REGION_FLAG_FLAG_MASK (3<<7) #define VM_MAP_REGION_FLAG_STACK (1<<9) #define VM_MAP_REGION_FLAG_HEAP (1<<10) #define VM_MAP_REGION_FLAG_DATA (1<<11) #define VM_MAP_REGION_FLAG_TEXT (1<<12) #define VM_MAP_REGION_FLAG_BSS (1<<13) #define VM_MAP_REGION_FLAG_VDSO (1<<14) #define VM_MAP_REGION_FLAG_MMAP (1<<15) #define VM_MAP_REGION_FLAG_SHM (1<<16) #define VM_MAP_REGION_FLAG_FIXED (1<<17) #define VM_MAP_REGION_FLAG_FIXED_NOREPLACE (1<<18) #define VM_MAP_REGION_FLAG_INVALID (1<<19) /* indicates that flags are not specified */
Thao tác chuyển đổi thuộc tính
Hàm OsCvtProtFlagsToRegionFlags
Hàm OsCvtProtFlagsToRegionFlags() chịu trách nhiệm chuyển đổi các thuộc tính bảo vệ sang các thuộc tính đánh dấu của phân đoạn bộ nhớ ảo. Hàm này được sử dụng trong các mô-đun gọi hệ thống và bộ nhớ chia sẻ. Các thuộc tính bảo vệ trong tham số unsigned long prot như PROT_READ, MAP_SHARED, v.v., được định nghĩa trong tệp third_party/musl/porting/liteos_a/kernel/include/sys/mman.h.
STATIC INLINE UINT32 OsCvtProtFlagsToRegionFlags(unsigned long prot, unsigned long flags)
{
UINT32 regionFlags = 0;
// Add base user permission flag
regionFlags |= VM_MAP_REGION_FLAG_PERM_USER;
// Map protection flags to region flags
if (prot & PROT_READ) {
regionFlags |= VM_MAP_REGION_FLAG_PERM_READ;
}
if (prot & PROT_WRITE) {
// If writable, it also implies readable
regionFlags |= (VM_MAP_REGION_FLAG_PERM_READ | VM_MAP_REGION_FLAG_PERM_WRITE);
}
if (prot & PROT_EXEC) {
// If executable, it also implies readable
regionFlags |= (VM_MAP_REGION_FLAG_PERM_READ | VM_MAP_REGION_FLAG_PERM_EXECUTE);
}
// Map mapping flags to region flags
if (flags & MAP_SHARED) {
regionFlags |= VM_MAP_REGION_FLAG_SHARED;
} else if (flags & MAP_PRIVATE) {
regionFlags |= VM_MAP_REGION_FLAG_PRIVATE;
}
if (flags & MAP_FIXED) {
regionFlags |= VM_MAP_REGION_FLAG_FIXED;
}
if (flags & MAP_FIXED_NOREPLACE) {
regionFlags |= VM_MAP_REGION_FLAG_FIXED_NOREPLACE;
}
return regionFlags;
}
Hàm OsCvtSecFlagsToAttrs và OsCvtSecAttsToFlags
Hàm OsCvtSecFlagsToAttrs() chuyển đổi các thuộc tính đánh dấu phân đoạn bộ nhớ thành các thuộc tính đánh dấu MMU cho mục bảng trang loại Section L1. Hàm này bao gồm hai hàm con: OsCvtSecCacheFlagsToMMUFlags() (được đánh dấu là ⑴) để xử lý thuộc tính bộ nhớ đệm và OsCvtSecAccessFlagsToMMUFlags() (được đánh dấu là ⑵) để xử lý quyền truy cập.
Hàm OsCvtSecAttsToFlags() (được đánh dấu là ⑶) là hàm ngược lại, chuyển đổi các thuộc tính đánh dấu MMU của mục bảng trang loại Section L1 trở lại thành các thuộc tính đánh dấu phân đoạn bộ nhớ.
/*⑴ Convert cache flags to MMU flags for L1 Section entries */
STATIC UINT32 OsCvtSecCacheFlagsToMMUFlags(UINT32 flags)
{
UINT32 mmuFlags = 0;
switch (flags & VM_MAP_REGION_FLAG_CACHE_MASK) {
case VM_MAP_REGION_FLAG_CACHED:
mmuFlags |= MMU_DESCRIPTOR_L1_TYPE_NORMAL_WRITE_BACK_ALLOCATE;
#ifdef LOSCFG_KERNEL_SMP
mmuFlags |= MMU_DESCRIPTOR_L1_SECTION_SHAREABLE;
#endif
break;
case VM_MAP_REGION_FLAG_STRONGLY_ORDERED:
mmuFlags |= MMU_DESCRIPTOR_L1_TYPE_STRONGLY_ORDERED;
break;
case VM_MAP_REGION_FLAG_UNCACHED:
mmuFlags |= MMU_DESCRIPTOR_L1_TYPE_NORMAL_NOCACHE;
break;
case VM_MAP_REGION_FLAG_UNCACHED_DEVICE:
mmuFlags |= MMU_DESCRIPTOR_L1_TYPE_DEVICE_SHARED;
break;
default:
return LOS_ERRNO_VM_INVALID_ARGS; /* Indicate an error */
}
return mmuFlags;
}
/*⑵ Convert access flags to MMU flags for L1 Section entries */
STATIC UINT32 OsCvtSecAccessFlagsToMMUFlags(UINT32 flags)
{
UINT32 mmuFlags = 0;
// Determine MMU access permissions based on region flags
if (flags & VM_MAP_REGION_FLAG_PERM_USER) {
if (flags & VM_MAP_REGION_FLAG_PERM_WRITE) {
mmuFlags |= MMU_DESCRIPTOR_L1_AP_P_RW_U_RW; // User Read/Write, Privileged Read/Write
} else {
mmuFlags |= MMU_DESCRIPTOR_L1_AP_P_RO_U_RO; // User Read-Only, Privileged Read-Only
}
} else { // Not VM_MAP_REGION_FLAG_PERM_USER
if (flags & VM_MAP_REGION_FLAG_PERM_WRITE) {
mmuFlags |= MMU_DESCRIPTOR_L1_AP_P_RW_U_NA; // User No Access, Privileged Read/Write
} else {
// Distinguish between read-only and no access for privileged mode
if (flags & VM_MAP_REGION_FLAG_PERM_READ) {
mmuFlags |= MMU_DESCRIPTOR_L1_AP_P_RO_U_NA; // User No Access, Privileged Read-Only
} else {
mmuFlags |= MMU_DESCRIPTOR_L1_AP_P_NA_U_NA; // User No Access, Privileged No Access
}
}
}
return mmuFlags;
}
/* Convert user-level region flags to L1 MMU descriptor flags */
STATIC UINT32 OsCvtSecFlagsToAttrs(UINT32 flags)
{
UINT32 mmuFlags;
// Convert cache attributes
mmuFlags = OsCvtSecCacheFlagsToMMUFlags(flags);
if (mmuFlags == LOS_ERRNO_VM_INVALID_ARGS) {
return mmuFlags; // Propagate error if cache conversion failed
}
// Set default L1 attributes
mmuFlags |= MMU_DESCRIPTOR_L1_SMALL_DOMAIN_CLIENT; // Assuming client domain for simplicity
// Convert access permissions
mmuFlags |= OsCvtSecAccessFlagsToMMUFlags(flags);
// Set Execute Never (XN) bit if not executable
if (!(flags & VM_MAP_REGION_FLAG_PERM_EXECUTE)) {
mmuFlags |= MMU_DESCRIPTOR_L1_SECTION_XN;
}
// Set Non-Secure (NS) bit if applicable
if (flags & VM_MAP_REGION_FLAG_NS) {
mmuFlags |= MMU_DESCRIPTOR_L1_SECTION_NON_SECURE;
}
// Set Non-Global bit if user permission is specified (often implies user-level access)
if (flags & VM_MAP_REGION_FLAG_PERM_USER) {
mmuFlags |= MMU_DESCRIPTOR_L1_SECTION_NON_GLOBAL;
}
return mmuFlags;
}
/*⑶ Convert L1 MMU descriptor flags back to region flags */
STATIC VOID OsCvtSecAttsToFlags(PTE_T l1Entry, UINT32 *flags)
{
*flags = 0;
// Decode Non-Secure (NS) attribute
if (l1Entry & MMU_DESCRIPTOR_L1_SECTION_NON_SECURE) {
*flags |= VM_MAP_REGION_FLAG_NS;
}
// Decode cache attributes
switch (l1Entry & MMU_DESCRIPTOR_L1_TEX_TYPE_MASK) {
case MMU_DESCRIPTOR_L1_TYPE_STRONGLY_ORDERED:
*flags |= VM_MAP_REGION_FLAG_STRONGLY_ORDERED;
break;
case MMU_DESCRIPTOR_L1_TYPE_NORMAL_NOCACHE:
*flags |= VM_MAP_REGION_FLAG_UNCACHED;
break;
case MMU_DESCRIPTOR_L1_TYPE_DEVICE_SHARED:
case MMU_DESCRIPTOR_L1_TYPE_DEVICE_NON_SHARED:
*flags |= VM_MAP_REGION_FLAG_UNCACHED_DEVICE;
break;
// Default case implicitly handles cached types
default:
// Assuming default is cached if not otherwise specified, could refine
*flags |= VM_MAP_REGION_FLAG_CACHED;
break;
}
// Decode access permissions and map to region flags
// Start with readable as a base for most valid permissions
*flags |= VM_MAP_REGION_FLAG_PERM_READ;
switch (l1Entry & MMU_DESCRIPTOR_L1_AP_MASK) {
case MMU_DESCRIPTOR_L1_AP_P_RO_U_NA: // Privileged Read-Only, User No Access
// Already has PERM_READ, no write/user flags needed
break;
case MMU_DESCRIPTOR_L1_AP_P_RW_U_NA: // Privileged Read/Write, User No Access
*flags |= VM_MAP_REGION_FLAG_PERM_WRITE; // Add write permission
break;
case MMU_DESCRIPTOR_L1_AP_P_RO_U_RO: // Privileged Read-Only, User Read-Only
*flags |= VM_MAP_REGION_FLAG_PERM_USER; // Add user permission
break;
case MMU_DESCRIPTOR_L1_AP_P_RW_U_RW: // Privileged Read/Write, User Read/Write
*flags |= VM_MAP_REGION_FLAG_PERM_USER | VM_MAP_REGION_FLAG_PERM_WRITE; // Add both
break;
default:
// Handle other cases or error if necessary
break;
}
// Decode Execute Never (XN) bit
if (!(l1Entry & MMU_DESCRIPTOR_L1_SECTION_XN)) {
*flags |= VM_MAP_REGION_FLAG_PERM_EXECUTE; // If XN is NOT set, it's executable
}
}
Hàm OsCvtPte2FlagsToAttrs và OsCvtPte2AttsToFlags
Tương tự như phần trước, hai hàm này thực hiện chuyển đổi giữa thuộc tính MMU của mục bảng trang loại L2 và thuộc tính đánh dấu phân đoạn bộ nhớ. Hàm OsCvtPte2FlagsToAttrs() chuyển đổi thuộc tính đánh dấu phân đoạn bộ nhớ thành thuộc tính MMU cho mục bảng trang L2, bao gồm hai hàm con: OsCvtPte2CacheFlagsToMMUFlags() (⑴) và OsCvtPte2AccessFlagsToMMUFlags() (⑵).
Hàm OsCvtPte2AttsToFlags() (⑶) là hàm ngược lại, chuyển đổi thuộc tính MMU của mục bảng trang L2 thành thuộc tính đánh dấu phân đoạn bộ nhớ.
/*⑴ Convert cache flags to MMU flags for L2 entries */
STATIC UINT32 OsCvtPte2CacheFlagsToMMUFlags(UINT32 flags)
{
UINT32 mmuFlags = 0;
switch (flags & VM_MAP_REGION_FLAG_CACHE_MASK) {
case VM_MAP_REGION_FLAG_CACHED:
mmuFlags |= MMU_DESCRIPTOR_L2_TYPE_NORMAL_WRITE_BACK_ALLOCATE;
#ifdef LOSCFG_KERNEL_SMP
mmuFlags |= MMU_DESCRIPTOR_L2_SHAREABLE;
#endif
break;
case VM_MAP_REGION_FLAG_STRONGLY_ORDERED:
mmuFlags |= MMU_DESCRIPTOR_L2_TYPE_STRONGLY_ORDERED;
break;
case VM_MAP_REGION_FLAG_UNCACHED:
mmuFlags |= MMU_DESCRIPTOR_L2_TYPE_NORMAL_NOCACHE;
break;
case VM_MAP_REGION_FLAG_UNCACHED_DEVICE:
mmuFlags |= MMU_DESCRIPTOR_L2_TYPE_DEVICE_SHARED;
break;
default:
return LOS_ERRNO_VM_INVALID_ARGS; /* Indicate invalid arguments */
}
return mmuFlags;
}
/*⑵ Convert access flags to MMU flags for L2 entries */
STATIC UINT32 OsCvtPte2AccessFlagsToMMUFlags(UINT32 flags)
{
UINT32 mmuFlags = 0;
// Determine MMU access permissions based on region flags for L2 entries
if (flags & VM_MAP_REGION_FLAG_PERM_USER) {
if (flags & VM_MAP_REGION_FLAG_PERM_WRITE) {
mmuFlags |= MMU_DESCRIPTOR_L2_AP_P_RW_U_RW; // User R/W, Privileged R/W
} else {
mmuFlags |= MMU_DESCRIPTOR_L2_AP_P_RO_U_RO; // User R/O, Privileged R/O
}
} else { // Not VM_MAP_REGION_FLAG_PERM_USER
if (flags & VM_MAP_REGION_FLAG_PERM_WRITE) {
mmuFlags |= MMU_DESCRIPTOR_L2_AP_P_RW_U_NA; // User No Access, Privileged R/W
} else {
// Differentiate between read-only and no access for privileged mode
if (flags & VM_MAP_REGION_FLAG_PERM_READ) {
mmuFlags |= MMU_DESCRIPTOR_L2_AP_P_RO_U_NA; // User No Access, Privileged R/O
} else {
mmuFlags |= MMU_DESCRIPTOR_L1_AP_P_NA_U_NA; // Fallback to No Access for both
}
}
}
return mmuFlags;
}
/* Convert user-level region flags to L2 MMU descriptor flags */
STATIC UINT32 OsCvtPte2FlagsToAttrs(UINT32 flags)
{
UINT32 mmuFlags;
// Convert cache attributes for L2
mmuFlags = OsCvtPte2CacheFlagsToMMUFlags(flags);
if (mmuFlags == LOS_ERRNO_VM_INVALID_ARGS) {
return mmuFlags; // Return error if cache conversion fails
}
// Convert access permissions for L2
mmuFlags |= OsCvtPte2AccessFlagsToMMUFlags(flags);
// Set Execute Never (XN) bit for L2 small pages
if (!(flags & VM_MAP_REGION_FLAG_PERM_EXECUTE)) {
mmuFlags |= MMU_DESCRIPTOR_L2_TYPE_SMALL_PAGE_XN;
} else {
mmuFlags |= MMU_DESCRIPTOR_L2_TYPE_SMALL_PAGE; // Mark as a normal small page if executable
}
// Set Non-Global bit if user permission is indicated
if (flags & VM_MAP_REGION_FLAG_PERM_USER) {
mmuFlags |= MMU_DESCRIPTOR_L2_NON_GLOBAL;
}
return mmuFlags;
}
/*⑶ Convert L2 MMU descriptor attributes back to region flags */
STATIC VOID OsCvtPte2AttsToFlags(PTE_T l1Entry, PTE_T l2Entry, UINT32 *flags)
{
*flags = 0;
// NS flag is only present on L1 entry for L2 descriptors
if (l1Entry & MMU_DESCRIPTOR_L1_PAGETABLE_NON_SECURE) {
*flags |= VM_MAP_REGION_FLAG_NS;
}
// Decode cache attributes from L2 entry
switch (l2Entry & MMU_DESCRIPTOR_L2_TEX_TYPE_MASK) {
case MMU_DESCRIPTOR_L2_TYPE_STRONGLY_ORDERED:
*flags |= VM_MAP_REGION_FLAG_STRONGLY_ORDERED;
break;
case MMU_DESCRIPTOR_L2_TYPE_NORMAL_NOCACHE:
*flags |= VM_MAP_REGION_FLAG_UNCACHED;
break;
case MMU_DESCRIPTOR_L2_TYPE_DEVICE_SHARED:
case MMU_DESCRIPTOR_L2_TYPE_DEVICE_NON_SHARED:
*flags |= VM_MAP_REGION_FLAG_UNCACHED_DEVICE;
break;
// Default implies cached, similar to L1 handling
default:
*flags |= VM_MAP_REGION_FLAG_CACHED;
break;
}
// Base assumption: readable
*flags |= VM_MAP_REGION_FLAG_PERM_READ;
// Decode access permissions from L2 entry
switch (l2Entry & MMU_DESCRIPTOR_L2_AP_MASK) {
case MMU_DESCRIPTOR_L2_AP_P_RO_U_NA: // Privileged Read-Only, User No Access
break; // Already has PERM_READ
case MMU_DESCRIPTOR_L2_AP_P_RW_U_NA: // Privileged Read/Write, User No Access
*flags |= VM_MAP_REGION_FLAG_PERM_WRITE;
break;
case MMU_DESCRIPTOR_L2_AP_P_RO_U_RO: // Privileged Read-Only, User Read-Only
*flags |= VM_MAP_REGION_FLAG_PERM_USER;
break;
case MMU_DESCRIPTOR_L2_AP_P_RW_U_RW: // Privileged Read/Write, User Read/Write
*flags |= VM_MAP_REGION_FLAG_PERM_USER | VM_MAP_REGION_FLAG_PERM_WRITE;
break;
default:
// Handle other cases or potential errors
break;
}
// Decode Execute Never (XN) bit for L2 small pages
if ((l2Entry & MMU_DESCRIPTOR_L2_TYPE_MASK) != MMU_DESCRIPTOR_L2_TYPE_SMALL_PAGE_XN) {
*flags |= VM_MAP_REGION_FLAG_PERM_EXECUTE; // If not XN, it's executable
}
}