162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * BIOS32 and PCI BIOS handling. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/pci.h> 762306a36Sopenharmony_ci#include <linux/init.h> 862306a36Sopenharmony_ci#include <linux/slab.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/uaccess.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <asm/pci_x86.h> 1362306a36Sopenharmony_ci#include <asm/e820/types.h> 1462306a36Sopenharmony_ci#include <asm/pci-functions.h> 1562306a36Sopenharmony_ci#include <asm/set_memory.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* BIOS32 signature: "_32_" */ 1862306a36Sopenharmony_ci#define BIOS32_SIGNATURE (('_' << 0) + ('3' << 8) + ('2' << 16) + ('_' << 24)) 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* PCI signature: "PCI " */ 2162306a36Sopenharmony_ci#define PCI_SIGNATURE (('P' << 0) + ('C' << 8) + ('I' << 16) + (' ' << 24)) 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* PCI service signature: "$PCI" */ 2462306a36Sopenharmony_ci#define PCI_SERVICE (('$' << 0) + ('P' << 8) + ('C' << 16) + ('I' << 24)) 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* PCI BIOS hardware mechanism flags */ 2762306a36Sopenharmony_ci#define PCIBIOS_HW_TYPE1 0x01 2862306a36Sopenharmony_ci#define PCIBIOS_HW_TYPE2 0x02 2962306a36Sopenharmony_ci#define PCIBIOS_HW_TYPE1_SPEC 0x10 3062306a36Sopenharmony_ci#define PCIBIOS_HW_TYPE2_SPEC 0x20 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ciint pcibios_enabled; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* According to the BIOS specification at: 3562306a36Sopenharmony_ci * http://members.datafast.net.au/dft0802/specs/bios21.pdf, we could 3662306a36Sopenharmony_ci * restrict the x zone to some pages and make it ro. But this may be 3762306a36Sopenharmony_ci * broken on some bios, complex to handle with static_protections. 3862306a36Sopenharmony_ci * We could make the 0xe0000-0x100000 range rox, but this can break 3962306a36Sopenharmony_ci * some ISA mapping. 4062306a36Sopenharmony_ci * 4162306a36Sopenharmony_ci * So we let's an rw and x hole when pcibios is used. This shouldn't 4262306a36Sopenharmony_ci * happen for modern system with mmconfig, and if you don't want it 4362306a36Sopenharmony_ci * you could disable pcibios... 4462306a36Sopenharmony_ci */ 4562306a36Sopenharmony_cistatic inline void set_bios_x(void) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci pcibios_enabled = 1; 4862306a36Sopenharmony_ci set_memory_x(PAGE_OFFSET + BIOS_BEGIN, (BIOS_END - BIOS_BEGIN) >> PAGE_SHIFT); 4962306a36Sopenharmony_ci if (__supported_pte_mask & _PAGE_NX) 5062306a36Sopenharmony_ci printk(KERN_INFO "PCI: PCI BIOS area is rw and x. Use pci=nobios if you want it NX.\n"); 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* 5462306a36Sopenharmony_ci * This is the standard structure used to identify the entry point 5562306a36Sopenharmony_ci * to the BIOS32 Service Directory, as documented in 5662306a36Sopenharmony_ci * Standard BIOS 32-bit Service Directory Proposal 5762306a36Sopenharmony_ci * Revision 0.4 May 24, 1993 5862306a36Sopenharmony_ci * Phoenix Technologies Ltd. 5962306a36Sopenharmony_ci * Norwood, MA 6062306a36Sopenharmony_ci * and the PCI BIOS specification. 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ciunion bios32 { 6462306a36Sopenharmony_ci struct { 6562306a36Sopenharmony_ci unsigned long signature; /* _32_ */ 6662306a36Sopenharmony_ci unsigned long entry; /* 32 bit physical address */ 6762306a36Sopenharmony_ci unsigned char revision; /* Revision level, 0 */ 6862306a36Sopenharmony_ci unsigned char length; /* Length in paragraphs should be 01 */ 6962306a36Sopenharmony_ci unsigned char checksum; /* All bytes must add up to zero */ 7062306a36Sopenharmony_ci unsigned char reserved[5]; /* Must be zero */ 7162306a36Sopenharmony_ci } fields; 7262306a36Sopenharmony_ci char chars[16]; 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci/* 7662306a36Sopenharmony_ci * Physical address of the service directory. I don't know if we're 7762306a36Sopenharmony_ci * allowed to have more than one of these or not, so just in case 7862306a36Sopenharmony_ci * we'll make pcibios_present() take a memory start parameter and store 7962306a36Sopenharmony_ci * the array there. 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic struct { 8362306a36Sopenharmony_ci unsigned long address; 8462306a36Sopenharmony_ci unsigned short segment; 8562306a36Sopenharmony_ci} bios32_indirect __initdata = { 0, __KERNEL_CS }; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci/* 8862306a36Sopenharmony_ci * Returns the entry point for the given service, NULL on error 8962306a36Sopenharmony_ci */ 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic unsigned long __init bios32_service(unsigned long service) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci unsigned char return_code; /* %al */ 9462306a36Sopenharmony_ci unsigned long address; /* %ebx */ 9562306a36Sopenharmony_ci unsigned long length; /* %ecx */ 9662306a36Sopenharmony_ci unsigned long entry; /* %edx */ 9762306a36Sopenharmony_ci unsigned long flags; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci local_irq_save(flags); 10062306a36Sopenharmony_ci __asm__("lcall *(%%edi); cld" 10162306a36Sopenharmony_ci : "=a" (return_code), 10262306a36Sopenharmony_ci "=b" (address), 10362306a36Sopenharmony_ci "=c" (length), 10462306a36Sopenharmony_ci "=d" (entry) 10562306a36Sopenharmony_ci : "0" (service), 10662306a36Sopenharmony_ci "1" (0), 10762306a36Sopenharmony_ci "D" (&bios32_indirect)); 10862306a36Sopenharmony_ci local_irq_restore(flags); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci switch (return_code) { 11162306a36Sopenharmony_ci case 0: 11262306a36Sopenharmony_ci return address + entry; 11362306a36Sopenharmony_ci case 0x80: /* Not present */ 11462306a36Sopenharmony_ci printk(KERN_WARNING "bios32_service(0x%lx): not present\n", service); 11562306a36Sopenharmony_ci return 0; 11662306a36Sopenharmony_ci default: /* Shouldn't happen */ 11762306a36Sopenharmony_ci printk(KERN_WARNING "bios32_service(0x%lx): returned 0x%x -- BIOS bug!\n", 11862306a36Sopenharmony_ci service, return_code); 11962306a36Sopenharmony_ci return 0; 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic struct { 12462306a36Sopenharmony_ci unsigned long address; 12562306a36Sopenharmony_ci unsigned short segment; 12662306a36Sopenharmony_ci} pci_indirect __ro_after_init = { 12762306a36Sopenharmony_ci .address = 0, 12862306a36Sopenharmony_ci .segment = __KERNEL_CS, 12962306a36Sopenharmony_ci}; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic int pci_bios_present __ro_after_init; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic int __init check_pcibios(void) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci u32 signature, eax, ebx, ecx; 13662306a36Sopenharmony_ci u8 status, major_ver, minor_ver, hw_mech; 13762306a36Sopenharmony_ci unsigned long flags, pcibios_entry; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if ((pcibios_entry = bios32_service(PCI_SERVICE))) { 14062306a36Sopenharmony_ci pci_indirect.address = pcibios_entry + PAGE_OFFSET; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci local_irq_save(flags); 14362306a36Sopenharmony_ci __asm__( 14462306a36Sopenharmony_ci "lcall *(%%edi); cld\n\t" 14562306a36Sopenharmony_ci "jc 1f\n\t" 14662306a36Sopenharmony_ci "xor %%ah, %%ah\n" 14762306a36Sopenharmony_ci "1:" 14862306a36Sopenharmony_ci : "=d" (signature), 14962306a36Sopenharmony_ci "=a" (eax), 15062306a36Sopenharmony_ci "=b" (ebx), 15162306a36Sopenharmony_ci "=c" (ecx) 15262306a36Sopenharmony_ci : "1" (PCIBIOS_PCI_BIOS_PRESENT), 15362306a36Sopenharmony_ci "D" (&pci_indirect) 15462306a36Sopenharmony_ci : "memory"); 15562306a36Sopenharmony_ci local_irq_restore(flags); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci status = (eax >> 8) & 0xff; 15862306a36Sopenharmony_ci hw_mech = eax & 0xff; 15962306a36Sopenharmony_ci major_ver = (ebx >> 8) & 0xff; 16062306a36Sopenharmony_ci minor_ver = ebx & 0xff; 16162306a36Sopenharmony_ci if (pcibios_last_bus < 0) 16262306a36Sopenharmony_ci pcibios_last_bus = ecx & 0xff; 16362306a36Sopenharmony_ci DBG("PCI: BIOS probe returned s=%02x hw=%02x ver=%02x.%02x l=%02x\n", 16462306a36Sopenharmony_ci status, hw_mech, major_ver, minor_ver, pcibios_last_bus); 16562306a36Sopenharmony_ci if (status || signature != PCI_SIGNATURE) { 16662306a36Sopenharmony_ci printk (KERN_ERR "PCI: BIOS BUG #%x[%08x] found\n", 16762306a36Sopenharmony_ci status, signature); 16862306a36Sopenharmony_ci return 0; 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci printk(KERN_INFO "PCI: PCI BIOS revision %x.%02x entry at 0x%lx, last bus=%d\n", 17162306a36Sopenharmony_ci major_ver, minor_ver, pcibios_entry, pcibios_last_bus); 17262306a36Sopenharmony_ci#ifdef CONFIG_PCI_DIRECT 17362306a36Sopenharmony_ci if (!(hw_mech & PCIBIOS_HW_TYPE1)) 17462306a36Sopenharmony_ci pci_probe &= ~PCI_PROBE_CONF1; 17562306a36Sopenharmony_ci if (!(hw_mech & PCIBIOS_HW_TYPE2)) 17662306a36Sopenharmony_ci pci_probe &= ~PCI_PROBE_CONF2; 17762306a36Sopenharmony_ci#endif 17862306a36Sopenharmony_ci return 1; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci return 0; 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic int pci_bios_read(unsigned int seg, unsigned int bus, 18462306a36Sopenharmony_ci unsigned int devfn, int reg, int len, u32 *value) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci unsigned long result = 0; 18762306a36Sopenharmony_ci unsigned long flags; 18862306a36Sopenharmony_ci unsigned long bx = (bus << 8) | devfn; 18962306a36Sopenharmony_ci u16 number = 0, mask = 0; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci WARN_ON(seg); 19262306a36Sopenharmony_ci if (!value || (bus > 255) || (devfn > 255) || (reg > 255)) 19362306a36Sopenharmony_ci return -EINVAL; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci raw_spin_lock_irqsave(&pci_config_lock, flags); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci switch (len) { 19862306a36Sopenharmony_ci case 1: 19962306a36Sopenharmony_ci number = PCIBIOS_READ_CONFIG_BYTE; 20062306a36Sopenharmony_ci mask = 0xff; 20162306a36Sopenharmony_ci break; 20262306a36Sopenharmony_ci case 2: 20362306a36Sopenharmony_ci number = PCIBIOS_READ_CONFIG_WORD; 20462306a36Sopenharmony_ci mask = 0xffff; 20562306a36Sopenharmony_ci break; 20662306a36Sopenharmony_ci case 4: 20762306a36Sopenharmony_ci number = PCIBIOS_READ_CONFIG_DWORD; 20862306a36Sopenharmony_ci break; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci __asm__("lcall *(%%esi); cld\n\t" 21262306a36Sopenharmony_ci "jc 1f\n\t" 21362306a36Sopenharmony_ci "xor %%ah, %%ah\n" 21462306a36Sopenharmony_ci "1:" 21562306a36Sopenharmony_ci : "=c" (*value), 21662306a36Sopenharmony_ci "=a" (result) 21762306a36Sopenharmony_ci : "1" (number), 21862306a36Sopenharmony_ci "b" (bx), 21962306a36Sopenharmony_ci "D" ((long)reg), 22062306a36Sopenharmony_ci "S" (&pci_indirect)); 22162306a36Sopenharmony_ci /* 22262306a36Sopenharmony_ci * Zero-extend the result beyond 8 or 16 bits, do not trust the 22362306a36Sopenharmony_ci * BIOS having done it: 22462306a36Sopenharmony_ci */ 22562306a36Sopenharmony_ci if (mask) 22662306a36Sopenharmony_ci *value &= mask; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&pci_config_lock, flags); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci return (int)((result & 0xff00) >> 8); 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic int pci_bios_write(unsigned int seg, unsigned int bus, 23462306a36Sopenharmony_ci unsigned int devfn, int reg, int len, u32 value) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci unsigned long result = 0; 23762306a36Sopenharmony_ci unsigned long flags; 23862306a36Sopenharmony_ci unsigned long bx = (bus << 8) | devfn; 23962306a36Sopenharmony_ci u16 number = 0; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci WARN_ON(seg); 24262306a36Sopenharmony_ci if ((bus > 255) || (devfn > 255) || (reg > 255)) 24362306a36Sopenharmony_ci return -EINVAL; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci raw_spin_lock_irqsave(&pci_config_lock, flags); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci switch (len) { 24862306a36Sopenharmony_ci case 1: 24962306a36Sopenharmony_ci number = PCIBIOS_WRITE_CONFIG_BYTE; 25062306a36Sopenharmony_ci break; 25162306a36Sopenharmony_ci case 2: 25262306a36Sopenharmony_ci number = PCIBIOS_WRITE_CONFIG_WORD; 25362306a36Sopenharmony_ci break; 25462306a36Sopenharmony_ci case 4: 25562306a36Sopenharmony_ci number = PCIBIOS_WRITE_CONFIG_DWORD; 25662306a36Sopenharmony_ci break; 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci __asm__("lcall *(%%esi); cld\n\t" 26062306a36Sopenharmony_ci "jc 1f\n\t" 26162306a36Sopenharmony_ci "xor %%ah, %%ah\n" 26262306a36Sopenharmony_ci "1:" 26362306a36Sopenharmony_ci : "=a" (result) 26462306a36Sopenharmony_ci : "0" (number), 26562306a36Sopenharmony_ci "c" (value), 26662306a36Sopenharmony_ci "b" (bx), 26762306a36Sopenharmony_ci "D" ((long)reg), 26862306a36Sopenharmony_ci "S" (&pci_indirect)); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&pci_config_lock, flags); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci return (int)((result & 0xff00) >> 8); 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci/* 27762306a36Sopenharmony_ci * Function table for BIOS32 access 27862306a36Sopenharmony_ci */ 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic const struct pci_raw_ops pci_bios_access = { 28162306a36Sopenharmony_ci .read = pci_bios_read, 28262306a36Sopenharmony_ci .write = pci_bios_write 28362306a36Sopenharmony_ci}; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci/* 28662306a36Sopenharmony_ci * Try to find PCI BIOS. 28762306a36Sopenharmony_ci */ 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic const struct pci_raw_ops *__init pci_find_bios(void) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci union bios32 *check; 29262306a36Sopenharmony_ci unsigned char sum; 29362306a36Sopenharmony_ci int i, length; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci /* 29662306a36Sopenharmony_ci * Follow the standard procedure for locating the BIOS32 Service 29762306a36Sopenharmony_ci * directory by scanning the permissible address range from 29862306a36Sopenharmony_ci * 0xe0000 through 0xfffff for a valid BIOS32 structure. 29962306a36Sopenharmony_ci */ 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci for (check = (union bios32 *) __va(0xe0000); 30262306a36Sopenharmony_ci check <= (union bios32 *) __va(0xffff0); 30362306a36Sopenharmony_ci ++check) { 30462306a36Sopenharmony_ci long sig; 30562306a36Sopenharmony_ci if (get_kernel_nofault(sig, &check->fields.signature)) 30662306a36Sopenharmony_ci continue; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci if (check->fields.signature != BIOS32_SIGNATURE) 30962306a36Sopenharmony_ci continue; 31062306a36Sopenharmony_ci length = check->fields.length * 16; 31162306a36Sopenharmony_ci if (!length) 31262306a36Sopenharmony_ci continue; 31362306a36Sopenharmony_ci sum = 0; 31462306a36Sopenharmony_ci for (i = 0; i < length ; ++i) 31562306a36Sopenharmony_ci sum += check->chars[i]; 31662306a36Sopenharmony_ci if (sum != 0) 31762306a36Sopenharmony_ci continue; 31862306a36Sopenharmony_ci if (check->fields.revision != 0) { 31962306a36Sopenharmony_ci printk("PCI: unsupported BIOS32 revision %d at 0x%p\n", 32062306a36Sopenharmony_ci check->fields.revision, check); 32162306a36Sopenharmony_ci continue; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci DBG("PCI: BIOS32 Service Directory structure at 0x%p\n", check); 32462306a36Sopenharmony_ci if (check->fields.entry >= 0x100000) { 32562306a36Sopenharmony_ci printk("PCI: BIOS32 entry (0x%p) in high memory, " 32662306a36Sopenharmony_ci "cannot use.\n", check); 32762306a36Sopenharmony_ci return NULL; 32862306a36Sopenharmony_ci } else { 32962306a36Sopenharmony_ci unsigned long bios32_entry = check->fields.entry; 33062306a36Sopenharmony_ci DBG("PCI: BIOS32 Service Directory entry at 0x%lx\n", 33162306a36Sopenharmony_ci bios32_entry); 33262306a36Sopenharmony_ci bios32_indirect.address = bios32_entry + PAGE_OFFSET; 33362306a36Sopenharmony_ci set_bios_x(); 33462306a36Sopenharmony_ci if (check_pcibios()) 33562306a36Sopenharmony_ci return &pci_bios_access; 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci break; /* Hopefully more than one BIOS32 cannot happen... */ 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci return NULL; 34162306a36Sopenharmony_ci} 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci/* 34462306a36Sopenharmony_ci * BIOS Functions for IRQ Routing 34562306a36Sopenharmony_ci */ 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistruct irq_routing_options { 34862306a36Sopenharmony_ci u16 size; 34962306a36Sopenharmony_ci struct irq_info *table; 35062306a36Sopenharmony_ci u16 segment; 35162306a36Sopenharmony_ci} __attribute__((packed)); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_cistruct irq_routing_table * pcibios_get_irq_routing_table(void) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci struct irq_routing_options opt; 35662306a36Sopenharmony_ci struct irq_routing_table *rt = NULL; 35762306a36Sopenharmony_ci int ret, map; 35862306a36Sopenharmony_ci unsigned long page; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci if (!pci_bios_present) 36162306a36Sopenharmony_ci return NULL; 36262306a36Sopenharmony_ci page = __get_free_page(GFP_KERNEL); 36362306a36Sopenharmony_ci if (!page) 36462306a36Sopenharmony_ci return NULL; 36562306a36Sopenharmony_ci opt.table = (struct irq_info *) page; 36662306a36Sopenharmony_ci opt.size = PAGE_SIZE; 36762306a36Sopenharmony_ci opt.segment = __KERNEL_DS; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci DBG("PCI: Fetching IRQ routing table... "); 37062306a36Sopenharmony_ci __asm__("push %%es\n\t" 37162306a36Sopenharmony_ci "push %%ds\n\t" 37262306a36Sopenharmony_ci "pop %%es\n\t" 37362306a36Sopenharmony_ci "lcall *(%%esi); cld\n\t" 37462306a36Sopenharmony_ci "pop %%es\n\t" 37562306a36Sopenharmony_ci "jc 1f\n\t" 37662306a36Sopenharmony_ci "xor %%ah, %%ah\n" 37762306a36Sopenharmony_ci "1:" 37862306a36Sopenharmony_ci : "=a" (ret), 37962306a36Sopenharmony_ci "=b" (map), 38062306a36Sopenharmony_ci "=m" (opt) 38162306a36Sopenharmony_ci : "0" (PCIBIOS_GET_ROUTING_OPTIONS), 38262306a36Sopenharmony_ci "1" (0), 38362306a36Sopenharmony_ci "D" ((long) &opt), 38462306a36Sopenharmony_ci "S" (&pci_indirect), 38562306a36Sopenharmony_ci "m" (opt) 38662306a36Sopenharmony_ci : "memory"); 38762306a36Sopenharmony_ci DBG("OK ret=%d, size=%d, map=%x\n", ret, opt.size, map); 38862306a36Sopenharmony_ci if (ret & 0xff00) 38962306a36Sopenharmony_ci printk(KERN_ERR "PCI: Error %02x when fetching IRQ routing table.\n", (ret >> 8) & 0xff); 39062306a36Sopenharmony_ci else if (opt.size) { 39162306a36Sopenharmony_ci rt = kmalloc(sizeof(struct irq_routing_table) + opt.size, GFP_KERNEL); 39262306a36Sopenharmony_ci if (rt) { 39362306a36Sopenharmony_ci memset(rt, 0, sizeof(struct irq_routing_table)); 39462306a36Sopenharmony_ci rt->size = opt.size + sizeof(struct irq_routing_table); 39562306a36Sopenharmony_ci rt->exclusive_irqs = map; 39662306a36Sopenharmony_ci memcpy(rt->slots, (void *) page, opt.size); 39762306a36Sopenharmony_ci printk(KERN_INFO "PCI: Using BIOS Interrupt Routing Table\n"); 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci free_page(page); 40162306a36Sopenharmony_ci return rt; 40262306a36Sopenharmony_ci} 40362306a36Sopenharmony_ciEXPORT_SYMBOL(pcibios_get_irq_routing_table); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ciint pcibios_set_irq_routing(struct pci_dev *dev, int pin, int irq) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci int ret; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci __asm__("lcall *(%%esi); cld\n\t" 41062306a36Sopenharmony_ci "jc 1f\n\t" 41162306a36Sopenharmony_ci "xor %%ah, %%ah\n" 41262306a36Sopenharmony_ci "1:" 41362306a36Sopenharmony_ci : "=a" (ret) 41462306a36Sopenharmony_ci : "0" (PCIBIOS_SET_PCI_HW_INT), 41562306a36Sopenharmony_ci "b" ((dev->bus->number << 8) | dev->devfn), 41662306a36Sopenharmony_ci "c" ((irq << 8) | (pin + 10)), 41762306a36Sopenharmony_ci "S" (&pci_indirect)); 41862306a36Sopenharmony_ci return !(ret & 0xff00); 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ciEXPORT_SYMBOL(pcibios_set_irq_routing); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_civoid __init pci_pcbios_init(void) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci if ((pci_probe & PCI_PROBE_BIOS) 42562306a36Sopenharmony_ci && ((raw_pci_ops = pci_find_bios()))) { 42662306a36Sopenharmony_ci pci_bios_present = 1; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 430