setup_processor() 를 호출하여 pro_info_list 구조체 정보를 전역 변수에 각각 저장한다.
setup_machine(machine_arch_type) 를 호출하여 machine_desc 구조체 포인터를 얻는다.
machine 이 soft_reboot 지원하는지 확인하고, boot_params 이 존재하면, tags 구조체 주조에 받아온다.
tags 형식이 예전에 사용하던 전달 방식이면, convert_to_tag_list 를 호출하여 tags 형식으로 변환하고, ATAG_CORE 로 시작하는지 확인하고, 아니면 디볼트 정의된 init_tags 를 사용한다.
machine 이 fixup 를 지원하는지 확인하고, 지원하면 호출한다. 여기서는 meminfo 구조체를 fix 하는 역활을 한다.
tags 를 처리하기 전에 meminfo 정보가 저장된 경우라면, ATAG_MEM를 ATAG_NONE 으로 변경하는 squash_mem_tags 를 호출한다.
parse_tags 를 호출하여 tags 를 처리한다.
init_mm 구조체에 start_code, end_eode, end_data, brk 값을 저장한다.
parse_cmdline 를 호출하여 command line 를 처리한다.
paging_init 를 호출하여 부트 메모리 allocator 를 초기화 한다.

'리눅스커널' 카테고리의 다른 글

[ convert_to_tag_list ] arch/arm/kernel/setup.c  (0) 2009.10.02
[ setup_processor ] arch/arm/kernel/setup.c  (0) 2009.10.02
[ volatile , barrier ]  (0) 2009.10.02
Memory Barrier  (0) 2009.10.02
[ __turn_mmu_on ] arch/arm/kernel/head.S  (0) 2009.10.02
Posted by blee
,
volatile 키워드는 volatile 키워드로 지정된 주소 공간에 대해서 read operation이 발생할때 마다 매번 주소 공간에서 read 합니다. 보통 gcc는 최적화 과정에서 어떤 변수 또는 주소 공간에 대해서 이전에 그 주소 공간의 값을 레지스터로 읽어 왔고, 프로세서가 명시적으로 해당 변수의 값을 변경하지 않았다면 다시 주소 공간에서 읽어오지 않고 레지스터에 읽어온 값을 사용하게 되어 있습니다. barrier() 같은 경우에 다음과 같은 매크로로 define 되어 있습니다.

#define barrier() __asm__ __volatile__("": : :"memory")

보면 아무런 operation도 없는 inline assembly 코드입니다. 콜론(':')으로 구분된 "memory" 표기되어 있는 세번째 필드는 clobber 필드라고 해서 해당 inline assembly가 수행된 후 값이 변경되는 것들을 기입합니다. 

gcc는 clobber 필드에 기
입된 내용을 토대로 다음 코드를 생성하면서 최적화를 수행하는데 일반적으로 값이 변경된 것들에 대해서는 어떤 값으로 변경 된지에 대한 정보가 없기 때문에 최적화를 수행할 수 없습니다. 
clobber 필드에 "memory"라고 표기 하는것은 모든 메모리 타입 저
장 장치(모든 레지스터, 모든 프래그, 모든 메모리 등)의 값이 변경됨을 의미합니다.  gcc는 이럴경우 __asm__ __volatile__("": : :"memory") 경계를 넘어가는 최적화 또는 instruction scheduling을 수행하지 않습니다.  때문에 __asm__ __volatile__("": : :"memory")를 사용하면 이전 코드의 수행 완료를 보장 할 수 있고 이후 코드가 __asm__ __volatile__("": : :"memory") 이전에 수행 되는 것을 방지 할 수 있습니다.

'리눅스커널' 카테고리의 다른 글

[ setup_processor ] arch/arm/kernel/setup.c  (0) 2009.10.02
[ setup_arch ] arch/arm/kernel/setup.c  (0) 2009.10.02
Memory Barrier  (0) 2009.10.02
[ __turn_mmu_on ] arch/arm/kernel/head.S  (0) 2009.10.02
[ __enable_mmu ] arch/arm/kernel/head.S  (0) 2009.10.02
Posted by blee
,

Memory Barrier

리눅스커널 2009. 10. 2. 12:12
 최근에는 명령 순서를 바꿔서 실행하는 CPU가 일반적이다. 이것은 연산 결과에 모순을 초래하지 않는 범위에서 실행 순서를 변경하고, CPU 자원 이용의 효율화를 목적으로 한다. 이 경우 단순히 그 CPU 전부가 연산 결과에 모순이 없는 것 뿐만 아니라, 다른 CPU에서 보았을 경우에도 연산 결과에 모순이 있으면 안 된다. 실제 메모리 상의 데이터는 다른 CPU로 부터 참조나 갱신을 하기 위해 CPU간의 동일한 데이터를 참조 할 때 그 데이터의 접근 순서를 제대로 지킬 필요가 있다. 이 메모리 접근 순서를 제어하기 위한 구조가 메모리 장벽(Memory Barrier)이다.
 메모리를 R,W,R,W,R,W 와 같은 순서로 참조되는 명령에서, CPU는 효율을 높이기 위해서 R,R,R,W,W,W 와 같이 명령을 재배치 할 수 있는데, 이러한 Re-Ordering 를 막기 위해 Memory Barrier 를 사용한다.

