From afaef01c001537fa97a25092d7f54d764dc7d8c1 Mon Sep 17 00:00:00 2001 From: Alexander Popov Date: Fri, 17 Aug 2018 01:16:58 +0300 Subject: x86/entry: Add STACKLEAK erasing the kernel stack at the end of syscalls The STACKLEAK feature (initially developed by PaX Team) has the following benefits: 1. Reduces the information that can be revealed through kernel stack leak bugs. The idea of erasing the thread stack at the end of syscalls is similar to CONFIG_PAGE_POISONING and memzero_explicit() in kernel crypto, which all comply with FDP_RIP.2 (Full Residual Information Protection) of the Common Criteria standard. 2. Blocks some uninitialized stack variable attacks (e.g. CVE-2017-17712, CVE-2010-2963). That kind of bugs should be killed by improving C compilers in future, which might take a long time. This commit introduces the code filling the used part of the kernel stack with a poison value before returning to userspace. Full STACKLEAK feature also contains the gcc plugin which comes in a separate commit. The STACKLEAK feature is ported from grsecurity/PaX. More information at: https://grsecurity.net/ https://pax.grsecurity.net/ This code is modified from Brad Spengler/PaX Team's code in the last public patch of grsecurity/PaX based on our understanding of the code. Changes or omissions from the original code are ours and don't reflect the original grsecurity/PaX code. Performance impact: Hardware: Intel Core i7-4770, 16 GB RAM Test #1: building the Linux kernel on a single core 0.91% slowdown Test #2: hackbench -s 4096 -l 2000 -g 15 -f 25 -P 4.2% slowdown So the STACKLEAK description in Kconfig includes: "The tradeoff is the performance impact: on a single CPU system kernel compilation sees a 1% slowdown, other systems and workloads may vary and you are advised to test this feature on your expected workload before deploying it". Signed-off-by: Alexander Popov Acked-by: Thomas Gleixner Reviewed-by: Dave Hansen Acked-by: Ingo Molnar Signed-off-by: Kees Cook --- scripts/gcc-plugins/Kconfig | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'scripts') diff --git a/scripts/gcc-plugins/Kconfig b/scripts/gcc-plugins/Kconfig index cb0c889e13aa..977b84e69787 100644 --- a/scripts/gcc-plugins/Kconfig +++ b/scripts/gcc-plugins/Kconfig @@ -139,4 +139,23 @@ config GCC_PLUGIN_RANDSTRUCT_PERFORMANCE in structures. This reduces the performance hit of RANDSTRUCT at the cost of weakened randomization. +config GCC_PLUGIN_STACKLEAK + bool "Erase the kernel stack before returning from syscalls" + depends on GCC_PLUGINS + depends on HAVE_ARCH_STACKLEAK + help + This option makes the kernel erase the kernel stack before + returning from system calls. That reduces the information which + kernel stack leak bugs can reveal and blocks some uninitialized + stack variable attacks. + + The tradeoff is the performance impact: on a single CPU system kernel + compilation sees a 1% slowdown, other systems and workloads may vary + and you are advised to test this feature on your expected workload + before deploying it. + + This plugin was ported from grsecurity/PaX. More information at: + * https://grsecurity.net/ + * https://pax.grsecurity.net/ + endif -- cgit v1.2.3 From 10e9ae9fabaf96c8e5227c1cd4827d58b3aa406d Mon Sep 17 00:00:00 2001 From: Alexander Popov Date: Fri, 17 Aug 2018 01:16:59 +0300 Subject: gcc-plugins: Add STACKLEAK plugin for tracking the kernel stack The STACKLEAK feature erases the kernel stack before returning from syscalls. That reduces the information which kernel stack leak bugs can reveal and blocks some uninitialized stack variable attacks. This commit introduces the STACKLEAK gcc plugin. It is needed for tracking the lowest border of the kernel stack, which is important for the code erasing the used part of the kernel stack at the end of syscalls (comes in a separate commit). The STACKLEAK feature is ported from grsecurity/PaX. More information at: https://grsecurity.net/ https://pax.grsecurity.net/ This code is modified from Brad Spengler/PaX Team's code in the last public patch of grsecurity/PaX based on our understanding of the code. Changes or omissions from the original code are ours and don't reflect the original grsecurity/PaX code. Signed-off-by: Alexander Popov Tested-by: Laura Abbott Signed-off-by: Kees Cook --- scripts/Makefile.gcc-plugins | 10 + scripts/gcc-plugins/Kconfig | 12 + scripts/gcc-plugins/stackleak_plugin.c | 427 +++++++++++++++++++++++++++++++++ 3 files changed, 449 insertions(+) create mode 100644 scripts/gcc-plugins/stackleak_plugin.c (limited to 'scripts') diff --git a/scripts/Makefile.gcc-plugins b/scripts/Makefile.gcc-plugins index 0a482f341576..46c5c6809806 100644 --- a/scripts/Makefile.gcc-plugins +++ b/scripts/Makefile.gcc-plugins @@ -26,6 +26,16 @@ gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_RANDSTRUCT) \ gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_RANDSTRUCT_PERFORMANCE) \ += -fplugin-arg-randomize_layout_plugin-performance-mode +gcc-plugin-$(CONFIG_GCC_PLUGIN_STACKLEAK) += stackleak_plugin.so +gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STACKLEAK) \ + += -DSTACKLEAK_PLUGIN +gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STACKLEAK) \ + += -fplugin-arg-stackleak_plugin-track-min-size=$(CONFIG_STACKLEAK_TRACK_MIN_SIZE) +ifdef CONFIG_GCC_PLUGIN_STACKLEAK + DISABLE_STACKLEAK_PLUGIN += -fplugin-arg-stackleak_plugin-disable +endif +export DISABLE_STACKLEAK_PLUGIN + # All the plugin CFLAGS are collected here in case a build target needs to # filter them out of the KBUILD_CFLAGS. GCC_PLUGINS_CFLAGS := $(strip $(addprefix -fplugin=$(objtree)/scripts/gcc-plugins/, $(gcc-plugin-y)) $(gcc-plugin-cflags-y)) diff --git a/scripts/gcc-plugins/Kconfig b/scripts/gcc-plugins/Kconfig index 977b84e69787..c65fdd823591 100644 --- a/scripts/gcc-plugins/Kconfig +++ b/scripts/gcc-plugins/Kconfig @@ -158,4 +158,16 @@ config GCC_PLUGIN_STACKLEAK * https://grsecurity.net/ * https://pax.grsecurity.net/ +config STACKLEAK_TRACK_MIN_SIZE + int "Minimum stack frame size of functions tracked by STACKLEAK" + default 100 + range 0 4096 + depends on GCC_PLUGIN_STACKLEAK + help + The STACKLEAK gcc plugin instruments the kernel code for tracking + the lowest border of the kernel stack (and for some other purposes). + It inserts the stackleak_track_stack() call for the functions with + a stack frame size greater than or equal to this parameter. + If unsure, leave the default value 100. + endif diff --git a/scripts/gcc-plugins/stackleak_plugin.c b/scripts/gcc-plugins/stackleak_plugin.c new file mode 100644 index 000000000000..2f48da98b5d4 --- /dev/null +++ b/scripts/gcc-plugins/stackleak_plugin.c @@ -0,0 +1,427 @@ +/* + * Copyright 2011-2017 by the PaX Team + * Modified by Alexander Popov + * Licensed under the GPL v2 + * + * Note: the choice of the license means that the compilation process is + * NOT 'eligible' as defined by gcc's library exception to the GPL v3, + * but for the kernel it doesn't matter since it doesn't link against + * any of the gcc libraries + * + * This gcc plugin is needed for tracking the lowest border of the kernel stack. + * It instruments the kernel code inserting stackleak_track_stack() calls: + * - after alloca(); + * - for the functions with a stack frame size greater than or equal + * to the "track-min-size" plugin parameter. + * + * This plugin is ported from grsecurity/PaX. For more information see: + * https://grsecurity.net/ + * https://pax.grsecurity.net/ + * + * Debugging: + * - use fprintf() to stderr, debug_generic_expr(), debug_gimple_stmt(), + * print_rtl() and print_simple_rtl(); + * - add "-fdump-tree-all -fdump-rtl-all" to the plugin CFLAGS in + * Makefile.gcc-plugins to see the verbose dumps of the gcc passes; + * - use gcc -E to understand the preprocessing shenanigans; + * - use gcc with enabled CFG/GIMPLE/SSA verification (--enable-checking). + */ + +#include "gcc-common.h" + +__visible int plugin_is_GPL_compatible; + +static int track_frame_size = -1; +static const char track_function[] = "stackleak_track_stack"; + +/* + * Mark these global variables (roots) for gcc garbage collector since + * they point to the garbage-collected memory. + */ +static GTY(()) tree track_function_decl; + +static struct plugin_info stackleak_plugin_info = { + .version = "201707101337", + .help = "track-min-size=nn\ttrack stack for functions with a stack frame size >= nn bytes\n" + "disable\t\tdo not activate the plugin\n" +}; + +static void stackleak_add_track_stack(gimple_stmt_iterator *gsi, bool after) +{ + gimple stmt; + gcall *stackleak_track_stack; + cgraph_node_ptr node; + int frequency; + basic_block bb; + + /* Insert call to void stackleak_track_stack(void) */ + stmt = gimple_build_call(track_function_decl, 0); + stackleak_track_stack = as_a_gcall(stmt); + if (after) { + gsi_insert_after(gsi, stackleak_track_stack, + GSI_CONTINUE_LINKING); + } else { + gsi_insert_before(gsi, stackleak_track_stack, GSI_SAME_STMT); + } + + /* Update the cgraph */ + bb = gimple_bb(stackleak_track_stack); + node = cgraph_get_create_node(track_function_decl); + gcc_assert(node); + frequency = compute_call_stmt_bb_frequency(current_function_decl, bb); + cgraph_create_edge(cgraph_get_node(current_function_decl), node, + stackleak_track_stack, bb->count, frequency); +} + +static bool is_alloca(gimple stmt) +{ + if (gimple_call_builtin_p(stmt, BUILT_IN_ALLOCA)) + return true; + +#if BUILDING_GCC_VERSION >= 4007 + if (gimple_call_builtin_p(stmt, BUILT_IN_ALLOCA_WITH_ALIGN)) + return true; +#endif + + return false; +} + +/* + * Work with the GIMPLE representation of the code. Insert the + * stackleak_track_stack() call after alloca() and into the beginning + * of the function if it is not instrumented. + */ +static unsigned int stackleak_instrument_execute(void) +{ + basic_block bb, entry_bb; + bool prologue_instrumented = false, is_leaf = true; + gimple_stmt_iterator gsi; + + /* + * ENTRY_BLOCK_PTR is a basic block which represents possible entry + * point of a function. This block does not contain any code and + * has a CFG edge to its successor. + */ + gcc_assert(single_succ_p(ENTRY_BLOCK_PTR_FOR_FN(cfun))); + entry_bb = single_succ(ENTRY_BLOCK_PTR_FOR_FN(cfun)); + + /* + * Loop through the GIMPLE statements in each of cfun basic blocks. + * cfun is a global variable which represents the function that is + * currently processed. + */ + FOR_EACH_BB_FN(bb, cfun) { + for (gsi = gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi)) { + gimple stmt; + + stmt = gsi_stmt(gsi); + + /* Leaf function is a function which makes no calls */ + if (is_gimple_call(stmt)) + is_leaf = false; + + if (!is_alloca(stmt)) + continue; + + /* Insert stackleak_track_stack() call after alloca() */ + stackleak_add_track_stack(&gsi, true); + if (bb == entry_bb) + prologue_instrumented = true; + } + } + + if (prologue_instrumented) + return 0; + + /* + * Special cases to skip the instrumentation. + * + * Taking the address of static inline functions materializes them, + * but we mustn't instrument some of them as the resulting stack + * alignment required by the function call ABI will break other + * assumptions regarding the expected (but not otherwise enforced) + * register clobbering ABI. + * + * Case in point: native_save_fl on amd64 when optimized for size + * clobbers rdx if it were instrumented here. + * + * TODO: any more special cases? + */ + if (is_leaf && + !TREE_PUBLIC(current_function_decl) && + DECL_DECLARED_INLINE_P(current_function_decl)) { + return 0; + } + + if (is_leaf && + !strncmp(IDENTIFIER_POINTER(DECL_NAME(current_function_decl)), + "_paravirt_", 10)) { + return 0; + } + + /* Insert stackleak_track_stack() call at the function beginning */ + bb = entry_bb; + if (!single_pred_p(bb)) { + /* gcc_assert(bb_loop_depth(bb) || + (bb->flags & BB_IRREDUCIBLE_LOOP)); */ + split_edge(single_succ_edge(ENTRY_BLOCK_PTR_FOR_FN(cfun))); + gcc_assert(single_succ_p(ENTRY_BLOCK_PTR_FOR_FN(cfun))); + bb = single_succ(ENTRY_BLOCK_PTR_FOR_FN(cfun)); + } + gsi = gsi_after_labels(bb); + stackleak_add_track_stack(&gsi, false); + + return 0; +} + +static bool large_stack_frame(void) +{ +#if BUILDING_GCC_VERSION >= 8000 + return maybe_ge(get_frame_size(), track_frame_size); +#else + return (get_frame_size() >= track_frame_size); +#endif +} + +/* + * Work with the RTL representation of the code. + * Remove the unneeded stackleak_track_stack() calls from the functions + * which don't call alloca() and don't have a large enough stack frame size. + */ +static unsigned int stackleak_cleanup_execute(void) +{ + rtx_insn *insn, *next; + + if (cfun->calls_alloca) + return 0; + + if (large_stack_frame()) + return 0; + + /* + * Find stackleak_track_stack() calls. Loop through the chain of insns, + * which is an RTL representation of the code for a function. + * + * The example of a matching insn: + * (call_insn 8 4 10 2 (call (mem (symbol_ref ("stackleak_track_stack") + * [flags 0x41] ) + * [0 stackleak_track_stack S1 A8]) (0)) 675 {*call} (expr_list + * (symbol_ref ("stackleak_track_stack") [flags 0x41] ) (expr_list (0) (nil))) (nil)) + */ + for (insn = get_insns(); insn; insn = next) { + rtx body; + + next = NEXT_INSN(insn); + + /* Check the expression code of the insn */ + if (!CALL_P(insn)) + continue; + + /* + * Check the expression code of the insn body, which is an RTL + * Expression (RTX) describing the side effect performed by + * that insn. + */ + body = PATTERN(insn); + + if (GET_CODE(body) == PARALLEL) + body = XVECEXP(body, 0, 0); + + if (GET_CODE(body) != CALL) + continue; + + /* + * Check the first operand of the call expression. It should + * be a mem RTX describing the needed subroutine with a + * symbol_ref RTX. + */ + body = XEXP(body, 0); + if (GET_CODE(body) != MEM) + continue; + + body = XEXP(body, 0); + if (GET_CODE(body) != SYMBOL_REF) + continue; + + if (SYMBOL_REF_DECL(body) != track_function_decl) + continue; + + /* Delete the stackleak_track_stack() call */ + delete_insn_and_edges(insn); +#if BUILDING_GCC_VERSION >= 4007 && BUILDING_GCC_VERSION < 8000 + if (GET_CODE(next) == NOTE && + NOTE_KIND(next) == NOTE_INSN_CALL_ARG_LOCATION) { + insn = next; + next = NEXT_INSN(insn); + delete_insn_and_edges(insn); + } +#endif + } + + return 0; +} + +static bool stackleak_gate(void) +{ + tree section; + + section = lookup_attribute("section", + DECL_ATTRIBUTES(current_function_decl)); + if (section && TREE_VALUE(section)) { + section = TREE_VALUE(TREE_VALUE(section)); + + if (!strncmp(TREE_STRING_POINTER(section), ".init.text", 10)) + return false; + if (!strncmp(TREE_STRING_POINTER(section), ".devinit.text", 13)) + return false; + if (!strncmp(TREE_STRING_POINTER(section), ".cpuinit.text", 13)) + return false; + if (!strncmp(TREE_STRING_POINTER(section), ".meminit.text", 13)) + return false; + } + + return track_frame_size >= 0; +} + +/* Build the function declaration for stackleak_track_stack() */ +static void stackleak_start_unit(void *gcc_data __unused, + void *user_data __unused) +{ + tree fntype; + + /* void stackleak_track_stack(void) */ + fntype = build_function_type_list(void_type_node, NULL_TREE); + track_function_decl = build_fn_decl(track_function, fntype); + DECL_ASSEMBLER_NAME(track_function_decl); /* for LTO */ + TREE_PUBLIC(track_function_decl) = 1; + TREE_USED(track_function_decl) = 1; + DECL_EXTERNAL(track_function_decl) = 1; + DECL_ARTIFICIAL(track_function_decl) = 1; + DECL_PRESERVE_P(track_function_decl) = 1; +} + +/* + * Pass gate function is a predicate function that gets executed before the + * corresponding pass. If the return value is 'true' the pass gets executed, + * otherwise, it is skipped. + */ +static bool stackleak_instrument_gate(void) +{ + return stackleak_gate(); +} + +#define PASS_NAME stackleak_instrument +#define PROPERTIES_REQUIRED PROP_gimple_leh | PROP_cfg +#define TODO_FLAGS_START TODO_verify_ssa | TODO_verify_flow | TODO_verify_stmts +#define TODO_FLAGS_FINISH TODO_verify_ssa | TODO_verify_stmts | TODO_dump_func \ + | TODO_update_ssa | TODO_rebuild_cgraph_edges +#include "gcc-generate-gimple-pass.h" + +static bool stackleak_cleanup_gate(void) +{ + return stackleak_gate(); +} + +#define PASS_NAME stackleak_cleanup +#define TODO_FLAGS_FINISH TODO_dump_func +#include "gcc-generate-rtl-pass.h" + +/* + * Every gcc plugin exports a plugin_init() function that is called right + * after the plugin is loaded. This function is responsible for registering + * the plugin callbacks and doing other required initialization. + */ +__visible int plugin_init(struct plugin_name_args *plugin_info, + struct plugin_gcc_version *version) +{ + const char * const plugin_name = plugin_info->base_name; + const int argc = plugin_info->argc; + const struct plugin_argument * const argv = plugin_info->argv; + int i = 0; + + /* Extra GGC root tables describing our GTY-ed data */ + static const struct ggc_root_tab gt_ggc_r_gt_stackleak[] = { + { + .base = &track_function_decl, + .nelt = 1, + .stride = sizeof(track_function_decl), + .cb = >_ggc_mx_tree_node, + .pchw = >_pch_nx_tree_node + }, + LAST_GGC_ROOT_TAB + }; + + /* + * The stackleak_instrument pass should be executed before the + * "optimized" pass, which is the control flow graph cleanup that is + * performed just before expanding gcc trees to the RTL. In former + * versions of the plugin this new pass was inserted before the + * "tree_profile" pass, which is currently called "profile". + */ + PASS_INFO(stackleak_instrument, "optimized", 1, + PASS_POS_INSERT_BEFORE); + + /* + * The stackleak_cleanup pass should be executed after the + * "reload" pass, when the stack frame size is final. + */ + PASS_INFO(stackleak_cleanup, "reload", 1, PASS_POS_INSERT_AFTER); + + if (!plugin_default_version_check(version, &gcc_version)) { + error(G_("incompatible gcc/plugin versions")); + return 1; + } + + /* Parse the plugin arguments */ + for (i = 0; i < argc; i++) { + if (!strcmp(argv[i].key, "disable")) + return 0; + + if (!strcmp(argv[i].key, "track-min-size")) { + if (!argv[i].value) { + error(G_("no value supplied for option '-fplugin-arg-%s-%s'"), + plugin_name, argv[i].key); + return 1; + } + + track_frame_size = atoi(argv[i].value); + if (track_frame_size < 0) { + error(G_("invalid option argument '-fplugin-arg-%s-%s=%s'"), + plugin_name, argv[i].key, argv[i].value); + return 1; + } + } else { + error(G_("unknown option '-fplugin-arg-%s-%s'"), + plugin_name, argv[i].key); + return 1; + } + } + + /* Give the information about the plugin */ + register_callback(plugin_name, PLUGIN_INFO, NULL, + &stackleak_plugin_info); + + /* Register to be called before processing a translation unit */ + register_callback(plugin_name, PLUGIN_START_UNIT, + &stackleak_start_unit, NULL); + + /* Register an extra GCC garbage collector (GGC) root table */ + register_callback(plugin_name, PLUGIN_REGISTER_GGC_ROOTS, NULL, + (void *)>_ggc_r_gt_stackleak); + + /* + * Hook into the Pass Manager to register new gcc passes. + * + * The stack frame size info is available only at the last RTL pass, + * when it's too late to insert complex code like a function call. + * So we register two gcc passes to instrument every function at first + * and remove the unneeded instrumentation later. + */ + register_callback(plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL, + &stackleak_instrument_pass_info); + register_callback(plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL, + &stackleak_cleanup_pass_info); + + return 0; +} -- cgit v1.2.3 From c8d126275a5fa59394fe17109bdb9812fed296b8 Mon Sep 17 00:00:00 2001 From: Alexander Popov Date: Fri, 17 Aug 2018 01:17:01 +0300 Subject: fs/proc: Show STACKLEAK metrics in the /proc file system Introduce CONFIG_STACKLEAK_METRICS providing STACKLEAK information about tasks via the /proc file system. In particular, /proc//stack_depth shows the maximum kernel stack consumption for the current and previous syscalls. Although this information is not precise, it can be useful for estimating the STACKLEAK performance impact for your workloads. Suggested-by: Ingo Molnar Signed-off-by: Alexander Popov Tested-by: Laura Abbott Signed-off-by: Kees Cook --- scripts/gcc-plugins/Kconfig | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'scripts') diff --git a/scripts/gcc-plugins/Kconfig b/scripts/gcc-plugins/Kconfig index c65fdd823591..b0a015ef5268 100644 --- a/scripts/gcc-plugins/Kconfig +++ b/scripts/gcc-plugins/Kconfig @@ -170,4 +170,16 @@ config STACKLEAK_TRACK_MIN_SIZE a stack frame size greater than or equal to this parameter. If unsure, leave the default value 100. +config STACKLEAK_METRICS + bool "Show STACKLEAK metrics in the /proc file system" + depends on GCC_PLUGIN_STACKLEAK + depends on PROC_FS + help + If this is set, STACKLEAK metrics for every task are available in + the /proc file system. In particular, /proc//stack_depth + shows the maximum kernel stack consumption for the current and + previous syscalls. Although this information is not precise, it + can be useful for estimating the STACKLEAK performance impact for + your workloads. + endif -- cgit v1.2.3 From 964c9dff0091893a9a74a88edf984c6da0b779f7 Mon Sep 17 00:00:00 2001 From: Alexander Popov Date: Fri, 17 Aug 2018 01:17:03 +0300 Subject: stackleak: Allow runtime disabling of kernel stack erasing Introduce CONFIG_STACKLEAK_RUNTIME_DISABLE option, which provides 'stack_erasing' sysctl. It can be used in runtime to control kernel stack erasing for kernels built with CONFIG_GCC_PLUGIN_STACKLEAK. Suggested-by: Ingo Molnar Signed-off-by: Alexander Popov Tested-by: Laura Abbott Signed-off-by: Kees Cook --- scripts/gcc-plugins/Kconfig | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'scripts') diff --git a/scripts/gcc-plugins/Kconfig b/scripts/gcc-plugins/Kconfig index b0a015ef5268..0d5c799688f0 100644 --- a/scripts/gcc-plugins/Kconfig +++ b/scripts/gcc-plugins/Kconfig @@ -182,4 +182,12 @@ config STACKLEAK_METRICS can be useful for estimating the STACKLEAK performance impact for your workloads. +config STACKLEAK_RUNTIME_DISABLE + bool "Allow runtime disabling of kernel stack erasing" + depends on GCC_PLUGIN_STACKLEAK + help + This option provides 'stack_erasing' sysctl, which can be used in + runtime to control kernel stack erasing for kernels built with + CONFIG_GCC_PLUGIN_STACKLEAK. + endif -- cgit v1.2.3 From a7ddcea58ae22d85d94eabfdd3de75c3742e376b Mon Sep 17 00:00:00 2001 From: Henrik Austad Date: Tue, 4 Sep 2018 00:15:23 +0200 Subject: Drop all 00-INDEX files from Documentation/ This is a respin with a wider audience (all that get_maintainer returned) and I know this spams a *lot* of people. Not sure what would be the correct way, so my apologies for ruining your inbox. The 00-INDEX files are supposed to give a summary of all files present in a directory, but these files are horribly out of date and their usefulness is brought into question. Often a simple "ls" would reveal the same information as the filenames are generally quite descriptive as a short introduction to what the file covers (it should not surprise anyone what Documentation/sched/sched-design-CFS.txt covers) A few years back it was mentioned that these files were no longer really needed, and they have since then grown further out of date, so perhaps it is time to just throw them out. A short status yields the following _outdated_ 00-INDEX files, first counter is files listed in 00-INDEX but missing in the directory, last is files present but not listed in 00-INDEX. List of outdated 00-INDEX: Documentation: (4/10) Documentation/sysctl: (0/1) Documentation/timers: (1/0) Documentation/blockdev: (3/1) Documentation/w1/slaves: (0/1) Documentation/locking: (0/1) Documentation/devicetree: (0/5) Documentation/power: (1/1) Documentation/powerpc: (0/5) Documentation/arm: (1/0) Documentation/x86: (0/9) Documentation/x86/x86_64: (1/1) Documentation/scsi: (4/4) Documentation/filesystems: (2/9) Documentation/filesystems/nfs: (0/2) Documentation/cgroup-v1: (0/2) Documentation/kbuild: (0/4) Documentation/spi: (1/0) Documentation/virtual/kvm: (1/0) Documentation/scheduler: (0/2) Documentation/fb: (0/1) Documentation/block: (0/1) Documentation/networking: (6/37) Documentation/vm: (1/3) Then there are 364 subdirectories in Documentation/ with several files that are missing 00-INDEX alltogether (and another 120 with a single file and no 00-INDEX). I don't really have an opinion to whether or not we /should/ have 00-INDEX, but the above 00-INDEX should either be removed or be kept up to date. If we should keep the files, I can try to keep them updated, but I rather not if we just want to delete them anyway. As a starting point, remove all index-files and references to 00-INDEX and see where the discussion is going. Signed-off-by: Henrik Austad Acked-by: "Paul E. McKenney" Just-do-it-by: Steven Rostedt Reviewed-by: Jens Axboe Acked-by: Paul Moore Acked-by: Greg Kroah-Hartman Acked-by: Mark Brown Acked-by: Mike Rapoport Cc: [Almost everybody else] Signed-off-by: Jonathan Corbet --- scripts/check_00index.sh | 67 ------------------------------------------------ 1 file changed, 67 deletions(-) delete mode 100755 scripts/check_00index.sh (limited to 'scripts') diff --git a/scripts/check_00index.sh b/scripts/check_00index.sh deleted file mode 100755 index aa47f5926c80..000000000000 --- a/scripts/check_00index.sh +++ /dev/null @@ -1,67 +0,0 @@ -#!/bin/bash -# SPDX-License-Identifier: GPL-2.0 - -cd Documentation/ - -# Check entries that should be removed - -obsolete="" -for i in $(tail -n +12 00-INDEX |grep -E '^[a-zA-Z0-9]+'); do - if [ ! -e $i ]; then - obsolete="$obsolete $i" - fi -done - -# Check directory entries that should be added -search="" -dir="" -for i in $(find . -maxdepth 1 -type d); do - if [ "$i" != "." ]; then - new=$(echo $i|perl -ne 's,./(.*),$1/,; print $_') - search="$search $new" - fi -done - -for i in $search; do - if [ "$(grep -P "^$i" 00-INDEX)" == "" ]; then - dir="$dir $i" - fi -done - -# Check file entries that should be added -search="" -file="" -for i in $(find . -maxdepth 1 -type f); do - if [ "$i" != "./.gitignore" ]; then - new=$(echo $i|perl -ne 's,./(.*),$1,; print $_') - search="$search $new" - fi -done - -for i in $search; do - if [ "$(grep -P "^$i\$" 00-INDEX)" == "" ]; then - file="$file $i" - fi -done - -# Output its findings - -echo -e "Documentation/00-INDEX check results:\n" - -if [ "$obsolete" != "" ]; then - echo -e "- Should remove those entries:\n\t$obsolete\n" -else - echo -e "- No obsolete entries\n" -fi - -if [ "$dir" != "" ]; then - echo -e "- Should document those directories:\n\t$dir\n" -else - echo -e "- No new directories to add\n" -fi - -if [ "$file" != "" ]; then - echo -e "- Should document those files:\n\t$file" -else - echo "- No new files to add" -fi -- cgit v1.2.3 From 80ffbaa5b1bd98e80e3239a3b8cfda2da433009a Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Mon, 3 Sep 2018 06:09:34 -0600 Subject: kallsyms: reduce size a little on 64-bit Both kallsyms_num_syms and kallsyms_markers[] don't really need to use unsigned long as their (base) types; unsigned int fully suffices. Signed-off-by: Jan Beulich Signed-off-by: Masahiro Yamada --- scripts/kallsyms.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c index a9186a98a37d..085b6a584fe0 100644 --- a/scripts/kallsyms.c +++ b/scripts/kallsyms.c @@ -405,7 +405,7 @@ static void write_src(void) } output_label("kallsyms_num_syms"); - printf("\tPTR\t%u\n", table_cnt); + printf("\t.long\t%u\n", table_cnt); printf("\n"); /* table of offset markers, that give the offset in the compressed stream @@ -434,7 +434,7 @@ static void write_src(void) output_label("kallsyms_markers"); for (i = 0; i < ((table_cnt + 255) >> 8); i++) - printf("\tPTR\t%d\n", markers[i]); + printf("\t.long\t%u\n", markers[i]); printf("\n"); free(markers); -- cgit v1.2.3 From 36f546a1bdb5d268aec97b2fe4800c91a4f5596a Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 4 Sep 2018 09:42:24 +0900 Subject: kallsyms: remove left-over Blackfin code These symbols were added by commit 028f042613c3 ("kallsyms: support kernel symbols in Blackfin on-chip memory") for Blackfin. The Blackfin support was removed by commit 4ba66a976072 ("arch: remove blackfin port"). Signed-off-by: Masahiro Yamada --- scripts/kallsyms.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'scripts') diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c index 085b6a584fe0..109a1af7e444 100644 --- a/scripts/kallsyms.c +++ b/scripts/kallsyms.c @@ -48,8 +48,6 @@ static unsigned long long relative_base; static struct addr_range text_ranges[] = { { "_stext", "_etext" }, { "_sinittext", "_einittext" }, - { "_stext_l1", "_etext_l1" }, /* Blackfin on-chip L1 inst SRAM */ - { "_stext_l2", "_etext_l2" }, /* Blackfin on-chip L2 SRAM */ }; #define text_range_text (&text_ranges[0]) #define text_range_inittext (&text_ranges[1]) -- cgit v1.2.3 From 487c7c7702ab12cfe6cd0b5a10364eefa1d2128f Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 12 Sep 2018 13:52:54 +0900 Subject: kbuild: prefix Makefile.dtbinst path with $(srctree) unconditionally $(srctree) always points to the top of the source tree whether KBUILD_SRC is set or not. Signed-off-by: Masahiro Yamada --- scripts/Kbuild.include | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/Kbuild.include b/scripts/Kbuild.include index ce53639a864a..46cc43e38866 100644 --- a/scripts/Kbuild.include +++ b/scripts/Kbuild.include @@ -193,7 +193,7 @@ modbuiltin := -f $(srctree)/scripts/Makefile.modbuiltin obj # Shorthand for $(Q)$(MAKE) -f scripts/Makefile.dtbinst obj= # Usage: # $(Q)$(MAKE) $(dtbinst)=dir -dtbinst := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.dtbinst obj +dtbinst := -f $(srctree)/scripts/Makefile.dtbinst obj ### # Shorthand for $(Q)$(MAKE) -f scripts/Makefile.clean obj= -- cgit v1.2.3 From 0d91bf584fe55349193afa96a2f36d288f65b218 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 12 Sep 2018 15:18:02 +0900 Subject: kbuild: remove old check for CFLAGS use This check has been here for more than a decade since commit 0c53c8e6eb45 ("kbuild: check for wrong use of CFLAGS"). Enough time for migration has passed. Signed-off-by: Masahiro Yamada --- scripts/Makefile.build | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'scripts') diff --git a/scripts/Makefile.build b/scripts/Makefile.build index 5a2d1c9578a0..cb0377427f94 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -36,21 +36,11 @@ subdir-ccflags-y := include scripts/Kbuild.include -# For backward compatibility check that these variables do not change -save-cflags := $(CFLAGS) - # The filename Kbuild has precedence over Makefile kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src)) kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile) include $(kbuild-file) -# If the save-* variables changed error out -ifeq ($(KBUILD_NOPEDANTIC),) - ifneq ("$(save-cflags)","$(CFLAGS)") - $(error CFLAGS was changed in "$(kbuild-file)". Fix it to use ccflags-y) - endif -endif - include scripts/Makefile.lib # Do not include host rules unless needed -- cgit v1.2.3 From 5a4630aadb9a9525474e9ac92965829f990cb5c4 Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Mon, 17 Sep 2018 17:07:54 +0930 Subject: ftrace: Build with CPPFLAGS to get -Qunused-arguments When building to record the mcount locations the kernel uses KBUILD_CFLAGS but not KBUILD_CPPFLAGS. This means it lacks -Qunused-arguments when building with clang, resulting in a lot of noisy warnings. Signed-off-by: Joel Stanley Reviewed-by: Nick Desaulniers Signed-off-by: Masahiro Yamada --- scripts/Makefile.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/Makefile.build b/scripts/Makefile.build index 5a2d1c9578a0..54da4b070db3 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -219,7 +219,7 @@ else sub_cmd_record_mcount = set -e ; perl $(srctree)/scripts/recordmcount.pl "$(ARCH)" \ "$(if $(CONFIG_CPU_BIG_ENDIAN),big,little)" \ "$(if $(CONFIG_64BIT),64,32)" \ - "$(OBJDUMP)" "$(OBJCOPY)" "$(CC) $(KBUILD_CFLAGS)" \ + "$(OBJDUMP)" "$(OBJCOPY)" "$(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS)" \ "$(LD) $(KBUILD_LDFLAGS)" "$(NM)" "$(RM)" "$(MV)" \ "$(if $(part-of-module),1,0)" "$(@)"; recordmcount_source := $(srctree)/scripts/recordmcount.pl -- cgit v1.2.3 From c36d5a6c74e7f1529f1af0cf39d3ecaa46c52e83 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 13 Sep 2018 08:58:55 -0500 Subject: scripts/dtc: Add yamltree.c to dtc sources dtc has a new source file, yamltree.c, that needs to be copied when syncing dtc sources. Signed-off-by: Rob Herring --- scripts/dtc/update-dtc-source.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/dtc/update-dtc-source.sh b/scripts/dtc/update-dtc-source.sh index 1a009fd195d0..7dd29a0362b8 100755 --- a/scripts/dtc/update-dtc-source.sh +++ b/scripts/dtc/update-dtc-source.sh @@ -32,7 +32,7 @@ DTC_UPSTREAM_PATH=`pwd`/../dtc DTC_LINUX_PATH=`pwd`/scripts/dtc DTC_SOURCE="checks.c data.c dtc.c dtc.h flattree.c fstree.c livetree.c srcpos.c \ - srcpos.h treesource.c util.c util.h version_gen.h Makefile.dtc \ + srcpos.h treesource.c util.c util.h version_gen.h yamltree.c Makefile.dtc \ dtc-lexer.l dtc-parser.y" LIBFDT_SOURCE="Makefile.libfdt fdt.c fdt.h fdt_addresses.c fdt_empty_tree.c \ fdt_overlay.c fdt_ro.c fdt_rw.c fdt_strerror.c fdt_sw.c \ -- cgit v1.2.3 From f858927fd6ce394a7f431153d44ad0a09e8f49a1 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 13 Sep 2018 08:59:25 -0500 Subject: scripts/dtc: Update to upstream version v1.4.7-14-gc86da84d30e4 Major changes are I2C and SPI bus checks, YAML output format (for future validation), some new libfdt functions, and more libfdt validation of dtbs. The YAML addition adds an optional dependency on libyaml. pkg-config is used to test for it and pkg-config became a kconfig dependency in 4.18. This adds the following commits from upstream: c86da84d30e4 Add support for YAML encoded output 361b5e7d8067 Make type_marker_length helper public bfbfab047e45 pylibfdt: Add a means to add and delete notes 9005f4108e7c pylibfdt: Allow delprop() to return errors b94c056b137e Make valgrind optional fd06c54d4711 tests: Better testing of dtc -I fs mode c3f50c9a86d9 tests: Allow dtbs_equal_unordered to ignore mem reserves 0ac9fdee37c7 dtc: trivial '-I fs -O dts' test 0fd1c8c783f3 pylibfdt: fdt_get_mem_rsv returns 2 uint64_t values 04853cad18f4 pylibfdt: Don't incorrectly / unnecessarily override uint64_t typemap 9619c8619c37 Kill bogus TYPE_BLOB marker type ac68ff92ae20 parser: add TYPE_STRING marker to path references 90a190eb04d9 checks: add SPI bus checks 53a1bd546905 checks: add I2C bus checks 88f18909db73 dtc: Bump version to v1.4.7 85bce8b2f06d tests: Correction to vg_prepare_blob() 57f7f9e7bc7c tests: Don't call memcmp() with NULL arguments c12b2b0c20eb libfdt: fdt_address_cells() and fdt_size_cells() 3fe0eeda0b7f livetree: Set phandle properties type to uint32 853649acceba pylibfdt: Support the sequential-write interface 9b0e4fe26093 tests: Improve fdt_resize() tests 1087504bb3e8 libfdt: Add necessary header padding in fdt_create() c72fa777e613 libfdt: Copy the struct region in fdt_resize() 32b9c6130762 Preserve datatype markers when emitting dts format 6dcb8ba408ec libfdt: Add helpers for accessing unaligned words 42607f21d43e tests: Fix incorrect check name 'prop_name_chars' 9d78c33bf8a1 tests: fix grep for checks error messages b770f3d1c13f pylibfdt: Support setting the name of a node 2f0d07e678e0 pylibfdt: Add functions to set and get properties as strings 354d3dc55939 pylibfdt: Update the bytearray size with pack() 3c374d46acce pylibfdt: Allow reading integer values from properties 49d32ce40bb4 pylibfdt: Use an unsigned type for fdt32_t 481246a0c13a pylibfdt: Avoid accessing the internal _fdt member in tests 9aafa33d99ed pylibfdt: Add functions to update properties 5a598671fdbf pylibfdt: Support device-tree creation/expansion 483e170625e1 pylibfdt: Add support for reading the memory reserve map 29bb05aa4200 pylibfdt: Add support for the rest of the header functions 582a7159a5d0 pylibfdt: Add support for fdt_next_node() f0f8c9169819 pylibfdt: Reorder functions to match libfdt.h 64a69d123935 pylibfdt: Return string instead of bytearray from getprop() 4d09a83420df fdtput: Add documentation e617cbe1bd67 fdtget: Add documentation 180a93924014 Use format specifiers in a bunch of places we should b9af3b396576 scripts/dtc: Fixed format mismatch in fprintf 4b8fcc3d015c libfdt: Add fdt_check_full() function c14223fb2292 tests: Use valgrind client requests for better checking 5b67d2b955a3 tests: Better handling of valgrind errors saving blobs e2556aaeb506 tests: Remove unused #define fb9c6abddaa8 Use size_t for blob lengths in utilfdt_read* 0112fda03bf6 libfdt: Add fdt_header_size() 6473a21d8bfe Consolidate utilfdt_read_len() variants d5db5382c5e5 libfdt: Safer access to memory reservations 719d582e98ec libfdt: Propagate name errors in fdt_getprop_by_offset() 70166d62a27f libfdt: Safer access to strings section eb890c0f77dc libfdt: Make fdt_check_header() more thorough 899d6fad93f3 libfdt: Improve sequential write state checking 04b5b4062ccd libfdt: Clean up header checking functions 44d3efedc816 Preserve datatype information when parsing dts f0be81bd8de0 Make Property a subclass of bytearray 24b1f3f064d4 pylibfdt: Add a method to access the device tree directly Signed-off-by: Rob Herring --- scripts/dtc/Makefile | 7 + scripts/dtc/Makefile.dtc | 4 + scripts/dtc/checks.c | 143 ++++++++++++++++++++ scripts/dtc/data.c | 4 +- scripts/dtc/dtc-parser.y | 16 ++- scripts/dtc/dtc.c | 11 ++ scripts/dtc/dtc.h | 13 ++ scripts/dtc/flattree.c | 2 +- scripts/dtc/libfdt/fdt.c | 81 +++++++++++- scripts/dtc/libfdt/fdt_addresses.c | 35 ++--- scripts/dtc/libfdt/fdt_overlay.c | 6 +- scripts/dtc/libfdt/fdt_ro.c | 199 ++++++++++++++++++++++++---- scripts/dtc/libfdt/fdt_rw.c | 28 ++-- scripts/dtc/libfdt/fdt_sw.c | 109 ++++++++++++---- scripts/dtc/libfdt/libfdt.h | 76 +++++++++-- scripts/dtc/libfdt/libfdt_env.h | 1 + scripts/dtc/libfdt/libfdt_internal.h | 5 +- scripts/dtc/livetree.c | 12 +- scripts/dtc/treesource.c | 225 ++++++++++++++++++------------- scripts/dtc/util.c | 23 +--- scripts/dtc/util.h | 20 +-- scripts/dtc/version_gen.h | 2 +- scripts/dtc/yamltree.c | 247 +++++++++++++++++++++++++++++++++++ 23 files changed, 1033 insertions(+), 236 deletions(-) create mode 100644 scripts/dtc/yamltree.c (limited to 'scripts') diff --git a/scripts/dtc/Makefile b/scripts/dtc/Makefile index 1c943e03eaf2..81c6f5132f12 100644 --- a/scripts/dtc/Makefile +++ b/scripts/dtc/Makefile @@ -11,6 +11,13 @@ dtc-objs += dtc-lexer.lex.o dtc-parser.tab.o # Source files need to get at the userspace version of libfdt_env.h to compile HOST_EXTRACFLAGS := -I$(src)/libfdt +ifeq ($(wildcard /usr/include/yaml.h),) +HOST_EXTRACFLAGS += -DNO_YAML +else +dtc-objs += yamltree.o +HOSTLDLIBS_dtc := -lyaml +endif + # Generated files need one more search path to include headers in source tree HOSTCFLAGS_dtc-lexer.lex.o := -I$(src) HOSTCFLAGS_dtc-parser.tab.o := -I$(src) diff --git a/scripts/dtc/Makefile.dtc b/scripts/dtc/Makefile.dtc index bece49b35535..d4375630a7f7 100644 --- a/scripts/dtc/Makefile.dtc +++ b/scripts/dtc/Makefile.dtc @@ -14,5 +14,9 @@ DTC_SRCS = \ treesource.c \ util.c +ifneq ($(NO_YAML),1) +DTC_SRCS += yamltree.c +endif + DTC_GEN_SRCS = dtc-lexer.lex.c dtc-parser.tab.c DTC_OBJS = $(DTC_SRCS:%.c=%.o) $(DTC_GEN_SRCS:%.c=%.o) diff --git a/scripts/dtc/checks.c b/scripts/dtc/checks.c index a2cc1036c915..9c9b0c328af6 100644 --- a/scripts/dtc/checks.c +++ b/scripts/dtc/checks.c @@ -962,6 +962,143 @@ static void check_simple_bus_reg(struct check *c, struct dt_info *dti, struct no } WARNING(simple_bus_reg, check_simple_bus_reg, NULL, ®_format, &simple_bus_bridge); +static const struct bus_type i2c_bus = { + .name = "i2c-bus", +}; + +static void check_i2c_bus_bridge(struct check *c, struct dt_info *dti, struct node *node) +{ + if (strprefixeq(node->name, node->basenamelen, "i2c-bus") || + strprefixeq(node->name, node->basenamelen, "i2c-arb")) { + node->bus = &i2c_bus; + } else if (strprefixeq(node->name, node->basenamelen, "i2c")) { + struct node *child; + for_each_child(node, child) { + if (strprefixeq(child->name, node->basenamelen, "i2c-bus")) + return; + } + node->bus = &i2c_bus; + } else + return; + + if (!node->children) + return; + + if (node_addr_cells(node) != 1) + FAIL(c, dti, node, "incorrect #address-cells for I2C bus"); + if (node_size_cells(node) != 0) + FAIL(c, dti, node, "incorrect #size-cells for I2C bus"); + +} +WARNING(i2c_bus_bridge, check_i2c_bus_bridge, NULL, &addr_size_cells); + +static void check_i2c_bus_reg(struct check *c, struct dt_info *dti, struct node *node) +{ + struct property *prop; + const char *unitname = get_unitname(node); + char unit_addr[17]; + uint32_t reg = 0; + int len; + cell_t *cells = NULL; + + if (!node->parent || (node->parent->bus != &i2c_bus)) + return; + + prop = get_property(node, "reg"); + if (prop) + cells = (cell_t *)prop->val.val; + + if (!cells) { + FAIL(c, dti, node, "missing or empty reg property"); + return; + } + + reg = fdt32_to_cpu(*cells); + snprintf(unit_addr, sizeof(unit_addr), "%x", reg); + if (!streq(unitname, unit_addr)) + FAIL(c, dti, node, "I2C bus unit address format error, expected \"%s\"", + unit_addr); + + for (len = prop->val.len; len > 0; len -= 4) { + reg = fdt32_to_cpu(*(cells++)); + if (reg > 0x3ff) + FAIL_PROP(c, dti, node, prop, "I2C address must be less than 10-bits, got \"0x%x\"", + reg); + + } +} +WARNING(i2c_bus_reg, check_i2c_bus_reg, NULL, ®_format, &i2c_bus_bridge); + +static const struct bus_type spi_bus = { + .name = "spi-bus", +}; + +static void check_spi_bus_bridge(struct check *c, struct dt_info *dti, struct node *node) +{ + + if (strprefixeq(node->name, node->basenamelen, "spi")) { + node->bus = &spi_bus; + } else { + /* Try to detect SPI buses which don't have proper node name */ + struct node *child; + + if (node_addr_cells(node) != 1 || node_size_cells(node) != 0) + return; + + for_each_child(node, child) { + struct property *prop; + for_each_property(child, prop) { + if (strprefixeq(prop->name, 4, "spi-")) { + node->bus = &spi_bus; + break; + } + } + if (node->bus == &spi_bus) + break; + } + + if (node->bus == &spi_bus && get_property(node, "reg")) + FAIL(c, dti, node, "node name for SPI buses should be 'spi'"); + } + if (node->bus != &spi_bus || !node->children) + return; + + if (node_addr_cells(node) != 1) + FAIL(c, dti, node, "incorrect #address-cells for SPI bus"); + if (node_size_cells(node) != 0) + FAIL(c, dti, node, "incorrect #size-cells for SPI bus"); + +} +WARNING(spi_bus_bridge, check_spi_bus_bridge, NULL, &addr_size_cells); + +static void check_spi_bus_reg(struct check *c, struct dt_info *dti, struct node *node) +{ + struct property *prop; + const char *unitname = get_unitname(node); + char unit_addr[9]; + uint32_t reg = 0; + cell_t *cells = NULL; + + if (!node->parent || (node->parent->bus != &spi_bus)) + return; + + prop = get_property(node, "reg"); + if (prop) + cells = (cell_t *)prop->val.val; + + if (!cells) { + FAIL(c, dti, node, "missing or empty reg property"); + return; + } + + reg = fdt32_to_cpu(*cells); + snprintf(unit_addr, sizeof(unit_addr), "%x", reg); + if (!streq(unitname, unit_addr)) + FAIL(c, dti, node, "SPI bus unit address format error, expected \"%s\"", + unit_addr); +} +WARNING(spi_bus_reg, check_spi_bus_reg, NULL, ®_format, &spi_bus_bridge); + static void check_unit_address_format(struct check *c, struct dt_info *dti, struct node *node) { @@ -1582,6 +1719,12 @@ static struct check *check_table[] = { &simple_bus_bridge, &simple_bus_reg, + &i2c_bus_bridge, + &i2c_bus_reg, + + &spi_bus_bridge, + &spi_bus_reg, + &avoid_default_addr_size, &avoid_unnecessary_addr_size, &unique_unit_address, diff --git a/scripts/dtc/data.c b/scripts/dtc/data.c index aa37a16c8891..4a204145cc7b 100644 --- a/scripts/dtc/data.c +++ b/scripts/dtc/data.c @@ -74,7 +74,8 @@ struct data data_copy_escape_string(const char *s, int len) struct data d; char *q; - d = data_grow_for(empty_data, len + 1); + d = data_add_marker(empty_data, TYPE_STRING, NULL); + d = data_grow_for(d, len + 1); q = d.val; while (i < len) { @@ -94,6 +95,7 @@ struct data data_copy_file(FILE *f, size_t maxlen) { struct data d = empty_data; + d = data_add_marker(d, TYPE_NONE, NULL); while (!feof(f) && (d.len < maxlen)) { size_t chunksize, ret; diff --git a/scripts/dtc/dtc-parser.y b/scripts/dtc/dtc-parser.y index 011a5b25539a..dd70ebf386f4 100644 --- a/scripts/dtc/dtc-parser.y +++ b/scripts/dtc/dtc-parser.y @@ -287,6 +287,7 @@ propdata: } | propdataprefix DT_REF { + $1 = data_add_marker($1, TYPE_STRING, $2); $$ = data_add_marker($1, REF_PATH, $2); } | propdataprefix DT_INCBIN '(' DT_STRING ',' integer_prim ',' integer_prim ')' @@ -340,22 +341,27 @@ arrayprefix: DT_BITS DT_LITERAL '<' { unsigned long long bits; + enum markertype type = TYPE_UINT32; bits = $2; - if ((bits != 8) && (bits != 16) && - (bits != 32) && (bits != 64)) { + switch (bits) { + case 8: type = TYPE_UINT8; break; + case 16: type = TYPE_UINT16; break; + case 32: type = TYPE_UINT32; break; + case 64: type = TYPE_UINT64; break; + default: ERROR(&@2, "Array elements must be" " 8, 16, 32 or 64-bits"); bits = 32; } - $$.data = empty_data; + $$.data = data_add_marker(empty_data, type, NULL); $$.bits = bits; } | '<' { - $$.data = empty_data; + $$.data = data_add_marker(empty_data, TYPE_UINT32, NULL); $$.bits = 32; } | arrayprefix integer_prim @@ -499,7 +505,7 @@ integer_unary: bytestring: /* empty */ { - $$ = empty_data; + $$ = data_add_marker(empty_data, TYPE_UINT8, NULL); } | bytestring DT_BYTE { diff --git a/scripts/dtc/dtc.c b/scripts/dtc/dtc.c index c36994e6eac5..64134aadb997 100644 --- a/scripts/dtc/dtc.c +++ b/scripts/dtc/dtc.c @@ -95,6 +95,9 @@ static const char * const usage_opts_help[] = { "\n\tOutput formats are:\n" "\t\tdts - device tree source text\n" "\t\tdtb - device tree blob\n" +#ifndef NO_YAML + "\t\tyaml - device tree encoded as YAML\n" +#endif "\t\tasm - assembler source", "\n\tBlob version to produce, defaults to "stringify(DEFAULT_FDT_VERSION)" (for dtb and asm output)", "\n\tOutput dependency file", @@ -128,6 +131,8 @@ static const char *guess_type_by_name(const char *fname, const char *fallback) return fallback; if (!strcasecmp(s, ".dts")) return "dts"; + if (!strcasecmp(s, ".yaml")) + return "yaml"; if (!strcasecmp(s, ".dtb")) return "dtb"; return fallback; @@ -350,6 +355,12 @@ int main(int argc, char *argv[]) if (streq(outform, "dts")) { dt_to_source(outf, dti); +#ifndef NO_YAML + } else if (streq(outform, "yaml")) { + if (!streq(inform, "dts")) + die("YAML output format requires dts input format\n"); + dt_to_yaml(outf, dti); +#endif } else if (streq(outform, "dtb")) { dt_to_blob(outf, dti, outversion); } else if (streq(outform, "asm")) { diff --git a/scripts/dtc/dtc.h b/scripts/dtc/dtc.h index 6d667701ab6a..cbe541525c2c 100644 --- a/scripts/dtc/dtc.h +++ b/scripts/dtc/dtc.h @@ -74,10 +74,17 @@ typedef uint32_t cell_t; /* Data blobs */ enum markertype { + TYPE_NONE, REF_PHANDLE, REF_PATH, LABEL, + TYPE_UINT8, + TYPE_UINT16, + TYPE_UINT32, + TYPE_UINT64, + TYPE_STRING, }; +extern const char *markername(enum markertype markertype); struct marker { enum markertype type; @@ -101,6 +108,8 @@ struct data { for_each_marker(m) \ if ((m)->type == (t)) +size_t type_marker_length(struct marker *m); + void data_free(struct data d); struct data data_grow_for(struct data d, int xlen); @@ -290,6 +299,10 @@ struct dt_info *dt_from_blob(const char *fname); void dt_to_source(FILE *f, struct dt_info *dti); struct dt_info *dt_from_source(const char *f); +/* YAML source */ + +void dt_to_yaml(FILE *f, struct dt_info *dti); + /* FS trees */ struct dt_info *dt_from_fs(const char *dirname); diff --git a/scripts/dtc/flattree.c b/scripts/dtc/flattree.c index 8d268fb785db..851ea87dbc0f 100644 --- a/scripts/dtc/flattree.c +++ b/scripts/dtc/flattree.c @@ -393,7 +393,7 @@ void dt_to_blob(FILE *f, struct dt_info *dti, int version) padlen = 0; if (quiet < 1) fprintf(stderr, - "Warning: blob size %d >= minimum size %d\n", + "Warning: blob size %"PRIu32" >= minimum size %d\n", fdt32_to_cpu(fdt.totalsize), minsize); } } diff --git a/scripts/dtc/libfdt/fdt.c b/scripts/dtc/libfdt/fdt.c index 7855a1787763..ae03b1112961 100644 --- a/scripts/dtc/libfdt/fdt.c +++ b/scripts/dtc/libfdt/fdt.c @@ -55,7 +55,12 @@ #include "libfdt_internal.h" -int fdt_check_header(const void *fdt) +/* + * Minimal sanity check for a read-only tree. fdt_ro_probe_() checks + * that the given buffer contains what appears to be a flattened + * device tree with sane information in its header. + */ +int fdt_ro_probe_(const void *fdt) { if (fdt_magic(fdt) == FDT_MAGIC) { /* Complete tree */ @@ -74,6 +79,78 @@ int fdt_check_header(const void *fdt) return 0; } +static int check_off_(uint32_t hdrsize, uint32_t totalsize, uint32_t off) +{ + return (off >= hdrsize) && (off <= totalsize); +} + +static int check_block_(uint32_t hdrsize, uint32_t totalsize, + uint32_t base, uint32_t size) +{ + if (!check_off_(hdrsize, totalsize, base)) + return 0; /* block start out of bounds */ + if ((base + size) < base) + return 0; /* overflow */ + if (!check_off_(hdrsize, totalsize, base + size)) + return 0; /* block end out of bounds */ + return 1; +} + +size_t fdt_header_size_(uint32_t version) +{ + if (version <= 1) + return FDT_V1_SIZE; + else if (version <= 2) + return FDT_V2_SIZE; + else if (version <= 3) + return FDT_V3_SIZE; + else if (version <= 16) + return FDT_V16_SIZE; + else + return FDT_V17_SIZE; +} + +int fdt_check_header(const void *fdt) +{ + size_t hdrsize; + + if (fdt_magic(fdt) != FDT_MAGIC) + return -FDT_ERR_BADMAGIC; + hdrsize = fdt_header_size(fdt); + if ((fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) + || (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION)) + return -FDT_ERR_BADVERSION; + if (fdt_version(fdt) < fdt_last_comp_version(fdt)) + return -FDT_ERR_BADVERSION; + + if ((fdt_totalsize(fdt) < hdrsize) + || (fdt_totalsize(fdt) > INT_MAX)) + return -FDT_ERR_TRUNCATED; + + /* Bounds check memrsv block */ + if (!check_off_(hdrsize, fdt_totalsize(fdt), fdt_off_mem_rsvmap(fdt))) + return -FDT_ERR_TRUNCATED; + + /* Bounds check structure block */ + if (fdt_version(fdt) < 17) { + if (!check_off_(hdrsize, fdt_totalsize(fdt), + fdt_off_dt_struct(fdt))) + return -FDT_ERR_TRUNCATED; + } else { + if (!check_block_(hdrsize, fdt_totalsize(fdt), + fdt_off_dt_struct(fdt), + fdt_size_dt_struct(fdt))) + return -FDT_ERR_TRUNCATED; + } + + /* Bounds check strings block */ + if (!check_block_(hdrsize, fdt_totalsize(fdt), + fdt_off_dt_strings(fdt), fdt_size_dt_strings(fdt))) + return -FDT_ERR_TRUNCATED; + + return 0; +} + const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len) { unsigned absoffset = offset + fdt_off_dt_struct(fdt); @@ -244,7 +321,7 @@ const char *fdt_find_string_(const char *strtab, int tabsize, const char *s) int fdt_move(const void *fdt, void *buf, int bufsize) { - FDT_CHECK_HEADER(fdt); + FDT_RO_PROBE(fdt); if (fdt_totalsize(fdt) > bufsize) return -FDT_ERR_NOSPACE; diff --git a/scripts/dtc/libfdt/fdt_addresses.c b/scripts/dtc/libfdt/fdt_addresses.c index eff4dbcc729d..49537b578d03 100644 --- a/scripts/dtc/libfdt/fdt_addresses.c +++ b/scripts/dtc/libfdt/fdt_addresses.c @@ -1,6 +1,7 @@ /* * libfdt - Flat Device Tree manipulation * Copyright (C) 2014 David Gibson + * Copyright (C) 2018 embedded brains GmbH * * libfdt is dual licensed: you can use it either under the terms of * the GPL, or the BSD license, at your option. @@ -55,42 +56,32 @@ #include "libfdt_internal.h" -int fdt_address_cells(const void *fdt, int nodeoffset) +static int fdt_cells(const void *fdt, int nodeoffset, const char *name) { - const fdt32_t *ac; + const fdt32_t *c; int val; int len; - ac = fdt_getprop(fdt, nodeoffset, "#address-cells", &len); - if (!ac) + c = fdt_getprop(fdt, nodeoffset, name, &len); + if (!c) return 2; - if (len != sizeof(*ac)) + if (len != sizeof(*c)) return -FDT_ERR_BADNCELLS; - val = fdt32_to_cpu(*ac); + val = fdt32_to_cpu(*c); if ((val <= 0) || (val > FDT_MAX_NCELLS)) return -FDT_ERR_BADNCELLS; return val; } -int fdt_size_cells(const void *fdt, int nodeoffset) +int fdt_address_cells(const void *fdt, int nodeoffset) { - const fdt32_t *sc; - int val; - int len; - - sc = fdt_getprop(fdt, nodeoffset, "#size-cells", &len); - if (!sc) - return 2; - - if (len != sizeof(*sc)) - return -FDT_ERR_BADNCELLS; - - val = fdt32_to_cpu(*sc); - if ((val < 0) || (val > FDT_MAX_NCELLS)) - return -FDT_ERR_BADNCELLS; + return fdt_cells(fdt, nodeoffset, "#address-cells"); +} - return val; +int fdt_size_cells(const void *fdt, int nodeoffset) +{ + return fdt_cells(fdt, nodeoffset, "#size-cells"); } diff --git a/scripts/dtc/libfdt/fdt_overlay.c b/scripts/dtc/libfdt/fdt_overlay.c index bf75388ec9a2..5fdab6c6371d 100644 --- a/scripts/dtc/libfdt/fdt_overlay.c +++ b/scripts/dtc/libfdt/fdt_overlay.c @@ -697,7 +697,7 @@ static int get_path_len(const void *fdt, int nodeoffset) int len = 0, namelen; const char *name; - FDT_CHECK_HEADER(fdt); + FDT_RO_PROBE(fdt); for (;;) { name = fdt_get_name(fdt, nodeoffset, &namelen); @@ -866,8 +866,8 @@ int fdt_overlay_apply(void *fdt, void *fdto) uint32_t delta = fdt_get_max_phandle(fdt); int ret; - FDT_CHECK_HEADER(fdt); - FDT_CHECK_HEADER(fdto); + FDT_RO_PROBE(fdt); + FDT_RO_PROBE(fdto); ret = overlay_adjust_local_phandles(fdto, delta); if (ret) diff --git a/scripts/dtc/libfdt/fdt_ro.c b/scripts/dtc/libfdt/fdt_ro.c index dfb3236da388..eafc14282892 100644 --- a/scripts/dtc/libfdt/fdt_ro.c +++ b/scripts/dtc/libfdt/fdt_ro.c @@ -76,17 +76,72 @@ static int fdt_nodename_eq_(const void *fdt, int offset, return 0; } +const char *fdt_get_string(const void *fdt, int stroffset, int *lenp) +{ + uint32_t absoffset = stroffset + fdt_off_dt_strings(fdt); + size_t len; + int err; + const char *s, *n; + + err = fdt_ro_probe_(fdt); + if (err != 0) + goto fail; + + err = -FDT_ERR_BADOFFSET; + if (absoffset >= fdt_totalsize(fdt)) + goto fail; + len = fdt_totalsize(fdt) - absoffset; + + if (fdt_magic(fdt) == FDT_MAGIC) { + if (stroffset < 0) + goto fail; + if (fdt_version(fdt) >= 17) { + if (stroffset >= fdt_size_dt_strings(fdt)) + goto fail; + if ((fdt_size_dt_strings(fdt) - stroffset) < len) + len = fdt_size_dt_strings(fdt) - stroffset; + } + } else if (fdt_magic(fdt) == FDT_SW_MAGIC) { + if ((stroffset >= 0) + || (stroffset < -fdt_size_dt_strings(fdt))) + goto fail; + if ((-stroffset) < len) + len = -stroffset; + } else { + err = -FDT_ERR_INTERNAL; + goto fail; + } + + s = (const char *)fdt + absoffset; + n = memchr(s, '\0', len); + if (!n) { + /* missing terminating NULL */ + err = -FDT_ERR_TRUNCATED; + goto fail; + } + + if (lenp) + *lenp = n - s; + return s; + +fail: + if (lenp) + *lenp = err; + return NULL; +} + const char *fdt_string(const void *fdt, int stroffset) { - return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset; + return fdt_get_string(fdt, stroffset, NULL); } static int fdt_string_eq_(const void *fdt, int stroffset, const char *s, int len) { - const char *p = fdt_string(fdt, stroffset); + int slen; + const char *p = fdt_get_string(fdt, stroffset, &slen); - return (strlen(p) == len) && (memcmp(p, s, len) == 0); + return p && (slen == len) && (memcmp(p, s, len) == 0); } uint32_t fdt_get_max_phandle(const void *fdt) @@ -115,21 +170,42 @@ uint32_t fdt_get_max_phandle(const void *fdt) return 0; } +static const struct fdt_reserve_entry *fdt_mem_rsv(const void *fdt, int n) +{ + int offset = n * sizeof(struct fdt_reserve_entry); + int absoffset = fdt_off_mem_rsvmap(fdt) + offset; + + if (absoffset < fdt_off_mem_rsvmap(fdt)) + return NULL; + if (absoffset > fdt_totalsize(fdt) - sizeof(struct fdt_reserve_entry)) + return NULL; + return fdt_mem_rsv_(fdt, n); +} + 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); + const struct fdt_reserve_entry *re; + + FDT_RO_PROBE(fdt); + re = fdt_mem_rsv(fdt, n); + if (!re) + return -FDT_ERR_BADOFFSET; + + *address = fdt64_ld(&re->address); + *size = fdt64_ld(&re->size); return 0; } int fdt_num_mem_rsv(const void *fdt) { - int i = 0; + int i; + const struct fdt_reserve_entry *re; - while (fdt64_to_cpu(fdt_mem_rsv_(fdt, i)->size) != 0) - i++; - return i; + for (i = 0; (re = fdt_mem_rsv(fdt, i)) != NULL; i++) { + if (fdt64_ld(&re->size) == 0) + return i; + } + return -FDT_ERR_TRUNCATED; } static int nextprop_(const void *fdt, int offset) @@ -161,7 +237,7 @@ int fdt_subnode_offset_namelen(const void *fdt, int offset, { int depth; - FDT_CHECK_HEADER(fdt); + FDT_RO_PROBE(fdt); for (depth = 0; (offset >= 0) && (depth >= 0); @@ -187,7 +263,7 @@ int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen) const char *p = path; int offset = 0; - FDT_CHECK_HEADER(fdt); + FDT_RO_PROBE(fdt); /* see if we have an alias */ if (*path != '/') { @@ -237,7 +313,7 @@ const char *fdt_get_name(const void *fdt, int nodeoffset, int *len) const char *nameptr; int err; - if (((err = fdt_check_header(fdt)) != 0) + if (((err = fdt_ro_probe_(fdt)) != 0) || ((err = fdt_check_node_offset_(fdt, nodeoffset)) < 0)) goto fail; @@ -303,7 +379,7 @@ static const struct fdt_property *fdt_get_property_by_offset_(const void *fdt, prop = fdt_offset_ptr_(fdt, offset); if (lenp) - *lenp = fdt32_to_cpu(prop->len); + *lenp = fdt32_ld(&prop->len); return prop; } @@ -340,7 +416,7 @@ static const struct fdt_property *fdt_get_property_namelen_(const void *fdt, offset = -FDT_ERR_INTERNAL; break; } - if (fdt_string_eq_(fdt, fdt32_to_cpu(prop->nameoff), + if (fdt_string_eq_(fdt, fdt32_ld(&prop->nameoff), name, namelen)) { if (poffset) *poffset = offset; @@ -393,7 +469,7 @@ const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, /* Handle realignment */ if (fdt_version(fdt) < 0x10 && (poffset + sizeof(*prop)) % 8 && - fdt32_to_cpu(prop->len) >= 8) + fdt32_ld(&prop->len) >= 8) return prop->data + 4; return prop->data; } @@ -406,12 +482,22 @@ const void *fdt_getprop_by_offset(const void *fdt, int offset, prop = fdt_get_property_by_offset_(fdt, offset, lenp); if (!prop) return NULL; - if (namep) - *namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); + if (namep) { + const char *name; + int namelen; + name = fdt_get_string(fdt, fdt32_ld(&prop->nameoff), + &namelen); + if (!name) { + if (lenp) + *lenp = namelen; + return NULL; + } + *namep = name; + } /* Handle realignment */ if (fdt_version(fdt) < 0x10 && (offset + sizeof(*prop)) % 8 && - fdt32_to_cpu(prop->len) >= 8) + fdt32_ld(&prop->len) >= 8) return prop->data + 4; return prop->data; } @@ -436,7 +522,7 @@ uint32_t fdt_get_phandle(const void *fdt, int nodeoffset) return 0; } - return fdt32_to_cpu(*php); + return fdt32_ld(php); } const char *fdt_get_alias_namelen(const void *fdt, @@ -462,7 +548,7 @@ int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen) int offset, depth, namelen; const char *name; - FDT_CHECK_HEADER(fdt); + FDT_RO_PROBE(fdt); if (buflen < 2) return -FDT_ERR_NOSPACE; @@ -514,7 +600,7 @@ int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, int offset, depth; int supernodeoffset = -FDT_ERR_INTERNAL; - FDT_CHECK_HEADER(fdt); + FDT_RO_PROBE(fdt); if (supernodedepth < 0) return -FDT_ERR_NOTFOUND; @@ -573,7 +659,7 @@ int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, const void *val; int len; - FDT_CHECK_HEADER(fdt); + FDT_RO_PROBE(fdt); /* FIXME: The algorithm here is pretty horrible: we scan each * property of a node in fdt_getprop(), then if that didn't @@ -599,7 +685,7 @@ int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle) if ((phandle == 0) || (phandle == -1)) return -FDT_ERR_BADPHANDLE; - FDT_CHECK_HEADER(fdt); + FDT_RO_PROBE(fdt); /* FIXME: The algorithm here is pretty horrible: we * potentially scan each property of a node in @@ -752,7 +838,7 @@ int fdt_node_offset_by_compatible(const void *fdt, int startoffset, { int offset, err; - FDT_CHECK_HEADER(fdt); + FDT_RO_PROBE(fdt); /* FIXME: The algorithm here is pretty horrible: we scan each * property of a node in fdt_node_check_compatible(), then if @@ -771,3 +857,66 @@ int fdt_node_offset_by_compatible(const void *fdt, int startoffset, return offset; /* error from fdt_next_node() */ } + +int fdt_check_full(const void *fdt, size_t bufsize) +{ + int err; + int num_memrsv; + int offset, nextoffset = 0; + uint32_t tag; + unsigned depth = 0; + const void *prop; + const char *propname; + + if (bufsize < FDT_V1_SIZE) + return -FDT_ERR_TRUNCATED; + err = fdt_check_header(fdt); + if (err != 0) + return err; + if (bufsize < fdt_totalsize(fdt)) + return -FDT_ERR_TRUNCATED; + + num_memrsv = fdt_num_mem_rsv(fdt); + if (num_memrsv < 0) + return num_memrsv; + + while (1) { + offset = nextoffset; + tag = fdt_next_tag(fdt, offset, &nextoffset); + + if (nextoffset < 0) + return nextoffset; + + switch (tag) { + case FDT_NOP: + break; + + case FDT_END: + if (depth != 0) + return -FDT_ERR_BADSTRUCTURE; + return 0; + + case FDT_BEGIN_NODE: + depth++; + if (depth > INT_MAX) + return -FDT_ERR_BADSTRUCTURE; + break; + + case FDT_END_NODE: + if (depth == 0) + return -FDT_ERR_BADSTRUCTURE; + depth--; + break; + + case FDT_PROP: + prop = fdt_getprop_by_offset(fdt, offset, &propname, + &err); + if (!prop) + return err; + break; + + default: + return -FDT_ERR_INTERNAL; + } + } +} diff --git a/scripts/dtc/libfdt/fdt_rw.c b/scripts/dtc/libfdt/fdt_rw.c index 9b829051e444..2e49855d7cf8 100644 --- a/scripts/dtc/libfdt/fdt_rw.c +++ b/scripts/dtc/libfdt/fdt_rw.c @@ -67,9 +67,9 @@ static int fdt_blocks_misordered_(const void *fdt, (fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt))); } -static int fdt_rw_check_hea