[커널 17차] 30주차
2021.03.30 22:12
2020년 3월 27일
linux version : mainline 5.9
init/main.c. start_kernel();
arm64_memblock_init() 진행중
- early_init_fdt_scan_reserved_mem() 분석
__reserved_mem_init_node의 for (i = __reservedmem_of_table; i < &__rmem_of_table_sentinel; i++) 관련 내용
include/linux/of_reserved_mem.h : line 30
- RESERVEDMEM_OF_DECLARE(name, compat, init) 이 정의되어 있다
- _OF_DECLARE 매크로를 이용해서 정의된다
include/linux/of.h : 1295 line
- _OF_DECLARE(table, name, compat, fn, fn_type) 이 정의되어 있다
- __section(__##table##_of_table)을 이용해서 정의된다
include/asm-generic/vmlinux.lds.h : 294 line
- #define RESERVEDMEM_OF_TABLES() OF_TABLE(CONFIG_OF_RESERVED_MEM, reservedmem)로
__reservedmem_of_table이 정의되어 있다
include/asm-generic/vmlinux.lds.h : 285 line
- _OF_TABLE_1 매크로로 __##name##_of_table = .; 영역이 정의되어 있다
- KEEP(*(__##name##_of_table)), KEEP(*(__##name##_of_table_end)) 도 정의된다
RESERVEDMEM_OF_DECLARE 매크로는 여러 드라이버 코드에서 정의되어 있음
- dma/fsl 등과 관련되어 정의되어 있는듯
for문에서 RESERVEDMEM_OF_DECLARE로 선언된 section의 항목들을 확인한다.
저녁 시간
이론 분석
http://jake.dothome.co.kr/pci-1/
참고 자료
https://en.wikipedia.org/wiki/Differential_signaling
http://melonicedlatte.com/computerarchitecture/2020/01/22/140000.html
http://www.xillybus.com/tutorials/pci-express-tlp-pcie-primer-tutorial-guide-1
자료
번호 | 제목 | 글쓴이 | 날짜 | 조회 수 |
---|---|---|---|---|
공지 | [공지] 스터디 정리 노트 공간입니다. | woos | 2016.05.14 | 627 |
68 | [커널 17차] 41주차 | ㅇㅇㅇ | 2021.06.13 | 36544 |
67 | [커널 18차] 3주차 | V4bel | 2021.06.12 | 103 |
66 | [커널 17차] 40주차 | ㅇㅇㅇ | 2021.06.05 | 283 |
65 | [커널 18차] 2주차 | V4bel | 2021.06.05 | 271 |
64 | [커널 17차] 39주차 | ㅇㅇㅇ | 2021.05.29 | 184 |
63 | [커널 18차] 1주차 | V4bel | 2021.05.29 | 3599 |
62 | [커널 17차] 38주차 | ㅇㅇㅇ | 2021.05.23 | 366 |
61 | [커널 17차] 37주차 | ㅇㅇㅇ | 2021.05.16 | 97 |
60 | [커널 17차] 36주차 [2] | ㅇㅇㅇ | 2021.05.09 | 133 |
59 | [커널 17차] 35주차 | ㅇㅇㅇ | 2021.05.05 | 85 |
58 | [커널 17차] 34주차 | ㅇㅇㅇ | 2021.04.25 | 99 |
57 | [커널 17차] 33주차 | ㅇㅇㅇ | 2021.04.18 | 195 |
56 | [커널 17차] 32주차 | ㅇㅇㅇ | 2021.04.11 | 443 |
55 | [커널 17차] 31주차 [2] | ㅇㅇㅇ | 2021.04.04 | 158 |
» | [커널 17차] 30주차 [1] | 주영 | 2021.03.30 | 203 |
53 | [커널 17차] 29주차 | ㅇㅇㅇ | 2021.03.26 | 103 |
52 | [커널 17차] 27주차 | 주영 | 2021.03.15 | 276 |
51 | [커널 17차] 27주차 | 주영 | 2021.03.08 | 172 |
50 | [커널 17차] 26주차 | 주영 | 2021.03.01 | 539 |
49 | [커널 17차] 25주차 | 주영 | 2021.02.23 | 91 |
.
본 글에 이어 보충하였습니다.
arm64_memblock_init()
early_init_fdt_scan_reserved_mem()
- early_init_dt_verify()에서 initial_boot_params를 FDT 포인터로 setting했었음
- FDT의 모든 mem reserve block을 읽어서 그 물리 주소들을 다 memblock에 reserve한다 (early_init_dt_reserve_memory_arch)
- 그리고 __fdt_scan_reserved_mem로 "reserved-memory" 이름의 node의 종속노드에 대해 memblock에 reserve를 수행한다
- 그 다음 __fdt_init_reserved_mem() 수행
fdt_get_mem_rsv(fdt, n, &address, &size)
- FDT_RO_PROBE(fdt)를 하는 이유는? sanity check로 보임
+-- 이 놈은 fdt를 읽어서 sanity check하고 fdt header에 써있는 fdt total_size를 리턴한다
- fdt의 n번째 mem reserve block을 가져와서 그 주소와 사이즈를 리턴한다
fdt_mem_rsv(fdt, n)
- fdt의 n번째 mem reserve block의 포인터를 리턴
fdt_off_mem_rsvmap()
- fdt에 있는 mem reserve block의 offset을 리턴
fdt_mem_rsv_(fdt, n)
- fdt의 n번째 mem reserve block의 주소를 리턴
early_init_dt_reserve_memory_arch()
- base부터 size까지 memblock에 reserve한다
- nomap이면 memblock에 reserve는 안하고 memblock에서 remove한다
__fdt_scan_reserved_mem()
- 이름이 "reserved-memory"인 node를 찾는다
- 찾았는데 "#size-cells" 특성이 dt_root_size_cells랑 다르거나
- "#address-cells" 특성이 dt_root_addr_cells랑 다르면 에러
- 또한 "ranges" 특성이 없으면 에러
- 에러가 나면 scan을 중단한다
- 찾으면 종속 노드에 대해서 다음을 수행한다
+-- of_fdt_device_is_available()로 available한 노드인지 체크하고 아니면 pass
+-- __reserved_mem_reserve_reg()으로 각 종속 노드에 대해서 mem reserve를 수행한다
- 모든 종속 노드에 대해서 다 수행했으면 return 1해서 scan을 끝낸다
+-- __reserved_mem_reserve_reg()가 실패하고 node의 "size" 특성이 NULL이면 fdt_reserved_mem_save_node()를 호출해서 static array인 reserved_mem에 reserved memory에 대한 fdt 정보를 기록한다 (memblock reserve array와는 별도의 array임)
__reserved_mem_reserve_reg(node, uname)
- 노드의 "reg" property와 그 cell 개수를 파악
- 노드가 "no-map" property를 가지는지 파악
- "reg" property의 cell 수만큼 루프를 돌면서 다음을 수행
+-- early_init_dt_reserve_memory_arch(base, size, nomap) 함수를 이용해서 nomap이면 base에서 size만큼을 memblock에서 제거하고 아니면 base에서 size만큼을 memblock에 reserve한다
+-- 첫 번째 cell에 대해서는 fdt_reserved_mem_save_node()를 호출해서 static array인 reserved_mem에 reserved memory에 대한 fdt 정보를 기록한다 (memblock reserve array와는 별도의 array임)
+-- 즉 노드 당 하나만 reserved_mem을 설정한다 --> 노드 당 하나만 init하면 되기 때문 (fdt_init_reserved_mem())
fdt_reserved_mem_save_node()
- static array인 reserved_mem이 전역 변수로 존재함
- 해당 array의 entry 개수도 reserved_mem_count라는 전역 변수로 존재함
- 이 함수는 이 array에 fdt node정보를 저장함 (node 포인터, uname, base, size)
- 그리고 reserved_mem_count++ 수행
fdt_init_reserved_mem()
- __rmem_check_for_overlap()로 overlap된 reserved regions을 체크한다
- 각 entry = rmem에 대해서 루프를 돌면서 아래를 수행한다
+-- entry의 fdt_node를 가져온다
+-- 해당 노드가 "no-map" 특성을 가지고 있는지 체크한다
+-- 해당 노드에서 "phandle" 특성을 읽어온다
+-- "phandle" 특성이 없으면 "linux,phandle" 특성을 읽어온다
+-- 해당 특성을 읽어서 entry의 phandle에 기록한다 (rmem->phandle)
+-- entry size(rmem->size)가 0이면 __reserved_mem_alloc_size() 함수 호출하여 node에 기록된 size를 찾고 memblock에 reserve한다
+-- rmem->size가 0이 아니거나, 0이라도 memblock reserve에 성공했으면 __reserved_mem_init_node()를 호출한다
+-- 에러가 발생하여 reserved_mem 초기화에 실패하면 memblock에 reserve된 영역을 모두 free하고 nomap이면 그후 memblock_add()한다
+-- nomap이 아닐 때만 free해야 하는 것 아닌가? 동작에는 문제없으나 낭비가 있는 것으로 보임
__rmem_check_for_overlap()
- 이 함수는 전역 변수 array reserved_mem 중에서 overlap을 체크하는 것으로 보임
- reserved_mem_count가 1이하이면 그냥 리턴
- reserved_mem를 sort한다
+-- 비교함수는 각 entry의 base 주소를 사용 --> 주소를 기준으로 sorting
- 루프를 돌면서 지금 entry와 다음 entry의 주소가 겹치는지를 체크한다
__reserved_mem_alloc_size()
- node의 "size" 특성에서 size를 읽어온다
+-- node의 "size" 특성을 읽어와서 NULL이면 에러 리턴
+-- node의 "size" 특성 length가 dt_root_size_cells x sizeof(__be32)와 다르면 에러 리턴
+-- 에러가 없으면 node의 "size" 특성으로부터 size 값을 읽어온다
- node의 "alignment" 특성을 읽어온다
+-- 특성이 NULL이 아니고 특성 length가 정확하면 특성으로부터 align값을 읽어온다
- node의 "no-map" 특성을 읽어온다
- CONFIG_CMA가 enable된 경우
+-- 노드가 "shared-dma-pool" 특성과 compatible하고 "reusable" 특성이 있고 nomap이 아니면
+-- CMA 요구사항에 따라 page size를 더 크게 align하여 align 변수를 설정
- node가 "alloc-ranges" 특성이 있으면
+-- 특성 길이가 t_len의 배수인지 체크하고 아니면 에러 리턴
+-- len/t_len 만큼 루프를 돌면서 아래를 수행한다
+-- t_len 만큼 특성을 읽어와서 allocation해야 하는 주소의 start/end를 계산한다
+-- early_init_dt_alloc_reserved_memory_arch()를 호출해서 start부터 end까지 size와 align, nomap을 조건으로 하여 reserved memory allocation을 수행한다
+-- reserve에 성공하면 loop를 중단하고 나온다
+-- 만약 node가 "alloc-ranges" 특성이 없으면 그냥 모든 물리공간에서 reserve할 영역을 찾는다
- reserve 못했으면 에러 리턴
- reserve 성공하면 base/size를 리턴하고 끝
early_init_dt_alloc_reserved_memory_arch()
- end가 0이면 end는 0xFFFF...FFFF로 설정
- align이 0이면 align은 SMP_CACHE_BYTES = L1_CACHE_BYTES으로 설정
- memblock_find_in_range()를 호출해서 start부터 end까지 size만큼의 연속된 물리 공간이 있는지 찾고 찾은 공간의 시작주소를 base로 리턴한다. 이 때 align 조건을 추가한다.
- 못 찾으면 에러 리턴
- 찾으면 base부터 size까지 memblock_reserve()를 수행한다
- nomap이면 memblock_reserve() 안하고 memblock_remove()한다
include/linux/of_reserved_mem.h : line 30
- RESERVEDMEM_OF_DECLARE(name, compat, init) 이 정의되어 있다
- _OF_DECLARE 매크로를 이용해서 정의됨
include/linux/of.h : 1295 line
- _OF_DECLARE(table, name, compat, fn, fn_type) 이 정의되어 있다
- __section(__##table##_of_table)을 이용해서 정의됨
include/asm-generic/vmlinux.lds.h : 294 line
- #define RESERVEDMEM_OF_TABLES() OF_TABLE(CONFIG_OF_RESERVED_MEM, reservedmem)로
__reservedmem_of_table이 정의되어 있다
include/asm-generic/vmlinux.lds.h : 285 line
- _OF_TABLE_1 매크로로 __##name##_of_table = .; 영역이 정의되어 있다
- KEEP(*(__##name##_of_table)), KEEP(*(__##name##_of_table_end)) 도 정의됨
RESERVEDMEM_OF_DECLARE 매크로는 여러 드라이버 코드에서 정의되어 있음
- dma/fsl 등과 관련되어 정의되어 있는듯
__reserved_mem_init_node()
- struct of_device_id (include/linux/mod_devicetable.h) : device의 name, type, compatiblility, 기타 data를 저장하는 자료구조
+-- 여기서 data는 reservedmem_of_init_fn type의 함수 포인터인 듯
- __reservedmem_of_table : of_device_id의 전역 변수 array
- __reservedmem_of_table의 entry들을 하나씩 루프를 돌면서 아래를 수행
+-- reservedmem_of_init_fn는 함수 포인터로 reserved_mem 포인터를 받아서 int를 리턴하는 함수
+-- entry의 data(reservedmem_of_init_fn type 함수 포인터)를 읽음
+-- entry의 compatible을 읽음 (compat = i->compatible)
+-- of_flat_dt_is_compatible()를 호출하여 rmem->fdt_node가 entry의 compat과 compatible한지 판정하고 아니면 continue
+-- 함수 data를 인자 rmem을 넣고 reserved_mem의 초기화를 수\행한다
+-- 초기화 성공하면 loop를 끝낸다
of_flat_dt_is_compatible()
- FDT를 인수로 하여 of_fdt_is_compatible() 호출
of_fdt_is_compatible()
- node에서 "compatible" 특성을 찾는다
- 없으면 그냥 리턴한다
- 있으면 인수 compat string과 특성 string이 같은지 판단하고 아니면 특성에 기록된 다음 string으로 넘어가서 다시 string비교를 수행한다
- 동일한 string이 발견되면 non-zero value를 리턴
- 양수 중 작은 값일 수록 more specific compatible value라고 함
- 즉 "compatible" 특성은 여러 개의 string으로 구성되고 각 string이 인자로 들어온 compat string과 같은지 차례로 비교하는 것임
- compatible하지 않으면 zero를 리턴
======================================
phandle : pointer handler
- 한 노드에서 다른 특정 노드를 가리킬 때 사용한다
- https://stackoverflow.com/questions/31095951/what-is-the-meaning-of-a-phandle-when-used-as-device-tree-node-name
- 예를 들어 hdmi가 인터럽트를 특정 cpu로 전달해야 하면 dtb에 phandle에 cpu를 지정해서 나중에 device driver에서 dtb를 읽어서 target cpu를 지정할 수 있게 한다
======================================
PCIe 전송 H/W부터 진행
- http://jhnet.co.uk/misc/handWavyPCIe.pdf
- https://pcisig.com/sites/default/files/files/PCI_Express_Basics_Background.pdf
- http://www.xillybus.com/tutorials/pci-express-tlp-pcie-primer-tutorial-guide-1
- http://www.xillybus.com/tutorials/pci-express-tlp-pcie-primer-tutorial-guide-2
MSI : bus를 통해 interrupt를 메시지로 보내서 interrupt controller의 register를 업데이트하는 방식
- https://developer.arm.com/documentation/198123/0300/Arm-CoreLink-GIC-fundamentals
PCIe configuration space
- http://melonicedlatte.com/computerarchitecture/2020/01/22/140000.html
- PCIe device를 관리하기 위한 가상의 주소공간으로 생각된다
BAR
- PCIe device가 호스트 메모리의 어느 영역에 mapping되어 있는지를 나타내는 주소
- 즉 device 0의 BAR가 0x8000_0000으로 설정되어 있으면 호스트 메모리의 0x8000_0000에 device 0가 mapping이 되어 있다.
- Device 0에 읽고 쓰기를 하려면 0x8000_0000에 읽고 쓰기를 해야 한다