diff --git a/Makefile b/Makefile
index 16ef1df..9bd559d 100644
--- a/Makefile
+++ b/Makefile
@@ -16,7 +16,8 @@ initddir = $(etcdir)/init.d
 
 MANPAGES = doc/iscsid.8 doc/iscsiadm.8 doc/iscsi_discovery.8
 PROGRAMS = usr/iscsid usr/iscsiadm utils/iscsi_discovery \
-		   utils/fwparam_ibft/fwparam_ibft utils/iscsi-iname
+		   utils/fwparam_ibft/fwparam_ibft \
+		   utils/fwparam_ppc/fwparam_ppc utils/iscsi-iname
 INSTALL = install
 ETCFILES = etc/iscsid.conf
 IFACEFILES = etc/iface.example
@@ -30,6 +31,7 @@ all:
 	$(MAKE) -C kernel
 	$(MAKE) -C utils
 	$(MAKE) -C utils/fwparam_ibft
+	$(MAKE) -C utils/fwparam_ppc
 	@echo
 	@echo "Compilation complete                Output file"
 	@echo "----------------------------------- ----------------"
@@ -39,6 +41,7 @@ all:
 	@echo "Built iSCSI daemon:                 usr/iscsid"
 	@echo "Built management application:       usr/iscsiadm"
 	@echo "Built utility:                      utils/fwparam_ibft/fwparam_ibft"
+	@echo "Built utility:                      utils/fwparam_ppc/fwparam_ppc"
 	@echo
 	@echo Read README file for detailed information.
 
@@ -47,6 +50,7 @@ clean:
 	$(MAKE) -C usr clean
 	$(MAKE) -C kernel clean
 	$(MAKE) -C utils/fwparam_ibft clean
+	$(MAKE) -C utils/fwparam_ppc clean
 
 # this is for safety
 # now -jXXX will still be safe
@@ -117,7 +121,7 @@ install_iname:
 	fi
 
 depend:
-	for dir in usr utils utils/fwparam_ibft; do \
+	for dir in usr utils utils/fwparam_ibft utils/fwparam_ppc; do \
 		$(MAKE) -C $$dir $@; \
 	done
 