#if __LINUX_ARM_ARCH__ >= 7
#define dmb() __asm__ __volatile__ ("dmb" : : : "memory")
#elif defined(CONFIG_CPU_XSC3) || __LINUX_ARM_ARCH__ == 6
#define dmb() __asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 5" \
                    : : "r" (0) : "memory")
#else
#define dmb() __asm__ __volatile__ ("" : : : "memory")
#endif
Posted by blee
,
__arm920_setup과 __enable_mmu에서 설정한 제어 레지스터의 값(R0)을 실제로 CP15 Register 1, control register 에 저장한다. 이제부터 MMU가 작동하고 가상 메모리 공간을 사용하게 된다. 모든 메모리 주소 참조는 주소 변환 과정을 거친다. IP 레지스터에 저장되어 있던 __mmap_switched 로 분기한다.
 
[__mmap_switched ] /arch/arm/kernel/head-common.S
 
__data_loc과 __data_start의 위치가 다를 경우 __data_start 부터 __bss_start 까지 __data_loc에 위치한 데이터를 복사한다. XIP 커널은 ROM에 있는 data 섹션(__data_loc)의 내용을 RAM(__data_start)으로 복사해야 한다.
BSS 영역을 0으로 초기화한다. CP15 Register 0 에서 읽어온 ID code (R9)와 부트로더 에서 넘긴 ARCH 넘버(R1)를 arch/arm/kernel/setup.c의 processor_id와 __machine_arch_type 변수에 저장한다.

__enable_mmu에서 처리했던 CP15 Register 1, control register 의 값(R0)을
arch/arm/kernel/entry-armv.S의 cr_alignment에 저장하고, A bit(Alignment Fault Checking)을 클리어 한 값을 cr_no_alignment에 저장하고,
스택포인터(sp)를 init_thread_union + THREAD_START_SP 로 설정한다. 

start_kernel 로 분기한다.

'리눅스커널' 카테고리의 다른 글

[ volatile , barrier ]  (0) 2009.10.02
Memory Barrier  (0) 2009.10.02
[ __enable_mmu ] arch/arm/kernel/head.S  (0) 2009.10.02
[ __arm920_setup ] arch/arm/mm/proc-arm920.S  (0) 2009.10.02
[ __create_page_tables ] /arch/arm/kernel/head.S  (0) 2009.10.02
Posted by blee
,
kernel 설정에 따라 CP15 Register 1 에 적용될 값을 설정한다.
CP15 Register 3, domain access control register 에 접근 권한을 설정한다.
CP15 Register 2, translation table base(TTB) register 에 페이지 테이블을 주소를 정한다.

 kernel 설정  비트   의미
  CONFIG_ALIGNMENT_TRAP
CR_A
 데이터 주소 정렬 여부 검사
  CONFIG_CPU_DCACHE_DISABLE
CR_C
 D-Cache disable
  ONFIG_CPU_BPREDICT_DISABLE
CR_Z
 예약됨
  CONFIG_CPU_ICACHE_DISABLE
CR_I
 I-Cache disable

 No  Domain   Type
 0
 DOMAIN_KERNEL
  DOMAIN_MANAGER
 0
 DOMAIN_TABLE
  DOMAIN_MANAGER
 1
 DOMAIN_USER
  DOMAIN_MANAGER
 2
 DOMAIN_IO
  DOMAIN_CLIENT

Posted by blee
,
proc_info_list의 __cpu_flush에 저장되어 있던 분기 명령이 실행되면서 ARM920 일경우 __arm920_setup으로 이동한다. I-Cache와 D-Cache를 무효화(invalidate)하고 쓰기 버퍼의 내용도 비운다. 더불어 I/D-TLB도 무효화한다.
CP15 제어 레지스터를 읽어서 MMU, Cache 관련 비트를 설정한다. (R0 레지스터)
 

[ __v6_setup ] arch/arm/mm/proc-v6.S


proc_info_list의 __cpu_flush에 저장되어 있던 분기 명령이 실행되면서 ARMv6 일경우
__v6_setup으로 이동한다.
I-Cache와 D-Cache를 무효화(invalidate)하고 쓰기 버퍼의 내용도 비운다. 더불어 I/D-TLB도 무효화한다.
CP15 제어 레지스터를 읽어서 MMU, Cache 관련 비트를 설정한다. (R0 레지스터)
 
