diff mbox series

[2/3] xen/ppc: Relocate kernel to physical address 0 on boot

Message ID 0802fad2743526da4fe49f0225e14161464f192e.1690582001.git.sanastasio@raptorengineering.com (mailing list archive)
State Superseded
Headers show
Series xen/ppc: Add early Radix MMU support | expand

Commit Message

Shawn Anastasio July 28, 2023, 10:21 p.m. UTC
Introduce a small assembly loop in `start` to copy the kernel to
physical address 0 before continuing. This ensures that the physical
address lines up with XEN_VIRT_START (0xc000000000000000) and allows us
to identity map the kernel when the MMU is set up in the next patch.

We are also able to start execution at XEN_VIRT_START after the copy
since hardware will ignore the top 4 address bits when operating in Real
Mode (MMU off).

Signed-off-by: Shawn Anastasio <sanastasio@raptorengineering.com>
---
 xen/arch/ppc/ppc64/head.S | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

Comments

Jan Beulich July 31, 2023, 3:46 p.m. UTC | #1
On 29.07.2023 00:21, Shawn Anastasio wrote:
> Introduce a small assembly loop in `start` to copy the kernel to
> physical address 0 before continuing. This ensures that the physical
> address lines up with XEN_VIRT_START (0xc000000000000000) and allows us
> to identity map the kernel when the MMU is set up in the next patch.

So PPC guarantees there's always a reasonable amount of memory at 0,
and that's available for use?

> --- a/xen/arch/ppc/ppc64/head.S
> +++ b/xen/arch/ppc/ppc64/head.S
> @@ -18,6 +18,33 @@ ENTRY(start)
>      addis   %r2, %r12, .TOC.-1b@ha
>      addi    %r2, %r2, .TOC.-1b@l
>  
> +    /*
> +     * Copy Xen to physical address zero and jump to XEN_VIRT_START
> +     * (0xc000000000000000). This works because the hardware will ignore the top
> +     * four address bits when the MMU is off.
> +     */
> +    LOAD_REG_ADDR(%r1, start)

I think you really mean _start here (which is missing from the linker
script), not start. See also Andrew's recent related RISC-V change.

> +    LOAD_IMM64(%r12, XEN_VIRT_START)
> +
> +    /* If we're at the correct address, skip copy */
> +    cmpld   %r1, %r12
> +    beq     .L_correct_address

Can this ever be the case, especially with the MMU-off behavior you
describe in the comment above? Wouldn't you need to ignore the top
four bits in the comparison?

> +    /* Copy bytes until _end */
> +    LOAD_REG_ADDR(%r11, _end)
> +    addi    %r1, %r1, -8
> +    li      %r13, -8
> +.L_copy_xen:
> +    ldu     %r10, 8(%r1)
> +    stdu    %r10, 8(%r13)
> +    cmpld   %r1, %r11
> +    blt     .L_copy_xen
> +
> +    /* Jump to XEN_VIRT_START */
> +    mtctr   %r12
> +    bctr
> +.L_correct_address:

Can the two regions potentially overlap? Looking at the ELF header
it's not clear to me what guarantees there are that this can't
happen.

Jan
Shawn Anastasio July 31, 2023, 11:37 p.m. UTC | #2
On 7/31/23 10:46 AM, Jan Beulich wrote:
> On 29.07.2023 00:21, Shawn Anastasio wrote:
>> Introduce a small assembly loop in `start` to copy the kernel to
>> physical address 0 before continuing. This ensures that the physical
>> address lines up with XEN_VIRT_START (0xc000000000000000) and allows us
>> to identity map the kernel when the MMU is set up in the next patch.
> 
> So PPC guarantees there's always a reasonable amount of memory at 0,
> and that's available for use?

Both Linux and FreeBSD rely on this being the case, so it's essentially
a de facto standard, though I'm not aware of any specification that
guarantees it.

