命令字:!ndx.devtree
用法描述
该命令可展示设备树的层级结构,呈现设备树节点的关键信息(含节点地址、名称、标志位及深度),并支持查看节点的属性数据(含属性名称、地址、值指针及长度)。
命令行参数支持:
-v 显示每个节点的属性信息
-v1 仅显示指定节点的属性
-a <地址> 指定自定义起始节点地址
-h /–help/ ? 显示帮助信息
开发过程
1. 找到关键全局变量
关键全局变量 | 作用 |
---|---|
of_root | Linux 内核中设备树的 根节点指针(struct device_node *of_root),是设备树遍历的起点 |
在drivers\of\base.c文件中
of_kset = kset_create_and_add(“devicetree”, NULL, firmware_kobj);
作用:在 sysfs 文件系统中创建并注册一个名为devicetree的kset(内核对象集合)。
for_each_of_allnodes(np) { _ofattach_node_sysfs(np); … }
作用:遍历所有设备树节点,并将每个节点挂载到 sysfs 中。
proc_symlink(“device-tree”, NULL, “/sys/firmware/devicetree/base”);
作用:在/proc文件系统中创建一个符号链接,指向 sysfs 中的设备树根节点。
_offind_all_nodes是具体的遍历树函数(dfs),当prev为NULL(首次调用遍历),返回设备树的根节点of_root,作为遍历的起点。
每个设备节点存放了属性链表的头节点, 获取头节点遍历链表获取的属性各个字段
2. 相关数据结构
设备树节点的结构体
3. 读取变量和相关偏移,输出信息
#include "ndx.h"
#include <windows.h>
#include <minwindef.h>
#include <WDBGEXTS.H>
#include <cstdio>
#include <vector>
#include <string>
#include <string.h>
#include <cctype>
#include <cstdlib>
#include <cerrno>
#include <wdbgexts.h>
#include <fstream>
#include <iostream>
#include "devtree.h"
// 全局变量,存储device_node结构体字段偏移量
static device_node_offsets g_device_node_offsets = { 0 };
// 全局变量,存储property结构体字段偏移量
static property_offsets g_property_offsets = { 0 };
// 初始化device_node和property结构体字段偏移量
bool InitOffsets(device_node_offsets* node_offsets, property_offsets* prop_offsets) {
bool success = true;
// 初始化device_node结构体字段偏移量
if (node_offsets) {
if (GetFieldOffset("device_node", "name", &node_offsets->name_offset) != 0) {
success = false;
}
if (GetFieldOffset("device_node", "phandle", &node_offsets->phandle_offset) != 0) {
success = false;
}
if (GetFieldOffset("device_node", "full_name", &node_offsets->full_name_offset) != 0) {
success = false;
}
if (GetFieldOffset("device_node", "fwnode", &node_offsets->fwnode_offset) != 0) {
success = false;
}
if (GetFieldOffset("device_node", "properties", &node_offsets->properties_offset) != 0) {
success = false;
}
if (GetFieldOffset("device_node", "deadprops", &node_offsets->deadprops_offset) != 0) {
success = false;
}
if (GetFieldOffset("device_node", "parent", &node_offsets->parent_offset) != 0) {
success = false;
}
if (GetFieldOffset("device_node", "child", &node_offsets->child_offset) != 0) {
success = false;
}
if (GetFieldOffset("device_node", "sibling", &node_offsets->sibling_offset) != 0) {
success = false;
}
if (GetFieldOffset("device_node", "_flags", &node_offsets->_flags_offset) != 0) {
success = false;
}
GetFieldOffset("device_node", "data", &node_offsets->data_offset);
GetFieldOffset("device_node", "unique_id", &node_offsets->unique_id_offset);
GetFieldOffset("device_node", "irq_trans", &node_offsets->irq_trans_offset);
}
// 初始化property结构体字段偏移量
if (prop_offsets) {
if (GetFieldOffset("property", "name", &prop_offsets->name_offset) != 0) {
success = false;
}
if (GetFieldOffset("property", "length", &prop_offsets->length_offset) != 0) {
success = false;
}
if (GetFieldOffset("property", "value", &prop_offsets->value_offset) != 0) {
success = false;
}
if (GetFieldOffset("property", "next", &prop_offsets->next_offset) != 0) {
success = false;
}
}
return success;
}
// phandle哈希函数实现
uint32_t of_phandle_cache_hash(phandle handle) {
return handle & (OF_PHANDLE_CACHE_SZ - 1);
}
// 显示节点的属性信息
void show_node_properties(ULONG64 node_addr) {
// 确保属性偏移量已初始化
if (g_property_offsets.name_offset == 0) {
if (!InitOffsets(NULL, &g_property_offsets)) {
dprintf("ERROR: Failed to initialize property offsets\n");
return;
}
}
ULONG bytes_read = 0;
ULONG64 properties_ptr = 0;
// 读取properties指针
if (!ExtReadMemory(node_addr + g_device_node_offsets.properties_offset, &properties_ptr, sizeof(ULONG64), &bytes_read) || bytes_read != sizeof(ULONG64)) {
dprintf("ERROR: Cannot read node properties pointer at address %p (offset +0x%x)\n",
(void*)node_addr, g_device_node_offsets.properties_offset);
return;
}
// 遍历属性链表
ULONG64 prop_addr = properties_ptr;
int prop_count = 0;
while (prop_addr) {
ULONG64 name_ptr = 0;
ULONG64 value_ptr = 0;
ULONG length = 0;
ULONG64 next_prop = 0;
// 读取属性名称指针
if (!ExtReadMemory(prop_addr + g_property_offsets.name_offset, &name_ptr, sizeof(ULONG64), &bytes_read)) {
break;
}
// 读取属性值指针
if (!ExtReadMemory(prop_addr + g_property_offsets.value_offset, &value_ptr, sizeof(ULONG64), &bytes_read)) {
break;
}
// 读取属性长度
if (!ExtReadMemory(prop_addr + g_property_offsets.length_offset, &length, sizeof(ULONG), &bytes_read)) {
break;
}
// 读取下一个属性指针
if (!ExtReadMemory(prop_addr + g_property_offsets.next_offset, &next_prop, sizeof(ULONG64), &bytes_read)) {
break;
}
// 读取属性名称
char name[128] = { 0 };
if (name_ptr) {
ExtReadMemory(name_ptr, name, sizeof(name) - 1, &bytes_read);
}
// 显示属性信息 - 使用固定宽度格式化输出,使各列对齐
dprintf(" Property[%03d]: %-30s Address: %-16p Value: %-16p Length: %-8u\n",
prop_count, name, (void*)prop_addr, (void*)value_ptr, length);
// 移动到下一个属性
prop_addr = next_prop;
prop_count++;
}
if (prop_count == 0) {
dprintf(" No properties found\n");
}
}
// 递归显示设备树结构,带有层级关系
// 显示节点信息的函数
void show_node_info(ULONG64 node_addr, int depth) {
if (g_device_node_offsets.name_offset == 0) {
if (!InitOffsets(&g_device_node_offsets, NULL)) {
dprintf("ERROR: Failed to initialize device_node offsets\n");
return;
}
}
if (node_addr == 0) {
dprintf("ERROR: Invalid node address (0x%p) at depth %d\n", (void*)node_addr, depth);
return;
}
ULONG bytes_read = 0;
ULONG64 fullname_ptr = 0;
// 读取fullname指针
if (!ExtReadMemory(node_addr + g_device_node_offsets.full_name_offset, &fullname_ptr, sizeof(ULONG64), &bytes_read) || bytes_read != sizeof(ULONG64)) {
dprintf("ERROR: Cannot read node fullname pointer at address %p (offset +0x%x)\n",
(void*)node_addr, g_device_node_offsets.full_name_offset);
return;
}
// 读取fullname字符串
char fullname[256] = { 0 };
if (fullname_ptr) {
ULONG bytes_read = 0;
if (!ExtReadMemory(fullname_ptr, fullname, sizeof(fullname) - 1, &bytes_read)) {
dprintf("ERROR: Cannot read fullname string at address %p\n", (void*)fullname_ptr);
}
}
// 读取节点的_flags和data字段
ULONG flags = 0;
if (g_device_node_offsets._flags_offset != 0) {
if (!ExtReadMemory(node_addr + g_device_node_offsets._flags_offset, &flags, sizeof(ULONG), &bytes_read)) {
dprintf("ERROR: Cannot read _flags at address %p \n", node_addr + g_device_node_offsets._flags_offset);
};
}
// 计算缩进空格数
int indent = depth * 2;
// 移除空行,直接输出节点信息
if (fullname[0] != '\0') {
// 计算需要补充的空格数,确保地址对齐
int name_length = indent + (int)strlen(fullname);
int padding = (name_length < 50) ? (50 - name_length) : 2;
// 在一行内输出所有节点信息
dprintf("%*s%-s%*s%p Flags: 0x%08x Depth: %d\n",
indent, "", fullname,
padding, "",
(void*)node_addr,
flags,
depth);
}
}
void show_device_tree_recursive(ULONG64 node_addr, int depth, bool traverse_siblings, bool show_properties) {
if (node_addr == 0) {
return;
}
ULONG bytes_read = 0;
ULONG64 child = 0, sibling = 0;
// 显示当前节点信息
show_node_info(node_addr, depth);
// 如果需要显示属性信息,则调用show_node_properties函数
if (show_properties) {
show_node_properties(node_addr);
}
// 读取子节点指针
if (!ExtReadMemory(node_addr + g_device_node_offsets.child_offset, &child, sizeof(ULONG64), &bytes_read)) {
dprintf("ERROR: Cannot read child pointer at address %p (offset +0x%x)\n",
(void*)node_addr, g_device_node_offsets.child_offset);
return;
}
// 递归处理子节点
if (child) {
show_device_tree_recursive(child, depth + 1, true, show_properties); // 子节点总是遍历其兄弟节点
}
// 读取兄弟节点指针
if (!ExtReadMemory(node_addr + g_device_node_offsets.sibling_offset, &sibling, sizeof(ULONG64), &bytes_read)) {
dprintf("ERROR: Cannot read sibling pointer ");
return;
}
// 处理兄弟节点
if (sibling && traverse_siblings) {
show_device_tree_recursive(sibling, depth, traverse_siblings, show_properties);
}
}
// 显示phandle缓存
void show_phandle_cache() {
dprintf("Phandle Cache (128 entries):\n");
dprintf("please wait...\n");
// 确保偏移量已初始化
if (g_device_node_offsets.name_offset == 0 || g_device_node_offsets.phandle_offset == 0) {
if (!InitOffsets(&g_device_node_offsets, NULL)) {
dprintf("ERROR: Failed to initialize device_node offsets\n");
return;
}
}
// 获取phandle_cache符号地址
ULONG64 phandle_cache_addr = 0;
GetExpressionEx("lk!phandle_cache", &phandle_cache_addr, NULL);
if (phandle_cache_addr == 0) {
dprintf("Error: Cannot find phandle_cache symbol\n");
return;
}
dprintf("phandle_cache address: %p\n", phandle_cache_addr);
// 读取128个device_node指针
ULONG64 cache_entries[OF_PHANDLE_CACHE_SZ];
ULONG bytes_read = 0;
if (!ExtReadMemory(phandle_cache_addr, cache_entries, sizeof(cache_entries), &bytes_read)) {
dprintf("Error: Cannot read phandle_cache from memory\n");
return;
}
// 读取所有非空节点的phandle和name
int valid_entries = 0;
ULONG64 name_ptrs[OF_PHANDLE_CACHE_SZ];
char names[OF_PHANDLE_CACHE_SZ][256];
bool read_success[OF_PHANDLE_CACHE_SZ] = { false };
// 先找出所有非空的节点地址
std::vector<int> valid_indices;
for (int i = 0; i < OF_PHANDLE_CACHE_SZ; i++) {
if (cache_entries[i] != 0) {
valid_indices.push_back(i);
}
}
// 一次性读取所有节点的phandle和name指针
for (int idx : valid_indices) {
// 读取name指针
if (read_success[idx]) {
read_success[idx] = ExtReadMemory(cache_entries[idx] + g_device_node_offsets.name_offset,
&name_ptrs[idx], sizeof(ULONG64), &bytes_read);
// 读取name字符串
if (read_success[idx] && name_ptrs[idx]) {
ExtReadMemory(name_ptrs[idx], names[idx], 255, &bytes_read);
}
}
}
// 显示结果
for (int idx : valid_indices) {
if (read_success[idx]) {
dprintf("%5d %12p %s\n", idx, (void*)cache_entries[idx],
name_ptrs[idx] && names[idx][0] ? names[idx] : "<no name>");
valid_entries++;
}
else {
dprintf("%5d %12p <read error>\n", idx, (void*)cache_entries[idx]);
}
}
dprintf("\nTotal valid entries: %d/%d\n", valid_entries, OF_PHANDLE_CACHE_SZ);
}
/**
* 显示使用帮助
*/
static void show_usage() {
dprintf(
"Linux Kernel Device Tree Commands:\n"
" !ndx.devtree Show device tree (default)\n"
" !ndx.devtree -v Show property information for each node\n"
" !ndx.devtree -v1 Show only properties of specified node\n"
" !ndx.devtree -a <address> Specify custom start node address\n"
" !ndx.devtree -a <address> -p Show device tree from custom address with properties\n"
" !ndx.devtree -a <address> -p1 Show only properties of node at custom address\n"
" !ndx.devtree -h, --help, ? Show this help\n"
);
}
// 解析设备树命令参数
bool parse_devtree_command(const char* args, ULONG64* out_node_addr, bool* is_custom_address, bool* show_properties, bool* show_single_node_properties) {
if (!out_node_addr || !is_custom_address || !show_properties || !show_single_node_properties) {
return false;
}
*out_node_addr = 0;
*is_custom_address = false;
*show_properties = false;
*show_single_node_properties = false;
// 创建参数副本以避免修改原始字符串
if (args && *args) {
char args_copy[256];
strncpy_s(args_copy, sizeof(args_copy), args, _TRUNCATE);
// 使用空格分割参数
char* context = nullptr;
char* token = strtok_s(args_copy, " ", &context);
while (token) {
// 解析命令选项
if (_stricmp(token, "-a") == 0) {
// 按地址指定根节点
char* addr_str = strtok_s(nullptr, " ", &context);
if (addr_str) {
if (GetExpressionEx(addr_str, out_node_addr, NULL)) {
*is_custom_address = true;
dprintf("Using custom start node address: %p\n", (void*)*out_node_addr);
}
else {
dprintf("ERROR: Invalid address format '%s'\n", addr_str);
return false;
}
}
else {
dprintf("ERROR: Missing address after -a parameter\n");
return false;
}
}
else if (_stricmp(token, "-v") == 0) {
// 显示属性信息
*show_properties = true;
dprintf("Property information will be displayed\n");
}
else if (_stricmp(token, "-v1") == 0) {
// 只显示指定节点的属性信息
*show_single_node_properties = true;
dprintf("Only properties of the specified node will be displayed\n");
}
else if (_stricmp(token, "-h") == 0 || _stricmp(token, "--help") == 0 || _stricmp(token, "?") == 0) {
// 显示帮助
show_usage();
return false;
}
else {
dprintf("ERROR: Unknown option '%s'\n", token);
show_usage();
return false;
}
// 获取下一个参数
token = strtok_s(nullptr, " ", &context);
}
}
// 如果没有指定自定义地址,则使用根节点
if (!*is_custom_address) {
// 获取根节点
ULONG64 of_root_addr = 0;
GetExpressionEx("lk!of_root", &of_root_addr, NULL);
if (of_root_addr) {
ULONG bytes_read = 0;
if (!ExtReadMemory(of_root_addr, out_node_addr, sizeof(ULONG64), &bytes_read) || !*out_node_addr) {
dprintf("Error: Cannot read of_root address from memory\n");
return false;
}
dprintf("Root Node Address: %p\n", (void*)*out_node_addr);
}
else {
dprintf("Error: Cannot find of_root symbol address\n");
return false;
}
}
return true;
}
DECLARE_API(devtree) {
//show_phandle_cache();
// 确保偏移量已初始化
if (g_device_node_offsets.name_offset == 0) {
if (!InitOffsets(&g_device_node_offsets, NULL)) {
dprintf("ERROR: Failed to initialize device_node offsets\n");
return;
}
}
// 解析命令参数
ULONG64 start_node_addr = 0;
bool is_custom_address = false;
bool show_properties = false;
bool show_single_node_properties = false;
if (!parse_devtree_command(args, &start_node_addr, &is_custom_address, &show_properties, &show_single_node_properties)) {
return;
}
// 如果指定了只显示单个节点的属性
if (show_single_node_properties) {
// 先显示节点信息,但不遍历子节点或兄弟节点
show_node_info(start_node_addr, 0);
// 再显示属性信息
show_node_properties(start_node_addr);
}
else {
// 显示设备树
// 如果是自定义地址,根节点不遍历它的兄弟节点
show_device_tree_recursive(start_node_addr, 0, !is_custom_address, show_properties);
}
}
4. 实例测试
!ndx.devtree
ls /proc/device-tree/clocks
作者:郭建程 创建时间:2025-09-19 10:50
最后编辑:郭建程 更新时间:2025-09-22 20:17
最后编辑:郭建程 更新时间:2025-09-22 20:17