加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
LoongArch-Add-back-compatibility-for-linux-kernel.patch 68.12 KB
一键复制 编辑 原始数据 按行查看 历史
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192
From 28dcf484821b95d7de85de33dbd41ee7682e5778 Mon Sep 17 00:00:00 2001
From: Xue Liu <liuxue@loongson.cn>
Date: Fri, 24 May 2024 14:16:02 +0800
Subject: [PATCH] LoongArch: Add back-compatibility for linux kernel.
---
configure.ac | 4 +-
grub-core/Makefile.core.def | 7 +-
grub-core/kern/efi/mm.c | 33 +-
grub-core/lib/loongarch64/relocator.c | 163 ++++
grub-core/lib/loongarch64/relocator_asm.S | 51 ++
grub-core/loader/loongarch64/linux-efi.c | 102 +++
grub-core/loader/loongarch64/linux-elf.c | 896 ++++++++++++++++++++++
grub-core/loader/loongarch64/linux.c | 437 +++++++++++
include/grub/loongarch64/efi/memory.h | 9 +-
include/grub/loongarch64/linux.h | 215 ++++++
include/grub/loongarch64/memory.h | 59 ++
include/grub/loongarch64/relocator.h | 38 +
12 files changed, 2008 insertions(+), 6 deletions(-)
create mode 100644 grub-core/lib/loongarch64/relocator.c
create mode 100644 grub-core/lib/loongarch64/relocator_asm.S
create mode 100644 grub-core/loader/loongarch64/linux-efi.c
create mode 100644 grub-core/loader/loongarch64/linux-elf.c
create mode 100644 grub-core/loader/loongarch64/linux.c
create mode 100644 include/grub/loongarch64/linux.h
create mode 100644 include/grub/loongarch64/memory.h
create mode 100644 include/grub/loongarch64/relocator.h
diff --git a/configure.ac b/configure.ac
index 71a2424a140d..96f4f5e9e554 100644
--- a/configure.ac
+++ b/configure.ac
@@ -146,7 +146,9 @@ case "$target_cpu" in
;;
arm*) target_cpu=arm ;;
aarch64*) target_cpu=arm64 ;;
- loongarch64) target_cpu=loongarch64 ;;
+ loongarch64) target_cpu=loongarch64
+ machine_CPPFLAGS="$machine_CPPFLAGS -DGRUB_CPU_LOONGARCH64=1"
+ ;;
riscv32*) target_cpu=riscv32 ;;
riscv64*) target_cpu=riscv64 ;;
esac
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index cc33ca57af8b..7f025e5cfcd6 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -1730,6 +1730,8 @@ module = {
x86_64_xen = lib/x86_64/xen/relocator.S;
xen = lib/i386/relocator_common_c.c;
x86_64_efi = lib/x86_64/efi/relocator.c;
+ loongarch64 = lib/loongarch64/relocator_asm.S;
+ loongarch64 = lib/loongarch64/relocator.c;
extra_dist = lib/i386/relocator_common.S;
extra_dist = kern/powerpc/cache_flush.S;
@@ -1739,6 +1741,7 @@ module = {
enable = x86;
enable = i386_xen_pvh;
enable = xen;
+ enable = loongarch64;
};
module = {
@@ -1875,7 +1878,9 @@ module = {
arm_efi = loader/efi/linux.c;
arm_uboot = loader/arm/linux.c;
arm64 = loader/arm64/efi/linux.c;
- loongarch64 = loader/efi/linux.c;
+ loongarch64 = loader/loongarch64/linux.c;
+ loongarch64 = loader/loongarch64/linux-efi.c;
+ loongarch64 = loader/loongarch64/linux-elf.c;
riscv32 = loader/efi/linux.c;
riscv64 = loader/efi/linux.c;
emu = loader/emu/linux.c;
diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c
index 9b1d3add715b..31bffb534414 100644
--- a/grub-core/kern/efi/mm.c
+++ b/grub-core/kern/efi/mm.c
@@ -39,7 +39,11 @@
#define MEMORY_MAP_SIZE 0x3000
/* The default heap size for GRUB itself in bytes. */
+#ifdef GRUB_CPU_LOONGARCH64
+#define DEFAULT_HEAP_SIZE 0x10000000
+#else
#define DEFAULT_HEAP_SIZE 0x2000000
+#endif
static void *finish_mmap_buf = 0;
static grub_efi_uintn_t finish_mmap_size = 0;
@@ -156,7 +160,11 @@ grub_efi_allocate_pages_real (grub_efi_physical_address_t address,
grub_efi_physical_address_t ret = address;
/* Limit the memory access to less than 4GB for 32-bit platforms. */
+#ifdef GRUB_CPU_LOONGARCH64
+ if (address > grub_efi_max_usable_address())
+#else
if (address > GRUB_EFI_MAX_USABLE_ADDRESS)
+#endif
{
char inv_addr[17], max_addr[17]; /* log16(2^64) = 16, plus NUL. */
@@ -184,7 +192,11 @@ grub_efi_allocate_pages_real (grub_efi_physical_address_t address,
{
/* Uggh, the address 0 was allocated... This is too annoying,
so reallocate another one. */
+#ifdef GRUB_CPU_LOONGARCH64
+ ret = grub_efi_max_usable_address();
+#else
ret = address;
+#endif
status = b->allocate_pages (alloctype, memtype, pages, &ret);
grub_efi_free_pages (0, pages);
if (status != GRUB_EFI_SUCCESS)
@@ -202,9 +214,15 @@ grub_efi_allocate_pages_real (grub_efi_physical_address_t address,
void *
grub_efi_allocate_any_pages (grub_efi_uintn_t pages)
{
+#ifdef GRUB_CPU_LOONGARCH64
+ return grub_efi_allocate_pages_real (grub_efi_max_usable_address(),
+ pages, GRUB_EFI_ALLOCATE_MAX_ADDRESS,
+ GRUB_EFI_LOADER_DATA);
+#else
return grub_efi_allocate_pages_real (GRUB_EFI_MAX_USABLE_ADDRESS,
pages, GRUB_EFI_ALLOCATE_MAX_ADDRESS,
GRUB_EFI_LOADER_DATA);
+#endif
}
void *
@@ -480,8 +498,10 @@ filter_memory_map (grub_efi_memory_descriptor_t *memory_map,
desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size))
{
if (desc->type == GRUB_EFI_CONVENTIONAL_MEMORY
-#if 1
- && desc->physical_start <= GRUB_EFI_MAX_ALLOCATION_ADDRESS
+#ifdef GRUB_CPU_LOONGARCH64
+ && desc->physical_start <= grub_efi_max_usable_address()
+#else
+ && desc->physical_start <= GRUB_EFI_MAX_USABLE_ADDRESS
#endif
&& desc->physical_start + PAGES_TO_BYTES (desc->num_pages) > 0x100000
&& desc->num_pages != 0)
@@ -496,7 +516,14 @@ filter_memory_map (grub_efi_memory_descriptor_t *memory_map,
desc->physical_start = 0x100000;
}
-#if 1
+#ifdef GRUB_CPU_LOONGARCH64
+ if (BYTES_TO_PAGES (filtered_desc->physical_start)
+ + filtered_desc->num_pages
+ > BYTES_TO_PAGES_DOWN (grub_efi_max_usable_address()))
+ filtered_desc->num_pages
+ = (BYTES_TO_PAGES_DOWN (grub_efi_max_usable_address())
+ - BYTES_TO_PAGES (filtered_desc->physical_start));
+#else
if (BYTES_TO_PAGES (filtered_desc->physical_start)
+ filtered_desc->num_pages
> BYTES_TO_PAGES_DOWN (GRUB_EFI_MAX_ALLOCATION_ADDRESS))
diff --git a/grub-core/lib/loongarch64/relocator.c b/grub-core/lib/loongarch64/relocator.c
new file mode 100644
index 000000000000..587fc585ab7a
--- /dev/null
+++ b/grub-core/lib/loongarch64/relocator.c
@@ -0,0 +1,163 @@
+/*
+ * 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/mm.h>
+#include <grub/misc.h>
+
+#include <grub/types.h>
+#include <grub/types.h>
+#include <grub/err.h>
+#include <grub/cache.h>
+
+#include <grub/loongarch64/relocator.h>
+#include <grub/relocator_private.h>
+
+extern grub_uint8_t grub_relocator_forward_start;
+extern grub_uint8_t grub_relocator_forward_end;
+extern grub_uint8_t grub_relocator_backward_start;
+extern grub_uint8_t grub_relocator_backward_end;
+
+#define REGW_SIZEOF (4 * sizeof (grub_uint32_t))
+#define JUMP_SIZEOF (2 * sizeof (grub_uint32_t))
+
+#define RELOCATOR_SRC_SIZEOF(x) (&grub_relocator_##x##_end \
+ - &grub_relocator_##x##_start)
+#define RELOCATOR_SIZEOF(x) (RELOCATOR_SRC_SIZEOF(x) \
+ + REGW_SIZEOF * 3)
+grub_size_t grub_relocator_align = sizeof (grub_uint64_t);
+grub_size_t grub_relocator_forward_size;
+grub_size_t grub_relocator_backward_size;
+grub_size_t grub_relocator_jumper_size = JUMP_SIZEOF + REGW_SIZEOF;
+
+void
+grub_cpu_relocator_init (void)
+{
+ grub_relocator_forward_size = RELOCATOR_SIZEOF(forward);
+ grub_relocator_backward_size = RELOCATOR_SIZEOF(backward);
+}
+
+static void
+write_reg (int regn, grub_uint64_t val, void **target)
+{
+ grub_uint32_t lu12iw=0x14000000;
+ grub_uint32_t ori=0x03800000;
+ grub_uint32_t lu32id=0x16000000;
+ grub_uint32_t lu52id=0x03000000;
+
+ *(grub_uint32_t *) *target = (lu12iw | (grub_uint32_t)((val & 0xfffff000)>>12<<5) | (grub_uint32_t)regn);;
+ *target = ((grub_uint32_t *) *target) + 1;
+ *(grub_uint32_t *) *target = (ori | (grub_uint32_t)((val & 0xfff)<<10) | (grub_uint32_t)(regn | regn<<5));
+ *target = ((grub_uint32_t *) *target) + 1;
+ *(grub_uint32_t *) *target = (lu32id | (grub_uint32_t)((val & 0xfffff00000000)>>32<<5) | (grub_uint32_t)regn);;
+ *target = ((grub_uint32_t *) *target) + 1;
+ *(grub_uint32_t *) *target = (lu52id | (grub_uint32_t)((val & 0xfff0000000000000)>>52<<10) | (grub_uint32_t)(regn | regn<<5));;
+ *target = ((grub_uint32_t *) *target) + 1;
+}
+
+static void
+write_jump (int regn, void **target)
+{
+ grub_uint32_t jirl=0x4c000000;
+
+ *(grub_uint32_t *) *target = (jirl | (grub_uint32_t)(regn<<5));
+ *target = ((grub_uint32_t *) *target) + 1;
+}
+
+void
+grub_cpu_relocator_jumper (void *rels, grub_addr_t addr)
+{
+ write_reg (1, addr, &rels);
+ write_jump (1, &rels);
+}
+
+void
+grub_cpu_relocator_backward (void *ptr0, void *src, void *dest,
+ grub_size_t size)
+{
+ void *ptr = ptr0;
+ write_reg (8, (grub_uint64_t) src, &ptr);
+ write_reg (9, (grub_uint64_t) dest, &ptr);
+ write_reg (10, (grub_uint64_t) size, &ptr);
+ grub_memcpy (ptr, &grub_relocator_backward_start,
+ RELOCATOR_SRC_SIZEOF (backward));
+}
+
+void
+grub_cpu_relocator_forward (void *ptr0, void *src, void *dest,
+ grub_size_t size)
+{
+ void *ptr = ptr0;
+ write_reg (8, (grub_uint64_t) src, &ptr);
+ write_reg (9, (grub_uint64_t) dest, &ptr);
+ write_reg (10, (grub_uint64_t) size, &ptr);
+ grub_memcpy (ptr, &grub_relocator_forward_start,
+ RELOCATOR_SRC_SIZEOF (forward));
+}
+
+grub_err_t
+grub_relocator64_boot (struct grub_relocator *rel,
+ struct grub_relocator64_state state)
+{
+ grub_relocator_chunk_t ch;
+ void *ptr;
+ grub_err_t err;
+ void *relst;
+ grub_size_t relsize;
+ grub_size_t stateset_size = 31 * REGW_SIZEOF + JUMP_SIZEOF;
+ unsigned i;
+ grub_addr_t vtarget;
+
+ err = grub_relocator_alloc_chunk_align (rel, &ch, 0x3000000,
+ (0xffffffff - stateset_size)
+ + 1, stateset_size,
+ grub_relocator_align,
+ GRUB_RELOCATOR_PREFERENCE_NONE, 0);
+ if (err)
+ return err;
+
+ ptr = get_virtual_current_address (ch);
+ for (i = 1; i < 32; i++)
+ write_reg (i, state.gpr[i], &ptr);
+ write_jump (state.jumpreg, &ptr);
+
+ vtarget = (grub_addr_t) grub_map_memory (get_physical_target_address (ch),
+ stateset_size);
+
+ err = grub_relocator_prepare_relocs (rel, vtarget, &relst, &relsize);
+ if (err)
+ return err;
+
+ grub_arch_sync_caches ((void *) relst, relsize);
+
+ asm volatile (
+ "ibar 0 \n");
+
+ grub_uint64_t val;
+ __asm__ __volatile__(
+ "li.w %0, 0x4\n\t"
+ "csrxchg $r0, %0, 0x0\n\t"
+ : "=r"(val)
+ :
+ :
+ );
+
+ ((void (*) (void)) relst) ();
+
+ /* Not reached. */
+ return GRUB_ERR_NONE;
+}
diff --git a/grub-core/lib/loongarch64/relocator_asm.S b/grub-core/lib/loongarch64/relocator_asm.S
new file mode 100644
index 000000000000..ffdccc903e5f
--- /dev/null
+++ b/grub-core/lib/loongarch64/relocator_asm.S
@@ -0,0 +1,51 @@
+/*
+ * 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/symbol.h>
+
+ .p2align 4 /* force 16-byte alignment */
+
+VARIABLE (grub_relocator_forward_start)
+
+copycont1:
+ ld.d $r11,$r8,0
+ st.d $r11,$r9,0
+ addi.d $r8, $r8, 8
+ addi.d $r10, $r10, -8
+ addi.d $r9, $r9, 8
+ bne $r10, $r0, copycont1
+
+VARIABLE (grub_relocator_forward_end)
+
+VARIABLE (grub_relocator_backward_start)
+
+ add.d $r9, $r9, $r10
+ add.d $r8, $r8, $r10
+ /* Backward movsl is implicitly off-by-one. compensate that. */
+ addi.d $r9, $r9, -8
+ addi.d $r8, $r8, -8
+copycont2:
+ ld.w $r11,$r8,0
+ st.w $r11,$r9,0
+ addi.d $r8, $r8, -8
+ addi.d $r10, $r10, -8
+ addi.d $r9, $r9, -8
+ bne $r10, $r0, copycont2
+
+VARIABLE (grub_relocator_backward_end)
+
diff --git a/grub-core/loader/loongarch64/linux-efi.c b/grub-core/loader/loongarch64/linux-efi.c
new file mode 100644
index 000000000000..8e2726163725
--- /dev/null
+++ b/grub-core/loader/loongarch64/linux-efi.c
@@ -0,0 +1,102 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2021 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/linux.h>
+#include <grub/efi/efi.h>
+#include <grub/cpu/linux.h>
+#include <grub/efi/memory.h>
+#include <grub/charset.h>
+
+#define GRUB_EFI_PE_MAGIC 0x5A4D
+
+grub_err_t
+finalize_efi_params_linux (struct linux_loongarch64_kernel_params *kernel_params)
+{
+ return grub_loongarch_setup_initrd_params();
+}
+
+grub_err_t
+grub_arch_efi_linux_check_image (struct linux_arch_kernel_header * lh)
+{
+ if ((lh->code0 & 0xffff) == GRUB_EFI_PE_MAGIC)
+ return GRUB_ERR_NONE;
+ else
+ return 1;
+
+ grub_dprintf ("linux", "UEFI stub kernel:\n");
+ grub_dprintf ("linux", "PE/COFF header @ %08x\n", lh->hdr_offset);
+
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_arch_efi_linux_boot_image (grub_addr_t addr, grub_size_t size, char *args)
+{
+ grub_efi_memory_mapped_device_path_t *mempath;
+ grub_efi_handle_t image_handle;
+ grub_efi_boot_services_t *b;
+ grub_efi_status_t status;
+ grub_efi_loaded_image_t *loaded_image;
+ int len;
+
+ mempath = grub_malloc (2 * sizeof (grub_efi_memory_mapped_device_path_t));
+ if (!mempath)
+ return grub_errno;
+
+ mempath[0].header.type = GRUB_EFI_HARDWARE_DEVICE_PATH_TYPE;
+ mempath[0].header.subtype = GRUB_EFI_MEMORY_MAPPED_DEVICE_PATH_SUBTYPE;
+ mempath[0].header.length = grub_cpu_to_le16_compile_time (sizeof (*mempath));
+ mempath[0].memory_type = GRUB_EFI_LOADER_DATA;
+ mempath[0].start_address = addr;
+ mempath[0].end_address = addr + size;
+
+ mempath[1].header.type = GRUB_EFI_END_DEVICE_PATH_TYPE;
+ mempath[1].header.subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
+ mempath[1].header.length = sizeof (grub_efi_device_path_t);
+
+ b = grub_efi_system_table->boot_services;
+ status = b->load_image (0, grub_efi_image_handle,
+ (grub_efi_device_path_t *) mempath,
+ (void *) addr, size, &image_handle);
+ if (status != GRUB_EFI_SUCCESS)
+ return grub_error (GRUB_ERR_BAD_OS, "cannot load image");
+
+ grub_dprintf ("linux", "linux command line: '%s'\n", args);
+
+ /* Convert command line to UCS-2 */
+ loaded_image = grub_efi_get_loaded_image (image_handle);
+ loaded_image->load_options_size = len =
+ (grub_strlen (args) + 1) * sizeof (grub_efi_char16_t);
+ loaded_image->load_options =
+ grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (loaded_image->load_options_size));
+ if (!loaded_image->load_options)
+ return grub_errno;
+
+ loaded_image->load_options_size =
+ 2 * grub_utf8_to_utf16 (loaded_image->load_options, len,
+ (grub_uint8_t *) args, len, NULL);
+
+ grub_dprintf ("linux", "starting image %p\n", image_handle);
+ status = b->start_image (image_handle, 0, NULL);
+
+ /* When successful, not reached */
+ b->unload_image (image_handle);
+ grub_efi_free_pages ((grub_addr_t) loaded_image->load_options,
+ GRUB_EFI_BYTES_TO_PAGES (loaded_image->load_options_size));
+
+ return grub_errno;
+}
diff --git a/grub-core/loader/loongarch64/linux-elf.c b/grub-core/loader/loongarch64/linux-elf.c
new file mode 100644
index 000000000000..c5d943437ed8
--- /dev/null
+++ b/grub-core/loader/loongarch64/linux-elf.c
@@ -0,0 +1,896 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2021 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/cpu/linux.h>
+#include <grub/linux.h>
+#include <grub/fdt.h>
+#include <grub/efi/efi.h>
+#include <grub/elfload.h>
+#include <grub/cpu/relocator.h>
+#include <grub/efi/memory.h>
+#include <grub/efi/graphics_output.h>
+
+#define GRUB_EFI_MMAP_NR_SLACK_SLOTS 8
+
+#define GRUB_ADDRESS_TYPE_SYSRAM 1
+#define GRUB_ADDRESS_TYPE_RESERVED 2
+#define GRUB_ADDRESS_TYPE_ACPI 3
+#define GRUB_ADDRESS_TYPE_NVS 4
+#define GRUB_ADDRESS_TYPE_PMEM 5
+
+#define GRUB_EFI_LOONGSON_BPI_TABLE_GUID \
+ { 0x4660f721, 0x2ec5, 0x416a, \
+ { 0x89, 0x9a, 0x43, 0x18, 0x02, 0x50, 0xa0, 0xc9 } \
+ }
+
+#define GRUB_EFI_LARCH_SCREEN_INFO_GUID \
+ { 0x07fd51a6, 0x9532, 0x926f, \
+ { 0x51, 0xdc, 0x6a, 0x63, 0x60, 0x2f, 0x84, 0xb4 } \
+ }
+
+#define GRUB_EFI_LARCH_CONSOLE_OUT_DEVICE_GUID \
+ { 0xd3b36f2c, 0xd551, 0x11d4, \
+ { 0x9a, 0x46, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d } \
+ }
+
+#define GRUB_EFI_LARCH_BOOT_MEMMAP_GUID \
+ { 0x800f683f, 0xd08b, 0x423a, \
+ { 0xa2, 0x93, 0x96, 0x5c, 0x3c, 0x6f, 0xe2, 0xb4 } \
+ }
+
+#define GRUB_EFI_LARCH_INITRD_MEDIA_GUID \
+ { 0x5568e427, 0x68fc, 0x4f3d, \
+ { 0xac, 0x74, 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68 } \
+ }
+
+#define GRUB_EFI_SCREEN_INFO_GUID \
+ { 0xe03fc20a, 0x85dc, 0x406e, \
+ { 0xb9, 0x0e, 0x4a, 0xb5, 0x02, 0x37, 0x1d, 0x95 } \
+ }
+
+static struct grub_relocator *relocator;
+static grub_guid_t compat_screen_info_guid = GRUB_EFI_LARCH_SCREEN_INFO_GUID;
+static grub_guid_t screen_info_guid = GRUB_EFI_SCREEN_INFO_GUID;
+
+void grub_linux_loongarch_elf_relocator_unload (void)
+{
+ grub_relocator_unload (relocator);
+}
+
+static void
+find_bits (unsigned long mask, grub_efi_uint8_t *pos, grub_efi_uint8_t *size)
+{
+ grub_efi_uint8_t first, len;
+
+ first = 0;
+ len = 0;
+
+ if (mask)
+ {
+ while (!(mask & 0x1))
+ {
+ mask = mask >> 1;
+ first++;
+ }
+
+ while (mask & 0x1)
+ {
+ mask = mask >> 1;
+ len++;
+ }
+ }
+
+ *pos = first;
+ *size = len;
+}
+
+static void
+setup_pixel_info (struct screen_info *si, grub_efi_uint32_t pixels_per_scan_line,
+ struct grub_efi_gop_pixel_bitmask pixel_info, int pixel_format)
+{
+ if (pixel_format == GRUB_EFI_GOT_RGBA8)
+ {
+ si->lfb_depth = 32;
+ si->lfb_linelength = pixels_per_scan_line * 4;
+ si->red_size = 8;
+ si->red_pos = 0;
+ si->green_size = 8;
+ si->green_pos = 8;
+ si->blue_size = 8;
+ si->blue_pos = 16;
+ si->rsvd_size = 8;
+ si->rsvd_pos = 24;
+ }
+ else if (pixel_format == GRUB_EFI_GOT_BGRA8)
+ {
+ si->lfb_depth = 32;
+ si->lfb_linelength = pixels_per_scan_line * 4;
+ si->red_size = 8;
+ si->red_pos = 16;
+ si->green_size = 8;
+ si->green_pos = 8;
+ si->blue_size = 8;
+ si->blue_pos = 0;
+ si->rsvd_size = 8;
+ si->rsvd_pos = 24;
+ }
+ else if (pixel_format == GRUB_EFI_GOT_BITMASK)
+ {
+ find_bits(pixel_info.r, &si->red_pos, &si->red_size);
+ find_bits(pixel_info.g, &si->green_pos, &si->green_size);
+ find_bits(pixel_info.b, &si->blue_pos, &si->blue_size);
+ find_bits(pixel_info.a, &si->rsvd_pos, &si->rsvd_size);
+ si->lfb_depth = si->red_size + si->green_size +
+ si->blue_size + si->rsvd_size;
+ si->lfb_linelength = (pixels_per_scan_line * si->lfb_depth) / 8;
+ }
+ else
+ {
+ si->lfb_depth = 4;
+ si->lfb_linelength = si->lfb_width / 2;
+ si->red_size = 0;
+ si->red_pos = 0;
+ si->green_size = 0;
+ si->green_pos = 0;
+ si->blue_size = 0;
+ si->blue_pos = 0;
+ si->rsvd_size = 0;
+ si->rsvd_pos = 0;
+ }
+}
+
+static struct screen_info *
+alloc_screen_info (void)
+{
+ grub_efi_status_t status;
+ grub_efi_boot_services_t *b;
+ struct screen_info *si;
+
+ b = grub_efi_system_table->boot_services;
+ status = b->allocate_pool (GRUB_EFI_RUNTIME_SERVICES_DATA,
+ sizeof(*si), (void**)&si);
+ if (status != GRUB_EFI_SUCCESS)
+ return NULL;
+
+ status = b->install_configuration_table (&compat_screen_info_guid, si);
+ if (status != GRUB_EFI_SUCCESS)
+ goto free_mem;
+
+ status = b->install_configuration_table (&screen_info_guid, si);
+ if (status == GRUB_EFI_SUCCESS)
+ return si;
+
+free_table:
+ b->install_configuration_table (&compat_screen_info_guid, NULL);
+free_mem:
+ b->free_pool (si);
+
+ return NULL;
+}
+
+static struct screen_info *
+setup_screen_info (void)
+{
+ grub_efi_boot_services_t *b;
+ grub_efi_handle_t gop_handle;
+ struct screen_info *si = NULL;
+ struct grub_efi_gop *gop, *first_gop;
+ grub_efi_handle_t *handles;
+ grub_efi_uintn_t num_handles, i;
+ grub_guid_t graphics_output_guid = GRUB_EFI_GOP_GUID;
+ grub_efi_uint16_t width, height;
+ grub_efi_uint32_t ext_lfb_base, pixels_per_scan_line;
+ grub_efi_uint64_t fb_base;
+ struct grub_efi_gop_pixel_bitmask pixel_info;
+ grub_efi_gop_pixel_format_t pixel_format;
+
+ si = alloc_screen_info();
+ if (!si)
+ return NULL;
+
+ handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL,
+ &graphics_output_guid, NULL, &num_handles);
+ if (!handles || num_handles == 0)
+ goto free_screen_info;
+
+ gop = NULL;
+ first_gop = NULL;
+
+ for (i = 0; i < num_handles; i++)
+ {
+ struct grub_efi_gop_mode *mode;
+ struct grub_efi_gop_mode_info *info = NULL;
+ grub_guid_t conout_proto = GRUB_EFI_LARCH_CONSOLE_OUT_DEVICE_GUID;
+ void *dummy = NULL;
+ grub_efi_uint8_t conout_found = 0;
+ grub_efi_uint64_t current_fb_base;
+
+ gop_handle = handles[i];
+ gop = grub_efi_open_protocol (gop_handle, &graphics_output_guid,
+ GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+
+ dummy = grub_efi_open_protocol (gop_handle, &conout_proto,
+ GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ if (dummy != NULL)
+ conout_found = 1;
+
+ mode = gop->mode;
+ info = mode->info;
+ current_fb_base = mode->fb_base;
+
+ if ((!first_gop || conout_found) &&
+ info->pixel_format != GRUB_EFI_GOT_BLT_ONLY)
+ {
+ /*
+ * Systems that use the UEFI Console Splitter may
+ * provide multiple GOP devices, not all of which are
+ * backed by real hardware. The workaround is to search
+ * for a GOP implementing the ConOut protocol, and if
+ * one isn't found, to just fall back to the first GOP.
+ */
+ width = info->width;
+ height = info->height;
+ pixel_format = info->pixel_format;
+ pixel_info = info->pixel_bitmask;
+ pixels_per_scan_line = info->pixels_per_scanline;
+ fb_base = current_fb_base;
+
+ /*
+ * Once we've found a GOP supporting ConOut,
+ * don't bother looking any further.
+ */
+ first_gop = gop;
+ if (conout_found)
+ break;
+ }
+ }
+
+ /* Did we find any GOPs? */
+ if (!first_gop)
+ goto free_screen_info;
+
+ /* EFI framebuffer */
+ si->orig_video_isVGA = GRUB_VIDEO_TYPE_EFI;
+
+ si->lfb_width = width;
+ si->lfb_height = height;
+ si->lfb_base = fb_base;
+ grub_dprintf ("loongson", "Screen info fb base: 0x%"PRIxGRUB_UINT32_T"\n",
+ si->lfb_base);
+
+ ext_lfb_base = (grub_uint64_t)fb_base >> 32;
+ if (ext_lfb_base) {
+ si->capabilities |= GRUB_VIDEO_CAPABILITY_64BIT_BASE;
+ si->ext_lfb_base = ext_lfb_base;
+ }
+ si->pages = 1;
+
+ setup_pixel_info(si, pixels_per_scan_line, pixel_info, pixel_format);
+
+ si->lfb_size = si->lfb_linelength * si->lfb_height;
+ si->capabilities |= GRUB_VIDEO_CAPABILITY_SKIP_QUIRKS;
+
+ return si;
+
+free_screen_info:
+ b = grub_efi_system_table->boot_services;
+ b->install_configuration_table (&compat_screen_info_guid, NULL);
+ b->install_configuration_table (&screen_info_guid, NULL);
+ if (si)
+ b->free_pool (si);
+
+ grub_dprintf ("loongson", "No screen info\n");
+ return NULL;
+}
+
+static grub_err_t
+allocate_memmap_and_exit_boot (struct linux_loongarch64_kernel_params *kernel_params)
+{
+ grub_err_t err;
+ grub_efi_status_t status;
+ grub_efi_uintn_t mmap_size, desc_size, size;
+ grub_efi_uint32_t desc_version;
+ grub_efi_memory_descriptor_t *mmap_buf;
+ grub_efi_boot_services_t *b;
+ struct efi_boot_memmap *m, tmp;
+ grub_guid_t boot_memmap_guid = GRUB_EFI_LARCH_BOOT_MEMMAP_GUID;
+
+ setup_screen_info();
+
+ grub_dprintf ("loongson", "ramdisk_addr:0x%"PRIxGRUB_UINT64_T", \
+ size:0x%"PRIxGRUB_UINT64_T"\n",
+ kernel_params->ramdisk_addr,
+ kernel_params->ramdisk_size);
+
+ /* Set initrd info to system table*/
+ err = grub_loongarch_setup_initrd_params();
+ if (err != GRUB_ERR_NONE)
+ {
+ grub_error(GRUB_ERR_IO, "failed to install initrd media");
+ return err;
+ }
+
+ tmp.map_size = 0;
+ status = grub_efi_get_memory_map (&tmp.map_size, NULL, &tmp.map_key,
+ &tmp.desc_size, &tmp.desc_ver);
+ if (status != 0) {
+ grub_error (GRUB_ERR_IO, "cannot get memory map");
+ goto uninstall_initrd_table;
+ }
+ size = tmp.map_size + tmp.desc_size * GRUB_EFI_MMAP_NR_SLACK_SLOTS;
+ m = grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (sizeof(*m) + size));
+ if (!m) {
+ grub_error (GRUB_ERR_IO, "cannot allocate m memory");
+ goto uninstall_initrd_table;
+ }
+
+ b = grub_efi_system_table->boot_services;
+ status = b->install_configuration_table (&boot_memmap_guid, m);
+ if (status != GRUB_EFI_SUCCESS) {
+ grub_error (GRUB_ERR_IO, "failed to install boot memmap");
+ goto free_m;
+ }
+
+ m->buff_size = m->map_size = size;
+ if (grub_efi_get_memory_map (&m->map_size, m->map,
+ &m->map_key, &m->desc_size,
+ &m->desc_ver) <= 0)
+ {
+ grub_error (GRUB_ERR_IO, "cannot get EFI memory map");
+ goto uninstall_mem_table;
+ }
+
+ mmap_size = grub_efi_find_mmap_size ();
+ if (! mmap_size)
+ goto uninstall_mem_table;
+
+ mmap_buf = grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (mmap_size));
+ err = grub_efi_finish_boot_services (&mmap_size, mmap_buf, NULL,
+ &desc_size, &desc_version);
+ if (err) {
+ grub_error (GRUB_ERR_IO, "failed to finish boot services");
+ goto free_map;
+ }
+
+ return 0;
+
+free_map:
+ if (mmap_buf)
+ grub_efi_free_pages ((grub_addr_t) mmap_buf,
+ GRUB_EFI_BYTES_TO_PAGES (mmap_size));
+
+uninstall_mem_table:
+ b->install_configuration_table (&boot_memmap_guid, NULL);
+
+free_m:
+ if (m)
+ grub_efi_free_pages ((grub_addr_t) m,
+ GRUB_EFI_BYTES_TO_PAGES (sizeof(*m) + size));
+
+uninstall_initrd_table:
+ grub_loongarch_remove_initrd_params();
+
+ return grub_error(GRUB_ERR_BAD_OS, "failed to V40 boot");
+}
+
+static grub_err_t
+allocate_fdt_and_exit_boot (struct linux_loongarch64_kernel_params *kernel_params)
+{
+ int node, retval;
+ grub_err_t err;
+ unsigned int size;
+ grub_efi_uintn_t mmap_size;
+ grub_efi_uintn_t desc_size;
+ grub_efi_uint32_t desc_version;
+ grub_efi_memory_descriptor_t *mmap_buf;
+
+ size = GRUB_FDT_EMPTY_TREE_SZ + FDT_ADDR_SIZE_EXTRA + GRUB_EFI_LINUX_FDT_EXTRA_SPACE;
+
+ kernel_params->fdt = grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (size));
+ if (!kernel_params->fdt)
+ return GRUB_ERR_OUT_OF_MEMORY;
+
+ grub_fdt_create_empty_tree (kernel_params->fdt, size);
+ grub_fdt_set_prop32 (kernel_params->fdt, 0, FDT_ADDR_CELLS_STRING, 2);
+ grub_fdt_set_prop32 (kernel_params->fdt, 0, FDT_SIZE_CELLS_STRING, 2);
+
+ node = grub_fdt_find_subnode (kernel_params->fdt, 0, "chosen");
+ if (node < 0)
+ node = grub_fdt_add_subnode (kernel_params->fdt, 0, "chosen");
+ if (node < 1)
+ goto failure;
+
+ grub_dprintf ("loongson", "command_line %s, len %ld\n",
+ (char *)kernel_params->linux_args,
+ grub_strlen(kernel_params->linux_args) + 1);
+ if ((kernel_params->linux_args != NULL) && (grub_strlen(kernel_params->linux_args) > 0)) {
+ retval = grub_fdt_set_prop (kernel_params->fdt, node, "bootargs", kernel_params->linux_args,
+ grub_strlen(kernel_params->linux_args) + 1);
+ if (retval)
+ goto failure;
+ }
+
+ /* Set initrd info */
+ if (kernel_params->ramdisk_addr && kernel_params->ramdisk_size)
+ {
+ grub_dprintf ("linux", "Initrd @ %p-%p\n",
+ (void *) kernel_params->ramdisk_addr,
+ (void *) (kernel_params->ramdisk_addr + kernel_params->ramdisk_size));
+
+ retval = grub_fdt_set_prop64 (kernel_params->fdt, node, "linux,initrd-start",
+ kernel_params->ramdisk_addr);
+ if (retval)
+ goto failure;
+ retval = grub_fdt_set_prop64 (kernel_params->fdt, node, "linux,initrd-end",
+ (grub_uint64_t) (kernel_params->ramdisk_addr + kernel_params->ramdisk_size));
+ if (retval)
+ goto failure;
+ }
+
+ node = grub_fdt_find_subnode (kernel_params->fdt, 0, "chosen");
+ retval = grub_fdt_set_prop64 (kernel_params->fdt, node, "linux,uefi-system-table",
+ (grub_uint64_t)grub_efi_system_table);
+ if (retval)
+ goto failure;
+
+ mmap_size = grub_efi_find_mmap_size ();
+ if (! mmap_size)
+ return grub_errno;
+ mmap_buf = grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (mmap_size));
+ if (! mmap_buf)
+ return grub_error (GRUB_ERR_IO, "cannot allocate memory map");
+ err = grub_efi_finish_boot_services (&mmap_size, mmap_buf, NULL,
+ &desc_size, &desc_version);
+ if (err)
+ return err;
+
+ if (!mmap_buf || !mmap_size || !desc_size)
+ return GRUB_ERR_BAD_ARGUMENT;
+
+ retval = grub_fdt_set_prop64 (kernel_params->fdt, node, "linux,uefi-mmap-start",
+ (grub_uint64_t)mmap_buf);
+ if (retval)
+ goto failure;
+
+ retval = grub_fdt_set_prop32 (kernel_params->fdt, node, "linux,uefi-mmap-size",
+ mmap_size);
+ if (retval)
+ goto failure;
+
+ retval = grub_fdt_set_prop32 (kernel_params->fdt, node, "linux,uefi-mmap-desc-size",
+ desc_size);
+ if (retval)
+ goto failure;
+
+ retval = grub_fdt_set_prop32 (kernel_params->fdt, node, "linux,uefi-mmap-desc-ver",
+ desc_version);
+ if (retval)
+ goto failure;
+
+ return GRUB_ERR_NONE;
+
+failure:
+ if (!kernel_params->fdt) {
+ return GRUB_ERR_BAD_OS;
+ }
+ grub_efi_free_pages ((grub_addr_t) kernel_params->fdt,
+ GRUB_EFI_BYTES_TO_PAGES (grub_fdt_get_totalsize (kernel_params->fdt)));
+ kernel_params->fdt = NULL;
+ return grub_error(GRUB_ERR_BAD_OS, "failed to install/update FDT");
+}
+
+static void
+grub_linux_loongarch_elf_make_argv (struct linux_loongarch64_kernel_params *kernel_params)
+{
+ static void* linux_args_addr;
+ int size;
+ grub_uint64_t *linux_argv;
+ char *args, *p, *linux_args;
+ int i, argc;
+ grub_err_t err;
+
+ argc = kernel_params->linux_argc;
+ args = kernel_params->linux_args;
+
+ /* new size */
+ p = args;
+ size = (argc + 3 + 1) * sizeof (grub_uint64_t); /* orig arguments */
+ for (i = 0; i < argc; i++)
+ {
+ size += ALIGN_UP (grub_strlen (p) + 1, 4);
+ p += grub_strlen (p) + 1;
+ }
+
+ if (kernel_params->ramdisk_addr && kernel_params->ramdisk_size)
+ {
+ size += ALIGN_UP (sizeof (GRUB_RD_START_STRING), 4)
+ + ALIGN_UP (sizeof (GRUB_RD_SIZE_STRING), 4)
+ + ALIGN_UP (sizeof (GRUB_INITRD_STRING), 4);
+ }
+ size = ALIGN_UP (size, 8);
+
+ /* alloc memory */
+ linux_args_addr = grub_linux_loongarch_alloc_virtual_mem_align (size, 8, &err);
+
+ linux_argv = linux_args_addr;
+ linux_args = (char *)(linux_argv + (argc + 1 + 3));
+ p = args;
+ for (i = 0; i < argc; i++)
+ {
+ grub_memcpy (linux_args, p, grub_strlen (p) + 1);
+ *linux_argv = (grub_uint64_t) (grub_addr_t) linux_args;
+ linux_argv++;
+ linux_args += ALIGN_UP (grub_strlen (p) + 1, 4);
+ p += grub_strlen (p) + 1;
+ }
+
+ if (kernel_params->ramdisk_addr && kernel_params->ramdisk_size)
+ {
+ /* rd_start */
+ grub_snprintf (linux_args,
+ sizeof (GRUB_RD_START_STRING),
+ "rd_start=0x%lx",
+ (grub_uint64_t) kernel_params->ramdisk_addr);
+ *linux_argv = (grub_uint64_t) (grub_addr_t) linux_args;
+ linux_argv++;
+ linux_args += ALIGN_UP (sizeof (GRUB_RD_START_STRING), 4);
+ kernel_params->linux_argc++;
+
+ /* rd_size */
+ grub_snprintf (linux_args,
+ sizeof (GRUB_RD_SIZE_STRING),
+ "rd_size=0x%lx",
+ (grub_uint64_t) kernel_params->ramdisk_size);
+ *linux_argv = (grub_uint64_t) (grub_addr_t) linux_args;
+ linux_argv++;
+ linux_args += ALIGN_UP (sizeof (GRUB_RD_SIZE_STRING), 4);
+ kernel_params->linux_argc++;
+
+ /* initrd */
+ grub_snprintf (linux_args,
+ sizeof (GRUB_INITRD_STRING),
+ "initrd=0x%lx,0x%lx",
+ ((grub_uint64_t) kernel_params->ramdisk_addr & 0xffffffff),
+ (grub_uint64_t) kernel_params->ramdisk_size);
+ *linux_argv = (grub_uint64_t) (grub_addr_t) linux_args;
+ linux_argv++;
+ linux_args += ALIGN_UP (sizeof (GRUB_INITRD_STRING), 4);
+ kernel_params->linux_argc++;
+ }
+
+ /* Reserve space for initrd arguments. */
+ *linux_argv = 0;
+
+ grub_free (kernel_params->linux_args);
+ kernel_params->linux_argv = (grub_addr_t) linux_args_addr;
+}
+
+grub_err_t
+grub_linux_loongarch_elf_linux_boot_image (struct linux_loongarch64_kernel_params
+ *kernel_params)
+{
+ struct boot_params_interface *boot_params = NULL;
+ struct grub_relocator64_state state;
+ grub_err_t err;
+
+ /* linux kernel type is ELF */
+ grub_memset (&state, 0, sizeof (state));
+
+ state.jumpreg = 1;
+ state.gpr[1] = kernel_params->kernel_addr; /* ra */
+ if (grub_linux_loongarch_elf_get_boot_params (&boot_params) == 0)
+ {
+ grub_dprintf("loongson", "V4.0 boot\n");
+ if (allocate_memmap_and_exit_boot (kernel_params) != GRUB_ERR_NONE)
+ return grub_errno;
+ state.gpr[4] = 1 << FLAGS_EFI_SUPPORT_BIT; /* a0 = flag */
+ state.gpr[5] = (grub_uint64_t)kernel_params->linux_args; /* a1 = cmdline */
+ state.gpr[6] = (grub_uint64_t)grub_efi_system_table; /* a2 = system_table */
+ } else {
+ grub_dprintf("loongson", "BPI boot\n");
+ grub_linux_loongarch_elf_make_argv (kernel_params);
+ state.gpr[4] = kernel_params->linux_argc; /* a0 = argc */
+ state.gpr[5] = kernel_params->linux_argv; /* a1 = args */
+ state.gpr[6] = (grub_uint64_t) boot_params; /* a2 = envp */
+ err = grub_linux_loongarch_elf_boot_params (boot_params);
+ if (err)
+ return err;
+ }
+
+ /* Boot the ELF kernel */
+ grub_relocator64_boot (relocator, state);
+
+ return GRUB_ERR_NONE;
+}
+
+void*
+grub_linux_loongarch_alloc_virtual_mem_addr (grub_addr_t addr,
+ grub_size_t size,
+ grub_err_t *err)
+{
+ relocator = grub_relocator_new ();
+ if (!relocator)
+ return NULL;
+
+ grub_relocator_chunk_t ch;
+ *err = grub_relocator_alloc_chunk_addr (relocator, &ch,
+ grub_vtop ((void *) addr),
+ size);
+ if (*err)
+ return NULL;
+ return get_virtual_current_address (ch);
+}
+
+void*
+grub_linux_loongarch_alloc_virtual_mem_align (grub_size_t size,
+ grub_size_t align,
+ grub_err_t *err)
+{
+ grub_relocator_chunk_t ch;
+
+ *err = grub_relocator_alloc_chunk_align (relocator, &ch,
+ 0, (0xffffffff - size) + 1,
+ size, align,
+ GRUB_RELOCATOR_PREFERENCE_LOW, 0);
+ return get_virtual_current_address (ch);
+}
+
+void*
+grub_linux_loongarch_alloc_initrd_mem_align (grub_size_t size,
+ grub_size_t align,
+ grub_err_t *err)
+{
+ grub_relocator_chunk_t ch;
+
+ /* Firstly try to allocate from memory higher than 256MB */
+ *err = grub_relocator_alloc_chunk_align (relocator, &ch,
+ 0x10000000, (0xffffffff - size) + 1, size, align,
+ GRUB_RELOCATOR_PREFERENCE_LOW, 0);
+ if (*err != GRUB_ERR_NONE)
+ {
+ /* Failed, try to allocate in range 0 ~ 256MB */
+ *err = grub_relocator_alloc_chunk_align (relocator, &ch,
+ 0, (0xfffffff - size) + 1, size, align,
+ GRUB_RELOCATOR_PREFERENCE_HIGH, 0);
+ }
+ return get_virtual_current_address (ch);
+}
+
+int
+grub_linux_loongarch_elf_get_boot_params (struct boot_params_interface **boot_params)
+{
+ grub_efi_configuration_table_t *tables;
+ grub_guid_t bpi_guid = GRUB_EFI_LOONGSON_BPI_TABLE_GUID;
+ unsigned int i;
+ int found = 0;
+
+ /* Look for Loongson BPI in UEFI config tables. */
+ tables = grub_efi_system_table->configuration_table;
+
+ for (i = 0; i < grub_efi_system_table->num_table_entries; i++)
+ if (grub_memcmp (&tables[i].vendor_guid, &bpi_guid, sizeof (bpi_guid)) == 0)
+ {
+ *boot_params = tables[i].vendor_table;
+ char *p = (char*) &((*boot_params)->signature);
+ if (grub_strncmp (p, "BPI", 3) == 0)
+ {
+ found = 1;
+ break;
+ }
+ }
+ return found;
+}
+
+static grub_uint8_t
+grub_kernel_update_checksum (const grub_uint8_t *buffer, grub_efi_uintn_t length)
+{
+ grub_uint8_t sum;
+ grub_efi_uintn_t count;
+
+ for (sum = 0, count = 0; count < length; count++)
+ {
+ sum = (grub_uint8_t) (sum + *(buffer + count));
+ }
+
+ return (grub_uint8_t) (0x100 - sum);
+}
+
+static grub_uint32_t
+grub_efi_loongarch64_memmap_sort (struct memmap array[],
+ grub_uint32_t length,
+ struct loongsonlist_mem_map* bpmem,
+ grub_uint32_t index,
+ grub_uint32_t memtype)
+{
+ grub_uint64_t tempmemsize = 0;
+ grub_uint32_t j = 0;
+ grub_uint32_t t = 0;
+
+ for(j = 0; j < length;)
+ {
+ tempmemsize = array[j].mem_size;
+ for(t = j + 1; t < length; t++)
+ {
+ if(array[j].mem_start + tempmemsize == array[t].mem_start)
+ {
+ tempmemsize += array[t].mem_size;
+ }
+ else
+ {
+ break;
+ }
+ }
+ bpmem->map[index].mem_type = memtype;
+ bpmem->map[index].mem_start = array[j].mem_start;
+ bpmem->map[index].mem_size = tempmemsize;
+ grub_printf("map[%d]:type %"PRIuGRUB_UINT32_T", start 0x%"
+ PRIxGRUB_UINT64_T", end 0x%"PRIxGRUB_UINT64_T"\n",
+ index,
+ bpmem->map[index].mem_type,
+ bpmem->map[index].mem_start,
+ bpmem->map[index].mem_start+ bpmem->map[index].mem_size
+ );
+ j = t;
+ index++;
+ }
+ return index;
+}
+
+grub_err_t
+grub_linux_loongarch_elf_boot_params (struct boot_params_interface *boot_params)
+{
+ grub_int8_t checksum = 0;
+ grub_err_t err;
+
+ struct loongsonlist_mem_map *loongson_mem_map = NULL;
+ struct _extention_list_hdr * listpointer = NULL;
+ grub_uint32_t tmp_index = 0;
+ grub_efi_memory_descriptor_t * lsdesc = NULL;
+
+ grub_uint32_t free_index = 0;
+ grub_uint32_t reserve_index = 0;
+ grub_uint32_t acpi_table_index = 0;
+ grub_uint32_t acpi_nvs_index = 0;
+
+ grub_efi_uintn_t mmap_size;
+ grub_efi_uintn_t desc_size;
+ grub_efi_memory_descriptor_t *mmap_buf;
+
+ struct memmap reserve_mem[GRUB_LOONGSON3_BOOT_MEM_MAP_MAX];
+ struct memmap free_mem[GRUB_LOONGSON3_BOOT_MEM_MAP_MAX];
+ struct memmap acpi_table_mem[GRUB_LOONGSON3_BOOT_MEM_MAP_MAX];
+ struct memmap acpi_nvs_mem[GRUB_LOONGSON3_BOOT_MEM_MAP_MAX];
+
+ grub_memset (reserve_mem, 0, sizeof(struct memmap) * GRUB_LOONGSON3_BOOT_MEM_MAP_MAX);
+ grub_memset (free_mem, 0, sizeof(struct memmap) * GRUB_LOONGSON3_BOOT_MEM_MAP_MAX);
+ grub_memset (acpi_table_mem, 0, sizeof(struct memmap) * GRUB_LOONGSON3_BOOT_MEM_MAP_MAX);
+ grub_memset (acpi_nvs_mem, 0, sizeof(struct memmap) * GRUB_LOONGSON3_BOOT_MEM_MAP_MAX);
+
+ /* Check extlist headers */
+ listpointer = boot_params->extlist;
+ for( ;listpointer != NULL; listpointer = listpointer->next)
+ {
+ char *pl= (char *)&(listpointer->signature);
+ if(grub_strncmp(pl, "MEM", 3) == 0)
+ {
+ loongson_mem_map = (struct loongsonlist_mem_map *)listpointer;
+ break;
+ }
+ }
+
+ mmap_size = grub_efi_find_mmap_size ();
+ if (! mmap_size)
+ return grub_errno;
+ mmap_buf = grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (mmap_size));
+ if (! mmap_buf)
+ return grub_error (GRUB_ERR_IO, "cannot allocate memory map");
+
+ err = grub_efi_finish_boot_services (&mmap_size, mmap_buf, NULL,
+ &desc_size, NULL);
+ if (err)
+ return err;
+
+ if (!mmap_buf || !mmap_size || !desc_size)
+ return -1;
+
+ /*
+ According to UEFI SPEC,mmap_buf is the accurate Memory Map array \
+ now we can fill platform specific memory structure.
+ */
+ for (lsdesc = mmap_buf; lsdesc < (grub_efi_memory_descriptor_t *)((char *)mmap_buf + mmap_size);
+ lsdesc = (grub_efi_memory_descriptor_t *)((char *)lsdesc + desc_size))
+ {
+ /* System RAM */
+ if((lsdesc->type != GRUB_EFI_ACPI_RECLAIM_MEMORY) && \
+ (lsdesc->type != GRUB_EFI_ACPI_MEMORY_NVS) && \
+ (lsdesc->type != GRUB_EFI_RUNTIME_SERVICES_DATA) && \
+ (lsdesc->type != GRUB_EFI_RUNTIME_SERVICES_CODE) && \
+ (lsdesc->type != GRUB_EFI_RESERVED_MEMORY_TYPE) && \
+ (lsdesc->type != GRUB_EFI_PAL_CODE))
+ {
+ free_mem[free_index].mem_type = GRUB_ADDRESS_TYPE_SYSRAM;
+ free_mem[free_index].mem_start = (lsdesc->physical_start) & GRUB_EFI_MAX_PHY_ADDRESS;
+ free_mem[free_index].mem_size = lsdesc->num_pages * GRUB_EFI_PAGE_SIZE;
+ free_index++;
+
+ /*ACPI*/
+ }else if((lsdesc->type == GRUB_EFI_ACPI_RECLAIM_MEMORY)){
+ acpi_table_mem[acpi_table_index].mem_type = GRUB_ADDRESS_TYPE_ACPI;
+ acpi_table_mem[acpi_table_index].mem_start = (lsdesc->physical_start) & GRUB_EFI_MAX_PHY_ADDRESS;
+ acpi_table_mem[acpi_table_index].mem_size = lsdesc->num_pages * GRUB_EFI_PAGE_SIZE;
+ acpi_table_index++;
+ }else if((lsdesc->type == GRUB_EFI_ACPI_MEMORY_NVS)){
+ acpi_nvs_mem[acpi_nvs_index].mem_type = GRUB_ADDRESS_TYPE_NVS;
+ acpi_nvs_mem[acpi_nvs_index].mem_start = (lsdesc->physical_start) & GRUB_EFI_MAX_PHY_ADDRESS;
+ acpi_nvs_mem[acpi_nvs_index].mem_size = lsdesc->num_pages * GRUB_EFI_PAGE_SIZE;
+ acpi_nvs_index++;
+
+ /* Reserve */
+ }else{
+ reserve_mem[reserve_index].mem_type = GRUB_ADDRESS_TYPE_RESERVED;
+ reserve_mem[reserve_index].mem_start = (lsdesc->physical_start) & GRUB_EFI_MAX_PHY_ADDRESS;
+ reserve_mem[reserve_index].mem_size = lsdesc->num_pages * GRUB_EFI_PAGE_SIZE;
+ reserve_index++;
+ }
+ }
+
+ tmp_index = loongson_mem_map->map_count;
+ /*System RAM Sort*/
+ tmp_index = grub_efi_loongarch64_memmap_sort(free_mem,
+ free_index,
+ loongson_mem_map,
+ tmp_index,
+ GRUB_ADDRESS_TYPE_SYSRAM);
+ /*ACPI Sort*/
+ tmp_index = grub_efi_loongarch64_memmap_sort(acpi_table_mem,
+ acpi_table_index,
+ loongson_mem_map,
+ tmp_index,
+ GRUB_ADDRESS_TYPE_ACPI);
+ tmp_index = grub_efi_loongarch64_memmap_sort(acpi_nvs_mem,
+ acpi_nvs_index,
+ loongson_mem_map,
+ tmp_index,
+ GRUB_ADDRESS_TYPE_NVS);
+
+ /*Reserve Sort*/
+ {
+ grub_uint64_t loongarch_addr;
+ asm volatile ("csrrd %0, 0x181" : "=r" (loongarch_addr));
+ if ((loongarch_addr & 0xff00000000000000) == 0x9000000000000000)
+ tmp_index = grub_efi_loongarch64_memmap_sort(reserve_mem,
+ reserve_index,
+ loongson_mem_map,
+ tmp_index,
+ GRUB_ADDRESS_TYPE_RESERVED);
+ else
+ tmp_index = grub_efi_loongarch64_memmap_sort(reserve_mem,
+ reserve_index,
+ loongson_mem_map,
+ tmp_index,
+ GRUB_ADDRESS_TYPE_RESERVED + 1);
+ }
+ loongson_mem_map->map_count = tmp_index;
+ loongson_mem_map->header.checksum = 0;
+
+ checksum = grub_kernel_update_checksum ((grub_uint8_t *) loongson_mem_map,
+ loongson_mem_map->header.length);
+ loongson_mem_map->header.checksum = checksum;
+
+ return grub_errno;
+}
diff --git a/grub-core/loader/loongarch64/linux.c b/grub-core/loader/loongarch64/linux.c
new file mode 100644
index 000000000000..7c8cd36fca15
--- /dev/null
+++ b/grub-core/loader/loongarch64/linux.c
@@ -0,0 +1,437 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2021 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/loader.h>
+#include <grub/misc.h>
+#include <grub/command.h>
+#include <grub/i18n.h>
+#include <grub/lib/cmdline.h>
+#include <grub/linux.h>
+#include <grub/cpu/linux.h>
+#include <grub/efi/memory.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+#define INITRD_MAX_ADDRESS_OFFSET (32ULL * 1024 * 1024 * 1024)
+
+#define GRUB_EFI_LARCH_INITRD_MEDIA_GUID \
+ { 0x5568e427, 0x68fc, 0x4f3d, \
+ { 0xac, 0x74, 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68 } \
+ }
+
+static struct linux_loongarch64_kernel_params kernel_params;
+
+static grub_addr_t phys_addr;
+static grub_dl_t my_mod;
+static int loaded;
+static int is_bpi_boot;
+static int grub_loongarch_linux_type = GRUB_LOONGARCH_LINUX_BAD;
+struct efi_initrd *initrd_tbl;
+
+grub_err_t
+grub_loongarch_setup_initrd_params (void)
+{
+ grub_efi_boot_services_t *b;
+ grub_guid_t initrd_media_guid = GRUB_EFI_LARCH_INITRD_MEDIA_GUID;
+ grub_efi_status_t status;
+
+ if (!kernel_params.ramdisk_addr || !kernel_params.ramdisk_size)
+ {
+ grub_error (GRUB_ERR_BAD_ARGUMENT,
+ N_("you need to load the initrd first"));
+ return GRUB_ERR_BAD_ARGUMENT;
+ }
+
+ /* Set initrd info to system table*/
+ b = grub_efi_system_table->boot_services;
+ initrd_tbl = grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (sizeof(struct efi_initrd)));
+ if (!initrd_tbl)
+ return grub_error (GRUB_ERR_IO, "cannot allocate tbl memory");
+
+ initrd_tbl->base = kernel_params.ramdisk_addr;
+ initrd_tbl->size = kernel_params.ramdisk_size;
+ status = b->install_configuration_table (&initrd_media_guid, initrd_tbl);
+ if (status != GRUB_EFI_SUCCESS)
+ {
+ grub_error (GRUB_ERR_IO, "failed to install initrd media");
+ goto free_tbl;
+ }
+
+ return GRUB_ERR_NONE;
+
+free_tbl:
+ if (initrd_tbl)
+ {
+ grub_efi_free_pages ((grub_addr_t) initrd_tbl,
+ GRUB_EFI_BYTES_TO_PAGES (sizeof(struct efi_initrd)));
+ }
+
+ return GRUB_ERR_IO;
+}
+
+void
+grub_loongarch_remove_initrd_params (void)
+{
+ grub_efi_boot_services_t *b;
+ grub_guid_t initrd_media_guid = GRUB_EFI_LARCH_INITRD_MEDIA_GUID;
+
+ if (!initrd_tbl)
+ return;
+
+ b = grub_efi_system_table->boot_services;
+ b->install_configuration_table (&initrd_media_guid, NULL);
+
+ grub_efi_free_pages ((grub_addr_t) initrd_tbl,
+ GRUB_EFI_BYTES_TO_PAGES (sizeof(struct efi_initrd)));
+
+ initrd_tbl = NULL;
+}
+
+
+static grub_err_t
+grub_linux_boot (void)
+{
+
+ if (grub_loongarch_linux_type == GRUB_LOONGARCH_LINUX_EFI) {
+ if (finalize_efi_params_linux (&kernel_params) != GRUB_ERR_NONE)
+ return grub_errno;
+ return (grub_arch_efi_linux_boot_image((grub_addr_t) kernel_params.kernel_addr,
+ kernel_params.kernel_size,
+ kernel_params.linux_args));
+ }
+ if (grub_loongarch_linux_type == GRUB_LOONGARCH_LINUX_ELF) {
+ return grub_linux_loongarch_elf_linux_boot_image (&kernel_params);
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_linux_unload (void)
+{
+
+ if (grub_loongarch_linux_type == GRUB_LOONGARCH_LINUX_EFI) {
+ if (kernel_params.ramdisk_addr)
+ grub_efi_free_pages ((grub_efi_physical_address_t) kernel_params.ramdisk_addr,
+ GRUB_EFI_BYTES_TO_PAGES (kernel_params.ramdisk_size));
+ kernel_params.ramdisk_size = 0;
+
+ if (kernel_params.kernel_addr)
+ grub_efi_free_pages ((grub_addr_t) kernel_params.kernel_addr,
+ GRUB_EFI_BYTES_TO_PAGES (kernel_params.kernel_size));
+ kernel_params.kernel_addr = 0;
+ }
+
+ if (grub_loongarch_linux_type == GRUB_LOONGARCH_LINUX_ELF) {
+ grub_free (kernel_params.linux_args);
+ kernel_params.linux_args = 0;
+ grub_linux_loongarch_elf_relocator_unload ();
+ }
+
+ grub_dl_unref (my_mod);
+ loaded = 0;
+ grub_loongarch_linux_type = GRUB_LOONGARCH_LINUX_BAD;
+
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_linux_loongarch_elf_load_kernel (grub_elf_t elf, const char *filename)
+{
+ Elf64_Addr base;
+ grub_err_t err;
+ grub_uint8_t *playground;
+ grub_uint64_t addr;
+ int flag;
+
+ /* Linux's entry point incorrectly contains a virtual address. */
+ kernel_params.kernel_addr = elf->ehdr.ehdr64.e_entry;
+ kernel_params.kernel_size = grub_elf64_size (elf, &base, 0);
+
+ if (kernel_params.kernel_size == 0)
+ return grub_errno;
+
+ phys_addr = base;
+ kernel_params.kernel_size = ALIGN_UP (base + kernel_params.kernel_size - base, 8);
+
+ asm volatile ("csrrd %0, 0x181" : "=r" (addr));
+ if (addr & 0x1) {
+ flag = GRUB_ELF_LOAD_FLAGS_NONE;
+ } else {
+ flag = GRUB_ELF_LOAD_FLAGS_30BITS;
+ base &= ~ELF64_LOADMASK;
+ kernel_params.kernel_addr &= ~ELF64_LOADMASK;
+ }
+
+ playground = grub_linux_loongarch_alloc_virtual_mem_addr (phys_addr,
+ kernel_params.kernel_size,
+ &err);
+ if (playground == NULL)
+ return err;
+
+ /* Now load the segments into the area we claimed. */
+ return grub_elf64_load (elf, filename, playground - base,
+ flag, 0, 0);
+}
+
+static grub_err_t
+grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ grub_file_t file = 0;
+ struct linux_arch_kernel_header lh;
+ struct boot_params_interface *boot_params = NULL;
+ grub_elf_t elf = NULL;
+ grub_err_t err;
+ grub_size_t cmdline_size;
+ int i;
+
+ grub_dl_ref (my_mod);
+
+ /* Release the previously used memory. */
+ grub_loader_unset ();
+
+ if (argc == 0)
+ {
+ grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+ goto fail;
+ }
+
+ file = grub_file_open (argv[0], GRUB_FILE_TYPE_LINUX_KERNEL);
+ if (!file)
+ goto fail;
+
+ kernel_params.kernel_size = grub_file_size (file);
+ grub_dprintf ("linux", "kernel file size: %" PRIuGRUB_SIZE "\n",
+ kernel_params.kernel_size);
+
+ if (grub_file_read (file, &lh, sizeof (lh)) < (long) sizeof (lh))
+ return grub_errno;
+
+ if (grub_arch_efi_linux_check_image (&lh) == GRUB_ERR_NONE) {
+ grub_loongarch_linux_type = GRUB_LOONGARCH_LINUX_EFI;
+ }
+
+ if (grub_loongarch_linux_type != GRUB_LOONGARCH_LINUX_EFI) {
+ elf = grub_elf_file (file, argv[0]);
+ if (elf != NULL)
+ {
+ /* linux kernel type is ELF */
+ grub_loongarch_linux_type = GRUB_LOONGARCH_LINUX_ELF;
+ if (elf->ehdr.ehdr64.e_type != ET_EXEC)
+ {
+ grub_error (GRUB_ERR_UNKNOWN_OS,
+ N_("this ELF file is not of the right type"));
+ goto fail;
+ }
+ if (elf->ehdr.ehdr64.e_machine != EM_LOONGARCH)
+ {
+ grub_error (GRUB_ERR_BAD_OS, "invalid magic number");
+ goto fail;
+ }
+
+ if (grub_elf_is_elf64 (elf))
+ {
+ err = grub_linux_loongarch_elf_load_kernel (elf, argv[0]);
+ if (err)
+ goto fail;
+ } else {
+ grub_error (GRUB_ERR_BAD_OS, N_("invalid arch-dependent ELF magic"));
+ goto fail;
+ }
+ grub_dprintf ("linux", "kernel @ %p\n", (void*) elf->ehdr.ehdr64.e_entry);
+ }
+ } else {
+ if (grub_file_seek (file, 0) == (grub_off_t) -1)
+ goto fail;
+
+ if (grub_file_read (file, &lh, sizeof (lh)) < (grub_ssize_t) sizeof (lh))
+ {
+ if (!grub_errno)
+ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+ argv[0]);
+ goto fail;
+ }
+
+ if (grub_arch_efi_linux_check_image (&lh) != GRUB_ERR_NONE)
+ {
+ goto fail;
+ }
+ /* linux kernel type is EFI */
+ grub_loongarch_linux_type = GRUB_LOONGARCH_LINUX_EFI;
+ kernel_params.kernel_addr = (grub_addr_t) grub_efi_allocate_any_pages (
+ GRUB_EFI_BYTES_TO_PAGES (kernel_params.kernel_size));
+ grub_dprintf ("linux", "kernel numpages: %" PRIuGRUB_SIZE "\n",
+ GRUB_EFI_BYTES_TO_PAGES (kernel_params.kernel_size));
+ if (!kernel_params.kernel_addr)
+ {
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
+ goto fail;
+ }
+
+ grub_file_seek (file, 0);
+ if (grub_file_read (file, (void*) kernel_params.kernel_addr, kernel_params.kernel_size)
+ < (grub_int64_t) kernel_params.kernel_size)
+ {
+ if (!grub_errno)
+ grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), argv[0]);
+ goto fail;
+ }
+
+ grub_dprintf ("linux", "kernel @ %p\n", (void*) kernel_params.kernel_addr);
+ }
+
+ cmdline_size = grub_loader_cmdline_size (argc, argv) + sizeof (LINUX_IMAGE)
+ + sizeof (GRUB_INITRD_STRING);
+ kernel_params.ramdisk_args_len = grub_loader_cmdline_size (argc, argv)
+ + sizeof (LINUX_IMAGE);
+ kernel_params.linux_argc = argc;
+ kernel_params.linux_args = grub_malloc (cmdline_size);
+ if (!kernel_params.linux_args)
+ {
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
+ goto fail;
+ }
+
+ grub_memcpy (kernel_params.linux_args, LINUX_IMAGE, sizeof (LINUX_IMAGE));
+
+ if (grub_linux_loongarch_elf_get_boot_params (&boot_params) == 1)
+ is_bpi_boot = 1;
+ else
+ is_bpi_boot = 0;
+
+ if (is_bpi_boot == 0)
+ {
+ err = grub_create_loader_cmdline (argc, argv,
+ (char*) ((grub_addr_t) kernel_params.linux_args + sizeof (LINUX_IMAGE) - 1),
+ kernel_params.ramdisk_args_len,
+ GRUB_VERIFY_KERNEL_CMDLINE);
+ if (err)
+ goto fail;
+ } else {
+ /* save args from linux cmdline */
+ char *p = kernel_params.linux_args;
+
+ p += sizeof (LINUX_IMAGE) - 1;
+ for (i=0; i < argc; i++)
+ {
+ grub_memcpy (p, argv[i], grub_strlen(argv[i]) + 1);
+ p += grub_strlen(argv[i]) + 1;
+ }
+ }
+
+ if (grub_errno == GRUB_ERR_NONE)
+ {
+ grub_loader_set (grub_linux_boot, grub_linux_unload, 0);
+ loaded = 1;
+ }
+
+fail:
+ if (elf != NULL) {
+ /* grub_elf_close will call grub_file_close() */
+ grub_elf_close (elf);
+ } else {
+ if (file)
+ grub_file_close (file);
+ }
+
+ if (grub_errno != GRUB_ERR_NONE)
+ {
+ grub_dl_unref (my_mod);
+ loaded = 0;
+ }
+
+ if (kernel_params.linux_args && !loaded)
+ grub_free (kernel_params.linux_args);
+
+ if (grub_loongarch_linux_type == GRUB_LOONGARCH_LINUX_EFI) {
+ if (kernel_params.kernel_addr && !loaded)
+ grub_efi_free_pages ((grub_addr_t) kernel_params.kernel_addr,
+ GRUB_EFI_BYTES_TO_PAGES (kernel_params.kernel_size));
+ }
+
+ return grub_errno;
+}
+
+static grub_err_t
+grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
+ int argc, char *argv[])
+{
+ struct grub_linux_initrd_context initrd_ctx = { 0, 0, 0 };
+ grub_size_t initrd_size;
+ void *initrd_mem = NULL;
+ grub_err_t err;
+
+ if (argc == 0)
+ {
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+ goto fail;
+ }
+
+ if (!loaded)
+ {
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("you need to load the kernel first"));
+ goto fail;
+ }
+
+ if (grub_initrd_init (argc, argv, &initrd_ctx))
+ goto fail;
+
+ initrd_size = grub_get_initrd_size (&initrd_ctx);
+ grub_dprintf ("linux", "Loading initrd\n");
+
+ initrd_mem = grub_linux_loongarch_alloc_initrd_mem_align (initrd_size, 0x10000, &err);
+ if (err)
+ goto fail;
+
+ if (!initrd_mem)
+ {
+ grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
+ goto fail;
+ }
+
+ if (grub_initrd_load (&initrd_ctx, initrd_mem))
+ goto fail;
+
+ /* save ramdisk addr and size */
+ kernel_params.ramdisk_addr = (grub_addr_t) initrd_mem;
+ kernel_params.ramdisk_size = initrd_size;
+ grub_dprintf ("linux", "ramdisk [addr=%p, size=0x%lx]\n",
+ (void *) initrd_mem, initrd_size);
+fail:
+ grub_initrd_close (&initrd_ctx);
+
+ return grub_errno;
+}
+
+static grub_command_t cmd_linux, cmd_initrd;
+
+GRUB_MOD_INIT(linux)
+{
+ cmd_linux = grub_register_command ("linux", grub_cmd_linux,
+ N_("FILE [ARGS...]"), N_("Load Linux."));
+ cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd,
+ N_("FILE"), N_("Load initrd."));
+ my_mod = mod;
+}
+
+GRUB_MOD_FINI(linux)
+{
+ grub_unregister_command (cmd_linux);
+ grub_unregister_command (cmd_initrd);
+}
diff --git a/include/grub/loongarch64/efi/memory.h b/include/grub/loongarch64/efi/memory.h
index 792258b2af20..fbe35a4b6bf4 100644
--- a/include/grub/loongarch64/efi/memory.h
+++ b/include/grub/loongarch64/efi/memory.h
@@ -19,7 +19,14 @@
#ifndef GRUB_MEMORY_CPU_HEADER
#include <grub/efi/memory.h>
-#define GRUB_EFI_MAX_USABLE_ADDRESS 0xfffffffffffULL
+static inline grub_uint64_t grub_efi_max_usable_address(void)
+{
+ grub_uint64_t addr;
+ asm volatile ("csrrd %0, 0x181" : "=r" (addr));
+ return addr |= 0xffffffffffUL;
+}
+
+#define GRUB_EFI_MAX_USABLE_ADDRESS (grub_efi_max_usable_address())
#define GRUB_EFI_MAX_ALLOCATION_ADDRESS GRUB_EFI_MAX_USABLE_ADDRESS
#endif /* ! GRUB_MEMORY_CPU_HEADER */
diff --git a/include/grub/loongarch64/linux.h b/include/grub/loongarch64/linux.h
new file mode 100644
index 000000000000..73876b8383bb
--- /dev/null
+++ b/include/grub/loongarch64/linux.h
@@ -0,0 +1,215 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2021 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/>.
+ */
+
+#ifndef GRUB_LOONGARCH64_LINUX_HEADER
+#define GRUB_LOONGARCH64_LINUX_HEADER 1
+
+#include <grub/types.h>
+
+/* LoongArch linux kernel type */
+#define GRUB_LOONGARCH_LINUX_BAD 0
+#define GRUB_LOONGARCH_LINUX_ELF 1
+#define GRUB_LOONGARCH_LINUX_EFI 2
+
+#define GRUB_LOONGSON3_BOOT_MEM_MAP_MAX 128
+
+#define GRUB_LINUX_LOONGARCH_MAGIC_SIGNATURE 0x6E6F73676E6F6F4C /* 'Loongson' */
+
+/* From linux/Documentation/loongarch/booting.txt
+ *
+ * 0-1: MZ
+ * 0x28: LoongArch\0
+ * 0x3c: PE/COFF头偏移
+ * 0x20e:内核版本号偏移-512
+ * riscv的version字段在0x20偏移处,现在LoongArch没有使用,是0
+ */
+struct linux_loongarch64_kernel_header
+{
+ grub_uint32_t code0; /* Executable code */
+ grub_uint32_t code1; /* Executable code */
+ grub_uint64_t text_offset; /* Image load offset */
+ grub_uint64_t res0; /* reserved */
+ grub_uint64_t res1; /* reserved */
+ grub_uint64_t res2; /* reserved */
+ grub_uint64_t magic; /* Magic number, little endian, "Loongson" */
+ grub_uint64_t res3; /* reserved */
+ grub_uint32_t res4; /* reserved */
+ grub_uint32_t hdr_offset; /* Offset of PE/COFF header */
+};
+
+struct linux_loongarch64_kernel_params
+{
+ grub_addr_t kernel_addr; /* kernel entry address */
+ grub_size_t kernel_size; /* kernel size */
+ grub_addr_t ramdisk_addr; /* initrd load address */
+ grub_size_t ramdisk_size; /* initrd size */
+ int ramdisk_args_len; /* position of initrd in linux_args */
+ int linux_argc; /* cmdline parameters number*/
+ grub_addr_t linux_argv; /* cmdline parameters address*/
+ void* linux_args;
+ void* fdt;
+};
+
+#include <grub/efi/efi.h>
+#include <grub/elfload.h>
+
+#define GRUB_EFI_MAX_PHY_ADDRESS 0xffffffffffffULL
+#define ELF32_LOADMASK (0xf0000000UL)
+#define ELF64_LOADMASK (0xf000000000000000ULL)
+#define FLAGS_EFI_SUPPORT_BIT 0
+
+/*initrd info*/
+#define GRUB_RD_START_STRING "rd_start=0xXXXXXXXXXXXXXXXX"
+#define GRUB_RD_SIZE_STRING "rd_size=0xXXXXXXXXXXXXXXXX"
+#define GRUB_INITRD_STRING "initrd=0xXXXXXXXXXXXXXXXX,0xXXXXXXXXXXXXXXXX"
+
+#define FDT_ADDR_CELLS_STRING "#address-cells"
+#define FDT_SIZE_CELLS_STRING "#size-cells"
+#define FDT_ADDR_SIZE_EXTRA ((2 * grub_fdt_prop_entry_size (sizeof(grub_uint32_t))) + \
+ sizeof (FDT_ADDR_CELLS_STRING) + \
+ sizeof (FDT_SIZE_CELLS_STRING))
+
+struct efi_boot_memmap {
+ grub_efi_uintn_t map_size;
+ grub_efi_uintn_t desc_size;
+ grub_efi_uint32_t desc_ver;
+ grub_efi_uintn_t map_key;
+ grub_efi_uintn_t buff_size;
+ grub_efi_memory_descriptor_t map[];
+};
+
+struct efi_initrd {
+ grub_efi_uintn_t base;
+ grub_efi_uintn_t size;
+};
+
+/*
+ * These are set up by the setup-routine at boot-time:
+ */
+struct screen_info {
+ grub_efi_uint8_t orig_x; /* 0x00 */
+ grub_efi_uint8_t orig_y; /* 0x01 */
+ grub_efi_uint16_t ext_mem_k; /* 0x02 */
+ grub_efi_uint16_t orig_video_page; /* 0x04 */
+ grub_efi_uint8_t orig_video_mode; /* 0x06 */
+ grub_efi_uint8_t orig_video_cols; /* 0x07 */
+ grub_efi_uint8_t flags; /* 0x08 */
+ grub_efi_uint8_t unused2; /* 0x09 */
+ grub_efi_uint16_t orig_video_ega_bx;/* 0x0a */
+ grub_efi_uint16_t unused3; /* 0x0c */
+ grub_efi_uint8_t orig_video_lines; /* 0x0e */
+ grub_efi_uint8_t orig_video_isVGA; /* 0x0f */
+ grub_efi_uint16_t orig_video_points;/* 0x10 */
+
+ /* VESA graphic mode -- linear frame buffer */
+ grub_efi_uint16_t lfb_width; /* 0x12 */
+ grub_efi_uint16_t lfb_height; /* 0x14 */
+ grub_efi_uint16_t lfb_depth; /* 0x16 */
+ grub_efi_uint32_t lfb_base; /* 0x18 */
+ grub_efi_uint32_t lfb_size; /* 0x1c */
+ grub_efi_uint16_t cl_magic, cl_offset; /* 0x20 */
+ grub_efi_uint16_t lfb_linelength; /* 0x24 */
+ grub_efi_uint8_t red_size; /* 0x26 */
+ grub_efi_uint8_t red_pos; /* 0x27 */
+ grub_efi_uint8_t green_size; /* 0x28 */
+ grub_efi_uint8_t green_pos; /* 0x29 */
+ grub_efi_uint8_t blue_size; /* 0x2a */
+ grub_efi_uint8_t blue_pos; /* 0x2b */
+ grub_efi_uint8_t rsvd_size; /* 0x2c */
+ grub_efi_uint8_t rsvd_pos; /* 0x2d */
+ grub_efi_uint16_t vesapm_seg; /* 0x2e */
+ grub_efi_uint16_t vesapm_off; /* 0x30 */
+ grub_efi_uint16_t pages; /* 0x32 */
+ grub_efi_uint16_t vesa_attributes; /* 0x34 */
+ grub_efi_uint32_t capabilities; /* 0x36 */
+ grub_efi_uint32_t ext_lfb_base; /* 0x3a */
+ grub_efi_uint8_t _reserved[2]; /* 0x3e */
+} __attribute__((packed));
+
+#define GRUB_VIDEO_TYPE_EFI 0x70
+#define GRUB_VIDEO_CAPABILITY_SKIP_QUIRKS (1 << 0)
+#define GRUB_VIDEO_CAPABILITY_64BIT_BASE (1 << 1) /* Frame buffer base is 64-bit */
+
+/* From arch/loongarch/include/asm/mach-loongson64/boot_param.h */
+struct _extention_list_hdr {
+ grub_uint64_t signature;
+ grub_uint32_t length;
+ grub_uint8_t revision;
+ grub_uint8_t checksum;
+ union {
+ struct _extention_list_hdr *next;
+ grub_uint64_t next_offset;
+ };
+
+} GRUB_PACKED;
+
+struct boot_params_interface {
+ grub_uint64_t signature; /* {"B", "P", "I", "0", "1", ... } */
+ grub_efi_system_table_t *systemtable;
+ union {
+ struct _extention_list_hdr *extlist;
+ grub_uint64_t extlist_offset;
+ };
+ grub_uint64_t flags;
+}GRUB_PACKED;
+
+struct loongsonlist_mem_map {
+ struct _extention_list_hdr header; /* {"M", "E", "M"} */
+ grub_uint8_t map_count;
+ struct memmap {
+ grub_uint32_t mem_type;
+ grub_uint64_t mem_start;
+ grub_uint64_t mem_size;
+ } GRUB_PACKED map[GRUB_LOONGSON3_BOOT_MEM_MAP_MAX];
+}GRUB_PACKED;
+
+grub_err_t
+finalize_efi_params_linux (struct linux_loongarch64_kernel_params *kernel_params);
+
+grub_err_t
+grub_linux_loongarch_elf_linux_boot_image (struct linux_loongarch64_kernel_params
+ *kernel_params);
+
+void*
+grub_linux_loongarch_alloc_virtual_mem_addr (grub_addr_t addr,
+ grub_size_t size,
+ grub_err_t *err);
+
+void*
+grub_linux_loongarch_alloc_virtual_mem_align (grub_size_t size,
+ grub_size_t align,
+ grub_err_t *err);
+
+void*
+grub_linux_loongarch_alloc_initrd_mem_align (grub_size_t size,
+ grub_size_t align,
+ grub_err_t *err);
+
+void
+grub_linux_loongarch_elf_relocator_unload (void);
+
+int
+grub_linux_loongarch_elf_get_boot_params (struct boot_params_interface **boot_params);
+
+grub_err_t
+grub_linux_loongarch_elf_boot_params (struct boot_params_interface *boot_params);
+
+grub_err_t
+grub_linux_loongarch_elf_load_kernel (grub_elf_t elf, const char *filename);
+
+#endif /* ! GRUB_LOONGARCH64_LINUX_HEADER */
diff --git a/include/grub/loongarch64/memory.h b/include/grub/loongarch64/memory.h
new file mode 100644
index 000000000000..cc9faefc1d9d
--- /dev/null
+++ b/include/grub/loongarch64/memory.h
@@ -0,0 +1,59 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2017 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/>.
+ */
+
+#ifndef GRUB_MEMORY_CPU_HEADER
+#define GRUB_MEMORY_CPU_HEADER 1
+
+#ifndef ASM_FILE
+#include <grub/symbol.h>
+#include <grub/err.h>
+#include <grub/types.h>
+#endif
+
+#ifndef ASM_FILE
+
+typedef grub_addr_t grub_phys_addr_t;
+
+static inline grub_phys_addr_t
+grub_vtop (void *a)
+{
+ if (-1 == ((grub_int64_t) a >> 32))
+ return ((grub_phys_addr_t) a) & 0x1fffffffUL;
+ return ((grub_phys_addr_t) a) & 0xffffffffffffUL;
+}
+
+static inline void *
+grub_map_memory (grub_phys_addr_t a, grub_size_t size)
+{
+ grub_uint64_t addr;
+ asm volatile ("csrrd %0, 0x181" : "=r" (addr));
+ if (addr & 0x1)
+ return (void *) (a | (addr & 0xffffffffffffff00UL));
+ else
+ return (void *) a;
+}
+
+static inline void
+grub_unmap_memory (void *a __attribute__ ((unused)),
+ grub_size_t size __attribute__ ((unused)))
+{
+}
+
+#endif
+
+#endif
diff --git a/include/grub/loongarch64/relocator.h b/include/grub/loongarch64/relocator.h
new file mode 100644
index 000000000000..cef3aaaf77ba
--- /dev/null
+++ b/include/grub/loongarch64/relocator.h
@@ -0,0 +1,38 @@
+/*
+ * 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/>.
+ */
+
+#ifndef GRUB_RELOCATOR_CPU_HEADER
+#define GRUB_RELOCATOR_CPU_HEADER 1
+
+#include <grub/types.h>
+#include <grub/err.h>
+#include <grub/relocator.h>
+
+struct grub_relocator64_state
+{
+ /* gpr[0] is ignored since it's hardwired to 0. */
+ grub_uint64_t gpr[32];
+ /* Register holding target $pc. */
+ int jumpreg;
+};
+
+grub_err_t
+grub_relocator64_boot (struct grub_relocator *rel,
+ struct grub_relocator64_state state);
+
+#endif /* ! GRUB_RELOCATOR_CPU_HEADER */
--
2.43.0
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化