📄 linux.c
字号:
/* * GRUB -- GRand Unified Bootloader * Copyright (C) 2006 Free Software Foundation, Inc. * * This program 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 2 of the License, or * (at your option) any later version. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */#include <grub/loader.h>#include <grub/machine/loader.h>#include <grub/file.h>#include <grub/disk.h>#include <grub/err.h>#include <grub/misc.h>#include <grub/types.h>#include <grub/rescue.h>#include <grub/dl.h>#include <grub/mm.h>#include <grub/term.h>#include <grub/cpu/linux.h>#include <grub/efi/api.h>#include <grub/efi/efi.h>#define NEXT_MEMORY_DESCRIPTOR(desc, size) \ ((grub_efi_memory_descriptor_t *) ((char *) (desc) + (size)))static grub_dl_t my_mod;static grub_size_t linux_mem_size;static int loaded;static void *real_mode_mem;static void *prot_mode_mem;static void *initrd_mem;static grub_efi_uintn_t real_mode_pages;static grub_efi_uintn_t prot_mode_pages;static grub_efi_uintn_t initrd_pages;static void *mmap_buf;static grub_uint8_t gdt[] __attribute__ ((aligned(16))) = { /* NULL. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Reserved. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Code segment. */ 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x9A, 0xCF, 0x00, /* Data segment. */ 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x92, 0xCF, 0x00 };struct gdt_descriptor{ grub_uint16_t dummy; grub_uint16_t limit; grub_uint32_t base;} __attribute__ ((aligned(4), packed));static struct gdt_descriptor gdt_desc = { 0, sizeof (gdt) - 1, (grub_addr_t) gdt };struct idt_descriptor{ grub_uint16_t dummy; grub_uint16_t limit; grub_uint32_t base;} __attribute__ ((aligned(4)));static struct idt_descriptor idt_desc = { 0, 0, 0 };static inline grub_size_tpage_align (grub_size_t size){ return (size + (1 << 12) - 1) & (~((1 << 12) - 1));}/* Find the optimal number of pages for the memory map. Is it better to move this code to efi/mm.c? */static grub_efi_uintn_tfind_mmap_size (void){ static grub_efi_uintn_t mmap_size = 0; if (mmap_size != 0) return mmap_size; mmap_size = (1 << 12); while (1) { int ret; grub_efi_memory_descriptor_t *mmap; grub_efi_uintn_t desc_size; mmap = grub_malloc (mmap_size); if (! mmap) return 0; ret = grub_efi_get_memory_map (&mmap_size, mmap, 0, &desc_size, 0); grub_free (mmap); if (ret < 0) grub_fatal ("cannot get memory map"); else if (ret > 0) break; mmap_size += (1 << 12); } /* Increase the size a bit for safety, because GRUB allocates more on later, and EFI itself may allocate more. */ mmap_size += (1 << 12); return page_align (mmap_size);}static voidfree_pages (void){ if (real_mode_mem) { grub_efi_free_pages ((grub_addr_t) real_mode_mem, real_mode_pages); real_mode_mem = 0; } if (prot_mode_mem) { grub_efi_free_pages ((grub_addr_t) prot_mode_mem, prot_mode_pages); prot_mode_mem = 0; } if (initrd_mem) { grub_efi_free_pages ((grub_addr_t) initrd_mem, initrd_pages); initrd_mem = 0; }}/* Allocate pages for the real mode code and the protected mode code for linux as well as a memory map buffer. */static intallocate_pages (grub_size_t real_size, grub_size_t prot_size){ grub_efi_uintn_t desc_size; grub_efi_memory_descriptor_t *mmap, *mmap_end; grub_efi_uintn_t mmap_size, tmp_mmap_size; grub_efi_memory_descriptor_t *desc; /* Make sure that each size is aligned to a page boundary. */ real_size = page_align (real_size + GRUB_DISK_SECTOR_SIZE); prot_size = page_align (prot_size); mmap_size = find_mmap_size (); grub_dprintf ("linux", "real_size = %x, prot_size = %x, mmap_size = %x\n", real_size, prot_size, mmap_size); /* Calculate the number of pages; Combine the real mode code with the memory map buffer for simplicity. */ real_mode_pages = ((real_size + mmap_size) >> 12); prot_mode_pages = (prot_size >> 12); /* Initialize the memory pointers with NULL for convenience. */ real_mode_mem = 0; prot_mode_mem = 0; /* Read the memory map temporarily, to find free space. */ mmap = grub_malloc (mmap_size); if (! mmap) return 0; tmp_mmap_size = mmap_size; if (grub_efi_get_memory_map (&tmp_mmap_size, mmap, 0, &desc_size, 0) <= 0) grub_fatal ("cannot get memory map"); mmap_end = NEXT_MEMORY_DESCRIPTOR (mmap, tmp_mmap_size); /* First, find free pages for the real mode code and the memory map buffer. */ for (desc = mmap; desc < mmap_end; desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size)) { /* Probably it is better to put the real mode code in the traditional space for safety. */ if (desc->type == GRUB_EFI_CONVENTIONAL_MEMORY && desc->physical_start <= 0x90000 && desc->num_pages >= real_mode_pages) { grub_efi_physical_address_t physical_end; grub_efi_physical_address_t addr; physical_end = desc->physical_start + (desc->num_pages << 12); if (physical_end > 0x90000) physical_end = 0x90000; grub_dprintf ("linux", "physical_start = %x, physical_end = %x\n", (unsigned) desc->physical_start, (unsigned) physical_end); addr = physical_end - real_size - mmap_size; if (addr < 0x10000) continue; grub_dprintf ("linux", "trying to allocate %u pages at %x\n", real_mode_pages, (unsigned) addr); real_mode_mem = grub_efi_allocate_pages (addr, real_mode_pages); if (! real_mode_mem) grub_fatal ("cannot allocate pages"); desc->num_pages -= real_mode_pages; break; } } if (! real_mode_mem) { grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate real mode pages"); goto fail; } mmap_buf = (void *) ((char *) real_mode_mem + real_size); /* Next, find free pages for the protected mode code. */ /* XXX what happens if anything is using this address? */ prot_mode_mem = grub_efi_allocate_pages (0x100000, prot_mode_pages); if (! prot_mode_mem) { grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate protected mode pages"); goto fail; } grub_free (mmap); return 1; fail: grub_free (mmap); free_pages (); return 0;}static grub_err_tgrub_linux_boot (void){ struct linux_kernel_header *lh; struct linux_kernel_params *params; grub_efi_uintn_t mmap_size; grub_efi_uintn_t map_key; grub_efi_uintn_t desc_size; grub_efi_uint32_t desc_version; lh = real_mode_mem; params = real_mode_mem; grub_dprintf ("linux", "code32_start = %x, idt_desc = %x, gdt_desc = %x\n", (unsigned) lh->code32_start, (grub_addr_t) &(idt_desc.limit), (grub_addr_t) &(gdt_desc.limit)); grub_dprintf ("linux", "idt = %x:%x, gdt = %x:%x\n", (unsigned) idt_desc.limit, (unsigned) idt_desc.base, (unsigned) gdt_desc.limit, (unsigned) gdt_desc.base); mmap_size = find_mmap_size (); if (grub_efi_get_memory_map (&mmap_size, mmap_buf, &map_key, &desc_size, &desc_version) <= 0) grub_fatal ("cannot get memory map"); if (! grub_efi_exit_boot_services (map_key)) grub_fatal ("cannot exit boot services"); /* Note that no boot services are available from here. */ /* Hardware interrupts are not safe any longer. */ asm volatile ("cli" : : ); /* Pass EFI parameters. */ params->efi_mem_desc_size = desc_size; params->efi_mem_desc_version = desc_version; params->efi_mmap = (grub_addr_t) mmap_buf; params->efi_mmap_size = mmap_size; /* Pass parameters. */ asm volatile ("movl %0, %%esi" : : "m" (real_mode_mem)); asm volatile ("movl %0, %%ecx" : : "m" (lh->code32_start)); asm volatile ("xorl %%ebx, %%ebx" : : ); /* Load the IDT and the GDT for the bootstrap. */ asm volatile ("lidt %0" : : "m" (idt_desc.limit)); asm volatile ("lgdt %0" : : "m" (gdt_desc.limit)); /* Enter Linux. */ asm volatile ("jmp *%%ecx" : : ); /* Never reach here. */ return GRUB_ERR_NONE;}static grub_err_tgrub_linux_unload (void){ free_pages (); grub_dl_unref (my_mod); loaded = 0; return GRUB_ERR_NONE;}voidgrub_rescue_cmd_linux (int argc, char *argv[]){ grub_file_t file = 0; struct linux_kernel_header lh; struct linux_kernel_params *params; grub_uint8_t setup_sects; grub_size_t real_size, prot_size; grub_ssize_t len; int i; char *dest; grub_dl_ref (my_mod); if (argc == 0) { grub_error (GRUB_ERR_BAD_ARGUMENT, "no kernel specified"); goto fail; } file = grub_file_open (argv[0]); if (! file) goto fail;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -