blob: ce977daa73236041e16a129dc7409591563ce762 [file] [log] [blame]
/* 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);