비트맵을 표시해 줄 page 몇 개가 필요한가?
 
[ find_bootmap_pfn ] arch/arm/mm/init.c
 부트맵이 위치할 pfn 를 찾는다. 

[ init_bootmem_core ] mm/bootmem.c
contig_bootmem_data 초기화 한다.
node_bootmem_map ( 부트맵 ) 을 모두 1 로 설정한다.

bootmem 할당
Posted by blee
,
인자로 넘어온 struct map_desc *md 해당하는 영역에 대한 페이지 테이블 맵핑 작업을 한다.
 
 while (length >= (PGDIR_SIZE / 2)) {
        alloc_init_section(virt, virt + off, prot_sect);
        virt   += (PGDIR_SIZE / 2);     // + 0x100000
        length -= (PGDIR_SIZE / 2);
    }
    while (length >= PAGE_SIZE) {
        alloc_init_page(virt, virt + off, prot_l1,prot_pte);
        virt   += PAGE_SIZE;
        length -= PAGE_SIZE;
    }
 
length 를 0x100000 감소 시키고, virt 는 0x100000 를 증가 시키며, alloc_init_section 를 호출해서 section 에 대한 맵핑을 한다.
length 가 0x100000 보다 작게 남는 경우엔, alloc_init_page 를 호출해서 1차 페이지 디스크립터 와 2차 페이지 디스크립터를 생성한다.
[ alloc_init_section ] arch/arm/mm/mmu.c
 
pmd_off_k 주어진 virtual address page directory index ( 2048 엔트리중에서 )
그리고, virtual address C01*4, C03*4 같이 홀수 이면, pmdp두번째 맴버에 값을 저장하도록 한다.
flush_pmd_entry 호출한다.
clear_pmd_entry, flush_pmd_entry 차이

[ alloc_init_page ] arch/arm/mm/mmu.c
 
pmd_off_k 주어진 virtual address page directory index ( 2048 엔트리중에서 )
*pmdp값이 비어 있는지 확인한다. 비어 있으면, 아직 pte 만들지 않았다
만들지 않았다면, alloc_bootmem_low_pages 부터 4K 할당 받는다.
할당 받은 address ptep 저장되고, 주소는 pmdp[0], 1024 만큼 더한 주소는 pmdp[1] 각각 저장된다.
 
 
    pgd                            pte
|            |
+--------+  +0
|            |--------> +------------+  +0
+--------+  +4          |  h/w pt 0    |
|            |--------> +------------+ +1024
+--------+  +8          |  h/w pt 1    |
|            |               +------------+ +2048
+--------+               |  Linux pt 0  |
|            |               +------------+ +3072
+--------+               |  Linux pt 1  |
|            |               +------------+ +4096
 
pmd_page_vaddr *pmdp 주어진 address 2048 만큼 더한다. 속성은 mask 날린다.
__pte_index 512 앤트리에 대한 인덱스이다.
pte_offset_kernel 커널 pte 주소를 반환하고, set_pte_ext 호출하여, 커널 pte 저장하고, 2048만큼 곳에 h.w pte 저장한다.

Posted by blee
,
meminfo bank 대해 map_memory_bank, create_mapping, alloc_init_section  차례로 호출하여 물리메모리에 대한 페이지 테이블 맵핑을 한다. SMDK2410 에서 노드가 1개이고, meminfo 정보가 아래와 같다면, 다음과 같은 페이지 테이블을 생성한다.

meminfo.nr_banks = 1
bank[0].start=0x30000000
bank[0].size=0x4000000
bank[0].node=0 

swapper_pg_dir ( 0xC0004000 )
 offset  level 1 descriptor
 0x000 * 4  0
  :  0
 0xC00 * 4  0x3000041e
 0xC01 * 4  0x3010041e
  :   :
 0xC3F * 4  0x33f0041e 
 0xC40 * 4  0 
  :  0 
  0xDFE * 4  0 
  :  
  0xFFF * 4  

bootmem_bootmap_pages 호출하여, 부트 allocator 표시 페이지 갯수를 표현하기 위해 얼마 만큼의 PAGE_SIZE 필요한지 계산한다.
pfn 갯수는 0x4000 , 비트 단위로 보기 때문에 byte 표현하면, 0x800 크기가 되는데, 이는 한개의 PAGE 표현 있다.
find_bootmap_pfn 호출하여, 부트 allocator 사용할 영역을 할당 받는다. 0x00030314 pfn 리턴하게 되는데, 기가 부트 allocator bitmap 영역이 것이다.
node_online_map 현재 node 대한 bit set 하고,
NODE_DATA(node) &contig_page_data 리턴한다.
init_bootmem_node , init_bootmem_core  차례로 실행하여, contig_page_data.bdata 가리키는 contig_bootmem_data 설정한다. 여기서 contig_bootmem_data.node_bootmem_map 에 할당받은 bitmap 주소가 지정된다. (0x3031400 가상주소 할당된다. 0xc0314000 ) 그리고, 0x800 만큼 0xff 로 초기화 한다.0x4000 만큼의 영역이 비트로 표현되기 때문에, byte로 변환된 사이즈 0x800 이 되는 것이다.
free_bootmem_node node 만큼 루프를 돌며 호출하는데, 하나의 노드이기 때문에 역쉬 한번만 호출한다.
free_bootmem_core 호출되어, 노드에 속한 물리메모리 만큼을 test_and_clear_bit 호출하여, 0 으로 초기화 한다. 모든 메모리 영역을 사용가능으로 설정하는 것이다.
reserved 영역은 다시 reserve_bootmem_node 호출하여 사용중 으로 표시 것이다.

Posted by blee
,

 
[ 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
,