[disk] Remove prev_free member
[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 "driver.h"
34 #include "device.h"
35 #include "disk.h"
36 #include "debug.h"
37
38 #ifndef _MSC_VER
39 static long long
40 __divdi3 (
41   long long u,
42   long long v
43  )
44 {
45   return u / v;
46 }
47 #endif
48
49 /* IRP_MJ_DEVICE_CONTROL dispatcher from disk/dev_ctl.c */
50 extern WV_F_DEV_CTL disk_dev_ctl__dispatch;
51 /* IRP_MJ_SCSI dispatcher from disk/scsi.c */
52 extern WV_F_DEV_SCSI disk_scsi__dispatch;
53 /* IRP_MJ_PNP dispatcher from disk/pnp.c */
54 extern WV_F_DEV_PNP disk_pnp__dispatch;
55
56 /* Forward declarations. */
57 static WV_F_DEV_FREE free_disk;
58 static WV_F_DEV_DISPATCH disk__power_;
59 static WV_F_DEV_DISPATCH disk__sys_ctl_;
60 static WV_F_DISK_INIT WvDiskDefaultInit_;
61 static WV_F_DISK_CLOSE WvDiskDefaultClose_;
62
63 /* Globals. */
64 static LIST_ENTRY disk_list;
65 static KSPIN_LOCK disk_list_lock;
66 winvblock__bool WvDiskIsRemovable[WvDiskMediaTypes] = { TRUE, FALSE, TRUE };
67 PWCHAR WvDiskCompatIds[WvDiskMediaTypes] = {
68     L"GenSFloppy",
69     L"GenDisk",
70     L"GenCdRom"
71   };
72 WV_S_DEV_IRP_MJ disk__irp_mj_ = {
73     disk__power_,
74     disk__sys_ctl_,
75     disk_dev_ctl__dispatch,
76     disk_scsi__dispatch,
77     disk_pnp__dispatch,
78   };
79
80 static winvblock__uint32 default_max_xfer_len(IN WV_SP_DISK_T disk_ptr) {
81     return 1024 * 1024;
82   }
83
84 /* Initialize a disk. */
85 static winvblock__bool STDCALL disk__init_(IN WV_SP_DEV_T dev) {
86     WV_SP_DISK_T disk_ptr = disk__get_ptr(dev);
87     return disk_ptr->disk_ops.Init(disk_ptr);
88   }
89
90 static winvblock__bool STDCALL WvDiskDefaultInit_(IN WV_SP_DISK_T disk_ptr) {
91     return TRUE;
92   }
93
94 static void STDCALL disk__close_(IN WV_SP_DEV_T dev_ptr) {
95     WV_SP_DISK_T disk_ptr = disk__get_ptr(dev_ptr);
96     disk_ptr->disk_ops.Close(disk_ptr);
97     return;
98   }
99
100 static void STDCALL WvDiskDefaultClose_(IN WV_SP_DISK_T disk_ptr) {
101     return;
102   }
103
104 static NTSTATUS STDCALL disk__power_(IN WV_SP_DEV_T dev, IN PIRP irp) {
105     PoStartNextPowerIrp(irp);
106     return driver__complete_irp(irp, 0, STATUS_NOT_SUPPORTED);
107   }
108
109 static NTSTATUS STDCALL disk__sys_ctl_(IN WV_SP_DEV_T dev, IN PIRP irp) {
110     return driver__complete_irp(irp, 0, irp->IoStatus.Status);
111   }
112
113 /**
114  * Create a disk PDO filled with the given disk parameters.
115  *
116  * @v dev_ptr           Populate PDO dev. ext. space from these details.
117  * @ret dev_obj_ptr     Points to the new PDO, or is NULL upon failure.
118  *
119  * Returns a Physical Device Object pointer on success, NULL for failure.
120  */
121 static PDEVICE_OBJECT STDCALL create_pdo(IN WV_SP_DEV_T dev_ptr) {
122     /**
123      * @v disk_ptr          Used for pointing to disk details
124      * @v status            Status of last operation
125      * @v dev_obj_ptr       The new node's physical device object (PDO)
126      * @v new_ext_size      The extension space size
127      * @v disk_types[]      Floppy, hard disk, optical disc specifics
128      * @v characteristics[] Floppy, hard disk, optical disc specifics
129      */
130     WV_SP_DISK_T disk_ptr;
131     NTSTATUS status;
132     PDEVICE_OBJECT dev_obj_ptr;
133     static DEVICE_TYPE disk_types[WvDiskMediaTypes] =
134       { FILE_DEVICE_DISK, FILE_DEVICE_DISK, FILE_DEVICE_CD_ROM };
135     static winvblock__uint32 characteristics[WvDiskMediaTypes] =
136       { FILE_REMOVABLE_MEDIA | FILE_FLOPPY_DISKETTE, 0,
137       FILE_REMOVABLE_MEDIA | FILE_READ_ONLY_DEVICE
138     };
139   
140     DBG("Entry\n");
141     /* Point to the disk details provided. */
142     disk_ptr = disk__get_ptr(dev_ptr);
143     /* Create the disk PDO. */
144     status = IoCreateDevice(
145         WvDriverObj,
146         sizeof (driver__dev_ext),
147         NULL,
148         disk_types[disk_ptr->Media],
149         FILE_AUTOGENERATED_DEVICE_NAME |
150           FILE_DEVICE_SECURE_OPEN |
151           characteristics[disk_ptr->Media],
152         FALSE,
153         &dev_obj_ptr
154       );
155     if (!NT_SUCCESS(status)) {
156         Error("IoCreateDevice", status);
157         return NULL;
158       }
159     /* Set associations for the PDO, device, disk. */
160     WvDevForDevObj(dev_obj_ptr, dev_ptr);
161     dev_ptr->Self = dev_obj_ptr;
162     KeInitializeEvent(&disk_ptr->SearchEvent, SynchronizationEvent, FALSE);
163     KeInitializeSpinLock(&disk_ptr->SpinLock);
164     /* Some device parameters. */
165     dev_obj_ptr->Flags |= DO_DIRECT_IO;         /* FIXME? */
166     dev_obj_ptr->Flags |= DO_POWER_INRUSH;      /* FIXME? */
167     DBG("Exit\n");
168     return dev_obj_ptr;
169   }
170
171 /*
172  * fat_extra and fat_super taken from syslinux/memdisk/setup.c by
173  * H. Peter Anvin.  Licensed under the terms of the GNU General Public
174  * License version 2 or later.
175  */
176 #ifdef _MSC_VER
177 #  pragma pack(1)
178 #endif
179 winvblock__def_struct(fat_extra) {
180     winvblock__uint8 bs_drvnum;
181     winvblock__uint8 bs_resv1;
182     winvblock__uint8 bs_bootsig;
183     winvblock__uint32 bs_volid;
184     char bs_vollab[11];
185     char bs_filsystype[8];
186   } __attribute__((packed));
187
188 winvblock__def_struct(fat_super) {
189     winvblock__uint8 bs_jmpboot[3];
190     char bs_oemname[8];
191     winvblock__uint16 bpb_bytspersec;
192     winvblock__uint8 bpb_secperclus;
193     winvblock__uint16 bpb_rsvdseccnt;
194     winvblock__uint8 bpb_numfats;
195     winvblock__uint16 bpb_rootentcnt;
196     winvblock__uint16 bpb_totsec16;
197     winvblock__uint8 bpb_media;
198     winvblock__uint16 bpb_fatsz16;
199     winvblock__uint16 bpb_secpertrk;
200     winvblock__uint16 bpb_numheads;
201     winvblock__uint32 bpb_hiddsec;
202     winvblock__uint32 bpb_totsec32;
203     union {
204         struct { fat_extra extra; } fat16;
205         struct {
206             winvblock__uint32 bpb_fatsz32;
207             winvblock__uint16 bpb_extflags;
208             winvblock__uint16 bpb_fsver;
209             winvblock__uint32 bpb_rootclus;
210             winvblock__uint16 bpb_fsinfo;
211             winvblock__uint16 bpb_bkbootsec;
212             char bpb_reserved[12];
213             /* Clever, eh?  Same fields, different offset... */
214             fat_extra extra;
215           } fat32 __attribute__((packed));
216       } x;
217   } __attribute__((__packed__));
218 #ifdef _MSC_VER
219 #  pragma pack()
220 #endif
221
222 /**
223  * Attempt to guess a disk's geometry.
224  *
225  * @v boot_sect_ptr     The MBR or VBR with possible geometry clues.
226  * @v disk_ptr          The disk to set the geometry for.
227  */
228 winvblock__lib_func void disk__guess_geometry(
229     IN WV_AP_DISK_BOOT_SECT boot_sect_ptr,
230     IN OUT WV_SP_DISK_T disk_ptr
231   ) {
232     winvblock__uint16 heads = 0, sects_per_track = 0, cylinders;
233     mbr_ptr as_mbr;
234
235     if ((boot_sect_ptr == NULL) || (disk_ptr == NULL))
236       return;
237
238     /*
239      * FAT superblock geometry checking taken from syslinux/memdisk/setup.c by
240      * H. Peter Anvin.  Licensed under the terms of the GNU General Public
241      * License version 2 or later.
242      */
243     {
244         /*
245          * Look for a FAT superblock and if we find something that looks
246          * enough like one, use geometry from that.  This takes care of
247          * megafloppy images and unpartitioned hard disks. 
248          */
249         fat_extra_ptr extra = NULL;
250         fat_super_ptr fs = (fat_super_ptr) boot_sect_ptr;
251   
252         if (
253             (fs->bpb_media == 0xf0 || fs->bpb_media >= 0xf8) &&
254             (fs->bs_jmpboot[0] == 0xe9 || fs->bs_jmpboot[0] == 0xeb) &&
255             fs->bpb_bytspersec == 512 &&
256             fs->bpb_numheads >= 1 &&
257             fs->bpb_numheads <= 256 &&
258             fs->bpb_secpertrk >= 1 &&
259             fs->bpb_secpertrk <= 63
260           ) {
261             extra = fs->bpb_fatsz16 ? &fs->x.fat16.extra : &fs->x.fat32.extra;
262             if (!(
263                 extra->bs_bootsig == 0x29 &&
264                 extra->bs_filsystype[0] == 'F' &&
265                 extra->bs_filsystype[1] == 'A' &&
266                 extra->bs_filsystype[2] == 'T'
267               ))
268               extra = NULL;
269           }
270         if (extra) {
271             heads = fs->bpb_numheads;
272             sects_per_track = fs->bpb_secpertrk;
273           }
274       } /* sub-scope */
275     /*
276      * If we couldn't parse a FAT superblock, try checking MBR params.
277      * Logic derived from syslinux/memdisk/setup.c by H. Peter Anvin.
278      */
279     as_mbr = (mbr_ptr) boot_sect_ptr;
280     if (
281         (heads == 0 ) &&
282         (sects_per_track == 0) &&
283         (as_mbr->mbr_sig == 0xAA55)
284       ) {
285         int i;
286
287         for (i = 0; i < 4; i++) {
288             if (
289                 !(as_mbr->partition[i].status & 0x7f) &&
290                 as_mbr->partition[i].type
291               ) {
292                 winvblock__uint8 h, s;
293
294                 h = chs_head(as_mbr->partition[i].chs_start) + 1;
295                 s = chs_sector(as_mbr->partition[i].chs_start);
296
297                 if (heads < h)
298                   heads = h;
299                 if (sects_per_track < s)
300                   sects_per_track = s;
301
302                 h = chs_head(as_mbr->partition[i].chs_end) + 1;
303                 s = chs_sector(as_mbr->partition[i].chs_end);
304
305                 if (heads < h)
306                   heads = h;
307                 if (sects_per_track < s)
308                   sects_per_track = s;
309               } /* if */
310           } /* for */
311       } /* if */
312     /* If we were unable to guess, use some hopeful defaults. */
313     if (!heads)
314       heads = 255;
315     if (!sects_per_track)
316       sects_per_track = 63;
317     /* Set params that are not already filled. */
318     if (!disk_ptr->Heads)
319       disk_ptr->Heads = heads;
320     if (!disk_ptr->Sectors)
321       disk_ptr->Sectors = sects_per_track;
322     if (!disk_ptr->Cylinders)
323       disk_ptr->Cylinders = disk_ptr->LBADiskSize / (heads * sects_per_track);
324   }
325
326 /**
327  * Create a new disk.
328  *
329  * @ret disk_ptr        The address of a new disk, or NULL for failure.
330  *
331  * See the header file for additional details.
332  */
333 winvblock__lib_func WV_SP_DISK_T disk__create(void) {
334     WV_SP_DEV_T dev_ptr;
335     WV_SP_DISK_T disk_ptr;
336
337     /*
338      * Disk devices might be used for booting and should
339      * not be allocated from a paged memory pool.
340      */
341     disk_ptr = wv_mallocz(sizeof *disk_ptr);
342     if (disk_ptr == NULL)
343       goto err_nodisk;
344
345     dev_ptr = disk_ptr->Dev;
346     WvDevInit(dev_ptr);
347
348     /* Track the new disk in our global list. */
349     ExInterlockedInsertTailList(
350         &disk_list,
351         &disk_ptr->tracking,
352         &disk_list_lock
353       );
354     /* Populate non-zero device defaults. */
355     disk_ptr->disk_ops.MaxXferLen = default_max_xfer_len;
356     disk_ptr->disk_ops.Init = WvDiskDefaultInit_;
357     disk_ptr->disk_ops.Close = WvDiskDefaultClose_;
358     dev_ptr->Ops.Close = disk__close_;
359     dev_ptr->Ops.CreatePdo = create_pdo;
360     dev_ptr->Ops.Free = free_disk;
361     dev_ptr->Ops.Init = disk__init_;
362     dev_ptr->ext = disk_ptr;
363     dev_ptr->IrpMj = &disk__irp_mj_;
364     KeInitializeSpinLock(&disk_ptr->SpinLock);
365
366     return disk_ptr;
367
368     err_nodisk:
369
370     return NULL;
371   }
372
373 /**
374  * Initialize the global, disk-common environment.
375  *
376  * @ret ntstatus        STATUS_SUCCESS or the NTSTATUS for a failure.
377  */
378 NTSTATUS disk__module_init(void) {
379     /* Initialize the global list of disks. */
380     InitializeListHead(&disk_list);
381     KeInitializeSpinLock(&disk_list_lock);
382
383     return STATUS_SUCCESS;
384   }
385
386 /**
387  * Default disk deletion operation.
388  *
389  * @v dev_ptr           Points to the disk device to delete.
390  */
391 static void STDCALL free_disk(IN WV_SP_DEV_T dev_ptr) {
392     WV_SP_DISK_T disk_ptr = disk__get_ptr(dev_ptr);
393
394     /*
395      * Track the disk deletion in our global list.  Unfortunately,
396      * for now we have faith that a disk won't be deleted twice and
397      * result in a race condition.  Something to keep in mind...
398      */
399     ExInterlockedRemoveHeadList(disk_ptr->tracking.Blink, &disk_list_lock);
400
401     wv_free(disk_ptr);
402   }
403
404 /* See header for details. */
405 NTSTATUS STDCALL disk__io(
406     IN WV_SP_DEV_T dev_ptr,
407     IN WV_E_DISK_IO_MODE mode,
408     IN LONGLONG start_sector,
409     IN winvblock__uint32 sector_count,
410     IN winvblock__uint8_ptr buffer,
411     IN PIRP irp
412   ) {
413     WV_SP_DISK_T disk_ptr;
414
415     /* Establish a pointer to the disk. */
416     disk_ptr = disk__get_ptr(dev_ptr);
417
418     return disk_ptr->disk_ops.Io(
419         dev_ptr,
420         mode,
421         start_sector,
422         sector_count,
423         buffer,
424         irp
425       );
426   }
427
428 /* See header for details. */
429 winvblock__uint32 disk__max_xfer_len(IN WV_SP_DISK_T disk_ptr) {
430     return disk_ptr->disk_ops.MaxXferLen(disk_ptr);
431   }