about summary refs log tree commit diff stats
path: root/boot.subx
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2021-03-21 22:10:17 -0700
committerKartik K. Agaram <vc@akkartik.com>2021-03-21 22:29:24 -0700
commitfba2146593b077829845efcfe4b343ce809f5d88 (patch)
tree5cda18c98876967530105ab95757fede599685f9 /boot.subx
parent89db4ec10001f08742be78596ef41f07ed5b124c (diff)
downloadmu-fba2146593b077829845efcfe4b343ce809f5d88.tar.gz
snapshot: reading from disk without BIOS!!
Both LBA and CHS coordinates are now working for the primary disk on the
primary bus.

Failure modes I ran into:
  - ATA ports are 16-bit values. Using instructions with 8-bit immediates
    will yield strange results. (I had to debug this twice because I missed
    poll-ata-primary-bus-primary-drive-regular-status-word the first time
    around.)

    Mu's toolchain has been found out here. bootstrap has good
    errors but doesn't support the instructions I need in boot.subx. The
    self-hosted phases support the instructions but provide no error-checking.
    Might be worth starting to add error-checking as I encounter the need.
    In this case, a vote for validating metadata sizes even if we don't
    validate that instructions pass in the right metadata sizes.

  - Can't poll readiness first thing. Maybe we need to always select the
    drive first.

  - Reading 8-bit values from a 16-bit port (data port 0x1f0) returns garbage.
    Reading 32-bit values however works totally fine; go figure. (Maybe
    it won't work on real hardware?)

    https://forum.osdev.org/viewtopic.php?t=36415

  - Passing in a 0 segment will never return data.
Diffstat (limited to 'boot.subx')
-rw-r--r--boot.subx214
1 files changed, 214 insertions, 0 deletions
diff --git a/boot.subx b/boot.subx
index 5cf7ee7d..1a2a6e64 100644
--- a/boot.subx
+++ b/boot.subx
@@ -914,5 +914,219 @@ Font:
 # }}}
 
 # offset 1800 (address 0x9400)
+== code 0x9400
+
+# Use 28-bit PIO mode to transfer one sector from the primary drive on the
+# primary bus.
+# Inspired by https://colorforth.github.io/ide.html
+#
+# Resources:
+#   https://wiki.osdev.org/ATA_PIO_Mode
+#   https://forum.osdev.org/viewtopic.php?f=1&p=167798
+#   read-sector, according to https://www.scs.stanford.edu/11wi-cs140/pintos/specs/ata-3-std.pdf
+read-a-sector:
+  # . prologue
+  55/push-ebp
+  89/<- %ebp 4/r32/esp
+  # . save registers
+  50/push-eax
+  52/push-edx
+  # check for floating bus
+  {
+    31/xor %eax 0/r32/eax
+    ba/copy-to-edx 0x1f7/imm32
+    ec/read-port-dx-into-al
+    81 7/subop/compare %eax 0xff/imm32
+    75/jump-if-!= break/disp8
+    (abort "primary bus has no drives")
+  }
+  # kick off read
+  (ata-drive-select 0xe0)  # primary drive; 4 LSBs contain 4 upper bits of LBA (here 0)
+#?   (ata-drive-select 0xa0)  # primary drive in CHS + head number
+  (ata-error 0)
+  (ata-sector-count 1)
+  (ata-lba 0 0 0)  # lower 24 bits of LBA
+#?   (ata-cyl-sector 1 0 0)
+  (ata-command 0x20)  # read sectors with retries
+  # poll for results
+  (poll-ata-primary-bus-primary-drive-regular-status-word)
+  # print out results
+  ba/copy-to-edx 0x1f0/imm32
+  b9/copy-to-ecx 0x10/imm32
+  {
+    81 7/subop/compare %ecx 0/imm32
+    74/jump-if-= break/disp8
+    ed/read-port-dx-into-eax
+    (draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0 %eax)
+    (move-cursor-to-left-margin-of-next-line 0)  # 0=screen
+    49/decrement-ecx
+    eb/jump loop/disp8
+  }
+  (abort "success")
+$read-256-sectors:end:
+  # . restore registers
+  5a/pop-to-edx
+  58/pop-to-eax
+  # . epilogue
+  89/<- %esp 5/r32/ebp
+  5d/pop-to-ebp
+  c3/return
+
+ata-drive-select:  # n: byte
+  # . prologue
+  55/push-ebp
+  89/<- %ebp 4/r32/esp
+  # . save registers
+  50/push-eax
+  52/push-edx
+  #
+  8b/-> *(ebp+8) 0/r32/eax
+  ba/copy-to-edx 0x1f6/imm32
+  ee/write-al-into-port-dx
+$ata-drive-select:end:
+  # . restore registers
+  5a/pop-to-edx
+  58/pop-to-eax
+  # . epilogue
+  89/<- %esp 5/r32/ebp
+  5d/pop-to-ebp
+  c3/return
+
+ata-error:  # n: byte
+  # . prologue
+  55/push-ebp
+  89/<- %ebp 4/r32/esp
+  # . save registers
+  50/push-eax
+  52/push-edx
+  #
+  8b/-> *(ebp+8) 0/r32/eax
+  ba/copy-to-edx 0x1f1/imm32
+  ee/write-al-into-port-dx
+$ata-error:end:
+  # . restore registers
+  5a/pop-to-edx
+  58/pop-to-eax
+  # . epilogue
+  89/<- %esp 5/r32/ebp
+  5d/pop-to-ebp
+  c3/return
+
+ata-sector-count:  # n: byte
+  # . prologue
+  55/push-ebp
+  89/<- %ebp 4/r32/esp
+  # . save registers
+  50/push-eax
+  52/push-edx
+  #
+  8b/-> *(ebp+8) 0/r32/eax
+  ba/copy-to-edx 0x1f2/imm32
+  ee/write-al-into-port-dx
+$ata-sector-count:end:
+  # . restore registers
+  5a/pop-to-edx
+  58/pop-to-eax
+  # . epilogue
+  89/<- %esp 5/r32/ebp
+  5d/pop-to-ebp
+  c3/return
+
+ata-lba:  # lo: byte, mid: byte, hi: byte
+  # . prologue
+  55/push-ebp
+  89/<- %ebp 4/r32/esp
+  # . save registers
+  50/push-eax
+  52/push-edx
+  # lo
+  8b/-> *(ebp+8) 0/r32/eax
+  ba/copy-to-edx 0x1f3/imm32
+  ee/write-al-into-port-dx
+  # mid
+  8b/-> *(ebp+0xc) 0/r32/eax
+  ba/copy-to-edx 0x1f4/imm32
+  ee/write-al-into-port-dx
+  # hi
+  8b/-> *(ebp+0x10) 0/r32/eax
+  ba/copy-to-edx 0x1f5/imm32
+  ee/write-al-into-port-dx
+$ata-lba:end:
+  # . restore registers
+  5a/pop-to-edx
+  58/pop-to-eax
+  # . epilogue
+  89/<- %esp 5/r32/ebp
+  5d/pop-to-ebp
+  c3/return
+
+# sector: [1, 63]
+# cylinder: [0, 1023]
+ata-cyl-sector:  # sector: byte, cyl-lo: byte, cyl-hi: byte
+  # . prologue
+  55/push-ebp
+  89/<- %ebp 4/r32/esp
+  # . save registers
+  50/push-eax
+  52/push-edx
+  # sector
+  8b/-> *(ebp+8) 0/r32/eax
+  ba/copy-to-edx 0x1f3/imm32
+  ee/write-al-into-port-dx
+  # cyl-lo
+  8b/-> *(ebp+0xc) 0/r32/eax
+  ba/copy-to-edx 0x1f4/imm32
+  ee/write-al-into-port-dx
+  # cyl-hi
+  8b/-> *(ebp+0x10) 0/r32/eax
+  ba/copy-to-edx 0x1f5/imm32
+  ee/write-al-into-port-dx
+$ata-lba:end:
+  # . restore registers
+  5a/pop-to-edx
+  58/pop-to-eax
+  # . epilogue
+  89/<- %esp 5/r32/ebp
+  5d/pop-to-ebp
+  c3/return
+
+ata-command:  # cmd: byte
+  # . prologue
+  55/push-ebp
+  89/<- %ebp 4/r32/esp
+  # . save registers
+  50/push-eax
+  52/push-edx
+  #
+  8b/-> *(ebp+8) 0/r32/eax
+  ba/copy-to-edx 0x1f7/imm32
+  ee/write-al-into-port-dx
+$ata-command:end:
+  # . restore registers
+  5a/pop-to-edx
+  58/pop-to-eax
+  # . epilogue
+  89/<- %esp 5/r32/ebp
+  5d/pop-to-ebp
+  c3/return
+
+poll-ata-primary-bus-primary-drive-regular-status-word:
+  # . save registers
+  50/push-eax
+  52/push-edx
+  {
+    ba/copy-to-edx 0x1f7/imm32
+    ec/read-port-dx-into-al
+    a8/test-bits-in-al 0x80/imm8/bsy  # set zf if bit 7 (most significant) is not set
+    75/jump-if-zf-not-set-and-bit-7-set loop/disp8
+    a8/test-bits-in-al 8/imm8/drq  # set zf if bit 3 is not set
+    74/jump-if-zf-set-and-bit-3-not-set loop/disp8
+  }
+$poll-ata-primary-bus-primary-drive-regular-status-word:end:
+  # . restore registers
+  5a/pop-to-edx
+  58/pop-to-eax
+  # . epilogue
+  c3/return
 
 # vim:ft=subx