ipl: Allow to boot beyond the 2GB disk limit (on most machines)

Most newer-generation PA-RISC machines provide firmware support for the
ENTRY_IO_BBLOCK_IN (ARG=16) PDC option which addresses sectors in terms
of 2048 byte blocks. This feature overcomes the limitations of the old
ENTRY_IO_BOOTIN (ARG=0) PDC option, which uses byte offsets and thus
requires that boot files are located inside the first 2GB of the drive.

This palo patch checks the IODC_FEATURES (byte 10) of the IODC block of
the boot device, and if the rightmost bit is set, it will use the
ENTRY_IO boot block input support.

Palo will output at startup if the machines has the 2GB limitation:
-> Boot limited to sectors below 2GB: YES/NO

Signed-off-by: Helge Deller <deller@gmx.de>
diff --git a/ipl/bootloader.h b/ipl/bootloader.h
index 0c0116d..8871d87 100644
--- a/ipl/bootloader.h
+++ b/ipl/bootloader.h
@@ -12,24 +12,28 @@
 
 #define MAX_FD 20
 
+#define __u64 unsigned long long
+
 /* pdc_misc.c */
 void die(const char *);
 void firmware_init(int started_wide);
 int pdc_default_width(int wide);
+int pdc_bootdisk_2GB_limit(void);
 int pdc_cons_duplex();
 int pdc_cons_mux(int *is_mux);
 int pdc_iodc_cin(char *buf, int size);
 void pdc_iodc_cout(const char *c, int size);
 int pdc_os_bits();
-int pdc_iodc_bootin(unsigned devaddr, char *memaddr, unsigned size);
+int pdc_iodc_bootin(__u64 devaddr, char *memaddr, unsigned size);
 int pdc_read_conspath(unsigned char *memaddr);
 int pdc_do_reset(void);
 int pdc_model_sysmodel(char *name);
 
 typedef void (*describe_t)(int fd, int *bufalign, int *blocksize);
-typedef int (*read_t)(int fd, char *buf, unsigned nbytes, unsigned devaddr);
+typedef int (*read_t)(int fd, char *buf, unsigned nbytes, __u64 devaddr);
 
 extern int Debug;
+extern int disk_2gb_limit;
 
 extern const char bld_info[];
 
@@ -41,7 +45,7 @@
 
 /* fileio.c */
 int fileio_open(describe_t describef, read_t readf);
-int seekread(int fd, char *buf, unsigned nbytes, unsigned devaddr);
+int seekread(int fd, char *buf, unsigned nbytes, __u64 devaddr);
 void describe(int fd, int *bufalign, int *blocksize);
 
 /* ext2.c */
@@ -54,7 +58,7 @@
 void ext2_close(int fd);
 
 /* lib.c */
-void blockprint(int zero_offset, char *buf, int nbytes);
+void blockprint(__u64 zero_offset, char *buf, int nbytes);
 char *malloc_aligned(int nbytes, int align);
 void *malloc(size_t nbytes);
 void mark(void **ptr);
@@ -89,7 +93,7 @@
 int printf(const char *fmt, ...);
 
 /* offset.c */
-int offset_open(int otherfd, int offset, int length);
+int offset_open(int otherfd, __u64 offset, __u64 length);
 
 /* crt0.S */
 typedef struct { unsigned save[32]; } jmp_buf;
diff --git a/ipl/byteio.c b/ipl/byteio.c
index ce1e597..07b34cd 100644
--- a/ipl/byteio.c
+++ b/ipl/byteio.c
@@ -31,10 +31,7 @@
     int align;
 
     /* data in 'bd_readbuf' was seekread from boot device offset 'bd_bufoffset' */
-    int devoffset;
-
-    /* offset of next byte to be seekread from device */
-    int devnextptr;
+    __u64 devoffset;
 
     /* pointer to the properly-aligned 'blocksize' buffer */
     char *readbuf;
@@ -43,9 +40,9 @@
 static struct bs *blockio = 0;
 
 static int
