ad01b332edd857808ae2ff3ac61f2fb144c7ee8f
[movitz-core.git] / losp / x86-pc / ata.lisp
blobad01b332edd857808ae2ff3ac61f2fb144c7ee8f
1 ;;;; Movitz ATA Hard Drive Driver
2 ;;;; --------------------------------------------------------------------------
3 ;;;; [13 Mar 2008] Martin Bealby
4 ;;;; Moved into muerte.x86-pc.ata package
5 ;;;; 'ata' prefix removed from function names
6 ;;;; [23 Feb 2008] Martin Bealby
7 ;;;; Partition reading and parsing functions added
8 ;;;; Read / write sector functions converted to work with lists of bytes
9 ;;;; [25 Oct 2007] Martin Bealby
10 ;;;; Rewritten from scratch based on http://www.osdever.net/tutorials/lba.php
11 ;;;; --------------------------------------------------------------------------
14 ;;;; --------------------------------------------------------------------------
15 ;;;; Package Setup
16 ;;;; --------------------------------------------------------------------------
17 (require :x86-pc/package)
19 (defpackage muerte.x86-pc.ata
20 (:use muerte.cl muerte.lib muerte)
21 (:export #:read-partition-table
22 #:get-partition-data
23 #:partition-active-p
24 #:partition-type
25 #:partition-start-offset
26 #:partition-size
27 #:lba-read-sector
28 #:lba-write-sector))
30 (provide :x86-pc/ata)
32 (in-package muerte.x86-pc.ata)
35 ;;;; --------------------------------------------------------------------------
36 ;;;; Constants
37 ;;;; --------------------------------------------------------------------------
38 (defconstant +controller0+ #x1F0)
39 (defconstant +controller1+ #x170)
41 (defconstant +offset-data+ 0)
42 (defconstant +offset-error-precomp+ 1)
43 (defconstant +offset-sector-count+ 2)
44 (defconstant +offset-lba1+ 3)
45 (defconstant +offset-lba2+ 4)
46 (defconstant +offset-lba3+ 5)
47 (defconstant +offset-drive-head+ 6)
48 (defconstant +offset-status-command+ 7)
51 (defconstant +command-read+ #x20)
52 (defconstant +command-write+ #x30)
53 (defconstant +bitflag-busy+ 7)
56 (defparameter *partitions* '((nil nil nil nil) ; controller 1 disc 1
57 (nil nil nil nil) ; controller 1 disc 2
58 (nil nil nil nil) ; controller 2 disc 1
59 (nil nil nil nil))) ; controller 2 disc 2
62 ;;;; --------------------------------------------------------------------------
63 ;;;; Drive Functions
64 ;;;; --------------------------------------------------------------------------
65 (defun busy-wait (controller)
66 "ATA Driver busy spin loop."
67 (loop while (logbitp +bitflag-busy+
68 (io-port (+ controller
69 +offset-status-command+)
70 :unsigned-byte8))))
72 (defun send-command (controller command)
73 "Send a command to an IDE controller."
74 (setf (io-port (+ controller +offset-status-command+) :unsigned-byte8)
75 command))
77 (defun lba-read-write-common (controller drive-number block-address)
78 (if (= 0 block-address)
79 (error "Sector zero is not a valid index for LBA addressing."))
80 ;; send a null
81 (setf (io-port (+ controller
82 +offset-error-precomp+) :unsigned-byte8) 0)
83 ;; send a sector count
84 (setf (io-port (+ controller
85 +offset-sector-count+):unsigned-byte8) 1)
86 ;; send the lowest 8 bits of the lba
87 (setf (io-port (+ controller +offset-lba1+) :unsigned-byte8)
88 (logand block-address #xFF))
89 ;; send the next 8 bits
90 (setf (io-port (+ controller +offset-lba2+) :unsigned-byte8)
91 (ash (logand block-address #xFF00)
92 -8))
93 ;; send the next 8 bits
94 (setf (io-port (+ controller +offset-lba3+) :unsigned-byte8)
95 (ash (logand block-address #xFF0000)
96 -16))
97 ;; send the last 4 bits and some magic bits
98 (setf (io-port (+ controller +offset-drive-head+) :unsigned-byte8)
99 (logand
100 (logior (ash (logand block-address #xF000000)
101 -24)
102 #xE0 ;; magic
103 (ash drive-number 4))
104 #x0F)))
107 (defun lba-read-sector (controller drive-number block-address)
108 "Reads a specified sector of data from the disk."
109 ;; Call common initialisation
110 (lba-read-write-common controller drive-number block-address)
111 ;; send read command
112 (send-command controller +command-read+)
113 ;; wait for the drive
114 (busy-wait controller)
115 ;; read the data
116 (loop for position from 0 to 255
117 for data = (io-port (+ controller +offset-data+)
118 :unsigned-byte16)
119 collect (logand #x00FF data)
120 collect (ash (logand #xFF00 data) -8)))
123 (defun lba-write-sector (controller drive-number block-address data)
124 "Writes data to a sector of the disk."
125 ;; data must be a list of 512 unsigned-byte8's
126 ;; based upon lba-read-sector-above
127 (lba-read-write-common controller drive-number block-address)
128 (send-command controller +command-write+)
129 (busy-wait controller)
130 (loop for position from 0 to 511 by 2
131 do (setf (io-port (+ controller +offset-data+) :unsigned-byte16)
132 (+ (ash (nth position data) 8)
133 (nth (+ 1 position) data)))))
136 ;;;; --------------------------------------------------------------------------
137 ;;;; Partition Functions
138 ;;;; --------------------------------------------------------------------------
139 (defun read-partition-table (controller drive-number)
140 "Reads and stores the partition table from the specified disk."
141 (cond ((> controller 1) ; assume 2 controllers for now
142 (error "Invalid controller number.")))
143 (cond ((> drive-number 1) ; assume 2 drives per controller for now
144 (error "Invalid driver number.")))
146 (let ((sector
147 (cond ((= 0 controller)
148 (lba-read-sector +controller0+ drive-number 1))
149 ((= 1 controller)
150 (lba-read-sector +controller1+ drive-number 1)))))
151 (setf (nth (+ controller drive-number) *partitions*)
152 (list (loop for offset from 446 to 461
153 collect (nth offset sector))
154 (loop for offset from 462 to 477
155 collect (nth offset sector))
156 (loop for offset from 478 to 493
157 collect (nth offset sector))
158 (loop for offset from 494 to 509
159 collect (nth offset sector))))))
161 (defun get-partition-data (controller drive-number partition)
162 "Returns the partition data for the specified controller and drive from the local cache.
163 The data can be supplied to:
164 partition-active-p
165 partition-type
166 partition-start-offset
167 partition-size"
168 (cond ((> controller 1) ; assume 2 controllers for now
169 (error "Invalid controller number.")))
170 (cond ((> drive-number 1) ; assume 2 drives per controller for now
171 (error "Invalid driver number.")))
172 (cond ((> partition 3) ; max 4 partitions per drive (no extended partitions for now)
173 (error "Invalid partition number.")))
174 (nth partition
175 (nth (+ controller drive-number) *partitions*)))
177 (defun partition-active-p (partition-data)
178 "Returns the state of the active flag for the partition."
179 (cond ((= #x80 (nth 0 partition-data))
181 (t nil)))
183 (defun partition-type (partition-data)
184 "Returns the partition type for the partition."
185 (let ((id (nth 4 partition-data)))
186 (cond ((= #x00 id)
187 "EMPTY")
188 ((= #x06 id)
189 "FAT16")
190 ((= #x07 id)
191 "NTFS")
192 ((= #x0b id)
193 "FAT32")
194 ((= #x0c id)
195 "FAT32-LBA")
196 ((= #x0e id)
197 "FAT16-LBA")
198 ((= #x82 id)
199 "LINUX-SWAP")
200 ((= #x83 id)
201 "LINUX")
202 ((= #xA5 id)
203 "FREEBSD")
204 ((= #xA6 id)
205 "OPENBSD")
206 ((= #xA9 id)
207 "NETBSD")
208 ((= #xE8 id)
209 "LUKS")
211 "UNKNOWN"))))
213 (defun partition-start-offset (partition-data)
214 "Returns the starting offset (LBA) for the partition."
215 (+ (nth 8 partition-data)
216 (ash (nth 9 partition-data)
218 (ash (nth 10 partition-data)
220 (ash (nth 11 partition-data)
221 24)))
223 (defun partition-size (partition-data)
224 "Returns the size of the partition."
225 (+ (nth 12 partition-data)
226 (ash (nth 13 partition-data)
228 (ash (nth 14 partition-data)
230 (ash (nth 15 partition-data)
231 24)))