Files
xen/xen/common/device-tree/kernel.c
T
Oleksii Kurochko a7ee73f581 xen/common: dom0less: introduce common kernel.c
The following functions don't have arch specific things so it is moved to
common:
- kernel_prboe()
- kernel_load()
- output_length()

Functions necessary for dom0less are only moved.

The following changes are done:
- Swap __init and return type of kernel_decompress() function to be
  consistent with defintions of functions in other files. The same
  for output_length().
- Wrap by "ifdef CONFIG_ARM" the call of kernel_uimage_probe() in
  kernel_probe() as uImage isn't really used nowadays thereby leave
  kernel_uimage_probe() call here just for compatability with Arm code.
- Introduce kernel_zimage_probe() to cover the case that arch can have
  different zimage header.
- Add ASSERT() for kernel_load() to check that it argument isn't NULL.
- Make kernel_uimage_probe() non-static in Arm's code as it is used in
  common/kernel.c.

Introduce CONFIG_DOMAIN_BUILD_HELPERS to not provide stubs for archs
which don't provide enough functionality to enable it.
Select CONFIG_DOMAIN_BUILD_HELPERS for CONFIG_ARM as only Arm supports
it, at the moment.

Signed-off-by: Oleksii Kurochko <oleksii.kurochko@gmail.com>
Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>
2025-05-05 14:38:59 -07:00

243 lines
6.8 KiB
C

