QUESTION
cache와 DRAM을 믿을 수 없는 x86 CPU가 어떤 memory를 stack과 gd로 사용해 첫 C 초기화에 도달하는가?
x86는 DRAM 전 stack을 Cache-As-RAM 또는 platform temporary RAM에 둔다. _x86boot_start와 _start, TPL/SPL/U-Boot proper의 실제 선택을 먼저 고정하고 CAR top에서 stack, gd와 malloc_f가 차지하는 주소를 계산한다.
board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다. 이 문장을 기준으로 코드를 위에서 아래로 읽으면, 함수 이름을 외우는 대신 어느 시점에 어떤 상태를 신뢰할 수 있는지 판단할 수 있다.
STRUCTURE
객체와 주소가 놓이는 구조
board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다.
CAR stack / gd_t / x86 GDT를 중심에 놓고 왼쪽의 입력이 어떤 검사를 거쳐 오른쪽 결과로 공개되는지 표시했다. 실제 디버깅에서는 각 블록의 주소와 크기를 로그에 대입한다.
CALL PATH
실행 흐름
화살표는 단순 호출 순서만 뜻하지 않는다. 각 단계가 성공을 반환할 때 다음 단계가 읽을 수 있는 상태가 무엇인지 함께 확인한다. 오류 반환이 발생하면 바로 다음 화살표로 진행하지 않고 해당 단계의 정리 경로를 따라간다.
STATE LEDGER
단계별 입력과 출력
호출 순서를 함수 이름으로만 외우지 않고, 각 단계가 무엇을 받아 무엇을 공개하는지 적은 표다. 실제 소스에서 생산 필드가 다르면 표를 고치는 방식으로 사용한다.
| # | 단계 | 진입 시 신뢰할 상태 | 성공 뒤 남아야 할 상태 | 다음 소비자 |
|---|---|---|---|---|
| 01 | cold reset or previous loader | DRAM과 정상 exception handler가 없는 32-bit 초기 문맥 | BIST and table pointer | 32-bit protected entry |
| 02 | 32-bit protected entry | cold reset or previous loader 완료 상태 | segment registers | GDT and blank IDT |
| 03 | GDT and blank IDT | 32-bit protected entry 완료 상태 | Cache-As-RAM range | car_init |
| 04 | car_init | GDT and blank IDT 완료 상태 | temporary stack | CAR stack |
| 05 | CAR stack | car_init 완료 상태 | global_data | gd and malloc_f |
| 06 | gd and malloc_f | CAR stack 완료 상태 | pre-relocation init | board_init_f |
| 07 | board_init_f | gd and malloc_f 완료 상태 | pre-relocation init | 최종 x86 U-Boot pre-relocation image 또는 다음 stage |
공통 불변 조건: board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다. 한 단계의 출력이 다음 단계의 입력 조건을 만족하지 않으면 오류가 실제로 드러난 위치보다 앞의 생산 단계부터 조사한다.
VISUAL WALKTHROUGH
주소와 객체의 이동을 그림으로 읽기
아래 그림은 호출 이름보다 주소, 객체 수명과 handoff 경계를 먼저 볼 수 있도록 구성했다. 실제 주소와 크기는 사용 중인 보드의 bdinfo, map과 linker symbol을 대입한다.
크기는 개념 비율이다. 실제 분석에서는 map, symbol과 runtime 출력의 시작·끝 주소를 대입한다.
두 열의 객체는 이름이 비슷해도 생성 시점, 사용 가능한 API와 해제 책임이 다르다.
Cold reset `_start`
01start16에서 protected mode 진입
02EAX의 BIST 보존
03ECX table pointer
04GDT/segment 재확인
05car_init으로 이동
Fail-safe `_x86boot_start`
01coreboot/EFI 등 previous loader
02BIST는 0으로 간주
03cache 상속 상태 제거
0432-bit bootstrap 재정리
05label 1에서 공통 경로
호출 이름만 나열하지 않고, 각 단계가 다음 단계에 넘기는 상태를 기준으로 실행 순서를 표시한다.
UPSTREAM SOURCE
원본 코드
아래 코드는 U-Boot v2026.07의 arch/x86/cpu/start.S에서 29-174줄을 그대로 가져온 것이다. 설명을 위해 실제 코드를 가짜 의사 코드로 바꾸지 않았다.
29#include <generated/asm-offsets.h>
30#include <linux/linkage.h>
31
32.section .text.start
33.code32
34.globl _start
35.type _start, @function
36.globl _x86boot_start
37_x86boot_start:
38 /*
39 * This is the fail-safe 32-bit bootstrap entry point.
40 *
41 * This code is used when booting from another boot loader like
42 * coreboot or EFI. So we repeat some of the same init found in
43 * start16.
44 */
45 cli
46 cld
47
48 /* Turn off cache (this might require a 486-class CPU) */
49 movl %cr0, %eax
50 orl $(X86_CR0_NW | X86_CR0_CD), %eax
51 movl %eax, %cr0
52 wbinvd
53
54 /*
55 * Zero the BIST (Built-In Self Test) value since we don't have it.
56 * It must be 0 or the previous loader would have reported an error.
57 */
58 movl $0, %ebp
59
60 jmp 1f
61
62 /* Add a way for tools to discover the _start entry point */
63 .align 4
64 .long 0x12345678
65_start:
66 /* This is the 32-bit cold-reset entry point, coming from start16 */
67
68 /* Save BIST */
69 movl %eax, %ebp
701:
71
72 /* Save table pointer */
73 movl %ecx, %esi
74
75#ifdef CONFIG_X86_LOAD_FROM_32_BIT
76 lgdt gdt_ptr2
77#endif
78
79 /* Load the segment registers to match the GDT loaded in start16.S */
80 movl $(X86_GDT_ENTRY_32BIT_DS * X86_GDT_ENTRY_SIZE), %eax
81 movw %ax, %fs
82 movw %ax, %ds
83 movw %ax, %gs
84 movw %ax, %es
85 movw %ax, %ss
86
87 /* Clear the interrupt vectors */
88 lidt blank_idt_ptr
89
90#ifdef CONFIG_USE_EARLY_BOARD_INIT
91 /*
92 * Critical early platform init - generally not used, we prefer init
93 * to happen later when we have a console, in case something goes
94 * wrong.
95 */
96 jmp early_board_init
97.globl early_board_init_ret
98early_board_init_ret:
99#endif
100
101 post_code(POST_START)
102
103 /* Initialise Cache-As-RAM */
104 jmp car_init
105.globl car_init_ret
106car_init_ret:
107#ifdef CONFIG_USE_CAR
108 /*
109 * We now have CONFIG_SYS_CAR_SIZE bytes of Cache-As-RAM (or SRAM,
110 * or fully initialised SDRAM - we really don't care which)
111 * starting at CONFIG_SYS_CAR_ADDR to be used as a temporary stack
112 * and early malloc() area. The MRC requires some space at the top.
113 *
114 * Stack grows down from top of CAR. We have:
115 *
116 * top-> CONFIG_SYS_CAR_ADDR + CONFIG_SYS_CAR_SIZE
117 * MRC area
118 * global_data with x86 global descriptor table
119 * early malloc area
120 * stack
121 * bottom-> CONFIG_SYS_CAR_ADDR
122 */
123 movl $(CONFIG_SYS_CAR_ADDR + CONFIG_SYS_CAR_SIZE - 4), %esp
124#ifdef CONFIG_DCACHE_RAM_MRC_VAR_SIZE
125 subl $CONFIG_DCACHE_RAM_MRC_VAR_SIZE, %esp
126#endif
127#else
128 /*
129 * Instructions for FSP1, but not FSP2:
130 * U-Boot enters here twice. For the first time it comes from
131 * car_init_done() with esp points to a temporary stack and esi
132 * set to zero. For the second time it comes from fsp_init_done()
133 * with esi holding the HOB list address returned by the FSP.
134 */
135#endif
136 /* Set up global data */
137 mov %esp, %eax
138 call board_init_f_alloc_reserve
139 mov %eax, %esp
140 call board_init_f_init_reserve
141
142#ifdef CONFIG_DEBUG_UART
143 call debug_uart_init
144#endif
145
146 /* Get address of global_data */
147 mov %fs:0, %edx
148#if defined(CONFIG_USE_HOB) && !defined(CONFIG_USE_CAR)
149 /* Store the HOB list if we have one */
150 test %esi, %esi
151 jz skip_hob
152 movl %esi, GD_HOB_LIST(%edx)
153
154#ifdef CONFIG_HAVE_FSP
155 /*
156 * After fsp_init() returns, the stack has already been switched to a
157 * place within system memory as defined by CONFIG_FSP_TEMP_RAM_ADDR.
158 * Enlarge the size of malloc() pool before relocation since we have
159 * plenty of memory now.
160 */
161 subl $CONFIG_FSP_SYS_MALLOC_F_LEN, %esp
162 movl %esp, GD_MALLOC_BASE(%edx)
163#endif
164skip_hob:
165#else
166 /* Store table pointer */
167 movl %esi, GD_TABLE(%edx)
168#endif
169 /* Store BIST */
170 movl %ebp, GD_BIST(%edx)
171
172 /* Set parameter to board_init_f() to boot flags */
173 post_code(POST_START_DONE)
174 xorl %eax, %eax
LINE BY LINE
29-174줄 해설
원본에 보이는 모든 줄을 순서대로 설명한다. 빈 줄도 block 경계로 남겨, 코드와 설명의 위치가 어긋나지 않게 했다.
#include <generated/asm-offsets.h>#include 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. architecture 또는 build stage 조건을 여닫는다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.
#include <linux/linkage.h>#include 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. architecture 또는 build stage 조건을 여닫는다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.
(빈 줄)#include <linux/linkage.h>까지의 동작과 .section .text.start에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 Cache-As-RAM range 상태가 아래 블록의 입력으로 사용되는 경계다.
.section .text.start원본 32번 줄의 .section .text.start는 앞의 이전 block 경계 결과를 받아 다음 .code32로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
.code32원본 33번 줄의 .code32는 앞의 .section .text.start 결과를 받아 다음 .globl _start로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
.globl _start원본 34번 줄의 .globl _start는 앞의 .code32 결과를 받아 다음 .type _start, @function로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
.type _start, @function원본 35번 줄의 .type _start, @function는 앞의 .globl _start 결과를 받아 다음 .globl _x86boot_start로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
.globl _x86boot_start원본 36번 줄의 .globl _x86boot_start는 앞의 .type _start, @function 결과를 받아 다음 _x86boot_start:로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
_x86boot_start:'_x86boot_start' label이다. 이곳을 참조하는 branch를 역검색하고 각 진입 경로의 register, stack, lock 조건이 같은지 확인한다.
/*원본 주석이 'block boundary'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* This is the fail-safe 32-bit bootstrap entry point.원본 주석이 'This is the fail-safe 32-bit bootstrap entry point.'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
*원본 주석이 'block boundary'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* This code is used when booting from another boot loader like원본 주석이 'This code is used when booting from another boot loader like'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* coreboot or EFI. So we repeat some of the same init found in원본 주석이 'coreboot or EFI. So we repeat some of the same init found in'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* start16.원본 주석이 'start16.'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
*/원본 주석이 'block boundary'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
cli원본 45번 줄의 cli는 앞의 */ 결과를 받아 다음 cld로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
cld원본 46번 줄의 cld는 앞의 cli 결과를 받아 다음 다음 block 경계로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
(빈 줄)cld까지의 동작과 /* Turn off cache (this might require a 486-class CPU) */에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 pre-relocation init 상태가 아래 블록의 입력으로 사용되는 경계다.
/* Turn off cache (this might require a 486-class CPU) */원본 주석이 'Turn off cache (this might require a 486-class CPU)'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
movl %cr0, %eax원본 49번 줄의 movl %cr0, %eax는 앞의 /* Turn off cache (this might require a 486-class CPU) */ 결과를 받아 다음 orl $(X86_CR0_NW | X86_CR0_CD), %eax로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
orl $(X86_CR0_NW | X86_CR0_CD), %eax원본 50번 줄의 orl $(X86_CR0_NW | X86_CR0_CD), %eax는 앞의 movl %cr0, %eax 결과를 받아 다음 movl %eax, %cr0로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
movl %eax, %cr0원본 51번 줄의 movl %eax, %cr0는 앞의 orl $(X86_CR0_NW | X86_CR0_CD), %eax 결과를 받아 다음 wbinvd로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
wbinvd원본 52번 줄의 wbinvd는 앞의 movl %eax, %cr0 결과를 받아 다음 다음 block 경계로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
(빈 줄)wbinvd까지의 동작과 /*에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 pre-relocation init 상태가 아래 블록의 입력으로 사용되는 경계다.
/*원본 주석이 'block boundary'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* Zero the BIST (Built-In Self Test) value since we don't have it.원본 주석이 'Zero the BIST (Built-In Self Test) value since we don't have it.'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* It must be 0 or the previous loader would have reported an error.원본 주석이 'It must be 0 or the previous loader would have reported an error.'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
*/원본 주석이 'block boundary'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
movl $0, %ebp원본 58번 줄의 movl $0, %ebp는 앞의 */ 결과를 받아 다음 다음 block 경계로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
(빈 줄)movl $0, %ebp까지의 동작과 jmp 1f에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 pre-relocation init 상태가 아래 블록의 입력으로 사용되는 경계다.
jmp 1f원본 60번 줄의 jmp 1f는 앞의 이전 block 경계 결과를 받아 다음 다음 block 경계로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
(빈 줄)jmp 1f까지의 동작과 /* Add a way for tools to discover the _start entry point */에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 pre-relocation init 상태가 아래 블록의 입력으로 사용되는 경계다.
/* Add a way for tools to discover the _start entry point */원본 주석이 'Add a way for tools to discover the _start entry point'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
.align 4원본 63번 줄의 .align 4는 앞의 /* Add a way for tools to discover the _start entry point */ 결과를 받아 다음 .long 0x12345678로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
.long 0x12345678원본 64번 줄의 .long 0x12345678는 앞의 .align 4 결과를 받아 다음 _start:로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
_start:16-bit cold-reset path가 32-bit protected mode를 만든 뒤 도착하는 고정 entry다. fail-safe _x86boot_start와 label 1에서 합쳐진다.
/* This is the 32-bit cold-reset entry point, coming from start16 */원본 주석이 'This is the 32-bit cold-reset entry point, coming from start16'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
(빈 줄)/* This is the 32-bit cold-reset entry point, coming from start16 */까지의 동작과 /* Save BIST */에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 pre-relocation init 상태가 아래 블록의 입력으로 사용되는 경계다.
/* Save BIST */원본 주석이 'Save BIST'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
movl %eax, %ebpcold reset 경로에서 firmware/CPU BIST 결과를 callee-saved EBP에 보존한다.
1:원본 70번 줄의 1:는 앞의 movl %eax, %ebp 결과를 받아 다음 다음 block 경계로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
(빈 줄)1:까지의 동작과 /* Save table pointer */에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 pre-relocation init 상태가 아래 블록의 입력으로 사용되는 경계다.
/* Save table pointer */원본 주석이 'Save table pointer'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
movl %ecx, %esicoreboot table 또는 firmware가 넘긴 table pointer를 board_init_f 전까지 보존한다.
(빈 줄)movl %ecx, %esi까지의 동작과 #ifdef CONFIG_X86_LOAD_FROM_32_BIT에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 pre-relocation init 상태가 아래 블록의 입력으로 사용되는 경계다.
#ifdef CONFIG_X86_LOAD_FROM_32_BIT#ifdef 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. 관련 설정은 CONFIG_X86_LOAD_FROM_32_BIT이다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.
lgdt gdt_ptr2원본 76번 줄의 lgdt gdt_ptr2는 앞의 #ifdef CONFIG_X86_LOAD_FROM_32_BIT 결과를 받아 다음 #endif로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
#endif#endif 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. architecture 또는 build stage 조건을 여닫는다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.
(빈 줄)#endif까지의 동작과 /* Load the segment registers to match the GDT loaded in start16.S */에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 pre-relocation init 상태가 아래 블록의 입력으로 사용되는 경계다.
/* Load the segment registers to match the GDT loaded in start16.S */원본 주석이 'Load the segment registers to match the GDT loaded in start16.S'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
movl $(X86_GDT_ENTRY_32BIT_DS * X86_GDT_ENTRY_SIZE), %eax원본 80번 줄의 movl $(X86_GDT_ENTRY_32BIT_DS * X86_GDT_ENTRY_SIZE), %eax는 앞의 /* Load the segment registers to match the GDT loaded in start16.S */ 결과를 받아 다음 movw %ax, %fs로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
movw %ax, %fs원본 81번 줄의 movw %ax, %fs는 앞의 movl $(X86_GDT_ENTRY_32BIT_DS * X86_GDT_ENTRY_SIZE), %eax 결과를 받아 다음 movw %ax, %ds로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
movw %ax, %ds원본 82번 줄의 movw %ax, %ds는 앞의 movw %ax, %fs 결과를 받아 다음 movw %ax, %gs로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
movw %ax, %gs원본 83번 줄의 movw %ax, %gs는 앞의 movw %ax, %ds 결과를 받아 다음 movw %ax, %es로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
movw %ax, %es원본 84번 줄의 movw %ax, %es는 앞의 movw %ax, %gs 결과를 받아 다음 movw %ax, %ss로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
movw %ax, %ss원본 85번 줄의 movw %ax, %ss는 앞의 movw %ax, %es 결과를 받아 다음 다음 block 경계로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
(빈 줄)movw %ax, %ss까지의 동작과 /* Clear the interrupt vectors */에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 pre-relocation init 상태가 아래 블록의 입력으로 사용되는 경계다.
/* Clear the interrupt vectors */원본 주석이 'Clear the interrupt vectors'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
lidt blank_idt_ptrlimit/base가 0인 IDT를 적재한다. 정상 handler를 준비한 것이 아니라 예기치 않은 interrupt를 차단하는 초기 상태다.
(빈 줄)lidt blank_idt_ptr까지의 동작과 #ifdef CONFIG_USE_EARLY_BOARD_INIT에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 pre-relocation init 상태가 아래 블록의 입력으로 사용되는 경계다.
#ifdef CONFIG_USE_EARLY_BOARD_INIT#ifdef 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. 관련 설정은 CONFIG_USE_EARLY_BOARD_INIT이다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.
/*원본 주석이 'block boundary'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* Critical early platform init - generally not used, we prefer init원본 주석이 'Critical early platform init - generally not used, we prefer init'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* to happen later when we have a console, in case something goes원본 주석이 'to happen later when we have a console, in case something goes'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* wrong.원본 주석이 'wrong.'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
*/원본 주석이 'block boundary'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
jmp early_board_init원본 96번 줄의 jmp early_board_init는 앞의 */ 결과를 받아 다음 .globl early_board_init_ret로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
.globl early_board_init_ret원본 97번 줄의 .globl early_board_init_ret는 앞의 jmp early_board_init 결과를 받아 다음 early_board_init_ret:로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
early_board_init_ret:'early_board_init_ret' label이다. 이곳을 참조하는 branch를 역검색하고 각 진입 경로의 register, stack, lock 조건이 같은지 확인한다.
#endif#endif 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. architecture 또는 build stage 조건을 여닫는다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.
(빈 줄)#endif까지의 동작과 post_code(POST_START)에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 pre-relocation init 상태가 아래 블록의 입력으로 사용되는 경계다.
post_code(POST_START)post_code(POST_START)를 호출한다. 반환값을 직접 사용하지 않으므로 이 함수가 실패를 내부 처리하는지 확인해야 한다. pointer 인자는 CAR stack / gd_t / x86 GDT의 소유권을 넘기는지 호출 동안만 빌리는지 구분하고, 호출 뒤 공개되는 상태를 pre-relocation init 항목과 대조한다.
(빈 줄)post_code(POST_START)까지의 동작과 /* Initialise Cache-As-RAM */에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 pre-relocation init 상태가 아래 블록의 입력으로 사용되는 경계다.
/* Initialise Cache-As-RAM */원본 주석이 'Initialise Cache-As-RAM'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
jmp car_initplatform별 Cache-As-RAM 또는 temporary RAM 생산자로 비복귀 진입한다.
.globl car_init_ret원본 105번 줄의 .globl car_init_ret는 앞의 jmp car_init 결과를 받아 다음 car_init_ret:로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
car_init_ret:'car_init_ret' label이다. 이곳을 참조하는 branch를 역검색하고 각 진입 경로의 register, stack, lock 조건이 같은지 확인한다.
#ifdef CONFIG_USE_CAR#ifdef 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. 관련 설정은 CONFIG_USE_CAR이다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.
/*원본 주석이 'block boundary'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* We now have CONFIG_SYS_CAR_SIZE bytes of Cache-As-RAM (or SRAM,원본 주석이 'We now have CONFIG_SYS_CAR_SIZE bytes of Cache-As-RAM (or SRAM,'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* or fully initialised SDRAM - we really don't care which)원본 주석이 'or fully initialised SDRAM - we really don't care which)'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* starting at CONFIG_SYS_CAR_ADDR to be used as a temporary stack원본 주석이 'starting at CONFIG_SYS_CAR_ADDR to be used as a temporary stack'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* and early malloc() area. The MRC requires some space at the top.원본 주석이 'and early malloc() area. The MRC requires some space at the top.'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
*원본 주석이 'block boundary'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* Stack grows down from top of CAR. We have:원본 주석이 'Stack grows down from top of CAR. We have:'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
*원본 주석이 'block boundary'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* top-> CONFIG_SYS_CAR_ADDR + CONFIG_SYS_CAR_SIZE원본 주석이 'top-> CONFIG_SYS_CAR_ADDR + CONFIG_SYS_CAR_SIZE'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* MRC area원본 주석이 'MRC area'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* global_data with x86 global descriptor table원본 주석이 'global_data with x86 global descriptor table'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* early malloc area원본 주석이 'early malloc area'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* stack원본 주석이 'stack'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* bottom-> CONFIG_SYS_CAR_ADDR원본 주석이 'bottom-> CONFIG_SYS_CAR_ADDR'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
*/원본 주석이 'block boundary'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
movl $(CONFIG_SYS_CAR_ADDR + CONFIG_SYS_CAR_SIZE - 4), %espCAR top에서 32-bit stack을 아래 방향으로 시작한다.
#ifdef CONFIG_DCACHE_RAM_MRC_VAR_SIZE#ifdef 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. 관련 설정은 CONFIG_DCACHE_RAM_MRC_VAR_SIZE이다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.
subl $CONFIG_DCACHE_RAM_MRC_VAR_SIZE, %esp원본 125번 줄의 subl $CONFIG_DCACHE_RAM_MRC_VAR_SIZE, %esp는 앞의 #ifdef CONFIG_DCACHE_RAM_MRC_VAR_SIZE 결과를 받아 다음 #endif로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
#endif#endif 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. architecture 또는 build stage 조건을 여닫는다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.
#else#else 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. architecture 또는 build stage 조건을 여닫는다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.
/*원본 주석이 'block boundary'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* Instructions for FSP1, but not FSP2:원본 주석이 'Instructions for FSP1, but not FSP2:'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* U-Boot enters here twice. For the first time it comes from원본 주석이 'U-Boot enters here twice. For the first time it comes from'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* car_init_done() with esp points to a temporary stack and esi원본 주석이 'car_init_done() with esp points to a temporary stack and esi'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* set to zero. For the second time it comes from fsp_init_done()원본 주석이 'set to zero. For the second time it comes from fsp_init_done()'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* with esi holding the HOB list address returned by the FSP.원본 주석이 'with esi holding the HOB list address returned by the FSP.'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
*/원본 주석이 'block boundary'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
#endif#endif 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. architecture 또는 build stage 조건을 여닫는다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.
/* Set up global data */원본 주석이 'Set up global data'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
mov %esp, %eaxmov %esp, %eax 명령이 register, memory 또는 PC를 직접 바꾼다. DRAM과 정상 exception handler가 없는 32-bit 초기 문맥에서 source operand의 이전 값과 destination operand의 새 값을 debugger로 확인한다.
call board_init_f_alloc_reserve현재 stack cursor 아래에 gd와 malloc_f 등 early runtime 영역을 예약한다.
mov %eax, %espmov %eax, %esp 명령이 register, memory 또는 PC를 직접 바꾼다. DRAM과 정상 exception handler가 없는 32-bit 초기 문맥에서 source operand의 이전 값과 destination operand의 새 값을 debugger로 확인한다.
call board_init_f_init_reserve원본 140번 줄의 call board_init_f_init_reserve는 앞의 mov %eax, %esp 결과를 받아 다음 다음 block 경계로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
(빈 줄)call board_init_f_init_reserve까지의 동작과 #ifdef CONFIG_DEBUG_UART에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 pre-relocation init 상태가 아래 블록의 입력으로 사용되는 경계다.
#ifdef CONFIG_DEBUG_UART#ifdef 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. 관련 설정은 CONFIG_DEBUG_UART이다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.
call debug_uart_init원본 143번 줄의 call debug_uart_init는 앞의 #ifdef CONFIG_DEBUG_UART 결과를 받아 다음 #endif로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
#endif#endif 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. architecture 또는 build stage 조건을 여닫는다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.
(빈 줄)#endif까지의 동작과 /* Get address of global_data */에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 pre-relocation init 상태가 아래 블록의 입력으로 사용되는 경계다.
/* Get address of global_data */원본 주석이 'Get address of global_data'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
mov %fs:0, %edxx86 segment 기반 global-data pointer를 읽는다. FS selector/GDT가 이 주소 계산의 일부다.
#if defined(CONFIG_USE_HOB) && !defined(CONFIG_USE_CAR)#if 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. 관련 설정은 CONFIG_USE_HOB, CONFIG_USE_CAR이다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.
/* Store the HOB list if we have one */원본 주석이 'Store the HOB list if we have one'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
test %esi, %esi원본 150번 줄의 test %esi, %esi는 앞의 /* Store the HOB list if we have one */ 결과를 받아 다음 jz skip_hob로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
jz skip_hob원본 151번 줄의 jz skip_hob는 앞의 test %esi, %esi 결과를 받아 다음 movl %esi, GD_HOB_LIST(%edx)로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
movl %esi, GD_HOB_LIST(%edx)GD_HOB_LIST(%edx)를 호출한다. 반환값을 직접 사용하지 않으므로 이 함수가 실패를 내부 처리하는지 확인해야 한다. pointer 인자는 CAR stack / gd_t / x86 GDT의 소유권을 넘기는지 호출 동안만 빌리는지 구분하고, 호출 뒤 공개되는 상태를 pre-relocation init 항목과 대조한다.
(빈 줄)movl %esi, GD_HOB_LIST(%edx)까지의 동작과 #ifdef CONFIG_HAVE_FSP에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 pre-relocation init 상태가 아래 블록의 입력으로 사용되는 경계다.
#ifdef CONFIG_HAVE_FSP#ifdef 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. 관련 설정은 CONFIG_HAVE_FSP이다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.
/*원본 주석이 'block boundary'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* After fsp_init() returns, the stack has already been switched to a원본 주석이 'After fsp_init() returns, the stack has already been switched to a'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* place within system memory as defined by CONFIG_FSP_TEMP_RAM_ADDR.원본 주석이 'place within system memory as defined by CONFIG_FSP_TEMP_RAM_ADDR.'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* Enlarge the size of malloc() pool before relocation since we have원본 주석이 'Enlarge the size of malloc() pool before relocation since we have'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* plenty of memory now.원본 주석이 'plenty of memory now.'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
*/원본 주석이 'block boundary'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
subl $CONFIG_FSP_SYS_MALLOC_F_LEN, %esp원본 161번 줄의 subl $CONFIG_FSP_SYS_MALLOC_F_LEN, %esp는 앞의 */ 결과를 받아 다음 movl %esp, GD_MALLOC_BASE(%edx)로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
movl %esp, GD_MALLOC_BASE(%edx)GD_MALLOC_BASE(%edx)를 호출한다. 반환값을 직접 사용하지 않으므로 이 함수가 실패를 내부 처리하는지 확인해야 한다. pointer 인자는 CAR stack / gd_t / x86 GDT의 소유권을 넘기는지 호출 동안만 빌리는지 구분하고, 호출 뒤 공개되는 상태를 pre-relocation init 항목과 대조한다.
#endif#endif 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. architecture 또는 build stage 조건을 여닫는다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.
skip_hob:'skip_hob' label이다. 이곳을 참조하는 branch를 역검색하고 각 진입 경로의 register, stack, lock 조건이 같은지 확인한다.
#else#else 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. architecture 또는 build stage 조건을 여닫는다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.
/* Store table pointer */원본 주석이 'Store table pointer'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
movl %esi, GD_TABLE(%edx)GD_TABLE(%edx)를 호출한다. 반환값을 직접 사용하지 않으므로 이 함수가 실패를 내부 처리하는지 확인해야 한다. pointer 인자는 CAR stack / gd_t / x86 GDT의 소유권을 넘기는지 호출 동안만 빌리는지 구분하고, 호출 뒤 공개되는 상태를 pre-relocation init 항목과 대조한다.
#endif#endif 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. architecture 또는 build stage 조건을 여닫는다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.
/* Store BIST */원본 주석이 'Store BIST'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
movl %ebp, GD_BIST(%edx)GD_BIST(%edx)를 호출한다. 반환값을 직접 사용하지 않으므로 이 함수가 실패를 내부 처리하는지 확인해야 한다. pointer 인자는 CAR stack / gd_t / x86 GDT의 소유권을 넘기는지 호출 동안만 빌리는지 구분하고, 호출 뒤 공개되는 상태를 pre-relocation init 항목과 대조한다.
(빈 줄)movl %ebp, GD_BIST(%edx)까지의 동작과 /* Set parameter to board_init_f() to boot flags */에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 pre-relocation init 상태가 아래 블록의 입력으로 사용되는 경계다.
/* Set parameter to board_init_f() to boot flags */원본 주석이 'Set parameter to board_init_f() to boot flags'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
post_code(POST_START_DONE)post_code(POST_START_DONE)를 호출한다. 반환값을 직접 사용하지 않으므로 이 함수가 실패를 내부 처리하는지 확인해야 한다. pointer 인자는 CAR stack / gd_t / x86 GDT의 소유권을 넘기는지 호출 동안만 빌리는지 구분하고, 호출 뒤 공개되는 상태를 pre-relocation init 항목과 대조한다.
xorl %eax, %eax원본 174번 줄의 xorl %eax, %eax는 앞의 post_code(POST_START_DONE) 결과를 받아 다음 다음 block 경계로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
RELATED SOURCE
함께 읽어야 하는 원본 코드
첫 코드 조각만으로 동작이 완성되지 않는 경우 호출 매크로, 객체 정의와 실제 실행 목록을 같은 페이지에 묶었다. 각 조각은 같은 기준 commit에서 가져왔다.
01. 64-bit loader가 기존 stack에서 직접 시작하는 entry
arch/x86/cpu/start64.S 7-50줄이다.
7 */
8
9#include <config.h>
10
11.section .text.start
12.code64
13.globl _start
14.type _start, @function
15_start:
16 /* Set up memory using the existing stack */
17 mov %rsp, %rdi
18 call board_init_f_alloc_reserve
19
20 mov %rax, %rsp
21 mov %rsp, %rdi
22
23 call board_init_f_init_reserve
24
25 xor %rdi, %rdi
26 call board_init_f
27 call board_init_f_r
28
29 /* Should not return here */
30 jmp .
31
32.globl board_init_f_r_trampoline64
33.type board_init_f_r_trampoline64, @function
34board_init_f_r_trampoline64:
35 /*
36 * SDRAM has been initialised, U-Boot code has been copied into
37 * RAM, BSS has been cleared and relocation adjustments have been
38 * made. It is now time to jump into the in-RAM copy of U-Boot
39 *
40 * %eax = Address of top of new stack
41 */
42
43 /* Stack grows down from top of SDRAM */
44 movq %rsi, %rsp
45
46 /* New gd is in rdi */
47
48 /* Re-enter U-Boot by calling board_init_f_r() */
49 call board_init_f_r
50
7-50줄 해설
*/원본 주석이 'block boundary'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
(빈 줄)*/까지의 동작과 #include <config.h>에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 segment registers 상태가 아래 블록의 입력으로 사용되는 경계다.
#include <config.h>#include 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. architecture 또는 build stage 조건을 여닫는다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.
(빈 줄)#include <config.h>까지의 동작과 .section .text.start에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 temporary stack 상태가 아래 블록의 입력으로 사용되는 경계다.
.section .text.start원본 11번 줄의 .section .text.start는 앞의 이전 block 경계 결과를 받아 다음 .code64로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
.code64원본 12번 줄의 .code64는 앞의 .section .text.start 결과를 받아 다음 .globl _start로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
.globl _start원본 13번 줄의 .globl _start는 앞의 .code64 결과를 받아 다음 .type _start, @function로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
.type _start, @function원본 14번 줄의 .type _start, @function는 앞의 .globl _start 결과를 받아 다음 _start:로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
_start:16-bit cold-reset path가 32-bit protected mode를 만든 뒤 도착하는 고정 entry다. fail-safe _x86boot_start와 label 1에서 합쳐진다.
/* Set up memory using the existing stack */원본 주석이 'Set up memory using the existing stack'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
mov %rsp, %rdimov %rsp, %rdi 명령이 register, memory 또는 PC를 직접 바꾼다. DRAM과 정상 exception handler가 없는 32-bit 초기 문맥에서 source operand의 이전 값과 destination operand의 새 값을 debugger로 확인한다.
call board_init_f_alloc_reserve현재 stack cursor 아래에 gd와 malloc_f 등 early runtime 영역을 예약한다.
(빈 줄)call board_init_f_alloc_reserve까지의 동작과 mov %rax, %rsp에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 pre-relocation init 상태가 아래 블록의 입력으로 사용되는 경계다.
mov %rax, %rspmov %rax, %rsp 명령이 register, memory 또는 PC를 직접 바꾼다. DRAM과 정상 exception handler가 없는 32-bit 초기 문맥에서 source operand의 이전 값과 destination operand의 새 값을 debugger로 확인한다.
mov %rsp, %rdimov %rsp, %rdi 명령이 register, memory 또는 PC를 직접 바꾼다. DRAM과 정상 exception handler가 없는 32-bit 초기 문맥에서 source operand의 이전 값과 destination operand의 새 값을 debugger로 확인한다.
(빈 줄)mov %rsp, %rdi까지의 동작과 call board_init_f_init_reserve에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 pre-relocation init 상태가 아래 블록의 입력으로 사용되는 경계다.
call board_init_f_init_reserve원본 23번 줄의 call board_init_f_init_reserve는 앞의 이전 block 경계 결과를 받아 다음 다음 block 경계로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
(빈 줄)call board_init_f_init_reserve까지의 동작과 xor %rdi, %rdi에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 pre-relocation init 상태가 아래 블록의 입력으로 사용되는 경계다.
xor %rdi, %rdi원본 25번 줄의 xor %rdi, %rdi는 앞의 이전 block 경계 결과를 받아 다음 call board_init_f로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
call board_init_fDRAM 발견, top-down reservation과 x86 copy/fixup initcall을 실행한다. 정상 경로는 이 call로 돌아오지 않는다.
call board_init_f_r원본 27번 줄의 call board_init_f_r는 앞의 call board_init_f 결과를 받아 다음 다음 block 경계로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
(빈 줄)call board_init_f_r까지의 동작과 /* Should not return here */에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 pre-relocation init 상태가 아래 블록의 입력으로 사용되는 경계다.
/* Should not return here */원본 주석이 'Should not return here'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
jmp .원본 30번 줄의 jmp .는 앞의 /* Should not return here */ 결과를 받아 다음 다음 block 경계로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
(빈 줄)jmp .까지의 동작과 .globl board_init_f_r_trampoline64에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 pre-relocation init 상태가 아래 블록의 입력으로 사용되는 경계다.
.globl board_init_f_r_trampoline64원본 32번 줄의 .globl board_init_f_r_trampoline64는 앞의 이전 block 경계 결과를 받아 다음 .type board_init_f_r_trampoline64, @function로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
.type board_init_f_r_trampoline64, @function원본 33번 줄의 .type board_init_f_r_trampoline64, @function는 앞의 .globl board_init_f_r_trampoline64 결과를 받아 다음 board_init_f_r_trampoline64:로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
board_init_f_r_trampoline64:'board_init_f_r_trampoline64' label이다. 이곳을 참조하는 branch를 역검색하고 각 진입 경로의 register, stack, lock 조건이 같은지 확인한다.
/*원본 주석이 'block boundary'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* SDRAM has been initialised, U-Boot code has been copied into원본 주석이 'SDRAM has been initialised, U-Boot code has been copied into'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* RAM, BSS has been cleared and relocation adjustments have been원본 주석이 'RAM, BSS has been cleared and relocation adjustments have been'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* made. It is now time to jump into the in-RAM copy of U-Boot원본 주석이 'made. It is now time to jump into the in-RAM copy of U-Boot'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
*원본 주석이 'block boundary'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* %eax = Address of top of new stack원본 주석이 '%eax = Address of top of new stack'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
*/원본 주석이 'block boundary'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
(빈 줄)*/까지의 동작과 /* Stack grows down from top of SDRAM */에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 pre-relocation init 상태가 아래 블록의 입력으로 사용되는 경계다.
/* Stack grows down from top of SDRAM */원본 주석이 'Stack grows down from top of SDRAM'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
movq %rsi, %rsp원본 44번 줄의 movq %rsi, %rsp는 앞의 /* Stack grows down from top of SDRAM */ 결과를 받아 다음 다음 block 경계로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
(빈 줄)movq %rsi, %rsp까지의 동작과 /* New gd is in rdi */에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 pre-relocation init 상태가 아래 블록의 입력으로 사용되는 경계다.
/* New gd is in rdi */원본 주석이 'New gd is in rdi'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
(빈 줄)/* New gd is in rdi */까지의 동작과 /* Re-enter U-Boot by calling board_init_f_r() */에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 pre-relocation init 상태가 아래 블록의 입력으로 사용되는 경계다.
/* Re-enter U-Boot by calling board_init_f_r() */원본 주석이 'Re-enter U-Boot by calling board_init_f_r()'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
call board_init_f_r원본 49번 줄의 call board_init_f_r는 앞의 /* Re-enter U-Boot by calling board_init_f_r() */ 결과를 받아 다음 다음 block 경계로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
(빈 줄)call board_init_f_r까지의 동작과 다음 block 경계에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 pre-relocation init 상태가 아래 블록의 입력으로 사용되는 경계다.
02. CAR top에서 gd와 malloc_f를 예약하는 공통 helper
common/init/board_init.c 74-135줄이다.
74 * alignment constraint of the architecture for which we are bulding.
75 *
76 * - GD is allocated last, so that the return value of this functions is
77 * both the bottom of the reserved area and the address of GD, should
78 * the calling context need it.
79 */
80
81ulong board_init_f_alloc_reserve(ulong top)
82{
83 /* Reserve early malloc arena */
84#ifndef CFG_MALLOC_F_ADDR
85#if CONFIG_IS_ENABLED(SYS_MALLOC_F)
86 top -= CONFIG_VAL(SYS_MALLOC_F_LEN);
87#endif
88#endif
89 /* LAST : reserve GD (rounded up to a multiple of 16 bytes) */
90 top = rounddown(top-sizeof(struct global_data), 16);
91
92 return top;
93}
94
95/*
96 * Initialize reserved space (which has been safely allocated on the C
97 * stack from the C runtime environment handling code).
98 *
99 * Notes:
100 *
101 * Actual reservation was done by the caller; the locations from base
102 * to base+size-1 (where 'size' is the value returned by the allocation
103 * function above) can be accessed freely without risk of corrupting the
104 * C runtime environment.
105 *
106 * IMPORTANT:
107 *
108 * Upon return from the allocation function above, on some architectures
109 * the caller will set gd to the lowest reserved location. Therefore, in
110 * this initialization function, the global data MUST be placed at base.
111 *
112 * ALSO IMPORTANT:
113 *
114 * On some architectures, gd will already be good when entering this
115 * function. On others, it will only be good once arch_setup_gd() returns.
116 * Therefore, global data accesses must be done:
117 *
118 * - through gd_ptr if before the call to arch_setup_gd();
119 *
120 * - through gd once arch_setup_gd() has been called.
121 *
122 * Do not use 'gd->' until arch_setup_gd() has been called!
123 *
124 * IMPORTANT TOO:
125 *
126 * Initialization for each "chunk" (GD, early malloc arena...) ends with
127 * an incrementation line of the form 'base += <some size>'. The last of
128 * these incrementations seems useless, as base will not be used any
129 * more after this incrementation; but if/when a new "chunk" is appended,
130 * this increment will be essential as it will give base right value for
131 * this new chunk (which will have to end with its own incrementation
132 * statement). Besides, the compiler's optimizer will silently detect
133 * and remove the last base incrementation, therefore leaving that last
134 * (seemingly useless) incrementation causes no code increase.
135 */
74-135줄 해설
* alignment constraint of the architecture for which we are bulding.원본 주석이 'alignment constraint of the architecture for which we are bulding.'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
*원본 주석이 'block boundary'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* - GD is allocated last, so that the return value of this functions is원본 주석이 '- GD is allocated last, so that the return value of this functions is'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* both the bottom of the reserved area and the address of GD, should원본 주석이 'both the bottom of the reserved area and the address of GD, should'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* the calling context need it.원본 주석이 'the calling context need it.'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
*/원본 주석이 'block boundary'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
(빈 줄)*/까지의 동작과 ulong board_init_f_alloc_reserve(ulong top)에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 pre-relocation init 상태가 아래 블록의 입력으로 사용되는 경계다.
ulong board_init_f_alloc_reserve(ulong top)board_init_f_alloc_reserve 함수 정의가 시작된다. 입력은 ulong top이며, DRAM과 정상 exception handler가 없는 32-bit 초기 문맥에서 호출된다는 전제로 반환 전까지의 상태 변화를 읽는다.
{바로 위 함수·조건·초기화의 block이 열린다. 이 scope 안에서 만들어지는 지역 객체와 오류 이동 지점을 CAR stack / gd_t / x86 GDT의 수명에 맞춰 묶어 읽는다.
/* Reserve early malloc arena */원본 주석이 'Reserve early malloc arena'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
#ifndef CFG_MALLOC_F_ADDR#ifndef 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. architecture 또는 build stage 조건을 여닫는다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.
#if CONFIG_IS_ENABLED(SYS_MALLOC_F)#if 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. 관련 설정은 CONFIG_IS_ENABLED이다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.
top -= CONFIG_VAL(SYS_MALLOC_F_LEN);top에 CONFIG_VAL(SYS_MALLOC_F_LEN)를 -= 연산으로 반영해 현재 scope의 계산 결과를 저장한다. 이후 board_init_f 단계가 이 값을 처음 소비하는 지점을 찾는다. 실패 경로가 이 field를 이전 값으로 되돌리거나 객체 전체를 폐기하는지도 확인한다.
#endif#endif 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. architecture 또는 build stage 조건을 여닫는다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.
#endif#endif 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. architecture 또는 build stage 조건을 여닫는다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.
/* LAST : reserve GD (rounded up to a multiple of 16 bytes) */원본 주석이 'LAST : reserve GD (rounded up to a multiple of 16 bytes)'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
top = rounddown(top-sizeof(struct global_data), 16);top = rounddown(top-sizeof(struct global_data), 16)를 선언한다. 함수 안 선언이면 현재 stack frame, file scope와 static이면 image의 data/BSS에 놓인다. 이 값이 CAR stack / gd_t / x86 GDT를 직접 소유하는지 pointer만 빌리는지, DRAM과 정상 exception handler가 없는 32-bit 초기 문맥를 벗어난 뒤에도 참조되는지 다음 대입과 callback 등록까지 따라간다.
(빈 줄)top = rounddown(top-sizeof(struct global_data), 16);까지의 동작과 return top;에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 pre-relocation init 상태가 아래 블록의 입력으로 사용되는 경계다.
return top;top를 호출자에게 반환한다. caller가 이 값을 검사한 뒤 부분 초기화된 CAR stack / gd_t / x86 GDT를 정리하거나 다음 단계로 진행하는지 확인한다.
}현재 block, initializer 또는 호출의 경계를 닫는다. 이 지점까지 획득한 resource가 성공 경로와 실패 경로에서 대칭인지 점검한다.
(빈 줄)}까지의 동작과 /*에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 pre-relocation init 상태가 아래 블록의 입력으로 사용되는 경계다.
/*원본 주석이 'block boundary'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* Initialize reserved space (which has been safely allocated on the C원본 주석이 'Initialize reserved space (which has been safely allocated on the C'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* stack from the C runtime environment handling code).원본 주석이 'stack from the C runtime environment handling code).'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
*원본 주석이 'block boundary'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* Notes:원본 주석이 'Notes:'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
*원본 주석이 'block boundary'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* Actual reservation was done by the caller; the locations from base원본 주석이 'Actual reservation was done by the caller; the locations from base'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* to base+size-1 (where 'size' is the value returned by the allocation원본 주석이 'to base+size-1 (where 'size' is the value returned by the allocation'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* function above) can be accessed freely without risk of corrupting the원본 주석이 'function above) can be accessed freely without risk of corrupting the'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* C runtime environment.원본 주석이 'C runtime environment.'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
*원본 주석이 'block boundary'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* IMPORTANT:원본 주석이 'IMPORTANT:'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
*원본 주석이 'block boundary'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* Upon return from the allocation function above, on some architectures원본 주석이 'Upon return from the allocation function above, on some architectures'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* the caller will set gd to the lowest reserved location. Therefore, in원본 주석이 'the caller will set gd to the lowest reserved location. Therefore, in'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* this initialization function, the global data MUST be placed at base.원본 주석이 'this initialization function, the global data MUST be placed at base.'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
*원본 주석이 'block boundary'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* ALSO IMPORTANT:원본 주석이 'ALSO IMPORTANT:'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
*원본 주석이 'block boundary'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* On some architectures, gd will already be good when entering this원본 주석이 'On some architectures, gd will already be good when entering this'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* function. On others, it will only be good once arch_setup_gd() returns.원본 주석이 'function. On others, it will only be good once arch_setup_gd() returns.'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* Therefore, global data accesses must be done:원본 주석이 'Therefore, global data accesses must be done:'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
*원본 주석이 'block boundary'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* - through gd_ptr if before the call to arch_setup_gd();원본 주석이 '- through gd_ptr if before the call to arch_setup_gd();'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
*원본 주석이 'block boundary'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* - through gd once arch_setup_gd() has been called.원본 주석이 '- through gd once arch_setup_gd() has been called.'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
*원본 주석이 'block boundary'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* Do not use 'gd->' until arch_setup_gd() has been called!원본 주석이 'Do not use 'gd->' until arch_setup_gd() has been called!'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
*원본 주석이 'block boundary'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* IMPORTANT TOO:원본 주석이 'IMPORTANT TOO:'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
*원본 주석이 'block boundary'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* Initialization for each "chunk" (GD, early malloc arena...) ends with원본 주석이 'Initialization for each "chunk" (GD, early malloc arena...) ends with'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* an incrementation line of the form 'base += <some size>'. The last of원본 주석이 'an incrementation line of the form 'base += <some size>'. The last of'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* these incrementations seems useless, as base will not be used any원본 주석이 'these incrementations seems useless, as base will not be used any'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* more after this incrementation; but if/when a new "chunk" is appended,원본 주석이 'more after this incrementation; but if/when a new "chunk" is appended,'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* this increment will be essential as it will give base right value for원본 주석이 'this increment will be essential as it will give base right value for'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* this new chunk (which will have to end with its own incrementation원본 주석이 'this new chunk (which will have to end with its own incrementation'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* statement). Besides, the compiler's optimizer will silently detect원본 주석이 'statement). Besides, the compiler's optimizer will silently detect'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* and remove the last base incrementation, therefore leaving that last원본 주석이 'and remove the last base incrementation, therefore leaving that last'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* (seemingly useless) incrementation causes no code increase.원본 주석이 '(seemingly useless) incrementation causes no code increase.'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
*/원본 주석이 'block boundary'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
03. x86이 FS segment에 gd pointer를 연결하는 초기화
arch/x86/cpu/i386/cpu.c 118-167줄이다.
118
119 gdt.len = (num_entries * X86_GDT_ENTRY_SIZE) - 1;
120 gdt.ptr = (ulong)boot_gdt;
121
122 asm volatile("lgdtl %0\n" : : "m" (gdt));
123}
124
125void arch_setup_gd(gd_t *new_gd)
126{
127 u64 *gdt_addr;
128
129 gdt_addr = new_gd->arch.gdt;
130
131 /*
132 * CS: code, read/execute, 4 GB, base 0
133 *
134 * Some OS (like VxWorks) requires GDT entry 1 to be the 32-bit CS
135 */
136 gdt_addr[X86_GDT_ENTRY_UNUSED] = GDT_ENTRY(0xc09b, 0, 0xfffff);
137 gdt_addr[X86_GDT_ENTRY_32BIT_CS] = GDT_ENTRY(0xc09b, 0, 0xfffff);
138
139 /* DS: data, read/write, 4 GB, base 0 */
140 gdt_addr[X86_GDT_ENTRY_32BIT_DS] = GDT_ENTRY(0xc093, 0, 0xfffff);
141
142 /*
143 * FS: data, read/write, sizeof (Global Data Pointer),
144 * base (Global Data Pointer)
145 */
146 new_gd->arch.gd_addr = new_gd;
147 gdt_addr[X86_GDT_ENTRY_32BIT_FS] = GDT_ENTRY(0x8093,
148 (ulong)&new_gd->arch.gd_addr,
149 sizeof(new_gd->arch.gd_addr) - 1);
150
151 /* 16-bit CS: code, read/execute, 64 kB, base 0 */
152 gdt_addr[X86_GDT_ENTRY_16BIT_CS] = GDT_ENTRY(0x009b, 0, 0x0ffff);
153
154 /* 16-bit DS: data, read/write, 64 kB, base 0 */
155 gdt_addr[X86_GDT_ENTRY_16BIT_DS] = GDT_ENTRY(0x0093, 0, 0x0ffff);
156
157 gdt_addr[X86_GDT_ENTRY_16BIT_FLAT_CS] = GDT_ENTRY(0x809b, 0, 0xfffff);
158 gdt_addr[X86_GDT_ENTRY_16BIT_FLAT_DS] = GDT_ENTRY(0x8093, 0, 0xfffff);
159 gdt_addr[X86_GDT_ENTRY_64BIT_CS] = GDT_ENTRY(0xaf9b, 0, 0xfffff);
160 gdt_addr[X86_GDT_ENTRY_64BIT_TS1] = GDT_ENTRY(0x8980, 0, 0xfffff);
161 gdt_addr[X86_GDT_ENTRY_64BIT_TS2] = 0;
162
163 load_gdt(gdt_addr, X86_GDT_NUM_ENTRIES);
164 load_ds(X86_GDT_ENTRY_32BIT_DS);
165 load_es(X86_GDT_ENTRY_32BIT_DS);
166 load_gs(X86_GDT_ENTRY_32BIT_DS);
167 load_ss(X86_GDT_ENTRY_32BIT_DS);
118-167줄 해설
(빈 줄)이전 block 경계까지의 동작과 gdt.len = (num_entries * X86_GDT_ENTRY_SIZE) - 1;에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 BIST and table pointer 상태가 아래 블록의 입력으로 사용되는 경계다.
gdt.len = (num_entries * X86_GDT_ENTRY_SIZE) - 1;gdt.len에 (num_entries * X86_GDT_ENTRY_SIZE) - 1를 = 연산으로 반영해 CAR stack / gd_t / x86 GDT와 연결된 field를 갱신한다. 이후 32-bit protected entry 단계가 이 값을 처음 소비하는 지점을 찾는다. 실패 경로가 이 field를 이전 값으로 되돌리거나 객체 전체를 폐기하는지도 확인한다.
gdt.ptr = (ulong)boot_gdt;gdt.ptr = (ulong)boot_gdt를 선언한다. 함수 안 선언이면 현재 stack frame, file scope와 static이면 image의 data/BSS에 놓인다. 이 값이 CAR stack / gd_t / x86 GDT를 직접 소유하는지 pointer만 빌리는지, DRAM과 정상 exception handler가 없는 32-bit 초기 문맥를 벗어난 뒤에도 참조되는지 다음 대입과 callback 등록까지 따라간다.
(빈 줄)gdt.ptr = (ulong)boot_gdt;까지의 동작과 asm volatile("lgdtl %0\n" : : "m" (gdt));에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 temporary stack 상태가 아래 블록의 입력으로 사용되는 경계다.
asm volatile("lgdtl %0\n" : : "m" (gdt));volatile("lgdtl %0\n" : : "m" (gdt))를 호출한다. 반환값을 직접 사용하지 않으므로 이 함수가 실패를 내부 처리하는지 확인해야 한다. pointer 인자는 CAR stack / gd_t / x86 GDT의 소유권을 넘기는지 호출 동안만 빌리는지 구분하고, 호출 뒤 공개되는 상태를 global_data 항목과 대조한다.
}현재 block, initializer 또는 호출의 경계를 닫는다. 이 지점까지 획득한 resource가 성공 경로와 실패 경로에서 대칭인지 점검한다.
(빈 줄)}까지의 동작과 void arch_setup_gd(gd_t *new_gd)에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 pre-relocation init 상태가 아래 블록의 입력으로 사용되는 경계다.
void arch_setup_gd(gd_t *new_gd)arch_setup_gd 함수 정의가 시작된다. 입력은 gd_t *new_gd이며, DRAM과 정상 exception handler가 없는 32-bit 초기 문맥에서 호출된다는 전제로 반환 전까지의 상태 변화를 읽는다.
{바로 위 함수·조건·초기화의 block이 열린다. 이 scope 안에서 만들어지는 지역 객체와 오류 이동 지점을 CAR stack / gd_t / x86 GDT의 수명에 맞춰 묶어 읽는다.
u64 *gdt_addr;원본 127번 줄의 u64 *gdt_addr;는 앞의 { 결과를 받아 다음 다음 block 경계로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
(빈 줄)u64 *gdt_addr;까지의 동작과 gdt_addr = new_gd->arch.gdt;에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 pre-relocation init 상태가 아래 블록의 입력으로 사용되는 경계다.
gdt_addr = new_gd->arch.gdt;gdt_addr에 new_gd->arch.gdt를 = 연산으로 반영해 현재 scope의 계산 결과를 저장한다. 주소·크기 값이면 단위와 정렬, 덧셈 overflow를 함께 검산한다. 이후 board_init_f 단계가 이 값을 처음 소비하는 지점을 찾는다. 실패 경로가 이 field를 이전 값으로 되돌리거나 객체 전체를 폐기하는지도 확인한다.
(빈 줄)gdt_addr = new_gd->arch.gdt;까지의 동작과 /*에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 pre-relocation init 상태가 아래 블록의 입력으로 사용되는 경계다.
/*원본 주석이 'block boundary'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* CS: code, read/execute, 4 GB, base 0원본 주석이 'CS: code, read/execute, 4 GB, base 0'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
*원본 주석이 'block boundary'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* Some OS (like VxWorks) requires GDT entry 1 to be the 32-bit CS원본 주석이 'Some OS (like VxWorks) requires GDT entry 1 to be the 32-bit CS'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
*/원본 주석이 'block boundary'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
gdt_addr[X86_GDT_ENTRY_UNUSED] = GDT_ENTRY(0xc09b, 0, 0xfffff);gdt_addr[X86_GDT_ENTRY_UNUSED]에 GDT_ENTRY(0xc09b, 0, 0xfffff)를 = 연산으로 반영해 현재 scope의 계산 결과를 저장한다. 주소·크기 값이면 단위와 정렬, 덧셈 overflow를 함께 검산한다. 이후 board_init_f 단계가 이 값을 처음 소비하는 지점을 찾는다. 실패 경로가 이 field를 이전 값으로 되돌리거나 객체 전체를 폐기하는지도 확인한다.
gdt_addr[X86_GDT_ENTRY_32BIT_CS] = GDT_ENTRY(0xc09b, 0, 0xfffff);gdt_addr[X86_GDT_ENTRY_32BIT_CS]에 GDT_ENTRY(0xc09b, 0, 0xfffff)를 = 연산으로 반영해 현재 scope의 계산 결과를 저장한다. 주소·크기 값이면 단위와 정렬, 덧셈 overflow를 함께 검산한다. 이후 board_init_f 단계가 이 값을 처음 소비하는 지점을 찾는다. 실패 경로가 이 field를 이전 값으로 되돌리거나 객체 전체를 폐기하는지도 확인한다.
(빈 줄)gdt_addr[X86_GDT_ENTRY_32BIT_CS] = GDT_ENTRY(0xc09b, 0, 0xfffff);까지의 동작과 /* DS: data, read/write, 4 GB, base 0 */에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 pre-relocation init 상태가 아래 블록의 입력으로 사용되는 경계다.
/* DS: data, read/write, 4 GB, base 0 */원본 주석이 'DS: data, read/write, 4 GB, base 0'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
gdt_addr[X86_GDT_ENTRY_32BIT_DS] = GDT_ENTRY(0xc093, 0, 0xfffff);gdt_addr[X86_GDT_ENTRY_32BIT_DS]에 GDT_ENTRY(0xc093, 0, 0xfffff)를 = 연산으로 반영해 현재 scope의 계산 결과를 저장한다. 주소·크기 값이면 단위와 정렬, 덧셈 overflow를 함께 검산한다. 이후 board_init_f 단계가 이 값을 처음 소비하는 지점을 찾는다. 실패 경로가 이 field를 이전 값으로 되돌리거나 객체 전체를 폐기하는지도 확인한다.
(빈 줄)gdt_addr[X86_GDT_ENTRY_32BIT_DS] = GDT_ENTRY(0xc093, 0, 0xfffff);까지의 동작과 /*에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 pre-relocation init 상태가 아래 블록의 입력으로 사용되는 경계다.
/*원본 주석이 'block boundary'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* FS: data, read/write, sizeof (Global Data Pointer),원본 주석이 'FS: data, read/write, sizeof (Global Data Pointer),'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
* base (Global Data Pointer)원본 주석이 'base (Global Data Pointer)'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
*/원본 주석이 'block boundary'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
new_gd->arch.gd_addr = new_gd;new_gd->arch.gd_addr에 new_gd를 = 연산으로 반영해 CAR stack / gd_t / x86 GDT와 연결된 field를 갱신한다. 주소·크기 값이면 단위와 정렬, 덧셈 overflow를 함께 검산한다. 이후 board_init_f 단계가 이 값을 처음 소비하는 지점을 찾는다. 실패 경로가 이 field를 이전 값으로 되돌리거나 객체 전체를 폐기하는지도 확인한다.
gdt_addr[X86_GDT_ENTRY_32BIT_FS] = GDT_ENTRY(0x8093,GDT_ENTRY(인자 없음)를 호출한다. 반환값을 직접 사용하지 않으므로 이 함수가 실패를 내부 처리하는지 확인해야 한다. pointer 인자는 CAR stack / gd_t / x86 GDT의 소유권을 넘기는지 호출 동안만 빌리는지 구분하고, 호출 뒤 공개되는 상태를 pre-relocation init 항목과 대조한다.
(ulong)&new_gd->arch.gd_addr,원본 148번 줄의 (ulong)&new_gd->arch.gd_addr,는 앞의 gdt_addr[X86_GDT_ENTRY_32BIT_FS] = GDT_ENTRY(0x8093, 결과를 받아 다음 sizeof(new_gd->arch.gd_addr) - 1);로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
sizeof(new_gd->arch.gd_addr) - 1);원본 149번 줄의 sizeof(new_gd->arch.gd_addr) - 1);는 앞의 (ulong)&new_gd->arch.gd_addr, 결과를 받아 다음 다음 block 경계로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다' 조건이 유지되는지 확인한다.
(빈 줄)sizeof(new_gd->arch.gd_addr) - 1);까지의 동작과 /* 16-bit CS: code, read/execute, 64 kB, base 0 */에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 pre-relocation init 상태가 아래 블록의 입력으로 사용되는 경계다.
/* 16-bit CS: code, read/execute, 64 kB, base 0 */원본 주석이 '16-bit CS: code, read/execute, 64 kB, base 0'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
gdt_addr[X86_GDT_ENTRY_16BIT_CS] = GDT_ENTRY(0x009b, 0, 0x0ffff);gdt_addr[X86_GDT_ENTRY_16BIT_CS]에 GDT_ENTRY(0x009b, 0, 0x0ffff)를 = 연산으로 반영해 현재 scope의 계산 결과를 저장한다. 주소·크기 값이면 단위와 정렬, 덧셈 overflow를 함께 검산한다. 이후 board_init_f 단계가 이 값을 처음 소비하는 지점을 찾는다. 실패 경로가 이 field를 이전 값으로 되돌리거나 객체 전체를 폐기하는지도 확인한다.
(빈 줄)gdt_addr[X86_GDT_ENTRY_16BIT_CS] = GDT_ENTRY(0x009b, 0, 0x0ffff);까지의 동작과 /* 16-bit DS: data, read/write, 64 kB, base 0 */에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 pre-relocation init 상태가 아래 블록의 입력으로 사용되는 경계다.
/* 16-bit DS: data, read/write, 64 kB, base 0 */원본 주석이 '16-bit DS: data, read/write, 64 kB, base 0'라고 기록한 줄이다. 바로 아래 구현이 이 전제와 같은 순서·단위를 사용하는지 대조한다.
gdt_addr[X86_GDT_ENTRY_16BIT_DS] = GDT_ENTRY(0x0093, 0, 0x0ffff);gdt_addr[X86_GDT_ENTRY_16BIT_DS]에 GDT_ENTRY(0x0093, 0, 0x0ffff)를 = 연산으로 반영해 현재 scope의 계산 결과를 저장한다. 주소·크기 값이면 단위와 정렬, 덧셈 overflow를 함께 검산한다. 이후 board_init_f 단계가 이 값을 처음 소비하는 지점을 찾는다. 실패 경로가 이 field를 이전 값으로 되돌리거나 객체 전체를 폐기하는지도 확인한다.
(빈 줄)gdt_addr[X86_GDT_ENTRY_16BIT_DS] = GDT_ENTRY(0x0093, 0, 0x0ffff);까지의 동작과 gdt_addr[X86_GDT_ENTRY_16BIT_FLAT_CS] = GDT_ENTRY(0x809b, 0, 0xfffff);에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 pre-relocation init 상태가 아래 블록의 입력으로 사용되는 경계다.
gdt_addr[X86_GDT_ENTRY_16BIT_FLAT_CS] = GDT_ENTRY(0x809b, 0, 0xfffff);gdt_addr[X86_GDT_ENTRY_16BIT_FLAT_CS]에 GDT_ENTRY(0x809b, 0, 0xfffff)를 = 연산으로 반영해 현재 scope의 계산 결과를 저장한다. 주소·크기 값이면 단위와 정렬, 덧셈 overflow를 함께 검산한다. 이후 board_init_f 단계가 이 값을 처음 소비하는 지점을 찾는다. 실패 경로가 이 field를 이전 값으로 되돌리거나 객체 전체를 폐기하는지도 확인한다.
gdt_addr[X86_GDT_ENTRY_16BIT_FLAT_DS] = GDT_ENTRY(0x8093, 0, 0xfffff);gdt_addr[X86_GDT_ENTRY_16BIT_FLAT_DS]에 GDT_ENTRY(0x8093, 0, 0xfffff)를 = 연산으로 반영해 현재 scope의 계산 결과를 저장한다. 주소·크기 값이면 단위와 정렬, 덧셈 overflow를 함께 검산한다. 이후 board_init_f 단계가 이 값을 처음 소비하는 지점을 찾는다. 실패 경로가 이 field를 이전 값으로 되돌리거나 객체 전체를 폐기하는지도 확인한다.
gdt_addr[X86_GDT_ENTRY_64BIT_CS] = GDT_ENTRY(0xaf9b, 0, 0xfffff);gdt_addr[X86_GDT_ENTRY_64BIT_CS]에 GDT_ENTRY(0xaf9b, 0, 0xfffff)를 = 연산으로 반영해 현재 scope의 계산 결과를 저장한다. 주소·크기 값이면 단위와 정렬, 덧셈 overflow를 함께 검산한다. 이후 board_init_f 단계가 이 값을 처음 소비하는 지점을 찾는다. 실패 경로가 이 field를 이전 값으로 되돌리거나 객체 전체를 폐기하는지도 확인한다.
gdt_addr[X86_GDT_ENTRY_64BIT_TS1] = GDT_ENTRY(0x8980, 0, 0xfffff);gdt_addr[X86_GDT_ENTRY_64BIT_TS1]에 GDT_ENTRY(0x8980, 0, 0xfffff)를 = 연산으로 반영해 현재 scope의 계산 결과를 저장한다. 주소·크기 값이면 단위와 정렬, 덧셈 overflow를 함께 검산한다. 이후 board_init_f 단계가 이 값을 처음 소비하는 지점을 찾는다. 실패 경로가 이 field를 이전 값으로 되돌리거나 객체 전체를 폐기하는지도 확인한다.
gdt_addr[X86_GDT_ENTRY_64BIT_TS2] = 0;gdt_addr[X86_GDT_ENTRY_64BIT_TS2]에 0를 = 연산으로 반영해 현재 scope의 계산 결과를 저장한다. 주소·크기 값이면 단위와 정렬, 덧셈 overflow를 함께 검산한다. 이후 board_init_f 단계가 이 값을 처음 소비하는 지점을 찾는다. 실패 경로가 이 field를 이전 값으로 되돌리거나 객체 전체를 폐기하는지도 확인한다.
(빈 줄)gdt_addr[X86_GDT_ENTRY_64BIT_TS2] = 0;까지의 동작과 load_gdt(gdt_addr, X86_GDT_NUM_ENTRIES);에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 pre-relocation init 상태가 아래 블록의 입력으로 사용되는 경계다.
load_gdt(gdt_addr, X86_GDT_NUM_ENTRIES);load_gdt(gdt_addr, X86_GDT_NUM_ENTRIES)를 호출한다. 반환값을 직접 사용하지 않으므로 이 함수가 실패를 내부 처리하는지 확인해야 한다. pointer 인자는 CAR stack / gd_t / x86 GDT의 소유권을 넘기는지 호출 동안만 빌리는지 구분하고, 호출 뒤 공개되는 상태를 pre-relocation init 항목과 대조한다.
load_ds(X86_GDT_ENTRY_32BIT_DS);load_ds(X86_GDT_ENTRY_32BIT_DS)를 호출한다. 반환값을 직접 사용하지 않으므로 이 함수가 실패를 내부 처리하는지 확인해야 한다. pointer 인자는 CAR stack / gd_t / x86 GDT의 소유권을 넘기는지 호출 동안만 빌리는지 구분하고, 호출 뒤 공개되는 상태를 pre-relocation init 항목과 대조한다.
load_es(X86_GDT_ENTRY_32BIT_DS);load_es(X86_GDT_ENTRY_32BIT_DS)를 호출한다. 반환값을 직접 사용하지 않으므로 이 함수가 실패를 내부 처리하는지 확인해야 한다. pointer 인자는 CAR stack / gd_t / x86 GDT의 소유권을 넘기는지 호출 동안만 빌리는지 구분하고, 호출 뒤 공개되는 상태를 pre-relocation init 항목과 대조한다.
load_gs(X86_GDT_ENTRY_32BIT_DS);load_gs(X86_GDT_ENTRY_32BIT_DS)를 호출한다. 반환값을 직접 사용하지 않으므로 이 함수가 실패를 내부 처리하는지 확인해야 한다. pointer 인자는 CAR stack / gd_t / x86 GDT의 소유권을 넘기는지 호출 동안만 빌리는지 구분하고, 호출 뒤 공개되는 상태를 pre-relocation init 항목과 대조한다.
load_ss(X86_GDT_ENTRY_32BIT_DS);load_ss(X86_GDT_ENTRY_32BIT_DS)를 호출한다. 반환값을 직접 사용하지 않으므로 이 함수가 실패를 내부 처리하는지 확인해야 한다. pointer 인자는 CAR stack / gd_t / x86 GDT의 소유권을 넘기는지 호출 동안만 빌리는지 구분하고, 호출 뒤 공개되는 상태를 pre-relocation init 항목과 대조한다.
DETAILS
내부 동작을 더 깊게 읽기
진입 조건을 먼저 고정한다
cold reset or previous loader에서 들어온 실행은 DRAM과 정상 exception handler가 없는 32-bit 초기 문맥에 놓여 있다. 이때 interrupt, MMU/cache, stack, heap 중 무엇이 이미 준비되었는지 소스의 호출자까지 올라가 확인한다. 같은 함수라도 SPL, relocation 전후, app thread처럼 호출 문맥이 달라지면 허용되는 API와 지연 시간이 달라진다.
x86는 DRAM 전 stack을 Cache-As-RAM 또는 platform temporary RAM에 둔다. _x86boot_start와 _start, TPL/SPL/U-Boot proper의 실제 선택을 먼저 고정하고 CAR top에서 stack, gd와 malloc_f가 차지하는 주소를 계산한다.
중심 객체의 생성과 공개를 나눈다
이 글의 중심 객체는 CAR stack / gd_t / x86 GDT다. 메모리를 확보한 시점, 필드를 채운 시점, 전역 list나 다른 subsystem에 공개한 시점을 구분한다. 공개 뒤 오류가 발생한다면 목록에서 제거하고 child, buffer, reference를 역순으로 정리하는지 확인한다.
빌드 산출물 관점에서는 x86 U-Boot pre-relocation image 안에 해당 symbol과 section이 실제로 포함되었는지도 map과 objdump로 검증한다.
주소, 크기와 정렬을 계산한다
부트 코드의 오류는 논리보다 주소 계산에서 먼저 드러나는 경우가 많다. source range, destination range, header가 말하는 payload size, block 또는 page 단위를 표로 적고 각 구간의 끝 주소를 직접 계산한다. 끝 주소는 start + size - 1인지 exclusive end인지 API 계약을 확인한다.
board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다.
성공 flag와 실제 완료 시점을 맞춘다
32-bit protected entry → GDT and blank IDT → car_init → CAR stack → gd and malloc_f 구간에서는 부분 초기화 상태가 생길 수 있다. flag, list insertion, callback 등록, storage write 완료 중 무엇이 성공의 기준인지 찾는다. hardware write나 DMA가 포함되면 함수 반환과 장치 완료가 같은 시점인지도 확인한다.
다른 CPU, interrupt handler, USB completion 또는 shell command가 상태를 관찰할 수 있다면 memory ordering과 lock 범위도 함께 읽는다.
마지막 handoff의 계약을 적는다
정상 경로는 board_init_f에서 끝난다. 이 단계가 함수 반환인지, scheduler 전환인지, 다른 image로의 비복귀 분기인지 구분한다. 비복귀 handoff라면 cache clean/invalidate, interrupt disable, 장치 quiesce, argument register와 FDT 또는 image address가 최종 점검 항목이다.
반환하는 경로라면 caller가 오류와 부분 성공을 구분하고 다음 후보 또는 복구 경로를 선택하는지 확인한다.
IMPLEMENTATION NOTES
구현을 읽을 때 놓치기 쉬운 부분
x86의 첫 entry는 하나가 아니라 cold reset, fail-safe 32-bit, SPL/TPL 경로로 갈린다
arch/x86/cpu/start.S의 _x86boot_start는 coreboot나 EFI처럼 다른 loader가 이미 32-bit 환경을 만든 경우를 위한 fail-safe entry이고, _start는 start16에서 넘어오는 cold-reset 경로다. 두 경로는 label 1에서 합쳐지지만 BIST와 table pointer의 출처가 다르다. 현재 board가 TPL, SPL, U-Boot proper 중 어느 stage에 이 파일을 쓰는지도 source 상단 주석과 최종 map에서 확인해야 한다.
64-bit loader가 arch/x86/cpu/start64.S로 직접 들어오는 설정은 또 다른 계약이다. 그 경로는 기존 %rsp가 유효하다고 전제하고 CAR 초기화 없이 board_init_f_alloc_reserve()부터 시작한다. 함수 이름이 같아도 진입 mode와 stack 생산자가 다르므로 한 개의 reset 흐름도로 합치면 안 된다.
cache를 끄는 동작과 Cache-As-RAM을 만드는 동작은 모순이 아니다
fail-safe entry는 CR0.NW/CD를 세우고 wbinvd로 이전 loader의 cache state를 정리한다. 이후 car_init은 platform별 MTRR, cache way 또는 FSP temporary RAM 계약으로 특정 physical range를 stack과 early malloc에 쓸 수 있게 만든다. 앞의 cache disable은 불명확한 상속 상태 제거이고 뒤의 CAR은 제어된 임시 memory 생성이다.
QEMU, coreboot, Intel FSP, Slim Bootloader는 car_init 구현이 서로 다르다. start.S만 읽고 cache line이 실제 DRAM처럼 write-back되는 과정을 추정하지 말고 link된 arch/x86/lib/*/car.S와 호출 후 %esp가 가리키는 range를 함께 확인한다.
blank IDT는 예외를 처리하는 표가 아니라 예외가 나면 즉시 멈추게 하는 상태다
lidt blank_idt_ptr는 limit와 base가 0인 IDT를 적재한다. 아직 정상 handler와 stack frame을 만들 수 없는 시점에 잘못된 interrupt vector를 따라가는 것을 막지만, #PF나 #GP가 발생하면 double/triple fault와 reset로 이어질 수 있다. 따라서 이 구간의 무출력 reset은 예외가 없었다는 증거가 아니다.
초기 디버깅에서는 serial보다 POST code, hardware debugger, QEMU -d int,cpu_reset이 더 직접적이다. _start, car_init_ret, board_init_f 직전에 breakpoint를 두고 CR0, GDTR, IDTR, ESP를 기록한다.
CAR top에서 stack, MRC area, gd와 early malloc의 실제 배치를 계산한다
CONFIG_SYS_CAR_ADDR + CONFIG_SYS_CAR_SIZE - 4가 첫 stack top이고, 필요하면 MRC variable 영역만큼 더 내린다. board_init_f_alloc_reserve()는 이 cursor에서 gd_t, 초기 GDT와 malloc_f 공간을 정렬해 예약하고 새 stack pointer를 반환한다. 단순히 CAR size가 크다고 안전한 것이 아니라 가장 깊은 call chain과 early allocation의 합이 bottom을 넘지 않아야 한다.
map에 표시된 sizeof(gd_t)와 .config의 SYS_MALLOC_F_LEN, DCACHE_RAM_MRC_VAR_SIZE를 대입해 각 start/end를 표로 만들 수 있다. stack watermark 또는 poison pattern을 CAR bottom부터 채우면 실제 최대 사용량을 확인할 수 있다.
x86의 gd pointer는 ARM64 x18과 다르게 FS segment를 통해 읽힌다
board_init_f_init_reserve()가 초기 global data를 만든 뒤 x86 assembly는 %fs:0에서 gd 주소를 읽는다. segment selector와 GDT base가 틀리면 일반 pointer처럼 보이는 첫 gd access부터 잘못된 주소를 사용한다. 이 때문에 segment register load, GDT descriptor와 gd 초기화를 같은 경계로 봐야 한다.
HOB를 쓰는 FSP 경로에서는 %esi가 firmware hand-off block list를 보존하고, 일반 경로에서는 table pointer를 GD_TABLE에 저장한다. 같은 register가 build option에 따라 다른 metadata를 전달한다.
board_init_f가 돌아오면 정상 경로가 아니다
x86에서는 board_init_f()의 initcall 목록 안에 copy, ELF relocation fixup, BSS clear와 jump_to_copy()가 포함될 수 있다. 정상 경로는 board_init_f_r_trampoline으로 비복귀 이동한다. 따라서 call board_init_f 다음의 POST 0x85와 die는 성공 continuation이 아니라 예상하지 못한 return을 잡는 경계다.
ARM64의 crt0_64.S가 board_init_f() 반환 뒤 relocate_code를 호출하는 구조와 가장 크게 다른 지점이다. architecture별 initcall_run_f() 전처리 결과를 보지 않으면 relocation 책임을 잘못 배치하게 된다.
초기 latency는 CAR 준비와 DRAM training을 나눠 측정한다
CAR 생성은 platform register/FSP 호출에 따라 시간이 달라지고, DRAM training은 이후 board_init_f initcall에서 더 큰 지연을 만들 수 있다. reset부터 첫 console까지 한 구간으로만 재면 어느 단계가 느린지 알 수 없다.
POST code와 TSC를 _start, car_init_ret, dram_init, jump_to_copy, board_init_f_r에 기록해 구간별 upper bound를 만든다. cold boot, warm reset, training cache 사용 여부도 분리한다.
CAR 128KiB 예제로 stack, gd와 malloc_f의 경계를 직접 계산한다
CONFIG_SYS_CAR_ADDR=0xfef00000, CONFIG_SYS_CAR_SIZE=0x20000이면 첫 ESP는 0xfef1fffc다. 여기서 16-byte 정렬 뒤 gd_t=0x1c0, 초기 GDT=0x40, SYS_MALLOC_F_LEN=0x4000을 차례로 내리면 early runtime cursor는 대략 0xfef1be00 아래가 된다. MRC variable 0x4000을 CAR 상단에 따로 둔다면 그만큼을 stack top 계산 전에 먼저 빼야 한다.
남은 [CAR bottom, runtime cursor)가 call frame과 local variable의 실제 stack budget이다. 예제 값만 고정해서 쓰지 말고 최종 sizeof(struct global_data), Kconfig 값과 objdump의 stack usage를 대입한다. CAR를 0xa5로 채운 뒤 가장 낮게 바뀐 주소를 찾으면 정적 계산과 실제 high-water의 차이도 얻을 수 있다.
FS selector, descriptor base와 fs:0의 최종 주소를 한 식으로 검산한다
예를 들어 FS selector가 0x18이면 GDT index는 0x18 >> 3 = 3이다. index 3 descriptor의 base가 0xfef1be00이고 instruction이 %fs:0을 읽으면 linear address도 0xfef1be00이다. 그 위치에 저장된 word가 new gd pointer여야 C의 gd macro가 기대한 값을 얻는다.
selector의 RPL/TI bit, GDTR.base, descriptor base field, %fs:offset 네 값을 분리해 기록한다. %fs 숫자만 맞아도 descriptor가 old CAR/GDT copy를 가리키면 relocation 뒤 stale gd를 읽는다.
x86 early boot의 시간 상한은 stage별 최대값의 합으로 둔다
부팅 상한을 Tentry + Tcar + Tdram + Treloc + Thandoff로 나눈다. 3.0GHz TSC에서 CAR 구간이 9,000,000 cycle이면 3ms이고 DRAM training이 1,500,000,000 cycle이면 500ms다. console 출력 시각 하나만 보면 503ms 전부를 assembly startup 지연으로 오해할 수 있다.
각 checkpoint는 CPU frequency가 고정되기 전일 수 있으므로 invariant TSC 지원 여부를 확인하거나 platform timer와 교차 검증한다. cold/warm 각각 100회에서 maximum과 percentile을 따로 보관하면 training cache miss 같은 긴 tail을 찾을 수 있다.
OBJECT LIFETIME
객체와 수명
| 대상 | 만들어지는 시점 | 유효 범위 | 확인할 조건 |
|---|---|---|---|
CAR stack / gd_t / x86 GDT | 32-bit protected entry | board_init_f 또는 오류 정리 완료까지 | board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다. |
| 입력 buffer / metadata | cold reset or previous loader | parse와 검증이 끝날 때까지 | 길이, 정렬, 소유권, 변조 가능성 |
| 등록된 list / descriptor | GDT and blank IDT | subsystem 종료 또는 image handoff까지 | 이중 등록, dangling pointer, 오류 unwind |
| hardware 또는 persistent state | 실제 write/probe가 완료된 뒤 | reset 또는 명시적 해제·갱신까지 | flush, timeout, 전원 차단, rollback |
x86 U-Boot pre-relocation image | link/image 생성 시점 | 다음 stage가 새 image로 교체할 때까지 | load address, entry, section과 header 일치 |
FAILURE PATH
실패 지점과 증상
| # | 조건 | 관찰되는 증상 | 먼저 볼 단계 |
|---|---|---|---|
| 01 | blank IDT 구간의 fault | triple fault와 즉시 reset | 32-bit protected entry |
| 02 | CAR 크기 부족 | stack과 gd 또는 malloc_f 충돌 | GDT and blank IDT |
| 03 | FS/GDT 오류 | 첫 gd access에서 잘못된 주소 | car_init |
| 04 | BIST/table pointer 훼손 | platform handoff metadata 소실 | CAR stack |
로그가 끊긴 마지막 함수만 고치지 않는다. 그 함수가 받은 주소, size, flag가 만들어진 앞 단계까지 올라가고, 오류 뒤 등록 객체와 hardware 상태가 남았는지도 확인한다.
EVIDENCE
소스 밖에서 확인할 증거
소스 해석은 실제 빌드 산출물과 target 로그로 닫아야 한다. 아래 명령의 보드 이름과 toolchain prefix는 사용 중인 빌드 환경에 맞게 바꾼다.
| # | 목적 | 명령 또는 계측 | 판정 기준 |
|---|---|---|---|
| 01 | 전처리 결과 | make <board>_defconfig && make V=1 | 선택된 CONFIG와 실제 compile/link 명령을 보관한다. |
| 02 | symbol과 section | ${CROSS_COMPILE}nm -n u-boot | grep '_start' | symbol 주소가 기대한 stage와 image에 들어갔는지 확인한다. |
| 03 | 어셈블리 대조 | ${CROSS_COMPILE}objdump -drS u-boot | C 한 줄이 실제 branch, relocation, memory access로 어떻게 바뀌었는지 본다. |
| 04 | 주소 배치 | bdinfo | relocaddr, malloc, FDT, stack과 DRAM bank 주소를 한 번에 기록한다. |
| 05 | runtime 관찰 | dm tree; dm uclass; printenv | driver model과 환경이 이 단계에서 실제로 공개되었는지 확인한다. |
LAB
직접 확인할 실험
- 01QEMU에서 _start, car_init_ret, board_init_f에 breakpoint
32-bit protected entry진입 전후에 BIST and table pointer의 주소·크기·반환값과 timestamp를 함께 남긴다. 결과는 정상 부팅 여부로 끝내지 말고 board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다. 조건이 처음 깨지는 줄을 기록한다. - 02CAR 전체를 pattern으로 채워 stack high-water 측정
GDT and blank IDT진입 전후에 segment registers의 주소·크기·반환값과 timestamp를 함께 남긴다. 결과는 정상 부팅 여부로 끝내지 말고 board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다. 조건이 처음 깨지는 줄을 기록한다. - 03GD 주소와 FS base/GDT descriptor 대조
car_init진입 전후에 Cache-As-RAM range의 주소·크기·반환값과 timestamp를 함께 남긴다. 결과는 정상 부팅 여부로 끝내지 말고 board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다. 조건이 처음 깨지는 줄을 기록한다. - 04POST code에 TSC를 붙여 CAR와 DRAM training 시간 분리
CAR stack진입 전후에 temporary stack의 주소·크기·반환값과 timestamp를 함께 남긴다. 결과는 정상 부팅 여부로 끝내지 말고 board_init_f에 들어갈 때 writable stack, 유효한 FS 기반 gd pointer, 보존된 BIST/table/FDT metadata가 CAR 범위 안에서 서로 겹치지 않아야 한다. 조건이 처음 깨지는 줄을 기록한다.
PRIMARY REFERENCES