grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH v2 6/8] LoongArch: Add awareness for LoongArch relocations


From: Xiaotian Wu
Subject: [PATCH v2 6/8] LoongArch: Add awareness for LoongArch relocations
Date: Wed, 23 Feb 2022 21:46:41 +0800

Signed-off-by: Xiaotian Wu <wuxiaotian@loongson.cn>
Signed-off-by: Zhou Yang <zhouyang@loongson.cn>
---
 grub-core/kern/dl.c                    |   9 +-
 grub-core/kern/loongarch64/dl.c        | 102 +++++++++++++
 grub-core/kern/loongarch64/dl_helper.c | 198 +++++++++++++++++++++++++
 include/grub/dl.h                      |   1 +
 util/grub-mkimagexx.c                  |  77 ++++++++++
 util/grub-module-verifier.c            |  26 ++++
 6 files changed, 410 insertions(+), 3 deletions(-)
 create mode 100644 grub-core/kern/loongarch64/dl.c
 create mode 100644 grub-core/kern/loongarch64/dl_helper.c

diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c
index 6a52de168..e58663279 100644
--- a/grub-core/kern/dl.c
+++ b/grub-core/kern/dl.c
@@ -225,7 +225,8 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
   unsigned i;
   const Elf_Shdr *s;
   grub_size_t tsize = 0, talign = 1;
-#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv)
+#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \
+  !defined (__loongarch__)
   grub_size_t tramp;
   grub_size_t got;
   grub_err_t err;
@@ -241,7 +242,8 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
        talign = s->sh_addralign;
     }
 
-#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv)
+#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \
+  !defined (__loongarch__)
   err = grub_arch_dl_get_tramp_got_size (e, &tramp, &got);
   if (err)
     return err;
@@ -304,7 +306,8 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
          mod->segment = seg;
        }
     }
-#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv)
+#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \
+  !defined (__loongarch__)
   ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, GRUB_ARCH_DL_TRAMP_ALIGN);
   mod->tramp = ptr;
   mod->trampptr = ptr;
