44d5c3342b8b5b5f00e18b5a0e87eb2802db2f66
[people/sha0/winvblock.git] / src / winvblock / ramdisk / ramdisk.c
1 /**
2  * Copyright (C) 2009-2010, Shao Miller <shao.miller@yrdsb.edu.on.ca>.
3  *
4  * This file is part of WinVBlock, derived from WinAoE.
5  *
6  * WinVBlock is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * WinVBlock is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with WinVBlock.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 /**
21  * @file
22  *
23  * RAM disk specifics.
24  */
25
26 #include <stdio.h>
27 #include <ntddk.h>
28
29 #include "winvblock.h"
30 #include "wv_stdlib.h"
31 #include "portable.h"
32 #include "driver.h"
33 #include "device.h"
34 #include "disk.h"
35 #include "ramdisk.h"
36 #include "debug.h"
37
38 /*
39  * Yield a pointer to the RAM disk
40  */
41 #define ramdisk_get_ptr( dev_ptr ) \
42   ( ( ramdisk__type_ptr ) ( disk__get_ptr ( dev_ptr ) )->ext )
43
44 /* Globals. */
45 static LIST_ENTRY ramdisk_list;
46 static KSPIN_LOCK ramdisk_list_lock;
47
48 /* Forward declarations. */
49 static WV_F_DEV_FREE free_ramdisk;
50
51 /* With thanks to karyonix, who makes FiraDisk */
52 static __inline void STDCALL
53 fast_copy (
54   void *dest,
55   const void *src,
56   size_t count
57  )
58 {
59   __movsd ( dest, src, count >> 2 );
60 }
61
62 static
63 disk__io_decl (
64   io
65  )
66 {
67   PHYSICAL_ADDRESS PhysicalAddress;
68   winvblock__uint8_ptr PhysicalMemory;
69   disk__type_ptr disk_ptr;
70   ramdisk__type_ptr ramdisk_ptr;
71
72   /*
73    * Establish pointers to the disk and RAM disk
74    */
75   disk_ptr = disk__get_ptr ( dev_ptr );
76   ramdisk_ptr = ramdisk_get_ptr ( dev_ptr );
77
78   if ( sector_count < 1 )
79     {
80       /*
81        * A silly request 
82        */
83       DBG ( "sector_count < 1; cancelling\n" );
84       irp->IoStatus.Information = 0;
85       irp->IoStatus.Status = STATUS_CANCELLED;
86       IoCompleteRequest ( irp, IO_NO_INCREMENT );
87       return STATUS_CANCELLED;
88     }
89
90   PhysicalAddress.QuadPart =
91     ramdisk_ptr->DiskBuf + ( start_sector * disk_ptr->SectorSize );
92   /*
93    * Possible precision loss
94    */
95   PhysicalMemory =
96     MmMapIoSpace ( PhysicalAddress, sector_count * disk_ptr->SectorSize,
97                    MmNonCached );
98   if ( !PhysicalMemory )
99     {
100       DBG ( "Could not map memory for RAM disk!\n" );
101       irp->IoStatus.Information = 0;
102       irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
103       IoCompleteRequest ( irp, IO_NO_INCREMENT );
104       return STATUS_INSUFFICIENT_RESOURCES;
105     }
106   if ( mode == disk__io_mode_write )
107     fast_copy ( PhysicalMemory, buffer, sector_count * disk_ptr->SectorSize );
108   else
109     fast_copy ( buffer, PhysicalMemory, sector_count * disk_ptr->SectorSize );
110   MmUnmapIoSpace ( PhysicalMemory, sector_count * disk_ptr->SectorSize );
111   irp->IoStatus.Information = sector_count * disk_ptr->SectorSize;
112   irp->IoStatus.Status = STATUS_SUCCESS;
113   IoCompleteRequest ( irp, IO_NO_INCREMENT );
114   return STATUS_SUCCESS;
115 }
116
117 static winvblock__uint32 STDCALL query_id(
118     IN WV_SP_DEV_T dev,
119     IN BUS_QUERY_ID_TYPE query_type,
120     IN OUT WCHAR (*buf)[512]
121   ) {
122     disk__type_ptr disk = disk__get_ptr(dev);
123     ramdisk__type_ptr ramdisk = ramdisk_get_ptr(dev);
124     static PWCHAR hw_ids[WvDiskMediaTypes] = {
125         winvblock__literal_w L"\\RAMFloppyDisk",
126         winvblock__literal_w L"\\RAMHardDisk",
127         winvblock__literal_w L"\\RAMOpticalDisc"
128       };
129
130     switch (query_type) {
131         case BusQueryDeviceID:
132           return swprintf(*buf, hw_ids[disk->Media]) + 1;
133
134         case BusQueryInstanceID:
135                 /* "Location" */
136           return swprintf(*buf, L"RAM_at_%08X", ramdisk->DiskBuf) + 1;
137
138         case BusQueryHardwareIDs: {
139             winvblock__uint32 tmp;
140
141             tmp = swprintf(*buf, hw_ids[disk->Media]) + 1;
142             tmp += swprintf(*buf + tmp, WvDiskCompatIds[disk->Media]) + 4;
143             return tmp;
144           }
145
146         case BusQueryCompatibleIDs:
147           return swprintf(*buf, WvDiskCompatIds[disk->Media]) + 4;
148
149         default:
150           return 0;
151       }
152   }
153
154 /**
155  * Create a new RAM disk
156  *
157  * @ret ramdisk_ptr     The address of a new RAM disk, or NULL for failure
158  *
159  * See the header file for additional details
160  */
161 ramdisk__type_ptr
162 ramdisk__create (
163   void
164  )
165 {
166   disk__type_ptr disk_ptr;
167   ramdisk__type_ptr ramdisk_ptr;
168
169   /*
170    * Try to create a disk
171    */
172   disk_ptr = disk__create (  );
173   if ( disk_ptr == NULL )
174     goto err_nodisk;
175   /*
176    * RAM disk devices might be used for booting and should
177    * not be allocated from a paged memory pool
178    */
179   ramdisk_ptr = wv_mallocz(sizeof *ramdisk_ptr);
180   if ( ramdisk_ptr == NULL )
181     goto err_noramdisk;
182   /*
183    * Track the new RAM disk in our global list
184    */
185   ExInterlockedInsertTailList ( &ramdisk_list, &ramdisk_ptr->tracking,
186                                 &ramdisk_list_lock );
187   /*
188    * Populate non-zero device defaults
189    */
190   ramdisk_ptr->disk = disk_ptr;
191   ramdisk_ptr->prev_free = disk_ptr->device->Ops.Free;
192   disk_ptr->device->Ops.Free = free_ramdisk;
193   disk_ptr->device->Ops.PnpId = query_id;
194   disk_ptr->disk_ops.io = io;
195   disk_ptr->ext = ramdisk_ptr;
196
197   return ramdisk_ptr;
198
199 err_noramdisk:
200
201   WvDevFree(disk_ptr->device);
202 err_nodisk:
203
204   return NULL;
205 }
206
207 /**
208  * Initialize the global, RAM disk-common environment.
209  *
210  * @ret ntstatus        STATUS_SUCCESS or the NTSTATUS for a failure.
211  */
212 NTSTATUS ramdisk__module_init(void) {
213     /* Initialize the global list of RAM disks. */
214     InitializeListHead(&ramdisk_list);
215     KeInitializeSpinLock(&ramdisk_list_lock);
216
217     return STATUS_SUCCESS;
218   }
219
220 /**
221  * Default RAM disk deletion operation.
222  *
223  * @v dev_ptr           Points to the RAM disk device to delete.
224  */
225 static void STDCALL free_ramdisk(IN WV_SP_DEV_T dev_ptr) {
226     disk__type_ptr disk_ptr = disk__get_ptr(dev_ptr);
227     ramdisk__type_ptr ramdisk_ptr = ramdisk_get_ptr(dev_ptr);
228     /* Free the "inherited class". */
229     ramdisk_ptr->prev_free(dev_ptr);
230     /*
231      * Track the RAM disk deletion in our global list.  Unfortunately,
232      * for now we have faith that a RAM disk won't be deleted twice and
233      * result in a race condition.  Something to keep in mind...
234      */
235     ExInterlockedRemoveHeadList(
236         ramdisk_ptr->tracking.Blink,
237                         &ramdisk_list_lock
238       );
239   
240     wv_free(ramdisk_ptr);
241   }