Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion arch/x86/boot/header.S
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,13 @@ xloadflags:
#define XLF56 0
#endif

.word XLF0 | XLF1 | XLF23 | XLF4 | XLF56
#ifdef CONFIG_MULTIKERNEL
# define XLF_MK XLF_MULTIKERNEL_IPI
#else
# define XLF_MK 0
#endif

.word XLF0 | XLF1 | XLF23 | XLF4 | XLF56 | XLF_MK

cmdline_size: .long COMMAND_LINE_SIZE-1 #length of the command line,
#added with boot protocol
Expand Down
5 changes: 3 additions & 2 deletions arch/x86/include/asm/multikernel.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ struct mk_spawn_context {
u32 target_apic_id; /* Target CPU's APIC ID */
u32 flags; /* MK_SPAWN_F_* flags */
u32 ready; /* Signal flag */
u32 reserved; /* Padding for alignment */
u32 abi_magic; /* Host/spawn generation marker */
/* Keep all existing context offsets unchanged. */
struct boot_params bp; /* Standard x86 boot params */
/* Optional boot data belongs after boot_params, in the zeroed tail. */
Expand Down Expand Up @@ -198,7 +198,8 @@ int mk_spawn_cpu(struct mk_instance *instance, int cpu,
/* The pool park set (park page, slot, page tables) as base,size pairs */
int mk_pool_park_regions(u64 *pairs, int max);

/* Initialize boot context tracking in spawn kernel */
/* Validate and initialize boot context tracking in spawn kernel */
struct mk_spawn_context *mk_validate_boot_context(phys_addr_t ctx_phys);
void mk_init_boot_context(phys_addr_t ctx_phys);


Expand Down
1 change: 1 addition & 0 deletions arch/x86/include/uapi/asm/bootparam.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#define XLF_5LEVEL (1<<5)
#define XLF_5LEVEL_ENABLED (1<<6)
#define XLF_MEM_ENCRYPTION (1<<7)
#define XLF_MULTIKERNEL_IPI 0x0100

#ifndef __ASSEMBLER__

Expand Down
24 changes: 24 additions & 0 deletions arch/x86/kernel/kexec-bzimage64.c
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,11 @@ static void *bzImage64_load(struct kimage *image, char *kernel,
.buf_max = ULONG_MAX, .top_down = true };

header = (struct setup_header *)(kernel + setup_hdr_offset);
if (image->type == KEXEC_TYPE_MULTIKERNEL &&
!(header->xloadflags & XLF_MULTIKERNEL_IPI)) {
pr_err("Loaded kernel lacks the required shared transport layout\n");
return ERR_PTR(-EPROTONOSUPPORT);
}
setup_sects = header->setup_sects;
if (setup_sects == 0)
setup_sects = 4;
Expand Down Expand Up @@ -747,10 +752,29 @@ static void *bzImage64_load(struct kimage *image, char *kernel,

/* For multikernel, setup custom e820 map */
if (image->type == KEXEC_TYPE_MULTIKERNEL) {
#ifdef CONFIG_MULTIKERNEL
image->arch.mk_boot_params = bootparam_load_addr;

/*
* setup_boot_parameters() copies the host subarchitecture. A
* spawn kernel must take the multikernel platform path instead.
*/
params->hdr.hardware_subarch = X86_SUBARCH_MULTIKERNEL;

/*
* The spawn trampoline enters the compressed kernel directly,
* bypassing purgatory. The x86 boot protocol's 64-bit entry is
* 0x200 bytes from the start of the protected-mode payload.
*/
image->arch.mk_kernel_entry = kernel_load_addr + 0x200;

ret = mk_e820_fill(image->mk_instance, params);
if (ret)
goto out_free_params;
#else
ret = -EOPNOTSUPP;
goto out_free_params;
#endif
}

/* Allocate loader specific data */
Expand Down
66 changes: 48 additions & 18 deletions arch/x86/kernel/kexec-vmlinux.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,16 @@ struct elf_kernel_info {

/*
* Find multikernel entry point from PT_NOTE section.
* Looks for note with name "Linux" and type 0x4d4b ('MK').
* The note type carries the generation; the descriptor remains one u64.
*/
static unsigned long find_multikernel_entry_note(const void *buf, size_t len,
const Elf64_Ehdr *ehdr)
#define MK_VMLINUX_LEGACY_NOTE_TYPE 0x4d4b

static int find_multikernel_entry_note(const void *buf, size_t len,
const Elf64_Ehdr *ehdr,
unsigned long *entry)
{
const Elf64_Phdr *phdrs = buf + ehdr->e_phoff;
bool legacy = false;
int i;

for (i = 0; i < ehdr->e_phnum; i++) {
Expand All @@ -91,25 +95,34 @@ static unsigned long find_multikernel_entry_note(const void *buf, size_t len,
if (ptr + note_size > end)
break;

if (nhdr->n_type == 0x4d4b &&
nhdr->n_namesz == 6 &&
if (nhdr->n_namesz == 6 &&
nhdr->n_descsz == sizeof(u64) &&
!memcmp(ptr + sizeof(*nhdr), "Linux", 6)) {
u64 entry = *(u64 *)(ptr + sizeof(*nhdr) +
ALIGN(nhdr->n_namesz, 4));
pr_info("multikernel: entry=0x%llx\n", entry);
return entry;
const u64 *note_entry;

note_entry = ptr + sizeof(*nhdr) +
ALIGN(nhdr->n_namesz, 4);
if (nhdr->n_type == MK_VMLINUX_NOTE_TYPE) {
*entry = *note_entry;
pr_info("multikernel: entry=0x%llx\n",
*note_entry);
return 0;
}
if (nhdr->n_type == MK_VMLINUX_LEGACY_NOTE_TYPE)
legacy = true;
}
ptr += note_size;
}
}
return 0;
return legacy ? -EPROTONOSUPPORT : -ENOENT;
}

/*
* Parse ELF kernel and extract key information
*/
static int kexec_parse_elf_kernel(const void *kernel_buf, unsigned long kernel_len,
static int kexec_parse_elf_kernel(const void *kernel_buf,
unsigned long kernel_len,
bool multikernel,
struct elf_kernel_info *info)
{
const Elf64_Ehdr *ehdr;
Expand Down Expand Up @@ -159,14 +172,25 @@ static int kexec_parse_elf_kernel(const void *kernel_buf, unsigned long kernel_l
* PT_NOTE contains physical offset from load base, not virtual address.
* This is the canonical way and survives symbol stripping.
*/
info->multikernel_entry = find_multikernel_entry_note(kernel_buf, kernel_len, ehdr);
if (!info->multikernel_entry) {
pr_err("multikernel_startup_64 entry offset not found in PT_NOTE\n");
return -ENOEXEC;
info->multikernel_entry = 0;
if (multikernel) {
int ret;

ret = find_multikernel_entry_note(kernel_buf, kernel_len, ehdr,
&info->multikernel_entry);
if (ret == -EPROTONOSUPPORT)
pr_err("legacy vmlinux note type 0x%x is incompatible; expected 0x%x\n",
MK_VMLINUX_LEGACY_NOTE_TYPE, MK_VMLINUX_NOTE_TYPE);
else if (ret)
pr_err("multikernel ABI note type 0x%x not found\n",
MK_VMLINUX_NOTE_TYPE);
if (ret)
return ret == -ENOENT ? -ENOEXEC : ret;

pr_info("Multikernel entry offset: 0x%lx\n",
info->multikernel_entry);
}

pr_info("Multikernel entry offset: 0x%lx\n", info->multikernel_entry);

/* Find lowest load address and calculate total memory needed */
phdr = (const Elf64_Phdr *)(kernel_buf + ehdr->e_phoff);
for (i = 0; i < ehdr->e_phnum; i++) {
Expand Down Expand Up @@ -326,12 +350,13 @@ static void *vmlinux_load(struct kimage *image, char *kernel,
.top_down = true };
struct kexec_buf pbuf = { .image = image, .buf_min = MIN_PURGATORY_ADDR,
.buf_max = ULONG_MAX, .top_down = true };
bool multikernel = image->type == KEXEC_TYPE_MULTIKERNEL;
int ret;

pr_info("Loading ELF vmlinux (type=%d)\n", image->type);

/* Parse ELF headers */
ret = kexec_parse_elf_kernel(kernel, kernel_len, &elf_info);
ret = kexec_parse_elf_kernel(kernel, kernel_len, multikernel, &elf_info);
if (ret) {
pr_err("Failed to parse ELF kernel: %d\n", ret);
return ERR_PTR(ret);
Expand Down Expand Up @@ -531,12 +556,17 @@ static void *vmlinux_load(struct kimage *image, char *kernel,

/* For multikernel, setup custom e820 map */
if (image->type == KEXEC_TYPE_MULTIKERNEL) {
#ifdef CONFIG_MULTIKERNEL
ret = mk_e820_fill(image->mk_instance, params);
if (ret) {
kvfree(ldata->kernel_buf);
kfree(ldata);
goto out_free_params;
}
#else
ret = -EOPNOTSUPP;
goto out_free_params;
#endif
}

ldata->bootparams_buf = params;
Expand Down
4 changes: 2 additions & 2 deletions arch/x86/kernel/platform-quirks.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ static void __init multikernel_setup_calibration(void)
{
phys_addr_t ctx_phys = orig_boot_params -
offsetof(struct mk_spawn_context, bp);
struct mk_spawn_context *ctx = __va(ctx_phys);
struct mk_spawn_context *ctx = mk_validate_boot_context(ctx_phys);

if (ctx->self_phys != ctx_phys || !ctx->boot_tsc_khz)
if (!ctx || !ctx->boot_tsc_khz)
return;

multikernel_tsc_khz = ctx->boot_tsc_khz;
Expand Down
3 changes: 2 additions & 1 deletion arch/x86/multikernel/head_64.S
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <linux/linkage.h>
#include <linux/init.h>
#include <linux/pgtable.h>
#include <linux/multikernel_abi.h>
#include <asm/segment.h>
#include <asm/page.h>
#include <asm/msr.h>
Expand Down Expand Up @@ -269,7 +270,7 @@ SYM_CODE_END(multikernel_secondary_startup)
.balign 4
.long 2f - 1f
.long 4f - 3f
.long 0x4d4b
.long MK_VMLINUX_NOTE_TYPE
1: .asciz "Linux"
2: .balign 4
3: .quad multikernel_startup_64 - __START_KERNEL_map
Expand Down
Loading