import RT-Thread@9217865c without bsp, libcpu and components/net

This commit is contained in:
Zihao Yu 2023-05-20 16:23:33 +08:00
commit e2376a3709
1414 changed files with 390370 additions and 0 deletions

View file

@ -0,0 +1,41 @@
# fdt
## 1、介绍
fdt基于libfdt进行封装可实现在内存或文件系统中加载dtb设备树对内存中的设备树修改、解析并转换为设备节点树通过该节点树开发者可通过设备树信息开发驱动。
### 1.1 目录结构
| 名称 | 说明 |
| ---- | ---- |
| docs | 文档目录 |
| examples | 例子目录,并有相应的一些说明 |
| inc | 头文件目录 |
| src | 源代码目录 |
### 1.2 许可证
fdt package 遵循 GPL-3.0 许可,详见 LICENSE 文件。
### 1.3 依赖
- RT-Thread 3.0+
## 2、如何打开 fdt
使用 fdt package 需要在 RT-Thread 的包管理器中选择它,具体路径如下:
```
RT-Thread online packages
tools packages --->
[*] Device Tree package in RT-Thread
```
## 3、使用 fdt
在打开 fdt package 后,当进行 bsp 编译时,它会被加入到 bsp 工程中进行编译。
* 完整的 API 手册可以访问这个[链接](docs/api.md)
* 更多文档位于 [`/docs`](/docs) 下,使用前 **务必查看**
## 4、注意事项
如果发生`libfdt`库冲突在package管理菜单中取消选择`Enable libfdt`
## 5、联系方式
* 维护GuEe-GUI
* 主页https://github.com/GuEe-GUI/fdt

View file

@ -0,0 +1,16 @@
# RT-Thread building script for bridge
import os
from building import *
cwd = GetCurrentDir()
objs = []
list = os.listdir(cwd)
if GetDepend('RT_USING_FDT'):
for d in list:
path = os.path.join(cwd, d)
if os.path.isfile(os.path.join(path, 'SConscript')):
objs = objs + SConscript(os.path.join(d, 'SConscript'))
Return('objs')

View file

@ -0,0 +1,13 @@
# 文档
## 软件包地址
- https://gitee.com/GuEe_GUI/fdt
## 文档列表
|文件名 |描述|
|:----- |:----|
|[examples.md](examples.md) |示例程序|
|[version.md](version.md) |版本信息|
|[api.md](api.md) |API 说明|

View file

@ -0,0 +1,434 @@
# fdt load API
## 从文件系统上读取设备树
```c
void *fdt_load_from_fs(char *dtb_filename)
```
| 参数 | 描述 |
|:------------------|:------------------------------------|
|dtb_filename | 设备树文件名 |
| **返回** | **描述** |
|void* | 设备树在内存上的地址 |
## 从内存上读取设备树
```c
void *fdt_load_from_memory(void *dtb_ptr, rt_bool_t is_clone)
```
| 参数 | 描述 |
|:------------------|:------------------------------------|
|dtb_ptr | 设备树在内存上的内存地址 |
|is_clone | 是否克隆到新的内存区域 |
| **返回** | **描述** |
|void* | 设备树在内存上的地址 |
# fdt set API
## 设置Linux启动参数
```c
rt_size_t fdt_set_linux_cmdline(void *fdt, char *cmdline)
```
| 参数 | 描述 |
|:------------------|:------------------------------------|
|fdt | 设备树指针 |
|cmdline | 启动参数 |
| **返回** | **描述** |
|rt_size_t | 修改设备树后设备树的大小 |
## 设置Linux init ramdisk
```c
rt_size_t fdt_set_linux_initrd(void *fdt, rt_uint64_t initrd_addr, rt_size_t initrd_size)
```
| 参数 | 描述 |
|:------------------|:------------------------------------|
|fdt | 设备树指针 |
|initrd_addr | init ramdisk 内存地址 |
|initrd_size | init 大小 |
| **返回** | **描述** |
|rt_size_t | 修改设备树后设备树的大小 |
## 设置节点属性值
```c
rt_size_t fdt_set_dtb_property(void *fdt, char *pathname, char *property_name, rt_uint32_t *cells, rt_size_t cells_size);
```
| 参数 | 描述 |
|:------------------|:------------------------------------|
|fdt | 设备树指针 |
|pathname | 节点路径 |
|property_name | 属性名称 |
|cells | 属性值地址 |
|cells_size | 属性值长度 |
| **返回** | **描述** |
|rt_size_t | 修改设备树后设备树的大小 |
## 添加保留内存
```c
rt_size_t fdt_add_dtb_memreserve(void *fdt, rt_uint64_t address, rt_uint64_t size)
```
| 参数 | 描述 |
|:------------------|:------------------------------------|
|fdt | 设备树指针 |
|address | 保留内存起始地址 |
|size | 保留内存大小 |
| **返回** | **描述** |
|rt_size_t | 修改设备树后设备树的大小 |
## 删除保留内存
```c
rt_size_t fdt_del_dtb_memreserve(void *fdt, rt_uint64_t address)
```
| 参数 | 描述 |
|:------------------|:------------------------------------|
|fdt | 设备树指针 |
|address | 保留内存起始地址 |
| **返回** | **描述** |
|rt_size_t | 修改设备树后设备树的大小 |
# fdt get API
## 获取设备树软件包执行状态
```c
rt_err_t fdt_get_exec_status()
```
| 参数 | 描述 |
|:------------------|:------------------------------------|
|无 | 无参数 |
| **返回** | **描述** |
|FDT_RET_NO_MEMORY | 内存不足 |
|FDT_RET_NO_LOADED | 设备树还未加载进内存 |
|FDT_RET_GET_OK | 执行成功 |
|FDT_RET_GET_EMPTY | 读取数据为空 |
## 将原始设备树转换为设备节点树
```c
struct dtb_node *fdt_get_dtb_list(void *fdt)
```
| 参数 | 描述 |
|:------------------|:------------------------------------|
|fdt | 设备树在内存上的地址 |
| **返回** | **描述** |
|struct dtb_node * | 设备节点树头指针 |
## 释放设备节点树内存
```c
void fdt_free_dtb_list(struct dtb_node *dtb_node_head)
```
| 参数 | 描述 |
|:------------------|:------------------------------------|
|dtb_node_head | 设备节点树头节点 |
| **返回** | **描述** |
|无返回值 | 无描述 |
示例:加载设备树
```c
#include <rtthread.h>
#include <fdt.h>
int main()
{
void *fdt = fdt_load_from_fs("sample.dtb");
if (fdt != RT_NULL)
{
struct dtb_node *dtb_node_list = fdt_get_dtb_list(fdt);
if (dtb_node_list != RT_NULL)
{
/* load dtb node list OK */
/* free dtb_node_list here */
fdt_free_dtb_list(dtb_node_list);
}
rt_free(fdt);
}
return 0;
}
```
## 打印设备树信息
```c
void fdt_get_dts_dump(struct dtb_node *dtb_node_head)
```
| 参数 | 描述 |
|:------------------|:------------------------------------|
|dtb_node_head | 设备节点树头节点 |
| **返回** | **描述** |
|无返回值 | 无描述 |
## 遍历设备节点树并使用程序定义的回调函数
```c
void fdt_get_enum_dtb_node(struct dtb_node *dtb_node_head, void (*callback(struct dtb_node *dtb_node)))
```
| 参数 | 描述 |
|:------------------|:------------------------------------|
|dtb_node_head | 设备节点树头节点 |
|callback | 程序定义的回调函数 |
| **返回** | **描述** |
|无返回值 | 无描述 |
示例:遍历设备树节点,并打印每个节点名称
```c
#include <rtthread.h>
#include <fdt.h>
void callback(struct dtb_node *node)
{
rt_kprintf("this node's name is %s\n", node->name);
}
int main()
{
/* loaded dtb_node */
extern struct dtb_node *dtb_node_list;
if (dtb_node_list != RT_NULL)
{
fdt_get_enum_dtb_node(dtb_node_list, callback);
}
return 0;
}
```
## 通过节点名称查找节点
```c
struct dtb_node *fdt_get_dtb_node_by_name_DFS(struct dtb_node *dtb_node, const char *nodename)
```
```c
struct dtb_node *fdt_get_dtb_node_by_name_BFS(struct dtb_node *dtb_node, const char *nodename)
```
| 参数 | 描述 |
|:------------------|:------------------------------------|
|dtb_node | 设备节点树节点 |
|nodename | 查找节点的名称 |
| **返回** | **描述** |
|struct dtb_node * | 返回查找的节点 |
|RT_NULL | 未找到为RT_NULL |
## 通过节点路径查找节点
```c
struct dtb_node *fdt_get_dtb_node_by_path(struct dtb_node *dtb_node, const char *pathname)
```
| 参数 | 描述 |
|:------------------|:------------------------------------|
|dtb_node | 设备节点树节点 |
|pathname | 查找节点的路径 |
| **返回** | **描述** |
|struct dtb_node * | 返回查找的节点 |
|RT_NULL | 未找到为RT_NULL |
## 通过节点phandle值查找节点
```c
struct dtb_node *fdt_get_dtb_node_by_phandle_DFS(struct dtb_node *dtb_node, phandle handle)
```
```c
struct dtb_node *fdt_get_dtb_node_by_phandle_BFS(struct dtb_node *dtb_node, phandle handle)
```
| 参数 | 描述 |
|:------------------|:------------------------------------|
|dtb_node | 设备节点树节点 |
|handle | 查找节点的phandle值 |
| **返回** | **描述** |
|struct dtb_node * | 返回查找的节点 |
|RT_NULL | 未找到为RT_NULL |
DFS和BFS是用于区分不同深度节点的搜索方法时间复杂度和空间复杂度都较高建议使用路径查找。
## 读取节点属性值的单位
```c
void fdt_get_dtb_node_cells(struct dtb_node *dtb_node, int *addr_cells, int *size_cells)
```
| 参数 | 描述 |
|:------------------|:------------------------------------|
|dtb_node | 设备节点树节点 |
|addr_cells | 返回的地址块的单位u32大小 |
|size_cells | 返回的尺寸块的单位u32大小 |
| **返回** | **描述** |
|无返回值 | 无描述 |
## 读取节点属性值
```c
void *fdt_get_dtb_node_property(struct dtb_node *dtb_node, const char *property_name, rt_size_t *property_size)
```
| 参数 | 描述 |
|:------------------|:------------------------------------|
|dtb_node | 设备节点树节点 |
|property_name | 属性名称 |
|property_size | 属性大小 |
| **返回** | **描述** |
|void * | 无描述 |
|RT_NULL | 该设备树没有该属性 |
读取的值为在设备树中存储的值CPU小端模式下可能需要使用其他API进行转换才是正确的值
## 读取预留内存信息
```c
struct dtb_memreserve *fdt_get_dtb_memreserve(struct dtb_node *dtb_node, int *memreserve_size)
```
| 参数 | 描述 |
|:------------------|:------------------------------------|
|dtb_node | 设备节点树节点 |
|memreserve_size | 返回的内存信息数组长度 |
| **返回** | **描述** |
|struct dtb_memreserve *| 内存预留信息数组的内存地址 |
|RT_NULL | 该设备树没有预留内存信息 |
## 读取节点的状态
```c
rt_bool_t fdt_get_dtb_node_status(struct dtb_node *dtb_node)
```
| 参数 | 描述 |
|:------------------|:------------------------------------|
|dtb_node | 设备节点树节点 |
| **返回** | **描述** |
|RT_FALSE | 状态为禁用 |
|RT_TRUE | 状态为使用 |
## 用于参数为字符串列表的函数
```c
fdt_string_list(string, ...)
```
| 参数 | 描述 |
|:------------------|:------------------------------------|
|string | 字符串集合 |
## 读取节点compatible标志匹配
```c
rt_bool_t fdt_get_dtb_node_compatible_match(struct dtb_node *dtb_node, char **compatibles)
```
| 参数 | 描述 |
|:------------------|:------------------------------------|
|dtb_node | 设备节点树节点 |
|compatibles | 所有要匹配的字符串列表 |
| **返回** | **描述** |
|RT_FALSE | 匹配失败 |
|RT_TRUE | 匹配成功 |
## 读取属性值中的字符串
```c
char *fdt_get_dtb_string_list_value(void *value, int size, int index)
```
| 参数 | 描述 |
|:------------------|:------------------------------------|
|value | 节点属性的内存地址 |
|size | 节点属性的尺寸 |
|index | 要读取字符串的索引 |
| **返回** | **描述** |
|char * | 字符串的内存地址 |
|RT_NULL | 该索引没有字符串 |
## 读取值为字符串属性下一个字符串
```c
char *fdt_get_dtb_string_list_value_next(void *value, void *end)
```
| 参数 | 描述 |
|:------------------|:------------------------------------|
|value | 节点属性的内存地址 |
|end | 节点属性的结束地址 |
| **返回** | **描述** |
|char * | 字符串的内存地址 |
|RT_NULL | 没有下一个字符串 |
## 读取值为u32属性的值
```c
rt_uint32_t fdt_get_dtb_cell_value(void *value)
```
| 参数 | 描述 |
|:------------------|:------------------------------------|
|value | 节点属性的内存地址 |
| **返回** | **描述** |
|rt_uint32_t | 该值小端的值 |
## 读取值为u8属性的值
```c
rt_uint8_t fdt_get_dtb_byte_value(void *value)
```
| 参数 | 描述 |
|:------------------|:------------------------------------|
|value | 节点属性的内存地址 |
| **返回** | **描述** |
|rt_uint8_t | 该值小端的值 |
# fdt foreach API
## 遍历属性中字符串宏
```c
for_each_property_string(node_ptr, property_name, str, size)
```
| 参数 | 描述 |
|:------------------|:------------------------------------|
|node_ptr | 设备节点树节点 |
|property_name | 节点属性名称 |
|str | 每次遍历到的字符串 |
|size | 用于保存节点属性的尺寸 |
## 遍历属性中u32宏
```c
for_each_property_cell(node_ptr, property_name, value, list, size)
```
| 参数 | 描述 |
|:------------------|:------------------------------------|
|node_ptr | 设备节点树节点 |
|property_name | 节点属性名称 |
|value | 每次遍历到的值 |
|list | 用于保存节点属性的值内存地址 |
|size | 用于保存节点属性的尺寸 |
## 遍历属性中u8宏
```c
for_each_property_byte(node_ptr, property_name, value, list, size)
```
| 参数 | 描述 |
|:------------------|:------------------------------------|
|node_ptr | 设备节点树节点 |
|property_name | 节点属性名称 |
|value | 每次遍历到的值 |
|list | 用于保存节点属性的值内存地址 |
|size | 用于保存节点属性的尺寸 |
## 遍历子节点宏
```c
for_each_node_child(node_ptr)
```
| 参数 | 描述 |
|:------------------|:------------------------------------|
|node_ptr | 设备节点树节点 |
## 遍历兄弟节点宏
```c
for_each_node_sibling(node_ptr)
```
| 参数 | 描述 |
|:------------------|:------------------------------------|
|node_ptr | 设备节点树节点 |

View file

@ -0,0 +1,81 @@
# fdt示例 #
`examples`文件夹中存放`bcm2711-rpi-4-b.dtb``vexpress-v2p-ca9.dtb`可供测试如果系统可以从bootloader或其他方式获取到bsp本身的dtb也可以通过修改示例程序进行测试
## fdt_dump
```bash
fdt_dump vexpress-v2p-ca9.dtb
```
#### 示例结果 ####
```bash
/dts-v1/;
/ {
model = "V2P-CA9";
arm,hbi = <0x191>;
arm,vexpress,site = <0xf>;
compatible = "arm,vexpress,v2p-ca9", "arm,vexpress";
interrupt-parent = <0x1>;
#address-cells = <0x1>;
#size-cells = <0x1>;
chosen {
};
aliases {
serial0 = "/smb@4000000/motherboard/iofpga@7,00000000/uart@9000";
serial1 = "/smb@4000000/motherboard/iofpga@7,00000000/uart@a000";
serial2 = "/smb@4000000/motherboard/iofpga@7,00000000/uart@b000";
serial3 = "/smb@4000000/motherboard/iofpga@7,00000000/uart@c000";
i2c0 = "/smb@4000000/motherboard/iofpga@7,00000000/i2c@16000";
i2c1 = "/smb@4000000/motherboard/iofpga@7,00000000/i2c@2000";
};
...... 省略
hsb@e0000000 {
compatible = "simple-bus";
#address-cells = <0x1>;
#size-cells = <0x1>;
ranges = <0x0 0xe0000000 0x20000000>;
#interrupt-cells = <0x1>;
interrupt-map-mask = <0x0 0x3>;
interrupt-map = <0x0 0x0 0x1 0x0 0x24 0x4 0x0 0x1 0x1 0x0 0x25 0x4 0x0 0x2 0x1 0x0 0x26 0x4 0x0 0x3 0x1 0x0 0x27 0x4>;
};
};
```
## fdt_test
```bash
fdt_test
```
#### 示例结果 ####
```bash
name = uart@9000
reg = <0x9000,0x1000>;
compatible = "arm,pl011","arm,primecell";
name = cpus
path = /cpus/cpu@0/
path = /cpus/cpu@1/
path = /cpus/cpu@2/
path = /cpus/cpu@3/
name = user1, lable = v2m:green:user1
name = user2, lable = v2m:green:user2
name = user3, lable = v2m:green:user3
name = user4, lable = v2m:green:user4
name = user5, lable = v2m:green:user5
name = user6, lable = v2m:green:user6
name = user7, lable = v2m:green:user7
name = user8, lable = v2m:green:user8
/memreserve/ 0x0000000000000000 0x0000000000001000;
phandle = <0x9>
name = bt_pins
path = /soc/gpio@7e200000/bt_pins/
brcm,pins = [2d 00]
```

View file

@ -0,0 +1,7 @@
# 版本和修订 #
| Date | Version | Author | Note |
| -------- | :-----: | :---- | :---- |
| 2021-9-1 | v0.0.1 | GuEe-GUI | 初始版本 |
| 2021-11-11 | v1.0.0 | GuEe-GUI | API确定版本 |
| | | | |

View file

@ -0,0 +1,10 @@
from building import *
cwd = GetCurrentDir()
src = Glob('*.c')
CPPPATH = [cwd]
group = DefineGroup('FDT', src, depend = ['FDT_USING_DEBUG'], CPPPATH = CPPPATH)
Return('group')

View file

