setup_arch 的 简化过程
1. 处理器初始化(cache itcm dtcm)
2. 匹配 板级 mdesc , 并解析 atags(包括core cmdline mem)
3. early_fixmap_init, 为 early console 的 设备地址 做映射做准备
4. parse_early_param , 解析 cmdline 中的 earlycon earlyprintk , 并初始化 uart 设备
5. 初始化 各种类型的 描述符表
6. 添加 各种(kernel/initrd/设备树/atags/设备树中预留的/)预留的内存到 memblock
7. 对 即将存储页表的地址 清0
8. 针对 多项内容(lowmem/设备树/vectors/各项设备)填充页表
9. 创建zero_page
10. 申请内存,用于存储 很多个struct page
11. 初始化所有的 struct page
13. request_resource 很多内容(整个内存/kernel_code/kernel_data)
- setup_arch(&command_line) 打印的信息
CPU: ARMv6-compatible processor [410fb766] revision 6 (ARMv7), cr=00c5387d
CPU: PIPT / VIPT nonaliasing data cache, VIPT nonaliasing instruction cache
Machine: SMDK6410
Memory policy: Data cache writeback
Samsung CPU ID: 0x36410101
CPU S3C6410 (id 0x36410101)
CPU: found DTCM0 8k @ 0c002000, enabled
CPU: moved DTCM0 8k to fffe8000, enabled
CPU: found DTCM1 8k @ 0c004000, enabled
CPU: moved DTCM1 8k to fffea000, enabled
CPU: found ITCM0 8k @ 00000000, not enabled
CPU: moved ITCM0 8k to fffe0000, enabled
CPU: found ITCM1 8k @ 00000000, not enabled
CPU: moved ITCM1 8k to fffe2000, enabled
Zone ranges:
Normal [mem 0x0000000050000000-0x000000005fffffff]
Movable zone start for each node
Early memory node ranges
node 0: [mem 0x0000000050000000-0x000000005fffffff]
Initmem setup node 0 [mem 0x0000000050000000-0x000000005fffffff]
CPU: All CPU(s) started in SVC mode.
setup_arch
atags_vaddr = 0xff800100;
setup_processor
// 设置 cache 相关
mdesc = setup_machine_tags(atags_vaddr, __machine_arch_type);
// 做两件事
// 1. 根据 board id (即__machine_arch_type) mdesc
// 2. 根据 atags_vaddr , 找 函数 parse 每一个 atags // 函数 为 __tagtable_begin 到 __tagtable_end 地址 中的 变量的 parse 成员
// u-boot 设置了 setup_start_tag setup_commandline_tag setup_memory_tags setup_end_tag
// 需要 __tagtable_parse_tag_core __tagtable_parse_tag_cmdline __tagtable_parse_tag_mem32 解析
// 依次做了
// core : root_mountflags &= ~MS_RDONLY; ROOT_DEV = old_decode_dev(tag->u.core.rootdev);
// cmdline : strlcpy(default_command_line, tag->u.cmdline.cmdline, COMMAND_LINE_SIZE);
// mem32 : arm_add_memory(tag->u.mem.start, tag->u.mem.size);
machine_desc = mdesc; // arch/arm/mach-s3c/mach-smdk6410.c 中的 MACHINE_START(SMDK6410, "SMDK6410")
machine_name = mdesc->name; // "SMDK6410"
dump_stack_set_arch_desc("%s", mdesc->name);
// 填充 dump_stack_arch_desc_str 为 "SMDK6410"
init_mm.start_code = (unsigned long) _text; // c0008000
init_mm.end_code = (unsigned long) _etext; // c0600000
init_mm.end_data = (unsigned long) _edata; // c088e548
init_mm.brk = (unsigned long) _end; // 等于 __bss_stop , c08c413c
strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);
*cmdline_p = cmd_line;
early_fixmap_init
// 写了两个一级页表
// 可以通过虚拟地址 ffef f000 索引 物理地址(1MB,还没填充) ?
// 消费者 earlycon_map->set_fixmap_io->__set_fixmap
early_ioremap_init
early_ioremap_setup
// 填充了个数组?
// slot_virt[0](地址看看 System.map) : ffe1f000
// slot_virt[1] : ffe3f000
// slot_virt[2] : ffe5f000
// slot_virt[3] : ffe7f000
// slot_virt[4] : ffe9f000
// slot_virt[5] : ffebf000
// slot_virt[6] : ffedf000
// 消费者 early_memremap->__early_ioremap
// 消费者 early_ioremap->__early_ioremap
// 在 OK6410A-linux-5.11 中 , 没有对 early_ioremap 的使用
// early_ioremap 的地址 属于 temporary fixed address
parse_early_param
// 此次 为 parse_args "early options"
// 处理用 early_param 定义的参数
// 例如 early_param("debug",debug_kernel);
/*
1. parse_args "early options"
early_param
2. parse_args "Booting kernel"
module_param
__setup
3. parse_args initcall_level_names[level]
module_param
*/
// 该实例中 无 early_param 对应的 commandline 字符串
early_mm_init(mdesc);
build_mem_type_table
// 在 arm-linux中,所有的页表类型 为 以下 13种
// arch/arm/include/asm/mach/map.h 中 的 MT_MEMORY_RW
// 填充 mem_types , 该 变量可用做接下来的一级描述符表内容,二级描述符表内容
// TODO : struct mem_type mem_types 成员的作用及值
// 消费者 为 create_mapping
early_paging_init(mdesc);
// null
setup_dma_zone(mdesc);
// null
xen_early_init
// null
efi_init
// null
adjust_lowmem_bounds
// 调整低端内存的边界
// memblock.current_limit:60000000
arm_memblock_init
1. 预留 kernel // 静态内存
memblock_reserve(__pa(KERNEL_START), KERNEL_END - KERNEL_START);
// 添加到 memblock.reserved 中
// pa:50100000,size:7c413c
2. 预留 initrd // 静态内存
arm_initrd_init
// null
3. 预留 页表
arm_mm_memblock_reserve
// pa:50004000,size:4000
4. 预留 板级desc中 要求预留的内存
mdesc->reserve
5. 预留 fdt中 要求预留的内存 (reserved-mem节点) // 一般又DMA管理
early_init_fdt_scan_reserved_mem
6. CMA(连续内存分配器) 的初始化 // 申请了一个公共的CMA区域
dma_contiguous_reserve(arm_dma_limit) // arm_dma_limit : ffffffff
// null , 因为没有配置 CONFIG_DMA_CMA
// 位于CMA管理区的内存,有映射页表,对于内核是 可见的
// 位于DMA管理区的内存,无映射页表,对于内核是不可见的
7. arm_memblock_steal_permitted = false;
8. memblock_dump_all
/*
MEMBLOCK configuration:
memory size = 0x10000000 reserved size = 0x007c813c
memory.cnt = 0x1
memory[0x0] [0x50000000-0x5fffffff], 0x10000000 bytes flags: 0x0
reserved.cnt = 0x2
reserved[0x0] [0x50004000-0x50007fff], 0x00004000 bytes flags: 0x0
reserved[0x1] [0x50100000-0x508c413b], 0x007c413c bytes flags: 0x0
*/
adjust_lowmem_bounds
// memblock.current_limit:60000000
early_ioremap_reset
early_ioremap_shutdown
// null
after_paging_init = 1;
paging_init // 该函数 中共有 19次 create_mapping
prepare_page_table
// 将 即将写入的页表地址(c0004000-c000741f)清0
// 1. for (addr = 0; addr < MODULES_VADDR; addr += PMD_SIZE) pmd_clear(pmd_off_k(addr));
// Clear out all the mappings below the kernel image.
// addr:0,pmd_off_k(addr):c0004000
// addr:200000,pmd_off_k(addr):c0004008
// ...
// bee00000,pmd_off_k(addr):c0006fb8
// 2. for ( ; addr < PAGE_OFFSET; addr += PMD_SIZE) pmd_clear(pmd_off_k(addr));
// Clear out all the mappings below the kernel image.
// addr:bf000000,pmd_off_k(addr):c0006fc0
// addr:bf200000,pmd_off_k(addr):c0006fc8
// ...
// addr:bfe00000,pmd_off_k(addr):c0006ff8
// 3. for (addr = __phys_to_virt(end); addr < VMALLOC_START; addr += PMD_SIZE) pmd_clear(pmd_off_k(addr));
// Clear out all the kernel space mappings, except for the first memory bank, up to the vmalloc region.
// addr:d0000000,pmd_off_k(addr):c0007400
// addr:d0200000,pmd_off_k(addr):c0007408
// addr:d0400000,pmd_off_k(addr):c0007410
// addr:d0600000,pmd_off_k(addr):c0007418
map_lowmem
// 根据 memblock 中的内存信息创建映射
create_mapping(&map); // 在这里,映射完了板子的所有物理内存 0x50000000 - 0x60000000
// 1. map.pfn:50000,map.virtual:c0000000,map.length:100000,map.type:a
// 2. map.pfn:50100,map.virtual:c0100000,map.length:700000,map.type:9
// 3. map.pfn:50800,map.virtual:c0800000,map.length:f800000,map.type:a
memblock_set_current_limit(arm_lowmem_limit); // 60000000
dma_contiguous_remap
// null
early_fixmap_shutdown
// no map
// TODO
devicemaps_init
create_mapping(&map);
create_mapping(&map);
create_mapping(&map);
mdesc->map_io/smdk6410_map_io
s3c64xx_init_io(smdk6410_iodesc, ARRAY_SIZE(smdk6410_iodesc));
iotable_init(s3c_iodesc, ARRAY_SIZE(s3c_iodesc)); // 10个成员
iotable_init(smdk6410_iodesc, size); // 1个成员
// 14 个 map
// 1. create a read-only mapping of the device tree
// map.pfn:50000,map.virtual:ff800000,map.length:200000,map.type:b
// 2. Create a mapping for the machine vectors at the high-vectors location (0xffff0000).
// map.pfn:5fffe,map.virtual:ffff0000,map.length:1000,map.type:8
// 3. Now create a kernel read-only mapping
// map.pfn:5ffff,map.virtual:ffff1000,map.length:1000,map.type:7
以上 1 做了 设备树/atags的映射,设备树/atags在内存中,但是这里额外做一次映射.那么现在设备树/atags的物理地址对应了两个虚拟地址
// 为了兼容start_kenrel之前做的 atags 映射?
以上 2 做了 向量表的映射,向量表在内存中,但是这里额外做一次映射,那么现在向量表的物理地址对应了两个虚拟地址
// 是因为 high vectors configured , pc = 0xFFFF0000
............................................................
// 4-14. Create the architecture specific mappings
// map.pfn:7e00f,map.virtual:f6100000,map.length:1000,map.type:0 // S3C64XX_PA_SYSCON
// map.pfn:70000,map.virtual:f6200000,map.length:1000,map.type:0 // S3C64XX_PA_SROM
// map.pfn:7f005,map.virtual:f7005000,map.length:1000,map.type:0 // S3C_PA_UART
// map.pfn:71200,map.virtual:f6000000,map.length:4000,map.type:0 // S3C64XX_PA_VIC0
// map.pfn:71300,map.virtual:f6010000,map.length:4000,map.type:0 // S3C64XX_PA_VIC1
// map.pfn:7f006,map.virtual:f6300000,map.length:4000,map.type:0 // S3C_PA_TIMER
// map.pfn:7f008,map.virtual:f6500000,map.length:1000,map.type:0 // S3C64XX_PA_GPIO
// map.pfn:74108,map.virtual:f6600000,map.length:1000,map.type:0 // S3C64XX_PA_MODEM
// map.pfn:7e004,map.virtual:f6400000,map.length:1000,map.type:0 // S3C64XX_PA_WATCHDOG
// map.pfn:7c100,map.virtual:f6700000,map.length:0400,map.type:0 // S3C64XX_PA_USB_HSPHY
// map.pfn:77100,map.virtual:f7100000,map.length:4000,map.type:0 // S3C_PA_FB
kmap_init
tcm_init
for (i = 0; i < dtcm_banks; i++) setup_tcm_bank
for (i = 0; i < itcm_banks; i++) setup_tcm_bank
// map.pfn:fffe8,map.virtual:fffe8000,map.length:4000,map.type:d
// map.pfn:fffe0,map.virtual:fffe0000,map.length:4000,map.type:e
top_pmd = pmd_off_k(0xffff0000);
zero_page = early_alloc(PAGE_SIZE);
// 第一次使用 memblock 内存管理器的 内存申请API
bootmem_init
// 在 linux-3.0.1 中 这里 存在着 arm_bootmem_init
// 在arm_bootmem_init 中 bootmem 接替 memblock 的内存管理
// 并且 向 其他的消费者 提供 申请释放内存的接口
// 在 linux-5.11 就没有了,看来是 memblock 已经完成了 申请释放内存的接口
// memblock API 实例 : request_standard_resources->memblock_alloc
// linux-3.0.1 中也提供了 memblock ,只不过 memblock 返回的是物理地址,也就是说 其管理的是 物理内存?
// linux-5.11 中提供的 memblock ,返回的是 虚拟地址,也就是说其管理的是 虚拟内存?
memblock_allow_resize // memblock 已经初始化完成?
memblock_can_resize = 1;
find_limits(&min_low_pfn, &max_low_pfn, &max_pfn);
early_memtest
sparse_init
zone_sizes_init // 开始初始化 buddy
free_area_init
empty_zero_page = virt_to_page(zero_page);
__flush_dcache_page(NULL, empty_zero_page);
/*
zero_page // 物理页面
https://www.it1352.com/1526752.html
1. 您的存储区域中的数据为零,则可以将此数据映射到零页,并使用"0"数据释放这些页.换句话说,这与内核如何节省内存有关
2. 还允许分配一个大数组,但不消耗内存.所有页面最初都是零页面,并且映射到相同的物理零页面.如果数组稀疏,则只有少数条目(4k大小)会占用内存.内核不需要清理(零)分配的内存.在填充条目中不会浪费'tlb'和'cache'.
总之
0. 这是一个物理页面, 且被填充了 0
1. 只要消费者的虚拟页面是 0 , 且是只读的, 则可以映射
2. 消费者的虚拟页面是可写的,那么 也可以映射,触发COW
*/
kasan_init
// null
request_standard_resources
res->name = "System RAM";
res->start = start; // 50000000
res->end = res_end; // 5fffffff
res->flags = IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY;
request_resource(&iomem_resource, res); // 注冊内存到iomem_resource
kernel_code.start:50008000
kernel_code.end:506fffff
request_resource(res, &kernel_code); // 注册 kernel_code 到 内存
kernel_data.start:50800000
kernel_data.end:5108013b
request_resource(res, &kernel_data); // 注册 kernel_data 到 内存
unflatten_device_tree
// null
arm_dt_init_cpu_maps
// null
psci_dt_init
// null
hyp_mode_check
// TODO
// 虚拟化CONFIG_ARM_VIRT_EXT相关,先不管
reserve_crashkernel
// null
handle_arch_irq = mdesc->handle_irq; //值 为 null
if (mdesc->init_early) // null
mdesc->init_early();
其他
arch/arm/include/asm/pgalloc.h
static inline void __pmd_populate(pmd_t *pmdp, phys_addr_t pte, pmdval_t prot)
https://github.com/novelinux/linux-4.x.y/blob/master/arch/arm/include/asm/pgalloc.h/__pmd_populate.md
early_fixmap_init // 设置的是一级页表
pmd_populate_kernel(&init_mm, pmd, bm_pte); // pmd : c0007ff8
__pmd_populate(pmdp, __pa(ptep), _PAGE_KERNEL_TABLE); // __pa(ptep):50724000 _PAGE_KERNEL_TABLE:11(十进制)
// __pmd_populate 建立了两个一级页表
// 第一个一级页表 的描述符 bit[10] 比 第二个一级页表的描述符 bit[10] 少 1
// 即 pmdval_1 + 256 * sizeof(pte_t) = pmdval_2
pmdp[0] = __pmd(pmdval); // addr : c0007ff8 , value :50724811 // 50724000 + 512*4 | 11
pmdp[1] = __pmd(pmdval + 256 * sizeof(pte_t)); // addr : c0007ffc ,value : 50724811+256*4 = 50724c11
create_mapping 的实现
create_mapping
__create_mapping
alloc_init_p4d
alloc_init_pud
alloc_init_pmd
__map_init_section // if (type->prot_sect && ((addr | next | phys) & ~SECTION_MASK) == 0) 满足,段表
*pmd = __pmd(phys | type->prot_sect | (ng ? PMD_SECT_nG : 0));
alloc_init_pte // if (type->prot_sect && ((addr | next | phys) & ~SECTION_MASK) == 0) 不满足,页表
arm_pte_alloc
alloc
__pmd_populate
pmdp[0] = __pmd(pmdval);
pmdp[1] = __pmd(pmdval + 256 * sizeof(pte_t));
create_mapping 写地址和写入的值
paging_init 函数 中共有 19次 create_mapping
19 次直接来自于 paging_init
3次 来自于 map_lowmem
1次 Map all the lowmem memory banks.
addr:c0007000,value:5000041e // 最低两位为10表明是段表
1次 Map all the lowmem memory banks.
addr:c0007004,value:5010040e
addr:c0007008,value:5020040e
...
addr:c000701c,value:5070040e
1次 Map all the lowmem memory banks.
addr:c0007020,value:5080041e
addr:c0007024,value:5090041e
...
addr:c00073fc,value:5ff0041e
14次来自于 devicemaps_init
1次 create a read-only mapping of the device tree
addr:c0007fe0,value:5000840e
addr:c0007fe4,value:5010840e
1次 Create a mapping for the machine vectors at the high-vectors location (0xffff0000).
addr:c0007ff8,value:5fffd861 PAGING // 最低两位为01表明是页表
addr:c0007ffc,value:5fffdc61 PAGING
1次 Now create a kernel read-only mapping
null // BUG_ON(pmd_bad(*pmd));
11次 mdesc->map_io -> iotable_init
addr:c0007d80,value:5fffb841 // 第1次 PAGING
addr:c0007d84,value:5fffbc41 // 第1次 PAGING
addr:c0007d88,value:5fffa841 // 第2次 PAGING
addr:c0007d8c,value:5fffac41 // 第2次 PAGING
addr:c0007dc0,value:5fff9841 // 第3次 PAGING
addr:c0007dc4,value:5fff9c41 // 第3次 PAGING
addr:c0007d90,value:5fff8841 // 第7次 PAGING
addr:c0007d94,value:5fff8c41 // 第7次 PAGING
addr:c0007d98,value:5fff7841 // 第8次 PAGING
addr:c0007d9c,value:5fff7c41 // 第8次 PAGING
addr:c0007ff0,value:5fff6811 // 第11次 PAGING
addr:c0007ff4,value:5fff6c11 // 第11次 PAGING
2次 来自于 tcm_init
1次 iotable_init(dtcm_iomap, 1);
null
1次 iotable_init(itcm_iomap, 1);
null
create_mapping 产生的效果
其实就是在页表/段表的位置写了页表/段表描述符
然后应用程序在 访问虚拟地址的时候,就可以访问到其对应的物理地址
以 下面为例
map.pfn:50000,map.virtual:c0000000,map.length:100000,map.type:a
addr:c0007000,value:5000041e
1.
在 虚拟地址 c0007000(对应物理地址50007000) 处 写入了 值(段表描述符) 5000041e
2.
访问 虚拟地址 c0000000的时候, 就可以访问到其对应的物理地址 50000 000
访问 虚拟地址 c0000004的时候, 就可以访问到其对应的物理地址 50000 004
...
访问 虚拟地址 c0100000的时候, 就可以访问到其对应的物理地址 50100 000
early_fixmap_init 的消费者
消费者 : earlycon_map->set_fixmap_io->__set_fixmap
early_param("earlycon", param_setup_earlycon); // earlycon 的实验 TODO early_printk
param_setup_earlycon
early_init_dt_scan_chosen_stdout // 设备树
of_setup_earlycon
earlycon_map
setup_earlycon // atags bootargs
register_earlycon
earlycon_map
early_ioremap_init 的消费者
copy_from_early_mem
early_memremap
__early_ioremap
return (void __iomem *)(offset + slot_virt[slot]);
arch/arm/include/asm/pgtable-2level.h
223 #define pmd_clear(pmdp) \
224 do { \
225 pmdp[0] = __pmd(0); \
226 pmdp[1] = __pmd(0); \
227 clean_pmd_entry(pmdp); \
228 } while (0)
zone_sizes_init
free_area_init(max_zone_pfn);
for_each_online_node free_area_init_node(nid)
alloc_node_mem_map // 申请空间用来存 所有的 struct page
map = memblock_alloc_node(size, SMP_CACHE_BYTES, pgdat->node_id); // size: 0x20 0000 / 2M // map :cfdf5000
pgdat->node_mem_map = map + offset; // cfdf5000 + 0 = cfdf5000 ;
free_area_init_core
for (j = 0; j < MAX_NR_ZONES; j++)
{
zone_init_internals
init_currently_empty_zone
zone_init_free_lists
memmap_init
memmap_init_zone(size, nid, zone, start_pfn, range_end_pfn, MEMINIT_EARLY, NULL, MIGRATE_MOVABLE); // start_pfn : 50000 ,end_pfn:60000
for (pfn = start_pfn; pfn < end_pfn; pfn++ ) //运行 0x10000 次 , 65536次 //
{
page = pfn_to_page(pfn);
// page 地址范围 为 cfdf5000 - cfff4fe0 , 间隔 为 0x20 (一个page结构体大小为0x20)
// 所有的page 共 2MB大小,
// 物理内存 共 256MB大小 ,
// 2M大小的 pages 管理 256MB物理内存
// cfff5000 - cfffffff 共 0xB000/45056/44KB字节
__init_single_page(page, pfn, zone, nid);
// 初始化 page 结构体变量
// 1. 建立 与 zone 的联系
// 2. 标记其 被使用 还是 free?
if (context == MEMINIT_HOTPLUG) __SetPageReserved(page);
if (IS_ALIGNED(pfn, pageblock_nr_pages)) { set_pageblock_migratetype(page, migratetype); cond_resched();}
}
}
数据
start_pfn:50000,end_pfn:60000,num:0x10000 // 65536个
0x10000个 struct page , 地址 从 cfdf5000 - cfff4fe0
cfdf5000
cfdf5020
...
cfff4fe0
memblock 在 setup_arch函数中的消费者
paging_init // 3
devicemaps_init // 2
vectors = early_alloc(PAGE_SIZE * 2); // 1
memblock_alloc
reserved[0x2] [0x5fffe000-0x5fffffff], 0x00002000 bytes flags: 0x0
mdesc->map_io // 1
s3c64xx_init_io
iotable_init
memblock_alloc
reserved[0x3] [0x5fffce70-0x5fffffff], 0x00003190 bytes flags: 0x0
memblock_alloc
reserved[0x3] [0x5fffce48-0x5fffffff], 0x000031b8 bytes flags: 0x0
bootmem_init // 1
zone_sizes_init
free_area_init_node
alloc_node_mem_map
memblock_alloc_node
reserved[0x4] [0x5fdf5000-0x5fffbfff], 0x00207000 bytes flags: 0x0
request_standard_resources // 1
memblock_alloc
reserved[0x5] [0x5fffcd80-0x5fffcd9f], 0x00000020 bytes flags: 0x0
request_resource
https://blog.csdn.net/dahailinan/article/details/111691678
只是看到 将 io资源 用 树结构 来表示出来
但是 用途 是什么,还不知道