Posted by blee
,
페이지 테이블을 KERNEL_RAM_PADDR - 0x4000(16K)에 만든다. 이 영역을 모두 0 으로 초기화 하고, struct proc_info_list 의 PROCINFO_MM_MMUFLAGS 값에 해당하는 __cpu_mm_mmu_flags  값을 읽어 와서 적용한다. 현재 PC가 있는 1Mbyte 영역과 MMU가 enable 되었을 때를 대비한 커널영역에 대한 1차 페이지 테이블을 생성하게 된다. 부트로더로 부터 전달되는 부트 파라미터에 대한 영역도 만들어 준다.
SMDK2410 일 경우, 물리메모리는 0x30000000 에서 시작, 커널은 0x30008000 에 있다. 이때 PC값은 0x30008000 보다 큰 어디를 가르키고 있을 것이다.

swapper_pg_dir ( 0xC0004000 )
 0ffset  Level 1 descriptor 
 0x000 * 4  
  :  
 0x300 * 4  0x300 << 20 |  __cpu_mm_mmu_flags
  :  
 0xC00 * 4  0xC00 << 20 |  __cpu_mm_mmu_flags
 0xC01 * 4  0xC01 << 20 |  __cpu_mm_mmu_flags
 0xC02 * 4  0xC02 << 20 |  __cpu_mm_mmu_flags
  :  
 0xFFF * 4  

Posted by blee
,
__arch_info_begin 부터 __arch_info_end 까지 ( 섹션 .arch.info.init 영역 )  저장된 machine_desc 구조체에서 MACHINFO_TYPE 값에 해당하는, 변수 nr 을 읽어 온다.

 이 값이 부
터로드에서 넘긴 arch 넘버(R1)와 같은지 비교하고, 같으면 리턴하고, 다르면 machine_desc 구조체 만큼 증가하여 __arch_info_end 까지 반복하게 된다. 성공 하면, R5 은 일치하는 machine_desc 구조체의 시작 주소를 가지게 되고, 실패하면 R5에 0값을 대입한다.

섹션 .arch.info.init 영역은 컴파일 시에 MACHINE_START define 에 의해서 저장되게 된다.
include/asm-arm/mach/arch.h 에 정의 되어 있다.

#define MACHINE_START(_type,_name)
          \
static const struct machine_desc __mach_desc_##_type    \
 __attribute_used__                 \
 __attribute__((__section__(".arch.info.init"))) = {    \
    .nr     = MACH_TYPE_##_type,        \
    .name       = _name,
MACH_TYPE_##_type 은 arch/arm/tools/mach-types 에 그 값들이 정의 되어 있다.

각 arch 별로 아래와 같은 정의가 존재한다.

// arch/arm/mach-s3c2410/mach-smdk2410.c
MACHINE_START(SMDK2410, "SMDK2410")
// arch/arm/mach-realview/realview_eb.c
MACHINE_START(REALVIEW_EB, "ARM-RealView EB“)
Posted by blee
,
 __proc_info_begin 부터 __proc_info_end 까지 ( 섹션 .proc.info.init 영역 )  저장된 proc_info_list 구조체에서 cpu_val, cpu_mask 값을 읽어 온다.

호출 되기전 R9에 Process ID가 저장되어 있고,
이 값을 읽어온 cpu_mask 과 mask 하고, cpu_val 과 같은 지 비교한다.

같으면, 리턴하고, 다르면 proc_info_list 구조체 만큼 이동하며 __proc_info_end 까지 반복한다.
성공 하면, R5 은 일치하는 proc_info_list 구조체의 시작 주소를 가지게 되고, 실패하면 R5에 0값을 대입한다.
Posted by blee
,
FIQ, IRQ 를 Disable 하고, SVC 모드로 한다.
CP15 Register 0 에서 Process ID 를 읽어 온다.
__lookup_processor_type 를 호출하고, 결과를 R10 에 저장한다. 결과가 0 이라면, __error_p 로 분기한다.
__loopup_machine_type 를 호출하고, 결과를 R8 에 저장한다. 결과가 0 이라면, __error_a 로 분기한다.
__create_page_table 를 호출하여, 페이지 테이블을 생성한다.
R13 에 __switch_data  주소를 저장하고, LR 에 __enalbe_mmu  주소를 저장하고, proc_info_list 구조체의 PROCINFO_INITFUNC 으로 PC 값을 바꾼다.
arch 별로 proc-***.S 파일 형태로 구분되어 있는 setup 코드를 수행한다.

ARM920T (arch/arm/mm/proc-arm920.S)
__arm920_setup
 
V6  (arch/arm/mm/proc-v6.S )
__v6_setup
Posted by blee
,