@ -0,0 +1,143 @@
#include <rtthread.h>
#include "dtb_node.h"
int dtb_test()
{
void *fdt;
if ((fdt = dtb_node_load_from_fs("vexpress-v2p-ca9.dtb")) != RT_NULL)
{
struct dtb_node *dtb_node_list = dtb_node_get_dtb_list(fdt);
if (dtb_node_list != RT_NULL)
{
struct dtb_node *serial0 = dtb_node_get_dtb_node_by_path(dtb_node_list, "/smb@4000000/motherboard/iofpga@7,00000000/uart@9000");
struct dtb_node *cpu = dtb_node_get_dtb_node_by_path(dtb_node_list, "/cpus");
struct dtb_node *user1 = dtb_node_get_dtb_node_by_path(dtb_node_list, "/smb@4000000/motherboard/leds/user1");
if (serial0 != RT_NULL)
{
int property_size;
rt_uint32_t u32_value;
rt_uint32_t *u32_ptr;
char *str_ptr;
rt_kprintf("name = %s\n", serial0->name);
rt_kputs("reg = <");
for_each_property_cell(serial0, "reg", u32_value, u32_ptr, property_size)
{
rt_kprintf("0x%x,", u32_value);
}
rt_kputs("\b>;\n");
rt_kputs("compatible = ");
for_each_property_string(serial0, "compatible", str_ptr, property_size)
{
rt_kprintf("\"%s\",", str_ptr);
}
rt_kputs("\b;\n");
}
if (cpu != RT_NULL)
{
struct dtb_node *cpu_child = cpu;
rt_kprintf("\nname = %s\n", cpu->name);
for_each_node_child(cpu_child)
{
rt_kprintf("path = %s\n", cpu_child->path);
}
}
if (user1 != RT_NULL)
{
struct dtb_node *user = user1;
rt_kprintf("\nname = %s, lable = %s\n", user1->name, dtb_node_get_dtb_node_property(user1, "label", RT_NULL));
for_each_node_sibling(user)
{
rt_kprintf("name = %s, lable = %s\n", user->name, dtb_node_get_dtb_node_property(user, "label", RT_NULL));
}
}
}
dtb_node_free_dtb_list(dtb_node_list);
}
if ((fdt = dtb_node_load_from_fs("bcm2711-rpi-4-b.dtb")) != RT_NULL)
{
struct dtb_node *dtb_node_list = dtb_node_get_dtb_list(fdt);
if (dtb_node_list != RT_NULL)
{
struct dtb_node *bt_pins;
int memreserve_size;
struct dtb_memreserve *memreserve;
bt_pins = dtb_node_get_dtb_node_by_name_DFS(dtb_node_list, "bt_pins");
bt_pins = dtb_node_get_dtb_node_by_name_BFS(dtb_node_list, bt_pins->name);
bt_pins = dtb_node_get_dtb_node_by_phandle_DFS(dtb_node_list, bt_pins->handle);
bt_pins = dtb_node_get_dtb_node_by_phandle_BFS(dtb_node_list, bt_pins->handle);
memreserve = dtb_node_get_dtb_memreserve(bt_pins, &memreserve_size);
if (memreserve_size > 0)
{
int i;
for (i = 0; i < memreserve_size; ++i)
{
rt_kputs("\n/memreserve/\t");
if (IN_64BITS_MODE)
{
rt_kprintf("0x%016x 0x%016x;", memreserve[i].address, memreserve[i].size);
}
else
{
rt_kprintf("0x%08x%08x 0x%08x%08x;", memreserve[i].address, memreserve[i].size);
}
}
}
if (bt_pins != RT_NULL)
{
int property_size;
rt_uint8_t u8_value;
rt_uint8_t *u8_ptr;
rt_kprintf("\n\nphandle = <0x%x>\n", bt_pins->handle);
rt_kprintf("name = %s\n", bt_pins->name);
rt_kprintf("path = %s\n", bt_pins->path);
rt_kputs("brcm,pins = [");
for_each_property_byte(bt_pins, "brcm,pins", u8_value, u8_ptr, property_size)
{
rt_kprintf("%02x ", u8_value);
}
rt_kputs("\b]\n");
}
}
dtb_node_free_dtb_list(dtb_node_list);
}
return 0;
}
MSH_CMD_EXPORT(dtb_test, dtb API test);
int dtb_dump(int argc, char** argv)
{
struct dtb_node *dtb_root_node = get_dtb_node_head();
if(argc == 1)
{
if (dtb_root_node != RT_NULL)
{
dtb_node_get_dts_dump(dtb_root_node);
}
}
else
{
rt_kprintf("invailed parameter\n");
}
return 0;
}
MSH_CMD_EXPORT(dtb_dump, dtb dump from mem);

View file

@ -0,0 +1,381 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _DTB_NODE_H__
#define _DTB_NODE_H__
#include "libfdt_env.h"
#include <rtthread.h>
#include <stdint.h>
//#define RT_DTB_DEBUG
#ifdef RT_DTB_DEBUG
#define debug(fmt, args...) rt_kprintf(fmt, ##args)
#else
#define debug(fmt, args...)
#endif
#define ERR_PTR(err) ((void *)((long)(err)))
#define PTR_ERR(ptr) ((long)(ptr))
#define IS_ERR(ptr) ((unsigned long)(ptr) > (unsigned long)(-1000))
#define DEV_ROOT_NODE_ADDR_CELLS_DEFAULT 2
#define DEV_ROOT_NODE_SIZE_CELLS_DEFAULT 1
/* will be optimized to only u64 or u32 by gcc */
#define IN_64BITS_MODE (sizeof(void *) == 8)
#define FDT_ROOT_ADDR_CELLS_DEFAULT 1
#define FDT_ROOT_SIZE_CELLS_DEFAULT 1
#define FDT_DTB_ALL_NODES_PATH_SIZE (32 * 1024)
#define FDT_DTB_PAD_SIZE 1024
#define FDT_RET_NO_MEMORY 2
#define FDT_RET_NO_LOADED 1
#define FDT_RET_GET_OK 0
#define FDT_RET_GET_EMPTY (-1)
typedef uint32_t phandle;
struct dtb_memreserve
{
uintptr_t address;
size_t size;
};
struct dtb_header
{
char root, zero; /* "/" */
struct dtb_memreserve *memreserve;
size_t memreserve_sz;
};
struct dtb_property
{
const char *name;
int size;
void *value;
struct dtb_property *next;
};
struct dtb_node
{
union
{
const char *name;
const struct dtb_header *header;
};
const char *path;
phandle handle;
struct dtb_property *properties;
struct dtb_node *parent;
struct dtb_node *child;
struct dtb_node *sibling;
};
#define FDT_MAX_PHANDLE_ARGS 16
/**
* struct dtb_node_phandle_args - structure to hold phandle and arguments
*
* This is used when decoding a phandle in a device tree property. Typically
* these look like this:
*
* wibble {
* phandle = <5>;
* };
*
* ...
* some-prop = <&wibble 1 2 3>
*
* Here &node is the phandle of the node 'wibble', i.e. 5. There are three
* arguments: 1, 2, 3.
*
* So when decoding the phandle in some-prop, np will point to wibble,
* args_count will be 3 and the three arguments will be in args.
*
* @np: Node that the phandle refers to
* @args_count: Number of arguments
* @args: Argument values
*/
struct fdt_phandle_args
{
struct dtb_node *np;
int args_count;
uint32_t args[FDT_MAX_PHANDLE_ARGS];
};
/*
* A single signal can be specified via a range of minimal and maximal values
* with a typical value, that lies somewhere inbetween.
*/
struct timing_entry
{
uint32_t min;
uint32_t typ;
uint32_t max;
};
void *get_fdt_blob(void);
struct dtb_node *get_dtb_node_head(void);
rt_bool_t dtb_node_active(void);
int device_tree_setup(void *mem_addr);
void *dtb_node_load_from_fs(char *dtb_filename);
void *dtb_node_load_from_memory(void *dtb_ptr, rt_bool_t is_clone);
size_t dtb_node_set_linux_cmdline(void *fdt, char *cmdline);
size_t dtb_node_set_linux_initrd(void *fdt, uint64_t initrd_addr, size_t initrd_size);
size_t dtb_node_set_dtb_property(void *fdt, char *pathname, char *property_name, uint32_t *cells, size_t cells_size);
size_t dtb_node_add_dtb_memreserve(void *fdt, uint64_t address, uint64_t size);
size_t dtb_node_del_dtb_memreserve(void *fdt, uint64_t address);
int dtb_node_get_exec_status();
struct dtb_node *dtb_node_get_dtb_list(void *fdt);
void dtb_node_free_dtb_list(struct dtb_node *dtb_node_head);
void dtb_node_get_dts_dump(struct dtb_node *dtb_node_head);
void dtb_node_get_enum_dtb_node(struct dtb_node *dtb_node_head, void(callback(struct dtb_node *dtb_node)));
struct dtb_node *dtb_node_get_dtb_node_by_name_DFS(struct dtb_node *dtb_node, const char *nodename);
struct dtb_node *dtb_node_get_dtb_node_by_name_BFS(struct dtb_node *dtb_node, const char *nodename);
struct dtb_node *dtb_node_get_dtb_node_by_path(struct dtb_node *dtb_node, const char *pathname);
struct dtb_node *dtb_node_get_dtb_node_by_phandle_DFS(struct dtb_node *dtb_node, phandle handle);
struct dtb_node *dtb_node_get_dtb_node_by_phandle_BFS(struct dtb_node *dtb_node, phandle handle);
void dtb_node_get_dtb_node_cells(struct dtb_node *dtb_node, int *addr_cells, int *size_cells);
struct dtb_memreserve *dtb_node_get_dtb_memreserve(struct dtb_node *dtb_node, int *memreserve_size);
uint8_t dtb_node_get_dtb_byte_value(void *value);
char *dtb_node_get_dtb_string_list_value(void *value, int size, int index);
char *dtb_node_get_dtb_string_list_value_next(void *value, void *end);
uint32_t dtb_node_get_dtb_cell_value(void *value);
rt_bool_t dtb_node_get_dtb_node_status(const struct dtb_node *dtb_node);
rt_bool_t dtb_node_get_dtb_node_compatible_match(const struct dtb_node *dtb_node, const char *compatibles);
/*dtb_node_access.c */
int dtb_node_read_u32(const struct dtb_node *dn, const char *propname, uint32_t *outp);
uint32_t dtb_node_read_u32_default(const struct dtb_node *node, const char *propname, uint32_t def);
int dtb_node_read_u32_index(const struct dtb_node *node, const char *propname, int index,
uint32_t *outp);
uint32_t dtb_node_read_u32_index_default(const struct dtb_node *node, const char *propname, int index,
uint32_t def);
int dtb_node_read_u32_array(const struct dtb_node *dn, const char *propname,
uint32_t *out_values, size_t sz);
int dtb_node_read_u32_index(const struct dtb_node *dn, const char *propname,
int index, uint32_t *outp);
int dtb_node_read_s32_default(const struct dtb_node *node, const char *propname, int32_t def);
int dtb_node_read_u64(const struct dtb_node *dn, const char *propname, uint64_t *outp);
uint64_t dtb_node_read_u64_default(const struct dtb_node *node, const char *propname, uint64_t def);
int dtb_node_n_addr_cells(const struct dtb_node *dn);
int dtb_node_n_size_cells(const struct dtb_node *dn);
int dtb_node_simple_addr_cells(const struct dtb_node *np);
int dtb_node_simple_size_cells(const struct dtb_node *np);
struct dtb_node *dtb_node_find_all_nodes(const struct dtb_node *prev);
struct dtb_node *dtb_node_find_node_by_phandle(phandle handle);
struct dtb_node *dtb_node_find_compatible_node(struct dtb_node *from, const char *compatible);
void *dtb_node_get_dtb_node_property_value(const struct dtb_node *dtb_node, const char *property_name, int *property_size);
struct dtb_property *dtb_node_get_dtb_node_property(const struct dtb_node *dtb_node, const char *property_name, int *property_size);
const struct dtb_node *dtb_node_find_node_by_prop_value(struct dtb_node *from, const char *propname, const void *propval, int proplen);
struct dtb_node *dtb_node_find_node_opts_by_path(const char *path,
const char **opts);
static inline struct dtb_node *dtb_node_find_node_by_path(const char *path)
{
return dtb_node_find_node_opts_by_path(path, NULL);
}
rt_bool_t dtb_node_device_is_available(const struct dtb_node *device);
struct dtb_node *dtb_node_get_parent(const struct dtb_node *node);
int dtb_node_property_match_string(const struct dtb_node *dn, const char *propname,
const char *string);
int dtb_node_property_read_string_helper(const struct dtb_node *dn,
const char *propname, const char **out_strs,
size_t sz, int skip);
/**
* of_property_read_string_index() - Find and read a string from a multiple
* strings property.
* @np: device node from which the property value is to be read.
* @propname: name of the property to be searched.
* @index: index of the string in the list of strings
* @out_string: pointer to null terminated return string, modified only if
* return value is 0.
*
* Search for a property in a device tree node and retrieve a null
* terminated string value (pointer to data, not a copy) in the list of strings
* contained in that property.
* Returns 0 on success, -EINVAL if the property does not exist, -ENODATA if
* property does not have a value, and -EILSEQ if the string is not
* null-terminated within the length of the property data.
*
* The out_string pointer is modified only if a valid string can be decoded.
*/
static inline int dtb_node_property_read_string_index(const struct dtb_node *dn,
const char *propname,
int index, const char **output)
{
int rc = dtb_node_property_read_string_helper(dn, propname, output, 1, index);
return rc < 0 ? rc : 0;
}
/**
* of_property_count_strings() - Find and return the number of strings from a
* multiple strings property.
* @np: device node from which the property value is to be read.
* @propname: name of the property to be searched.
*
* Search for a property in a device tree node and retrieve the number of null
* terminated string contain in it. Returns the number of strings on
* success, -EINVAL if the property does not exist, -ENODATA if property
* does not have a value, and -EILSEQ if the string is not null-terminated
* within the length of the property data.
*/
static inline int dtb_node_property_count_strings(const struct dtb_node *dn,
const char *propname)
{
return dtb_node_property_read_string_helper(dn, propname, NULL, 0, 0);
}
struct dtb_node *dtb_node_parse_phandle(const struct dtb_node *dn,
const char *phandle_name, int index);
int dtb_node_parse_phandle_with_args(const struct dtb_node *dn,
const char *list_name, const char *cells_name,
int index, struct fdt_phandle_args *out_args);
int dtb_node_count_phandle_with_args(const struct dtb_node *dn,
const char *list_name, const char *cells_name);
/* dtb_node_addr.c */
const uint32_t *dtb_node_get_address(const struct dtb_node *dev, int index,
uint64_t *size, unsigned int *flags);
#define dtb_node_string_list(string, ...) ((char *[]){string, ##__VA_ARGS__, NULL})
#define for_each_property_string(node_ptr, property_name, str, size) \
for (str = dtb_node_get_dtb_node_property_value(node_ptr, property_name, &size), \
size += (typeof(size))(size_t)str; \
str && *str; \
str = dtb_node_get_dtb_string_list_value_next((void *)str, (void *)(size_t)size))
#define for_each_property_cell(node_ptr, property_name, value, list, size) \
for (list = dtb_node_get_dtb_node_property_value(node_ptr, property_name, &size), \
value = dtb_node_get_dtb_cell_value(list), \
size /= sizeof(uint32_t); \
size > 0; \
value = dtb_node_get_dtb_cell_value(++list), --size)
#define for_each_property_byte(node_ptr, property_name, value, list, size) \
for (list = dtb_node_get_dtb_node_property_value(node_ptr, property_name, &size), \
value = dtb_node_get_dtb_byte_value(list); \
size > 0; \
value = dtb_node_get_dtb_byte_value(++list), --size)
#define for_each_node_child(node_ptr) \
for (node_ptr = (node_ptr ? node_ptr->child : NULL); \
node_ptr != NULL; \
node_ptr = node_ptr->sibling)
#define for_each_node_sibling(node_ptr) \
for (node_ptr = (node_ptr ? node_ptr->sibling : NULL); \
node_ptr != NULL; \
node_ptr = node_ptr->sibling)
#define for_each_of_allnodes_from(from, dn) \
for (dn = dtb_node_find_all_nodes(from); dn; dn = dtb_node_find_all_nodes(dn))
#define for_each_of_allnodes(dn) for_each_of_allnodes_from(NULL, dn)
#define dtb_node_get(x) (x)
static inline void dtb_node_put(const struct dtb_node *np)
{
}
/* Helper to read a big number; size is in cells (not bytes) */
static inline uint64_t dtb_node_read_number(const uint32_t *cell, int size)
{
uint64_t r = 0;
while (size--)
r = (r << 32) | fdt32_to_cpu(*(cell++));
return r;
}
/**
* ofnode_valid() - check if an ofnode is valid
*
* @return true if the reference contains a valid ofnode, RT_FALSE if it is NULL
*/
static inline rt_bool_t dtb_node_valid(const struct dtb_node *node)
{
if (dtb_node_active())
return node != NULL;
return RT_FALSE;
}
/*dtb_base.c */
rt_bool_t dtb_node_read_bool(const struct dtb_node *node, const char *propname);
const void *dtb_node_read_prop(const struct dtb_node *node, const char *propname, int *sizep);
const char *dtb_node_read_string(const struct dtb_node *node, const char *propname);
const struct dtb_node *dtb_node_find_subnode(const struct dtb_node *node, const char *subnode_name);
int dtb_node_read_u32_array(const struct dtb_node *node, const char *propname,
uint32_t *out_values, size_t sz);
struct dtb_node *dtb_node_first_subnode(const struct dtb_node *node);
struct dtb_node *dtb_node_next_subnode(const struct dtb_node *node);
struct dtb_node *dtb_node_get_parent(const struct dtb_node *node);
const char *dtb_node_get_name(const struct dtb_node *node);
struct dtb_node *dtb_node_get_by_phandle(uint32_t phandle);
int dtb_node_read_size(const struct dtb_node *node, const char *propname);
int dtb_node_get_addr_and_size_by_index(const struct dtb_node *node, int index, size_t *addr, size_t *size);
size_t dtb_node_get_addr_index(const struct dtb_node *node, int index);
size_t dtb_node_get_addr(const struct dtb_node *node);
int dtb_node_stringlist_search(const struct dtb_node *node, const char *property,
const char *string);
int dtb_node_read_string_index(const struct dtb_node *node, const char *property, int index,
const char **outp);
int dtb_node_read_string_count(const struct dtb_node *node, const char *property);
struct dtb_node *dtb_node_path(const char *path);
const char *dtb_node_get_chosen_prop(const char *name);
struct dtb_node *dtb_node_get_chosen_node(const char *name);
const void *dtb_node_get_property(const struct dtb_node *node, const char *propname, int *lenp);
rt_bool_t dtb_node_is_available(const struct dtb_node *node);
size_t dtb_node_get_addr_size(const struct dtb_node *node, const char *property,
size_t *sizep);
const uint8_t *dtb_node_read_u8_array_ptr(const struct dtb_node *node, const char *propname, size_t sz);
int dtb_node_find_all_compatible_node(const struct dtb_node *from, const char *compatible, struct dtb_node **node_table, int max_num, int *node_num);
int dtb_node_write_prop(const struct dtb_node *node, const char *propname, int len,
const void *value);
int dtb_node_write_string(const struct dtb_node *node, const char *propname, const char *value);
int dtb_node_set_enabled(const struct dtb_node *node, rt_bool_t value);
int dtb_node_irq_get(struct dtb_node *dev, int index);
int dtb_node_irq_get_byname(struct dtb_node *dev, const char *name);
int dtb_node_irq_count(struct dtb_node *dev);
/**
* dtb_node_for_each_subnode() - iterate over all subnodes of a parent
*
* @node: child node (ofnode, lvalue)
* @parent: parent node (ofnode)
*
* This is a wrapper around a for loop and is used like so:
*
* ofnode node;
*
* dtb_node_for_each_subnode(node, parent) {
* Use node
* ...
* }
*
* Note that this is implemented as a macro and @node is used as
* iterator in the loop. The parent variable can be a constant or even a
* literal.
*/
#define dtb_node_for_each_subnode(node, parent) \
for (node = dtb_node_first_subnode(parent); \
dtb_node_valid(node); \
node = dtb_node_next_subnode(node))
#endif /* RT_FDT_H__ */

