Add ARM64 relocator code to support multiboot2 loader Signed-off-by: Chris plant <ch...@monkeyircd.org>
--- grub-core/lib/arm64/relocator.c | 267 ++++++++++++++++++++++++++++ grub-core/lib/arm64/relocator_asm.S | 57 ++++++ include/grub/arm64/relocator.h | 52 ++++++ 3 files changed, 376 insertions(+) create mode 100644 grub-core/lib/arm64/relocator.c create mode 100644 grub-core/lib/arm64/relocator_asm.S create mode 100644 include/grub/arm64/relocator.h diff --git a/grub-core/lib/arm64/relocator.c b/grub- core/lib/arm64/relocator.c new file mode 100644 index 000000000..a287bd5ff --- /dev/null +++ b/grub-core/lib/arm64/relocator.c @@ -0,0 +1,267 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020 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/arm64/relocator.h> +#include <grub/relocator_private.h> + + +/* Defines to simplify creating machine code instructions */ + +#define AARCH64_INSTR_MOVZ_64 0xd2800000 +#define AARCH64_INSTR_MOVK_64 0xf2800000 +#define AARCH64_INSTR_MOVZ_32 0x52800000 +#define AARCH64_INSTR_MOVK_32 0x72800000 +#define AARCH64_INSTR_BR 0xd61f0000 +#define AARCH64_INSTR_NOP 0xd503201f + +#define AARCH64_INSTR_SHIFT_16 1 << 21 +#define AARCH64_INSTR_SHIFT_32 2 << 21 +#define AARCH64_INSTR_SHIFT_48 3 << 21 + +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; +extern grub_uint32_t grub_relocator_check_endian; + +#define REGW32_SIZEOF (2 * sizeof (grub_uint32_t)) +#define REGW64_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) \ + + REGW32_SIZEOF * 3) +grub_size_t grub_relocator_align = sizeof (grub_uint32_t); +grub_size_t grub_relocator_forward_size; +grub_size_t grub_relocator_backward_size; +grub_size_t grub_relocator_jumper_size = JUMP_SIZEOF + REGW64_SIZEOF; + +void +grub_cpu_relocator_init (void) +{ + grub_relocator_forward_size = RELOCATOR_SIZEOF(forward); + grub_relocator_backward_size = RELOCATOR_SIZEOF(backward); +} + +/* + * Create machine code in memory @ *target to write val + * to register regn using 64 bit register width + */ +static void +write_reg64 (int regn, grub_uint64_t val, void **target) +{ + grub_uint16_t hword; + + hword = val >> 48 & 0xFFFF; + *(grub_uint32_t *) *target = AARCH64_INSTR_MOVZ_64 | AARCH64_INSTR_SHIFT_48 | + (hword << 5) | regn; + *target = ((grub_uint32_t *) *target) + 1; + + hword = val >> 32 & 0xFFFF; + *(grub_uint32_t *) *target = AARCH64_INSTR_MOVK_64 | AARCH64_INSTR_SHIFT_32 | + (hword << 5) | regn; + *target = ((grub_uint32_t *) *target) + 1; + + hword = val >> 16 & 0xFFFF; + *(grub_uint32_t *) *target = AARCH64_INSTR_MOVK_64 | AARCH64_INSTR_SHIFT_16 | + (hword << 5) | regn; + *target = ((grub_uint32_t *) *target) + 1; + + hword = val >> 0 & 0xFFFF; + *(grub_uint32_t *) *target = AARCH64_INSTR_MOVK_64 | (hword << 5) | regn; + *target = ((grub_uint32_t *) *target) + 1; +} + +/* + * Create machine code in memory @ *target to write val + * to register regn using 32 bit register width + */ +static void +write_reg32 (int regn, grub_uint32_t val, void **target) +{ + grub_uint16_t hword; + + hword = val >> 16 & 0xFFFF; + *(grub_uint32_t *) *target = AARCH64_INSTR_MOVZ_32 | AARCH64_INSTR_SHIFT_16 | + (hword << 5) | regn; + *target = ((grub_uint32_t *) *target) + 1; + + + hword = val & 0xFFFF; + *(grub_uint32_t *) *target = AARCH64_INSTR_MOVK_32 | (hword << 5) | regn; + + *target = ((grub_uint32_t *) *target) + 1; +} + + + +/* + * Create machine code in memory @ *target to jump to the + * address in register regn + */ +static void +write_jump (int regn, void **target) +{ + + /* br x(regn) */ + *(grub_uint32_t *) *target = AARCH64_INSTR_BR | (regn << 5); + *target = ((grub_uint32_t *) *target) + 1; + + /* nop. */ + *(grub_uint32_t *) *target = AARCH64_INSTR_NOP; + *target = ((grub_uint32_t *) *target) + 1; +} + + +/* + * Write machine code to load a 64 bit memory location to + * a register and then jump to it + */ +void +grub_cpu_relocator_jumper (void *rels, grub_addr_t addr) +{ + /* Write address to the register */ + write_reg64(8,addr,&rels); + + /* Jump to the address in the register */ + write_jump(8,&rels); +} + +/* + * Use general purpose registers to pass variables + * src = x8, dest = x9, size = x10 + */ +void +grub_cpu_relocator_backward (void *ptr, void *src, void *dest, + grub_size_t size) +{ + + write_reg64(8,(grub_uint64_t) src, &ptr); + write_reg64(9,(grub_uint64_t) dest, &ptr); + write_reg64(10,(grub_uint64_t) size, &ptr); + + grub_memcpy (ptr, &grub_relocator_backward_start, + RELOCATOR_SRC_SIZEOF (backward)); +} + + +void +grub_cpu_relocator_forward (void *ptr, void *src, void *dest, + grub_size_t size) +{ + write_reg64(8,(grub_uint64_t) src, &ptr); + write_reg64(9,(grub_uint64_t) dest, &ptr); + write_reg64(10,(grub_uint64_t) size, &ptr); + + grub_memcpy (ptr, &grub_relocator_forward_start, + RELOCATOR_SRC_SIZEOF (forward)); +} + + +grub_err_t +grub_relocator32_boot (struct grub_relocator *rel, + struct grub_relocator32_state state) +{ + grub_relocator_chunk_t ch; + void *ptr; + grub_err_t err; + void *relst; + grub_size_t relsize; + grub_size_t stateset_size = 32 * REGW32_SIZEOF + JUMP_SIZEOF; + unsigned i; + grub_addr_t vtarget; + + err = grub_relocator_alloc_chunk_align (rel, &ch, 0, + (0xffffffff - stateset_size) + + 1, stateset_size, + sizeof (grub_uint32_t), + GRUB_RELOCATOR_PREFERENCE_NONE, 0); + + if (err) + return err; + + ptr = get_virtual_current_address (ch); + + for (i = 0; i < 32; i++) + write_reg32 (i, state.w[i], &ptr); + + write_jump (state.pc_reg, &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); + + ((void (*) (void)) (relst)) (); + + /* Not reached. */ + return GRUB_ERR_NONE; + +} + +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 = 32 * REGW64_SIZEOF + JUMP_SIZEOF; + unsigned i; + + err = grub_relocator_alloc_chunk_align (rel, &ch, 0, + (0xffffffff - stateset_size) + + 1, stateset_size, + sizeof (grub_uint32_t), + GRUB_RELOCATOR_PREFERENCE_NONE, 0); + if (err) + return err; + + ptr = get_virtual_current_address (ch); + for (i = 0; i < 32; i++) + write_reg64 (i, state.x[i], &ptr); + write_jump (state.pc_reg, &ptr); + + err = grub_relocator_prepare_relocs (rel, get_physical_target_address(ch), &relst, &relsize); + if (err) + return err; + + grub_arch_sync_caches ((void *) relst, relsize); + + ((void (*) (void)) relst) (); + + /* Not reached. */ + return GRUB_ERR_NONE; + + +} diff --git a/grub-core/lib/arm64/relocator_asm.S b/grub- core/lib/arm64/relocator_asm.S new file mode 100644 index 000000000..117f98dbe --- /dev/null +++ b/grub-core/lib/arm64/relocator_asm.S @@ -0,0 +1,57 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020 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/>;. + */ + +/* + * aarch64/ARM64 relocator code, heavily based on MIPS code + * adapted by Chris Plant + */ + +#include <grub/symbol.h> + + .p2align 4 /* force 16-byte alignment */ + +VARIABLE (grub_relocator_forward_start) + +copycont1: + ldr x11,[x8] + str x11,[x9] + add x8,x8,#1 + add x9,x9,#1 + sub x10,x10,#1 + cbnz x10, copycont1 + +VARIABLE (grub_relocator_forward_end) + +VARIABLE (grub_relocator_backward_start) + + add x9,x9,x10 + add x8,x8,x10 + /* Backward movsl is implicitly off-by-one. compensate that. */ + sub x9,x9,#1 + sub x8,x8,#1 +copycont2: + ldr x11,[x8] + str x11,[x9] + sub x8,x8,#1 + sub x9,x9,#1 + sub x10,x10, #1 + cbnz x10, copycont2 + + +VARIABLE (grub_relocator_backward_end) + diff --git a/include/grub/arm64/relocator.h b/include/grub/arm64/relocator.h new file mode 100644 index 000000000..4409c472a --- /dev/null +++ b/include/grub/arm64/relocator.h @@ -0,0 +1,52 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020 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 +{ + /* General Purpose Registers (xn) */ + grub_uint64_t x[32]; + + /* The register containing the new pc value */ + int pc_reg; +}; + +struct grub_relocator32_state +{ + /* General Purpose Registers (xn) */ + grub_uint32_t w[32]; + + int pc_reg; +}; + +grub_err_t +grub_relocator64_boot (struct grub_relocator *rel, + struct grub_relocator64_state state); + + +grub_err_t +grub_relocator32_boot (struct grub_relocator *rel, + struct grub_relocator32_state state); + +#endif /* ! GRUB_RELOCATOR_CPU_HEADER */ -- 2.28.0 _______________________________________________ Grub-devel mailing list Grub-devel@gnu.org https://lists.gnu.org/mailman/listinfo/grub-devel