[ bootmem_init ] arch/arm/mm/init.c
 
물리 메모리 영역 만큼 부트 메모리 allocator 를 만들고 초기화 한다.
meminfo 구조체에서 bank size 0 경우, node MAX_NUMNODES 보다 것은 node -1 설정한다. 정보이며, node 별로 bank 정보를 참조 무시하게 만든다.
check_initrd 호출하여, ramdisk image 어느 node 속하는지 판단한다.
노드별로 bootmem_init_node 호출 하여, end_pfn 값을 갱신하게 되는데, 과정에서 memend_pfn 값도 결정된다. memend_pfn 노드들 중에 가장 높은 메모리 pfn 저장한다.
high_memory memend_pfn 에서 PAGE_SHIFT 만큼 곱하여 물리 메모리의 끝을 가리킨다.
max_pfn pfn 갯수를 저장한다.
// include/linux/nodemask.h
#define for_each_node(node)    for_each_node_mask((node), node_possible_map)
#if MAX_NUMNODES > 1
#define for_each_node_mask(node, mask)          \
    for ((node) = first_node(mask);         \
        (node) < MAX_NUMNODES;          \
        (node) = next_node((node), (mask)))
#else /* MAX_NUMNODES == 1 */
#define for_each_node_mask(node, mask)          \
    if (!nodes_empty(mask))             \
        for ((node) = 0; (node) < 1; (node)++)
#endif /* MAX_NUMNODES */
smdk2410 에서는 MAX_NUMNODES 값은 1 된다.
결과로 저장되는 변수들의 값은 다음과 같다.
high_memory = 0xC4000000, max_pfn = 0x00004000, mamend_pfn=0x00034000

[ check_initrd ] arch/arm/mm/init.c
 
phys_initrd_start ,phys_initrd_size 전역변수를 참조하여, initrd 존재하는 메모리 노드를 구한다.
CONFIG_BLK_DEV_INITRD 정의 되어 있지 않다면, initrd_node = -2 저장되고,
phys_initrd_size 0 아니고, meminfo bank 내에 존재 하지 않다면, initrd_node = -1 저장된다.
어느 노드에 속하는지 찾아서 initrd_node 저장한다.
NUMA 지원되지 않는다면, 노드는 0 하나 뿐이다.
Posted by blee
,
페이지 테이블에서 다음 영역을 0 으로 초기화 한다.
1. 0 ~ MODULE_START ( 0 ~ 0xbee00000 )
2. MODULE_START ~ PAGE_OFFSET ( 0xbf000000 ~ 0xbfe00000 )
3. 물리메모리의 끝  ~ VMALLOC_END ( 0xc4000000 ~ 0xdfe00000 )
MODULE_START = 0xBF000000
PAGE_OFFSET = 0xC0000000
VMALLOC_END = 0xE0000000 ( s3c2410 ), 0xD0000000 ( integrator )
 
swapper_pg_dir ( 0xC0004000 )

 offset   level 1 descriptor
 0x000 * 4   0
  :   0
 0xC00 * 4  
 0xC01 * 4  
  :  
 0xC40 * 4   0
  :   0
 0xDFE * 4   0
  :  
 0xFFF * 4  

Posted by blee
,
ID Code
MRC p15,0,<Rd>,c0,c0,0; reads Main ID register
ARM9  : 0x40129200
ARM11 MPCore : 0x410FB024
Memory Model Feature Register 0
MRC p15,0,<Rd>,c0,c1,4
ARM11 MPCore : 0x01100103
ARM920T 은 CPU_ARCH_ARMv4T 를 ARM11 MPCore 는 CPU_ARCH_ARMv7 를 리턴 한다.
( 왜 CPU_ARCH_ARMv6 인 것 같은데? )
Posted by blee
,
메모리 오류를 검출하고 정정하기 위한 방법으로 패리티, ECC 메모리가 있는데요. 최근에는 만드는 사람들이 만들어서 에러 발생하지 않는다고 합니다. 하지만, 이론상으로는 발생할 확률이 여전히 남아 있고, 일반 가정용으로 사용하는 메모리는 이를 눈감아 줄만 하다고 하네요. 하지만, 정밀한 작업을 해야 하는 시스템에서는 여전히 이러한 오류 검출과 정정 기능이 구현된 메모리를 사용한다고 합니다. 각설하고, static void __init build_mem_type_table(void) 에서 mem_types 설정 사용하는 ecc_mask 초기값은 0 으로 설정되 있습니다.

