M .github/workflows/build.yml => .github/workflows/build.yml +3 -1
@@ 61,7 61,9 @@ jobs:
- name: Install Dependencies
if: env.EXTRA_PKGS != ''
- run: sudo apt-get -q=2 install ${{ env.EXTRA_PKGS }}
+ run: |
+ sudo apt-get update
+ sudo apt-get -q=2 install ${{ env.EXTRA_PKGS }}
- name: Build
run: |
M configure.ac => configure.ac +1 -1
@@ 4,7 4,7 @@ dnl
dnl
dnl ---Required
-AC_INIT(kexec-tools, 2.0.23)
+AC_INIT(kexec-tools, 2.0.24.git)
AC_CONFIG_AUX_DIR(./config)
AC_CONFIG_HEADERS([include/config.h])
AC_LANG(C)
M kexec/arch/arm64/crashdump-arm64.c => kexec/arch/arm64/crashdump-arm64.c +38 -43
@@ 27,11 27,11 @@
static struct memory_ranges system_memory_rgns;
/* memory range reserved for crashkernel */
-struct memory_range crash_reserved_mem;
+struct memory_range crash_reserved_mem[CRASH_MAX_RESERVED_RANGES];
struct memory_ranges usablemem_rgns = {
.size = 0,
- .max_size = 1,
- .ranges = &crash_reserved_mem,
+ .max_size = CRASH_MAX_RESERVED_RANGES,
+ .ranges = crash_reserved_mem,
};
struct memory_range elfcorehdr_mem;
@@ 47,27 47,6 @@ static struct crash_elf_info elf_info = {
};
/*
- * Note: The returned value is correct only if !CONFIG_RANDOMIZE_BASE.
- */
-static uint64_t get_kernel_page_offset(void)
-{
- int i;
-
- if (elf_info.kern_vaddr_start == UINT64_MAX)
- return UINT64_MAX;
-
- /* Current max virtual memory range is 48-bits. */
- for (i = 48; i > 0; i--)
- if (!(elf_info.kern_vaddr_start & (1UL << i)))
- break;
-
- if (i <= 0)
- return UINT64_MAX;
- else
- return UINT64_MAX << i;
-}
-
-/*
* iomem_range_callback() - callback called for each iomem region
* @data: not used
* @nr: not used
@@ 91,14 70,22 @@ static int iomem_range_callback(void *UNUSED(data), int UNUSED(nr),
return mem_regions_alloc_and_add(&system_memory_rgns,
base, length, RANGE_RAM);
else if (strncmp(str, KERNEL_CODE, strlen(KERNEL_CODE)) == 0) {
+
+ unsigned long long kva_text = get_kernel_sym("_text");
+ unsigned long long kva_stext = get_kernel_sym("_stext");
+ unsigned long long kva_text_end = get_kernel_sym("__init_begin");
+
/*
* old: kernel_code.start = __pa_symbol(_text);
* new: kernel_code.start = __pa_symbol(_stext);
*
- * By utilizing the fact that paddr(_text) should align on 2MB, plus
- * _stext - _text <= 64K.
+ * For compatibility, deduce by comparing the gap "__init_begin - _stext"
+ * and the res size of "Kernel code" in /proc/iomem
*/
- elf_info.kern_paddr_start = base & ((0xffffffffffffffffUL) << 21);
+ if (kva_text_end - kva_stext == length)
+ elf_info.kern_paddr_start = base - (kva_stext - kva_text);
+ else
+ elf_info.kern_paddr_start = base;
}
else if (strncmp(str, KERNEL_DATA, strlen(KERNEL_DATA)) == 0)
elf_info.kern_size = base + length - elf_info.kern_paddr_start;
@@ 111,7 98,7 @@ int is_crashkernel_mem_reserved(void)
if (!usablemem_rgns.size)
kexec_iomem_for_each_line(NULL, iomem_range_callback, NULL);
- return crash_reserved_mem.start != crash_reserved_mem.end;
+ return usablemem_rgns.size;
}
/*
@@ 125,6 112,8 @@ int is_crashkernel_mem_reserved(void)
*/
static int crash_get_memory_ranges(void)
{
+ int i;
+
/*
* First read all memory regions that can be considered as
* system memory including the crash area.
@@ 132,16 121,19 @@ static int crash_get_memory_ranges(void)
if (!usablemem_rgns.size)
kexec_iomem_for_each_line(NULL, iomem_range_callback, NULL);
- /* allow only a single region for crash dump kernel */
- if (usablemem_rgns.size != 1)
+ /* allow one or two regions for crash dump kernel */
+ if (!usablemem_rgns.size)
return -EINVAL;
- dbgprint_mem_range("Reserved memory range", &crash_reserved_mem, 1);
+ dbgprint_mem_range("Reserved memory range",
+ usablemem_rgns.ranges, usablemem_rgns.size);
- if (mem_regions_alloc_and_exclude(&system_memory_rgns,
- &crash_reserved_mem)) {
- fprintf(stderr, "Cannot allocate memory for ranges\n");
- return -ENOMEM;
+ for (i = 0; i < usablemem_rgns.size; i++) {
+ if (mem_regions_alloc_and_exclude(&system_memory_rgns,
+ &crash_reserved_mem[i])) {
+ fprintf(stderr, "Cannot allocate memory for ranges\n");
+ return -ENOMEM;
+ }
}
/*
@@ 190,7 182,7 @@ int load_crashdump_segments(struct kexec_info *info)
if (err)
return EFAILED;
- elf_info.page_offset = get_kernel_page_offset();
+ get_page_offset((unsigned long *)&elf_info.page_offset);
dbgprintf("%s: page_offset: %016llx\n", __func__,
elf_info.page_offset);
@@ 202,7 194,8 @@ int load_crashdump_segments(struct kexec_info *info)
return EFAILED;
elfcorehdr = add_buffer_phys_virt(info, buf, bufsz, bufsz, 0,
- crash_reserved_mem.start, crash_reserved_mem.end,
+ crash_reserved_mem[usablemem_rgns.size - 1].start,
+ crash_reserved_mem[usablemem_rgns.size - 1].end,
-1, 0);
elfcorehdr_mem.start = elfcorehdr;
@@ 220,21 213,23 @@ int load_crashdump_segments(struct kexec_info *info)
* virt_to_phys() in add_segment().
* So let's fix up those values for later use so the memory base
* (arm64_mm.phys_offset) will be correctly replaced with
- * crash_reserved_mem.start.
+ * crash_reserved_mem[usablemem_rgns.size - 1].start.
*/
void fixup_elf_addrs(struct mem_ehdr *ehdr)
{
struct mem_phdr *phdr;
int i;
- ehdr->e_entry += - arm64_mem.phys_offset + crash_reserved_mem.start;
+ ehdr->e_entry += -arm64_mem.phys_offset +
+ crash_reserved_mem[usablemem_rgns.size - 1].start;
for (i = 0; i < ehdr->e_phnum; i++) {
phdr = &ehdr->e_phdr[i];
if (phdr->p_type != PT_LOAD)
continue;
phdr->p_paddr +=
- (-arm64_mem.phys_offset + crash_reserved_mem.start);
+ (-arm64_mem.phys_offset +
+ crash_reserved_mem[usablemem_rgns.size - 1].start);
}
}
@@ 243,11 238,11 @@ int get_crash_kernel_load_range(uint64_t *start, uint64_t *end)
if (!usablemem_rgns.size)
kexec_iomem_for_each_line(NULL, iomem_range_callback, NULL);
- if (!crash_reserved_mem.end)
+ if (!usablemem_rgns.size)
return -1;
- *start = crash_reserved_mem.start;
- *end = crash_reserved_mem.end;
+ *start = crash_reserved_mem[usablemem_rgns.size - 1].start;
+ *end = crash_reserved_mem[usablemem_rgns.size - 1].end;
return 0;
}
M kexec/arch/arm64/crashdump-arm64.h => kexec/arch/arm64/crashdump-arm64.h +5 -2
@@ 14,10 14,13 @@
#include "kexec.h"
-#define CRASH_MAX_MEMORY_RANGES 32
+#define CRASH_MAX_MEMORY_RANGES 32768
+
+/* crash dump kernel support at most two regions, low_region and high region. */
+#define CRASH_MAX_RESERVED_RANGES 2
extern struct memory_ranges usablemem_rgns;
-extern struct memory_range crash_reserved_mem;
+extern struct memory_range crash_reserved_mem[];
extern struct memory_range elfcorehdr_mem;
extern int load_crashdump_segments(struct kexec_info *info);
M kexec/arch/arm64/kexec-arm64.c => kexec/arch/arm64/kexec-arm64.c +176 -81
@@ 54,7 54,7 @@
static bool try_read_phys_offset_from_kcore = false;
/* Machine specific details. */
-static int va_bits;
+static int va_bits = -1;
static unsigned long page_offset;
/* Global varables the core kexec routines expect. */
@@ 456,22 456,32 @@ static void fill_property(void *buf, uint64_t val, uint32_t cells)
}
}
-static int fdt_setprop_range(void *fdt, int nodeoffset,
- const char *name, struct memory_range *range,
+static int fdt_setprop_ranges(void *fdt, int nodeoffset, const char *name,
+ struct memory_range *ranges, int nr_ranges, bool reverse,
uint32_t address_cells, uint32_t size_cells)
{
void *buf, *prop;
size_t buf_size;
- int result;
+ int i, result;
+ struct memory_range *range;
- buf_size = (address_cells + size_cells) * sizeof(uint32_t);
+ buf_size = (address_cells + size_cells) * sizeof(uint32_t) * nr_ranges;
prop = buf = xmalloc(buf_size);
+ if (!buf)
+ return -ENOMEM;
- fill_property(prop, range->start, address_cells);
- prop += address_cells * sizeof(uint32_t);
+ for (i = 0; i < nr_ranges; i++) {
+ if (reverse)
+ range = ranges + (nr_ranges - 1 - i);
+ else
+ range = ranges + i;
- fill_property(prop, range->end - range->start + 1, size_cells);
- prop += size_cells * sizeof(uint32_t);
+ fill_property(prop, range->start, address_cells);
+ prop += address_cells * sizeof(uint32_t);
+
+ fill_property(prop, range->end - range->start + 1, size_cells);
+ prop += size_cells * sizeof(uint32_t);
+ }
result = fdt_setprop(fdt, nodeoffset, name, buf, buf_size);
@@ 493,7 503,7 @@ static int setup_2nd_dtb(struct dtb *dtb, char *command_line, int on_crash)
int len, range_len;
int nodeoffset;
int new_size;
- int result, kaslr_seed;
+ int i, result, kaslr_seed;
result = fdt_check_header(dtb->buf);
@@ 524,18 534,20 @@ static int setup_2nd_dtb(struct dtb *dtb, char *command_line, int on_crash)
goto on_error;
}
- if (!cells_size_fitted(address_cells, size_cells,
- &crash_reserved_mem)) {
- fprintf(stderr, "kexec: usable memory range doesn't fit cells-size.\n");
- result = -EINVAL;
- goto on_error;
+ for (i = 0; i < usablemem_rgns.size; i++) {
+ if (!cells_size_fitted(address_cells, size_cells,
+ &crash_reserved_mem[i])) {
+ fprintf(stderr, "kexec: usable memory range doesn't fit cells-size.\n");
+ result = -EINVAL;
+ goto on_error;
+ }
}
/* duplicate dt blob */
range_len = sizeof(uint32_t) * (address_cells + size_cells);
new_size = fdt_totalsize(dtb->buf)
+ fdt_prop_len(PROP_ELFCOREHDR, range_len)
- + fdt_prop_len(PROP_USABLE_MEM_RANGE, range_len);
+ + fdt_prop_len(PROP_USABLE_MEM_RANGE, range_len * usablemem_rgns.size);
new_buf = xmalloc(new_size);
result = fdt_open_into(dtb->buf, new_buf, new_size);
@@ 619,8 631,8 @@ static int setup_2nd_dtb(struct dtb *dtb, char *command_line, int on_crash)
if (on_crash) {
/* add linux,elfcorehdr */
nodeoffset = fdt_path_offset(new_buf, "/chosen");
- result = fdt_setprop_range(new_buf, nodeoffset,
- PROP_ELFCOREHDR, &elfcorehdr_mem,
+ result = fdt_setprop_ranges(new_buf, nodeoffset,
+ PROP_ELFCOREHDR, &elfcorehdr_mem, 1, false,
address_cells, size_cells);
if (result) {
dbgprintf("%s: fdt_setprop failed: %s\n", __func__,
@@ 629,10 641,17 @@ static int setup_2nd_dtb(struct dtb *dtb, char *command_line, int on_crash)
goto on_error;
}
- /* add linux,usable-memory-range */
+ /*
+ * add linux,usable-memory-range
+ *
+ * crash dump kernel support one or two regions, to make
+ * compatibility with existing user-space and older kdump, the
+ * low region is always the last one.
+ */
nodeoffset = fdt_path_offset(new_buf, "/chosen");
- result = fdt_setprop_range(new_buf, nodeoffset,
- PROP_USABLE_MEM_RANGE, &crash_reserved_mem,
+ result = fdt_setprop_ranges(new_buf, nodeoffset,
+ PROP_USABLE_MEM_RANGE,
+ usablemem_rgns.ranges, usablemem_rgns.size, true,
address_cells, size_cells);
if (result) {
dbgprintf("%s: fdt_setprop failed: %s\n", __func__,
@@ 665,13 684,13 @@ unsigned long arm64_locate_kernel_segment(struct kexec_info *info)
if (info->kexec_flags & KEXEC_ON_CRASH) {
unsigned long hole_end;
- hole = (crash_reserved_mem.start < mem_min ?
- mem_min : crash_reserved_mem.start);
+ hole = (crash_reserved_mem[usablemem_rgns.size - 1].start < mem_min ?
+ mem_min : crash_reserved_mem[usablemem_rgns.size - 1].start);
hole = _ALIGN_UP(hole, MiB(2));
hole_end = hole + arm64_mem.text_offset + arm64_mem.image_size;
if ((hole_end > mem_max) ||
- (hole_end > crash_reserved_mem.end)) {
+ (hole_end > crash_reserved_mem[usablemem_rgns.size - 1].end)) {
dbgprintf("%s: Crash kernel out of range\n", __func__);
hole = ULONG_MAX;
}
@@ 745,7 764,7 @@ int arm64_load_other_segments(struct kexec_info *info,
hole_min = image_base + arm64_mem.image_size;
if (info->kexec_flags & KEXEC_ON_CRASH)
- hole_max = crash_reserved_mem.end;
+ hole_max = crash_reserved_mem[usablemem_rgns.size - 1].end;
else
hole_max = ULONG_MAX;
@@ 859,7 878,7 @@ void add_segment(struct kexec_info *info, const void *buf, size_t bufsz,
add_segment_phys_virt(info, buf, bufsz, base, memsz, 1);
}
-static inline void set_phys_offset(uint64_t v, char *set_method)
+static inline void set_phys_offset(int64_t v, char *set_method)
{
if (arm64_mem.phys_offset == arm64_mem_ngv
|| v < arm64_mem.phys_offset) {
@@ 876,7 895,18 @@ static inline void set_phys_offset(uint64_t v, char *set_method)
static int get_va_bits(void)
{
- unsigned long long stext_sym_addr = get_kernel_sym("_stext");
+ unsigned long long stext_sym_addr;
+
+ /*
+ * if already got from kcore
+ */
+ if (va_bits != -1)
+ goto out;
+
+
+ /* For kernel older than v4.19 */
+ fprintf(stderr, "Warning, can't get the VA_BITS from kcore\n");
+ stext_sym_addr = get_kernel_sym("_stext");
if (stext_sym_addr == 0) {
fprintf(stderr, "Can't get the symbol of _stext.\n");
@@ 900,6 930,7 @@ static int get_va_bits(void)
return -1;
}
+out:
dbgprintf("va_bits : %d\n", va_bits);
return 0;
@@ 909,26 940,51 @@ static int get_va_bits(void)
* get_page_offset - Helper for getting PAGE_OFFSET
*/
-static int get_page_offset(void)
+int get_page_offset(unsigned long *page_offset)
{
+ unsigned long long text_sym_addr, kernel_va_mid;
int ret;
+ text_sym_addr = get_kernel_sym("_text");
+ if (text_sym_addr == 0) {
+ fprintf(stderr, "Can't get the symbol of _text to calculate page_offset.\n");
+ return -1;
+ }
+
ret = get_va_bits();
if (ret < 0)
return ret;
- page_offset = (0xffffffffffffffffUL) << (va_bits - 1);
- dbgprintf("page_offset : %lx\n", page_offset);
+ /* Since kernel 5.4, kernel image is put above
+ * UINT64_MAX << (va_bits - 1)
+ */
+ kernel_va_mid = UINT64_MAX << (va_bits - 1);
+ /* older kernel */
+ if (text_sym_addr < kernel_va_mid)
+ *page_offset = UINT64_MAX << (va_bits - 1);
+ else
+ *page_offset = UINT64_MAX << va_bits;
+
+ dbgprintf("page_offset : %lx\n", *page_offset);
return 0;
}
+static void arm64_scan_vmcoreinfo(char *pos)
+{
+ const char *str;
+
+ str = "NUMBER(VA_BITS)=";
+ if (memcmp(str, pos, strlen(str)) == 0)
+ va_bits = strtoul(pos + strlen(str), NULL, 10);
+}
+
/**
- * get_phys_offset_from_vmcoreinfo_pt_note - Helper for getting PHYS_OFFSET
+ * get_phys_offset_from_vmcoreinfo_pt_note - Helper for getting PHYS_OFFSET (and va_bits)
* from VMCOREINFO note inside 'kcore'.
*/
-static int get_phys_offset_from_vmcoreinfo_pt_note(unsigned long *phys_offset)
+static int get_phys_offset_from_vmcoreinfo_pt_note(long *phys_offset)
{
int fd, ret = 0;
@@ 937,6 993,7 @@ static int get_phys_offset_from_vmcoreinfo_pt_note(unsigned long *phys_offset)
return EFAILED;
}
+ arch_scan_vmcoreinfo = arm64_scan_vmcoreinfo;
ret = read_phys_offset_elf_kcore(fd, phys_offset);
close(fd);
@@ 948,13 1005,13 @@ static int get_phys_offset_from_vmcoreinfo_pt_note(unsigned long *phys_offset)
* from PT_LOADs inside 'kcore'.
*/
-int get_phys_base_from_pt_load(unsigned long *phys_offset)
+int get_phys_base_from_pt_load(long *phys_offset)
{
int i, fd, ret;
unsigned long long phys_start;
unsigned long long virt_start;
- ret = get_page_offset();
+ ret = get_page_offset(&page_offset);
if (ret < 0)
return ret;
@@ 979,12 1036,21 @@ int get_phys_base_from_pt_load(unsigned long *phys_offset)
return 0;
}
-static bool to_be_excluded(char *str)
+static bool to_be_excluded(char *str, unsigned long long start, unsigned long long end)
{
+ if (!strncmp(str, CRASH_KERNEL, strlen(CRASH_KERNEL))) {
+ uint64_t load_start, load_end;
+
+ if (!get_crash_kernel_load_range(&load_start, &load_end) &&
+ (load_start == start) && (load_end == end))
+ return false;
+
+ return true;
+ }
+
if (!strncmp(str, SYSTEM_RAM, strlen(SYSTEM_RAM)) ||
!strncmp(str, KERNEL_CODE, strlen(KERNEL_CODE)) ||
- !strncmp(str, KERNEL_DATA, strlen(KERNEL_DATA)) ||
- !strncmp(str, CRASH_KERNEL, strlen(CRASH_KERNEL)))
+ !strncmp(str, KERNEL_DATA, strlen(KERNEL_DATA)))
return false;
else
return true;
@@ 997,7 1063,7 @@ static bool to_be_excluded(char *str)
int get_memory_ranges(struct memory_range **range, int *ranges,
unsigned long kexec_flags)
{
- unsigned long phys_offset = UINT64_MAX;
+ long phys_offset = -1;
FILE *fp;
const char *iomem = proc_iomem();
char line[MAX_LINE], *str;
@@ 1019,7 1085,7 @@ int get_memory_ranges(struct memory_range **range, int *ranges,
*/
ret = get_phys_offset_from_vmcoreinfo_pt_note(&phys_offset);
if (!ret) {
- if (phys_offset != UINT64_MAX)
+ if (phys_offset != -1)
set_phys_offset(phys_offset,
"vmcoreinfo pt_note");
} else {
@@ 1031,7 1097,7 @@ int get_memory_ranges(struct memory_range **range, int *ranges,
*/
ret = get_phys_base_from_pt_load(&phys_offset);
if (!ret)
- if (phys_offset != UINT64_MAX)
+ if (phys_offset != -1)
set_phys_offset(phys_offset,
"pt_load");
}
@@ 1066,7 1132,7 @@ int get_memory_ranges(struct memory_range **range, int *ranges,
memranges.size - 1,
memranges.ranges[memranges.size - 1].start,
memranges.ranges[memranges.size - 1].end);
- } else if (to_be_excluded(str)) {
+ } else if (to_be_excluded(str, start, end)) {
if (!memranges.size)
continue;
@@ 1144,46 1210,34 @@ int machine_verify_elf_rel(struct mem_ehdr *ehdr)
return (ehdr->e_machine == EM_AARCH64);
}
+enum aarch64_rel_type {
+ R_AARCH64_NONE = 0,
+ R_AARCH64_ABS64 = 257,
+ R_AARCH64_PREL32 = 261,
+ R_AARCH64_MOVW_UABS_G0_NC = 264,
+ R_AARCH64_MOVW_UABS_G1_NC = 266,
+ R_AARCH64_MOVW_UABS_G2_NC = 268,
+ R_AARCH64_MOVW_UABS_G3 =269,
+ R_AARCH64_LD_PREL_LO19 = 273,
+ R_AARCH64_ADR_PREL_LO21 = 274,
+ R_AARCH64_ADR_PREL_PG_HI21 = 275,
+ R_AARCH64_ADD_ABS_LO12_NC = 277,
+ R_AARCH64_JUMP26 = 282,
+ R_AARCH64_CALL26 = 283,
+ R_AARCH64_LDST64_ABS_LO12_NC = 286,
+ R_AARCH64_LDST128_ABS_LO12_NC = 299
+};
+
+static uint32_t get_bits(uint32_t value, int start, int end)
+{
+ uint32_t mask = ((uint32_t)1 << (end + 1 - start)) - 1;
+ return (value >> start) & mask;
+}
+
void machine_apply_elf_rel(struct mem_ehdr *ehdr, struct mem_sym *UNUSED(sym),
unsigned long r_type, void *ptr, unsigned long address,
unsigned long value)
{
-#if !defined(R_AARCH64_ABS64)
-# define R_AARCH64_ABS64 257
-#endif
-
-#if !defined(R_AARCH64_PREL32)
-# define R_AARCH64_PREL32 261
-#endif
-
-#if !defined(R_AARCH64_LD_PREL_LO19)
-# define R_AARCH64_LD_PREL_LO19 273
-#endif
-
-#if !defined(R_AARCH64_ADR_PREL_LO21)
-# define R_AARCH64_ADR_PREL_LO21 274
-#endif
-
-#if !defined(R_AARCH64_ADR_PREL_PG_HI21)
-# define R_AARCH64_ADR_PREL_PG_HI21 275
-#endif
-
-#if !defined(R_AARCH64_ADD_ABS_LO12_NC)
-# define R_AARCH64_ADD_ABS_LO12_NC 277
-#endif
-
-#if !defined(R_AARCH64_JUMP26)
-# define R_AARCH64_JUMP26 282
-#endif
-
-#if !defined(R_AARCH64_CALL26)
-# define R_AARCH64_CALL26 283
-#endif
-
-#if !defined(R_AARCH64_LDST64_ABS_LO12_NC)
-# define R_AARCH64_LDST64_ABS_LO12_NC 286
-#endif
-
uint64_t *loc64;
uint32_t *loc32;
uint64_t *location = (uint64_t *)ptr;
@@ 1191,18 1245,47 @@ void machine_apply_elf_rel(struct mem_ehdr *ehdr, struct mem_sym *UNUSED(sym),
uint64_t imm;
const char *type = NULL;
- switch(r_type) {
+ switch((enum aarch64_rel_type)r_type) {
case R_AARCH64_ABS64:
type = "ABS64";
loc64 = ptr;
- *loc64 = cpu_to_elf64(ehdr, elf64_to_cpu(ehdr, *loc64) + value);
+ *loc64 = cpu_to_elf64(ehdr, value);
break;
case R_AARCH64_PREL32:
type = "PREL32";
loc32 = ptr;
- *loc32 = cpu_to_elf32(ehdr,
- elf32_to_cpu(ehdr, *loc32) + value - address);
+ *loc32 = cpu_to_elf32(ehdr, value - address);
+ break;
+
+ /* Set a MOV[KZ] immediate field to bits [15:0] of X. No overflow check */
+ case R_AARCH64_MOVW_UABS_G0_NC:
+ type = "MOVW_UABS_G0_NC";
+ loc32 = ptr;
+ imm = get_bits(value, 0, 15);
+ *loc32 = cpu_to_le32(le32_to_cpu(*loc32) + (imm << 5));
+ break;
+ /* Set a MOV[KZ] immediate field to bits [31:16] of X. No overflow check */
+ case R_AARCH64_MOVW_UABS_G1_NC:
+ type = "MOVW_UABS_G1_NC";
+ loc32 = ptr;
+ imm = get_bits(value, 16, 31);
+ *loc32 = cpu_to_le32(le32_to_cpu(*loc32) + (imm << 5));
+ break;
+ /* Set a MOV[KZ] immediate field to bits [47:32] of X. No overflow check */
+ case R_AARCH64_MOVW_UABS_G2_NC:
+ type = "MOVW_UABS_G2_NC";
+ loc32 = ptr;
+ imm = get_bits(value, 32, 47);
+ *loc32 = cpu_to_le32(le32_to_cpu(*loc32) + (imm << 5));
break;
+ /* Set a MOV[KZ] immediate field to bits [63:48] of X */
+ case R_AARCH64_MOVW_UABS_G3:
+ type = "MOVW_UABS_G3";
+ loc32 = ptr;
+ imm = get_bits(value, 48, 63);
+ *loc32 = cpu_to_le32(le32_to_cpu(*loc32) + (imm << 5));
+ break;
+
case R_AARCH64_LD_PREL_LO19:
type = "LD_PREL_LO19";
loc32 = ptr;
@@ 1243,6 1326,7 @@ void machine_apply_elf_rel(struct mem_ehdr *ehdr, struct mem_sym *UNUSED(sym),
*loc32 = cpu_to_le32(le32_to_cpu(*loc32)
+ (((value - address) >> 2) & 0x3ffffff));
break;
+ /* encode imm field with bits [11:3] of value */
case R_AARCH64_LDST64_ABS_LO12_NC:
if (value & 7)
die("%s: ERROR Unaligned value: %lx\n", __func__,
@@ 1252,6 1336,17 @@ void machine_apply_elf_rel(struct mem_ehdr *ehdr, struct mem_sym *UNUSED(sym),
*loc32 = cpu_to_le32(le32_to_cpu(*loc32)
+ ((value & 0xff8) << (10 - 3)));
break;
+
+ /* encode imm field with bits [11:4] of value */
+ case R_AARCH64_LDST128_ABS_LO12_NC:
+ if (value & 15)
+ die("%s: ERROR Unaligned value: %lx\n", __func__,
+ value);
+ type = "LDST128_ABS_LO12_NC";
+ loc32 = ptr;
+ imm = value & 0xff0;
+ *loc32 = cpu_to_le32(le32_to_cpu(*loc32) + (imm << (10 - 4)));
+ break;
default:
die("%s: ERROR Unknown type: %lu\n", __func__, r_type);
break;
M kexec/arch/arm64/kexec-arm64.h => kexec/arch/arm64/kexec-arm64.h +2 -1
@@ 58,7 58,7 @@ extern off_t initrd_size;
*/
struct arm64_mem {
- uint64_t phys_offset;
+ int64_t phys_offset;
uint64_t text_offset;
uint64_t image_size;
uint64_t vp_offset;
@@ 69,6 69,7 @@ extern struct arm64_mem arm64_mem;
uint64_t get_phys_offset(void);
uint64_t get_vp_offset(void);
+int get_page_offset(unsigned long *offset);
static inline void reset_vp_offset(void)
{
M kexec/arch/mips/crashdump-mips.c => kexec/arch/mips/crashdump-mips.c +4 -1
@@ 334,7 334,10 @@ static int patch_elf_info(void)
if (strncmp(line, "cpu model", 9) == 0) {
/* OCTEON uses a different page_offset. */
if (strstr(line, "Octeon"))
- elf_info64.page_offset = 0x8000000000000000ULL;
+ elf_info64.page_offset = OCTEON_PAGE_OFFSET;
+ /* LOONGSON uses a different page_offset. */
+ else if (strstr(line, "Loongson"))
+ elf_info64.page_offset = LOONGSON_PAGE_OFFSET;
break;
}
}
M kexec/arch/mips/crashdump-mips.h => kexec/arch/mips/crashdump-mips.h +2 -0
@@ 13,6 13,8 @@ int load_crashdump_segments(struct kexec_info *info, char *mod_cmdline,
#endif
#define __pa(x) ((unsigned long)(X) & 0x7fffffff)
+#define LOONGSON_PAGE_OFFSET 0xffffffff80000000ULL
+#define OCTEON_PAGE_OFFSET 0x8000000000000000ULL
#define CRASH_MAX_MEMMAP_NR (KEXEC_MAX_SEGMENTS + 1)
#define CRASH_MAX_MEMORY_RANGES (MAX_MEMORY_RANGES + 2)
M kexec/arch/mips/kexec-elf-mips.c => kexec/arch/mips/kexec-elf-mips.c +74 -2
@@ 40,6 40,77 @@ static const int probe_debug = 0;
#define CMDLINE_PREFIX "kexec "
static char cmdline_buf[COMMAND_LINE_SIZE] = CMDLINE_PREFIX;
+/* Converts unsigned long to ascii string. */
+static void ultoa(unsigned long i, char *str)
+{
+ int j = 0, k;
+ char tmp;
+
+ do {
+ str[j++] = i % 10 + '0';
+ } while ((i /= 10) > 0);
+ str[j] = '\0';
+
+ /* Reverse the string. */
+ for (j = 0, k = strlen(str) - 1; j < k; j++, k--) {
+ tmp = str[k];
+ str[k] = str[j];
+ str[j] = tmp;
+ }
+}
+
+/* Adds initrd parameters to command line. */
+static int cmdline_add_initrd(char *cmdline, unsigned long addr, char *new_para)
+{
+ int cmdlen, len;
+ char str[30], *ptr;
+
+ ptr = str;
+ strcpy(str, new_para);
+ ptr += strlen(str);
+ ultoa(addr, ptr);
+ len = strlen(str);
+ cmdlen = strlen(cmdline) + len;
+ if (cmdlen > (COMMAND_LINE_SIZE - 1))
+ die("Command line overflow\n");
+ strcat(cmdline, str);
+
+ return 0;
+}
+
+/* add initrd to cmdline to compatible with previous platforms. */
+static int patch_initrd_info(char *cmdline, unsigned long base,
+ unsigned long size)
+{
+ const char cpuinfo[] = "/proc/cpuinfo";
+ char line[MAX_LINE];
+ FILE *fp;
+ unsigned long page_offset = PAGE_OFFSET;
+
+ fp = fopen(cpuinfo, "r");
+ if (!fp) {
+ fprintf(stderr, "Cannot open %s: %s\n",
+ cpuinfo, strerror(errno));
+ return -1;
+ }
+ while (fgets(line, sizeof(line), fp) != 0) {
+ if (strncmp(line, "cpu model", 9) == 0) {
+ if (strstr(line, "Loongson")) {
+ /* LOONGSON64 uses a different page_offset. */
+ if (arch_options.core_header_type ==
+ CORE_TYPE_ELF64)
+ page_offset = LOONGSON_PAGE_OFFSET;
+ cmdline_add_initrd(cmdline,
+ page_offset + base, " rd_start=");
+ cmdline_add_initrd(cmdline, size, " rd_size=");
+ break;
+ }
+ }
+ }
+ fclose(fp);
+ return 0;
+}
+
int elf_mips_probe(const char *buf, off_t len)
{
struct mem_ehdr ehdr;
@@ 171,9 242,10 @@ int elf_mips_load(int argc, char **argv, const char *buf, off_t len,
/* Now that the buffer for initrd is prepared, update the dtb
* with an appropriate location */
dtb_set_initrd(&dtb_buf, &dtb_length, initrd_base, initrd_base + initrd_size);
- }
-
+ /* Add the initrd parameters to cmdline */
+ patch_initrd_info(cmdline_buf, initrd_base, initrd_size);
+ }
/* This is a legacy method for commandline passing used
* currently by Octeon CPUs only */
add_buffer(info, cmdline_buf, sizeof(cmdline_buf),
M kexec/arch/mips/kexec-mips.c => kexec/arch/mips/kexec-mips.c +13 -2
@@ 109,15 109,17 @@ int arch_process_options(int argc, char **argv)
};
static const char short_options[] = KEXEC_ARCH_OPT_STR;
int opt;
+ char *cmdline = NULL;
+ const char *append = NULL;
while ((opt = getopt_long(argc, argv, short_options,
options, 0)) != -1) {
switch (opt) {
case OPT_APPEND:
- arch_options.command_line = optarg;
+ append = optarg;
break;
case OPT_REUSE_CMDLINE:
- arch_options.command_line = get_command_line();
+ cmdline = get_command_line();
break;
case OPT_DTB:
arch_options.dtb_file = optarg;
@@ 130,6 132,15 @@ int arch_process_options(int argc, char **argv)
}
}
+ arch_options.command_line = concat_cmdline(cmdline, append);
+
+ dbgprintf("%s:%d: command_line: %s\n", __func__, __LINE__,
+ arch_options.command_line);
+ dbgprintf("%s:%d: initrd: %s\n", __func__, __LINE__,
+ arch_options.initrd_file);
+ dbgprintf("%s:%d: dtb: %s\n", __func__, __LINE__,
+ arch_options.dtb_file);
+
return 0;
}
M kexec/arch/s390/crashdump-s390.c => kexec/arch/s390/crashdump-s390.c +2 -1
@@ 52,7 52,8 @@ static int create_elf_header(struct kexec_info *info, unsigned long crash_base,
elfcorehdr_size = bufsz;
snprintf(str, sizeof(str), " elfcorehdr=%ld@%ldK\n",
elfcorehdr_size, elfcorehdr / 1024);
- command_line_add(str);
+ if (command_line_add(info, str))
+ return -1;
#endif
return 0;
}
M kexec/arch/s390/include/arch/options.h => kexec/arch/s390/include/arch/options.h +6 -4
@@ 1,9 1,10 @@
#ifndef KEXEC_ARCH_S390_OPTIONS_H
#define KEXEC_ARCH_S390_OPTIONS_H
-#define OPT_ARCH_MAX (OPT_MAX+0)
-#define OPT_APPEND OPT_MAX+0
-#define OPT_RAMDISK OPT_MAX+1
+#define OPT_ARCH_MAX (OPT_MAX+0)
+#define OPT_APPEND (OPT_MAX+0)
+#define OPT_RAMDISK (OPT_MAX+1)
+#define OPT_REUSE_CMDLINE (OPT_MAX+2)
/* Options relevant to the architecture (excluding loader-specific ones),
* in this case none:
@@ 31,7 32,8 @@
KEXEC_ARCH_OPTIONS \
{"command-line", 1, 0, OPT_APPEND}, \
{"append", 1, 0, OPT_APPEND}, \
- {"initrd", 1, 0, OPT_RAMDISK},
+ {"initrd", 1, 0, OPT_RAMDISK}, \
+ {"reuse-cmdline", 0, 0, OPT_REUSE_CMDLINE },
#define KEXEC_ALL_OPT_STR KEXEC_ARCH_OPT_STR
M kexec/arch/s390/kexec-elf-rel-s390.c => kexec/arch/s390/kexec-elf-rel-s390.c +2 -1
@@ 56,6 56,7 @@ void machine_apply_elf_rel(struct mem_ehdr *UNUSED(ehdr),
case R_390_PC16: /* PC relative 16 bit. */
case R_390_PC16DBL: /* PC relative 16 bit shifted by 1. */
case R_390_PC32DBL: /* PC relative 32 bit shifted by 1. */
+ case R_390_PLT32DBL: /* 32 bit PC rel. PLT shifted by 1. */
case R_390_PC32: /* PC relative 32 bit. */
case R_390_PC64: /* PC relative 64 bit. */
val -= address;
@@ 63,7 64,7 @@ void machine_apply_elf_rel(struct mem_ehdr *UNUSED(ehdr),
*(unsigned short *) loc = val;
else if (r_type == R_390_PC16DBL)
*(unsigned short *) loc = val >> 1;
- else if (r_type == R_390_PC32DBL)
+ else if (r_type == R_390_PC32DBL || r_type == R_390_PLT32DBL)
*(unsigned int *) loc = val >> 1;
else if (r_type == R_390_PC32)
*(unsigned int *) loc = val;
M kexec/arch/s390/kexec-image.c => kexec/arch/s390/kexec-image.c +53 -31
@@ 25,7 25,6 @@
#include <fcntl.h>
static uint64_t crash_base, crash_end;
-static char command_line[COMMAND_LINESIZE];
static void add_segment_check(struct kexec_info *info, const void *buf,
size_t bufsz, unsigned long base, size_t memsz)
@@ 36,13 35,18 @@ static void add_segment_check(struct kexec_info *info, const void *buf,
add_segment(info, buf, bufsz, crash_base + base, memsz);
}
-int command_line_add(const char *str)
+int command_line_add(struct kexec_info *info, const char *str)
{
- if (strlen(command_line) + strlen(str) + 1 > COMMAND_LINESIZE) {
- fprintf(stderr, "Command line too long.\n");
+ char *tmp = NULL;
+
+ tmp = concat_cmdline(info->command_line, str);
+ if (!tmp) {
+ fprintf(stderr, "out of memory\n");
return -1;
}
- strcat(command_line, str);
+
+ free(info->command_line);
+ info->command_line = tmp;
return 0;
}
@@ 53,10 57,7 @@ int image_s390_load_file(int argc, char **argv, struct kexec_info *info)
static const struct option options[] =
{
- KEXEC_OPTIONS
- {"command-line", 1, 0, OPT_APPEND},
- {"append", 1, 0, OPT_APPEND},
- {"initrd", 1, 0, OPT_RAMDISK},
+ KEXEC_ALL_OPTIONS
{0, 0, 0, 0},
};
static const char short_options[] = KEXEC_OPT_STR "";
@@ 64,12 65,16 @@ int image_s390_load_file(int argc, char **argv, struct kexec_info *info)
while ((opt = getopt_long(argc, argv, short_options, options, 0)) != -1) {
switch(opt) {
case OPT_APPEND:
- if (command_line_add(optarg))
+ if (command_line_add(info, optarg))
return -1;
break;
case OPT_RAMDISK:
ramdisk = optarg;
break;
+ case OPT_REUSE_CMDLINE:
+ free(info->command_line);
+ info->command_line = get_command_line();
+ break;
}
}
@@ 78,13 83,16 @@ int image_s390_load_file(int argc, char **argv, struct kexec_info *info)
if (info->initrd_fd == -1) {
fprintf(stderr, "Could not open initrd file %s:%s\n",
ramdisk, strerror(errno));
+ free(info->command_line);
+ info->command_line = NULL;
return -1;
}
}
- info->command_line = command_line;
- info->command_line_len = strlen (command_line) + 1;
-
+ if (info->command_line)
+ info->command_line_len = strlen(info->command_line) + 1;
+ else
+ info->command_line_len = 0;
return 0;
}
@@ 97,22 105,18 @@ image_s390_load(int argc, char **argv, const char *kernel_buf,
const char *ramdisk;
off_t ramdisk_len;
unsigned int ramdisk_origin;
- int opt;
+ int opt, ret = -1;
if (info->file_mode)
return image_s390_load_file(argc, argv, info);
static const struct option options[] =
{
- KEXEC_OPTIONS
- {"command-line", 1, 0, OPT_APPEND},
- {"append", 1, 0, OPT_APPEND},
- {"initrd", 1, 0, OPT_RAMDISK},
+ KEXEC_ALL_OPTIONS
{0, 0, 0, 0},
};
static const char short_options[] = KEXEC_OPT_STR "";
- command_line[0] = 0;
ramdisk = NULL;
ramdisk_len = 0;
ramdisk_origin = 0;
@@ 120,9 124,13 @@ image_s390_load(int argc, char **argv, const char *kernel_buf,
while ((opt = getopt_long(argc,argv,short_options,options,0)) != -1) {
switch(opt) {
case OPT_APPEND:
- if (command_line_add(optarg))
+ if (command_line_add(info, optarg))
return -1;
break;
+ case OPT_REUSE_CMDLINE:
+ free(info->command_line);
+ info->command_line = get_command_line();
+ break;
case OPT_RAMDISK:
ramdisk = optarg;
break;
@@ 132,7 140,7 @@ image_s390_load(int argc, char **argv, const char *kernel_buf,
if (info->kexec_flags & KEXEC_ON_CRASH) {
if (parse_iomem_single("Crash kernel\n", &crash_base,
&crash_end))
- return -1;
+ goto out;
}
/* Add kernel segment */
@@ 151,7 159,7 @@ image_s390_load(int argc, char **argv, const char *kernel_buf,
rd_buffer = slurp_file_mmap(ramdisk, &ramdisk_len);
if (rd_buffer == NULL) {
fprintf(stderr, "Could not read ramdisk.\n");
- return -1;
+ goto out;
}
ramdisk_origin = MAX(RAMDISK_ORIGIN_ADDR, kernel_size);
ramdisk_origin = _ALIGN_UP(ramdisk_origin, 0x100000);
@@ 160,7 168,7 @@ image_s390_load(int argc, char **argv, const char *kernel_buf,
}
if (info->kexec_flags & KEXEC_ON_CRASH) {
if (load_crashdump_segments(info, crash_base, crash_end))
- return -1;
+ goto out;
} else {
info->entry = (void *) IMAGE_READ_OFFSET;
}
@@ 183,15 191,28 @@ image_s390_load(int argc, char **argv, const char *kernel_buf,
*tmp = crash_end - crash_base + 1;
}
}
- /*
- * We will write a probably given command line.
- * First, erase the old area, then setup the new parameters:
- */
- if (strlen(command_line) != 0) {
- memset(krnl_buffer + COMMAND_LINE_OFFS, 0, COMMAND_LINESIZE);
- memcpy(krnl_buffer + COMMAND_LINE_OFFS, command_line, strlen(command_line));
+
+ if (info->command_line) {
+ unsigned long maxsize;
+ char *dest = krnl_buffer + COMMAND_LINE_OFFS;
+
+ maxsize = *(unsigned long *)(krnl_buffer + MAX_COMMAND_LINESIZE_OFFS);
+ if (!maxsize)
+ maxsize = LEGACY_COMMAND_LINESIZE;
+
+ if (strlen(info->command_line) > maxsize-1) {
+ fprintf(stderr, "command line too long, maximum allowed size %ld\n",
+ maxsize-1);
+ goto out;
+ }
+ strncpy(dest, info->command_line, maxsize-1);
+ dest[maxsize-1] = '\0';
}
- return 0;
+ ret = 0;
+out:
+ free(info->command_line);
+ info->command_line = NULL;
+ return ret;
}
int
@@ 210,5 231,6 @@ image_s390_usage(void)
printf("--command-line=STRING Set the kernel command line to STRING.\n"
"--append=STRING Set the kernel command line to STRING.\n"
"--initrd=FILENAME Use the file FILENAME as a ramdisk.\n"
+ "--reuse-cmdline Use kernel command line from running system.\n"
);
}
M kexec/arch/s390/kexec-s390.h => kexec/arch/s390/kexec-s390.h +11 -10
@@ 10,16 10,17 @@
#ifndef KEXEC_S390_H
#define KEXEC_S390_H
-#define IMAGE_READ_OFFSET 0x10000
+#define IMAGE_READ_OFFSET 0x10000
-#define RAMDISK_ORIGIN_ADDR 0x800000
-#define INITRD_START_OFFS 0x408
-#define INITRD_SIZE_OFFS 0x410
-#define OLDMEM_BASE_OFFS 0x418
-#define OLDMEM_SIZE_OFFS 0x420
-#define COMMAND_LINE_OFFS 0x480
-#define COMMAND_LINESIZE 896
-#define MAX_MEMORY_RANGES 1024
+#define RAMDISK_ORIGIN_ADDR 0x800000
+#define INITRD_START_OFFS 0x408
+#define INITRD_SIZE_OFFS 0x410
+#define OLDMEM_BASE_OFFS 0x418
+#define OLDMEM_SIZE_OFFS 0x420
+#define MAX_COMMAND_LINESIZE_OFFS 0x430
+#define COMMAND_LINE_OFFS 0x480
+#define LEGACY_COMMAND_LINESIZE 896
+#define MAX_MEMORY_RANGES 1024
#define MAX(x, y) ((x) > (y) ? (x) : (y))
#define MIN(x, y) ((x) < (y) ? (x) : (y))
@@ 32,6 33,6 @@ extern int load_crashdump_segments(struct kexec_info *info,
unsigned long crash_end);
extern int get_memory_ranges_s390(struct memory_range range[], int *ranges,
int with_crashk);
-extern int command_line_add(const char *str);
+extern int command_line_add(struct kexec_info *info, const char *str);
#endif /* KEXEC_S390_H */
M kexec/kexec-elf-rel.c => kexec/kexec-elf-rel.c +7 -3
@@ 168,6 168,10 @@ int build_elf_rel_info(const char *buf, off_t len, struct mem_ehdr *ehdr,
return 0;
}
+static unsigned long get_section_addralign(struct mem_shdr *shdr)
+{
+ return (shdr->sh_addralign == 0) ? 1 : shdr->sh_addralign;
+}
int elf_rel_load(struct mem_ehdr *ehdr, struct kexec_info *info,
unsigned long min, unsigned long max, int end)
@@ 219,7 223,7 @@ int elf_rel_load(struct mem_ehdr *ehdr, struct kexec_info *info,
}
if (shdr->sh_type != SHT_NOBITS) {
unsigned long align;
- align = shdr->sh_addralign;
+ align = get_section_addralign(shdr);
/* See if I need more alignment */
if (buf_align < align) {
buf_align = align;
@@ 231,7 235,7 @@ int elf_rel_load(struct mem_ehdr *ehdr, struct kexec_info *info,
}
else {
unsigned long align;
- align = shdr->sh_addralign;
+ align = get_section_addralign(shdr);
/* See if I need more alignment */
if (bss_align < align) {
bss_align = align;
@@ 265,7 269,7 @@ int elf_rel_load(struct mem_ehdr *ehdr, struct kexec_info *info,
if (!(shdr->sh_flags & SHF_ALLOC)) {
continue;
}
- align = shdr->sh_addralign;
+ align = get_section_addralign(shdr);
if (shdr->sh_type != SHT_NOBITS) {
unsigned long off;
/* Adjust the address */
M kexec/kexec-xen.c => kexec/kexec-xen.c +8 -4
@@ 247,21 247,24 @@ int xen_kexec_status(uint64_t kexec_flags)
return ret;
}
-void xen_kexec_exec(uint64_t kexec_flags)
+int xen_kexec_exec(uint64_t kexec_flags)
{
xc_interface *xch;
uint8_t type = KEXEC_TYPE_DEFAULT;
+ int ret;
xch = xc_interface_open(NULL, NULL, 0);
if (!xch)
- return;
+ return -1;
if (kexec_flags & KEXEC_LIVE_UPDATE)
type = KEXEC_TYPE_LIVE_UPDATE;
- xc_kexec_exec(xch, type);
+ ret = xc_kexec_exec(xch, type);
xc_interface_close(xch);
+
+ return ret;
}
#else /* ! HAVE_LIBXENCTRL */
@@ 286,8 289,9 @@ int xen_kexec_status(uint64_t kexec_flags)
return -1;
}
-void xen_kexec_exec(uint64_t kexec_flags)
+int xen_kexec_exec(uint64_t kexec_flags)
{
+ return -1;
}
#endif
M kexec/kexec.c => kexec/kexec.c +81 -20
@@ 902,13 902,28 @@ static int my_shutdown(void)
}
/*
- * Exec the new kernel (reboot)
+ * Exec the new kernel. If successful, this triggers an immediate reboot
+ * and does not return, but Xen Live Update is an exception (more on this
+ * below).
*/
static int my_exec(void)
{
- if (xen_present())
- xen_kexec_exec(kexec_flags);
- else
+ if (xen_present()) {
+ int ret;
+
+ /*
+ * There are two cases in which the Xen hypercall may return:
+ * 1) An error occurred, e.g. the kexec image was not loaded.
+ * The exact error is indicated by errno.
+ * 2) Live Update was successfully scheduled. Note that unlike
+ * a normal kexec, Live Update happens asynchronously, i.e.
+ * the hypercall merely schedules the kexec operation and
+ * returns immediately.
+ */
+ ret = xen_kexec_exec(kexec_flags);
+ if ((kexec_flags & KEXEC_LIVE_UPDATE) && !ret)
+ return 0;
+ } else
reboot(LINUX_REBOOT_CMD_KEXEC);
/* I have failed if I make it here */
fprintf(stderr, "kexec failed: %s\n",
@@ 1106,6 1121,57 @@ static void remove_parameter(char *line, const char *param_name)
}
}
+static ssize_t _read(int fd, void *buf, size_t count)
+{
+ ssize_t ret, offset = 0;
+
+ do {
+ ret = read(fd, buf + offset, count - offset);
+ if (ret < 0) {
+ if ((errno == EINTR) || (errno == EAGAIN))
+ continue;
+ return ret;
+ }
+ offset += ret;
+ } while (ret && offset < count);
+
+ return offset;
+}
+
+static char *slurp_proc_file(const char *filename, size_t *len)
+{
+ ssize_t ret, startpos = 0;
+ unsigned int size = 64;
+ char *buf = NULL, *tmp;
+ int fd;
+
+ fd = open(filename, O_RDONLY);
+ if (fd == -1)
+ return NULL;
+
+ do {
+ size *= 2;
+ tmp = realloc(buf, size);
+ if (!tmp) {
+ free(buf);
+ return NULL;
+ }
+ buf = tmp;
+
+ ret = _read(fd, buf + startpos, size - startpos);
+ if (ret < 0) {
+ free(buf);
+ return NULL;
+ }
+
+ startpos += ret;
+
+ } while(ret);
+
+ *len = startpos;
+ return buf;
+}
+
/*
* Returns the contents of the current command line to be used with
* --reuse-cmdline option. The function gets called from architecture specific
@@ 1121,25 1187,19 @@ static void remove_parameter(char *line, const char *param_name)
*/
char *get_command_line(void)
{
- FILE *fp;
- char *line;
- const int sizeof_line = 2048;
-
- line = malloc(sizeof_line);
- if (line == NULL)
- die("Could not allocate memory to read /proc/cmdline.");
-
- fp = fopen("/proc/cmdline", "r");
- if (!fp)
- die("Could not open /proc/cmdline.");
-
- if (fgets(line, sizeof_line, fp) == NULL)
- die("Can't read /proc/cmdline.");
+ char *p, *line;
+ size_t size;
- fclose(fp);
+ line = slurp_proc_file("/proc/cmdline", &size);
+ if (!line || !size)
+ die("Failed to read /proc/cmdline\n");
/* strip newline */
- line[strlen(line) - 1] = '\0';
+ line[size-1] = '\0';
+
+ p = strpbrk(line, "\r\n");
+ if (p)
+ *p = '\0';
remove_parameter(line, "BOOT_IMAGE");
if (kexec_flags & KEXEC_ON_CRASH)
@@ 1294,6 1354,7 @@ static int do_kexec_file_load(int fileind, int argc, char **argv,
case EMSGSIZE:
/* Reject by default. */
default:
+ fprintf(stderr, "kexec_file_load failed: %s\n", strerror(errno));
ret = EFAILED;
break;
M kexec/kexec.h => kexec/kexec.h +1 -1
@@ 324,7 324,7 @@ void cmdline_add_liveupdate(char **base);
int xen_present(void);
int xen_kexec_load(struct kexec_info *info);
int xen_kexec_unload(uint64_t kexec_flags);
-void xen_kexec_exec(uint64_t kexec_flags);
+int xen_kexec_exec(uint64_t kexec_flags);
int xen_kexec_status(uint64_t kexec_flags);
extern unsigned long long get_kernel_sym(const char *text);
M kexec/lzma.c => kexec/lzma.c +39 -0
@@ 155,6 155,42 @@ ssize_t lzread(LZFILE *lzfile, void *buf, size_t len)
}
}
+int is_lzma_file(const char *filename)
+{
+ FILE *fp;
+ int ret = 0;
+ uint8_t buf[13];
+
+ if (!filename)
+ return 0;
+
+ fp = fopen(filename, "r");
+ if (fp == NULL)
+ return 0;
+
+ const size_t size = fread(buf, 1, sizeof(buf), fp);
+
+ if (size != 13) {
+ /* file is too small to be a lzma file. */
+ fclose(fp);
+ return 0;
+ }
+
+ lzma_filter filter = { .id = LZMA_FILTER_LZMA1 };
+
+ switch (lzma_properties_decode(&filter, NULL, buf, 5)) {
+ case LZMA_OK:
+ ret = 1;
+ break;
+ default:
+ /* It's not a lzma file */
+ ret = 0;
+ }
+
+ fclose(fp);
+ return ret;
+}
+
char *lzma_decompress_file(const char *filename, off_t *r_size)
{
LZFILE *fp;
@@ 168,6 204,9 @@ char *lzma_decompress_file(const char *filename, off_t *r_size)
if (!filename)
return NULL;
+ if (!is_lzma_file(filename))
+ return NULL;
+
fp = lzopen(filename, "rb");
if (fp == 0) {
dbgprintf("Cannot open `%s'\n", filename);
M purgatory/Makefile => purgatory/Makefile +1 -1
@@ 49,7 49,7 @@ $(PURGATORY): CFLAGS=$(PURGATORY_EXTRA_CFLAGS) \
$($(ARCH)_PURGATORY_EXTRA_CFLAGS) \
-Os -fno-builtin -ffreestanding \
-fno-zero-initialized-in-bss \
- -fno-PIC -fno-PIE -fno-stack-protector
+ -fno-PIC -fno-PIE -fno-stack-protector -fno-tree-vectorize
$(PURGATORY): CPPFLAGS=$($(ARCH)_PURGATORY_EXTRA_CFLAGS) \
-I$(srcdir)/purgatory/include \
M util_lib/elf_info.c => util_lib/elf_info.c +31 -4
@@ 310,6 310,8 @@ int get_pt_load(int idx,
#define NOT_FOUND_LONG_VALUE (-1)
+void (*arch_scan_vmcoreinfo)(char *pos);
+
void scan_vmcoreinfo(char *start, size_t size)
{
char *last = start + size - 1;
@@ 551,6 553,9 @@ void scan_vmcoreinfo(char *start, size_t size)
}
}
+ if (arch_scan_vmcoreinfo != NULL)
+ (*arch_scan_vmcoreinfo)(pos);
+
if (last_line)
break;
}
@@ 758,8 763,9 @@ static void dump_dmesg_structured(int fd, void (*handler)(char*, unsigned int))
{
#define OUT_BUF_SIZE 4096
uint64_t log_buf, log_buf_offset, ts_nsec;
- uint32_t log_first_idx, log_next_idx, current_idx, len = 0, i;
+ uint32_t log_buf_len, log_first_idx, log_next_idx, current_idx, len = 0, i;
char *buf, out_buf[OUT_BUF_SIZE];
+ bool has_wrapped_around = false;
ssize_t ret;
char *msg;
uint16_t text_len;
@@ 806,6 812,7 @@ static void dump_dmesg_structured(int fd, void (*handler)(char*, unsigned int))
}
log_buf = read_file_pointer(fd, vaddr_to_offset(log_buf_vaddr));
+ log_buf_len = read_file_s32(fd, vaddr_to_offset(log_buf_len_vaddr));
log_first_idx = read_file_u32(fd, vaddr_to_offset(log_first_idx_vaddr));
log_next_idx = read_file_u32(fd, vaddr_to_offset(log_next_idx_vaddr));
@@ 877,11 884,31 @@ static void dump_dmesg_structured(int fd, void (*handler)(char*, unsigned int))
* and read the message at the start of the buffer.
*/
loglen = struct_val_u16(buf, log_offset_len);
- if (!loglen)
+ if (!loglen) {
+ if (has_wrapped_around) {
+ if (len && handler)
+ handler(out_buf, len);
+ fprintf(stderr, "Cycle when parsing dmesg detected.\n");
+ fprintf(stderr, "The prink log_buf is most likely corrupted.\n");
+ fprintf(stderr, "log_buf = 0x%lx, idx = 0x%x\n",
+ log_buf, current_idx);
+ exit(68);
+ }
current_idx = 0;
- else
+ has_wrapped_around = true;
+ } else {
/* Move to next record */
current_idx += loglen;
+ if(current_idx > log_buf_len - log_sz) {
+ if (len && handler)
+ handler(out_buf, len);
+ fprintf(stderr, "Index outside log_buf detected.\n");
+ fprintf(stderr, "The prink log_buf is most likely corrupted.\n");
+ fprintf(stderr, "log_buf = 0x%lx, idx = 0x%x\n",
+ log_buf, current_idx);
+ exit(69);
+ }
+ }
}
free(buf);
if (len && handler)
@@ 1236,7 1263,7 @@ int read_elf(int fd)
return 0;
}
-int read_phys_offset_elf_kcore(int fd, unsigned long *phys_off)
+int read_phys_offset_elf_kcore(int fd, long *phys_off)
{
int ret;
M util_lib/include/elf_info.h => util_lib/include/elf_info.h +2 -1
@@ 28,8 28,9 @@ int get_pt_load(int idx,
unsigned long long *phys_end,
unsigned long long *virt_start,
unsigned long long *virt_end);
-int read_phys_offset_elf_kcore(int fd, unsigned long *phys_off);
+int read_phys_offset_elf_kcore(int fd, long *phys_off);
int read_elf(int fd);
void dump_dmesg(int fd, void (*handler)(char*, unsigned int));
+extern void (*arch_scan_vmcoreinfo)(char *pos);
#endif /* ELF_INFO_H */