/* * * * Open Hack'Ware BIOS bloc devices management * * Copyright (c) 2004-2005 Jocelyn Mayer * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License V2 * as published by the Free Software Foundation * * 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 #include "bios.h" #undef DPRINTF #define DPRINTF(fmt, args...) do { } while (0) struct bloc_device_t { int device; /* Hardware */ uint32_t io_base; int drv; /* Geometry */ int heads; int trks; int sects; int seclen; /* Position */ int bloc; int vbloc; int vpos; /* Access */ int (*init)(bloc_device_t *bd, int device); int (*read_sector)(bloc_device_t *bd, void *buffer, int secnum); int (*ioctl)(bloc_device_t *bd, int func, void *args); /* buffer */ char *buffer; /* Private data */ int tmp; void *private; #ifdef USE_OPENFIRMWARE void *OF_private; #endif /* Partitions */ part_t *parts, *bparts; part_t *boot_part; int bpartnum; /* Chain */ bloc_device_t *next; }; static bloc_device_t *bd_list; static int fdc_initialize (bloc_device_t *bd, int device); static int fdc_read_sector (bloc_device_t *bd, void *buffer, int secnum); static int ide_initialize (bloc_device_t *bd, int device); static int ide_read_sector (bloc_device_t *bd, void *buffer, int secnum); static int ide_reset (bloc_device_t *bd); static int mem_initialize (bloc_device_t *bd, int device); static int mem_read_sector (bloc_device_t *bd, void *buffer, int secnum); static int mem_ioctl (bloc_device_t *bd, int func, void *args); bloc_device_t *bd_open (int device) { bloc_device_t *bd; int num; bd = bd_get(device); if (bd != NULL) return bd; bd = malloc(sizeof(bloc_device_t)); if (bd == NULL) return NULL; bd->ioctl = NULL; switch (device) { case 'a': num = 0; bd->init = &fdc_initialize; bd->read_sector = &fdc_read_sector; break; case 'b': num = 1; bd->init = &fdc_initialize; bd->read_sector = &fdc_read_sector; break; case 'c': num = 0; bd->init = &ide_initialize; bd->read_sector = &ide_read_sector; break; case 'd': num = 1; bd->init = &ide_initialize; bd->read_sector = &ide_read_sector; break; case 'e': num = 2; bd->init = &ide_initialize; bd->read_sector = &ide_read_sector; break; case 'f': num = 3; bd->init = &ide_initialize; bd->read_sector = &ide_read_sector; break; case 'm': num = 0; bd->init = &mem_initialize; bd->read_sector = &mem_read_sector; bd->ioctl = &mem_ioctl; break; default: return NULL; } bd->bloc = -1; if ((*bd->init)(bd, num) < 0) { free(bd); return NULL; } bd->buffer = malloc(bd->seclen); if (bd->buffer == NULL) { free(bd); return NULL; } bd->device = device; return bd; } int bd_seek (bloc_device_t *bd, uint32_t bloc, uint32_t pos) { uint32_t maxbloc; maxbloc = bd_maxbloc(bd); if (bloc > maxbloc) { DPRINTF("%p bloc: %d maxbloc: %d C: %d H: %d S: %d\n", bd, bloc, maxbloc, bd->trks, bd->heads, bd->sects); return -1; } bd->vbloc = bloc; bd->vpos = pos; DPRINTF("%s: %p %08x %08x %08x %08x %08x\n", __func__, bd, bloc, pos, bd->bloc, bd->vbloc, bd->vpos); return 0; } int bd_read (bloc_device_t *bd, void *buffer, int len) { int clen, total; for (total = 0; len > 0; total += clen) { if (bd->vbloc != bd->bloc) { /* Do physical seek */ #if 0 DPRINTF("Read sector %d\n", bd->vbloc); #endif if ((*bd->read_sector)(bd, bd->buffer, bd->vbloc) < 0) { printf("Error reading bloc %d\n", bd->vbloc); return -1; } bd->bloc = bd->vbloc; } clen = bd->seclen - bd->vpos; if (clen > len) clen = len; memcpy(buffer, bd->buffer + bd->vpos, clen); #if 0 DPRINTF("%s: %p copy %d bytes (%08x %08x %08x) %08x %08x %08x %08x\n", __func__, bd, clen, bd->bloc, bd->vbloc, bd->vpos, ((uint32_t *)buffer)[0], ((uint32_t *)buffer)[1], ((uint32_t *)buffer)[2], ((uint32_t *)buffer)[3]); #endif bd->vpos += clen; if (bd->vpos == bd->seclen) { bd->vbloc++; bd->vpos = 0; } buffer += clen; len -= clen; } return total; } int bd_write (unused bloc_device_t *bd, unused const void *buffer, unused int len) { return -1; } int bd_ioctl (bloc_device_t *bd, int func, void *args) { if (bd->ioctl == NULL) return -1; return (*bd->ioctl)(bd, func, args); } void bd_close (unused bloc_device_t *bd) { } void bd_reset_all(void) { bloc_device_t *bd; for (bd = bd_list; bd != NULL; bd = bd->next) { if (bd->init == &ide_initialize) { /* reset IDE drive because Darwin wants all IDE devices to be reset */ ide_reset(bd); } } } uint32_t bd_seclen (bloc_device_t *bd) { return bd->seclen; } uint32_t bd_maxbloc (bloc_device_t *bd) { return bd_CHS2sect(bd, bd->trks, 0, 1); } /* XXX: to be suppressed */ void bd_set_boot_part (bloc_device_t *bd, part_t *partition, int partnum) { dprintf("%s: part %p (%p) %d\n", __func__, partition, bd->boot_part, partnum); if (bd->boot_part == NULL) { bd->boot_part = partition; bd->bpartnum = partnum; } } part_t **_bd_parts (bloc_device_t *bd) { return &bd->parts; } part_t **_bd_bparts (bloc_device_t *bd) { return &bd->bparts; } void bd_set_boot_device (bloc_device_t *bd) { #if defined (USE_OPENFIRMWARE) OF_blockdev_set_boot_device(bd->OF_private, bd->bpartnum, "\\\\ofwboot"); #endif } part_t *bd_probe (int boot_device) { char devices[] = { /*'a', 'b',*/ 'c', 'd', 'e', 'f', 'm', '\0', }; bloc_device_t *bd, **cur; part_t *boot_part, *tmp; int i, force_raw; boot_part = NULL; /* Probe bloc devices */ for (i = 0; devices[i] != '\0'; i++) { if (devices[i] == 'm' && boot_device != 'm') break; bd = bd_open(devices[i]); if (bd != NULL) { DPRINTF("New bloc device %c: %p\n", devices[i], bd); for (cur = &bd_list; *cur != NULL; cur = &(*cur)->next) continue; *cur = bd; } else { DPRINTF("No bloc device %c\n", devices[i]); } } /* Probe partitions for each bloc device found */ for (bd = bd_list; bd != NULL; bd = bd->next) { dprintf("Probe partitions for device %c\n", bd->device); if (bd->device == 'm') force_raw = 1; else force_raw = 0; tmp = part_probe(bd, force_raw); if (boot_device == bd->device) { boot_part = tmp; bd_set_boot_device(bd); } } return boot_part; } bloc_device_t *bd_get (int device) { bloc_device_t *cur; for (cur = bd_list; cur != NULL; cur = cur->next) { if (cur->device == device) { DPRINTF("%s: found device %c: %p\n", __func__, device, cur); return cur; } } return NULL; } void bd_put (unused bloc_device_t *bd) { } void bd_sect2CHS (bloc_device_t *bd, uint32_t secnum, int *cyl, int *head, int *sect) { uint32_t tmp; tmp = secnum / bd->sects; *sect = secnum - (tmp * bd->sects) + 1; *cyl = tmp / bd->heads; *head = tmp - (*cyl * bd->heads); } uint32_t bd_CHS2sect (bloc_device_t *bd, int cyl, int head, int sect) { return (((cyl * bd->heads) + head) * bd->sects) + sect - 1; } /* Floppy driver */ #define FDC_OUT_BASE (0x03F0) #define FDC_DOR_PORT (FDC_OUT_BASE + 0x0002) #define FDC_TAPE_PORT (FDC_OUT_BASE + 0x0003) #define FDC_MAIN_STATUS (FDC_OUT_BASE + 0x0004) #define FDC_WRITE_PORT (FDC_OUT_BASE + 0x0005) #define FDC_READ_PORT (FDC_OUT_BASE + 0x0005) static int fdc_read_data (uint8_t *addr, int len) { uint8_t status; int i; for (i = 0; i < len; i++) { status = inb(FDC_MAIN_STATUS); if ((status & 0xD0) != 0xD0) { #if 0 ERROR("fdc_read_data: read data status != READ_DATA: %0x\n", status); #endif return -1; } addr[i] = inb(FDC_READ_PORT); } return 0; } static inline int fdc_write_cmd (uint8_t cmd) { uint8_t status; status = inb(FDC_MAIN_STATUS); if ((status & 0xC0) != 0x80) { #if 0 ERROR("fdc_write_cmd: read data status != WRITE_CMD: %0x\n", status); #endif return -1; } outb(FDC_WRITE_PORT, cmd); return 0; } static int fdc_reset (void) { uint8_t dor; dor = inb(FDC_DOR_PORT); /* Stop motors & enter reset */ dor &= ~0x34; outb(FDC_DOR_PORT, dor); usleep(1000); /* leave reset state */ dor |= 0x04; outb(FDC_DOR_PORT, dor); usleep(1000); return 0; } static int fdc_recalibrate (int drv) { uint8_t data[2]; if (drv == 0) data[0] = 0; else data[0] = 1; if (fdc_write_cmd(0x07) < 0) { ERROR("fdc_recalibrate != WRITE_CMD\n"); return -1; } if (fdc_write_cmd(data[0]) < 0) { ERROR("fdc_recalibrate data\n"); return -1; } /* Wait for drive to go out of busy state */ while ((inb(FDC_MAIN_STATUS) & 0x0F) != 0x00) continue; /* Check command status */ if (fdc_write_cmd(0x08) < 0) { ERROR("fdc_recalibrate != SENSE_INTERRUPT_STATUS\n"); return -1; } data[0] = inb(FDC_READ_PORT); data[1] = inb(FDC_READ_PORT); if (data[0] & 0xD0) { /* recalibrate / seek failed */ return -1; } /* Status should be WRITE_CMD right now */ if ((inb(FDC_MAIN_STATUS) & 0xD0) != 0x80) { ERROR("fdc_recalibrate status\n"); return -1; } return 0; } static int fdc_start_read (int drv, uint8_t hd, uint8_t trk, uint8_t sect, int mt) { uint8_t fdc_cmd[9], status; int i; fdc_cmd[0] = 0x66; if (mt) fdc_cmd[0] |= 0x80; fdc_cmd[1] = 0x00; if (hd) fdc_cmd[1] |= 0x04; if (drv) fdc_cmd[1] |= 0x01; fdc_cmd[2] = trk; fdc_cmd[3] = hd; fdc_cmd[4] = sect; fdc_cmd[5] = 0x02; fdc_cmd[6] = 0x12; fdc_cmd[7] = 0x00; fdc_cmd[8] = 0x00; for (i = 0; i < (int)sizeof(fdc_cmd); i++) { status = inb(FDC_MAIN_STATUS); if ((status & 0xC0) != 0x80) { ERROR("fdc_start_read: write command status != WRITE_CMD: %0x\n", status); return -1; } outb(FDC_WRITE_PORT, fdc_cmd[i]); } status = inb(FDC_MAIN_STATUS); if ((status & 0xD0) != 0xD0) { ERROR("fdc_read_sector: status != READ_DATA: %0x\n", status); return -1; } return 0; } /* FDC driver entry points */ static int fdc_initialize (bloc_device_t *bd, int device) { uint8_t fifo[10]; #if 0 uint32_t tape; #endif int status; if (device > 1) return -1; DPRINTF("Init FDC drive %d\n", device); /* Manage 1.44 MB disks only, for now */ bd->drv = device; bd->heads = 2; bd->trks = 80; bd->sects = 18; bd->seclen = 512; bd->tmp = -1; fdc_reset(); /* Dump registers */ if (fdc_write_cmd(0x0E) < 0) { #if 0 ERROR("fdc_reset: DUMP_REGISTER != WRITE_CMD\n"); #endif return -1; } if (fdc_read_data(fifo, 10) < 0) { ERROR("fdc_reset: DUMP_REGISTER data\n"); return -1; } /* SPECIFY: be sure we're not in DMA mode */ if (fdc_write_cmd(0x03) < 0) { ERROR("fdc_reset: SPECIFY != WRITE_CMD\n"); return -1; } if (fdc_write_cmd(fifo[4]) < 0 || fdc_write_cmd(fifo[5] | 0x01)) { ERROR("fdc_reset: SPECIFY data\n"); return -1; } /* Status should be WRITE_CMD right now */ status = inb(FDC_MAIN_STATUS); if ((status & 0xD0) != 0x80) { ERROR("fdc_initialise: read data status != WRITE_CMD: %0x\n", status); return -1; } /* RECALIBRATE */ if (fdc_recalibrate(device) < 0) { printf("fd%c: no floppy inserted\n", 'a' + device); return -1; } printf("fd%c initialized\n", 'a' + device); return 0; } static int fdc_read_sector (bloc_device_t *bd, void *buffer, int secnum) { int head, cyl, sect; int need_restart; #if DEBUG_BIOS > 1 printf("Read fdc sector: %d at: %0x\n", secnum, (uint32_t)buffer); bd_sect2CHS(bd, secnum, &cyl, &head, §); printf("cur: %d hd: %d trk: %d sect: %d\n", bd->bloc, head, cyl, sect); #endif if (secnum != bd->tmp) { if (fdc_reset() < 0 || fdc_recalibrate(bd->drv) < 0) return -1; need_restart = 1; } else { need_restart = 0; } bd_sect2CHS(bd, secnum, &cyl, &head, §); if (need_restart == 1 || (head == 0 && sect == 1)) { if (need_restart == 0) { /* Read the status */ uint8_t tmp[7]; while (fdc_read_data(tmp, 1) == 0) continue; } #if !defined (DEBUG_BIOS) printf("."); #endif if (fdc_start_read(bd->drv, head, cyl, sect, 1) < 0) return -1; bd->bloc = secnum; bd->tmp = secnum; } if (fdc_read_data(buffer, bd->seclen) < 0) return -1; bd->tmp++; return bd->seclen; } /* SCSI subset */ /* SPC: primary commands, common to all devices */ static int spc_inquiry_req (void *buffer, int maxlen) { uint8_t *p; p = buffer; p[0] = 0x12; /* No page code */ p[1] = 0x00; p[2] = 0x00; p[3] = maxlen >> 8; p[4] = maxlen; p[5] = 0x00; return 6; } static int spc_inquiry_treat (void *buffer, int len) { const uint8_t *p; if (len < 36) return -1; p = buffer; if ((p[0] >> 5) != 0) { ERROR("Logical unit not ready\n"); return -1; } return p[0] & 0x1F; } static int spc_test_unit_ready_req (void *buffer) { uint8_t *p; p = buffer; p[0] = 0x00; p[1] = 0x00; p[2] = 0x00; p[3] = 0x00; p[4] = 0x00; p[5] = 0x00; return 6; } /* MMC: multimedia commands */ static int mmc_read_capacity_req (void *buffer) { uint8_t *p; p = buffer; p[0] = 0x25; p[1] = 0x00; p[2] = 0x00; p[3] = 0x00; p[4] = 0x00; p[5] = 0x00; p[6] = 0x00; p[7] = 0x00; p[8] = 0x00; p[9] = 0x00; return 10; } static int mmc_read_capacity_treat (uint32_t *size, uint32_t *ssize, const void *buffer, int len) { const uint8_t *p; if (len != 8) return -1; p = buffer; /* Only handle CDROM address mode for now */ *size = ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]) + 1; *ssize = ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]); return 0; } static int mmc_read12_req (void *buffer, uint32_t LBA, uint32_t size) { uint8_t *p; p = buffer; p[0] = 0xA8; p[1] = 0x00; p[2] = LBA >> 24; p[3] = LBA >> 16; p[4] = LBA >> 8; p[5] = LBA; p[6] = size >> 24; p[7] = size >> 16; p[8] = size >> 8; p[9] = size; p[10] = 0x00; p[11] = 0x00; return 12; } /* IDE disk driver */ static uint32_t ide_base[2] = { 0x1F0, 0x170, }; static uint32_t ide_base2[2] = { 0x3F6, 0x376, }; typedef struct ide_ops_t { uint8_t (*port_read)(bloc_device_t *bd, int port); void (*port_write)(bloc_device_t *bd, int port, uint8_t value); uint32_t (*data_readl)(bloc_device_t *bd); void (*data_writel)(bloc_device_t *bd, uint32_t val); void (*control_write)(bloc_device_t *bd, uint32_t val); uint32_t base[4]; #ifdef USE_OPENFIRMWARE void *OF_private[2]; #endif } ide_ops_t; /* IDE ISA access */ static uint8_t ide_isa_port_read (bloc_device_t *bd, int port) { return inb(bd->io_base + port); } static void ide_isa_port_write (bloc_device_t *bd, int port, uint8_t value) { outb(bd->io_base + port, value); } static uint32_t ide_isa_data_readl (bloc_device_t *bd) { return inl(bd->io_base); } static void ide_isa_data_writel (bloc_device_t *bd, uint32_t val) { return outl(bd->io_base, val); } static void ide_isa_control_write (bloc_device_t *bd, uint32_t val) { outb(bd->tmp, val); } static ide_ops_t ide_isa_ops = { &ide_isa_port_read, &ide_isa_port_write, &ide_isa_data_readl, &ide_isa_data_writel, &ide_isa_control_write, { 0, }, #ifdef USE_OPENFIRMWARE { NULL, }, #endif }; static ide_ops_t *ide_pci_ops; /* IDE PCI access for pc */ static uint8_t ide_pci_port_read (bloc_device_t *bd, int port) { uint8_t value; value = inb(bd->io_base + port); return value; } static void ide_pci_port_write (bloc_device_t *bd, int port, uint8_t value) { outb(bd->io_base + port, value); } static uint32_t ide_pci_data_readl (bloc_device_t *bd) { return inl(bd->io_base); } static void ide_pci_data_writel (bloc_device_t *bd, uint32_t val) { outl(bd->io_base, val); } static void ide_pci_control_write (bloc_device_t *bd, uint32_t val) { outb(bd->tmp + 2, val); } static ide_ops_t ide_pci_pc_ops = { &ide_pci_port_read, &ide_pci_port_write, &ide_pci_data_readl, &ide_pci_data_writel, &ide_pci_control_write, { 0, }, #ifdef USE_OPENFIRMWARE { NULL, }, #endif }; void ide_pci_pc_register (uint32_t io_base0, uint32_t io_base1, uint32_t io_base2, uint32_t io_base3, void *OF_private0, void *OF_private1) { if (ide_pci_ops == NULL) { ide_pci_ops = malloc(sizeof(ide_ops_t)); if (ide_pci_ops == NULL) return; memcpy(ide_pci_ops, &ide_pci_pc_ops, sizeof(ide_ops_t)); } if ((io_base0 != 0 || io_base1 != 0) && ide_pci_ops->base[0] == 0 && ide_pci_ops->base[2] == 0) { ide_pci_ops->base[0] = io_base0; ide_pci_ops->base[2] = io_base1; #ifdef USE_OPENFIRMWARE ide_pci_ops->OF_private[0] = OF_private0; #endif } if ((io_base2 != 0 || io_base3 != 0) && ide_pci_ops->base[1] == 0 && ide_pci_ops->base[3] == 0) { ide_pci_ops->base[1] = io_base2; ide_pci_ops->base[3] = io_base3; #ifdef USE_OPENFIRMWARE ide_pci_ops->OF_private[1] = OF_private1; #endif } } /* IDE PCI access for pmac */ static uint8_t ide_pmac_port_read (bloc_device_t *bd, int port) { uint32_t addr; if (port != 8) addr = bd->io_base + (port << 4); else addr = bd->io_base + 0x160; eieio(); return *((uint8_t *)addr); } static void ide_pmac_port_write (bloc_device_t *bd, int port, uint8_t value) { uint32_t addr; if (port != 8) addr = bd->io_base + (port << 4); else addr = bd->io_base + 0x160; *((uint8_t *)addr) = value; eieio(); } static uint32_t ide_pmac_data_readl (bloc_device_t *bd) { eieio(); return ldswap32((uint32_t *)bd->io_base); // return *((uint32_t *)bd->io_base); } static void ide_pmac_data_writel (bloc_device_t *bd, uint32_t val) { // *((uint32_t *)bd->io_base) = val; stswap32((uint32_t *)bd->io_base, val); eieio(); } static void ide_pmac_control_write (bloc_device_t *bd, uint32_t val) { ide_pmac_port_write(bd, 8, val); } static ide_ops_t ide_pmac_ops = { &ide_pmac_port_read, &ide_pmac_port_write, &ide_pmac_data_readl, &ide_pmac_data_writel, &ide_pmac_control_write, { 0, }, #ifdef USE_OPENFIRMWARE { NULL, }, #endif }; void ide_pci_pmac_register (uint32_t io_base0, uint32_t io_base1, unused void *OF_private) { if (ide_pci_ops == NULL) { ide_pci_ops = malloc(sizeof(ide_ops_t)); if (ide_pci_ops == NULL) return; memcpy(ide_pci_ops, &ide_pmac_ops, sizeof(ide_ops_t)); } if (io_base0 != 0 && ide_pci_ops->base[0] == 0) { ide_pci_ops->base[0] = io_base0; #ifdef USE_OPENFIRMWARE ide_pci_ops->OF_private[0] = OF_private; #endif } if (io_base1 != 0 && ide_pci_ops->base[1] == 0) { ide_pci_ops->base[1] = io_base1; #ifdef USE_OPENFIRMWARE ide_pci_ops->OF_private[1] = OF_private; #endif } } static inline uint8_t ide_port_read (bloc_device_t *bd, int port) { ide_ops_t *ops = bd->private; return ops->port_read(bd, port); } static inline void ide_port_write (bloc_device_t *bd, int port, uint8_t value) { ide_ops_t *ops = bd->private; ops->port_write(bd, port, value); } static inline uint32_t ide_data_readl (bloc_device_t *bd) { ide_ops_t *ops = bd->private; return ops->data_readl(bd); } static inline void ide_data_writel (bloc_device_t *bd, uint32_t val) { ide_ops_t *ops = bd->private; return ops->data_writel(bd, val); } static inline void ide_control_write (bloc_device_t *bd, uint32_t val) { ide_ops_t *ops = bd->private; return ops->control_write(bd, val); } static int ide_reset (bloc_device_t *bd) { int status, is_cdrom, lcyl; ide_control_write(bd, 0x04); status = ide_port_read(bd, 0x07); if (status != 0x90) { return -1; } ide_control_write(bd, 0x00); if (bd->drv == 0) ide_port_write(bd, 0x06, 0xa0); else ide_port_write(bd, 0x06, 0xb0); lcyl = ide_port_read(bd, 0x04); switch (lcyl) { case 0x00: /* IDE drive */ is_cdrom = 0; break; case 0x14: /* ATAPI device */ is_cdrom = 1; break; default: return -1; } return is_cdrom; } static void atapi_pad_req (void *buffer, int len); static void atapi_make_req (bloc_device_t *bd, uint32_t *buffer, int maxlen); static int atapi_read_sector (bloc_device_t *bd, void *buffer, int secnum); static int ide_initialize (bloc_device_t *bd, int device) { #ifdef USE_OPENFIRMWARE void *OF_parent; #endif const unsigned char *devname, *devtype, *alias; uint32_t atapi_buffer[9]; uint32_t size; int status, base, is_cdrom, len, i; if (device > 1) base = 1; else base = 0; if (ide_pci_ops != NULL) { bd->private = ide_pci_ops; bd->io_base = ide_pci_ops->base[base]; bd->tmp = ide_pci_ops->base[2 + base]; if (bd->io_base == 0x00000000 || bd->io_base == 0xFFFFFFFF) { ERROR("No IDE drive %c\n", device); return -1; } } else { bd->private = &ide_isa_ops; bd->io_base = ide_base[base]; bd->tmp = ide_base2[base]; } bd->drv = device & 1; DPRINTF("Init IDE drive %d-%d (%d)\n", base, bd->drv, device); is_cdrom = ide_reset(bd); printf("ide%d: drive %d: ", (device >> 1), bd->drv); switch(is_cdrom) { case 0: printf("Hard Disk\n"); devname = "disk"; devtype = "hd"; alias = "hd"; break; case 1: printf("CD-ROM\n"); devname = "cdrom"; devtype = "cdrom"; alias = "cd"; break; default: printf("none\n"); devname = NULL; devtype = NULL; alias = NULL; break; } if (is_cdrom < 0) return -1; #ifdef USE_OPENFIRMWARE /* Register disk into OF tree */ OF_parent = ide_pci_ops->OF_private[base]; if (OF_parent != NULL) { bd->OF_private = OF_blockdev_register(OF_parent, bd, devtype, devname, bd->drv, alias); } #endif /* Select drive */ if (bd->drv == 0) ide_port_write(bd, 0x06, 0x40); else ide_port_write(bd, 0x06, 0x50); /* WIN_DEVICE_RESET */ ide_port_write(bd, 0x07, 0x08); status = ide_port_read(bd, 0x07); if (is_cdrom) { if (status != 0x00) { ERROR("WIN_DEVICE_RESET : status %0x != 0x00 (is_cdrom: %d)\n", status, is_cdrom); return -1; } /* TEST_UNIT_READY */ DPRINTF("TEST_UNIT_READY\n"); len = spc_test_unit_ready_req(&atapi_buffer); atapi_pad_req(&atapi_buffer, len); ide_port_write(bd, 0x07, 0xA0); status = ide_port_read(bd, 0x07); if (status != 0x08) { ERROR("ATAPI TEST_UNIT_READY : status %0x != 0x08\n", status); /*return -1;*/ /* fails to boot from cdrom? */ } for (i = 0; i < 3; i++) { ide_data_writel(bd, ldswap32(&atapi_buffer[i])); } status = ide_port_read(bd, 0x07); if (status != 0x40) { ERROR("ATAPI TEST_UNIT_READY : status %0x != 0x40\n", status); return -1; } /* INQUIRY */ DPRINTF("INQUIRY\n"); len = spc_inquiry_req(&atapi_buffer, 36); atapi_pad_req(&atapi_buffer, len); atapi_make_req(bd, atapi_buffer, 36); status = ide_port_read(bd, 0x07); if (status != 0x48) { ERROR("ATAPI INQUIRY : status %0x != 0x48\n", status); return -1; } for (i = 0; i < 9; i++) stswap32(&atapi_buffer[i], ide_data_readl(bd)); if (spc_inquiry_treat(&atapi_buffer, 36) != 0x05) { ERROR("Only ATAPI CDROMs are handled for now\n"); return -1; } /* READ_CAPACITY */ DPRINTF("READ_CAPACITY\n"); len = mmc_read_capacity_req(&atapi_buffer); atapi_pad_req(&atapi_buffer, len); atapi_make_req(bd, atapi_buffer, 8); status = ide_port_read(bd, 0x07); if (status != 0x48) { ERROR("ATAPI READ_CAPACITY : status %0x != 0x48\n", status); return -1; } for (i = 0; i < 2; i++) stswap32(&atapi_buffer[i], ide_data_readl(bd)); if (mmc_read_capacity_treat(&size, &bd->seclen, &atapi_buffer, 8) != 0) { ERROR("Error retrieving ATAPI CDROM capacity\n"); return -1; } bd->read_sector = &atapi_read_sector; DPRINTF("ATAPI: size=%d ssize=%d\n", size, bd->seclen); } else { if (status != 0x41) { ERROR("WIN_DEVICE_RESET : status %0x != 0x41 (is_cdrom: %d)\n", status, is_cdrom); return -1; } /* WIN_READ_NATIVE_MAX */ ide_port_write(bd, 0x07, 0xF8); status = ide_port_read(bd, 0x07); if (status != 0x40) { ERROR("WIN_READ_NATIVE_MAX : status %0x != 0x40\n", status); return -1; } /* Retrieve parameters */ size = (ide_port_read(bd, 0x06) & ~0xF0) << 24; size |= ide_port_read(bd, 0x05) << 16; size |= ide_port_read(bd, 0x04) << 8; size |= ide_port_read(bd, 0x03); bd->seclen = 512; } bd->heads = 16; bd->sects = 64; bd->trks = (size + (16 * 64 - 1)) >> 10; return 0; } static void atapi_pad_req (void *buffer, int len) { uint8_t *p; p = buffer; memset(p + len, 0, 12 - len); } static void atapi_make_req (bloc_device_t *bd, uint32_t *buffer, int maxlen) { int i; /* select drive */ if (bd->drv == 0) ide_port_write(bd, 0x06, 0x40); else ide_port_write(bd, 0x06, 0x50); ide_port_write(bd, 0x04, maxlen & 0xff); ide_port_write(bd, 0x05, (maxlen >> 8) & 0xff); ide_port_write(bd, 0x07, 0xA0); for (i = 0; i < 3; i++) ide_data_writel(bd, ldswap32(&buffer[i])); } static int atapi_read_sector (bloc_device_t *bd, void *buffer, int secnum) { uint32_t atapi_buffer[4]; uint8_t *p; uint32_t status, value; int i, len; len = mmc_read12_req(atapi_buffer, secnum, 1); atapi_pad_req(&atapi_buffer, len); atapi_make_req(bd, atapi_buffer, bd->seclen); status = ide_port_read(bd, 0x07); if (status != 0x48) { ERROR("ATAPI READ12 : status %0x != 0x48\n", status); return -1; } p = buffer; for (i = 0; i < bd->seclen; i += 4) { value = ide_data_readl(bd); *p++ = value; *p++ = value >> 8; *p++ = value >> 16; *p++ = value >> 24; } status = ide_port_read(bd, 0x07); if (status != 0x40) { ERROR("ATAPI READ12 done : status %0x != 0x48\n", status); return -1; } return 0; } static int ide_read_sector (bloc_device_t *bd, void *buffer, int secnum) { uint32_t value; uint8_t *p; int status; int i; bd->drv &= 1; // printf("ide_read_sector: drv %d secnum %d buf %p\n", bd->drv, secnum, buffer); /* select drive & set highest bytes */ if (bd->drv == 0) ide_port_write(bd, 0x06, 0x40 | (secnum >> 24)); else ide_port_write(bd, 0x06, 0x50 | (secnum >> 24)); /* Set hcyl */ ide_port_write(bd, 0x05, secnum >> 16); /* Set lcyl */ ide_port_write(bd, 0x04, secnum >> 8); /* Set sect */ ide_port_write(bd, 0x03, secnum); /* Read 1 sector */ ide_port_write(bd, 0x02, 1); /* WIN_READ */ ide_port_write(bd, 0x07, 0x20); status = ide_port_read(bd, 0x07); // DPRINTF("ide_read_sector: try %d\n", secnum); if (status != 0x58) { ERROR("ide_read_sector: %d status %0x != 0x58\n", secnum, status); return -1; } /* Get data */ p = buffer; for (i = 0; i < bd->seclen; i += 4) { value = ide_data_readl(bd); *p++ = value; *p++ = value >> 8; *p++ = value >> 16; *p++ = value >> 24; } status = ide_port_read(bd, 0x07); if (status != 0x50) { ERROR("ide_read_sector 6: status %0x != 0x50\n", status); return -1; } return bd->seclen; } /* Memory image access driver */ static int mem_initialize (bloc_device_t *bd, int device) { bd->seclen = 512; bd->private = NULL; bd->heads = 1; bd->sects = 1; bd->trks = 1; return device == 'm'; } static int mem_read_sector (bloc_device_t *bd, void *buffer, int secnum) { if (buffer != (char *)bd->private + (bd->seclen * secnum)) { memmove(buffer, (char *)bd->private + (bd->seclen * secnum), bd->seclen); } return bd->seclen; } static int mem_ioctl (bloc_device_t *bd, int func, void *args) { uint32_t *u32; int ret; switch (func) { case MEM_SET_ADDR: bd->private = args; ret = 0; break; case MEM_SET_SIZE: u32 = args; bd->trks = (*u32 + bd->seclen - 1) / bd->seclen + 1; default: ret = -1; break; } return ret; }