View file

@ -0,0 +1,10 @@
from building import *
cwd = GetCurrentDir()
src = Glob('*.c')
CPPPATH = [cwd]
group = DefineGroup('FDT', src, depend = ['RT_USING_FDTLIB'], CPPPATH = CPPPATH)
Return('group')

View file

@ -0,0 +1,249 @@
/*
* libfdt - Flat Device Tree manipulation
* Copyright (C) 2006 David Gibson, IBM Corporation.
*
* libfdt is dual licensed: you can use it either under the terms of
* the GPL, or the BSD license, at your option.
*
* a) This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
* MA 02110-1301 USA
*
* Alternatively,
*
* b) Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* 1. Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "libfdt_env.h"
#include "fdt.h"
#include "libfdt.h"
#include "libfdt_internal.h"
int fdt_check_header(const void *fdt)
{
if (fdt_magic(fdt) == FDT_MAGIC) {
/* Complete tree */
if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
return -FDT_ERR_BADVERSION;
if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION)
return -FDT_ERR_BADVERSION;
} else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
/* Unfinished sequential-write blob */
if (fdt_size_dt_struct(fdt) == 0)
return -FDT_ERR_BADSTATE;
} else {
return -FDT_ERR_BADMAGIC;
}
return 0;
}
const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len)
{
unsigned absoffset = offset + fdt_off_dt_struct(fdt);
if ((absoffset < offset)
|| ((absoffset + len) < absoffset)
|| (absoffset + len) > fdt_totalsize(fdt))
return NULL;
if (fdt_version(fdt) >= 0x11)
if (((offset + len) < offset)
|| ((offset + len) > fdt_size_dt_struct(fdt)))
return NULL;
return _fdt_offset_ptr(fdt, offset);
}
uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset)
{
const fdt32_t *tagp, *lenp;
uint32_t tag;
int offset = startoffset;
const char *p;
*nextoffset = -FDT_ERR_TRUNCATED;
tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);
if (!tagp)
return FDT_END; /* premature end */
tag = fdt32_to_cpu(*tagp);
offset += FDT_TAGSIZE;
*nextoffset = -FDT_ERR_BADSTRUCTURE;
switch (tag) {
case FDT_BEGIN_NODE:
/* skip name */
do {
p = fdt_offset_ptr(fdt, offset++, 1);
} while (p && (*p != '\0'));
if (!p)
return FDT_END; /* premature end */
break;
case FDT_PROP:
lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
if (!lenp)
return FDT_END; /* premature end */
/* skip-name offset, length and value */
offset += sizeof(struct fdt_property) - FDT_TAGSIZE
+ fdt32_to_cpu(*lenp);
break;
case FDT_END:
case FDT_END_NODE:
case FDT_NOP:
break;
default:
return FDT_END;
}
if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset))
return FDT_END; /* premature end */
*nextoffset = FDT_TAGALIGN(offset);
return tag;
}
int _fdt_check_node_offset(const void *fdt, int offset)
{
if ((offset < 0) || (offset % FDT_TAGSIZE)
|| (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE))
return -FDT_ERR_BADOFFSET;
return offset;
}
int _fdt_check_prop_offset(const void *fdt, int offset)
{
if ((offset < 0) || (offset % FDT_TAGSIZE)
|| (fdt_next_tag(fdt, offset, &offset) != FDT_PROP))
return -FDT_ERR_BADOFFSET;
return offset;
}
int fdt_next_node(const void *fdt, int offset, int *depth)
{
int nextoffset = 0;
uint32_t tag;
if (offset >= 0)
if ((nextoffset = _fdt_check_node_offset(fdt, offset)) < 0)
return nextoffset;
do {
offset = nextoffset;
tag = fdt_next_tag(fdt, offset, &nextoffset);
switch (tag) {
case FDT_PROP:
case FDT_NOP:
break;
case FDT_BEGIN_NODE:
if (depth)
(*depth)++;
break;
case FDT_END_NODE:
if (depth && ((--(*depth)) < 0))
return nextoffset;
break;
case FDT_END:
if ((nextoffset >= 0)
|| ((nextoffset == -FDT_ERR_TRUNCATED) && !depth))
return -FDT_ERR_NOTFOUND;
else
return nextoffset;
}
} while (tag != FDT_BEGIN_NODE);
return offset;
}
int fdt_first_subnode(const void *fdt, int offset)
{
int depth = 0;
offset = fdt_next_node(fdt, offset, &depth);
if (offset < 0 || depth != 1)
return -FDT_ERR_NOTFOUND;
return offset;
}
int fdt_next_subnode(const void *fdt, int offset)
{
int depth = 1;
/*
* With respect to the parent, the depth of the next subnode will be
* the same as the last.
*/
do {
offset = fdt_next_node(fdt, offset, &depth);
if (offset < 0 || depth < 1)
return -FDT_ERR_NOTFOUND;
} while (depth > 1);
return offset;
}
const char *_fdt_find_string(const char *strtab, int tabsize, const char *s)
{
int len = strlen(s) + 1;
const char *last = strtab + tabsize - len;
const char *p;
for (p = strtab; p <= last; p++)
if (memcmp(p, s, len) == 0)
return p;
return NULL;
}
int fdt_move(const void *fdt, void *buf, int bufsize)
{
FDT_CHECK_HEADER(fdt);
if (fdt_totalsize(fdt) > bufsize)
return -FDT_ERR_NOSPACE;
memmove(buf, fdt, fdt_totalsize(fdt));
return 0;
}

View file

@ -0,0 +1,125 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-12-20 RT-Thread the first version
*/
#ifndef _FDT_H
#define _FDT_H
/*
* libfdt - Flat Device Tree manipulation
* Copyright (C) 2006 David Gibson, IBM Corporation.
* Copyright 2012 Kim Phillips, Freescale Semiconductor.
*
* libfdt is dual licensed: you can use it either under the terms of
* the GPL, or the BSD license, at your option.
*
* a) This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
* MA 02110-1301 USA
*
* Alternatively,
*
* b) Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* 1. Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __ASSEMBLY__
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
struct fdt_header {
fdt32_t magic; /* magic word FDT_MAGIC */
fdt32_t totalsize; /* total size of DT block */
fdt32_t off_dt_struct; /* offset to structure */
fdt32_t off_dt_strings; /* offset to strings */
fdt32_t off_mem_rsvmap; /* offset to memory reserve map */
fdt32_t version; /* format version */
fdt32_t last_comp_version; /* last compatible version */
/* version 2 fields below */
fdt32_t boot_cpuid_phys; /* Which physical CPU id we're
booting on */
/* version 3 fields below */
fdt32_t size_dt_strings; /* size of the strings block */
/* version 17 fields below */
fdt32_t size_dt_struct; /* size of the structure block */
};
struct fdt_reserve_entry {
fdt64_t address;
fdt64_t size;
};
struct fdt_node_header {
fdt32_t tag;
char name[0];
};
struct fdt_property {
fdt32_t tag;
fdt32_t len;
fdt32_t nameoff;
char data[0];
};
#endif /* !__ASSEMBLY */
#define FDT_MAGIC 0xd00dfeed /* 4: version, 4: total size */
#define FDT_TAGSIZE sizeof(fdt32_t)
#define FDT_BEGIN_NODE 0x1 /* Start node: full name */
#define FDT_END_NODE 0x2 /* End node */
#define FDT_PROP 0x3 /* Property: name off,
size, content */
#define FDT_NOP 0x4 /* nop */
#define FDT_END 0x9
#define FDT_V1_SIZE (7*sizeof(fdt32_t))
#define FDT_V2_SIZE (FDT_V1_SIZE + sizeof(fdt32_t))
#define FDT_V3_SIZE (FDT_V2_SIZE + sizeof(fdt32_t))
#define FDT_V16_SIZE FDT_V3_SIZE
#define FDT_V17_SIZE (FDT_V16_SIZE + sizeof(fdt32_t))
#endif /* _FDT_H */

View file

@ -0,0 +1,99 @@
// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
/*
* libfdt - Flat Device Tree manipulation
* Copyright (C) 2014 David Gibson <david@gibson.dropbear.id.au>
* Copyright (C) 2018 embedded brains GmbH
*/
#include "libfdt_env.h"
#include <fdt.h>
#include <libfdt.h>
#include "libfdt_internal.h"
static int fdt_cells(const void *fdt, int nodeoffset, const char *name)
{
const fdt32_t *c;
uint32_t val;
int len;
c = fdt_getprop(fdt, nodeoffset, name, &len);
if (!c)
return len;
if (len != sizeof(*c))
return -FDT_ERR_BADNCELLS;
val = fdt32_to_cpu(*c);
if (val > FDT_MAX_NCELLS)
return -FDT_ERR_BADNCELLS;
return (int)val;
}
int fdt_address_cells(const void *fdt, int nodeoffset)
{
int val;
val = fdt_cells(fdt, nodeoffset, "#address-cells");
if (val == 0)
return -FDT_ERR_BADNCELLS;
if (val == -FDT_ERR_NOTFOUND)
return 2;
return val;
}
int fdt_size_cells(const void *fdt, int nodeoffset)
{
int val;
val = fdt_cells(fdt, nodeoffset, "#size-cells");
if (val == -FDT_ERR_NOTFOUND)
return 1;
return val;
}
/* This function assumes that [address|size]_cells is 1 or 2 */
int fdt_appendprop_addrrange(void *fdt, int parent, int nodeoffset,
const char *name, uint64_t addr, uint64_t size)
{
int addr_cells, size_cells, ret;
uint8_t data[sizeof(fdt64_t) * 2], *prop;
ret = fdt_address_cells(fdt, parent);
if (ret < 0)
return ret;
addr_cells = ret;
ret = fdt_size_cells(fdt, parent);
if (ret < 0)
return ret;
size_cells = ret;
/* check validity of address */
prop = data;
if (addr_cells == 1) {
if ((addr > UINT32_MAX) || ((UINT32_MAX + 1 - addr) < size))
return -FDT_ERR_BADVALUE;
fdt32_st(prop, (uint32_t)addr);
} else if (addr_cells == 2) {
fdt64_st(prop, addr);
} else {
return -FDT_ERR_BADNCELLS;
}
/* check validity of size */
prop += addr_cells * sizeof(fdt32_t);
if (size_cells == 1) {
if (size > UINT32_MAX)
return -FDT_ERR_BADVALUE;
fdt32_st(prop, (uint32_t)size);
} else if (size_cells == 2) {
fdt64_st(prop, size);
} else {
return -FDT_ERR_BADNCELLS;
}
return fdt_appendprop(fdt, nodeoffset, name, data,
(addr_cells + size_cells) * sizeof(fdt32_t));
}

View file

@ -0,0 +1,84 @@
/*
* libfdt - Flat Device Tree manipulation
* Copyright (C) 2012 David Gibson, IBM Corporation.
*
* libfdt is dual licensed: you can use it either under the terms of
* the GPL, or the BSD license, at your option.
*
* a) This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
* MA 02110-1301 USA
*
* Alternatively,
*
* b) Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* 1. Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "libfdt_env.h"
#include "fdt.h"
#include "libfdt.h"
#include "libfdt_internal.h"
int fdt_create_empty_tree(void *buf, int bufsize)
{
int err;
err = fdt_create(buf, bufsize);
if (err)
return err;
err = fdt_finish_reservemap(buf);
if (err)
return err;
err = fdt_begin_node(buf, "");
if (err)
return err;
err = fdt_end_node(buf);
if (err)
return err;
err = fdt_finish(buf);
if (err)
return err;
return fdt_open_into(buf, buf, bufsize);
}

View file

@ -0,0 +1,701 @@
/*
* libfdt - Flat Device Tree manipulation
* Copyright (C) 2006 David Gibson, IBM Corporation.
*
* libfdt is dual licensed: you can use it either under the terms of
* the GPL, or the BSD license, at your option.
*
* a) This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
* MA 02110-1301 USA
*
* Alternatively,
*
* b) Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* 1. Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "libfdt_env.h"
#include "fdt.h"
#include "libfdt.h"
#include "libfdt_internal.h"
static int _fdt_nodename_eq(const void *fdt, int offset,
const char *s, int len)
{
const char *p = fdt_offset_ptr(fdt, offset + FDT_TAGSIZE, len+1);
if (! p)
/* short match */
return 0;
if (memcmp(p, s, len) != 0)
return 0;
if (p[len] == '\0')
return 1;
else if (!memchr(s, '@', len) && (p[len] == '@'))
return 1;
else
return 0;
}
const char *fdt_string(const void *fdt, int stroffset)
{
return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
}
static int _fdt_string_eq(const void *fdt, int stroffset,
const char *s, int len)
{
const char *p = fdt_string(fdt, stroffset);
return (strlen(p) == len) && (memcmp(p, s, len) == 0);
}
uint32_t fdt_get_max_phandle(const void *fdt)
{
uint32_t max_phandle = 0;
int offset;
for (offset = fdt_next_node(fdt, -1, NULL);;
offset = fdt_next_node(fdt, offset, NULL)) {
uint32_t phandle;
if (offset == -FDT_ERR_NOTFOUND)
return max_phandle;
if (offset < 0)
return (uint32_t)-1;
phandle = fdt_get_phandle(fdt, offset);
if (phandle == (uint32_t)-1)
continue;
if (phandle > max_phandle)
max_phandle = phandle;
}
return 0;
}
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);
*size = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->size);
return 0;
}
int fdt_num_mem_rsv(const void *fdt)
{
int i = 0;
while (fdt64_to_cpu(_fdt_mem_rsv(fdt, i)->size) != 0)
i++;
return i;
}
static int _nextprop(const void *fdt, int offset)
{
uint32_t tag;
int nextoffset;
do {
tag = fdt_next_tag(fdt, offset, &nextoffset);
switch (tag) {
case FDT_END:
if (nextoffset >= 0)
return -FDT_ERR_BADSTRUCTURE;
else
return nextoffset;
case FDT_PROP:
return offset;
}
offset = nextoffset;
} while (tag == FDT_NOP);
return -FDT_ERR_NOTFOUND;
}
int fdt_subnode_offset_namelen(const void *fdt, int offset,
const char *name, int namelen)
{
int depth;
FDT_CHECK_HEADER(fdt);
for (depth = 0;
(offset >= 0) && (depth >= 0);
offset = fdt_next_node(fdt, offset, &depth))
if ((depth == 1)
&& _fdt_nodename_eq(fdt, offset, name, namelen))
return offset;
if (depth < 0)
return -FDT_ERR_NOTFOUND;
return offset; /* error */
}
int fdt_subnode_offset(const void *fdt, int parentoffset,
const char *name)
{
return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
}
int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen)
{
const char *end = path + namelen;
const char *p = path;
int offset = 0;
FDT_CHECK_HEADER(fdt);
/* see if we have an alias */
if (*path != '/') {
const char *q = memchr(path, '/', end - p);
if (!q)
q = end;
p = fdt_get_alias_namelen(fdt, p, q - p);
if (!p)
return -FDT_ERR_BADPATH;
offset = fdt_path_offset(fdt, p);
p = q;
}
while (p < end) {
const char *q;
while (*p == '/') {
p++;
if (p == end)
return offset;
}
q = memchr(p, '/', end - p);
if (! q)
q = end;
offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
if (offset < 0)
return offset;
p = q;
}
return offset;
}
int fdt_path_offset(const void *fdt, const char *path)
{
return fdt_path_offset_namelen(fdt, path, strlen(path));
}
const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
{
const struct fdt_node_header *nh = _fdt_offset_ptr(fdt, nodeoffset);
int err;
if (((err = fdt_check_header(fdt)) != 0)
|| ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0))
goto fail;
if (len)
*len = strlen(nh->name);
return nh->name;
fail:
if (len)
*len = err;
return NULL;
}
int fdt_first_property_offset(const void *fdt, int nodeoffset)
{
int offset;
if ((offset = _fdt_check_node_offset(fdt, nodeoffset)) < 0)
return offset;
return _nextprop(fdt, offset);
}
int fdt_next_property_offset(const void *fdt, int offset)
{
if ((offset = _fdt_check_prop_offset(fdt, offset)) < 0)
return offset;
return _nextprop(fdt, offset);
}
const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
int offset,
int *lenp)
{
int err;
const struct fdt_property *prop;
if ((err = _fdt_check_prop_offset(fdt, offset)) < 0) {
if (lenp)
*lenp = err;
return NULL;
}
prop = _fdt_offset_ptr(fdt, offset);
if (lenp)
*lenp = fdt32_to_cpu(prop->len);
return prop;
}
const struct fdt_property *fdt_get_property_namelen(const void *fdt,
int offset,
const char *name,
int namelen, int *lenp)
{
for (offset = fdt_first_property_offset(fdt, offset);
(offset >= 0);
(offset = fdt_next_property_offset(fdt, offset))) {
const struct fdt_property *prop;
if (!(prop = fdt_get_property_by_offset(fdt, offset, lenp))) {
offset = -FDT_ERR_INTERNAL;
break;
}
if (_fdt_string_eq(fdt, fdt32_to_cpu(prop->nameoff),
name, namelen))
return prop;
}
if (lenp)
*lenp = offset;
return NULL;
}
const struct fdt_property *fdt_get_property(const void *fdt,
int nodeoffset,
const char *name, int *lenp)
{
return fdt_get_property_namelen(fdt, nodeoffset, name,
strlen(name), lenp);
}
const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
const char *name, int namelen, int *lenp)
{
const struct fdt_property *prop;
prop = fdt_get_property_namelen(fdt, nodeoffset, name, namelen, lenp);
if (! prop)
return NULL;
return prop->data;
}
const void *fdt_getprop_by_offset(const void *fdt, int offset,
const char **namep, int *lenp)
{
const struct fdt_property *prop;
prop = fdt_get_property_by_offset(fdt, offset, lenp);
if (!prop)
return NULL;
if (namep)
*namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
return prop->data;
}
const void *fdt_getprop(const void *fdt, int nodeoffset,
const char *name, int *lenp)
{
return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp);
}
uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
{
const fdt32_t *php;
int len;
/* FIXME: This is a bit sub-optimal, since we potentially scan
* over all the properties twice. */
php = fdt_getprop(fdt, nodeoffset, "phandle", &len);
if (!php || (len != sizeof(*php))) {
php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
if (!php || (len != sizeof(*php)))
return 0;
}
return fdt32_to_cpu(*php);
}
const char *fdt_get_alias_namelen(const void *fdt,
const char *name, int namelen)
{
int aliasoffset;
aliasoffset = fdt_path_offset(fdt, "/aliases");
if (aliasoffset < 0)
return NULL;
return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL);
}
const char *fdt_get_alias(const void *fdt, const char *name)
{
return fdt_get_alias_namelen(fdt, name, strlen(name));
}
int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
{
int pdepth = 0, p = 0;
int offset, depth, namelen;
const char *name;
FDT_CHECK_HEADER(fdt);
if (buflen < 2)
return -FDT_ERR_NOSPACE;
for (offset = 0, depth = 0;
(offset >= 0) && (offset <= nodeoffset);
offset = fdt_next_node(fdt, offset, &depth)) {
while (pdepth > depth) {
do {
p--;
} while (buf[p-1] != '/');
pdepth--;
}
if (pdepth >= depth) {
name = fdt_get_name(fdt, offset, &namelen);
if (!name)
return namelen;
if ((p + namelen + 1) <= buflen) {
memcpy(buf + p, name, namelen);
p += namelen;
buf[p++] = '/';
pdepth++;
}
}
if (offset == nodeoffset) {
if (pdepth < (depth + 1))
return -FDT_ERR_NOSPACE;
if (p > 1) /* special case so that root path is "/", not "" */
p--;
buf[p] = '\0';
return 0;
}
}
if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
return -FDT_ERR_BADOFFSET;
else if (offset == -FDT_ERR_BADOFFSET)
return -FDT_ERR_BADSTRUCTURE;
return offset; /* error from fdt_next_node() */
}
int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
int supernodedepth, int *nodedepth)
{
int offset, depth;
int supernodeoffset = -FDT_ERR_INTERNAL;
FDT_CHECK_HEADER(fdt);
if (supernodedepth < 0)
return -FDT_ERR_NOTFOUND;
for (offset = 0, depth = 0;
(offset >= 0) && (offset <= nodeoffset);
offset = fdt_next_node(fdt, offset, &depth)) {
if (depth == supernodedepth)
supernodeoffset = offset;
if (offset == nodeoffset) {
if (nodedepth)
*nodedepth = depth;
if (supernodedepth > depth)
return -FDT_ERR_NOTFOUND;
else
return supernodeoffset;
}
}
if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
return -FDT_ERR_BADOFFSET;
else if (offset == -FDT_ERR_BADOFFSET)
return -FDT_ERR_BADSTRUCTURE;
return offset; /* error from fdt_next_node() */
}
int fdt_node_depth(const void *fdt, int nodeoffset)
{
int nodedepth;
int err;
err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
if (err)
return (err < 0) ? err : -FDT_ERR_INTERNAL;
return nodedepth;
}
int fdt_parent_offset(const void *fdt, int nodeoffset)
{
int nodedepth = fdt_node_depth(fdt, nodeoffset);
if (nodedepth < 0)
return nodedepth;
return fdt_supernode_atdepth_offset(fdt, nodeoffset,
nodedepth - 1, NULL);
}
int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
const char *propname,
const void *propval, int proplen)
{
int offset;
const void *val;
int len;
FDT_CHECK_HEADER(fdt);
/* FIXME: The algorithm here is pretty horrible: we scan each
* property of a node in fdt_getprop(), then if that didn't
* find what we want, we scan over them again making our way
* to the next node. Still it's the easiest to implement
* approach; performance can come later. */
for (offset = fdt_next_node(fdt, startoffset, NULL);
offset >= 0;
offset = fdt_next_node(fdt, offset, NULL)) {
val = fdt_getprop(fdt, offset, propname, &len);
if (val && (len == proplen)
&& (memcmp(val, propval, len) == 0))
return offset;
}
return offset; /* error from fdt_next_node() */
}
int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
{
int offset;
if ((phandle == 0) || (phandle == -1))
return -FDT_ERR_BADPHANDLE;
FDT_CHECK_HEADER(fdt);
/* FIXME: The algorithm here is pretty horrible: we
* potentially scan each property of a node in
* fdt_get_phandle(), then if that didn't find what
* we want, we scan over them again making our way to the next
* node. Still it's the easiest to implement approach;
* performance can come later. */
for (offset = fdt_next_node(fdt, -1, NULL);
offset >= 0;
offset = fdt_next_node(fdt, offset, NULL)) {
if (fdt_get_phandle(fdt, offset) == phandle)
return offset;
}
return offset; /* error from fdt_next_node() */
}
int fdt_stringlist_contains(const char *strlist, int listlen, const char *str)
{
int len = strlen(str);
const char *p;
while (listlen >= len) {
if (memcmp(str, strlist, len+1) == 0)
return 1;
p = memchr(strlist, '\0', listlen);
if (!p)
return 0; /* malformed strlist.. */
listlen -= (p-strlist) + 1;
strlist = p + 1;
}
return 0;
}
int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property)
{
const char *list, *end;
int length, count = 0;
list = fdt_getprop(fdt, nodeoffset, property, &length);
if (!list)
return length;
end = list + length;
while (list < end) {
length = strnlen(list, end - list) + 1;
/* Abort if the last string isn't properly NUL-terminated. */
if (list + length > end)
return -FDT_ERR_BADVALUE;
list += length;
count++;
}
return count;
}
int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,
const char *string)
{
int length, len, idx = 0;
const char *list, *end;
list = fdt_getprop(fdt, nodeoffset, property, &length);
if (!list)
return length;
len = strlen(string) + 1;
end = list + length;
while (list < end) {
length = strnlen(list, end - list) + 1;
/* Abort if the last string isn't properly NUL-terminated. */
if (list + length > end)
return -FDT_ERR_BADVALUE;
if (length == len && memcmp(list, string, length) == 0)
return idx;
list += length;
idx++;
}
return -FDT_ERR_NOTFOUND;
}
const char *fdt_stringlist_get(const void *fdt, int nodeoffset,
const char *property, int idx,
int *lenp)
{
const char *list, *end;
int length;
list = fdt_getprop(fdt, nodeoffset, property, &length);
if (!list) {
if (lenp)
*lenp = length;
return NULL;
}
end = list + length;
while (list < end) {
length = strnlen(list, end - list) + 1;
/* Abort if the last string isn't properly NUL-terminated. */
if (list + length > end) {
if (lenp)
*lenp = -FDT_ERR_BADVALUE;
return NULL;
}
if (idx == 0) {
if (lenp)
*lenp = length - 1;
return list;
}
list += length;
idx--;
}
if (lenp)
*lenp = -FDT_ERR_NOTFOUND;
return NULL;
}
int fdt_node_check_compatible(const void *fdt, int nodeoffset,
const char *compatible)
{
const void *prop;
int len;
prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
if (!prop)
return len;
return !fdt_stringlist_contains(prop, len, compatible);
}
int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
const char *compatible)
{
int offset, err;
FDT_CHECK_HEADER(fdt);
/* FIXME: The algorithm here is pretty horrible: we scan each
* property of a node in fdt_node_check_compatible(), then if
* that didn't find what we want, we scan over them again
* making our way to the next node. Still it's the easiest to
* implement approach; performance can come later. */
for (offset = fdt_next_node(fdt, startoffset, NULL);
offset >= 0;
offset = fdt_next_node(fdt, offset, NULL)) {
err = fdt_node_check_compatible(fdt, offset, compatible);
if ((err < 0) && (err != -FDT_ERR_NOTFOUND))
return err;
else if (err == 0)
return offset;
}
return offset; /* error from fdt_next_node() */
}

