diff mbox series

lib: sbi_illegal_insn: Emulate c.li

Message ID 20231109082540.52144-1-wxjstz@126.com
State Not Applicable
Headers show
Series lib: sbi_illegal_insn: Emulate c.li | expand

Commit Message

Xiang W Nov. 9, 2023, 8:25 a.m. UTC
The Linux kernel RISC-V image format allows that UEFI Images can also
be booted by non-UEFI firmware. However for that to work, the PE/Image
combo requires that 'MZ' is a valid instruction. On RISC-V, 'MZ' is
only a valid instruction if the hardware is C capable [1]. So add
Emulate c.li

Signed-off-by: Björn Töpel <bjorn@rivosinc.com>
Signed-off-by: Xiang W <wxjstz@126.com>
---
 lib/sbi/sbi_illegal_insn.c | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)

Comments

Inochi Amaoto Nov. 9, 2023, 9:35 a.m. UTC | #1
>The Linux kernel RISC-V image format allows that UEFI Images can also
>be booted by non-UEFI firmware. However for that to work, the PE/Image
>combo requires that 'MZ' is a valid instruction. On RISC-V, 'MZ' is
>only a valid instruction if the hardware is C capable [1]. So add
>Emulate c.li
>
>Signed-off-by: Björn Töpel <bjorn@rivosinc.com>
>Signed-off-by: Xiang W <wxjstz@126.com>
>---
> lib/sbi/sbi_illegal_insn.c | 18 +++++++++++++++++-
> 1 file changed, 17 insertions(+), 1 deletion(-)
>
>diff --git a/lib/sbi/sbi_illegal_insn.c b/lib/sbi/sbi_illegal_insn.c
>index 2be4757..4ab10f4 100644
>--- a/lib/sbi/sbi_illegal_insn.c
>+++ b/lib/sbi/sbi_illegal_insn.c
>@@ -102,6 +102,22 @@ static int system_opcode_insn(ulong insn, struct sbi_trap_regs *regs)
> 	return 0;
> }
>
>+static int compressed_insn(ulong insn, struct sbi_trap_regs *regs) {
>+	unsigned long imm, rd;
>+	unsigned long *regs_p = (unsigned long *)regs;
>+
>+	if ((insn & 0xe003) == 0x4001) { /* c.li */
>+		imm = (insn >> 2) & 0x1f;
>+		imm |= ((insn >> 12) & 1) ? -32 : 0;
>+		rd = (insn >> 7) & 0x1f;
>+		if (rd)
>+			regs_p[rd] = imm;
>+		return 0;
>+	}
>+
>+	return truly_illegal_insn(insn, regs);
>+}
>+
> static const illegal_insn_func illegal_insn_table[32] = {
> 	truly_illegal_insn, /* 0 */
> 	truly_illegal_insn, /* 1 */
>@@ -160,7 +176,7 @@ int sbi_illegal_insn_handler(ulong insn, struct sbi_trap_regs *regs)
> 			return sbi_trap_redirect(regs, &uptrap);
> 		}
> 		if ((insn & 3) != 3)
>-			return truly_illegal_insn(insn, regs);
>+			return compressed_insn(insn, regs);
> 	}
>
> 	return illegal_insn_table[(insn & 0x7c) >> 2](insn, regs);
>--
>2.42.0
>

I think this patch may be unnecessary.

After searching the boot flow of UEFI, I only found two implementations:

For QEMU RISC-V virt platform and SG2042 platform:
SBI -> (SEC -> PEI -> DXE) -> bootloader -> OS
               UEFI

For SiFive U540 platform:
(SEC -> OpenSBI -> PEI -> DXE) -> bootloader -> OS
             UEFI