-delivercache(struct bs *b, char **buf, unsigned *devaddr, unsigned endaddr)
+delivercache(struct bs *b, char **buf, __u64 *devaddr, __u64 endaddr)
 {
-    unsigned block = *devaddr & ~(b->blocksize - 1);
+    __u64 block = *devaddr & ~((__u64)b->blocksize - 1);
     int offset = *devaddr - block;
     int n = 0;
 
@@ -57,7 +54,7 @@
 
 	/* copy into user's buffer */
 	memcpy(*buf, b->readbuf + offset, n);
-	if (0 && Debug) printf("delivercache: %d bytes from devaddr 0x%x to 0x%p\r\n",
+	if (0 && Debug) printf("delivercache: %d bytes from devaddr 0x%llx to 0x%p\r\n",
 		n, *devaddr, *buf);
 
 	*devaddr += n;
@@ -68,10 +65,10 @@
 }
 
 static int
-cacheblock(struct bs *b, unsigned devaddr)
+cacheblock(struct bs *b, __u64 devaddr)
 {
     int n;
-    unsigned block = devaddr & ~(b->blocksize - 1);
+    __u64 block = devaddr & ~((__u64)b->blocksize - 1);
 
     if ((n = seekread(b->fd, b->readbuf, b->blocksize, block)) != b->blocksize)
     {
@@ -81,17 +78,17 @@
 	return 0;
     }
     b->devoffset = block;
-    if (0 && Debug) printf("cacheblock: cached block at devaddr 0x%x\r\n", devaddr);
+    if (0 && Debug) printf("cacheblock: cached block at devaddr 0x%llx\r\n", devaddr);
     return 1;
 }
 
-static int byteio_read(int fd, char *buf, unsigned count, unsigned devaddr)
+static int byteio_read(int fd, char *buf, unsigned count, __u64 devaddr)
 {
     struct bs *b = &blockio[fd];
-    unsigned endaddr = devaddr + count;
-    unsigned remember = devaddr;
+    __u64 endaddr = devaddr + count;
+    __u64 remember = devaddr;
 
-    if (Debug) printf("byteio_read(fd:%d, buf:%p, count:%u, devaddr:0x%x)\r\n",
+    if (Debug) printf("byteio_read(fd:%d, buf:%p, count:%u, devaddr:0x%llx)\r\n",
 	fd, buf, count, devaddr);
 
     while (devaddr < endaddr)
@@ -99,7 +96,7 @@
 	int n, bigread;
 	char *alignedbuf;
 
-	if (0 && Debug) printf("b_r: buf:0x%p devaddr 0x%x %s endaddr 0x%x\r\n",
+	if (0 && Debug) printf("b_r: buf:0x%p devaddr 0x%llx %s endaddr 0x%llx\r\n",
 		buf, devaddr, 
 		(devaddr & (b->blocksize - 1)) == 0 ? "aligned" : "unaligned",
 		endaddr);
@@ -134,9 +131,9 @@
 	bigread -= alignedbuf - buf;
 
 	/* must be a multiple of blocksize */
-	bigread &= ~(b->blocksize - 1);
+	bigread &= ~((__u64)b->blocksize - 1);
 
-	if (Debug) printf("reading %u bytes from dev %x to ubuf %p\r\n",
+	if (Debug) printf("reading %u bytes from dev 0x%llx to ubuf %p\r\n",
 				bigread, devaddr, alignedbuf);
 	/* read the blocks into user's buf */
 	if ((n = seekread(b->fd, alignedbuf, bigread, devaddr))
diff --git a/ipl/ext2.c b/ipl/ext2.c
index 9b46a37..a468790 100644
--- a/ipl/ext2.c
+++ b/ipl/ext2.c
@@ -338,7 +338,7 @@
 	struct ext2_inode *ip;
 	struct inode_table_entry *itp = 0;
 	int group;
-	long offset;
+	__u64 offset;
 
 
 	ip = 0;
@@ -363,11 +363,11 @@
 	printf("group is %d\n", group);
 #endif
 	offset = partition_offset
-		+ ((long) ext2_gds(group)->bg_inode_table * (long)ext2_blocksize)
+		+ ((__u64) ext2_gds(group)->bg_inode_table * ext2_blocksize)
 		+ (((ino - 1) % EXT2_INODES_PER_GROUP(&sb))
 		   * EXT2_INODE_SIZE(&sb));
 #ifdef DEBUG
-	printf("ext2_iget: reading %ld bytes at offset %ld "
+	printf("ext2_iget: reading %ld bytes at offset %lld "
 	       "(%ld + (%d * %d) + ((%d) %% %d) * %d) "
 	       "(inode %d -> table %d)\n", 
 	       sizeof(struct ext2_inode), offset, partition_offset,
@@ -560,7 +560,7 @@
 	int blkno;
 	int iblkno;
 	int diblkno;
-	unsigned long offset;
+	__u64 offset;
 
 	if (ip->i_flags & EXT3_EXTENTS_FL)
 		return ext3_extent_blkno(ip, blkoff);
@@ -583,7 +583,7 @@
 
 		/* Read the indirect block */
 		if (cached_iblkno != iblkno) {
-			offset = partition_offset + (long)iblkno * (long)ext2_blocksize;
+			offset = partition_offset + (__u64)iblkno * ext2_blocksize;
 			if (cons_read(dev, iblkbuf, ext2_blocksize, offset)
 			    != ext2_blocksize)
 			{
@@ -610,7 +610,7 @@
 
 		/* Read in the double-indirect block */
 		if (cached_diblkno != diblkno) {
-			offset = partition_offset + (long) diblkno * (long) ext2_blocksize;
+			offset = partition_offset + (__u64) diblkno * ext2_blocksize;
 			if (cons_read(dev, diblkbuf, ext2_blocksize, offset)
 			    != ext2_blocksize)
 			{
@@ -631,7 +631,7 @@
 		/* Read the indirect block */
     
 		if (cached_iblkno != iblkno) {
-			offset = partition_offset + (long) iblkno * (long) ext2_blocksize;
+			offset = partition_offset + (__u64) iblkno * ext2_blocksize;
 			if (cons_read(dev, iblkbuf, ext2_blocksize, offset)
 			    != ext2_blocksize)
 			{
@@ -667,7 +667,8 @@
 static int ext2_breadi(struct ext2_inode *ip, long blkno, long nblks,
 		       char *buffer)
 {
-	long dev_blkno, ncontig, offset, nbytes, tot_bytes;
+	long dev_blkno, ncontig, nbytes, tot_bytes;
+	__u64 offset;
 
 	if (Debug) printf("ext2_breadi(%p, %ld, %ld, %p)\n",
 		ip, blkno, nblks, buffer);
@@ -698,9 +699,9 @@
 			memset(buffer, 0, nbytes);
 		} else {
 			/* Read it for real */
-			offset = partition_offset + (long) dev_blkno* (long) ext2_blocksize;
+			offset = partition_offset + (__u64) dev_blkno * ext2_blocksize;
 #ifdef DEBUG
-			printf("ext2_bread: reading %ld bytes at offset %ld\n",
+			printf("ext2_bread: reading %ld bytes at offset %lld\n",
 			       nbytes, offset);
 #endif
 			if (cons_read(dev, buffer, nbytes, offset)
@@ -960,12 +961,12 @@
 	}
 }
 
-static int ext2_read(int fd, char *buf, unsigned count, unsigned devaddr)
+static int ext2_read(int fd, char *buf, unsigned count, __u64 devaddr)
 {
 	struct ext2_inode * ip;
 	ip = fd2inode[fd];
 
-	if (Debug) printf("ext2_read(%d, 0x%p, %d, %d)\n",
+	if (Debug) printf("ext2_read(%d, 0x%p, %d, %lld)\n",
 		fd, buf, count, devaddr);
 
 	return ext2_breadi(ip,
diff --git a/ipl/fileio.c b/ipl/fileio.c
index 3788afd..7b31a69 100644
--- a/ipl/fileio.c
+++ b/ipl/fileio.c
@@ -44,11 +44,11 @@
     return d;
 }
 
-int seekread(int fd, char *buf, unsigned nbytes, unsigned devaddr)
+int seekread(int fd, char *buf, unsigned nbytes, __u64 devaddr)
 {
     int r = -1;
 
-    if (0) printf("seekread(%d, 0x%p, %d, %d)\r\n", fd, buf, nbytes, devaddr);
+    if (0) printf("seekread(%d, 0x%p, %d, 0x%llx)\r\n", fd, buf, nbytes, devaddr);
     if (fileio[fd].readf != 0)
 	r = (*fileio[fd].readf)(fd, buf, nbytes, devaddr);
 
diff --git a/ipl/ipl.c b/ipl/ipl.c
index ffa4112..ef422d3 100644
--- a/ipl/ipl.c
+++ b/ipl/ipl.c
@@ -19,6 +19,7 @@
 
 int Debug = 0;
 int interactive = 0;
+int disk_2gb_limit;
 
 /*
  * Beware: pdc_model_sysmodel() may return a machine name which has trailing
@@ -328,8 +329,8 @@
     partition_transform(&part, NULL);
 
     /* partition table starts from zero */
-    part_fd = offset_open(bootdev, 512 * partition[part - 1].start,
-			      512 * partition[part - 1].length);
+    part_fd = offset_open(bootdev, 512ULL * partition[part - 1].start,
+			      512ULL * partition[part - 1].length);
     if(ext2_mount(part_fd, 0, 0) == -1) {
 	printf("Failed to mount partition %d\n", part);
 	return;
@@ -535,6 +536,9 @@
 	printf("palo loaded at %p-%p.\n", &_start, &_end);
     }
 
+    disk_2gb_limit = pdc_bootdisk_2GB_limit();
+    printf("Boot limited to sectors below 2GB: %s\n", disk_2gb_limit ? "YES":"NO");
+
  restart:
 
     blocked_bootdev = pdc_bootdev_open();
@@ -809,7 +813,7 @@
 
 	pp = &partition[kern_part - 1];
     
-	part_fd = offset_open(bootdev, 512 * pp->start, 512 * pp->length);
+	part_fd = offset_open(bootdev, 512ULL * pp->start, 512ULL * pp->length);
 
 	mount_fd = ext2_mount(part_fd, 0, 0);
 	if (0) printf("ext2_mount(partition %d) returns %d\n",
diff --git a/ipl/lib.c b/ipl/lib.c
index de7ad6d..3a39727 100644
--- a/ipl/lib.c
+++ b/ipl/lib.c
@@ -8,13 +8,13 @@
 #include "bootloader.h"
 
 void
-blockprint(int zero_offset, char *buf, int nbytes)
+blockprint(__u64 zero_offset, char *buf, int nbytes)
 {
     int i;
     for (i = 0; i < nbytes; i += 16)
     {
 	int j;
-	printf("%d:", zero_offset + i);
+	printf("%lld:", zero_offset + i);
 	for (j = 0; j < 16; j++)
 	{
 	    printf(" %02x", buf[i + j] & 0xff);
diff --git a/ipl/offset.c b/ipl/offset.c
index 8441091..c048461 100644
--- a/ipl/offset.c
+++ b/ipl/offset.c
@@ -12,15 +12,15 @@
 
 struct offsets {
     int fd;
-    int start;
-    int length;
+    __u64 start;
+    __u64 length;
 } ofd [MAX_FD];
 
-static int offset_read(int fd, char *buf, unsigned count, unsigned devaddr)
+static int offset_read(int fd, char *buf, unsigned count, __u64 devaddr)
 {
     struct offsets *o = &ofd[fd];
 
-    if (Debug) printf("offset_read(%d, 0x%p, %d, %d)\r\n",
+    if (Debug) printf("offset_read(%d, 0x%p, %d, %lld)\r\n",
 		    fd, buf, count, devaddr);
 
     /* truncate 'count' according to max device/file size */
@@ -28,7 +28,7 @@
 	count = o->length - devaddr;
     else if (o->length > 0 && devaddr + count > o->length)
     {
-	printf("offset_read(%d, 0x%p, %d, %d) can't seek past %d\r\n",
+	printf("offset_read(%d, 0x%p, %d, %lld) can't seek past %lld\r\n",
 			fd, buf, count, devaddr, o->length);
 	return -1;
     }
@@ -43,7 +43,7 @@
 }
 
 /* returns true if OK */
-int offset_open(int otherfd, int offset, int length)
+int offset_open(int otherfd, __u64 offset, __u64 length)
 {
     int fd = fileio_open(offset_describe, offset_read);
 
@@ -56,7 +56,7 @@
 	o->length = length;
     }
 
-     if (Debug) printf("offset_open(%d, %d, %d) = %d\n",
+     if (Debug) printf("offset_open(%d, %lld, %lld) = %d\n",
 	otherfd, offset, length, fd);
 
     return fd;
diff --git a/ipl/pdc_bootio.c b/ipl/pdc_bootio.c
index 3810fb4..c23e0b2 100644
--- a/ipl/pdc_bootio.c
+++ b/ipl/pdc_bootio.c
@@ -18,24 +18,20 @@
 static int pdc_bootdev_read(int fd,
 			char *dest,
 			unsigned int n,
-			unsigned int seek)
+			__u64 seek)
 {
-    int nbytes = 0;
-    static unsigned devaddr = 0;
+    unsigned long nbytes = 0;
+    static __u64 devaddr = 0;
 
-    if (0 && Debug) printf("pdc_bootdev_read(fd:%d, dest:0x%p, n:%u, seek:0x%x) 0x%x\n",
-			fd, dest, n, seek, devaddr);
+    if (1 && Debug) printf("pdc_bootdev_read(fd:%d, dest:0x%p, n:%u, seek:0x%llx)\n",
+			fd, dest, n, seek);
 
     if ((unsigned int)dest & 0x3f) {
 	printf("\nERROR: Boot device I/O buffer not properly aligned.\n");
 	return -1;
-    } else if (n % 2048) {
+    } else if (n % FW_BLOCKSIZE) {
 	printf("\nERROR: Boot device read size not a multiple of 2048.\n");
 	return -1;
-    } else if (seek & 0x80000000) {
-	/* an unreliable test */
-	printf("Information: Boot device can't seek past 2Gb (ignore next error).\n");
-	return -1;
     }
 
     if (PAGE0->mem_boot.cl_class == CL_RANDOM)
@@ -48,14 +44,14 @@
 	/* check for rewind semantic */
 	if (seek < devaddr)
 	{
-	    printf("NOTE: pdc_bootdev_read() asked to seek to 0x%x from 0x%x, rewinding...\n", seek, devaddr);
+	    printf("NOTE: pdc_bootdev_read() asked to seek to 0x%llx from 0x%llx, rewinding...\n", seek, devaddr);
 	    /* IODC needs devaddr 0 to do a rewind */
 	    devaddr = 0;
 	}
 
 	while (devaddr < seek)
 	{
-	    unsigned nseek = seek - devaddr;
+	    __u64 nseek = seek - devaddr;
 	    int count;
 	    if (nseek > n)
 		nseek = n;
@@ -80,7 +76,7 @@
 	/* how many bytes should be read? */
 	count = n - nbytes;
 
-	if (Debug) printf("pdc_iodc_bootin(dev:0x%x, buf:0x%p, count:%u) = ",
+	if (Debug) printf("pdc_iodc_bootin(dev:0x%llx, buf:0x%p, count:%u) = ",
 		devaddr, dest+nbytes, count);
 
 	count = pdc_iodc_bootin(devaddr, dest + nbytes, count);
@@ -89,12 +85,9 @@
 
 	if (0 && Debug)
 	{
-	    printf("%d@0x%x ", count, devaddr);
-	    {
-		int i;
-		for (i = 0; i < 16; i++)
-		    printf(" %02x", dest[nbytes + i] & 0xff);
-	    }
+	    int i;
+	    for (i = 0; i < 16; i++)
+		printf(" %02x", dest[nbytes + i] & 0xff);
 	    printf("\n");
 	}
 
@@ -130,7 +123,7 @@
 				    int *blocksize)
 {
     if (bufalign != 0)
-	*bufalign = 64;
+	*bufalign = disk_2gb_limit ? 64 : FW_BLOCKSIZE;
 
     if (blocksize != 0)
 	*blocksize = FW_BLOCKSIZE;
diff --git a/ipl/pdc_misc.c b/ipl/pdc_misc.c
index f2231ab..b2296da 100644
--- a/ipl/pdc_misc.c
+++ b/ipl/pdc_misc.c
@@ -237,6 +237,31 @@
     return r;
 }
 
+/*
+  See https://parisc.wiki.kernel.org/images-parisc/9/9c/Pdc11-v0.96-Ch3-IODC.pdf The
+  IODC_FEATURES (byte 10) byte specifies which optional IODC feature are
+  supported by this module.  The rightmost bit (block field) specifies whether
+  ENTRY_IO support block input (ARG1=16) and Boot block output (ARG1=17) are
+  supported.
+ */
+int
+pdc_bootdisk_2GB_limit(void)
+{
+    int r;
+    struct pdc_iodc iodc __attribute__ ((aligned (8)));
+
+    iodc.features = 0;
+    r = firmware_call(mem_pdc, PDC_IODC, PDC_IODC_READ,
+                pdc_result, PAGE0->mem_boot.hpa,
+		PDC_IODC_INDEX_DATA, &iodc, sizeof(iodc));
+
+    /* check boot block feature */
+    if (r >= 0)
+	return (iodc.features & 1) == 0;
+
+    return 1;
+}
+
 int
 pdc_cons_duplex()
 {
@@ -331,14 +356,35 @@
 }
 
 int
-pdc_iodc_bootin(unsigned devaddr, char *memaddr, unsigned size)
+pdc_iodc_bootin(__u64 devaddr, char *memaddr, unsigned size)
 {
-    int r;
+    int r = -1;
 
-    r = firmware_call(PAGE0->mem_boot.iodc_io,
+    if (!disk_2gb_limit) {
+       unsigned long a = devaddr / FW_BLOCKSIZE;
+       unsigned long s = (size + FW_BLOCKSIZE - 1) / FW_BLOCKSIZE;
+       r = firmware_call(PAGE0->mem_boot.iodc_io,
+		    PAGE0->mem_boot.hpa, ENTRY_IO_BBLOCK_IN,
+		    PAGE0->mem_boot.spa, PAGE0->mem_boot.dp.layers,
+		    pdc_result, a, memaddr, s, s);
+	if (r >= 0)
+	{
+		convert_from_wide(pdc_result);
+		/* ENTRY_IO_BBLOCK_IN returns blocks, not bytes */
+		return pdc_result[0] * FW_BLOCKSIZE;	/* return count in bytes */
+	}
+    }
+
+    if (r < 0) {
+        /* check for overflow - ENTRY_IO_BOOTIN allows up to 2GB only */
+        if (devaddr >> 31)
+                r = PDC_INVALID_ARG;
+        else
+                r = firmware_call(PAGE0->mem_boot.iodc_io,
 		    PAGE0->mem_boot.hpa, ENTRY_IO_BOOTIN,
 		    PAGE0->mem_boot.spa, PAGE0->mem_boot.dp.layers,
-		    pdc_result, devaddr, memaddr, size, size);
+		    pdc_result, (unsigned long)devaddr, memaddr, size, size);
+    }
 
     if (r == 3)	/* EOF */
     {
diff --git a/ipl/vsprintf.c b/ipl/vsprintf.c
index 797272f..4a7a060 100644
--- a/ipl/vsprintf.c
+++ b/ipl/vsprintf.c
@@ -38,11 +38,11 @@
 
 #define do_div(n,base) ({ \
 int __res; \
-__res = ((unsigned long) n) % (unsigned) base; \
-n = ((unsigned long) n) / (unsigned) base; \
+__res = ((unsigned long long) n) % (unsigned) base; \
+n = ((unsigned long long) n) / (unsigned) base; \
 __res; })
 
-static char * number(char * str, long num, int base, int size, int precision
+static char * number(char * str, unsigned long long num, int base, int size, int precision
 	,int type)
 {
 	char c,sign,tmp[66];
@@ -115,7 +115,7 @@
 int vsprintf(char *buf, const char *fmt, va_list args)
 {
 	int len;
-	unsigned long num;
+	unsigned long long num;
 	int i, base;
 	char * str;
 	const char *s;
@@ -183,7 +183,7 @@
 
 		/* default base */
 		base = 10;
-
+again:
 		switch (*fmt) {
 		case 'c':
 			if (!(flags & LEFT))
@@ -246,6 +246,11 @@
 			base = 16;
 			break;
 
+		case 'l':
+			qualifier = 'L';
+			fmt++;
+			goto again;
+
 		case 'd':
 		case 'i':
 			flags |= SIGN;
@@ -260,7 +265,9 @@
 				--fmt;
 			continue;
 		}
-		if (qualifier == 'l')
+		if (qualifier == 'L')
+			num = va_arg(args, unsigned long long);
+		else if (qualifier == 'l')
 			num = va_arg(args, unsigned long);
 		else if (qualifier == 'h') {
 			num = (unsigned short) va_arg(args, int);
diff --git a/lib/common.h b/lib/common.h
index cec873c..d755a0a 100644
--- a/lib/common.h
+++ b/lib/common.h
@@ -143,7 +143,9 @@
 
 #define MAXPARTS 16
 
-int seekread(int fd, char *buf, unsigned nbytes, unsigned devaddr);
+extern int disk_2gb_limit;
+
+int seekread(int fd, char *buf, unsigned nbytes, __u64 devaddr);
 #define STRUCTWRITE(f, data, where) seekwrite((f), (char *)&data, sizeof data, (where))
 #define STRUCTREAD(f, data, where) seekread((f), (char *)&data, sizeof data, (where))
 #define GZIP_STRUCTREAD(loadable, f, data, where)			\
@@ -155,7 +157,6 @@
 /* diskpart.c */
 extern int is_extended(int id);
 extern int load_partitions(int bootdev, struct diskpartition *mptab, int maxparts);
-extern void print_ptab(struct diskpartition *mptab, int maxparts);
 extern void print_ptab_pretty(struct diskpartition *mptab, int maxparts);
 
 #endif /* __ASSEMBLY__ */
diff --git a/lib/diskpart.c b/lib/diskpart.c
index dd3982c..5fae1b9 100644
--- a/lib/diskpart.c
+++ b/lib/diskpart.c
@@ -52,37 +52,31 @@
 	offset = mptab[ex].start;
 	while (extnum < maxparts)
 	{
-	    /* we're currently using 32-bit file seeks which is ok since
-	     * the IPL is also limited to 2G right now.  On disks > 4GB
-	     * this next read may fail. Handle that gracefully.
-	     * Yes, we know newer IODC can read/write > 2GB but
-	     * it would confuse too many people if the docs attempted
-	     * to explain for no tangible benefit. And moving boot disks
-	     * between systems will always work (assuming a useable
-	     * kernel is present).
-	     */
-	    if (offset >= (2 * (GB / 512))) { /* weird () to quiet compiler */
-		printf("Skipping extended partition %d - beyond reach of IPL\n\r",
+	    if (disk_2gb_limit && offset >= (2 * (GB / 512))) { /* weird () to quiet compiler */
+		printf("NOTE: Extended partition %d might be beyond reach of IPL\r\n",
 				extnum + 1);
-		break;
 	    }
-	    if (seekread(bootdev, (char *)&fb, sizeof fb, 512 * offset) == -1) {
+
+	    fb.dosmagic[0] = 0; // wipe dosmagic flag just to be on the safe side
+	    if (seekread(bootdev, (char *)&fb, sizeof fb, 512ULL * offset) == -1) {
 		printf("seekread(bootdev,..., 512 * 0x%x) failed!\n\r", offset);
 		break;
 	    }
 	    if (fb.dosmagic[0] != 0x55 || fb.dosmagic[1] != 0xaa)
 	    {
-		printf("Bad DOS magic in extended partition\n\r");
+		// Seems to not be an extended partition, so exit.
 		break;
 	    }
 
 	    mptab[extnum].start = __le32_to_cpu(ptab[0].start_sect) + offset;
 	    mptab[extnum].length = __le32_to_cpu(ptab[0].nr_sects);
 	    mptab[extnum].id = ptab[0].sys_ind;
+
+	    offset = mptab[extnum].start + __le32_to_cpu(ptab[1].start_sect);
+
 	    extnum++;
 	    if (!is_extended(ptab[1].sys_ind))
 		break;
-	    offset = mptab[ex].start + __le32_to_cpu(ptab[1].start_sect);
 	}
     }
 
@@ -90,20 +84,6 @@
 }
 
 void
-print_ptab(struct diskpartition *mptab, int maxparts)
-{
-    int i;
-    for (i = 0; i < maxparts; i++)
-    {
-	if (mptab[i].id != 0)
-	    printf("/dev/ida%-2d %02x %10u %10u\n\r",
-		i + 1, mptab[i].id,
-		mptab[i].start,
-		mptab[i].length);
-    }
-}
-
-void
 print_ptab_pretty(struct diskpartition *mptab, int maxparts)
 {
     int i;
diff --git a/palo/palo.c b/palo/palo.c
index a68cef7..23fbc80 100644
--- a/palo/palo.c
+++ b/palo/palo.c
@@ -38,6 +38,8 @@
 static int Install = 0;
 int verbose = 0;
 
+int disk_2gb_limit = 0;
+
 /* compute the sum of words in an 4-byte aligned region */
 int
 checksum(void *p, size_t len)
diff --git a/palo/palo.h b/palo/palo.h
index 38d662b..c6ea3fd 100644
--- a/palo/palo.h
+++ b/palo/palo.h
@@ -11,7 +11,7 @@
 extern void error(int number, ...);
 
 /* paloio.c */
-extern int seekwrite(int fd, char *buf, unsigned size, unsigned where);
-extern int seekread(int fd, char *buf, unsigned size, unsigned where);
+extern int seekwrite(int fd, char *buf, unsigned size, __u64 where);
+extern int seekread(int fd, char *buf, unsigned size, __u64 where);
 extern int cat(int out, int in);
 extern int fsize(int fd);
diff --git a/palo/paloio.c b/palo/paloio.c
index 0e3dba1..a925e17 100644
--- a/palo/paloio.c
+++ b/palo/paloio.c
@@ -14,19 +14,14 @@
 #include "palo.h"
 
 int
-seekwrite(int fd, char *buf, unsigned size, unsigned where)
+seekwrite(int fd, char *buf, unsigned size, __u64 where)
 {
     char check[1024];
     int n = size < sizeof check ? size : sizeof check;
     int r = 0;
     off_t off;
 
-    if (0) printf("seekwrite(%d, %p, %u, %u)\n", fd, buf, size, where);
-
-    if (where & 0x80000000)
-    {
-	r = -1;
-    }
+    if (1) printf("seekwrite(%d, %p, %u, %llu)\n", fd, buf, size, where);
 
     fsync(fd);
     if (r != -1 && lseek(fd, where, SEEK_SET) != where)
@@ -79,16 +74,11 @@
 }
 
 int
-seekread(int fd, char *buf, unsigned size, unsigned where)
+seekread(int fd, char *buf, unsigned size, __u64 where)
 {
     off_t off;
     int r = 0;
-    if (0) printf("seekread(%d, %p, %x, %d)\n", fd, buf, size, where);
-
-    if (where & 0x80000000)
-    {
-	r = -1;
-    }
+    if (1) printf("seekread(%d, %p, %x, %lld)\n", fd, buf, size, where);
 
     if (r != -1 && lseek(fd, where, SEEK_SET) != where)
     {