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. 这一招将 Numpy 加速 700 倍!!!
  2. DCNv2 windows编译 2021ok
  3. apache 服务发布多个项目,只需要更改配置文件(需要设定虚拟主机)
  4. ubuntu下配置eclipse环境
  5. [HEOI2016/TJOI2016]求和
  6. python基础(part11)-作用域LEGB
  7. 漫步数学分析二十五——等连续函数
  8. mysql加入新的从节点怎么配置,Mysql 5.7从节点配置多线程主从复制的方法详解
  9. oracle中冗余,各位有没有检查冗余索引的脚本
  10. 不愿做「奴隶」的程序员们组建了一个王国
  11. UI设计教程学习分享:APP布局
  12. 史上最全微信运营工具
  13. Vue2.0安装教程
  14. ip rule 命令
  15. 常见的行业认证和资质清单介绍
  16. 分享渴垂栋懈沁心烟帆新上线的网站如何在几天内优化至首页
  17. Ubuntu上,tftpd-hpa 启动失败的解决方法.
  18. 服务器压力测试 性能测试 AB、Webbench、Tsung
  19. 关于Linux安装OpenLDAP说明
  20. 服务器上zip 解压

热门文章

  1. ip网络广播对讲的特点
  2. 吴恩达机器学习(七)—— 神经网络:Representation
  3. Oracle查询表字段信息及注释
  4. pyqt5 最小化 系统托盘_PyQt5 系统托盘
  5. 使用Word轻松实现PDF转Word
  6. C语言---求符合给定条件的整数集
  7. 博客营销有什么价值?应注意什么?
  8. “喜新厌旧”的喜茶,该如何撑起600亿的估值?
  9. 尤达表达式_研究生院:“做……还是不做。 没有尝试” –尤达
  10. Ubuntu16.04无声音问题解决及网页视频无法播放问题解决