/* * 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 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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; int type; grub_size_t size; int bar; }; struct iterator_ctx { struct resource *resources; grub_size_t nresources; }; static int count_cards (grub_pci_device_t dev __attribute__ ((unused)), grub_pci_id_t pciid __attribute__ ((unused)), void *data) { int *cnt = data; (*cnt)++; return 0; } 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; 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) { int ncards = 0; struct iterator_ctx ctx; grub_pci_iterate (count_cards, &ncards); { struct resource resources[ncards * 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 (); }