Skip to contentSkip to conversation

x86: OLPC: add support for calling into OpenFirmware (v5)

PATCH In review Andres Salomon 3 participants 7 messages Jun 18, 2010 – Jun 19, 2010

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 x86: OLPC: add support for calling into OpenFirmware (v5)

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.

---
 Documentation/x86/zero-page.txt  |    1 +
 arch/x86/Kconfig                 |    9 +++
 arch/x86/include/asm/bootparam.h |   11 ++++-
 arch/x86/include/asm/olpc_ofw.h  |   31 +++++++++++
 arch/x86/kernel/Makefile         |    1 +
 arch/x86/kernel/head_32.S        |    6 ++
 arch/x86/kernel/olpc.c           |   12 ++---
 arch/x86/kernel/olpc_ofw.c       |  106 ++++++++++++++++++++++++++++++++++++++
 arch/x86/kernel/setup.c          |    6 ++
 9 files changed, 175 insertions(+), 8 deletions(-)
 create mode 100644 arch/x86/include/asm/olpc_ofw.h
 create mode 100644 arch/x86/kernel/olpc_ofw.c
Documentation/x86/zero-page.txt +1 -0
index feb37e1..cf5437d 100644
@@ -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
arch/x86/Kconfig +9 -0
index dcb0593..71c194d 100644
@@ -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
arch/x86/include/asm/bootparam.h +10 -1
index 6be33d8..8e62185 100644
@@ -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 */
arch/x86/include/asm/olpc_ofw.h new file +31 -0
new file mode 100644
index 0000000..3e63d85
@@ -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 */
arch/x86/kernel/Makefile +1 -0
index e77b220..0925676 100644
@@ -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
arch/x86/kernel/head_32.S +6 -0
index 37c3d4b..ff4c453 100644
@@ -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)
arch/x86/kernel/olpc.c +5 -7
index 8297160..1566052 100644
@@ -21,10 +21,7 @@
 #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);
 	}
arch/x86/kernel/olpc_ofw.c new file +106 -0

106 lines not on this page · raw message

arch/x86/kernel/setup.c +6 -0
index b4ae4ac..b008e78 100644
@@ -102,6 +102,7 @@
 
 #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;
Signature · 1 line
1.5.6.5
#1 lore raw

arch/x86/kernel/olpc_ofw.c line 40

Quoted 5 lines · Andres Salomon
arch/x86/kernel/olpc_ofw.c new file +5 -0
+
+	/* install OFW's PDE permanently into the kernel's pgtable */
+	set_pgd(&swapper_pg_dir[OLPC_OFW_PDE_NR], *ofw_pde);
+	flush_tlb();
+	early_iounmap(base, sizeof(olpc_ofw_pgd) * PTRS_PER_PGD);

I just realized... this flush_tlb() is actually not necessary since on x86 it is always legal to go from a less permissive configuration to a more permissive, and the initial configuration is "not present" which is maximally nonpermissive.

Could you take it out and make sure it still works?

-hpa

#2 lore raw
Quoted 12 lines · H. Peter Anvin

arch/x86/kernel/olpc_ofw.c line 40

Quoted 5 lines · Andres Salomon
arch/x86/kernel/olpc_ofw.c new file +5 -0
+
+	/* install OFW's PDE permanently into the kernel's pgtable */
+	set_pgd(&swapper_pg_dir[OLPC_OFW_PDE_NR], *ofw_pde);
+	flush_tlb();
+	early_iounmap(base, sizeof(olpc_ofw_pgd) * PTRS_PER_PGD);

I just realized... this flush_tlb() is actually not necessary since on x86 it is always legal to go from a less permissive configuration to a more permissive, and the initial configuration is "not present" which is maximally nonpermissive.

Quoted 1 line

Could you take it out and make sure it still works?

small nit: an optimization barrier would still be needed if the result is being relied on by the kernel.

Thanks,

Ingo

#3 lore raw
Quoted 15 lines · Ingo Molnar
Quoted 12 lines · H. Peter Anvin

arch/x86/kernel/olpc_ofw.c line 40