Both invoke SBI at a very early stage and use raw vaild address, and
handling the UEFI header is more like bootloader's job. I have no idea
about why this should be handled by SBI. FYI, Björn Töpel.
Xiang W Nov. 9, 2023, 10:04 a.m. UTC | #2
在 2023-11-09星期四的 17:35 +0800,Inochi Amaoto写道:
> > The Linux kernel RISC-V image format allows that UEFI Images can also
> > be booted by non-UEFI firmware. However for that to work, the PE/Image
> > combo requires that 'MZ' is a valid instruction. On RISC-V, 'MZ' is
> > only a valid instruction if the hardware is C capable [1]. So add
> > Emulate c.li
> > 
> > Signed-off-by: Björn Töpel <bjorn@rivosinc.com>
> > Signed-off-by: Xiang W <wxjstz@126.com>
> > ---
> > lib/sbi/sbi_illegal_insn.c | 18 +++++++++++++++++-
> > 1 file changed, 17 insertions(+), 1 deletion(-)
> > 
> > diff --git a/lib/sbi/sbi_illegal_insn.c b/lib/sbi/sbi_illegal_insn.c
> > index 2be4757..4ab10f4 100644
> > --- a/lib/sbi/sbi_illegal_insn.c
> > +++ b/lib/sbi/sbi_illegal_insn.c
> > @@ -102,6 +102,22 @@ static int system_opcode_insn(ulong insn, struct sbi_trap_regs *regs)
> > 	return 0;
> > }
> > 
> > +static int compressed_insn(ulong insn, struct sbi_trap_regs *regs) {
> > +	unsigned long imm, rd;
> > +	unsigned long *regs_p = (unsigned long *)regs;
> > +
> > +	if ((insn & 0xe003) == 0x4001) { /* c.li */
> > +		imm = (insn >> 2) & 0x1f;
> > +		imm |= ((insn >> 12) & 1) ? -32 : 0;
> > +		rd = (insn >> 7) & 0x1f;
> > +		if (rd)
> > +			regs_p[rd] = imm;
> > +		return 0;
> > +	}
> > +
> > +	return truly_illegal_insn(insn, regs);
> > +}
> > +
> > static const illegal_insn_func illegal_insn_table[32] = {
> > 	truly_illegal_insn, /* 0 */
> > 	truly_illegal_insn, /* 1 */
> > @@ -160,7 +176,7 @@ int sbi_illegal_insn_handler(ulong insn, struct sbi_trap_regs *regs)
> > 			return sbi_trap_redirect(regs, &uptrap);
> > 		}
> > 		if ((insn & 3) != 3)
> > -			return truly_illegal_insn(insn, regs);
> > +			return compressed_insn(insn, regs);
> > 	}
> > 
> > 	return illegal_insn_table[(insn & 0x7c) >> 2](insn, regs);
> > --
> > 2.42.0
> > 
> 
> I think this patch may be unnecessary.
> 
> After searching the boot flow of UEFI, I only found two implementations:
> 
> For QEMU RISC-V virt platform and SG2042 platform:
> SBI -> (SEC -> PEI -> DXE) -> bootloader -> OS
>                UEFI
> 
> For SiFive U540 platform:
> (SEC -> OpenSBI -> PEI -> DXE) -> bootloader -> OS
>              UEFI
> 
> Both invoke SBI at a very early stage and use raw vaild address, and
> handling the UEFI header is more like bootloader's job. I have no idea
> about why this should be handled by SBI. FYI, Björn Töpel.
This patch is used to solve the problem of non-UEFI firmware booting the
kernel. The UEFI boot kernel should parse the PE file header, so this
patch is not needed. But non-UEFI probably treats the kernel as a
binary image.

