mirror of
https://github.com/ipxe/ipxe
synced 2025-12-07 18:00:28 +03:00
[lkrn] Add basic support for the RISC-V Linux kernel image format
The RISC-V and AArch64 bare-metal kernel images share a common header format, and require essentially the same execution environment: loaded close to the start of RAM, entered with paging disabled, and passed a pointer to a flattened device tree that describes the hardware and any boot arguments. Implement basic support for executing bare-metal RISC-V and AArch64 kernel images. The (trivial) AArch64-specific code path is untested since we do not yet have the ability to build for any bare-metal AArch64 platforms. Constructing and passing an initramfs image is not yet supported. Rename the IMAGE_BZIMAGE build configuration option to IMAGE_LKRN, since "bzImage" is specific to x86. To retain backwards compatibility with existing local build configurations, we leave IMAGE_BZIMAGE as the enabled option in config/default/pcbios.h and treat IMAGE_LKRN as a synonym for IMAGE_BZIMAGE when building for x86 BIOS. Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
30
src/arch/arm64/include/bits/lkrn.h
Normal file
30
src/arch/arm64/include/bits/lkrn.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef _BITS_LKRN_H
|
||||
#define _BITS_LKRN_H
|
||||
|
||||
/** @file
|
||||
*
|
||||
* Linux kernel image invocation
|
||||
*
|
||||
*/
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
/** Header magic value */
|
||||
#define LKRN_MAGIC_ARCH LKRN_MAGIC_AARCH64
|
||||
|
||||
/**
|
||||
* Jump to kernel entry point
|
||||
*
|
||||
* @v entry Kernel entry point
|
||||
* @v fdt Device tree
|
||||
*/
|
||||
static inline __attribute__ (( noreturn )) void
|
||||
lkrn_jump ( physaddr_t entry, physaddr_t fdt ) {
|
||||
register unsigned long x0 asm ( "x0" ) = fdt;
|
||||
|
||||
__asm__ __volatile__ ( "br %1"
|
||||
: : "r" ( x0 ), "r" ( entry ) );
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
#endif /* _BITS_LKRN_H */
|
||||
34
src/arch/riscv/include/bits/lkrn.h
Normal file
34
src/arch/riscv/include/bits/lkrn.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#ifndef _BITS_LKRN_H
|
||||
#define _BITS_LKRN_H
|
||||
|
||||
/** @file
|
||||
*
|
||||
* Linux kernel image invocation
|
||||
*
|
||||
*/
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
#include <ipxe/hart.h>
|
||||
|
||||
/** Header magic value */
|
||||
#define LKRN_MAGIC_ARCH LKRN_MAGIC_RISCV
|
||||
|
||||
/**
|
||||
* Jump to kernel entry point
|
||||
*
|
||||
* @v entry Kernel entry point
|
||||
* @v fdt Device tree
|
||||
*/
|
||||
static inline __attribute__ (( noreturn )) void
|
||||
lkrn_jump ( physaddr_t entry, physaddr_t fdt ) {
|
||||
register unsigned long a0 asm ( "a0" ) = boot_hart;
|
||||
register unsigned long a1 asm ( "a1" ) = fdt;
|
||||
|
||||
__asm__ __volatile__ ( "call disable_paging\n\t"
|
||||
"jr %2\n\t"
|
||||
: : "r" ( a0 ), "r" ( a1 ), "r" ( entry ) );
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
#endif /* _BITS_LKRN_H */
|
||||
@@ -22,6 +22,7 @@
|
||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
#include <config/console.h>
|
||||
#include <config/general.h>
|
||||
|
||||
/** @file
|
||||
*
|
||||
@@ -48,3 +49,13 @@ REQUIRE_OBJECT ( vesafb );
|
||||
#ifdef CONSOLE_INT13
|
||||
REQUIRE_OBJECT ( int13con );
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Drag in all requested image types
|
||||
*
|
||||
*/
|
||||
|
||||
/* Allow IMAGE_LKRN to be a synonynm for IMAGE_BZIMAGE */
|
||||
#ifdef IMAGE_LKRN
|
||||
REQUIRE_OBJECT ( bzimage );
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
* You can also choose to distribute this program under the terms of
|
||||
* the Unmodified Binary Distribution Licence (as given in the file
|
||||
* COPYING.UBDL), provided that you have satisfied its requirements.
|
||||
*/
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
#include <config/general.h>
|
||||
|
||||
/** @file
|
||||
*
|
||||
* SBI-specific configuration options
|
||||
*
|
||||
*/
|
||||
|
||||
PROVIDE_REQUIRING_SYMBOL();
|
||||
|
||||
/*
|
||||
* Drag in all requested image types
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef IMAGE_LKRN
|
||||
REQUIRE_OBJECT ( lkrn );
|
||||
#endif
|
||||
|
||||
@@ -30,6 +30,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
#define TIME_NULL
|
||||
|
||||
#define IMAGE_SCRIPT
|
||||
#define IMAGE_LKRN
|
||||
#define IMAGE_GZIP
|
||||
|
||||
#define REBOOT_CMD
|
||||
#define POWEROFF_CMD
|
||||
|
||||
@@ -117,7 +117,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
//#define IMAGE_MULTIBOOT /* MultiBoot image support */
|
||||
//#define IMAGE_PXE /* PXE image support */
|
||||
//#define IMAGE_SCRIPT /* iPXE script image support */
|
||||
//#define IMAGE_BZIMAGE /* Linux bzImage image support */
|
||||
//#define IMAGE_LKRN /* Linux kernel image support */
|
||||
//#define IMAGE_COMBOOT /* SYSLINUX COMBOOT image support */
|
||||
//#define IMAGE_EFI /* EFI image support */
|
||||
//#define IMAGE_SDI /* SDI image support */
|
||||
|
||||
254
src/image/lkrn.c
Normal file
254
src/image/lkrn.c
Normal file
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
* Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
* You can also choose to distribute this program under the terms of
|
||||
* the Unmodified Binary Distribution Licence (as given in the file
|
||||
* COPYING.UBDL), provided that you have satisfied its requirements.
|
||||
*/
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <byteswap.h>
|
||||
#include <ipxe/image.h>
|
||||
#include <ipxe/memmap.h>
|
||||
#include <ipxe/uaccess.h>
|
||||
#include <ipxe/segment.h>
|
||||
#include <ipxe/io.h>
|
||||
#include <ipxe/fdt.h>
|
||||
#include <ipxe/lkrn.h>
|
||||
|
||||
/** @file
|
||||
*
|
||||
* Linux kernel image format
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Parse kernel image
|
||||
*
|
||||
* @v image Kernel image
|
||||
* @v ctx Kernel image context
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int lkrn_parse ( struct image *image, struct lkrn_context *ctx ) {
|
||||
const struct lkrn_header *hdr;
|
||||
|
||||
/* Initialise context */
|
||||
memset ( ctx, 0, sizeof ( *ctx ) );
|
||||
|
||||
/* Read image header */
|
||||
if ( image->len < sizeof ( *hdr ) ) {
|
||||
DBGC ( image, "LKRN %s too short for header\n", image->name );
|
||||
return -ENOEXEC;
|
||||
}
|
||||
hdr = image->data;
|
||||
|
||||
/* Check magic value */
|
||||
if ( hdr->magic != cpu_to_le32 ( LKRN_MAGIC_ARCH ) ) {
|
||||
DBGC ( image, "LKRN %s bad magic value %#08x\n",
|
||||
image->name, le32_to_cpu ( hdr->magic ) );
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
/* Record load offset */
|
||||
ctx->offset = le64_to_cpu ( hdr->text_offset );
|
||||
|
||||
/* Record and check image size */
|
||||
ctx->filesz = image->len;
|
||||
ctx->memsz = le64_to_cpu ( hdr->image_size );
|
||||
if ( ctx->filesz > ctx->memsz ) {
|
||||
DBGC ( image, "LKRN %s invalid image size %#zx/%#zx\n",
|
||||
image->name, ctx->filesz, ctx->memsz );
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locate start of RAM
|
||||
*
|
||||
* @v image Kernel image
|
||||
* @v ctx Kernel image context
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int lkrn_ram ( struct image *image, struct lkrn_context *ctx ) {
|
||||
struct memmap_region region;
|
||||
|
||||
/* Locate start of RAM */
|
||||
for_each_memmap ( ®ion, 0 ) {
|
||||
memmap_dump ( ®ion );
|
||||
if ( ! ( region.flags & MEMMAP_FL_MEMORY ) )
|
||||
continue;
|
||||
ctx->ram = region.addr;
|
||||
DBGC ( image, "LKRN %s RAM starts at %#08lx\n",
|
||||
image->name, ctx->ram );
|
||||
return 0;
|
||||
}
|
||||
|
||||
DBGC ( image, "LKRN %s found no RAM\n", image->name );
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load kernel image
|
||||
*
|
||||
* @v image Kernel image
|
||||
* @v ctx Kernel image context
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int lkrn_load ( struct image *image, struct lkrn_context *ctx ) {
|
||||
void *dest;
|
||||
int rc;
|
||||
|
||||
/* Record entry point */
|
||||
ctx->entry = ( ctx->ram + ctx->offset );
|
||||
dest = phys_to_virt ( ctx->entry );
|
||||
DBGC ( image, "LKRN %s loading to [%#08lx,%#08lx,%#08lx)\n",
|
||||
image->name, ctx->entry, ( ctx->entry + ctx->filesz ),
|
||||
( ctx->entry + ctx->memsz ) );
|
||||
|
||||
/* Prepare segment */
|
||||
if ( ( rc = prep_segment ( dest, ctx->filesz, ctx->memsz ) ) != 0 ) {
|
||||
DBGC ( image, "LKRN %s could not prepare kernel "
|
||||
"segment: %s\n", image->name, strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Copy to segment */
|
||||
memcpy ( dest, image->data, ctx->filesz );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct device tree
|
||||
*
|
||||
* @v image Kernel image
|
||||
* @v ctx Kernel image context
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int lkrn_fdt ( struct image *image, struct lkrn_context *ctx ) {
|
||||
struct fdt_header *fdt;
|
||||
void *dest;
|
||||
size_t len;
|
||||
int rc;
|
||||
|
||||
/* Build device tree (which may change system memory map) */
|
||||
if ( ( rc = fdt_create ( &fdt, image->cmdline ) ) != 0 )
|
||||
goto err_create;
|
||||
len = be32_to_cpu ( fdt->totalsize );
|
||||
|
||||
/* Place device tree after kernel, rounded up to a page boundary */
|
||||
ctx->fdt = ( ( ctx->ram + ctx->offset + ctx->memsz + PAGE_SIZE - 1 ) &
|
||||
~( PAGE_SIZE - 1 ) );
|
||||
dest = phys_to_virt ( ctx->fdt );
|
||||
DBGC ( image, "LKRN %s FDT at [%#08lx,%#08lx)\n",
|
||||
image->name, ctx->fdt, ( ctx->fdt + len ) );
|
||||
|
||||
/*
|
||||
* No further allocations are permitted after this point,
|
||||
* since we are about to start loading segments.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Prepare segment */
|
||||
if ( ( rc = prep_segment ( dest, len, len ) ) != 0 ) {
|
||||
DBGC ( image, "LKRN %s could not prepare FDT segment: %s\n",
|
||||
image->name, strerror ( rc ) );
|
||||
goto err_segment;
|
||||
}
|
||||
|
||||
/* Copy to segment */
|
||||
memcpy ( dest, fdt, len );
|
||||
|
||||
/* Success */
|
||||
rc = 0;
|
||||
|
||||
err_segment:
|
||||
fdt_remove ( fdt );
|
||||
err_create:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute kernel image
|
||||
*
|
||||
* @v image Kernel image
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int lkrn_exec ( struct image *image ) {
|
||||
struct lkrn_context ctx;
|
||||
int rc;
|
||||
|
||||
/* Parse header */
|
||||
if ( ( rc = lkrn_parse ( image, &ctx ) ) != 0 )
|
||||
return rc;
|
||||
|
||||
/* Locate start of RAM */
|
||||
if ( ( rc = lkrn_ram ( image, &ctx ) ) != 0 )
|
||||
return rc;
|
||||
|
||||
/* Create device tree (which may change system memory map) */
|
||||
if ( ( rc = lkrn_fdt ( image, &ctx ) ) != 0 )
|
||||
return rc;
|
||||
|
||||
/* Load kernel image (after all allocations are finished) */
|
||||
if ( ( rc = lkrn_load ( image, &ctx ) ) != 0 )
|
||||
return rc;
|
||||
|
||||
/* Jump to kernel entry point */
|
||||
DBGC ( image, "LKRN %s jumping to kernel at %#08lx\n",
|
||||
image->name, ctx.entry );
|
||||
lkrn_jump ( ctx.entry, ctx.fdt );
|
||||
|
||||
/* There is no way for the image to return, since we provide
|
||||
* no return address.
|
||||
*/
|
||||
assert ( 0 );
|
||||
|
||||
return -ECANCELED; /* -EIMPOSSIBLE */
|
||||
}
|
||||
|
||||
/**
|
||||
* Probe kernel image
|
||||
*
|
||||
* @v image Kernel image
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int lkrn_probe ( struct image *image ) {
|
||||
struct lkrn_context ctx;
|
||||
int rc;
|
||||
|
||||
/* Parse header */
|
||||
if ( ( rc = lkrn_parse ( image, &ctx ) ) != 0 )
|
||||
return rc;
|
||||
|
||||
DBGC ( image, "LKRN %s is a Linux kernel\n", image->name );
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Linux kernel image type */
|
||||
struct image_type lkrn_image_type __image_type ( PROBE_NORMAL ) = {
|
||||
.name = "lkrn",
|
||||
.probe = lkrn_probe,
|
||||
.exec = lkrn_exec,
|
||||
};
|
||||
17
src/include/bits/lkrn.h
Normal file
17
src/include/bits/lkrn.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef _BITS_LKRN_H
|
||||
#define _BITS_LKRN_H
|
||||
|
||||
/** @file
|
||||
*
|
||||
* Dummy architecture-specific Linux kernel image invocation
|
||||
*
|
||||
* This file is included only if the architecture does not provide its
|
||||
* own version of this file.
|
||||
*
|
||||
*/
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
#define LKRN_MAGIC_ARCH 0
|
||||
|
||||
#endif /* _BITS_LKRN_H */
|
||||
@@ -328,6 +328,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
#define ERRFILE_zlib ( ERRFILE_IMAGE | 0x000b0000 )
|
||||
#define ERRFILE_gzip ( ERRFILE_IMAGE | 0x000c0000 )
|
||||
#define ERRFILE_efi_siglist ( ERRFILE_IMAGE | 0x000d0000 )
|
||||
#define ERRFILE_lkrn ( ERRFILE_IMAGE | 0x000e0000 )
|
||||
|
||||
#define ERRFILE_asn1 ( ERRFILE_OTHER | 0x00000000 )
|
||||
#define ERRFILE_chap ( ERRFILE_OTHER | 0x00010000 )
|
||||
|
||||
69
src/include/ipxe/lkrn.h
Normal file
69
src/include/ipxe/lkrn.h
Normal file
@@ -0,0 +1,69 @@
|
||||
#ifndef _IPXE_LKRN_H
|
||||
#define _IPXE_LKRN_H
|
||||
|
||||
/** @file
|
||||
*
|
||||
* Linux kernel images
|
||||
*
|
||||
*/
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/** Kernel image header */
|
||||
struct lkrn_header {
|
||||
/** Executable code */
|
||||
uint32_t code[2];
|
||||
/** Image load offset */
|
||||
uint64_t text_offset;
|
||||
/** Image size */
|
||||
uint64_t image_size;
|
||||
/** Flags */
|
||||
uint64_t flags;
|
||||
/** Reserved */
|
||||
uint8_t reserved_a[24];
|
||||
/** Magic */
|
||||
uint32_t magic;
|
||||
/** Reserved */
|
||||
uint8_t reserved_b[4];
|
||||
} __attribute__ (( packed ));
|
||||
|
||||
/** Kernel magic value */
|
||||
#define LKRN_MAGIC( a, b, c, d ) \
|
||||
( ( (a) << 0 ) | ( (b) << 8 ) | ( (c) << 16 ) | ( (d) << 24 ) )
|
||||
|
||||
/** Kernel magic value for AArch64 */
|
||||
#define LKRN_MAGIC_AARCH64 LKRN_MAGIC ( 'A', 'R', 'M', 0x64 )
|
||||
|
||||
/** Kernel magic value for RISC-V */
|
||||
#define LKRN_MAGIC_RISCV LKRN_MAGIC ( 'R', 'S', 'C', 0x05 )
|
||||
|
||||
/** Kernel image context */
|
||||
struct lkrn_context {
|
||||
/** Load offset */
|
||||
size_t offset;
|
||||
/** File size */
|
||||
size_t filesz;
|
||||
/** Memory size */
|
||||
size_t memsz;
|
||||
|
||||
/** Start of RAM */
|
||||
physaddr_t ram;
|
||||
/** Entry point */
|
||||
physaddr_t entry;
|
||||
/** Device tree */
|
||||
physaddr_t fdt;
|
||||
};
|
||||
|
||||
#include <bits/lkrn.h>
|
||||
|
||||
/**
|
||||
* Jump to kernel entry point
|
||||
*
|
||||
* @v entry Kernel entry point
|
||||
* @v fdt Device tree
|
||||
*/
|
||||
void lkrn_jump ( physaddr_t entry, physaddr_t fdt );
|
||||
|
||||
#endif /* _IPXE_LKRN_H */
|
||||
Reference in New Issue
Block a user