>> --- a/xen/arch/ppc/ppc64/head.S
>> +++ b/xen/arch/ppc/ppc64/head.S
>> @@ -18,6 +18,33 @@ ENTRY(start)
>>      addis   %r2, %r12, .TOC.-1b@ha
>>      addi    %r2, %r2, .TOC.-1b@l
>>  
>> +    /*
>> +     * Copy Xen to physical address zero and jump to XEN_VIRT_START
>> +     * (0xc000000000000000). This works because the hardware will ignore the top
>> +     * four address bits when the MMU is off.
>> +     */
>> +    LOAD_REG_ADDR(%r1, start)
> 
> I think you really mean _start here (which is missing from the linker
> script),

The PIC patch series fixes the missing _start definition in the linker
script. In the cover letter of v2 I'll add a clear note that this series
is based on that one.

> not start. See also Andrew's recent related RISC-V change.

Good point. In practice this worked because the `start` function was the
first thing in the first section of the linker script, but of course
using _start here is more correct.

> 
>> +    LOAD_IMM64(%r12, XEN_VIRT_START)
>> +
>> +    /* If we're at the correct address, skip copy */
>> +    cmpld   %r1, %r12
>> +    beq     .L_correct_address
> 
> Can this ever be the case, especially with the MMU-off behavior you
> describe in the comment above? Wouldn't you need to ignore the top
> four bits in the comparison?

It will always be the case after the code jumps to XEN_VIRT_START after
the copy takes place. I could have it jump past the copy loop entirely,
but then I'd need to duplicate the TOC setup.

>> +    /* Copy bytes until _end */
>> +    LOAD_REG_ADDR(%r11, _end)
>> +    addi    %r1, %r1, -8
>> +    li      %r13, -8
>> +.L_copy_xen:
>> +    ldu     %r10, 8(%r1)
>> +    stdu    %r10, 8(%r13)
>> +    cmpld   %r1, %r11
>> +    blt     .L_copy_xen
>> +
>> +    /* Jump to XEN_VIRT_START */
>> +    mtctr   %r12
>> +    bctr
>> +.L_correct_address:
> 
> Can the two regions potentially overlap? Looking at the ELF header
> it's not clear to me what guarantees there are that this can't
> happen.

As I understand it, any bootloader that placed the kernel at a low
enough address for this to be an issue wouldn't be able to boot Linux or
FreeBSD, so in practice it's a safe bet that this won't be the case.

> Jan

Thanks,
Shawn
Jan Beulich Aug. 1, 2023, 6:08 a.m. UTC | #3
On 01.08.2023 01:37, Shawn Anastasio wrote:
> On 7/31/23 10:46 AM, Jan Beulich wrote:
>> On 29.07.2023 00:21, Shawn Anastasio wrote:
>>> +    /* If we're at the correct address, skip copy */
>>> +    cmpld   %r1, %r12
>>> +    beq     .L_correct_address
>>
>> Can this ever be the case, especially with the MMU-off behavior you
>> describe in the comment above? Wouldn't you need to ignore the top
>> four bits in the comparison?
> 
> It will always be the case after the code jumps to XEN_VIRT_START after
> the copy takes place.

Well, of course.

> I could have it jump past the copy loop entirely,
> but then I'd need to duplicate the TOC setup.

I don't think I understand this part of your reply: .L_correct_address
_is_ past the copy loop.

>>> +    /* Copy bytes until _end */
>>> +    LOAD_REG_ADDR(%r11, _end)
>>> +    addi    %r1, %r1, -8
>>> +    li      %r13, -8
>>> +.L_copy_xen:
>>> +    ldu     %r10, 8(%r1)
>>> +    stdu    %r10, 8(%r13)
>>> +    cmpld   %r1, %r11
>>> +    blt     .L_copy_xen
>>> +
>>> +    /* Jump to XEN_VIRT_START */
>>> +    mtctr   %r12
>>> +    bctr
>>> +.L_correct_address:
>>
>> Can the two regions potentially overlap? Looking at the ELF header
>> it's not clear to me what guarantees there are that this can't
>> happen.
> 
> As I understand it, any bootloader that placed the kernel at a low
> enough address for this to be an issue wouldn't be able to boot Linux or
> FreeBSD, so in practice it's a safe bet that this won't be the case.

Fair enough then.

