Kevin O'Connor | 31d8c8a | 2008-03-04 19:56:41 -0500 | [diff] [blame] | 1 | // 16bit code to access cdrom drives. |
| 2 | // |
| 3 | // Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> |
| 4 | // Copyright (C) 2002 MandrakeSoft S.A. |
| 5 | // |
Kevin O'Connor | b1b7c2a | 2009-01-15 20:52:58 -0500 | [diff] [blame] | 6 | // This file may be distributed under the terms of the GNU LGPLv3 license. |
Kevin O'Connor | 31d8c8a | 2008-03-04 19:56:41 -0500 | [diff] [blame] | 7 | |
| 8 | #include "disk.h" // cdrom_13 |
| 9 | #include "util.h" // memset |
Kevin O'Connor | 9521e26 | 2008-07-04 13:04:29 -0400 | [diff] [blame] | 10 | #include "bregs.h" // struct bregs |
| 11 | #include "biosvar.h" // GET_EBDA |
Kevin O'Connor | 4524bf7 | 2008-12-31 00:31:03 -0500 | [diff] [blame] | 12 | #include "atabits.h" // ATA_TYPE_ATAPI |
Kevin O'Connor | 31d8c8a | 2008-03-04 19:56:41 -0500 | [diff] [blame] | 13 | |
| 14 | |
| 15 | /**************************************************************** |
| 16 | * CDROM functions |
| 17 | ****************************************************************/ |
| 18 | |
| 19 | // read disk drive size |
| 20 | static void |
| 21 | cdrom_1315(struct bregs *regs, u8 device) |
| 22 | { |
| 23 | disk_ret(regs, DISK_RET_EADDRNOTFOUND); |
| 24 | } |
| 25 | |
| 26 | // lock |
| 27 | static void |
| 28 | cdrom_134500(struct bregs *regs, u8 device) |
| 29 | { |
Kevin O'Connor | 0881537 | 2008-12-29 21:16:31 -0500 | [diff] [blame] | 30 | u16 ebda_seg = get_ebda_seg(); |
| 31 | u8 locks = GET_EBDA2(ebda_seg, cdrom_locks[device]); |
Kevin O'Connor | 31d8c8a | 2008-03-04 19:56:41 -0500 | [diff] [blame] | 32 | if (locks == 0xff) { |
| 33 | regs->al = 1; |
| 34 | disk_ret(regs, DISK_RET_ETOOMANYLOCKS); |
| 35 | return; |
| 36 | } |
Kevin O'Connor | 0881537 | 2008-12-29 21:16:31 -0500 | [diff] [blame] | 37 | SET_EBDA2(ebda_seg, cdrom_locks[device], locks + 1); |
Kevin O'Connor | 31d8c8a | 2008-03-04 19:56:41 -0500 | [diff] [blame] | 38 | regs->al = 1; |
| 39 | disk_ret(regs, DISK_RET_SUCCESS); |
| 40 | } |
| 41 | |
| 42 | // unlock |
| 43 | static void |
| 44 | cdrom_134501(struct bregs *regs, u8 device) |
| 45 | { |
Kevin O'Connor | 0881537 | 2008-12-29 21:16:31 -0500 | [diff] [blame] | 46 | u16 ebda_seg = get_ebda_seg(); |
| 47 | u8 locks = GET_EBDA2(ebda_seg, cdrom_locks[device]); |
Kevin O'Connor | 31d8c8a | 2008-03-04 19:56:41 -0500 | [diff] [blame] | 48 | if (locks == 0x00) { |
| 49 | regs->al = 0; |
| 50 | disk_ret(regs, DISK_RET_ENOTLOCKED); |
| 51 | return; |
| 52 | } |
| 53 | locks--; |
Kevin O'Connor | 0881537 | 2008-12-29 21:16:31 -0500 | [diff] [blame] | 54 | SET_EBDA2(ebda_seg, cdrom_locks[device], locks); |
Kevin O'Connor | 31d8c8a | 2008-03-04 19:56:41 -0500 | [diff] [blame] | 55 | regs->al = (locks ? 1 : 0); |
| 56 | disk_ret(regs, DISK_RET_SUCCESS); |
| 57 | } |
| 58 | |
| 59 | // status |
| 60 | static void |
| 61 | cdrom_134502(struct bregs *regs, u8 device) |
| 62 | { |
Kevin O'Connor | 609da23 | 2008-12-28 23:18:57 -0500 | [diff] [blame] | 63 | u8 locks = GET_EBDA(cdrom_locks[device]); |
Kevin O'Connor | 31d8c8a | 2008-03-04 19:56:41 -0500 | [diff] [blame] | 64 | regs->al = (locks ? 1 : 0); |
| 65 | disk_ret(regs, DISK_RET_SUCCESS); |
| 66 | } |
| 67 | |
| 68 | static void |
| 69 | cdrom_1345XX(struct bregs *regs, u8 device) |
| 70 | { |
| 71 | disk_ret(regs, DISK_RET_EPARAM); |
| 72 | } |
| 73 | |
| 74 | // IBM/MS lock/unlock drive |
| 75 | static void |
| 76 | cdrom_1345(struct bregs *regs, u8 device) |
| 77 | { |
| 78 | switch (regs->al) { |
| 79 | case 0x00: cdrom_134500(regs, device); break; |
| 80 | case 0x01: cdrom_134501(regs, device); break; |
| 81 | case 0x02: cdrom_134502(regs, device); break; |
| 82 | default: cdrom_1345XX(regs, device); break; |
| 83 | } |
| 84 | } |
| 85 | |
| 86 | // IBM/MS eject media |
| 87 | static void |
| 88 | cdrom_1346(struct bregs *regs, u8 device) |
| 89 | { |
Kevin O'Connor | 609da23 | 2008-12-28 23:18:57 -0500 | [diff] [blame] | 90 | u8 locks = GET_EBDA(cdrom_locks[device]); |
Kevin O'Connor | 31d8c8a | 2008-03-04 19:56:41 -0500 | [diff] [blame] | 91 | if (locks != 0) { |
| 92 | disk_ret(regs, DISK_RET_ELOCKED); |
| 93 | return; |
| 94 | } |
| 95 | |
| 96 | // FIXME should handle 0x31 no media in device |
| 97 | // FIXME should handle 0xb5 valid request failed |
| 98 | |
| 99 | // Call removable media eject |
| 100 | struct bregs br; |
| 101 | memset(&br, 0, sizeof(br)); |
| 102 | br.ah = 0x52; |
| 103 | call16_int(0x15, &br); |
| 104 | |
| 105 | if (br.ah || br.flags & F_CF) { |
| 106 | disk_ret(regs, DISK_RET_ELOCKED); |
| 107 | return; |
| 108 | } |
| 109 | disk_ret(regs, DISK_RET_SUCCESS); |
| 110 | } |
| 111 | |
| 112 | // IBM/MS extended media change |
| 113 | static void |
| 114 | cdrom_1349(struct bregs *regs, u8 device) |
| 115 | { |
Kevin O'Connor | 6c78122 | 2008-03-09 12:19:23 -0400 | [diff] [blame] | 116 | set_fail(regs); |
Kevin O'Connor | 31d8c8a | 2008-03-04 19:56:41 -0500 | [diff] [blame] | 117 | // always send changed ?? |
| 118 | regs->ah = DISK_RET_ECHANGED; |
Kevin O'Connor | 31d8c8a | 2008-03-04 19:56:41 -0500 | [diff] [blame] | 119 | } |
| 120 | |
| 121 | static void |
| 122 | cdrom_ok(struct bregs *regs, u8 device) |
| 123 | { |
| 124 | disk_ret(regs, DISK_RET_SUCCESS); |
| 125 | } |
| 126 | |
| 127 | static void |
| 128 | cdrom_wp(struct bregs *regs, u8 device) |
| 129 | { |
| 130 | disk_ret(regs, DISK_RET_EWRITEPROTECT); |
| 131 | } |
| 132 | |
| 133 | void |
| 134 | cdrom_13(struct bregs *regs, u8 device) |
| 135 | { |
| 136 | //debug_stub(regs); |
| 137 | |
| 138 | switch (regs->ah) { |
| 139 | case 0x15: cdrom_1315(regs, device); break; |
| 140 | case 0x45: cdrom_1345(regs, device); break; |
| 141 | case 0x46: cdrom_1346(regs, device); break; |
| 142 | case 0x49: cdrom_1349(regs, device); break; |
| 143 | |
| 144 | // These functions are the same as for hard disks |
| 145 | case 0x01: |
| 146 | case 0x41: |
| 147 | case 0x42: |
| 148 | case 0x44: |
| 149 | case 0x47: |
| 150 | case 0x48: |
| 151 | case 0x4e: |
| 152 | disk_13(regs, device); |
| 153 | break; |
| 154 | |
| 155 | // all these functions return SUCCESS |
| 156 | case 0x00: // disk controller reset |
| 157 | case 0x09: // initialize drive parameters |
| 158 | case 0x0c: // seek to specified cylinder |
| 159 | case 0x0d: // alternate disk reset |
| 160 | case 0x10: // check drive ready |
| 161 | case 0x11: // recalibrate |
| 162 | case 0x14: // controller internal diagnostic |
| 163 | case 0x16: // detect disk change |
| 164 | cdrom_ok(regs, device); |
| 165 | break; |
| 166 | |
| 167 | // all these functions return disk write-protected |
| 168 | case 0x03: // write disk sectors |
| 169 | case 0x05: // format disk track |
| 170 | case 0x43: // IBM/MS extended write |
| 171 | cdrom_wp(regs, device); |
| 172 | break; |
| 173 | |
| 174 | default: disk_13XX(regs, device); break; |
| 175 | } |
| 176 | } |
| 177 | |
| 178 | |
| 179 | /**************************************************************** |
| 180 | * CD emulation |
| 181 | ****************************************************************/ |
| 182 | |
Kevin O'Connor | 31d8c8a | 2008-03-04 19:56:41 -0500 | [diff] [blame] | 183 | // read disk drive parameters |
| 184 | static void |
| 185 | cdemu_1308(struct bregs *regs, u8 device) |
| 186 | { |
Kevin O'Connor | 4a16ef6 | 2008-12-31 00:09:28 -0500 | [diff] [blame] | 187 | u16 ebda_seg = get_ebda_seg(); |
| 188 | u16 nlc = GET_EBDA2(ebda_seg, cdemu.cylinders) - 1; |
| 189 | u16 nlh = GET_EBDA2(ebda_seg, cdemu.heads) - 1; |
| 190 | u16 nlspt = GET_EBDA2(ebda_seg, cdemu.spt); |
Kevin O'Connor | 31d8c8a | 2008-03-04 19:56:41 -0500 | [diff] [blame] | 191 | |
| 192 | regs->al = 0x00; |
| 193 | regs->bl = 0x00; |
| 194 | regs->ch = nlc & 0xff; |
| 195 | regs->cl = ((nlc >> 2) & 0xc0) | (nlspt & 0x3f); |
| 196 | regs->dh = nlh; |
| 197 | // FIXME ElTorito Various. should send the real count of drives 1 or 2 |
| 198 | // FIXME ElTorito Harddisk. should send the HD count |
| 199 | regs->dl = 0x02; |
Kevin O'Connor | 4a16ef6 | 2008-12-31 00:09:28 -0500 | [diff] [blame] | 200 | u8 media = GET_EBDA2(ebda_seg, cdemu.media); |
Kevin O'Connor | 31d8c8a | 2008-03-04 19:56:41 -0500 | [diff] [blame] | 201 | if (media <= 3) |
| 202 | regs->bl = media * 2; |
| 203 | |
| 204 | regs->es = SEG_BIOS; |
Kevin O'Connor | 117fc21 | 2008-04-13 18:17:02 -0400 | [diff] [blame] | 205 | regs->di = (u32)&diskette_param_table2; |
Kevin O'Connor | 31d8c8a | 2008-03-04 19:56:41 -0500 | [diff] [blame] | 206 | |
| 207 | disk_ret(regs, DISK_RET_SUCCESS); |
| 208 | } |
| 209 | |
| 210 | void |
| 211 | cdemu_13(struct bregs *regs) |
| 212 | { |
| 213 | //debug_stub(regs); |
| 214 | |
Kevin O'Connor | 4a16ef6 | 2008-12-31 00:09:28 -0500 | [diff] [blame] | 215 | u16 ebda_seg = get_ebda_seg(); |
| 216 | u8 device = GET_EBDA2(ebda_seg, cdemu.controller_index) * 2; |
| 217 | device += GET_EBDA2(ebda_seg, cdemu.device_spec); |
Kevin O'Connor | 31d8c8a | 2008-03-04 19:56:41 -0500 | [diff] [blame] | 218 | |
| 219 | switch (regs->ah) { |
Kevin O'Connor | aa2590c | 2008-03-22 23:13:24 -0400 | [diff] [blame] | 220 | // These functions are the same as for hard disks |
| 221 | case 0x02: |
| 222 | case 0x04: |
| 223 | disk_13(regs, device); |
| 224 | break; |
| 225 | |
Kevin O'Connor | aa7ddd7 | 2008-03-22 23:58:26 -0400 | [diff] [blame] | 226 | // These functions are the same as standard CDROM. |
| 227 | case 0x00: |
| 228 | case 0x01: |
| 229 | case 0x03: |
| 230 | case 0x05: |
| 231 | case 0x09: |
| 232 | case 0x0c: |
| 233 | case 0x0d: |
| 234 | case 0x10: |
| 235 | case 0x11: |
| 236 | case 0x14: |
| 237 | case 0x15: |
| 238 | case 0x16: |
| 239 | cdrom_13(regs, device); |
| 240 | break; |
| 241 | |
Kevin O'Connor | 31d8c8a | 2008-03-04 19:56:41 -0500 | [diff] [blame] | 242 | case 0x08: cdemu_1308(regs, device); break; |
Kevin O'Connor | aa2590c | 2008-03-22 23:13:24 -0400 | [diff] [blame] | 243 | |
Kevin O'Connor | aa7ddd7 | 2008-03-22 23:58:26 -0400 | [diff] [blame] | 244 | default: disk_13XX(regs, device); break; |
Kevin O'Connor | 31d8c8a | 2008-03-04 19:56:41 -0500 | [diff] [blame] | 245 | } |
| 246 | } |
| 247 | |
| 248 | struct eltorito_s { |
| 249 | u8 size; |
| 250 | u8 media; |
| 251 | u8 emulated_drive; |
| 252 | u8 controller_index; |
| 253 | u32 ilba; |
| 254 | u16 device_spec; |
| 255 | u16 buffer_segment; |
| 256 | u16 load_segment; |
| 257 | u16 sector_count; |
| 258 | u8 cylinders; |
| 259 | u8 sectors; |
| 260 | u8 heads; |
| 261 | }; |
| 262 | |
| 263 | #define SET_INT13ET(regs,var,val) \ |
| 264 | SET_FARVAR((regs)->ds, ((struct eltorito_s*)((regs)->si+0))->var, (val)) |
| 265 | |
| 266 | // ElTorito - Terminate disk emu |
| 267 | void |
| 268 | cdemu_134b(struct bregs *regs) |
| 269 | { |
| 270 | // FIXME ElTorito Hardcoded |
Kevin O'Connor | 4a16ef6 | 2008-12-31 00:09:28 -0500 | [diff] [blame] | 271 | u16 ebda_seg = get_ebda_seg(); |
Kevin O'Connor | 31d8c8a | 2008-03-04 19:56:41 -0500 | [diff] [blame] | 272 | SET_INT13ET(regs, size, 0x13); |
Kevin O'Connor | 4a16ef6 | 2008-12-31 00:09:28 -0500 | [diff] [blame] | 273 | SET_INT13ET(regs, media, GET_EBDA2(ebda_seg, cdemu.media)); |
| 274 | SET_INT13ET(regs, emulated_drive, GET_EBDA2(ebda_seg, cdemu.emulated_drive)); |
| 275 | SET_INT13ET(regs, controller_index |
| 276 | , GET_EBDA2(ebda_seg, cdemu.controller_index)); |
| 277 | SET_INT13ET(regs, ilba, GET_EBDA2(ebda_seg, cdemu.ilba)); |
| 278 | SET_INT13ET(regs, device_spec, GET_EBDA2(ebda_seg, cdemu.device_spec)); |
| 279 | SET_INT13ET(regs, buffer_segment, GET_EBDA2(ebda_seg, cdemu.buffer_segment)); |
| 280 | SET_INT13ET(regs, load_segment, GET_EBDA2(ebda_seg, cdemu.load_segment)); |
| 281 | SET_INT13ET(regs, sector_count, GET_EBDA2(ebda_seg, cdemu.sector_count)); |
| 282 | SET_INT13ET(regs, cylinders, GET_EBDA2(ebda_seg, cdemu.cylinders)); |
| 283 | SET_INT13ET(regs, sectors, GET_EBDA2(ebda_seg, cdemu.spt)); |
| 284 | SET_INT13ET(regs, heads, GET_EBDA2(ebda_seg, cdemu.heads)); |
Kevin O'Connor | 31d8c8a | 2008-03-04 19:56:41 -0500 | [diff] [blame] | 285 | |
| 286 | // If we have to terminate emulation |
| 287 | if (regs->al == 0x00) { |
| 288 | // FIXME ElTorito Various. Should be handled accordingly to spec |
Kevin O'Connor | 4a16ef6 | 2008-12-31 00:09:28 -0500 | [diff] [blame] | 289 | SET_EBDA2(ebda_seg, cdemu.active, 0x00); // bye bye |
Kevin O'Connor | 31d8c8a | 2008-03-04 19:56:41 -0500 | [diff] [blame] | 290 | } |
| 291 | |
| 292 | disk_ret(regs, DISK_RET_SUCCESS); |
| 293 | } |
Kevin O'Connor | 180a959 | 2008-03-04 22:50:53 -0500 | [diff] [blame] | 294 | |
| 295 | |
| 296 | /**************************************************************** |
| 297 | * CD booting |
| 298 | ****************************************************************/ |
| 299 | |
| 300 | // Request SENSE |
Kevin O'Connor | a05223c | 2008-06-28 12:15:57 -0400 | [diff] [blame] | 301 | static int |
Kevin O'Connor | a0c6879 | 2009-02-12 20:47:59 -0500 | [diff] [blame^] | 302 | atapi_get_sense(int device, u8 *asc, u8 *ascq) |
Kevin O'Connor | 180a959 | 2008-03-04 22:50:53 -0500 | [diff] [blame] | 303 | { |
Kevin O'Connor | a0c6879 | 2009-02-12 20:47:59 -0500 | [diff] [blame^] | 304 | u8 atacmd[12], buffer[18]; |
Kevin O'Connor | 180a959 | 2008-03-04 22:50:53 -0500 | [diff] [blame] | 305 | memset(atacmd, 0, sizeof(atacmd)); |
| 306 | atacmd[0] = ATA_CMD_REQUEST_SENSE; |
| 307 | atacmd[4] = sizeof(buffer); |
Kevin O'Connor | a05223c | 2008-06-28 12:15:57 -0400 | [diff] [blame] | 308 | int ret = ata_cmd_packet(device, atacmd, sizeof(atacmd), sizeof(buffer) |
Kevin O'Connor | 35ae726 | 2009-01-19 15:44:44 -0500 | [diff] [blame] | 309 | , MAKE_FLATPTR(GET_SEG(SS), buffer)); |
Kevin O'Connor | a0c6879 | 2009-02-12 20:47:59 -0500 | [diff] [blame^] | 310 | if (ret) |
Kevin O'Connor | a05223c | 2008-06-28 12:15:57 -0400 | [diff] [blame] | 311 | return ret; |
Kevin O'Connor | 180a959 | 2008-03-04 22:50:53 -0500 | [diff] [blame] | 312 | |
| 313 | *asc = buffer[12]; |
| 314 | *ascq = buffer[13]; |
| 315 | |
| 316 | return 0; |
| 317 | } |
| 318 | |
Kevin O'Connor | a0c6879 | 2009-02-12 20:47:59 -0500 | [diff] [blame^] | 319 | // Request capacity |
| 320 | static int |
| 321 | atapi_read_capacity(int device, u32 *blksize, u32 *sectors) |
| 322 | { |
| 323 | u8 packet[12], buf[8]; |
| 324 | memset(packet, 0, sizeof(packet)); |
| 325 | packet[0] = 0x25; /* READ CAPACITY */ |
| 326 | int ret = ata_cmd_packet(device, packet, sizeof(packet), sizeof(buf) |
| 327 | , MAKE_FLATPTR(GET_SEG(SS), buf)); |
| 328 | if (ret) |
| 329 | return ret; |
| 330 | |
| 331 | *blksize = (((u32)buf[4] << 24) | ((u32)buf[5] << 16) |
| 332 | | ((u32)buf[6] << 8) | ((u32)buf[7] << 0)); |
| 333 | *sectors = (((u32)buf[0] << 24) | ((u32)buf[1] << 16) |
| 334 | | ((u32)buf[2] << 8) | ((u32)buf[3] << 0)); |
| 335 | |
| 336 | return 0; |
| 337 | } |
| 338 | |
Kevin O'Connor | a05223c | 2008-06-28 12:15:57 -0400 | [diff] [blame] | 339 | static int |
Kevin O'Connor | 180a959 | 2008-03-04 22:50:53 -0500 | [diff] [blame] | 340 | atapi_is_ready(u16 device) |
| 341 | { |
Kevin O'Connor | a0c6879 | 2009-02-12 20:47:59 -0500 | [diff] [blame^] | 342 | dprintf(6, "atapi_is_ready (device=%d)\n", device); |
Kevin O'Connor | 180a959 | 2008-03-04 22:50:53 -0500 | [diff] [blame] | 343 | |
Kevin O'Connor | a0c6879 | 2009-02-12 20:47:59 -0500 | [diff] [blame^] | 344 | /* Retry READ CAPACITY for 5 seconds unless MEDIUM NOT PRESENT is |
| 345 | * reported by the device. If the device reports "IN PROGRESS", |
Kevin O'Connor | 180a959 | 2008-03-04 22:50:53 -0500 | [diff] [blame] | 346 | * 30 seconds is added. */ |
Kevin O'Connor | a0c6879 | 2009-02-12 20:47:59 -0500 | [diff] [blame^] | 347 | u32 blksize, sectors; |
| 348 | int in_progress = 0; |
| 349 | u64 end = calc_future_tsc(5000); |
| 350 | for (;;) { |
| 351 | if (rdtscll() > end) { |
Kevin O'Connor | a05223c | 2008-06-28 12:15:57 -0400 | [diff] [blame] | 352 | dprintf(1, "read capacity failed\n"); |
Kevin O'Connor | 180a959 | 2008-03-04 22:50:53 -0500 | [diff] [blame] | 353 | return -1; |
| 354 | } |
Kevin O'Connor | a0c6879 | 2009-02-12 20:47:59 -0500 | [diff] [blame^] | 355 | |
| 356 | int ret = atapi_read_capacity(device, &blksize, §ors); |
| 357 | if (!ret) |
| 358 | // Success |
Kevin O'Connor | 180a959 | 2008-03-04 22:50:53 -0500 | [diff] [blame] | 359 | break; |
| 360 | |
Kevin O'Connor | a0c6879 | 2009-02-12 20:47:59 -0500 | [diff] [blame^] | 361 | u8 asc, ascq; |
Kevin O'Connor | 180a959 | 2008-03-04 22:50:53 -0500 | [diff] [blame] | 362 | ret = atapi_get_sense(device, &asc, &ascq); |
Kevin O'Connor | a0c6879 | 2009-02-12 20:47:59 -0500 | [diff] [blame^] | 363 | if (ret) |
| 364 | // Error - retry. |
Kevin O'Connor | 180a959 | 2008-03-04 22:50:53 -0500 | [diff] [blame] | 365 | continue; |
| 366 | |
Kevin O'Connor | a0c6879 | 2009-02-12 20:47:59 -0500 | [diff] [blame^] | 367 | // Sense succeeded. |
Kevin O'Connor | 180a959 | 2008-03-04 22:50:53 -0500 | [diff] [blame] | 368 | if (asc == 0x3a) { /* MEDIUM NOT PRESENT */ |
Kevin O'Connor | a05223c | 2008-06-28 12:15:57 -0400 | [diff] [blame] | 369 | dprintf(1, "Device reports MEDIUM NOT PRESENT\n"); |
Kevin O'Connor | 180a959 | 2008-03-04 22:50:53 -0500 | [diff] [blame] | 370 | return -1; |
| 371 | } |
| 372 | |
| 373 | if (asc == 0x04 && ascq == 0x01 && !in_progress) { |
| 374 | /* IN PROGRESS OF BECOMING READY */ |
| 375 | printf("Waiting for device to detect medium... "); |
| 376 | /* Allow 30 seconds more */ |
Kevin O'Connor | a0c6879 | 2009-02-12 20:47:59 -0500 | [diff] [blame^] | 377 | end = calc_future_tsc(30000); |
Kevin O'Connor | 180a959 | 2008-03-04 22:50:53 -0500 | [diff] [blame] | 378 | in_progress = 1; |
| 379 | } |
| 380 | } |
| 381 | |
Kevin O'Connor | a0c6879 | 2009-02-12 20:47:59 -0500 | [diff] [blame^] | 382 | if (blksize != GET_GLOBAL(ATA.devices[device].blksize)) { |
| 383 | printf("Unsupported sector size %u\n", blksize); |
Kevin O'Connor | 180a959 | 2008-03-04 22:50:53 -0500 | [diff] [blame] | 384 | return -1; |
| 385 | } |
Kevin O'Connor | 180a959 | 2008-03-04 22:50:53 -0500 | [diff] [blame] | 386 | |
Kevin O'Connor | a05223c | 2008-06-28 12:15:57 -0400 | [diff] [blame] | 387 | dprintf(6, "sectors=%u\n", sectors); |
Kevin O'Connor | 4a16ef6 | 2008-12-31 00:09:28 -0500 | [diff] [blame] | 388 | printf("%dMB medium detected\n", sectors>>(20-11)); |
Kevin O'Connor | 180a959 | 2008-03-04 22:50:53 -0500 | [diff] [blame] | 389 | return 0; |
| 390 | } |
| 391 | |
Kevin O'Connor | a05223c | 2008-06-28 12:15:57 -0400 | [diff] [blame] | 392 | static int |
Kevin O'Connor | 180a959 | 2008-03-04 22:50:53 -0500 | [diff] [blame] | 393 | atapi_is_cdrom(u8 device) |
| 394 | { |
| 395 | if (device >= CONFIG_MAX_ATA_DEVICES) |
| 396 | return 0; |
| 397 | |
Kevin O'Connor | 609da23 | 2008-12-28 23:18:57 -0500 | [diff] [blame] | 398 | if (GET_GLOBAL(ATA.devices[device].type) != ATA_TYPE_ATAPI) |
Kevin O'Connor | 180a959 | 2008-03-04 22:50:53 -0500 | [diff] [blame] | 399 | return 0; |
| 400 | |
Kevin O'Connor | 609da23 | 2008-12-28 23:18:57 -0500 | [diff] [blame] | 401 | if (GET_GLOBAL(ATA.devices[device].device) != ATA_DEVICE_CDROM) |
Kevin O'Connor | 180a959 | 2008-03-04 22:50:53 -0500 | [diff] [blame] | 402 | return 0; |
| 403 | |
| 404 | return 1; |
| 405 | } |
| 406 | |
| 407 | // Compare a string on the stack to one in the code segment. |
| 408 | static int |
| 409 | streq_cs(u8 *s1, char *cs_s2) |
| 410 | { |
| 411 | u8 *s2 = (u8*)cs_s2; |
| 412 | for (;;) { |
Kevin O'Connor | 15157a3 | 2008-12-13 11:10:37 -0500 | [diff] [blame] | 413 | if (*s1 != GET_GLOBAL(*s2)) |
Kevin O'Connor | 180a959 | 2008-03-04 22:50:53 -0500 | [diff] [blame] | 414 | return 0; |
| 415 | if (! *s1) |
| 416 | return 1; |
| 417 | s1++; |
| 418 | s2++; |
| 419 | } |
| 420 | } |
| 421 | |
Kevin O'Connor | a05223c | 2008-06-28 12:15:57 -0400 | [diff] [blame] | 422 | int |
Kevin O'Connor | 180a959 | 2008-03-04 22:50:53 -0500 | [diff] [blame] | 423 | cdrom_boot() |
| 424 | { |
| 425 | // Find out the first cdrom |
| 426 | u8 device; |
| 427 | for (device=0; device<CONFIG_MAX_ATA_DEVICES; device++) |
| 428 | if (atapi_is_cdrom(device)) |
| 429 | break; |
Kevin O'Connor | 37ef9ae | 2008-11-09 17:35:05 -0500 | [diff] [blame] | 430 | if (device >= CONFIG_MAX_ATA_DEVICES) |
| 431 | // cdrom not found |
| 432 | return 2; |
Kevin O'Connor | 180a959 | 2008-03-04 22:50:53 -0500 | [diff] [blame] | 433 | |
Kevin O'Connor | a05223c | 2008-06-28 12:15:57 -0400 | [diff] [blame] | 434 | int ret = atapi_is_ready(device); |
Kevin O'Connor | 180a959 | 2008-03-04 22:50:53 -0500 | [diff] [blame] | 435 | if (ret) |
Kevin O'Connor | 65e6342 | 2008-07-19 14:12:32 -0400 | [diff] [blame] | 436 | dprintf(1, "atapi_is_ready returned %d\n", ret); |
Kevin O'Connor | 180a959 | 2008-03-04 22:50:53 -0500 | [diff] [blame] | 437 | |
| 438 | // Read the Boot Record Volume Descriptor |
| 439 | u8 buffer[2048]; |
Kevin O'Connor | 4524bf7 | 2008-12-31 00:31:03 -0500 | [diff] [blame] | 440 | struct disk_op_s dop; |
| 441 | dop.driveid = device; |
| 442 | dop.lba = 0x11; |
| 443 | dop.count = 1; |
Kevin O'Connor | 35ae726 | 2009-01-19 15:44:44 -0500 | [diff] [blame] | 444 | dop.buf_fl = MAKE_FLATPTR(GET_SEG(SS), buffer); |
Kevin O'Connor | 4524bf7 | 2008-12-31 00:31:03 -0500 | [diff] [blame] | 445 | ret = cdrom_read(&dop); |
Kevin O'Connor | 180a959 | 2008-03-04 22:50:53 -0500 | [diff] [blame] | 446 | if (ret) |
| 447 | return 3; |
| 448 | |
| 449 | // Validity checks |
| 450 | if (buffer[0]) |
| 451 | return 4; |
| 452 | if (!streq_cs(&buffer[1], "CD001\001EL TORITO SPECIFICATION")) |
| 453 | return 5; |
| 454 | |
| 455 | // ok, now we calculate the Boot catalog address |
| 456 | u32 lba = *(u32*)&buffer[0x47]; |
| 457 | |
| 458 | // And we read the Boot Catalog |
Kevin O'Connor | 4524bf7 | 2008-12-31 00:31:03 -0500 | [diff] [blame] | 459 | dop.lba = lba; |
| 460 | ret = cdrom_read(&dop); |
Kevin O'Connor | 180a959 | 2008-03-04 22:50:53 -0500 | [diff] [blame] | 461 | if (ret) |
| 462 | return 7; |
| 463 | |
| 464 | // Validation entry |
| 465 | if (buffer[0x00] != 0x01) |
| 466 | return 8; // Header |
| 467 | if (buffer[0x01] != 0x00) |
| 468 | return 9; // Platform |
| 469 | if (buffer[0x1E] != 0x55) |
| 470 | return 10; // key 1 |
| 471 | if (buffer[0x1F] != 0xAA) |
| 472 | return 10; // key 2 |
| 473 | |
| 474 | // Initial/Default Entry |
| 475 | if (buffer[0x20] != 0x88) |
| 476 | return 11; // Bootable |
| 477 | |
Kevin O'Connor | 4a16ef6 | 2008-12-31 00:09:28 -0500 | [diff] [blame] | 478 | u16 ebda_seg = get_ebda_seg(); |
Kevin O'Connor | dfa1650 | 2008-03-22 20:13:08 -0400 | [diff] [blame] | 479 | u8 media = buffer[0x21]; |
Kevin O'Connor | 4a16ef6 | 2008-12-31 00:09:28 -0500 | [diff] [blame] | 480 | SET_EBDA2(ebda_seg, cdemu.media, media); |
Kevin O'Connor | 180a959 | 2008-03-04 22:50:53 -0500 | [diff] [blame] | 481 | |
Kevin O'Connor | 4a16ef6 | 2008-12-31 00:09:28 -0500 | [diff] [blame] | 482 | SET_EBDA2(ebda_seg, cdemu.controller_index, device/2); |
| 483 | SET_EBDA2(ebda_seg, cdemu.device_spec, device%2); |
Kevin O'Connor | 180a959 | 2008-03-04 22:50:53 -0500 | [diff] [blame] | 484 | |
| 485 | u16 boot_segment = *(u16*)&buffer[0x22]; |
| 486 | if (!boot_segment) |
| 487 | boot_segment = 0x07C0; |
Kevin O'Connor | 4a16ef6 | 2008-12-31 00:09:28 -0500 | [diff] [blame] | 488 | SET_EBDA2(ebda_seg, cdemu.load_segment, boot_segment); |
| 489 | SET_EBDA2(ebda_seg, cdemu.buffer_segment, 0x0000); |
Kevin O'Connor | 180a959 | 2008-03-04 22:50:53 -0500 | [diff] [blame] | 490 | |
| 491 | u16 nbsectors = *(u16*)&buffer[0x26]; |
Kevin O'Connor | 4a16ef6 | 2008-12-31 00:09:28 -0500 | [diff] [blame] | 492 | SET_EBDA2(ebda_seg, cdemu.sector_count, nbsectors); |
Kevin O'Connor | 180a959 | 2008-03-04 22:50:53 -0500 | [diff] [blame] | 493 | |
| 494 | lba = *(u32*)&buffer[0x28]; |
Kevin O'Connor | 4a16ef6 | 2008-12-31 00:09:28 -0500 | [diff] [blame] | 495 | SET_EBDA2(ebda_seg, cdemu.ilba, lba); |
Kevin O'Connor | 180a959 | 2008-03-04 22:50:53 -0500 | [diff] [blame] | 496 | |
| 497 | // And we read the image in memory |
Kevin O'Connor | 4524bf7 | 2008-12-31 00:31:03 -0500 | [diff] [blame] | 498 | dop.lba = lba * 4; |
| 499 | dop.count = nbsectors; |
Kevin O'Connor | 35ae726 | 2009-01-19 15:44:44 -0500 | [diff] [blame] | 500 | dop.buf_fl = MAKE_FLATPTR(boot_segment, 0); |
Kevin O'Connor | 4524bf7 | 2008-12-31 00:31:03 -0500 | [diff] [blame] | 501 | ret = cdrom_read_512(&dop); |
Kevin O'Connor | 180a959 | 2008-03-04 22:50:53 -0500 | [diff] [blame] | 502 | if (ret) |
| 503 | return 12; |
| 504 | |
Kevin O'Connor | dfa1650 | 2008-03-22 20:13:08 -0400 | [diff] [blame] | 505 | if (media == 0) { |
| 506 | // No emulation requested - return success. |
| 507 | |
| 508 | // FIXME ElTorito Hardcoded. cdrom is hardcoded as device 0xE0. |
| 509 | // Win2000 cd boot needs to know it booted from cd |
Kevin O'Connor | 4a16ef6 | 2008-12-31 00:09:28 -0500 | [diff] [blame] | 510 | SET_EBDA2(ebda_seg, cdemu.emulated_drive, 0xE0); |
Kevin O'Connor | dfa1650 | 2008-03-22 20:13:08 -0400 | [diff] [blame] | 511 | |
| 512 | return 0; |
| 513 | } |
| 514 | |
| 515 | // Emulation of a floppy/harddisk requested |
| 516 | if (! CONFIG_CDROM_EMU) |
| 517 | return 13; |
| 518 | |
| 519 | // Set emulated drive id and increase bios installed hardware |
| 520 | // number of devices |
| 521 | if (media < 4) { |
| 522 | // Floppy emulation |
Kevin O'Connor | 4a16ef6 | 2008-12-31 00:09:28 -0500 | [diff] [blame] | 523 | SET_EBDA2(ebda_seg, cdemu.emulated_drive, 0x00); |
Kevin O'Connor | dfa1650 | 2008-03-22 20:13:08 -0400 | [diff] [blame] | 524 | SETBITS_BDA(equipment_list_flags, 0x41); |
Kevin O'Connor | 95827c4 | 2009-02-07 00:04:57 -0500 | [diff] [blame] | 525 | |
| 526 | switch (media) { |
| 527 | case 0x01: // 1.2M floppy |
| 528 | SET_EBDA2(ebda_seg, cdemu.spt, 15); |
| 529 | SET_EBDA2(ebda_seg, cdemu.cylinders, 80); |
| 530 | SET_EBDA2(ebda_seg, cdemu.heads, 2); |
| 531 | break; |
| 532 | case 0x02: // 1.44M floppy |
| 533 | SET_EBDA2(ebda_seg, cdemu.spt, 18); |
| 534 | SET_EBDA2(ebda_seg, cdemu.cylinders, 80); |
| 535 | SET_EBDA2(ebda_seg, cdemu.heads, 2); |
| 536 | break; |
| 537 | case 0x03: // 2.88M floppy |
| 538 | SET_EBDA2(ebda_seg, cdemu.spt, 36); |
| 539 | SET_EBDA2(ebda_seg, cdemu.cylinders, 80); |
| 540 | SET_EBDA2(ebda_seg, cdemu.heads, 2); |
| 541 | break; |
| 542 | } |
Kevin O'Connor | dfa1650 | 2008-03-22 20:13:08 -0400 | [diff] [blame] | 543 | } else { |
| 544 | // Harddrive emulation |
Kevin O'Connor | 4a16ef6 | 2008-12-31 00:09:28 -0500 | [diff] [blame] | 545 | SET_EBDA2(ebda_seg, cdemu.emulated_drive, 0x80); |
| 546 | SET_BDA(hdcount, GET_BDA(hdcount) + 1); |
Kevin O'Connor | dfa1650 | 2008-03-22 20:13:08 -0400 | [diff] [blame] | 547 | |
Kevin O'Connor | 95827c4 | 2009-02-07 00:04:57 -0500 | [diff] [blame] | 548 | // Peak at partition table to get chs. |
| 549 | struct mbr_s *mbr = (void*)0; |
| 550 | u8 sptcyl = GET_FARVAR(boot_segment, mbr->partitions[0].last.sptcyl); |
| 551 | u8 cyllow = GET_FARVAR(boot_segment, mbr->partitions[0].last.cyllow); |
| 552 | u8 heads = GET_FARVAR(boot_segment, mbr->partitions[0].last.heads); |
| 553 | |
| 554 | SET_EBDA2(ebda_seg, cdemu.spt, sptcyl & 0x3f); |
| 555 | SET_EBDA2(ebda_seg, cdemu.cylinders, ((sptcyl<<2)&0x300) + cyllow + 1); |
| 556 | SET_EBDA2(ebda_seg, cdemu.heads, heads + 1); |
Kevin O'Connor | 180a959 | 2008-03-04 22:50:53 -0500 | [diff] [blame] | 557 | } |
| 558 | |
Kevin O'Connor | dfa1650 | 2008-03-22 20:13:08 -0400 | [diff] [blame] | 559 | // everything is ok, so from now on, the emulation is active |
Kevin O'Connor | 4a16ef6 | 2008-12-31 00:09:28 -0500 | [diff] [blame] | 560 | SET_EBDA2(ebda_seg, cdemu.active, 0x01); |
Kevin O'Connor | a05223c | 2008-06-28 12:15:57 -0400 | [diff] [blame] | 561 | dprintf(6, "cdemu media=%d\n", media); |
Kevin O'Connor | 180a959 | 2008-03-04 22:50:53 -0500 | [diff] [blame] | 562 | |
| 563 | return 0; |
| 564 | } |