All of lore.kernel.org
 help / color / mirror / Atom feed
From: Milton Miller <miltonm@bga.com>
To: <linuxppc-dev@ozlabs.org>
Cc: firmware@them.com
Subject: [PATCH] qemu-system-ppc -prep initial port
Date: Mon, 27 Aug 2007 23:03:29 -0500 (CDT)	[thread overview]
Message-ID: <miltonm-qemu-0@bga.com> (raw)

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@bga.com>
+ * Copyright (C) 2006 Paul Mackerras, IBM Corp. <paulus@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@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@ha\n"
+"	addi	5,5,_end@l\n"
+"	lis	3,_start@ha\n"
+"	addi	3,3,_start@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@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@h\n"
+"	ori	0,0,_zimage_start@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@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@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@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@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@cs.nmt.edu)
+ *
+ * Support for PReP (Motorola MTX/MVME)
+ * by Troy Benjegerdes (hozer@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

                 reply	other threads:[~2007-08-28  4:03 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=miltonm-qemu-0@bga.com \
    --to=miltonm@bga.com \
    --cc=firmware@them.com \
    --cc=linuxppc-dev@ozlabs.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.