hyp_mode_check()

하이퍼바이저에서 부팅된 경우인지 아닌지 메시지를 출력하여 안내한다.

  • 하이퍼바이저에서 부팅
    • “CPU: All CPU(s) started in HYP mode.”
    • “CPU: Virtualization extensions available.”
  • 일반 부팅
    • “CPU: All CPU(s) started in SVC mode.”

 

hyp_mode_check()

arch/arm/kernel/setup.c

#ifndef ZIMAGE
void __init hyp_mode_check(void)
{
#ifdef CONFIG_ARM_VIRT_EXT
        sync_boot_mode();

        if (is_hyp_mode_available()) {
                pr_info("CPU: All CPU(s) started in HYP mode.\n");
                pr_info("CPU: Virtualization extensions available.\n");
        } else if (is_hyp_mode_mismatched()) {
                pr_warn("CPU: WARNING: CPU(s) started in wrong/inconsistent modes (primary CPU mode 0x%x)\n",
                        __boot_cpu_mode & MODE_MASK);
                pr_warn("CPU: This may indicate a broken bootloader or firmware.\n");
        } else       
                pr_info("CPU: All CPU(s) started in SVC mode.\n");
#endif               
}
#endif
  • #ifndef ZIMAGE
    • ZIMAGE는 compressed 이미지를 빌드 시 assembly 루틴에서는 true이지만 그 외 decompressed 커널 이미지를 빌드할 때에는 항상 false로 되어 있다.
  •  sync_boot_mode()
    • CONFIG_ARM_VIRT_EXT 커널 옵션이 사용되는 경우에 한해 전역 __boot_cpu_mode 객체 영역에 대해 inner & outer 캐시 flush를 수행한다.
  • if (is_hyp_mode_available()) {
    • 커널 진입 시 CPU 모드가 하이퍼모드였으면
  • } else if (is_hyp_mode_mismatched()) {
    • 커널 진입 시 CPU 모드가 하이퍼 모드 또는 SVC 모드가 아닌 경우

 

sync_boot_mode()

arch/arm/include/asm/virt.h

/*
 * __boot_cpu_mode records what mode the primary CPU was booted in.
 * A correctly-implemented bootloader must start all CPUs in the same mode:
 * if it fails to do this, the flag BOOT_CPU_MODE_MISMATCH is set to indicate
 * that some CPU(s) were booted in a different mode.
 *
 * This allows the kernel to flag an error when the secondaries have come up.
 */
extern int __boot_cpu_mode;

static inline void sync_boot_mode(void)
{
        /*  
         * As secondaries write to __boot_cpu_mode with caches disabled, we
         * must flush the corresponding cache entries to ensure the visibility
         * of their writes.
         */
        sync_cache_r(&__boot_cpu_mode);
}
  • 전역 __boot_cpu_mode 변수 영역에 대해 inner & outer 캐시 flush를 수행한다.

 

sync_cache_r()

arch/arm/include/asm/cacheflush.h

#define sync_cache_r(ptr) __sync_cache_range_r(ptr, sizeof *(ptr))
  • long으로 선언된 전역 __boot_cpu_mode 변수 위치에 대해 inner & outer 캐시 flush를 수행한다.
    • long 값이므로 32bit 시스템에서는 4 byte , 64bit 시스템에서는 8 byte 영역만큼에 대해 flush를 수행하게 된다.

 

__sync_cache_range_r()

arch/arm/include/asm/cacheflush.h

/*
 * Ensure preceding writes to *p by other CPUs are visible to
 * subsequent reads by this CPU.  We must be careful not to
 * discard data simultaneously written by another CPU, hence the
 * usage of flush rather than invalidate operations.
 */
static inline void __sync_cache_range_r(volatile void *p, size_t size)
{
        char *_p = (char *)p;

#ifdef CONFIG_OUTER_CACHE
        if (outer_cache.flush_range) {
                /*
                 * Ensure dirty data migrated from other CPUs into our cache
                 * are cleaned out safely before the outer cache is cleaned:
                 */
                __cpuc_clean_dcache_area(_p, size);

                /* Clean and invalidate stale data for *p from outer ... */
                outer_flush_range(__pa(_p), __pa(_p + size));
        }
#endif

        /* ... and inner cache: */
        __cpuc_flush_dcache_area(_p, size);
}

지정된 range에 대해 inner & outer 캐시 flush를 수행한다.

  • CONFIG_OUTER_CACHE
    • outer 캐시가 사용되는 시스템에서 사용하는 커널 옵션
    • rpi2: outer 캐시를 사용하지 않는다.
  • if (outer_cache.flush_range) {
    • outer 캐시를 사용하는 시스템에서 flush_range에 연결된 함수가 존재하는 경우
  • __cpuc_clean_dcache_area(_p, size);
    • 지정 range의 outer 캐시를 flush하기 전에 먼저 cpu 캐시 즉 inner 캐시를 먼저 clean 작업을 해야한다.
    • ARMv7:
      • inner d-cache 영역에 대해 clean 오퍼레이션 대신 flush를 구현하였다.
  • outer_flush_range(__pa(_p), __pa(_p + size));
    • 지정 range의 outer cache에 대한 flush(clean & invalidate)를 수행한다.
  • __cpuc_flush_dcache_area(_p, size);
    • 지정 range의 inner d-cache에 대한 flush(clean & invalidate)를 수행한다.

 

arch/arm/include/asm/cacheflush.h

/*
 * There is no __cpuc_clean_dcache_area but we use it anyway for
 * code intent clarity, and alias it to __cpuc_flush_dcache_area.
 */
#define __cpuc_clean_dcache_area __cpuc_flush_dcache_area
  • ARMv7에서는 clean 구현을 따로 준비하지 않았고 따라서 flush 구현을 사용한다.

 

arch/arm/include/asm/cacheflush.h

#define __cpuc_flush_dcache_area        cpu_cache.flush_kern_dcache_area
  • ARMv7: v7_flush_kern_dcache_area()

 

outer_flush_range()

arch/arm/include/asm/outercache.h

/**     
 * outer_flush_range - clean and invalidate outer cache lines
 * @start: starting physical address, inclusive
 * @end: end physical address, exclusive
 */
static inline void outer_flush_range(phys_addr_t start, phys_addr_t end)
{
        if (outer_cache.flush_range)
                outer_cache.flush_range(start, end);
}
  • outer_cache가 구현된 머신에서 지정 range의 outer cache에 대한 flush(clean & invalidate)를 수행한다.

 

is_hyp_mode_available()

arch/arm/include/asm/virt.h

/* Reports the availability of HYP mode */
static inline bool is_hyp_mode_available(void)
{
        return ((__boot_cpu_mode & MODE_MASK) == HYP_MODE &&
                !(__boot_cpu_mode & BOOT_CPU_MODE_MISMATCH));
}

 

is_hyp_mode_mismatched()

arch/arm/include/asm/virt.h

/* Check if the bootloader has booted CPUs in different modes */
static inline bool is_hyp_mode_mismatched(void)
{
        return !!(__boot_cpu_mode & BOOT_CPU_MODE_MISMATCH);
}

 

arch/arm/include/asm/virt.h

/*
 * Flag indicating that the kernel was not entered in the same mode on every
 * CPU.  The zImage loader stashes this value in an SPSR, so we need an
 * architecturally defined flag bit here.
 */
#define BOOT_CPU_MODE_MISMATCH  PSR_N_BIT

 

arch/arm/include/uapi/asm/ptrace.h

/*
 * PSR bits
 * Note on V7M there is no mode contained in the PSR
 */
#define USR26_MODE      0x00000000
#define FIQ26_MODE      0x00000001
#define IRQ26_MODE      0x00000002
#define SVC26_MODE      0x00000003
#if defined(__KERNEL__) && defined(CONFIG_CPU_V7M)
/*
 * Use 0 here to get code right that creates a userspace
 * or kernel space thread.
 */
#define USR_MODE        0x00000000
#define SVC_MODE        0x00000000
#else
#define USR_MODE        0x00000010
#define SVC_MODE        0x00000013
#endif
#define FIQ_MODE        0x00000011
#define IRQ_MODE        0x00000012
#define ABT_MODE        0x00000017
#define HYP_MODE        0x0000001a
#define UND_MODE        0x0000001b
#define SYSTEM_MODE     0x0000001f
#define MODE32_BIT      0x00000010
#define MODE_MASK       0x0000001f

 

arch/arm/include/uapi/asm/ptrace.h

/*
 * Use 0 here to get code right that creates a userspace
 * or kernel space thread.
 */
#define USR_MODE        0x00000000
#define SVC_MODE        0x00000000
#else
#define USR_MODE        0x00000010
#define SVC_MODE        0x00000013
#endif
#define FIQ_MODE        0x00000011
#define IRQ_MODE        0x00000012
#define ABT_MODE        0x00000017 
#define HYP_MODE        0x0000001a
#define UND_MODE        0x0000001b
#define SYSTEM_MODE     0x0000001f
#define MODE32_BIT      0x00000010
#define MODE_MASK       0x0000001f

 

전역 변수

__boot_cpu_mode

arch/arm/include/asm/virt.h

/*
 * __boot_cpu_mode records what mode the primary CPU was booted in.
 * A correctly-implemented bootloader must start all CPUs in the same mode:
 * if it fails to do this, the flag BOOT_CPU_MODE_MISMATCH is set to indicate
 * that some CPU(s) were booted in a different mode.
 *
 * This allows the kernel to flag an error when the secondaries have come up.
 */
extern int __boot_cpu_mode;

 

arch/arm/kernel/hyp-stub.S

#ifndef ZIMAGE
/*
 * For the kernel proper, we need to find out the CPU boot mode long after
 * boot, so we need to store it in a writable variable.
 *
 * This is not in .bss, because we set it sufficiently early that the boot-time
 * zeroing of .bss would clobber it.
 */
.data
ENTRY(__boot_cpu_mode)
        .long   0

 

 

답글 남기기

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