Signed-off-by: Xiaotian Wu <wuxiaot...@loongson.cn>
---
 conf/Makefile.common                   |  3 +
 configure.ac                           | 14 +++--
 grub-core/kern/dl.c                    |  9 +--
 grub-core/kern/loongarch64/dl.c        | 71 ++++++++++++++++++++-
 grub-core/kern/loongarch64/dl_helper.c | 68 ++++++++++++++++++++
 include/grub/elf.h                     |  6 ++
 include/grub/loongarch64/reloc.h       |  4 ++
 util/grub-mkimagexx.c                  | 87 +++++++++++++++++++++++++-
 util/grub-module-verifier.c            |  5 ++
 9 files changed, 250 insertions(+), 17 deletions(-)

diff --git a/conf/Makefile.common b/conf/Makefile.common
index 2d8f1bf2e..77f20e441 100644
--- a/conf/Makefile.common
+++ b/conf/Makefile.common
@@ -17,6 +17,9 @@ endif
 if COND_arm64
   CFLAGS_PLATFORM += -mcmodel=large
 endif
+if COND_loongarch64
+  CFLAGS_PLATFORM += -mcmodel=large
+endif
 if COND_powerpc_ieee1275
   CFLAGS_PLATFORM += -mcpu=powerpc
 endif
diff --git a/configure.ac b/configure.ac
index 71f65a70d..c8daf5dfb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -859,14 +859,16 @@ if ( test "x$target_cpu" = xi386 || test "x$target_cpu" = 
xx86_64 ); then
 fi
 
 if test "x$target_cpu" = xloongarch64; then
-  AC_CACHE_CHECK([whether -Wa,-mla-global-with-abs works], 
[grub_cv_cc_mla_global_with_abs], [
-    CFLAGS="$TARGET_CFLAGS -Wa,-mla-global-with-abs -Werror"
+  AC_CACHE_CHECK([whether _mno_explicit_relocs works], 
[grub_cv_cc_mno_explicit_relocs], [
+    CFLAGS="$TARGET_CFLAGS -mno-explicit-relocs -Werror"
     AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])],
-       [grub_cv_cc_mla_global_with_abs=yes],
-       [grub_cv_cc_mla_global_with_abs=no])
+       [grub_cv_cc_mno_explicit_relocs=yes],
+       [grub_cv_cc_mno_explicit_relocs=no])
   ])
-
-  if test "x$grub_cv_cc_mla_global_with_abs" = xyes; then
+  if test "x$grub_cv_cc_mno_explicit_relocs" = xyes; then
+    TARGET_CFLAGS="$TARGET_CFLAGS -mno-explicit-relocs"
+    TARGET_CCASFLAGS="$TARGET_CCASFLAGS -mno-explicit-relocs"
+  else
     TARGET_CFLAGS="$TARGET_CFLAGS -Wa,-mla-global-with-abs"
     TARGET_CCASFLAGS="$TARGET_CCASFLAGS -Wa,-mla-global-with-abs"
   fi
diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c
index 0bf40caa6..e447fd0fa 100644
--- a/grub-core/kern/dl.c
+++ b/grub-core/kern/dl.c
@@ -225,8 +225,7 @@ 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) && \
-  !defined (__loongarch__)
+#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv)
   grub_size_t tramp;
   grub_size_t got;
   grub_err_t err;
@@ -242,8 +241,7 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
        talign = s->sh_addralign;
     }
 
-#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \
-  !defined (__loongarch__)
+#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv)
   err = grub_arch_dl_get_tramp_got_size (e, &tramp, &got);
   if (err)
     return err;
@@ -306,8 +304,7 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
          mod->segment = seg;
        }
     }
-#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \
-  !defined (__loongarch__)
+#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv)
   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
index 3a6aa91cd..20be4f5d5 100644
--- a/grub-core/kern/loongarch64/dl.c
+++ b/grub-core/kern/loongarch64/dl.c
@@ -49,6 +49,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
                               Elf_Shdr *s, grub_dl_segment_t seg)
 {
   Elf_Rel *rel, *max;
+  unsigned unmatched_got_pc_page = 0;
   struct grub_loongarch64_stack stack;
   grub_loongarch64_stack_init (&stack);
 
@@ -58,7 +59,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
        rel = (Elf_Rel *) ((char *) rel + s->sh_entsize))
     {
       Elf_Sym *sym;
-      grub_uint64_t *place;
+      void *place;
       grub_uint64_t sym_addr;
 
       if (rel->r_offset >= seg->size)
@@ -72,12 +73,19 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
       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);
