memblock初始化过程中fdt mem处理

导读

本部分将memblock 初始化时FDT中reserved memory的扫描处理细化整理,调用情况如下:

code目录:

涉及到的目录 简单描述
./kernel-4.9/include/linux/of_reserved_mem.h reserved_mem 结构相关定义位置
./kernel-4.9/include/linux/of.h _OF_DECLARE宏定义位置
./kernel-4.9/drivers/of/fdt.c fdt 接口实现位置
./kernel-4.9/scripts/dtc/libfdt/fdt_ro.c fdt_get_mem_rsv实现位置
./kernel-4.9/drivers/of/of_reserved_mem.c fdt和reserved mem相关函数实现位置

1. 调用入口

这个函数是扫描reserve节点,添加到memblock管理中

/*** early_init_fdt_scan_reserved_mem() - create reserved memory regions** This function grabs memory from early allocator for device exclusive use* defined in device tree structures. It should be called by arch specific code* once the early allocator (i.e. memblock) has been fully activated.*/
void __init early_init_fdt_scan_reserved_mem(void)
{int n;u64 base, size;if (!initial_boot_params) return;//是否已经可以访问到FDT/* Process header /memreserve/ fields */for (n = 0; ; n++) {//在dts中有些vendor会定义memreserve,地址和size,会在这里读取并配置为reserved,当前平台没有配置这个//像这样:/memreserve/ 0x80000000 0x00010000;fdt_get_mem_rsv(initial_boot_params, n, &base, &size);//读取地址if (!size) break;early_init_dt_reserve_memory_arch(base, size, 0);//针对这些地址,如果是nomap则从memblock中删除,否则标记为memblock reserve}of_scan_flat_dt(__fdt_scan_reserved_mem, NULL);//主要找到的是这个:扫描所有节点,并对每个节点调用__fdt_scan_reserved_memfdt_init_reserved_mem();
}

处理的入口函数,做三件事:

  1. 对memreserve fields的地址添加处理:标记nomap的从memblock中删除,其他的添加到memblock.reserve中;
  2. 扫描设备树中的所有节点,找到reserved-memory节点及其子节点,解析其地址范围;
  3. 对于找到的需要reserved的地址空间,添加处理

这个函数是在扫描所有FDT的设备节点,并针对于每一个节点调用回调;

1.1 memreserve部分处理

从FDT中获取到base和size:fdt_get_mem_rsv(initial_boot_params, n, &base, &size)

int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
{FDT_CHECK_HEADER(fdt);*address = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->address);//将address写入到传入的地址中*size = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->size);//将size写入到传入的地址中return 0;
}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);//标记有nomap则将其从memblock管理中删除;return memblock_reserve(base, size);//没有标记nomap,则添加到reserve region中
}

这里处理很简单,不做过多分析;

1.2 FDT扫描

本部分来看具体扫描FDT节点过程;

核心函数是:of_scan_flat_dt(__fdt_scan_reserved_mem, NULL),先上code:

/*** of_scan_flat_dt - scan flattened tree blob and call callback on each.* @it: callback function* @data: context data pointer** This function is used to scan the flattened device-tree, it is used to extract the memory information at boot before we can unflatten the tree*/
int __init of_scan_flat_dt(int (*it)(unsigned long node, const char *uname, int depth, void *data), void *data)
{const void *blob = initial_boot_params;const char *pathp;int offset, rc = 0, depth = -1;if (!blob)  return 0;for (offset = fdt_next_node(blob, -1, &depth); offset >= 0 && depth >= 0 && !rc; offset = fdt_next_node(blob, offset, &depth)) {//循环扫描所有节点,对每个节点调用传入的it函数,针对此case为:__fdt_scan_reserved_mempathp = fdt_get_name(blob, offset, NULL);if (*pathp == '/')  pathp = kbasename(pathp);rc = it(offset, pathp, depth, data);//等待到这里返回1的时候跳出,即找到所需要的node了;}return rc;
}

整理:

  1. 对当前所有FDT节点进行扫描,注意这里是还没有做flat操作;
  2. 对每个节点调用 __fdt_scan_reserved_mem 确认其是否为reserved-memory节点
  3. 循环跳出条件为offset >= 0 && depth >= 0 && !rc,则__fdt_scan_reserved_mem返回值为1时,则处理完成,跳出循环

1.2.1 扫描判断reserved-memory

详细来看每个节点的扫描过程:fdt_scan_reserved_mem

static int __init __fdt_scan_reserved_mem(unsigned long node, const char *uname, int depth, void *data)
{static int found;const char *status;int err;if (!found && depth == 1 && strcmp(uname, "reserved-memory") == 0) {//找到了reserved-memory,且depth为1,接下来是要扫描其子节点if (__reserved_mem_check_root(node) != 0) {pr_err("Reserved memory: unsupported node format, ignoring\n");/* break scan */return 1;}found = 1;/* scan next node */return 0;} else if (!found) {//没找到则继续扫下一个节点/* scan next node */return 0;} else if (found && depth < 2) {//已经找到了reserved-memory,并且已经扫描完成了所有他的子节点,跳出扫描,/* scanning of /reserved-memory has been finished */return 1;}status = of_get_flat_dt_prop(node, "status", NULL);//对于reserved-memory的子节点,status节点如果不是OK,则返回,一般这个节点都没定义if (status && strcmp(status, "okay") != 0 && strcmp(status, "ok") != 0) return 0;err = __reserved_mem_reserve_reg(node, uname);//解析reserved-memory中的二元组,即reg中base和size,如果没有则退出if (err == -ENOENT && of_get_flat_dt_prop(node, "size", NULL)) fdt_reserved_mem_save_node(node, uname, 0, 0);/* scan next node */return 0;
}

整理:

  1. 判断条件:depth == 1 && strcmp(uname, “reserved-memory”),即只判断深度为1的name是否为reserved-memory,对于其他节点则直接返回,可以提高效率;
  2. 对于已经找到reserved-memory后,继续扫描其subnode,直至其所有子节点都已经扫描完成;
    1. found 配置为1;
    2. depth >= 2;
    3. 判断该节点status属性,对于reserved mem部分,一般都没有定义status;
    4. 解析节点中base地址和size大小,然后通过early_init_dt_reserve_memory_arch处理完成;
  3. reserved-memory节点已经扫描完成,下一个depth=1的节点时退出循环;

其中一个关键函数:

static int __init __reserved_mem_reserve_reg(unsigned long node, const char *uname)
{int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32);phys_addr_t base, size;int len;const __be32 *prop;int nomap, first = 1;prop = of_get_flat_dt_prop(node, "reg", &len);//获取reg节点中数据if (!prop) return -ENOENT;if (len && len % t_len != 0) {//无效节点返回pr_err("Reserved memory: invalid reg property in '%s', skipping node.\n", uname);return -EINVAL;}nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL;//获取nomap信息while (len >= t_len) {base = dt_mem_next_cell(dt_root_addr_cells, &prop);size = dt_mem_next_cell(dt_root_size_cells, &prop);//获取到reg的数据if (size && early_init_dt_reserve_memory_arch(base, size, nomap) == 0)//size不为0,然后就去调用early_init_dt_reserve_memory_arch分配或者释放到memblockpr_debug("Reserved memory: reserved region for node '%s': base %pa, size %ld MiB\n", uname, &base, (unsigned long)size / SZ_1M);elsepr_info("Reserved memory: failed to reserve memory for node '%s': base %pa, size %ld MiB\n", uname, &base, (unsigned long)size / SZ_1M);len -= t_len;if (first) {fdt_reserved_mem_save_node(node, uname, base, size);first = 0;}}return 0;
}//构造reserved_mem结构,将从FDT中扫描到的reserved mem节点,添加到reserved_mem管理;
void __init fdt_reserved_mem_save_node(unsigned long node, const char *uname,  phys_addr_t base, phys_addr_t size)
{struct reserved_mem *rmem = &reserved_mem[reserved_mem_count];if (reserved_mem_count == ARRAY_SIZE(reserved_mem)) {pr_err("not enough space all defined regions.\n");return;}rmem->fdt_node = node;rmem->name = uname;rmem->base = base;rmem->size = size;reserved_mem_count++;return;
}

整理:

  1. 获取reg节点中定义的address以及size,并添加到memblock管理中去(ps:nomap的去除)
  2. 将每个节点都添加到reserved_mem管理

1.3 reserved_mem处理

将当前所有添加到reserved_mem做下一步处理,上code:

/*** fdt_init_reserved_mem - allocate and init all saved reserved memory regions*/
void __init fdt_init_reserved_mem(void)
{int i;__rmem_check_for_overlap();
//检测当前的reserved region中是否存在重叠项,有的话也只是打印了个log
//打印:OVERLAP DETECTED!\n%s (%pa--%pa) overlaps with %s (%pa--%pa)for (i = 0; i < reserved_mem_count; i++) {struct reserved_mem *rmem = &reserved_mem[i];//当前所有的reserve memunsigned long node = rmem->fdt_node;int len;const __be32 *prop;int err = 0;
//获取node的phandle,每个节点都会有phandle或者linux,phandle属性,这个是在dtc编译的时候添加的;prop = of_get_flat_dt_prop(node, "phandle", &len);if (!prop) prop = of_get_flat_dt_prop(node, "linux,phandle", &len);if (prop) rmem->phandle = of_read_number(prop, len/4);
//size为0则这段reserved mem需要动态分配,       if (rmem->size == 0) err = __reserved_mem_alloc_size(node, rmem->name, &rmem->base, &rmem->size);//size为0的时候动态分配if (err == 0) __reserved_mem_init_node(rmem);//分配成功后,init node}
}

整理:

  1. 检测当前所有的reserved_mem,确认是否有重叠部分,如果有则打印提示;
  2. 对每个reserved_mem检查其phandle(句柄)情况;
  3. size为0则,这个case需要动态分配
  4. err为0有两种case:1. 静态分配;2. 动态分配成功;总之这里需要调用对应的初始化函数;

reserved_mem结构:

struct reserved_mem {const char          *name;//reserved mem nameunsigned long          fdt_node;//节点unsigned long          phandle;//句柄const struct reserved_mem_ops   *ops;//操作接口phys_addr_t          base;//基地址phys_addr_t           size;//大小void               *priv;
};
struct reserved_mem_ops {int    (*device_init)(struct reserved_mem *rmem, struct device *dev);void  (*device_release)(struct reserved_mem *rmem, struct device *dev);
};
static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS];
static int reserved_mem_count;

