Path: blob/master/arch/loongarch/kernel/machine_kexec_file.c
29521 views
// SPDX-License-Identifier: GPL-2.01/*2* kexec_file for LoongArch3*4* Author: Youling Tang <[email protected]>5* Copyright (C) 2025 KylinSoft Corporation.6*7* Most code is derived from LoongArch port of kexec-tools8*/910#define pr_fmt(fmt) "kexec_file: " fmt1112#include <linux/ioport.h>13#include <linux/kernel.h>14#include <linux/kexec.h>15#include <linux/memblock.h>16#include <linux/slab.h>17#include <linux/string.h>18#include <linux/types.h>19#include <linux/vmalloc.h>20#include <asm/bootinfo.h>2122const struct kexec_file_ops * const kexec_file_loaders[] = {23&kexec_efi_ops,24&kexec_elf_ops,25NULL26};2728int arch_kimage_file_post_load_cleanup(struct kimage *image)29{30vfree(image->elf_headers);31image->elf_headers = NULL;32image->elf_headers_sz = 0;3334return kexec_image_post_load_cleanup_default(image);35}3637/* Add the "kexec_file" command line parameter to command line. */38static void cmdline_add_loader(unsigned long *cmdline_tmplen, char *modified_cmdline)39{40int loader_strlen;4142loader_strlen = sprintf(modified_cmdline + (*cmdline_tmplen), "kexec_file ");43*cmdline_tmplen += loader_strlen;44}4546/* Add the "initrd=start,size" command line parameter to command line. */47static void cmdline_add_initrd(struct kimage *image, unsigned long *cmdline_tmplen,48char *modified_cmdline, unsigned long initrd)49{50int initrd_strlen;5152initrd_strlen = sprintf(modified_cmdline + (*cmdline_tmplen), "initrd=0x%lx,0x%lx ",53initrd, image->initrd_buf_len);54*cmdline_tmplen += initrd_strlen;55}5657#ifdef CONFIG_CRASH_DUMP5859static int prepare_elf_headers(void **addr, unsigned long *sz)60{61int ret, nr_ranges;62uint64_t i;63phys_addr_t start, end;64struct crash_mem *cmem;6566nr_ranges = 2; /* for exclusion of crashkernel region */67for_each_mem_range(i, &start, &end)68nr_ranges++;6970cmem = kmalloc(struct_size(cmem, ranges, nr_ranges), GFP_KERNEL);71if (!cmem)72return -ENOMEM;7374cmem->max_nr_ranges = nr_ranges;75cmem->nr_ranges = 0;76for_each_mem_range(i, &start, &end) {77cmem->ranges[cmem->nr_ranges].start = start;78cmem->ranges[cmem->nr_ranges].end = end - 1;79cmem->nr_ranges++;80}8182/* Exclude crashkernel region */83ret = crash_exclude_mem_range(cmem, crashk_res.start, crashk_res.end);84if (ret < 0)85goto out;8687if (crashk_low_res.end) {88ret = crash_exclude_mem_range(cmem, crashk_low_res.start, crashk_low_res.end);89if (ret < 0)90goto out;91}9293ret = crash_prepare_elf64_headers(cmem, true, addr, sz);9495out:96kfree(cmem);97return ret;98}99100/*101* Add the "mem=size@start" command line parameter to command line, indicating the102* memory region the new kernel can use to boot into.103*/104static void cmdline_add_mem(unsigned long *cmdline_tmplen, char *modified_cmdline)105{106int mem_strlen = 0;107108mem_strlen = sprintf(modified_cmdline + (*cmdline_tmplen), "mem=0x%llx@0x%llx ",109crashk_res.end - crashk_res.start + 1, crashk_res.start);110*cmdline_tmplen += mem_strlen;111112if (crashk_low_res.end) {113mem_strlen = sprintf(modified_cmdline + (*cmdline_tmplen), "mem=0x%llx@0x%llx ",114crashk_low_res.end - crashk_low_res.start + 1, crashk_low_res.start);115*cmdline_tmplen += mem_strlen;116}117}118119/* Add the "elfcorehdr=size@start" command line parameter to command line. */120static void cmdline_add_elfcorehdr(struct kimage *image, unsigned long *cmdline_tmplen,121char *modified_cmdline, unsigned long elfcorehdr_sz)122{123int elfcorehdr_strlen = 0;124125elfcorehdr_strlen = sprintf(modified_cmdline + (*cmdline_tmplen), "elfcorehdr=0x%lx@0x%lx ",126elfcorehdr_sz, image->elf_load_addr);127*cmdline_tmplen += elfcorehdr_strlen;128}129130#endif131132/*133* Try to add the initrd to the image. If it is not possible to find valid134* locations, this function will undo changes to the image and return non zero.135*/136int load_other_segments(struct kimage *image,137unsigned long kernel_load_addr, unsigned long kernel_size,138char *initrd, unsigned long initrd_len, char *cmdline, unsigned long cmdline_len)139{140int ret = 0;141unsigned long cmdline_tmplen = 0;142unsigned long initrd_load_addr = 0;143unsigned long orig_segments = image->nr_segments;144char *modified_cmdline = NULL;145struct kexec_buf kbuf;146147kbuf.image = image;148/* Don't allocate anything below the kernel */149kbuf.buf_min = kernel_load_addr + kernel_size;150151modified_cmdline = kzalloc(COMMAND_LINE_SIZE, GFP_KERNEL);152if (!modified_cmdline)153return -EINVAL;154155cmdline_add_loader(&cmdline_tmplen, modified_cmdline);156/* Ensure it's null terminated */157modified_cmdline[COMMAND_LINE_SIZE - 1] = '\0';158159#ifdef CONFIG_CRASH_DUMP160/* Load elf core header */161if (image->type == KEXEC_TYPE_CRASH) {162void *headers;163unsigned long headers_sz;164165ret = prepare_elf_headers(&headers, &headers_sz);166if (ret < 0) {167pr_err("Preparing elf core header failed\n");168goto out_err;169}170171kbuf.buffer = headers;172kbuf.bufsz = headers_sz;173kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;174kbuf.memsz = headers_sz;175kbuf.buf_align = SZ_64K; /* largest supported page size */176kbuf.buf_max = ULONG_MAX;177kbuf.top_down = true;178179ret = kexec_add_buffer(&kbuf);180if (ret < 0) {181vfree(headers);182goto out_err;183}184image->elf_headers = headers;185image->elf_load_addr = kbuf.mem;186image->elf_headers_sz = headers_sz;187188kexec_dprintk("Loaded elf core header at 0x%lx bufsz=0x%lx memsz=0x%lx\n",189image->elf_load_addr, kbuf.bufsz, kbuf.memsz);190191/* Add the mem=size@start parameter to the command line */192cmdline_add_mem(&cmdline_tmplen, modified_cmdline);193194/* Add the elfcorehdr=size@start parameter to the command line */195cmdline_add_elfcorehdr(image, &cmdline_tmplen, modified_cmdline, headers_sz);196}197#endif198199/* Load initrd */200if (initrd) {201kbuf.buffer = initrd;202kbuf.bufsz = initrd_len;203kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;204kbuf.memsz = initrd_len;205kbuf.buf_align = 0;206/* within 1GB-aligned window of up to 32GB in size */207kbuf.buf_max = round_down(kernel_load_addr, SZ_1G) + (unsigned long)SZ_1G * 32;208kbuf.top_down = false;209210ret = kexec_add_buffer(&kbuf);211if (ret < 0)212goto out_err;213initrd_load_addr = kbuf.mem;214215kexec_dprintk("Loaded initrd at 0x%lx bufsz=0x%lx memsz=0x%lx\n",216initrd_load_addr, kbuf.bufsz, kbuf.memsz);217218/* Add the initrd=start,size parameter to the command line */219cmdline_add_initrd(image, &cmdline_tmplen, modified_cmdline, initrd_load_addr);220}221222if (cmdline_len + cmdline_tmplen > COMMAND_LINE_SIZE) {223pr_err("Appending command line exceeds COMMAND_LINE_SIZE\n");224ret = -EINVAL;225goto out_err;226}227228memcpy(modified_cmdline + cmdline_tmplen, cmdline, cmdline_len);229cmdline = modified_cmdline;230image->arch.cmdline_ptr = (unsigned long)cmdline;231232return 0;233234out_err:235image->nr_segments = orig_segments;236kfree(modified_cmdline);237return ret;238}239240241