| /* Copyright 2018 Google, Inc. |
| * |
| * This software is licensed under the terms of the GNU General Public |
| * License version 2, as published by the Free Software Foundation, and |
| * may be copied, distributed, and modified under those terms. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| */ |
| #include <linux/kallsyms.h> |
| #include <linux/module.h> |
| #include <linux/io.h> |
| #include <linux/of_address.h> |
| #include <linux/bldr_debug_tools.h> |
| #include <linux/utsname.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 long kallsyms_num_syms |
| __attribute__((weak, section(".rodata"))); |
| |
| extern const unsigned long kallsyms_relative_base |
| __attribute__((weak, section(".rodata"))); |
| |
| extern const u8 kallsyms_token_table[] __weak; |
| extern const u16 kallsyms_token_index[] __weak; |
| |
| extern const unsigned long kallsyms_markers[] __weak; |
| |
| /* |
| * 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; |
| phys_addr_t _addresses_va; |
| phys_addr_t _relative_va; |
| phys_addr_t _stext_va; |
| phys_addr_t _etext_va; |
| phys_addr_t _sinittext_va; |
| phys_addr_t _einittext_va; |
| phys_addr_t _end_va; |
| phys_addr_t _offsets_va; |
| phys_addr_t _names_va; |
| phys_addr_t _token_table_va; |
| phys_addr_t _token_index_va; |
| phys_addr_t _markers_va; |
| |
| /* For frame pointer */ |
| u32 thread_size; |
| |
| /* For virt_to_phys */ |
| u32 va_bits; |
| u64 page_offset; |
| u64 phys_offset; |
| u64 kimage_voffset; |
| |
| /* For linux banner */ |
| u8 last_uts_release[__NEW_UTS_LEN]; |
| |
| /* For mmu table */ |
| u64 swapper_pg_dir; |
| } __packed; |
| |
| struct kernel_all_info { |
| u32 magic_number; |
| u32 combined_checksum; |
| struct kernel_info info; |
| } __packed; |
| |
| static void backup_kernel_info(void) |
| { |
| struct device_node *np; |
| struct resource res; |
| struct kernel_all_info all_info; |
| struct kernel_info *info; |
| void __iomem *info_base; |
| u32 *checksum_info; |
| int num_reg = 0; |
| int ret, index; |
| |
| np = of_find_compatible_node(NULL, NULL, "bootloader_kinfo"); |
| if (!np) { |
| pr_warn("%s: bootloader_kinfo node does not exist\n", __func__); |
| return; |
| } |
| |
| ret = of_address_to_resource(np, num_reg, &res); |
| if(ret) { |
| pr_warn("%s: invalid argument, ret %d\n", __func__, ret); |
| return; |
| } |
| |
| if ((!res.start) || |
| (resource_size(&res) < sizeof(struct kernel_all_info))) { |
| pr_warn("%s: unexpected resource start %llx and size %llx\n", |
| __func__, res.start, resource_size(&res)); |
| return; |
| } |
| |
| info_base = ioremap(res.start, resource_size(&res)); |
| if (!info_base) { |
| pr_warn("%s: bootloader kernel info offset mapping failed\n", |
| __func__); |
| return; |
| } |
| |
| memset(&all_info, 0, sizeof(all_info)); |
| memset_io(info_base, 0, resource_size(&res)); |
| 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; |
| info->_addresses_va = (phys_addr_t)kallsyms_addresses; |
| info->_relative_va = (phys_addr_t)kallsyms_relative_base; |
| info->_stext_va = (phys_addr_t)_stext; |
| info->_etext_va = (phys_addr_t)_etext; |
| info->_sinittext_va = (phys_addr_t)_sinittext; |
| info->_einittext_va = (phys_addr_t)_einittext; |
| info->_end_va = (phys_addr_t)_end; |
| info->_offsets_va = (phys_addr_t)kallsyms_offsets; |
| info->_names_va = (phys_addr_t)kallsyms_names; |
| info->_token_table_va = (phys_addr_t)kallsyms_token_table; |
| info->_token_index_va = (phys_addr_t)kallsyms_token_index; |
| info->_markers_va = (phys_addr_t)kallsyms_markers; |
| info->thread_size = THREAD_SIZE; |
| info->va_bits = VA_BITS; |
| info->page_offset = PAGE_OFFSET; |
| info->phys_offset = PHYS_OFFSET; |
| info->kimage_voffset = kimage_voffset; |
| strlcpy(info->last_uts_release, init_utsname()->release, |
| sizeof(info->last_uts_release)); |
| info->swapper_pg_dir = (u64)swapper_pg_dir; |
| |
| checksum_info = (u32 *)info; |
| for (index = 0; index < sizeof(struct kernel_info)/sizeof(u32); |
| index++) { |
| all_info.combined_checksum ^= checksum_info[index]; |
| } |
| |
| all_info.magic_number = BOOT_DEBUG_MAGIC; |
| memcpy_toio(info_base, &all_info, sizeof(struct kernel_all_info)); |
| iounmap(info_base); |
| } |
| |
| static int __init kdebuginfo_init(void) |
| { |
| /* Backup kernel information for bootloader */ |
| backup_kernel_info(); |
| |
| return 0; |
| } |
| device_initcall(kdebuginfo_init); |