Regards,
Xiang W
Inochi Amaoto Nov. 9, 2023, 11:20 a.m. UTC | #3
>在 2023-11-09星期四的 17:35 +0800,Inochi Amaoto写道:
>>> The Linux kernel RISC-V image format allows that UEFI Images can also
>>> be booted by non-UEFI firmware. However for that to work, the PE/Image
>>> combo requires that 'MZ' is a valid instruction. On RISC-V, 'MZ' is
>>> only a valid instruction if the hardware is C capable [1]. So add
>>> Emulate c.li
>>>
>>> Signed-off-by: Björn Töpel <bjorn@rivosinc.com>
>>> Signed-off-by: Xiang W <wxjstz@126.com>
>>> ---
>>> lib/sbi/sbi_illegal_insn.c | 18 +++++++++++++++++-
>>> 1 file changed, 17 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/lib/sbi/sbi_illegal_insn.c b/lib/sbi/sbi_illegal_insn.c
>>> index 2be4757..4ab10f4 100644
>>> --- a/lib/sbi/sbi_illegal_insn.c
>>> +++ b/lib/sbi/sbi_illegal_insn.c
>>> @@ -102,6 +102,22 @@ static int system_opcode_insn(ulong insn, struct sbi_trap_regs *regs)
>>> 	return 0;
>>> }
>>>
>>> +static int compressed_insn(ulong insn, struct sbi_trap_regs *regs) {
>>> +	unsigned long imm, rd;
>>> +	unsigned long *regs_p = (unsigned long *)regs;
>>> +
>>> +	if ((insn & 0xe003) == 0x4001) { /* c.li */
>>> +		imm = (insn >> 2) & 0x1f;
>>> +		imm |= ((insn >> 12) & 1) ? -32 : 0;
>>> +		rd = (insn >> 7) & 0x1f;
>>> +		if (rd)
>>> +			regs_p[rd] = imm;
>>> +		return 0;
>>> +	}
>>> +
>>> +	return truly_illegal_insn(insn, regs);
>>> +}
>>> +
>>> static const illegal_insn_func illegal_insn_table[32] = {
>>> 	truly_illegal_insn, /* 0 */
>>> 	truly_illegal_insn, /* 1 */
>>> @@ -160,7 +176,7 @@ int sbi_illegal_insn_handler(ulong insn, struct sbi_trap_regs *regs)
>>> 			return sbi_trap_redirect(regs, &uptrap);
>>> 		}
>>> 		if ((insn & 3) != 3)
>>> -			return truly_illegal_insn(insn, regs);
>>> +			return compressed_insn(insn, regs);
>>> 	}
>>>
>>> 	return illegal_insn_table[(insn & 0x7c) >> 2](insn, regs);
>>> --
>>> 2.42.0
>>>
>>
>> I think this patch may be unnecessary.
>>
>> After searching the boot flow of UEFI, I only found two implementations:
>>
>> For QEMU RISC-V virt platform and SG2042 platform:
>> SBI -> (SEC -> PEI -> DXE) -> bootloader -> OS
>>                UEFI
>>
>> For SiFive U540 platform:
>> (SEC -> OpenSBI -> PEI -> DXE) -> bootloader -> OS
>>              UEFI
>>
>> Both invoke SBI at a very early stage and use raw vaild address, and
>> handling the UEFI header is more like bootloader's job. I have no idea
>> about why this should be handled by SBI. FYI, Björn Töpel.
>This patch is used to solve the problem of non-UEFI firmware booting the
>kernel. The UEFI boot kernel should parse the PE file header, so this
>patch is not needed. But non-UEFI probably treats the kernel as a
>binary image.

This is not a vaild scenario for UEFI binary. IIRC, even the aarch64 arch
does not support using UEFI binary in this way. I agree with Jessica about
the fact that they are different. If you insist, I suggest using a custom
bootloader to handle this.

>
>Regards,
>Xiang W
>
>
Xiang W Nov. 9, 2023, 11:53 a.m. UTC | #4
在 2023-11-09星期四的 19:20 +0800,Inochi Amaoto写道:
> > 在 2023-11-09星期四的 17:35 +0800,Inochi Amaoto写道:
> > > > The Linux kernel RISC-V image format allows that UEFI Images can also
> > > > be booted by non-UEFI firmware. However for that to work, the PE/Image
> > > > combo requires that 'MZ' is a valid instruction. On RISC-V, 'MZ' is
> > > > only a valid instruction if the hardware is C capable [1]. So add
> > > > Emulate c.li
> > > > 
> > > > Signed-off-by: Björn Töpel <bjorn@rivosinc.com>
> > > > Signed-off-by: Xiang W <wxjstz@126.com>
> > > > ---
> > > > lib/sbi/sbi_illegal_insn.c | 18 +++++++++++++++++-
> > > > 1 file changed, 17 insertions(+), 1 deletion(-)
> > > > 
> > > > diff --git a/lib/sbi/sbi_illegal_insn.c b/lib/sbi/sbi_illegal_insn.c
> > > > index 2be4757..4ab10f4 100644
> > > > --- a/lib/sbi/sbi_illegal_insn.c
> > > > +++ b/lib/sbi/sbi_illegal_insn.c
> > > > @@ -102,6 +102,22 @@ static int system_opcode_insn(ulong insn, struct sbi_trap_regs *regs)
> > > > 	return 0;
> > > > }
> > > > 
> > > > +static int compressed_insn(ulong insn, struct sbi_trap_regs *regs) {
> > > > +	unsigned long imm, rd;
> > > > +	unsigned long *regs_p = (unsigned long *)regs;
> > > > +
> > > > +	if ((insn & 0xe003) == 0x4001) { /* c.li */
> > > > +		imm = (insn >> 2) & 0x1f;
> > > > +		imm |= ((insn >> 12) & 1) ? -32 : 0;
> > > > +		rd = (insn >> 7) & 0x1f;
> > > > +		if (rd)
> > > > +			regs_p[rd] = imm;
> > > > +		return 0;
> > > > +	}
> > > > +
> > > > +	return truly_illegal_insn(insn, regs);
> > > > +}
> > > > +
> > > > static const illegal_insn_func illegal_insn_table[32] = {
> > > > 	truly_illegal_insn, /* 0 */
> > > > 	truly_illegal_insn, /* 1 */
> > > > @@ -160,7 +176,7 @@ int sbi_illegal_insn_handler(ulong insn, struct sbi_trap_regs *regs)
> > > > 			return sbi_trap_redirect(regs, &uptrap);
> > > > 		}
> > > > 		if ((insn & 3) != 3)
> > > > -			return truly_illegal_insn(insn, regs);
> > > > +			return compressed_insn(insn, regs);
> > > > 	}
> > > > 
> > > > 	return illegal_insn_table[(insn & 0x7c) >> 2](insn, regs);
> > > > --
> > > > 2.42.0
> > > > 
> > > 
> > > I think this patch may be unnecessary.
> > > 
> > > After searching the boot flow of UEFI, I only found two implementations:
> > > 
> > > For QEMU RISC-V virt platform and SG2042 platform:
> > > SBI -> (SEC -> PEI -> DXE) -> bootloader -> OS
> > >                UEFI
> > > 
> > > For SiFive U540 platform:
> > > (SEC -> OpenSBI -> PEI -> DXE) -> bootloader -> OS
> > >              UEFI
> > > 
> > > Both invoke SBI at a very early stage and use raw vaild address, and
> > > handling the UEFI header is more like bootloader's job. I have no idea
> > > about why this should be handled by SBI. FYI, Björn Töpel.
> > This patch is used to solve the problem of non-UEFI firmware booting the
> > kernel. The UEFI boot kernel should parse the PE file header, so this
> > patch is not needed. But non-UEFI probably treats the kernel as a
> > binary image.
> 
> This is not a vaild scenario for UEFI binary. IIRC, even the aarch64 arch
> does not support using UEFI binary in this way. I agree with Jessica about
> the fact that they are different. If you insist, I suggest using a custom
> bootloader to handle this.
Now this modification only adds an instruction to simulate and will not affect
the other programs.

