diff options
Diffstat (limited to 'grub-core/kern/i386')
26 files changed, 3025 insertions, 0 deletions
diff --git a/grub-core/kern/i386/coreboot/cbtable.c b/grub-core/kern/i386/coreboot/cbtable.c new file mode 100644 index 0000000..34a2b59 --- /dev/null +++ b/grub-core/kern/i386/coreboot/cbtable.c @@ -0,0 +1,44 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2007,2008,2013 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/i386/coreboot/memory.h> +#include <grub/coreboot/lbio.h> +#include <grub/types.h> +#include <grub/err.h> +#include <grub/misc.h> +#include <grub/dl.h> + +GRUB_MOD_LICENSE ("GPLv3+"); + +grub_linuxbios_table_header_t +grub_linuxbios_get_tables (void) +{ + grub_linuxbios_table_header_t table_header; + /* Assuming table_header is aligned to its size (8 bytes). */ + for (table_header = (grub_linuxbios_table_header_t) 0x500; + table_header < (grub_linuxbios_table_header_t) 0x1000; table_header++) + if (grub_linuxbios_check_signature (table_header)) + return table_header; + + for (table_header = (grub_linuxbios_table_header_t) 0xf0000; + table_header < (grub_linuxbios_table_header_t) 0x100000; table_header++) + if (grub_linuxbios_check_signature (table_header)) + return table_header; + + return 0; +} diff --git a/grub-core/kern/i386/coreboot/init.c b/grub-core/kern/i386/coreboot/init.c new file mode 100644 index 0000000..3314f02 --- /dev/null +++ b/grub-core/kern/i386/coreboot/init.c @@ -0,0 +1,143 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2013 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/kernel.h> +#include <grub/mm.h> +#include <grub/machine/time.h> +#include <grub/machine/memory.h> +#include <grub/machine/console.h> +#include <grub/offsets.h> +#include <grub/types.h> +#include <grub/err.h> +#include <grub/dl.h> +#include <grub/misc.h> +#include <grub/loader.h> +#include <grub/env.h> +#include <grub/cache.h> +#include <grub/time.h> +#include <grub/symbol.h> +#include <grub/cpu/io.h> +#include <grub/cpu/floppy.h> +#include <grub/cpu/tsc.h> +#include <grub/video.h> + +extern grub_uint8_t _start[]; +extern grub_uint8_t _end[]; +extern grub_uint8_t _edata[]; + +void __attribute__ ((noreturn)) +grub_exit (void) +{ + /* We can't use grub_fatal() in this function. This would create an infinite + loop, since grub_fatal() calls grub_abort() which in turn calls grub_exit(). */ + while (1) + grub_cpu_idle (); +} + +grub_addr_t grub_modbase = GRUB_KERNEL_I386_COREBOOT_MODULES_ADDR; +static grub_uint64_t modend; +static int have_memory = 0; + +/* Helper for grub_machine_init. */ +static int +heap_init (grub_uint64_t addr, grub_uint64_t size, grub_memory_type_t type, + void *data __attribute__ ((unused))) +{ + grub_uint64_t begin = addr, end = addr + size; + +#if GRUB_CPU_SIZEOF_VOID_P == 4 + /* Restrict ourselves to 32-bit memory space. */ + if (begin > GRUB_ULONG_MAX) + return 0; + if (end > GRUB_ULONG_MAX) + end = GRUB_ULONG_MAX; +#endif + + if (type != GRUB_MEMORY_AVAILABLE) + return 0; + + /* Avoid the lower memory. */ + if (begin < GRUB_MEMORY_MACHINE_LOWER_SIZE) + begin = GRUB_MEMORY_MACHINE_LOWER_SIZE; + + if (modend && begin < modend) + begin = modend; + + if (end <= begin) + return 0; + + grub_mm_init_region ((void *) (grub_addr_t) begin, (grub_size_t) (end - begin)); + + have_memory = 1; + + return 0; +} + +#ifndef GRUB_MACHINE_MULTIBOOT + +void +grub_machine_init (void) +{ + modend = grub_modules_get_end (); + + grub_video_coreboot_fb_early_init (); + + grub_vga_text_init (); + + grub_machine_mmap_iterate (heap_init, NULL); + if (!have_memory) + grub_fatal ("No memory found"); + + grub_video_coreboot_fb_late_init (); + + grub_font_init (); + grub_gfxterm_init (); + + grub_tsc_init (); +} + +#else + +void +grub_machine_init (void) +{ + modend = grub_modules_get_end (); + + grub_vga_text_init (); + + grub_machine_mmap_init (); + grub_machine_mmap_iterate (heap_init, NULL); + + grub_tsc_init (); +} + +#endif + +void +grub_machine_get_bootlocation (char **device __attribute__ ((unused)), + char **path __attribute__ ((unused))) +{ +} + +void +grub_machine_fini (int flags) +{ + if (flags & GRUB_LOADER_FLAG_NORETURN) + grub_vga_text_fini (); + grub_stop_floppy (); +} diff --git a/grub-core/kern/i386/coreboot/startup.S b/grub-core/kern/i386/coreboot/startup.S new file mode 100644 index 0000000..df6adba --- /dev/null +++ b/grub-core/kern/i386/coreboot/startup.S @@ -0,0 +1,62 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2003,2005,2006,2007,2008 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> +#include <grub/machine/memory.h> +#include <grub/offsets.h> +#include <multiboot.h> +#include <multiboot2.h> + +/* + * Note: GRUB is compiled with the options -mrtd and -mregparm=3. + * So the first three arguments are passed in %eax, %edx, and %ecx, + * respectively, and if a function has a fixed number of arguments + * and the number if greater than three, the function must return + * with "ret $N" where N is ((the number of arguments) - 3) * 4. + */ + + .file "startup.S" + .text + .globl start, _start +start: +_start: +#ifdef GRUB_MACHINE_MULTIBOOT + cmpl $MULTIBOOT_BOOTLOADER_MAGIC, %eax + jne 0f + movl %ebx, EXT_C(startup_multiboot_info) +0: +#endif + + /* initialize the stack */ + movl $GRUB_MEMORY_MACHINE_PROT_STACK, %esp + + /* jump to the main body of C code */ + jmp EXT_C(grub_main) + +/* + * Support for booting GRUB from a Multiboot boot loader (e.g. GRUB itself). + */ + .p2align 2 /* force 4-byte alignment */ +multiboot_header: + /* magic */ + .long 0x1BADB002 + /* flags */ + .long MULTIBOOT_MEMORY_INFO + /* checksum */ + .long -0x1BADB002 - MULTIBOOT_MEMORY_INFO + diff --git a/grub-core/kern/i386/dl.c b/grub-core/kern/i386/dl.c new file mode 100644 index 0000000..1346da5 --- /dev/null +++ b/grub-core/kern/i386/dl.c @@ -0,0 +1,81 @@ +/* dl-386.c - arch-dependent part of loadable module support */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2005,2007,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/dl.h> +#include <grub/elf.h> +#include <grub/misc.h> +#include <grub/err.h> +#include <grub/i18n.h> + +/* Check if EHDR is a valid ELF header. */ +grub_err_t +grub_arch_dl_check_header (void *ehdr) +{ + Elf_Ehdr *e = ehdr; + + /* Check the magic numbers. */ + if (e->e_ident[EI_CLASS] != ELFCLASS32 + || e->e_ident[EI_DATA] != ELFDATA2LSB + || e->e_machine != EM_386) + return grub_error (GRUB_ERR_BAD_OS, N_("invalid arch-dependent ELF magic")); + + return GRUB_ERR_NONE; +} + +/* Relocate symbols. */ +grub_err_t +grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, + Elf_Shdr *s, grub_dl_segment_t seg) +{ + Elf_Rel *rel, *max; + + for (rel = (Elf_Rel *) ((char *) ehdr + s->sh_offset), + max = (Elf_Rel *) ((char *) rel + s->sh_size); + rel < max; + rel = (Elf_Rel *) ((char *) rel + s->sh_entsize)) + { + Elf_Word *addr; + Elf_Sym *sym; + + if (seg->size < rel->r_offset) + return grub_error (GRUB_ERR_BAD_MODULE, + "reloc offset is out of the segment"); + + addr = (Elf_Word *) ((char *) seg->addr + rel->r_offset); + sym = (Elf_Sym *) ((char *) mod->symtab + + mod->symsize * ELF_R_SYM (rel->r_info)); + + switch (ELF_R_TYPE (rel->r_info)) + { + case R_386_32: + *addr += sym->st_value; + break; + + case R_386_PC32: + *addr += (sym->st_value - (grub_addr_t) addr); + break; + default: + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + N_("relocation 0x%x is not implemented yet"), + ELF_R_TYPE (rel->r_info)); + } + } + + return GRUB_ERR_NONE; +} diff --git a/grub-core/kern/i386/efi/init.c b/grub-core/kern/i386/efi/init.c new file mode 100644 index 0000000..46476e2 --- /dev/null +++ b/grub-core/kern/i386/efi/init.c @@ -0,0 +1,48 @@ +/* init.c - initialize an x86-based EFI system */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007 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/types.h> +#include <grub/misc.h> +#include <grub/mm.h> +#include <grub/err.h> +#include <grub/dl.h> +#include <grub/cache.h> +#include <grub/kernel.h> +#include <grub/efi/efi.h> +#include <grub/i386/tsc.h> +#include <grub/loader.h> + +void +grub_machine_init (void) +{ + grub_efi_init (); + grub_tsc_init (); +} + +void +grub_machine_fini (int flags) +{ + if (!(flags & GRUB_LOADER_FLAG_NORETURN)) + return; + + grub_efi_fini (); + + if (!(flags & GRUB_LOADER_FLAG_EFI_KEEP_ALLOCATED_MEMORY)) + grub_efi_memory_fini (); +} diff --git a/grub-core/kern/i386/efi/startup.S b/grub-core/kern/i386/efi/startup.S new file mode 100644 index 0000000..fc5ea3d --- /dev/null +++ b/grub-core/kern/i386/efi/startup.S @@ -0,0 +1,36 @@ +/* startup.S - bootstrap GRUB itself */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2010 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 <config.h> +#include <grub/symbol.h> + + .file "startup.S" + .text + .globl start, _start +start: +_start: + /* + * EFI_SYSTEM_TABLE * and EFI_HANDLE are passed on the stack. + */ + movl 4(%esp), %eax + movl %eax, EXT_C(grub_efi_image_handle) + movl 8(%esp), %eax + movl %eax, EXT_C(grub_efi_system_table) + call EXT_C(grub_main) + ret diff --git a/grub-core/kern/i386/efi/tsc.c b/grub-core/kern/i386/efi/tsc.c new file mode 100644 index 0000000..4b93ba8 --- /dev/null +++ b/grub-core/kern/i386/efi/tsc.c @@ -0,0 +1,40 @@ +/* kern/i386/tsc.c - x86 TSC time source implementation + * Requires Pentium or better x86 CPU that supports the RDTSC instruction. + * This module uses the PIT to calibrate the TSC to + * real time. + * + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 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/types.h> +#include <grub/time.h> +#include <grub/misc.h> +#include <grub/i386/tsc.h> +#include <grub/efi/efi.h> +#include <grub/efi/api.h> + +int +grub_tsc_calibrate_from_efi (void) +{ + grub_uint64_t start_tsc, end_tsc; + /* Use EFI Time Service to calibrate TSC */ + start_tsc = grub_get_tsc (); + efi_call_1 (grub_efi_system_table->boot_services->stall, 1000); + end_tsc = grub_get_tsc (); + grub_tsc_rate = grub_divmod64 ((1ULL << 32), end_tsc - start_tsc, 0); + return 1; +} diff --git a/grub-core/kern/i386/ieee1275/startup.S b/grub-core/kern/i386/ieee1275/startup.S new file mode 100644 index 0000000..62cf348 --- /dev/null +++ b/grub-core/kern/i386/ieee1275/startup.S @@ -0,0 +1,40 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2003,2005,2006,2007,2008 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> +#include <grub/offsets.h> +#include <multiboot.h> +#include <multiboot2.h> + +/* + * Note: GRUB is compiled with the options -mrtd and -mregparm=3. + * So the first three arguments are passed in %eax, %edx, and %ecx, + * respectively, and if a function has a fixed number of arguments + * and the number if greater than three, the function must return + * with "ret $N" where N is ((the number of arguments) - 3) * 4. + */ + + .file "startup.S" + .text + .globl start, _start + +start: +_start: + movl %eax, EXT_C(grub_ieee1275_entry_fn) + jmp EXT_C(grub_main) + diff --git a/grub-core/kern/i386/int.S b/grub-core/kern/i386/int.S new file mode 100644 index 0000000..862a542 --- /dev/null +++ b/grub-core/kern/i386/int.S @@ -0,0 +1,134 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2010,2011 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/>. + */ + +FUNCTION(grub_bios_interrupt) + pushf + cli + popf + pushl %ebp + pushl %ecx + pushl %eax + pushl %ebx + pushl %esi + pushl %edi + pushl %edx + + movb %al, intno + movl (%edx), %eax + movl %eax, LOCAL(bios_register_eax) + movw 4(%edx), %ax + movw %ax, LOCAL(bios_register_es) + movw 6(%edx), %ax + movw %ax, LOCAL(bios_register_ds) + movw 8(%edx), %ax + movw %ax, LOCAL(bios_register_flags) + + movl 12(%edx), %ebx + movl 16(%edx), %ecx + movl 20(%edx), %edi + movl 24(%edx), %esi + movl 28(%edx), %edx + + /* + Via C3 CPUs have cache coherence problems, so we need to call + wbinvd at these 2 points. As wbinvd slows down boot, don't do + it on non-VIA. 9090 is nop nop. */ +VARIABLE(grub_bios_via_workaround1) + .byte 0x90, 0x90 + + PROT_TO_REAL + .code16 + pushf + cli + + mov %ds, %ax + push %ax + + /* movw imm16, %ax*/ + .byte 0xb8 +LOCAL(bios_register_es): + .short 0 + movw %ax, %es + /* movw imm16, %ax*/ + .byte 0xb8 +LOCAL(bios_register_ds): + .short 0 + movw %ax, %ds + + /* movw imm16, %ax*/ + .byte 0xb8 +LOCAL(bios_register_flags): + .short 0 + push %ax + popf + + /* movl imm32, %eax*/ + .byte 0x66, 0xb8 +LOCAL(bios_register_eax): + .long 0 + + /* int imm8. */ + .byte 0xcd +intno: + .byte 0 + + movl %eax, %cs:LOCAL(bios_register_eax) + movw %ds, %ax + movw %ax, %cs:LOCAL(bios_register_ds) + pop %ax + mov %ax, %ds + pushf + pop %ax + movw %ax, LOCAL(bios_register_flags) + mov %es, %ax + movw %ax, LOCAL(bios_register_es) + + popf + +VARIABLE(grub_bios_via_workaround2) + .byte 0x90, 0x90 + + REAL_TO_PROT + .code32 + + popl %eax + + movl %ebx, 12(%eax) + movl %ecx, 16(%eax) + movl %edi, 20(%eax) + movl %esi, 24(%eax) + movl %edx, 28(%eax) + + movl %eax, %edx + + movl LOCAL(bios_register_eax), %eax + movl %eax, (%edx) + movw LOCAL(bios_register_es), %ax + movw %ax, 4(%edx) + movw LOCAL(bios_register_ds), %ax + movw %ax, 6(%edx) + movw LOCAL(bios_register_flags), %ax + movw %ax, 8(%edx) + + popl %edi + popl %esi + popl %ebx + popl %eax + popl %ecx + popl %ebp + ret diff --git a/grub-core/kern/i386/multiboot_mmap.c b/grub-core/kern/i386/multiboot_mmap.c new file mode 100644 index 0000000..e8f4f34 --- /dev/null +++ b/grub-core/kern/i386/multiboot_mmap.c @@ -0,0 +1,73 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2003,2004,2005,2006,2007,2008,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/types.h> +#include <grub/multiboot.h> +#include <grub/err.h> +#include <grub/misc.h> + +/* A pointer to the MBI in its initial location. */ +struct multiboot_info *startup_multiboot_info; + +/* The MBI has to be copied to our BSS so that it won't be + overwritten. This is its final location. */ +static struct multiboot_info kern_multiboot_info; + +/* Unfortunately we can't use heap at this point. But 32 looks like a sane + limit (used by memtest86). */ +static grub_uint8_t mmap_entries[sizeof (struct multiboot_mmap_entry) * 32]; + +void +grub_machine_mmap_init (void) +{ + if (! startup_multiboot_info) + grub_fatal ("Unable to find Multiboot Information (is CONFIG_MULTIBOOT disabled in coreboot?)"); + + /* Move MBI to a safe place. */ + grub_memmove (&kern_multiboot_info, startup_multiboot_info, sizeof (struct multiboot_info)); + + if ((kern_multiboot_info.flags & MULTIBOOT_INFO_MEM_MAP) == 0) + grub_fatal ("Missing Multiboot memory information"); + + /* Move the memory map to a safe place. */ + if (kern_multiboot_info.mmap_length > sizeof (mmap_entries)) + { + grub_printf ("WARNING: Memory map size exceeds limit (0x%x > 0x%x); it will be truncated\n", + kern_multiboot_info.mmap_length, sizeof (mmap_entries)); + kern_multiboot_info.mmap_length = sizeof (mmap_entries); + } + grub_memmove (mmap_entries, (void *) kern_multiboot_info.mmap_addr, kern_multiboot_info.mmap_length); + kern_multiboot_info.mmap_addr = (grub_uint32_t) mmap_entries; +} + +grub_err_t +grub_machine_mmap_iterate (grub_memory_hook_t hook, void *hook_data) +{ + struct multiboot_mmap_entry *entry = (void *) kern_multiboot_info.mmap_addr; + + while ((unsigned long) entry < kern_multiboot_info.mmap_addr + kern_multiboot_info.mmap_length) + { + if (hook (entry->addr, entry->len, entry->type, hook_data)) + break; + + entry = (void *) ((grub_addr_t) entry + entry->size + sizeof (entry->size)); + } + + return 0; +} diff --git a/grub-core/kern/i386/pc/acpi.c b/grub-core/kern/i386/pc/acpi.c new file mode 100644 index 0000000..297f5d0 --- /dev/null +++ b/grub-core/kern/i386/pc/acpi.c @@ -0,0 +1,83 @@ +/* acpi.c - get acpi tables. */ +/* + * 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/acpi.h> +#include <grub/misc.h> + +struct grub_acpi_rsdp_v10 * +grub_machine_acpi_get_rsdpv1 (void) +{ + int ebda_len; + grub_uint8_t *ebda, *ptr; + + grub_dprintf ("acpi", "Looking for RSDP. Scanning EBDA\n"); + ebda = (grub_uint8_t *) ((* ((grub_uint16_t *) 0x40e)) << 4); + ebda_len = * (grub_uint16_t *) ebda; + if (! ebda_len) /* FIXME do we really need this check? */ + goto scan_bios; + for (ptr = ebda; ptr < ebda + 0x400; ptr += 16) + if (grub_memcmp (ptr, GRUB_RSDP_SIGNATURE, GRUB_RSDP_SIGNATURE_SIZE) == 0 + && grub_byte_checksum (ptr, sizeof (struct grub_acpi_rsdp_v10)) == 0 + && ((struct grub_acpi_rsdp_v10 *) ptr)->revision == 0) + return (struct grub_acpi_rsdp_v10 *) ptr; + +scan_bios: + grub_dprintf ("acpi", "Looking for RSDP. Scanning BIOS\n"); + for (ptr = (grub_uint8_t *) 0xe0000; ptr < (grub_uint8_t *) 0x100000; + ptr += 16) + if (grub_memcmp (ptr, GRUB_RSDP_SIGNATURE, GRUB_RSDP_SIGNATURE_SIZE) == 0 + && grub_byte_checksum (ptr, sizeof (struct grub_acpi_rsdp_v10)) == 0 + && ((struct grub_acpi_rsdp_v10 *) ptr)->revision == 0) + return (struct grub_acpi_rsdp_v10 *) ptr; + return 0; +} + +struct grub_acpi_rsdp_v20 * +grub_machine_acpi_get_rsdpv2 (void) +{ + int ebda_len; + grub_uint8_t *ebda, *ptr; + + grub_dprintf ("acpi", "Looking for RSDP. Scanning EBDA\n"); + ebda = (grub_uint8_t *) ((* ((grub_uint16_t *) 0x40e)) << 4); + ebda_len = * (grub_uint16_t *) ebda; + if (! ebda_len) /* FIXME do we really need this check? */ + goto scan_bios; + for (ptr = ebda; ptr < ebda + 0x400; ptr += 16) + if (grub_memcmp (ptr, GRUB_RSDP_SIGNATURE, GRUB_RSDP_SIGNATURE_SIZE) == 0 + && grub_byte_checksum (ptr, sizeof (struct grub_acpi_rsdp_v10)) == 0 + && ((struct grub_acpi_rsdp_v10 *) ptr)->revision != 0 + && ((struct grub_acpi_rsdp_v20 *) ptr)->length < 1024 + && grub_byte_checksum (ptr, ((struct grub_acpi_rsdp_v20 *) ptr)->length) + == 0) + return (struct grub_acpi_rsdp_v20 *) ptr; + +scan_bios: + grub_dprintf ("acpi", "Looking for RSDP. Scanning BIOS\n"); + for (ptr = (grub_uint8_t *) 0xe0000; ptr < (grub_uint8_t *) 0x100000; + ptr += 16) + if (grub_memcmp (ptr, GRUB_RSDP_SIGNATURE, GRUB_RSDP_SIGNATURE_SIZE) == 0 + && grub_byte_checksum (ptr, sizeof (struct grub_acpi_rsdp_v10)) == 0 + && ((struct grub_acpi_rsdp_v10 *) ptr)->revision != 0 + && ((struct grub_acpi_rsdp_v20 *) ptr)->length < 1024 + && grub_byte_checksum (ptr, ((struct grub_acpi_rsdp_v20 *) ptr)->length) + == 0) + return (struct grub_acpi_rsdp_v20 *) ptr; + return 0; +} diff --git a/grub-core/kern/i386/pc/init.c b/grub-core/kern/i386/pc/init.c new file mode 100644 index 0000000..27bc68b --- /dev/null +++ b/grub-core/kern/i386/pc/init.c @@ -0,0 +1,271 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2010 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/kernel.h> +#include <grub/mm.h> +#include <grub/machine/boot.h> +#include <grub/i386/floppy.h> +#include <grub/machine/memory.h> +#include <grub/machine/console.h> +#include <grub/machine/kernel.h> +#include <grub/machine/int.h> +#include <grub/types.h> +#include <grub/err.h> +#include <grub/dl.h> +#include <grub/misc.h> +#include <grub/loader.h> +#include <grub/env.h> +#include <grub/cache.h> +#include <grub/time.h> +#include <grub/cpu/cpuid.h> +#include <grub/cpu/tsc.h> +#include <grub/machine/time.h> + +struct mem_region +{ + grub_addr_t addr; + grub_size_t size; +}; + +#define MAX_REGIONS 32 + +static struct mem_region mem_regions[MAX_REGIONS]; +static int num_regions; + +void (*grub_pc_net_config) (char **device, char **path); + +/* + * return the real time in ticks, of which there are about + * 18-20 per second + */ +grub_uint64_t +grub_rtc_get_time_ms (void) +{ + struct grub_bios_int_registers regs; + + regs.eax = 0; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x1a, ®s); + + return ((regs.ecx << 16) | (regs.edx & 0xffff)) * 55ULL; +} + +void +grub_machine_get_bootlocation (char **device, char **path) +{ + char *ptr; + grub_uint8_t boot_drive, dos_part, bsd_part; + + boot_drive = (grub_boot_device >> 24); + dos_part = (grub_boot_device >> 16); + bsd_part = (grub_boot_device >> 8); + + /* No hardcoded root partition - make it from the boot drive and the + partition number encoded at the install time. */ + if (boot_drive == GRUB_BOOT_MACHINE_PXE_DL) + { + if (grub_pc_net_config) + grub_pc_net_config (device, path); + return; + } + + /* XXX: This should be enough. */ +#define DEV_SIZE 100 + *device = grub_malloc (DEV_SIZE); + ptr = *device; + grub_snprintf (*device, DEV_SIZE, + "%cd%u", (boot_drive & 0x80) ? 'h' : 'f', + boot_drive & 0x7f); + ptr += grub_strlen (ptr); + + if (dos_part != 0xff) + grub_snprintf (ptr, DEV_SIZE - (ptr - *device), + ",%u", dos_part + 1); + ptr += grub_strlen (ptr); + + if (bsd_part != 0xff) + grub_snprintf (ptr, DEV_SIZE - (ptr - *device), ",%u", + bsd_part + 1); + ptr += grub_strlen (ptr); + *ptr = 0; +} + +/* Add a memory region. */ +static void +add_mem_region (grub_addr_t addr, grub_size_t size) +{ + if (num_regions == MAX_REGIONS) + /* Ignore. */ + return; + + mem_regions[num_regions].addr = addr; + mem_regions[num_regions].size = size; + num_regions++; +} + +/* Compact memory regions. */ +static void +compact_mem_regions (void) +{ + int i, j; + + /* Sort them. */ + for (i = 0; i < num_regions - 1; i++) + for (j = i + 1; j < num_regions; j++) + if (mem_regions[i].addr > mem_regions[j].addr) + { + struct mem_region tmp = mem_regions[i]; + mem_regions[i] = mem_regions[j]; + mem_regions[j] = tmp; + } + + /* Merge overlaps. */ + for (i = 0; i < num_regions - 1; i++) + if (mem_regions[i].addr + mem_regions[i].size >= mem_regions[i + 1].addr) + { + j = i + 1; + + if (mem_regions[i].addr + mem_regions[i].size + < mem_regions[j].addr + mem_regions[j].size) + mem_regions[i].size = (mem_regions[j].addr + mem_regions[j].size + - mem_regions[i].addr); + + grub_memmove (mem_regions + j, mem_regions + j + 1, + (num_regions - j - 1) * sizeof (struct mem_region)); + i--; + num_regions--; + } +} + +grub_addr_t grub_modbase; +extern grub_uint8_t _start[], _edata[]; + +/* Helper for grub_machine_init. */ +static int +mmap_iterate_hook (grub_uint64_t addr, grub_uint64_t size, + grub_memory_type_t type, + void *data __attribute__ ((unused))) +{ + /* Avoid the lower memory. */ + if (addr < GRUB_MEMORY_MACHINE_UPPER_START) + { + if (size <= GRUB_MEMORY_MACHINE_UPPER_START - addr) + return 0; + + size -= GRUB_MEMORY_MACHINE_UPPER_START - addr; + addr = GRUB_MEMORY_MACHINE_UPPER_START; + } + + /* Ignore >4GB. */ + if (addr <= 0xFFFFFFFF && type == GRUB_MEMORY_AVAILABLE) + { + grub_size_t len; + + len = (grub_size_t) ((addr + size > 0xFFFFFFFF) + ? 0xFFFFFFFF - addr + : size); + add_mem_region (addr, len); + } + + return 0; +} + +extern grub_uint16_t grub_bios_via_workaround1, grub_bios_via_workaround2; + +/* Via needs additional wbinvd. */ +static void +grub_via_workaround_init (void) +{ + grub_uint32_t manufacturer[3], max_cpuid; + if (! grub_cpu_is_cpuid_supported ()) + return; + + grub_cpuid (0, max_cpuid, manufacturer[0], manufacturer[2], manufacturer[1]); + + if (grub_memcmp (manufacturer, "CentaurHauls", 12) != 0) + return; + + grub_bios_via_workaround1 = 0x090f; + grub_bios_via_workaround2 = 0x090f; + asm volatile ("wbinvd"); +} + +void +grub_machine_init (void) +{ + int i; +#if 0 + int grub_lower_mem; +#endif + grub_addr_t modend; + + /* This has to happen before any BIOS calls. */ + grub_via_workaround_init (); + + grub_modbase = GRUB_MEMORY_MACHINE_DECOMPRESSION_ADDR + (_edata - _start); + + /* Initialize the console as early as possible. */ + grub_console_init (); + + /* This sanity check is useless since top of GRUB_MEMORY_MACHINE_RESERVED_END + is used for stack and if it's unavailable we wouldn't have gotten so far. + */ +#if 0 + grub_lower_mem = grub_get_conv_memsize () << 10; + + /* Sanity check. */ + if (grub_lower_mem < GRUB_MEMORY_MACHINE_RESERVED_END) + grub_fatal ("too small memory"); +#endif + +/* FIXME: This prevents loader/i386/linux.c from using low memory. When our + heap implements support for requesting a chunk in low memory, this should + no longer be a problem. */ +#if 0 + /* Add the lower memory into free memory. */ + if (grub_lower_mem >= GRUB_MEMORY_MACHINE_RESERVED_END) + add_mem_region (GRUB_MEMORY_MACHINE_RESERVED_END, + grub_lower_mem - GRUB_MEMORY_MACHINE_RESERVED_END); +#endif + + grub_machine_mmap_iterate (mmap_iterate_hook, NULL); + + compact_mem_regions (); + + modend = grub_modules_get_end (); + for (i = 0; i < num_regions; i++) + { + grub_addr_t beg = mem_regions[i].addr; + grub_addr_t fin = mem_regions[i].addr + mem_regions[i].size; + if (modend && beg < modend) + beg = modend; + if (beg >= fin) + continue; + grub_mm_init_region ((void *) beg, fin - beg); + } + + grub_tsc_init (); +} + +void +grub_machine_fini (int flags) +{ + if (flags & GRUB_LOADER_FLAG_NORETURN) + grub_console_fini (); + grub_stop_floppy (); +} diff --git a/grub-core/kern/i386/pc/mmap.c b/grub-core/kern/i386/pc/mmap.c new file mode 100644 index 0000000..c0c3c35 --- /dev/null +++ b/grub-core/kern/i386/pc/mmap.c @@ -0,0 +1,193 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2003,2004,2005,2006,2007,2008 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/machine/int.h> +#include <grub/err.h> +#include <grub/types.h> +#include <grub/misc.h> + +struct grub_machine_mmap_entry +{ + grub_uint32_t size; + grub_uint64_t addr; + grub_uint64_t len; +#define GRUB_MACHINE_MEMORY_AVAILABLE 1 +#define GRUB_MACHINE_MEMORY_RESERVED 2 +#define GRUB_MACHINE_MEMORY_ACPI 3 +#define GRUB_MACHINE_MEMORY_NVS 4 +#define GRUB_MACHINE_MEMORY_BADRAM 5 + grub_uint32_t type; +} GRUB_PACKED; + + +/* + * + * grub_get_conv_memsize(i) : return the conventional memory size in KB. + * BIOS call "INT 12H" to get conventional memory size + * The return value in AX. + */ +static inline grub_uint16_t +grub_get_conv_memsize (void) +{ + struct grub_bios_int_registers regs; + + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x12, ®s); + return regs.eax & 0xffff; +} + +/* + * grub_get_ext_memsize() : return the extended memory size in KB. + * BIOS call "INT 15H, AH=88H" to get extended memory size + * The return value in AX. + * + */ +static inline grub_uint16_t +grub_get_ext_memsize (void) +{ + struct grub_bios_int_registers regs; + + regs.eax = 0x8800; + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + grub_bios_interrupt (0x15, ®s); + return regs.eax & 0xffff; +} + +/* Get a packed EISA memory map. Lower 16 bits are between 1MB and 16MB + in 1KB parts, and upper 16 bits are above 16MB in 64KB parts. If error, return zero. + BIOS call "INT 15H, AH=E801H" to get EISA memory map, + AX = memory between 1M and 16M in 1K parts. + BX = memory above 16M in 64K parts. +*/ + +static inline grub_uint32_t +grub_get_eisa_mmap (void) +{ + struct grub_bios_int_registers regs; + + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + regs.eax = 0xe801; + grub_bios_interrupt (0x15, ®s); + + if ((regs.eax & 0xff00) == 0x8600) + return 0; + + return (regs.eax & 0xffff) | (regs.ebx << 16); +} + +/* + * + * grub_get_mmap_entry(addr, cont) : address and old continuation value (zero to + * start), for the Query System Address Map BIOS call. + * + * Sets the first 4-byte int value of "addr" to the size returned by + * the call. If the call fails, sets it to zero. + * + * Returns: new (non-zero) continuation value, 0 if done. + */ +/* Get a memory map entry. Return next continuation value. Zero means + the end. */ +static grub_uint32_t +grub_get_mmap_entry (struct grub_machine_mmap_entry *entry, + grub_uint32_t cont) +{ + struct grub_bios_int_registers regs; + + regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT; + + /* place address (+4) in ES:DI */ + regs.es = ((grub_addr_t) &entry->addr) >> 4; + regs.edi = ((grub_addr_t) &entry->addr) & 0xf; + + /* set continuation value */ + regs.ebx = cont; + + /* set default maximum buffer size */ + regs.ecx = sizeof (*entry) - sizeof (entry->size); + + /* set EDX to 'SMAP' */ + regs.edx = 0x534d4150; + + regs.eax = 0xe820; + grub_bios_interrupt (0x15, ®s); + + /* write length of buffer (zero if error) into ADDR */ + if ((regs.flags & GRUB_CPU_INT_FLAGS_CARRY) || regs.eax != 0x534d4150 + || regs.ecx < 0x14 || regs.ecx > 0x400) + entry->size = 0; + else + entry->size = regs.ecx; + + /* return the continuation value */ + return regs.ebx; +} + +grub_err_t +grub_machine_mmap_iterate (grub_memory_hook_t hook, void *hook_data) +{ + grub_uint32_t cont = 0; + struct grub_machine_mmap_entry *entry + = (struct grub_machine_mmap_entry *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR; + int e820_works = 0; + + while (1) + { + grub_memset (entry, 0, sizeof (*entry)); + + cont = grub_get_mmap_entry (entry, cont); + + if (!entry->size) + break; + + if (entry->len) + e820_works = 1; + if (entry->len + && hook (entry->addr, entry->len, + /* GRUB mmaps have been defined to match with + the E820 definition. + Therefore, we can just pass type through. */ + entry->type, hook_data)) + break; + + if (! cont) + break; + } + + if (!e820_works) + { + grub_uint32_t eisa_mmap = grub_get_eisa_mmap (); + + if (hook (0x0, ((grub_uint32_t) grub_get_conv_memsize ()) << 10, + GRUB_MEMORY_AVAILABLE, hook_data)) + return 0; + + if (eisa_mmap) + { + if (hook (0x100000, (eisa_mmap & 0xFFFF) << 10, + GRUB_MEMORY_AVAILABLE, hook_data) == 0) + hook (0x1000000, eisa_mmap & ~0xFFFF, GRUB_MEMORY_AVAILABLE, + hook_data); + } + else + hook (0x100000, ((grub_uint32_t) grub_get_ext_memsize ()) << 10, + GRUB_MEMORY_AVAILABLE, hook_data); + } + + return 0; +} diff --git a/grub-core/kern/i386/pc/startup.S b/grub-core/kern/i386/pc/startup.S new file mode 100644 index 0000000..b8a9b33 --- /dev/null +++ b/grub-core/kern/i386/pc/startup.S @@ -0,0 +1,217 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2003,2005,2006,2007,2008,2009,2011 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/>. + */ + + +/* + * Note: These functions defined in this file may be called from C. + * Be careful of that you must not modify some registers. Quote + * from gcc-2.95.2/gcc/config/i386/i386.h: + + 1 for registers not available across function calls. + These must include the FIXED_REGISTERS and also any + registers that can be used without being saved. + The latter must include the registers where values are returned + and the register where structure-value addresses are passed. + Aside from that, you can include as many other registers as you like. + + ax,dx,cx,bx,si,di,bp,sp,st,st1,st2,st3,st4,st5,st6,st7,arg +{ 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } + */ + +/* + * Note: GRUB is compiled with the options -mrtd and -mregparm=3. + * So the first three arguments are passed in %eax, %edx, and %ecx, + * respectively, and if a function has a fixed number of arguments + * and the number is greater than three, the function must return + * with "ret $N" where N is ((the number of arguments) - 3) * 4. + */ + +#include <config.h> +#include <grub/symbol.h> +#include <multiboot.h> +#ifdef __APPLE__ +#include <grub/i386/pc/memory.h> +#endif + + .file "startup.S" + + .text + + .globl start, _start, __start +start: +_start: +__start: +#ifdef __APPLE__ +LOCAL(start): +#endif + .code32 + + movl %ecx, (LOCAL(real_to_prot_addr) - _start) (%esi) + movl %edi, (LOCAL(prot_to_real_addr) - _start) (%esi) + movl %eax, (EXT_C(grub_realidt) - _start) (%esi) + + /* copy back the decompressed part (except the modules) */ +#ifdef __APPLE__ + movl $EXT_C(_edata), %ecx + subl $LOCAL(start), %ecx +#else + movl $(_edata - _start), %ecx +#endif + movl $(_start), %edi + rep + movsb + + movl $LOCAL (cont), %esi + jmp *%esi +LOCAL(cont): + +#if 0 + /* copy modules before cleaning out the bss */ + movl EXT_C(grub_total_module_size), %ecx + movl EXT_C(grub_kernel_image_size), %esi + addl %ecx, %esi + addl $_start, %esi + decl %esi + movl $END_SYMBOL, %edi + addl %ecx, %edi + decl %edi + std + rep + movsb +#endif + +#ifdef __APPLE__ + /* clean out the bss */ + movl $EXT_C(_edata), %edi + + /* compute the bss length */ + movl $GRUB_MEMORY_MACHINE_SCRATCH_ADDR, %ecx +#else + /* clean out the bss */ + movl $BSS_START_SYMBOL, %edi + + /* compute the bss length */ + movl $END_SYMBOL, %ecx +#endif + subl %edi, %ecx + + /* clean out */ + xorl %eax, %eax + cld + rep + stosb + + movl %edx, EXT_C(grub_boot_device) + + /* + * Call the start of main body of C code. + */ + call EXT_C(grub_main) + +LOCAL(real_to_prot_addr): + .long 0 +LOCAL(prot_to_real_addr): + .long 0 + + .macro PROT_TO_REAL + movl LOCAL(prot_to_real_addr), %eax + call *%eax + .endm + + .macro REAL_TO_PROT + movl LOCAL(real_to_prot_addr), %eax + calll *%eax + .endm + +/* + * grub_exit() + * + * Exit the system. + */ +FUNCTION(grub_exit) + PROT_TO_REAL + .code16 + /* Tell the BIOS a boot failure. If this does not work, reboot. */ + int $0x18 + /* set 0x472 to 0x0000 for cold boot (0x1234 for warm boot) */ + xorw %ax, %ax + movw $0x0472, %di + movw %ax, (%di) + ljmp $0xf000, $0xfff0 + .code32 + +/* + * int grub_pxe_call (int func, void* data, grub_uint32_t pxe_rm_entry); + */ +FUNCTION(grub_pxe_call) + pushl %ebp + movl %esp, %ebp + pushl %esi + pushl %edi + pushl %ebx + + movl %ecx, %ebx + movl %eax, %ecx + movl %edx, %eax + andl $0xF, %eax + shrl $4, %edx + shll $16, %edx + addl %eax, %edx + + PROT_TO_REAL + .code16 + + pushl %ebx + pushl %edx + pushw %cx + movw %sp, %bx + lcall *%ss:6(%bx) + cld + addw $10, %sp + movw %ax, %cx + + REAL_TO_PROT + .code32 + + movzwl %cx, %eax + + popl %ebx + popl %edi + popl %esi + popl %ebp + ret + +#include "../int.S" + +VARIABLE(grub_realidt) + .long 0 + +#ifdef __APPLE__ + /* Older versions of objconv assume that there is the same number + of text and data sections. Hence this dummy. */ + .section __TEXT, __zz_dummy + .byte 0 + .globl EXT_C(_edata) + .globl EXT_C(grub_boot_device) + .zerofill __DATA, __aa_before_bss, EXT_C(_edata), 1, 0 + .zerofill __DATA, __bss, EXT_C(grub_boot_device), 4, 2 +#else + .bss +VARIABLE(grub_boot_device) + .long 0 +#endif diff --git a/grub-core/kern/i386/qemu/init.c b/grub-core/kern/i386/qemu/init.c new file mode 100644 index 0000000..271b6fb --- /dev/null +++ b/grub-core/kern/i386/qemu/init.c @@ -0,0 +1,276 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2013 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/kernel.h> +#include <grub/mm.h> +#include <grub/machine/time.h> +#include <grub/machine/memory.h> +#include <grub/machine/console.h> +#include <grub/offsets.h> +#include <grub/types.h> +#include <grub/err.h> +#include <grub/dl.h> +#include <grub/misc.h> +#include <grub/loader.h> +#include <grub/env.h> +#include <grub/cache.h> +#include <grub/time.h> +#include <grub/symbol.h> +#include <grub/cpu/io.h> +#include <grub/cpu/floppy.h> +#include <grub/cpu/tsc.h> +#include <grub/machine/kernel.h> +#include <grub/pci.h> + +extern grub_uint8_t _start[]; +extern grub_uint8_t _end[]; +extern grub_uint8_t _edata[]; + +void __attribute__ ((noreturn)) +grub_exit (void) +{ + /* We can't use grub_fatal() in this function. This would create an infinite + loop, since grub_fatal() calls grub_abort() which in turn calls grub_exit(). */ + while (1) + grub_cpu_idle (); +} + +grub_addr_t grub_modbase; + +/* Helper for grub_machine_init. */ +static int +heap_init (grub_uint64_t addr, grub_uint64_t size, grub_memory_type_t type, + void *data __attribute__ ((unused))) +{ + grub_uint64_t begin = addr, end = addr + size; + +#if GRUB_CPU_SIZEOF_VOID_P == 4 + /* Restrict ourselves to 32-bit memory space. */ + if (begin > GRUB_ULONG_MAX) + return 0; + if (end > GRUB_ULONG_MAX) + end = GRUB_ULONG_MAX; +#endif + + if (type != GRUB_MEMORY_AVAILABLE) + return 0; + + /* Avoid the lower memory. */ + if (begin < GRUB_MEMORY_MACHINE_LOWER_SIZE) + begin = GRUB_MEMORY_MACHINE_LOWER_SIZE; + + if (end <= begin) + return 0; + + grub_mm_init_region ((void *) (grub_addr_t) begin, (grub_size_t) (end - begin)); + + return 0; +} + +struct resource +{ + grub_pci_device_t dev; + grub_size_t size; + unsigned type:4; + unsigned bar:3; +}; + +struct iterator_ctx +{ + struct resource *resources; + grub_size_t nresources; +}; + +/* We don't support bridges, so can't have more than 32 devices. */ +#define MAX_DEVICES 32 + +static int +find_resources (grub_pci_device_t dev, + grub_pci_id_t pciid __attribute__ ((unused)), + void *data) +{ + struct iterator_ctx *ctx = data; + int bar; + + if (ctx->nresources >= MAX_DEVICES * 6) + return 1; + + for (bar = 0; bar < 6; bar++) + { + grub_pci_address_t addr; + grub_uint32_t ones, zeros, mask; + struct resource *res; + addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG0 + + 4 * bar); + grub_pci_write (addr, 0xffffffff); + grub_pci_read (addr); + ones = grub_pci_read (addr); + grub_pci_write (addr, 0); + grub_pci_read (addr); + zeros = grub_pci_read (addr); + if (ones == zeros) + continue; + res = &ctx->resources[ctx->nresources++]; + if ((zeros & GRUB_PCI_ADDR_SPACE_MASK) == GRUB_PCI_ADDR_SPACE_IO) + mask = GRUB_PCI_ADDR_SPACE_MASK; + else + mask = (GRUB_PCI_ADDR_MEM_TYPE_MASK | GRUB_PCI_ADDR_SPACE_MASK | GRUB_PCI_ADDR_MEM_PREFETCH); + + res->type = ones & mask; + res->dev = dev; + res->bar = bar; + res->size = (~((zeros ^ ones)) | mask) + 1; + if ((zeros & (GRUB_PCI_ADDR_MEM_TYPE_MASK | GRUB_PCI_ADDR_SPACE_MASK)) + == (GRUB_PCI_ADDR_SPACE_MEMORY | GRUB_PCI_ADDR_MEM_TYPE_64)) + bar++; + } + return 0; +} + +static int +enable_cards (grub_pci_device_t dev, + grub_pci_id_t pciid __attribute__ ((unused)), + void *data __attribute__ ((unused))) +{ + grub_uint16_t cmd = 0; + grub_pci_address_t addr; + grub_uint32_t class; + int bar; + + for (bar = 0; bar < 6; bar++) + { + grub_uint32_t val; + addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG0 + + 4 * bar); + val = grub_pci_read (addr); + if (!val) + continue; + if ((val & GRUB_PCI_ADDR_SPACE_MASK) == GRUB_PCI_ADDR_SPACE_IO) + cmd |= GRUB_PCI_COMMAND_IO_ENABLED; + else + cmd |= GRUB_PCI_COMMAND_MEM_ENABLED; + } + + class = (grub_pci_read (addr) >> 16) & 0xffff; + + if (class == GRUB_PCI_CLASS_SUBCLASS_VGA) + cmd |= GRUB_PCI_COMMAND_IO_ENABLED + | GRUB_PCI_COMMAND_MEM_ENABLED; + + if (class == GRUB_PCI_CLASS_SUBCLASS_USB) + return 0; + + addr = grub_pci_make_address (dev, GRUB_PCI_REG_COMMAND); + grub_pci_write (addr, cmd); + + return 0; +} + +static void +grub_pci_assign_addresses (void) +{ + struct iterator_ctx ctx; + + { + struct resource resources[MAX_DEVICES * 6]; + int done; + unsigned i; + ctx.nresources = 0; + ctx.resources = resources; + grub_uint32_t memptr = 0xf0000000; + grub_uint16_t ioptr = 0x1000; + + grub_pci_iterate (find_resources, &ctx); + /* FIXME: do we need a better sort here? */ + do + { + done = 0; + for (i = 0; i + 1 < ctx.nresources; i++) + if (resources[i].size < resources[i+1].size) + { + struct resource t; + t = resources[i]; + resources[i] = resources[i+1]; + resources[i+1] = t; + done = 1; + } + } + while (done); + + for (i = 0; i < ctx.nresources; i++) + { + grub_pci_address_t addr; + addr = grub_pci_make_address (resources[i].dev, + GRUB_PCI_REG_ADDRESS_REG0 + + 4 * resources[i].bar); + if ((resources[i].type & GRUB_PCI_ADDR_SPACE_MASK) + == GRUB_PCI_ADDR_SPACE_IO) + { + grub_pci_write (addr, ioptr | resources[i].type); + ioptr += resources[i].size; + } + else + { + grub_pci_write (addr, memptr | resources[i].type); + memptr += resources[i].size; + if ((resources[i].type & (GRUB_PCI_ADDR_MEM_TYPE_MASK + | GRUB_PCI_ADDR_SPACE_MASK)) + == (GRUB_PCI_ADDR_SPACE_MEMORY | GRUB_PCI_ADDR_MEM_TYPE_64)) + { + addr = grub_pci_make_address (resources[i].dev, + GRUB_PCI_REG_ADDRESS_REG0 + + 4 * resources[i].bar + 4); + grub_pci_write (addr, 0); + } + } + } + grub_pci_iterate (enable_cards, NULL); + } +} + +void +grub_machine_init (void) +{ + grub_modbase = grub_core_entry_addr + (_edata - _start); + + grub_pci_assign_addresses (); + + grub_qemu_init_cirrus (); + + grub_vga_text_init (); + + grub_machine_mmap_init (); + grub_machine_mmap_iterate (heap_init, NULL); + + + grub_tsc_init (); +} + +void +grub_machine_get_bootlocation (char **device __attribute__ ((unused)), + char **path __attribute__ ((unused))) +{ +} + +void +grub_machine_fini (int flags) +{ + if (flags & GRUB_LOADER_FLAG_NORETURN) + grub_vga_text_fini (); + grub_stop_floppy (); +} diff --git a/grub-core/kern/i386/qemu/mmap.c b/grub-core/kern/i386/qemu/mmap.c new file mode 100644 index 0000000..f449637 --- /dev/null +++ b/grub-core/kern/i386/qemu/mmap.c @@ -0,0 +1,107 @@ +/* + * 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/i386/memory.h> +#include <grub/machine/memory.h> +#include <grub/machine/boot.h> +#include <grub/types.h> +#include <grub/err.h> +#include <grub/misc.h> +#include <grub/cmos.h> +#include <grub/offsets.h> + +#define QEMU_CMOS_MEMSIZE_HIGH 0x35 +#define QEMU_CMOS_MEMSIZE_LOW 0x34 + +#define QEMU_CMOS_MEMSIZE2_HIGH 0x31 +#define QEMU_CMOS_MEMSIZE2_LOW 0x30 + +#define min(a,b) ((a) > (b) ? (b) : (a)) + +extern char _start[]; +extern char _end[]; + +static grub_uint64_t mem_size, above_4g; + +void +grub_machine_mmap_init (void) +{ + grub_uint8_t high, low, b, c, d; + grub_cmos_read (QEMU_CMOS_MEMSIZE_HIGH, &high); + grub_cmos_read (QEMU_CMOS_MEMSIZE_LOW, &low); + mem_size = ((grub_uint64_t) high) << 24 + | ((grub_uint64_t) low) << 16; + if (mem_size > 0) + { + /* Don't ask... */ + mem_size += (16 * 1024 * 1024); + } + else + { + grub_cmos_read (QEMU_CMOS_MEMSIZE2_HIGH, &high); + grub_cmos_read (QEMU_CMOS_MEMSIZE2_LOW, &low); + mem_size + = ((((grub_uint64_t) high) << 18) | (((grub_uint64_t) low) << 10)) + + 1024 * 1024; + } + + grub_cmos_read (0x5b, &b); + grub_cmos_read (0x5c, &c); + grub_cmos_read (0x5d, &d); + above_4g = (((grub_uint64_t) b) << 16) + | (((grub_uint64_t) c) << 24) + | (((grub_uint64_t) d) << 32); +} + +grub_err_t +grub_machine_mmap_iterate (grub_memory_hook_t hook, void *hook_data) +{ + if (hook (0x0, + (grub_addr_t) _start, + GRUB_MEMORY_AVAILABLE, hook_data)) + return 1; + + if (hook ((grub_addr_t) _end, + 0xa0000 - (grub_addr_t) _end, + GRUB_MEMORY_AVAILABLE, hook_data)) + return 1; + + if (hook (GRUB_MEMORY_MACHINE_UPPER, + 0x100000 - GRUB_MEMORY_MACHINE_UPPER, + GRUB_MEMORY_RESERVED, hook_data)) + return 1; + + /* Everything else is free. */ + if (hook (0x100000, + min (mem_size, (grub_uint32_t) -GRUB_BOOT_MACHINE_SIZE) - 0x100000, + GRUB_MEMORY_AVAILABLE, hook_data)) + return 1; + + /* Protect boot.img, which contains the gdt. It is mapped at the top of memory + (it is also mapped below 0x100000, but we already reserved that area). */ + if (hook ((grub_uint32_t) -GRUB_BOOT_MACHINE_SIZE, + GRUB_BOOT_MACHINE_SIZE, + GRUB_MEMORY_RESERVED, hook_data)) + return 1; + + if (above_4g != 0 && hook (0x100000000ULL, above_4g, + GRUB_MEMORY_AVAILABLE, hook_data)) + return 1; + + return 0; +} diff --git a/grub-core/kern/i386/qemu/startup.S b/grub-core/kern/i386/qemu/startup.S new file mode 100644 index 0000000..0d89858 --- /dev/null +++ b/grub-core/kern/i386/qemu/startup.S @@ -0,0 +1,75 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2003,2005,2006,2007,2008,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 <config.h> +#include <grub/symbol.h> + +#include <grub/machine/memory.h> +#include <grub/machine/kernel.h> + + .text + .code32 + .globl _start +_start: + jmp codestart + + .org GRUB_KERNEL_I386_QEMU_CORE_ENTRY_ADDR +VARIABLE(grub_core_entry_addr) + .long 0 + +codestart: + /* Relocate to low memory. First we figure out our location. + We will derive the rom start address from it. */ + call 1f +1: popl %esi + + /* Rom size is a multiple of 64 kiB. With this we get the + value of `grub_core_entry_addr' in %esi. */ + xorw %si, %si + + movl $(_edata - _start), %ecx + movl $_start, %edi + cld + rep + movsb + ljmp $GRUB_MEMORY_MACHINE_PROT_MODE_CSEG, $1f +1: + + /* clean out the bss */ + movl $BSS_START_SYMBOL, %edi + + /* compute the bss length */ + movl $END_SYMBOL, %ecx + subl %edi, %ecx + + /* clean out */ + xorl %eax, %eax + cld + rep + stosb + + /* + * Call the start of main body of C code. + */ + call EXT_C(grub_main) + + /* This should never happen. */ + cli +1: + hlt + jmp 1b diff --git a/grub-core/kern/i386/realmode.S b/grub-core/kern/i386/realmode.S new file mode 100644 index 0000000..265cdcb --- /dev/null +++ b/grub-core/kern/i386/realmode.S @@ -0,0 +1,281 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2003,2005,2006,2007,2009,2010 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> + +/* + * Note: These functions defined in this file may be called from C. + * Be careful of that you must not modify some registers. Quote + * from gcc-2.95.2/gcc/config/i386/i386.h: + + 1 for registers not available across function calls. + These must include the FIXED_REGISTERS and also any + registers that can be used without being saved. + The latter must include the registers where values are returned + and the register where structure-value addresses are passed. + Aside from that, you can include as many other registers as you like. + + ax,dx,cx,bx,si,di,bp,sp,st,st1,st2,st3,st4,st5,st6,st7,arg +{ 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } + */ + +/* + * Note: GRUB is compiled with the options -mrtd and -mregparm=3. + * So the first three arguments are passed in %eax, %edx, and %ecx, + * respectively, and if a function has a fixed number of arguments + * and the number if greater than three, the function must return + * with "ret $N" where N is ((the number of arguments) - 3) * 4. + */ + +/* + * This is the area for all of the special variables. + */ + +protstack: + .long GRUB_MEMORY_MACHINE_PROT_STACK + + .macro PROT_TO_REAL + call prot_to_real + .endm + + .macro REAL_TO_PROT + calll real_to_prot + .endm + +/* + * This is the Global Descriptor Table + * + * An entry, a "Segment Descriptor", looks like this: + * + * 31 24 19 16 7 0 + * ------------------------------------------------------------ + * | | |B| |A| | | |1|0|E|W|A| | + * | BASE 31..24 |G|/|L|V| LIMIT |P|DPL| TYPE | BASE 23:16 | 4 + * | | |D| |L| 19..16| | |1|1|C|R|A| | + * ------------------------------------------------------------ + * | | | + * | BASE 15..0 | LIMIT 15..0 | 0 + * | | | + * ------------------------------------------------------------ + * + * Note the ordering of the data items is reversed from the above + * description. + */ + + .p2align 5 /* force 32-byte alignment */ +gdt: + .word 0, 0 + .byte 0, 0, 0, 0 + + /* -- code segment -- + * base = 0x00000000, limit = 0xFFFFF (4 KiB Granularity), present + * type = 32bit code execute/read, DPL = 0 + */ + .word 0xFFFF, 0 + .byte 0, 0x9A, 0xCF, 0 + + /* -- data segment -- + * base = 0x00000000, limit 0xFFFFF (4 KiB Granularity), present + * type = 32 bit data read/write, DPL = 0 + */ + .word 0xFFFF, 0 + .byte 0, 0x92, 0xCF, 0 + + /* -- 16 bit real mode CS -- + * base = 0x00000000, limit 0x0FFFF (1 B Granularity), present + * type = 16 bit code execute/read only/conforming, DPL = 0 + */ + .word 0xFFFF, 0 + .byte 0, 0x9E, 0, 0 + + /* -- 16 bit real mode DS -- + * base = 0x00000000, limit 0x0FFFF (1 B Granularity), present + * type = 16 bit data read/write, DPL = 0 + */ + .word 0xFFFF, 0 + .byte 0, 0x92, 0, 0 + + + .p2align 5 +/* this is the GDT descriptor */ +gdtdesc: + .word 0x27 /* limit */ + .long gdt /* addr */ +LOCAL(realidt): + .word 0x400 + .long 0 +protidt: + .word 0 + .long 0 + +/* + * These next two routines, "real_to_prot" and "prot_to_real" are structured + * in a very specific way. Be very careful when changing them. + * + * NOTE: Use of either one messes up %eax and %ebp. + */ + +real_to_prot: + .code16 + cli + + /* load the GDT register */ + xorw %ax, %ax + movw %ax, %ds +#ifdef GRUB_MACHINE_QEMU + /* + qemu is special: gdtdesc is in ROM. + %cs = 0xf000 + _start + GRUB_BOOT_MACHINE_SIZE = 0x100000 + So + _start + GRUB_BOOT_MACHINE_SIZE - 0x10000 points to the same point + as %cs. + gdtdesc - (_start + GRUB_BOOT_MACHINE_SIZE - 0x10000) + = gdtdesc - _start - GRUB_BOOT_MACHINE_SIZE + 0x10000 + but the later can be computed by assembly. + */ + lgdtl %cs:(gdtdesc - _start - GRUB_BOOT_MACHINE_SIZE + 0x10000) +#else + lgdtl gdtdesc +#endif + + /* turn on protected mode */ + movl %cr0, %eax + orl $GRUB_MEMORY_CPU_CR0_PE_ON, %eax + movl %eax, %cr0 + + /* jump to relocation, flush prefetch queue, and reload %cs */ + ljmpl $GRUB_MEMORY_MACHINE_PROT_MODE_CSEG, $protcseg + + .code32 +protcseg: + /* reload other segment registers */ + movw $GRUB_MEMORY_MACHINE_PROT_MODE_DSEG, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + movw %ax, %ss + + /* put the return address in a known safe location */ + movl (%esp), %eax + movl %eax, GRUB_MEMORY_MACHINE_REAL_STACK + + /* get protected mode stack */ + movl protstack, %eax + movl %eax, %esp + movl %eax, %ebp + + /* get return address onto the right stack */ + movl GRUB_MEMORY_MACHINE_REAL_STACK, %eax + movl %eax, (%esp) + + /* zero %eax */ + xorl %eax, %eax + + sidt LOCAL(realidt) + lidt protidt + + /* return on the old (or initialized) stack! */ + ret + /* prot_to_real assumes that this code is under 64K which is not + true for qemu. */ +#ifndef GRUB_MACHINE_QEMU +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2003,2005,2006,2007,2009,2010 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/>. + */ + +prot_to_real: + /* just in case, set GDT */ + lgdt gdtdesc + + sidt protidt + lidt LOCAL(realidt) + + /* save the protected mode stack */ + movl %esp, %eax + movl %eax, protstack + + /* get the return address */ + movl (%esp), %eax + movl %eax, GRUB_MEMORY_MACHINE_REAL_STACK + + /* set up new stack */ + movl $GRUB_MEMORY_MACHINE_REAL_STACK, %eax + movl %eax, %esp + movl %eax, %ebp + + /* set up segment limits */ + movw $GRUB_MEMORY_MACHINE_PSEUDO_REAL_DSEG, %ax + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + movw %ax, %ss + + /* this might be an extra step */ + /* jump to a 16 bit segment */ + ljmp $GRUB_MEMORY_MACHINE_PSEUDO_REAL_CSEG, $tmpcseg + +tmpcseg: + .code16 + + /* clear the PE bit of CR0 */ + movl %cr0, %eax + andl $(~GRUB_MEMORY_CPU_CR0_PE_ON), %eax + movl %eax, %cr0 + + /* flush prefetch queue, reload %cs */ + ljmpl $0, $realcseg + +realcseg: + /* we are in real mode now + * set up the real mode segment registers : DS, SS, ES + */ + /* zero %eax */ + xorl %eax, %eax + + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + movw %ax, %ss + +#ifdef GRUB_MACHINE_PCBIOS + /* restore interrupts */ + sti +#endif + + /* return on new stack! */ + retl +#endif + .code32 diff --git a/grub-core/kern/i386/tsc.c b/grub-core/kern/i386/tsc.c new file mode 100644 index 0000000..9293b16 --- /dev/null +++ b/grub-core/kern/i386/tsc.c @@ -0,0 +1,78 @@ +/* kern/i386/tsc.c - x86 TSC time source implementation + * Requires Pentium or better x86 CPU that supports the RDTSC instruction. + * This module calibrates the TSC to real time. + * + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 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/types.h> +#include <grub/time.h> +#include <grub/misc.h> +#include <grub/i386/tsc.h> +#include <grub/i386/cpuid.h> + +/* This defines the value TSC had at the epoch (that is, when we calibrated it). */ +static grub_uint64_t tsc_boot_time; + +/* Calibrated TSC rate. (In ms per 2^32 ticks) */ +/* We assume that the tick is less than 1 ms and hence this value fits + in 32-bit. */ +grub_uint32_t grub_tsc_rate; + +static grub_uint64_t +grub_tsc_get_time_ms (void) +{ + grub_uint64_t a = grub_get_tsc () - tsc_boot_time; + grub_uint64_t ah = a >> 32; + grub_uint64_t al = a & 0xffffffff; + + return ((al * grub_tsc_rate) >> 32) + ah * grub_tsc_rate; +} + +static int +calibrate_tsc_hardcode (void) +{ + grub_tsc_rate = 5368;/* 800 MHz */ + return 1; +} + +void +grub_tsc_init (void) +{ + if (!grub_cpu_is_tsc_supported ()) + { +#if defined (GRUB_MACHINE_PCBIOS) || defined (GRUB_MACHINE_IEEE1275) + grub_install_get_time_ms (grub_rtc_get_time_ms); +#else + grub_fatal ("no TSC found"); +#endif + return; + } + + tsc_boot_time = grub_get_tsc (); + +#if defined (GRUB_MACHINE_XEN) || defined (GRUB_MACHINE_XEN_PVH) + (void) (grub_tsc_calibrate_from_xen () || calibrate_tsc_hardcode()); +#elif defined (GRUB_MACHINE_EFI) + (void) (grub_tsc_calibrate_from_pmtimer () || grub_tsc_calibrate_from_pit () || grub_tsc_calibrate_from_efi() || calibrate_tsc_hardcode()); +#elif defined (GRUB_MACHINE_COREBOOT) + (void) (grub_tsc_calibrate_from_pmtimer () || grub_tsc_calibrate_from_pit () || calibrate_tsc_hardcode()); +#else + (void) (grub_tsc_calibrate_from_pit () || calibrate_tsc_hardcode()); +#endif + grub_install_get_time_ms (grub_tsc_get_time_ms); +} diff --git a/grub-core/kern/i386/tsc_pit.c b/grub-core/kern/i386/tsc_pit.c new file mode 100644 index 0000000..67990bf --- /dev/null +++ b/grub-core/kern/i386/tsc_pit.c @@ -0,0 +1,84 @@ +/* kern/i386/tsc.c - x86 TSC time source implementation + * Requires Pentium or better x86 CPU that supports the RDTSC instruction. + * This module uses the PIT to calibrate the TSC to + * real time. + * + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 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/types.h> +#include <grub/time.h> +#include <grub/misc.h> +#include <grub/i386/tsc.h> +#include <grub/i386/pit.h> +#include <grub/cpu/io.h> + +static int +grub_pit_wait (void) +{ + int ret = 0; + + /* Disable timer2 gate and speaker. */ + grub_outb (grub_inb (GRUB_PIT_SPEAKER_PORT) + & ~ (GRUB_PIT_SPK_DATA | GRUB_PIT_SPK_TMR2), + GRUB_PIT_SPEAKER_PORT); + + /* Set tics. */ + grub_outb (GRUB_PIT_CTRL_SELECT_2 | GRUB_PIT_CTRL_READLOAD_WORD, + GRUB_PIT_CTRL); + /* 0xffff ticks: 55ms. */ + grub_outb (0xff, GRUB_PIT_COUNTER_2); + grub_outb (0xff, GRUB_PIT_COUNTER_2); + + /* Enable timer2 gate, keep speaker disabled. */ + grub_outb ((grub_inb (GRUB_PIT_SPEAKER_PORT) & ~ GRUB_PIT_SPK_DATA) + | GRUB_PIT_SPK_TMR2, + GRUB_PIT_SPEAKER_PORT); + + if ((grub_inb (GRUB_PIT_SPEAKER_PORT) & GRUB_PIT_SPK_TMR2_LATCH) == 0x00) { + ret = 1; + /* Wait. */ + while ((grub_inb (GRUB_PIT_SPEAKER_PORT) & GRUB_PIT_SPK_TMR2_LATCH) == 0x00); + } + + /* Disable timer2 gate and speaker. */ + grub_outb (grub_inb (GRUB_PIT_SPEAKER_PORT) + & ~ (GRUB_PIT_SPK_DATA | GRUB_PIT_SPK_TMR2), + GRUB_PIT_SPEAKER_PORT); + + return ret; +} + +/* Calibrate the TSC based on the RTC. */ +int +grub_tsc_calibrate_from_pit (void) +{ + /* First calibrate the TSC rate (relative, not absolute time). */ + grub_uint64_t start_tsc, end_tsc; + + start_tsc = grub_get_tsc (); + if (!grub_pit_wait ()) + return 0; + end_tsc = grub_get_tsc (); + + grub_tsc_rate = 0; + if (end_tsc > start_tsc) + grub_tsc_rate = grub_divmod64 ((55ULL << 32), end_tsc - start_tsc, 0); + if (grub_tsc_rate == 0) + return 0; + return 1; +} diff --git a/grub-core/kern/i386/tsc_pmtimer.c b/grub-core/kern/i386/tsc_pmtimer.c new file mode 100644 index 0000000..c9c3616 --- /dev/null +++ b/grub-core/kern/i386/tsc_pmtimer.c @@ -0,0 +1,88 @@ +/* kern/i386/tsc.c - x86 TSC time source implementation + * Requires Pentium or better x86 CPU that supports the RDTSC instruction. + * This module uses the PIT to calibrate the TSC to + * real time. + * + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 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/types.h> +#include <grub/time.h> +#include <grub/misc.h> +#include <grub/i386/tsc.h> +#include <grub/i386/pmtimer.h> +#include <grub/acpi.h> +#include <grub/cpu/io.h> + +grub_uint64_t +grub_pmtimer_wait_count_tsc (grub_port_t pmtimer, + grub_uint16_t num_pm_ticks) +{ + grub_uint32_t start; + grub_uint32_t last; + grub_uint32_t cur, end; + grub_uint64_t start_tsc; + grub_uint64_t end_tsc; + int num_iter = 0; + + start = grub_inl (pmtimer) & 0xffffff; + last = start; + end = start + num_pm_ticks; + start_tsc = grub_get_tsc (); + while (1) + { + cur = grub_inl (pmtimer) & 0xffffff; + if (cur < last) + cur |= 0x1000000; + num_iter++; + if (cur >= end) + { + end_tsc = grub_get_tsc (); + return end_tsc - start_tsc; + } + /* Check for broken PM timer. + 50000000 TSCs is between 5 ms (10GHz) and 200 ms (250 MHz) + if after this time we still don't have 1 ms on pmtimer, then + pmtimer is broken. + */ + if ((num_iter & 0xffffff) == 0 && grub_get_tsc () - start_tsc > 5000000) { + return 0; + } + } +} + +int +grub_tsc_calibrate_from_pmtimer (void) +{ + struct grub_acpi_fadt *fadt; + grub_port_t pmtimer; + grub_uint64_t tsc_diff; + + fadt = grub_acpi_find_fadt (); + if (!fadt) + return 0; + pmtimer = fadt->pmtimer; + if (!pmtimer) + return 0; + + /* It's 3.579545 MHz clock. Wait 1 ms. */ + tsc_diff = grub_pmtimer_wait_count_tsc (pmtimer, 3580); + if (tsc_diff == 0) + return 0; + grub_tsc_rate = grub_divmod64 ((1ULL << 32), tsc_diff, 0); + return 1; +} diff --git a/grub-core/kern/i386/xen/hypercall.S b/grub-core/kern/i386/xen/hypercall.S new file mode 100644 index 0000000..09d75c3 --- /dev/null +++ b/grub-core/kern/i386/xen/hypercall.S @@ -0,0 +1,43 @@ +/* hypercall.S - wrappers for Xen hypercalls */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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> +#include <grub/xen.h> + +FUNCTION(grub_xen_hypercall) + pushl %ebp + movl %esp, %ebp + pushl %esi + pushl %edi + pushl %ebx + + /* call number already in %eax. */ + /* %edx -> %ebx*/ + /* %ecx -> %ecx*/ + movl %edx, %ebx + movl 8(%ebp), %edx + movl 12(%ebp), %esi + movl 16(%ebp), %edi + movl 20(%ebp), %ebp + int $0x82 + popl %ebx + popl %edi + popl %esi + popl %ebp + ret diff --git a/grub-core/kern/i386/xen/pvh.c b/grub-core/kern/i386/xen/pvh.c new file mode 100644 index 0000000..91fbca8 --- /dev/null +++ b/grub-core/kern/i386/xen/pvh.c @@ -0,0 +1,369 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2018 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/kernel.h> +#include <grub/misc.h> +#include <grub/memory.h> +#include <grub/mm.h> +#include <grub/i386/cpuid.h> +#include <grub/i386/io.h> +#include <grub/xen.h> +#include <xen/hvm/start_info.h> +#include <grub/i386/linux.h> +#include <grub/machine/kernel.h> +#include <grub/machine/memory.h> +#include <xen/hvm/params.h> +#include <xen/memory.h> + +#define XEN_MEMORY_MAP_SIZE 128 + +grub_uint64_t grub_rsdp_addr; + +static char hypercall_page[GRUB_XEN_PAGE_SIZE] + __attribute__ ((aligned (GRUB_XEN_PAGE_SIZE))); + +static grub_uint32_t xen_cpuid_base; +static struct start_info grub_xen_start_page; +static struct grub_e820_mmap_entry map[XEN_MEMORY_MAP_SIZE]; +static unsigned int nr_map_entries; + +static void +grub_xen_cons_msg (const char *msg) +{ + const char *c; + + for (c = msg; *c; c++) + grub_outb (*c, XEN_HVM_DEBUGCONS_IOPORT); +} + +static void +grub_xen_panic (const char *msg) +{ + grub_xen_cons_msg (msg); + grub_xen_cons_msg ("System halted!\n"); + + asm volatile ("cli"); + + while (1) + { + asm volatile ("hlt"); + } +} + +static void +grub_xen_cpuid_base (void) +{ + grub_uint32_t base, eax, signature[3]; + + for (base = 0x40000000; base < 0x40010000; base += 0x100) + { + grub_cpuid (base, eax, signature[0], signature[1], signature[2]); + if (!grub_memcmp ("XenVMMXenVMM", signature, 12) && (eax - base) >= 2) + { + xen_cpuid_base = base; + return; + } + } + + grub_xen_panic ("Found no Xen signature!\n"); +} + +static void +grub_xen_setup_hypercall_page (void) +{ + grub_uint32_t msr, addr, eax, ebx, ecx, edx; + + /* Get base address of Xen-specific MSRs. */ + grub_cpuid (xen_cpuid_base + 2, eax, ebx, ecx, edx); + msr = ebx; + addr = (grub_uint32_t) (&hypercall_page); + + /* Specify hypercall page address for Xen. */ + asm volatile ("wrmsr" : : "c" (msr), "a" (addr), "d" (0) : "memory"); +} + +int +grub_xen_hypercall (grub_uint32_t callno, grub_uint32_t a0, + grub_uint32_t a1, grub_uint32_t a2, + grub_uint32_t a3, grub_uint32_t a4, + grub_uint32_t a5 __attribute__ ((unused))) +{ + grub_uint32_t res; + + asm volatile ("call *%[callno]" + : "=a" (res), "+b" (a0), "+c" (a1), "+d" (a2), + "+S" (a3), "+D" (a4) + : [callno] "a" (&hypercall_page[callno * 32]) + : "memory"); + return res; +} + +static grub_uint32_t +grub_xen_get_param (int idx) +{ + struct xen_hvm_param xhv; + int r; + + xhv.domid = DOMID_SELF; + xhv.index = idx; + r = grub_xen_hypercall (__HYPERVISOR_hvm_op, HVMOP_get_param, + (grub_uint32_t) (&xhv), 0, 0, 0, 0); + if (r < 0) + grub_xen_panic ("Could not get parameter from Xen!\n"); + return xhv.value; +} + +static void * +grub_xen_add_physmap (unsigned int space, void *addr) +{ + struct xen_add_to_physmap xatp; + + xatp.domid = DOMID_SELF; + xatp.idx = 0; + xatp.space = space; + xatp.gpfn = (grub_addr_t) addr >> GRUB_XEN_LOG_PAGE_SIZE; + if (grub_xen_hypercall (__HYPERVISOR_memory_op, XENMEM_add_to_physmap, + (grub_uint32_t) (&xatp), 0, 0, 0, 0)) + grub_xen_panic ("Memory_op hypercall failed!\n"); + return addr; +} + +static void +grub_xen_sort_mmap (void) +{ + grub_uint64_t from, to; + unsigned int i; + struct grub_e820_mmap_entry tmp; + + /* Align map entries to page boundaries. */ + for (i = 0; i < nr_map_entries; i++) + { + from = map[i].addr; + to = from + map[i].len; + if (map[i].type == GRUB_MEMORY_AVAILABLE) + { + from = ALIGN_UP (from, GRUB_XEN_PAGE_SIZE); + to = ALIGN_DOWN (to, GRUB_XEN_PAGE_SIZE); + } + else + { + from = ALIGN_DOWN (from, GRUB_XEN_PAGE_SIZE); + to = ALIGN_UP (to, GRUB_XEN_PAGE_SIZE); + } + map[i].addr = from; + map[i].len = to - from; + } + + again: + /* Sort entries by start address. */ + for (i = 1; i < nr_map_entries; i++) + { + if (map[i].addr >= map[i - 1].addr) + continue; + tmp = map[i]; + map[i] = map[i - 1]; + map[i - 1] = tmp; + i = 0; + } + + /* Detect overlapping areas. */ + for (i = 1; i < nr_map_entries; i++) + { + if (map[i].addr >= map[i - 1].addr + map[i - 1].len) + continue; + tmp = map[i - 1]; + map[i - 1].len = map[i].addr - map[i - 1].addr; + if (map[i].addr + map[i].len >= tmp.addr + tmp.len) + continue; + if (nr_map_entries < ARRAY_SIZE (map)) + { + map[nr_map_entries].addr = map[i].addr + map[i].len; + map[nr_map_entries].len = tmp.addr + tmp.len - map[nr_map_entries].addr; + map[nr_map_entries].type = tmp.type; + nr_map_entries++; + goto again; + } + } + + /* Merge adjacent entries. */ + for (i = 1; i < nr_map_entries; i++) + { + if (map[i].type == map[i - 1].type && + map[i].addr == map[i - 1].addr + map[i - 1].len) + { + map[i - 1].len += map[i].len; + map[i] = map[nr_map_entries - 1]; + nr_map_entries--; + goto again; + } + } +} + +static void +grub_xen_get_mmap (void) +{ + struct xen_memory_map memmap; + + memmap.nr_entries = ARRAY_SIZE (map); + set_xen_guest_handle (memmap.buffer, map); + if (grub_xen_hypercall (__HYPERVISOR_memory_op, XENMEM_memory_map, + (grub_uint32_t) (&memmap), 0, 0, 0, 0)) + grub_xen_panic ("Could not get memory map from Xen!\n"); + nr_map_entries = memmap.nr_entries; + + grub_xen_sort_mmap (); +} + +static void +grub_xen_set_mmap (void) +{ + struct xen_foreign_memory_map memmap; + + memmap.domid = DOMID_SELF; + memmap.map.nr_entries = nr_map_entries; + set_xen_guest_handle (memmap.map.buffer, map); + grub_xen_hypercall (__HYPERVISOR_memory_op, XENMEM_set_memory_map, + (grub_uint32_t) (&memmap), 0, 0, 0, 0); +} + +static void +grub_xen_mm_init_regions (void) +{ + grub_uint64_t modend, from, to; + unsigned int i; + + modend = grub_modules_get_end (); + + for (i = 0; i < nr_map_entries; i++) + { + if (map[i].type != GRUB_MEMORY_AVAILABLE) + continue; + from = map[i].addr; + to = from + map[i].len; + if (from < modend) + from = modend; + if (from >= to || from >= (1ULL << 32)) + continue; + if (to > (1ULL << 32)) + to = 1ULL << 32; + grub_mm_init_region ((void *) (grub_addr_t) from, to - from); + } +} + +static grub_uint64_t +grub_xen_find_page (grub_uint64_t start) +{ + unsigned int i, j; + grub_uint64_t last = start; + + /* + * Try to find a e820 map hole below 4G. + * Relies on page-aligned entries (addr and len) and input (start). + */ + + for (i = 0; i < nr_map_entries; i++) + { + if (last > map[i].addr + map[i].len) + continue; + if (last < map[i].addr) + return last; + if ((map[i].addr >> 32) || ((map[i].addr + map[i].len) >> 32)) + break; + last = map[i].addr + map[i].len; + } + if (i == nr_map_entries) + return last; + + /* No hole found, use the highest RAM page below 4G and reserve it. */ + if (nr_map_entries == ARRAY_SIZE (map)) + grub_xen_panic ("Memory map size limit reached!\n"); + for (i = 0, j = 0; i < nr_map_entries; i++) + { + if (map[i].type != GRUB_MEMORY_AVAILABLE) + continue; + if (map[i].addr >> 32) + break; + j = i; + if ((map[i].addr + map[i].len) >> 32) + break; + } + if (map[j].type != GRUB_MEMORY_AVAILABLE) + grub_xen_panic ("No free memory page found!\n"); + if ((map[j].addr + map[j].len) >> 32) + last = (1ULL << 32) - GRUB_XEN_PAGE_SIZE; + else + last = map[j].addr + map[j].len - GRUB_XEN_PAGE_SIZE; + map[nr_map_entries].addr = last; + map[nr_map_entries].len = GRUB_XEN_PAGE_SIZE; + map[nr_map_entries].type = GRUB_MEMORY_RESERVED; + nr_map_entries++; + grub_xen_sort_mmap (); + + return last; +} + +void +grub_xen_setup_pvh (void) +{ + grub_addr_t par; + + grub_xen_cpuid_base (); + grub_xen_setup_hypercall_page (); + grub_xen_get_mmap (); + + /* Setup Xen data. */ + grub_xen_start_page_addr = &grub_xen_start_page; + + par = grub_xen_get_param (HVM_PARAM_CONSOLE_PFN); + grub_xen_start_page_addr->console.domU.mfn = par; + grub_xen_xcons = (void *) (grub_addr_t) (par << GRUB_XEN_LOG_PAGE_SIZE); + par = grub_xen_get_param (HVM_PARAM_CONSOLE_EVTCHN); + grub_xen_start_page_addr->console.domU.evtchn = par; + + par = grub_xen_get_param (HVM_PARAM_STORE_PFN); + grub_xen_start_page_addr->store_mfn = par; + grub_xen_xenstore = (void *) (grub_addr_t) (par << GRUB_XEN_LOG_PAGE_SIZE); + par = grub_xen_get_param (HVM_PARAM_STORE_EVTCHN); + grub_xen_start_page_addr->store_evtchn = par; + + par = grub_xen_find_page (0); + grub_xen_grant_table = grub_xen_add_physmap (XENMAPSPACE_grant_table, + (void *) par); + par = grub_xen_find_page (par + GRUB_XEN_PAGE_SIZE); + grub_xen_shared_info = grub_xen_add_physmap (XENMAPSPACE_shared_info, + (void *) par); + grub_xen_set_mmap (); + + grub_xen_mm_init_regions (); + + grub_rsdp_addr = pvh_start_info->rsdp_paddr; +} + +grub_err_t +grub_machine_mmap_iterate (grub_memory_hook_t hook, void *hook_data) +{ + unsigned int i; + + for (i = 0; i < nr_map_entries; i++) + { + if (map[i].len && hook (map[i].addr, map[i].len, map[i].type, hook_data)) + break; + } + + return GRUB_ERR_NONE; +} diff --git a/grub-core/kern/i386/xen/startup.S b/grub-core/kern/i386/xen/startup.S new file mode 100644 index 0000000..fbe8300 --- /dev/null +++ b/grub-core/kern/i386/xen/startup.S @@ -0,0 +1,38 @@ +/* startup.S - bootstrap GRUB itself */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 <config.h> +#include <grub/symbol.h> + + .file "startup.S" + .text + .globl start, _start + .code32 + +start: +_start: + leal LOCAL(stack_end), %esp + movl %esi, EXT_C(grub_xen_start_page_addr) + + call EXT_C(grub_main) + /* Doesn't return. */ + + .bss + .space (1 << 22) +LOCAL(stack_end): diff --git a/grub-core/kern/i386/xen/startup_pvh.S b/grub-core/kern/i386/xen/startup_pvh.S new file mode 100644 index 0000000..363c318 --- /dev/null +++ b/grub-core/kern/i386/xen/startup_pvh.S @@ -0,0 +1,81 @@ +/* startup.S - bootstrap GRUB itself */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2018 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 <config.h> +#include <grub/symbol.h> +#include <grub/machine/memory.h> + + .file "startup_pvh.S" + .text + .globl start, _start + .code32 + +start: +_start: + cld + lgdt gdtdesc + ljmp $GRUB_MEMORY_MACHINE_PROT_MODE_CSEG, $1f +1: + movl $GRUB_MEMORY_MACHINE_PROT_MODE_DSEG, %eax + mov %eax, %ds + mov %eax, %es + mov %eax, %fs + mov %eax, %gs + mov %eax, %ss + leal LOCAL(stack_end), %esp + + /* Save address of start info structure. */ + mov %ebx, pvh_start_info + call EXT_C(grub_main) + /* Doesn't return. */ + + .p2align 3 +gdt: + .word 0, 0 + .byte 0, 0, 0, 0 + + /* -- code segment -- + * base = 0x00000000, limit = 0xFFFFF (4 KiB Granularity), present + * type = 32bit code execute/read, DPL = 0 + */ + .word 0xFFFF, 0 + .byte 0, 0x9A, 0xCF, 0 + + /* -- data segment -- + * base = 0x00000000, limit 0xFFFFF (4 KiB Granularity), present + * type = 32 bit data read/write, DPL = 0 + */ + .word 0xFFFF, 0 + .byte 0, 0x92, 0xCF, 0 + + .p2align 3 +/* this is the GDT descriptor */ +gdtdesc: + .word 0x17 /* limit */ + .long gdt /* addr */ + + .p2align 2 +/* Saved pointer to start info structure. */ + .globl pvh_start_info +pvh_start_info: + .long 0 + + .bss + .space GRUB_MEMORY_MACHINE_PROT_STACK_SIZE +LOCAL(stack_end): diff --git a/grub-core/kern/i386/xen/tsc.c b/grub-core/kern/i386/xen/tsc.c new file mode 100644 index 0000000..8792b30 --- /dev/null +++ b/grub-core/kern/i386/xen/tsc.c @@ -0,0 +1,40 @@ +/* kern/i386/tsc.c - x86 TSC time source implementation + * Requires Pentium or better x86 CPU that supports the RDTSC instruction. + * This module uses the PIT to calibrate the TSC to + * real time. + * + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 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/types.h> +#include <grub/time.h> +#include <grub/misc.h> +#include <grub/i386/tsc.h> +#include <grub/xen.h> + +int +grub_tsc_calibrate_from_xen (void) +{ + grub_uint64_t t; + t = grub_xen_shared_info->vcpu_info[0].time.tsc_to_system_mul; + if (grub_xen_shared_info->vcpu_info[0].time.tsc_shift > 0) + t <<= grub_xen_shared_info->vcpu_info[0].time.tsc_shift; + else + t >>= -grub_xen_shared_info->vcpu_info[0].time.tsc_shift; + grub_tsc_rate = grub_divmod64 (t, 1000000, 0); + return 1; +} |