summaryrefslogtreecommitdiffstats
path: root/src/core/disk.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/disk.c')
-rw-r--r--src/core/disk.c283
1 files changed, 283 insertions, 0 deletions
diff --git a/src/core/disk.c b/src/core/disk.c
new file mode 100644
index 000000000..2d31b6273
--- /dev/null
+++ b/src/core/disk.c
@@ -0,0 +1,283 @@
+#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);
+}