Regards,
Xiang W
> 
> > 
> > Regards,
> > Xiang W
> > 
> >
Björn Töpel Nov. 9, 2023, 2:48 p.m. UTC | #5
On Thu, 9 Nov 2023 at 09:26, Xiang W <wxjstz@126.com> wrote:
>
> The Linux kernel RISC-V image format allows that UEFI Images can also
> be booted by non-UEFI firmware. However for that to work, the PE/Image
> combo requires that 'MZ' is a valid instruction. On RISC-V, 'MZ' is
> only a valid instruction if the hardware is C capable [1]. So add
> Emulate c.li
>
> Signed-off-by: Björn Töpel <bjorn@rivosinc.com>
> Signed-off-by: Xiang W <wxjstz@126.com>
> ---
>  lib/sbi/sbi_illegal_insn.c | 18 +++++++++++++++++-
>  1 file changed, 17 insertions(+), 1 deletion(-)
>
> diff --git a/lib/sbi/sbi_illegal_insn.c b/lib/sbi/sbi_illegal_insn.c
> index 2be4757..4ab10f4 100644
> --- a/lib/sbi/sbi_illegal_insn.c
> +++ b/lib/sbi/sbi_illegal_insn.c
> @@ -102,6 +102,22 @@ static int system_opcode_insn(ulong insn, struct sbi_trap_regs *regs)
>         return 0;
>  }
>
> +static int compressed_insn(ulong insn, struct sbi_trap_regs *regs) {
> +       unsigned long imm, rd;
> +       unsigned long *regs_p = (unsigned long *)regs;
> +
> +       if ((insn & 0xe003) == 0x4001) { /* c.li */
> +               imm = (insn >> 2) & 0x1f;
> +               imm |= ((insn >> 12) & 1) ? -32 : 0;
> +               rd = (insn >> 7) & 0x1f;
> +               if (rd)
> +                       regs_p[rd] = imm;
> +               return 0;
> +       }

The mepc update is missing, so this patch will not work.

That aside, what Jess pointed out is that on a machine *NOT*
supporting C, we're not emulating anything. The 16b instruction
parcels do not exist here, so we cannot really emulate that. Instead,
what the firmware gets is a 32b bogus/nonexisting instruction.
Emulating that is... weird. A valid concern! ;-)

The Linux Image spec says that code0 can be 'MZ' (and will be for UEFI
images). One correct fix (I think) is changing the non-UEFI loader,
fixing up code0 if it's MZ, making sure not to execute that (well, you
could on C capable machines).  That, or changing specs.


