[PATCH] qemu-system-ppc -prep initial port

Milton Miller miltonm at bga.com
Tue Aug 28 14:03:29 EST 2007


Hi.

Some people have been asking about booting powerpc kernels on qemu.

Based on some comments about the existing rom for qemu, I looked
at OpenHackWare enough to discern how qemu started its execution
and how it gets its config.   

I then proceeded to write a wrapper patch to use as the rom and then
proceeded with a minimal platform (based on David Gibson's prep patch)
to boot a ARCH=powerpc kernel.

These patches are being posted early to allow others to use them,
they are not ready to be merged.

What works:
	-prep  (not default pmac, or core99 in later qemu)
	serial console in bootwrapper
	serial console in kernel (console=ttyS0)
	kernel must be loaded with --kernel=
		vmlinux, or vmlinuz (gzip'ed elf).
		zImage.qemu, zImage.initrd.qemu (with compiler assumptions)
	-initrd (optional)
	-append (optional)
	ide-disk and cdrom appear to work
	floppy is untested, and needs io port added.

What doesn't work:
	boot from disk
	vga (needs bios post, maybe pci)
	pci (config cycles are not working, at least on 0.6.3)

How it works:
	wrapper is compiled to run as rom
		copied from rom to ram (linked address)
		supplies initial device tree blob (because dtb = NULL)
		reads nvram for memory size, kernel, initrd, command line
	if invoked with device tree, then it only uses attached kernel
		initrd either attached or from kernel

what you need:
	2.6.23-rc3 + [ from powerpc.git for-2.6.24 branch ]
	commit 0602801c22ea1767cd0298b11da140bd5cb764d2
	    [POWERPC] bootwrapper: dt_xlate_range() bugfixes
	commit a73ac50c4787b1b28d5c94bb18c60352f5dd7d6f
	    [POWERPC] bootwrapper: Add dt_is_compatible()
	commit 6e913c67b3eb93e2b8bc1dc0ff854f00a760f41b
	    [POWERPC] bootwrapper: Add 16-bit I/O, sync(), eieio(), and barrier()
	commit dc4f397d6e385c4ea0fe9732df911a86f1a78c9a
	    [POWERPC] bootwrapper: serial_console_init() fixes
	commit 2f1d4899321be87bc5f0c4ee0e62c9d9ced05f80
	    [POWERPC] bootwrapper: Move linker symbols into ops.h

	tell wrapper where initrd is, so it can move it if needed
		http://patchwork.ozlabs.org/linuxppc/patch?id=12168

	tell code where vmlinuz is
		http://patchwork.ozlabs.org/linuxppc/patch?id=12178

	build rom without vmlinuz
		http://patchwork.ozlabs.org/linuxppc/patch?id=12180

	Subject: Move serial_dev_init to device_initcall()
		http://patchwork.ozlabs.org/linuxppc/patch?id=13097


To run:
	build ppc_rom.bin using CONFIG_DEVICE_TREE=prep.dts with the
	dtc installed.  This will be based on zBoot.qemu and will not
	have a kernel attached.  Put this in a directory and invoke
	qemu-system-ppc -prep -L <dir-with-rom> -kernel <kernel>
	where kernel is either zImage.qemu or a (possibly compressed)
	vmlinux elf , add ide drives, -append, and -initrd as desired.

My tree has all patches in for-2.6.24 that touch arch/powerpc/boot plus
ojn's patch plus my kexec series plus the prep patch, so there will be
some rejects, especially to makefiles etc.

The first hunk to main.c to check loader_info for a command line after
the fixup callback should be a seperate patch.

The boot/Makefile has a hack to avoid ovewriting qemu.dtb.

Index: kernel/arch/powerpc/boot/main.c
===================================================================
--- kernel.orig/arch/powerpc/boot/main.c	2007-08-24 03:49:35.000000000 -0500
+++ kernel/arch/powerpc/boot/main.c	2007-08-24 03:50:21.000000000 -0500
@@ -181,6 +181,11 @@ void start(void)
 	if (platform_ops.fixups)
 		platform_ops.fixups();
 
+	/* check again, in case fixups told us about it */
+	if ((loader_info.cmdline_len > 0) && (cmdline[0] == '\0'))
+		memmove(cmdline, loader_info.cmdline,
+			min(loader_info.cmdline_len, COMMAND_LINE_SIZE-1));
+
 	printf("\n\rzImage starting: loaded at 0x%p (sp: 0x%p)\n\r",
 	       _start, get_sp());
 
Index: kernel/arch/powerpc/boot/qemu.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ kernel/arch/powerpc/boot/qemu.c	2007-08-27 20:46:10.000000000 -0500
@@ -0,0 +1,237 @@
+/*
+ * QEMU PReP-specific stuff for the zImage boot wrapper.
+ *
+ * Copyright (C) 2007 Milton Miller, IBM Corp. <miltonm at bga.com>
+ * Copyright (C) 2006 Paul Mackerras, IBM Corp. <paulus at samba.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License.
+ *
+ * nvram format obtained from OpenHackWare 0.4
+ * Copyright (C) 2004-2005 Jocelyn Mayer (l_indien at magic.fr)
+ *
+ */
+
+asm(
+/*
+ * gcc 4.1.2 seems to place this first, even with the block at the end of
+ * the file.  Put it first for good measure.  If the branch ends up at the
+ * beginning of the image then it can be started like a kernel.
+ */
+"	b	_zimage_start\n"	/* gcc 4.1.2 places this first */
+
+/*
+ * This code will be loaded by qemu as a rom at the end of memory, and
+ * execution will start at address -4.  Since the rom is read only, we
+ * and the zImage code wants to run where loaded, we must copy it to ram.
+ *
+ * move to linked place
+ * end with a branch to this code
+ * this code will be copied with dd to the end of the rom image
+ */
+
+"	.globl	copy_rom_start\n"
+"copy_rom_start:\n"
+"	bl	1f\n"
+"1:	mflr	4\n"
+"	clrrwi	4,4,19\n"		/* ROM_BITS -- get beginning of rom */
+"	lis	5,_end at ha\n"
+"	addi	5,5,_end at l\n"
+"	lis	3,_start at ha\n"
+"	addi	3,3,_start at l\n"
+"	subf	5,3,5\n"
+"	addi	5,5,0x1f\n"
+"	srwi	5,5,5\n"
+"	mtctr	5\n"
+"2:	lwz	24,0(4)\n"		/* copy to linked addresss */
+"	lwz	25,4(4)\n"
+"	lwz	26,8(4)\n"
+"	lwz	27,12(4)\n"
+"	lwz	28,16(4)\n"
+"	lwz	29,20(4)\n"
+"	lwz	30,24(4)\n"
+"	lwz	31,28(4)\n"
+"	stw	24,0(3)\n"
+"	stw	25,4(3)\n"
+"	stw	26,8(3)\n"
+"	stw	27,12(3)\n"
+"	stw	28,16(3)\n"
+"	stw	29,20(3)\n"
+"	stw	30,24(3)\n"
+"	stw	31,28(3)\n"
+"	dcbf	0,3\n"
+"	sync\n"
+"	icbi	0,3\n"
+"	addi	3,3,32\n"
+"	addi	4,4,32\n"
+"	bdnz	2b\n"
+"	sync\n"
+"	isync\n"
+
+	/*
+	 * Put a branch to self on a few vectors in case we get an unexpected
+	 * interrupt.  At least you can dump regs in the qemu monitor.
+	 */
+"	lis	0,0x48000000 at h\n"
+"	stw	0,0x700(0)\n"
+"	stw	0,0x300(0)\n"
+"	stw	0,0x400(0)\n"
+"	stw	0,0x900(0)\n"
+"	stw	0,0x800(0)\n"
+"	stw	0,0x200(0)\n"
+
+	/* branch to linked address */
+"	lis	0,_zimage_start at h\n"
+"	ori	0,0,_zimage_start at l\n"
+"	mtctr	0\n"
+"	li	3,0\n"
+"	bctrl\n"
+
+	/* must be last word before copy_rom_end */
+"	b	copy_rom_start\n"
+"	.globl	copy_rom_end\n"
+"copy_rom_end:\n"
+);
+
+
+#include <stddef.h>
+#include "string.h"
+#include "stdio.h"
+#include "ops.h"
+#include "page.h"
+#include "io.h"
+#include "gunzip_util.h"
+#include "flatdevtree.h"
+
+BSS_STACK(16*1024);
+
+unsigned char *nvram = (void *)0x80000074;	/* address of nvram on PReP */
+
+static void qemu_nvram_write(unsigned short addr, char data)
+{
+	out_8(nvram + 0, addr & 0xff);
+	out_8(nvram + 1, addr >> 8);
+	out_8(nvram + 3, data);
+}
+
+static char qemu_nvram_read(unsigned short addr)
+{
+	char data;
+	out_8(nvram + 0, addr & 0xff);
+	out_8(nvram + 1, addr >> 8);
+	data = in_8(nvram + 3);
+
+	return data;
+}
+
+static void qemu_nvram_fetch(unsigned short addr, unsigned short len, void *p)
+{
+	char *buf = p;
+	int i;
+
+	for (i = 0; i < len; i ++)
+		buf[i] = qemu_nvram_read(addr + i);	/* big endian */
+}
+
+static char arch[17];
+static void * image_start;
+static unsigned int image_len;
+
+void qemu_find_vmlinuz(struct gunzip_state *state, void **srcp,
+	unsigned long *lenp)
+{
+	*srcp = image_start;
+	*lenp = image_len;
+	gunzip_start(state, image_start, image_len);
+}
+
+/* if no device tree, read the nvram to find out about our system */
+/* based on openhackware 0.4 nvram.c get_nvram_config */
+static void qemu_fixups(void)
+{
+	char buf[32];
+	int word;
+
+	printf("qemu_fixups\n\r");
+
+	qemu_nvram_fetch(0, 16, buf);
+	printf("sig: %s", buf);
+	if (strcmp(buf, "QEMU_BIOS"))
+		fatal("no qemu sig found");
+	qemu_nvram_fetch(0x10, 16, &word);
+	if (word != 2)
+		fatal("wrong qemu nvram version");
+	/* compute crc over 0-0xfc, compare to crc in 0xfc */
+	/* size at 0x14, mult 256 0x400-0x2000 */
+	qemu_nvram_fetch(0x20, 16, buf);
+	strncpy(arch, buf, sizeof(arch)-1);
+	if (strcmp(arch,"PREP"))
+		fatal("don't understand arch %s", arch);
+
+	/* XXX: put the model in the device tree */
+
+	qemu_nvram_fetch(0x30, 16, &word);
+	printf("memory: %x\n\r", word);
+	dt_fixup_memory(0, word);
+	printf("boot device %c\n\r", qemu_nvram_read(0x34));
+	qemu_nvram_fetch(0x38, 16, &word);
+	image_start = (void *)word;
+	qemu_nvram_fetch(0x3c, 16, &word);
+	image_len = word;
+	printf("kernel %p %x\n\r", image_start, image_len);
+	qemu_nvram_fetch(0x40, 16, &word);
+	loader_info.cmdline = (void *)word;
+	qemu_nvram_fetch(0x44, 16, &word);
+	loader_info.cmdline_len = word;
+	printf("cmdline %p %x\n\r", loader_info.cmdline,
+		loader_info.cmdline_len);
+	printf("cmdline is %s\n\r", loader_info.cmdline);
+	qemu_nvram_fetch(0x48, 16, &word);
+	loader_info.initrd_addr = word;
+	qemu_nvram_fetch(0x4c, 16, &word);
+	loader_info.initrd_size = word;
+	printf("initrd %lx %lx\n\r", loader_info.initrd_addr,
+		loader_info.initrd_size);
+	/* XXX: nvram image addr at 50 */
+	/* XXX: vga width, height, depth = shorts at 54, 56, 58 */
+}
+
+/* if loaded with a device tree, then just read the tree */
+static void tree_fixups(void)
+{
+	/* get initrd from tree */
+	dt_find_initrd();
+
+	/* we must have an attached image for device-tree boot */
+}
+
+static void *heap_end;	/* set in platform_init */
+
+static void *qemu_vmlinux_alloc(unsigned long size)
+{
+	if (size < (unsigned long)_start)
+		return 0;
+
+	return (void *)_ALIGN_UP((unsigned long) heap_end, 4096);
+}
+
+void platform_init(void *r3)
+{
+	struct boot_param_header *bph = r3;
+
+	heap_end = simple_alloc_init(_end, 4096*1024, 4096, 512);
+
+	if (bph)  {
+		ft_init(bph, bph->totalsize, 128);
+		platform_ops.fixups = tree_fixups;
+	} else {
+		ft_init(_dtb_start, _dtb_end - _dtb_start, 128);
+		platform_ops.fixups = qemu_fixups;
+		platform_ops.find_vmlinuz = qemu_find_vmlinuz;
+	}
+	platform_ops.vmlinux_alloc = qemu_vmlinux_alloc;
+
+	serial_console_init();
+
+	// platform_ops.vmlinux_alloc = of_vmlinux_alloc;
+}
Index: kernel/arch/powerpc/boot/Makefile
===================================================================
--- kernel.orig/arch/powerpc/boot/Makefile	2007-08-24 05:46:36.000000000 -0500
+++ kernel/arch/powerpc/boot/Makefile	2007-08-27 22:49:03.000000000 -0500
@@ -49,7 +49,7 @@ src-wlib := string.S crt0.S stdio.c main
 		cpm-serial.c
 src-plat := of.c cuboot-83xx.c cuboot-85xx.c holly.c crt0_kexec.S \
 		cuboot-ebony.c treeboot-ebony.c prpmc2800.c crt0_bml.S bml.c \
-		ps3-head.S ps3-hvcall.S ps3.c prep.c \
+		ps3-head.S ps3-hvcall.S ps3.c prep.c qemu.c \
 		treeboot-bamboo.c cuboot-8xx.c cuboot-pq2.c
 src-boot := $(src-wlib) $(src-plat) empty.c
 
@@ -144,6 +144,7 @@ image-$(CONFIG_PPC_HOLLY)		+= zImage.hol
 image-$(CONFIG_PPC_PRPMC2800)		+= zImage.prpmc2800
 image-$(CONFIG_PPC_ISERIES)		+= zImage.iseries
 image-$(CONFIG_PPC_PREP)		+= zImage.prep
+image-$(CONFIG_PPC_QEMU)		+= zImage.qemu zBoot.qemu
 image-$(CONFIG_DEFAULT_UIMAGE)		+= uImage
 image-$(CONFIG_KEXEC)			+= $(kexec-y)
 image-$(CONFIG_KEXEC)			+= $(patsubst zImage%,zBoot%,$(kexec-y))
@@ -201,6 +202,9 @@ $(obj)/zImage.iseries: vmlinux
 $(obj)/zBoot.%: $(wrapperbits) $(dts)
 	$(call if_changed,wrap,$*,$(dts),,,$(obj)/empty.o)
 
+# horrible hack to avoid overlapping calls to wrapper script clobbering dtb.
+$(obj)/zImage.qemu: $(obj)/zBoot.qemu
+
 $(obj)/zImage.ps3: vmlinux  $(wrapper) $(wrapperbits) $(srctree)/$(src)/dts/ps3.dts
 	$(STRIP) -s -R .comment $< -o vmlinux.strip
 	$(call cmd,wrap,ps3,$(srctree)/$(src)/dts/ps3.dts,,)
@@ -234,8 +238,8 @@ install: $(CONFIGURE) $(addprefix $(obj)
 	sh -x $(srctree)/$(src)/install.sh "$(KERNELRELEASE)" vmlinux System.map "$(INSTALL_PATH)" $<
 
 # anything not in $(targets)
-clean-files += $(image-) $(initrd-) zImage zImage.initrd cuImage.* treeImage.* \
-	otheros.bld $(kexec-)
+clean-files += $(image-) $(initrd-) zImage zImage.initrd cuImage.* treeImage.*
+clean-files += otheros.bld ppc_rom.bin $(kexec-)
 
 # clean up files cached by wrapper
 clean-kernel := vmlinux.strip vmlinux.bin empty.o.bin
Index: kernel/arch/powerpc/boot/dts/qemu.dts
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ kernel/arch/powerpc/boot/dts/qemu.dts	2007-08-27 20:48:34.000000000 -0500
@@ -0,0 +1,100 @@
+/*
+ * QEMU PReP skeleton device tree
+ * from PReP skeleton device tree
+ *
+ * Milton Miller
+ */
+
+/ {
+	device_type = "qemu";
+	model = "QEMU,PReP";
+	compatible = "qemu-prep";
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		cpu at 0 {
+			device_type = "cpu";
+			reg = <0>;
+			clock-frequency =<d#200000000>;
+			bus-frequency = <d#100000000>;
+			timebase-frequency = <d#25000000>;	/* correct? */
+			i-cache-line-size = <d#32>;
+			d-cache-line-size = <d#32>;
+			d-cache-size = <0>;
+			i-cache-size = <0>;
+
+		};
+	};
+
+	memory {
+		device_type = "memory";
+		// dummy range here, zImage wrapper will fill in the actual
+		// amount of memory from the nvram
+		reg = <00000000 01000000>;
+	};
+
+	pci at 80000000 {
+		device_type = "pci";
+		compatible = "prep-pci";
+		clock-frequency = <01fca055>;
+		reg = <80000000 7ffff>;
+		/* 8259-interrupt-acknowledge = <bffffff0>; */
+		#address-cells = <3>;
+		#size-cells = <2>;
+		ranges=<01000000 00000000 00000000 80000000 00000000 00800000
+			02000000 00000000 00000000 c0000000 00000000 01000000>;
+		interrupt-map-mask = <f800 0 0 7>;
+		interrupt-map = <6000 0 0 1	&PIC8259 6 0
+				 8000 0 0 1	&PIC8259 7 0
+				 9000 0 0 1	&PIC8259 2 0
+				 b000 0 0 1	&PIC8259 1 0>;
+
+		isa {
+			device_type = "isa";
+			#address-cells = <2>;
+			#size-cells = <1>;
+			#interrupt-cells = <2>;
+			ranges = <00000001 00000000
+				  01005800 00000000 00000000  00010000
+				  00000000 00000000
+				  02005800 00000000 00000000  01000000>;
+
+			parallel {
+				device_type = "parallel";
+				compatible = "pnpPNP,401", "pnpPNP,400";
+				reg =  <00000001 000003bc  00000008
+					00000001 000007bc  00000006>;
+				interrupts = <00000007 00000003>;
+				interrupt-parent = <&PIC8259>;
+			};
+
+			serial at 3f8 {
+				device_type = "serial";
+				compatible = "pnpPNP,501", "pnpPNP,500",
+						"ns16550";
+				virtual-reg = <800003f8>;
+				clock-frequency = <001c2000>;
+				reg =  <00000001 000003f8  00000008>;
+				interrupts = <00000004 00000003>;
+				interrupt-parent = <&PIC8259>;
+			};
+			PIC8259: interrupt-controller {
+				device_type = "i8259";
+				compatible = "prep,iic";
+				interrupt-controller;
+				reg = <	00000001 00000020  00000002
+					00000001 000000a0  00000002
+					00000001 000004d0  00000002>;
+			};
+		};
+	};
+
+	chosen {
+		linux,stdout-path = "/pci/isa/serial at 3f8";
+	};
+};
+
Index: kernel/arch/powerpc/platforms/Kconfig
===================================================================
--- kernel.orig/arch/powerpc/platforms/Kconfig	2007-08-24 05:46:36.000000000 -0500
+++ kernel/arch/powerpc/platforms/Kconfig	2007-08-24 05:46:58.000000000 -0500
@@ -47,6 +47,7 @@ source "arch/powerpc/platforms/chrp/Kcon
 source "arch/powerpc/platforms/52xx/Kconfig"
 source "arch/powerpc/platforms/powermac/Kconfig"
 source "arch/powerpc/platforms/prep/Kconfig"
+source "arch/powerpc/platforms/qemu/Kconfig"
 source "arch/powerpc/platforms/maple/Kconfig"
 source "arch/powerpc/platforms/pasemi/Kconfig"
 source "arch/powerpc/platforms/celleb/Kconfig"
Index: kernel/arch/powerpc/boot/wrapper
===================================================================
--- kernel.orig/arch/powerpc/boot/wrapper	2007-08-24 05:46:36.000000000 -0500
+++ kernel/arch/powerpc/boot/wrapper	2007-08-27 22:49:09.000000000 -0500
@@ -320,4 +320,53 @@ ps3)
 
     gzip --force -9 --stdout "$ofile.bin" > "$object/otheros.bld"
     ;;
+qemu)
+    # this part makes the zBoot.qemu a bootrom for qemu.
+    #
+    # the qemu bootrom is 512k , located at the top of the 32 bit address
+    # space.   The fixed entrypoint is address -4.
+
+    copy_rom_start=0x`${CROSS}nm "$ofile" \
+        | grep ' copy_rom_start$'       \
+        | cut -d' ' -f1`
+    copy_rom_start=`printf "%d" $copy_rom_start`
+    copy_rom_end=0x`${CROSS}nm "$ofile" \
+        | grep ' copy_rom_end$'       \
+        | cut -d' ' -f1`
+    copy_rom_end=`printf "%d" $copy_rom_end`
+    rom_start=0x`${CROSS}nm "$ofile" \
+        | grep ' _start$'       \
+        | cut -d' ' -f1`
+
+    rom_start=`printf "%d" $rom_start`
+    rom_size=$((1<<19))
+    copy_rom_len=$(($copy_rom_end-$copy_rom_start))
+    copy_rom_dst=$(($rom_size-$copy_rom_len))
+    copy_rom_src=$(($copy_rom_start-$rom_start))
+
+    img_size=`${CROSS}size "$ofile" | tail -n1 | cut -f4`
+    if [ $img_size -gt $copy_rom_dst ]
+    then
+	echo Image "${ofile##*/}" too big to fit in rom, skipping rom build.
+    else
+	romfile="$object/ppc_rom.bin"
+	tmpfile="$object/ppc_rom.$$"
+
+	rm -f "$tmpfile"
+	${CROSS}objcopy -O binary --pad-to=$(($rom_size+$rom_start)) \
+	    --gap-fill=0xff  "$ofile" "$tmpfile"
+
+	msg=$(dd if="$tmpfile" of="$tmpfile" conv=notrunc \
+	    skip=$copy_rom_src seek=$copy_rom_dst      \
+	    count=$copy_rom_len bs=1 2>&1)
+
+	if [ $? -ne "0" ]; then
+	    echo $msg
+	    rm -f "$tmpfile"
+	    exit 2
+	fi
+
+	mv "$tmpfile" "$romfile"
+    fi
+    ;;
 esac