View file

@ -0,0 +1,489 @@
/*
* libfdt - Flat Device Tree manipulation
* Copyright (C) 2006 David Gibson, IBM Corporation.
*
* libfdt is dual licensed: you can use it either under the terms of
* the GPL, or the BSD license, at your option.
*
* a) This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
* MA 02110-1301 USA
*
* Alternatively,
*
* b) Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* 1. Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "libfdt_env.h"
#include "fdt.h"
#include "libfdt.h"
#include "libfdt_internal.h"
static int _fdt_blocks_misordered(const void *fdt,
int mem_rsv_size, int struct_size)
{
return (fdt_off_mem_rsvmap(fdt) < FDT_ALIGN(sizeof(struct fdt_header), 8))
|| (fdt_off_dt_struct(fdt) <
(fdt_off_mem_rsvmap(fdt) + mem_rsv_size))
|| (fdt_off_dt_strings(fdt) <
(fdt_off_dt_struct(fdt) + struct_size))
|| (fdt_totalsize(fdt) <
(fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt)));
}
static int _fdt_rw_check_header(void *fdt)
{
FDT_CHECK_HEADER(fdt);
if (fdt_version(fdt) < 17)
return -FDT_ERR_BADVERSION;
if (_fdt_blocks_misordered(fdt, sizeof(struct fdt_reserve_entry),
fdt_size_dt_struct(fdt)))
return -FDT_ERR_BADLAYOUT;
if (fdt_version(fdt) > 17)
fdt_set_version(fdt, 17);
return 0;
}
#define FDT_RW_CHECK_HEADER(fdt) \
{ \
int __err; \
if ((__err = _fdt_rw_check_header(fdt)) != 0) \
return __err; \
}
static inline int _fdt_data_size(void *fdt)
{
return fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);
}
static int _fdt_splice(void *fdt, void *splicepoint, int oldlen, int newlen)
{
char *p = splicepoint;
char *end = (char *)fdt + _fdt_data_size(fdt);
if (((p + oldlen) < p) || ((p + oldlen) > end))
return -FDT_ERR_BADOFFSET;
if ((p < (char *)fdt) || ((end - oldlen + newlen) < (char *)fdt))
return -FDT_ERR_BADOFFSET;
if ((end - oldlen + newlen) > ((char *)fdt + fdt_totalsize(fdt)))
return -FDT_ERR_NOSPACE;
memmove(p + newlen, p + oldlen, end - p - oldlen);
return 0;
}
static int _fdt_splice_mem_rsv(void *fdt, struct fdt_reserve_entry *p,
int oldn, int newn)
{
int delta = (newn - oldn) * sizeof(*p);
int err;
err = _fdt_splice(fdt, p, oldn * sizeof(*p), newn * sizeof(*p));
if (err)
return err;
fdt_set_off_dt_struct(fdt, fdt_off_dt_struct(fdt) + delta);
fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta);
return 0;
}
static int _fdt_splice_struct(void *fdt, void *p,
int oldlen, int newlen)
{
int delta = newlen - oldlen;
int err;
if ((err = _fdt_splice(fdt, p, oldlen, newlen)))
return err;
fdt_set_size_dt_struct(fdt, fdt_size_dt_struct(fdt) + delta);
fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta);
return 0;
}
static int _fdt_splice_string(void *fdt, int newlen)
{
void *p = (char *)fdt
+ fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);
int err;
if ((err = _fdt_splice(fdt, p, 0, newlen)))
return err;
fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) + newlen);
return 0;
}
static int _fdt_find_add_string(void *fdt, const char *s)
{
char *strtab = (char *)fdt + fdt_off_dt_strings(fdt);
const char *p;
char *new;
int len = strlen(s) + 1;
int err;
p = _fdt_find_string(strtab, fdt_size_dt_strings(fdt), s);
if (p)
/* found it */
return (p - strtab);
new = strtab + fdt_size_dt_strings(fdt);
err = _fdt_splice_string(fdt, len);
if (err)
return err;
memcpy(new, s, len);
return (new - strtab);
}
int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size)
{
struct fdt_reserve_entry *re;
int err;
FDT_RW_CHECK_HEADER(fdt);
re = _fdt_mem_rsv_w(fdt, fdt_num_mem_rsv(fdt));
err = _fdt_splice_mem_rsv(fdt, re, 0, 1);
if (err)
return err;
re->address = cpu_to_fdt64(address);
re->size = cpu_to_fdt64(size);
return 0;
}
int fdt_del_mem_rsv(void *fdt, int n)
{
struct fdt_reserve_entry *re = _fdt_mem_rsv_w(fdt, n);
FDT_RW_CHECK_HEADER(fdt);
if (n >= fdt_num_mem_rsv(fdt))
return -FDT_ERR_NOTFOUND;
return _fdt_splice_mem_rsv(fdt, re, 1, 0);
}
static int _fdt_resize_property(void *fdt, int nodeoffset, const char *name,
int len, struct fdt_property **prop)
{
int oldlen;
int err;
*prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen);
if (! (*prop))
return oldlen;
if ((err = _fdt_splice_struct(fdt, (*prop)->data, FDT_TAGALIGN(oldlen),
FDT_TAGALIGN(len))))
return err;
(*prop)->len = cpu_to_fdt32(len);
return 0;
}
static int _fdt_add_property(void *fdt, int nodeoffset, const char *name,
int len, struct fdt_property **prop)
{
int proplen;
int nextoffset;
int namestroff;
int err;
if ((nextoffset = _fdt_check_node_offset(fdt, nodeoffset)) < 0)
return nextoffset;
namestroff = _fdt_find_add_string(fdt, name);
if (namestroff < 0)
return namestroff;
*prop = _fdt_offset_ptr_w(fdt, nextoffset);
proplen = sizeof(**prop) + FDT_TAGALIGN(len);
err = _fdt_splice_struct(fdt, *prop, 0, proplen);
if (err)
return err;
(*prop)->tag = cpu_to_fdt32(FDT_PROP);
(*prop)->nameoff = cpu_to_fdt32(namestroff);
(*prop)->len = cpu_to_fdt32(len);
return 0;
}
int fdt_set_name(void *fdt, int nodeoffset, const char *name)
{
char *namep;
int oldlen, newlen;
int err;
FDT_RW_CHECK_HEADER(fdt);
namep = (char *)(uintptr_t)fdt_get_name(fdt, nodeoffset, &oldlen);
if (!namep)
return oldlen;
newlen = strlen(name);
err = _fdt_splice_struct(fdt, namep, FDT_TAGALIGN(oldlen+1),
FDT_TAGALIGN(newlen+1));
if (err)
return err;
memcpy(namep, name, newlen+1);
return 0;
}
int fdt_setprop(void *fdt, int nodeoffset, const char *name,
const void *val, int len)
{
struct fdt_property *prop;
int err;
FDT_RW_CHECK_HEADER(fdt);
err = _fdt_resize_property(fdt, nodeoffset, name, len, &prop);
if (err == -FDT_ERR_NOTFOUND)
err = _fdt_add_property(fdt, nodeoffset, name, len, &prop);
if (err)
return err;
if (len)
memcpy(prop->data, val, len);
return 0;
}
int fdt_appendprop(void *fdt, int nodeoffset, const char *name,
const void *val, int len)
{
struct fdt_property *prop;
int err, oldlen, newlen;
FDT_RW_CHECK_HEADER(fdt);
prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen);
if (prop) {
newlen = len + oldlen;
err = _fdt_splice_struct(fdt, prop->data,
FDT_TAGALIGN(oldlen),
FDT_TAGALIGN(newlen));
if (err)
return err;
prop->len = cpu_to_fdt32(newlen);
memcpy(prop->data + oldlen, val, len);
} else {
err = _fdt_add_property(fdt, nodeoffset, name, len, &prop);
if (err)
return err;
memcpy(prop->data, val, len);
}
return 0;
}
int fdt_delprop(void *fdt, int nodeoffset, const char *name)
{
struct fdt_property *prop;
int len, proplen;
FDT_RW_CHECK_HEADER(fdt);
prop = fdt_get_property_w(fdt, nodeoffset, name, &len);
if (! prop)
return len;
proplen = sizeof(*prop) + FDT_TAGALIGN(len);
return _fdt_splice_struct(fdt, prop, proplen, 0);
}
int fdt_add_subnode_namelen(void *fdt, int parentoffset,
const char *name, int namelen)
{
struct fdt_node_header *nh;
int offset, nextoffset;
int nodelen;
int err;
uint32_t tag;
fdt32_t *endtag;
FDT_RW_CHECK_HEADER(fdt);
offset = fdt_subnode_offset_namelen(fdt, parentoffset, name, namelen);
if (offset >= 0)
return -FDT_ERR_EXISTS;
else if (offset != -FDT_ERR_NOTFOUND)
return offset;
/* Try to place the new node after the parent's properties */
fdt_next_tag(fdt, parentoffset, &nextoffset); /* skip the BEGIN_NODE */
do {
offset = nextoffset;
tag = fdt_next_tag(fdt, offset, &nextoffset);
} while ((tag == FDT_PROP) || (tag == FDT_NOP));
nh = _fdt_offset_ptr_w(fdt, offset);
nodelen = sizeof(*nh) + FDT_TAGALIGN(namelen+1) + FDT_TAGSIZE;
err = _fdt_splice_struct(fdt, nh, 0, nodelen);
if (err)
return err;
nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE);
memset(nh->name, 0, FDT_TAGALIGN(namelen+1));
memcpy(nh->name, name, namelen);
endtag = (fdt32_t *)((char *)nh + nodelen - FDT_TAGSIZE);
*endtag = cpu_to_fdt32(FDT_END_NODE);
return offset;
}
int fdt_add_subnode(void *fdt, int parentoffset, const char *name)
{
return fdt_add_subnode_namelen(fdt, parentoffset, name, strlen(name));
}
int fdt_del_node(void *fdt, int nodeoffset)
{
int endoffset;
FDT_RW_CHECK_HEADER(fdt);
endoffset = _fdt_node_end_offset(fdt, nodeoffset);
if (endoffset < 0)
return endoffset;
return _fdt_splice_struct(fdt, _fdt_offset_ptr_w(fdt, nodeoffset),
endoffset - nodeoffset, 0);
}
static void _fdt_packblocks(const char *old, char *new,
int mem_rsv_size, int struct_size)
{
int mem_rsv_off, struct_off, strings_off;
mem_rsv_off = FDT_ALIGN(sizeof(struct fdt_header), 8);
struct_off = mem_rsv_off + mem_rsv_size;
strings_off = struct_off + struct_size;
memmove(new + mem_rsv_off, old + fdt_off_mem_rsvmap(old), mem_rsv_size);
fdt_set_off_mem_rsvmap(new, mem_rsv_off);
memmove(new + struct_off, old + fdt_off_dt_struct(old), struct_size);
fdt_set_off_dt_struct(new, struct_off);
fdt_set_size_dt_struct(new, struct_size);
memmove(new + strings_off, old + fdt_off_dt_strings(old),
fdt_size_dt_strings(old));
fdt_set_off_dt_strings(new, strings_off);
fdt_set_size_dt_strings(new, fdt_size_dt_strings(old));
}
int fdt_open_into(const void *fdt, void *buf, int bufsize)
{
int err;
int mem_rsv_size, struct_size;
int newsize;
const char *fdtstart = fdt;
const char *fdtend = fdtstart + fdt_totalsize(fdt);
char *tmp;
FDT_CHECK_HEADER(fdt);
mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)
* sizeof(struct fdt_reserve_entry);
if (fdt_version(fdt) >= 17) {
struct_size = fdt_size_dt_struct(fdt);
} else {
struct_size = 0;
while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END)
;
if (struct_size < 0)
return struct_size;
}
if (!_fdt_blocks_misordered(fdt, mem_rsv_size, struct_size)) {
/* no further work necessary */
err = fdt_move(fdt, buf, bufsize);
if (err)
return err;
fdt_set_version(buf, 17);
fdt_set_size_dt_struct(buf, struct_size);
fdt_set_totalsize(buf, bufsize);
return 0;
}
/* Need to reorder */
newsize = FDT_ALIGN(sizeof(struct fdt_header), 8) + mem_rsv_size
+ struct_size + fdt_size_dt_strings(fdt);
if (bufsize < newsize)
return -FDT_ERR_NOSPACE;
/* First attempt to build converted tree at beginning of buffer */
tmp = buf;
/* But if that overlaps with the old tree... */
if (((tmp + newsize) > fdtstart) && (tmp < fdtend)) {
/* Try right after the old tree instead */
tmp = (char *)(uintptr_t)fdtend;
if ((tmp + newsize) > ((char *)buf + bufsize))
return -FDT_ERR_NOSPACE;
}
_fdt_packblocks(fdt, tmp, mem_rsv_size, struct_size);
memmove(buf, tmp, newsize);
fdt_set_magic(buf, FDT_MAGIC);
fdt_set_totalsize(buf, bufsize);
fdt_set_version(buf, 17);
fdt_set_last_comp_version(buf, 16);
fdt_set_boot_cpuid_phys(buf, fdt_boot_cpuid_phys(fdt));
return 0;
}
int fdt_pack(void *fdt)
{
int mem_rsv_size;
FDT_RW_CHECK_HEADER(fdt);
mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)
* sizeof(struct fdt_reserve_entry);
_fdt_packblocks(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt));
fdt_set_totalsize(fdt, _fdt_data_size(fdt));
return 0;
}

