grub_err_t grub_relocator16_boot (struct grub_relocator *rel, struct grub_relocator16_state state) { grub_err_t err; void *relst; grub_relocator_chunk_t ch; /* Put it higher than the byte it checks for A20 check. */ err = grub_relocator_alloc_chunk_align (rel, &ch, 0x8010, 0xa0000 - RELOCATOR_SIZEOF (16) - GRUB_RELOCATOR16_STACK_SIZE, RELOCATOR_SIZEOF (16) + GRUB_RELOCATOR16_STACK_SIZE, 16, GRUB_RELOCATOR_PREFERENCE_NONE, 0); if (err) return err; grub_relocator16_cs = state.cs; grub_relocator16_ip = state.ip; grub_relocator16_ds = state.ds; grub_relocator16_es = state.es; grub_relocator16_fs = state.fs; grub_relocator16_gs = state.gs; grub_relocator16_ss = state.ss; grub_relocator16_sp = state.sp; grub_relocator16_ebp = state.ebp; grub_relocator16_ebx = state.ebx; grub_relocator16_edx = state.edx; grub_relocator16_esi = state.esi; #ifdef GRUB_MACHINE_PCBIOS grub_relocator16_idt = *grub_realidt; #else grub_relocator16_idt.base = 0; grub_relocator16_idt.limit = 0; #endif grub_relocator16_keep_a20_enabled = state.a20; grub_memmove (get_virtual_current_address (ch), &grub_relocator16_start, RELOCATOR_SIZEOF (16)); err = grub_relocator_prepare_relocs (rel, get_physical_target_address (ch), &relst, NULL); if (err) return err; asm volatile ("cli"); ((void (*) (void)) relst) (); /* Not reached. */ return GRUB_ERR_NONE; }
grub_err_t grub_relocator32_boot (struct grub_relocator *rel, struct grub_relocator32_state state, int avoid_efi_bootservices) { grub_err_t err; void *relst; grub_relocator_chunk_t ch; err = grub_relocator_alloc_chunk_align (rel, &ch, 0, (0xffffffff - RELOCATOR_SIZEOF (32)) + 1, RELOCATOR_SIZEOF (32), 16, GRUB_RELOCATOR_PREFERENCE_NONE, avoid_efi_bootservices); if (err) return err; grub_relocator32_eax = state.eax; grub_relocator32_ebx = state.ebx; grub_relocator32_ecx = state.ecx; grub_relocator32_edx = state.edx; grub_relocator32_eip = state.eip; grub_relocator32_esp = state.esp; grub_relocator32_ebp = state.ebp; grub_relocator32_esi = state.esi; grub_relocator32_edi = state.edi; grub_memmove (get_virtual_current_address (ch), &grub_relocator32_start, RELOCATOR_SIZEOF (32)); err = grub_relocator_prepare_relocs (rel, get_physical_target_address (ch), &relst, NULL); if (err) return err; asm volatile ("cli"); ((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_addr_t min_addr, grub_addr_t max_addr) { grub_err_t err; void *relst; grub_relocator_chunk_t ch; err = grub_relocator_alloc_chunk_align (rel, &ch, min_addr, max_addr - RELOCATOR_SIZEOF (64), RELOCATOR_SIZEOF (64), 16, GRUB_RELOCATOR_PREFERENCE_NONE, 0); if (err) return err; grub_relocator64_rax = state.rax; grub_relocator64_rbx = state.rbx; grub_relocator64_rcx = state.rcx; grub_relocator64_rdx = state.rdx; grub_relocator64_rip = state.rip; grub_relocator64_rsp = state.rsp; grub_relocator64_rsi = state.rsi; grub_relocator64_cr3 = state.cr3; grub_memmove (get_virtual_current_address (ch), &grub_relocator64_start, RELOCATOR_SIZEOF (64)); err = grub_relocator_prepare_relocs (rel, get_physical_target_address (ch), &relst, NULL); if (err) return err; asm volatile ("cli"); ((void (*) (void)) relst) (); /* Not reached. */ return GRUB_ERR_NONE; }
void grub_cpu_relocator_init (void) { grub_relocator_forward_size = RELOCATOR_SIZEOF(forward); grub_relocator_backward_size = RELOCATOR_SIZEOF(backward); }
grub_err_t PREFIX (boot) (void *relocator, grub_uint32_t dest, struct grub_relocator32_state state) { grub_size_t size; char *playground; playground = (char *) relocator - PRE_REGION_SIZE; size = *(grub_size_t *) playground; grub_dprintf ("relocator", "Relocator: source: %p, destination: 0x%x, size: 0x%lx\n", relocator, (unsigned) dest, (unsigned long) size); /* Very unlikely condition: Relocator may risk overwrite itself. Just move it a bit up. */ if ((grub_addr_t) dest < (grub_addr_t) relocator + (RELOCATOR_SIZEOF (backward) + RELOCATOR_ALIGN) && (grub_addr_t) dest + (RELOCATOR_SIZEOF (forward) + RELOCATOR_ALIGN) > (grub_addr_t) relocator) { void *relocator_new = ((grub_uint8_t *) relocator) + (RELOCATOR_SIZEOF (forward) + RELOCATOR_ALIGN) + (RELOCATOR_SIZEOF (backward) + RELOCATOR_ALIGN); grub_dprintf ("relocator", "Overwrite condition detected moving " "relocator from %p to %p\n", relocator, relocator_new); grub_memmove (relocator_new, relocator, (RELOCATOR_SIZEOF (forward) + RELOCATOR_ALIGN) + size + (RELOCATOR_SIZEOF (backward) + RELOCATOR_ALIGN)); relocator = relocator_new; } if ((grub_addr_t) dest >= (grub_addr_t) relocator) { int overhead; overhead = dest - ALIGN_UP (dest - RELOCATOR_SIZEOF (backward) - RELOCATOR_ALIGN, RELOCATOR_ALIGN); grub_dprintf ("relocator", "Backward relocator: code %p, source: %p, " "destination: 0x%x, size: 0x%lx\n", (char *) relocator - overhead, (char *) relocator - overhead, (unsigned) dest - overhead, (unsigned long) size + overhead); write_call_relocator_bw ((char *) relocator - overhead, (char *) relocator - overhead, dest - overhead, size + overhead, state); } else { int overhead; overhead = ALIGN_UP (dest + size, RELOCATOR_ALIGN) + RELOCATOR_SIZEOF (forward) - (dest + size); grub_dprintf ("relocator", "Forward relocator: code %p, source: %p, " "destination: 0x%x, size: 0x%lx\n", (char *) relocator + size + overhead - RELOCATOR_SIZEOF (forward), relocator, (unsigned) dest, (unsigned long) size + overhead); write_call_relocator_fw ((char *) relocator + size + overhead - RELOCATOR_SIZEOF (forward), relocator, dest, size + overhead, state); } /* Not reached. */ return GRUB_ERR_NONE; }