From 28dcf484821b95d7de85de33dbd41ee7682e5778 Mon Sep 17 00:00:00 2001 From: Xue Liu 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 . + */ + +#include +#include + +#include +#include +#include +#include + +#include +#include + +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 . + */ + +#include + + .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 . + */ +#include +#include +#include +#include +#include + +#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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +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 -#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 . + */ + +#ifndef GRUB_LOONGARCH64_LINUX_HEADER +#define GRUB_LOONGARCH64_LINUX_HEADER 1 + +#include + +/* 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 +#include + +#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 . + */ + +#ifndef GRUB_MEMORY_CPU_HEADER +#define GRUB_MEMORY_CPU_HEADER 1 + +#ifndef ASM_FILE +#include +#include +#include +#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 . + */ + +#ifndef GRUB_RELOCATOR_CPU_HEADER +#define GRUB_RELOCATOR_CPU_HEADER 1 + +#include +#include +#include + +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