// arch/arm/mm/mmu.c

static unsigned int ecc_mask __initdata = 0;

하지만, make menuconfig default command line 으로 "ecc=on" 설정되면,
ecc_mask = PMD_PROTECTION 값을 대입 합니다.

이를 처리하는 부분은 __early_param("ecc=", early_ecc) static void __init early_ecc(char **p) 함수로 구현되어 있습니다.

PMD_PROTECTION 아래와 같이 정의 되어 있으며,
// include/asm-arm/pgtable-hwdef.h
#define PMD_PROTECTION (1 << 9) /* v5 */

이는 first level descriptor p bit 해당합니다. ARM11 MPCore Tech Ref 문서에서 5.11 Hardware page table translation 부분의 페이지 261 보면,  아래와 같은 글이 있습니다.

If the P bit is supported and set for the memory region, it indicates to the system memory
controller that this memory region has ECC enabled.
Posted by blee
,
CPU 에 따라 Cache Policy, mem_types, protection_map 설정한다.
smdk2410 에서의 설정 내용으로 cache_policies[CPOLICY_WRITEBACK] 설정한다.
.policy     = "writeback",
.cr_mask    = 0,
.pmd        = PMD_SECT_WB,
.pte        = PTE_BUFFERABLE|PTE_CACHEABLE,
#define PMD_SECT_WB     (PMD_SECT_CACHEABLE | PMD_SECT_BUFFERABLE)
 
protection_map[0]=0x0000000f  L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_CACHEABLE | L_PTE_BUFFERABLE
protection_map[1]=0x0000005f  L_PTE_USER | L_PTE_EXEC | L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_CACHEABLE | L_PTE_BUFFERABLE
protection_map[2]=0x0000005f
protection_map[3]=0x0000005f
protection_map[4]=0x0000005f
protection_map[5]=0x0000005f
protection_map[6]=0x0000005f
protection_map[7]=0x0000005f
protection_map[8]=0x0000000f
protection_map[9]=0x0000005f
protection_map[10]=0x0000007f L_PTE_USER | L_PTE_WRITE | L_PTE_EXEC | L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_CACHEABLE | L_PTE_BUFFERABLE
protection_map[11]=0x0000007f
protection_map[12]=0x0000005f
protection_map[13]=0x0000005f
protection_map[14]=0x0000007f
protection_map[15]=0x0000007f
 
