diff mbox series

[1/5] hw/mips: Add a bootloader helper

Message ID 20201207050231.2712-2-jiaxun.yang@flygoat.com
State New
Headers show
Series MIPS Bootloader helper | expand

Commit Message

Jiaxun Yang Dec. 7, 2020, 5:02 a.m. UTC
Add a bootloader helper to generate simple bootloaders for kernel.
It can help us reduce inline hex hack and also keep MIPS release 6
compatibility easier.

Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
---
 hw/mips/bootloader.c      | 150 ++++++++++++++++++++++++++++++++++++++
 hw/mips/meson.build       |   2 +-
 include/hw/mips/cpudevs.h |   8 ++
 3 files changed, 159 insertions(+), 1 deletion(-)
 create mode 100644 hw/mips/bootloader.c

Comments

Philippe Mathieu-Daudé Dec. 7, 2020, 6:14 p.m. UTC | #1
Hi Jiaxun,

On 12/7/20 6:02 AM, Jiaxun Yang wrote:
> Add a bootloader helper to generate simple bootloaders for kernel.
> It can help us reduce inline hex hack and also keep MIPS release 6
> compatibility easier.

Great idea :)

> 
> Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
> ---
>  hw/mips/bootloader.c      | 150 ++++++++++++++++++++++++++++++++++++++
>  hw/mips/meson.build       |   2 +-
>  include/hw/mips/cpudevs.h |   8 ++
>  3 files changed, 159 insertions(+), 1 deletion(-)
>  create mode 100644 hw/mips/bootloader.c
> 
> diff --git a/hw/mips/bootloader.c b/hw/mips/bootloader.c
> new file mode 100644
> index 0000000000..3210c26bb7
> --- /dev/null
> +++ b/hw/mips/bootloader.c
> @@ -0,0 +1,150 @@
> +/*
> + * Utility for QEMU MIPS to generate it's simple bootloader
> + *
> + * Instructions used here are carefully selected to keep compatibility with
> + * MIPS Release 6.
> + *
> + * Copyright (C) 2020 Jiaxun Yang <jiaxun.yang@flygoat.com>
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/bitops.h"
> +#include "cpu.h"
> +#include "hw/mips/cpudevs.h"

Please keep the include local, and name it accordingly (bootloader.h).

Also, can you use an enum for the register values to make the code
easier to review?

enum {
    R_a0 = 4,
    R_a1 = 5,
    ...
    R_t0 = 26,
    R_t1 = 27,
    ...
    R_pc = 31,
};

> +
> +/* Base types */
> +static void bl_gen_nop(uint32_t **p)
> +{
> +    stl_p(*p, 0);
> +    *p = *p + 1;
> +}
> +
> +static void bl_gen_r_type(uint32_t **p, uint8_t opcode, uint8_t rs, uint8_t rt,
> +                            uint8_t rd, uint8_t shift, uint8_t funct)
> +{
> +    uint32_t insn = 0;
> +
> +    insn = deposit32(insn, 26, 6, opcode);
> +    insn = deposit32(insn, 21, 5, rs);
> +    insn = deposit32(insn, 16, 5, rt);
> +    insn = deposit32(insn, 11, 5, rd);
> +    insn = deposit32(insn, 6, 5, shift);
> +    insn = deposit32(insn, 0, 6, funct);
> +
> +    stl_p(*p, insn);
> +    *p = *p + 1;
> +}
> +
> +static void bl_gen_i_type(uint32_t **p, uint8_t opcode, uint8_t rs, uint8_t rt,
> +                            uint16_t imm)
> +{
> +    uint32_t insn = 0;
> +
> +    insn = deposit32(insn, 26, 6, opcode);
> +    insn = deposit32(insn, 21, 5, rs);
> +    insn = deposit32(insn, 16, 5, rt);
> +    insn = deposit32(insn, 0, 16, imm);
> +
> +    stl_p(*p, insn);
> +    *p = *p + 1;
> +}
> +
> +/* Single instructions */
> +static void bl_gen_dsll(uint32_t **p, uint8_t rd, uint8_t rt, uint8_t sa)
> +{
> +    /* R6: OK, 32: NO */
> +    bl_gen_r_type(p, 0, 0, rt, rd, sa, 0x38);
> +}

