Pull kmalloc into release branch
[pandora-kernel.git] / arch / ia64 / kernel / efi.c
index a3aa45c..b13c055 100644 (file)
@@ -8,6 +8,8 @@
  * Copyright (C) 1999-2003 Hewlett-Packard Co.
  *     David Mosberger-Tang <davidm@hpl.hp.com>
  *     Stephane Eranian <eranian@hpl.hp.com>
+ * (c) Copyright 2006 Hewlett-Packard Development Company, L.P.
+ *     Bjorn Helgaas <bjorn.helgaas@hp.com>
  *
  * All EFI Runtime Services are not implemented yet as EFI only
  * supports physical mode addressing on SoftSDV. This is to be fixed
@@ -18,7 +20,6 @@
  * Goutham Rao: <goutham.rao@intel.com>
  *     Skip non-WB memory and ignore empty memory ranges.
  */
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
@@ -247,6 +248,32 @@ typedef struct kern_memdesc {
 
 static kern_memdesc_t *kern_memmap;
 
+#define efi_md_size(md)        (md->num_pages << EFI_PAGE_SHIFT)
+
+static inline u64
+kmd_end(kern_memdesc_t *kmd)
+{
+       return (kmd->start + (kmd->num_pages << EFI_PAGE_SHIFT));
+}
+
+static inline u64
+efi_md_end(efi_memory_desc_t *md)
+{
+       return (md->phys_addr + efi_md_size(md));
+}
+
+static inline int
+efi_wb(efi_memory_desc_t *md)
+{
+       return (md->attribute & EFI_MEMORY_WB);
+}
+
+static inline int
+efi_uc(efi_memory_desc_t *md)
+{
+       return (md->attribute & EFI_MEMORY_UC);
+}
+
 static void
 walk (efi_freemem_callback_t callback, void *arg, u64 attr)
 {
@@ -384,24 +411,16 @@ efi_init (void)
        efi_config_table_t *config_tables;
        efi_char16_t *c16;
        u64 efi_desc_size;
-       char *cp, *end, vendor[100] = "unknown";
+       char *cp, vendor[100] = "unknown";
        extern char saved_command_line[];
        int i;
 
        /* it's too early to be able to use the standard kernel command line support... */
        for (cp = saved_command_line; *cp; ) {
                if (memcmp(cp, "mem=", 4) == 0) {
-                       cp += 4;
-                       mem_limit = memparse(cp, &end);
-                       if (end != cp)
-                               break;
-                       cp = end;
+                       mem_limit = memparse(cp + 4, &cp);
                } else if (memcmp(cp, "max_addr=", 9) == 0) {
-                       cp += 9;
-                       max_addr = GRANULEROUNDDOWN(memparse(cp, &end));
-                       if (end != cp)
-                               break;
-                       cp = end;
+                       max_addr = GRANULEROUNDDOWN(memparse(cp + 9, &cp));
                } else {
                        while (*cp != ' ' && *cp)
                                ++cp;
@@ -432,7 +451,7 @@ efi_init (void)
        /* Show what we know for posterity */
        c16 = __va(efi.systab->fw_vendor);
        if (c16) {
-               for (i = 0;i < (int) sizeof(vendor) && *c16; ++i)
+               for (i = 0;i < (int) sizeof(vendor) - 1 && *c16; ++i)
                        vendor[i] = *c16++;
                vendor[i] = '\0';
        }
@@ -440,24 +459,33 @@ 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 = __va(config_tables[i].table);
+                       efi.mps = 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 = __va(config_tables[i].table);
+                       efi.acpi20 = 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 = __va(config_tables[i].table);
+                       efi.acpi = 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 = __va(config_tables[i].table);
+                       efi.smbios = 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 = __va(config_tables[i].table);
+                       efi.sal_systab = 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 = __va(config_tables[i].table);
+                       efi.hcdp = config_tables[i].table;
                        printk(" HCDP=0x%lx", config_tables[i].table);
                }
        }
@@ -595,8 +623,20 @@ efi_get_iobase (void)
        return 0;
 }
 
-u32
-efi_mem_type (unsigned long phys_addr)
+static struct kern_memdesc *
+kern_memory_descriptor (unsigned long phys_addr)
+{
+       struct kern_memdesc *md;
+
+       for (md = kern_memmap; md->start != ~0UL; md++) {
+               if (phys_addr - md->start < (md->num_pages << EFI_PAGE_SHIFT))
+                        return md;
+       }
+       return 0;
+}
+
+static efi_memory_desc_t *
+efi_memory_descriptor (unsigned long phys_addr)
 {
        void *efi_map_start, *efi_map_end, *p;
        efi_memory_desc_t *md;
@@ -610,56 +650,151 @@ efi_mem_type (unsigned long phys_addr)
                md = p;
 
                if (phys_addr - md->phys_addr < (md->num_pages << EFI_PAGE_SHIFT))
-                        return md->type;
+                        return md;
        }
        return 0;
 }
 
