msm_minidump.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
  4. * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
  5. */
  6. #define pr_fmt(fmt) "Minidump: " fmt
  7. #include <linux/init.h>
  8. #include <linux/export.h>
  9. #include <linux/kernel.h>
  10. #include <linux/module.h>
  11. #include <linux/of.h>
  12. #include <linux/platform_device.h>
  13. #include <linux/err.h>
  14. #include <linux/elf.h>
  15. #include <linux/errno.h>
  16. #include <linux/string.h>
  17. #include <linux/slab.h>
  18. #include <linux/gunyah/gh_rm_drv.h>
  19. #include <linux/soc/qcom/smem.h>
  20. #include <soc/qcom/minidump.h>
  21. #include "debug_symbol.h"
  22. #include "minidump_private.h"
  23. #include "elf.h"
  24. /* minidump core structure */
  25. struct md_core {
  26. const struct md_ops *ops;
  27. };
  28. static bool md_init_done;
  29. static struct md_core md_core;
  30. DEFINE_SPINLOCK(mdt_lock);
  31. unsigned int md_num_regions;
  32. EXPORT_SYMBOL_GPL(md_num_regions);
  33. struct md_elfhdr minidump_elfheader;
  34. EXPORT_SYMBOL_GPL(minidump_elfheader);
  35. /* Number of pending entries to be added in ToC regions */
  36. static LIST_HEAD(pending_list);
  37. inline char *elf_lookup_string(struct elfhdr *hdr, int offset)
  38. {
  39. char *strtab = elf_str_table(hdr);
  40. if ((strtab == NULL) || (minidump_elfheader.strtable_idx < offset))
  41. return NULL;
  42. return strtab + offset;
  43. }
  44. EXPORT_SYMBOL_GPL(elf_lookup_string);
  45. inline unsigned int set_section_name(const char *name)
  46. {
  47. char *strtab = elf_str_table(minidump_elfheader.ehdr);
  48. int idx = minidump_elfheader.strtable_idx;
  49. int ret = 0;
  50. if ((strtab == NULL) || (name == NULL))
  51. return 0;
  52. ret = idx;
  53. idx += strscpy((strtab + idx), name, MAX_REGION_NAME_LENGTH);
  54. minidump_elfheader.strtable_idx = idx + 1;
  55. return ret;
  56. }
  57. EXPORT_SYMBOL_GPL(set_section_name);
  58. struct md_region md_get_region(char *name)
  59. {
  60. struct md_region tmp = {0};
  61. tmp = md_core.ops->get_region(name);
  62. return tmp;
  63. }
  64. /* Update elf header */
  65. void md_add_elf_header(const struct md_region *entry)
  66. {
  67. struct elfhdr *hdr = minidump_elfheader.ehdr;
  68. struct elf_shdr *shdr = elf_section(hdr, hdr->e_shnum++);
  69. struct elf_phdr *phdr = elf_program(hdr, hdr->e_phnum++);
  70. shdr->sh_type = SHT_PROGBITS;
  71. shdr->sh_name = set_section_name(entry->name);
  72. shdr->sh_addr = (elf_addr_t)entry->virt_addr;
  73. shdr->sh_size = entry->size;
  74. shdr->sh_flags = SHF_WRITE;
  75. shdr->sh_offset = minidump_elfheader.elf_offset;
  76. shdr->sh_entsize = 0;
  77. phdr->p_type = PT_LOAD;
  78. phdr->p_offset = minidump_elfheader.elf_offset;
  79. phdr->p_vaddr = entry->virt_addr;
  80. phdr->p_paddr = entry->phys_addr;
  81. phdr->p_filesz = phdr->p_memsz = entry->size;
  82. phdr->p_flags = PF_R | PF_W;
  83. minidump_elfheader.elf_offset += shdr->sh_size;
  84. }
  85. EXPORT_SYMBOL_GPL(md_add_elf_header);
  86. int msm_minidump_clear_headers(const struct md_region *entry)
  87. {
  88. struct elfhdr *hdr = minidump_elfheader.ehdr;
  89. struct elf_shdr *shdr = NULL, *tshdr = NULL;
  90. struct elf_phdr *phdr = NULL, *tphdr = NULL;
  91. int pidx, shidx, strln, i;
  92. char *shname;
  93. u64 esize;
  94. esize = entry->size;
  95. for (i = 0; i < hdr->e_phnum; i++) {
  96. phdr = elf_program(hdr, i);
  97. if ((phdr->p_paddr == entry->phys_addr) &&
  98. (phdr->p_memsz == entry->size))
  99. break;
  100. }
  101. if (i == hdr->e_phnum) {
  102. printk_deferred("Cannot find entry in elf\n");
  103. return -EINVAL;
  104. }
  105. pidx = i;
  106. for (i = 0; i < hdr->e_shnum; i++) {
  107. shdr = elf_section(hdr, i);
  108. shname = elf_lookup_string(hdr, shdr->sh_name);
  109. if (shname && !strcmp(shname, entry->name))
  110. if ((shdr->sh_addr == entry->virt_addr) &&
  111. (shdr->sh_size == entry->size))
  112. break;
  113. }
  114. if (i == hdr->e_shnum) {
  115. printk_deferred("Cannot find entry in elf\n");
  116. return -EINVAL;
  117. }
  118. shidx = i;
  119. if (shdr->sh_offset != phdr->p_offset) {
  120. printk_deferred("Invalid entry details in elf, Minidump broken..\n");
  121. return -EINVAL;
  122. }
  123. /* Clear name in string table */
  124. strln = strlen(shname) + 1;
  125. memmove(shname, shname + strln,
  126. (minidump_elfheader.strtable_idx - shdr->sh_name));
  127. minidump_elfheader.strtable_idx -= strln;
  128. /* Clear program header */
  129. tphdr = elf_program(hdr, pidx);
  130. for (i = pidx; i < hdr->e_phnum - 1; i++) {
  131. tphdr = elf_program(hdr, i + 1);
  132. phdr = elf_program(hdr, i);
  133. memcpy(phdr, tphdr, sizeof(struct elf_phdr));
  134. phdr->p_offset = phdr->p_offset - esize;
  135. }
  136. memset(tphdr, 0, sizeof(struct elf_phdr));
  137. hdr->e_phnum--;
  138. /* Clear section header */
  139. tshdr = elf_section(hdr, shidx);
  140. for (i = shidx; i < hdr->e_shnum - 1; i++) {
  141. tshdr = elf_section(hdr, i + 1);
  142. shdr = elf_section(hdr, i);
  143. memcpy(shdr, tshdr, sizeof(struct elf_shdr));
  144. shdr->sh_offset -= esize;
  145. shdr->sh_name -= strln;
  146. }
  147. memset(tshdr, 0, sizeof(struct elf_shdr));
  148. hdr->e_shnum--;
  149. minidump_elfheader.elf_offset -= esize;
  150. return 0;
  151. }
  152. EXPORT_SYMBOL_GPL(msm_minidump_clear_headers);
  153. bool msm_minidump_enabled(void)
  154. {
  155. bool ret = false;
  156. if (md_core.ops)
  157. ret = md_core.ops->md_enable();
  158. return ret;
  159. }
  160. EXPORT_SYMBOL_GPL(msm_minidump_enabled);
  161. int msm_minidump_get_available_region(void)
  162. {
  163. int res = -EBUSY;
  164. if (md_core.ops)
  165. res = md_core.ops->get_available_region();
  166. return res;
  167. }
  168. EXPORT_SYMBOL_GPL(msm_minidump_get_available_region);
  169. static inline int validate_region(const struct md_region *entry)
  170. {
  171. if (!entry)
  172. return -EINVAL;
  173. if ((strlen(entry->name) > MAX_NAME_LENGTH) || !entry->virt_addr ||
  174. (!IS_ALIGNED(entry->size, 4))) {
  175. printk_deferred("Invalid entry details\n");
  176. return -EINVAL;
  177. }
  178. return 0;
  179. }
  180. int msm_minidump_update_region(int regno, const struct md_region *entry)
  181. {
  182. int ret = 0;
  183. /* Ensure that init completes before we update regions */
  184. if (!smp_load_acquire(&md_init_done))
  185. return -EINVAL;
  186. if (validate_region(entry) || (regno >= MAX_NUM_ENTRIES))
  187. return -EINVAL;
  188. if (md_core.ops)
  189. ret = md_core.ops->update_region(regno, entry);
  190. else
  191. return -EINVAL;
  192. return ret;
  193. }
  194. EXPORT_SYMBOL_GPL(msm_minidump_update_region);
  195. int msm_minidump_add_region(const struct md_region *entry)
  196. {
  197. struct md_pending_region *pending_region;
  198. unsigned long flags;
  199. int ret;
  200. if (validate_region(entry))
  201. return -EINVAL;
  202. if (md_core.ops) {
  203. ret = md_core.ops->add_region(entry);
  204. } else {
  205. /* Local table not initialized
  206. * add to pending list, need free after initialized
  207. */
  208. pr_info("Minidump driver hasn't probe, add region to pending list\n");
  209. spin_lock_irqsave(&mdt_lock, flags);
  210. if (md_num_regions >= MAX_NUM_ENTRIES) {
  211. printk_deferred("Maximum entries reached\n");
  212. spin_unlock_irqrestore(&mdt_lock, flags);
  213. return -ENOMEM;
  214. }
  215. pending_region = kzalloc(sizeof(*pending_region), GFP_ATOMIC);
  216. if (!pending_region) {
  217. spin_unlock_irqrestore(&mdt_lock, flags);
  218. return -ENOMEM;
  219. }
  220. pending_region->entry = *entry;
  221. list_add_tail(&pending_region->list, &pending_list);
  222. ret = md_num_regions;
  223. md_num_regions++;
  224. spin_unlock_irqrestore(&mdt_lock, flags);
  225. }
  226. return ret;
  227. }
  228. EXPORT_SYMBOL_GPL(msm_minidump_add_region);
  229. int msm_minidump_remove_region(const struct md_region *entry)
  230. {
  231. int ret;
  232. if (!entry)
  233. return -EINVAL;
  234. /* Ensure that init completes before we remove regions */
  235. if (!smp_load_acquire(&md_init_done))
  236. return -EINVAL;
  237. if (md_core.ops)
  238. ret = md_core.ops->remove_region(entry);
  239. else
  240. ret = -EINVAL;
  241. if (ret)
  242. printk_deferred("Failed to remove region:%s\n", entry->name);
  243. return ret;
  244. }
  245. EXPORT_SYMBOL_GPL(msm_minidump_remove_region);
  246. static int msm_minidump_add_header(void)
  247. {
  248. struct elfhdr *ehdr;
  249. struct elf_shdr *shdr;
  250. struct elf_phdr *phdr;
  251. unsigned int strtbl_off, elfh_size, phdr_off;
  252. char *banner, *linux_banner;
  253. linux_banner = DEBUG_SYMBOL_LOOKUP(linux_banner);
  254. /* Header buffer contains:
  255. * elf header, MAX_NUM_ENTRIES+4 of section and program elf headers,
  256. * string table section and linux banner.
  257. */
  258. elfh_size = sizeof(*ehdr) + MAX_STRTBL_SIZE +
  259. (strlen(linux_banner) + 1) +
  260. ((sizeof(*shdr) + sizeof(*phdr))
  261. * (MAX_NUM_ENTRIES + 4));
  262. elfh_size = ALIGN(elfh_size, 4);
  263. minidump_elfheader.ehdr = kzalloc(elfh_size, GFP_KERNEL);
  264. if (!minidump_elfheader.ehdr)
  265. return -ENOMEM;
  266. ehdr = minidump_elfheader.ehdr;
  267. /* Assign section/program headers offset */
  268. minidump_elfheader.shdr = shdr = (struct elf_shdr *)(ehdr + 1);
  269. minidump_elfheader.phdr = phdr =
  270. (struct elf_phdr *)(shdr + MAX_NUM_ENTRIES);
  271. phdr_off = sizeof(*ehdr) + (sizeof(*shdr) * MAX_NUM_ENTRIES);
  272. memcpy(ehdr->e_ident, ELFMAG, SELFMAG);
  273. ehdr->e_ident[EI_CLASS] = ELF_CLASS;
  274. ehdr->e_ident[EI_DATA] = ELF_DATA;
  275. ehdr->e_ident[EI_VERSION] = EV_CURRENT;
  276. ehdr->e_ident[EI_OSABI] = ELF_OSABI;
  277. ehdr->e_type = ET_CORE;
  278. ehdr->e_machine = ELF_ARCH;
  279. ehdr->e_version = EV_CURRENT;
  280. ehdr->e_ehsize = sizeof(*ehdr);
  281. ehdr->e_phoff = phdr_off;
  282. ehdr->e_phentsize = sizeof(*phdr);
  283. ehdr->e_shoff = sizeof(*ehdr);
  284. ehdr->e_shentsize = sizeof(*shdr);
  285. ehdr->e_shstrndx = 1;
  286. minidump_elfheader.elf_offset = elfh_size;
  287. /*
  288. * First section header should be NULL,
  289. * 2nd section is string table.
  290. */
  291. minidump_elfheader.strtable_idx = 1;
  292. strtbl_off = sizeof(*ehdr) +
  293. ((sizeof(*phdr) + sizeof(*shdr)) * MAX_NUM_ENTRIES);
  294. shdr++;
  295. shdr->sh_type = SHT_STRTAB;
  296. shdr->sh_offset = (elf_addr_t)strtbl_off;
  297. shdr->sh_size = MAX_STRTBL_SIZE;
  298. shdr->sh_entsize = 0;
  299. shdr->sh_flags = 0;
  300. shdr->sh_name = set_section_name("STR_TBL");
  301. shdr++;
  302. /* 4th section is linux banner */
  303. banner = (char *)ehdr + strtbl_off + MAX_STRTBL_SIZE;
  304. strscpy(banner, linux_banner, MAX_STRTBL_SIZE);
  305. shdr->sh_type = SHT_PROGBITS;
  306. shdr->sh_offset = (elf_addr_t)(strtbl_off + MAX_STRTBL_SIZE);
  307. shdr->sh_size = strlen(linux_banner) + 1;
  308. shdr->sh_addr = (elf_addr_t)linux_banner;
  309. shdr->sh_entsize = 0;
  310. shdr->sh_flags = SHF_WRITE;
  311. shdr->sh_name = set_section_name("linux_banner");
  312. phdr->p_type = PT_LOAD;
  313. phdr->p_offset = (elf_addr_t)(strtbl_off + MAX_STRTBL_SIZE);
  314. phdr->p_vaddr = (elf_addr_t)linux_banner;
  315. phdr->p_paddr = virt_to_phys(linux_banner);
  316. phdr->p_filesz = phdr->p_memsz = strlen(linux_banner) + 1;
  317. phdr->p_flags = PF_R | PF_W;
  318. /* Update headers count*/
  319. ehdr->e_phnum = 1;
  320. /* KELF header and 3th section will be added in platform specific driver */
  321. if (md_core.ops)
  322. md_core.ops->add_header(shdr, ehdr, elfh_size);
  323. else
  324. return -EINVAL;
  325. return 0;
  326. }
  327. int msm_minidump_driver_probe(const struct md_init_data *data)
  328. {
  329. int ret = 0;
  330. if (!debug_symbol_available())
  331. return -EPROBE_DEFER;
  332. md_core.ops = data->ops;
  333. ret = md_core.ops->init_md_table();
  334. if (ret) {
  335. pr_err("Minidump table initialize failed\n");
  336. goto out;
  337. }
  338. /* First entry would be ELF header */
  339. ret = msm_minidump_add_header();
  340. if (ret < 0) {
  341. pr_err("failed to init minidump header\n");
  342. return ret;
  343. }
  344. ret = md_core.ops->add_pending_entry(&pending_list);
  345. if (ret) {
  346. pr_err("Add pending entry failed\n");
  347. goto out;
  348. }
  349. /* All updates above should be visible, before init completes */
  350. smp_store_release(&md_init_done, true);
  351. msm_minidump_log_init();
  352. pr_info("Enabled with max number of regions %d\n",
  353. CONFIG_MINIDUMP_MAX_ENTRIES);
  354. out:
  355. return ret;
  356. }
  357. EXPORT_SYMBOL_GPL(msm_minidump_driver_probe);
  358. MODULE_DESCRIPTION("MSM Mini Dump Driver");
  359. MODULE_IMPORT_NS(MINIDUMP);
  360. MODULE_LICENSE("GPL v2");