View file

@ -0,0 +1,100 @@
/*
* libfdt - Flat Device Tree manipulation
* Copyright (C) 2006 David Gibson, IBM Corporation.
*
* libfdt is dual licensed: you can use it either under the terms of
* the GPL, or the BSD license, at your option.
*
* a) This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
* MA 02110-1301 USA
*
* Alternatively,
*
* b) Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* 1. Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "libfdt_env.h"
#include "fdt.h"
#include "libfdt.h"
#include "libfdt_internal.h"
struct fdt_errtabent {
const char *str;
};
#define FDT_ERRTABENT(val) \
[(val)] = { .str = #val, }
static struct fdt_errtabent fdt_errtable[] = {
FDT_ERRTABENT(FDT_ERR_NOTFOUND),
FDT_ERRTABENT(FDT_ERR_EXISTS),
FDT_ERRTABENT(FDT_ERR_NOSPACE),
FDT_ERRTABENT(FDT_ERR_BADOFFSET),
FDT_ERRTABENT(FDT_ERR_BADPATH),
FDT_ERRTABENT(FDT_ERR_BADPHANDLE),
FDT_ERRTABENT(FDT_ERR_BADSTATE),
FDT_ERRTABENT(FDT_ERR_TRUNCATED),
FDT_ERRTABENT(FDT_ERR_BADMAGIC),
FDT_ERRTABENT(FDT_ERR_BADVERSION),
FDT_ERRTABENT(FDT_ERR_BADSTRUCTURE),
FDT_ERRTABENT(FDT_ERR_BADLAYOUT),
FDT_ERRTABENT(FDT_ERR_INTERNAL),
FDT_ERRTABENT(FDT_ERR_BADNCELLS),
FDT_ERRTABENT(FDT_ERR_BADVALUE),
FDT_ERRTABENT(FDT_ERR_BADOVERLAY),
FDT_ERRTABENT(FDT_ERR_NOPHANDLES),
};
#define FDT_ERRTABSIZE (sizeof(fdt_errtable) / sizeof(fdt_errtable[0]))
const char *fdt_strerror(int errval)
{
if (errval > 0)
return "<valid offset/length>";
else if (errval == 0)
return "<no error>";
else if (errval > -FDT_ERRTABSIZE) {
const char *s = fdt_errtable[-errval].str;
if (s)
return s;
}
return "<unknown error>";
}

View file

@ -0,0 +1,286 @@
/*
* libfdt - Flat Device Tree manipulation
* Copyright (C) 2006 David Gibson, IBM Corporation.
*
* libfdt is dual licensed: you can use it either under the terms of
* the GPL, or the BSD license, at your option.
*
* a) This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
* MA 02110-1301 USA
*
* Alternatively,
*
* b) Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* 1. Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "libfdt_env.h"
#include "fdt.h"
#include "libfdt.h"
#include "libfdt_internal.h"
static int _fdt_sw_check_header(void *fdt)
{
if (fdt_magic(fdt) != FDT_SW_MAGIC)
return -FDT_ERR_BADMAGIC;
/* FIXME: should check more details about the header state */
return 0;
}
#define FDT_SW_CHECK_HEADER(fdt) \
{ \
int err; \
if ((err = _fdt_sw_check_header(fdt)) != 0) \
return err; \
}
static void *_fdt_grab_space(void *fdt, size_t len)
{
int offset = fdt_size_dt_struct(fdt);
int spaceleft;
spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt)
- fdt_size_dt_strings(fdt);
if ((offset + len < offset) || (offset + len > spaceleft))
return NULL;
fdt_set_size_dt_struct(fdt, offset + len);
return _fdt_offset_ptr_w(fdt, offset);
}
int fdt_create(void *buf, int bufsize)
{
void *fdt = buf;
if (bufsize < sizeof(struct fdt_header))
return -FDT_ERR_NOSPACE;
memset(buf, 0, bufsize);
fdt_set_magic(fdt, FDT_SW_MAGIC);
fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION);
fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION);
fdt_set_totalsize(fdt, bufsize);
fdt_set_off_mem_rsvmap(fdt, FDT_ALIGN(sizeof(struct fdt_header),
sizeof(struct fdt_reserve_entry)));
fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt));
fdt_set_off_dt_strings(fdt, bufsize);
return 0;
}
int fdt_resize(void *fdt, void *buf, int bufsize)
{
size_t headsize, tailsize;
char *oldtail, *newtail;
FDT_SW_CHECK_HEADER(fdt);
headsize = fdt_off_dt_struct(fdt);
tailsize = fdt_size_dt_strings(fdt);
if ((headsize + tailsize) > bufsize)
return -FDT_ERR_NOSPACE;
oldtail = (char *)fdt + fdt_totalsize(fdt) - tailsize;
newtail = (char *)buf + bufsize - tailsize;
/* Two cases to avoid clobbering data if the old and new
* buffers partially overlap */
if (buf <= fdt) {
memmove(buf, fdt, headsize);
memmove(newtail, oldtail, tailsize);
} else {
memmove(newtail, oldtail, tailsize);
memmove(buf, fdt, headsize);
}
fdt_set_off_dt_strings(buf, bufsize);
fdt_set_totalsize(buf, bufsize);
return 0;
}
int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size)
{
struct fdt_reserve_entry *re;
int offset;
FDT_SW_CHECK_HEADER(fdt);
if (fdt_size_dt_struct(fdt))
return -FDT_ERR_BADSTATE;
offset = fdt_off_dt_struct(fdt);
if ((offset + sizeof(*re)) > fdt_totalsize(fdt))
return -FDT_ERR_NOSPACE;
re = (struct fdt_reserve_entry *)((char *)fdt + offset);
re->address = cpu_to_fdt64(addr);
re->size = cpu_to_fdt64(size);
fdt_set_off_dt_struct(fdt, offset + sizeof(*re));
return 0;
}
int fdt_finish_reservemap(void *fdt)
{
return fdt_add_reservemap_entry(fdt, 0, 0);
}
int fdt_begin_node(void *fdt, const char *name)
{
struct fdt_node_header *nh;
int namelen = strlen(name) + 1;
FDT_SW_CHECK_HEADER(fdt);
nh = _fdt_grab_space(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen));
if (! nh)
return -FDT_ERR_NOSPACE;
nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE);
memcpy(nh->name, name, namelen);
return 0;
}
int fdt_end_node(void *fdt)
{
fdt32_t *en;
FDT_SW_CHECK_HEADER(fdt);
en = _fdt_grab_space(fdt, FDT_TAGSIZE);
if (! en)
return -FDT_ERR_NOSPACE;
*en = cpu_to_fdt32(FDT_END_NODE);
return 0;
}
static int _fdt_find_add_string(void *fdt, const char *s)
{
char *strtab = (char *)fdt + fdt_totalsize(fdt);
const char *p;
int strtabsize = fdt_size_dt_strings(fdt);
int len = strlen(s) + 1;
int struct_top, offset;
p = _fdt_find_string(strtab - strtabsize, strtabsize, s);
if (p)
return p - strtab;
/* Add it */
offset = -strtabsize - len;
struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
if (fdt_totalsize(fdt) + offset < struct_top)
return 0; /* no more room :( */
memcpy(strtab + offset, s, len);
fdt_set_size_dt_strings(fdt, strtabsize + len);
return offset;
}
int fdt_property(void *fdt, const char *name, const void *val, int len)
{
struct fdt_property *prop;
int nameoff;
FDT_SW_CHECK_HEADER(fdt);
nameoff = _fdt_find_add_string(fdt, name);
if (nameoff == 0)
return -FDT_ERR_NOSPACE;
prop = _fdt_grab_space(fdt, sizeof(*prop) + FDT_TAGALIGN(len));
if (! prop)
return -FDT_ERR_NOSPACE;
prop->tag = cpu_to_fdt32(FDT_PROP);
prop->nameoff = cpu_to_fdt32(nameoff);
prop->len = cpu_to_fdt32(len);
memcpy(prop->data, val, len);
return 0;
}
int fdt_finish(void *fdt)
{
char *p = (char *)fdt;
fdt32_t *end;
int oldstroffset, newstroffset;
uint32_t tag;
int offset, nextoffset;
FDT_SW_CHECK_HEADER(fdt);
/* Add terminator */
end = _fdt_grab_space(fdt, sizeof(*end));
if (! end)
return -FDT_ERR_NOSPACE;
*end = cpu_to_fdt32(FDT_END);
/* Relocate the string table */
oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt);
newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt));
fdt_set_off_dt_strings(fdt, newstroffset);
/* Walk the structure, correcting string offsets */
offset = 0;
while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) {
if (tag == FDT_PROP) {
struct fdt_property *prop =
_fdt_offset_ptr_w(fdt, offset);
int nameoff;
nameoff = fdt32_to_cpu(prop->nameoff);
nameoff += fdt_size_dt_strings(fdt);
prop->nameoff = cpu_to_fdt32(nameoff);
}
offset = nextoffset;
}
if (nextoffset < 0)
return nextoffset;
/* Finally, adjust the header */
fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt));
fdt_set_magic(fdt, FDT_MAGIC);
return 0;
}

View file

@ -0,0 +1,137 @@
/*
* libfdt - Flat Device Tree manipulation
* Copyright (C) 2006 David Gibson, IBM Corporation.
*
* libfdt is dual licensed: you can use it either under the terms of
* the GPL, or the BSD license, at your option.
*
* a) This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
* MA 02110-1301 USA
*
* Alternatively,
*
* b) Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* 1. Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "libfdt_env.h"
#include "fdt.h"
#include "libfdt.h"
#include "libfdt_internal.h"
int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset,
const char *name, int namelen,
uint32_t idx, const void *val,
int len)
{
void *propval;
int proplen;
propval = fdt_getprop_namelen_w(fdt, nodeoffset, name, namelen,
&proplen);
if (!propval)
return proplen;
if (proplen < (len + idx))
return -FDT_ERR_NOSPACE;
memcpy((char *)propval + idx, val, len);
return 0;
}
int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name,
const void *val, int len)
{
const void *propval;
int proplen;
propval = fdt_getprop(fdt, nodeoffset, name, &proplen);
if (! propval)
return proplen;
if (proplen != len)
return -FDT_ERR_NOSPACE;
return fdt_setprop_inplace_namelen_partial(fdt, nodeoffset, name,
strlen(name), 0,
val, len);
}
static void _fdt_nop_region(void *start, int len)
{
fdt32_t *p;
for (p = start; (char *)p < ((char *)start + len); p++)
*p = cpu_to_fdt32(FDT_NOP);
}
int fdt_nop_property(void *fdt, int nodeoffset, const char *name)
{
struct fdt_property *prop;
int len;
prop = fdt_get_property_w(fdt, nodeoffset, name, &len);
if (! prop)
return len;
_fdt_nop_region(prop, len + sizeof(*prop));
return 0;
}
int _fdt_node_end_offset(void *fdt, int offset)
{
int depth = 0;
while ((offset >= 0) && (depth >= 0))
offset = fdt_next_node(fdt, offset, &depth);
return offset;
}
int fdt_nop_node(void *fdt, int nodeoffset)
{
int endoffset;
endoffset = _fdt_node_end_offset(fdt, nodeoffset);
if (endoffset < 0)
return endoffset;
_fdt_nop_region(fdt_offset_ptr_w(fdt, nodeoffset, 0),
endoffset - nodeoffset);
return 0;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,113 @@
#ifndef _LIBFDT_ENV_H
#define _LIBFDT_ENV_H
/*
* libfdt - Flat Device Tree manipulation
* Copyright (C) 2006 David Gibson, IBM Corporation.
* Copyright 2012 Kim Phillips, Freescale Semiconductor.
*
* libfdt is dual licensed: you can use it either under the terms of
* the GPL, or the BSD license, at your option.
*
* a) This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
* MA 02110-1301 USA
*
* Alternatively,
*
* b) Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* 1. Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#ifdef __CHECKER__
#define FDT_FORCE __attribute__((force))
#define FDT_BITWISE __attribute__((bitwise))
#else
#define FDT_FORCE
#define FDT_BITWISE
#endif
typedef uint16_t FDT_BITWISE fdt16_t;
typedef uint32_t FDT_BITWISE fdt32_t;
typedef uint64_t FDT_BITWISE fdt64_t;
typedef uint64_t unaligned_fdt64_t __attribute__((aligned(4)));
#define EXTRACT_BYTE(x, n) ((unsigned long long)((uint8_t *)&x)[n])
#define CPU_TO_FDT16(x) ((EXTRACT_BYTE(x, 0) << 8) | EXTRACT_BYTE(x, 1))
#define CPU_TO_FDT32(x) ((EXTRACT_BYTE(x, 0) << 24) | (EXTRACT_BYTE(x, 1) << 16) | \
(EXTRACT_BYTE(x, 2) << 8) | EXTRACT_BYTE(x, 3))
#define CPU_TO_FDT64(x) ((EXTRACT_BYTE(x, 0) << 56) | (EXTRACT_BYTE(x, 1) << 48) | \
(EXTRACT_BYTE(x, 2) << 40) | (EXTRACT_BYTE(x, 3) << 32) | \
(EXTRACT_BYTE(x, 4) << 24) | (EXTRACT_BYTE(x, 5) << 16) | \
(EXTRACT_BYTE(x, 6) << 8) | EXTRACT_BYTE(x, 7))
static inline uint16_t fdt16_to_cpu(fdt16_t x)
{
return (FDT_FORCE uint16_t)CPU_TO_FDT16(x);
}
static inline fdt16_t cpu_to_fdt16(uint16_t x)
{
return (FDT_FORCE fdt16_t)CPU_TO_FDT16(x);
}
static inline uint32_t fdt32_to_cpu(fdt32_t x)
{
return (FDT_FORCE uint32_t)CPU_TO_FDT32(x);
}
static inline fdt32_t cpu_to_fdt32(uint32_t x)
{
return (FDT_FORCE fdt32_t)CPU_TO_FDT32(x);
}
static inline uint64_t fdt64_to_cpu(fdt64_t x)
{
return (FDT_FORCE uint64_t)CPU_TO_FDT64(x);
}
static inline fdt64_t cpu_to_fdt64(uint64_t x)
{
return (FDT_FORCE fdt64_t)CPU_TO_FDT64(x);
}
#undef CPU_TO_FDT64
#undef CPU_TO_FDT32
#undef CPU_TO_FDT16
#undef EXTRACT_BYTE
#endif /* _LIBFDT_ENV_H */

View file

@ -0,0 +1,95 @@
#ifndef _LIBFDT_INTERNAL_H
#define _LIBFDT_INTERNAL_H
/*
* libfdt - Flat Device Tree manipulation
* Copyright (C) 2006 David Gibson, IBM Corporation.
*
* libfdt is dual licensed: you can use it either under the terms of
* the GPL, or the BSD license, at your option.
*
* a) This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
* MA 02110-1301 USA
*
* Alternatively,
*
* b) Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* 1. Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "fdt.h"
#define FDT_ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1))
#define FDT_TAGALIGN(x) (FDT_ALIGN((x), FDT_TAGSIZE))
#define FDT_CHECK_HEADER(fdt) \
{ \
int __err; \
if ((__err = fdt_check_header(fdt)) != 0) \
return __err; \
}
int _fdt_check_node_offset(const void *fdt, int offset);
int _fdt_check_prop_offset(const void *fdt, int offset);
const char *_fdt_find_string(const char *strtab, int tabsize, const char *s);
int _fdt_node_end_offset(void *fdt, int nodeoffset);
static inline const void *_fdt_offset_ptr(const void *fdt, int offset)
{
return (const char *)fdt + fdt_off_dt_struct(fdt) + offset;
}
static inline void *_fdt_offset_ptr_w(void *fdt, int offset)
{
return (void *)(uintptr_t)_fdt_offset_ptr(fdt, offset);
}
static inline const struct fdt_reserve_entry *_fdt_mem_rsv(const void *fdt, int n)
{
const struct fdt_reserve_entry *rsv_table =
(const struct fdt_reserve_entry *)
((const char *)fdt + fdt_off_mem_rsvmap(fdt));
return rsv_table + n;
}
static inline struct fdt_reserve_entry *_fdt_mem_rsv_w(void *fdt, int n)
{
return (void *)(uintptr_t)_fdt_mem_rsv(fdt, n);
}
#define FDT_SW_MAGIC (~FDT_MAGIC)
#endif /* _LIBFDT_INTERNAL_H */

View file

@ -0,0 +1,17 @@
from building import *
cwd = GetCurrentDir()
src = Glob('*.c')
list = os.listdir(cwd)
CPPPATH = [cwd + '/../inc' , cwd + '/libfdt']
objs = []
group = DefineGroup('FDT', src, depend = ['RT_USING_FDT'], CPPPATH = CPPPATH)
for d in list:
path = os.path.join(cwd, d)
if os.path.isfile(os.path.join(path, 'SConscript')):
objs = objs + SConscript(os.path.join(d, 'SConscript'))
objs = objs + group
Return('objs')

View file

