Home | History | Annotate | Download | only in fs
      1 #include <core.h>
      2 #include <com32.h>
      3 #include <fs.h>
      4 #include <ilog2.h>
      5 
      6 #define RETRY_COUNT 6
      7 
      8 static inline sector_t chs_max(const struct disk *disk)
      9 {
     10     return (sector_t)disk->secpercyl << 10;
     11 }
     12 
     13 struct edd_rdwr_packet {
     14     uint16_t size;
     15     uint16_t blocks;
     16     far_ptr_t buf;
     17     uint64_t lba;
     18 };
     19 
     20 struct edd_disk_params {
     21     uint16_t  len;
     22     uint16_t  flags;
     23     uint32_t  phys_c;
     24     uint32_t  phys_h;
     25     uint32_t  phys_s;
     26     uint64_t  sectors;
     27     uint16_t  sector_size;
     28     far_ptr_t dpte;
     29     uint16_t  devpath_key;
     30     uint8_t   devpath_len;
     31     uint8_t   _pad1[3];
     32     char      bus_type[4];
     33     char      if_type[8];
     34     uint8_t   if_path[8];
     35     uint8_t   dev_path[16];
     36     uint8_t   _pad2;
     37     uint8_t   devpath_csum;	/* Depends on devpath_len! */
     38 } __attribute__((packed));
     39 
     40 static inline bool is_power_of_2(uint32_t x)
     41 {
     42     return !(x & (x-1));
     43 }
     44 
     45 static int chs_rdwr_sectors(struct disk *disk, void *buf,
     46 			    sector_t lba, size_t count, bool is_write)
     47 {
     48     char *ptr = buf;
     49     char *tptr;
     50     size_t chunk, freeseg;
     51     int sector_shift = disk->sector_shift;
     52     uint32_t xlba = lba + disk->part_start; /* Truncated LBA (CHS is << 2 TB) */
     53     uint32_t t;
     54     uint32_t c, h, s;
     55     com32sys_t ireg, oreg;
     56     size_t done = 0;
     57     size_t bytes;
     58     int retry;
     59     uint32_t maxtransfer = disk->maxtransfer;
     60 
     61     if (lba + disk->part_start >= chs_max(disk))
     62 	return 0;		/* Impossible CHS request */
     63 
     64     memset(&ireg, 0, sizeof ireg);
     65 
     66     ireg.eax.b[1] = 0x02 + is_write;
     67     ireg.edx.b[0] = disk->disk_number;
     68 
     69     while (count) {
     70 	chunk = count;
     71 	if (chunk > maxtransfer)
     72 	    chunk = maxtransfer;
     73 
     74 	freeseg = (0x10000 - ((size_t)ptr & 0xffff)) >> sector_shift;
     75 
     76 	if ((size_t)buf <= 0xf0000 && freeseg) {
     77 	    /* Can do a direct load */
     78 	    tptr = ptr;
     79 	} else {
     80 	    /* Either accessing high memory or we're crossing a 64K line */
     81 	    tptr = core_xfer_buf;
     82 	    freeseg = (0x10000 - ((size_t)tptr & 0xffff)) >> sector_shift;
     83 	}
     84 	if (chunk > freeseg)
     85 	    chunk = freeseg;
     86 
     87 	s = xlba % disk->s;
     88 	t = xlba / disk->s;
     89 	h = t % disk->h;
     90 	c = t / disk->h;
     91 
     92 	if (chunk > (disk->s - s))
     93 	    chunk = disk->s - s;
     94 
     95 	bytes = chunk << sector_shift;
     96 
     97 	if (tptr != ptr && is_write)
     98 	    memcpy(tptr, ptr, bytes);
     99 
    100 	ireg.eax.b[0] = chunk;
    101 	ireg.ecx.b[1] = c;
    102 	ireg.ecx.b[0] = ((c & 0x300) >> 2) | (s+1);
    103 	ireg.edx.b[1] = h;
    104 	ireg.ebx.w[0] = OFFS(tptr);
    105 	ireg.es       = SEG(tptr);
    106 
    107 	retry = RETRY_COUNT;
    108 
    109         for (;;) {
    110 	    if (c < 1024) {
    111 		dprintf("CHS[%02x]: %u @ %llu (%u/%u/%u) %04x:%04x %s %p\n",
    112 			ireg.edx.b[0], chunk, xlba, c, h, s+1,
    113 			ireg.es, ireg.ebx.w[0],
    114 			(ireg.eax.b[1] & 1) ? "<-" : "->",
    115 			ptr);
    116 
    117 		__intcall(0x13, &ireg, &oreg);
    118 		if (!(oreg.eflags.l & EFLAGS_CF))
    119 		    break;
    120 
    121 		dprintf("CHS: error AX = %04x\n", oreg.eax.w[0]);
    122 
    123 		if (retry--)
    124 		    continue;
    125 
    126 		/*
    127 		 * For any starting value, this will always end with
    128 		 * ..., 1, 0
    129 		 */
    130 		chunk >>= 1;
    131 		if (chunk) {
    132 		    maxtransfer = chunk;
    133 		    retry = RETRY_COUNT;
    134 		    ireg.eax.b[0] = chunk;
    135 		    continue;
    136 		}
    137 	    }
    138 
    139 	    printf("CHS: Error %04x %s sector %llu (%u/%u/%u)\n",
    140 		   oreg.eax.w[0],
    141 		   is_write ? "writing" : "reading",
    142 		   lba, c, h, s+1);
    143 	    return done;	/* Failure */
    144 	}
    145 
    146 	bytes = chunk << sector_shift;
    147 
    148 	if (tptr != ptr && !is_write)
    149 	    memcpy(ptr, tptr, bytes);
    150 
    151 	/* If we dropped maxtransfer, it eventually worked, so remember it */
    152 	disk->maxtransfer = maxtransfer;
    153 
    154 	ptr   += bytes;
    155 	xlba  += chunk;
    156 	count -= chunk;
    157 	done  += chunk;
    158     }
    159 
    160     return done;
    161 }
    162 
    163 static int edd_rdwr_sectors(struct disk *disk, void *buf,
    164 			    sector_t lba, size_t count, bool is_write)
    165 {
    166     static __lowmem struct edd_rdwr_packet pkt;
    167     char *ptr = buf;
    168     char *tptr;
    169     size_t chunk, freeseg;
    170     int sector_shift = disk->sector_shift;
    171     com32sys_t ireg, oreg, reset;
    172     size_t done = 0;
    173     size_t bytes;
    174     int retry;
    175     uint32_t maxtransfer = disk->maxtransfer;
    176 
    177     memset(&ireg, 0, sizeof ireg);
    178 
    179     ireg.eax.b[1] = 0x42 + is_write;
    180     ireg.edx.b[0] = disk->disk_number;
    181     ireg.ds       = SEG(&pkt);
    182     ireg.esi.w[0] = OFFS(&pkt);
    183 
    184     memset(&reset, 0, sizeof reset);
    185 
    186     lba += disk->part_start;
    187     while (count) {
    188 	chunk = count;
    189 	if (chunk > maxtransfer)
    190 	    chunk = maxtransfer;
    191 
    192 	freeseg = (0x10000 - ((size_t)ptr & 0xffff)) >> sector_shift;
    193 
    194 	if ((size_t)ptr <= 0xf0000 && freeseg) {
    195 	    /* Can do a direct load */
    196 	    tptr = ptr;
    197 	} else {
    198 	    /* Either accessing high memory or we're crossing a 64K line */
    199 	    tptr = core_xfer_buf;
    200 	    freeseg = (0x10000 - ((size_t)tptr & 0xffff)) >> sector_shift;
    201 	}
    202 	if (chunk > freeseg)
    203 	    chunk = freeseg;
    204 
    205 	bytes = chunk << sector_shift;
    206 
    207 	if (tptr != ptr && is_write)
    208 	    memcpy(tptr, ptr, bytes);
    209 
    210 	retry = RETRY_COUNT;
    211 
    212 	for (;;) {
    213 	    pkt.size   = sizeof pkt;
    214 	    pkt.blocks = chunk;
    215 	    pkt.buf    = FAR_PTR(tptr);
    216 	    pkt.lba    = lba;
    217 
    218 	    dprintf("EDD[%02x]: %u @ %llu %04x:%04x %s %p\n",
    219 		    ireg.edx.b[0], pkt.blocks, pkt.lba,
    220 		    pkt.buf.seg, pkt.buf.offs,
    221 		    (ireg.eax.b[1] & 1) ? "<-" : "->",
    222 		    ptr);
    223 
    224 	    __intcall(0x13, &ireg, &oreg);
    225 	    if (!(oreg.eflags.l & EFLAGS_CF))
    226 		break;
    227 
    228 	    dprintf("EDD: error AX = %04x\n", oreg.eax.w[0]);
    229 
    230 	    if (retry--)
    231 		continue;
    232 
    233 	    /*
    234 	     * Some systems seem to get "stuck" in an error state when
    235 	     * using EBIOS.  Doesn't happen when using CBIOS, which is
    236 	     * good, since some other systems get timeout failures
    237 	     * waiting for the floppy disk to spin up.
    238 	     */
    239 	    __intcall(0x13, &reset, NULL);
    240 
    241 	    /* For any starting value, this will always end with ..., 1, 0 */
    242 	    chunk >>= 1;
    243 	    if (chunk) {
    244 		maxtransfer = chunk;
    245 		retry = RETRY_COUNT;
    246 		continue;
    247 	    }
    248 
    249 	    /*
    250 	     * Total failure.  There are systems which identify as
    251 	     * EDD-capable but aren't; the known such systems return
    252 	     * error code AH=1 (invalid function), but let's not
    253 	     * assume that for now.
    254 	     *
    255 	     * Try to fall back to CHS.  If the LBA is absurd, the
    256 	     * chs_max() test in chs_rdwr_sectors() will catch it.
    257 	     */
    258 	    done = chs_rdwr_sectors(disk, buf, lba - disk->part_start,
    259 				    count, is_write);
    260 	    if (done == (count << sector_shift)) {
    261 		/* Successful, assume this is a CHS disk */
    262 		disk->rdwr_sectors = chs_rdwr_sectors;
    263 		return done;
    264 	    }
    265 	    printf("EDD: Error %04x %s sector %llu\n",
    266 		   oreg.eax.w[0],
    267 		   is_write ? "writing" : "reading",
    268 		   lba);
    269 	    return done;	/* Failure */
    270 	}
    271 
    272 	bytes = chunk << sector_shift;
    273 
    274 	if (tptr != ptr && !is_write)
    275 	    memcpy(ptr, tptr, bytes);
    276 
    277 	/* If we dropped maxtransfer, it eventually worked, so remember it */
    278 	disk->maxtransfer = maxtransfer;
    279 
    280 	ptr   += bytes;
    281 	lba   += chunk;
    282 	count -= chunk;
    283 	done  += chunk;
    284     }
    285     return done;
    286 }
    287 
    288 struct disk *bios_disk_init(void *private)
    289 {
    290     static struct disk disk;
    291     struct bios_disk_private *priv = (struct bios_disk_private *)private;
    292     com32sys_t *regs = priv->regs;
    293     static __lowmem struct edd_disk_params edd_params;
    294     com32sys_t ireg, oreg;
    295     uint8_t devno = regs->edx.b[0];
    296     bool cdrom = regs->edx.b[1];
    297     sector_t part_start = regs->ecx.l | ((sector_t)regs->ebx.l << 32);
    298     uint16_t bsHeads = regs->esi.w[0];
    299     uint16_t bsSecPerTrack = regs->edi.w[0];
    300     uint32_t MaxTransfer = regs->ebp.l;
    301     bool ebios;
    302     int sector_size;
    303     unsigned int hard_max_transfer;
    304 
    305     memset(&ireg, 0, sizeof ireg);
    306     ireg.edx.b[0] = devno;
    307 
    308     if (cdrom) {
    309 	/*
    310 	 * The query functions don't work right on some CD-ROM stacks.
    311 	 * Known affected systems: ThinkPad T22, T23.
    312 	 */
    313 	sector_size = 2048;
    314 	ebios = true;
    315 	hard_max_transfer = 32;
    316     } else {
    317 	sector_size = 512;
    318 	ebios = false;
    319 	hard_max_transfer = 63;
    320 
    321 	/* CBIOS parameters */
    322 	disk.h = bsHeads;
    323 	disk.s = bsSecPerTrack;
    324 
    325 	if ((int8_t)devno < 0) {
    326 	    /* Get hard disk geometry from BIOS */
    327 
    328 	    ireg.eax.b[1] = 0x08;
    329 	    __intcall(0x13, &ireg, &oreg);
    330 
    331 	    if (!(oreg.eflags.l & EFLAGS_CF)) {
    332 		disk.h = oreg.edx.b[1] + 1;
    333 		disk.s = oreg.ecx.b[0] & 63;
    334 	    }
    335 	}
    336 
    337         memset(&ireg, 0, sizeof ireg);
    338 	/* Get EBIOS support */
    339 	ireg.eax.b[1] = 0x41;
    340 	ireg.ebx.w[0] = 0x55aa;
    341 	ireg.edx.b[0] = devno;
    342 	ireg.eflags.b[0] = 0x3;	/* CF set */
    343 
    344 	__intcall(0x13, &ireg, &oreg);
    345 
    346 	if (!(oreg.eflags.l & EFLAGS_CF) &&
    347 	    oreg.ebx.w[0] == 0xaa55 && (oreg.ecx.b[0] & 1)) {
    348 	    ebios = true;
    349 	    hard_max_transfer = 127;
    350 
    351 	    /* Query EBIOS parameters */
    352 	    /* The memset() is needed once this function can be called
    353 	       more than once */
    354 	    /* memset(&edd_params, 0, sizeof edd_params);  */
    355 	    edd_params.len = sizeof edd_params;
    356 
    357             memset(&ireg, 0, sizeof ireg);
    358 	    ireg.eax.b[1] = 0x48;
    359 	    ireg.edx.b[0] = devno;
    360 	    ireg.ds = SEG(&edd_params);
    361 	    ireg.esi.w[0] = OFFS(&edd_params);
    362 	    __intcall(0x13, &ireg, &oreg);
    363 
    364 	    if (!(oreg.eflags.l & EFLAGS_CF) && oreg.eax.b[1] == 0) {
    365 		if (edd_params.len < sizeof edd_params)
    366 		    memset((char *)&edd_params + edd_params.len, 0,
    367 			   sizeof edd_params - edd_params.len);
    368 
    369 		if (edd_params.sector_size >= 512 &&
    370 		    is_power_of_2(edd_params.sector_size))
    371 		    sector_size = edd_params.sector_size;
    372 	    }
    373 	}
    374 
    375     }
    376 
    377     disk.disk_number   = devno;
    378     disk.sector_size   = sector_size;
    379     disk.sector_shift  = ilog2(sector_size);
    380     disk.part_start    = part_start;
    381     disk.secpercyl     = disk.h * disk.s;
    382     disk.rdwr_sectors  = ebios ? edd_rdwr_sectors : chs_rdwr_sectors;
    383 
    384     if (!MaxTransfer || MaxTransfer > hard_max_transfer)
    385 	MaxTransfer = hard_max_transfer;
    386 
    387     disk.maxtransfer   = MaxTransfer;
    388 
    389     dprintf("disk %02x cdrom %d type %d sector %u/%u offset %llu limit %u\n",
    390 	    devno, cdrom, ebios, sector_size, disk.sector_shift,
    391 	    part_start, disk.maxtransfer);
    392 
    393     disk.private = private;
    394     return &disk;
    395 }
    396 
    397 void pm_fs_init(com32sys_t *regs)
    398 {
    399 	static struct bios_disk_private priv;
    400 
    401 	priv.regs = regs;
    402 	fs_init((const struct fs_ops **)regs->eax.l, (void *)&priv);
    403 }
    404