| From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
| From: Jone Chou <jonechou@google.com> |
| Date: Fri, 16 Oct 2020 19:40:55 +0800 |
| Subject: ANDROID: staging: add debug-kinfo driver |
| |
| Backup kernel information for bootloader usage. |
| |
| Specifics: |
| - The kallsyms symbols for unwind_backtrace |
| - Page directory pointer |
| - UTS_RELEASE |
| - BUILD_INFO(ro.build.fingerprint) |
| |
| [CPNOTE: 06/07/21] Lee: Pinged Will for an update |
| |
| Bug: 170851792 |
| Bug: 169101608 |
| Signed-off-by: Jone Chou <jonechou@google.com> |
| Signed-off-by: Will McVicker <willmcvicker@google.com> |
| Change-Id: Ida76bf90315652b8debc081a010bc5720a5a186e |
| [Lee: Squash in subsequent changes] |
| Signed-off-by: Lee Jones <lee.jones@linaro.org> |
| --- |
| drivers/staging/android/Kconfig | 11 ++ |
| drivers/staging/android/Makefile | 1 + |
| drivers/staging/android/debug_kinfo.c | 199 ++++++++++++++++++++++++++ |
| drivers/staging/android/debug_kinfo.h | 71 +++++++++ |
| 4 files changed, 282 insertions(+) |
| create mode 100644 drivers/staging/android/debug_kinfo.c |
| create mode 100644 drivers/staging/android/debug_kinfo.h |
| |
| diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig |
| --- a/drivers/staging/android/Kconfig |
| +++ b/drivers/staging/android/Kconfig |
| @@ -14,6 +14,17 @@ config ASHMEM |
| It is, in theory, a good memory allocator for low-memory devices, |
| because it can discard shared memory units when under memory pressure. |
| |
| +config DEBUG_KINFO |
| + bool "Debug Kernel Information Support" |
| + depends on KALLSYMS |
| + help |
| + This supports kernel information backup for bootloader usage. |
| + Specifics: |
| + - The kallsyms symbols for unwind_backtrace |
| + - Page directory pointer |
| + - UTS_RELEASE |
| + - BUILD_INFO(ro.build.fingerprint) |
| + |
| endif # if ANDROID |
| |
| endmenu |
| diff --git a/drivers/staging/android/Makefile b/drivers/staging/android/Makefile |
| --- a/drivers/staging/android/Makefile |
| +++ b/drivers/staging/android/Makefile |
| @@ -2,3 +2,4 @@ |
| ccflags-y += -I$(src) # needed for trace events |
| |
| obj-$(CONFIG_ASHMEM) += ashmem.o |
| +obj-$(CONFIG_DEBUG_KINFO) += debug_kinfo.o |
| diff --git a/drivers/staging/android/debug_kinfo.c b/drivers/staging/android/debug_kinfo.c |
| new file mode 100644 |
| --- /dev/null |
| +++ b/drivers/staging/android/debug_kinfo.c |
| @@ -0,0 +1,199 @@ |
| +// SPDX-License-Identifier: GPL-2.0 |
| +/* |
| + * debug_kinfo.c - backup kernel information for bootloader usage |
| + * |
| + * Copyright 2002 Rusty Russell <rusty@rustcorp.com.au> IBM Corporation |
| + * Copyright 2021 Google LLC |
| + */ |
| + |
| +#include <linux/platform_device.h> |
| +#include <linux/kallsyms.h> |
| +#include <linux/vmalloc.h> |
| +#include <linux/module.h> |
| +#include <linux/of_address.h> |
| +#include <linux/of_reserved_mem.h> |
| +#include <linux/pgtable.h> |
| +#include <asm/module.h> |
| +#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: |
| + vunmap(all_info_addr); |
| + 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 <jonechou@google.com>"); |
| +MODULE_DESCRIPTION("Debug Kinfo Driver"); |
| +MODULE_LICENSE("GPL v2"); |
| diff --git a/drivers/staging/android/debug_kinfo.h b/drivers/staging/android/debug_kinfo.h |
| new file mode 100644 |
| --- /dev/null |
| +++ b/drivers/staging/android/debug_kinfo.h |
| @@ -0,0 +1,71 @@ |
| +// SPDX-License-Identifier: GPL-2.0 |
| +/* |
| + * debug_kinfo.h - backup kernel information for bootloader usage |
| + * |
| + * Copyright 2021 Google LLC |
| + */ |
| + |
| +#ifndef DEBUG_KINFO_H |
| +#define DEBUG_KINFO_H |
| + |
| +#include <linux/utsname.h> |
| + |
| +#define BUILD_INFO_LEN 256 |
| +#define DEBUG_KINFO_MAGIC 0xCCEEDDFF |
| + |
| +/* |
| + * Header structure must be byte-packed, since the table is provided to |
| + * bootloader. |
| + */ |
| +struct kernel_info { |
| + /* For kallsyms */ |
| + __u8 enabled_all; |
| + __u8 enabled_base_relative; |
| + __u8 enabled_absolute_percpu; |
| + __u8 enabled_cfi_clang; |
| + __u32 num_syms; |
| + __u16 name_len; |
| + __u16 bit_per_long; |
| + __u16 module_name_len; |
| + __u16 symbol_len; |
| + __u64 _addresses_pa; |
| + __u64 _relative_pa; |
| + __u64 _stext_pa; |
| + __u64 _etext_pa; |
| + __u64 _sinittext_pa; |
| + __u64 _einittext_pa; |
| + __u64 _end_pa; |
| + __u64 _offsets_pa; |
| + __u64 _names_pa; |
| + __u64 _token_table_pa; |
| + __u64 _token_index_pa; |
| + __u64 _markers_pa; |
| + |
| + /* For frame pointer */ |
| + __u32 thread_size; |
| + |
| + /* For virt_to_phys */ |
| + __u64 swapper_pg_dir_pa; |
| + |
| + /* For linux banner */ |
| + __u8 last_uts_release[__NEW_UTS_LEN]; |
| + |
| + /* Info of running build */ |
| + __u8 build_info[BUILD_INFO_LEN]; |
| + |
| + /* For module kallsyms */ |
| + __u32 enabled_modules_tree_lookup; |
| + __u32 mod_core_layout_offset; |
| + __u32 mod_init_layout_offset; |
| + __u32 mod_kallsyms_offset; |
| + __u64 module_start_va; |
| + __u64 module_end_va; |
| +} __packed; |
| + |
| +struct kernel_all_info { |
| + __u32 magic_number; |
| + __u32 combined_checksum; |
| + struct kernel_info info; |
| +} __packed; |
| + |
| +#endif // DEBUG_KINFO_H |