// SPDX-License-Identifier: GPL-2.0 /* * debug_kinfo.c - backup kernel information for bootloader usage * * Copyright 2002 Rusty Russell IBM Corporation * Copyright 2021 Google LLC */ #include #include #include #include #include #include #include #include #include "debug_kinfo.h" /* * These will be re-linked against their real values * during the second link stage. */ extern const unsigned long kallsyms_addresses[] __weak; extern const int kallsyms_offsets[] __weak; extern const u8 kallsyms_names[] __weak; /* * Tell the compiler that the count isn't in the small data section if the arch * has one (eg: FRV). */ extern const unsigned int kallsyms_num_syms __weak __section(".rodata"); extern const unsigned long kallsyms_relative_base __weak __section(".rodata"); extern const u8 kallsyms_token_table[] __weak; extern const u16 kallsyms_token_index[] __weak; extern const unsigned int kallsyms_markers[] __weak; static void *all_info_addr; static u32 all_info_size; static void update_kernel_all_info(struct kernel_all_info *all_info) { int index; struct kernel_info *info; u32 *checksum_info; all_info->magic_number = DEBUG_KINFO_MAGIC; all_info->combined_checksum = 0; info = &(all_info->info); checksum_info = (u32 *)info; for (index = 0; index < sizeof(*info) / sizeof(u32); index++) all_info->combined_checksum ^= checksum_info[index]; } static int build_info_set(const char *str, const struct kernel_param *kp) { struct kernel_all_info *all_info; size_t build_info_size; int ret = 0; if (all_info_addr == 0 || all_info_size == 0) { ret = -EPERM; goto Exit; } all_info = (struct kernel_all_info *)all_info_addr; build_info_size = sizeof(all_info->info.build_info); memcpy(&all_info->info.build_info, str, min(build_info_size - 1, strlen(str))); update_kernel_all_info(all_info); if (strlen(str) > build_info_size) { pr_warn("%s: Build info buffer (len: %zd) can't hold entire string '%s'\n", __func__, build_info_size, str); ret = -ENOMEM; } Exit: return ret; } static const struct kernel_param_ops build_info_op = { .set = build_info_set, }; module_param_cb(build_info, &build_info_op, NULL, 0200); MODULE_PARM_DESC(build_info, "Write build info to field 'build_info' of debug kinfo."); static int debug_kinfo_probe(struct platform_device *pdev) { struct device_node *mem_region; struct reserved_mem *rmem; struct kernel_all_info *all_info; struct kernel_info *info; mem_region = of_parse_phandle(pdev->dev.of_node, "memory-region", 0); if (!mem_region) { dev_warn(&pdev->dev, "no such memory-region\n"); return -ENODEV; } rmem = of_reserved_mem_lookup(mem_region); if (!rmem) { dev_warn(&pdev->dev, "no such reserved mem of node name %s\n", pdev->dev.of_node->name); return -ENODEV; } /* Need to wait for reserved memory to be mapped */ if (!rmem->priv) { return -EPROBE_DEFER; } if (!rmem->base || !rmem->size) { dev_warn(&pdev->dev, "unexpected reserved memory\n"); return -EINVAL; } if (rmem->size < sizeof(struct kernel_all_info)) { dev_warn(&pdev->dev, "unexpected reserved memory size\n"); return -EINVAL; } all_info_addr = rmem->priv; all_info_size = rmem->size; memset(all_info_addr, 0, sizeof(struct kernel_all_info)); all_info = (struct kernel_all_info *)all_info_addr; info = &(all_info->info); info->enabled_all = IS_ENABLED(CONFIG_KALLSYMS_ALL); info->enabled_base_relative = IS_ENABLED(CONFIG_KALLSYMS_BASE_RELATIVE); info->enabled_absolute_percpu = IS_ENABLED(CONFIG_KALLSYMS_ABSOLUTE_PERCPU); info->enabled_cfi_clang = IS_ENABLED(CONFIG_CFI_CLANG); info->num_syms = kallsyms_num_syms; info->name_len = KSYM_NAME_LEN; info->bit_per_long = BITS_PER_LONG; info->module_name_len = MODULE_NAME_LEN; info->symbol_len = KSYM_SYMBOL_LEN; if (!info->enabled_base_relative) info->_addresses_pa = (u64)__pa_symbol((volatile void *)kallsyms_addresses); else { info->_relative_pa = (u64)__pa_symbol((volatile void *)kallsyms_relative_base); info->_offsets_pa = (u64)__pa_symbol((volatile void *)kallsyms_offsets); } info->_stext_pa = (u64)__pa_symbol(_stext); info->_etext_pa = (u64)__pa_symbol(_etext); info->_sinittext_pa = (u64)__pa_symbol(_sinittext); info->_einittext_pa = (u64)__pa_symbol(_einittext); info->_end_pa = (u64)__pa_symbol(_end); info->_names_pa = (u64)__pa_symbol((volatile void *)kallsyms_names); info->_token_table_pa = (u64)__pa_symbol((volatile void *)kallsyms_token_table); info->_token_index_pa = (u64)__pa_symbol((volatile void *)kallsyms_token_index); info->_markers_pa = (u64)__pa_symbol((volatile void *)kallsyms_markers); info->thread_size = THREAD_SIZE; info->swapper_pg_dir_pa = (u64)__pa_symbol(swapper_pg_dir); strlcpy(info->last_uts_release, init_utsname()->release, sizeof(info->last_uts_release)); info->enabled_modules_tree_lookup = IS_ENABLED(CONFIG_MODULES_TREE_LOOKUP); info->mod_core_layout_offset = offsetof(struct module, core_layout); info->mod_init_layout_offset = offsetof(struct module, init_layout); info->mod_kallsyms_offset = offsetof(struct module, kallsyms); #if defined(CONFIG_RANDOMIZE_BASE) && defined(MODULES_VSIZE) info->module_start_va = module_alloc_base; info->module_end_va = info->module_start_va + MODULES_VSIZE; #elif defined(CONFIG_MODULES) && defined(MODULES_VADDR) info->module_start_va = MODULES_VADDR; info->module_end_va = MODULES_END; #else info->module_start_va = VMALLOC_START; info->module_end_va = VMALLOC_END; #endif update_kernel_all_info(all_info); return 0; } static const struct of_device_id debug_kinfo_of_match[] = { { .compatible = "google,debug-kinfo" }, {}, }; MODULE_DEVICE_TABLE(of, debug_kinfo_of_match); static struct platform_driver debug_kinfo_driver = { .probe = debug_kinfo_probe, .driver = { .name = "debug-kinfo", .of_match_table = of_match_ptr(debug_kinfo_of_match), }, }; module_platform_driver(debug_kinfo_driver); MODULE_AUTHOR("Jone Chou "); MODULE_DESCRIPTION("Debug Kinfo Driver"); MODULE_LICENSE("GPL v2");