最后一个步骤,__reserved_mem_init_node调用各个设备初始化:


static int __init __reserved_mem_init_node(struct reserved_mem *rmem)
{extern const struct of_device_id __reservedmem_of_table[];const struct of_device_id *i;for (i = __reservedmem_of_table; i < &__rmem_of_table_sentinel; i++) {reservedmem_of_init_fn initfn = i->data;const char *compat = i->compatible;if (!of_flat_dt_is_compatible(rmem->fdt_node, compat)) continue;if (initfn(rmem) == 0) {//这里调用的即对应设备的init函数pr_info("initialized node %s, compatible id %s\n", rmem->name, compat);return 0;}}return -ENOENT;
}

宏定义的处理:

//在实际使用位置定义,根据compatible找到其调用的函数
RESERVEDMEM_OF_DECLARE(reserve_memory_ram_console, "XXX,ram_console", ram_console_binary_reserve_memory);#define RESERVEDMEM_OF_DECLARE(name, compat, init)   _OF_DECLARE(reservedmem, name, compat, init, reservedmem_of_init_fn)#define _OF_DECLARE(table, name, compat, fn, fn_type)           \static const struct of_device_id __of_table_##name     \__used __section(__##table##_of_table)         \= { .compatible = compat,                \.data = (fn == (fn_type)NULL) ? fn : fn  }  

转换完后就是这样子滴:

static const struct of_device_id __of_table_reserve_memory_ram_console __used __section(__reservedmem_of_table) =
{
.compatible = compat,
.data = ram_console_binary_reserve_memory
}

  1. __of_table_reserve_memory_ram_console这个函数被放在了section: __reservedmem_of_table中
  2. compatible匹配的时候即可调用到对应的.data,该值实质为我们传入的函数 ram_console_binary_reserve_memory;

对应在System.map中可以看到:

fff8008f98708 T __reservedmem_of_table
fff8008f987d0 t __of_table_reserve_memory_ipc
fff8008f98898 t __of_table_reserve_memory_minirdump
fff8008f98960 t __of_table_reserve_memory_ram_console
fff8008f98a28 t __of_table_ramconsole_mem_reserved
fff8008f98af0 t __of_table_viss_log_reserved
fff8008f98bb8 t __of_table_hsm_log_reserved
fff8008f98c80 t __of_table_reserve_memory_metazone
fff8008f98d48 t __of_table_trustzone_share_reserved

2 总结

此部分简单梳理在memblock init过程中对FDT中节点处理的操作:

  1. 找到FDT中reserved-mem节点节点,这个对应的逻辑还是有点意思的;
  2. 获取到该节点数据,将其添加到memblock.reserve或者从memblock中删除;
  3. 构建一个reserved_mem结构,对其做初始化处理,在对应reserved region匹配的compatible的设备中会通过宏绑定到初始化函数;
    1. linux中太多这种宏转换了,需要习惯

memblock初始化过程中fdt mem处理相关推荐

  1. Direct Sparse Odometry (一)初始化过程中的光度误差优化

    Direct Sparse Odometry (DSO) 初始化过程中的导数推导 文章目录 Direct Sparse Odometry (DSO) 初始化过程中的导数推导 前言 一.光度误差建模 二 ...

  2. 操作系统学习:进程、线程与Linux0.12初始化过程概述

    本文参考书籍 1.操作系统真相还原 2.Linux内核完全剖析:基于0.12内核 3.x86汇编语言 从实模式到保护模式 ps:基于x86硬件的pc系统 进程 进程是一种控制流集合,集合中至少包含一条 ...

  3. 启动期间的内存管理之初始化过程概述----Linux内存管理(九)

    转载地址:https://blog.csdn.net/gatieme/article/details/52403148 日期 内核版本 架构 作者 GitHub CSDN 2016-09-01 Lin ...

  4. 超详细解读ORB-SLAM3 单目初始化过程(上篇)

    学习ORB-SLAM3单目视觉SLAM中,发现有很多知识点需要展开和深入,同时又需要对系统有整体的认知,为了强化记忆,记录该系列笔记,为自己图方便,也希望对大家有所启发. 因为知识有限,因此先记录初始 ...

  5. [Spring 深度解析]第7章 IoC容器的初始化过程

    7. IoC容器的初始化过程 ​ 简单来说,IoC容器的初始化是由前面介绍的refresh()方法来启动的,这个方法标志着IoC容器的正式启动.具体来说,这个启动包括BeanDefinition的Re ...

  6. 解析 Java 类和对象的初始化过程 由一个单态模式引出的问题谈起

    在 IBM Bluemix 云平台上开发并部署您的下一个应用. 开始您的试用 问题引入 近日我在调试一个枚举类型的解析器程序,该解析器是将数据库内一万多条枚举代码装载到缓存中,为了实现快速定位枚举代码 ...

  7. 《Spring技术内幕》——2.3节IoC容器的初始化过程

    2.3 IoC容器的初始化过程 简单来说,IoC容器的初始化是由前面介绍的refresh()方法来启动的,这个方法标志着IoC容器的正式启动.具体来说,这个启动包括BeanDefinition的Res ...

  8. java类初始化_Java的类/实例初始化过程

    昨天看到群里面有人分享了一道题目,我答错了,于是趁机了解了下Java的类/对象初始化过程: 程序的输出见文章最后 程序A主要考察的是 类实例初始化 .简单验证了下,类实例初始化过程如下:父类实例初始化 ...

  9. 【NVMe2.0b 9】控制器初始化过程

    控制器初始化过程 3.5控制器初始化 3.5.1基于内存传输的控制器初始化 3.5.2基于消息传输的控制器初始化 3.5.3Controller Ready Modes During Initiali ...

最新文章

  1. 计算机中的大端模式与小端模式
  2. WEB 进程的查看和关闭(kill)
  3. idea社区版和企业版区别_IntelliJ IDEA 旗舰版与社区版有什么不同
  4. const常量与define宏定义的区别
  5. python 对象转dict_如何将python dict对象转换为java等效对象?
  6. html提交表单给php邮件发送,在HTML表单中通过PHP自动发送电子邮件
  7. WEB产品展示Bootstrap4模板
  8. u盘版linux initramfs,Linux_内核升级后 出现initramfs错误问题解决, 出现initramfs找不到/root /hos - phpStudy...
  9. 蓝桥杯 ALGO-148 算法训练 5-1最小公倍数
  10. 喵哈哈村小学上课啦(欧拉函数)
  11. 如何升级浏览器_涨姿势|教你用手机一键升级路由器软件(固件)
  12. Chrome 跨域调试
  13. Xen虚拟机加入Puppet环境如何解决时间同步问题
  14. 基于SSM开发的房屋租赁系统 JAVA
  15. 物联网消息服务器,GitHub - tian-yuan/CMQ: go 实现的分布式开源物联网MQTT消息服务器...
  16. 中小企业知识管理难题怎么做?天翎与群晖携手给出最优解
  17. actions vuex 请求_vue中vuex的actions里面请求接口,提交给mutations报错
  18. linux 卸载yum源,CentOS下rpm包与yum安装与卸载更新系统源
  19. 无约束优化之单纯形法(Nelder-Mead Algorithm)
  20. 无法卸载vue2.x提示up to date

热门文章

  1. IP地址和子网划分_IP格式和子网掩码
  2. Dota2 高分段英雄热度爬虫(模拟CSRF,RSA登陆)
  3. 互联网创业者前途光明
  4. 申请社交平台appkey详细教程
  5. 《战胜华尔街》精华 - 彼得·林奇的25条投资法则
  6. 联想Y7000、R720系列蓝屏死机解决方案
  7. 物理方法与实践练习题
  8. 【图像笔记】RGB图像转灰度图像
  9. 【第3版emWin教程】第48章 emWin6.x对话框基础知识
  10. 位段是什么玩意?你听说过吗??