@ -0,0 +1,648 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "libfdt.h"
#include "dtb_node.h"
/* "/aliaes" node */
static struct dtb_node *fdt_aliases;
/**
* of_find_property_value_of_size() - find property of given size
*
* Search for a property in a device node and validate the requested size.
*
* @np: device node from which the property value is to be read.
* @propname: name of the property to be searched.
* @len: requested length of property value
*
* @return the property value on success, -EINVAL if the property does not
* exist, -ENODATA if property does not have a value, and -EOVERFLOW if the
* property data isn't large enough.
*/
static void *dtb_node_find_property_value_of_size(const struct dtb_node *dn,
const char *propname, uint32_t len)
{
struct dtb_property *prop = dtb_node_get_dtb_node_property(dn, propname, NULL);
if (!prop)
return ERR_PTR(-EINVAL);
if (!prop->value)
return ERR_PTR(-ENODATA);
if (len > prop->size)
return ERR_PTR(-EOVERFLOW);
return prop->value;
}
int dtb_node_read_u32(const struct dtb_node *dn, const char *propname, uint32_t *outp)
{
const uint32_t *val;
debug("%s: %s: \n", __func__, propname);
if (!dn)
return -EINVAL;
val = dtb_node_find_property_value_of_size(dn, propname, sizeof(*outp));
if (IS_ERR(val))
{
debug("(not found)\n");
return PTR_ERR(val);
}
*outp = fdt32_to_cpu(*val);
debug("%#x (%d)\n", *outp, *outp);
return 0;
}
uint32_t dtb_node_read_u32_default(const struct dtb_node *node, const char *propname, uint32_t def)
{
dtb_node_read_u32(node, propname, &def);
return def;
}
int dtb_node_read_u32_array(const struct dtb_node *dn, const char *propname,
uint32_t *out_values, size_t sz)
{
const uint32_t *val;
debug("%s: %s: ", __func__, propname);
val = dtb_node_find_property_value_of_size(dn, propname,
sz * sizeof(*out_values));
if (IS_ERR(val))
return PTR_ERR(val);
debug("size %zd, val:%d\n", sz, *val);
while (sz--)
*out_values++ = fdt32_to_cpu(*val++);
return 0;
}
uint32_t dtb_node_read_u32_index_default(const struct dtb_node *node, const char *propname, int index,
uint32_t def)
{
RT_ASSERT(dtb_node_valid(node));
dtb_node_read_u32_index(node, propname, index, &def);
return def;
}
int dtb_node_read_s32_default(const struct dtb_node *node, const char *propname, int32_t def)
{
RT_ASSERT(dtb_node_valid(node));
dtb_node_read_u32(node, propname, (uint32_t *)&def);
return def;
}
int dtb_node_read_u32_index(const struct dtb_node *dn, const char *propname,
int index, uint32_t *outp)
{
const uint32_t *val;
debug("%s: %s: ", __func__, propname);
if (!dn)
return -EINVAL;
val = dtb_node_find_property_value_of_size(dn, propname,
sizeof(*outp) * (index + 1));
if (IS_ERR(val))
{
debug("(not found)\n");
return PTR_ERR(val);
}
*outp = fdt32_to_cpu(val[index]);
debug("%#x (%d)\n", *outp, *outp);
return 0;
}
int dtb_node_read_u64(const struct dtb_node *dn, const char *propname, uint64_t *outp)
{
const uint64_t *val;
debug("%s: %s: ", __func__, propname);
if (!dn)
return -EINVAL;
val = dtb_node_find_property_value_of_size(dn, propname, sizeof(*outp));
if (IS_ERR(val))
{
debug("(not found)\n");
return PTR_ERR(val);
}
*outp = fdt64_to_cpu(*val);
debug("%#llx (%lld)\n", (unsigned long long)*outp,
(unsigned long long)*outp);
return 0;
}
uint64_t dtb_node_read_u64_default(const struct dtb_node *node, const char *propname, uint64_t def)
{
RT_ASSERT(dtb_node_valid(node));
dtb_node_read_u64(node, propname, &def);
return def;
}
int dtb_node_n_addr_cells(const struct dtb_node *dn)
{
const uint32_t *ip;
do
{
if (dn->parent)
dn = dn->parent;
ip = dtb_node_get_dtb_node_property_value(dn, "#address-cells", NULL);
if (ip)
return fdt32_to_cpu(*ip);
} while (dn->parent);
/* No #address-cells property for the root node */
return DEV_ROOT_NODE_ADDR_CELLS_DEFAULT;
}
int dtb_node_n_size_cells(const struct dtb_node *dn)
{
const uint32_t *ip;
do
{
if (dn->parent)
dn = dn->parent;
ip = dtb_node_get_dtb_node_property_value(dn, "#size-cells", NULL);
if (ip)
return fdt32_to_cpu(*ip);
} while (dn->parent);
/* No #size-cells property for the root node */
return DEV_ROOT_NODE_SIZE_CELLS_DEFAULT;
}
int dtb_node_simple_addr_cells(const struct dtb_node *dn)
{
const uint32_t *ip;
ip = dtb_node_get_dtb_node_property_value(dn, "#address-cells", NULL);
if (ip)
return fdt32_to_cpu(*ip);
/* Return a default of 2 to match fdt_address_cells()*/
return 2;
}
int dtb_node_simple_size_cells(const struct dtb_node *dn)
{
const uint32_t *ip;
ip = dtb_node_get_dtb_node_property_value(dn, "#size-cells", NULL);
if (ip)
return fdt32_to_cpu(*ip);
/* Return a default of 2 to match fdt_size_cells()*/
return 2;
}
struct dtb_property *dtb_node_get_dtb_node_property(const struct dtb_node *dtb_node, const char *property_name, int *property_size)
{
struct dtb_property *dtb_property = NULL;
if (dtb_node != NULL && property_name != NULL)
{
dtb_property = dtb_node->properties;
while (dtb_property != NULL)
{
if (!strcmp(dtb_property->name, property_name))
{
if (property_size != NULL)
{
*property_size = dtb_property->size;
}
return dtb_property;
}
dtb_property = dtb_property->next;
}
}
return dtb_property;
}
#define for_each_property_of_node(dn, pp) \
for (pp = dn->properties; pp != NULL; pp = pp->next)
struct dtb_node *dtb_node_find_node_opts_by_path(const char *path,
const char **opts)
{
struct dtb_node *np = NULL;
struct dtb_property *pp;
const char *separator = strchr(path, ':');
if (opts)
*opts = separator ? separator + 1 : NULL;
if (strcmp(path, "/") == 0)
return dtb_node_get(get_dtb_node_head());
/* The path could begin with an alias */
if (*path != '/')
{
int len;
const char *p = separator;
if (!p)
p = strchrnul(path, '/');
len = p - path;
/* of_aliases must not be NULL */
if (!fdt_aliases)
return NULL;
for_each_property_of_node(fdt_aliases, pp)
{
if (strlen(pp->name) == len && !strncmp(pp->name, path,
len))
{
np = dtb_node_find_node_by_path(pp->value);
break;
}
}
if (!np)
return NULL;
path = p;
}
/* Step down the tree matching path components */
if (!np)
np = dtb_node_get(get_dtb_node_head());
while (np && *path == '/')
{
struct dtb_node *tmp = np;
path++; /* Increment past '/' delimiter */
np = dtb_node_get_dtb_node_by_path(np, path);
dtb_node_put(tmp);
path = strchrnul(path, '/');
if (separator && separator < path)
break;
}
return np;
}
struct dtb_node *dtb_node_find_compatible_node(struct dtb_node *from, const char *compatible)
{
struct dtb_node *dn;
for_each_of_allnodes_from(from, dn)
{
if (dtb_node_get_dtb_node_compatible_match(dn, compatible) &&
dtb_node_get(dn))
break;
}
dtb_node_put(from);
return dn;
}
void *dtb_node_get_dtb_node_property_value(const struct dtb_node *dtb_node, const char *property_name, int *property_size)
{
struct dtb_property *dtb_property = dtb_node_get_dtb_node_property(dtb_node, property_name, NULL);
if (!dtb_property || !dtb_property->value || !dtb_property->size)
{
return NULL;
}
if (property_size != NULL)
{
*property_size = dtb_property->size;
}
return dtb_property->value;
}
const struct dtb_node *dtb_node_find_node_by_prop_value(struct dtb_node *from,
const char *propname,
const void *propval, int proplen)
{
struct dtb_node *np;
void *value;
for_each_of_allnodes_from(from, np)
{
value = dtb_node_get_dtb_node_property_value(np, propname, &proplen);
if (!memcmp(value, propval, proplen) && dtb_node_get(np))
break;
}
dtb_node_put(from);
return np;
}
struct dtb_node *dtb_node_find_all_nodes(const struct dtb_node *prev)
{
const struct dtb_node *dn;
if (!prev)
{
dn = get_dtb_node_head();
}
else if (prev->child)
{
dn = prev->child;
}
else
{
/*
* Walk back up looking for a sibling, or the end of the
* structure
*/
dn = prev;
while (dn->parent && !dn->sibling)
dn = dn->parent;
dn = dn->sibling; /* Might be null at the end of the tree */
}
return (struct dtb_node *)dn;
}
rt_bool_t dtb_node_device_is_available(const struct dtb_node *device)
{
const char *status;
int statlen;
if (!device)
return RT_FALSE;
status = dtb_node_get_dtb_node_property_value(device, "status", &statlen);
if (status == NULL)
return RT_TRUE;
if (statlen > 0)
{
if (!strcmp(status, "okay"))
return RT_TRUE;
}
return RT_FALSE;
}
struct dtb_node *dtb_node_get_parent(const struct dtb_node *node)
{
const struct dtb_node *dn;
if (!node)
return NULL;
dn = dtb_node_get(node->parent);
return (struct dtb_node *)dn;
}
struct dtb_node *dtb_node_find_node_by_phandle(phandle handle)
{
struct dtb_node *dn;
if (!handle)
return NULL;
for_each_of_allnodes(dn) if (dn->handle == handle) break;
(void)dtb_node_get(dn);
return dn;
}
int dtb_node_property_match_string(const struct dtb_node *dn, const char *propname,
const char *string)
{
const struct dtb_property *prop = dtb_node_get_dtb_node_property(dn, propname, NULL);
size_t l;
int i;
const char *p, *end;
if (!prop)
return -EINVAL;
if (!prop->value)
return -ENODATA;
p = prop->value;
end = p + prop->size;
for (i = 0; p < end; i++, p += l)
{
l = strnlen(p, end - p) + 1;
if (p + l > end)
return -EILSEQ;
debug("comparing %s with %s\n", string, p);
if (strcmp(string, p) == 0)
return i; /* Found it; return index */
}
return -ENODATA;
}
/**
* of_property_read_string_helper() - Utility helper for parsing string properties
* @np: device node from which the property value is to be read.
* @propname: name of the property to be searched.
* @out_strs: output array of string pointers.
* @sz: number of array elements to read.
* @skip: Number of strings to skip over at beginning of list.
*
* Don't call this function directly. It is a utility helper for the
* of_property_read_string*() family of functions.
*/
int dtb_node_property_read_string_helper(const struct dtb_node *dn,
const char *propname, const char **out_strs,
size_t sz, int skip)
{
const struct dtb_property *prop = dtb_node_get_dtb_node_property(dn, propname, NULL);
int l = 0, i = 0;
const char *p, *end;
if (!prop)
return -EINVAL;
if (!prop->value)
return -ENODATA;
p = prop->value;
end = p + prop->size;
for (i = 0; p < end && (!out_strs || i < skip + sz); i++, p += l)
{
l = strnlen(p, end - p) + 1;
if (p + l > end)
return -EILSEQ;
if (out_strs && i >= skip)
*out_strs++ = p;
}
i -= skip;
return i <= 0 ? -ENODATA : i;
}
static int __dtb_node_parse_phandle_with_args(const struct dtb_node *dn,
const char *list_name,
const char *cells_name,
int cell_count, int index,
struct fdt_phandle_args *out_args)
{
const uint32_t *list, *list_end;
int rc = 0, cur_index = 0;
uint32_t count = 0;
struct dtb_node *node = NULL;
phandle phandle;
int size;
/* Retrieve the phandle list property */
list = dtb_node_get_dtb_node_property_value(dn, list_name, &size);
if (!list)
return -ENOENT;
list_end = list + size / sizeof(*list);
/* Loop over the phandles until all the requested entry is found */
while (list < list_end)
{
rc = -EINVAL;
count = 0;
/*
* If phandle is 0, then it is an empty entry with no
* arguments. Skip forward to the next entry.
*/
phandle = fdt32_to_cpu(*(list++));
if (phandle)
{
/*
* Find the provider node and parse the #*-cells
* property to determine the argument length.
*
* This is not needed if the cell count is hard-coded
* (i.e. cells_name not set, but cell_count is set),
* except when we're going to return the found node
* below.
*/
if (cells_name || cur_index == index)
{
node = dtb_node_find_node_by_phandle(phandle);
if (!node)
{
debug("%s: could not find phandle\n",
dn->path);
goto err;
}
}
if (cells_name)
{
if (dtb_node_read_u32(node, cells_name, &count))
{
debug("%s: could not get %s for %s\n",
dn->path, cells_name,
node->path);
goto err;
}
}
else
{
count = cell_count;
}
/*
* Make sure that the arguments actually fit in the
* remaining property data length
*/
if (list + count > list_end)
{
debug("%s: arguments longer than property\n",
dn->path);
goto err;
}
}
/*
* All of the error cases above bail out of the loop, so at
* this point, the parsing is successful. If the requested
* index matches, then fill the out_args structure and return,
* or return -ENOENT for an empty entry.
*/
rc = -ENOENT;
if (cur_index == index)
{
if (!phandle)
goto err;
if (out_args)
{
int i;
if (count > FDT_MAX_PHANDLE_ARGS)
count = FDT_MAX_PHANDLE_ARGS;
out_args->np = node;
out_args->args_count = count;
for (i = 0; i < count; i++)
out_args->args[i] =
fdt32_to_cpu(*(list++));
}
else
{
dtb_node_put(node);
}
/* Found it! return success */
return 0;
}
dtb_node_put(node);
node = NULL;
list += count;
cur_index++;
}
/*
* Unlock node before returning result; will be one of:
* -ENOENT : index is for empty phandle
* -EINVAL : parsing error on data
* [1..n] : Number of phandle (count mode; when index = -1)
*/
rc = index < 0 ? cur_index : -ENOENT;
err:
if (node)
dtb_node_put(node);
return rc;
}
struct dtb_node *dtb_node_parse_phandle(const struct dtb_node *dn,
const char *phandle_name, int index)
{
struct fdt_phandle_args args;
if (index < 0)
return NULL;
if (__dtb_node_parse_phandle_with_args(dn, phandle_name, NULL, 0, index,
&args))
return NULL;
return args.np;
}
int dtb_node_parse_phandle_with_args(const struct dtb_node *dn,
const char *list_name, const char *cells_name,
int index, struct fdt_phandle_args *out_args)
{
if (index < 0)
return -EINVAL;
return __dtb_node_parse_phandle_with_args(dn, list_name, cells_name, 0,
index, out_args);
}
int dtb_node_count_phandle_with_args(const struct dtb_node *dn,
const char *list_name, const char *cells_name)
{
return __dtb_node_parse_phandle_with_args(dn, list_name, cells_name, 0,
-1, NULL);
}

View file

@ -0,0 +1,57 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "libfdt.h"
#include "dtb_node.h"
/* Max address size we deal with */
#define FDT_MAX_ADDR_CELLS 4
#define FDT_CHECK_ADDR_COUNT(na) ((na) > 0 && (na) <= FDT_MAX_ADDR_CELLS)
#define FDT_CHECK_COUNTS(na, ns) (FDT_CHECK_ADDR_COUNT(na) && (ns) > 0)
static void dtb_node_default_count_cells(const struct dtb_node *dn,
int *addrc, int *sizec)
{
if (addrc)
*addrc = dtb_node_n_addr_cells(dn);
if (sizec)
*sizec = dtb_node_n_size_cells(dn);
}
const uint32_t *dtb_node_get_address(const struct dtb_node *dev, int index,
uint64_t *size, unsigned int *flags)
{
const uint32_t *prop;
int psize;
struct dtb_node *parent;
int onesize, i, na, ns;
/* Get parent */
parent = dtb_node_get_parent(dev);
if (parent == NULL)
return NULL;
dtb_node_default_count_cells(dev, &na, &ns);
if (!FDT_CHECK_ADDR_COUNT(na))
return NULL;
/* Get "reg" or "assigned-addresses" property */
prop = dtb_node_get_dtb_node_property_value(dev, "reg", &psize);
if (prop == NULL)
return NULL;
psize /= 4;
onesize = na + ns;
for (i = 0; psize >= onesize; psize -= onesize, prop += onesize, i++)
if (i == index)
{
if (size)
*size = dtb_node_read_number(prop + na, ns);
if (flags)
*flags = 0x200;
return prop;
}
return NULL;
}

View file

