- 몇 개의 전역 변수에 CPU 아키텍처와 밀접한 구조체를 설정하고 재진입 익셉션 핸들러를 위한 스택 할당 등 CPU와 관련된 초기화를 수행한다.
setup_processor()
- read_cpuid_id()
- read_cpuid() 함수를 호출하는 단순 매크로
- MIDR 레지스터를 통해 CPU_ID를 알아온다.
- lookup_processor_type()
- proc_info_list 구조체 포인터를 알아온다.
- rpi2: __v7_ca7mp_proc_info: 위치를 가리킨다.
- __get_cpu_architecture()
- MMFR0 레지스터를 통해 CPU 아키텍처를 알아온다.
- rpi2: 9(CPU_ARCH_ARMv7으로 define 됨)
- MMFR0 레지스터를 통해 CPU 아키텍처를 알아온다.
- 전역변수에 문자열 및 특정 구조체 포인터 대입
- cpu_name
- 라즈베리파이2: 문자열 “ARMv7 Processor”를 가리키는 주소가 담김
- __cpu_architecture
- 정수 값 9(CPU_ARCH_ARMv7으로 define 됨)
- processor
- v7_processor_functions 객체를 가리키는 processor 구조체 포인터
- cpu_tlb
- v7wbi_tlb_fns 객체를 가리키는 cpu_tlb_fns 구조체 포인터
- cpu_user
- v6_user_fns 객체를 가리키는 cpu_user_fns 구조체와 포인터
- cpu_cache
- v7_cache_fns 객체를 가리키는 cpu_cache_fns 구조체 포인터
- rpi2: multi 캐시를 사용하지 않으므로 이 전역 변수는 사용되지 않음
- v7_cache_fns 객체를 가리키는 cpu_cache_fns 구조체 포인터
- 그외 설정되는 전역 변수 들
- elf_hwcap, cachepolicy, erratum_a15_798181_handler, cacheid
- cpu_name
- cpuid_init_hwcaps()
- elf_hwcap 전역 변수에 CPU 아키텍처가 지원하는 하드웨어 캐파(capacity)들을 대입한다.
- init_default_cache_policy()
- cachepolicy 전역 변수에 아키텍처가 지원하는 1차 페이지 테이블(ARM 32bit에서는 PMD)의 캐시 정책 정책 값을 대입한다.
- 라즈베리파이2: 4 (CPOLICY_WRITEALLOC)
- erratum_a15_798181_init()
- CONFIG_ARM_ERRATA_798181가 설정되어 있는 경우만 동작
- erratum_a15_798181_handler 전역 변수에 핸들러 함수 등록
- erratum_a15_798181_broadcast 또는 erratum_a15_798181_partial
- elf_hwcap_fixup()
- elf_hwcap 전역 변수에서 CPU 아키텍처에 따라 HWCAP_TLS 또는 HWCAP_SWP를 제거한다.
- cacheid_init()
- cacheid 전역변수 값에 캐시 형태를 설정한다.
- 라즈베리파이2: CACHEID_VIPT_NONALIASING | CACHEID_VIPT_I_ALIASING
- cpu_init()
- per-CPU 스택 설정
- CPU 아키텍처 전용 초기화 함수 호출
- 재진입 exception 핸들러를 위한 스택 설정
arch/arm/kernel/setup.c
static void __init setup_processor(void)
{
struct proc_info_list *list;
/*
* locate processor in the list of supported processor
* types. The linker builds this table for us from the
* entries in arch/arm/mm/proc-*.S
*/
list = lookup_processor_type(read_cpuid_id());
if (!list) {
pr_err("CPU configuration botched (ID %08x), unable to continue.\n",
read_cpuid_id());
while (1);
}
cpu_name = list->cpu_name;
__cpu_architecture = __get_cpu_architecture();
#ifdef MULTI_CPU
processor = *list->proc;
#endif
#ifdef MULTI_TLB
cpu_tlb = *list->tlb;
#endif
#ifdef MULTI_USER
cpu_user = *list->user;
#endif
#ifdef MULTI_CACHE
cpu_cache = *list->cache;
#endif
pr_info("CPU: %s [%08x] revision %d (ARMv%s), cr=%08lx\n",
cpu_name, read_cpuid_id(), read_cpuid_id() & 15,
proc_arch[cpu_architecture()], get_cr());
snprintf(init_utsname()->machine, __NEW_UTS_LEN + 1, "%s%c",
list->arch_name, ENDIANNESS);
snprintf(elf_platform, ELF_PLATFORM_SIZE, "%s%c",
list->elf_name, ENDIANNESS);
elf_hwcap = list->elf_hwcap;
cpuid_init_hwcaps();
#ifndef CONFIG_ARM_THUMB
elf_hwcap &= ~(HWCAP_THUMB | HWCAP_IDIVT);
#endif
#ifdef CONFIG_MMU
init_default_cache_policy(list->__cpu_mm_mmu_flags);
#endif
erratum_a15_798181_init();
elf_hwcap_fixup();
cacheid_init();
cpu_init();
}
read_cpuid()
- MIDR 레지스터 값을 리턴한다.
arch/arm/include/asm/cputype.h
#define read_cpuid(reg) \
({ \
unsigned int __val; \
asm("mrc p15, 0, %0, c0, c0, " __stringify(reg) \
: "=r" (__val) \
: \
: "cc"); \
__val; \
})
__stringify() 매크로
- 인수에 문자열을 그대로 사용할 수 있도록 한다.
- 컴파일 시 -DFOO=bar 옵션을 사용한 경우 __stringify(FOO)를 사용하는 경우 이를 bar로 변경해준다.
include/linux/stringify.h
/* Indirect stringification. Doing two levels allows the parameter to be a * macro itself. For example, compile with -DFOO=bar, __stringify(FOO) * converts to "bar". */ #define __stringify_1(x...) #x #define __stringify(x...) __stringify_1(x)
lookup_processor_type()
- lookup_processor_type() 함수를 통해 proc_info_list 구조체 포인터를 알아온다.
arch/arm/kernel/head-common.S
/*
* This provides a C-API version of __lookup_processor_type
*/
ENTRY(lookup_processor_type)
stmfd sp!, {r4 - r6, r9, lr}
mov r9, r0
bl __lookup_processor_type
mov r0, r5
ldmfd sp!, {r4 - r6, r9, pc}
ENDPROC(lookup_processor_type)
proc_info_list 구조체
- head.S에서 CPU 정보를 읽었었다.
- 참고: kernel/head.S – __lookup_processor_type: | 문c
- .init.data 섹션에 저장됨
- 항목 설명과 라즈베리파이2에서의 값:
- cpu_val:
- 0x410f_c070
- cpu_mask:
- 0xff0f_fff0
- __cpu_mm_mmu_flags: 메모리 주소용 페이지 엔트리 속성
- MD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_AP_READ | PMD_SECT_AF | PMD_FLAGS_SMP
- __cpu_io_mmu_flags: 입출력장치용 페이지 엔트리 속성
- PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_AP_READ | PMD_SECT_AF
- __cpu_flush:
- __v7_ca7mp_setup() 함수 포인터
- 내부에 CPU 설정 및 캐시 flush, ttbr 설정 등 캐시와 관련된 설정도 하는 루틴이 담겨있다.
- __v7_ca7mp_setup() 함수 포인터
- arch_name:
- 문자열 “armv7″을 가리키는 주소가 담김 (.rodata 섹션)
- elf_name:
- 문자열 “v7″을 가리키는 주소가 담김 (.rodata 섹션)
- elf_hwcap:
- 프로세서 아키텍처의 하드웨어 캐파(h/w capability)
- rpi2: 0x168096
- HWCAP_LPAE(b20) | HWCAP_IDIVT(b18) | HWCAP_IDIVA(b17) | HWCAP_TLS(b15) | HWCAP_EDSP(b7) | HWCAP_FAST_MULT(b4) | HWCAP_THUMB(b2) | HWCAP_HALF(b1)
- cpu_name:
- 문자열 “ARMv7 Processor”를 가리키는 주소가 담김
- proc:
- v7_processor_functions 객체를 가리키는 processor 구조체 포인터
- tlb:
- v7wbi_tlb_fns 객체를 가리키는 cpu_tlb_fns 구조체 포인터
- user:
- v6_user_fns 객체를 가리키는 cpu_user_fns 구조체 포인터
- cache:
- v7_cache_fns 객체를 가리키는 cpu_cache_fns 구조체 포인터
- cpu_val:
arch/arm/include/asm/procinfo.h
/*
* Note! struct processor is always defined if we're
* using MULTI_CPU, otherwise this entry is unused,
* but still exists.
*
* NOTE! The following structure is defined by assembly
* language, NOT C code. For more information, check:
* arch/arm/mm/proc-*.S and arch/arm/kernel/head.S
*/
struct proc_info_list {
unsigned int cpu_val;
unsigned int cpu_mask;
unsigned long __cpu_mm_mmu_flags; /* used by head.S */
unsigned long __cpu_io_mmu_flags; /* used by head.S */
unsigned long __cpu_flush; /* used by head.S */
const char *arch_name;
const char *elf_name;
unsigned int elf_hwcap;
const char *cpu_name;
struct processor *proc;
struct cpu_tlb_fns *tlb;
struct cpu_user_fns *user;
struct cpu_cache_fns *cache;
};
__v7_ca7mp_proc_info
- .proc.info.init 섹션에 저장된다.
- proc_info_list 구조체와 동일
- 라즈베리파이2: ARM Cortex A7
arch/arm/mm/proc-v7.S
/*
* ARM Ltd. Cortex A7 processor.
*/
.type __v7_ca7mp_proc_info, #object
__v7_ca7mp_proc_info:
.long 0x410fc070
.long 0xff0ffff0
__v7_proc __v7_ca7mp_setup
.size __v7_ca7mp_proc_info, . - __v7_ca7mp_proc_info
__v7_proc 매크로
arch/arm/mm/proc-v7.S
- 라즈베리파이2:
- cpu_arch_name: 문자열 “armv7″을 가리키는 주소가 담김 (.rodata 섹션)
- cpu_elf_name: 문자열 “v7″을 가리키는 주소가 담김 (.rodata 섹션)
.macro __v7_proc initfunc, mm_mmuflags = 0, io_mmuflags = 0, hwcaps = 0, proc_fns = v7_processor_functions
ALT_SMP(.long PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_AP_READ | \
PMD_SECT_AF | PMD_FLAGS_SMP | \mm_mmuflags)
ALT_UP(.long PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_AP_READ | \
PMD_SECT_AF | PMD_FLAGS_UP | \mm_mmuflags)
.long PMD_TYPE_SECT | PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ | PMD_SECT_AF | \io_mmuflags
W(b) \initfunc
.long cpu_arch_name
.long cpu_elf_name
.long HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB | HWCAP_FAST_MULT | \
HWCAP_EDSP | HWCAP_TLS | \hwcaps
.long cpu_v7_name
.long \proc_fns
.long v7wbi_tlb_fns
.long v6_user_fns
.long v7_cache_fns
.endm
define_processor_functions 매크로
- <name>_processor_functions라는 오브젝트를 만들어낸다.
- 라즈베리파이2: v7_processor_functions
- processor 구조체와 동일
- .init.data 섹션에 저장됨
arch/arm/mm/proc-v7.S
@ define struct processor (see <asm/proc-fns.h> and proc-macros.S)
define_processor_functions v7, dabort=v7_early_abort, pabort=v7_pabort, suspend=1
arch/arm/mm/proc-macros.S
.macro define_processor_functions name:req, dabort:req, pabort:req, nommu=0, suspend=0
.type \name\()_processor_functions, #object
.align 2
ENTRY(\name\()_processor_functions)
.word \dabort
.word \pabort
.word cpu_\name\()_proc_init
.word cpu_\name\()_proc_fin
.word cpu_\name\()_reset
.word cpu_\name\()_do_idle
.word cpu_\name\()_dcache_clean_area
.word cpu_\name\()_switch_mm
.if \nommu
.word 0
.else
.word cpu_\name\()_set_pte_ext
.endif
.if \suspend
.word cpu_\name\()_suspend_size
#ifdef CONFIG_ARM_CPU_SUSPEND
.word cpu_\name\()_do_suspend
.word cpu_\name\()_do_resume
#else
.word 0
.word 0
#endif
.else
.word 0
.word 0
.word 0
.endif
.size \name\()_processor_functions, . - \name\()_processor_functions
.endm
processor 구조체
arch/arm/include/asm/proc-fns.h
/*
* Don't change this structure - ASM code relies on it.
*/
extern struct processor {
/* MISC
* get data abort address/flags
*/
void (*_data_abort)(unsigned long pc);
/*
* Retrieve prefetch fault address
*/
unsigned long (*_prefetch_abort)(unsigned long lr);
/*
* Set up any processor specifics
*/
void (*_proc_init)(void);
/*
* Disable any processor specifics
*/
void (*_proc_fin)(void);
/*
* Special stuff for a reset
*/
void (*reset)(unsigned long addr) __attribute__((noreturn));
/*
* Idle the processor
*/
int (*_do_idle)(void);
/*
* Processor architecture specific
*/
/*
* clean a virtual address range from the
* D-cache without flushing the cache.
*/
void (*dcache_clean_area)(void *addr, int size);
/*
* Set the page table
*/
void (*switch_mm)(phys_addr_t pgd_phys, struct mm_struct *mm);
/*
* Set a possibly extended PTE. Non-extended PTEs should
* ignore 'ext'.
*/
#ifdef CONFIG_ARM_LPAE
void (*set_pte_ext)(pte_t *ptep, pte_t pte);
#else
void (*set_pte_ext)(pte_t *ptep, pte_t pte, unsigned int ext);
#endif
/* Suspend/resume */
unsigned int suspend_size;
void (*do_suspend)(void *);
void (*do_resume)(void *);
} processor;
cpu_user_fns 구조체
arch/arm/mm/copypage-v6.c
struct cpu_user_fns v6_user_fns __initdata = {
.cpu_clear_user_highpage = v6_clear_user_highpage_nonaliasing,
.cpu_copy_user_highpage = v6_copy_user_highpage_nonaliasing,
};
define_tlb_functions 매크로
- cpu_tlb_fns 구조체와 동일
- v7wbi_tlb_fns 오브젝트가 만들어진다.
- .init.data 섹션에 저장됨
arch/arm/mm/tlb-v7.S
/* define struct cpu_tlb_fns (see <asm/tlbflush.h> and proc-macros.S) */
define_tlb_functions v7wbi, v7wbi_tlb_flags_up, flags_smp=v7wbi_tlb_flags_smp
arch/arm/mm/proc-macros.S
.macro define_tlb_functions name:req, flags_up:req, flags_smp
.type \name\()_tlb_fns, #object
ENTRY(\name\()_tlb_fns)
.long \name\()_flush_user_tlb_range
.long \name\()_flush_kern_tlb_range
.ifnb \flags_smp
ALT_SMP(.long \flags_smp )
ALT_UP(.long \flags_up )
.else
.long \flags_up
.endif
.size \name\()_tlb_fns, . - \name\()_tlb_fns
.endm
cpu_tlb_fns 구조체
arch/arm/include/asm/tlbflush.h
struct cpu_tlb_fns {
void (*flush_user_range)(unsigned long, unsigned long, struct vm_area_struct *);
void (*flush_kern_range)(unsigned long, unsigned long);
unsigned long tlb_flags;
};
define_cache_functions 매크로
- cpu_cache_fns 구조체와 동일
- 라즈베리파이2: v7_cache_fns 오브젝트가 만들어진다.
- .init.data 섹션에 저장됨
arch/arm/mm/cache-v7.S
@ define struct cpu_cache_fns (see <asm/cacheflush.h> and proc-macros.S)
define_cache_functions v7
arch/arm/mm/proc-macros.S
.macro define_cache_functions name:req
.align 2
.type \name\()_cache_fns, #object
ENTRY(\name\()_cache_fns)
.long \name\()_flush_icache_all
.long \name\()_flush_kern_cache_all
.long \name\()_flush_kern_cache_louis
.long \name\()_flush_user_cache_all
.long \name\()_flush_user_cache_range
.long \name\()_coherent_kern_range
.long \name\()_coherent_user_range
.long \name\()_flush_kern_dcache_area
.long \name\()_dma_map_area
.long \name\()_dma_unmap_area
.long \name\()_dma_flush_range
.size \name\()_cache_fns, . - \name\()_cache_fns
.endm
cpu_cache_fns 구조체
arch/arm/include/asm/cacheflush.h
struct cpu_cache_fns {
void (*flush_icache_all)(void);
void (*flush_kern_all)(void);
void (*flush_kern_louis)(void);
void (*flush_user_all)(void);
void (*flush_user_range)(unsigned long, unsigned long, unsigned int);
void (*coherent_kern_range)(unsigned long, unsigned long);
int (*coherent_user_range)(unsigned long, unsigned long);
void (*flush_kern_dcache_area)(void *, size_t);
void (*dma_map_area)(const void *, size_t, int);
void (*dma_unmap_area)(const void *, size_t, int);
void (*dma_flush_range)(const void *, const void *);
};
cpuid_init_hwcaps()
- ARMv7 이상의 CPU 아키텍처에서만 동작한다.
- read_cpuid_ext()를 사용하여 레지스터 ISAR0.Devide_instrs를 읽어와서 아래 기능을 지원하면 elf_hwcap에 추가
- HWCAP_IDIVA
- 이 기능이 선택되면 HWCPA_IDIVT도 자동 선택된다.
- HWCPA_IDIVT
- HWCAP_IDIVA
- read_cpuid_ext()를 사용하여 레지스터 MMFR0.vmsa를 읽어와서 값이 5(Long-descriptor translation table 지원)이상인 경우 hwcap에 추가
- HWCAP_LPAE
- 라즈베리파이2(bcm2709)의 경우 LPAE가 support된다.
- HWCAP_LPAE
- 라즈베리파이2: 0x168097
- HWCAP_LPAE(b20) | HWCAP_IDIVT(b18) | HWCAP_IDIVA(b17) | HWCAP_TLS(b15) | HWCAP_EDSP(b7) | HWCAP_FAST_MULT(b4) | HWCAP_THUMB(b2) | HWCAP_HALF(b1) | HWCAP_SWP(b0)
arch/arm/kernel/setup.c
static void __init cpuid_init_hwcaps(void)
{
unsigned int divide_instrs, vmsa;
if (cpu_architecture() < CPU_ARCH_ARMv7)
return;
divide_instrs = (read_cpuid_ext(CPUID_EXT_ISAR0) & 0x0f000000) >> 24;
switch (divide_instrs) {
case 2:
elf_hwcap |= HWCAP_IDIVA;
case 1:
elf_hwcap |= HWCAP_IDIVT;
}
/* LPAE implies atomic ldrd/strd instructions */
vmsa = (read_cpuid_ext(CPUID_EXT_MMFR0) & 0xf) >> 0;
if (vmsa >= 5)
elf_hwcap |= HWCAP_LPAE;
}
elf_hwcap_fixup()
- read_cpu_id()를 사용하여 레지스터 MIDR을 읽어 cpu id값을 읽어온다.
- read_cpuid_part()를 사용하여 레지스터 MIDR의 CPU 파트를 비교하여 ARM1136이고 리비전코드가 r1p0 미만인 경우 elf_hwcap 에 HWCAP_TLS를 제거하고 루틴을 빠져나간다. 루틴을 빠져나가는 이유는 해당 조건의 CPU는 exclusive loads and store(ldrex/strex)를 지원하는 명령들이 없기 때문에 다음으로 이어지는 루틴을 실행할 필요가 없다.
- rpi2: TLS 레지스터를 지원한다. (CP15의 TPIDRURO 레지스터)
- read_cpuid_ext()를 사용하여 레지스터 ISAR3.SynchPrim_instrs 값 + ISAR4.SynchPrim_instrs를 읽어와서 0x13보다 크면 LDREX/STREX 명령이 지원되는 것으로 판단하여 elf_hwcap 에 HWCAP_SWP를 제거한다.
- 라즈베리파이2: 0x168096
- HWCAP_LPAE(b20) | HWCAP_IDIVT(b18) | HWCAP_IDIVA(b17) | HWCAP_TLS(b15) | HWCAP_EDSP(b7) | HWCAP_FAST_MULT(b4) | HWCAP_THUMB(b2) | HWCAP_HALF(b1) |
HWCAP_SWP(b0)
- HWCAP_LPAE(b20) | HWCAP_IDIVT(b18) | HWCAP_IDIVA(b17) | HWCAP_TLS(b15) | HWCAP_EDSP(b7) | HWCAP_FAST_MULT(b4) | HWCAP_THUMB(b2) | HWCAP_HALF(b1) |
arch/arm/kernel/setup.c
static void __init elf_hwcap_fixup(void)
{
unsigned id = read_cpuid_id();
unsigned sync_prim;
/*
* HWCAP_TLS is available only on 1136 r1p0 and later,
* see also kuser_get_tls_init.
*/
if (read_cpuid_part() == ARM_CPU_PART_ARM1136 &&
((id >> 20) & 3) == 0) {
elf_hwcap &= ~HWCAP_TLS;
return;
}
/* Verify if CPUID scheme is implemented */
if ((id & 0x000f0000) != 0x000f0000)
return;
/*
* If the CPU supports LDREX/STREX and LDREXB/STREXB,
* avoid advertising SWP; it may not be atomic with
* multiprocessing cores.
*/
sync_prim = ((read_cpuid_ext(CPUID_EXT_ISAR3) >> 8) & 0xf0) |
((read_cpuid_ext(CPUID_EXT_ISAR4) >> 20) & 0x0f);
if (sync_prim >= 0x13)
elf_hwcap &= ~HWCAP_SWP;
}
init_default_cache_policy()
- 전역 변수 unsigned int 선언된 cachepolicy 값 결정
- 메모리 속성(pmd 엔트리 속성)을 알아내고 미리 정의되어 있는 cache_policies[].pmd와 일치하는 배열 인덱스를 찾아낸다.
- 라즈베리파이2: cachepolicy <- 구조체 배열의 인덱스인 4 (CPOLICY_WRITEALLOC 캐시 정책)
- .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 long initial_pmd_value __initdata = 0;
/*
* Initialise the cache_policy variable with the initial state specified
* via the "pmd" value. This is used to ensure that on ARMv6 and later,
* the C code sets the page tables up with the same policy as the head
* assembly code, which avoids an illegal state where the TLBs can get
* confused. See comments in early_cachepolicy() for more information.
*/
void __init init_default_cache_policy(unsigned long pmd)
{
int i;
initial_pmd_value = pmd;
pmd &= PMD_SECT_TEX(1) | PMD_SECT_BUFFERABLE | PMD_SECT_CACHEABLE;
for (i = 0; i < ARRAY_SIZE(cache_policies); i++)
if (cache_policies[i].pmd == pmd) {
cachepolicy = i;
break;
}
if (i == ARRAY_SIZE(cache_policies))
pr_err("ERROR: could not find cache policy\n");
}
cacheid_init()
- read_cpuid_cachetype()를 통해 캐시 타입을 읽어와서 cacheid에 data 캐시와 instruction 캐시 형태를 설정한다.
- cacheid 전역변수 값에 캐시 형태를 설정한다.
- bit0~2: d-cache 플래그
- bit3~5: i-cache 플래그
- 라즈베리파이2:
- L1 d-cache: CACHEID_VIPT_NONALIASING
- L1 i-cache: CACHEID_VIPT_I_ALIASING
- L1 명령 캐시가 캐시 aliasing이 필요한 캐시이다.
- cache aliasing 용어 대신 page coloring 또는 cache coloring이라는 용어를 사용하기도 한다.
- L1 명령 캐시가 캐시 aliasing이 필요한 캐시이다.
- cacheid 관련 define
- CACHEID_VIVT
- CACHEID_VIPT_NONALIASING
- CACHEID_VIPT_ALIASING
- CACHEID_VIPT
- CACHEID_ASID_TAGGED
- CACHEID_VIPT_I_ALIASING
- CACHEID_PIPT
arch/arm/kernel/setup.c
static void __init cacheid_init(void)
{
unsigned int arch = cpu_architecture();
if (arch == CPU_ARCH_ARMv7M) {
cacheid = 0;
} else if (arch >= CPU_ARCH_ARMv6) {
unsigned int cachetype = read_cpuid_cachetype();
if ((cachetype & (7 << 29)) == 4 << 29) {
/* ARMv7 register format */
arch = CPU_ARCH_ARMv7;
cacheid = CACHEID_VIPT_NONALIASING;
switch (cachetype & (3 << 14)) {
case (1 << 14):
cacheid |= CACHEID_ASID_TAGGED;
break;
case (3 << 14):
cacheid |= CACHEID_PIPT;
break;
}
} else {
arch = CPU_ARCH_ARMv6;
if (cachetype & (1 << 23))
cacheid = CACHEID_VIPT_ALIASING;
else
cacheid = CACHEID_VIPT_NONALIASING;
}
if (cpu_has_aliasing_icache(arch))
cacheid |= CACHEID_VIPT_I_ALIASING;
} else {
cacheid = CACHEID_VIVT;
}
pr_info("CPU: %s data cache, %s instruction cache\n",
cache_is_vivt() ? "VIVT" :
cache_is_vipt_aliasing() ? "VIPT aliasing" :
cache_is_vipt_nonaliasing() ? "PIPT / VIPT nonaliasing" : "unknown",
cache_is_vivt() ? "VIVT" :
icache_is_vivt_asid_tagged() ? "VIVT ASID tagged" :
icache_is_vipt_aliasing() ? "VIPT aliasing" :
icache_is_pipt() ? "PIPT" :
cache_is_vipt_nonaliasing() ? "VIPT nonaliasing" : "unknown");
}
cpu_init()
- 하나의 CPU 설정을 초기화한다.
- set_my_cpu_offset()
- TPIDRPRW(Thread ID 레지스터)를 사용하여 per-CPU 스택을 설정한다.
- cpu_proc_init()
- 아키텍처에 따른 프로세서 초기화 루틴 수행
- 아키텍처가 커널 빌드 시 고정된 경우
- #define cpu_proc_init __glue(CPU_NAME,_proc_init)
- #define __glue(name,fn) ____glue(name,fn)
- #define ____glue(name,fn) name##fn
- #define __glue(name,fn) ____glue(name,fn)
- #define cpu_proc_init __glue(CPU_NAME,_proc_init)
- 아키텍처가 커널 빌드 시 고정되지 않고 MULTI_CPU를 사용한 경우 (Device Tree를 사용하는 경우 MULTI_CPU를 사용한다)
- #define cpu_proc_init processor._proc_init
- cpu_v7_proc_init()
- 함수 내부에서는 아무것도 하지 않고 그냥 함수를 리턴한다.
- cpu_v7_proc_init()
- #define cpu_proc_init processor._proc_init
- 라즈베리파이2:
- CONFIG_CPU_V7를 사용하면 MULTI_CPU가 define 되고 따라서 processor._proc_init 구조체를 통하여 cpu_v7_proc_init() 호출
- 아키텍처가 커널 빌드 시 고정된 경우
- 아키텍처에 따른 프로세서 초기화 루틴 수행
- 아래 모드들 전환한 후 재진입 exception 핸들러들을 위해 스택을 설정하고 마지막으로 다시 SVC_MODE로 돌아온다.
- IRQ_MODE
- ABT_MODE
- UND_MODE
- FIQ_MODE
arch/arm/kernel/setup.c
/*
* cpu_init - initialise one CPU.
*
* cpu_init sets up the per-CPU stacks.
*/
void notrace cpu_init(void)
{
#ifndef CONFIG_CPU_V7M
unsigned int cpu = smp_processor_id();
struct stack *stk = &stacks[cpu];
if (cpu >= NR_CPUS) {
pr_crit("CPU%u: bad primary CPU number\n", cpu);
BUG();
}
/*
* This only works on resume and secondary cores. For booting on the
* boot cpu, smp_prepare_boot_cpu is called after percpu area setup.
*/
set_my_cpu_offset(per_cpu_offset(cpu));
cpu_proc_init();
/*
* Define the placement constraint for the inline asm directive below.
* In Thumb-2, msr with an immediate value is not allowed.
*/
#ifdef CONFIG_THUMB2_KERNEL
#define PLC "r"
#else
#define PLC "I"
#endif
/*
* setup stacks for re-entrant exception handlers
*/
__asm__ (
"msr cpsr_c, %1\n\t"
"add r14, %0, %2\n\t"
"mov sp, r14\n\t"
"msr cpsr_c, %3\n\t"
"add r14, %0, %4\n\t"
"mov sp, r14\n\t"
"msr cpsr_c, %5\n\t"
"add r14, %0, %6\n\t"
"mov sp, r14\n\t"
"msr cpsr_c, %7\n\t"
"add r14, %0, %8\n\t"
"mov sp, r14\n\t"
"msr cpsr_c, %9"
:
: "r" (stk),
PLC (PSR_F_BIT | PSR_I_BIT | IRQ_MODE),
"I" (offsetof(struct stack, irq[0])),
PLC (PSR_F_BIT | PSR_I_BIT | ABT_MODE),
"I" (offsetof(struct stack, abt[0])),
PLC (PSR_F_BIT | PSR_I_BIT | UND_MODE),
"I" (offsetof(struct stack, und[0])),
PLC (PSR_F_BIT | PSR_I_BIT | FIQ_MODE),
"I" (offsetof(struct stack, fiq[0])),
PLC (PSR_F_BIT | PSR_I_BIT | SVC_MODE)
: "r14");
#endif
}
관련 전역 변수
arch/arm/kernel/setup.c
unsigned int processor_id; EXPORT_SYMBOL(processor_id); unsigned int __machine_arch_type __read_mostly; EXPORT_SYMBOL(__machine_arch_type); unsigned int cacheid __read_mostly; EXPORT_SYMBOL(cacheid); unsigned int __atags_pointer __initdata; unsigned int system_rev; EXPORT_SYMBOL(system_rev); unsigned int system_serial_low; EXPORT_SYMBOL(system_serial_low); unsigned int system_serial_high; EXPORT_SYMBOL(system_serial_high); unsigned int elf_hwcap __read_mostly; EXPORT_SYMBOL(elf_hwcap); unsigned int elf_hwcap2 __read_mostly; EXPORT_SYMBOL(elf_hwcap2); #ifdef MULTI_CPU struct processor processor __read_mostly; #endif #ifdef MULTI_TLB struct cpu_tlb_fns cpu_tlb __read_mostly; #endif #ifdef MULTI_USER struct cpu_user_fns cpu_user __read_mostly; #endif #ifdef MULTI_CACHE struct cpu_cache_fns cpu_cache __read_mostly; #endif #ifdef CONFIG_OUTER_CACHE struct outer_cache_fns outer_cache __read_mostly; EXPORT_SYMBOL(outer_cache); #endif
__read_mostly
참고: 함수선언부 관련 매크로-1 | 문c