diff --git a/utils/fwparam_ppc/Makefile b/utils/fwparam_ppc/Makefile
new file mode 100644
index 0000000..ac5754c
--- /dev/null
+++ b/utils/fwparam_ppc/Makefile
@@ -0,0 +1,69 @@
+#
+#  Copyright (c) 2006,2007 IBM Corporation Doug Maxey <dwm@austin.ibm.com>
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+#
+#  Copyright (C) IBM Corporation. 2007
+#  Author: Doug Maxey <dwm@austin.ibm.com>
+#
+#
+
+prog = fwparam_ppc
+objects = prom_lex.o prom_parse.tab.o fwparam_ppc_main.o
+
+OPTFLAGS ?= -O2
+WARNFLAGS ?= -Wall
+CFLAGS += $(OPTFLAGS) $(WARNFLAGS)
+
+GENFILES = prom_lex.c prom_parse.tab.c prom_parse.tab.h
+
+BISONFLAGS = -d
+FLEXFLAGS =  -t
+
+# turn off #line number markers
+NOL = 0
+ifneq (0 ,$(NOL))
+	FLEXFLAGS += -L
+	BISONFLAGS +=  --no-lines
+endif
+
+all: $(prog)
+
+${prog} : $(objects) Makefile
+	${CC} -o ${prog} $(objects)
+
+ %.c : %.l
+	flex ${FLEXFLAGS} $< | perl -pe '/define YYLMAX/ && s{8192}{2048}' >$@
+
+ %.tab.c %.tab.h: %.y
+	bison  ${BISONFLAGS} $<
+
+${GENFILES}: Makefile
+
+# standalone version of the lexer.
+prom_lex: prom_lex.c
+	${CC} ${CFLAGS} -DSTANDALONE -DLEXDEBUG -o $@ $<
+
+${objects}: prom_parse.tab.h prom_parse.h
+
+.PHONY : clean
+clean :
+	-rm -f ${prog} $(objects) prom_lex *.output *~
+
+clean-gen : clean
+	rm -f ${GENFILES}
diff --git a/utils/fwparam_ppc/fwparam_ppc_main.c b/utils/fwparam_ppc/fwparam_ppc_main.c
new file mode 100644
index 0000000..a21e5ec
--- /dev/null
+++ b/utils/fwparam_ppc/fwparam_ppc_main.c
@@ -0,0 +1,429 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * Copyright (C) IBM Corporation. 2007
+ * Author: Doug Maxey <dwm@austin.ibm.com>
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "iscsi_obp.h"
+#include "prom_parse.h"
+
+void* yy_scan_string(const char *str);
+int yyparse(struct ofw_dev *ofwdev);
+
+#define MAC_FILE "/mac-address"
+
+char *progname;
+int debug;
+char default_file_name[] = "/proc/device-tree/choices/bootpath";
+char *filename = default_file_name;
+char *bootpath_val;
+char *mac_path;
+char *mac_val;
+int boot_selected_only;
+int bytes_read;
+char *prefix;
+
+/*
+ * Prefix strings, for the "prefixN:NAME=value".
+ */
+#define NETWORK		"network"
+#define INITIATOR	"iscsi-initiator"
+#define TGT		"target"
+
+void pr_param(const char *prefix, int instance, const char *item, char *param)
+{
+	fprintf(stdout, " %s%d:%s=%s", prefix, instance, item, param);
+}
+
+/*
+ * Take the path to the property under chosen, and swizzle to make that
+ * the base for the device path discovered.
+ */
+int locate_print_mac(struct ofw_dev *ofwdev)
+{
+	int error = 0;
+	char *dt_top = strdup(filename);
+	char *chop_at;
+	int mac_path_len = strlen(ofwdev->dev_path) + strlen(MAC_FILE) + 2;
+	char *mac_file;
+	int mac_fd;
+
+	/*
+	 * FIXME: We need to walk the entire tree for other ethernet
+	 * or iscsi-toe devices to get the device instance (eth0...).
+	 * Since the only platform this works on ATM is blade, and
+	 * pseries blades must use eth1 (or higher), use that for now.
+	 */
+
+	/*
+	 * What is the path to the device-tree?  The only valid
+	 * directories to locate the property are under /aliases or
+	 * /chosen.
+	 */
+
+	chop_at = strstr(dt_top, "/chosen");
+	if (!chop_at)
+		chop_at = strstr(dt_top, "/aliases");
+
+	if (!chop_at) {
+		error = ENXIO;
+		goto lpm_bail;
+	}
+
+	dt_top[chop_at - dt_top] = 0;
+	mac_path_len += strlen(dt_top);
+	mac_file = malloc(mac_path_len);
+	if (!mac_file) {
+		error = ENOMEM;
+		fprintf(stderr, "%s: malloc %s, %s\n", progname, filename,
+			strerror(errno));
+		goto lpm_bail;
+	}
+
+	snprintf(mac_file, mac_path_len, "%s/%s%s", dt_top, ofwdev->dev_path, MAC_FILE);
+	mac_fd = open(mac_file, O_RDONLY);
+	if (mac_fd < 0) {
+		error = errno;
+		fprintf(stderr, "%s: open %s, %s\n", progname, mac_file,
+			strerror(errno));
+		goto lpm_bail;
+	}
+
+	bytes_read = read(mac_fd, ofwdev->mac, 6);
+	if (bytes_read != 6) {
+		error = EIO;
+		fprintf(stderr, "%s: read %s, %s\n", progname, mac_file,
+			strerror(errno));
+		goto lpm_bail;
+	}
+	close(mac_fd);
+
+	fprintf(stdout, "%s0:HWADDR=%02x:%02x:%02x:%02x:%02x:%02x ", prefix,
+		ofwdev->mac[0], ofwdev->mac[1], ofwdev->mac[2],
+		ofwdev->mac[3], ofwdev->mac[4], ofwdev->mac[5]);
+
+lpm_bail:
+	return error;
+}
+
+int print_params(struct ofw_dev *ofwdev)
+{
+	int error = 0;
+
+	prefix = "iSCSI_INITIATOR_";
+
+	error = locate_print_mac(ofwdev);
+	if (error)
+		goto print_bail;
+
+	/*
+	 * We have all the strings present.  Now print them.
+	 */
+	if (ofwdev->param[OBP_PARAM_INAME])
+		pr_param(prefix, 0, "NAME", ofwdev->param[OBP_PARAM_INAME]->val);
+	if (ofwdev->param[OBP_PARAM_CIADDR])
+		pr_param(prefix, 0, "IPADDR", ofwdev->param[OBP_PARAM_CIADDR]->val);
+	if (ofwdev->param[OBP_PARAM_SUBNET_MASK])
+		pr_param(prefix, 0, "MASK", ofwdev->param[OBP_PARAM_SUBNET_MASK]->val);
+
+	prefix = "target";
+	if (ofwdev->param[OBP_PARAM_SIADDR])
+		pr_param(prefix, 0, "IPADDR", ofwdev->param[OBP_PARAM_SIADDR]->val);
+	if (ofwdev->param[OBP_PARAM_IPORT])
+		pr_param(prefix, 0, "PORT", ofwdev->param[OBP_PARAM_IPORT]->val);
+	if (ofwdev->param[OBP_PARAM_ILUN])
+		pr_param(prefix, 0, "LUN", ofwdev->param[OBP_PARAM_ILUN]->val);
+	if (ofwdev->param[OBP_PARAM_ITNAME])
+		pr_param(prefix, 0, "NAME", ofwdev->param[OBP_PARAM_ITNAME]->val);
+	if (ofwdev->param[OBP_PARAM_ISID])
+		pr_param(prefix, 0, "ISID", ofwdev->param[OBP_PARAM_ISID]->val);
+
+	/*
+	 * chap stuff is always associated with the target
+	 */
+	if (ofwdev->param[OBP_PARAM_ICHAPID])
+		pr_param(prefix, 0, "CHAP_NAME", ofwdev->param[OBP_PARAM_ICHAPID]->val);
+	if (ofwdev->param[OBP_PARAM_CHAPPW])
+		pr_param(prefix, 0, "CHAP_NAME", ofwdev->param[OBP_PARAM_ICHAPPW]->val);
+	if (ofwdev->param[OBP_PARAM_CHAPID])
+		pr_param(prefix, 0, "CHAP_NAME_IN", ofwdev->param[OBP_PARAM_CHAPID]->val);
+	if (ofwdev->param[OBP_PARAM_CHAPPW])
+		pr_param(prefix, 0, "CHAP_PASSWORD_IN", ofwdev->param[OBP_PARAM_CHAPPW]->val);
+
+	putchar('\n');
+
+print_bail:
+	return error;
+}
+
+const char *obp_qual_set(struct ofw_dev *ofwdev, const char *qual)
+{
+	if (!strcmp("bootp", qual))
+		ofwdev->quals[ofwdev->qual_count++] = OBP_QUAL_BOOTP;
+	else if (!strcmp("dhcp", qual))
+		ofwdev->quals[ofwdev->qual_count++] = OBP_QUAL_DHCP;
+	else if (!strcmp("ipv6", qual))
+		ofwdev->quals[ofwdev->qual_count++] = OBP_QUAL_IPV6;
+	else if (!strcmp("iscsi", qual)) {
+		ofwdev->type = OFW_DT_ISCSI;
+		ofwdev->quals[ofwdev->qual_count++] = OBP_QUAL_ISCSI;
+	}
+	else if (!strcmp("isns", qual))
+		ofwdev->quals[ofwdev->qual_count++] = OBP_QUAL_ISNS;
+	else if (!strcmp("ping", qual))
+		ofwdev->quals[ofwdev->qual_count++] = OBP_QUAL_PING;
+	else if (!strcmp("slp", qual))
+		ofwdev->quals[ofwdev->qual_count++] = OBP_QUAL_SLP;
+	else
+		printf("%s: %s UNKNOWN\n", __func__, qual);
+	return qual;
+}
+
+void add_obp_parm(struct ofw_dev *ofwdev, enum obp_param parm, const char *str)
+{
+	int psz = sizeof(struct ofw_obp_param) + strlen(str);
+
+	ofwdev->param[parm] = malloc(psz);
+	if (ofwdev->param[parm] == NULL) {
+		printf("%s: ENOMEM!\n", __func__);
+		return;
+	}
+	memset(ofwdev->param[parm], 0, psz);
+	ofwdev->param[parm]->len = psz;
+	strcpy(ofwdev->param[parm]->val, str);
+}
+
+void obp_parm_addr(struct ofw_dev *ofwdev, const char *parm, const char *addr)
+{
+	if (!strcmp("siaddr", parm))
+		add_obp_parm(ofwdev, OBP_PARAM_SIADDR, addr);
+	else if (!strcmp("ciaddr", parm))
+		add_obp_parm(ofwdev, OBP_PARAM_CIADDR, addr);
+	else if (!strcmp("giaddr", parm))
+		add_obp_parm(ofwdev, OBP_PARAM_GIADDR, addr);
+	else if (!strcmp("subnet-mask", parm))
+		add_obp_parm(ofwdev, OBP_PARAM_SUBNET_MASK, addr);
+	else
+		printf("%s: %s UNKNOWN\n", __func__, parm);
+}
+
+void obp_parm_iqn(struct ofw_dev *ofwdev, const char *parm, const char *iqn)
+{
+	if (!strcmp("itname", parm))
+		add_obp_parm(ofwdev, OBP_PARAM_ITNAME, iqn);
+	else if (!strcmp("iname", parm))
+		add_obp_parm(ofwdev, OBP_PARAM_INAME, iqn);
+	else
+		printf("%s: %s UNKNOWN\n", __func__, parm);
+}
+
+void obp_parm_hexnum(struct ofw_dev *ofwdev, const char *parm, const char *numstr)
+{
+	if (!strcmp("bootp-retries", parm))
+		add_obp_parm(ofwdev, OBP_PARAM_BOOTP_RETRIES, numstr);
+	else if (!strcmp("tftp-retries", parm))
+		add_obp_parm(ofwdev, OBP_PARAM_TFTP_RETRIES, numstr);
+	else if (!strcmp("iport", parm))
+		add_obp_parm(ofwdev, OBP_PARAM_IPORT, numstr);
+	else if (!strcmp("ilun", parm))
+		add_obp_parm(ofwdev, OBP_PARAM_ILUN, numstr);
+	else if (!strcmp("isid", parm))
+		add_obp_parm(ofwdev, OBP_PARAM_ISID, numstr);
+	else
+		printf("%s: %s UNKNOWN <%s>\n", __func__, parm, numstr);
+}
+
+void obp_parm_str(struct ofw_dev *ofwdev, const char *parm, const char *str)
+{
+	if (!strcmp("filename", parm))
+		add_obp_parm(ofwdev, OBP_PARAM_FILENAME, str);
+	else if (!strcmp("ichapid", parm))
+		add_obp_parm(ofwdev, OBP_PARAM_ICHAPID, str);
+	else if (!strcmp("ichappw", parm))
+		add_obp_parm(ofwdev, OBP_PARAM_ICHAPPW, str);
+	else if (!strcmp("chapid", parm))
+		add_obp_parm(ofwdev, OBP_PARAM_CHAPID, str);
+	else if (!strcmp("chappw", parm))
+		add_obp_parm(ofwdev, OBP_PARAM_CHAPPW, str);
+	else
+		printf("%s: %s UNKNOWN <%s>\n", __func__, parm, str);
+}
+
+void yyerror(struct ofw_dev *ofwdev, const char *msg)
+{
+#ifdef YYLTYPE
+    fprintf(stderr, "error at l%d.c%d\n", yylloc.last_line, yylloc.last_column);
+#else
+    fprintf(stderr, "error at??\n");
+#endif
+}
+
+void dump_obp_params(struct ofw_dev *dev)
+{
+	static const char *param_name[] = {
+		"blocksize",
+		"bootp-retries",
+		"target-chapid",
+		"target-chappwd",
+		"target-addr",
+		"filename",
+		"gateway",
+		"my-chapid",
+		"my-chappwd",
+		"target-lun",
+		"my-name",
+		"target-port",
+		"target-isid",
+		"target-name",
+		"my-addr",
+		"subnet-mask",
+		"tftp-retries",
+		"timeout"
+	};
+	int i;
+
+	for (i = OBP_PARAM_BLKSIZE; i < OBP_PARAM_COUNT; i++) {
+		if (dev->param[i])
+			pr_param("foo", 0, param_name[i], dev->param[i]->val);
+	}
+}
+
+int parse_params(const char *buf, struct ofw_dev *ofwdev)
+{
+	int error = 0;
+#if YYDEBUG
+	yydebug = 1;
+#endif
+
+
+	if (yy_scan_string(buf))
+		error = yyparse(ofwdev);
+
+	return error;
+}
+
+int
+main(int argc, char **argv)
+{
+	int fd, option, error;
+	struct stat bootpath_stat;
+	struct ofw_dev ofwdev = OFW_DEV_INIT;
+
+
+	progname = argv[0];
+
+	/*
+	 * For powerpc, our operations are fundamentally different.
+	 *
+	 * Where the x86 method searches memory, we look in the
+	 * ppc procfs device-tree to obtain the data.
+	 *
+	 */
+	while (1) {
+		option = getopt(argc, argv, "f:m:s:e:vhb");
+		if (option == -1)
+			break;
+		switch (option) {
+		case 'b':
+			boot_selected_only = 1;
+			break;
+		case 'e':
+			break;
+		case 'f':
+			filename = optarg;
+			break;
+		case 's':
+			break;
+		case 'v':
+			debug++;
+			break;
+		default:
+			fprintf(stderr, "Unknown or bad option '%c'\n", option);
+		case 'h':
+			printf("Usage: %s OPTIONS\n"
+			       "-b (x86 only) print only fw boot selected sections\n"
+			       "-f file_to_search (default %s)\n"
+			       "-s (x86 only) offset to start search\n"
+			       "-e (x86 only) length of search\n"
+			       "-v verbose\n",
+			       progname, default_file_name);
+			exit(1);
+		}
+	}
+
+	if (debug)
+		fprintf(stderr, "%s: file:%s; debug:%d\n", progname, filename, debug);
+
+	error = stat(filename, &bootpath_stat);
+	if (error < 0) {
+		fprintf(stderr, "%s: stat %s, %s\n", progname, filename, strerror(errno));
+		exit(errno);
+	}
+
+	bootpath_val = malloc(bootpath_stat.st_size);
+	if (!bootpath_val) {
+		fprintf(stderr, "%s: Could not open %s: %s (%d)\n",
+			progname, filename, strerror(ENOMEM), ENOMEM);
+		exit(ENOMEM);
+	}
+
+	fd = open(filename, O_RDONLY);
+	if (fd < 0) {
+		fprintf(stderr, "%s: Could not open %s: %s (%d)\n",
+			progname, filename, strerror(errno), errno);
+		exit(1);
+	}
+
+	bytes_read = read(fd, bootpath_val, bootpath_stat.st_size);
+	close(fd);
+	if (bytes_read != bootpath_stat.st_size) {
+		error = EIO;
+		fprintf(stderr, "%s: Could not open %s: %s (%d)\n",
+			progname, filename, strerror(error), error);
+		exit(error);
+	}
+
+	close(fd);
+
+	/*
+	 * We should find *almost* everything we need in the bootpath,
+	 * save the mac-address.
+	 */
+	if (strstr(bootpath_val, "iscsi"))
+		error = parse_params(bootpath_val, &ofwdev);
+	else
+		/* yikes! we did not boot from iscsi.  tsk, tsk.  */
+		error = 1;
+
+	if (!error) {
+		if (debug)
+			dump_obp_params(&ofwdev);
+
+		error = print_params(&ofwdev);
+	}
+	exit(error);
+}
diff --git a/utils/fwparam_ppc/iscsi_obp.h b/utils/fwparam_ppc/iscsi_obp.h
new file mode 100644
index 0000000..92a3af1
--- /dev/null
+++ b/utils/fwparam_ppc/iscsi_obp.h
@@ -0,0 +1,112 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * Copyright (C) IBM Corporation. 2007
+ * Author: Doug Maxey <dwm@austin.ibm.com>
+ *
+ */
+
+#ifndef ISCSI_OBP_H_
+#define ISCSI_OBP_H_
+
+enum  ofw_dev_type {
+	OFW_DT_NONE,
+	OFW_DT_BLOCK,
+	OFW_DT_NETWORK,
+	OFW_DT_ISCSI,
+};
+
+enum obp_tftp_qual {
+    OBP_QUAL_NONE,
+    OBP_QUAL_BOOTP,
+    OBP_QUAL_DHCP,
+    OBP_QUAL_IPV6,
+    OBP_QUAL_ISCSI,
+    OBP_QUAL_ISNS,
+    OBP_QUAL_PING,
+    OBP_QUAL_SLP,
+    OBP_QUAL_COUNT,		/* Numnber of defined OBP qualifiers */
+};
+
+enum obp_param {
+	OBP_PARAM_NONE,
+	OBP_PARAM_BLKSIZE,
+	OBP_PARAM_BOOTP_RETRIES,
+	OBP_PARAM_CHAPID,	/* target chap id */
+	OBP_PARAM_CHAPPW,	/* target chap password */
+	OBP_PARAM_CIADDR,	/* client ip addr */
+	OBP_PARAM_FILENAME,	/* boot filename */
+	OBP_PARAM_GIADDR,	/* gateway addr */
+	OBP_PARAM_ICHAPID,	/* initiator chapid */
+	OBP_PARAM_ICHAPPW,	/* initiator chap password */
+	OBP_PARAM_ILUN,		/* misnomer, really the target lun */
+	OBP_PARAM_INAME,	/* initiator iqn */
+	OBP_PARAM_IPORT,	/* initiator port, defaults to 3260 */
+	OBP_PARAM_ISID,		/* session id */
+	OBP_PARAM_ITNAME,	/* target iqn */
+	OBP_PARAM_SIADDR,	/* server ip address. */
+	OBP_PARAM_SUBNET_MASK,
+	OBP_PARAM_TFTP_RETRIES,
+	OBP_PARAM_TIMEOUT,	/* ping timeout period. */
+
+	OBP_PARAM_COUNT,	/* number of defined OBP_PARAMs */
+};
+
+struct ofw_obp_param {
+	unsigned char  len;	/* length of value string. */
+	char           val[1];	/* string value from the property */
+};
+
+struct ofw_node {
+	char          driver_name[32];
+	char          unit_address[31];
+	unsigned char dev_arg_len;
+	char          device_arguments[1];
+};
+
+struct ofw_dev {
+	enum ofw_dev_type type;
+	int qual_count;
+	enum obp_tftp_qual quals[OBP_QUAL_COUNT];
+	int dev_path_len;
+	char *dev_path;
+	int node_depth;
+	struct ofw_node *nodes;	/* nodes in path to device */
+	struct ofw_obp_param *param[OBP_PARAM_COUNT];
+	int cfg_filepath_len;
+	char *cfg_filepath;
+	int cfg_part;
+	unsigned char mac[6];
+};
+
+#define OFW_DEV_INIT {				\
+	.type = OFW_DT_BLOCK,			\
+	.dev_path = NULL,			\
+	.node_depth = 0,			\
+	.nodes = NULL,				\
+	.param = {0},				\
+	.cfg_filepath = NULL,			\
+	.cfg_filepath_len = 0,			\
+	.cfg_part = -1				\
+}
+
+const char *obp_qual_set(struct ofw_dev *ofwdev, const char *qual);
+void add_obp_parm(struct ofw_dev *ofwdev, enum obp_param parm, const char *str);
+void obp_parm_addr(struct ofw_dev *ofwdev, const char *parm, const char *addr);
+void obp_parm_iqn(struct ofw_dev *ofwdev, const char *parm, const char *iqn);
+void obp_parm_hexnum(struct ofw_dev *ofwdev, const char *parm, const char *numstr);
+void obp_parm_str(struct ofw_dev *ofwdev, const char *parm, const char *str);
+
+#endif /* ISCSI_OBP_H_ */
diff --git a/utils/fwparam_ppc/prom_lex.l b/utils/fwparam_ppc/prom_lex.l
new file mode 100644
index 0000000..6502b71
--- /dev/null
+++ b/utils/fwparam_ppc/prom_lex.l
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2006 IBM Corporation.
+ * Doug Maxey <dwm@austin.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* definitions */
+%option array
+
+%{
+#include "prom_parse.h"
+
+#undef LEXDEBUG
+#ifdef LEXDEBUG
+#define dbg(a) dbgprint((a))
+#else
+#define dbg(a) do {} while (0)
+#endif  /* LEXDEBUG */
+
+#define upval(d)				\
+    dbg(#d);					\
+    yylval.str[0] = 0;                          \
+    strcat(yylval.str, yytext);			\
+    yylloc.first_column = yylloc.last_column;	\
+    yylloc.last_column += yyleng;		\
+    return d
+
+void dbgprint(const char *item) { fprintf(stderr, "%s: \"%s\" len=%d ", item, yytext, yyleng);}
+
+%}
+
+%option noyywrap
+%option never-interactive
+
+VDEVICE     vdevice
+VDEVINST    gscsi
+VDEVDEV     dev
+VDEVRAW     rawio
+                                /* CHOSEN uses only boot related paths. */
+CHOSEN      bootpath|bootargs|iscsi-bootargs|nas-bootdevice
+BUSNAME     ata|i2c|ide|pci|sata|scsi|usb
+BOOTDEV     cdrom|disk|ethernet|iscsi-(disk[0-9]|toe)|sd
+HEX4        [[:xdigit:]]{1,4}
+HEX16       [[:xdigit:]]{5,16}
+IPV4        [0-9]{1,3}(\.[0-9]{1,3}){3}
+IQN         iqn\.[-[:alnum:]:.]{1,219}
+OBPQUAL     bootp|dhcp|ipv6|iscsi|isns|slp
+OBPPARM     blksize|bootp-retries|chapid|chappw|ciaddr|filename|giaddr|ichapid|ichappw|ilun|iname|iport|isid|itname|siaddr|subnet-mask|tftp-retries
+FILENAME    \\[-[:alnum:]\\\.]{1,}
+
+%% /* rules */
+
+{CHOSEN}      { upval(CHOSEN); }
+{VDEVICE}     { upval(VDEVICE); }
+{VDEVINST}    { upval(VDEVINST); }
+{VDEVDEV}     { upval(VDEVDEV); }
+{VDEVRAW}     { upval(VDEVRAW); }
+{OBPQUAL}     { upval(OBPQUAL); }
+{BUSNAME}     { upval(BUSNAME); }
+{IPV4}        { upval(IPV4); }
+{IQN}         { upval(IQN); }
+{BOOTDEV}     { upval(BOOTDEV); }
+{OBPPARM}     { upval(OBPPARM); }
+{HEX4}        { upval(HEX4); }
+{HEX16}       { upval(HEX16); }
+{FILENAME}    { upval(FILENAME); }
+[ \t\n]+      {                  /* eat all whitespace. */
+	yylloc.first_column = yylloc.last_column;
+	yylloc.last_column += yyleng;
+}
+.             {			/* any other single char. */
+	dbg("??");
+	yylloc.first_column = yylloc.last_column;
+	yylloc.last_column += yyleng;
+	return *yytext;
+}
+
+%% /* user code */
+
diff --git a/utils/fwparam_ppc/prom_parse.h b/utils/fwparam_ppc/prom_parse.h
new file mode 100644
index 0000000..d544f0d
--- /dev/null
+++ b/utils/fwparam_ppc/prom_parse.h
@@ -0,0 +1,39 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * Copyright (C) IBM Corporation. 2007
+ * Author: Doug Maxey <dwm@austin.ibm.com>
+ *
+ */
+
+#ifndef PROM_PARSE_H_
+#define PROM_PARSE_H_
+
+#include <stdlib.h>
+#include <string.h>
+#include "iscsi_obp.h"
+
+struct ofw_dev;
+void yyerror(struct ofw_dev *ofwdev, const char *msg);
+extern int yyleng;
+extern int yydebug;
+#include <stdio.h>
+extern FILE *yyin;
+extern char yytext[];
+
+#include "prom_parse.tab.h"
+
+
+#endif /* PROM_PARSE_H_ */
diff --git a/utils/fwparam_ppc/prom_parse.y b/utils/fwparam_ppc/prom_parse.y
new file mode 100644
index 0000000..000198c
--- /dev/null
+++ b/utils/fwparam_ppc/prom_parse.y
@@ -0,0 +1,258 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * Copyright (C) IBM Corporation. 2007
+ * Author: Doug Maxey <dwm@austin.ibm.com>
+ *
+ */
+/* - DEFINITION section. */
+
+%{
+    /* literal block. include lines, decls, defns. */
+//#define YYDEBUG 1
+#if YYDEBUG
+#define DPRINT(fmt,...) printf(fmt,__VA_ARGS__)
+#else
+#define DPRINT(fmt,...) do {} while(0)
+#endif
+#include "prom_parse.h"
+#include "iscsi_obp.h"
+
+%}
+%union {
+        char str[256];
+}
+
+/* definitions. */
+%token <str> BUSNAME BOOTDEV
+%token <str> IPV4 IQN
+%token <str> OBPPARM OBPQUAL
+%token <str> HEX4 HEX16
+%token <str> VDEVICE VDEVINST VDEVDEV VDEVRAW
+%token <str> CHOSEN
+%token <str> FILENAME
+
+%type <str> devpath busses bus bootdev
+%type <str> disklabel diskpart
+%type <str> vdevice vdev_parms vdev_parm
+%type <str> obp_quals obp_qual obp_params obp_param
+%type <str> ipaddr ipv4 ipv6
+%type <str> hexpart hexseq
+
+%locations
+%parse-param {struct ofw_dev *ofwdev}
+
+%%
+
+devpath: '/'   {
+            DPRINT("****rootonly: \"%s\"\n", "/");
+        }
+    | '/' busses  bootdev  {
+            DPRINT("****devpath busses:\n/%s/%s\n", $2, $3);
+        }
+    | '/' busses  bootdev disklabel {
+            ofwdev->dev_path = malloc(strlen($<str>2) + strlen($<str>3) + 3);
+            sprintf(ofwdev->dev_path, "/%s/%s", $<str>2, $<str>3);
+            DPRINT("****devpath busses bootdev disklabel:\n/%s/%s%s\n", $2, $3, $4);
+        }
+    | '/' busses  bootdev obp_quals obp_params {
+            ofwdev->dev_path = malloc(strlen($<str>2) + strlen($<str>3) + 3);
+            sprintf(ofwdev->dev_path, "/%s/%s", $<str>2, $<str>3);
+            DPRINT("****busses bootdev obp_quals obp_parms:\n/%s/%s:%s%s\n", $2, $3, $4, $5);
+        }
+    | '/' busses  bootdev obp_quals obp_params disklabel {
+            ofwdev->dev_path = malloc(strlen($<str>2) + strlen($<str>3) + 3);
+            sprintf(ofwdev->dev_path, "/%s/%s", $<str>2, $<str>3);
+            DPRINT("****busses bootdev obp_quals obp_parms disklabel:\n/%s:%s%s%s\n", $2, $4, $5, $6);
+        }
+    | '/' vdevice bootdev vdev_parms obp_quals obp_params disklabel {
+            DPRINT("****vdevice bootdev obp_parms disklabel:\n/%s:%s%s%s%s\n", $2, $4, $5, $6, $7);
+        }
+    ;
+
+busses:    bus  {
+            strcpy($$, $1);
+        }
+    | busses '/' bus {
+            sprintf($$, "%s/%s", $<str>1, $<str>3);
+        }
+    ;
+
+bus:    BUSNAME {
+            strcpy($$, $1);
+        }
+    | BUSNAME '@' HEX4 {
+            sprintf($$, "%s@%s", $<str>1, $<str>3);
+        }
+    | BUSNAME '@' HEX4 ',' HEX4 {
+            sprintf($$, "%s@%s,%s", $<str>1, $<str>3, $<str>5);
+        }
+    | BUSNAME '@' HEX16 {
+            sprintf($$, "%s@%s", $<str>1, $<str>3);
+        }
+    | BUSNAME ',' HEX4 '@' HEX16  {
+            sprintf($$, "%s,%s@%s", $<str>1, $<str>3, $<str>5);
+        }
+    ;
+
+
+bootdev:  '/' BOOTDEV ':' {
+            sprintf($$, "/%s", $<str>2);
+        }
+    | '/' BOOTDEV '@' HEX4 ':' {
+            sprintf($$, "/%s@%s", $<str>2, $<str>4);
+        }
+    | '/' BOOTDEV '@' HEX4 ',' HEX4 ':' {
+            sprintf($$, "/%s@%s,%s", $<str>2, $<str>4, $<str>6);
+        }
+    ;
+
+vdevice: VDEVICE '/' VDEVINST {
+            sprintf($$, "%s/%s", $<str>1, $<str>3);
+        }
+    ;
+
+vdev_parms: ':' vdev_parm {
+            sprintf($$, ":%s", $<str>2);
+        }
+    | vdev_parms ',' vdev_parm {
+            sprintf($$, "%s,%s", $<str>1, $<str>3);
+        }
+    | vdev_parms ',' VDEVRAW {
+            sprintf($$, "%s,%s", $<str>1, $<str>3);
+        }
+    ;
+
+vdev_parm: VDEVDEV '=' CHOSEN {
+            sprintf($$, "%s=%s", $<str>1, $<str>3);
+        }
+    ;
+
+obp_params: ',' obp_param   {
+            sprintf($$, ",%s", $2);
+        }
+    | obp_params ',' obp_param {
+            sprintf($$, "%s,%s", $<str>1, $<str>3);
+        }
+    | obp_params ',' disklabel {
+            sprintf($$, "%s,%s", $<str>1, $<str>3);
+        }
+    ;
+
+obp_param: HEX4 {
+            sprintf($$, "%s", $1);
+        }
+    | OBPPARM '=' HEX16 {
+            /* luns > 0 are the SAM-3+ hex representation. */
+            obp_parm_hexnum(ofwdev, $<str>1, $<str>3);
+            sprintf($$, "%s=%s", $<str>1, $<str>3);
+        }
+    | OBPPARM '=' ipaddr {
+            obp_parm_addr(ofwdev, $<str>1, $<str>3);
+            sprintf($$, "%s=%s", $<str>1, $<str>3);
+        }
+    | OBPPARM '=' IQN {
+            obp_parm_iqn(ofwdev, $<str>1, $<str>3);
+            sprintf($$, "%s=%s", $<str>1, $<str>3);
+        }
+    | OBPPARM '=' HEX4 {
+            obp_parm_hexnum(ofwdev, $<str>1, $<str>3);
+            sprintf($$, "%s=%s", $<str>1, $<str>3);
+        }
+    | OBPPARM '=' FILENAME {
+            obp_parm_str(ofwdev, $<str>1, $<str>3);
+            sprintf($$, "%s=%s", $<str>1, $<str>3);
+        }
+    ;
+
+obp_quals: obp_qual {
+            sprintf($$, "%s", $1);
+        }
+    |  obp_quals ',' obp_qual {
+            sprintf($$, "%s,%s", $<str>1, $<str>3);
+        }
+    ;
+
+obp_qual: OBPQUAL {
+            sprintf($$, "%s", obp_qual_set(ofwdev, $<str>1));
+        }
+    | vdev_parm {
+            sprintf($$, "%s", $<str>1);
+        }
+    ;
+
+ipaddr: ipv4 {
+            sprintf($$, "%s", $<str>1);
+        }
+    | ipv6 {
+            sprintf($$, "%s", $<str>1);
+        }
+    ;
+
+ipv4: IPV4 {
+            sprintf($$, "%s", $1);
+        }
+    ;
+
+ipv6: hexpart {
+            sprintf($$, "%s", $1);
+        }
+    | hexpart ':' ipv4 {
+            sprintf($$, "%s:%s", $1, $3);
+        }
+    ;
+
+hexpart: hexseq {
+            sprintf($$, "%s", $1);
+        }
+    | hexpart "::"  {
+            sprintf($$, "%s::", $<str>1);
+        }
+    | hexpart "::" hexseq {
+            sprintf($$, "%s::%s", $<str>1, $<str>3);
+        }
+    | "::" hexseq {
+            sprintf($$, "::%s", $<str>2);
+        }
+    ;
+
+hexseq:  HEX4 {
+            sprintf($$, "%s", $1);
+        }
+    | hexseq ":" HEX4 {
+            sprintf($$, "%s:%s", $<str>1, $<str>3);
+        }
+    ;
+
+disklabel:   diskpart {
+            sprintf($$, "%s", $<str>1);
+        }
+    | HEX4 diskpart {
+            sprintf($$, "%s%s", $<str>1, $<str>2);
+        }
+    | '@' HEX4 ',' HEX4 diskpart {
+            sprintf($$, "@%s,%s%s", $<str>2, $<str>4, $<str>5);
+        }
+    ;
+
+diskpart: ':' HEX4 {
+            sprintf($$, ":%s", $<str>2);
+        }
+    | ':' HEX4 ',' FILENAME {
+            sprintf($$, ":%s,%s", $<str>2, $<str>4);
+        }
+    ;
+
+%%
