diff options
Diffstat (limited to '')
-rw-r--r-- | grub-core/mmap/i386/pc/mmap.c | 211 | ||||
-rw-r--r-- | grub-core/mmap/i386/pc/mmap_helper.S | 163 |
2 files changed, 374 insertions, 0 deletions
diff --git a/grub-core/mmap/i386/pc/mmap.c b/grub-core/mmap/i386/pc/mmap.c new file mode 100644 index 0000000..6ab4f67 --- /dev/null +++ b/grub-core/mmap/i386/pc/mmap.c @@ -0,0 +1,211 @@ +/* Mmap management. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009 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/machine/memory.h> +#include <grub/memory.h> +#include <grub/misc.h> +#include <grub/term.h> +#include <grub/loader.h> + +#define min(a,b) (((a) < (b)) ? (a) : (b)) + +static void *hooktarget = 0; + +extern grub_uint8_t grub_machine_mmaphook_start; +extern grub_uint8_t grub_machine_mmaphook_end; +extern grub_uint8_t grub_machine_mmaphook_int12; +extern grub_uint8_t grub_machine_mmaphook_int15; + +static grub_uint16_t grub_machine_mmaphook_int12offset = 0; +static grub_uint16_t grub_machine_mmaphook_int12segment = 0; +extern grub_uint16_t grub_machine_mmaphook_int15offset; +extern grub_uint16_t grub_machine_mmaphook_int15segment; + +extern grub_uint16_t grub_machine_mmaphook_mmap_num; +extern grub_uint16_t grub_machine_mmaphook_kblow; +extern grub_uint16_t grub_machine_mmaphook_kbin16mb; +extern grub_uint16_t grub_machine_mmaphook_64kbin4gb; + +/* Helper for preboot. */ +static int fill_hook (grub_uint64_t addr, grub_uint64_t size, + grub_memory_type_t type, void *data) +{ + struct grub_e820_mmap_entry **hookmmapcur = data; + grub_dprintf ("mmap", "mmap chunk %llx-%llx:%x\n", addr, addr + size, type); + (*hookmmapcur)->addr = addr; + (*hookmmapcur)->len = size; + (*hookmmapcur)->type = type; + (*hookmmapcur)++; + return 0; +} + +static grub_err_t +preboot (int noreturn __attribute__ ((unused))) +{ + struct grub_e820_mmap_entry *hookmmap, *hookmmapcur; + + if (! hooktarget) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "no space is allocated for memory hook"); + + grub_dprintf ("mmap", "installing preboot handlers\n"); + + hookmmapcur = hookmmap = (struct grub_e820_mmap_entry *) + ((grub_uint8_t *) hooktarget + (&grub_machine_mmaphook_end + - &grub_machine_mmaphook_start)); + + grub_mmap_iterate (fill_hook, &hookmmapcur); + grub_machine_mmaphook_mmap_num = hookmmapcur - hookmmap; + + grub_machine_mmaphook_kblow = grub_mmap_get_lower () >> 10; + grub_machine_mmaphook_kbin16mb + = min (grub_mmap_get_upper (),0x3f00000ULL) >> 10; + grub_machine_mmaphook_64kbin4gb + = min (grub_mmap_get_post64 (), 0xfc000000ULL) >> 16; + + /* Correct BDA. */ + *((grub_uint16_t *) 0x413) = grub_mmap_get_lower () >> 10; + + /* Save old interrupt handlers. */ + grub_machine_mmaphook_int12offset = *((grub_uint16_t *) 0x48); + grub_machine_mmaphook_int12segment = *((grub_uint16_t *) 0x4a); + grub_machine_mmaphook_int15offset = *((grub_uint16_t *) 0x54); + grub_machine_mmaphook_int15segment = *((grub_uint16_t *) 0x56); + + grub_dprintf ("mmap", "hooktarget = %p\n", hooktarget); + + /* Install the interrupt handlers. */ + grub_memcpy (hooktarget, &grub_machine_mmaphook_start, + &grub_machine_mmaphook_end - &grub_machine_mmaphook_start); + + *((grub_uint16_t *) 0x4a) = ((grub_addr_t) hooktarget) >> 4; + *((grub_uint16_t *) 0x56) = ((grub_addr_t) hooktarget) >> 4; + *((grub_uint16_t *) 0x48) = &grub_machine_mmaphook_int12 + - &grub_machine_mmaphook_start; + *((grub_uint16_t *) 0x54) = &grub_machine_mmaphook_int15 + - &grub_machine_mmaphook_start; + + return GRUB_ERR_NONE; +} + +static grub_err_t +preboot_rest (void) +{ + /* Restore old interrupt handlers. */ + *((grub_uint16_t *) 0x48) = grub_machine_mmaphook_int12offset; + *((grub_uint16_t *) 0x4a) = grub_machine_mmaphook_int12segment; + *((grub_uint16_t *) 0x54) = grub_machine_mmaphook_int15offset; + *((grub_uint16_t *) 0x56) = grub_machine_mmaphook_int15segment; + + return GRUB_ERR_NONE; +} + +/* Helper for malloc_hook. */ +static int +count_hook (grub_uint64_t addr __attribute__ ((unused)), + grub_uint64_t size __attribute__ ((unused)), + grub_memory_type_t type __attribute__ ((unused)), void *data) +{ + int *regcount = data; + (*regcount)++; + return 0; +} + +static grub_err_t +malloc_hook (void) +{ + static int reentry = 0; + static int mmapregion = 0; + static int slots_available = 0; + int hooksize; + int regcount = 0; + + if (reentry) + return GRUB_ERR_NONE; + + grub_dprintf ("mmap", "registering\n"); + + grub_mmap_iterate (count_hook, ®count); + + /* Mapping hook itself may introduce up to 2 additional regions. */ + regcount += 2; + + if (regcount <= slots_available) + return GRUB_ERR_NONE; + + if (mmapregion) + { + grub_mmap_free_and_unregister (mmapregion); + mmapregion = 0; + hooktarget = 0; + } + + hooksize = &grub_machine_mmaphook_end - &grub_machine_mmaphook_start + + regcount * sizeof (struct grub_e820_mmap_entry); + /* Allocate an integer number of KiB. */ + hooksize = ((hooksize - 1) | 0x3ff) + 1; + slots_available = (hooksize - (&grub_machine_mmaphook_end + - &grub_machine_mmaphook_start)) + / sizeof (struct grub_e820_mmap_entry); + + reentry = 1; + hooktarget + = grub_mmap_malign_and_register (16, ALIGN_UP (hooksize, 16), &mmapregion, + GRUB_MEMORY_RESERVED, + GRUB_MMAP_MALLOC_LOW); + reentry = 0; + + if (! hooktarget) + { + slots_available = 0; + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "no space for mmap hook"); + } + return GRUB_ERR_NONE; +} + +grub_err_t +grub_machine_mmap_register (grub_uint64_t start __attribute__ ((unused)), + grub_uint64_t size __attribute__ ((unused)), + int type __attribute__ ((unused)), + int handle __attribute__ ((unused))) +{ + grub_err_t err; + static struct grub_preboot *preb_handle = 0; + + err = malloc_hook (); + if (err) + return err; + + if (! preb_handle) + { + grub_dprintf ("mmap", "adding preboot\n"); + preb_handle + = grub_loader_register_preboot_hook (preboot, preboot_rest, + GRUB_LOADER_PREBOOT_HOOK_PRIO_MEMORY); + if (! preb_handle) + return grub_errno; + } + return GRUB_ERR_NONE; +} + +grub_err_t +grub_machine_mmap_unregister (int handle __attribute__ ((unused))) +{ + return GRUB_ERR_NONE; +} diff --git a/grub-core/mmap/i386/pc/mmap_helper.S b/grub-core/mmap/i386/pc/mmap_helper.S new file mode 100644 index 0000000..8e251a1 --- /dev/null +++ b/grub-core/mmap/i386/pc/mmap_helper.S @@ -0,0 +1,163 @@ +/* Mmap management. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009 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> + +#define DS(x) ((x) - LOCAL (segstart)) + +LOCAL (segstart): +VARIABLE(grub_machine_mmaphook_start) + .code16 +VARIABLE(grub_machine_mmaphook_int12) + push %ds + push %cs + pop %ds +#ifdef __APPLE__ + LOCAL(kblow_offset) = DS (LOCAL (kblow)) + movw LOCAL(kblow_offset), %ax +#else + movw DS (LOCAL (kblow)), %ax +#endif + pop %ds + iret + +VARIABLE(grub_machine_mmaphook_int15) + pushf + cmpw $0xe801, %ax + jz LOCAL (e801) + cmpw $0xe820, %ax + jz LOCAL (e820) + cmpb $0x88, %ah + jz LOCAL (h88) + popf + /* ljmp */ + .byte 0xea +VARIABLE (grub_machine_mmaphook_int15offset) + .word 0 +VARIABLE (grub_machine_mmaphook_int15segment) + .word 0 + +LOCAL (e801): + popf + push %ds + push %cs + pop %ds +#ifdef __APPLE__ + LOCAL(kbin16mb_offset) = DS (LOCAL (kbin16mb)) + LOCAL(m64kbin4gb_offset) = DS (LOCAL (m64kbin4gb)) + movw LOCAL(kbin16mb_offset), %ax + movw LOCAL(m64kbin4gb_offset), %bx +#else + movw DS (LOCAL (kbin16mb)), %ax + movw DS (LOCAL (m64kbin4gb)), %bx +#endif + movw %ax, %cx + movw %bx, %dx + pop %ds + clc + jmp LOCAL (iret_cf) + +LOCAL (h88): + popf + push %ds + push %cs + pop %ds +#ifdef __APPLE__ + movw LOCAL(kbin16mb_offset), %ax +#else + movw DS (LOCAL (kbin16mb)), %ax +#endif + pop %ds + clc + jmp LOCAL (iret_cf) + +LOCAL (e820): + popf + push %ds + push %cs + pop %ds + cmpw $20, %cx + jb LOCAL (errexit) +#ifdef __APPLE__ + LOCAL(mmap_num_offset) = DS (LOCAL (mmap_num)) + cmpw LOCAL(mmap_num_offset), %bx +#else + cmpw DS (LOCAL (mmap_num)), %bx +#endif + jae LOCAL (errexit) + cmp $0x534d4150, %edx + jne LOCAL (errexit) + push %si + push %di + movw $20, %cx +#ifdef __APPLE__ + LOCAL(mmaphook_map_offset) = DS(LOCAL (mmaphook_mmap)) + movw $LOCAL(mmaphook_map_offset), %si +#else + movw $(DS(LOCAL (mmaphook_mmap))), %si +#endif + mov %bx, %ax + imul $20, %ax + add %ax, %si + rep movsb + pop %di + pop %si + movl $20, %ecx + inc %bx +#ifdef __APPLE__ + cmpw LOCAL(mmap_num_offset), %bx +#else + cmpw DS(LOCAL (mmap_num)), %bx +#endif + jb LOCAL (noclean) + xor %bx, %bx +LOCAL (noclean): + mov $0x534d4150, %eax + pop %ds + clc + jmp LOCAL (iret_cf) +LOCAL (errexit): + mov $0x534d4150, %eax + pop %ds + xor %bx, %bx + stc + +LOCAL (iret_cf): + push %bp + mov %sp, %bp + setc 6(%bp) + pop %bp + iret + +VARIABLE(grub_machine_mmaphook_mmap_num) +LOCAL (mmap_num): + .word 0 +VARIABLE(grub_machine_mmaphook_kblow) +LOCAL (kblow): + .word 0 +VARIABLE (grub_machine_mmaphook_kbin16mb) +LOCAL (kbin16mb): + .word 0 +VARIABLE (grub_machine_mmaphook_64kbin4gb) +LOCAL (m64kbin4gb): + .word 0 +LOCAL (mmaphook_mmap): + /* Memory map is placed just after the interrupt handlers. */ +VARIABLE(grub_machine_mmaphook_end) + .byte 0 |