X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Fia64%2Fkernel%2Fefi.c;fp=arch%2Fia64%2Fkernel%2Fefi.c;h=9990320b6f9a7ee7edad9c41f988a4681c7e2018;hb=64ba3f394c830ec48a1c31b53dcae312c56f1604;hp=bb8770a177b5f3c6eac55a11e83ac9cbc9d389e9;hpb=be1e6109ac94a859551f8e1774eb9a8469fe055c;p=linux-2.6.git diff --git a/arch/ia64/kernel/efi.c b/arch/ia64/kernel/efi.c index bb8770a17..9990320b6 100644 --- a/arch/ia64/kernel/efi.c +++ b/arch/ia64/kernel/efi.c @@ -8,8 +8,6 @@ * Copyright (C) 1999-2003 Hewlett-Packard Co. * David Mosberger-Tang * Stephane Eranian - * (c) Copyright 2006 Hewlett-Packard Development Company, L.P. - * Bjorn Helgaas * * All EFI Runtime Services are not implemented yet as EFI only * supports physical mode addressing on SoftSDV. This is to be fixed @@ -20,6 +18,7 @@ * Goutham Rao: * Skip non-WB memory and ignore empty memory ranges. */ +#include #include #include #include @@ -459,33 +458,24 @@ efi_init (void) printk(KERN_INFO "EFI v%u.%.02u by %s:", efi.systab->hdr.revision >> 16, efi.systab->hdr.revision & 0xffff, vendor); - efi.mps = EFI_INVALID_TABLE_ADDR; - efi.acpi = EFI_INVALID_TABLE_ADDR; - efi.acpi20 = EFI_INVALID_TABLE_ADDR; - efi.smbios = EFI_INVALID_TABLE_ADDR; - efi.sal_systab = EFI_INVALID_TABLE_ADDR; - efi.boot_info = EFI_INVALID_TABLE_ADDR; - efi.hcdp = EFI_INVALID_TABLE_ADDR; - efi.uga = EFI_INVALID_TABLE_ADDR; - for (i = 0; i < (int) efi.systab->nr_tables; i++) { if (efi_guidcmp(config_tables[i].guid, MPS_TABLE_GUID) == 0) { - efi.mps = config_tables[i].table; + efi.mps = __va(config_tables[i].table); printk(" MPS=0x%lx", config_tables[i].table); } else if (efi_guidcmp(config_tables[i].guid, ACPI_20_TABLE_GUID) == 0) { - efi.acpi20 = config_tables[i].table; + efi.acpi20 = __va(config_tables[i].table); printk(" ACPI 2.0=0x%lx", config_tables[i].table); } else if (efi_guidcmp(config_tables[i].guid, ACPI_TABLE_GUID) == 0) { - efi.acpi = config_tables[i].table; + efi.acpi = __va(config_tables[i].table); printk(" ACPI=0x%lx", config_tables[i].table); } else if (efi_guidcmp(config_tables[i].guid, SMBIOS_TABLE_GUID) == 0) { - efi.smbios = config_tables[i].table; + efi.smbios = __va(config_tables[i].table); printk(" SMBIOS=0x%lx", config_tables[i].table); } else if (efi_guidcmp(config_tables[i].guid, SAL_SYSTEM_TABLE_GUID) == 0) { - efi.sal_systab = config_tables[i].table; + efi.sal_systab = __va(config_tables[i].table); printk(" SALsystab=0x%lx", config_tables[i].table); } else if (efi_guidcmp(config_tables[i].guid, HCDP_TABLE_GUID) == 0) { - efi.hcdp = config_tables[i].table; + efi.hcdp = __va(config_tables[i].table); printk(" HCDP=0x%lx", config_tables[i].table); } } @@ -623,20 +613,28 @@ efi_get_iobase (void) return 0; } -static struct kern_memdesc * -kern_memory_descriptor (unsigned long phys_addr) +static efi_memory_desc_t * +efi_memory_descriptor (unsigned long phys_addr) { - struct kern_memdesc *md; + void *efi_map_start, *efi_map_end, *p; + efi_memory_desc_t *md; + u64 efi_desc_size; + + efi_map_start = __va(ia64_boot_param->efi_memmap); + efi_map_end = efi_map_start + ia64_boot_param->efi_memmap_size; + efi_desc_size = ia64_boot_param->efi_memdesc_size; - for (md = kern_memmap; md->start != ~0UL; md++) { - if (phys_addr - md->start < (md->num_pages << EFI_PAGE_SHIFT)) + for (p = efi_map_start; p < efi_map_end; p += efi_desc_size) { + md = p; + + if (phys_addr - md->phys_addr < (md->num_pages << EFI_PAGE_SHIFT)) return md; } - return NULL; + return 0; } -static efi_memory_desc_t * -efi_memory_descriptor (unsigned long phys_addr) +static int +efi_memmap_has_mmio (void) { void *efi_map_start, *efi_map_end, *p; efi_memory_desc_t *md; @@ -649,10 +647,10 @@ efi_memory_descriptor (unsigned long phys_addr) for (p = efi_map_start; p < efi_map_end; p += efi_desc_size) { md = p; - if (phys_addr - md->phys_addr < (md->num_pages << EFI_PAGE_SHIFT)) - return md; + if (md->type == EFI_MEMORY_MAPPED_IO) + return 1; } - return NULL; + return 0; } u32 @@ -676,125 +674,72 @@ efi_mem_attributes (unsigned long phys_addr) } EXPORT_SYMBOL(efi_mem_attributes); -u64 -efi_mem_attribute (unsigned long phys_addr, unsigned long size) +/* + * Determines whether the memory at phys_addr supports the desired + * attribute (WB, UC, etc). If this returns 1, the caller can safely + * access *size bytes at phys_addr with the specified attribute. + */ +static int +efi_mem_attribute_range (unsigned long phys_addr, unsigned long *size, u64 attr) { - unsigned long end = phys_addr + size; efi_memory_desc_t *md = efi_memory_descriptor(phys_addr); - u64 attr; + unsigned long md_end; - if (!md) + if (!md || (md->attribute & attr) != attr) return 0; - /* - * EFI_MEMORY_RUNTIME is not a memory attribute; it just tells - * the kernel that firmware needs this region mapped. - */ - attr = md->attribute & ~EFI_MEMORY_RUNTIME; do { - unsigned long md_end = efi_md_end(md); - - if (end <= md_end) - return attr; + md_end = efi_md_end(md); + if (phys_addr + *size <= md_end) + return 1; md = efi_memory_descriptor(md_end); - if (!md || (md->attribute & ~EFI_MEMORY_RUNTIME) != attr) - return 0; - } while (md); - return 0; -} - -u64 -kern_mem_attribute (unsigned long phys_addr, unsigned long size) -{ - unsigned long end = phys_addr + size; - struct kern_memdesc *md; - u64 attr; - - /* - * This is a hack for ioremap calls before we set up kern_memmap. - * Maybe we should do efi_memmap_init() earlier instead. - */ - if (!kern_memmap) { - attr = efi_mem_attribute(phys_addr, size); - if (attr & EFI_MEMORY_WB) - return EFI_MEMORY_WB; - return 0; - } - - md = kern_memory_descriptor(phys_addr); - if (!md) - return 0; - - attr = md->attribute; - do { - unsigned long md_end = kmd_end(md); - - if (end <= md_end) - return attr; - - md = kern_memory_descriptor(md_end); - if (!md || md->attribute != attr) - return 0; + if (!md || (md->attribute & attr) != attr) { + *size = md_end - phys_addr; + return 1; + } } while (md); return 0; } -EXPORT_SYMBOL(kern_mem_attribute); +/* + * For /dev/mem, we only allow read & write system calls to access + * write-back memory, because read & write don't allow the user to + * control access size. + */ int -valid_phys_addr_range (unsigned long phys_addr, unsigned long size) +valid_phys_addr_range (unsigned long phys_addr, unsigned long *size) { - u64 attr; - - /* - * /dev/mem reads and writes use copy_to_user(), which implicitly - * uses a granule-sized kernel identity mapping. It's really - * only safe to do this for regions in kern_memmap. For more - * details, see Documentation/ia64/aliasing.txt. - */ - attr = kern_mem_attribute(phys_addr, size); - if (attr & EFI_MEMORY_WB || attr & EFI_MEMORY_UC) - return 1; - return 0; + return efi_mem_attribute_range(phys_addr, size, EFI_MEMORY_WB); } +/* + * We allow mmap of anything in the EFI memory map that supports + * either write-back or uncacheable access. For uncacheable regions, + * the supported access sizes are system-dependent, and the user is + * responsible for using the correct size. + * + * Note that this doesn't currently allow access to hot-added memory, + * because that doesn't appear in the boot-time EFI memory map. + */ int -valid_mmap_phys_addr_range (unsigned long pfn, unsigned long size) +valid_mmap_phys_addr_range (unsigned long phys_addr, unsigned long *size) { - /* - * MMIO regions are often missing from the EFI memory map. - * We must allow mmap of them for programs like X, so we - * currently can't do any useful validation. - */ - return 1; -} - -pgprot_t -phys_mem_access_prot(struct file *file, unsigned long pfn, unsigned long size, - pgprot_t vma_prot) -{ - unsigned long phys_addr = pfn << PAGE_SHIFT; - u64 attr; + if (efi_mem_attribute_range(phys_addr, size, EFI_MEMORY_WB)) + return 1; - /* - * For /dev/mem mmap, we use user mappings, but if the region is - * in kern_memmap (and hence may be covered by a kernel mapping), - * we must use the same attribute as the kernel mapping. - */ - attr = kern_mem_attribute(phys_addr, size); - if (attr & EFI_MEMORY_WB) - return pgprot_cacheable(vma_prot); - else if (attr & EFI_MEMORY_UC) - return pgprot_noncached(vma_prot); + if (efi_mem_attribute_range(phys_addr, size, EFI_MEMORY_UC)) + return 1; /* - * Some chipsets don't support UC access to memory. If - * WB is supported, we prefer that. + * Some firmware doesn't report MMIO regions in the EFI memory map. + * The Intel BigSur (a.k.a. HP i2000) has this problem. In this + * case, we can't use the EFI memory map to validate mmap requests. */ - if (efi_mem_attribute(phys_addr, size) & EFI_MEMORY_WB) - return pgprot_cacheable(vma_prot); + if (!efi_memmap_has_mmio()) + return 1; - return pgprot_noncached(vma_prot); + return 0; } int __init @@ -923,7 +868,7 @@ find_memmap_space (void) void efi_memmap_init(unsigned long *s, unsigned long *e) { - struct kern_memdesc *k, *prev = NULL; + struct kern_memdesc *k, *prev = 0; u64 contig_low=0, contig_high=0; u64 as, ae, lim; void *efi_map_start, *efi_map_end, *p, *q;