Quoted 5 lines · Andres Salomon
arch/x86/kernel/olpc_ofw.c new file +5 -0
+
+	/* install OFW's PDE permanently into the kernel's pgtable */
+	set_pgd(&swapper_pg_dir[OLPC_OFW_PDE_NR], *ofw_pde);
+	flush_tlb();
+	early_iounmap(base, sizeof(olpc_ofw_pgd) * PTRS_PER_PGD);

I just realized... this flush_tlb() is actually not necessary since on x86 it is always legal to go from a less permissive configuration to a more permissive, and the initial configuration is "not present" which is maximally nonpermissive.

Quoted 5 lines
Quoted 1 line

Could you take it out and make sure it still works?

small nit: an optimization barrier would still be needed if the result is being relied on by the kernel.

In this case the optimization barrier is implicit at the return from an out of line function.

-hpa

#5 lore raw
In reply to H. Peter Anvin
Quoted 13 lines · On Fri, 18 Jun 2010 12:49:41 -0700 "H. Peter Anvin" <[email protected]> wrote:

arch/x86/kernel/olpc_ofw.c line 40

Quoted 6 lines · Andres Salomon
(quoted diff) +2 -0
+
+	/* install OFW's PDE permanently into the kernel's pgtable

*/

arch/x86/kernel/olpc_ofw.c new file +3 -0
+	set_pgd(&swapper_pg_dir[OLPC_OFW_PDE_NR], *ofw_pde);
+	flush_tlb();
+	early_iounmap(base, sizeof(olpc_ofw_pgd) * PTRS_PER_PGD);

I just realized... this flush_tlb() is actually not necessary since on x86 it is always legal to go from a less permissive configuration to a more permissive, and the initial configuration is "not present" which is maximally nonpermissive.

Quoted 3 lines

Could you take it out and make sure it still works?

-hpa

Yep, seems to work fine w/out it. Anything else before I send off v6 of the patch?

#6 lore raw
Quoted 16 lines · Andres Salomon
Quoted 13 lines · On Fri, 18 Jun 2010 12:49:41 -0700 "H. Peter Anvin" <[email protected]> wrote:

arch/x86/kernel/olpc_ofw.c line 40

Quoted 6 lines · Andres Salomon
(quoted diff) +2 -0
+
+	/* install OFW's PDE permanently into the kernel's pgtable

*/

arch/x86/kernel/olpc_ofw.c new file +3 -0
+	set_pgd(&swapper_pg_dir[OLPC_OFW_PDE_NR], *ofw_pde);
+	flush_tlb();
+	early_iounmap(base, sizeof(olpc_ofw_pgd) * PTRS_PER_PGD);

I just realized... this flush_tlb() is actually not necessary since on x86 it is always legal to go from a less permissive configuration to a more permissive, and the initial configuration is "not present" which is maximally nonpermissive.

Quoted 6 lines
Quoted 3 lines

Could you take it out and make sure it still works?

-hpa

Yep, seems to work fine w/out it. Anything else before I send off v6 of the patch?

Not that I can see.

-hpa

#4 lore raw
In reply to H. Peter Anvin
Quoted 16 lines · H. Peter Anvin
Quoted 15 lines · Ingo Molnar
Quoted 12 lines · H. Peter Anvin

arch/x86/kernel/olpc_ofw.c line 40

Quoted 5 lines · Andres Salomon
arch/x86/kernel/olpc_ofw.c new file +5 -0
+
+	/* install OFW's PDE permanently into the kernel's pgtable */
+	set_pgd(&swapper_pg_dir[OLPC_OFW_PDE_NR], *ofw_pde);
+	flush_tlb();
+	early_iounmap(base, sizeof(olpc_ofw_pgd) * PTRS_PER_PGD);

I just realized... this flush_tlb() is actually not necessary since on x86 it is always legal to go from a less permissive configuration to a more permissive, and the initial configuration is "not present" which is maximally nonpermissive.

Quoted 8 lines
Quoted 5 lines
Quoted 1 line

Could you take it out and make sure it still works?

small nit: an optimization barrier would still be needed if the result is being relied on by the kernel.

In this case the optimization barrier is implicit at the return from an out of line function.

Which non-obvious side-effect at minimum deserves a comment. (but yes, i'm just nitpicking)

Thanks,

Ingo