2 * Copyright (C) 2009-2010, Shao Miller <shao.miller@yrdsb.edu.on.ca>.
3 * Copyright 2006-2008, V.
4 * For WinAoE contact information, see http://winaoe.org/
6 * This file is part of WinVBlock, derived from WinAoE.
8 * WinVBlock is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
13 * WinVBlock is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with WinVBlock. If not, see <http://www.gnu.org/licenses/>.
25 * Disk device specifics.
30 #include "winvblock.h"
31 #include "wv_stdlib.h"
38 #include "disk_dev_ctl.h"
39 #include "disk_scsi.h"
54 static winvblock__uint32 next_disk = 0;
55 static LIST_ENTRY disk_list;
56 static KSPIN_LOCK disk_list_lock;
57 winvblock__bool disk__removable[disk__media_count] = { TRUE, FALSE, TRUE };
58 PWCHAR disk__compat_ids[disk__media_count] =
59 { L"GenSFloppy", L"GenDisk", L"GenCdRom" };
61 /* Forward declarations. */
62 static device__free_func free_disk;
65 disk__max_xfer_len_decl (
72 /* Initialize a disk. */
73 static winvblock__bool STDCALL disk__init_(IN struct device__type * dev) {
74 disk__type_ptr disk_ptr = disk__get_ptr(dev);
75 return disk_ptr->disk_ops.init(disk_ptr);
78 disk__init_decl ( default_init )
88 disk__type_ptr disk_ptr = disk__get_ptr ( dev_ptr );
89 disk_ptr->disk_ops.close ( disk_ptr );
93 disk__close_decl ( default_close )
98 static NTSTATUS STDCALL power(
99 IN PDEVICE_OBJECT DeviceObject,
101 IN PIO_STACK_LOCATION Stack,
102 IN struct device__type * dev_ptr,
103 OUT winvblock__bool_ptr completion_ptr
106 PoStartNextPowerIrp ( Irp );
107 Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
108 IoCompleteRequest ( Irp, IO_NO_INCREMENT );
109 *completion_ptr = TRUE;
110 return STATUS_NOT_SUPPORTED;
113 static NTSTATUS STDCALL sys_ctl(
114 IN PDEVICE_OBJECT DeviceObject,
116 IN PIO_STACK_LOCATION Stack,
117 IN struct device__type * dev_ptr,
118 OUT winvblock__bool_ptr completion_ptr
121 NTSTATUS status = Irp->IoStatus.Status;
122 IoCompleteRequest ( Irp, IO_NO_INCREMENT );
123 *completion_ptr = TRUE;
128 * Create a disk PDO filled with the given disk parameters.
130 * @v dev_ptr Populate PDO dev. ext. space from these details.
131 * @ret dev_obj_ptr Points to the new PDO, or is NULL upon failure.
133 * Returns a Physical Device Object pointer on success, NULL for failure.
135 static PDEVICE_OBJECT STDCALL create_pdo(IN struct device__type * dev_ptr) {
137 * @v disk_ptr Used for pointing to disk details
138 * @v status Status of last operation
139 * @v dev_obj_ptr The new node's physical device object (PDO)
140 * @v new_ext_size The extension space size
141 * @v disk_types[] Floppy, hard disk, optical disc specifics
142 * @v characteristics[] Floppy, hard disk, optical disc specifics
144 disk__type_ptr disk_ptr;
146 PDEVICE_OBJECT dev_obj_ptr;
147 static DEVICE_TYPE disk_types[disk__media_count] =
148 { FILE_DEVICE_DISK, FILE_DEVICE_DISK, FILE_DEVICE_CD_ROM };
149 static winvblock__uint32 characteristics[disk__media_count] =
150 { FILE_REMOVABLE_MEDIA | FILE_FLOPPY_DISKETTE, 0,
151 FILE_REMOVABLE_MEDIA | FILE_READ_ONLY_DEVICE
156 * Point to the disk details provided
158 disk_ptr = disk__get_ptr ( dev_ptr );
160 * Create the disk PDO
163 IoCreateDevice ( driver__obj_ptr, sizeof ( driver__dev_ext ), NULL,
164 disk_types[disk_ptr->media],
165 FILE_AUTOGENERATED_DEVICE_NAME | FILE_DEVICE_SECURE_OPEN |
166 characteristics[disk_ptr->media], FALSE, &dev_obj_ptr );
167 if ( !NT_SUCCESS ( status ) )
169 Error ( "IoCreateDevice", status );
173 * Set associations for the PDO, device, disk
175 device__set(dev_obj_ptr, dev_ptr);
176 dev_ptr->Self = dev_obj_ptr;
177 KeInitializeEvent ( &disk_ptr->SearchEvent, SynchronizationEvent, FALSE );
178 KeInitializeSpinLock ( &disk_ptr->SpinLock );
179 disk_ptr->DiskNumber = InterlockedIncrement ( &next_disk ) - 1;
181 * Some device parameters
183 dev_obj_ptr->Flags |= DO_DIRECT_IO; /* FIXME? */
184 dev_obj_ptr->Flags |= DO_POWER_INRUSH; /* FIXME? */
190 * fat_extra and fat_super taken from syslinux/memdisk/setup.c by
191 * H. Peter Anvin. Licensed under the terms of the GNU General Public
192 * License version 2 or later.
197 winvblock__def_struct ( fat_extra )
199 winvblock__uint8 bs_drvnum;
200 winvblock__uint8 bs_resv1;
201 winvblock__uint8 bs_bootsig;
202 winvblock__uint32 bs_volid;
204 char bs_filsystype[8];
205 } __attribute__ ( ( packed ) );
207 winvblock__def_struct ( fat_super )
209 winvblock__uint8 bs_jmpboot[3];
211 winvblock__uint16 bpb_bytspersec;
212 winvblock__uint8 bpb_secperclus;
213 winvblock__uint16 bpb_rsvdseccnt;
214 winvblock__uint8 bpb_numfats;
215 winvblock__uint16 bpb_rootentcnt;
216 winvblock__uint16 bpb_totsec16;
217 winvblock__uint8 bpb_media;
218 winvblock__uint16 bpb_fatsz16;
219 winvblock__uint16 bpb_secpertrk;
220 winvblock__uint16 bpb_numheads;
221 winvblock__uint32 bpb_hiddsec;
222 winvblock__uint32 bpb_totsec32;
231 winvblock__uint32 bpb_fatsz32;
232 winvblock__uint16 bpb_extflags;
233 winvblock__uint16 bpb_fsver;
234 winvblock__uint32 bpb_rootclus;
235 winvblock__uint16 bpb_fsinfo;
236 winvblock__uint16 bpb_bkbootsec;
237 char bpb_reserved[12];
239 * Clever, eh? Same fields, different offset...
242 } fat32 __attribute__ ( ( packed ) );
244 } __attribute__ ( ( __packed__ ) );
251 * Attempt to guess a disk's geometry
253 * @v boot_sect_ptr The MBR or VBR with possible geometry clues
254 * @v disk_ptr The disk to set the geometry for
256 winvblock__lib_func void
257 disk__guess_geometry (
258 IN disk__boot_sect_ptr boot_sect_ptr,
259 IN OUT disk__type_ptr disk_ptr
262 winvblock__uint16 heads = 0,
267 if ( ( boot_sect_ptr == NULL ) || ( disk_ptr == NULL ) )
271 * FAT superblock geometry checking taken from syslinux/memdisk/setup.c by
272 * H. Peter Anvin. Licensed under the terms of the GNU General Public
273 * License version 2 or later.
277 * Look for a FAT superblock and if we find something that looks
278 * enough like one, use geometry from that. This takes care of
279 * megafloppy images and unpartitioned hard disks.
281 fat_extra_ptr extra = NULL;
282 fat_super_ptr fs = ( fat_super_ptr ) boot_sect_ptr;
284 if ( ( fs->bpb_media == 0xf0 || fs->bpb_media >= 0xf8 )
285 && ( fs->bs_jmpboot[0] == 0xe9 || fs->bs_jmpboot[0] == 0xeb )
286 && fs->bpb_bytspersec == 512 && fs->bpb_numheads >= 1
287 && fs->bpb_numheads <= 256 && fs->bpb_secpertrk >= 1
288 && fs->bpb_secpertrk <= 63 )
290 extra = fs->bpb_fatsz16 ? &fs->x.fat16.extra : &fs->x.fat32.extra;
292 ( extra->bs_bootsig == 0x29 && extra->bs_filsystype[0] == 'F'
293 && extra->bs_filsystype[1] == 'A'
294 && extra->bs_filsystype[2] == 'T' ) )
299 heads = fs->bpb_numheads;
300 sects_per_track = fs->bpb_secpertrk;
304 * If we couldn't parse a FAT superblock, try checking MBR params.
305 * Logic derived from syslinux/memdisk/setup.c by H. Peter Anvin
307 as_mbr = ( mbr_ptr ) boot_sect_ptr;
308 if ( ( heads == 0 ) && ( sects_per_track == 0 )
309 && ( as_mbr->mbr_sig == 0xAA55 ) )
312 for ( i = 0; i < 4; i++ )
314 if ( !( as_mbr->partition[i].status & 0x7f )
315 && as_mbr->partition[i].type )
320 h = chs_head ( as_mbr->partition[i].chs_start ) + 1;
321 s = chs_sector ( as_mbr->partition[i].chs_start );
325 if ( sects_per_track < s )
328 h = chs_head ( as_mbr->partition[i].chs_end ) + 1;
329 s = chs_sector ( as_mbr->partition[i].chs_end );
333 if ( sects_per_track < s )
339 * If we were unable to guess, use some hopeful defaults
343 if ( !sects_per_track )
344 sects_per_track = 63;
346 * Set params that are not already filled
348 if ( !disk_ptr->Heads )
349 disk_ptr->Heads = heads;
350 if ( !disk_ptr->Sectors )
351 disk_ptr->Sectors = sects_per_track;
352 if ( !disk_ptr->Cylinders )
353 disk_ptr->Cylinders = disk_ptr->LBADiskSize / ( heads * sects_per_track );
356 /* Disk dispatch routine. */
357 static NTSTATUS STDCALL (disk_dispatch)(
358 IN PDEVICE_OBJECT (dev),
362 winvblock__bool (completion) = FALSE;
363 static const irp__handling (handling_table)[] = {
365 * Major, minor, any major?, any minor?, handler
366 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
367 * Note that the fall-through case must come FIRST!
368 * Why? It sets completion to true, so others won't be called.
370 { 0, 0, TRUE, TRUE, driver__not_supported },
371 { IRP_MJ_CLOSE, 0, FALSE, TRUE, driver__create_close },
372 { IRP_MJ_CREATE, 0, FALSE, TRUE, driver__create_close },
373 { IRP_MJ_DEVICE_CONTROL, 0, FALSE, TRUE, disk_dev_ctl__dispatch },
374 { IRP_MJ_SYSTEM_CONTROL, 0, FALSE, TRUE, sys_ctl },
375 { IRP_MJ_POWER, 0, FALSE, TRUE, power },
376 { IRP_MJ_SCSI, 0, FALSE, TRUE, disk_scsi__dispatch },
377 { IRP_MJ_PNP, 0, FALSE, TRUE, disk_pnp__simple },
379 IRP_MN_QUERY_CAPABILITIES, FALSE, FALSE,
380 disk_pnp__query_capabilities },
382 IRP_MN_QUERY_BUS_INFORMATION, FALSE, FALSE,
383 disk_pnp__query_bus_info },
385 IRP_MN_QUERY_DEVICE_RELATIONS, FALSE, FALSE,
386 disk_pnp__query_dev_relations },
388 IRP_MN_QUERY_DEVICE_TEXT, FALSE, FALSE,
389 disk_pnp__query_dev_text },
391 IRP_MN_QUERY_ID, FALSE, FALSE, disk_pnp__query_id },
394 status = irp__process_with_table(
398 sizeof handling_table,
402 if (status != STATUS_PENDING)
403 Debug_IrpEnd(irp, status);
412 * @ret disk_ptr The address of a new disk, or NULL for failure
414 * See the header file for additional details
416 winvblock__lib_func disk__type_ptr
421 struct device__type * dev_ptr;
422 disk__type_ptr disk_ptr;
425 * Try to create a device
427 dev_ptr = device__create ( );
428 if ( dev_ptr == NULL )
431 * Disk devices might be used for booting and should
432 * not be allocated from a paged memory pool
434 disk_ptr = wv_mallocz(sizeof *disk_ptr);
435 if ( disk_ptr == NULL )
438 * Track the new disk in our global list
440 ExInterlockedInsertTailList ( &disk_list, &disk_ptr->tracking,
443 * Populate non-zero device defaults
445 disk_ptr->device = dev_ptr;
446 disk_ptr->prev_free = dev_ptr->ops.free;
447 disk_ptr->disk_ops.max_xfer_len = default_max_xfer_len;
448 disk_ptr->disk_ops.init = default_init;
449 disk_ptr->disk_ops.close = default_close;
450 dev_ptr->dispatch = disk_dispatch;
451 dev_ptr->ops.close = close;
452 dev_ptr->ops.create_pdo = create_pdo;
453 dev_ptr->ops.free = free_disk;
454 dev_ptr->ops.init = disk__init_;
455 dev_ptr->ext = disk_ptr;
456 KeInitializeSpinLock ( &disk_ptr->SpinLock );
462 device__free ( dev_ptr );
469 * Initialize the global, disk-common environment
471 * @ret ntstatus STATUS_SUCCESS or the NTSTATUS for a failure
479 * Initialize the global list of disks
481 InitializeListHead ( &disk_list );
482 KeInitializeSpinLock ( &disk_list_lock );
484 return STATUS_SUCCESS;
488 * Default disk deletion operation.
490 * @v dev_ptr Points to the disk device to delete.
492 static void STDCALL free_disk(IN struct device__type * dev_ptr)
494 disk__type_ptr disk_ptr = disk__get_ptr(dev_ptr);
495 /* Free the "inherited class". */
496 disk_ptr->prev_free(dev_ptr);
498 * Track the disk deletion in our global list. Unfortunately,
499 * for now we have faith that a disk won't be deleted twice and
500 * result in a race condition. Something to keep in mind...
502 ExInterlockedRemoveHeadList(disk_ptr->tracking.Blink, &disk_list_lock);
507 /* See header for details */
508 disk__io_decl ( disk__io )
510 disk__type_ptr disk_ptr;
513 * Establish a pointer to the disk
515 disk_ptr = disk__get_ptr ( dev_ptr );
517 return disk_ptr->disk_ops.io ( dev_ptr, mode, start_sector, sector_count,
521 /* See header for details */
522 disk__max_xfer_len_decl ( disk__max_xfer_len )
524 return disk_ptr->disk_ops.max_xfer_len ( disk_ptr );
527 /* See header for details */
528 disk__pnp_id_decl ( disk__query_id )
530 return disk_ptr->disk_ops.pnp_id ( disk_ptr, query_type, buf_512 );