메모리 타입별로 페이지 테이블을 구성하는 엔트리에 대한 속성을 구성한다.
커맨드라인 파라메터
- “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()
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
- SMP 시스템인 경우
/*
* 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)
- .pmd= PMD_SECT_WBWA
- rpi2: WBWA 캐시속성
- if (cpu_arch == CPU_ARCH_ARMv7 && (read_cpuid_ext(CPUID_EXT_MMFR0) & 0xF) == 4) {
- ARMv7이면서 MMFR0.vmsa가 4이면 PMD_PXNTABLE(bit 0)을 사용한다.
- rpi2: MMFR0.vmsa=5 (Cortex-A7)
- 2015년 12월 커널 v4.5-rc1에서 “== 4″를 “>= 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)
- user_pgprot
- MT_LOW_VECTORS 및 MT_HIGH_VECTORS 타입에는 vecs_pgprot를 추가한다.
- vecs_pgprot
- rpi2: Share, WriteAlloc(0111)
- vecs_pgprot
- 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) 비트를 의미한다.
- 플래그 변수로 early_ecc() 함수에 의해 on으로 설정될 수 있다.
- 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 추가된다.
- PMD_TYPE_TABLE | PMD_BIT4 | PMD_DOMAIN(DOMAIN_USER) | 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 대신 사용하는 속성인데 아래 그림에서는 생략하였다.
위의 비트 속성을 사용하여 아래 그림과 같이 속성 필드의 주요 내용을 별도로 표기하였다.
- TCM(Tight Coupled Memory)는 캐시와 같이 매우 빠른 메모리이므로 별도의 캐시 및 버퍼를 사용할 필요가 없다.
- SO(Strongly Ordered Memory)는 Out-of-order memory access를 막기 위해 캐시 및 버퍼를 사용하지 않는다.
- TRE를 사용하는 경우 PRRR/NMRR 레지스터에 매핑된 속성을 사용한다.
참고
- 페이지 테이블 엔트리 속성 | 문c



항상 잘보고 있습니다. 감사합니다.
별 말씀을요~ 저두 감사합니다.