/* Disk functions Copyright (C) 1996 Pete A. Zaitcev 1996,1997 Jakub Jelinek 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. */ #include #include static int net = 0; static int floppy = 0; static unsigned int flash = 0; static int fd; static unsigned long long seekp; static char bootdevice[4096]; static char currentdevice[4096]; char *silo_disk_get_bootdevice(void) { return bootdevice; } int silo_disk_open(char *device) { strcpy (currentdevice, device); net = 0; floppy = 0; seekp = 0xffffffffffffffffULL; switch (prom_vers) { case PROM_V0: { char buffer[20], *p; if (strlen (device) < 20) { /* v0 prom likes to paint in devopen parameter sometimes... */ strcpy (buffer, device); p = buffer; } else p = device; fd = (*romvec->pv_v0devops.v0_devopen) (p); if (device[0] == 'f' && device[1] == 'd') floppy = 1; else if ((device[0] == 'l' || device[0] == 'i') && device[1] == 'e') net = 1; } break; case PROM_V2: case PROM_V3: { int node; char buffer[20]; fd = (*romvec->pv_v2devops.v2_dev_open) (device); if ((unsigned)(fd + 1) > 1) { node = (romvec->pv_v2devops.v2_inst2pkg) (fd); prom_getstring (node, "device_type", buffer, 20); if (!strcmp (buffer, "network")) net = 1; } } break; case PROM_P1275: { int node; char buffer [20]; fd = p1275_cmd ("open", 1, device); if ((unsigned)(fd + 1) > 1) { node = p1275_cmd ("instance-to-package", 1, fd); /* * Don't use our argument due to devalias. * Alas, flash has no device_type property. */ prom_getstring (node, "name", buffer, 20); if (!strcmp (buffer, "flash-memory")) { int reg[3]; reg[0] = 0; reg[1] = 0; reg[2] = 0; prom_getproperty (node, "reg", (char*)®[0], sizeof (reg)); flash = reg[1]; } else { prom_getstring (node, "device_type", buffer, 20); if (!strcmp (buffer, "network")) net = 1; } } } break; } if (fd == 0 || fd == -1) { printf ("\nFatal error: Couldn't open device %s\n", device); return -1; } return 0; } extern unsigned char boot_part, boot_parts[32]; extern unsigned char raid_dsk_number; int get_boot_part(void) { int ret = boot_part; if (raid_dsk_number > 32) printf("Internal error. RAID disk number should be at most 32.\n"); else if (raid_dsk_number) ret = boot_parts[raid_dsk_number - 1]; return ret; } int silo_diskinit(void) { fd = 0; if (prom_vers == PROM_V0) { struct linux_arguments_v0 *ap = *romvec->pv_v0bootargs; char *s = bootdevice; *s++ = ap->boot_dev[0]; *s++ = ap->boot_dev[1]; *s++ = '('; *s++ = (ap->boot_dev_ctrl & 07) + '0'; *s++ = ','; if ((*s = ap->boot_dev_unit / 10 + '0') != '0') s++; *s++ = ap->boot_dev_unit % 10 + '0'; *s++ = ','; *s++ = get_boot_part() + '0'; *s++ = ')'; *s = 0; } else { char *p; if (prom_vers == PROM_P1275) prom_getproperty (prom_chosen, "bootpath", bootdevice, sizeof(bootdevice)); else strcpy (bootdevice, *romvec->pv_v2bootargs.bootpath); p = strchr (bootdevice, ':'); if (!p) { p = strchr (bootdevice, 0); *p++ = ':'; *p++ = get_boot_part() + 'a'; *p = 0; } else if (p[1] >= 'a' && p[1] <= 'z' && !p[2]) p[1] = get_boot_part() + 'a'; } return silo_disk_open(bootdevice); } static void silo_disk_reopen(void) { char c; c = *currentdevice; silo_disk_close(); *currentdevice = c; silo_disk_open(currentdevice); } static unsigned int flash_ld(unsigned int offset) { unsigned int retval; offset += flash; __asm__ __volatile__("lda [%2] %1, %0\n\t" : "=r" (retval) : "i" (0x20), "r" (offset)); return retval; } int silo_disk_read(char *buff, int size, unsigned long long offset) { if (!size) return 0; if (prom_vers == PROM_V0) { if (net) return (*romvec->pv_v0devops.v0_rdnetdev) (fd, size, buff); else { char buffer[512]; int i = 0, j, k, rc = 0, ret = 0; if (offset & 0x1ff) { if (size > 512 - (offset & 0x1ff)) i = 512 - (offset & 0x1ff); else i = size; for (j = 0; j < 5; j++) { rc = (*romvec->pv_v0devops.v0_rdblkdev) (fd, 1, (unsigned)(offset >> 9), buffer); if (rc) break; silo_disk_reopen(); } if (rc != 1) return -1; memcpy (buff, buffer + (offset & 0x1ff), i); buff += i; size -= i; offset = ((offset + 512) & ~0x1ffULL); ret = i; } if (size >> 9) { for (j = 0; j < 5; j++) { rc = (*romvec->pv_v0devops.v0_rdblkdev) (fd, size >> 9, (unsigned)(offset >> 9), buff); if (rc) break; silo_disk_reopen(); } if (rc != (size >> 9)) { /* Lets try if the floppy is not happy because the read size is too large for it */ for (k = 0; k < (size >> 9); k++) { for (j = 0; j < 5; j++) { rc = (*romvec->pv_v0devops.v0_rdblkdev) (fd, 1, (unsigned)(offset >> 9) + k, buff + (k << 9)); if (rc) break; silo_disk_reopen(); } if (rc != 1) return -1; } } i = (size & (~0x1ff)); ret += i; buff += i; offset += i; } size &= 0x1ff; if (size) { for (j = 0; j < 5; j++) { rc = (*romvec->pv_v0devops.v0_rdblkdev) (fd, 1, (unsigned)(offset >> 9), buffer); if (rc) break; silo_disk_reopen(); } if (rc != 1) return -1; memcpy (buff, buffer, size); ret += size; } return ret; } } else { int rc = 0; if (flash) { unsigned int word; int xlen; int count; if (offset >= 0x1000000) { /* Not very precise but will work... */ printf ("Reading beyond 16MB of flash, bad filesystem.\n"); return -1; } /* * Right thing is to map stuff in, then do a copy. * We, however, are not sure that PROM does not leak mappings. * So, let's kludge data in with ASI_BYPASS. * Note that flash must be accessed with 32 bits loads. */ offset += 1024; /* XXX Offset of flash filesystem */ count = 0; xlen = 4 - (offset & 3); if (xlen != 4) { word = flash_ld (offset & ~3); memcpy (buff, ((char *)&word) + (4 - xlen), xlen); buff += xlen; offset += xlen; count += xlen; } while (count + 4 <= size) { word = flash_ld (offset); memcpy (buff, (char *)&word, 4); buff += 4; offset += 4; count += 4; } xlen = size - count; if (xlen != 0) { word = flash_ld (offset); memcpy (buff, (char *)&word, xlen); buff += xlen; offset += xlen; count += xlen; } return count; } if (!net) { if (prom_vers != PROM_P1275) { if (((romvec->pv_printrev >> 16) < 2 || ((romvec->pv_printrev >> 16) == 2 && (romvec->pv_printrev && 0xffff) < 6)) && offset >= 0x40000000) { printf ("Buggy old PROMs don't allow reading past 1GB from start of the disk. Send complaints to SMCC\n"); return -1; } } if (seekp != offset) { if (prom_vers == PROM_P1275) { if ((rc = p1275_cmd ("seek", P1275_ARG_64B(2) | 3, fd, 0, offset)) == -1) return -1; } else { if ((*romvec->pv_v2devops.v2_dev_seek) (fd, (unsigned)(offset >> 32), (unsigned)offset) == -1) return -1; } seekp = offset; } } if (prom_vers == PROM_P1275) { rc = p1275_cmd ("read", 3, fd, buff, size); } else { int i; for (i = 0; i < 2; i++) { rc = (*romvec->pv_v2devops.v2_dev_read) (fd, buff, size); if (rc == size) break; silo_disk_reopen(); if ((*romvec->pv_v2devops.v2_dev_seek) (fd, (unsigned)(offset >> 32), (unsigned)offset) == -1) return -1; } if (rc != size && size > 32768) { int j, s = size; silo_disk_reopen(); while (size) { if (size < 32768) j = size; else j = 32768; if (silo_disk_read(buff, j, offset) != j) return -1; size -= j; offset += j; buff += j; } return s; } } if (!net) { seekp += size; if (rc == size) return size; } else return rc; } return -1; } void silo_disk_close(void) { if (*currentdevice) { switch (prom_vers) { case PROM_V0: (*romvec->pv_v0devops.v0_devclose) (fd); break; case PROM_V2: case PROM_V3: (*romvec->pv_v2devops.v2_dev_close) (fd); break; case PROM_P1275: p1275_cmd ("close", 1, fd); break; } } *currentdevice = 0; } int silo_disk_setdisk(char *device) { if (!strcmp(currentdevice, device)) return 0; silo_disk_close(); return silo_disk_open(device); } /* * XXX Good thing would be to have an argument, perhaps some device name. * XXX Other option is to make silo_disk_open() to return partitionable flag. * XXX Retrofit floppy ((flash == 0) && (floppy == 0) && (net == 0)); */ int silo_disk_partitionable(void) { return (flash == 0); } .