+      place = (void *) ((grub_addr_t)seg->addr + rel->r_offset);
 
       switch (ELF_R_TYPE (rel->r_info))
        {
        case R_LARCH_64:
-         *place = sym_addr;
+         {
+           grub_uint64_t *abs_place = place;
+
+           grub_dprintf ("dl", "reloc_abs64 %p => 0x%016llx, %p\n",
+                         place, (unsigned long long) sym_addr, abs_place);
+
+           *abs_place += (grub_uint64_t) sym_addr;
+         }
          break;
        case R_LARCH_MARK_LA:
          break;
@@ -85,6 +93,63 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
        case R_LARCH_SOP_PUSH_PLT_PCREL:
          grub_loongarch64_sop_push (&stack, sym_addr - (grub_uint64_t)place);
          break;
+       case R_LARCH_B26:
+         {
+           grub_uint32_t *abs_place = place;
+           grub_ssize_t off = sym_addr - (grub_addr_t) place;
+
+           grub_loongarch64_b26 (abs_place, off);
+         }
+         break;
+       case R_LARCH_PCALA_HI20:
+         {
+           grub_uint32_t *abs_place = place;
+           grub_int32_t off = (((sym_addr + 0x800) & ~0xfffULL) - 
((grub_addr_t)place & ~0xfffULL));
+
+           grub_loongarch64_xxx_hi20 (abs_place, off);
+         }
+         break;
+       case R_LARCH_PCALA_LO12:
+         {
+           grub_uint32_t *abs_place = place;
+           grub_loongarch64_xxx_lo12 (abs_place, sym_addr);
+         }
+         break;
+       case R_LARCH_GOT_PC_HI20:
+         {
+           grub_uint64_t *gp = mod->gotptr;
+           grub_int64_t gpoffset;
+           Elf_Rela *rel2;
+
+           gpoffset = (((grub_uint64_t) gp + 0x800) & ~0xfff) - 
(((grub_uint64_t) place) & ~0xfff);
+
+           *gp = (grub_uint64_t) sym_addr;
+           mod->gotptr = gp + 1;
+           unmatched_got_pc_page++;
+           grub_loongarch64_xxx_hi20 (place, gpoffset);
+           for (rel2 = (Elf_Rela *) ((char *) rel + s->sh_entsize);
+                rel2 < (Elf_Rela *) max;
+                rel2 = (Elf_Rela *) ((char *) rel2 + s->sh_entsize))
+             if (ELF_R_SYM (rel2->r_info) == ELF_R_SYM (rel->r_info)
+                 && ((Elf_Rela *) rel)->r_addend == rel2->r_addend
+                 && ELF_R_TYPE (rel2->r_info) == R_LARCH_GOT_PC_LO12)
+               {
+                 grub_uint32_t *place2;
+                  place2 = (grub_uint32_t *) ((grub_addr_t) seg->addr + 
rel2->r_offset);
+                 grub_loongarch64_xxx_lo12 (place2, (grub_uint64_t) gp);
+                 break;
+               }
+           if (rel2 >= (Elf_Rela *) max)
+             return grub_error (GRUB_ERR_BAD_MODULE,
+                                "GOT_PC_HI20 without matching GOT_PC_LO12");
+         }
+         break;
+       case R_LARCH_GOT_PC_LO12:
+         if (unmatched_got_pc_page == 0)
+           return grub_error (GRUB_ERR_BAD_MODULE,
+                              "GOT_PC_LO12 without matching GOT_PC_HI2");
+         unmatched_got_pc_page--;
+         break;
        GRUB_LOONGARCH64_RELOCATION (&stack, place, sym_addr)
        default:
          {
diff --git a/grub-core/kern/loongarch64/dl_helper.c 
b/grub-core/kern/loongarch64/dl_helper.c
index 627d1fff2..a5b5b8ee9 100644
--- a/grub-core/kern/loongarch64/dl_helper.c
+++ b/grub-core/kern/loongarch64/dl_helper.c
@@ -200,3 +200,71 @@ grub_loongarch64_sop_32_s_0_10_10_16_s2 
(grub_loongarch64_stack_t stack,
   *place =(*place) | (((a >> 2) & 0xffff) << 10);
   *place =(*place) | ((a >> 18) & 0x3ff);
 }
+
+void grub_loongarch64_b26 (grub_uint32_t *place, grub_int64_t offset)
+{
+  grub_uint32_t val;
+  const grub_uint32_t insmask = grub_cpu_to_le32_compile_time (0xfc000000);
+
+  grub_dprintf ("dl", "  reloc_xxxx64 %p %c= 0x%llx\n",
+               place, offset > 0 ? '+' : '-',
+               offset < 0 ? (long long) -(unsigned long long) offset : offset);
+
+  val = ((offset >> 18) & 0x3ff) | (((offset >> 2) & 0xffff) << 10);
+
+  *place &= insmask;
+  *place |= grub_cpu_to_le32 (val) & ~insmask;
+}
+
+void grub_loongarch64_xxx_hi20 (grub_uint32_t *place, grub_int64_t offset)
+{
+  const grub_uint32_t insmask = grub_cpu_to_le32_compile_time (0xfe00001f);
+  grub_uint32_t val;
+
+  offset >>= 12;
+  val = ((offset & 0xfffff) << 5);
+
+  *place &= insmask;
+  *place |= grub_cpu_to_le32 (val) & ~insmask;
+}
+
+void grub_loongarch64_xxx_lo12 (grub_uint32_t *place, grub_int64_t offset)
+{
+  const grub_uint32_t insmask = grub_cpu_to_le32_compile_time (0xffc003ff);
+
+  *place &= insmask;
+  *place |= grub_cpu_to_le32 (offset << 10) & ~insmask;
+}
+
+grub_err_t
+grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp,
+                                grub_size_t *got)
+{
+  const Elf64_Ehdr *e = ehdr;
+  const Elf64_Shdr *s;
+  unsigned i;
+
+  *tramp = 0;
+  *got = 0;
+
+  for (i = 0, s = (Elf64_Shdr *) ((char *) e + grub_le_to_cpu64 (e->e_shoff));
+       i < grub_le_to_cpu16 (e->e_shnum);
+       i++, s = (Elf64_Shdr *) ((char *) s + grub_le_to_cpu16 
(e->e_shentsize)))
+    if (s->sh_type == grub_cpu_to_le32_compile_time (SHT_RELA))
+      {
+       const Elf64_Rela *rel, *max;
+
+       for (rel = (Elf64_Rela *) ((char *) e + grub_le_to_cpu64 
(s->sh_offset)),
+              max = (const Elf64_Rela *) ((char *) rel + grub_le_to_cpu64 
(s->sh_size));
+            rel < max; rel = (const Elf64_Rela *) ((char *) rel + 
grub_le_to_cpu64 (s->sh_entsize)))
+         switch (ELF64_R_TYPE (grub_le_to_cpu64 (rel->r_info)))
+           {
+           case R_LARCH_GOT_PC_HI20:
+           case R_LARCH_GOT_PC_LO12:
+             *got += 8;
+             break;
+           }
+      }
+
+  return GRUB_ERR_NONE;
+}
diff --git a/include/grub/elf.h b/include/grub/elf.h
index 1c8d4f5d5..5c17058f3 100644
--- a/include/grub/elf.h
+++ b/include/grub/elf.h
@@ -2554,6 +2554,12 @@ typedef Elf32_Addr Elf32_Conflict;
 #define R_LARCH_SOP_POP_32_S_0_5_10_16_S2     44
 #define R_LARCH_SOP_POP_32_S_0_10_10_16_S2    45
 
+#define R_LARCH_B26                          66
+#define R_LARCH_PCALA_HI20                   71
+#define R_LARCH_PCALA_LO12                   72
+#define R_LARCH_GOT_PC_HI20                  75
+#define R_LARCH_GOT_PC_LO12                  76
+
 #ifdef GRUB_TARGET_WORDSIZE
 #if GRUB_TARGET_WORDSIZE == 32
 
diff --git a/include/grub/loongarch64/reloc.h b/include/grub/loongarch64/reloc.h
index 0b7d66ddc..ece565491 100644
--- a/include/grub/loongarch64/reloc.h
+++ b/include/grub/loongarch64/reloc.h
@@ -57,6 +57,10 @@ void grub_loongarch64_sop_32_s_0_5_10_16_s2  
(grub_loongarch64_stack_t stack,
 void grub_loongarch64_sop_32_s_0_10_10_16_s2 (grub_loongarch64_stack_t stack,
                                              grub_uint64_t *place);
 
+void grub_loongarch64_b26        (grub_uint32_t *place, grub_int64_t offset);
+void grub_loongarch64_xxx_hi20   (grub_uint32_t *place, grub_int64_t offset);
+void grub_loongarch64_xxx_lo12           (grub_uint32_t *place, grub_int64_t 
offset);
+
 #define GRUB_LOONGARCH64_RELOCATION(STACK, PLACE, OFFSET)      \
   case R_LARCH_SOP_PUSH_ABSOLUTE:                              \
     grub_loongarch64_sop_push (STACK, OFFSET);                 \
diff --git a/util/grub-mkimagexx.c b/util/grub-mkimagexx.c
index 358b9ab47..291015011 100644
--- a/util/grub-mkimagexx.c
+++ b/util/grub-mkimagexx.c
@@ -1128,11 +1128,20 @@ SUFFIX (relocate_addrs) (Elf_Ehdr *e, struct 
section_metadata *smd,
               }
             case EM_LOONGARCH:
               {
+                grub_int64_t pc;
+
+                grub_uint32_t *t32 = (grub_uint32_t *) target;
                 sym_addr += addend;
+
+                pc = offset + target_section_addr + image_target->vaddr_offset;
+
                 switch (ELF_R_TYPE (info))
                   {
                   case R_LARCH_64:
-                    *target = grub_host_to_target64 (grub_target_to_host64 
(*target) + sym_addr);
+                    {
+                      grub_uint64_t *t64 = (grub_uint64_t *) target;
+                      *t64 = grub_host_to_target64 (grub_target_to_host64 
(*t64) + sym_addr);
+                    }
                     break;
                   case R_LARCH_MARK_LA:
                     break;
@@ -1143,6 +1152,62 @@ SUFFIX (relocate_addrs) (Elf_Ehdr *e, struct 
section_metadata *smd,
                                                  +offset
                                                  +image_target->vaddr_offset));
                     break;
+                  case R_LARCH_B26:
+                    {
+                      grub_int64_t off;
+
+                      off = sym_addr - target_section_addr - offset - 
image_target->vaddr_offset;
+
+                      grub_loongarch64_b26 (t32, off);
+                    }
+                    break;
+                  case R_LARCH_PCALA_HI20:
+                    {
+                      grub_int32_t hi20;
+
+                      hi20 = (((sym_addr + 0x800) & ~0xfffULL) - (pc & 
~0xfffULL));
+
+                      grub_loongarch64_xxx_hi20 (t32, hi20);
+                    }
+                    break;
+                  case R_LARCH_PCALA_LO12:
+                    grub_loongarch64_xxx_lo12 (t32, sym_addr);
+                    break;
+                  case R_LARCH_GOT_PC_HI20:
+                    {
+                      Elf64_Rela *rel2;
+                      grub_int32_t hi20;
+                      unsigned k;
+
+                      grub_int64_t gpoffset = (char *) gpptr - (char *) 
pe_target + image_target->vaddr_offset;
+
+                      hi20 = ((gpoffset + 0x800) & ~0xfffULL) - (pc & 
~0xfffULL);
+
+                      *gpptr = grub_host_to_target64 (sym_addr);
+                      unmatched_adr_got_page++;
+                      grub_loongarch64_xxx_hi20 ((grub_uint32_t *)target, 
hi20);
+                      for (k = 0, rel2 = (Elf_Rela *) ((char *) r + r_size);
+                           k < num_rs;
+                           k++, rel2 = (Elf_Rela *) ((char *) rel2 + r_size))
+                        if (ELF_R_SYM (rel2->r_info) == ELF_R_SYM (r->r_info)
+                            && r->r_addend == rel2->r_addend
+                            && ELF_R_TYPE (rel2->r_info) == 
R_LARCH_GOT_PC_LO12)
+                          {
+                            t32 = (grub_uint32_t *) SUFFIX 
(get_target_address) (e, target_section,
+                                                                               
  grub_target_to_host (rel2->r_offset), image_target),
+                            grub_loongarch64_xxx_lo12 (t32, gpoffset);
+                            break;
+                          }
+                      if (k >= num_rs)
+                        grub_util_error ("GOT_PC_HI20 without matching 
GOT_PC_LO12");
+                      gpptr++;
+                    }
+                    break;
+                  case R_LARCH_GOT_PC_LO12:
+                    if (unmatched_adr_got_page == 0)
+                      grub_util_error ("GOT_PC_LO12 without matching 
GOT_PC_HI2");
+                    unmatched_adr_got_page--;
+                    break;
                   GRUB_LOONGARCH64_RELOCATION (&stack, target, sym_addr)
                   default:
                     grub_util_error (_("relocation 0x%x is not implemented 
yet"),
@@ -1736,6 +1801,11 @@ translate_relocation_pe (struct translate_context *ctx,
        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:
+       case R_LARCH_B26:
+       case R_LARCH_PCALA_HI20:
+       case R_LARCH_PCALA_LO12:
+       case R_LARCH_GOT_PC_HI20:
+       case R_LARCH_GOT_PC_LO12:
          grub_util_info ("  %s:  not adding fixup: 0x%08x : 0x%08x",
                          __FUNCTION__,
                          (unsigned int) addr,
@@ -2095,7 +2165,7 @@ make_reloc_section (Elf_Ehdr *e, struct 
grub_mkimage_layout *layout,
                       + image_target->vaddr_offset,
                       2 * layout->ia64jmpnum,
                       image_target);
-  if (image_target->elf_target == EM_IA_64 || image_target->elf_target == 
EM_AARCH64)
+  if (image_target->elf_target == EM_IA_64 || image_target->elf_target == 
EM_AARCH64 || image_target->elf_target == EM_LOONGARCH)
     create_u64_fixups (&ctx,
                       layout->got_off
                       + image_target->vaddr_offset,
@@ -2246,6 +2316,8 @@ SUFFIX (locate_sections) (Elf_Ehdr *e, const char 
*kernel_path,
   /* Page-aligning simplifies relocation handling.  */
   if (image_target->elf_target == EM_AARCH64)
     layout->align = 4096;
+  if (image_target->elf_target == EM_LOONGARCH)
+    layout->align = 4096;
 
   layout->kernel_size = 0;
 
@@ -2464,6 +2536,17 @@ SUFFIX (grub_mkimage_load_image) (const char 
*kernel_path,
 
          grub_arm64_dl_get_tramp_got_size (e, &tramp, &layout->got_size);
 
+         layout->got_off = layout->kernel_size;
+         layout->kernel_size += ALIGN_UP (layout->got_size, 16);
+       }
+      if (image_target->elf_target == EM_LOONGARCH)
+       {
+         grub_size_t tramp;
+
+         layout->kernel_size = ALIGN_UP (layout->kernel_size, 16);
+
+         grub_arch_dl_get_tramp_got_size (e, &tramp, &layout->got_size);
+
          layout->got_off = layout->kernel_size;
          layout->kernel_size += ALIGN_UP (layout->got_size, 16);
        }
diff --git a/util/grub-module-verifier.c b/util/grub-module-verifier.c
index b510461fa..88351e7eb 100644
--- a/util/grub-module-verifier.c
+++ b/util/grub-module-verifier.c
@@ -140,6 +140,11 @@ struct grub_module_verifier_arch archs[] = {
       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,
+      R_LARCH_B26,
+      R_LARCH_PCALA_HI20,
+      R_LARCH_PCALA_LO12,
+      R_LARCH_GOT_PC_HI20,
+      R_LARCH_GOT_PC_LO12,
       -1
     }, (int[]){
       -1
-- 
2.35.1


_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

Reply via email to