|
@@ -65,112 +65,84 @@ extern void int13_exec_fail ( void );
|
65
|
65
|
static LIST_HEAD ( drives );
|
66
|
66
|
|
67
|
67
|
/**
|
68
|
|
- * Convert CHS address to linear address
|
|
68
|
+ * INT 13, 00 - Reset disk system
|
69
|
69
|
*
|
70
|
70
|
* @v drive Emulated drive
|
71
|
|
- * @v ch Low bits of cylinder number
|
72
|
|
- * @v cl (bits 7:6) High bits of cylinder number
|
73
|
|
- * @v cl (bits 5:0) Sector number
|
74
|
|
- * @v dh Head number
|
75
|
|
- * @ret lba LBA address
|
76
|
|
- *
|
|
71
|
+ * @ret status Status code
|
77
|
72
|
*/
|
78
|
|
-static unsigned long chs_to_lba ( struct int13_drive *drive,
|
79
|
|
- struct i386_all_regs *ix86 ) {
|
80
|
|
- unsigned int cylinder;
|
81
|
|
- unsigned int head;
|
82
|
|
- unsigned int sector;
|
83
|
|
- unsigned long lba;
|
84
|
|
-
|
85
|
|
- cylinder = ( ( ( ix86->regs.cl & 0xc0 ) << 8 ) | ix86->regs.ch );
|
86
|
|
- head = ix86->regs.dh;
|
87
|
|
- sector = ( ix86->regs.cl & 0x3f );
|
88
|
|
-
|
89
|
|
- assert ( cylinder < drive->cylinders );
|
90
|
|
- assert ( head < drive->heads );
|
91
|
|
- assert ( ( sector >= 1 ) && ( sector <= drive->sectors_per_track ) );
|
92
|
|
-
|
93
|
|
- lba = ( ( ( ( cylinder * drive->heads ) + head )
|
94
|
|
- * drive->sectors_per_track ) + sector - 1 );
|
95
|
|
-
|
96
|
|
- DBG ( "C/H/S address %x/%x/%x -> LBA %lx\n",
|
97
|
|
- cylinder, head, sector, lba );
|
98
|
|
-
|
99
|
|
- return lba;
|
|
73
|
+static int int13_reset ( struct int13_drive *drive __unused,
|
|
74
|
+ struct i386_all_regs *ix86 __unused ) {
|
|
75
|
+ DBG ( "Reset drive\n" );
|
|
76
|
+ return 0;
|
100
|
77
|
}
|
101
|
78
|
|
102
|
79
|
/**
|
103
|
|
- * Read from drive to real-mode data buffer
|
|
80
|
+ * INT 13, 01 - Get status of last operation
|
104
|
81
|
*
|
105
|
82
|
* @v drive Emulated drive
|
106
|
|
- * @v lba LBA starting sector number
|
107
|
|
- * @v data Data buffer
|
108
|
|
- * @v count Number of sectors to read
|
109
|
83
|
* @ret status Status code
|
110
|
84
|
*/
|
111
|
|
-static int int13_read ( struct int13_drive *drive, uint64_t lba,
|
112
|
|
- struct segoff data, unsigned long count ) {
|
113
|
|
- struct block_device *blockdev = drive->blockdev;
|
114
|
|
- userptr_t buffer = real_to_user ( data.segment, data.offset );
|
115
|
|
- int rc;
|
116
|
|
-
|
117
|
|
- DBG ( "Read %lx sectors from %llx to %04x:%04x\n", count,
|
118
|
|
- ( unsigned long long ) lba, data.segment, data.offset );
|
119
|
|
-
|
120
|
|
- if ( ( rc = blockdev->read ( blockdev, lba, count, buffer ) ) != 0 )
|
121
|
|
- return INT13_STATUS_READ_ERROR;
|
122
|
|
-
|
123
|
|
- return 0;
|
|
85
|
+static int int13_get_last_status ( struct int13_drive *drive,
|
|
86
|
+ struct i386_all_regs *ix86 __unused ) {
|
|
87
|
+ DBG ( "Get status of last operation\n" );
|
|
88
|
+ return drive->last_status;
|
124
|
89
|
}
|
125
|
90
|
|
126
|
91
|
/**
|
127
|
|
- * Write from real-mode data buffer to drive
|
|
92
|
+ * Read / write sectors
|
128
|
93
|
*
|
129
|
94
|
* @v drive Emulated drive
|
130
|
|
- * @v lba LBA starting sector number
|
131
|
|
- * @v data Data buffer
|
132
|
|
- * @v count Number of sectors to read
|
|
95
|
+ * @v al Number of sectors to read or write (must be nonzero)
|
|
96
|
+ * @v ch Low bits of cylinder number
|
|
97
|
+ * @v cl (bits 7:6) High bits of cylinder number
|
|
98
|
+ * @v cl (bits 5:0) Sector number
|
|
99
|
+ * @v dh Head number
|
|
100
|
+ * @v es:bx Data buffer
|
|
101
|
+ * @v io Read / write method
|
133
|
102
|
* @ret status Status code
|
|
103
|
+ * @ret al Number of sectors read or written
|
134
|
104
|
*/
|
135
|
|
-static int int13_write ( struct int13_drive *drive, uint64_t lba,
|
136
|
|
- struct segoff data, unsigned long count ) {
|
|
105
|
+static int int13_rw_sectors ( struct int13_drive *drive,
|
|
106
|
+ struct i386_all_regs *ix86,
|
|
107
|
+ int ( * io ) ( struct block_device *blockdev,
|
|
108
|
+ uint64_t block,
|
|
109
|
+ unsigned long count,
|
|
110
|
+ userptr_t buffer ) ) {
|
137
|
111
|
struct block_device *blockdev = drive->blockdev;
|
138
|
|
- userptr_t buffer = real_to_user ( data.segment, data.offset );
|
139
|
|
- int rc;
|
|
112
|
+ unsigned int cylinder, head, sector;
|
|
113
|
+ unsigned long lba;
|
|
114
|
+ unsigned int count;
|
|
115
|
+ userptr_t buffer;
|
140
|
116
|
|
141
|
|
- DBG ( "Write %lx sectors from %04x:%04x to %llx\n", count,
|
142
|
|
- data.segment, data.offset, ( unsigned long long ) lba );
|
|
117
|
+ /* Calculate parameters */
|
|
118
|
+ cylinder = ( ( ( ix86->regs.cl & 0xc0 ) << 8 ) | ix86->regs.ch );
|
|
119
|
+ assert ( cylinder < drive->cylinders );
|
|
120
|
+ head = ix86->regs.dh;
|
|
121
|
+ assert ( head < drive->heads );
|
|
122
|
+ sector = ( ix86->regs.cl & 0x3f );
|
|
123
|
+ assert ( ( sector >= 1 ) && ( sector <= drive->sectors_per_track ) );
|
|
124
|
+ lba = ( ( ( ( cylinder * drive->heads ) + head )
|
|
125
|
+ * drive->sectors_per_track ) + sector - 1 );
|
|
126
|
+ count = ix86->regs.al;
|
|
127
|
+ buffer = real_to_user ( ix86->segs.es, ix86->regs.bx );
|
143
|
128
|
|
144
|
|
- if ( ( rc = blockdev->write ( blockdev, lba, count, buffer ) ) != 0 )
|
145
|
|
- return INT13_STATUS_WRITE_ERROR;
|
|
129
|
+ DBG ( "C/H/S %d/%d/%d = LBA %#lx <-> %04x:%04x (count %d)\n", cylinder,
|
|
130
|
+ head, sector, lba, ix86->segs.es, ix86->regs.bx, count );
|
146
|
131
|
|
147
|
|
- return 0;
|
148
|
|
-}
|
|
132
|
+ /* Validate blocksize */
|
|
133
|
+ if ( blockdev->blksize != INT13_BLKSIZE ) {
|
|
134
|
+ DBG ( "Invalid blocksize (%zd) for non-extended read/write\n",
|
|
135
|
+ blockdev->blksize );
|
|
136
|
+ return INT13_STATUS_INVALID;
|
|
137
|
+ }
|
|
138
|
+
|
|
139
|
+ /* Read from / write to block device */
|
|
140
|
+ if ( io ( blockdev, lba, count, buffer ) != 0 )
|
|
141
|
+ return INT13_STATUS_READ_ERROR;
|
149
|
142
|
|
150
|
|
-/**
|
151
|
|
- * INT 13, 00 - Reset disk system
|
152
|
|
- *
|
153
|
|
- * @v drive Emulated drive
|
154
|
|
- * @ret status Status code
|
155
|
|
- */
|
156
|
|
-static int int13_reset ( struct int13_drive *drive __unused,
|
157
|
|
- struct i386_all_regs *ix86 __unused ) {
|
158
|
|
- DBG ( "Reset drive\n" );
|
159
|
143
|
return 0;
|
160
|
144
|
}
|
161
|
145
|
|
162
|
|
-/**
|
163
|
|
- * INT 13, 01 - Get status of last operation
|
164
|
|
- *
|
165
|
|
- * @v drive Emulated drive
|
166
|
|
- * @ret status Status code
|
167
|
|
- */
|
168
|
|
-static int int13_get_last_status ( struct int13_drive *drive,
|
169
|
|
- struct i386_all_regs *ix86 __unused ) {
|
170
|
|
- DBG ( "Get status of last operation\n" );
|
171
|
|
- return drive->last_status;
|
172
|
|
-}
|
173
|
|
-
|
174
|
146
|
/**
|
175
|
147
|
* INT 13, 02 - Read sectors
|
176
|
148
|
*
|
|
@@ -186,20 +158,8 @@ static int int13_get_last_status ( struct int13_drive *drive,
|
186
|
158
|
*/
|
187
|
159
|
static int int13_read_sectors ( struct int13_drive *drive,
|
188
|
160
|
struct i386_all_regs *ix86 ) {
|
189
|
|
- unsigned long lba = chs_to_lba ( drive, ix86 );
|
190
|
|
- unsigned int count = ix86->regs.al;
|
191
|
|
- struct segoff data = {
|
192
|
|
- .segment = ix86->segs.es,
|
193
|
|
- .offset = ix86->regs.bx,
|
194
|
|
- };
|
195
|
|
-
|
196
|
|
- if ( drive->blockdev->blksize != INT13_BLKSIZE ) {
|
197
|
|
- DBG ( "Invalid blocksize (%zd) for non-extended read\n",
|
198
|
|
- drive->blockdev->blksize );
|
199
|
|
- return INT13_STATUS_INVALID;
|
200
|
|
- }
|
201
|
|
-
|
202
|
|
- return int13_read ( drive, lba, data, count );
|
|
161
|
+ DBG ( "Read: " );
|
|
162
|
+ return int13_rw_sectors ( drive, ix86, drive->blockdev->read );
|
203
|
163
|
}
|
204
|
164
|
|
205
|
165
|
/**
|
|
@@ -217,20 +177,8 @@ static int int13_read_sectors ( struct int13_drive *drive,
|
217
|
177
|
*/
|
218
|
178
|
static int int13_write_sectors ( struct int13_drive *drive,
|
219
|
179
|
struct i386_all_regs *ix86 ) {
|
220
|
|
- unsigned long lba = chs_to_lba ( drive, ix86 );
|
221
|
|
- unsigned int count = ix86->regs.al;
|
222
|
|
- struct segoff data = {
|
223
|
|
- .segment = ix86->segs.es,
|
224
|
|
- .offset = ix86->regs.bx,
|
225
|
|
- };
|
226
|
|
-
|
227
|
|
- if ( drive->blockdev->blksize != INT13_BLKSIZE ) {
|
228
|
|
- DBG ( "Invalid blocksize (%zd) for non-extended write\n",
|
229
|
|
- drive->blockdev->blksize );
|
230
|
|
- return INT13_STATUS_INVALID;
|
231
|
|
- }
|
232
|
|
-
|
233
|
|
- return int13_write ( drive, lba, data, count );
|
|
180
|
+ DBG ( "Write: " );
|
|
181
|
+ return int13_rw_sectors ( drive, ix86, drive->blockdev->write );
|
234
|
182
|
}
|
235
|
183
|
|
236
|
184
|
/**
|
|
@@ -280,6 +228,42 @@ static int int13_extension_check ( struct int13_drive *drive __unused,
|
280
|
228
|
}
|
281
|
229
|
}
|
282
|
230
|
|
|
231
|
+/**
|
|
232
|
+ * Extended read / write
|
|
233
|
+ *
|
|
234
|
+ * @v drive Emulated drive
|
|
235
|
+ * @v ds:si Disk address packet
|
|
236
|
+ * @v io Read / write method
|
|
237
|
+ * @ret status Status code
|
|
238
|
+ */
|
|
239
|
+static int int13_extended_rw ( struct int13_drive *drive,
|
|
240
|
+ struct i386_all_regs *ix86,
|
|
241
|
+ int ( * io ) ( struct block_device *blockdev,
|
|
242
|
+ uint64_t block,
|
|
243
|
+ unsigned long count,
|
|
244
|
+ userptr_t buffer ) ) {
|
|
245
|
+ struct block_device *blockdev = drive->blockdev;
|
|
246
|
+ struct int13_disk_address addr;
|
|
247
|
+ uint64_t lba;
|
|
248
|
+ unsigned long count;
|
|
249
|
+ userptr_t buffer;
|
|
250
|
+
|
|
251
|
+ /* Read parameters from disk address structure */
|
|
252
|
+ copy_from_real ( &addr, ix86->segs.ds, ix86->regs.si, sizeof ( addr ));
|
|
253
|
+ lba = addr.lba;
|
|
254
|
+ count = addr.count;
|
|
255
|
+ buffer = real_to_user ( addr.buffer.segment, addr.buffer.offset );
|
|
256
|
+
|
|
257
|
+ DBG ( "LBA %#llx <-> %04x:%04x (count %ld)\n", (unsigned long long)lba,
|
|
258
|
+ addr.buffer.segment, addr.buffer.offset, count );
|
|
259
|
+
|
|
260
|
+ /* Read from / write to block device */
|
|
261
|
+ if ( io ( blockdev, lba, count, buffer ) != 0 )
|
|
262
|
+ return INT13_STATUS_READ_ERROR;
|
|
263
|
+
|
|
264
|
+ return 0;
|
|
265
|
+}
|
|
266
|
+
|
283
|
267
|
/**
|
284
|
268
|
* INT 13, 42 - Extended read
|
285
|
269
|
*
|
|
@@ -289,11 +273,8 @@ static int int13_extension_check ( struct int13_drive *drive __unused,
|
289
|
273
|
*/
|
290
|
274
|
static int int13_extended_read ( struct int13_drive *drive,
|
291
|
275
|
struct i386_all_regs *ix86 ) {
|
292
|
|
- struct int13_disk_address addr;
|
293
|
|
-
|
294
|
|
- copy_from_real ( &addr, ix86->segs.ds, ix86->regs.si,
|
295
|
|
- sizeof ( addr ) );
|
296
|
|
- return int13_read ( drive, addr.lba, addr.buffer, addr.count );
|
|
276
|
+ DBG ( "Extended read: " );
|
|
277
|
+ return int13_extended_rw ( drive, ix86, drive->blockdev->read );
|
297
|
278
|
}
|
298
|
279
|
|
299
|
280
|
/**
|
|
@@ -305,11 +286,8 @@ static int int13_extended_read ( struct int13_drive *drive,
|
305
|
286
|
*/
|
306
|
287
|
static int int13_extended_write ( struct int13_drive *drive,
|
307
|
288
|
struct i386_all_regs *ix86 ) {
|
308
|
|
- struct int13_disk_address addr;
|
309
|
|
-
|
310
|
|
- copy_from_real ( &addr, ix86->segs.ds, ix86->regs.si,
|
311
|
|
- sizeof ( addr ) );
|
312
|
|
- return int13_write ( drive, addr.lba, addr.buffer, addr.count );
|
|
289
|
+ DBG ( "Extended write: " );
|
|
290
|
+ return int13_extended_rw ( drive, ix86, drive->blockdev->write );
|
313
|
291
|
}
|
314
|
292
|
|
315
|
293
|
/**
|
|
@@ -351,7 +329,7 @@ static void int13 ( struct i386_all_regs *ix86 ) {
|
351
|
329
|
if ( drive->drive != ix86->regs.dl )
|
352
|
330
|
continue;
|
353
|
331
|
|
354
|
|
- DBG ( "INT 13, %02x on drive %02x\n", ix86->regs.ah,
|
|
332
|
+ DBG ( "INT 13,%02x (%02x): ", ix86->regs.ah,
|
355
|
333
|
ix86->regs.dl );
|
356
|
334
|
|
357
|
335
|
switch ( ix86->regs.ah ) {
|