]> bbs.cooldavid.org Git - net-next-2.6.git/commitdiff
x86, olpc: Add support for calling into OpenFirmware
authorAndres Salomon <dilinger@queued.net>
Fri, 18 Jun 2010 21:46:53 +0000 (17:46 -0400)
committerH. Peter Anvin <hpa@linux.intel.com>
Fri, 18 Jun 2010 21:54:36 +0000 (14:54 -0700)
Add support for saving OFW's cif, and later calling into it to run OFW
commands.  OFW remains resident in memory, living within virtual range
0xff800000 - 0xffc00000.  A single page directory entry points to the
pgdir that OFW actually uses, so rather than saving the entire page
table, we grab and install that one entry permanently in the kernel's
page table.

This is currently only used by the OLPC XO.  Note that this particular
calling convention breaks PAE and PAT, and so cannot be used on newer
x86 hardware.

Signed-off-by: Andres Salomon <dilinger@queued.net>
LKML-Reference: <20100618174653.7755a39a@dev.queued.net>
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
Documentation/x86/zero-page.txt
arch/x86/Kconfig
arch/x86/include/asm/bootparam.h
arch/x86/include/asm/olpc_ofw.h [new file with mode: 0644]
arch/x86/kernel/Makefile
arch/x86/kernel/head_32.S
arch/x86/kernel/olpc.c
arch/x86/kernel/olpc_ofw.c [new file with mode: 0644]
arch/x86/kernel/setup.c

index feb37e177010517f8f1f1d1edee3a96ed77f793f..cf5437deda81a003864f9e8a5f4e533d276a189d 100644 (file)
@@ -18,6 +18,7 @@ Offset        Proto   Name            Meaning
 080/010        ALL     hd0_info        hd0 disk parameter, OBSOLETE!!
 090/010        ALL     hd1_info        hd1 disk parameter, OBSOLETE!!
 0A0/010        ALL     sys_desc_table  System description table (struct sys_desc_table)
+0B0/010        ALL     olpc_ofw_header OLPC's OpenFirmware CIF and friends
 140/080        ALL     edid_info       Video mode setup (struct edid_info)
 1C0/020        ALL     efi_info        EFI 32 information (struct efi_info)
 1E0/004        ALL     alk_mem_k       Alternative mem check, in KB
index dcb0593b4a66348a204a44fee3366c5da0848ad1..71c194db2e0ad4b6598b65f3f2b60665ae7f10e0 100644 (file)
@@ -2062,6 +2062,15 @@ config OLPC
          Add support for detecting the unique features of the OLPC
          XO hardware.
 
+config OLPC_OPENFIRMWARE
+       bool "Support for OLPC's Open Firmware"
+       depends on !X86_64 && !X86_PAE
+       default y if OLPC
+       help
+         This option adds support for the implementation of Open Firmware
+         that is used on the OLPC XO-1 Children's Machine.
+         If unsure, say N here.
+
 endif # X86_32
 
 config K8_NB
index 6be33d83c7168cd9fea62149e7124310fbacbb02..8e6218550e774b56fd30f3171bc163bfb511b947 100644 (file)
@@ -70,6 +70,14 @@ struct sys_desc_table {
        __u8  table[14];
 };
 