We should convert cpu_supports_isa() as:
bool cpu_supports_isa(MIPSCPU *cpu, uint64_t isa);
so passing a MIPSCPU (or CPUMIPSState) argument, you can do:

static void bl_gen_dsll(MIPSCPU *cpu, uint32_t **p,
                        uint8_t rd, uint8_t rt, uint8_t sa)
{
    if (cpu_supports_isa(cpu, ISA_MIPS32R6 | ISA_MIPS64R6)) {
        bl_gen_r_type(p, 0, 0, rt, rd, sa, 0x38);
    } else {
        g_assert_not_reached(); /* unsupported */
    }
}

> +
> +static void bl_gen_daddiu(uint32_t **p, uint8_t rt, uint8_t rs, uint16_t imm)
> +{
> +    /* R6: OK, 32: NO */

Ditto, etc...

> +    bl_gen_i_type(p, 0x19, rs, rt, imm);
> +}
> +
> +static void bl_gen_jalr(uint32_t **p, uint8_t rs)
> +{
> +    /* R6: OK, 32: OK */
> +    bl_gen_r_type(p, 0, rs, 0, 31, 0, 0x9);
> +}
> +
> +static void bl_gen_lui(uint32_t **p, uint8_t rt, uint16_t imm)
> +{
> +    /* R6: It's a alias of AUI with RS = 0, 32: OK */
> +    bl_gen_i_type(p, 0xf, 0, rt, imm);
> +}
> +
> +static void bl_gen_ori(uint32_t **p, uint8_t rt, uint8_t rs, uint16_t imm)
> +{
> +    /* R6: OK, 32: OK */
> +    bl_gen_i_type(p, 0xd, rs, rt, imm);
> +}
> +
> +static void bl_gen_sw(uint32_t **p, uint8_t rt, uint8_t base, uint16_t offset)
> +{
> +    /* R6: OK, 32: NO */
> +    bl_gen_i_type(p, 0x2b, base, rt, offset);
> +}
> +
> +static void bl_gen_sd(uint32_t **p, uint8_t rt, uint8_t base, uint16_t offset)
> +{
> +    /* R6: OK, 32: NO */
> +    bl_gen_i_type(p, 0x3f, base, rt, offset);
> +}
> +
> +/* Pseudo instructions */
> +static void bl_gen_li(uint32_t **p, uint8_t rt, uint32_t imm)
> +{
> +    /* R6: OK, 32 OK */
> +    bl_gen_lui(p, rt, extract32(imm, 16, 16));
> +    bl_gen_ori(p, rt, rt, extract32(imm, 0, 16));
> +}
> +
> +static void bl_gen_dli(uint32_t **p, uint8_t rt, uint64_t imm)
> +{
> +    /* R6: OK, 32 NO */
> +    bl_gen_li(p, rt, extract64(imm, 32, 32));
> +    bl_gen_dsll(p, rt, rt, 16);
> +    bl_gen_daddiu(p, rt, rt, extract64(imm, 16, 16));
> +    bl_gen_dsll(p, rt, rt, 16);
> +    bl_gen_daddiu(p, rt, rt, extract64(imm, 0, 16));
> +}
> +
> +/* Helpers */
> +void bl_gen_jump_to(uint32_t **p, uint32_t jump_addr)

bl_gen_jump_to_u32?

> +{
> +    /* Use ra to jump */
> +    bl_gen_li(p, 31, jump_addr);
> +    bl_gen_jalr(p, 31);
> +    bl_gen_nop(p); /* delay slot, useless for R6 */
> +}
> +
> +void bl_gen_jump_kernel(uint32_t **p, uint32_t sp, uint32_t a0,
> +                        uint32_t a1, uint32_t a2, uint32_t a3,
> +                        uint32_t kernel_addr)

bl_gen_jump_kernel_u32?

> +{
> +    bl_gen_li(p, 29, sp);
> +    bl_gen_li(p, 4, a0);
> +    bl_gen_li(p, 5, a1);
> +    bl_gen_li(p, 6, a2);
> +    bl_gen_li(p, 7, a3);
> +
> +    bl_gen_jump_to(p, kernel_addr);
> +}
> +
> +void bl_gen_writel(uint32_t **p, uint32_t val, uint32_t addr)

