diff --git a/ChangeLog b/ChangeLog index 6971b091d..b6fe1935b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +2012-05-03 Matthew Garrett +2012-05-03 Vladimir Serbinenko + + Suspend broadcom cards in order to stop their DMA. + + * grub-core/Makefile.am (KERNEL_HEADER_FILES): Add pci.h on x86 EFI. + * grub-core/Makefile.core.def (kernel): Add pci.c on x86 EFI. + (pci): Don't build on x86 EFI. + * grub-core/bus/pci.c (grub_pci_find_capability): New function. + * grub-core/kern/efi/mm.c (stop_broadcom) [__i386__ || __x86_64__]: + New function. + (grub_efi_finish_boot_services) [__i386__ || __x86_64__]: Call + stop_broadcom if running on EFI. + * include/grub/pci.h (GRUB_PCI_CLASS_NETWORK): New enum value. + (GRUB_PCI_CAP_POWER_MANAGEMENT): Likewise. + (GRUB_PCI_VENDOR_BROADCOM): Likewise. + (grub_pci_find_capability): New proto. + 2012-05-03 Vladimir Serbinenko * docs/grub.texi: Remove dot from the extension as it apparently diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am index 3dd6de7fc..4d8096dfa 100644 --- a/grub-core/Makefile.am +++ b/grub-core/Makefile.am @@ -96,6 +96,7 @@ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/efi.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/time.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/disk.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/pit.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/pci.h endif if COND_i386_coreboot @@ -126,6 +127,7 @@ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/efi.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/time.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/disk.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i386/pit.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/pci.h endif if COND_ia64_efi diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 45a8b0f5d..000cf0dd8 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -139,11 +139,13 @@ kernel = { i386_efi = kern/i386/tsc.c; i386_efi = kern/i386/efi/init.c; + i386_efi = bus/pci.c; x86_64_efi = kern/i386/tsc.c; x86_64_efi = kern/x86_64/dl.c; x86_64_efi = kern/x86_64/efi/callwrap.S; x86_64_efi = kern/i386/efi/init.c; + x86_64_efi = bus/pci.c; ia64_efi = kern/ia64/efi/startup.S; ia64_efi = kern/ia64/efi/init.c; @@ -475,8 +477,6 @@ module = { common = bus/pci.c; enable = i386_pc; - enable = i386_efi; - enable = x86_64_efi; enable = i386_ieee1275; enable = i386_coreboot; enable = i386_multiboot; diff --git a/grub-core/bus/pci.c b/grub-core/bus/pci.c index ad85738df..17dea30a1 100644 --- a/grub-core/bus/pci.c +++ b/grub-core/bus/pci.c @@ -140,3 +140,34 @@ grub_pci_iterate (grub_pci_iteratefunc_t hook) } } } + +grub_uint8_t +grub_pci_find_capability (grub_pci_device_t dev, grub_uint8_t cap) +{ + grub_uint8_t pos = 0x34; + int ttl = 48; + + while (ttl--) + { + grub_uint8_t id; + grub_pci_address_t addr; + + addr = grub_pci_make_address (dev, pos); + pos = grub_pci_read_byte (addr); + if (pos < 0x40) + break; + + pos &= ~3; + + addr = grub_pci_make_address (dev, pos); + id = grub_pci_read_byte (addr); + + if (id == 0xff) + break; + + if (id == cap) + return pos; + pos++; + } + return 0; +} diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c index 8b9e6ec25..a2edc84b3 100644 --- a/grub-core/kern/efi/mm.c +++ b/grub-core/kern/efi/mm.c @@ -22,6 +22,10 @@ #include #include +#if defined (__i386__) || defined (__x86_64__) +#include +#endif + #define NEXT_MEMORY_DESCRIPTOR(desc, size) \ ((grub_efi_memory_descriptor_t *) ((char *) (desc) + (size))) @@ -103,6 +107,43 @@ grub_efi_free_pages (grub_efi_physical_address_t address, efi_call_2 (b->free_pages, address, pages); } +#if defined (__i386__) || defined (__x86_64__) + +static void +stop_broadcom (void) +{ + auto int NESTED_FUNC_ATTR find_card (grub_pci_device_t dev, + grub_pci_id_t pciid); + + int NESTED_FUNC_ATTR find_card (grub_pci_device_t dev, + grub_pci_id_t pciid) + { + grub_pci_address_t addr; + grub_uint8_t cap; + grub_uint16_t pm_state; + + if ((pciid & 0xffff) != GRUB_PCI_VENDOR_BROADCOM) + return 0; + + addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS); + if (grub_pci_read (addr) >> 24 != GRUB_PCI_CLASS_NETWORK) + return 0; + cap = grub_pci_find_capability (dev, GRUB_PCI_CAP_POWER_MANAGEMENT); + if (!cap) + return 0; + addr = grub_pci_make_address (dev, cap + 4); + pm_state = grub_pci_read_word (addr); + pm_state = pm_state | 0x03; + grub_pci_write_word (addr, pm_state); + grub_pci_read_word (addr); + return 0; + } + + grub_pci_iterate (find_card); +} + +#endif + grub_err_t grub_efi_finish_boot_services (grub_efi_uintn_t *outbuf_size, void *outbuf, grub_efi_uintn_t *map_key, @@ -112,6 +153,14 @@ grub_efi_finish_boot_services (grub_efi_uintn_t *outbuf_size, void *outbuf, grub_efi_boot_services_t *b; grub_efi_status_t status; +#if defined (__i386__) || defined (__x86_64__) + const grub_uint16_t apple[] = { 'A', 'p', 'p', 'l', 'e' }; + int is_apple; + + is_apple = (grub_memcmp (grub_efi_system_table->firmware_vendor, + apple, sizeof (apple)) == 0); +#endif + if (grub_efi_get_memory_map (&finish_mmap_size, finish_mmap_buf, &finish_key, &finish_desc_size, &finish_desc_version) < 0) return grub_error (GRUB_ERR_IO, "couldn't retrieve memory map"); @@ -145,6 +194,11 @@ grub_efi_finish_boot_services (grub_efi_uintn_t *outbuf_size, void *outbuf, if (efi_desc_version) *efi_desc_version = finish_desc_version; +#if defined (__i386__) || defined (__x86_64__) + if (is_apple) + stop_broadcom (); +#endif + return GRUB_ERR_NONE; } diff --git a/include/grub/pci.h b/include/grub/pci.h index 2c2806889..2154d2951 100644 --- a/include/grub/pci.h +++ b/include/grub/pci.h @@ -81,6 +81,19 @@ #define GRUB_PCI_STATUS_DEVSEL_TIMING_SHIFT 9 #define GRUB_PCI_STATUS_DEVSEL_TIMING_MASK 0x0600 #define GRUB_PCI_CLASS_SUBCLASS_VGA 0x0300 +enum + { + GRUB_PCI_CLASS_NETWORK = 0x02 + }; +enum + { + GRUB_PCI_CAP_POWER_MANAGEMENT = 0x01 + }; + +enum + { + GRUB_PCI_VENDOR_BROADCOM = 0x14e4 + }; #ifndef ASM_FILE typedef grub_uint32_t grub_pci_id_t; @@ -146,6 +159,8 @@ grub_dma_virt2phys (volatile void *virt, struct grub_pci_dma_chunk *chunk) + grub_dma_get_phys (chunk)); } +grub_uint8_t +EXPORT_FUNC (grub_pci_find_capability) (grub_pci_device_t dev, grub_uint8_t cap); #endif