The floppy disk controller was incorrectly setting the FD_SR0_SEEK
(Seek End) status bit during automatic track crossing in multi-track
read/write operations. This caused legacy operating systems like Minix 2
to interpret successful read operations as errors, resulting in
"Unrecoverable disk error" messages for blocks that crossed track
boundaries.

When executing multi-sector READ/WRITE commands with the MT (multi-track)
flag set, the FDC would correctly advance to the next track when needed
to continue the transfer. However, it was incorrectly setting the SE
(Seek End) bit in Status Register 0 (ST0) during this automatic track
advancement.

According to the Intel 82078 datasheet and related documentation, the
SE bit (bit 5, value 0x20) in ST0 should only be set:
1. After explicit SEEK or RECALIBRATE commands
2. After READ/WRITE commands that perform an "implied seek" (when the
   command specifies a different cylinder/head/sector than the current
   position and EIS flag is not set)

The SE bit should NOT be set during automatic track crossing that occurs
as part of an ongoing multi-track data transfer. This automatic track
advancement is part of the normal multi-track operation, not a seek.

This bug prevented Minix 2 and potentially other legacy operating systems
from booting. The OS floppy driver would detect the unexpected SE bit and
interpret it as a read error, even though the data was transferred
successfully. This particularly affected 1024-byte filesystem blocks that
spanned track boundaries.

Modified fdctrl_seek_to_next_sect() to remove the line that set
FD_SR0_SEEK when advancing to the next track during multi-track
operations. The function now:
- In multi-track mode: advances tracks/heads as needed WITHOUT setting
  the SE bit
- In non-multi-track mode: stops at end of track without seeking (also
  without setting SE bit, as no seek occurs)

The SE bit is still correctly set by explicit SEEK and RECALIBRATE
commands elsewhere in the code.

Fixes: c5139bd9 (fdc: fix FD_SR0_SEEK for non-DMA transfers and multi
sectors transfers)
Signed-off-by: Pedro J. Ruiz <[email protected]>
---
 hw/block/fdc.c         | 9 ++++++---
 tests/qtest/fdc-test.c | 2 +-
 2 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/hw/block/fdc.c b/hw/block/fdc.c
index 4585640af9..21713c9c41 100644
--- a/hw/block/fdc.c
+++ b/hw/block/fdc.c
@@ -1403,14 +1403,17 @@ static int fdctrl_seek_to_next_sect(FDCtrl *fdctrl, 
FDrive *cur_drv)
             } else {
                 new_head = 0;
                 new_track++;
-                fdctrl->status0 |= FD_SR0_SEEK;
+                /* Don't set FD_SR0_SEEK for implicit track crossing during
+                 * multi-track transfers. SEEK bit must only be set for
+                 * explicit SEEK commands, not automatic track advancement.
+                 */
                 if ((cur_drv->flags & FDISK_DBL_SIDES) == 0) {
                     ret = 0;
                 }
             }
         } else {
-            fdctrl->status0 |= FD_SR0_SEEK;
-            new_track++;
+            /* Not in multi-track mode: stop at end of track and don't seek. */
+            FLOPPY_DPRINTF("end of track, stopping transfer\n");
             ret = 0;
         }
         if (ret == 1) {
diff --git a/tests/qtest/fdc-test.c b/tests/qtest/fdc-test.c
index 1b37a8a4d2..9edfbb5a40 100644
--- a/tests/qtest/fdc-test.c
+++ b/tests/qtest/fdc-test.c
@@ -519,7 +519,7 @@ static void test_read_no_dma_19(void)
 
     outb(FLOPPY_BASE + reg_dor, inb(FLOPPY_BASE + reg_dor) & ~0x08);
     send_seek(0);
-    ret = send_read_no_dma_command(19, 0x20);
+    ret = send_read_no_dma_command(19, 0x00);
     g_assert(ret == 0);
 }
 
-- 
2.50.1 (Apple Git-155)


Reply via email to