bl_gen_write_u32?

> +{
> +    bl_gen_li(p, 26, val);
> +    bl_gen_li(p, 27, addr);
> +    bl_gen_sw(p, 26, 27, 0x0);
> +}
> +
> +void bl_gen_writeq(uint32_t **p, uint64_t val, uint32_t addr)

Well, addr has to be uint64_t... else you wrap KSEG1 on 64-bit.

bl_gen_write_u64?

> +{
> +    /* 64 Only */

       if (!cpu_supports_isa(cpu, ISA_MIPS64)) {
           g_assert_not_reached(); /* unsupported */
       }

> +    bl_gen_dli(p, 26, val);
> +    bl_gen_li(p, 27, addr);
> +    bl_gen_sd(p, 26, 27, 0x0);
> +}
> diff --git a/hw/mips/meson.build b/hw/mips/meson.build
> index bcdf96be69..053459377f 100644
> --- a/hw/mips/meson.build
> +++ b/hw/mips/meson.build
> @@ -1,5 +1,5 @@
>  mips_ss = ss.source_set()
> -mips_ss.add(files('addr.c', 'mips_int.c'))
> +mips_ss.add(files('addr.c', 'bootloader.c', 'mips_int.c'))
>  mips_ss.add(when: 'CONFIG_FULOONG', if_true: files('fuloong2e.c'))
>  mips_ss.add(when: 'CONFIG_JAZZ', if_true: files('jazz.c'))
>  mips_ss.add(when: 'CONFIG_MALTA', if_true: files('gt64xxx_pci.c', 'malta.c'))
> diff --git a/include/hw/mips/cpudevs.h b/include/hw/mips/cpudevs.h
> index 291f59281a..0b3e060c95 100644
> --- a/include/hw/mips/cpudevs.h
> +++ b/include/hw/mips/cpudevs.h
> @@ -12,6 +12,14 @@ uint64_t cpu_mips_kvm_um_phys_to_kseg0(void *opaque, uint64_t addr);
>  bool mips_um_ksegs_enabled(void);
>  void mips_um_ksegs_enable(void);
>  
> +/* bootloader.c */

Not related to CPU internal devices, add to hw/mips/bootloader.h.

> +void bl_gen_jump_to(uint32_t **p, uint32_t jump_addr);
> +void bl_gen_jump_kernel(uint32_t **p, uint32_t sp, uint32_t a0,
> +                        uint32_t a1, uint32_t a2, uint32_t a3,
> +                        uint32_t kernel_addr);
> +void bl_gen_writel(uint32_t **p, uint32_t val, uint32_t addr);
> +void bl_gen_writeq(uint32_t **p, uint64_t val, uint32_t addr);
> +
>  /* mips_int.c */
>  void cpu_mips_irq_init_cpu(MIPSCPU *cpu);
>  
>
Philippe Mathieu-Daudé Dec. 7, 2020, 6:27 p.m. UTC | #2
On 12/7/20 7:14 PM, Philippe Mathieu-Daudé wrote:
> Hi Jiaxun,
> 
> On 12/7/20 6:02 AM, Jiaxun Yang wrote:
>> Add a bootloader helper to generate simple bootloaders for kernel.
>> It can help us reduce inline hex hack and also keep MIPS release 6
>> compatibility easier.
> 
> Great idea :)
> 
>>
>> Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
>> ---
>>  hw/mips/bootloader.c      | 150 ++++++++++++++++++++++++++++++++++++++
>>  hw/mips/meson.build       |   2 +-
>>  include/hw/mips/cpudevs.h |   8 ++
>>  3 files changed, 159 insertions(+), 1 deletion(-)
>>  create mode 100644 hw/mips/bootloader.c
...
>> +void bl_gen_writeq(uint32_t **p, uint64_t val, uint32_t addr)
> 
> Well, addr has to be uint64_t... else you wrap KSEG1 on 64-bit.

Oops I misread addr/val.

