Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


imported SEABIOS source tree
[palacios.git] / bios / seabios / src / floppy.c
1 // 16bit code to access floppy drives.
2 //
3 // Copyright (C) 2008,2009  Kevin O'Connor <kevin@koconnor.net>
4 // Copyright (C) 2002  MandrakeSoft S.A.
5 //
6 // This file may be distributed under the terms of the GNU LGPLv3 license.
7
8 #include "types.h" // u8
9 #include "disk.h" // DISK_RET_SUCCESS
10 #include "config.h" // CONFIG_FLOPPY
11 #include "biosvar.h" // SET_BDA
12 #include "util.h" // wait_irq
13 #include "cmos.h" // inb_cmos
14 #include "pic.h" // eoi_pic1
15 #include "bregs.h" // struct bregs
16 #include "boot.h" // boot_add_floppy
17 #include "pci.h" // pci_to_bdf
18 #include "pci_ids.h" // PCI_CLASS_BRIDGE_ISA
19
20 #define FLOPPY_SIZE_CODE 0x02 // 512 byte sectors
21 #define FLOPPY_DATALEN 0xff   // Not used - because size code is 0x02
22 #define FLOPPY_MOTOR_TICKS 37 // ~2 seconds
23 #define FLOPPY_FILLBYTE 0xf6
24 #define FLOPPY_GAPLEN 0x1B
25 #define FLOPPY_FORMAT_GAPLEN 0x6c
26
27 // New diskette parameter table adding 3 parameters from IBM
28 // Since no provisions are made for multiple drive types, most
29 // values in this table are ignored.  I set parameters for 1.44M
30 // floppy here
31 struct floppy_ext_dbt_s diskette_param_table2 VAR16VISIBLE = {
32     .dbt = {
33         .specify1       = 0xAF, // step rate 12ms, head unload 240ms
34         .specify2       = 0x02, // head load time 4ms, DMA used
35         .shutoff_ticks  = FLOPPY_MOTOR_TICKS, // ~2 seconds
36         .bps_code       = FLOPPY_SIZE_CODE,
37         .sectors        = 18,
38         .interblock_len = FLOPPY_GAPLEN,
39         .data_len       = FLOPPY_DATALEN,
40         .gap_len        = FLOPPY_FORMAT_GAPLEN,
41         .fill_byte      = FLOPPY_FILLBYTE,
42         .settle_time    = 0x0F, // 15ms
43         .startup_time   = 0x08, // 1 second
44     },
45     .max_track      = 79,   // maximum track
46     .data_rate      = 0,    // data transfer rate
47     .drive_type     = 4,    // drive type in cmos
48 };
49
50 // Since no provisions are made for multiple drive types, most
51 // values in this table are ignored.  I set parameters for 1.44M
52 // floppy here
53 struct floppy_dbt_s diskette_param_table VAR16FIXED(0xefc7) = {
54     .specify1       = 0xAF,
55     .specify2       = 0x02,
56     .shutoff_ticks  = FLOPPY_MOTOR_TICKS,
57     .bps_code       = FLOPPY_SIZE_CODE,
58     .sectors        = 18,
59     .interblock_len = FLOPPY_GAPLEN,
60     .data_len       = FLOPPY_DATALEN,
61     .gap_len        = FLOPPY_FORMAT_GAPLEN,
62     .fill_byte      = FLOPPY_FILLBYTE,
63     .settle_time    = 0x0F,
64     .startup_time   = 0x08,
65 };
66
67 struct floppyinfo_s {
68     struct chs_s chs;
69     u8 config_data;
70     u8 media_state;
71 };
72
73 struct floppyinfo_s FloppyInfo[] VAR16VISIBLE = {
74     // Unknown
75     { {0, 0, 0}, 0x00, 0x00},
76     // 1 - 360KB, 5.25" - 2 heads, 40 tracks, 9 sectors
77     { {2, 40, 9}, 0x00, 0x25},
78     // 2 - 1.2MB, 5.25" - 2 heads, 80 tracks, 15 sectors
79     { {2, 80, 15}, 0x00, 0x25},
80     // 3 - 720KB, 3.5"  - 2 heads, 80 tracks, 9 sectors
81     { {2, 80, 9}, 0x00, 0x17},
82     // 4 - 1.44MB, 3.5" - 2 heads, 80 tracks, 18 sectors
83     { {2, 80, 18}, 0x00, 0x17},
84     // 5 - 2.88MB, 3.5" - 2 heads, 80 tracks, 36 sectors
85     { {2, 80, 36}, 0xCC, 0xD7},
86     // 6 - 160k, 5.25"  - 1 heads, 40 tracks, 8 sectors
87     { {1, 40, 8}, 0x00, 0x27},
88     // 7 - 180k, 5.25"  - 1 heads, 40 tracks, 9 sectors
89     { {1, 40, 9}, 0x00, 0x27},
90     // 8 - 320k, 5.25"  - 2 heads, 40 tracks, 8 sectors
91     { {2, 40, 8}, 0x00, 0x27},
92 };
93
94 struct drive_s *
95 init_floppy(int floppyid, int ftype)
96 {
97     if (ftype <= 0 || ftype >= ARRAY_SIZE(FloppyInfo)) {
98         dprintf(1, "Bad floppy type %d\n", ftype);
99         return NULL;
100     }
101
102     struct drive_s *drive_g = malloc_fseg(sizeof(*drive_g));
103     if (!drive_g) {
104         warn_noalloc();
105         return NULL;
106     }
107     memset(drive_g, 0, sizeof(*drive_g));
108     drive_g->cntl_id = floppyid;
109     drive_g->type = DTYPE_FLOPPY;
110     drive_g->blksize = DISK_SECTOR_SIZE;
111     drive_g->floppy_type = ftype;
112     drive_g->sectors = (u64)-1;
113
114     memcpy(&drive_g->lchs, &FloppyInfo[ftype].chs
115            , sizeof(FloppyInfo[ftype].chs));
116     return drive_g;
117 }
118
119 static void
120 addFloppy(int floppyid, int ftype)
121 {
122     struct drive_s *drive_g = init_floppy(floppyid, ftype);
123     if (!drive_g)
124         return;
125     char *desc = znprintf(MAXDESCSIZE, "Floppy [drive %c]", 'A' + floppyid);
126     struct pci_device *pci = pci_find_class(PCI_CLASS_BRIDGE_ISA); /* isa-to-pci bridge */
127     int prio = bootprio_find_fdc_device(pci, PORT_FD_BASE, floppyid);
128     boot_add_floppy(drive_g, desc, prio);
129 }
130
131 void
132 floppy_setup(void)
133 {
134     if (! CONFIG_FLOPPY)
135         return;
136     dprintf(3, "init floppy drives\n");
137
138     if (CONFIG_COREBOOT) {
139         // XXX - disable floppies on coreboot for now.
140     } else {
141         u8 type = inb_cmos(CMOS_FLOPPY_DRIVE_TYPE);
142         if (type & 0xf0)
143             addFloppy(0, type >> 4);
144         if (type & 0x0f)
145             addFloppy(1, type & 0x0f);
146     }
147
148     outb(0x02, PORT_DMA1_MASK_REG);
149
150     enable_hwirq(6, FUNC16(entry_0e));
151 }
152
153 // Find a floppy type that matches a given image size.
154 int
155 find_floppy_type(u32 size)
156 {
157     int i;
158     for (i=1; i<ARRAY_SIZE(FloppyInfo); i++) {
159         struct chs_s *c = &FloppyInfo[i].chs;
160         if (c->cylinders * c->heads * c->spt * DISK_SECTOR_SIZE == size)
161             return i;
162     }
163     return -1;
164 }
165
166
167 /****************************************************************
168  * Low-level floppy IO
169  ****************************************************************/
170
171 static void
172 floppy_reset_controller(void)
173 {
174     // Reset controller
175     u8 val8 = inb(PORT_FD_DOR);
176     outb(val8 & ~0x04, PORT_FD_DOR);
177     outb(val8 | 0x04, PORT_FD_DOR);
178
179     // Wait for controller to come out of reset
180     while ((inb(PORT_FD_STATUS) & 0xc0) != 0x80)
181         ;
182 }
183
184 static int
185 wait_floppy_irq(void)
186 {
187     ASSERT16();
188     u8 v;
189     for (;;) {
190         if (!GET_BDA(floppy_motor_counter))
191             return -1;
192         v = GET_BDA(floppy_recalibration_status);
193         if (v & FRS_TIMEOUT)
194             break;
195         // Could use wait_irq() here, but that causes issues on
196         // bochs, so use yield() instead.
197         yield();
198     }
199
200     v &= ~FRS_TIMEOUT;
201     SET_BDA(floppy_recalibration_status, v);
202     return 0;
203 }
204
205 static void
206 floppy_prepare_controller(u8 floppyid)
207 {
208     CLEARBITS_BDA(floppy_recalibration_status, FRS_TIMEOUT);
209
210     // turn on motor of selected drive, DMA & int enabled, normal operation
211     u8 prev_reset = inb(PORT_FD_DOR) & 0x04;
212     u8 dor = 0x10;
213     if (floppyid)
214         dor = 0x20;
215     dor |= 0x0c;
216     dor |= floppyid;
217     outb(dor, PORT_FD_DOR);
218
219     // reset the disk motor timeout value of INT 08
220     SET_BDA(floppy_motor_counter, FLOPPY_MOTOR_TICKS);
221
222     // wait for drive readiness
223     while ((inb(PORT_FD_STATUS) & 0xc0) != 0x80)
224         ;
225
226     if (!prev_reset)
227         wait_floppy_irq();
228 }
229
230 static int
231 floppy_pio(u8 *cmd, u8 cmdlen)
232 {
233     floppy_prepare_controller(cmd[1] & 1);
234
235     // send command to controller
236     u8 i;
237     for (i=0; i<cmdlen; i++)
238         outb(cmd[i], PORT_FD_DATA);
239
240     int ret = wait_floppy_irq();
241     if (ret) {
242         floppy_reset_controller();
243         return -1;
244     }
245
246     return 0;
247 }
248
249 static int
250 floppy_cmd(struct disk_op_s *op, u16 count, u8 *cmd, u8 cmdlen)
251 {
252     // es:bx = pointer to where to place information from diskette
253     u32 addr = (u32)op->buf_fl;
254
255     // check for 64K boundary overrun
256     u16 end = count - 1;
257     u32 last_addr = addr + end;
258     if ((addr >> 16) != (last_addr >> 16))
259         return DISK_RET_EBOUNDARY;
260
261     u8 mode_register = 0x4a; // single mode, increment, autoinit disable,
262     if (cmd[0] == 0xe6)
263         // read
264         mode_register = 0x46;
265
266     //DEBUGF("floppy dma c2\n");
267     outb(0x06, PORT_DMA1_MASK_REG);
268     outb(0x00, PORT_DMA1_CLEAR_FF_REG); // clear flip-flop
269     outb(addr, PORT_DMA_ADDR_2);
270     outb(addr>>8, PORT_DMA_ADDR_2);
271     outb(0x00, PORT_DMA1_CLEAR_FF_REG); // clear flip-flop
272     outb(end, PORT_DMA_CNT_2);
273     outb(end>>8, PORT_DMA_CNT_2);
274
275     // port 0b: DMA-1 Mode Register
276     // transfer type=write, channel 2
277     outb(mode_register, PORT_DMA1_MODE_REG);
278
279     // port 81: DMA-1 Page Register, channel 2
280     outb(addr>>16, PORT_DMA_PAGE_2);
281
282     outb(0x02, PORT_DMA1_MASK_REG); // unmask channel 2
283
284     int ret = floppy_pio(cmd, cmdlen);
285     if (ret)
286         return DISK_RET_ETIMEOUT;
287
288     // check port 3f4 for accessibility to status bytes
289     if ((inb(PORT_FD_STATUS) & 0xc0) != 0xc0)
290         return DISK_RET_ECONTROLLER;
291
292     // read 7 return status bytes from controller
293     u8 i;
294     for (i=0; i<7; i++) {
295         u8 v = inb(PORT_FD_DATA);
296         cmd[i] = v;
297         SET_BDA(floppy_return_status[i], v);
298     }
299
300     return DISK_RET_SUCCESS;
301 }
302
303
304 /****************************************************************
305  * Floppy media sense
306  ****************************************************************/
307
308 static inline void
309 set_diskette_current_cyl(u8 floppyid, u8 cyl)
310 {
311     SET_BDA(floppy_track[floppyid], cyl);
312 }
313
314 static void
315 floppy_drive_recal(u8 floppyid)
316 {
317     // send Recalibrate command (2 bytes) to controller
318     u8 data[12];
319     data[0] = 0x07;  // 07: Recalibrate
320     data[1] = floppyid; // 0=drive0, 1=drive1
321     floppy_pio(data, 2);
322
323     SETBITS_BDA(floppy_recalibration_status, 1<<floppyid);
324     set_diskette_current_cyl(floppyid, 0);
325 }
326
327 static int
328 floppy_media_sense(struct drive_s *drive_g)
329 {
330     // for now cheat and get drive type from CMOS,
331     // assume media is same as drive type
332
333     // ** config_data **
334     // Bitfields for diskette media control:
335     // Bit(s)  Description (Table M0028)
336     //  7-6  last data rate set by controller
337     //        00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
338     //  5-4  last diskette drive step rate selected
339     //        00=0Ch, 01=0Dh, 10=0Eh, 11=0Ah
340     //  3-2  {data rate at start of operation}
341     //  1-0  reserved
342
343     // ** media_state **
344     // Bitfields for diskette drive media state:
345     // Bit(s)  Description (Table M0030)
346     //  7-6  data rate
347     //    00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
348     //  5  double stepping required (e.g. 360kB in 1.2MB)
349     //  4  media type established
350     //  3  drive capable of supporting 4MB media
351     //  2-0  on exit from BIOS, contains
352     //    000 trying 360kB in 360kB
353     //    001 trying 360kB in 1.2MB
354     //    010 trying 1.2MB in 1.2MB
355     //    011 360kB in 360kB established
356     //    100 360kB in 1.2MB established
357     //    101 1.2MB in 1.2MB established
358     //    110 reserved
359     //    111 all other formats/drives
360
361     u8 ftype = GET_GLOBAL(drive_g->floppy_type);
362     SET_BDA(floppy_last_data_rate, GET_GLOBAL(FloppyInfo[ftype].config_data));
363     u8 floppyid = GET_GLOBAL(drive_g->cntl_id);
364     SET_BDA(floppy_media_state[floppyid]
365             , GET_GLOBAL(FloppyInfo[ftype].media_state));
366     return DISK_RET_SUCCESS;
367 }
368
369 static int
370 check_recal_drive(struct drive_s *drive_g)
371 {
372     u8 floppyid = GET_GLOBAL(drive_g->cntl_id);
373     if ((GET_BDA(floppy_recalibration_status) & (1<<floppyid))
374         && (GET_BDA(floppy_media_state[floppyid]) & FMS_MEDIA_DRIVE_ESTABLISHED))
375         // Media is known.
376         return DISK_RET_SUCCESS;
377
378     // Recalibrate drive.
379     floppy_drive_recal(floppyid);
380
381     // Sense media.
382     return floppy_media_sense(drive_g);
383 }
384
385
386 /****************************************************************
387  * Floppy handlers
388  ****************************************************************/
389
390 static void
391 lba2chs(struct disk_op_s *op, u8 *track, u8 *sector, u8 *head)
392 {
393     u32 lba = op->lba;
394
395     u32 tmp = lba + 1;
396     u16 nlspt = GET_GLOBAL(op->drive_g->lchs.spt);
397     *sector = tmp % nlspt;
398
399     tmp /= nlspt;
400     u16 nlh = GET_GLOBAL(op->drive_g->lchs.heads);
401     *head = tmp % nlh;
402
403     tmp /= nlh;
404     *track = tmp;
405 }
406
407 // diskette controller reset
408 static int
409 floppy_reset(struct disk_op_s *op)
410 {
411     u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
412     set_diskette_current_cyl(floppyid, 0); // current cylinder
413     return DISK_RET_SUCCESS;
414 }
415
416 // Read Diskette Sectors
417 static int
418 floppy_read(struct disk_op_s *op)
419 {
420     int res = check_recal_drive(op->drive_g);
421     if (res)
422         goto fail;
423
424     u8 track, sector, head;
425     lba2chs(op, &track, &sector, &head);
426
427     // send read-normal-data command (9 bytes) to controller
428     u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
429     u8 data[12];
430     data[0] = 0xe6; // e6: read normal data
431     data[1] = (head << 2) | floppyid; // HD DR1 DR2
432     data[2] = track;
433     data[3] = head;
434     data[4] = sector;
435     data[5] = FLOPPY_SIZE_CODE;
436     data[6] = sector + op->count - 1; // last sector to read on track
437     data[7] = FLOPPY_GAPLEN;
438     data[8] = FLOPPY_DATALEN;
439
440     res = floppy_cmd(op, op->count * DISK_SECTOR_SIZE, data, 9);
441     if (res)
442         goto fail;
443
444     if (data[0] & 0xc0) {
445         res = DISK_RET_ECONTROLLER;
446         goto fail;
447     }
448
449     // ??? should track be new val from return_status[3] ?
450     set_diskette_current_cyl(floppyid, track);
451     return DISK_RET_SUCCESS;
452 fail:
453     op->count = 0; // no sectors read
454     return res;
455 }
456
457 // Write Diskette Sectors
458 static int
459 floppy_write(struct disk_op_s *op)
460 {
461     int res = check_recal_drive(op->drive_g);
462     if (res)
463         goto fail;
464
465     u8 track, sector, head;
466     lba2chs(op, &track, &sector, &head);
467
468     // send write-normal-data command (9 bytes) to controller
469     u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
470     u8 data[12];
471     data[0] = 0xc5; // c5: write normal data
472     data[1] = (head << 2) | floppyid; // HD DR1 DR2
473     data[2] = track;
474     data[3] = head;
475     data[4] = sector;
476     data[5] = FLOPPY_SIZE_CODE;
477     data[6] = sector + op->count - 1; // last sector to write on track
478     data[7] = FLOPPY_GAPLEN;
479     data[8] = FLOPPY_DATALEN;
480
481     res = floppy_cmd(op, op->count * DISK_SECTOR_SIZE, data, 9);
482     if (res)
483         goto fail;
484
485     if (data[0] & 0xc0) {
486         if (data[1] & 0x02)
487             res = DISK_RET_EWRITEPROTECT;
488         else
489             res = DISK_RET_ECONTROLLER;
490         goto fail;
491     }
492
493     // ??? should track be new val from return_status[3] ?
494     set_diskette_current_cyl(floppyid, track);
495     return DISK_RET_SUCCESS;
496 fail:
497     op->count = 0; // no sectors read
498     return res;
499 }
500
501 // Verify Diskette Sectors
502 static int
503 floppy_verify(struct disk_op_s *op)
504 {
505     int res = check_recal_drive(op->drive_g);
506     if (res)
507         goto fail;
508
509     u8 track, sector, head;
510     lba2chs(op, &track, &sector, &head);
511
512     // ??? should track be new val from return_status[3] ?
513     u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
514     set_diskette_current_cyl(floppyid, track);
515     return DISK_RET_SUCCESS;
516 fail:
517     op->count = 0; // no sectors read
518     return res;
519 }
520
521 // format diskette track
522 static int
523 floppy_format(struct disk_op_s *op)
524 {
525     int ret = check_recal_drive(op->drive_g);
526     if (ret)
527         return ret;
528
529     u8 head = op->lba;
530
531     // send format-track command (6 bytes) to controller
532     u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
533     u8 data[12];
534     data[0] = 0x4d; // 4d: format track
535     data[1] = (head << 2) | floppyid; // HD DR1 DR2
536     data[2] = FLOPPY_SIZE_CODE;
537     data[3] = op->count; // number of sectors per track
538     data[4] = FLOPPY_FORMAT_GAPLEN;
539     data[5] = FLOPPY_FILLBYTE;
540
541     ret = floppy_cmd(op, op->count * 4, data, 6);
542     if (ret)
543         return ret;
544
545     if (data[0] & 0xc0) {
546         if (data[1] & 0x02)
547             return DISK_RET_EWRITEPROTECT;
548         return DISK_RET_ECONTROLLER;
549     }
550
551     set_diskette_current_cyl(floppyid, 0);
552     return DISK_RET_SUCCESS;
553 }
554
555 int
556 process_floppy_op(struct disk_op_s *op)
557 {
558     if (!CONFIG_FLOPPY)
559         return 0;
560
561     switch (op->command) {
562     case CMD_RESET:
563         return floppy_reset(op);
564     case CMD_READ:
565         return floppy_read(op);
566     case CMD_WRITE:
567         return floppy_write(op);
568     case CMD_VERIFY:
569         return floppy_verify(op);
570     case CMD_FORMAT:
571         return floppy_format(op);
572     default:
573         op->count = 0;
574         return DISK_RET_EPARAM;
575     }
576 }
577
578
579 /****************************************************************
580  * HW irqs
581  ****************************************************************/
582
583 // INT 0Eh Diskette Hardware ISR Entry Point
584 void VISIBLE16
585 handle_0e(void)
586 {
587     debug_isr(DEBUG_ISR_0e);
588     if (! CONFIG_FLOPPY)
589         goto done;
590
591     if ((inb(PORT_FD_STATUS) & 0xc0) != 0xc0) {
592         outb(0x08, PORT_FD_DATA); // sense interrupt status
593         while ((inb(PORT_FD_STATUS) & 0xc0) != 0xc0)
594             ;
595         do {
596             inb(PORT_FD_DATA);
597         } while ((inb(PORT_FD_STATUS) & 0xc0) == 0xc0);
598     }
599     // diskette interrupt has occurred
600     SETBITS_BDA(floppy_recalibration_status, FRS_TIMEOUT);
601
602 done:
603     eoi_pic1();
604 }
605
606 // Called from int08 handler.
607 void
608 floppy_tick(void)
609 {
610     if (! CONFIG_FLOPPY)
611         return;
612
613     // time to turn off drive(s)?
614     u8 fcount = GET_BDA(floppy_motor_counter);
615     if (fcount) {
616         fcount--;
617         SET_BDA(floppy_motor_counter, fcount);
618         if (fcount == 0)
619             // turn motor(s) off
620             outb(inb(PORT_FD_DOR) & 0xcf, PORT_FD_DOR);
621     }
622 }