9d911cc6568e2444865c99b9fe4435a0fb799da5
[people/sha0/winvblock.git] / src / winvblock / disk / disk.c
1 /**
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/
5  *
6  * This file is part of WinVBlock, derived from WinAoE.
7  *
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.
12  *
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.
17  *
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/>.
20  */
21
22 /**
23  * @file
24  *
25  * Disk device specifics.
26  */
27
28 #include <ntddk.h>
29
30 #include "winvblock.h"
31 #include "wv_stdlib.h"
32 #include "portable.h"
33 #include "irp.h"
34 #include "driver.h"
35 #include "device.h"
36 #include "disk.h"
37 #include "disk_pnp.h"
38 #include "disk_dev_ctl.h"
39 #include "disk_scsi.h"
40 #include "debug.h"
41
42 #ifndef _MSC_VER
43 static long long
44 __divdi3 (
45   long long u,
46   long long v
47  )
48 {
49   return u / v;
50 }
51 #endif
52
53 /* Globals. */
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" };
60
61 /* Forward declarations. */
62 static device__free_func free_disk;
63
64 static
65 disk__max_xfer_len_decl (
66   default_max_xfer_len
67  )
68 {
69   return 1024 * 1024;
70 }
71
72 static
73 device__init_decl (
74   init
75  )
76 {
77   disk__type_ptr disk_ptr = disk__get_ptr ( dev_ptr );
78   return disk_ptr->disk_ops.init ( disk_ptr );
79 }
80
81 disk__init_decl ( default_init )
82 {
83   return TRUE;
84 }
85
86 static
87 device__close_decl (
88   close
89  )
90 {
91   disk__type_ptr disk_ptr = disk__get_ptr ( dev_ptr );
92   disk_ptr->disk_ops.close ( disk_ptr );
93   return;
94 }
95
96 disk__close_decl ( default_close )
97 {
98   return;
99 }
100
101 static NTSTATUS STDCALL power(
102     IN PDEVICE_OBJECT DeviceObject,
103     IN PIRP Irp,
104     IN PIO_STACK_LOCATION Stack,
105     IN struct _device__type * dev_ptr,
106     OUT winvblock__bool_ptr completion_ptr
107   )
108   {
109     PoStartNextPowerIrp ( Irp );
110     Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
111     IoCompleteRequest ( Irp, IO_NO_INCREMENT );
112     *completion_ptr = TRUE;
113     return STATUS_NOT_SUPPORTED;
114   }
115
116 static NTSTATUS STDCALL sys_ctl(
117     IN PDEVICE_OBJECT DeviceObject,
118     IN PIRP Irp,
119     IN PIO_STACK_LOCATION Stack,
120     IN struct _device__type * dev_ptr,
121     OUT winvblock__bool_ptr completion_ptr
122   )
123 {
124   NTSTATUS status = Irp->IoStatus.Status;
125   IoCompleteRequest ( Irp, IO_NO_INCREMENT );
126   *completion_ptr = TRUE;
127   return status;
128 }
129
130 /**
131  * Create a disk PDO filled with the given disk parameters.
132  *
133  * @v dev_ptr           Populate PDO dev. ext. space from these details.
134  * @ret dev_obj_ptr     Points to the new PDO, or is NULL upon failure.
135  *
136  * Returns a Physical Device Object pointer on success, NULL for failure.
137  */
138 static
139 device__create_pdo_decl (
140   create_pdo
141  )
142 {
143   /**
144    * @v disk_ptr          Used for pointing to disk details
145    * @v status            Status of last operation
146    * @v dev_obj_ptr       The new node's physical device object (PDO)
147    * @v new_ext_size      The extension space size
148    * @v disk_types[]      Floppy, hard disk, optical disc specifics
149    * @v characteristics[] Floppy, hard disk, optical disc specifics
150    */
151   disk__type_ptr disk_ptr;
152   NTSTATUS status;
153   PDEVICE_OBJECT dev_obj_ptr;
154   static DEVICE_TYPE disk_types[disk__media_count] =
155     { FILE_DEVICE_DISK, FILE_DEVICE_DISK, FILE_DEVICE_CD_ROM };
156   static winvblock__uint32 characteristics[disk__media_count] =
157     { FILE_REMOVABLE_MEDIA | FILE_FLOPPY_DISKETTE, 0,
158     FILE_REMOVABLE_MEDIA | FILE_READ_ONLY_DEVICE
159   };
160
161   DBG ( "Entry\n" );
162   /*
163    * Point to the disk details provided
164    */
165   disk_ptr = disk__get_ptr ( dev_ptr );
166   /*
167    * Create the disk PDO
168    */
169   status =
170     IoCreateDevice ( driver__obj_ptr, sizeof ( driver__dev_ext ), NULL,
171                      disk_types[disk_ptr->media],
172                      FILE_AUTOGENERATED_DEVICE_NAME | FILE_DEVICE_SECURE_OPEN |
173                      characteristics[disk_ptr->media], FALSE, &dev_obj_ptr );
174   if ( !NT_SUCCESS ( status ) )
175     {
176       Error ( "IoCreateDevice", status );
177       return NULL;
178     }
179   /*
180    * Set associations for the PDO, device, disk
181    */
182   ( ( driver__dev_ext_ptr ) dev_obj_ptr->DeviceExtension )->device = dev_ptr;
183   dev_ptr->Self = dev_obj_ptr;
184   KeInitializeEvent ( &disk_ptr->SearchEvent, SynchronizationEvent, FALSE );
185   KeInitializeSpinLock ( &disk_ptr->SpinLock );
186   disk_ptr->DiskNumber = InterlockedIncrement ( &next_disk ) - 1;
187   /*
188    * Some device parameters
189    */
190   dev_obj_ptr->Flags |= DO_DIRECT_IO;   /* FIXME? */
191   dev_obj_ptr->Flags |= DO_POWER_INRUSH;        /* FIXME? */
192   DBG ( "Exit\n" );
193   return dev_obj_ptr;
194 }
195
196 /*
197  * fat_extra and fat_super taken from syslinux/memdisk/setup.c by
198  * H. Peter Anvin.  Licensed under the terms of the GNU General Public
199  * License version 2 or later.
200  */
201 #ifdef _MSC_VER
202 #  pragma pack(1)
203 #endif
204 winvblock__def_struct ( fat_extra )
205 {
206   winvblock__uint8 bs_drvnum;
207   winvblock__uint8 bs_resv1;
208   winvblock__uint8 bs_bootsig;
209   winvblock__uint32 bs_volid;
210   char bs_vollab[11];
211   char bs_filsystype[8];
212 } __attribute__ ( ( packed ) );
213
214 winvblock__def_struct ( fat_super )
215 {
216   winvblock__uint8 bs_jmpboot[3];
217   char bs_oemname[8];
218   winvblock__uint16 bpb_bytspersec;
219   winvblock__uint8 bpb_secperclus;
220   winvblock__uint16 bpb_rsvdseccnt;
221   winvblock__uint8 bpb_numfats;
222   winvblock__uint16 bpb_rootentcnt;
223   winvblock__uint16 bpb_totsec16;
224   winvblock__uint8 bpb_media;
225   winvblock__uint16 bpb_fatsz16;
226   winvblock__uint16 bpb_secpertrk;
227   winvblock__uint16 bpb_numheads;
228   winvblock__uint32 bpb_hiddsec;
229   winvblock__uint32 bpb_totsec32;
230   union
231   {
232     struct
233     {
234       fat_extra extra;
235     } fat16;
236     struct
237     {
238       winvblock__uint32 bpb_fatsz32;
239       winvblock__uint16 bpb_extflags;
240       winvblock__uint16 bpb_fsver;
241       winvblock__uint32 bpb_rootclus;
242       winvblock__uint16 bpb_fsinfo;
243       winvblock__uint16 bpb_bkbootsec;
244       char bpb_reserved[12];
245       /*
246        * Clever, eh?  Same fields, different offset... 
247        */
248       fat_extra extra;
249     } fat32 __attribute__ ( ( packed ) );
250   } x;
251 } __attribute__ ( ( __packed__ ) );
252
253 #ifdef _MSC_VER
254 #  pragma pack()
255 #endif
256
257 /**
258  * Attempt to guess a disk's geometry
259  *
260  * @v boot_sect_ptr     The MBR or VBR with possible geometry clues
261  * @v disk_ptr          The disk to set the geometry for
262  */
263 winvblock__lib_func void
264 disk__guess_geometry (
265   IN disk__boot_sect_ptr boot_sect_ptr,
266   IN OUT disk__type_ptr disk_ptr
267  )
268 {
269   winvblock__uint16 heads = 0,
270     sects_per_track = 0,
271     cylinders;
272   mbr_ptr as_mbr;
273
274   if ( ( boot_sect_ptr == NULL ) || ( disk_ptr == NULL ) )
275     return;
276
277   /*
278    * FAT superblock geometry checking taken from syslinux/memdisk/setup.c by
279    * H. Peter Anvin.  Licensed under the terms of the GNU General Public
280    * License version 2 or later.
281    */
282   {
283     /*
284      * Look for a FAT superblock and if we find something that looks
285      * enough like one, use geometry from that.  This takes care of
286      * megafloppy images and unpartitioned hard disks. 
287      */
288     fat_extra_ptr extra = NULL;
289     fat_super_ptr fs = ( fat_super_ptr ) boot_sect_ptr;
290
291     if ( ( fs->bpb_media == 0xf0 || fs->bpb_media >= 0xf8 )
292          && ( fs->bs_jmpboot[0] == 0xe9 || fs->bs_jmpboot[0] == 0xeb )
293          && fs->bpb_bytspersec == 512 && fs->bpb_numheads >= 1
294          && fs->bpb_numheads <= 256 && fs->bpb_secpertrk >= 1
295          && fs->bpb_secpertrk <= 63 )
296       {
297         extra = fs->bpb_fatsz16 ? &fs->x.fat16.extra : &fs->x.fat32.extra;
298         if ( !
299              ( extra->bs_bootsig == 0x29 && extra->bs_filsystype[0] == 'F'
300                && extra->bs_filsystype[1] == 'A'
301                && extra->bs_filsystype[2] == 'T' ) )
302           extra = NULL;
303       }
304     if ( extra )
305       {
306         heads = fs->bpb_numheads;
307         sects_per_track = fs->bpb_secpertrk;
308       }
309   }
310   /*
311    * If we couldn't parse a FAT superblock, try checking MBR params.
312    * Logic derived from syslinux/memdisk/setup.c by H. Peter Anvin
313    */
314   as_mbr = ( mbr_ptr ) boot_sect_ptr;
315   if ( ( heads == 0 ) && ( sects_per_track == 0 )
316        && ( as_mbr->mbr_sig == 0xAA55 ) )
317     {
318       int i;
319       for ( i = 0; i < 4; i++ )
320         {
321           if ( !( as_mbr->partition[i].status & 0x7f )
322                && as_mbr->partition[i].type )
323             {
324               winvblock__uint8 h,
325                s;
326
327               h = chs_head ( as_mbr->partition[i].chs_start ) + 1;
328               s = chs_sector ( as_mbr->partition[i].chs_start );
329
330               if ( heads < h )
331                 heads = h;
332               if ( sects_per_track < s )
333                 sects_per_track = s;
334
335               h = chs_head ( as_mbr->partition[i].chs_end ) + 1;
336               s = chs_sector ( as_mbr->partition[i].chs_end );
337
338               if ( heads < h )
339                 heads = h;
340               if ( sects_per_track < s )
341                 sects_per_track = s;
342             }
343         }
344     }
345   /*
346    * If we were unable to guess, use some hopeful defaults
347    */
348   if ( !heads )
349     heads = 255;
350   if ( !sects_per_track )
351     sects_per_track = 63;
352   /*
353    * Set params that are not already filled
354    */
355   if ( !disk_ptr->Heads )
356     disk_ptr->Heads = heads;
357   if ( !disk_ptr->Sectors )
358     disk_ptr->Sectors = sects_per_track;
359   if ( !disk_ptr->Cylinders )
360     disk_ptr->Cylinders = disk_ptr->LBADiskSize / ( heads * sects_per_track );
361 }
362
363 /* Disk dispatch routine. */
364 static NTSTATUS STDCALL (disk_dispatch)(
365     IN PDEVICE_OBJECT (dev),
366     IN PIRP (irp)
367   ) {
368     NTSTATUS (status);
369     winvblock__bool (completion) = FALSE;
370     static const irp__handling (handling_table)[] = {
371         /*
372          * Major, minor, any major?, any minor?, handler
373          * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
374          * Note that the fall-through case must come FIRST!
375          * Why? It sets completion to true, so others won't be called.
376          */
377         {                     0, 0,  TRUE,  TRUE,  driver__not_supported },
378         {          IRP_MJ_CLOSE, 0, FALSE,  TRUE,   driver__create_close },
379         {         IRP_MJ_CREATE, 0, FALSE,  TRUE,   driver__create_close },
380         { IRP_MJ_DEVICE_CONTROL, 0, FALSE,  TRUE, disk_dev_ctl__dispatch },
381         { IRP_MJ_SYSTEM_CONTROL, 0, FALSE,  TRUE,                sys_ctl },
382         {          IRP_MJ_POWER, 0, FALSE,  TRUE,                  power },
383         {           IRP_MJ_SCSI, 0, FALSE,  TRUE,    disk_scsi__dispatch },
384         {            IRP_MJ_PNP, 0, FALSE,  TRUE,       disk_pnp__simple },
385         {            IRP_MJ_PNP,
386          IRP_MN_QUERY_CAPABILITIES, FALSE, FALSE,
387                                             disk_pnp__query_capabilities },
388         {            IRP_MJ_PNP,
389       IRP_MN_QUERY_BUS_INFORMATION, FALSE, FALSE,
390                                                 disk_pnp__query_bus_info },
391         {            IRP_MJ_PNP,
392      IRP_MN_QUERY_DEVICE_RELATIONS, FALSE, FALSE,
393                                            disk_pnp__query_dev_relations },
394         {            IRP_MJ_PNP,
395           IRP_MN_QUERY_DEVICE_TEXT, FALSE, FALSE,
396                                                 disk_pnp__query_dev_text },
397         {            IRP_MJ_PNP,
398                    IRP_MN_QUERY_ID, FALSE, FALSE,     disk_pnp__query_id },
399       };
400
401     status = irp__process_with_table(
402         dev,
403         irp,
404         handling_table,
405         sizeof handling_table,
406         &completion
407       );
408     #ifdef DEBUGIRPS
409     if (status != STATUS_PENDING)
410       Debug_IrpEnd(irp, status);
411     #endif
412
413     return status;
414   }
415
416 /**
417  * Create a new disk
418  *
419  * @ret disk_ptr        The address of a new disk, or NULL for failure
420  *
421  * See the header file for additional details
422  */
423 winvblock__lib_func disk__type_ptr
424 disk__create (
425   void
426  )
427 {
428   device__type_ptr dev_ptr;
429   disk__type_ptr disk_ptr;
430
431   /*
432    * Try to create a device
433    */
434   dev_ptr = device__create (  );
435   if ( dev_ptr == NULL )
436     goto err_nodev;
437   /*
438    * Disk devices might be used for booting and should
439    * not be allocated from a paged memory pool
440    */
441   disk_ptr = wv_mallocz(sizeof *disk_ptr);
442   if ( disk_ptr == NULL )
443     goto err_nodisk;
444   /*
445    * Track the new disk in our global list
446    */
447   ExInterlockedInsertTailList ( &disk_list, &disk_ptr->tracking,
448                                 &disk_list_lock );
449   /*
450    * Populate non-zero device defaults
451    */
452   disk_ptr->device = dev_ptr;
453   disk_ptr->prev_free = dev_ptr->ops.free;
454   disk_ptr->disk_ops.max_xfer_len = default_max_xfer_len;
455   disk_ptr->disk_ops.init = default_init;
456   disk_ptr->disk_ops.close = default_close;
457   dev_ptr->dispatch = disk_dispatch;
458   dev_ptr->ops.close = close;
459   dev_ptr->ops.create_pdo = create_pdo;
460   dev_ptr->ops.free = free_disk;
461   dev_ptr->ops.init = init;
462   dev_ptr->ext = disk_ptr;
463   KeInitializeSpinLock ( &disk_ptr->SpinLock );
464
465   return disk_ptr;
466
467 err_nodisk:
468
469   device__free ( dev_ptr );
470 err_nodev:
471
472   return NULL;
473 }
474
475 /**
476  * Initialize the global, disk-common environment
477  *
478  * @ret ntstatus        STATUS_SUCCESS or the NTSTATUS for a failure
479  */
480 NTSTATUS
481 disk__init (
482   void
483  )
484 {
485   /*
486    * Initialize the global list of disks
487    */
488   InitializeListHead ( &disk_list );
489   KeInitializeSpinLock ( &disk_list_lock );
490
491   return STATUS_SUCCESS;
492 }
493
494 /**
495  * Default disk deletion operation.
496  *
497  * @v dev_ptr           Points to the disk device to delete.
498  */
499 static void STDCALL free_disk(IN device__type_ptr dev_ptr)
500   {
501     disk__type_ptr disk_ptr = disk__get_ptr(dev_ptr);
502     /* Free the "inherited class". */
503     disk_ptr->prev_free(dev_ptr);
504     /*
505      * Track the disk deletion in our global list.  Unfortunately,
506      * for now we have faith that a disk won't be deleted twice and
507      * result in a race condition.  Something to keep in mind...
508      */
509     ExInterlockedRemoveHeadList(disk_ptr->tracking.Blink, &disk_list_lock);
510   
511     wv_free(disk_ptr);
512   }
513
514 /* See header for details */
515 disk__io_decl ( disk__io )
516 {
517   disk__type_ptr disk_ptr;
518
519   /*
520    * Establish a pointer to the disk
521    */
522   disk_ptr = disk__get_ptr ( dev_ptr );
523
524   return disk_ptr->disk_ops.io ( dev_ptr, mode, start_sector, sector_count,
525                                  buffer, irp );
526 }
527
528 /* See header for details */
529 disk__max_xfer_len_decl ( disk__max_xfer_len )
530 {
531   return disk_ptr->disk_ops.max_xfer_len ( disk_ptr );
532 }
533
534 /* See header for details */
535 disk__pnp_id_decl ( disk__query_id )
536 {
537   return disk_ptr->disk_ops.pnp_id ( disk_ptr, query_type, buf_512 );
538 }