build_mem_type_table()

메모리 타입별로 페이지 테이블을 구성하는 엔트리에 대한 속성을 구성한다.

 

커맨드라인 파라메터

  • “cachepolicy=”
    • 캐시 속성을 선택할 수 있다.
    • “uncached”, “buffered”, “writethrough”, “writeback”, “writealloc” 중 하나를 지정할 수 있다.
    • 관련 함수는 early_cachepolicy()
  • “nocache”
    • nocache 옵션은 deprecated되었고 buffered로 동작시킨다.
    • 관련 함수는 early_nocache()
  • “nowb”
    • nowb 옵션은 deprecated되었고 uncached로 동작시킨다.
    • 관련 함수는 early_nowrite()
  • “ecc=”
    • “on”, “off”를 사용할 수 있다.
    • 관련 함수는 early_ecc()

build_mem_type_table()

build_mem_type_table-3

 

arch/arm/mm/mmu.c

/*
 * Adjust the PMD section entries according to the CPU in use.
 */
static void __init build_mem_type_table(void)
{
        struct cachepolicy *cp; 
        unsigned int cr = get_cr();
        pteval_t user_pgprot, kern_pgprot, vecs_pgprot;
        pteval_t hyp_device_pgprot, s2_pgprot, s2_device_pgprot;
        int cpu_arch = cpu_architecture();
        int i;

        if (cpu_arch < CPU_ARCH_ARMv6) {
#if defined(CONFIG_CPU_DCACHE_DISABLE)
                if (cachepolicy > CPOLICY_BUFFERED)
                        cachepolicy = CPOLICY_BUFFERED;
#elif defined(CONFIG_CPU_DCACHE_WRITETHROUGH)
                if (cachepolicy > CPOLICY_WRITETHROUGH)
                        cachepolicy = CPOLICY_WRITETHROUGH;
#endif
        }
        if (cpu_arch < CPU_ARCH_ARMv5) {
                if (cachepolicy >= CPOLICY_WRITEALLOC)
                        cachepolicy = CPOLICY_WRITEBACK;
                ecc_mask = 0; 
        }

        if (is_smp()) {
                if (cachepolicy != CPOLICY_WRITEALLOC) {
                        pr_warn("Forcing write-allocate cache policy for SMP\n");
                        cachepolicy = CPOLICY_WRITEALLOC;
                }
                if (!(initial_pmd_value & PMD_SECT_S)) {
                        pr_warn("Forcing shared mappings for SMP\n");
                        initial_pmd_value |= PMD_SECT_S;
                }
        }
  • unsigned int cr = get_cr();
    • cr <- read SCTLR
  • cachepolicy
    • 각 아키텍처가 지원하는 캐시 정책이 담긴다.
    • rpi2: CPOLICY_WRITEALLOC
  • if (is_smp()) {
    • SMP 시스템인 경우
      • cachepolicy = CPOLICY_WRITEALLOC
      • initial_pmd_value |= PMD_SECT_S
        • rpi2: PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_AP_READ | PMD_SECT_AF | PMD_SECT_TEX(1) | PMD_SECT_CACHEABLE | PMD_SECT_BUFFERABLE | PMD_SECT_S

 

        /*
         * Strip out features not present on earlier architectures.
         * Pre-ARMv5 CPUs don't have TEX bits.  Pre-ARMv6 CPUs or those
         * without extended page tables don't have the 'Shared' bit.
         */
        if (cpu_arch < CPU_ARCH_ARMv5)
                for (i = 0; i < ARRAY_SIZE(mem_types); i++)
                        mem_types[i].prot_sect &= ~PMD_SECT_TEX(7);
        if ((cpu_arch < CPU_ARCH_ARMv6 || !(cr & CR_XP)) && !cpu_is_xsc3())
                for (i = 0; i < ARRAY_SIZE(mem_types); i++)
                        mem_types[i].prot_sect &= ~PMD_SECT_S;

        /*
         * ARMv5 and lower, bit 4 must be set for page tables (was: cache
         * "update-able on write" bit on ARM610).  However, Xscale and
         * Xscale3 require this bit to be cleared.
         */
        if (cpu_is_xscale() || cpu_is_xsc3()) {
                for (i = 0; i < ARRAY_SIZE(mem_types); i++) {
                        mem_types[i].prot_sect &= ~PMD_BIT4;
                        mem_types[i].prot_l1 &= ~PMD_BIT4;
                }
        } else if (cpu_arch < CPU_ARCH_ARMv6) {
                for (i = 0; i < ARRAY_SIZE(mem_types); i++) {
                        if (mem_types[i].prot_l1)
                                mem_types[i].prot_l1 |= PMD_BIT4;
                        if (mem_types[i].prot_sect)
                                mem_types[i].prot_sect |= PMD_BIT4;
                }
        }
  • if (cpu_arch < CPU_ARCH_ARMv5)
    • ARMv5 미만 아키텍처인 경우 mem_types[]에서 PMD_SECT_TEX 비트들을 제거한다.
    • TEX(Type Extension)을 지원하지 않는 아키텍처이다.
  • if ((cpu_arch < CPU_ARCH_ARMv6 || !(cr & CR_XP)) && !cpu_is_xsc3())
    • ARMv6 미만 아키텍처이거나 xscale CPU가 아니면서 XP(Extended Page)를 지원하지 않으면 mem_types[]에서 PMD_SECT_S 비트를 제거한다.
    • ARMv6 아키텍처 부터 Share 플래그를 지원한다.
  •  ARMv5 이하의 아키텍처는 mem_types[]에서 PMD_BIT4 플래그를 삭제하는데 xscale와 scale3에서는 PMD_BIT4 플래그를 제거해야 한다.

 

        /*
         * Mark the device areas according to the CPU/architecture.
         */
        if (cpu_is_xsc3() || (cpu_arch >= CPU_ARCH_ARMv6 && (cr & CR_XP))) {
                if (!cpu_is_xsc3()) {
                        /*
                         * Mark device regions on ARMv6+ as execute-never
                         * to prevent speculative instruction fetches.
                         */
                        mem_types[MT_DEVICE].prot_sect |= PMD_SECT_XN;
                        mem_types[MT_DEVICE_NONSHARED].prot_sect |= PMD_SECT_XN;
                        mem_types[MT_DEVICE_CACHED].prot_sect |= PMD_SECT_XN;
                        mem_types[MT_DEVICE_WC].prot_sect |= PMD_SECT_XN;

                        /* Also setup NX memory mapping */
                        mem_types[MT_MEMORY_RW].prot_sect |= PMD_SECT_XN;
                }
                if (cpu_arch >= CPU_ARCH_ARMv7 && (cr & CR_TRE)) {
                        /*
                         * For ARMv7 with TEX remapping,
                         * - shared device is SXCB=1100
                         * - nonshared device is SXCB=0100
                         * - write combine device mem is SXCB=0001
                         * (Uncached Normal memory)
                         */
                        mem_types[MT_DEVICE].prot_sect |= PMD_SECT_TEX(1);
                        mem_types[MT_DEVICE_NONSHARED].prot_sect |= PMD_SECT_TEX(1);
                        mem_types[MT_DEVICE_WC].prot_sect |= PMD_SECT_BUFFERABLE;
                } else if (cpu_is_xsc3()) {
                        /*
                         * For Xscale3,
                         * - shared device is TEXCB=00101
                         * - nonshared device is TEXCB=01000
                         * - write combine device mem is TEXCB=00100
                         * (Inner/Outer Uncacheable in xsc3 parlance)
                         */
                        mem_types[MT_DEVICE].prot_sect |= PMD_SECT_TEX(1) | PMD_SECT_BUFFERED;
                        mem_types[MT_DEVICE_NONSHARED].prot_sect |= PMD_SECT_TEX(2);
                        mem_types[MT_DEVICE_WC].prot_sect |= PMD_SECT_TEX(1);
                } else {
                        /*
                         * For ARMv6 and ARMv7 without TEX remapping,
                         * - shared device is TEXCB=00001
                         * - nonshared device is TEXCB=01000
                         * - write combine device mem is TEXCB=00100
                         * (Uncached Normal in ARMv6 parlance).
                         */
                        mem_types[MT_DEVICE].prot_sect |= PMD_SECT_BUFFERED;
                        mem_types[MT_DEVICE_NONSHARED].prot_sect |= PMD_SECT_TEX(2);
                        mem_types[MT_DEVICE_WC].prot_sect |= PMD_SECT_TEX(1);
                }
        } else {
                /*
                 * On others, write combining is "Uncached/Buffered"
                 */
                mem_types[MT_DEVICE_WC].prot_sect |= PMD_SECT_BUFFERABLE;
        }

아키텍처에 맞게 디바이스 속성을 설정한다.

  • if (cpu_is_xsc3() || (cpu_arch >= CPU_ARCH_ARMv6 && (cr & CR_XP))) {
    • 아키텍처가 free-scale 이거나 ARMv7 이상이면서 TRE=1 인경우
    • rpi2의 경우 ARMv7이면서 SCTLR.TRE=1
  • if (!cpu_is_xsc3()) {
    • free-scale CPU가 아닌경우 execute-never 플래그를 설정한다.
  • if (cpu_arch >= CPU_ARCH_ARMv7 && (cr & CR_TRE)) {
    • 아키텍처가 ARMv7 이상이면서 TRE=1인 경우 TEX 비트를 설정한다. DEVICE_WC의 경우는 Buffrable로 고정시킨다.

 

        /*
         * Now deal with the memory-type mappings
         */
        cp = &cache_policies[cachepolicy];
        vecs_pgprot = kern_pgprot = user_pgprot = cp->pte;
        s2_pgprot = cp->pte_s2;
        hyp_device_pgprot = mem_types[MT_DEVICE].prot_pte;
        s2_device_pgprot = mem_types[MT_DEVICE].prot_pte_s2;

#ifndef CONFIG_ARM_LPAE
        /*
         * We don't use domains on ARMv6 (since this causes problems with
         * v6/v7 kernels), so we must use a separate memory type for user
         * r/o, kernel r/w to map the vectors page.
         */
        if (cpu_arch == CPU_ARCH_ARMv6)
                vecs_pgprot |= L_PTE_MT_VECTORS;

        /*
         * Check is it with support for the PXN bit
         * in the Short-descriptor translation table format descriptors.
         */
        if (cpu_arch == CPU_ARCH_ARMv7 &&
                (read_cpuid_ext(CPUID_EXT_MMFR0) & 0xF) == 4) {
                user_pmd_table |= PMD_PXNTABLE;
        }
#endif
  • 전역 변수 vecs_pgprot, kern_pgprot, user_pgprot에 아키텍처에 해당하는 캐시 속성을 부여한다.
    • rpi2: WBWA 캐시속성
      • .pmd= PMD_SECT_WBWA
        • PMD_SECT_TEX(1) | PMD_SECT_CACHEABLE | PMD_SECT_BUFFERABLE
      • .pte=L_PTE_MT_WRITEALLOC(7)
  • if (cpu_arch == CPU_ARCH_ARMv7 && (read_cpuid_ext(CPUID_EXT_MMFR0) & 0xF) == 4) {

 

        /*
         * ARMv6 and above have extended page tables.
         */
        if (cpu_arch >= CPU_ARCH_ARMv6 && (cr & CR_XP)) {
#ifndef CONFIG_ARM_LPAE
                /*
                 * Mark cache clean areas and XIP ROM read only
                 * from SVC mode and no access from userspace.
                 */
                mem_types[MT_ROM].prot_sect |= PMD_SECT_APX|PMD_SECT_AP_WRITE;
                mem_types[MT_MINICLEAN].prot_sect |= PMD_SECT_APX|PMD_SECT_AP_WRITE;
                mem_types[MT_CACHECLEAN].prot_sect |= PMD_SECT_APX|PMD_SECT_AP_WRITE;
#endif

                /*
                 * If the initial page tables were created with the S bit
                 * set, then we need to do the same here for the same
                 * reasons given in early_cachepolicy().
                 */
                if (initial_pmd_value & PMD_SECT_S) {
                        user_pgprot |= L_PTE_SHARED;
                        kern_pgprot |= L_PTE_SHARED;
                        vecs_pgprot |= L_PTE_SHARED;
                        s2_pgprot |= L_PTE_SHARED;
                        mem_types[MT_DEVICE_WC].prot_sect |= PMD_SECT_S;
                        mem_types[MT_DEVICE_WC].prot_pte |= L_PTE_SHARED;
                        mem_types[MT_DEVICE_CACHED].prot_sect |= PMD_SECT_S;
                        mem_types[MT_DEVICE_CACHED].prot_pte |= L_PTE_SHARED;
                        mem_types[MT_MEMORY_RWX].prot_sect |= PMD_SECT_S;
                        mem_types[MT_MEMORY_RWX].prot_pte |= L_PTE_SHARED;
                        mem_types[MT_MEMORY_RW].prot_sect |= PMD_SECT_S;
                        mem_types[MT_MEMORY_RW].prot_pte |= L_PTE_SHARED;
                        mem_types[MT_MEMORY_DMA_READY].prot_pte |= L_PTE_SHARED;
                        mem_types[MT_MEMORY_RWX_NONCACHED].prot_sect |= PMD_SECT_S;
                        mem_types[MT_MEMORY_RWX_NONCACHED].prot_pte |= L_PTE_SHARED;
                }
        }

아키텍처에 맞게 메모리 타입에대한 접근권한, Share, 캐시 속성을 결정한다.

  • if (cpu_arch >= CPU_ARCH_ARMv6 && (cr & CR_XP)) {
    • ARMv6 이상이면서 SCTLR.CR_XP가 있으면 MT_ROM, MT_MINICLEAN 및 MT_CACHECLEAN 타입에서 svc 모드에서 읽기만 가능하고 userspace에서는 access를 할 수 없도록 한다.
    • rpi2: CR_XP(extended page) 기능이 있다.
  • if (initial_pmd_value & PMD_SECT_S) {
    • 섹션 shared 비트가 있는 경우 관련 타입의 share 속성을 설정한다.

 

        /*
         * Non-cacheable Normal - intended for memory areas that must
         * not cause dirty cache line writebacks when used
         */
        if (cpu_arch >= CPU_ARCH_ARMv6) {
                if (cpu_arch >= CPU_ARCH_ARMv7 && (cr & CR_TRE)) {
                        /* Non-cacheable Normal is XCB = 001 */
                        mem_types[MT_MEMORY_RWX_NONCACHED].prot_sect |=
                                PMD_SECT_BUFFERED;
                } else {
                        /* For both ARMv6 and non-TEX-remapping ARMv7 */
                        mem_types[MT_MEMORY_RWX_NONCACHED].prot_sect |=
                                PMD_SECT_TEX(1);
                }
        } else {
                mem_types[MT_MEMORY_RWX_NONCACHED].prot_sect |= PMD_SECT_BUFFERABLE;
        }

#ifdef CONFIG_ARM_LPAE
        /*
         * Do not generate access flag faults for the kernel mappings.
         */
        for (i = 0; i < ARRAY_SIZE(mem_types); i++) {
                mem_types[i].prot_pte |= PTE_EXT_AF;
                if (mem_types[i].prot_sect)
                        mem_types[i].prot_sect |= PMD_SECT_AF;
        }
        kern_pgprot |= PTE_EXT_AF;
        vecs_pgprot |= PTE_EXT_AF;

        /*
         * Set PXN for user mappings
         */
        user_pgprot |= PTE_EXT_PXN;
#endif

Non-cacheable Normal 메모리에 대한 속성을 설정한다.

  • rpi2: MT_MEMORY_RWX_NONCACHED 타입에 대해 Buffered 속성을 추가한다.
  • LPAE를 사용하는 경우 Access Flag Fault 기능을 사용하기 위해 PTE_EXT_AF 비트를 추가한다.
    • user_pgprot에 PXN 비트도 추가한다.

 

        for (i = 0; i < 16; i++) {
                pteval_t v = pgprot_val(protection_map[i]);
                protection_map[i] = __pgprot(v | user_pgprot);
        }

        mem_types[MT_LOW_VECTORS].prot_pte |= vecs_pgprot;
        mem_types[MT_HIGH_VECTORS].prot_pte |= vecs_pgprot;

        pgprot_user   = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | user_pgprot);
        pgprot_kernel = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG |
                                 L_PTE_DIRTY | kern_pgprot);
        pgprot_s2  = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | s2_pgprot);
        pgprot_s2_device  = __pgprot(s2_device_pgprot);
        pgprot_hyp_device  = __pgprot(hyp_device_pgprot);

        mem_types[MT_LOW_VECTORS].prot_l1 |= ecc_mask;
        mem_types[MT_HIGH_VECTORS].prot_l1 |= ecc_mask;
        mem_types[MT_MEMORY_RWX].prot_sect |= ecc_mask | cp->pmd;
        mem_types[MT_MEMORY_RWX].prot_pte |= kern_pgprot;
        mem_types[MT_MEMORY_RW].prot_sect |= ecc_mask | cp->pmd;
        mem_types[MT_MEMORY_RW].prot_pte |= kern_pgprot;
        mem_types[MT_MEMORY_DMA_READY].prot_pte |= kern_pgprot;
        mem_types[MT_MEMORY_RWX_NONCACHED].prot_sect |= ecc_mask;
        mem_types[MT_ROM].prot_sect |= cp->pmd;

        switch (cp->pmd) {
        case PMD_SECT_WT:
                mem_types[MT_CACHECLEAN].prot_sect |= PMD_SECT_WT;
                break;
        case PMD_SECT_WB:
        case PMD_SECT_WBWA:
                mem_types[MT_CACHECLEAN].prot_sect |= PMD_SECT_WB;
                break;
        }
        pr_info("Memory policy: %sData cache %s\n",
                ecc_mask ? "ECC enabled, " : "", cp->policy);

        for (i = 0; i < ARRAY_SIZE(mem_types); i++) {
                struct mem_type *t = &mem_types[i];
                if (t->prot_l1)
                        t->prot_l1 |= PMD_DOMAIN(t->domain);
                if (t->prot_sect)
                        t->prot_sect |= PMD_DOMAIN(t->domain);
        }
}
  • protection_map[]에 user_pgprot를 추가한다.
    • user_pgprot
      • rpi2: Share, WriteAlloc(0111)
  • MT_LOW_VECTORS 및 MT_HIGH_VECTORS 타입에는 vecs_pgprot를 추가한다.
    • vecs_pgprot
      • rpi2: Share, WriteAlloc(0111)
  • pgprot_user   = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | user_pgprot);
    • pgprot_user에 user_pgprot, Present 및 Young 비트를 추가한다.
  • pgprot_kernel = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY | kern_pgprot);
    • pgprot_kernel에 kern_pgprot, Present, Young 및 Dirty 비트를 추가한다
  • 각 메모리 타입에 맞는 비트를 추가로 설정한다.
  • 마지막으로 각 타입의 속성에 도메인 속성을 추가한다.

 

구조체 및 전역 변수

 

cachepolicy 구조체

arch/arm/mm/mmu.c

struct cachepolicy {
        const char      policy[16];
        unsigned int    cr_mask;
        pmdval_t        pmd;
        pteval_t        pte;
        pteval_t        pte_s2;
};
  • 캐시 타입에 따른 pmd와 pte 엔트리의 속성값이 설정되어 있다.
  • pte_s2의 경우 hyper visor를 사용하는 경우 사용되는 pte 대신 사용하는 값이다.

 

cache_policies[]

static struct cachepolicy cache_policies[] __initdata = {
        {
                .policy         = "uncached",
                .cr_mask        = CR_W|CR_C,
                .pmd            = PMD_SECT_UNCACHED,
                .pte            = L_PTE_MT_UNCACHED,
                .pte_s2         = s2_policy(L_PTE_S2_MT_UNCACHED),
        }, {
                .policy         = "buffered",
                .cr_mask        = CR_C,
                .pmd            = PMD_SECT_BUFFERED,
                .pte            = L_PTE_MT_BUFFERABLE,
                .pte_s2         = s2_policy(L_PTE_S2_MT_UNCACHED),
        }, {
                .policy         = "writethrough",
                .cr_mask        = 0,
                .pmd            = PMD_SECT_WT,
                .pte            = L_PTE_MT_WRITETHROUGH,
                .pte_s2         = s2_policy(L_PTE_S2_MT_WRITETHROUGH),
        }, {
                .policy         = "writeback",
                .cr_mask        = 0,
                .pmd            = PMD_SECT_WB,
                .pte            = L_PTE_MT_WRITEBACK,
                .pte_s2         = s2_policy(L_PTE_S2_MT_WRITEBACK),
        }, {
                .policy         = "writealloc",
                .cr_mask        = 0,
                .pmd            = PMD_SECT_WBWA,
                .pte            = L_PTE_MT_WRITEALLOC,
                .pte_s2         = s2_policy(L_PTE_S2_MT_WRITEBACK),
        }
};

 

기타 전역 변수

arch/arm/mm/mmu.c

static unsigned int cachepolicy __initdata = CPOLICY_WRITEBACK;
static unsigned int ecc_mask __initdata = 0;
static unsigned long initial_pmd_value __initdata = 0;
pgprot_t pgprot_user;
pgprot_t pgprot_kernel;
pgprot_t pgprot_hyp_device;
pgprot_t pgprot_s2;
pgprot_t pgprot_s2_device;

  • cachepolicy
    • 캐시 정책으로 이미 setup_arch() -> setup_processor() -> init_default_cache_policy() 함수에서 설정되었다.
    • rpi2: 4(CPOLICY_WRITEALLOC)
  • ecc_mask
    • 플래그 변수로 early_ecc() 함수에 의해 on으로 설정될 수 있다.
      • cmdline에서 “ecc=on”으로 early_ecc() 함수를 호출할 수 있다.
      • pmd 엔트리의 PMD_PROTECTION(bit 9) 비트를 의미한다.
  • initial_pmd_value
    • pmd 엔트리 초기 값으로 이미 setup_arch() -> setup_processor() -> init_default_cache_policy() 함수에서 설정되었다.
    • rpi2: __cpu_mm_mmu_flags
      • PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_AP_READ | PMD_SECT_AF |  PMD_SECT_TEX(1) | PMD_SECT_CACHEABLE | PMD_SECT_BUFFERABLE | PMD_SECT_S
  • pmd_user_table
    • PMD_TYPE_TABLE | PMD_BIT4 | PMD_DOMAIN(DOMAIN_USER) | PMD_PXNTABLE
      • rpi2: 2015년 12월 커널 v4.5-rc1을 적용할 때 PMD_PXNTABLE 추가된다.
  • pgprot_user
    • L_PTE_SHARED | L_PTE_MT_WRITEALLOC | L_PTE_YOUNG | L_PTE_PRESENT
  • pgprot_kern
    • L_PTE_SHARED | L_PTE_DIRTY | L_PTE_MT_WRITEALLOC | L_PTE_YOUNG | L_PTE_PRESENT
  • pgprot_hyp_device, pgprot_s2, pgprot_s2_device
    • 하이퍼 바이저에서 사용하는 변수로 설명 생략

 

mem_types[]

다음 그림은 rpi2 시스템에서 build_mem_type_table() 함수가 수행된 후 메모리 타입에 대한 속성을 표시하였다.

  • .prot_sect
    • 1차 변환 수행 시 사용하는  섹션 페이지 descriptor 엔트리 속성
  • .prot_l1
    • 2차 변환 수행시 사용하는 1차 페이지 테이블 descriptor 엔트리 속성
  • .prot_pte
    • 2차 변환 수행시 사용하는 2차 페이지 테이블 descriptor 엔트리 속성
  • .prot_pte_s2
    • 하이퍼 바이저에서는 .prot_pte 대신 사용하는 속성인데 아래 그림에서는 생략하였다.

build_mem_type_table-1b

 

위의 비트 속성을 사용하여 아래 그림과 같이 속성 필드의 주요 내용을 별도로 표기하였다.

  • TCM(Tight Coupled Memory)는 캐시와 같이 매우 빠른 메모리이므로 별도의 캐시 및 버퍼를 사용할 필요가 없다.
  • SO(Strongly Ordered Memory)는 Out-of-order memory access를 막기 위해 캐시 및 버퍼를 사용하지 않는다.
  • TRE를 사용하는 경우 PRRR/NMRR 레지스터에 매핑된 속성을 사용한다.

build_mem_type_table-2a

 

참고

답글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.