Просмотр исходного кода

Add INT 13 emulation layer (which provides an interface to gPXE block

devices).
tags/v0.9.3
Michael Brown 18 лет назад
Родитель
Сommit
d2c22ec9bb
2 измененных файлов: 632 добавлений и 0 удалений
  1. 157
    0
      src/arch/i386/include/int13.h
  2. 475
    0
      src/arch/i386/interface/pcbios/int13.c

+ 157
- 0
src/arch/i386/include/int13.h Просмотреть файл

@@ -0,0 +1,157 @@
1
+#ifndef INT13_H
2
+#define INT13_H
3
+
4
+/** @file
5
+ *
6
+ * INT 13 emulation
7
+ *
8
+ */
9
+
10
+#include <stdint.h>
11
+#include <gpxe/list.h>
12
+
13
+struct block_device;
14
+
15
+/**
16
+ * @defgroup int13ops INT 13 operation codes
17
+ * @{
18
+ */
19
+
20
+/** Reset disk system */
21
+#define INT13_RESET			0x00
22
+/** Get status of last operation */
23
+#define INT13_GET_LAST_STATUS		0x01
24
+/** Read sectors */
25
+#define INT13_READ_SECTORS		0x02
26
+/** Write sectors */
27
+#define INT13_WRITE_SECTORS		0x03
28
+/** Get drive parameters */
29
+#define INT13_GET_PARAMETERS		0x08
30
+/** Extended read */
31
+#define INT13_EXTENDED_READ		0x42
32
+/** Extended write */
33
+#define INT13_EXTENDED_WRITE		0x43
34
+/** Get extended drive parameters */
35
+#define INT13_GET_EXTENDED_PARAMETERS	0x48
36
+
37
+/** @} */
38
+
39
+/**
40
+ * @defgroup int13status INT 13 status codes
41
+ * @{
42
+ */
43
+
44
+/** Operation completed successfully */
45
+#define INT13_STATUS_SUCCESS		0x00
46
+/** Invalid function or parameter */
47
+#define INT13_STATUS_INVALID		0x01
48
+/** Read error */
49
+#define INT13_STATUS_READ_ERROR		0x04
50
+/** Write error */
51
+#define INT13_STATUS_WRITE_ERROR	0xcc
52
+
53
+/** @} */
54
+
55
+/** Block size for non-extended INT 13 calls */
56
+#define INT13_BLKSIZE 512
57
+
58
+/** An INT 13 emulated drive */
59
+struct int13_drive {
60
+	/** List of all registered drives */
61
+	struct list_head list;
62
+
63
+	/** Underlying block device */
64
+	struct block_device *blockdev;
65
+
66
+	/** BIOS drive number (0x80-0xff) */
67
+	unsigned int drive;
68
+	/** Number of cylinders
69
+	 *
70
+	 * The cylinder number field in an INT 13 call is ten bits
71
+	 * wide, giving a maximum of 1024 cylinders.  Conventionally,
72
+	 * when the 7.8GB limit of a CHS address is exceeded, it is
73
+	 * the number of cylinders that is increased beyond the
74
+	 * addressable limit.
75
+	 */
76
+	unsigned int cylinders;
77
+	/** Number of heads
78
+	 *
79
+	 * The head number field in an INT 13 call is eight bits wide,
80
+	 * giving a maximum of 256 heads.  However, apparently all
81
+	 * versions of MS-DOS up to and including Win95 fail with 256
82
+	 * heads, so the maximum encountered in practice is 255.
83
+	 */
84
+	unsigned int heads;
85
+	/** Number of sectors per track
86
+	 *
87
+	 * The sector number field in an INT 13 call is six bits wide,
88
+	 * giving a maximum of 63 sectors, since sector numbering
89
+	 * (unlike head and cylinder numbering) starts at 1, not 0.
90
+	 */
91
+	unsigned int sectors_per_track;
92
+
93
+	/** Status of last operation */
94
+	int last_status;
95
+};
96
+
97
+/** An INT 13 disk address packet */
98
+struct int13_disk_address {
99
+	/** Size of the packet, in bytes */
100
+	uint8_t bufsize;
101
+	/** Reserved, must be zero */
102
+	uint8_t reserved;
103
+	/** Block count */
104
+	uint16_t count;
105
+	/** Data buffer */
106
+	struct segoff buffer;
107
+	/** Starting block number */
108
+	uint64_t lba;
109
+	/** Data buffer (EDD-3.0 only) */
110
+	uint64_t buffer_phys;
111
+};
112
+
113
+/** INT 13 disk parameters */
114
+struct int13_disk_parameters {
115
+	/** Size of this structure */
116
+	uint16_t bufsize;
117
+	/** Flags */
118
+	uint16_t flags;
119
+	/** Number of cylinders */
120
+	uint32_t cylinders;
121
+	/** Number of heads */
122
+	uint32_t heads;
123
+	/** Number of sectors per track */
124
+	uint32_t sectors_per_track;
125
+	/** Total number of sectors on drive */
126
+	uint64_t sectors;
127
+	/** Bytes per sector */
128
+	uint16_t sector_size;
129
+	
130
+};
131
+
132
+/**
133
+ * @defgroup int13flags INT 13 disk parameter flags
134
+ * @{
135
+ */
136
+
137
+/** DMA boundary errors handled transparently */
138
+#define INT13_FL_DMA_TRANSPARENT 0x01
139
+/** CHS information is valid */
140
+#define INT13_FL_CHS_VALID	 0x02
141
+/** Removable drive */
142
+#define INT13_FL_REMOVABLE	 0x04
143
+/** Write with verify supported */
144
+#define INT13_FL_VERIFIABLE	 0x08
145
+/** Has change-line supported (valid only for removable drives) */
146
+#define INT13_FL_CHANGE_LINE	 0x10
147
+/** Drive can be locked (valid only for removable drives) */
148
+#define INT13_FL_LOCKABLE	 0x20
149
+/** CHS is max possible, not current media (valid only for removable drives) */
150
+#define INT13_FL_CHS_MAX	 0x40
151
+
152
+/** @} */
153
+
154
+extern void register_int13_drive ( struct int13_drive *drive );
155
+extern void unregister_int13_drive ( struct int13_drive *drive );
156
+
157
+#endif /* INT13_H */