Björn
Xiang W Nov. 9, 2023, 3:22 p.m. UTC | #6
在 2023-11-09星期四的 15:48 +0100,Björn Töpel写道:
> On Thu, 9 Nov 2023 at 09:26, Xiang W <wxjstz@126.com> wrote:
> > 
> > The Linux kernel RISC-V image format allows that UEFI Images can also
> > be booted by non-UEFI firmware. However for that to work, the PE/Image
> > combo requires that 'MZ' is a valid instruction. On RISC-V, 'MZ' is
> > only a valid instruction if the hardware is C capable [1]. So add
> > Emulate c.li
> > 
> > Signed-off-by: Björn Töpel <bjorn@rivosinc.com>
> > Signed-off-by: Xiang W <wxjstz@126.com>
> > ---
> >  lib/sbi/sbi_illegal_insn.c | 18 +++++++++++++++++-
> >  1 file changed, 17 insertions(+), 1 deletion(-)
> > 
> > diff --git a/lib/sbi/sbi_illegal_insn.c b/lib/sbi/sbi_illegal_insn.c
> > index 2be4757..4ab10f4 100644
> > --- a/lib/sbi/sbi_illegal_insn.c
> > +++ b/lib/sbi/sbi_illegal_insn.c
> > @@ -102,6 +102,22 @@ static int system_opcode_insn(ulong insn, struct sbi_trap_regs *regs)
> >         return 0;
> >  }
> > 
> > +static int compressed_insn(ulong insn, struct sbi_trap_regs *regs) {
> > +       unsigned long imm, rd;
> > +       unsigned long *regs_p = (unsigned long *)regs;
> > +
> > +       if ((insn & 0xe003) == 0x4001) { /* c.li */
> > +               imm = (insn >> 2) & 0x1f;
> > +               imm |= ((insn >> 12) & 1) ? -32 : 0;
> > +               rd = (insn >> 7) & 0x1f;
> > +               if (rd)
> > +                       regs_p[rd] = imm;
> > +               return 0;
> > +       }
> 
> The mepc update is missing, so this patch will not work.
> 
> That aside, what Jess pointed out is that on a machine *NOT*
> supporting C, we're not emulating anything. The 16b instruction
> parcels do not exist here, so we cannot really emulate that. Instead,
> what the firmware gets is a 32b bogus/nonexisting instruction.
> Emulating that is... weird. A valid concern! ;-)
> 
> The Linux Image spec says that code0 can be 'MZ' (and will be for UEFI
> images). One correct fix (I think) is changing the non-UEFI loader,
> fixing up code0 if it's MZ, making sure not to execute that (well, you
> could on C capable machines).  That, or changing specs.
Ok. let's abandon.

Regards,
Xiang W

> 
> 
> Björn
diff mbox series

Patch

diff --git a/lib/sbi/sbi_illegal_insn.c b/lib/sbi/sbi_illegal_insn.c
index 2be4757..4ab10f4 100644
--- a/lib/sbi/sbi_illegal_insn.c
+++ b/lib/sbi/sbi_illegal_insn.c
@@ -102,6 +102,22 @@  static int system_opcode_insn(ulong insn, struct sbi_trap_regs *regs)
 	return 0;
 }
 
+static int compressed_insn(ulong insn, struct sbi_trap_regs *regs) {
+	unsigned long imm, rd;
+	unsigned long *regs_p = (unsigned long *)regs;
+
+	if ((insn & 0xe003) == 0x4001) { /* c.li */
+		imm = (insn >> 2) & 0x1f;
+		imm |= ((insn >> 12) & 1) ? -32 : 0;
+		rd = (insn >> 7) & 0x1f;
+		if (rd)
+			regs_p[rd] = imm;
+		return 0;
+	}
+
+	return truly_illegal_insn(insn, regs);
+}
+
 static const illegal_insn_func illegal_insn_table[32] = {
 	truly_illegal_insn, /* 0 */
 	truly_illegal_insn, /* 1 */
@@ -160,7 +176,7 @@  int sbi_illegal_insn_handler(ulong insn, struct sbi_trap_regs *regs)
 			return sbi_trap_redirect(regs, &uptrap);
 		}
 		if ((insn & 3) != 3)
-			return truly_illegal_insn(insn, regs);
+			return compressed_insn(insn, regs);
 	}
 
 	return illegal_insn_table[(insn & 0x7c) >> 2](insn, regs);