| /* |
| * This file is subject to the terms and conditions of the GNU General Public |
| * License. See the file "COPYING" in the main directory of this archive |
| * for more details. |
| * |
| * Copyright (C) Hewlett-Packard (Paul Bame) paul_bame@hp.com |
| */ |
| #include <stddef.h> |
| #include <asm/pdc.h> |
| #include "bootloader.h" |
| #undef PAGE0 |
| #define PAGE0 ((struct zeropage *)0x00000000) |
| |
| /* |
| * Sequential seekread from boot device. |
| * Return -1 if failed, or 0 if successful. |
| */ |
| static int pdc_bootdev_read(int fd, |
| char *dest, |
| unsigned int n, |
| __u64 seek) |
| { |
| unsigned long nbytes = 0; |
| static __u64 devaddr = 0; |
| |
| 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 % FW_BLOCKSIZE) { |
| printf("\nERROR: Boot device read size not a multiple of 2048.\n"); |
| return -1; |
| } |
| |
| if (PAGE0->mem_boot.cl_class == CL_RANDOM) |
| { |
| devaddr = seek; |
| } |
| else |
| { |
| |
| /* check for rewind semantic */ |
| if (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) |
| { |
| __u64 nseek = seek - devaddr; |
| int count; |
| if (nseek > n) |
| nseek = n; |
| if ((count = pdc_iodc_bootin(devaddr, dest, nseek)) < 0) |
| { |
| die("pdc_iodc_bootin() died during seekread\r\n"); |
| devaddr += nseek; |
| if (devaddr > 30*1024*1024) /* 30MB */ |
| printf("If you boot via tftp you probably reached the 32MB limit.\n"); |
| pdc_do_reset(); |
| return -1; |
| } |
| devaddr += count; |
| } |
| } |
| |
| while (nbytes < n) |
| { |
| int count; |
| |
| /* how many bytes should be read? */ |
| count = n - nbytes; |
| |
| 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); |
| |
| if (Debug) printf("%d\r\n", count); |
| |
| if (0 && Debug) |
| { |
| int i; |
| for (i = 0; i < 16; i++) |
| printf(" %02x", dest[nbytes + i] & 0xff); |
| printf("\n"); |
| } |
| |
| if (count == 0) |
| { |
| break; |
| } |
| else if (count > 0) |
| { |
| if (devaddr >= seek) |
| { |
| /* normal seekread */ |
| nbytes += count; |
| } |
| devaddr += count; |
| } |
| else |
| { |
| /* this could happen after a partial good seekread, which will */ |
| /* essentially be lost right now */ |
| if (Debug) printf("\nERROR: Read from boot device failed (status = %d).\n", |
| count); |
| |
| devaddr += (n - nbytes); |
| return -1; |
| } |
| } |
| |
| return nbytes; |
| } |
| |
| static void pdc_bootdev_describe(int fd, int *bufalign, |
| int *blocksize) |
| { |
| if (bufalign != 0) |
| *bufalign = disk_2gb_limit ? 64 : FW_BLOCKSIZE; |
| |
| if (blocksize != 0) |
| *blocksize = FW_BLOCKSIZE; |
| } |
| |
| /* returns true if OK */ |
| int pdc_bootdev_open() |
| { |
| return fileio_open(pdc_bootdev_describe, pdc_bootdev_read); |
| } |