diff --git a/grub-core/kern/loongarch64/dl.c b/grub-core/kern/loongarch64/dl.c
new file mode 100644
index 000000000..3a6aa91cd
--- /dev/null
+++ b/grub-core/kern/loongarch64/dl.c
@@ -0,0 +1,102 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2022 Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/dl.h>
+#include <grub/elf.h>
+#include <grub/misc.h>
+#include <grub/err.h>
+#include <grub/types.h>
+#include <grub/mm.h>
+#include <grub/i18n.h>
+#include <grub/cpu/reloc.h>
+
+/* Check if EHDR is a valid ELF header.  */
+grub_err_t
+grub_arch_dl_check_header (void *ehdr)
+{
+  Elf_Ehdr *e = ehdr;
+
+  /* Check the magic numbers.  */
+  if (e->e_ident[EI_CLASS] != ELFCLASS64
+      || e->e_ident[EI_DATA] != ELFDATA2LSB || e->e_machine != EM_LOONGARCH)
+    return grub_error (GRUB_ERR_BAD_OS, N_("invalid arch-dependent ELF 
magic"));
+
+  return GRUB_ERR_NONE;
+}
+
+#pragma GCC diagnostic ignored "-Wcast-align"
+
+/*
+ * Unified function for both REL and RELA.
+ */
+grub_err_t
+grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
+                              Elf_Shdr *s, grub_dl_segment_t seg)
+{
+  Elf_Rel *rel, *max;
+  struct grub_loongarch64_stack stack;
+  grub_loongarch64_stack_init (&stack);
+
+  for (rel = (Elf_Rel *) ((char *) ehdr + s->sh_offset),
+        max = (Elf_Rel *) ((char *) rel + s->sh_size);
+       rel < max;
+       rel = (Elf_Rel *) ((char *) rel + s->sh_entsize))
+    {
+      Elf_Sym *sym;
+      grub_uint64_t *place;
+      grub_uint64_t sym_addr;
+
+      if (rel->r_offset >= seg->size)
+       return grub_error (GRUB_ERR_BAD_MODULE,
+                          "reloc offset is outside the segment");
+
+      sym = (Elf_Sym *) ((char*)mod->symtab
+                        + mod->symsize * ELF_R_SYM (rel->r_info));
+
+      sym_addr = sym->st_value;
+      if (s->sh_type == SHT_RELA)
+       sym_addr += ((Elf_Rela *) rel)->r_addend;
+
+      place = (grub_uint64_t *) ((grub_addr_t)seg->addr + rel->r_offset);
+
+      switch (ELF_R_TYPE (rel->r_info))
+       {
+       case R_LARCH_64:
+         *place = sym_addr;
+         break;
+       case R_LARCH_MARK_LA:
+         break;
+       case R_LARCH_SOP_PUSH_PCREL:
+       case R_LARCH_SOP_PUSH_PLT_PCREL:
+         grub_loongarch64_sop_push (&stack, sym_addr - (grub_uint64_t)place);
+         break;
+       GRUB_LOONGARCH64_RELOCATION (&stack, place, sym_addr)
+       default:
+         {
+           char rel_info[17]; /* log16(2^64) = 16, plus NUL.  */
+
+           grub_snprintf (rel_info, sizeof (rel_info) - 1, "%" 
PRIxGRUB_UINT64_T,
+                          (grub_uint64_t) ELF_R_TYPE (rel->r_info));
+           return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+                              N_("relocation 0x%s is not implemented yet"), 
rel_info);
+         }
+         break;
+       }
+    }
+  return GRUB_ERR_NONE;
+}
diff --git a/grub-core/kern/loongarch64/dl_helper.c 
b/grub-core/kern/loongarch64/dl_helper.c
new file mode 100644
index 000000000..becc89565
--- /dev/null
+++ b/grub-core/kern/loongarch64/dl_helper.c
@@ -0,0 +1,198 @@
+/* dl_helper.c - relocation helper functions for modules and grub-mkimage */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2022 Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/dl.h>
+#include <grub/elf.h>
+#include <grub/misc.h>
+#include <grub/err.h>
+#include <grub/mm.h>
+#include <grub/i18n.h>
+#include <grub/cpu/reloc.h>
+
+static void grub_loongarch64_stack_push (grub_loongarch64_stack_t stack, 
grub_uint64_t x);
+static grub_uint64_t grub_loongarch64_stack_pop (grub_loongarch64_stack_t 
stack);
+
+void
+grub_loongarch64_stack_init (grub_loongarch64_stack_t stack)
+{
+  stack->top = -1;
+  stack->count = LOONGARCH64_STACK_MAX;
+}
+
+static void
+grub_loongarch64_stack_push (grub_loongarch64_stack_t stack, grub_uint64_t x)
+{
+  if (stack->top == stack->count)
+    return;
+  stack->data[++stack->top] = x;
+}
+
+static grub_uint64_t
+grub_loongarch64_stack_pop (grub_loongarch64_stack_t stack)
+{
+  if (stack->top == -1)
+    return -1;
+  return stack->data[stack->top--];
+}
+
+void
+grub_loongarch64_sop_push (grub_loongarch64_stack_t stack, grub_int64_t offset)
+{
+  grub_loongarch64_stack_push (stack, offset);
+}
+
+/* opr2 = pop (), opr1 = pop (), push (opr1 - opr2) */
+void
+grub_loongarch64_sop_sub (grub_loongarch64_stack_t stack)
+{
+  grub_uint64_t a, b;
+  b = grub_loongarch64_stack_pop (stack);
+  a = grub_loongarch64_stack_pop (stack);
+  grub_loongarch64_stack_push (stack, a - b);
+}
+
+/* opr2 = pop (), opr1 = pop (), push (opr1 << opr2) */
+void
+grub_loongarch64_sop_sl (grub_loongarch64_stack_t stack)
+{
+  grub_uint64_t a, b;
+  b = grub_loongarch64_stack_pop (stack);
+  a = grub_loongarch64_stack_pop (stack);
+  grub_loongarch64_stack_push (stack, a << b);
+}
+
+/* opr2 = pop (), opr1 = pop (), push (opr1 >> opr2) */
+void
+grub_loongarch64_sop_sr (grub_loongarch64_stack_t stack)
+{
+  grub_uint64_t a, b;
+  b = grub_loongarch64_stack_pop (stack);
+  a = grub_loongarch64_stack_pop (stack);
+  grub_loongarch64_stack_push (stack, a >> b);
+}
+
+/* opr2 = pop (), opr1 = pop (), push (opr1 + opr2) */
+void
+grub_loongarch64_sop_add (grub_loongarch64_stack_t stack)
+{
+  grub_uint64_t a, b;
+  b = grub_loongarch64_stack_pop (stack);
+  a = grub_loongarch64_stack_pop (stack);
+  grub_loongarch64_stack_push (stack, a + b);
+}
+
+/* opr2 = pop (), opr1 = pop (), push (opr1 & opr2) */
+void
+grub_loongarch64_sop_and (grub_loongarch64_stack_t stack)
+{
+  grub_uint64_t a, b;
+  b = grub_loongarch64_stack_pop (stack);
+  a = grub_loongarch64_stack_pop (stack);
+  grub_loongarch64_stack_push (stack, a & b);
+}
+
+/* opr3 = pop (), opr2 = pop (), opr1 = pop (), push (opr1 ? opr2 : opr3) */
+void
+grub_loongarch64_sop_if_else (grub_loongarch64_stack_t stack)
+{
+  grub_uint64_t a, b, c;
+  c = grub_loongarch64_stack_pop (stack);
+  b = grub_loongarch64_stack_pop (stack);
+  a = grub_loongarch64_stack_pop (stack);
+
+  if (a) {
+      grub_loongarch64_stack_push (stack, b);
+  } else {
+      grub_loongarch64_stack_push (stack, c);
+  }
+}
+
+/* opr1 = pop (), (*(uint32_t *) PC) [14 ... 10] = opr1 [4 ... 0] */
+void
+grub_loongarch64_sop_32_s_10_5 (grub_loongarch64_stack_t stack,
+                               grub_uint64_t *place)
+{
+  grub_uint64_t a = grub_loongarch64_stack_pop (stack);
+  *place |= ((a & 0x1f) << 10);
+}
+
+/* opr1 = pop (), (*(uint32_t *) PC) [21 ... 10] = opr1 [11 ... 0] */
+void
+grub_loongarch64_sop_32_u_10_12 (grub_loongarch64_stack_t stack,
+                                grub_uint64_t *place)
+{
+  grub_uint64_t a = grub_loongarch64_stack_pop (stack);
+  *place = *place | ((a & 0xfff) << 10);
+}
+
+/* opr1 = pop (), (*(uint32_t *) PC) [21 ... 10] = opr1 [11 ... 0] */
+void
+grub_loongarch64_sop_32_s_10_12 (grub_loongarch64_stack_t stack,
+                                grub_uint64_t *place)
+{
+  grub_uint64_t a = grub_loongarch64_stack_pop (stack);
+  *place = (*place) | ((a & 0xfff) << 10);
+}
+
+/* opr1 = pop (), (*(uint32_t *) PC) [25 ... 10] = opr1 [15 ... 0] */
+void
+grub_loongarch64_sop_32_s_10_16 (grub_loongarch64_stack_t stack,
+                                grub_uint64_t *place)
+{
+  grub_uint64_t a = grub_loongarch64_stack_pop (stack);
+  *place = (*place) | ((a & 0xffff) << 10);
+}
+
+/* opr1 = pop (), (*(uint32_t *) PC) [25 ... 10] = opr1 [17 ... 2] */
+void
+grub_loongarch64_sop_32_s_10_16_s2 (grub_loongarch64_stack_t stack,
+                                   grub_uint64_t *place)
+{
+  grub_uint64_t a = grub_loongarch64_stack_pop (stack);
+  *place = (*place) | (((a >> 2) & 0xffff) << 10);
+}
+
+/* opr1 = pop (), (*(uint32_t *) PC) [24 ... 5] = opr1 [19 ... 0] */
+void
+grub_loongarch64_sop_32_s_5_20 (grub_loongarch64_stack_t stack, grub_uint64_t 
*place)
+{
+  grub_uint64_t a = grub_loongarch64_stack_pop (stack);
+  *place = (*place) | ((a & 0xfffff)<<5);
+}
+
+/* opr1 = pop (), (*(uint32_t *) PC) [4 ... 0] = opr1 [22 ... 18] */
+void
+grub_loongarch64_sop_32_s_0_5_10_16_s2 (grub_loongarch64_stack_t stack,
+                                       grub_uint64_t *place)
+{
+  grub_uint64_t a = grub_loongarch64_stack_pop (stack);
+
+  *place =(*place) | (((a >> 2) & 0xffff) << 10);
+  *place =(*place) | ((a >> 18) & 0x1f);
+}
+
+/* opr1 = pop (), (*(uint32_t *) PC) [9 ... 0] = opr1 [27 ... 18] */
+void
+grub_loongarch64_sop_32_s_0_10_10_16_s2 (grub_loongarch64_stack_t stack,
+                                        grub_uint64_t *place)
+{
+  grub_uint64_t a = grub_loongarch64_stack_pop (stack);
+  *place =(*place) | (((a >> 2) & 0xffff) << 10);
+  *place =(*place) | ((a >> 18) & 0x3ff);
+}
diff --git a/include/grub/dl.h b/include/grub/dl.h
index b3753c9ca..3b9b19933 100644
--- a/include/grub/dl.h
+++ b/include/grub/dl.h
@@ -301,6 +301,7 @@ grub_arch_dl_get_tramp_got_size (const void *ehdr, 
grub_size_t *tramp,
 #endif
 
 #if defined (__aarch64__) || defined (__sparc__) || \
+    defined (__loongarch64) || \
     (defined(__riscv) && (__riscv_xlen == 64))
 #define GRUB_ARCH_DL_TRAMP_ALIGN 8
 #define GRUB_ARCH_DL_GOT_ALIGN 8
diff --git a/util/grub-mkimagexx.c b/util/grub-mkimagexx.c
index d78fa3e53..2136ee36f 100644
--- a/util/grub-mkimagexx.c
+++ b/util/grub-mkimagexx.c
@@ -44,6 +44,7 @@
 #include <grub/arm/reloc.h>
 #include <grub/arm64/reloc.h>
 #include <grub/ia64/reloc.h>
+#include <grub/loongarch64/reloc.h>
 #include <grub/osdep/hostfile.h>
 #include <grub/util/install.h>
 #include <grub/util/mkimage.h>
@@ -784,6 +785,8 @@ SUFFIX (relocate_addrs) (Elf_Ehdr *e, struct 
section_metadata *smd,
   struct grub_ia64_trampoline *tr = (void *) (pe_target + tramp_off);
   grub_uint64_t *gpptr = (void *) (pe_target + got_off);
   unsigned unmatched_adr_got_page = 0;
+  struct grub_loongarch64_stack stack;
+  grub_loongarch64_stack_init (&stack);
 #define MASK19 ((1 << 19) - 1)
 #else
   grub_uint32_t *tr = (void *) (pe_target + tramp_off);
@@ -1123,6 +1126,31 @@ SUFFIX (relocate_addrs) (Elf_Ehdr *e, struct 
section_metadata *smd,
                   }
               break;
               }
+            case EM_LOONGARCH:
+              {
+                sym_addr += addend;
+                switch (ELF_R_TYPE (info))
+                  {
+                  case R_LARCH_64:
+                    *target = grub_host_to_target64 (grub_target_to_host64 
(*target) + sym_addr);
+                    break;
+                  case R_LARCH_MARK_LA:
+                    break;
+                  case R_LARCH_SOP_PUSH_PCREL:
+                  case R_LARCH_SOP_PUSH_PLT_PCREL:
+                    grub_loongarch64_sop_push (&stack, sym_addr
+                                               -(target_section_addr
+                                                 +offset
+                                                 +image_target->vaddr_offset));
+                    break;
+                  GRUB_LOONGARCH64_RELOCATION (&stack, target, sym_addr)
+                  default:
+                    grub_util_error (_("relocation 0x%x is not implemented 
yet"),
+                                     (unsigned int) ELF_R_TYPE (info));
+                    break;
+                  }
+                break;
+              }
 #endif
 #if defined(MKIMAGE_ELF32)
             case EM_ARM:
@@ -1668,6 +1696,55 @@ translate_relocation_pe (struct translate_context *ctx,
        }
       break;
       break;
+    case EM_LOONGARCH:
+      switch (ELF_R_TYPE (info))
+       {
+       case R_LARCH_64:
+         {
+           ctx->current_address = add_fixup_entry (&ctx->lst,
+                                                   GRUB_PE32_REL_BASED_DIR64,
+                                                   addr, 0, 
ctx->current_address,
+                                                   image_target);
+         }
+         break;
+       case R_LARCH_MARK_LA:
+         {
+           ctx->current_address = add_fixup_entry (&ctx->lst,
+                                                   
GRUB_PE32_REL_BASED_LOONGARCH64_MARK_LA,
+                                                   addr, 0, 
ctx->current_address,
+                                                   image_target);
+         }
+         break;
+         /* Relative relocations do not require fixup entries. */
+       case R_LARCH_NONE:
+       case R_LARCH_SOP_PUSH_PCREL:
+       case R_LARCH_SOP_PUSH_ABSOLUTE:
+       case R_LARCH_SOP_PUSH_PLT_PCREL:
+       case R_LARCH_SOP_SUB:
+       case R_LARCH_SOP_SL:
+       case R_LARCH_SOP_SR:
+       case R_LARCH_SOP_ADD:
+       case R_LARCH_SOP_AND:
+       case R_LARCH_SOP_IF_ELSE:
+       case R_LARCH_SOP_POP_32_S_10_5:
+       case R_LARCH_SOP_POP_32_U_10_12:
+       case R_LARCH_SOP_POP_32_S_10_12:
+       case R_LARCH_SOP_POP_32_S_10_16:
+       case R_LARCH_SOP_POP_32_S_10_16_S2:
+       case R_LARCH_SOP_POP_32_S_5_20:
+       case R_LARCH_SOP_POP_32_S_0_5_10_16_S2:
+       case R_LARCH_SOP_POP_32_S_0_10_10_16_S2:
+         grub_util_info ("  %s:  not adding fixup: 0x%08x : 0x%08x",
+                         __FUNCTION__,
+                         (unsigned int) addr,
+                         (unsigned int) ctx->current_address);
+         break;
+       default:
+         grub_util_error (_("relocation 0x%x is not implemented yet"),
+                          (unsigned int) ELF_R_TYPE (info));
+         break;
+       }
+      break;
 #if defined(MKIMAGE_ELF32)
     case EM_ARM:
       switch (ELF_R_TYPE (info))
diff --git a/util/grub-module-verifier.c b/util/grub-module-verifier.c
index 163529ca9..b510461fa 100644
--- a/util/grub-module-verifier.c
+++ b/util/grub-module-verifier.c
@@ -119,6 +119,32 @@ struct grub_module_verifier_arch archs[] = {
       R_AARCH64_PREL32,
       -1
     } },
+  { "loongarch64", 8, 0, EM_LOONGARCH, GRUB_MODULE_VERIFY_SUPPORTS_REL | 
GRUB_MODULE_VERIFY_SUPPORTS_RELA, (int[]){
+      R_LARCH_NONE,
+      R_LARCH_64,
+      R_LARCH_MARK_LA,
+      R_LARCH_SOP_PUSH_PCREL,
+      R_LARCH_SOP_PUSH_ABSOLUTE,
+      R_LARCH_SOP_PUSH_PLT_PCREL,
+      R_LARCH_SOP_SUB,
+      R_LARCH_SOP_SL,
+      R_LARCH_SOP_SR,
+      R_LARCH_SOP_ADD,
+      R_LARCH_SOP_AND,
+      R_LARCH_SOP_IF_ELSE,
+      R_LARCH_SOP_POP_32_S_10_5,
+      R_LARCH_SOP_POP_32_U_10_12,
+      R_LARCH_SOP_POP_32_S_10_12,
+      R_LARCH_SOP_POP_32_S_10_16,
+      R_LARCH_SOP_POP_32_S_10_16_S2,
+      R_LARCH_SOP_POP_32_S_5_20,
+      R_LARCH_SOP_POP_32_S_0_5_10_16_S2,
+      R_LARCH_SOP_POP_32_S_0_10_10_16_S2,
+      -1
+    }, (int[]){
+      -1
+    }
+  },
   { "riscv32", 4, 0, EM_RISCV, GRUB_MODULE_VERIFY_SUPPORTS_REL | 
GRUB_MODULE_VERIFY_SUPPORTS_RELA, (int[]){
       R_RISCV_32,
       R_RISCV_64,
-- 
2.32.0




reply via email to

[Prev in Thread] Current Thread [Next in Thread]