[MT_DEVICE]
prot_pte 0x000000a3 L_PTE_DIRTY | L_PTE_WRITE | L_PTE_YOUNG | L_PTE_PRESENT
prot_l1 0x00000011 PMD_BIT4 | PMD_TYPE_TABLE
prot_sect 0x00000412 PMD_SECT_AP_WRITE | PMD_BIT4 | PMD_TYPE_SECT | PMD_SECT_UNCACHED
domain 0x00000002 DOMAIN_IO
[MT_CACHECLEAN]
prot_pte 0x00000000
prot_l1 0x00000000
prot_sect 0x0000001e PMD_BIT4 | PMD_SECT_CACHEABLE | PMD_SECT_BUFFERABLE | PMD_TYPE_SECT
domain 0x00000000 DOMAIN_KERNEL
[MT_MINICLEAN]
prot_pte 0x00000000
prot_l1 0x00000000
prot_sect 0x0000001a PMD_SECT_TEX(1) | PMD_BIT4 | PMD_SECT_CACHEABLE | PMD_TYPE_SECT
domain 0x00000000 DOMAIN_KERNEL
[MT_LOW_VECTORS]
prot_pte 0x000000cf L_PTE_DIRTY | L_PTE_EXEC | L_PTE_CACHEABLE | L_PTE_BUFFERABLE | L_PTE_YOUNG | L_PTE_PRESENT
prot_l1 0x00000011 PMD_BIT4 | PMD_TYPE_TABLE
prot_sect 0x00000000
domain 0x00000001 DOMAIN_USER
[MT_HIGH_VECTORS]
prot_pte 0x000000df L_PTE_DIRTY | L_PTE_EXEC | L_PTE_USER | L_PTE_CACHEABLE | L_PTE_BUFFERABLE | L_PTE_YOUNG | L_PTE_PRESENT
prot_l1 0x00000011 PMD_BIT4 | PMD_TYPE_TABLE
prot_sect 0x00000000
domain 0x00000001 DOMAIN_USER

[MT_MEMORY]
prot_pte 0x00000000
prot_l1 0x00000000
prot_sect 0x0000041e PMD_SECT_AP_WRITE | PMD_BIT4 | PMD_SECT_CACHEABLE | PMD_SECT_BUFFERABLE | PMD_TYPE_SECT
domain 0x00000000 DOMAIN_KERNEL

[MT_ROM]
prot_pte 0x00000000
prot_l1 0x00000000
prot_sect 0x0000001e PMD_BIT4 | PMD_SECT_CACHEABLE | PMD_SECT_BUFFERABLE | PMD_TYPE_SECT
domain 0x00000000 DOMAIN_KERNEL

[MT_IXP2000_DEVICE]
prot_pte 0x000000a3 L_PTE_DIRTY | L_PTE_WRITE | L_PTE_YOUNG | L_PTE_PRESENT
prot_l1 0x00000011 PMD_BIT4 | PMD_TYPE_TABLE
prot_sect 0x00001416 PMD_SECT_TEX(1) | PMD_SECT_AP_WRITE | PMD_BIT4 | PMD_SECT_BUFFERABLE | PMD_TYPE_SECT | PMD_SECT_UNCACHED
domain 0x00000002 DOMAIN_IO

[MT_NONSHARED_DEVICE]
prot_pte 0x00000000
prot_l1 0x00000011 PMD_BIT4 | PMD_TYPE_TABLE
prot_sect 0x00002412 PMD_SECT_TEX(2) | PMD_SECT_AP_WRITE | PMD_BIT4 | PMD_TYPE_SECT
domain 0x00000002 DOMAIN_IO
Posted by blee
,
예전 전달 방식이라면, tag list 형식으로 고쳐준다. 

[ parse_tags ] arch/arm/kernel/setup.c
 
ATAG_NONE 일 때까지 루프 돌면서 parse_tag 에 태그(struct tag)를 인자로 넘겨 호출한다.
parse_tag 는 넘겨온 tag 가 __tagtable_begin에서 __tagtable_end 까지 섹션 .taglist.init 영역 안에 저장되어 있는 tagtable 구조체의 tag 값을 비교하여 같은 것이 있다면, tagtable 구조체의 parse 함수를 호출한다. tagtable 구조체는 include/asm-arm/setup.h 정의되어 있다.
struct tagtable {
    __u32 tag;
    int (*parse)(const struct tag *);
};
.taglist.init 영역에 값을 넣는 매크로는 __tagtable 로써 include/asm-arm/setup.h 에 정의되어 있다.

#define __tag __attribute_used__ __attribute__((__section__(".taglist.init")))
#define __tagtable(tag, fn) \
static struct tagtable __tagtable_##fn __tag = { tag, fn }

arch/arm/kernel/setup.c
에 다음과 같은 코드가 존재한다.