Jan
Shawn Anastasio Aug. 3, 2023, 5:16 p.m. UTC | #4
On 8/1/23 1:08 AM, Jan Beulich wrote:
> On 01.08.2023 01:37, Shawn Anastasio wrote:
>> On 7/31/23 10:46 AM, Jan Beulich wrote:
>>> On 29.07.2023 00:21, Shawn Anastasio wrote:
>>>> +    /* If we're at the correct address, skip copy */
>>>> +    cmpld   %r1, %r12
>>>> +    beq     .L_correct_address
>>>
>>> Can this ever be the case, especially with the MMU-off behavior you
>>> describe in the comment above? Wouldn't you need to ignore the top
>>> four bits in the comparison?
>>
>> It will always be the case after the code jumps to XEN_VIRT_START after
>> the copy takes place.
> 
> Well, of course.
> 
>> I could have it jump past the copy loop entirely,
>> but then I'd need to duplicate the TOC setup.
> 
> I don't think I understand this part of your reply: .L_correct_address
> _is_ past the copy loop.

Sorry, let me elaborate. I meant that I could have the end of the copy
loop (the mtctr + btctr preceeding .L_correct_address) jump to
(XEN_VIRT_START + .L_correct_address) as opposed to XEN_VIRT_START so
that the address comparison you originally commented on wouldn't be hit
again.

This would mean adding another TOC setup block at .L_correct_address,
though, since we'd be skipping over the one at the beginning of the
routine and the TOC needs to be reconfigured after the relocation.

>>>> +    /* Copy bytes until _end */
>>>> +    LOAD_REG_ADDR(%r11, _end)
>>>> +    addi    %r1, %r1, -8
>>>> +    li      %r13, -8
>>>> +.L_copy_xen:
>>>> +    ldu     %r10, 8(%r1)
>>>> +    stdu    %r10, 8(%r13)
>>>> +    cmpld   %r1, %r11
>>>> +    blt     .L_copy_xen
>>>> +
>>>> +    /* Jump to XEN_VIRT_START */
>>>> +    mtctr   %r12
>>>> +    bctr
>>>> +.L_correct_address:
>>>
>>> Can the two regions potentially overlap? Looking at the ELF header
>>> it's not clear to me what guarantees there are that this can't
>>> happen.
>>
>> As I understand it, any bootloader that placed the kernel at a low
>> enough address for this to be an issue wouldn't be able to boot Linux or
>> FreeBSD, so in practice it's a safe bet that this won't be the case.
> 
> Fair enough then.
> 
> Jan

Thanks,
Shawn
diff mbox series

Patch

diff --git a/xen/arch/ppc/ppc64/head.S b/xen/arch/ppc/ppc64/head.S
index 5ac2dad2ee..beff8257fa 100644
--- a/xen/arch/ppc/ppc64/head.S
+++ b/xen/arch/ppc/ppc64/head.S
@@ -18,6 +18,33 @@  ENTRY(start)
     addis   %r2, %r12, .TOC.-1b@ha
     addi    %r2, %r2, .TOC.-1b@l
 
+    /*
+     * Copy Xen to physical address zero and jump to XEN_VIRT_START
+     * (0xc000000000000000). This works because the hardware will ignore the top
+     * four address bits when the MMU is off.
+     */
+    LOAD_REG_ADDR(%r1, start)
+    LOAD_IMM64(%r12, XEN_VIRT_START)
+
+    /* If we're at the correct address, skip copy */
+    cmpld   %r1, %r12
+    beq     .L_correct_address
+
+    /* Copy bytes until _end */
+    LOAD_REG_ADDR(%r11, _end)
+    addi    %r1, %r1, -8
+    li      %r13, -8
+.L_copy_xen:
+    ldu     %r10, 8(%r1)
+    stdu    %r10, 8(%r13)
+    cmpld   %r1, %r11
+    blt     .L_copy_xen
+
+    /* Jump to XEN_VIRT_START */
+    mtctr   %r12
+    bctr
+.L_correct_address:
+
     /* set up the initial stack */
     LOAD_REG_ADDR(%r1, cpu0_boot_stack)
     li      %r11, 0