mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: "H. Peter Anvin" <hpa@zytor.com>
To: "Jason A. Donenfeld" <Jason@zx2c4.com>
Cc: pbonzini@redhat.com, ebiggers@kernel.org, x86@kernel.org,
	linux-kernel@vger.kernel.org, qemu-devel@nongnu.org,
	ardb@kernel.org, kraxel@redhat.com, bp@alien8.de,
	philmd@linaro.org
Subject: Re: [PATCH qemu] x86: don't let decompressed kernel image clobber setup_data
Date: Wed, 28 Dec 2022 18:13:34 -0800	[thread overview]
Message-ID: <0baf674b-c7e7-a010-375d-ea1132495c44@zytor.com> (raw)
In-Reply-To: <9188EEE9-2759-4389-B39E-0FEBBA3FA57D@zytor.com>

[-- Attachment #1: Type: text/plain, Size: 2251 bytes --]

On 12/28/22 15:58, H. Peter Anvin wrote:
> On December 28, 2022 8:57:54 AM PST, "Jason A. Donenfeld" <Jason@zx2c4.com> wrote:
>> HELLO H. PETER ANVIN,
>> E
>> L
>> L
>> O
>>
>> On Wed, Dec 28, 2022 at 05:30:30PM +0100, Jason A. Donenfeld wrote:
>>>> Fix looks good, glad you figured out the problem.
>>>
>>> I mean, kind of. The solution here sucks, especially given that in the
>>> worst case, setup_data just gets dropped. I'm half inclined to consider
>>> this a kernel bug instead, and add some code to relocate setup_data
>>> prior to decompression, and then fix up all the links. It seems like
>>> this would be a lot more robust.
>>>
>>> I just wish the people who wrote this stuff would chime in. I've had
>>> x86@kernel.org CC'd but so far, no input from them.
>>
>> Apparently you are the x86 boot guru. What do you want to happen here?
>> Your input would be very instrumental.
>>
>> Jason
> 
> Hi!
> 
> Glad you asked.
> 
> So the kernel load addresses are parameterized in the kernel image
> setup header. One of the things that are so parameterized are the
> size and possible realignment of the kernel image in memory.
> 
> I'm very confused where you are getting the 64 MB number from. There
> should not be any such limitation.
> 
> In general, setup_data should be able to go anywhere the initrd can
> go, and so is subject to the same address cap (896 MB for old
> kernels, 4 GB on newer ones; this address too is enumerated in the
> header.)
> 
> If you want to put setup_data above 4 GB, it *should* be ok if and
> only if the kernel supports loading the initrd high, too (again,
> enumerated in the header.
> 
> TL;DR: put setup_data where you put the initrd (before or after
> doesn't matter.)
> 
> To be maximally conservative, link the setup_data list in order from
> lowest to highest address; currently there is no such item of
> relevance, but in the future there may be setup_data items needed by
> the BIOS part of the bootstrap in which case they would have to be <
> 1 MB and precede any items > 1 MB for obvious reasons. That being
> said, with BIOS dying it is not all that likely that such entries
> will ever be needed.
> 

So let me try for an algorithm. Attached as a text file to avoid line 
break damage.

	-hpa

[-- Attachment #2: kernel-data-addresses.txt --]
[-- Type: text/plain, Size: 5844 bytes --]

Here is an attempted description with pseudo-C code:

First of all, take a 4K page of memory and *initialize it to zero*.
{
    #include <asm/bootparam.h>	/* From the uapi kernel sources */

    /* Allocated somewhere in your code... */
    extern unsigned char *kernel_image;		/* Kernel file */
    extern struct boot_params *boot_params;	/* 4K buffer */
    extern uint32_t kernel_image_size;		/* Size of kernel file */

    /* Callbacks into your code */
    extern bool is_bios_boot(void);
    extern uint32_t end_of_low_memory(void); /* For BIOS boot */
    /*
     * This MUST return an alignment address between start_address
     * and max_address...
     */
    extern uint64_t maybe_relocate_kernel(uint64_t start_address,
	  uint64_t max_address, uint32_t alignment);

    /*
     * Convenience pointer into the kernel image; modifications
     * done here should be reflected in the loaded kernel image
     */
    struct setup_header * const kernel_setup_header =
	(struct setup_header *)(kernel_image + 0x1f1);

    /* Initialize boot_params to zero!!! */
    memset(boot_params, 0, sizeof *boot_params);
}

Copy the setup header starting at file offset 0x1f1 to offset 0x1f1
into that page:
{
    int setup_length =
	kernel_setup_header->header == 0x53726448
	? (kernel_setup_header->jump >> 8) + 17 : 15;

    memcpy(&boot_params->hdr, kernel_setup_header, setup_length);
}

Now you can compute values including ones are omitted by older kernels:
{
    /*
     * Split between the part of the kernel to be loaded into
     * low memory (for 16-bit boot, otherwise it can be safely
     * omitted) and the part to be loaded into high memory.
     */
    if (!boot_params->hdr.setup_sects)
	boot_param->hdr.setup_sects = 4;

    int high_kernel_start = (boot_param->hdr.setup_sects+1) << 9;

    /*
     * Highest permitted address for the high part of the kernel image,
     * initrd, command line (*except for 16-bit boot*), and setup_data
     *
     * max_initrd_addr here is exclusive
     */
    uint64_t max_initrd_addr = (uint64_t)boot_params->hdr.initrd_addr_max + 1;
    if (boot_params->hdr.version < 0x0200)
	max_initrd_addr = 0;	/* No initrd supported */
    else if (boot_params->hdr.version < 0x0203)
	max_initrd_addr = 0x38000000;
    else if (boot_params->hdr.version >= 0x020c &&
	       (boot_params->hdr.xloadflags & XLF_CAN_BE_LOADED_ABOVE_4G))
	max_initrd_addr = (uint64_t)1 << 52; /* Architecture-imposed limit */

    /*
     * Maximum command line size *including terminating null*
     */
    unsigned int cmdline_size;
    if (boot_params->hdr.version < 0x0200)
	cmdline_size = 0;	/* No command line supported */
    else if (boot_params->hdr.version < 0x0206)
	boot_params->hdr.cmdline_size = 256;
    else
	boot_params->hdr.cmdline_size + 1;

    /* Command line size including terminating null */

    /*
     * Load addresses for the low and high kernels, respectively
     */
    uint32_t low_kernel_address;
    uint64_t cmdline_addr;	/* Address to load the command line */

    if (is_bios_boot()) {
	if (!(boot_params->hdr.loadflags & LOADED_HIGH)) {
	    low_kernel_address = 0x90000;
	} else {
	    /*
	     * Recommended to be the lowest available address between
	     * 0x10000 and 0x90000
	     */
	    low_kernel_address = preferred_low_kernel_address();
	}

	uint32_t lowkernel_max;

	lowkernel_max = low_kernel_address + 0x10000;
	if (boot_params.hdr.version >= 0x0202)
	    lowkernel_max += (cmdline_size + 15) & ~15;

	/*
	 * end_of_low_memory() is usually given by *(uint8_t *)0x413 << 10
	 */
	if (lowkernel_max > end_of_low_memory())
	    lowkernel_max = end_of_low_memory();

	cmdline_addr = (lowkernel_max - cmdline_size) & ~15;
	if (boot_params->hdr.version >= 0x0202)
	    kernel_setup_header->cmd_line_ptr = cmdline_addr;
	else if (boot_params->hdr.version >= 0x0200)
	    kernel_setup_header->setup_move_size =
		lowkernel_max - low_kernel_address;

	if (boot_params.hdr.version >= 0x0201) {
	    kernel_setup_header->heap_end_ptr
		= cmdline_addr - low_kernel_address - 0x0200;
	    kernel_setup_header->loadflags |= CAN_USE_HEAP;
	}
    } else {
	low_kernel_address = 0;	/* Not used for non-BIOS boot */
	cmdline_addr = 0;	/* Not assigned yet */
    }

    /*
     * Default load address for the high kernel, and if it can be relocated
     */
    uint64_t high_kernel_address;
    uint32_t high_kernel_size;	/* The amount of memory the high kernel needs */
    bool relocatable_kernel = false;
    uint32_t high_kernel_alignment = 0x400000; /* Kernel runtime alignment */

    if (!(boot_params->hdr.loadflags & LOADED_HIGH)) {
	high_kernel_address = 0x10000;
    } else {
	if (boot_params->hdr.version >= 0x020a)
	    high_kernel_address = boot_params->hdr.pref_address;
	else
	    high_kernel_address = 0x100000;

	if (boot_params->hdr.version >= 0x0205 &&
	    boot_params->hdr.relocatable_kernel) {
	    relocatable_kernel = true;
	    high_kernel_alignment = boot_params->hdr.kernel_alignment;
	}
    }

    /*
     * Linear memory area needed by the kernel
     */
    uint32_t kernel_mem_size;
    if (boot_params->hdr.version >= 0x020a)
	kernel_mem_size = boot_params->hdr.init_size;
    else
	kernel_mem_size = kernel_image_size << 2; /* Pure guesswork... */

    /* Relocate the kernel load address if desired */
    if (relocatable_kernel) {
	high_kernel_address =
	    maybe_relocate_kernel(high_kernel_address,
				  max_initrd_addr - kernel_mem_size,
				  high_kernel_aligment);
    }

    /* Adjust for possible internal kernel realigment */
    kernel_mem_size += (-high_kernel_address) & (high_kernel_alignment - 1);

    /*
     * Determine the minimum safe address for loading initrd, setup_data,
     * and, if cmdline_addr == 0 (i.e. !is_bios_boot()), the command line.
     */
    uint64_t min_initrd_addr = high_kernel_address + kernel_mem_size;
}

  reply	other threads:[~2022-12-29  2:14 UTC|newest]

Thread overview: 46+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-12-28 14:38 Jason A. Donenfeld
2022-12-28 16:02 ` Philippe Mathieu-Daudé
2022-12-28 16:30   ` Jason A. Donenfeld
2022-12-28 16:57     ` Jason A. Donenfeld
2022-12-28 23:58       ` H. Peter Anvin
2022-12-29  2:13         ` H. Peter Anvin [this message]
2022-12-29  2:31         ` Jason A. Donenfeld
2022-12-29  7:28           ` Philippe Mathieu-Daudé
2022-12-29  7:30           ` H. Peter Anvin
2022-12-29  7:31           ` H. Peter Anvin
2022-12-29 12:47             ` Borislav Petkov
2022-12-30 15:54               ` Jason A. Donenfeld
2022-12-30 17:01                 ` Borislav Petkov
2022-12-30 17:07                   ` Jason A. Donenfeld
2022-12-30 19:54                     ` Borislav Petkov
2022-12-30 21:58                       ` H. Peter Anvin
2022-12-30 22:10                         ` Jason A. Donenfeld
2022-12-31  1:06                           ` H. Peter Anvin
2022-12-31  1:14                             ` H. Peter Anvin
2022-12-31 12:55                             ` Jason A. Donenfeld
2022-12-31 13:40                             ` Borislav Petkov
2022-12-31 13:44                               ` Jason A. Donenfeld
2022-12-31 13:48                                 ` Borislav Petkov
2022-12-31 13:51                                   ` Jason A. Donenfeld
2022-12-31 14:24                                     ` Borislav Petkov
2022-12-31 18:22                                       ` Jason A. Donenfeld
2022-12-31 19:00                                         ` Borislav Petkov
2023-01-01  3:21                                           ` H. Peter Anvin
2023-01-01  3:31                                             ` H. Peter Anvin
2023-01-02  6:01                                               ` Borislav Petkov
2023-01-02  6:17                                                 ` Borislav Petkov
2023-01-02  9:32                                                   ` Ard Biesheuvel
2023-01-02 13:36                                                     ` Borislav Petkov
2023-01-02 15:03                                                       ` Ard Biesheuvel
2023-01-02  5:50                                             ` Borislav Petkov
2023-01-01  4:33                                         ` H. Peter Anvin
2023-01-01  4:55                                           ` Mika Penttilä
2023-01-01  5:13                                             ` H. Peter Anvin
2022-12-30 15:59             ` Jason A. Donenfeld
2022-12-30 16:21               ` Jason A. Donenfeld
2022-12-30 19:13               ` H. Peter Anvin
2022-12-31  9:48               ` Borislav Petkov
2022-12-31 12:54                 ` Jason A. Donenfeld
2022-12-31 13:35                   ` Borislav Petkov
2022-12-31 13:42                     ` Jason A. Donenfeld
2022-12-30 18:30 ` Jason A. Donenfeld

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=0baf674b-c7e7-a010-375d-ea1132495c44@zytor.com \
    --to=hpa@zytor.com \
    --cc=Jason@zx2c4.com \
    --cc=ardb@kernel.org \
    --cc=bp@alien8.de \
    --cc=ebiggers@kernel.org \
    --cc=kraxel@redhat.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=pbonzini@redhat.com \
    --cc=philmd@linaro.org \
    --cc=qemu-devel@nongnu.org \
    --cc=x86@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®