__tagtable(ATAG_CORE, parse_tag_core);
__tagtable(ATAG_MEM, parse_tag_mem32);
:
__tagtable(ATAG_REVISION, parse_tag_revision);
 
tagtable[0]
  ATAG_REVISION   <-- __tagtable_begin
    parse_tag_revision  
 :  :  
 :   ATAG_MEM  *.taglist.init
 :   parse_tag_mem32  
tagtable[n]
  ATAG_CORE  
 
  parse_tag_core   <-- __tagtable_end
 
[ parse_cmdline ] arch/arm/kernel/setup.c
 
default_command_line 저장되어 있는 문자열을 분석한다. 값은 커널 옵션에서 지정한 값이며, 만약 부트로더에서 ATAG_CMDLINE 태그로 넘겼을 경우 부터로더에서 넘긴 값으로 대체된다.
__early_begin 부터 __early_end 까지 .early_param.init 영역에 저장되어 있는 early_params 구조체의 arg 값을 string 처음부터 끝까지 memcmp 메모리 비교를 하고, 같으면 해당 함수에 일치하는 문자 다음부터 포인터를 인자로 넘기며 호출한다. 그렇지 않으면 cmdline_p에 내용을 복사한다.
early_params 구조체는 include/asm-arm/setup.h 정의되어 .
( cmdline_p=
xxx=1234 yyy=1234 형태로 저장된다.)

struct early_params {
    const char *arg;
    void (*fn)(char **p);
};

.early_param.init 영역에 값을 넣는 매크로는
__early_param 로써 include/asm-arm/setup.h 에 정의되어 있다.

#define __early_param(name,fn)
                  \
static struct early_params __early_##fn __attribute_used__  \
__attribute__((__section__(".early_param.init"))) = { name, fn }

arch/arm/kernel/setup.c
다음과 같은 코드가 존재한다.
__early_param("initrd=", early_initrd);
__early_param("mem=", early_mem);

arch/arm/mm/mmu.c
다음과 같은 코드가 존재한다.
__early_param("cachepolicy=", early_cachepolicy);
__early_param("nocache", early_nocache);
__early_param("nowb", early_nowrite);
__early_param("ecc=", early_ecc);

“initrd=“
<-- __early_begin
 early_initrd
 :
 “nowb”
.early_param.init
 early_nowrite
 “ecc=“
 early_ecc
<-- __early_end
Posted by blee
,
lookup_processor_type에 processor_id 를 인자로 전달하여 호출한다.
processor_id
는 head.S 에서 저장한 CPU ID 값이며, 이 함수의 호출 결과로 구조체 proc_info_list 에 대한 포인터를 얻는다.
각 전역 변수에 값을 대입한다.  elf_hwcap 는 하드웨어 지원사항을 나타낸다.
init_uts_ns.name.machine 를 arch_name, ENDIANNESS 스트링으로 저장한다.
elf_platform 를 elf_name 과 ENDIANNESS 스트링으로 저장한다.
cpu_proc_int 를 호출하는데, processor._proc_init() 로 정의되어 있다.
CPU별로 정
의되어 있는 processor_function내에 존재하는 초기화 코드를 수행한다.

// arch/arm/mm/proc-arm920.S
arm920_processor_functions
// arch/arm/mm/proc-v6.S
v6_processor_functions

두 코드 다 하는 일 없이 바로 리턴한다.
 
 cpu_name = list->cpu_name;
#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
    elf_hwcap = list->elf_hwcap;

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

[ build_mem_type_table ] arch/arm/mm/mmu.c  (0) 2009.10.02
[ convert_to_tag_list ] arch/arm/kernel/setup.c  (0) 2009.10.02
[ setup_arch ] arch/arm/kernel/setup.c  (0) 2009.10.02
[ volatile , barrier ]  (0) 2009.10.02
Memory Barrier  (0) 2009.10.02
Posted by blee
,
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
,