diff --git a/include/grub/i386/pci.h b/include/grub/i386/pci.h index f4f08ab..996f642 100644 --- a/include/grub/i386/pci.h +++ b/include/grub/i386/pci.h @@ -32,4 +32,39 @@ grub_pci_read (grub_pci_address_t addr) return grub_inl (GRUB_PCI_DATA_REG); } +static inline grub_uint16_t +grub_pci_read_word (grub_pci_address_t addr) +{ + grub_outl (addr & ~3, GRUB_PCI_ADDR_REG); + return grub_inw (GRUB_PCI_DATA_REG + (addr & 3)); +} + +static inline grub_uint8_t +grub_pci_read_byte (grub_pci_address_t addr) +{ + grub_outl (addr & ~3, GRUB_PCI_ADDR_REG); + return grub_inb (GRUB_PCI_DATA_REG + (addr & 3)); +} + +static inline void +grub_pci_write (grub_pci_address_t addr, grub_uint32_t data) +{ + grub_outl (addr, GRUB_PCI_ADDR_REG); + grub_outl (data, GRUB_PCI_DATA_REG); +} + +static inline void +grub_pci_write_word (grub_pci_address_t addr, grub_uint16_t data) +{ + grub_outl (addr & ~3, GRUB_PCI_ADDR_REG); + grub_outw (data, GRUB_PCI_DATA_REG + (addr & 3)); +} + +static inline void +grub_pci_write_byte (grub_pci_address_t addr, grub_uint8_t data) +{ + grub_outl (addr & ~3, GRUB_PCI_ADDR_REG); + grub_outb (data, GRUB_PCI_DATA_REG + (addr & 3)); +} + #endif /* GRUB_CPU_PCI_H */ diff --git a/include/grub/pci.h b/include/grub/pci.h index aceee49..7c8b505 100644 --- a/include/grub/pci.h +++ b/include/grub/pci.h @@ -36,8 +36,8 @@ #define GRUB_PCI_ADDR_IO_MASK ~0x03 typedef grub_uint32_t grub_pci_id_t; -typedef int (*grub_pci_iteratefunc_t) (int bus, int device, int func, - grub_pci_id_t pciid); +typedef int NESTED_FUNC_ATTR (*grub_pci_iteratefunc_t) + (int bus, int device, int func, grub_pci_id_t pciid); typedef grub_uint32_t grub_pci_address_t; grub_pci_address_t EXPORT_FUNC(grub_pci_make_address) (int bus, int device, diff --git a/loader/i386/efi/linux.c b/loader/i386/efi/linux.c index c62198a..6e545eb 100644 --- a/loader/i386/efi/linux.c +++ b/loader/i386/efi/linux.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #define GRUB_LINUX_CL_OFFSET 0x1000 @@ -248,9 +249,9 @@ allocate_pages (grub_size_t prot_size) } grub_dprintf ("linux", "real_mode_mem = %lx, real_mode_pages = %x, " - "prot_mode_mem = %lx, prot_mode_pages = %x\n", - (unsigned long) real_mode_mem, (unsigned) real_mode_pages, - (unsigned long) prot_mode_mem, (unsigned) prot_mode_pages); + "prot_mode_mem = %lx, prot_mode_pages = %x\n", + (unsigned long) real_mode_mem, (unsigned) real_mode_pages, + (unsigned long) prot_mode_mem, (unsigned) prot_mode_pages); grub_free (mmap); return 1; @@ -263,8 +264,8 @@ allocate_pages (grub_size_t prot_size) static void grub_e820_add_region (struct grub_e820_mmap *e820_map, int *e820_num, - grub_uint64_t start, grub_uint64_t size, - grub_uint32_t type) + grub_uint64_t start, grub_uint64_t size, + grub_uint32_t type) { int n = *e820_num; @@ -285,19 +286,82 @@ grub_e820_add_region (struct grub_e820_mmap *e820_map, int *e820_num, static grub_efi_guid_t acpi_guid = GRUB_EFI_ACPI_TABLE_GUID; static grub_efi_guid_t acpi2_guid = GRUB_EFI_ACPI_20_TABLE_GUID; +static grub_efi_guid_t smbios_guid = GRUB_EFI_SMBIOS_TABLE_GUID; #define EBDA_SEG_ADDR 0x40e #define LOW_MEM_ADDR 0x413 -#define FAKE_EBDA_SEG 0x9fc0 +#define LOW_MEM_TOP 0xa000 + +#define BLANK_MEM 0xffffffff +#define VBIOS_ADDR 0xc0000 +#define SBIOS_ADDR 0xf0000 + +static int rom_enabled; + +static int +enable_rom_area (void) +{ + grub_pci_address_t addr; + grub_uint32_t *rom_ptr; + + if (rom_enabled) + return 0; + + rom_enabled++; + + rom_ptr = (grub_uint32_t *) VBIOS_ADDR; + if (*rom_ptr != BLANK_MEM) + { + grub_printf ("ROM image present.\n"); + return 0; + } + + addr = grub_pci_make_address (0, 0, 0, 36); + grub_pci_write_byte (addr++, 0x30); + grub_pci_write_byte (addr++, 0x33); + grub_pci_write_byte (addr++, 0x33); + grub_pci_write_byte (addr++, 0x33); + grub_pci_write_byte (addr++, 0x33); + grub_pci_write_byte (addr++, 0x33); + grub_pci_write_byte (addr++, 0x33); + grub_pci_write_byte (addr, 0); + + *rom_ptr = 0; + if (*rom_ptr != 0) + { + grub_printf ("Can\'t enable rom area.\n"); + return 0; + } + + return 1; +} + +static void +lock_rom_area (void) +{ + grub_pci_address_t addr; + + addr = grub_pci_make_address (0, 0, 0, 36); + grub_pci_write_byte (addr++, 0x10); + grub_pci_write_byte (addr++, 0x11); + grub_pci_write_byte (addr++, 0x11); + grub_pci_write_byte (addr++, 0x11); + grub_pci_write_byte (addr, 0x11); +} static void fake_bios_data (void) { unsigned i; - void *acpi; + void *acpi, *smbios; grub_uint16_t *ebda_seg_ptr, *low_mem_ptr; + char *addr; + + if (*((grub_uint32_t *) SBIOS_ADDR) != BLANK_MEM) + return; acpi = 0; + smbios = 0; for (i = 0; i < grub_efi_system_table->num_table_entries; i++) { grub_efi_guid_t *guid = @@ -317,21 +381,30 @@ fake_bios_data (void) acpi = t; grub_printf ("ACPI: %p\n", t); } + else if (! grub_memcmp (guid, &smbios_guid, sizeof (grub_efi_guid_t))) + { + smbios = grub_efi_system_table->configuration_table[i].vendor_table; + grub_printf ("SMBIOS: %p\n", smbios); + } } - if (acpi == 0) - return; - ebda_seg_ptr = (grub_uint16_t *) EBDA_SEG_ADDR; low_mem_ptr = (grub_uint16_t *) LOW_MEM_ADDR; + if ((! *ebda_seg_ptr) && (! *low_mem_ptr)) + { + *ebda_seg_ptr = LOW_MEM_TOP; + *low_mem_ptr = (LOW_MEM_TOP >> 6); + } - if ((*ebda_seg_ptr) || (*low_mem_ptr)) - return; - - *ebda_seg_ptr = FAKE_EBDA_SEG; - *low_mem_ptr = FAKE_EBDA_SEG >> 6; + addr = (char *) SBIOS_ADDR; + if (acpi) + { + grub_memcpy (addr, acpi, 512); + addr += 512; + } - grub_memcpy ((char *) (FAKE_EBDA_SEG << 4), acpi, 1024); + if (smbios) + grub_memcpy (addr, (char *) smbios + 16, 16); } #ifdef __x86_64__ @@ -353,13 +426,17 @@ grub_linux_boot (void) grub_efi_memory_descriptor_t *desc; int e820_num; - fake_bios_data (); + if (enable_rom_area ()) + { + fake_bios_data (); + lock_rom_area (); + } params = real_mode_mem; grub_dprintf ("linux", "code32_start = %x, idt_desc = %lx, gdt_desc = %lx\n", (unsigned) params->code32_start, - (unsigned long) &(idt_desc.limit), + (unsigned long) &(idt_desc.limit), (unsigned long) &(gdt_desc.limit)); grub_dprintf ("linux", "idt = %x:%lx, gdt = %x:%lx\n", (unsigned) idt_desc.limit, (unsigned long) idt_desc.base, @@ -376,69 +453,69 @@ grub_linux_boot (void) desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size)) { switch (desc->type) - { - case GRUB_EFI_ACPI_RECLAIM_MEMORY: - grub_e820_add_region (params->e820_map, &e820_num, - desc->physical_start, - desc->num_pages << 12, - GRUB_E820_ACPI); - break; - - case GRUB_EFI_ACPI_MEMORY_NVS: - grub_e820_add_region (params->e820_map, &e820_num, - desc->physical_start, - desc->num_pages << 12, - GRUB_E820_NVS); - break; - - case GRUB_EFI_RUNTIME_SERVICES_CODE: - grub_e820_add_region (params->e820_map, &e820_num, - desc->physical_start, - desc->num_pages << 12, - GRUB_E820_EXEC_CODE); - break; - - case GRUB_EFI_LOADER_CODE: - case GRUB_EFI_LOADER_DATA: - case GRUB_EFI_BOOT_SERVICES_CODE: - case GRUB_EFI_BOOT_SERVICES_DATA: - case GRUB_EFI_CONVENTIONAL_MEMORY: - { - grub_uint64_t start, size, end; - - start = desc->physical_start; - size = desc->num_pages << 12; - end = start + size; - - /* Skip A0000 - 100000 region. */ - if ((start < 0x100000ULL) && (end > 0xA0000ULL)) - { - if (start < 0xA0000ULL) - { - grub_e820_add_region (params->e820_map, &e820_num, - start, - 0xA0000ULL - start, - GRUB_E820_RAM); - } - - if (end <= 0x100000ULL) - continue; - - start = 0x100000ULL; - size = end - start; - } - - grub_e820_add_region (params->e820_map, &e820_num, - start, size, GRUB_E820_RAM); - break; - } - - default: - grub_e820_add_region (params->e820_map, &e820_num, - desc->physical_start, - desc->num_pages << 12, - GRUB_E820_RESERVED); - } + { + case GRUB_EFI_ACPI_RECLAIM_MEMORY: + grub_e820_add_region (params->e820_map, &e820_num, + desc->physical_start, + desc->num_pages << 12, + GRUB_E820_ACPI); + break; + + case GRUB_EFI_ACPI_MEMORY_NVS: + grub_e820_add_region (params->e820_map, &e820_num, + desc->physical_start, + desc->num_pages << 12, + GRUB_E820_NVS); + break; + + case GRUB_EFI_RUNTIME_SERVICES_CODE: + grub_e820_add_region (params->e820_map, &e820_num, + desc->physical_start, + desc->num_pages << 12, + GRUB_E820_EXEC_CODE); + break; + + case GRUB_EFI_LOADER_CODE: + case GRUB_EFI_LOADER_DATA: + case GRUB_EFI_BOOT_SERVICES_CODE: + case GRUB_EFI_BOOT_SERVICES_DATA: + case GRUB_EFI_CONVENTIONAL_MEMORY: + { + grub_uint64_t start, size, end; + + start = desc->physical_start; + size = desc->num_pages << 12; + end = start + size; + + /* Skip A0000 - 100000 region. */ + if ((start < 0x100000ULL) && (end > 0xA0000ULL)) + { + if (start < 0xA0000ULL) + { + grub_e820_add_region (params->e820_map, &e820_num, + start, + 0xA0000ULL - start, + GRUB_E820_RAM); + } + + if (end <= 0x100000ULL) + continue; + + start = 0x100000ULL; + size = end - start; + } + + grub_e820_add_region (params->e820_map, &e820_num, + start, size, GRUB_E820_RAM); + break; + } + + default: + grub_e820_add_region (params->e820_map, &e820_num, + desc->physical_start, + desc->num_pages << 12, + GRUB_E820_RESERVED); + } } params->mmap_size = e820_num; @@ -520,41 +597,150 @@ static grub_efi_guid_t uga_draw_guid = GRUB_EFI_UGA_DRAW_GUID; #define FBTEST_STEP (0x10000 >> 2) #define FBTEST_COUNT 8 -static grub_uint32_t fb_list[]= - {0x40000000, 0x80000000, 0xc0000000, 0}; +static int +find_line_len (grub_uint32_t *fb_base, grub_uint32_t *line_len) +{ + grub_uint32_t *base = (grub_uint32_t *) (grub_target_addr_t) *fb_base; + int i; + + for (i = 0; i < FBTEST_COUNT; i++, base += FBTEST_STEP) + { + if ((*base & RGB_MASK) == RGB_MAGIC) + { + int j; + + for (j = LINE_MIN; j <= LINE_MAX; j++) + { + if ((base[j] & RGB_MASK) == RGB_MAGIC) + { + *fb_base = (grub_uint32_t) (grub_target_addr_t) base; + *line_len = j << 2; + + return 1; + } + } + + break; + } + } + + return 0; +} static int find_framebuf (grub_uint32_t *fb_base, grub_uint32_t *line_len) { - grub_uint32_t *fb; + int found = 0; + + auto int NESTED_FUNC_ATTR find_card (int bus, int dev, int func, + grub_pci_id_t pciid); - for (fb = fb_list; *fb; fb++) + int NESTED_FUNC_ATTR find_card (int bus, int dev, int func, + grub_pci_id_t pciid __attribute__ ((unused))) { - grub_uint32_t *base = (grub_uint32_t *) (grub_target_addr_t) *fb; - int i; + grub_pci_address_t addr; - for (i = 0; i < FBTEST_COUNT; i++, base += FBTEST_STEP) - { - if ((*base & RGB_MASK) == RGB_MAGIC) + addr = grub_pci_make_address (bus, dev, func, 2); + if (grub_pci_read (addr) >> 24 == 0x3) + { + int i; + + addr = grub_pci_make_address (bus, dev, func, 4); + for (i = 0; i < 6; i++, addr += 4) { - int j; - - for (j = LINE_MIN; j <= LINE_MAX; j++) - { - if ((base[j] & RGB_MASK) == RGB_MAGIC) - { - *fb_base = (grub_uint32_t) (grub_target_addr_t) base; - *line_len = j << 2; - - return 0; - } - } - - break; - } - } + grub_uint32_t old_bar1, old_bar2, type; + grub_uint64_t base64; + + old_bar1 = grub_pci_read (addr); + if ((! old_bar1) || (old_bar1 & GRUB_PCI_ADDR_SPACE_IO)) + continue; + + type = old_bar1 & GRUB_PCI_ADDR_MEM_TYPE_MASK; + if (! (old_bar1 & GRUB_PCI_ADDR_MEM_PREFETCH)) + { + if (type == GRUB_PCI_ADDR_MEM_TYPE_64) + { + i++; + addr += 4; + } + continue; + } + + if (type == GRUB_PCI_ADDR_MEM_TYPE_64) + { + if (i == 5) + break; + + old_bar2 = grub_pci_read (addr + 4); + } + else + old_bar2 = 0; + + base64 = old_bar2; + base64 <<= 32; + base64 |= (old_bar1 & GRUB_PCI_ADDR_MEM_MASK); + + *fb_base = base64; + if (find_line_len (fb_base, line_len)) + { + grub_pci_address_t cmd_addr, rom_addr; + grub_uint16_t old_cmd; + grub_uint64_t limit64; + + cmd_addr = grub_pci_make_address (bus, dev, func, 1); + old_cmd = grub_pci_read_word (cmd_addr); + + /* Disable mem access. */ + grub_pci_write_word (cmd_addr, old_cmd & 0xfffc); + + grub_pci_write (addr, 0xffffffff); + if (type == GRUB_PCI_ADDR_MEM_TYPE_64) + grub_pci_write (addr + 4, 0xffffffff); + + if (type == GRUB_PCI_ADDR_MEM_TYPE_64) + { + limit64 = grub_pci_read (addr + 4); + limit64 <<= 32; + } + else + limit64 = 0; + limit64 |= (grub_pci_read (addr) & GRUB_PCI_ADDR_MEM_MASK); + limit64 = (~limit64) + 1; + + /* Restore old state. */ + grub_pci_write (addr, old_bar1); + if (type == GRUB_PCI_ADDR_MEM_TYPE_64) + grub_pci_write (addr + 4, old_bar2); + grub_pci_write_word (cmd_addr, old_cmd); + + grub_printf ("Display controller: %d:%d.%d\n", + bus, dev, func); + grub_printf ("Video memory base: 0x%llx\n", + (unsigned long long) base64); + grub_printf ("Video memory size: 0x%llx\n", + (unsigned long long) limit64); + + rom_addr = grub_pci_make_address (bus, dev, func, 12); + grub_printf ("Expansion ROM: 0x%x\n", + grub_pci_read (rom_addr)); + + found++; + return 1; + } + + if (type == GRUB_PCI_ADDR_MEM_TYPE_64) + { + i++; + addr += 4; + } + } + } + + return 0; } - return 1; + + grub_pci_iterate (find_card); + return found; } static int @@ -580,13 +766,13 @@ grub_linux_setup_video (struct linux_kernel_params *params) ret = find_framebuf (&fb_base, &line_len); grub_efi_set_text_mode (1); - if (ret) + if (! ret) { grub_printf ("Can\'t find frame buffer address\n"); return 1; } - grub_printf ("Video frame buffer: 0x%x\n", fb_base); + grub_printf ("Frame buffer base: 0x%x\n", fb_base); grub_printf ("Video line length: %d\n", line_len); params->lfb_width = width; @@ -848,16 +1034,16 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), } else if (grub_memcmp (argv[i], "video=", 6) == 0) { - if (grub_memcmp (&argv[i][6], "vesafb", 6) == 0) - video_type = GRUB_VIDEO_TYPE_VLFB; - else if (grub_memcmp (&argv[i][6], "efifb", 5) == 0) - video_type = GRUB_VIDEO_TYPE_EFI; + if (grub_memcmp (&argv[i][6], "vesafb", 6) == 0) + video_type = GRUB_VIDEO_TYPE_VLFB; + else if (grub_memcmp (&argv[i][6], "efifb", 5) == 0) + video_type = GRUB_VIDEO_TYPE_EFI; } if (video_type) { if (! grub_linux_setup_video (params)) - params->have_vga = video_type; + params->have_vga = video_type; } /* Specify the boot file. */ @@ -946,7 +1132,7 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), /* Usually, the compression ratio is about 50%. */ addr_min = (grub_addr_t) prot_mode_mem + ((prot_mode_pages * 3) << 12) - + page_align (size); + + page_align (size); /* Find the highest address to put the initrd. */ mmap_size = find_mmap_size (); @@ -968,7 +1154,7 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), physical_end = addr_max; if (physical_end < page_align (size)) - continue; + continue; physical_end -= page_align (size); @@ -1009,7 +1195,64 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), return grub_errno; } -static grub_command_t cmd_linux, cmd_initrd; +static grub_err_t +grub_cmd_loadrom (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + grub_file_t file; + int size; + + if (argc == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "No rom image specified"); + + file = grub_file_open (argv[0]); + if (! file) + return grub_errno; + + size = file->size; + if ((size < 0x10000) || (size > 0x40000)) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid rom image size"); + goto quit; + } + + if (! enable_rom_area ()) + goto quit; + + do + { + grub_file_read (file, (char *) VBIOS_ADDR, size); + if (grub_errno) + break; + + if (argc > 1) + { + grub_file_close (file); + file = grub_file_open (argv[1]); + if (! file) + break; + + size = file->size; + if (size != 1024) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid ivt size"); + break; + } + + grub_file_read (file, (char *) 0, size); + } + } + while (0); + + fake_bios_data (); + lock_rom_area (); + + quit: + grub_file_close (file); + return grub_errno; +} + +static grub_command_t cmd_linux, cmd_initrd, cmd_loadrom; GRUB_MOD_INIT(linux) { @@ -1017,6 +1260,10 @@ GRUB_MOD_INIT(linux) 0, "load linux"); cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd, 0, "load initrd"); + cmd_loadrom = grub_register_command ("loadrom", grub_cmd_loadrom, + "loadrom bios_dump [ivt_dump]", + "load bios rom image"); + my_mod = mod; } @@ -1024,4 +1271,5 @@ GRUB_MOD_FINI(linux) { grub_unregister_command (cmd_linux); grub_unregister_command (cmd_initrd); + grub_unregister_command (cmd_loadrom); }