3 #include <conio.h> /* this is where Open Watcom hides the outp() etc. functions */
12 #include <hw/vga/vga.h>
13 #include <hw/pci/pci.h>
14 #include <hw/dos/dos.h>
15 #include <hw/8254/8254.h> /* 8254 timer */
16 #include <hw/8259/8259.h> /* 8259 PIC interrupts */
17 #include <hw/vga/vgagui.h>
18 #include <hw/vga/vgatty.h>
19 #include <hw/ide/idelib.h>
41 static unsigned char drive_write_warning = 1;
43 static const char *drive_write_test_menustrings[] = {
44 "Show IDE register taskfile", /* 0 */
45 "*mode*", /* 1 */ /* rewritten (CHS, LBA, CHS MULTI, etc) */
46 drive_readwrite_test_geo,
47 drive_readwrite_test_chs,
48 drive_readwrite_test_numsec,
49 drive_readwrite_test_mult,
51 "Write sectors continuously"
54 static void do_hdd_drive_write_test(struct ide_controller *ide,unsigned char which,unsigned char continuous,struct drive_rw_test_info *nfo) {
55 unsigned char user_esc = 0;
56 struct vga_msg_box vgabox;
57 struct ide_taskfile *tsk;
58 unsigned long tlen_sect;
59 unsigned int cleared=0;
64 if (nfo->read_sectors == 0) return;
66 /* multiple mode: make sure the multiple sector count doesn't exceed our buffer size.
67 * if it does, try to set multiple count to lesser value. */
68 if (nfo->mode == DRIVE_RW_MODE_CHSMULTIPLE || nfo->mode == DRIVE_RW_MODE_LBAMULTIPLE || nfo->mode == DRIVE_RW_MODE_LBA48_MULTIPLE) {
69 if (nfo->multiple_sectors > ((unsigned long)sizeof(cdrom_sector) / 512UL) && nfo->multiple_sectors > 1) {
70 /* even though most IDE devices really only support power-of-2 sizes, we do a scan downward anyway */
71 c = ((unsigned long)sizeof(cdrom_sector) / 512UL);
74 do_ide_set_multiple_mode(ide,which,c);
78 do_ide_identify((unsigned char*)info,sizeof(info),ide,which,0xEC/*ATA IDENTIFY DEVICE*/);
79 drive_rw_test_nfo.multiple_sectors = info[59]&0xFF;
82 if (nfo->multiple_sectors > ((unsigned long)sizeof(cdrom_sector) / 512UL))
89 if (nfo->multiple_sectors > ((unsigned long)sizeof(cdrom_sector) / 512UL))
91 if (nfo->multiple_sectors == 0)
95 idelib_controller_reset_irq_counter(ide); /* IRQ will fire after command completion */
96 tsk = idelib_controller_get_taskfile(ide,-1/*selected drive*/);
98 again: /* jump point: send execution back here for another sector */
99 if (do_ide_controller_user_wait_busy_controller(ide) != 0 || do_ide_controller_user_wait_drive_ready(ide) < 0)
101 idelib_controller_ack_irq(ide); /* <- make sure to ack IRQ */
103 tlen_sect = nfo->read_sectors;
104 if (tlen_sect > ((unsigned long)sizeof(cdrom_sector) / 512UL))
105 tlen_sect = ((unsigned long)sizeof(cdrom_sector) / 512UL);
107 /* C/H/S continuous: limit reads to within track, don't cross. we can't assume the drive will do that. */
108 if (continuous && (nfo->mode == DRIVE_RW_MODE_CHS || nfo->mode == DRIVE_RW_MODE_CHSMULTIPLE)) {
109 if ((nfo->sector + tlen_sect) > nfo->num_sector)
110 tlen_sect = (nfo->num_sector + 1 - nfo->sector);
113 tlen = tlen_sect * 512UL;
115 if (nfo->mode == DRIVE_RW_MODE_LBA48 || nfo->mode == DRIVE_RW_MODE_LBA48_MULTIPLE) {
116 tsk->sector_count = tlen_sect;
117 tsk->lba0_3 = nfo->lba & 0xFF;
118 tsk->lba1_4 = (nfo->lba >> 8) & 0xFF;
119 tsk->lba2_5 = (nfo->lba >> 16) & 0xFF;
120 tsk->lba0_3 |= ((nfo->lba >> 24) & 0xFF) << 8;
121 tsk->lba1_4 |= ((nfo->lba >> 32) & 0xFF) << 8;
122 tsk->lba2_5 |= ((nfo->lba >> 40) & 0xFF) << 8;
123 tsk->head_select = (which << 4) | 0x40;
125 if (nfo->mode == DRIVE_RW_MODE_LBA48)
126 tsk->command = 0x34; /* WRITE SECTORS EXT */
127 else if (nfo->mode == DRIVE_RW_MODE_LBA48_MULTIPLE)
128 tsk->command = 0x39; /* WRITE MULTIPLE EXT */
130 if (idelib_controller_apply_taskfile(ide,0xFC/*base_io+2-7*/,IDELIB_TASKFILE_LBA48_UPDATE|IDELIB_TASKFILE_LBA48/*set LBA48*/) < 0)
134 if (tsk->sector_count > 256) return;
135 tsk->sector_count = (tlen_sect&0xFF);
136 if (nfo->mode == DRIVE_RW_MODE_CHS || nfo->mode == DRIVE_RW_MODE_CHSMULTIPLE) {
137 tsk->chs_sector = nfo->sector;
138 tsk->chs_cylinder_low = nfo->cylinder & 0xFF;
139 tsk->chs_cylinder_high = (nfo->cylinder >> 8) & 0xFF;
140 tsk->head_select = (nfo->head & 0xF) | (which << 4) | 0xA0;
142 else if (nfo->mode == DRIVE_RW_MODE_LBA || nfo->mode == DRIVE_RW_MODE_LBAMULTIPLE) {
143 tsk->lba0_3 = nfo->lba & 0xFF;
144 tsk->lba1_4 = (nfo->lba >> 8) & 0xFF;
145 tsk->lba2_5 = (nfo->lba >> 16) & 0xFF;
146 tsk->head_select = ((nfo->lba >> 24) & 0xF) | (which << 4) | 0xE0;
149 if (nfo->mode == DRIVE_RW_MODE_CHS || nfo->mode == DRIVE_RW_MODE_LBA)
150 tsk->command = 0x30; /* WRITE SECTORS */
151 else if (nfo->mode == DRIVE_RW_MODE_CHSMULTIPLE || nfo->mode == DRIVE_RW_MODE_LBAMULTIPLE)
152 tsk->command = 0xC5; /* WRITE MULTIPLE */
154 if (idelib_controller_apply_taskfile(ide,0xFC/*base_io+2-7*/,IDELIB_TASKFILE_LBA48_UPDATE/*clear LBA48*/) < 0)
158 /* wait for drive ready. drive will NOT fire an IRQ until the write is done. */
159 if (do_ide_controller_user_wait_busy_controller(ide) != 0 || do_ide_controller_user_wait_drive_ready(ide) < 0)
162 if (!idelib_controller_is_error(ide)) { /* OK. success. now read the data */
163 unsigned int ret_len = 0,drq_len;
166 for (i=0;i < tlen;i++) cdrom_sector[i] = i + (i >> 8);
168 while (ret_len < tlen) {
169 if (idelib_controller_is_error(ide)) {
170 vga_msg_box_create(&vgabox,"Error",0,0);
171 wait_for_enter_or_escape();
172 vga_msg_box_destroy(&vgabox);
176 if (do_ide_controller_user_wait_drive_drq(ide) < 0) {
181 if (nfo->mode == DRIVE_RW_MODE_CHSMULTIPLE || nfo->mode == DRIVE_RW_MODE_LBAMULTIPLE ||
182 nfo->mode == DRIVE_RW_MODE_LBA48_MULTIPLE) {
183 drq_len = nfo->multiple_sectors * 512;
184 if ((ret_len+drq_len) > tlen) drq_len = tlen - ret_len;
189 /* OK. write it out and acknowledge */
190 idelib_write_pio_general(cdrom_sector+ret_len,drq_len,ide,IDELIB_PIO_WIDTH_DEFAULT);
193 /* you're supposed to wait for IRQ after writing the sector */
194 if (ide->flags.io_irq_enable) {
195 if (do_ide_controller_user_wait_irq(ide,1) < 0) {
199 idelib_controller_reset_irq_counter(ide); /* IRQ will fire after command completion */
200 idelib_controller_ack_irq(ide); /* <- or else it won't fire again */
203 if (do_ide_controller_user_wait_busy_controller(ide) != 0 || do_ide_controller_user_wait_drive_ready(ide) < 0) {
209 /* ---- draw contents on the screen ---- */
210 vga_write_color(0x0E);
217 vga_write("Sector writing:\n");
218 if (nfo->mode == DRIVE_RW_MODE_CHS || nfo->mode == DRIVE_RW_MODE_CHSMULTIPLE) {
219 sprintf(tmp,"CHS %u/%u/%u ",nfo->cylinder,nfo->head,nfo->sector); vga_write(tmp);
222 sprintf(tmp,"%llu-%llu",nfo->lba,nfo->lba+(unsigned long long)tlen_sect-1ULL); vga_write(tmp);
225 if (!idelib_controller_is_error(ide)) { /* OK. success. now read the data */
226 vga_write_color(0x0A);
227 vga_write(" PASSED\n");
230 vga_write_color(0x0C);
231 vga_write(" FAILED\n");
234 if (continuous && !user_esc) {
237 if (c == 0) c = getch() << 8;
244 if ((c=wait_for_enter_or_escape()) == 27)
245 return; /* allow user to exit early by hitting ESC */
248 if (c != 27 && !user_esc) {
249 /* if the user hit ENTER, then read another sector and display that too */
250 if (nfo->mode == DRIVE_RW_MODE_CHS || nfo->mode == DRIVE_RW_MODE_CHSMULTIPLE) {
251 nfo->sector += (unsigned long long)tlen_sect;
252 while (nfo->sector > nfo->num_sector) {
253 nfo->sector -= nfo->num_sector;
256 while (nfo->head >= nfo->num_head) {
257 nfo->head -= nfo->num_head;
261 if (nfo->cylinder >= 16384) {
262 nfo->cylinder = 16383;
263 nfo->sector = nfo->num_sector;
264 nfo->head = nfo->num_head - 1;
267 nfo->lba = (unsigned long long)nfo->cylinder * (unsigned long long)nfo->num_sector * (unsigned long long)nfo->num_head;
268 nfo->lba += (unsigned long long)nfo->head * (unsigned long long)nfo->num_sector;
269 nfo->lba += (unsigned long long)nfo->sector - 1ULL;
272 nfo->lba += (unsigned long long)tlen_sect;
274 nfo->sector = (int)(nfo->lba % (unsigned long long)nfo->num_sector) + 1;
275 nfo->head = (int)((nfo->lba / (unsigned long long)nfo->num_sector) % (unsigned long long)nfo->num_head);
276 if (nfo->lba >= (16384ULL * (unsigned long long)nfo->num_sector * (unsigned long long)nfo->num_head)) {
277 nfo->cylinder = 16383;
278 nfo->sector = nfo->num_sector;
279 nfo->head = nfo->num_head - 1;
282 nfo->cylinder = (int)((nfo->lba / (unsigned long long)nfo->num_sector) / (unsigned long long)nfo->num_head);
290 common_ide_success_or_error_vga_msg_box(ide,&vgabox);
291 wait_for_enter_or_escape();
292 vga_msg_box_destroy(&vgabox);
296 void do_drive_write_test(struct ide_controller *ide,unsigned char which) {
297 struct menuboxbounds mbox;
305 if (drive_write_warning) {
306 struct vga_msg_box box;
308 vga_msg_box_create(&box,
309 "WARNING: This test is destructive. It overwrites the data on the disk.\n"
310 " If you value the data on your hard drive DO NOT RUN THIS TEST.\n"
312 "Hit ESC to cancel, ENTER to continue"
314 c = wait_for_enter_or_escape();
315 vga_msg_box_destroy(&box);
318 vga_msg_box_create(&box,
319 "WARNING: Are you sure? The contents of the hard disk will be overwritten\n"
320 " in the process of carrying out this test!\n"
322 "Hit ESC to cancel, ENTER to continue"
324 c = wait_for_enter_or_escape();
325 vga_msg_box_destroy(&box);
328 vga_msg_box_create(&box,
329 "FINAL WARNING: This test will overwrite the sectors on the disk with\n"
330 " test data. The test is not undoable. ARE YOU SURE?\n"
332 "If you confirm, this warning will not show again until the test program\n"
335 "Hit ESC to cancel, ENTER to continue"
337 c = wait_for_enter_or_escape();
338 vga_msg_box_destroy(&box);
342 drive_write_warning = 0;
345 /* UI element vars */
346 menuboxbounds_set_def_list(&mbox,/*ofsx=*/4,/*ofsy=*/7,/*cols=*/1);
347 menuboxbounds_set_item_strings_arraylen(&mbox,drive_write_test_menustrings);
355 for (y=0;y < vga_height;y++) {
356 for (x=0;x < vga_width;x++) {
357 *vga++ = 0x1E00 + 177;
363 vga_write_color(0x1F);
364 vga_write(" IDE r/w tests ");
365 sprintf(tmp,"@%X",ide->base_io);
367 if (ide->alt_io != 0) {
368 sprintf(tmp," alt %X",ide->alt_io);
372 sprintf(tmp," IRQ %d",ide->irq);
375 vga_write(which ? " Slave" : " Master");
377 sprintf(tmp," lba=%u lba48=%u multiple=%u",
378 drive_rw_test_nfo.can_do_lba?1:0,
379 drive_rw_test_nfo.can_do_lba48?1:0,
380 drive_rw_test_nfo.can_do_multiple?1:0);
383 while (vga_pos_x < vga_width && vga_pos_x != 0) vga_writec(' ');
385 vga_write_color(0xC);
386 vga_write("WARNING: This code talks directly to your hard disk controller.");
387 while (vga_pos_x < vga_width && vga_pos_x != 0) vga_writec(' ');
388 vga_write_color(0xC);
389 vga_write(" If you value the data on your hard drive do not run this program.");
390 while (vga_pos_x < vga_width && vga_pos_x != 0) vga_writec(' ');
396 drive_write_test_menustrings[1] = drive_readwrite_test_modes[drive_rw_test_nfo.mode];
398 sprintf(drive_readwrite_test_geo,"Geometry: C/H/S %u/%u/%u LBA %llu",
399 drive_rw_test_nfo.num_cylinder,
400 drive_rw_test_nfo.num_head,
401 drive_rw_test_nfo.num_sector,
402 drive_rw_test_nfo.max_lba);
404 sprintf(drive_readwrite_test_chs,"Position: C/H/S %u/%u/%u LBA %llu",
405 drive_rw_test_nfo.cylinder,
406 drive_rw_test_nfo.head,
407 drive_rw_test_nfo.sector,
408 drive_rw_test_nfo.lba);
410 sprintf(drive_readwrite_test_numsec,"Number of sectors per read: %u",
411 drive_rw_test_nfo.read_sectors);
413 sprintf(drive_readwrite_test_mult,"Multiple mode: %u sectors (max=%u)",
414 drive_rw_test_nfo.multiple_sectors,
415 drive_rw_test_nfo.max_multiple_sectors);
417 vga_moveto(mbox.ofsx,mbox.ofsy - 2);
418 vga_write_color((select == -1) ? 0x70 : 0x0F);
419 vga_write("Back to IDE controller main menu");
420 while (vga_pos_x < (mbox.width+mbox.ofsx) && vga_pos_x != 0) vga_writec(' ');
422 menuboxbound_redraw(&mbox,select);
426 if (c == 0) c = getch() << 8;
436 case 0: /* show IDE register taskfile */
437 do_common_show_ide_taskfile(ide,which);
438 redraw = backredraw = 1;
441 do_drive_readwrite_test_choose_mode(ide,which,&drive_rw_test_nfo);
442 redraw = backredraw = 1;
444 case 2: /* Edit geometry/max LBA */
445 do_drive_readwrite_edit_chslba(ide,which,&drive_rw_test_nfo,/*editgeo*/1);
448 case 3: /* Edit position */
449 do_drive_readwrite_edit_chslba(ide,which,&drive_rw_test_nfo,/*editgeo*/0);
452 case 4: /* Number of sectors */
453 c = prompt_sector_count();
454 if (c >= 1 && c <= 256) {
455 drive_rw_test_nfo.read_sectors = c;
459 case 5: /* Multiple mode sector count */
460 c = prompt_sector_count();
461 if (c >= 0 && c <= 255) {
462 do_ide_set_multiple_mode(ide,which,c);
466 do_ide_identify((unsigned char*)info,sizeof(info),ide,which,0xEC/*ATA IDENTIFY DEVICE*/);
467 drive_rw_test_nfo.multiple_sectors = info[59]&0xFF;
472 case 6: /*Read sectors*/
473 case 7: /*Read sectors continuously*/
474 do_hdd_drive_write_test(ide,which,/*continuous=*/(select==7),&drive_rw_test_nfo);
475 redraw = backredraw = 1;
479 else if (c == 0x4800) {
481 select = mbox.item_max;
485 else if (c == 0x4B00) { /* left */
489 if (drive_rw_test_nfo.mode == 0)
490 drive_rw_test_nfo.mode = DRIVE_RW_MODE_MAX-1;
492 drive_rw_test_nfo.mode--;
493 } while (!drive_rw_test_mode_supported(&drive_rw_test_nfo));
495 case 4: /* Number of sectors */
496 if (drive_rw_test_nfo.read_sectors > 1)
497 drive_rw_test_nfo.read_sectors--;
503 else if (c == 0x4D00) { /* right */
507 if (++drive_rw_test_nfo.mode >= DRIVE_RW_MODE_MAX)
508 drive_rw_test_nfo.mode = 0;
509 } while (!drive_rw_test_mode_supported(&drive_rw_test_nfo));
511 case 4: /* Number of sectors */
512 if (drive_rw_test_nfo.read_sectors < 256)
513 drive_rw_test_nfo.read_sectors++;
519 else if (c == 0x5000) {
520 if (++select > mbox.item_max)