Matt Domsch | 0584396 | 2009-11-02 11:51:24 -0600 | [diff] [blame] | 1 | #include <linux/acpi.h> |
| 2 | #include <linux/pci.h> |
| 3 | |
| 4 | #define PREFIX "ACPI: " |
| 5 | |
| 6 | static inline unsigned long parse_acpi_hest_ia_machine_check(struct acpi_hest_ia_machine_check *p) |
| 7 | { |
| 8 | return sizeof(*p) + |
| 9 | (sizeof(struct acpi_hest_ia_error_bank) * p->num_hardware_banks); |
| 10 | } |
| 11 | |
| 12 | static inline unsigned long parse_acpi_hest_ia_corrected(struct acpi_hest_ia_corrected *p) |
| 13 | { |
| 14 | return sizeof(*p) + |
| 15 | (sizeof(struct acpi_hest_ia_error_bank) * p->num_hardware_banks); |
| 16 | } |
| 17 | |
| 18 | static inline unsigned long parse_acpi_hest_ia_nmi(struct acpi_hest_ia_nmi *p) |
| 19 | { |
| 20 | return sizeof(*p); |
| 21 | } |
| 22 | |
| 23 | static inline unsigned long parse_acpi_hest_generic(struct acpi_hest_generic *p) |
| 24 | { |
| 25 | return sizeof(*p); |
| 26 | } |
| 27 | |
| 28 | static inline unsigned int hest_match_pci(struct acpi_hest_aer_common *p, struct pci_dev *pci) |
| 29 | { |
| 30 | return (0 == pci_domain_nr(pci->bus) && |
| 31 | p->bus == pci->bus->number && |
| 32 | p->device == PCI_SLOT(pci->devfn) && |
| 33 | p->function == PCI_FUNC(pci->devfn)); |
| 34 | } |
| 35 | |
| 36 | static unsigned long parse_acpi_hest_aer(void *hdr, int type, struct pci_dev *pci, int *firmware_first) |
| 37 | { |
| 38 | struct acpi_hest_aer_common *p = hdr + sizeof(struct acpi_hest_header); |
| 39 | unsigned long rc=0; |
| 40 | u8 pcie_type = 0; |
| 41 | u8 bridge = 0; |
| 42 | switch (type) { |
| 43 | case ACPI_HEST_TYPE_AER_ROOT_PORT: |
| 44 | rc = sizeof(struct acpi_hest_aer_root); |
| 45 | pcie_type = PCI_EXP_TYPE_ROOT_PORT; |
| 46 | break; |
| 47 | case ACPI_HEST_TYPE_AER_ENDPOINT: |
| 48 | rc = sizeof(struct acpi_hest_aer); |
| 49 | pcie_type = PCI_EXP_TYPE_ENDPOINT; |
| 50 | break; |
| 51 | case ACPI_HEST_TYPE_AER_BRIDGE: |
| 52 | rc = sizeof(struct acpi_hest_aer_bridge); |
| 53 | if ((pci->class >> 16) == PCI_BASE_CLASS_BRIDGE) |
| 54 | bridge = 1; |
| 55 | break; |
| 56 | } |
| 57 | |
| 58 | if (p->flags & ACPI_HEST_GLOBAL) { |
| 59 | if ((pci->is_pcie && (pci->pcie_type == pcie_type)) || bridge) |
| 60 | *firmware_first = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST); |
| 61 | } |
| 62 | else |
| 63 | if (hest_match_pci(p, pci)) |
| 64 | *firmware_first = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST); |
| 65 | return rc; |
| 66 | } |
| 67 | |
| 68 | static int acpi_hest_firmware_first(struct acpi_table_header *stdheader, struct pci_dev *pci) |
| 69 | { |
| 70 | struct acpi_table_hest *hest = (struct acpi_table_hest *)stdheader; |
| 71 | void *p = (void *)hest + sizeof(*hest); /* defined by the ACPI 4.0 spec */ |
| 72 | struct acpi_hest_header *hdr = p; |
| 73 | |
| 74 | int i; |
| 75 | int firmware_first = 0; |
| 76 | static unsigned char printed_unused = 0; |
| 77 | static unsigned char printed_reserved = 0; |
| 78 | |
| 79 | for (i=0, hdr=p; p < (((void *)hest) + hest->header.length) && i < hest->error_source_count; i++) { |
| 80 | switch (hdr->type) { |
| 81 | case ACPI_HEST_TYPE_IA32_CHECK: |
| 82 | p += parse_acpi_hest_ia_machine_check(p); |
| 83 | break; |
| 84 | case ACPI_HEST_TYPE_IA32_CORRECTED_CHECK: |
| 85 | p += parse_acpi_hest_ia_corrected(p); |
| 86 | break; |
| 87 | case ACPI_HEST_TYPE_IA32_NMI: |
| 88 | p += parse_acpi_hest_ia_nmi(p); |
| 89 | break; |
| 90 | /* These three should never appear */ |
| 91 | case ACPI_HEST_TYPE_NOT_USED3: |
| 92 | case ACPI_HEST_TYPE_NOT_USED4: |
| 93 | case ACPI_HEST_TYPE_NOT_USED5: |
| 94 | if (!printed_unused) { |
| 95 | printk(KERN_DEBUG PREFIX |
| 96 | "HEST Error Source list contains an obsolete type (%d).\n", hdr->type); |
| 97 | printed_unused = 1; |
| 98 | } |
| 99 | break; |
| 100 | case ACPI_HEST_TYPE_AER_ROOT_PORT: |
| 101 | case ACPI_HEST_TYPE_AER_ENDPOINT: |
| 102 | case ACPI_HEST_TYPE_AER_BRIDGE: |
| 103 | p += parse_acpi_hest_aer(p, hdr->type, pci, &firmware_first); |
| 104 | break; |
| 105 | case ACPI_HEST_TYPE_GENERIC_ERROR: |
| 106 | p += parse_acpi_hest_generic(p); |
| 107 | break; |
| 108 | /* These should never appear either */ |
| 109 | case ACPI_HEST_TYPE_RESERVED: |
| 110 | default: |
| 111 | if (!printed_reserved) { |
| 112 | printk(KERN_DEBUG PREFIX |
| 113 | "HEST Error Source list contains a reserved type (%d).\n", hdr->type); |
| 114 | printed_reserved = 1; |
| 115 | } |
| 116 | break; |
| 117 | } |
| 118 | } |
| 119 | return firmware_first; |
| 120 | } |
| 121 | |
| 122 | int acpi_hest_firmware_first_pci(struct pci_dev *pci) |
| 123 | { |
| 124 | acpi_status status = AE_NOT_FOUND; |
| 125 | struct acpi_table_header *hest = NULL; |
| 126 | status = acpi_get_table(ACPI_SIG_HEST, 1, &hest); |
| 127 | |
| 128 | if (ACPI_SUCCESS(status)) { |
| 129 | if (acpi_hest_firmware_first(hest, pci)) { |
| 130 | return 1; |
| 131 | } |
| 132 | } |
| 133 | return 0; |
| 134 | } |
| 135 | EXPORT_SYMBOL_GPL(acpi_hest_firmware_first_pci); |