Commit fd699c76552bbfa66631f019be415a87dbb08237

Authored by Andres Salomon
Committed by H. Peter Anvin
1 parent 7e27d6e778

x86, olpc: Add support for calling into OpenFirmware

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>

Showing 9 changed files with 173 additions and 8 deletions Side-by-side Diff

Documentation/x86/zero-page.txt
... ... @@ -18,6 +18,7 @@
18 18 080/010 ALL hd0_info hd0 disk parameter, OBSOLETE!!
19 19 090/010 ALL hd1_info hd1 disk parameter, OBSOLETE!!
20 20 0A0/010 ALL sys_desc_table System description table (struct sys_desc_table)
  21 +0B0/010 ALL olpc_ofw_header OLPC's OpenFirmware CIF and friends
21 22 140/080 ALL edid_info Video mode setup (struct edid_info)
22 23 1C0/020 ALL efi_info EFI 32 information (struct efi_info)
23 24 1E0/004 ALL alk_mem_k Alternative mem check, in KB
... ... @@ -2062,6 +2062,15 @@
2062 2062 Add support for detecting the unique features of the OLPC
2063 2063 XO hardware.
2064 2064  
  2065 +config OLPC_OPENFIRMWARE
  2066 + bool "Support for OLPC's Open Firmware"
  2067 + depends on !X86_64 && !X86_PAE
  2068 + default y if OLPC
  2069 + help
  2070 + This option adds support for the implementation of Open Firmware
  2071 + that is used on the OLPC XO-1 Children's Machine.
  2072 + If unsure, say N here.
  2073 +
2065 2074 endif # X86_32
2066 2075  
2067 2076 config K8_NB
arch/x86/include/asm/bootparam.h
... ... @@ -70,6 +70,14 @@
70 70 __u8 table[14];
71 71 };
72 72  
  73 +/* Gleaned from OFW's set-parameters in cpu/x86/pc/linux.fth */
  74 +struct olpc_ofw_header {
  75 + __u32 ofw_magic; /* OFW signature */
  76 + __u32 ofw_version;
  77 + __u32 cif_handler; /* callback into OFW */
  78 + __u32 irq_desc_table;
  79 +} __attribute__((packed));
  80 +