@ -0,0 +1,574 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "dtb_node.h"
#include "libfdt.h"
#include "libfdt_env.h"
#include <ctype.h>
static const char *_parse_integer_fixup_radix(const char *s, unsigned int *base)
{
if (*base == 0)
{
if (s[0] == '0')
{
if (tolower(s[1]) == 'x' && isxdigit((int)s[2]))
*base = 16;
else
*base = 8;
}
else
*base = 10;
}
if (*base == 16 && s[0] == '0' && tolower(s[1]) == 'x')
s += 2;
return s;
}
unsigned long simple_strtoul(const char *cp, char **endp,
unsigned int base)
{
unsigned long result = 0;
unsigned long value;
cp = _parse_integer_fixup_radix(cp, &base);
while (isxdigit((int)*cp) && (value = isdigit((int)*cp) ? *cp-'0' : (islower((int)*cp)
? toupper(*cp) : *cp)-'A'+10) < base)
{
result = result*base + value;
cp++;
}
if (endp)
*endp = (char *)cp;
return result;
}
int strict_strtoul(const char *cp, unsigned int base, unsigned long *res)
{
char *tail;
unsigned long val;
size_t len;
*res = 0;
len = strlen(cp);
if (len == 0)
return -EINVAL;
val = simple_strtoul(cp, &tail, base);
if (tail == cp)
return -EINVAL;
if ((*tail == '\0') ||
((len == (size_t)(tail - cp) + 1) && (*tail == '\n')))
{
*res = val;
return 0;
}
return -EINVAL;
}
long simple_strtol(const char *cp, char **endp, unsigned int base)
{
if (*cp == '-')
return -simple_strtoul(cp + 1, endp, base);
return simple_strtoul(cp, endp, base);
}
rt_bool_t dtb_node_read_bool(const struct dtb_node *node, const char *propname)
{
const void *prop;
RT_ASSERT(dtb_node_valid(node));
debug("%s: %s: ", __func__, propname);
prop = dtb_node_get_property(node, propname, NULL);
debug("%s\n", prop ? "true" : "false");
return prop ? RT_TRUE : RT_FALSE;
}
const void *dtb_node_read_prop(const struct dtb_node *node, const char *propname, int *sizep)
{
const char *val = NULL;
int len;
RT_ASSERT(dtb_node_valid(node));
debug("%s: %s: ", __func__, propname);
struct dtb_property *prop = dtb_node_get_dtb_node_property(node, propname, &len);
if (prop)
{
val = prop->value;
len = prop->size;
}
if (!val)
{
debug("<not found>\n");
if (sizep)
*sizep = -FDT_ERR_NOTFOUND;
return NULL;
}
if (sizep)
*sizep = len;
return val;
}
const char *dtb_node_read_string(const struct dtb_node *node, const char *propname)
{
const char *str;
int len;
str = dtb_node_read_prop(node, propname, &len);
if (!str)
return NULL;
if (strnlen(str, len) >= len)
{
debug("<invalid>\n");
return NULL;
}
debug("%s\n", str);
return str;
}
const struct dtb_node *dtb_node_find_subnode(const struct dtb_node *node, const char *subnode_name)
{
const struct dtb_node *subnode;
RT_ASSERT(dtb_node_valid(node));
debug("%s: %s: ", __func__, subnode_name);
for (node = node->child; node; node = node->sibling)
{
if (!strcmp(subnode_name, node->name))
break;
}
subnode = node;
debug("%s\n", dtb_node_valid(subnode) ?\
dtb_node_get_name(subnode) : "<none>");
return subnode;
}
struct dtb_node *dtb_node_first_subnode(const struct dtb_node *node)
{
RT_ASSERT(dtb_node_valid(node));
return node->child;
}
struct dtb_node *dtb_node_next_subnode(const struct dtb_node *node)
{
RT_ASSERT(dtb_node_valid(node));
return node->sibling;
}
const char *dtb_node_get_name(const struct dtb_node *node)
{
if (!dtb_node_valid(node))
{
debug("%s node not valid\n", __func__);
return NULL;
}
return strrchr(node->path, '/') + 1;
}
struct dtb_node *dtb_node_get_by_phandle(uint32_t phandle)
{
if (dtb_node_active())
return dtb_node_find_node_by_phandle(phandle);
return NULL;
}
int dtb_node_read_size(const struct dtb_node *node, const char *propname)
{
struct dtb_property *prop = dtb_node_get_dtb_node_property( node, propname, NULL);
if (prop)
return prop->size;
return -EINVAL;
}
int dtb_node_get_addr_and_size_by_index(const struct dtb_node *node, int index, size_t *addr, size_t *size)
{
const uint32_t *prop;
int psize;
int onesize, na, ns;
na = dtb_node_n_addr_cells(node);
ns = dtb_node_n_size_cells(node);
prop = dtb_node_get_dtb_node_property_value(node, "reg", &psize);
if (prop == NULL)
{
return -1;
}
psize /= 4;
onesize = na + ns;
if (psize >= (index + 1) * onesize)
{
prop += index * onesize;
if (addr)
{
*addr = dtb_node_read_number(prop, na);
}
if (size)
{
*size = dtb_node_read_number(prop + na, ns);
}
return 0;
}
return -1;
}
size_t dtb_node_get_addr_index(const struct dtb_node *node, int index)
{
int na;
size_t size;
const uint32_t *prop_val;
uint flags;
prop_val = dtb_node_get_address(node, index,
(uint64_t *)&size, &flags);
if (!prop_val)
return -1;
na = dtb_node_n_addr_cells(node);
return dtb_node_read_number(prop_val, na);
}
size_t dtb_node_get_addr(const struct dtb_node *node)
{
return dtb_node_get_addr_index(node, 0);
}
int dtb_node_stringlist_search(const struct dtb_node *node, const char *property,
const char *string)
{
return dtb_node_property_match_string(node, property, string);
}
int dtb_node_read_string_index(const struct dtb_node *node, const char *property, int index,
const char **outp)
{
return dtb_node_property_read_string_index(node, property, index, outp);
}
int dtb_node_read_string_count(const struct dtb_node *node, const char *property)
{
return dtb_node_property_count_strings(node, property);
}
struct dtb_node *dtb_node_path(const char *path)
{
if (dtb_node_active())
return dtb_node_find_node_by_path(path);
return NULL;
}
const char *dtb_node_get_chosen_prop(const char *name)
{
const struct dtb_node *chosen_node;
chosen_node = (const struct dtb_node *)dtb_node_path("/chosen");
return dtb_node_read_string(chosen_node, name);
}
struct dtb_node *dtb_node_get_chosen_node(const char *name)
{
const char *prop;
prop = dtb_node_get_chosen_prop(name);
if (!prop)
return NULL;
return dtb_node_path(prop);
}
const void *dtb_node_get_property(const struct dtb_node *node, const char *propname, int *lenp)
{
return dtb_node_get_dtb_node_property_value(node, propname, lenp);
}
rt_bool_t dtb_node_is_available(const struct dtb_node *node)
{
return dtb_node_device_is_available(node);
}
size_t dtb_node_get_addr_size(const struct dtb_node *node, const char *property,
size_t *sizep)
{
int na, ns;
int psize;
const uint32_t *prop = dtb_node_get_dtb_node_property_value(node, property, &psize);
if (!prop)
return -1;
na = dtb_node_n_addr_cells(node);
ns = dtb_node_n_size_cells(node);
*sizep = dtb_node_read_number(prop + na, ns);
return dtb_node_read_number(prop, na);
}
const uint8_t *dtb_node_read_u8_array_ptr(const struct dtb_node *node, const char *propname,
size_t sz)
{
int psize;
const uint32_t *prop = dtb_node_get_dtb_node_property_value(node, propname, &psize);
if (!prop || sz != psize)
return NULL;
return (uint8_t *)prop;
}
int dtb_node_find_all_compatible_node(const struct dtb_node *from, const char *compatible, struct dtb_node **node_table, int max_num, int *node_num)
{
const struct dtb_node *dn;
int num = 0;
for_each_of_allnodes_from(from, dn)
{
if (dtb_node_get_dtb_node_compatible_match(dn, compatible) &&
dtb_node_get(dn))
{
num++;
*node_table = (struct dtb_node *)dn;
node_table++;
if (num >= max_num)
{
break;
}
}
}
*node_num = num;
dtb_node_put(from);
return 0;
}
int dtb_node_write_prop(const struct dtb_node *node, const char *propname, int len,
const void *value)
{
struct dtb_property *pp;
struct dtb_property *pp_last = NULL;
struct dtb_property *new;
if (!dtb_node_active())
return -ENOSYS;
if (!node)
return -EINVAL;
for (pp = node->properties; pp; pp = pp->next)
{
if (strcmp(pp->name, propname) == 0)
{
/* Property exists -> change value */
pp->value = (void *)value;
pp->size = len;
return 0;
}
pp_last = pp;
}
if (!pp_last)
return -ENOENT;
/* Property does not exist -> append new property */
new = (struct dtb_property *)malloc(sizeof(struct dtb_property));
if (!new)
return -ENOMEM;
new->name = strdup(propname);
if (!new->name)
{
free(new);
return -ENOMEM;
}
new->value = (void *)value;
new->size = len;
new->next = NULL;
pp_last->next = new;
return 0;
}
int dtb_node_write_string(const struct dtb_node *node, const char *propname, const char *value)
{
if (!dtb_node_active())
return -ENOSYS;
RT_ASSERT(dtb_node_valid(node));
debug("%s: %s = %s", __func__, propname, value);
return dtb_node_write_prop(node, propname, strlen(value) + 1, value);
}
int dtb_node_set_enabled(const struct dtb_node *node, rt_bool_t value)
{
if (!dtb_node_active())
return -ENOSYS;
RT_ASSERT(dtb_node_valid(node));
if (value)
return dtb_node_write_string(node, "status", "okay");
else
return dtb_node_write_string(node, "status", "disable");
}
/**
* dtb_node_irq_find_parent - Given a device node, find its interrupt parent node
* @child: pointer to device node
*
* Returns a pointer to the interrupt parent node, or NULL if the interrupt
* parent could not be determined.
*/
static struct dtb_node *dtb_node_irq_find_parent(struct dtb_node *child)
{
struct dtb_node *p;
phandle parent;
if (!dtb_node_get(child))
return NULL;
do
{
if (dtb_node_read_u32_array(child, "interrupt-parent", &parent, 1))
{
p = dtb_node_get_parent(child);
}
else
{
p = dtb_node_get_by_phandle(parent);
}
dtb_node_put(child);
child = p;
} while (p && dtb_node_get_property(p, "#interrupt-cells", NULL) == NULL);
return p;
}
int dtb_node_irq_get(struct dtb_node *dev, int index)
{
int rc = 0;
struct fdt_phandle_args out_irq;
struct dtb_node *p;
uint32_t intsize;
int res, i;
p = dtb_node_irq_find_parent(dev);
if (p == NULL)
return -EINVAL;
/* Get size of interrupt specifier */
if (dtb_node_read_u32_array(p, "#interrupt-cells", &intsize, 1))
{
res = -EINVAL;
goto out;
}
debug(" path:%s, parent=%pOF, intsize=%d\n", p->path,p, intsize);
/* Copy intspec into irq structure */
out_irq.np = p;
out_irq.args_count = intsize;
for (i = 0; i < intsize; i++)
{
res = dtb_node_read_u32_index(dev, "interrupts",
(index * 3 + i),
out_irq.args + i);
if (res)
goto out;
}
rc = out_irq.args[1];
out:
dtb_node_put(p);
return rc;
}
/**
* dtb_node_irq_get_byname - Decode a node's IRQ and return it as a Linux IRQ number
* @dev: pointer to device tree node
* @name: IRQ name
*
* Returns Linux IRQ number on success, or 0 on the IRQ mapping failure, or
* -EPROBE_DEFER if the IRQ domain is not yet created, or error code in case
* of any other failure.
*/
int dtb_node_irq_get_byname(struct dtb_node *dev, const char *name)
{
int index;
if (!name)
return -EINVAL;
index = dtb_node_stringlist_search(dev, "interrupt-names", name);
if (index < 0)
return index;
return dtb_node_irq_get(dev, index);
}
/**
* dtb_node_irq_count - Count the number of IRQs a node uses
* @dev: pointer to device tree node
*/
int dtb_node_irq_count(struct dtb_node *device)
{
struct dtb_node *p;
uint32_t intsize;
int nr = 0, res = 0;
p = dtb_node_irq_find_parent(device);
if (p == NULL)
return -EINVAL;
/* Get size of interrupt specifier */
if (dtb_node_read_u32_array(p, "#interrupt-cells", &intsize, 1))
{
res = -EINVAL;
goto out;
}
debug(" path:%s, parent=%pOF, intsize=%d\n", p->path,p, intsize);
res = dtb_node_read_size(device, "interrupts");
if (res < 0)
{
goto out;
}
nr = res / (intsize * 4);
out:
dtb_node_put(p);
return nr;
}

View file