Index: kernel/arch/powerpc/platforms/qemu/Kconfig
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ kernel/arch/powerpc/platforms/qemu/Kconfig	2007-08-24 05:46:58.000000000 -0500
@@ -0,0 +1,10 @@
+config PPC_QEMU
+	bool "PowerPC Reference Platform (PReP) based QEMU emulated systems"
+	depends on PPC_MULTIPLATFORM && PPC32
+	select PPC_I8259
+	select PPC_INDIRECT_PCI
+	select PPC_UDBG_16550
+	select PPC_NATIVE
+	select WANT_DEVICE_TREE
+	default n
+
Index: kernel/arch/powerpc/platforms/qemu/Makefile
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ kernel/arch/powerpc/platforms/qemu/Makefile	2007-08-24 05:46:58.000000000 -0500
@@ -0,0 +1,2 @@
+obj-y			+= setup.o
+obj-$(CONFIG_PCI)	+= pci.o
Index: kernel/arch/powerpc/platforms/qemu/pci.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ kernel/arch/powerpc/platforms/qemu/pci.c	2007-08-27 20:14:37.000000000 -0500
@@ -0,0 +1,133 @@
+/*
+ * Port to arch/powerpc:
+ * Copyright 2007 David Gibson, IBM Corporation.
+ *
+ * Port to qemu:
+ * Copyright 2007 Milton Miller, IBM Corporation.
+ *
+ * Based on OpenHackware 0.4
+ * Copyright (c) 2004-2005 Jocelyn Mayer
+ *
+ * pci config based on arch/powerpc/platforms/chrp/pci.c GoldenGate code
+ *
+ */
+
+#include <linux/init.h>
+
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/pci-bridge.h>
+#include <asm/udbg.h>
+
+static volatile void __iomem *qemu_config_addr(struct pci_bus *bus,
+	unsigned int devfn, int off)
+{
+	int dev, fn;
+	struct pci_controller *hose = bus->sysdata;
+
+	if (!hose->cfg_data)
+		return NULL;
+
+	if (bus->number != 0)
+		return NULL;
+
+	dev = devfn >> 3;
+	fn = devfn & 7;
+
+	if (dev < 11 || dev > 21)
+		return NULL;
+
+	return hose->cfg_data + ((1 << dev) | (fn << 8) | off);
+}
+
+int qemu_read_config(struct pci_bus *bus, unsigned int devfn, int off,
+			   int len, u32 *val)
+{
+	volatile void __iomem *cfg_data = qemu_config_addr(bus, devfn, off);
+
+	/* check, should this be 0? */
+	if (cfg_data == NULL)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	/*
+	 * Note: the caller has already checked that off is
+	 * suitably aligned and that len is 1, 2 or 4.
+	 */
+	switch (len) {
+	case 1:
+		*val =  in_8(cfg_data);
+		break;
+	case 2:
+		*val = in_le16(cfg_data);
+		break;
+	default:
+		*val = in_le32(cfg_data);
+		break;
+	}
+	return PCIBIOS_SUCCESSFUL;
+}
+
+int qemu_write_config(struct pci_bus *bus, unsigned int devfn, int off,
+			    int len, u32 val)
+{
+	volatile void __iomem *cfg_data = qemu_config_addr(bus, devfn, off);
+
+	if (cfg_data == NULL)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	/*
+	 * Note: the caller has already checked that off is
+	 * suitably aligned and that len is 1, 2 or 4.
+	 */
+	switch (len) {
+	case 1:
+		out_8(cfg_data, val);
+		break;
+	case 2:
+		out_le16(cfg_data, val);
+		break;
+	default:
+		out_le32(cfg_data, val);
+		break;
+	}
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops qemu_pci_ops =
+{
+	qemu_read_config,
+	qemu_write_config
+};
+
+void __init qemu_find_bridges(void)
+{
+	struct device_node *phb;
+	struct pci_controller *hose;
+
+	phb = of_find_node_by_type(NULL, "pci");
+	if (!phb) {
+		printk(KERN_ERR "PReP: Cannot find PCI bridge OF node\n");
+		return;
+	}
+
+	hose = pcibios_alloc_controller(phb);
+	if (!hose)
+		return;
+
+	pci_process_bridge_OF_ranges(hose, phb, 1);
+
+#define PREP_PCI_DRAM_OFFSET 	0x80000000
+
+	pci_dram_offset = PREP_PCI_DRAM_OFFSET;
+	ISA_DMA_THRESHOLD = 0x00ffffff;
+	DMA_MODE_READ = 0x44;
+	DMA_MODE_WRITE = 0x48;
+
+	hose->cfg_data = ioremap(0x80000000, 1 << 22);
+
+	hose->ops = &qemu_pci_ops;
+
+	udbg_init_uart(hose->io_base_virt + 0x3f8, 0, 0);
+	register_early_udbg_console();
+	printk(KERN_INFO "qemu_find_bridges: config at %p\n", hose->cfg_data);
+}
+
Index: kernel/arch/powerpc/platforms/qemu/setup.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ kernel/arch/powerpc/platforms/qemu/setup.c	2007-08-27 20:24:59.000000000 -0500
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 1995  Linus Torvalds
+ * Adapted from 'alpha' version by Gary Thomas
+ * Modified by Cort Dougan (cort at cs.nmt.edu)
+ *
+ * Support for PReP (Motorola MTX/MVME)
+ * by Troy Benjegerdes (hozer at drgw.net)
+ *
+ * Port to arch/powerpc:
+ * Copyright 2007 David Gibson, IBM Corporation.
+ *
+ * Port to qemu:
+ * Copyright 2007 Milton Miller, IBM Corporation.
+ *
+ * Based on OpenHackware 0.4
+ * Copyright (c) 2004-2005 Jocelyn Mayer
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/seq_file.h>
+#include <linux/initrd.h>
+#include <linux/ide.h>
+
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/pci-bridge.h>
+/* #include <asm/mpic.h> */
+#include <asm/i8259.h>
+#include <asm/time.h>
+#include <asm/udbg.h>
+
+static const char *qemu_model = "(unknown)";
+
+extern void qemu_find_bridges(void);
+
+#if 0
+
+/* useful ISA ports */
+#define PREP_SYSCTL	0x81c
+/* present in the IBM reference design; possibly identical in Mot boxes: */
+#define PREP_IBM_PM1	0x82a	/* power management register 1 */
+#define PREP_IBM_PLANAR	0x852	/* planar ID - identifies the motherboard */
+#define PREP_IBM_DISP	0x8c0	/* 4-digit LED display */
+
+
+
+
+/* Used by all Motorola PReP */
+static void prep_restart(char *cmd)
+{
+	local_irq_disable(); /* no interrupts */
+
+	/* set exception prefix high - to the prom */
+	mtmsr(mfmsr() | MSR_IP);
+
+	/* make sure bit 0 (reset) is a 0 */
+	outb(inb(0x92) & ~1L, 0x92);
+	/* signal a reset to system control port A - soft reset */
+	outb(inb(0x92) | 1, 0x92);
+
+	for (;;)
+		;
+	/* not reached */
+}
+
+static void prep_halt(void)
+{
+	local_irq_disable(); /* no interrupts */
+
+	/* set exception prefix high - to the prom */
+	mtmsr(mfmsr() | MSR_IP);
+
+	for (;;)
+		;
+	/* not reached */
+}
+
+/*
+ * On most IBM PReP's, power management is handled by a Signetics 87c750
+ * behind the Utah component on the ISA bus. To access the 750 you must write
+ * a series of nibbles to port 0x82a (decoded by the Utah). This is described
+ * somewhat in the IBM Carolina Technical Specification.
+ * -Hollis
+ */
+static void
+utah_sig87c750_setbit(unsigned int bytenum, unsigned int bitnum, int value)
+{
+	/*
+	 * byte1: 0 0 0 1 0  d  a5 a4
+	 * byte2: 0 0 0 1 a3 a2 a1 a0
+	 *
+	 * d = the bit's value, enabled or disabled
+	 * (a5 a4 a3) = the byte number, minus 20
+	 * (a2 a1 a0) = the bit number
+	 *
+	 * example: set the 5th bit of byte 21 (21.5)
+	 *     a5 a4 a3 = 001 (byte 1)
+	 *     a2 a1 a0 = 101 (bit 5)
+	 *
+	 *     byte1 = 0001 0100 (0x14)
+	 *     byte2 = 0001 1101 (0x1d)
+	 */
+	unsigned char byte1=0x10, byte2=0x10;
+
+	/* the 750's '20.0' is accessed as '0.0' through Utah (which adds 20) */
+	bytenum -= 20;
+
+	byte1 |= (!!value) << 2;		/* set d */
+	byte1 |= (bytenum >> 1) & 0x3;	/* set a5, a4 */
+
+	byte2 |= (bytenum & 0x1) << 3;	/* set a3 */
+	byte2 |= bitnum & 0x7;			/* set a2, a1, a0 */
+
+	outb(byte1, PREP_IBM_PM1);	/* first nibble */
+	mb();
+	udelay(100);				/* important: let controller recover */
+
+	outb(byte2, PREP_IBM_PM1);	/* second nibble */
+	mb();
+	udelay(100);				/* important: let controller recover */
+}
+
+static void prep_sig750_poweroff(void)
+{
+	/* tweak the power manager found in most IBM PRePs (except Thinkpads) */
+
+	local_irq_disable();
+	/* set exception prefix high - to the prom */
+	mtmsr(mfmsr() | MSR_IP);
+
+	utah_sig87c750_setbit(21, 5, 1); /* set bit 21.5, "PMEXEC_OFF" */
+
+	for (;;)
+		;
+	/* not reached */
+}
+
+#endif
+
+/* cpuinfo code common to all IBM PReP */
+static void qemu_ibm_cpuinfo(struct seq_file *m)
+{
+	seq_printf(m, "machine\t\t: PReP %s\n", qemu_model);
+}
+
+#define NVRAM_AS0 0x74
+#define NVRAM_AS1 0x75
+#define NVRAM_DAT 0x77
+
+static unsigned char qemu_nvram_read_val(int addr)
+{
+	outb(NVRAM_AS0, addr & 0xff);
+	outb(NVRAM_AS1, (addr >> 8) & 0xff);
+	return inb(NVRAM_DAT);
+}
+
+
+static void qemu_nvram_write_val(int addr, unsigned char val)
+{
+	outb(NVRAM_AS0, addr & 0xff);
+	outb(NVRAM_AS1, (addr >> 8) & 0xff);
+	outb(NVRAM_DAT, val);
+}
+
+
+static void __init qemu_setup_arch(void)
+{
+	struct device_node *root;
+	const char *model;
+
+	root = of_find_node_by_path("/");
+	model = of_get_property(root, "model", NULL);
+	of_node_put(root);
+	if (model)
+		qemu_model = model;
+
+	/* init to some ~sane value until calibrate_delay() runs */
+	/* loops_per_jiffy = 50000000; */
+
+	/* Lookup PCI host bridges */
+	qemu_find_bridges();
+
+	/* Read in NVRAM data */
+/* 	init_qemu_nvram(); */
+}
+
+static void __init qemu_find_8259(void)
+{
+	struct device_node *pic = NULL;
+	unsigned long int_ack = 0;
+
+	pic = of_find_node_by_type(NULL, "i8259");
+	if (!pic) {
+		printk(KERN_ERR "No interrupt controller found!\n");
+		return;
+	}
+
+	/* polling */
+	i8259_init(pic, int_ack);
+	ppc_md.get_irq = i8259_irq;
+
+	/* hack: set default host until interrupt map and pci fixed */
+	irq_set_default_host(i8259_get_host());
+}
+
+static void __init qemu_init_IRQ(void)
+{
+	qemu_find_8259();
+}
+
+#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
+/*
+ * IDE stuff.
+ */
+static int qemu_ide_default_irq(unsigned long base)
+{
+	switch (base) {
+	case 0x1f0: return 13;
+	case 0x170: return 13;
+	case 0x1e8: return 11;
+	case 0x168: return 10;
+	case 0xfff0: return 14;		/* MCP(N)750 ide0 */
+	case 0xffe0: return 15;		/* MCP(N)750 ide1 */
+	default: return 0;
+	}
+}
+
+static unsigned long qemu_ide_default_io_base(int index)
+{
+	switch (index) {
+	case 0: return 0x1f0;
+	case 1: return 0x170;
+	case 2: return 0x1e8;
+	case 3: return 0x168;
+	default:
+		return 0;
+	}
+}
+#endif
+
+#if 0
+static int __init prep_request_io(void)
+{
+#ifdef CONFIG_NVRAM
+	request_region(PREP_NVRAM_AS0, 0x8, "nvram");
+#endif
+	request_region(0x00,0x20,"dma1");
+	request_region(0x40,0x20,"timer");
+	request_region(0x80,0x10,"dma page reg");
+	request_region(0xc0,0x20,"dma2");
+
+	return 0;
+}
+device_initcall(prep_request_io);
+#endif
+
+
+static int __init qemu_probe(void)
+{
+	if (! of_flat_dt_is_compatible(of_get_flat_dt_root(), "qemu-prep"))
+		return 0;
+
+#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
+	ppc_ide_md.default_irq = qemu_ide_default_irq;
+	ppc_ide_md.default_io_base = qemu_ide_default_io_base;
+#endif
+
+	return 1;
+}
+
+define_machine(qemu) {
+	.name			= "QEMU",
+	.probe			= qemu_probe,
+	.setup_arch		= qemu_setup_arch,
+	.progress		= udbg_progress,
+	.show_cpuinfo		= qemu_ibm_cpuinfo,
+	.init_IRQ		= qemu_init_IRQ,
+/* 	.pcibios_fixup		= qemu_pcibios_fixup, */
+/*	.restart		= qemu_restart, */
+/*	.power_off		= qemu_halt, */
+/*	.halt			= qemu_halt, */
+/* 	.time_init		= todc_time_init, */
+/* 	.set_rtc_time		= todc_set_rtc_time, */
+/* 	.get_rtc_time		= todc_get_rtc_time, */
+	.calibrate_decr		= generic_calibrate_decr,
+ 	.nvram_read_val		= qemu_nvram_read_val,
+ 	.nvram_write_val	= qemu_nvram_write_val,
+	.phys_mem_access_prot	= pci_phys_mem_access_prot,
+};
Index: kernel/arch/powerpc/platforms/Makefile
===================================================================
--- kernel.orig/arch/powerpc/platforms/Makefile	2007-08-24 05:46:36.000000000 -0500
+++ kernel/arch/powerpc/platforms/Makefile	2007-08-24 05:46:58.000000000 -0500
@@ -7,6 +7,7 @@ endif
 endif
 obj-$(CONFIG_PPC_CHRP)		+= chrp/
 obj-$(CONFIG_PPC_PREP)		+= prep/
+obj-$(CONFIG_PPC_QEMU)		+= qemu/
 #obj-$(CONFIG_4xx)		+= 4xx/
 obj-$(CONFIG_44x)		+= 44x/
 obj-$(CONFIG_PPC_MPC52xx)	+= 52xx/
Index: kernel/arch/powerpc/boot/.gitignore
===================================================================
--- kernel.orig/arch/powerpc/boot/.gitignore	2007-08-27 21:10:42.000000000 -0500
+++ kernel/arch/powerpc/boot/.gitignore	2007-08-27 21:11:02.000000000 -0500
@@ -17,6 +17,7 @@ infutil.h
 kernel-vmlinux.strip.c
 kernel-vmlinux.strip.gz
 mktree
+ppc_rom.bin
 uImage
 cuImage.*
 zImage



More information about the Linuxppc-dev mailing list