3.5.1.69. early_alloc
static void __init *early_alloc(unsigned long sz)
{
return early_alloc_aligned(sz, sz);
}
early_alloc
用于早期分配指定长度的物理内存.参数sz指向需求物理内存的长度
3.5.1.70. early_alloc_aligned
static void __init *early_alloc_aligned(unsigned long sz, unsigned long align)
{
void *ptr = __va(memblock_phys_alloc(sz, align));
memset(ptr, 0, sz);
return ptr;
}
early_alloc_aligned
用于早期分配指定长度的物理内存,参数sz指向需求物理内存的长度,align指向对齐方式
3.5.1.71. early_cma
static int __init early_cma(char *p)
{
if(!p)
return -EINVAL;
//解析cmdline的长度
size_cmdline = memparse(p, &p);
//判断字符串中是否含有"@"字符
if(*p != '@')
return 0;
base_cmdline = memparse(p + 1, &p);
if(*p != '-') {
limit_cmdline = base_cmdline + size_cmdline;
return 0;
}
limit_cmdline = memparse(p + 1, &p);
return 0;
}
early_param("cma", early_cma);
early_cma
用于从CMDLINE中解析”cma=”参数,cma参数支持的格式如下
cma=nn[MG]@[start][MG][-end[MG]]
例如:
"cma=20M@0x68000000-0x70000000"
上面的例子就是CMA的大小为20M,这20M物理内存必须在0x68000000-0x70000000范围内查找.
3.5.1.72. early_fixmap_shutdown
static void __init early_fixmap_shutdown(void)
{
int i;
//获得__end_Of_permanent_fixed_addresses对应的虚拟地址
unsigned long va = fix_to_virt(__end_of_permanent_fixed_addresses - 1);
pte_offset_fixmap = pte_offset_late_fixmap;
//清除虚拟地址对应的PMD入口
pmd_clear(fixmap_pmd(va));
//刷新tbl
local_flush_tlb_kernel_page(va);
for(i = 0; i < __end_of_permanent_fixed_addresses; i++) {
pte_t *pte;
struct map_desc map;
//获得FIXMAP区域内的虚拟地址
map.virtual = fix_to_virt(i);
//获得虚拟地址对应的PTE入口
pte = pte_offset_early_fixmap(pmd_off_k(map.virtual), map.virtual);
//如果PTE为空或者PTE表项不包含L_PTE_MT_DEV_SHARD,那么不为其建立页表,结束本次循环
if(pte_none(*pte) || (pte_val(*pte) & L_PTE_MT_MASK) != L_PTE_MT_DEV_SHARED)
continue;
//
map.pfn = pte_pfn(*pte);
map.type = MT_DEVICE;
map.length = PAGE_SIZE;
//建立页表
create_mapping(&map);
}
}
early_fixmap_shutdown
作用是为FIXMAP区域建立页表
3.5.1.73. early_ioremap_init
void __init early_ioremap_init(void)
{
early_ioremap_setup();
}
early_ioremap_init
用于初始化早期的I/O使用的虚拟地址
3.5.1.74. early_ioremap_setup
void __init early_ioremap_setup(void)
{
int i;
for(i = 0; i < FIX_BTMAPS_SLOTS; i++)
if(WARN_ON(prev_map[i]))
break;
for(i = 0; i < FIX_BTMAPS_SLOTS; i++)
//将所有SLOT对应的虚拟地址存储在slot_virt数组中
slot_virt[i] = __fix_to_virt(FIX_BTMAP_BEGIN - NR_FIX_BTMAPS * i);
}
early_ioremap_setup
设置早期I/O映射的虚拟地址,函数在FIXMAP内存区域分配了一块虚拟地址给早期的I/O使用,
一个包含FIX_BTMAPS_SLOTS个slot,每个slot包含了NR_FIX_BTMAPS
3.5.1.75. early_init_dt_add_memory_arch
//base: 物理内存的基地址
//size: 物理内存的长度
void __init __weak early_init_dt_add_memory_arch(u64 base, u64 size)
{
const u64 phys_offset = MIN_MEMBLOCK_ADDR;
//将基地址和长度与MEMBLOCK管理的最大和最小物理内存信息做检测
if(size < PAGE_SIZE - (base & ~PAGE_MASK)) {
return;
}
if(!PAGE_ALIGNED(base)) {
size -= PAGE_SIZE - (base & ~PAGE_MASK);
base = PAGE_ALIGN(base);
}
size &= PAGE_MASK;
if(base > MAX_MEMBLOCK_ADDR)
return;
if(base + size - 1 > MAX_MEMBLOCK_ADDR)
size = MAX_MEMBLOCK_ADDR - base + 1;
if(base + size < phys_offset)
return;
if(base < phys_offset) {
size -= phys_offset - base;
base = phys_offset;
}
//将物理内存信息更新到MEMBLOCK内存管理器里
memblock_add(base, size);
}
early_init_dt_add_memory_arch
用于从DTB中获得内存的基地址和长度之后,进行对齐和检测操作,并将基地址对应
长度的物理内存信息添加到MEMBLOCK内存管理器里
3.5.1.76. early_init_dt_alloc_reserved_memory_arch
//size: 分配大小
//align: 对齐方式
//start: 可以分配的起始地址
//end: 可以分配的终止地址
//nomap: 指明是否为预留区内存建立页表
//res_base: 用于分配物理内存的起始地址
int __init __weak early_init_dt_alloc_reserved_memory_arch(phys_addr_t size,
phys_addr_t align, phys_addr_t start, phys_addr_t end, bool nomap, phys_addr_t *res_base)
{
phys_addr_t base;
//判断end和align是否符合要求,并处理
end = !end ? MEMBLOCK_ALLOC_ANYWHERE : end;
align = !align ? SMP_CACHE_BYTES : align;
//从指定的物理内存中分配所需要大小的物理内存
base = __memblock_alloc_base(size, align, end);
if(!base)
return -ENOMEM;
//如果分配的地址小于指定的起始地址,则释放申请的内存并退出
if(base < start) {
memblock_free(base, size);
return -ENOMEM;
}
//将申请的物理内存地址写入res_base中
*res_base = base;
//如果不需要对新分配的物理内存建立页表
if(nomap)
return memblock_remove(base, size);
return 0;
}
early_init_dt_alloc_reserved_memory_arch
用于为DTS中”reseved-memory”节点中的特定预留区分配物理内存,该类预留区不包含”reg”属性,
只包含”size”属性,因此需要对这里预留区分配物理内存
3.5.1.77. early_init_dt_check_for_initrd
//node: 代表chosen节点在dtb区域内的偏移
static void __init early_init_dt_check_for_initrd(unsigned long node)
{
u64 start, end;
int len;
const __be32 *prop;
//获得linux,initrd-start属性值
prop = of_get_flat_dt_prop(node, "linux,initrd-start", &len);
if(!prop)
return;
start = of_read_number(prop, len/4);
//获得linux,initrd-end属性值
prop = of_get_flat_dt_prop(node, "linux,initrd-end", &len);
if(!prop)
return;
end = of_read_number(prop, len/4);
//将inird的地址写入全局变量initrd_start, initrd_end中
__early_init_dt_declare_initrd(start, end);
phys_initrd_start = start;
phys_initrd_size = end - start;
}
early_init_dt_check_for_initrd
用于从DTB中读出INITRD信息,并设置INITRD相关的全局变量
3.5.1.78. __early_init_dt_declare_initrd
static void __early_init_dt_declare_initd(unsigned long start, unsigned long end)
{
if(!IS_ENABLED(CONFIG_ARM64)) {
initrd_start = (unsigned long)__va(start);
initrd_end = (unsigned long)__va(end);
initrd_below_start_ok = 1;
}
}
__early_init_dt_declare_initrd
用于在系统启动早期,设置INITRD的范围.
3.5.1.79. early_init_dt_reserve_memory_arch
//base: 指向DTB占用物理内存区的起始地址
//size: 大小
//nomap: 是否给DTB占用的物理内存建立页表
int __init __weak early_init_dt_reserve_memory_arch(phys_addr_t base,
phys_addr_t size, bool nomap)
{
if(nomap)
return memblock_remove(base, size);
return memblock_reserve(base, size);
}
early_init_dt_reserve_memory_arch
根据不同的条件,将DTB占用的物理内存加入到MEMBLOCK的指定区域
3.5.1.80. early_init_dt_scan_chosen
//node: dtb中chosen节点在dtb devicetree structure区域内的偏移
//uname: chosen节点的节点名字
//depth: chosen字节点的前套数
//data: 指向cmdline
int __init early_init_dt_scan_chosen(unsigned long node, const char *name,
int depth, void *data)
{
int l;
const char *p;
//检查depth及data有效性
//检查节点名字是否为chosen或chosen@0
if(depth != 1 || !data || (strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0))
return 0;
//从dtb的chosen节点中读取并设置initrd相关的数据
early_init_dt_check_for_initrd(node);
//读取bootargs属性值,如果存在则拷贝到data
p = of_get_flat_dt_prop(node, "bootargs", &l);
if(p != NULL && l > 0)
strlcpy(data, p, min((int)l, COMMAND_LINE_SIZE));
#ifdef CONFIG_CMDLINE
#if defined(CONFIG_CMDLINE_EXTEND)
//如果存在CONFIG_CMDLINE_EXTEND宏,则将其中的cmdline拼接到data后
strlcat(data, " ", COMMAND_LINE_SIZE);
strlcat(data, CONFIG_CMDLINE, COMMAND_LINE_SIZE);
#if defined(CONFIG_CMDLINE_FORCE)
strlcpy(CONFIG_CMDLINE, COMMAND_LINE_SIZE);
#else
if(!((char *)data)[0])
strlcpy(data, CONFIG_CMDLINE, COMMAND_LINE_SIZE);
#endif
#endif
return 1;
}
early_init_dt_scan_chosen
用于从DTB的chosen节点中读取bootargs参数并与内核提供的CMDLINE,最终合成系统启动时需要的cmdline.
3.5.1.81. early_init_dt_scan_memory
//node: 指向节点的偏移
//uname: 节点名字
//depth: 节点深度
//data: 私有数据
int __init early_init_dt_scan_memory(unsigned long node, const char *uname,
int depth, void *data)
{
//获得当前节点的device_type属性值
const char *type = of_get_flat_dt_prop(node, "device_type", NULL);
cosnt __be32 *reg, *endp;
int l;
bool hotpluggable;
//如果属性值不存在或不是memory则直接返回
if(type == NULL || strcmp(type, "memory") != 0)
return 0;
//读取linux,usable-memory属性值,如果不存在则读取reg属性值
reg = of_get_flat_dt_prop(node, "linux,usable-memory", &l);
if(reg == NULL)
reg = of_get_flat_dt_prop(node, "reg", &l);
if(reg == NULL)
return 0;
//
endp = reg + (1 / sizeof(__be32));
hotpluggable = of_get_flat_dt_prop(node, "hotpluggable", NULL);
while((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) {
u64 base, size;
base = dt_mem_next_cell(dt_root_addr_cells, ®);
size = dt_mem_next_cell(dt_root_size_cells, ®);
if(size == 0)
continue;
early_init_dt_add_memory_arch(base, size);
//如果系统不支持热拔插的内存条则结束本次循环
if(!hotpluggable)
continue;
}
return 0;
}
early_init_dt_scan_memory
用于获得DTB memory节点的信息,即物理内存的信息,并将物理内存的信息更新到MEMBLOCK内存分配器中.
3.5.1.82. early_init_dt_sacn_nodes
void __init early_init_dt_scan_nodes(void)
{
int rc = 0;
//获得cmdline信息
rc = of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);
//获得根节点的address-cells和size-cells信息
of_scan_flat_dt(early_init_dt_scan_root, NULL);
//获得内存信息
of_scan_flat_dt(early_init_dt_scan_memory, NULL);
}
early_init_dt_scan_nodes
用于从DTB获得cmdline信息,以及物理内存信息.
3.5.1.83. early_init_dt_scan_root
int __init early_init_dt_scan_root(unsigned long node, const char *uname, int depth, void *data)
{
const __be32 *prop;
//根节点的depth为0,如果非0值直接返回
if(depth != 0)
return 0;
dt_root_size_cells = OF_ROOT_NODE_SIZE_CELLS_DEFAULT;
dt_root_addr_cells = OF_ROOT_NODE_ADDR_CELLS_DEFAULT;
prop = of_get_flat_dt_prop(node, "#size-cells", NULL);
if(prop)
dt_root_size_cells = be32_to_cpup(prop);
prop = of_get_flat_dt_prop(node, "#address-cells", NULL);
if(prop)
dt_root_addr_cells = be32_to_cpup(prop);
return 1;
}
early_init_dt_scan_root
用于获得根节点#size_cells用于#address-cells对应的属性值
3.5.1.84. early_init_dt_verify
//params: dtb所在的虚拟地址
bool __init early_init_dt_verify(void *params)
{
if(!params)
return false;
//检查dtb header的有效性
if(fdt_check_header(params))
return false;
//将params参数赋值给initial_boot_params,生成dtb的crc
of_fdt_crc32 = crc32_be(~0, initial_boot_params, fdt_totalsize(initial_boot_params));
return true;
}
early_init_dt_verify
用于检查dtb的有效性
3.5.1.85. early_init_fdt_reserve_self
void __init early_init_fdt_reserve_self(void)
{
if(!initial_boot_params)
return;
early_init_dt_reserve_memory_arch(__pa(initial_boot_params), fdt_totalsize(initial_boot_params), 0);
}
early_init_fdt_reserve_self
将dtb将入到MEMBLOCK的预留区内.initial_boot_params指向dtb所在的虚拟地址
3.5.1.86. early_init_fdt_scan_reserved_mem
void __init early_init_fdt_scan_reserved_mem(void)
{
int n;
u64 base, size;
//判断initial_boot_params有效性
if(!initial_boot_params)
return;
//遍历memory reserved mapping区域内的所有预留区
fo(n = 0; ; n++)
{
//获取区域内的起始地址和长度
fdt_get_mem_rsv(initial_boot_params, n, &base, &size);
if(!size)
break;
//加入到MEMBLOCK分配器的预留区内
early_init_dt_reserve_memory_arch(base, size, 0);
}
//将dts的reserved-memory节点中子节点对应的预留区加入到系统的reserved_mem数组
of_scan_flat_dt(__fdt_scan_reserved_mem, NULL);
fdt_init_reserved_mem();
}
early_init_fdt_scan_reserved_mem
用于将dtb中memory reserved mapping区域内的预留区加入到MEMBLOCK内存分配器的预留区.
并将dts reserved-memory节点的子节点加入到系统预留区,并初始化节点
3.5.1.87. early_mm_init
//mdesc: machine_desc结构
void __init early_mm_init(const struct machine_desc *mdesc)
{
//根据体系设置PTE和PMD在mem_types[]数组中的标志
build_mem_type_table();
//初始化早期的页表
early_paging_init(mdesc);
}
early_mm_init
用于初始化简单的页表项PTE和PMD.
3.5.1.88. early_pte_alloc
//pmd: PMD入口
//addr: 虚拟地址
//prot: 页表标志
static pte_t * __init early_pte_alloc(pmd_t *pmd, unsigned long addr, unsigned long prot)
{
return arm_pte_alloc(pmd, addr, prot, early_alloc);
}
early_pte_alloc
用于内核启动早期,分配并初始化一个PTE页表
3.5.1.89. early_trap_init
//vectors_base: 新异常向量表所在的虚拟地址
void __init early_trap_init(void *vectors_base)
{
#ifndef CONFIG_CPU_V7M
unsigned long vectors = (unsigned long)vectors_base;
extern char __stubs_start[], __stubs_end[];
extern char __vectors_start, __vectors_end[];
unsigned i;
vectors_page = vectors_base;
for(i = 0; i < PAGE_SIZE / sizeof(u32); i++)
((u32 *)vectors_base)[i] = 0xe7fddef1;
memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
memcpy((void *)vectors + 0x1000, __stubs_start, __stubs_end - __stubs_start);
kuser_init(vectors_base);
flush_icache_range(vectors, vectors + PAGE_SIZE * 2);
#else
#endif
}
early_trap_init
用于早期异常向量表的建立.
3.5.1.90. elf_hwcap_fixup
static void __init elf_hwcap_fixup(void)
{
unsigned id = read_cpuid_id();
if(read_cpuid_part() == ARM_CPU_PART_ARM1136 && ((id >> 20) & 3) == 0) {
elf_hwcap &= ~HWCAP_TLS;
return;
}
if((id & 0x000f0000) != 0x000f0000)
return;
if(cpuid_feature_extract(CPUID_EXT_ISAR3, 12) > 1 ||
(cpuid_feature_extract(CPUID_EXT_ISAR3, 12) == 1 &&
cpuid_feature_extract(CPUID_EXT_ISAR4, 20) >= 3))
elf_hwcap &= ~HWCAP_SWP;
}
elf_hwcap_fixup
用于修改正ARM硬件支持的信息.
3.5.1.91. end_of_stack
static inline unsigned long *end_of_stack(struct task_struct *p)
{
#ifdef CONFIG_STACK_GROWSUP
return (unsigned long *)((unsigned long)task_thread_info(p) + THREAD_SIZE) - 1;
#else
return (unsigned long *)(task_thread_info(p) + 1);
#endif
}
end_of_stack
用于获得进程堆栈栈顶的地址