73 81 struct efi_info {
74 82 __u32 efi_loader_signature;
75 83 __u32 efi_systab;
... ... @@ -92,7 +100,8 @@
92 100 __u8 hd0_info[16]; /* obsolete! */ /* 0x080 */
93 101 __u8 hd1_info[16]; /* obsolete! */ /* 0x090 */
94 102 struct sys_desc_table sys_desc_table; /* 0x0a0 */
95   - __u8 _pad4[144]; /* 0x0b0 */
  103 + struct olpc_ofw_header olpc_ofw_header; /* 0x0b0 */
  104 + __u8 _pad4[128]; /* 0x0c0 */
96 105 struct edid_info edid_info; /* 0x140 */
97 106 struct efi_info efi_info; /* 0x1c0 */
98 107 __u32 alt_mem_k; /* 0x1e0 */
arch/x86/include/asm/olpc_ofw.h
  1 +#ifndef _ASM_X86_OLPC_OFW_H
  2 +#define _ASM_X86_OLPC_OFW_H
  3 +
  4 +/* index into the page table containing the entry OFW occupies */
  5 +#define OLPC_OFW_PDE_NR 1022
  6 +
  7 +#define OLPC_OFW_SIG 0x2057464F /* aka "OFW " */
  8 +
  9 +#ifdef CONFIG_OLPC_OPENFIRMWARE
  10 +
  11 +/* run an OFW command by calling into the firmware */
  12 +#define olpc_ofw(name, args, res) \
  13 + __olpc_ofw((name), ARRAY_SIZE(args), args, ARRAY_SIZE(res), res)
  14 +
  15 +extern int __olpc_ofw(const char *name, int nr_args, void **args, int nr_res,
  16 + void **res);
  17 +
  18 +/* determine whether OFW is available and lives in the proper memory */
  19 +extern void olpc_ofw_detect(void);
  20 +
  21 +/* install OFW's pde permanently into the kernel's pgtable */
  22 +extern void setup_olpc_ofw_pgd(void);
  23 +
  24 +#else /* !CONFIG_OLPC_OPENFIRMWARE */
  25 +
  26 +static inline void olpc_ofw_detect(void) { }
  27 +static inline void setup_olpc_ofw_pgd(void) { }
  28 +
  29 +#endif /* !CONFIG_OLPC_OPENFIRMWARE */
  30 +
  31 +#endif /* _ASM_X86_OLPC_OFW_H */
arch/x86/kernel/Makefile
... ... @@ -104,6 +104,7 @@
104 104 scx200-y += scx200_32.o
105 105  
106 106 obj-$(CONFIG_OLPC) += olpc.o
  107 +obj-$(CONFIG_OLPC_OPENFIRMWARE) += olpc_ofw.o
107 108 obj-$(CONFIG_X86_MRST) += mrst.o
108 109  
109 110 microcode-y := microcode_core.o
arch/x86/kernel/head_32.S
... ... @@ -131,6 +131,12 @@
131 131 movsl
132 132 1:
133 133  
  134 +#ifdef CONFIG_OLPC_OPENFIRMWARE
  135 + /* save OFW's pgdir table for later use when calling into OFW */
  136 + movl %cr3, %eax
  137 + movl %eax, pa(olpc_ofw_pgd)
  138 +#endif
  139 +
134 140 #ifdef CONFIG_PARAVIRT
135 141 /* This is can only trip for a broken bootloader... */
136 142 cmpw $0x207, pa(boot_params + BP_version)
arch/x86/kernel/olpc.c
... ... @@ -21,11 +21,8 @@
21 21 #include <asm/geode.h>
22 22 #include <asm/setup.h>
23 23 #include <asm/olpc.h>
  24 +#include <asm/olpc_ofw.h>
24 25  
25   -#ifdef CONFIG_OPEN_FIRMWARE
26   -#include <asm/ofw.h>
27   -#endif
28   -
29 26 struct olpc_platform_t olpc_platform_info;
30 27 EXPORT_SYMBOL_GPL(olpc_platform_info);
31 28  
32 29  
33 30  
... ... @@ -188,14 +185,15 @@
188 185 }
189 186 EXPORT_SYMBOL_GPL(olpc_ec_cmd);
190 187  
191   -#ifdef CONFIG_OPEN_FIRMWARE
  188 +#ifdef CONFIG_OLPC_OPENFIRMWARE