> 
> bl_gen_write_u64?
> 
>> +{
>> +    /* 64 Only */
> 
>        if (!cpu_supports_isa(cpu, ISA_MIPS64)) {
>            g_assert_not_reached(); /* unsupported */
>        }
> 
>> +    bl_gen_dli(p, 26, val);
>> +    bl_gen_li(p, 27, addr);
>> +    bl_gen_sd(p, 26, 27, 0x0);
>> +}
diff mbox series

Patch

diff --git a/hw/mips/bootloader.c b/hw/mips/bootloader.c
new file mode 100644
index 0000000000..3210c26bb7
--- /dev/null
+++ b/hw/mips/bootloader.c
@@ -0,0 +1,150 @@ 
+/*
+ * Utility for QEMU MIPS to generate it's simple bootloader
+ *
+ * Instructions used here are carefully selected to keep compatibility with
+ * MIPS Release 6.
+ *
+ * Copyright (C) 2020 Jiaxun Yang <jiaxun.yang@flygoat.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/bitops.h"
+#include "cpu.h"
+#include "hw/mips/cpudevs.h"
+
+/* Base types */
+static void bl_gen_nop(uint32_t **p)
+{
+    stl_p(*p, 0);
+    *p = *p + 1;
+}
+
+static void bl_gen_r_type(uint32_t **p, uint8_t opcode, uint8_t rs, uint8_t rt,
+                            uint8_t rd, uint8_t shift, uint8_t funct)
+{
+    uint32_t insn = 0;
+
+    insn = deposit32(insn, 26, 6, opcode);
+    insn = deposit32(insn, 21, 5, rs);
+    insn = deposit32(insn, 16, 5, rt);
+    insn = deposit32(insn, 11, 5, rd);
+    insn = deposit32(insn, 6, 5, shift);
+    insn = deposit32(insn, 0, 6, funct);
+
+    stl_p(*p, insn);
+    *p = *p + 1;
+}
+
+static void bl_gen_i_type(uint32_t **p, uint8_t opcode, uint8_t rs, uint8_t rt,
+                            uint16_t imm)
+{
+    uint32_t insn = 0;
+
+    insn = deposit32(insn, 26, 6, opcode);
+    insn = deposit32(insn, 21, 5, rs);
+    insn = deposit32(insn, 16, 5, rt);
+    insn = deposit32(insn, 0, 16, imm);
+
+    stl_p(*p, insn);
+    *p = *p + 1;
+}
+
+/* Single instructions */
+static void bl_gen_dsll(uint32_t **p, uint8_t rd, uint8_t rt, uint8_t sa)
+{
+    /* R6: OK, 32: NO */
+    bl_gen_r_type(p, 0, 0, rt, rd, sa, 0x38);
+}
+
+static void bl_gen_daddiu(uint32_t **p, uint8_t rt, uint8_t rs, uint16_t imm)
+{
+    /* R6: OK, 32: NO */
+    bl_gen_i_type(p, 0x19, rs, rt, imm);
+}
+
+static void bl_gen_jalr(uint32_t **p, uint8_t rs)
+{
+    /* R6: OK, 32: OK */
+    bl_gen_r_type(p, 0, rs, 0, 31, 0, 0x9);
+}
+
+static void bl_gen_lui(uint32_t **p, uint8_t rt, uint16_t imm)
+{
+    /* R6: It's a alias of AUI with RS = 0, 32: OK */
+    bl_gen_i_type(p, 0xf, 0, rt, imm);
+}
+
+static void bl_gen_ori(uint32_t **p, uint8_t rt, uint8_t rs, uint16_t imm)
+{
+    /* R6: OK, 32: OK */
+    bl_gen_i_type(p, 0xd, rs, rt, imm);
+}
+
+static void bl_gen_sw(uint32_t **p, uint8_t rt, uint8_t base, uint16_t offset)
+{
+    /* R6: OK, 32: NO */
+    bl_gen_i_type(p, 0x2b, base, rt, offset);
+}
+
+static void bl_gen_sd(uint32_t **p, uint8_t rt, uint8_t base, uint16_t offset)
+{
+    /* R6: OK, 32: NO */
+    bl_gen_i_type(p, 0x3f, base, rt, offset);
+}
+
+/* Pseudo instructions */
+static void bl_gen_li(uint32_t **p, uint8_t rt, uint32_t imm)
+{
+    /* R6: OK, 32 OK */
+    bl_gen_lui(p, rt, extract32(imm, 16, 16));
+    bl_gen_ori(p, rt, rt, extract32(imm, 0, 16));
+}
+
+static void bl_gen_dli(uint32_t **p, uint8_t rt, uint64_t imm)
+{
+    /* R6: OK, 32 NO */
+    bl_gen_li(p, rt, extract64(imm, 32, 32));
+    bl_gen_dsll(p, rt, rt, 16);
+    bl_gen_daddiu(p, rt, rt, extract64(imm, 16, 16));
+    bl_gen_dsll(p, rt, rt, 16);
+    bl_gen_daddiu(p, rt, rt, extract64(imm, 0, 16));
+}
+
+/* Helpers */
+void bl_gen_jump_to(uint32_t **p, uint32_t jump_addr)
+{
+    /* Use ra to jump */
+    bl_gen_li(p, 31, jump_addr);
+    bl_gen_jalr(p, 31);
+    bl_gen_nop(p); /* delay slot, useless for R6 */
+}
+
+void bl_gen_jump_kernel(uint32_t **p, uint32_t sp, uint32_t a0,
+                        uint32_t a1, uint32_t a2, uint32_t a3,
+                        uint32_t kernel_addr)
+{
+    bl_gen_li(p, 29, sp);
+    bl_gen_li(p, 4, a0);
+    bl_gen_li(p, 5, a1);
+    bl_gen_li(p, 6, a2);
+    bl_gen_li(p, 7, a3);
+
+    bl_gen_jump_to(p, kernel_addr);
+}
+
+void bl_gen_writel(uint32_t **p, uint32_t val, uint32_t addr)
+{
+    bl_gen_li(p, 26, val);
+    bl_gen_li(p, 27, addr);
+    bl_gen_sw(p, 26, 27, 0x0);
+}
+
+void bl_gen_writeq(uint32_t **p, uint64_t val, uint32_t addr)
+{
+    /* 64 Only */
+    bl_gen_dli(p, 26, val);
+    bl_gen_li(p, 27, addr);
+    bl_gen_sd(p, 26, 27, 0x0);
+}
diff --git a/hw/mips/meson.build b/hw/mips/meson.build
index bcdf96be69..053459377f 100644
--- a/hw/mips/meson.build
+++ b/hw/mips/meson.build
@@ -1,5 +1,5 @@ 
 mips_ss = ss.source_set()
