Phân tích thuộc tính cờ trong ánh xạ không gian địa chỉ của OpenHarmony

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
    }
}

Thẻ: openharmony MMU virtual memory Memory Mapping ARM Architecture

Đăng vào ngày 9 tháng 10 lúc 13:01