+u32
+efi_mem_type (unsigned long phys_addr)
+{
+       efi_memory_desc_t *md = efi_memory_descriptor(phys_addr);
+
+       if (md)
+               return md->type;
+       return 0;
+}
+
 u64
 efi_mem_attributes (unsigned long phys_addr)
 {
-       void *efi_map_start, *efi_map_end, *p;
-       efi_memory_desc_t *md;
-       u64 efi_desc_size;
+       efi_memory_desc_t *md = efi_memory_descriptor(phys_addr);
 
-       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;
+       if (md)
+               return md->attribute;
+       return 0;
+}
+EXPORT_SYMBOL(efi_mem_attributes);
 
-       for (p = efi_map_start; p < efi_map_end; p += efi_desc_size) {
-               md = p;
+u64
+efi_mem_attribute (unsigned long phys_addr, unsigned long size)
+{
+       unsigned long end = phys_addr + size;
+       efi_memory_desc_t *md = efi_memory_descriptor(phys_addr);
+       u64 attr;
 
-               if (phys_addr - md->phys_addr < (md->num_pages << EFI_PAGE_SHIFT))
-                       return md->attribute;
+       if (!md)
+               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 = 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;
+       } while (md);
        return 0;
 }
-EXPORT_SYMBOL(efi_mem_attributes);
+EXPORT_SYMBOL(kern_mem_attribute);
 
 int
-valid_phys_addr_range (unsigned long phys_addr, unsigned long *size)
+valid_phys_addr_range (unsigned long phys_addr, unsigned long size)
 {
-       void *efi_map_start, *efi_map_end, *p;
-       efi_memory_desc_t *md;
-       u64 efi_desc_size;
+       u64 attr;
 
-       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;
+       /*
+        * /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;
+}
 
-       for (p = efi_map_start; p < efi_map_end; p += efi_desc_size) {
-               md = p;
+int
+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;
+}
 
-               if (phys_addr - md->phys_addr < (md->num_pages << EFI_PAGE_SHIFT)) {
-                       if (!(md->attribute & EFI_MEMORY_WB))
-                               return 0;
+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 (*size > md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - phys_addr)
-                               *size = md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - phys_addr;
-                       return 1;
-               }
-       }
-       return 0;
+       /*
+        * 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);
+
+       /*
+        * Some chipsets don't support UC access to memory.  If
+        * WB is supported, we prefer that.
+        */
+       if (efi_mem_attribute(phys_addr, size) & EFI_MEMORY_WB)
+               return pgprot_cacheable(vma_prot);
+
+       return pgprot_noncached(vma_prot);
 }
 
 int __init
@@ -707,32 +842,6 @@ efi_uart_console_only(void)
        return 0;
 }
 
-#define efi_md_size(md)        (md->num_pages << EFI_PAGE_SHIFT)
-
-static inline u64
-kmd_end(kern_memdesc_t *kmd)
-{
-       return (kmd->start + (kmd->num_pages << EFI_PAGE_SHIFT));
-}
-
-static inline u64
-efi_md_end(efi_memory_desc_t *md)
-{
-       return (md->phys_addr + efi_md_size(md));
-}
-
-static inline int
-efi_wb(efi_memory_desc_t *md)
-{
-       return (md->attribute & EFI_MEMORY_WB);
-}
-
-static inline int
-efi_uc(efi_memory_desc_t *md)
-{
-       return (md->attribute & EFI_MEMORY_UC);
-}
-
 /*
  * Look for the first granule aligned memory descriptor memory
  * that is big enough to hold EFI memory map. Make sure this