- 몇 개의 전역 변수에 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