cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

remoteproc_coredump.c (13812B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Coredump functionality for Remoteproc framework.
      4 *
      5 * Copyright (c) 2020, The Linux Foundation. All rights reserved.
      6 */
      7
      8#include <linux/completion.h>
      9#include <linux/devcoredump.h>
     10#include <linux/device.h>
     11#include <linux/kernel.h>
     12#include <linux/remoteproc.h>
     13#include "remoteproc_internal.h"
     14#include "remoteproc_elf_helpers.h"
     15
     16struct rproc_coredump_state {
     17	struct rproc *rproc;
     18	void *header;
     19	struct completion dump_done;
     20};
     21
     22/**
     23 * rproc_coredump_cleanup() - clean up dump_segments list
     24 * @rproc: the remote processor handle
     25 */
     26void rproc_coredump_cleanup(struct rproc *rproc)
     27{
     28	struct rproc_dump_segment *entry, *tmp;
     29
     30	list_for_each_entry_safe(entry, tmp, &rproc->dump_segments, node) {
     31		list_del(&entry->node);
     32		kfree(entry);
     33	}
     34}
     35
     36/**
     37 * rproc_coredump_add_segment() - add segment of device memory to coredump
     38 * @rproc:	handle of a remote processor
     39 * @da:		device address
     40 * @size:	size of segment
     41 *
     42 * Add device memory to the list of segments to be included in a coredump for
     43 * the remoteproc.
     44 *
     45 * Return: 0 on success, negative errno on error.
     46 */
     47int rproc_coredump_add_segment(struct rproc *rproc, dma_addr_t da, size_t size)
     48{
     49	struct rproc_dump_segment *segment;
     50
     51	segment = kzalloc(sizeof(*segment), GFP_KERNEL);
     52	if (!segment)
     53		return -ENOMEM;
     54
     55	segment->da = da;
     56	segment->size = size;
     57
     58	list_add_tail(&segment->node, &rproc->dump_segments);
     59
     60	return 0;
     61}
     62EXPORT_SYMBOL(rproc_coredump_add_segment);
     63
     64/**
     65 * rproc_coredump_add_custom_segment() - add custom coredump segment
     66 * @rproc:	handle of a remote processor
     67 * @da:		device address
     68 * @size:	size of segment
     69 * @dumpfn:	custom dump function called for each segment during coredump
     70 * @priv:	private data
     71 *
     72 * Add device memory to the list of segments to be included in the coredump
     73 * and associate the segment with the given custom dump function and private
     74 * data.
     75 *
     76 * Return: 0 on success, negative errno on error.
     77 */
     78int rproc_coredump_add_custom_segment(struct rproc *rproc,
     79				      dma_addr_t da, size_t size,
     80				      void (*dumpfn)(struct rproc *rproc,
     81						     struct rproc_dump_segment *segment,
     82						     void *dest, size_t offset,
     83						     size_t size),
     84				      void *priv)
     85{
     86	struct rproc_dump_segment *segment;
     87
     88	segment = kzalloc(sizeof(*segment), GFP_KERNEL);
     89	if (!segment)
     90		return -ENOMEM;
     91
     92	segment->da = da;
     93	segment->size = size;
     94	segment->priv = priv;
     95	segment->dump = dumpfn;
     96
     97	list_add_tail(&segment->node, &rproc->dump_segments);
     98
     99	return 0;
    100}
    101EXPORT_SYMBOL(rproc_coredump_add_custom_segment);
    102
    103/**
    104 * rproc_coredump_set_elf_info() - set coredump elf information
    105 * @rproc:	handle of a remote processor
    106 * @class:	elf class for coredump elf file
    107 * @machine:	elf machine for coredump elf file
    108 *
    109 * Set elf information which will be used for coredump elf file.
    110 *
    111 * Return: 0 on success, negative errno on error.
    112 */
    113int rproc_coredump_set_elf_info(struct rproc *rproc, u8 class, u16 machine)
    114{
    115	if (class != ELFCLASS64 && class != ELFCLASS32)
    116		return -EINVAL;
    117
    118	rproc->elf_class = class;
    119	rproc->elf_machine = machine;
    120
    121	return 0;
    122}
    123EXPORT_SYMBOL(rproc_coredump_set_elf_info);
    124
    125static void rproc_coredump_free(void *data)
    126{
    127	struct rproc_coredump_state *dump_state = data;
    128
    129	vfree(dump_state->header);
    130	complete(&dump_state->dump_done);
    131}
    132
    133static void *rproc_coredump_find_segment(loff_t user_offset,
    134					 struct list_head *segments,
    135					 size_t *data_left)
    136{
    137	struct rproc_dump_segment *segment;
    138
    139	list_for_each_entry(segment, segments, node) {
    140		if (user_offset < segment->size) {
    141			*data_left = segment->size - user_offset;
    142			return segment;
    143		}
    144		user_offset -= segment->size;
    145	}
    146
    147	*data_left = 0;
    148	return NULL;
    149}
    150
    151static void rproc_copy_segment(struct rproc *rproc, void *dest,
    152			       struct rproc_dump_segment *segment,
    153			       size_t offset, size_t size)
    154{
    155	bool is_iomem = false;
    156	void *ptr;
    157
    158	if (segment->dump) {
    159		segment->dump(rproc, segment, dest, offset, size);
    160	} else {
    161		ptr = rproc_da_to_va(rproc, segment->da + offset, size, &is_iomem);
    162		if (!ptr) {
    163			dev_err(&rproc->dev,
    164				"invalid copy request for segment %pad with offset %zu and size %zu)\n",
    165				&segment->da, offset, size);
    166			memset(dest, 0xff, size);
    167		} else {
    168			if (is_iomem)
    169				memcpy_fromio(dest, (void const __iomem *)ptr, size);
    170			else
    171				memcpy(dest, ptr, size);
    172		}
    173	}
    174}
    175
    176static ssize_t rproc_coredump_read(char *buffer, loff_t offset, size_t count,
    177				   void *data, size_t header_sz)
    178{
    179	size_t seg_data, bytes_left = count;
    180	ssize_t copy_sz;
    181	struct rproc_dump_segment *seg;
    182	struct rproc_coredump_state *dump_state = data;
    183	struct rproc *rproc = dump_state->rproc;
    184	void *elfcore = dump_state->header;
    185
    186	/* Copy the vmalloc'ed header first. */
    187	if (offset < header_sz) {
    188		copy_sz = memory_read_from_buffer(buffer, count, &offset,
    189						  elfcore, header_sz);
    190
    191		return copy_sz;
    192	}
    193
    194	/*
    195	 * Find out the segment memory chunk to be copied based on offset.
    196	 * Keep copying data until count bytes are read.
    197	 */
    198	while (bytes_left) {
    199		seg = rproc_coredump_find_segment(offset - header_sz,
    200						  &rproc->dump_segments,
    201						  &seg_data);
    202		/* EOF check */
    203		if (!seg) {
    204			dev_info(&rproc->dev, "Ramdump done, %lld bytes read",
    205				 offset);
    206			break;
    207		}
    208
    209		copy_sz = min_t(size_t, bytes_left, seg_data);
    210
    211		rproc_copy_segment(rproc, buffer, seg, seg->size - seg_data,
    212				   copy_sz);
    213
    214		offset += copy_sz;
    215		buffer += copy_sz;
    216		bytes_left -= copy_sz;
    217	}
    218
    219	return count - bytes_left;
    220}
    221
    222/**
    223 * rproc_coredump() - perform coredump
    224 * @rproc:	rproc handle
    225 *
    226 * This function will generate an ELF header for the registered segments
    227 * and create a devcoredump device associated with rproc. Based on the
    228 * coredump configuration this function will directly copy the segments
    229 * from device memory to userspace or copy segments from device memory to
    230 * a separate buffer, which can then be read by userspace.
    231 * The first approach avoids using extra vmalloc memory. But it will stall
    232 * recovery flow until dump is read by userspace.
    233 */
    234void rproc_coredump(struct rproc *rproc)
    235{
    236	struct rproc_dump_segment *segment;
    237	void *phdr;
    238	void *ehdr;
    239	size_t data_size;
    240	size_t offset;
    241	void *data;
    242	u8 class = rproc->elf_class;
    243	int phnum = 0;
    244	struct rproc_coredump_state dump_state;
    245	enum rproc_dump_mechanism dump_conf = rproc->dump_conf;
    246
    247	if (list_empty(&rproc->dump_segments) ||
    248	    dump_conf == RPROC_COREDUMP_DISABLED)
    249		return;
    250
    251	if (class == ELFCLASSNONE) {
    252		dev_err(&rproc->dev, "Elf class is not set\n");
    253		return;
    254	}
    255
    256	data_size = elf_size_of_hdr(class);
    257	list_for_each_entry(segment, &rproc->dump_segments, node) {
    258		/*
    259		 * For default configuration buffer includes headers & segments.
    260		 * For inline dump buffer just includes headers as segments are
    261		 * directly read from device memory.
    262		 */
    263		data_size += elf_size_of_phdr(class);
    264		if (dump_conf == RPROC_COREDUMP_ENABLED)
    265			data_size += segment->size;
    266
    267		phnum++;
    268	}
    269
    270	data = vmalloc(data_size);
    271	if (!data)
    272		return;
    273
    274	ehdr = data;
    275
    276	memset(ehdr, 0, elf_size_of_hdr(class));
    277	/* e_ident field is common for both elf32 and elf64 */
    278	elf_hdr_init_ident(ehdr, class);
    279
    280	elf_hdr_set_e_type(class, ehdr, ET_CORE);
    281	elf_hdr_set_e_machine(class, ehdr, rproc->elf_machine);
    282	elf_hdr_set_e_version(class, ehdr, EV_CURRENT);
    283	elf_hdr_set_e_entry(class, ehdr, rproc->bootaddr);
    284	elf_hdr_set_e_phoff(class, ehdr, elf_size_of_hdr(class));
    285	elf_hdr_set_e_ehsize(class, ehdr, elf_size_of_hdr(class));
    286	elf_hdr_set_e_phentsize(class, ehdr, elf_size_of_phdr(class));
    287	elf_hdr_set_e_phnum(class, ehdr, phnum);
    288
    289	phdr = data + elf_hdr_get_e_phoff(class, ehdr);
    290	offset = elf_hdr_get_e_phoff(class, ehdr);
    291	offset += elf_size_of_phdr(class) * elf_hdr_get_e_phnum(class, ehdr);
    292
    293	list_for_each_entry(segment, &rproc->dump_segments, node) {
    294		memset(phdr, 0, elf_size_of_phdr(class));
    295		elf_phdr_set_p_type(class, phdr, PT_LOAD);
    296		elf_phdr_set_p_offset(class, phdr, offset);
    297		elf_phdr_set_p_vaddr(class, phdr, segment->da);
    298		elf_phdr_set_p_paddr(class, phdr, segment->da);
    299		elf_phdr_set_p_filesz(class, phdr, segment->size);
    300		elf_phdr_set_p_memsz(class, phdr, segment->size);
    301		elf_phdr_set_p_flags(class, phdr, PF_R | PF_W | PF_X);
    302		elf_phdr_set_p_align(class, phdr, 0);
    303
    304		if (dump_conf == RPROC_COREDUMP_ENABLED)
    305			rproc_copy_segment(rproc, data + offset, segment, 0,
    306					   segment->size);
    307
    308		offset += elf_phdr_get_p_filesz(class, phdr);
    309		phdr += elf_size_of_phdr(class);
    310	}
    311	if (dump_conf == RPROC_COREDUMP_ENABLED) {
    312		dev_coredumpv(&rproc->dev, data, data_size, GFP_KERNEL);
    313		return;
    314	}
    315
    316	/* Initialize the dump state struct to be used by rproc_coredump_read */
    317	dump_state.rproc = rproc;
    318	dump_state.header = data;
    319	init_completion(&dump_state.dump_done);
    320
    321	dev_coredumpm(&rproc->dev, NULL, &dump_state, data_size, GFP_KERNEL,
    322		      rproc_coredump_read, rproc_coredump_free);
    323
    324	/*
    325	 * Wait until the dump is read and free is called. Data is freed
    326	 * by devcoredump framework automatically after 5 minutes.
    327	 */
    328	wait_for_completion(&dump_state.dump_done);
    329}
    330
    331/**
    332 * rproc_coredump_using_sections() - perform coredump using section headers
    333 * @rproc:	rproc handle
    334 *
    335 * This function will generate an ELF header for the registered sections of
    336 * segments and create a devcoredump device associated with rproc. Based on
    337 * the coredump configuration this function will directly copy the segments
    338 * from device memory to userspace or copy segments from device memory to
    339 * a separate buffer, which can then be read by userspace.
    340 * The first approach avoids using extra vmalloc memory. But it will stall
    341 * recovery flow until dump is read by userspace.
    342 */
    343void rproc_coredump_using_sections(struct rproc *rproc)
    344{
    345	struct rproc_dump_segment *segment;
    346	void *shdr;
    347	void *ehdr;
    348	size_t data_size;
    349	size_t strtbl_size = 0;
    350	size_t strtbl_index = 1;
    351	size_t offset;
    352	void *data;
    353	u8 class = rproc->elf_class;
    354	int shnum;
    355	struct rproc_coredump_state dump_state;
    356	unsigned int dump_conf = rproc->dump_conf;
    357	char *str_tbl = "STR_TBL";
    358
    359	if (list_empty(&rproc->dump_segments) ||
    360	    dump_conf == RPROC_COREDUMP_DISABLED)
    361		return;
    362
    363	if (class == ELFCLASSNONE) {
    364		dev_err(&rproc->dev, "Elf class is not set\n");
    365		return;
    366	}
    367
    368	/*
    369	 * We allocate two extra section headers. The first one is null.
    370	 * Second section header is for the string table. Also space is
    371	 * allocated for string table.
    372	 */
    373	data_size = elf_size_of_hdr(class) + 2 * elf_size_of_shdr(class);
    374	shnum = 2;
    375
    376	/* the extra byte is for the null character at index 0 */
    377	strtbl_size += strlen(str_tbl) + 2;
    378
    379	list_for_each_entry(segment, &rproc->dump_segments, node) {
    380		data_size += elf_size_of_shdr(class);
    381		strtbl_size += strlen(segment->priv) + 1;
    382		if (dump_conf == RPROC_COREDUMP_ENABLED)
    383			data_size += segment->size;
    384		shnum++;
    385	}
    386
    387	data_size += strtbl_size;
    388
    389	data = vmalloc(data_size);
    390	if (!data)
    391		return;
    392
    393	ehdr = data;
    394	memset(ehdr, 0, elf_size_of_hdr(class));
    395	/* e_ident field is common for both elf32 and elf64 */
    396	elf_hdr_init_ident(ehdr, class);
    397
    398	elf_hdr_set_e_type(class, ehdr, ET_CORE);
    399	elf_hdr_set_e_machine(class, ehdr, rproc->elf_machine);
    400	elf_hdr_set_e_version(class, ehdr, EV_CURRENT);
    401	elf_hdr_set_e_entry(class, ehdr, rproc->bootaddr);
    402	elf_hdr_set_e_shoff(class, ehdr, elf_size_of_hdr(class));
    403	elf_hdr_set_e_ehsize(class, ehdr, elf_size_of_hdr(class));
    404	elf_hdr_set_e_shentsize(class, ehdr, elf_size_of_shdr(class));
    405	elf_hdr_set_e_shnum(class, ehdr, shnum);
    406	elf_hdr_set_e_shstrndx(class, ehdr, 1);
    407
    408	/*
    409	 * The zeroth index of the section header is reserved and is rarely used.
    410	 * Set the section header as null (SHN_UNDEF) and move to the next one.
    411	 */
    412	shdr = data + elf_hdr_get_e_shoff(class, ehdr);
    413	memset(shdr, 0, elf_size_of_shdr(class));
    414	shdr += elf_size_of_shdr(class);
    415
    416	/* Initialize the string table. */
    417	offset = elf_hdr_get_e_shoff(class, ehdr) +
    418		 elf_size_of_shdr(class) * elf_hdr_get_e_shnum(class, ehdr);
    419	memset(data + offset, 0, strtbl_size);
    420
    421	/* Fill in the string table section header. */
    422	memset(shdr, 0, elf_size_of_shdr(class));
    423	elf_shdr_set_sh_type(class, shdr, SHT_STRTAB);
    424	elf_shdr_set_sh_offset(class, shdr, offset);
    425	elf_shdr_set_sh_size(class, shdr, strtbl_size);
    426	elf_shdr_set_sh_entsize(class, shdr, 0);
    427	elf_shdr_set_sh_flags(class, shdr, 0);
    428	elf_shdr_set_sh_name(class, shdr, elf_strtbl_add(str_tbl, ehdr, class, &strtbl_index));
    429	offset += elf_shdr_get_sh_size(class, shdr);
    430	shdr += elf_size_of_shdr(class);
    431
    432	list_for_each_entry(segment, &rproc->dump_segments, node) {
    433		memset(shdr, 0, elf_size_of_shdr(class));
    434		elf_shdr_set_sh_type(class, shdr, SHT_PROGBITS);
    435		elf_shdr_set_sh_offset(class, shdr, offset);
    436		elf_shdr_set_sh_addr(class, shdr, segment->da);
    437		elf_shdr_set_sh_size(class, shdr, segment->size);
    438		elf_shdr_set_sh_entsize(class, shdr, 0);
    439		elf_shdr_set_sh_flags(class, shdr, SHF_WRITE);
    440		elf_shdr_set_sh_name(class, shdr,
    441				     elf_strtbl_add(segment->priv, ehdr, class, &strtbl_index));
    442
    443		/* No need to copy segments for inline dumps */
    444		if (dump_conf == RPROC_COREDUMP_ENABLED)
    445			rproc_copy_segment(rproc, data + offset, segment, 0,
    446					   segment->size);
    447		offset += elf_shdr_get_sh_size(class, shdr);
    448		shdr += elf_size_of_shdr(class);
    449	}
    450
    451	if (dump_conf == RPROC_COREDUMP_ENABLED) {
    452		dev_coredumpv(&rproc->dev, data, data_size, GFP_KERNEL);
    453		return;
    454	}
    455
    456	/* Initialize the dump state struct to be used by rproc_coredump_read */
    457	dump_state.rproc = rproc;
    458	dump_state.header = data;
    459	init_completion(&dump_state.dump_done);
    460
    461	dev_coredumpm(&rproc->dev, NULL, &dump_state, data_size, GFP_KERNEL,
    462		      rproc_coredump_read, rproc_coredump_free);
    463
    464	/* Wait until the dump is read and free is called. Data is freed
    465	 * by devcoredump framework automatically after 5 minutes.
    466	 */
    467	wait_for_completion(&dump_state.dump_done);
    468}
    469EXPORT_SYMBOL(rproc_coredump_using_sections);