@ -0,0 +1,820 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "libfdt.h"
#include "dtb_node.h"
static struct
{
const char *ptr;
const char *end;
char *cur;
} paths_buf = {NULL, NULL};
static void *current_fdt;
int fdt_exec_status = FDT_RET_GET_OK;
int dtb_node_get_exec_status()
{
return fdt_exec_status;
}
static int _dtb_node_get_dtb_properties_list(struct dtb_property *dtb_property, off_t node_off)
{
/* caller alrealy checked current_fdt */
off_t property_off = fdt_first_property_offset(current_fdt, node_off);
struct fdt_property *fdt_property;
if (property_off < 0)
{
return FDT_RET_GET_EMPTY;
}
for (;;)
{
fdt_property = (struct fdt_property *)fdt_get_property_by_offset(current_fdt, property_off, &dtb_property->size);
if (fdt_property != NULL)
{
dtb_property->name = fdt_string(current_fdt, fdt32_to_cpu(fdt_property->nameoff));
dtb_property->value = fdt_property->data;
dtb_property->size = fdt32_to_cpu(fdt_property->len);
}
property_off = fdt_next_property_offset(current_fdt, property_off);
if (property_off >= 0)
{
dtb_property->next = (struct dtb_property *)malloc(sizeof(struct dtb_property));
if (dtb_property->next == NULL)
{
return FDT_RET_NO_MEMORY;
}
dtb_property = dtb_property->next;
}
else
{
dtb_property->next = NULL;
break;
}
}
return FDT_RET_GET_OK;
}
static int _dtb_node_get_dtb_nodes_list(struct dtb_node *dtb_node_head, struct dtb_node *dtb_node, const char *pathname)
{
off_t root_off;
off_t node_off;
int pathname_sz;
int node_name_sz;
/* caller alrealy checked current_fdt */
if ((root_off = fdt_path_offset(current_fdt, pathname)) >= 0)
{
pathname_sz = strlen(pathname);
node_off = fdt_first_subnode(current_fdt, root_off);
if (node_off < 0)
{
return FDT_RET_GET_EMPTY;
}
for (;;)
{
dtb_node->parent = dtb_node_head;
dtb_node->sibling = NULL;
dtb_node->name = fdt_get_name(current_fdt, node_off, &node_name_sz);
/* parent_path + name + '/' + '\0' */
if (paths_buf.cur + pathname_sz + node_name_sz + 2 < paths_buf.end)
{
dtb_node->path = (const char *)paths_buf.cur;
strncpy(paths_buf.cur, pathname, pathname_sz);
paths_buf.cur += pathname_sz;
strncpy(paths_buf.cur, (char *)dtb_node->name, node_name_sz);
paths_buf.cur += node_name_sz;
*paths_buf.cur++ = '/';
*paths_buf.cur++ = '\0';
}
else
{
dtb_node->path = NULL;
rt_kprintf("\033[31m\rERROR: `FDT_DTB_ALL_NODES_PATH_SIZE' = %d bytes is configured too low.\033[0m\n", FDT_DTB_ALL_NODES_PATH_SIZE);
return FDT_RET_NO_MEMORY;
}
dtb_node->handle = fdt_get_phandle(current_fdt, node_off);
dtb_node->properties = (struct dtb_property *)malloc(sizeof(struct dtb_property));
dtb_node->child = (struct dtb_node *)malloc(sizeof(struct dtb_node));
if (dtb_node->properties == NULL || dtb_node->child == NULL)
{
return FDT_RET_NO_MEMORY;
}
fdt_exec_status = _dtb_node_get_dtb_properties_list(dtb_node->properties, node_off);
if (fdt_exec_status == FDT_RET_GET_EMPTY)
{
free(dtb_node->properties);
dtb_node->properties = NULL;
}
else if (fdt_exec_status != FDT_RET_GET_OK)
{
return fdt_exec_status;
}
fdt_exec_status = _dtb_node_get_dtb_nodes_list(dtb_node, dtb_node->child, dtb_node->path);
if (fdt_exec_status == FDT_RET_GET_EMPTY)
{
free(dtb_node->child);
dtb_node->child = NULL;
}
else if (fdt_exec_status != FDT_RET_GET_OK)
{
return fdt_exec_status;
}
node_off = fdt_next_subnode(current_fdt, node_off);
if (node_off >= 0)
{
dtb_node->sibling = (struct dtb_node *)malloc(sizeof(struct dtb_node));
if (dtb_node->sibling == NULL)
{
return FDT_RET_NO_MEMORY;
}
dtb_node = dtb_node->sibling;
}
else
{
break;
}
}
}
return FDT_RET_GET_OK;
}
struct dtb_node *dtb_node_get_dtb_list(void *fdt)
{
int root_off;
struct dtb_node *dtb_node_head = NULL;
if (fdt == NULL)
{
fdt_exec_status = FDT_RET_NO_LOADED;
goto fail;
}
current_fdt = fdt;
if ((dtb_node_head = (struct dtb_node *)malloc(sizeof(struct dtb_node))) == NULL)
{
fdt_exec_status = FDT_RET_NO_MEMORY;
goto fail;
}
if (paths_buf.ptr == NULL)
{
paths_buf.ptr = (char *)malloc(FDT_DTB_ALL_NODES_PATH_SIZE);
if (paths_buf.ptr == NULL)
{
fdt_exec_status = FDT_RET_NO_MEMORY;
goto fail;
}
paths_buf.cur = (char *)paths_buf.ptr;
paths_buf.end = (char *)(paths_buf.ptr + FDT_DTB_ALL_NODES_PATH_SIZE);
}
root_off = fdt_path_offset(fdt, "/");
if ((dtb_node_head->header = (struct dtb_header *)malloc(sizeof(struct dtb_header))) == NULL)
{
fdt_exec_status = FDT_RET_NO_MEMORY;
goto fail;
}
else
{
((struct dtb_header *)dtb_node_head->header)->root = '/';
((struct dtb_header *)dtb_node_head->header)->zero = '\0';
((struct dtb_header *)dtb_node_head->header)->memreserve_sz = fdt_num_mem_rsv(fdt);
if (dtb_node_head->header->memreserve_sz > 0)
{
int i;
int memreserve_sz = dtb_node_head->header->memreserve_sz;
uint32_t off_mem_rsvmap = fdt_off_mem_rsvmap(fdt);
struct fdt_reserve_entry *rsvmap = (struct fdt_reserve_entry *)((char *)fdt + off_mem_rsvmap);
((struct dtb_header *)dtb_node_head->header)->memreserve = (struct dtb_memreserve *)malloc(sizeof(struct dtb_memreserve) * memreserve_sz);
if (dtb_node_head->header->memreserve == NULL)
{
fdt_exec_status = FDT_RET_NO_MEMORY;
goto fail;
}
for (i = 0; i < memreserve_sz; ++i)
{
((struct dtb_header *)dtb_node_head->header)->memreserve[i].address = fdt64_to_cpu(rsvmap[i].address);
((struct dtb_header *)dtb_node_head->header)->memreserve[i].size = fdt64_to_cpu(rsvmap[i].size);
}
}
else
{
((struct dtb_header *)dtb_node_head->header)->memreserve = NULL;
}
}
dtb_node_head->path = paths_buf.ptr;
*paths_buf.cur++ = '/';
*paths_buf.cur++ = '\0';
dtb_node_head->parent = NULL;
dtb_node_head->sibling = NULL;
dtb_node_head->handle = fdt_get_phandle(fdt, root_off);
dtb_node_head->properties = (struct dtb_property *)malloc(sizeof(struct dtb_property));
dtb_node_head->child = (struct dtb_node *)malloc(sizeof(struct dtb_node));
if (dtb_node_head->properties == NULL || dtb_node_head->child == NULL)
{
fdt_exec_status = FDT_RET_NO_MEMORY;
goto fail;
}
if ((fdt_exec_status = _dtb_node_get_dtb_properties_list(dtb_node_head->properties, root_off)) != FDT_RET_GET_OK)
{
goto fail;
}
if ((fdt_exec_status = _dtb_node_get_dtb_nodes_list(dtb_node_head, dtb_node_head->child, dtb_node_head->path)) != FDT_RET_GET_OK)
{
goto fail;
}
/* paths_buf.ptr addr save in the dtb_node_head's path */
paths_buf.ptr = NULL;
paths_buf.cur = NULL;
return dtb_node_head;
fail:
if (dtb_node_head != NULL)
{
dtb_node_free_dtb_list(dtb_node_head);
}
return NULL;
}
static void _dtb_node_free_dtb_node(struct dtb_node *dtb_node)
{
struct dtb_node *dtb_node_last;
struct dtb_property *dtb_property;
struct dtb_property *dtb_property_last;
while (dtb_node != NULL)
{
dtb_property = dtb_node->properties;
while (dtb_property != NULL)
{
dtb_property_last = dtb_property;
dtb_property = dtb_property->next;
free(dtb_property_last);
}
_dtb_node_free_dtb_node(dtb_node->child);
dtb_node_last = dtb_node;
dtb_node = dtb_node->sibling;
free(dtb_node_last);
}
}
void dtb_node_free_dtb_list(struct dtb_node *dtb_node_head)
{
if (dtb_node_head == NULL)
{
return;
}
/* only root node can free all path buffer */
if (dtb_node_head->parent == NULL || (dtb_node_head->path != NULL && !strcmp(dtb_node_head->path, "/")))
{
if (dtb_node_head->path != NULL)
{
free((void *)dtb_node_head->path);
}
if (dtb_node_head->header != NULL)
{
if (dtb_node_head->header->memreserve != NULL)
{
free((void *)dtb_node_head->header->memreserve);
}
free((void *)dtb_node_head->header);
}
}
_dtb_node_free_dtb_node(dtb_node_head);
}
static void _dtb_node_printf_depth(int depth)
{
int i = depth;
while (i --> 0)
{
rt_kputs("\t");
}
}
rt_bool_t _dtb_node_test_string_list(const void *value, int size)
{
const char *str = value;
const char *str_start, *str_end;
if (size == 0)
{
return RT_FALSE;
}
/* string end with '\0' */
if (str[size - 1] != '\0')
{
return RT_FALSE;
}
/* get string list end */
str_end = str + size;
while (str < str_end)
{
str_start = str;
/* before string list end, not '\0' and a printable characters */
while (str < str_end && *str && ((unsigned char)*str >= ' ' && (unsigned char)*str <= '~'))
{
++str;
}
/* not zero, or not increased */
if (*str != '\0' || str == str_start)
{
return RT_FALSE;
}
/* next string */
++str;
}
return RT_TRUE;
}
static void _dtb_node_printf_dtb_node_info(struct dtb_node *dtb_node)
{
static int depth = 0;
struct dtb_property *dtb_property;
while (dtb_node != NULL)
{
rt_kputs("\n");
_dtb_node_printf_depth(depth);
rt_kputs(dtb_node->name);
rt_kputs(" {\n");
++depth;
dtb_property = dtb_node->properties;
while (dtb_property != NULL)
{
_dtb_node_printf_depth(depth);
rt_kputs(dtb_property->name);
if (dtb_property->size > 0)
{
int size = dtb_property->size;
char *value = dtb_property->value;
rt_kputs(" = ");
if (_dtb_node_test_string_list(value, size) == RT_TRUE)
{
/* print string list */
char *str = value;
do
{
rt_kprintf("\"%s\"", str);
str += strlen(str) + 1;
rt_kputs(", ");
} while (str < value + size);
rt_kputs("\b\b");
}
else if ((size % 4) == 0)
{
/* print addr and size cell */
int i;
fdt32_t *cell = (fdt32_t *)value;
rt_kputs("<");
for (i = 0, size /= 4; i < size; ++i)
{
rt_kprintf("0x%x ", fdt32_to_cpu(cell[i]));
}
rt_kputs("\b>");
}
else
{
/* print bytes array */
int i;
uint8_t *byte = (uint8_t *)value;
rt_kputs("[");
for (i = 0; i < size; ++i)
{
rt_kprintf("%02x ", *byte++);
}
rt_kputs("\b]");
}
}
rt_kputs(";\n");
dtb_property = dtb_property->next;
}
_dtb_node_printf_dtb_node_info(dtb_node->child);
dtb_node = dtb_node->sibling;
--depth;
_dtb_node_printf_depth(depth);
rt_kputs("};\n");
}
}
void dtb_node_get_dts_dump(struct dtb_node *dtb_node_head)
{
if (dtb_node_head != NULL)
{
int i = dtb_node_head->header->memreserve_sz;
rt_kputs("/dts-v1/;\n");
while (i --> 0)
{
rt_kprintf("\n/memreserve/\t0x%lx 0x%zx;", dtb_node_head->header->memreserve[i].address, dtb_node_head->header->memreserve[i].size);
}
_dtb_node_printf_dtb_node_info(dtb_node_head);
}
}
static void _dtb_node_get_enum_dtb_node(struct dtb_node *dtb_node, void (callback(struct dtb_node *dtb_node)))
{
while (dtb_node != NULL)
{
callback(dtb_node);
_dtb_node_get_enum_dtb_node(dtb_node->child, callback);
dtb_node = dtb_node->sibling;
}
}
void dtb_node_get_enum_dtb_node(struct dtb_node *dtb_node_head, void (callback(struct dtb_node *dtb_node)))
{
if (dtb_node_head == NULL || callback == NULL)
{
return;
}
_dtb_node_get_enum_dtb_node(dtb_node_head, callback);
}
struct dtb_node *dtb_node_get_dtb_node_by_name_DFS(struct dtb_node *dtb_node, const char *nodename)
{
struct dtb_node *dtb_node_child;
while (dtb_node != NULL)
{
if (!strcmp(nodename, dtb_node->name))
{
return dtb_node;
}
dtb_node_child = dtb_node_get_dtb_node_by_name_DFS(dtb_node->child, nodename);
if (dtb_node_child != NULL)
{
return dtb_node_child;
}
dtb_node = dtb_node->sibling;
}
return NULL;
}
struct dtb_node *dtb_node_get_dtb_node_by_name_BFS(struct dtb_node *dtb_node, const char *nodename)
{
if (dtb_node != NULL)
{
struct dtb_node *dtb_node_child, *dtb_node_head = dtb_node;
while (dtb_node != NULL)
{
if (!strcmp(nodename, dtb_node->name))
{
return dtb_node;
}
dtb_node = dtb_node->sibling;
}
dtb_node = dtb_node_head;
while (dtb_node != NULL)
{
dtb_node_child = dtb_node_get_dtb_node_by_name_BFS(dtb_node->child, nodename);
if (dtb_node_child != NULL)
{
return dtb_node_child;
}
dtb_node = dtb_node->sibling;
}
}
return NULL;
}
struct dtb_node *dtb_node_get_dtb_node_by_path(struct dtb_node *dtb_node, const char *pathname)
{
int i = 0;
char *node_name;
char *pathname_clone;
int pathname_sz;
if (pathname == NULL || dtb_node == NULL)
{
return NULL;
}
/* skip '/' */
if (*pathname == '/')
{
++pathname;
}
/* root not have sibling, so skip */
if (dtb_node->parent == NULL || !strcmp(dtb_node->path, "/"))
{
dtb_node = dtb_node->child;
}
pathname_sz = strlen(pathname) + 1;
pathname_clone = (char *)malloc(pathname_sz);
if (pathname_clone == NULL)
{
return NULL;
}
strncpy(pathname_clone, pathname, pathname_sz);
node_name = pathname_clone;
while (pathname_clone[i])
{
if (pathname_clone[i] == '/')
{
/* set an end of name that can used to strcmp */
pathname_clone[i] = '\0';
while (dtb_node != NULL)
{
if (!strcmp(dtb_node->name, node_name))
{
break;
}
dtb_node = dtb_node->sibling;
}
if (dtb_node == NULL)
{
free(pathname_clone);
return NULL;
}
dtb_node = dtb_node->child;
node_name = &pathname_clone[i + 1];
}
++i;
}
/*
* found the end node and it's name:
* (pathname[pathname_sz - 1]) is '\0'
* (&pathname_clone[i] - node_name) is the node_name's length
*/
node_name = &((char *)pathname)[(pathname_sz - 1) - (&pathname_clone[i] - node_name)];
free(pathname_clone);
while (dtb_node != NULL)
{
if (!strcmp(dtb_node->name, node_name))
{
return dtb_node;
}
dtb_node = dtb_node->sibling;
}
return NULL;
}
struct dtb_node *dtb_node_get_dtb_node_by_phandle_DFS(struct dtb_node *dtb_node, phandle handle)
{
struct dtb_node *dtb_node_child;
while (dtb_node != NULL)
{
if (dtb_node->handle == handle)
{
return dtb_node;
}
dtb_node_child = dtb_node_get_dtb_node_by_phandle_DFS(dtb_node->child, handle);
if (dtb_node_child != NULL)
{
return dtb_node_child;
}
dtb_node = dtb_node->sibling;
}
return NULL;
}
struct dtb_node *dtb_node_get_dtb_node_by_phandle_BFS(struct dtb_node *dtb_node, phandle handle)
{
if (dtb_node != NULL)
{
struct dtb_node *dtb_node_child, *dtb_node_head = dtb_node;
while (dtb_node != NULL)
{
if (dtb_node->handle == handle)
{
return dtb_node;
}
dtb_node = dtb_node->sibling;
}
dtb_node = dtb_node_head;
while (dtb_node != NULL)
{
dtb_node_child = dtb_node_get_dtb_node_by_phandle_BFS(dtb_node->child, handle);
if (dtb_node_child != NULL)
{
return dtb_node_child;
}
dtb_node = dtb_node->sibling;
}
}
return NULL;
}
void dtb_node_get_dtb_node_cells(struct dtb_node *dtb_node, int *addr_cells, int *size_cells)
{
if (dtb_node != NULL && addr_cells != NULL && size_cells != NULL)
{
struct dtb_property *dtb_property;
*addr_cells = -1;
*size_cells = -1;
/* if couldn't found, check parent */
while ((dtb_node = dtb_node->parent) != NULL)
{
dtb_property = dtb_node->properties;
while (dtb_property != NULL)
{
if (!strcmp(dtb_property->name, "#address-cells"))
{
*addr_cells = fdt32_to_cpu(*(int *)dtb_property->value);
}
else if (!strcmp(dtb_property->name, "#size-cells"))
{
*size_cells = fdt32_to_cpu(*(int *)dtb_property->value);
}
if (*addr_cells != -1 && *size_cells != -1)
{
return;
}
dtb_property = dtb_property->next;
}
}
if (*addr_cells == -1)
{
*addr_cells = FDT_ROOT_ADDR_CELLS_DEFAULT;
}
if (*size_cells == -1)
{
*size_cells = FDT_ROOT_SIZE_CELLS_DEFAULT;
}
}
}
struct dtb_memreserve *dtb_node_get_dtb_memreserve(struct dtb_node *dtb_node, int *memreserve_size)
{
if (dtb_node != NULL && memreserve_size != NULL)
{
struct dtb_node *dtb_node_root = dtb_node;
while (dtb_node_root != NULL)
{
if (!strcmp(dtb_node_root->path, "/"))
{
break;
}
dtb_node_root = dtb_node_root->parent;
}
if(dtb_node_root == NULL) return NULL;
*memreserve_size = dtb_node_root->header->memreserve_sz;
return dtb_node_root->header->memreserve;
}
return NULL;
}
rt_bool_t dtb_node_get_dtb_node_status(const struct dtb_node *dtb_node)
{
if (dtb_node != NULL)
{
char *status = dtb_node_get_dtb_node_property_value(dtb_node, "status", NULL);
if (status != NULL)
{
return (!strcmp(status, "okay") || !strcmp(status, "ok")) ? RT_TRUE : RT_FALSE;
}
return RT_TRUE;
}
return RT_FALSE;
}
rt_bool_t dtb_node_get_dtb_node_compatible_match(const struct dtb_node *dtb_node, const char *compatibles)
{
if (dtb_node != NULL)
{
if (compatibles != NULL)
{
char *str_ptr;
int prop_sz;
for_each_property_string(dtb_node, "compatible", str_ptr, prop_sz)
{
if (!strcmp(compatibles, str_ptr))
{
return RT_TRUE;
}
}
}
}
return RT_FALSE;
}
char *dtb_node_get_dtb_string_list_value(void *value, int size, int index)
{
int i = 0;
char *str = value;
if (str != NULL)
{
do
{
if (i++ == index)
{
return str;
}
str += strlen(str) + 1;
} while (str < (char *)value + size);
}
return NULL;
}
char *dtb_node_get_dtb_string_list_value_next(void *value, void *end)
{
char *str = value;
if (str != NULL)
{
str += strlen(str) + 1;
if (str < (char *)end)
{
return str;
}
}
return NULL;
}
uint32_t dtb_node_get_dtb_cell_value(void *value)
{
return fdt32_to_cpu(*(fdt32_t *)value);
}
uint8_t dtb_node_get_dtb_byte_value(void *value)
{
return *(uint8_t *)value;
}

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "dtb_node.h"
static void *dtb_root = NULL;
static struct dtb_node *dtb_node_list = NULL;
void *get_fdt_blob(void)
{
return dtb_root;
}
struct dtb_node *get_dtb_node_head(void)
{
return dtb_node_list;
}
rt_bool_t dtb_node_active(void)
{
return dtb_node_list != NULL;
}
int device_tree_setup(void *mem_addr)
{
if(mem_addr)
{
if ((dtb_root = dtb_node_load_from_memory(mem_addr,1)) != NULL)
{
dtb_node_list = dtb_node_get_dtb_list(dtb_root);
if (dtb_node_list != NULL)
{
return -1;
}
}
return 0;
}
return -1;
}

View file

@ -0,0 +1,107 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "libfdt.h"
#include <stdio.h>
#include <unistd.h>
#include "dtb_node.h"
extern int fdt_exec_status;
rt_bool_t dtb_node_check(void *fdt)
{
return fdt_check_header(fdt) == 0 ? RT_TRUE : RT_FALSE;
}
void *dtb_node_load_from_fs(char *dtb_filename)
{
void *fdt = NULL;
size_t dtb_sz;
int fd = -1;
if (dtb_filename == NULL)
{
fdt_exec_status = FDT_RET_GET_EMPTY;
goto end;
}
fd = open(dtb_filename, O_RDONLY, 0);
if (fd == -1)
{
rt_kprintf("File `%s' not found.\n", dtb_filename);
fdt_exec_status = FDT_RET_GET_EMPTY;
goto end;
}
dtb_sz = lseek(fd, 0, SEEK_END);
if (dtb_sz > 0)
{
if ((fdt = (struct fdt_header *)malloc(sizeof(uint8_t) * dtb_sz)) == NULL)
{
fdt_exec_status = FDT_RET_NO_MEMORY;
goto end;
}
lseek(fd, 0, SEEK_SET);
read(fd, fdt, sizeof(uint8_t) * dtb_sz);
if (dtb_node_check(fdt) == RT_FALSE)
{
free(fdt);
fdt=NULL;
}
}
end:
if (fd != -1)
{
close(fd);
}
return fdt;
}
void *dtb_node_load_from_memory(void *dtb_ptr, rt_bool_t is_clone)
{
void *fdt = NULL;
if (dtb_ptr == NULL)
{
fdt_exec_status = FDT_RET_GET_EMPTY;
goto end;
}
if (dtb_node_check(dtb_ptr) == RT_FALSE)
{
fdt_exec_status = FDT_RET_GET_EMPTY;
fdt = NULL;
goto end;
}
if (is_clone)
{
size_t dtb_sz = fdt_totalsize(dtb_ptr);
if (dtb_sz > 0)
{
if ((fdt = (size_t *)malloc(dtb_sz)) != NULL)
{
memcpy(fdt, dtb_ptr, dtb_sz);
}
else
{
fdt_exec_status = FDT_RET_NO_MEMORY;
}
}
}
else
{
fdt = dtb_ptr;
}
end:
return fdt;
}

View file

@ -0,0 +1,166 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "libfdt.h"
#include "dtb_node.h"
static off_t dtb_node_find_and_add_subnode(void *fdt, char* name)
{
off_t chosen_offset = 0;
chosen_offset = fdt_subnode_offset(fdt, 0, name);
if (chosen_offset == -FDT_ERR_NOTFOUND)
{
chosen_offset = fdt_add_subnode(fdt, 0, name);
}
return chosen_offset;
}
size_t dtb_node_set_linux_cmdline(void *fdt, char *cmdline)
{
off_t chosen_offset;
size_t cmdline_size;
if (cmdline == NULL || fdt == NULL)
{
goto end;
}
chosen_offset = dtb_node_find_and_add_subnode(fdt, "chosen");
cmdline_size = strlen(cmdline);
/* install bootargs */
if (chosen_offset >= 0 || chosen_offset == -FDT_ERR_EXISTS)
{
if (fdt_setprop(fdt, chosen_offset, "bootargs", cmdline, cmdline_size) < 0)
{
fdt_open_into(fdt, fdt, fdt_totalsize(fdt) + FDT_DTB_PAD_SIZE);
fdt_setprop(fdt, chosen_offset, "bootargs", cmdline, cmdline_size);
}
}
end:
return fdt_totalsize(fdt);
}
size_t dtb_node_set_linux_initrd(void *fdt, uint64_t initrd_addr, size_t initrd_size)
{
uint64_t addr, size_ptr;
off_t chosen_offset;
int i;
if (fdt == NULL)
{
goto end;
}
chosen_offset = dtb_node_find_and_add_subnode(fdt, "chosen");
/* update the entry */
for (i = fdt_num_mem_rsv(fdt) - 1; i >= 0; --i)
{
fdt_get_mem_rsv(fdt, i, &addr, &size_ptr);
if (addr == initrd_addr)
{
fdt_del_mem_rsv(fdt, i);
break;
}
}
/* add the memory */
if (fdt_add_mem_rsv(fdt, initrd_addr, initrd_size) < 0)
{
/* move the memory */
fdt_open_into(fdt, fdt, fdt_totalsize(fdt) + FDT_DTB_PAD_SIZE);
if (fdt_add_mem_rsv(fdt, initrd_addr, initrd_size) < 0)
{
goto end;
}
}
/* install initrd */
if (chosen_offset >= 0 || chosen_offset == -FDT_ERR_EXISTS)
{
chosen_offset = fdt_path_offset(fdt, "/chosen");
if (IN_64BITS_MODE)
{
fdt_setprop_u64(fdt, chosen_offset, "linux,initrd-start", initrd_addr);
fdt_setprop_u64(fdt, chosen_offset, "linux,initrd-end", initrd_addr + initrd_size);
}
else
{
fdt_setprop_u32(fdt, chosen_offset, "linux,initrd-start", initrd_addr);
fdt_setprop_u32(fdt, chosen_offset, "linux,initrd-end", initrd_addr + initrd_size);
}
}
end:
return fdt_totalsize(fdt);
}
size_t dtb_node_set_dtb_property(void *fdt, char *pathname, char *property_name, uint32_t *cells, size_t cells_size)
{
int node_off;
if (fdt == NULL)
{
goto end;
}
node_off = fdt_path_offset(fdt, pathname);
if (node_off >= 0 && cells_size != 0)
{
fdt_setprop(fdt, node_off, property_name, cells, cells_size);
}
end:
return fdt_totalsize(fdt);
}
size_t dtb_node_add_dtb_memreserve(void *fdt, uint64_t address, uint64_t size)
{
if (fdt == NULL)
{
goto end;
}
fdt_add_mem_rsv(fdt, address, size);
end:
return fdt_totalsize(fdt);
}
size_t dtb_node_del_dtb_memreserve(void *fdt, uint64_t address)
{
int i;
int num_mem_rsvmap;
uint32_t off_mem_rsvmap;
struct fdt_reserve_entry *rsvmap;
if (fdt == NULL)
{
goto end;
}
num_mem_rsvmap = fdt_num_mem_rsv(fdt);
off_mem_rsvmap = fdt_off_mem_rsvmap(fdt);
rsvmap = (struct fdt_reserve_entry *)((char *)fdt + off_mem_rsvmap);
for (i = 0; i < num_mem_rsvmap; ++i)
{
if (address == fdt64_to_cpu(rsvmap[i].address))
{
fdt_del_mem_rsv(fdt, i);
break;
}
}
end:
return fdt_totalsize(fdt);
}