123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283 |
- #include "etherboot.h"
- #include "disk.h"
-
- #undef disk_disable
-
- static int dummy(void *unused __unused)
- {
- return (0);
- }
-
- static unsigned char disk_buffer[DISK_BUFFER_SIZE];
- struct disk disk =
- {
- {
- 0, /* dev.disable */
- {
- 0,
- 0,
- PCI_BUS_TYPE,
- }, /* dev.devid */
- 0, /* index */
- 0, /* type */
- PROBE_FIRST, /* how_probe */
- PROBE_NONE, /* to_probe */
- 0, /* failsafe */
- 0, /* type_index */
- {}, /* state */
- },
- (int (*)(struct disk *, sector_t ))dummy, /* read */
- 0 - 1, /* drive */
- 0, /* hw_sector_size */
- 0, /* sectors_per_read */
- 0, /* bytes */
- 0, /* sectors */
- 0, /* sector */
- disk_buffer, /* buffer */
- 0, /* priv */
-
- 0, /* disk_offset */
- 0, /* direction */
- };
-
-
- static int disk_read(
- struct disk *disk, unsigned char *buffer, sector_t sector)
- {
- int result;
- sector_t base_sector;
-
- /* Note: I do not handle disk wrap around here! */
-
- /* Compute the start of the track cache */
- base_sector = sector;
- /* Support sectors_per_read > 1 only on small disks */
- if ((sizeof(sector_t) > sizeof(unsigned long)) &&
- (disk->sectors_per_read > 1)) {
- unsigned long offset;
- offset = ((unsigned long)sector) % disk->sectors_per_read;
- base_sector -= offset;
- }
-
- /* See if I need to update the track cache */
- if ((sector < disk->sector) ||
- sector >= disk->sector + (disk->bytes >> 9)) {
- twiddle();
- result = disk->read(disk, base_sector);
- if (result < 0)
- return result;
- }
- /* Service the request from the track cache */
- memcpy(buffer, disk->buffer + ((sector - base_sector)<<9), SECTOR_SIZE);
- return 0;
- }
-
- static int disk_read_sectors(
- struct disk *disk,
- unsigned char *buffer,
- sector_t base_sector, unsigned int sectors)
- {
- sector_t sector = 0;
- unsigned long offset;
- int result = 0;
-
- for(offset = 0; offset < sectors; offset++) {
- sector = base_sector + offset;
- if (sector >= disk->sectors) {
- sector -= disk->sectors;
- }
- result = disk_read(disk, buffer + (offset << 9), sector);
- if (result < 0)
- break;
- }
- if (result < 0) {
- printf("disk read error at 0x%lx\n", sector);
- }
- return result;
- }
-
- static os_download_t probe_buffer(unsigned char *buffer, unsigned int len,
- int increment, unsigned int offset, unsigned int *roffset)
- {
- os_download_t os_download;
- unsigned int end;
- end = 0;
- os_download = 0;
- if (increment > 0) {
- end = len - SECTOR_SIZE;
- }
- do {
- offset += increment;
- os_download = probe_image(buffer + offset, len - offset);
- } while(!os_download && (offset != end));
- *roffset = offset;
- return os_download;
- }
-
- static int load_image(
- struct disk *disk,
- unsigned char *buffer, unsigned int buf_sectors,
- sector_t block, unsigned int offset,
- os_download_t os_download)
- {
- sector_t skip_sectors;
-
- skip_sectors = 0;
- while(1) {
- skip_sectors = os_download(buffer + offset,
- (buf_sectors << 9) - offset, 0);
-
- block += skip_sectors + buf_sectors;
- if (block >= disk->sectors) {
- block -= disk->sectors;
- }
-
- offset = 0;
- buf_sectors = 1;
- if (disk_read_sectors(disk, buffer, block, 1) < 0) {
- return 0;
- }
- }
- return -1;
- }
-
- int disk_probe(struct dev *dev)
- {
- struct disk *disk = (struct disk *)dev;
- if (dev->how_probe == PROBE_NEXT) {
- disk->drive += 1;
- }
- return probe(dev);
- }
-
-
- int disk_load_configuration(struct dev *dev)
- {
- /* Start with the very simplest possible disk configuration */
- struct disk *disk = (struct disk *)dev;
- disk->direction = (dev->failsafe)?-1:1;
- disk->disk_offset = 0;
- return 0;
- }
-
- int disk_load(struct dev *dev)
- {
- struct disk *disk = (struct disk *)dev;
- /* 16K == 8K in either direction from the start of the disk */
- static unsigned char buffer[32*SECTOR_SIZE];
- os_download_t os_download;
- unsigned int offset;
- unsigned int len;
- unsigned int buf_sectors;
- volatile sector_t block;
- volatile int inc, increment;
- int i;
- int result;
- jmp_buf real_restart;
-
-
- printf("Searching for image...\n");
- result = 0;
- /* Only check for 16byte aligned images */
- increment = (disk->direction < 0)?-16:16;
- /* Load a buffer, and see if it contains the start of an image
- * we can boot from disk.
- */
- len = sizeof(buffer);
- buf_sectors = sizeof(buffer) / SECTOR_SIZE;
- inc = increment;
- block = (disk->disk_offset) >> 9;
- if (buf_sectors/2 > block) {
- block = (disk->sectors - (buf_sectors/2)) + block;
- }
- /* let probe buffer assume offset always needs to be incremented */
- offset = (len/2 + ((disk->disk_offset) & 0x1ff)) - inc;
-
- /* Catch longjmp so if this image fails to load, I start looking
- * for the next image where I left off looking for this image.
- */
- memcpy(&real_restart, &restart_etherboot, sizeof(jmp_buf));
- i = setjmp(restart_etherboot);
- if ((i != 0) && (i != -2)) {
- memcpy(&restart_etherboot, &real_restart, sizeof(jmp_buf));
- longjmp(restart_etherboot, i);
- }
- /* Read the canidate sectors into the buffer */
- if (disk_read_sectors(disk, buffer, block, buf_sectors) < 0) {
- result = -1;
- goto out;
- }
- if (inc == increment) {
- os_download = probe_buffer(buffer, len, inc, offset, &offset);
- if (os_download)
- goto load_image;
- inc = -inc;
- }
- os_download = probe_buffer(buffer, len, inc, offset, &offset);
- if (!os_download) {
- result = -1;
- goto out;
- }
- load_image:
- printf("Loading image...\n");
- result = load_image(disk, buffer, buf_sectors, block, offset, os_download);
- out:
- memcpy(&restart_etherboot, &real_restart, sizeof(jmp_buf));
- return result;
- }
-
- int url_file(const char *name,
- int (*fnc)(unsigned char *, unsigned int, unsigned int, int) __unused)
- {
- unsigned int drive;
- unsigned long disk_offset;
- int direction;
- int type;
-
- disk_offset = 0;
- direction = 1;
- if (memcmp(name, "disk", 4) == 0) {
- type = DISK_DRIVER;
- name += 4;
- }
- else if (memcmp(name, "floppy", 6) == 0) {
- type = FLOPPY_DRIVER;
- name += 6;
- }
- else {
- printf("Unknown device type\n");
- return 0;
- }
- drive = strtoul(name, &name, 10);
- if ((name[0] == '+') || (name[0] == '-')) {
- direction = (name[0] == '-')? -1 : 1;
- name++;
- disk_offset = strtoul(name, &name, 10);
- }
- if (name[0]) {
- printf("Junk '%s' at end of disk url\n", name);
- return 0;
- }
- memset(&disk, 0, sizeof(disk));
- disk.buffer = disk_buffer;
- disk.drive = 0;
- disk.dev.how_probe = PROBE_FIRST;
- disk.dev.type = type;
- do {
- disk_disable();
- disk.dev.how_probe = disk_probe(&disk.dev);
- if (disk.dev.how_probe == PROBE_FAILED) {
- printf("Not that many drives\n");
- return 0;
- }
- } while(disk.drive < drive);
- disk.direction = direction;
- disk.disk_offset = disk_offset;
-
- return disk_load(&disk.dev);
- }
-
- void disk_disable(void)
- {
- disable(&disk.dev);
- }
|