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