+/* Gleaned from OFW's set-parameters in cpu/x86/pc/linux.fth */
+struct olpc_ofw_header {
+       __u32 ofw_magic;        /* OFW signature */
+       __u32 ofw_version;
+       __u32 cif_handler;      /* callback into OFW */
+       __u32 irq_desc_table;
+} __attribute__((packed));
+
 struct efi_info {
        __u32 efi_loader_signature;
        __u32 efi_systab;
@@ -92,7 +100,8 @@ struct boot_params {
        __u8  hd0_info[16];     /* obsolete! */         /* 0x080 */
        __u8  hd1_info[16];     /* obsolete! */         /* 0x090 */
        struct sys_desc_table sys_desc_table;           /* 0x0a0 */
-       __u8  _pad4[144];                               /* 0x0b0 */
+       struct olpc_ofw_header olpc_ofw_header;         /* 0x0b0 */
+       __u8  _pad4[128];                               /* 0x0c0 */
        struct edid_info edid_info;                     /* 0x140 */
        struct efi_info efi_info;                       /* 0x1c0 */
        __u32 alt_mem_k;                                /* 0x1e0 */
diff --git a/arch/x86/include/asm/olpc_ofw.h b/arch/x86/include/asm/olpc_ofw.h
new file mode 100644 (file)
index 0000000..3e63d85
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef _ASM_X86_OLPC_OFW_H
+#define _ASM_X86_OLPC_OFW_H
+
+/* index into the page table containing the entry OFW occupies */
+#define OLPC_OFW_PDE_NR 1022
+
+#define OLPC_OFW_SIG 0x2057464F        /* aka "OFW " */
+
+#ifdef CONFIG_OLPC_OPENFIRMWARE
+
+/* run an OFW command by calling into the firmware */
+#define olpc_ofw(name, args, res) \
+       __olpc_ofw((name), ARRAY_SIZE(args), args, ARRAY_SIZE(res), res)
+
+extern int __olpc_ofw(const char *name, int nr_args, void **args, int nr_res,
+               void **res);
+
+/* determine whether OFW is available and lives in the proper memory */
+extern void olpc_ofw_detect(void);
+
+/* install OFW's pde permanently into the kernel's pgtable */
+extern void setup_olpc_ofw_pgd(void);
+
+#else /* !CONFIG_OLPC_OPENFIRMWARE */
+
+static inline void olpc_ofw_detect(void) { }
+static inline void setup_olpc_ofw_pgd(void) { }
+
+#endif /* !CONFIG_OLPC_OPENFIRMWARE */
+
+#endif /* _ASM_X86_OLPC_OFW_H */
index e77b220837214cef81deff278e2f79ef1ed202a7..0925676266bdbc9cbcf03811916b9f8b9b9e627e 100644 (file)
@@ -104,6 +104,7 @@ obj-$(CONFIG_SCx200)                += scx200.o
 scx200-y                       += scx200_32.o
 
 obj-$(CONFIG_OLPC)             += olpc.o
+obj-$(CONFIG_OLPC_OPENFIRMWARE)        += olpc_ofw.o
 obj-$(CONFIG_X86_MRST)         += mrst.o
 
 microcode-y                            := microcode_core.o
index 37c3d4b17d859d6ee38029a83f2abcaee6d4dc05..ff4c453e13f3807344fff90bd2741078dc90481c 100644 (file)
@@ -131,6 +131,12 @@ ENTRY(startup_32)
        movsl
 1:
 
+#ifdef CONFIG_OLPC_OPENFIRMWARE
+       /* save OFW's pgdir table for later use when calling into OFW */
+       movl %cr3, %eax
+       movl %eax, pa(olpc_ofw_pgd)
+#endif
+
 #ifdef CONFIG_PARAVIRT
        /* This is can only trip for a broken bootloader... */
        cmpw $0x207, pa(boot_params + BP_version)
index 8297160c41b3949058e1a6a55bcb9979d0cfa88a..156605281f569583168aa98c1ab76619264198f4 100644 (file)
 #include <asm/geode.h>
 #include <asm/setup.h>
 #include <asm/olpc.h>
-
-#ifdef CONFIG_OPEN_FIRMWARE
-#include <asm/ofw.h>
-#endif
+#include <asm/olpc_ofw.h>
 
 struct olpc_platform_t olpc_platform_info;
 EXPORT_SYMBOL_GPL(olpc_platform_info);
@@ -188,14 +185,15 @@ err:
 }
 EXPORT_SYMBOL_GPL(olpc_ec_cmd);
 
-#ifdef CONFIG_OPEN_FIRMWARE
+#ifdef CONFIG_OLPC_OPENFIRMWARE
 static void __init platform_detect(void)
 {
        size_t propsize;
        __be32 rev;
+       void *args[] = { NULL, "board-revision-int", &rev, (void *)4 };
+       void *res[] = { &propsize };
 
-       if (ofw("getprop", 4, 1, NULL, "board-revision-int", &rev, 4,
-                       &propsize) || propsize != 4) {
+       if (olpc_ofw("getprop", args, res) || propsize != 4) {
                printk(KERN_ERR "ofw: getprop call failed!\n");
                rev = cpu_to_be32(0);
        }
diff --git a/arch/x86/kernel/olpc_ofw.c b/arch/x86/kernel/olpc_ofw.c
new file mode 100644 (file)
index 0000000..469ee43
--- /dev/null
@@ -0,0 +1,104 @@
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <asm/page.h>
+#include <asm/setup.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/olpc_ofw.h>
+
+/* address of OFW callback interface; will be NULL if OFW isn't found */
+static int (*olpc_ofw_cif)(int *);
+
+/* page dir entry containing OFW's pgdir table; filled in by head_32.S */
+u32 olpc_ofw_pgd __initdata;
+
+static DEFINE_SPINLOCK(ofw_lock);
+
+#define MAXARGS 10
+
+void __init setup_olpc_ofw_pgd(void)
+{
+       pgd_t *base, *ofw_pde;
+
+       if (!olpc_ofw_cif)
+               return;
+
+       /* fetch OFW's PDE */
+       base = early_ioremap(olpc_ofw_pgd, sizeof(olpc_ofw_pgd) * PTRS_PER_PGD);
+       if (!base) {
+               printk(KERN_ERR "failed to remap OFW's pgd - disabling OFW!\n");
+               olpc_ofw_cif = NULL;
+               return;
+       }
+       ofw_pde = &base[OLPC_OFW_PDE_NR];
+
+       /* install OFW's PDE permanently into the kernel's pgtable */
+       set_pgd(&swapper_pg_dir[OLPC_OFW_PDE_NR], *ofw_pde);
+       early_iounmap(base, sizeof(olpc_ofw_pgd) * PTRS_PER_PGD);
+}
+
+int __olpc_ofw(const char *name, int nr_args, void **args, int nr_res,
+               void **res)
+{
+       int ofw_args[MAXARGS + 3];
+       unsigned long flags;
+       int ret, i, *p;
+
+       BUG_ON(nr_args + nr_res > MAXARGS);
+
+       if (!olpc_ofw_cif)
+               return -EIO;
+
+       ofw_args[0] = (int)name;
+       ofw_args[1] = nr_args;
+       ofw_args[2] = nr_res;
+
+       p = &ofw_args[3];
+       for (i = 0; i < nr_args; i++, p++)
+               *p = (int)args[i];
+
+       /* call into ofw */
+       spin_lock_irqsave(&ofw_lock, flags);
+       ret = olpc_ofw_cif(ofw_args);
+       spin_unlock_irqrestore(&ofw_lock, flags);
+
+       if (!ret) {
+               for (i = 0; i < nr_res; i++, p++)
+                       *((int *)res[i]) = *p;
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(__olpc_ofw);
+
+/* OFW cif _should_ be above this address */
+#define OFW_MIN 0xff000000
+
+/* OFW starts on a 1MB boundary */
+#define OFW_BOUND (1<<20)
+
+void __init olpc_ofw_detect(void)
+{
+       struct olpc_ofw_header *hdr = &boot_params.olpc_ofw_header;
+       unsigned long start;
+
+       /* ensure OFW booted us by checking for "OFW " string */
+       if (hdr->ofw_magic != OLPC_OFW_SIG)
+               return;
+
+       olpc_ofw_cif = (int (*)(int *))hdr->cif_handler;
+
+       if ((unsigned long)olpc_ofw_cif < OFW_MIN) {
+               printk(KERN_ERR "OFW detected, but cif has invalid address 0x%lx - disabling.\n",
+                               (unsigned long)olpc_ofw_cif);
+               olpc_ofw_cif = NULL;
+               return;
+       }
+
+       /* determine where OFW starts in memory */
+       start = round_down((unsigned long)olpc_ofw_cif, OFW_BOUND);
+       printk(KERN_INFO "OFW detected in memory, cif @ 0x%lx (reserving top %ldMB)\n",
+                       (unsigned long)olpc_ofw_cif, (-start) >> 20);
+       reserve_top_address(-start);
+}
index b4ae4acbd031061d022a0c3e2873b7fd346a9263..b008e7883207abe580a696e9b418dbddec84dabb 100644 (file)
 
 #include <asm/paravirt.h>
 #include <asm/hypervisor.h>
+#include <asm/olpc_ofw.h>
 
 #include <asm/percpu.h>
 #include <asm/topology.h>
@@ -736,10 +737,15 @@ void __init setup_arch(char **cmdline_p)
        /* VMI may relocate the fixmap; do this before touching ioremap area */
        vmi_init();
 
+       /* OFW also may relocate the fixmap */
+       olpc_ofw_detect();
+
        early_trap_init();
        early_cpu_init();
        early_ioremap_init();
 
+       setup_olpc_ofw_pgd();
+
        ROOT_DEV = old_decode_dev(boot_params.hdr.root_dev);
        screen_info = boot_params.screen_info;
        edid_info = boot_params.edid_info;