-mips_ss.add(files('addr.c', 'mips_int.c'))
+mips_ss.add(files('addr.c', 'bootloader.c', 'mips_int.c'))
 mips_ss.add(when: 'CONFIG_FULOONG', if_true: files('fuloong2e.c'))
 mips_ss.add(when: 'CONFIG_JAZZ', if_true: files('jazz.c'))
 mips_ss.add(when: 'CONFIG_MALTA', if_true: files('gt64xxx_pci.c', 'malta.c'))
diff --git a/include/hw/mips/cpudevs.h b/include/hw/mips/cpudevs.h
index 291f59281a..0b3e060c95 100644
--- a/include/hw/mips/cpudevs.h
+++ b/include/hw/mips/cpudevs.h
@@ -12,6 +12,14 @@  uint64_t cpu_mips_kvm_um_phys_to_kseg0(void *opaque, uint64_t addr);
 bool mips_um_ksegs_enabled(void);
 void mips_um_ksegs_enable(void);
 
+/* bootloader.c */
+void bl_gen_jump_to(uint32_t **p, uint32_t jump_addr);
+void bl_gen_jump_kernel(uint32_t **p, uint32_t sp, uint32_t a0,
+                        uint32_t a1, uint32_t a2, uint32_t a3,
+                        uint32_t kernel_addr);
+void bl_gen_writel(uint32_t **p, uint32_t val, uint32_t addr);
+void bl_gen_writeq(uint32_t **p, uint64_t val, uint32_t addr);
+
 /* mips_int.c */
 void cpu_mips_irq_init_cpu(MIPSCPU *cpu);