aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel
diff options
context:
space:
mode:
authorAndres Salomon <dilinger@queued.net>2010-06-18 17:46:53 -0400
committerH. Peter Anvin <hpa@linux.intel.com>2010-06-18 17:54:36 -0400
commitfd699c76552bbfa66631f019be415a87dbb08237 (patch)
tree49f136f22fc94230af214f9c9c21a6fc0b7180e1 /arch/x86/kernel
parent7e27d6e778cd87b6f2415515d7127eba53fe5d02 (diff)
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>
Diffstat (limited to 'arch/x86/kernel')
-rw-r--r--arch/x86/kernel/Makefile1
-rw-r--r--arch/x86/kernel/head_32.S6
-rw-r--r--arch/x86/kernel/olpc.c12
-rw-r--r--arch/x86/kernel/olpc_ofw.c104
-rw-r--r--arch/x86/kernel/setup.c6
5 files changed, 122 insertions, 7 deletions
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index e77b22083721..0925676266bd 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -104,6 +104,7 @@ obj-$(CONFIG_SCx200) += scx200.o
104scx200-y += scx200_32.o 104scx200-y += scx200_32.o
105 105
106obj-$(CONFIG_OLPC) += olpc.o 106obj-$(CONFIG_OLPC) += olpc.o
107obj-$(CONFIG_OLPC_OPENFIRMWARE) += olpc_ofw.o
107obj-$(CONFIG_X86_MRST) += mrst.o 108obj-$(CONFIG_X86_MRST) += mrst.o
108 109
109microcode-y := microcode_core.o 110microcode-y := microcode_core.o
diff --git a/arch/x86/kernel/head_32.S b/arch/x86/kernel/head_32.S
index 37c3d4b17d85..ff4c453e13f3 100644
--- a/arch/x86/kernel/head_32.S
+++ b/arch/x86/kernel/head_32.S
@@ -131,6 +131,12 @@ ENTRY(startup_32)
131 movsl 131 movsl
1321: 1321:
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#ifdef CONFIG_PARAVIRT 140#ifdef CONFIG_PARAVIRT
135 /* This is can only trip for a broken bootloader... */ 141 /* This is can only trip for a broken bootloader... */
136 cmpw $0x207, pa(boot_params + BP_version) 142 cmpw $0x207, pa(boot_params + BP_version)
diff --git a/arch/x86/kernel/olpc.c b/arch/x86/kernel/olpc.c
index 8297160c41b3..156605281f56 100644
--- a/arch/x86/kernel/olpc.c
+++ b/arch/x86/kernel/olpc.c
@@ -21,10 +21,7 @@
21#include <asm/geode.h> 21#include <asm/geode.h>
22#include <asm/setup.h> 22#include <asm/setup.h>
23#include <asm/olpc.h> 23#include <asm/olpc.h>
24 24#include <asm/olpc_ofw.h>
25#ifdef CONFIG_OPEN_FIRMWARE
26#include <asm/ofw.h>
27#endif
28 25
29struct olpc_platform_t olpc_platform_info; 26struct olpc_platform_t olpc_platform_info;
30EXPORT_SYMBOL_GPL(olpc_platform_info); 27EXPORT_SYMBOL_GPL(olpc_platform_info);
@@ -188,14 +185,15 @@ err:
188} 185}
189EXPORT_SYMBOL_GPL(olpc_ec_cmd); 186EXPORT_SYMBOL_GPL(olpc_ec_cmd);
190 187
191#ifdef CONFIG_OPEN_FIRMWARE 188#ifdef CONFIG_OLPC_OPENFIRMWARE
192static void __init platform_detect(void) 189static void __init platform_detect(void)
193{ 190{
194 size_t propsize; 191 size_t propsize;
195 __be32 rev; 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, 196 if (olpc_ofw("getprop", args, res) || propsize != 4) {
198 &propsize) || propsize != 4) {
199 printk(KERN_ERR "ofw: getprop call failed!\n"); 197 printk(KERN_ERR "ofw: getprop call failed!\n");
200 rev = cpu_to_be32(0); 198 rev = cpu_to_be32(0);
201 } 199 }
diff --git a/arch/x86/kernel/olpc_ofw.c b/arch/x86/kernel/olpc_ofw.c
new file mode 100644
index 000000000000..469ee4384295
--- /dev/null
+++ b/arch/x86/kernel/olpc_ofw.c
@@ -0,0 +1,104 @@
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 */
11static int (*olpc_ofw_cif)(int *);
12
13/* page dir entry containing OFW's pgdir table; filled in by head_32.S */
14u32 olpc_ofw_pgd __initdata;
15
16static DEFINE_SPINLOCK(ofw_lock);
17
18#define MAXARGS 10
19
20void __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
41int __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}
73EXPORT_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
81void __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}
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index b4ae4acbd031..b008e7883207 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -102,6 +102,7 @@
102 102
103#include <asm/paravirt.h> 103#include <asm/paravirt.h>
104#include <asm/hypervisor.h> 104#include <asm/hypervisor.h>
105#include <asm/olpc_ofw.h>
105 106
106#include <asm/percpu.h> 107#include <asm/percpu.h>
107#include <asm/topology.h> 108#include <asm/topology.h>
@@ -736,10 +737,15 @@ void __init setup_arch(char **cmdline_p)
736 /* VMI may relocate the fixmap; do this before touching ioremap area */ 737 /* VMI may relocate the fixmap; do this before touching ioremap area */
737 vmi_init(); 738 vmi_init();
738 739
740 /* OFW also may relocate the fixmap */
741 olpc_ofw_detect();
742
739 early_trap_init(); 743 early_trap_init();
740 early_cpu_init(); 744 early_cpu_init();
741 early_ioremap_init(); 745 early_ioremap_init();
742 746
747 setup_olpc_ofw_pgd();
748
743 ROOT_DEV = old_decode_dev(boot_params.hdr.root_dev); 749 ROOT_DEV = old_decode_dev(boot_params.hdr.root_dev);
744 screen_info = boot_params.screen_info; 750 screen_info = boot_params.screen_info;
745 edid_info = boot_params.edid_info; 751 edid_info = boot_params.edid_info;