赞
踩
使用的源码包为华为官方的ascend200AI加速模块的SDK,其下载地址位于:点击跳转
使用的固件与驱动版本为:1.0.9.alpha
压缩包名称为:A200-3000-sdk_20.2.0.zip
将A200-3000-sdk_20.2.0.zip解压后可以看到Ascend310-source-minirc.tar.gz压缩包,这个压缩包里有ascend200AI加速模块的linux内核源码包、设备树及驱动文件等。
/memreserve/
定义的保留内存信息;表示整个设备树的块和地址信息。
struct fdt_header {
uint32_t magic;
uint32_t totalsize;
uint32_t off_dt_struct;
uint32_t off_dt_strings;
uint32_t off_mem_rsvmap;
uint32_t version;
uint32_t last_comp_version;
uint32_t boot_cpuid_phys;
uint32_t size_dt_strings;
uint32_t size_dt_struct;
};
例如:
00000000: D0 0D FE ED 00 00 25 40 00 00 00 38 00 00 21 C8 P.~m..%@...8..!H
00000010: 00 00 00 28 00 00 00 11 00 00 00 10 00 00 00 00 ...(............
00000020: 00 00 03 78 00 00 21 90 00 00 00 00 00 00 00 00 ...x..!.........
00000030: 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 ................
00000040: 00 00 00 03 00 00 00 04 00 00 00 00 00 00 00 01 ................
00000050: 00 00 00 03 00 00 00 04 00 00 00 0F 00 00 00 01 ................
00000060: 00 00 00 03 00 00 00 0F 00 00 00 1B 78 6C 6E 78 ............xlnx
00000070: 2C 7A 79 6E 71 2D 37 30 30 30 00 00 00 00 00 01 ,zynq-7000......
00000080: 63 68 6F 73 65 6E 00 00 00 00 00 03 00 00 00 16 chosen..........
00000090: 00 00 00 26 63 6F 6E 73 6F 6C 65 3D 74 74 79 50 ...&console=ttyP
000000a0: 53 30 2C 31 31 35 32 30 30 00 00 00 00 00 00 02 S0,115200.......
000000b0: 00 00 00 01 61 6C 69 61 73 65 73 00 00 00 00 03 ....aliases.....
000000c0: 00 00 00 18 00 00 00 2F 2F 61 6D 62 61 2F 65 74 .......//amba/et
设备树的memreserve信息,只有16个字节,8个字节表示保留内存的address,8个字节表示保留内存的size,他在内核解析设备树时告诉内核要保留哪里的内存在存放设备树。
存放所有属性的值,每个节点以0x00000001开始,后面接节点名字,以0x00000002结束,0x00000003表示后面要跟属性值了,后面依次是4字节的val的长度,4字节val的名字在string block的偏移,然后跟val的字符。整个 structure block以0x00000009结束。在dts文件中会有节点的别名,编译为dtb文件是没有的,外部别名添加在信息会全部汇总在节点里。
例如:
dts:
#size-cells = <0x1>; compatible = "xlnx,zynq-7000"; chosen { bootargs = "console=ttyPS0,115200"; }; aliases { ethernet0 = "/amba/ethernet@e000b000"; serial0 = "/amba/serial@e0001000"; serial1 = "/amba/serial@e0000000"; spi0 = "/amba/spi@e000d000"; spi1 = "/amba/spi@e0006000"; spi2 = "/amba/spi@e0007000"; }; memory { device_type = "memory"; reg = <0x0 0x10000000>; }; cpus {
dtb:
00000070: 2C 7A 79 6E 71 2D 37 30 30 30 00 00 00 00 00 01 ,zynq-7000...... 00000080: 63 68 6F 73 65 6E 00 00 00 00 00 03 00 00 00 16 chosen.......... 00000090: 00 00 00 26 63 6F 6E 73 6F 6C 65 3D 74 74 79 50 ...&console=ttyP 000000a0: 53 30 2C 31 31 35 32 30 30 00 00 00 00 00 00 02 S0,115200....... 000000b0: 00 00 00 01 61 6C 69 61 73 65 73 00 00 00 00 03 ....aliases..... 000000c0: 00 00 00 18 00 00 00 2F 2F 61 6D 62 61 2F 65 74 .......//amba/et 000000d0: 68 65 72 6E 65 74 40 65 30 30 30 62 30 30 30 00 hernet@e000b000. 000000e0: 00 00 00 03 00 00 00 16 00 00 00 39 2F 61 6D 62 ...........9/amb 000000f0: 61 2F 73 65 72 69 61 6C 40 65 30 30 30 31 30 30 a/serial@e000100 00000100: 30 00 00 00 00 00 00 03 00 00 00 16 00 00 00 41 0..............A 00000110: 2F 61 6D 62 61 2F 73 65 72 69 61 6C 40 65 30 30 /amba/serial@e00 00000120: 30 30 30 30 30 00 00 00 00 00 00 03 00 00 00 13 00000........... 00000130: 00 00 00 49 2F 61 6D 62 61 2F 73 70 69 40 65 30 ...I/amba/spi@e0 00000140: 30 30 64 30 30 30 00 00 00 00 00 03 00 00 00 13 00d000.......... 00000150: 00 00 00 4E 2F 61 6D 62 61 2F 73 70 69 40 65 30 ...N/amba/spi@e0 00000160: 30 30 36 30 30 30 00 00 00 00 00 03 00 00 00 13 006000.......... 00000170: 00 00 00 53 2F 61 6D 62 61 2F 73 70 69 40 65 30 ...S/amba/spi@e0 00000180: 30 30 37 30 30 30 00 00 00 00 00 02 00 00 00 01 007000.......... 00000190: 6D 65 6D 6F 72 79 00 00 00 00 00 03 00 00 00 07 memory.......... 000001a0: 00 00 00 58 6D 65 6D 6F 72 79 00 00 00 00 00 03 ...Xmemory...... 000001b0: 00 00 00 08 00 00 00 64 00 00 00 00 10 00 00 00 .......d........
首先0x00000001+63 68 6F 73 65 6E 00(chosen),表示chosen节点开始,00 00 00 03表示后面要跟一个属性,00 00 00 16表示val有22个字节,即console=ttyPS0,115200,00 00 00 26表示在string block的偏移地址,然后跟val,灾后00 00 00 00 02结束该节点。
存放设备树所有的属性名字,例如model、compatible等。
000021a0: 00 00 00 00 00 00 00 1F 00 00 00 02 00 00 00 00 ................ 000021b0: 00 00 00 20 00 00 00 02 00 00 00 02 00 00 00 02 ................ 000021c0: 00 00 00 02 00 00 00 09 23 61 64 64 72 65 73 73 ........#address 000021d0: 2D 63 65 6C 6C 73 00 23 73 69 7A 65 2D 63 65 6C -cells.#size-cel 000021e0: 6C 73 00 63 6F 6D 70 61 74 69 62 6C 65 00 62 6F ls.compatible.bo 000021f0: 6F 74 61 72 67 73 00 65 74 68 65 72 6E 65 74 30 otargs.ethernet0 00002200: 00 73 65 72 69 61 6C 30 00 73 65 72 69 61 6C 31 .serial0.serial1 00002210: 00 73 70 69 30 00 73 70 69 31 00 73 70 69 32 00 .spi0.spi1.spi2. 00002220: 64 65 76 69 63 65 5F 74 79 70 65 00 72 65 67 00 device_type.reg. 00002230: 63 6C 6F 63 6B 73 00 63 6C 6F 63 6B 2D 6C 61 74 clocks.clock-lat 00002240: 65 6E 63 79 00 63 70 75 30 2D 73 75 70 70 6C 79 ency.cpu0-supply 00002250: 00 6F 70 65 72 61 74 69 6E 67 2D 70 6F 69 6E 74 .operating-point 00002260: 73 00 69 6E 74 65 72 72 75 70 74 73 00 69 6E 74 s.interrupts.int 00002270: 65 72 72 75 70 74 2D 70 61 72 65 6E 74 00 72 65 errupt-parent.re 00002280: 67 75 6C 61 74 6F 72 2D 6E 61 6D 65 00 72 65 67 gulator-name.reg 00002290: 75 6C 61 74 6F 72 2D 6D 69 6E 2D 6D 69 63 72 6F ulator-min-micro
按照顺序存放字符,以0x00(字符‘.’)来区分单词
这个machine_desc主要是匹配根节点的compatible属性。这个结构体会描述和硬件相关的一些信息。
mdesc = setup_machine_fdt(__atags_pointer),__atags_pointer是uboot传入的参数存放的位置。
内核会根据根节点compatible中string的顺序去找合适的machine_desc,每个machine_desc是表明这个内核能支持哪些设备。例如arch/arm/mach_zynq/common.c
static const char * const zynq_dt_match[] = { "xlnx,zynq-7000", NULL }; DT_MACHINE_START(XILINX_EP107, "Xilinx Zynq Platform") /* 64KB way size, 8-way associativity, parity disabled */ .l2c_aux_val = 0x00400000, .l2c_aux_mask = 0xffbfffff, .smp = smp_ops(zynq_smp_ops), .map_io = zynq_map_io, .init_irq = zynq_irq_init, .init_machine = zynq_init_machine, .init_late = zynq_init_late, .init_time = zynq_timer_init, .dt_compat = zynq_dt_match, .reserve = zynq_memory_init, MACHINE_END
这个dt_compat表示能匹配到"xlnx,zynq-7000",表示本内核支持设备树根节点为"xlnx,zynq-7000"的设备。DT_MACHINE_START是一个__attribute__的宏,他将名字为__mach_desc_XILINX_EP107的结构体放到名为.arch.info.init段中。linux内核将所有用MACHINE_START定义的结构体都被组织到.arch.info.init段中,和内核模块注册的机制一样,同时定义了__arch_info_begin和__arch_info_end 两个空的machine_desc结构体对象,在链接脚本中会将.arch.info.init段链接在这两个变量中间,这样就能通过__arch_info_begin变量地址遍历到整个.arch.info.init段了
函数调用过程:
start_kernel -->setup_arch //如果设备树正确则使用设备树来设置machine -->mdesc = setup_machine_fdt(__atags_pointer); //找到最匹配的machine_desc并且返回 -->mdesc = of_flat_dt_match_machine(mdesc_best, arch_get_next_mach); -->get_next_compat //按顺序遍历.arch.info.init段的内容 -->arch_get_next_mach { static const struct machine_desc *mdesc = __arch_info_begin; const struct machine_desc *m = mdesc; if (m >= __arch_info_end) return NULL; mdesc++; *match = m->dt_compat; return m; } //如果设备树解析出错,则使用uboot传入的参数 -->mdesc = setup_machine_tags(__atags_pointer, __machine_arch_type);
设备树节点:
/memreserve/ 0x0000000000000000 0x0000000000010000;
/ {
compatible = "hisilicon,hi1910-asic", "hisilicon,mini";
hisi,boardid = <1 0 0 4>;
去内核源码中grep"hisilicon,hi1910-asic", “hisilicon,mini”;是找不到的,然后就跳进去看了下
start_kernel
-->setup_arch // arch/arm64/kernel/setup.c
//注意这里是没有返回值的
-->setup_machine_fdt(__fdt_pointer);
-->of_flat_dt_get_machine_name();//Clinux-4.19/drivers/of/fdt.c
{
const char *name;
//返回0
unsigned long dt_root = of_get_flat_dt_root();
name = of_get_flat_dt_prop(dt_root, "model", NULL);
if (!name)
name = of_get_flat_dt_prop(dt_root, "compatible", NULL);
return name;
}
这里直接找的是根节点下的model属性,找不到的话找compatible,然后pr_info(“Machine model: %s\n”, name);打印了一下信息,然后就没了,也没有machine_desc的返回值。
后面的一些内核和硬件架构相关的初始化代码好像也没有用到这个machine_desc,所以有没有这个machine_desc因该没有关系。
start_kernel -->setup_arch // arch/arm64/kernel/setup.c -->setup_machine_fdt(__fdt_pointer); -->early_init_dt_scan //检查设备树的魔数是否为D0 0D FE ED 00 00 25 40 //版本version是否正确,这样好像是早期的版本dtb文件分布不同 //将设备树的首地址传给initial_boot_params,后面经常用到 -->early_init_dt_verify //解析设备树的部分节点 //1.chosen节点下的bootargs节点 //2.{size,address}-cells节点 //3.memory,reg,linux,usable-memory等和内存相关的节点信息 -->early_init_dt_scan_nodes { of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line); of_scan_flat_dt(early_init_dt_scan_root, NULL); of_scan_flat_dt(early_init_dt_scan_memory, NULL); }
和内存初始化相关的一些操作,没有整明白,网上查了下大概是使用设备树中的memreserve节点的信息,将这个地方保留存放设备树,不要让内核使用。
start_kernel -->setup_arch // arch/arm64/kernel/setup.c -->unflatten_device_tree // initial_boot_params存放的是dtb文件的起始地址 of_root是device_node结构体,根节点会保存在里面 -->__unflatten_device_tree(initial_boot_params, NULL, &of_root, early_init_dt_alloc_memory_arch, false); //unflatten_dt_nodes第一次调用,计算出整个设备树需要多少个device_node结构体 //然后返回需要的大小size -->size = unflatten_dt_nodes(blob, NULL, dad, NULL); //根据上面的size申请内存 -->mem = dt_alloc(size + 4, __alignof__(struct device_node)); //unflatten_dt_nodes第二次调用,传入mem,真正的开始将dtb文件构造为device_node -->unflatten_dt_nodes(blob, mem, dad, mynodes); //遍历能用的节点,如果没有status则返回true //如果status属性为ok或者okay则返回true //否则返回false //按顺序遍历能用的节点 ...fdt_next_node()... -->of_fdt_device_is_available { const char *status = fdt_getprop(blob, node, "status", NULL); if (!status) return true; if (!strcmp(status, "ok") || !strcmp(status, "okay")) return true; return false; } -->populate_node { //获得节点名字,长度存在l pathp = fdt_get_name(blob, offset, &l); //申请内存,此时申请的内存为一个device_node结构体加上节点名字的长度 allocl = ++l; np = unflatten_dt_alloc(mem, sizeof(struct device_node) + allocl, __alignof__(struct device_node)); //这个full的指针指向的是sizeof(struct device_node)后面的节点名字 np->full_name = fn = ((char *)np) + sizeof(*np); //设置parent、sibling、child指针 if (dad != NULL) { np->parent = dad; np->sibling = dad->child; dad->child = np; } //构造本届点device_node中的properties //properties结构体存放属性名、值,通过next指针串成链表 populate_properties(blob, offset, mem, np, pathp, dryrun); //如果有name和device_type则设置device_node的name和device_type,没有则为null np->name = of_get_property(np, "name", NULL); np->type = of_get_property(np, "device_type", NULL); if (!np->name) np->name = "<NULL>"; if (!np->type) np->type = "<NULL>"; }
device_node结构体内容如下所示:
struct device_node { const char *name; const char *type; phandle phandle; const char *full_name; struct fwnode_handle fwnode; struct property *properties; struct property *deadprops; /* removed properties */ struct device_node *parent; struct device_node *child; struct device_node *sibling; #if defined(CONFIG_OF_KOBJ) struct kobject kobj; #endif unsigned long _flags; void *data; #if defined(CONFIG_SPARC) const char *path_component_name; unsigned int unique_id; struct of_irq_controller *irq_trans; #endif };
跟节点下每一个大括号都会被转化为1个device_node结构体:
property 结构体:
struct property {
char *name;
int length;
void *value;
struct property *next;
#if defined(CONFIG_OF_DYNAMIC) || defined(CONFIG_SPARC)
unsigned long _flags;
#endif
#if defined(CONFIG_OF_PROMTREE)
unsigned int unique_id;
#endif
#if defined(CONFIG_OF_KOBJ)
struct bin_attribute attr;
#endif
};
start_kernel
-->rest_init
-->pid = kernel_thread(kernel_init, NULL, CLONE_FS);
-->kernel_init
-->kernel_init_freeable
-->do_basic_setup
-->do_initcalls
{
//内核initcall机
for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
do_initcall_level(level);
}
//drivers/of/platform.c,内核initcall机制,会放到指定的段中按顺序指向段内的函数
arch_initcall_sync(of_platform_default_populate_init);
#define arch_initcall_sync(fn) __define_initcall(fn, 3s)
of_platform_default_populate_init //处理特殊的节点,没用过 -->for_each_matching_node(node, reserved_mem_matches) -->of_platform_device_create(node, NULL, NULL); -->node = of_find_node_by_path("/firmware"); //处理除上以外所有的节点 -->of_platform_default_populate //of_default_bus_match_table表中的子节点也能被转化为platform_device const struct of_device_id of_default_bus_match_table[] = { { .compatible = "simple-bus", }, { .compatible = "simple-mfd", }, { .compatible = "isa", }, #ifdef CONFIG_ARM_AMBA { .compatible = "arm,amba-bus", }, #endif /* CONFIG_ARM_AMBA */ {} /* Empty terminated list */ }; -->of_platform_populate(root, of_default_bus_match_table, lookup, parent); { //获取根节点 root = root ? of_node_get(root) : of_find_node_by_path("/"); { //获取根节点的子节点 for_each_child_of_node(root, child) { //对每个子结点执行这个函数 //默认根节点下的子节点都是总线 rc = of_platform_bus_create(child, matches, lookup, parent, true); //开始创建platform_device -->of_platform_device_create_pdata //将io、中断和reg属性转化为resources资源。 //里面还会处理dev结构体的其他内容,都是根据device_node来的 -->of_device_alloc //如果该节点的compatible不含有of_default_bus_match_table里的内容,则返回 //否则处理它的子节点 if (!dev || !of_match_node(matches, bus)) return 0; } } }
platform_device 结构体如下:
struct platform_device { const char *name; int id; bool id_auto; struct device dev; u32 num_resources; struct resource *resource; const struct platform_device_id *id_entry; char *driver_override; /* Driver name to force a match */ /* MFD cell pointer */ struct mfd_cell *mfd_cell; /* arch specific additions */ struct pdev_archdata archdata; };
//drivers/base/platform.c static int platform_match(struct device *dev, struct device_driver *drv) { struct platform_device *pdev = to_platform_device(dev); struct platform_driver *pdrv = to_platform_driver(drv); /* When driver_override is set, only bind to the matching driver */ if (pdev->driver_override) return !strcmp(pdev->driver_override, drv->name); /* Attempt an OF style match first */ if (of_driver_match_device(dev, drv)) return 1; /* Then try ACPI style match */ if (acpi_driver_match_device(dev, drv)) return 1; /* Then try to match against the id table */ if (pdrv->id_table) return platform_match_id(pdrv->id_table, pdev) != NULL; /* fall-back to driver name match */ return (strcmp(pdev->name, drv->name) == 0); }
platform总线和设备匹配优先级:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。