setup_processor()

  • 몇 개의 전역 변수에 CPU 아키텍처와 밀접한 구조체를 설정하고 재진입 익셉션 핸들러를 위한 스택 할당 등 CPU와 관련된 초기화를 수행한다.

setup_processor_d

 

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 됨)
  • 전역변수에 문자열 및 특정 구조체 포인터 대입
    • 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 구조체 포인터
    • 그외 설정되는 전역 변수 들
      • elf_hwcap, cachepolicy, erratum_a15_798181_handler, cacheid
  • 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 설정 등 캐시와 관련된 설정도 하는 루틴이 담겨있다.
    • arch_name:
      • 문자열 “armv7″을 가리키는 주소가 담김 (.rodata 섹션)
    • elf_name:
      • 문자열 “v7″을 가리키는 주소가 담김 (.rodata 섹션)
    • elf_hwcap:
      • 프로세서 아키텍처의 하드웨어 캐파(h/w capability)
      • HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB | HWCAP_FAST_MULT | HWCAP_EDSP | HWCAP_TLS
    • 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 구조체 포인터

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 또는 HWCAP_IDIVT
  • read_cpuid_ext()를 사용하여 레지스터 MMFR0.vmsa를 읽어와서 값이 5(Long-descriptor translation table 지원)이상인 경우 hwcap에 추가
    • HWCAP_LPAE
    • 라즈베리파이2(bcm2709)의 경우 MMFR0 값을 확인하지 않아 정확히 LPAE가 support가 되는지 알 수 없지만 일단 support되는 것으로 가정한다.
  • 라즈베리파이2: HWCAP_LPAE(b20) | 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)를 지원하는 명령들이 없기 때문에 다음으로 이어지는 루틴을 실행할 필요가 없다.
  • read_cpuid_ext()를 사용하여 레지스터 ISAR3.SynchPrim_instrs 값 + ISAR4.SynchPrim_instrs를 읽어와서 0x13보다 크면 LDREX/STREX 명령이 지원되는 것으로 판단하여 elf_hwcap 에 HWCAP_SWP를 제거한다.
  • 라즈베리파이2: HWCAP_LPAE(b20) | 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 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
  • 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
      • 아키텍처가 커널 빌드 시 고정되지 않고 MULTI_CPU를 사용한 경우
        • #define cpu_proc_init                processor._proc_init
          • cpu_v7_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

답글 남기기

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