192 189 static void __init platform_detect(void)
193 190 {
194 191 size_t propsize;
195 192 __be32 rev;
  193 + void *args[] = { NULL, "board-revision-int", &rev, (void *)4 };
  194 + void *res[] = { &propsize };
196 195  
197   - if (ofw("getprop", 4, 1, NULL, "board-revision-int", &rev, 4,
198   - &propsize) || propsize != 4) {
  196 + if (olpc_ofw("getprop", args, res) || propsize != 4) {
199 197 printk(KERN_ERR "ofw: getprop call failed!\n");
200 198 rev = cpu_to_be32(0);
201 199 }
arch/x86/kernel/olpc_ofw.c
  1 +#include <linux/kernel.h>
  2 +#include <linux/module.h>
  3 +#include <linux/init.h>
  4 +#include <asm/page.h>
  5 +#include <asm/setup.h>
  6 +#include <asm/io.h>
  7 +#include <asm/pgtable.h>
  8 +#include <asm/olpc_ofw.h>
  9 +
  10 +/* address of OFW callback interface; will be NULL if OFW isn't found */
  11 +static int (*olpc_ofw_cif)(int *);
  12 +
  13 +/* page dir entry containing OFW's pgdir table; filled in by head_32.S */
  14 +u32 olpc_ofw_pgd __initdata;
  15 +
  16 +static DEFINE_SPINLOCK(ofw_lock);
  17 +
  18 +#define MAXARGS 10
  19 +
  20 +void __init setup_olpc_ofw_pgd(void)
  21 +{
  22 + pgd_t *base, *ofw_pde;
  23 +
  24 + if (!olpc_ofw_cif)
  25 + return;
  26 +
  27 + /* fetch OFW's PDE */
  28 + base = early_ioremap(olpc_ofw_pgd, sizeof(olpc_ofw_pgd) * PTRS_PER_PGD);
  29 + if (!base) {
  30 + printk(KERN_ERR "failed to remap OFW's pgd - disabling OFW!\n");
  31 + olpc_ofw_cif = NULL;
  32 + return;
  33 + }
  34 + ofw_pde = &base[OLPC_OFW_PDE_NR];
  35 +
  36 + /* install OFW's PDE permanently into the kernel's pgtable */
  37 + set_pgd(&swapper_pg_dir[OLPC_OFW_PDE_NR], *ofw_pde);
  38 + early_iounmap(base, sizeof(olpc_ofw_pgd) * PTRS_PER_PGD);
  39 +}
  40 +
  41 +int __olpc_ofw(const char *name, int nr_args, void **args, int nr_res,
  42 + void **res)
  43 +{
  44 + int ofw_args[MAXARGS + 3];
  45 + unsigned long flags;
  46 + int ret, i, *p;
  47 +
  48 + BUG_ON(nr_args + nr_res > MAXARGS);
  49 +
  50 + if (!olpc_ofw_cif)
  51 + return -EIO;
  52 +
  53 + ofw_args[0] = (int)name;
  54 + ofw_args[1] = nr_args;
  55 + ofw_args[2] = nr_res;
  56 +
  57 + p = &ofw_args[3];
  58 + for (i = 0; i < nr_args; i++, p++)
  59 + *p = (int)args[i];
  60 +
  61 + /* call into ofw */
  62 + spin_lock_irqsave(&ofw_lock, flags);
  63 + ret = olpc_ofw_cif(ofw_args);
  64 + spin_unlock_irqrestore(&ofw_lock, flags);
  65 +
  66 + if (!ret) {
  67 + for (i = 0; i < nr_res; i++, p++)
  68 + *((int *)res[i]) = *p;
  69 + }
  70 +
  71 + return ret;
  72 +}
  73 +EXPORT_SYMBOL_GPL(__olpc_ofw);
  74 +
  75 +/* OFW cif _should_ be above this address */
  76 +#define OFW_MIN 0xff000000
  77 +
  78 +/* OFW starts on a 1MB boundary */
  79 +#define OFW_BOUND (1<<20)
  80 +
  81 +void __init olpc_ofw_detect(void)
  82 +{
  83 + struct olpc_ofw_header *hdr = &boot_params.olpc_ofw_header;
  84 + unsigned long start;
  85 +
  86 + /* ensure OFW booted us by checking for "OFW " string */
  87 + if (hdr->ofw_magic != OLPC_OFW_SIG)
  88 + return;
  89 +
  90 + olpc_ofw_cif = (int (*)(int *))hdr->cif_handler;
  91 +
  92 + if ((unsigned long)olpc_ofw_cif < OFW_MIN) {
  93 + printk(KERN_ERR "OFW detected, but cif has invalid address 0x%lx - disabling.\n",
  94 + (unsigned long)olpc_ofw_cif);
  95 + olpc_ofw_cif = NULL;
  96 + return;
  97 + }
  98 +
  99 + /* determine where OFW starts in memory */
  100 + start = round_down((unsigned long)olpc_ofw_cif, OFW_BOUND);
  101 + printk(KERN_INFO "OFW detected in memory, cif @ 0x%lx (reserving top %ldMB)\n",
  102 + (unsigned long)olpc_ofw_cif, (-start) >> 20);
  103 + reserve_top_address(-start);
  104 +}
arch/x86/kernel/setup.c
... ... @@ -102,6 +102,7 @@
102 102  
103 103 #include <asm/paravirt.h>
104 104 #include <asm/hypervisor.h>
  105 +#include <asm/olpc_ofw.h>
105 106  
106 107 #include <asm/percpu.h>
107 108 #include <asm/topology.h>
108 109  
... ... @@ -736,9 +737,14 @@
736 737 /* VMI may relocate the fixmap; do this before touching ioremap area */
737 738 vmi_init();
738 739  
  740 + /* OFW also may relocate the fixmap */
  741 + olpc_ofw_detect();
  742 +
739 743 early_trap_init();
740 744 early_cpu_init();
741 745 early_ioremap_init();
  746 +
  747 + setup_olpc_ofw_pgd();
742 748  
743 749 ROOT_DEV = old_decode_dev(boot_params.hdr.root_dev);
744 750 screen_info = boot_params.screen_info;