+ 475
- 0
src/arch/i386/interface/pcbios/int13.c Просмотреть файл

@@ -0,0 +1,475 @@
1
+/*
2
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
3
+ *
4
+ * This program is free software; you can redistribute it and/or
5
+ * modify it under the terms of the GNU General Public License as
6
+ * published by the Free Software Foundation; either version 2 of the
7
+ * License, or any later version.
8
+ *
9
+ * This program is distributed in the hope that it will be useful, but
10
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
+ * General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with this program; if not, write to the Free Software
16
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
+ */
18
+
19
+#include <limits.h>
20
+#include <assert.h>
21
+#include <gpxe/list.h>
22
+#include <gpxe/blockdev.h>
23
+#include <realmode.h>
24
+#include <bios.h>
25
+#include <biosint.h>
26
+#include <int13.h>
27
+
28
+/** @file
29
+ *
30
+ * INT 13 emulation
31
+ *
32
+ * This module provides a mechanism for exporting block devices via
33
+ * the BIOS INT 13 disk interrupt interface.  
34
+ *
35
+ */
36
+
37
+/** Vector for chaining to other INT 13 handlers */
38
+static struct segoff __text16 ( int13_vector );
39
+#define int13_vector __use_text16 ( int13_vector )
40
+
41
+/** Assembly wrapper */
42
+extern void int13_wrapper ( void );
43
+
44
+/** List of registered emulated drives */
45
+static LIST_HEAD ( drives );
46
+
47
+/**
48
+ * Convert CHS address to linear address
49
+ *
50
+ * @v drive		Emulated drive
51
+ * @v ch		Low bits of cylinder number
52
+ * @v cl (bits 7:6)	High bits of cylinder number
53
+ * @v cl (bits 5:0)	Sector number
54
+ * @v dh		Head number
55
+ * @ret lba		LBA address
56
+ * 
57
+ */
58
+static unsigned long chs_to_lba ( struct int13_drive *drive,
59
+				  struct i386_all_regs *ix86 ) {
60
+	unsigned int cylinder;
61
+	unsigned int head;
62
+	unsigned int sector;
63
+	unsigned long lba;
64
+
65
+	cylinder = ( ( ( ix86->regs.cl & 0xc0 ) << 8 ) | ix86->regs.ch );
66
+	head = ix86->regs.dh;
67
+	sector = ( ix86->regs.cl & 0x3f );
68
+
69
+	assert ( cylinder < drive->cylinders );
70
+	assert ( head < drive->heads );
71
+	assert ( ( sector >= 1 ) && ( sector <= drive->sectors_per_track ) );
72
+
73
+	lba = ( ( ( ( cylinder * drive->heads ) + head )
74
+		  * drive->sectors_per_track ) + sector - 1 );
75
+
76
+	DBG ( "C/H/S address %x/%x/%x -> LBA %x\n",
77
+	      cylinder, head, sector, lba );
78
+
79
+	return lba;
80
+}
81
+
82
+/**
83
+ * Read from drive to real-mode data buffer
84
+ *
85
+ * @v drive		Emulated drive
86
+ * @v lba		LBA starting sector number
87
+ * @v data		Data buffer
88
+ * @v count		Number of sectors to read
89
+ * @ret status		Status code
90
+ */
91
+static int int13_read ( struct int13_drive *drive, uint64_t lba,
92
+			struct segoff data, unsigned long count ) {
93
+	struct block_device *blockdev = drive->blockdev;
94
+	size_t blksize = blockdev->blksize;
95
+	uint8_t buffer[blksize];
96
+	int rc;
97
+
98
+	DBG ( "Read %lx sectors from %llx to %04x:%04x\n", count,
99
+	      ( unsigned long long ) lba, data.segment, data.offset );
100
+	while ( count-- ) {
101
+		if ( ( rc = blockdev->read ( blockdev, lba, buffer ) ) != 0 )
102
+			return INT13_STATUS_READ_ERROR;
103
+		copy_to_real ( data.segment, data.offset, buffer, blksize );
104
+		data.offset += blksize;
105
+		lba++;
106
+	}
107
+	return 0;
108
+}
109
+
110
+/**
111
+ * Write from real-mode data buffer to drive
112
+ *
113
+ * @v drive		Emulated drive
114
+ * @v lba		LBA starting sector number
115
+ * @v data		Data buffer
116
+ * @v count		Number of sectors to read
117
+ * @ret status		Status code
118
+ */
119
+static int int13_write ( struct int13_drive *drive, uint64_t lba,
120
+			 struct segoff data, unsigned long count ) {
121
+	struct block_device *blockdev = drive->blockdev;
122
+	size_t blksize = blockdev->blksize;
123
+	uint8_t buffer[blksize];
124
+	int rc;
125
+
126
+	DBG ( "Write %lx sectors from %04x:%04x to %llx\n", count,
127
+	      data.segment, data.offset, ( unsigned long long ) lba );
128
+	while ( count-- ) {
129
+		copy_from_real ( buffer, data.segment, data.offset, blksize );
130
+		if ( ( rc = blockdev->write ( blockdev, lba, buffer ) ) != 0 )
131
+			return INT13_STATUS_WRITE_ERROR;
132
+		data.offset += blksize;
133
+		lba++;
134
+	}
135
+	return 0;
136
+}
137
+
138
+/**
139
+ * INT 13, 00 - Reset disk system
140
+ *
141
+ * @v drive		Emulated drive
142
+ * @ret status		Status code
143
+ */
144
+static int int13_reset ( struct int13_drive *drive __unused,
145
+			 struct i386_all_regs *ix86 __unused ) {
146
+	DBG ( "Reset drive\n" );
147
+	return 0;
148
+}
149
+
150
+/**
151
+ * INT 13, 01 - Get status of last operation
152
+ *
153
+ * @v drive		Emulated drive
154
+ * @ret status		Status code
155
+ */
156
+static int int13_get_last_status ( struct int13_drive *drive,
157
+				   struct i386_all_regs *ix86 __unused ) {
158
+	DBG ( "Get status of last operation\n" );
159
+	return drive->last_status;
160
+}
161
+
162
+/**
163
+ * INT 13, 02 - Read sectors
164
+ *
165
+ * @v drive		Emulated drive
166
+ * @v al		Number of sectors to read (must be nonzero)
167
+ * @v ch		Low bits of cylinder number
168
+ * @v cl (bits 7:6)	High bits of cylinder number
169
+ * @v cl (bits 5:0)	Sector number
170
+ * @v dh		Head number
171
+ * @v es:bx		Data buffer
172
+ * @ret status		Status code
173
+ * @ret al		Number of sectors read
174
+ */
175
+static int int13_read_sectors ( struct int13_drive *drive,
176
+				struct i386_all_regs *ix86 ) {
177
+	unsigned long lba = chs_to_lba ( drive, ix86 );
178
+	unsigned int count = ix86->regs.al;
179
+	struct segoff data = {
180
+		.segment = ix86->segs.es,
181
+		.offset = ix86->regs.bx,
182
+	};
183
+
184
+	if ( drive->blockdev->blksize != INT13_BLKSIZE ) {
185
+		DBG ( "Invalid blocksize (%d) for non-extended read\n",
186
+		      drive->blockdev->blksize );
187
+		return INT13_STATUS_INVALID;
188
+	}
189
+
190
+	return int13_read ( drive, lba, data, count );
191
+}
192
+
193
+/**
194
+ * INT 13, 03 - Write sectors
195
+ *
196
+ * @v drive		Emulated drive
197
+ * @v al		Number of sectors to write (must be nonzero)
198
+ * @v ch		Low bits of cylinder number
199
+ * @v cl (bits 7:6)	High bits of cylinder number
200
+ * @v cl (bits 5:0)	Sector number
201
+ * @v dh		Head number
202
+ * @v es:bx		Data buffer
203
+ * @ret status		Status code
204
+ * @ret al		Number of sectors written
205
+ */
206
+static int int13_write_sectors ( struct int13_drive *drive,
207
+				 struct i386_all_regs *ix86 ) {
208
+	unsigned long lba = chs_to_lba ( drive, ix86 );
209
+	unsigned int count = ix86->regs.al;
210
+	struct segoff data = {
211
+		.segment = ix86->segs.es,
212
+		.offset = ix86->regs.bx,
213
+	};
214
+
215
+	if ( drive->blockdev->blksize != INT13_BLKSIZE ) {
216
+		DBG ( "Invalid blocksize (%d) for non-extended write\n",
217
+		      drive->blockdev->blksize );
218
+		return INT13_STATUS_INVALID;
219
+	}
220
+
221
+	return int13_write ( drive, lba, data, count );
222
+}
223
+
224
+/**
225
+ * INT 13, 08 - Get drive parameters
226
+ *
227
+ * @v drive		Emulated drive
228
+ * @ret status		Status code
229
+ * @ret ch		Low bits of maximum cylinder number
230
+ * @ret cl (bits 7:6)	High bits of maximum cylinder number
231
+ * @ret cl (bits 5:0)	Maximum sector number
232
+ * @ret dh		Maximum head number
233
+ * @ret dl		Number of drives
234
+ */
235
+static int int13_get_parameters ( struct int13_drive *drive,
236
+				  struct i386_all_regs *ix86 ) {
237
+	unsigned int max_cylinder = drive->cylinders - 1;
238
+	unsigned int max_head = drive->heads - 1;
239
+	unsigned int max_sector = drive->sectors_per_track; /* sic */
240
+
241
+	DBG ( "Get drive parameters\n" );
242
+
243
+	ix86->regs.ch = ( max_cylinder & 0xff );
244
+	ix86->regs.cl = ( ( ( max_cylinder >> 8 ) << 6 ) | max_sector );
245
+	ix86->regs.dh = max_head;
246
+	get_real ( ix86->regs.dl, BDA_SEG, BDA_NUM_DRIVES );
247
+	return 0;
248
+}
249
+
250
+/**
251
+ * INT 13, 42 - Extended read
252
+ *
253
+ * @v drive		Emulated drive
254
+ * @v ds:si		Disk address packet
255
+ * @ret status		Status code
256
+ */
257
+static int int13_extended_read ( struct int13_drive *drive,
258
+				 struct i386_all_regs *ix86 ) {
259
+	struct int13_disk_address addr;
260
+
261
+	copy_from_real ( &addr, ix86->segs.ds, ix86->regs.si,
262
+			 sizeof ( addr ) );
263
+	return int13_read ( drive, addr.lba, addr.buffer, addr.count );
264
+}
265
+
266
+/**
267
+ * INT 13, 43 - Extended write
268
+ *
269
+ * @v drive		Emulated drive
270
+ * @v ds:si		Disk address packet
271
+ * @ret status		Status code
272
+ */
273
+static int int13_extended_write ( struct int13_drive *drive,
274
+				  struct i386_all_regs *ix86 ) {
275
+	struct int13_disk_address addr;
276
+
277
+	copy_from_real ( &addr, ix86->segs.ds, ix86->regs.si,
278
+			 sizeof ( addr ) );
279
+	return int13_write ( drive, addr.lba, addr.buffer, addr.count );
280
+}
281
+
282
+/**
283
+ * INT 13, 48 - Get extended parameters
284
+ *
285
+ * @v drive		Emulated drive
286
+ * @v ds:si		Drive parameter table
287
+ * @ret status		Status code
288
+ */
289
+static int int13_get_extended_parameters ( struct int13_drive *drive,
290
+					   struct i386_all_regs *ix86 ) {
291
+	struct int13_disk_parameters params = {
292
+		.bufsize = sizeof ( params ),
293
+		.flags = INT13_FL_DMA_TRANSPARENT,
294
+		.cylinders = drive->cylinders,
295
+		.heads = drive->heads,
296
+		.sectors_per_track = drive->sectors_per_track,
297
+		.sectors = drive->blockdev->blocks,
298
+		.sector_size = drive->blockdev->blksize,
299
+	};
300
+	
301
+	DBG ( "Get extended drive parameters to %04x:%04x\n",
302
+	      ix86->segs.ds, ix86->regs.si );
303
+
304
+	copy_to_real ( ix86->segs.ds, ix86->regs.si, &params,
305
+		       sizeof ( params ) );
306
+	return 0;
307
+}
308
+
309
+/**
310
+ * INT 13 handler
311
+ *
312
+ */
313
+static void int13 ( struct i386_all_regs *ix86 ) {
314
+	struct int13_drive *drive;
315
+	int status;
316
+
317
+	list_for_each_entry ( drive, &drives, list ) {
318
+		if ( drive->drive != ix86->regs.dl )
319
+			continue;
320
+		
321
+		DBG ( "INT 13, %02x on drive %02x\n", ix86->regs.ah,
322
+		      ix86->regs.dl );
323
+	
324
+		switch ( ix86->regs.ah ) {
325
+		case INT13_RESET:
326
+			status = int13_reset ( drive, ix86 );
327
+			break;
328
+		case INT13_GET_LAST_STATUS:
329
+			status = int13_get_last_status ( drive, ix86 );
330
+			break;
331
+		case INT13_READ_SECTORS:
332
+			status = int13_read_sectors ( drive, ix86 );
333
+			break;
334
+		case INT13_WRITE_SECTORS:
335
+			status = int13_write_sectors ( drive, ix86 );
336
+			break;
337
+		case INT13_GET_PARAMETERS:
338
+			status = int13_get_parameters ( drive, ix86 );
339
+			break;
340
+		case INT13_EXTENDED_READ:
341
+			status = int13_extended_read ( drive, ix86 );
342
+			break;
343
+		case INT13_EXTENDED_WRITE:
344
+			status = int13_extended_write ( drive, ix86 );
345
+			break;
346
+		case INT13_GET_EXTENDED_PARAMETERS:
347
+			status = int13_get_extended_parameters ( drive, ix86 );
348
+			break;
349
+		default:
350
+			DBG ( "Unrecognised INT 13\n" );
351
+			status = INT13_STATUS_INVALID;
352
+			break;
353
+		}
354
+
355
+		/* Store status for INT 13,01 */
356
+		drive->last_status = status;
357
+		/* All functions return status via %ah and CF */
358
+		ix86->regs.ah = status;
359
+		if ( status ) {
360
+			ix86->flags |= CF;
361
+			DBG ( "INT13 failed with status %x\n", status );
362
+		}
363
+		/* Set OF to indicate to wrapper not to chain this call */
364
+		ix86->flags |= OF;
365
+	}
366
+}
367
+
368
+/**
369
+ * Hook INT 13 handler
370
+ *
371
+ */
372
+static void hook_int13 ( void ) {
373
+	/* Assembly wrapper to call int13().  int13() sets OF if we
374
+	 * should not chain to the previous handler.  (The wrapper
375
+	 * clears CF and OF before calling int13()).
376
+	 */
377
+	__asm__  __volatile__ ( ".section \".text16\", \"ax\", @progbits\n\t"
378
+				".code16\n\t"
379
+				"\nint13_wrapper:\n\t"
380
+				"orb $0, %%al\n\t" /* clear CF and OF */
381
+				"pushl %0\n\t" /* call int13() */
382
+				"data32 call prot_call\n\t"
383
+				"jo 1f\n\t" /* chain if OF not set */
384
+				"pushfw\n\t"
385
+				"lcall *%%cs:int13_vector\n\t"
386
+				"\n1:\n\t"
387
+				"call 2f\n\t" /* return with flags intact */
388
+				"lret $2\n\t"
389
+				"\n2:\n\t"
390
+				"ret $4\n\t"
391
+				".previous\n\t"
392
+				".code32\n\t" : :
393
+				"i" ( int13 ) );
394
+
395
+	hook_bios_interrupt ( 0x13, ( unsigned int ) int13_wrapper,
396
+			      &int13_vector );
397
+}
398
+
399
+/**
400
+ * Unhook INT 13 handler
401
+ */
402
+static void unhook_int13 ( void ) {
403
+	unhook_bios_interrupt ( 0x13, ( unsigned int ) int13_wrapper,
404
+				&int13_vector );
405
+}
406
+
407
+/**
408
+ * Register INT 13 emulated drive
409
+ *
410
+ * @v drive		Emulated drive
411
+ *
412
+ * Registers the drive with the INT 13 emulation subsystem, and hooks
413
+ * the INT 13 interrupt vector (if not already hooked).
414
+ *
415
+ * The underlying block device must be valid.  A drive number and
416
+ * geometry will be assigned if left blank.
417
+ */
418
+void register_int13_drive ( struct int13_drive *drive ) {
419
+	uint8_t num_drives;
420
+	unsigned long blocks;
421
+	unsigned long blocks_per_cyl;
422
+
423
+	/* Give drive a default geometry if none specified */
424
+	if ( ! drive->heads )
425
+		drive->heads = 255;
426
+	if ( ! drive->sectors_per_track )
427
+		drive->sectors_per_track = 63;
428
+	if ( ! drive->cylinders ) {
429
+		/* Avoid attempting a 64-bit divide on a 32-bit system */
430
+		blocks = ( ( drive->blockdev->blocks <= ULONG_MAX ) ?
431
+			   drive->blockdev->blocks : ULONG_MAX );
432
+		blocks_per_cyl = ( drive->heads * drive->sectors_per_track );
433
+		assert ( blocks_per_cyl != 0 );
434
+		drive->cylinders = ( blocks / blocks_per_cyl );
435
+	}
436
+
437
+	/* Assign drive number if none specified, update BIOS drive count */
438
+	get_real ( num_drives, BDA_SEG, BDA_NUM_DRIVES );
439
+	if ( ! drive->drive )
440
+		drive->drive = ( num_drives | 0x80 );
441
+	if ( num_drives <= ( drive->drive & 0x7f ) )
442
+		num_drives = ( ( drive->drive & 0x7f ) + 1 );
443
+	put_real ( num_drives, BDA_SEG, BDA_NUM_DRIVES );
444
+
445
+	DBG ( "Registered INT13 drive %02x with C/H/S geometry %d/%d/%d\n",
446
+	      drive->drive, drive->cylinders, drive->heads,
447
+	      drive->sectors_per_track );
448
+
449
+	/* Hook INT 13 vector if not already hooked */
450
+	if ( list_empty ( &drives ) )
451
+		hook_int13();
452
+
453
+	/* Add to list of emulated drives */
454
+	list_add ( &drive->list, &drives );
455
+}
456
+
457
+/**
458
+ * Unregister INT 13 emulated drive
459
+ *
460
+ * @v drive		Emulated drive
461
+ *
462
+ * Unregisters the drive from the INT 13 emulation subsystem.  If this
463
+ * is the last emulated drive, the INT 13 vector is unhooked (if
464
+ * possible).
465
+ */
466
+void unregister_int13_drive ( struct int13_drive *drive ) {
467
+	/* Remove from list of emulated drives */
468
+	list_del ( &drive->list );
469
+
470
+	DBG ( "Unregistered INT13 drive %02x\n", drive->drive );
471
+
472
+	/* Unhook INT 13 vector if no more drives */
473
+	if ( list_empty ( &drives ) )
474
+		unhook_int13();
475
+}

Загрузка…
Отмена
Сохранить