#include <xen/bootfdt.h>
#include <xen/device_tree.h>
#include <xen/fdt-kernel.h>
#include <xen/errno.h>
#include <xen/gunzip.h>
#include <xen/init.h>
#include <xen/lib.h>
#include <xen/mm.h>
#include <xen/pfn.h>
#include <xen/sched.h>
#include <xen/types.h>
#include <xen/vmap.h>
#include <asm/page.h>
#include <asm/setup.h>
static uint32_t __init output_length(char *image, unsigned long image_len)
{
return *(uint32_t *)&image[image_len - 4];
}
int __init kernel_decompress(struct bootmodule *mod, uint32_t offset)
{
char *output, *input;
char magic[2];
int rc;
unsigned int kernel_order_out;
paddr_t output_size;
struct page_info *pages;
mfn_t mfn;
int i;
paddr_t addr = mod->start;
paddr_t size = mod->size;
if ( size < offset )
return -EINVAL;
/*
* It might be that gzip header does not appear at the start address
* (e.g. in case of compressed uImage) so take into account offset to
* gzip header.
*/
addr += offset;
size -= offset;
if ( size < 2 )
return -EINVAL;
copy_from_paddr(magic, addr, sizeof(magic));
/* only gzip is supported */
if ( !gzip_check(magic, size) )
return -EINVAL;
input = ioremap_cache(addr, size);
if ( input == NULL )
return -EFAULT;
output_size = output_length(input, size);
kernel_order_out = get_order_from_bytes(output_size);
pages = alloc_domheap_pages(NULL, kernel_order_out, 0);
if ( pages == NULL )
{
iounmap(input);
return -ENOMEM;
}
mfn = page_to_mfn(pages);
output = vmap_contig(mfn, 1 << kernel_order_out);
rc = perform_gunzip(output, input, size);
clean_dcache_va_range(output, output_size);
iounmap(input);
vunmap(output);
if ( rc )
{
free_domheap_pages(pages, kernel_order_out);
return rc;
}
mod->start = page_to_maddr(pages);
mod->size = output_size;
/*
* Need to free pages after output_size here because they won't be
* freed by discard_initial_modules
*/
i = PFN_UP(output_size);
for ( ; i < (1 << kernel_order_out); i++ )
free_domheap_page(pages + i);
/*
* When using static heap feature, don't give bootmodules memory back to
* the heap allocator
*/
if ( using_static_heap )
return 0;
/*
* When freeing the kernel, we need to pass the module start address and
* size as they were before taking an offset to gzip header into account,
* so that the entire region will be freed.
*/
addr -= offset;
size += offset;
/*
* Free the original kernel, update the pointers to the
* decompressed kernel
*/
fw_unreserved_regions(addr, addr + size, init_domheap_pages, 0);
return 0;
}
int __init kernel_probe(struct kernel_info *info,
const struct dt_device_node *domain)
{
struct bootmodule *mod = NULL;
struct bootcmdline *cmd = NULL;
struct dt_device_node *node;
u64 kernel_addr, initrd_addr, dtb_addr, size;
int rc;
/*
* We need to initialize start to 0. This field may be populated during
* kernel_xxx_probe() if the image has a fixed entry point (for e.g.
* uimage.ep).
* We will use this to determine if the image has a fixed entry point or
* the load address should be used as the start address.
*/
info->entry = 0;
/* domain is NULL only for the hardware domain */
if ( domain == NULL )
{
ASSERT(is_hardware_domain(info->d));
mod = boot_module_find_by_kind(BOOTMOD_KERNEL);
info->kernel_bootmodule = mod;
info->initrd_bootmodule = boot_module_find_by_kind(BOOTMOD_RAMDISK);
cmd = boot_cmdline_find_by_kind(BOOTMOD_KERNEL);
if ( cmd )
info->cmdline = &cmd->cmdline[0];
}
else
{
const char *name = NULL;
dt_for_each_child_node(domain, node)
{
if ( dt_device_is_compatible(node, "multiboot,kernel") )
{
u32 len;
const __be32 *val;
val = dt_get_property(node, "reg", &len);
dt_get_range(&val, node, &kernel_addr, &size);
mod = boot_module_find_by_addr_and_kind(
BOOTMOD_KERNEL, kernel_addr);
info->kernel_bootmodule = mod;
}
else if ( dt_device_is_compatible(node, "multiboot,ramdisk") )
{
u32 len;
const __be32 *val;
val = dt_get_property(node, "reg", &len);
dt_get_range(&val, node, &initrd_addr, &size);
info->initrd_bootmodule = boot_module_find_by_addr_and_kind(
BOOTMOD_RAMDISK, initrd_addr);
}
else if ( dt_device_is_compatible(node, "multiboot,device-tree") )
{
uint32_t len;
const __be32 *val;
val = dt_get_property(node, "reg", &len);
if ( val == NULL )
continue;
dt_get_range(&val, node, &dtb_addr, &size);
info->dtb_bootmodule = boot_module_find_by_addr_and_kind(
BOOTMOD_GUEST_DTB, dtb_addr);
}
else
continue;
}
name = dt_node_name(domain);
cmd = boot_cmdline_find_by_name(name);
if ( cmd )
info->cmdline = &cmd->cmdline[0];
}
if ( !mod || !mod->size )
{
printk(XENLOG_ERR "Missing kernel boot module?\n");
return -ENOENT;
}
printk("Loading %pd kernel from boot module @ %"PRIpaddr"\n",
info->d, info->kernel_bootmodule->start);
if ( info->initrd_bootmodule )
printk("Loading ramdisk from boot module @ %"PRIpaddr"\n",
info->initrd_bootmodule->start);
/*
* uImage isn't really used nowadays thereby leave kernel_uimage_probe()
* call here just for compatability with Arm code.
*/
#ifdef CONFIG_ARM
/*
* uImage header always appears at the top of the image (even compressed),
* so it needs to be probed first. Note that in case of compressed uImage,
* kernel_decompress is called from kernel_uimage_probe making the function
* self-containing (i.e. fall through only in case of a header not found).
*/
rc = kernel_uimage_probe(info, mod);
if ( rc != -ENOENT )
return rc;
#endif
/*
* If it is a gzip'ed image, 32bit or 64bit, uncompress it.
* At this point, gzip header appears (if at all) at the top of the image,
* so pass 0 as an offset.
*/
rc = kernel_decompress(mod, 0);
if ( rc && rc != -EINVAL )
return rc;
rc = kernel_zimage_probe(info, mod->start, mod->size);
return rc;
}
void __init kernel_load(struct kernel_info *info)
{
ASSERT(info && info->load);
info->load(info);
}