Rev 2644 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
2644 | clevermous | 1 | ; Disk driver to create FAT16/FAT32 memory-based temporary disk aka RAM disk. |
2 | ; (c) CleverMouse |
||
3 | |||
4 | ; Note: in the ideal world, a disk driver should not care about a file system |
||
5 | ; on it. In the current world, however, there is no way to format a disk in |
||
6 | ; FAT, so this part of file-system-specific operations is included in the |
||
7 | ; driver. |
||
8 | |||
9 | ; When this driver is loading, it registers itself in the system and does |
||
10 | ; nothing more. When loaded, this driver controls pseudo-disk devices |
||
11 | ; named /tmp#/, where # is a digit from 0 to 9. The driver does not create |
||
12 | ; any device by itself, waiting for instructions from an application. |
||
13 | ; The driver responds to the following IOCTLs from a control application: |
||
14 | SRV_GETVERSION equ 0 ; input ignored, |
||
15 | ; output = dword API_VERSION |
||
16 | DEV_ADD_DISK equ 1 ; input = structure add_disk_struc, |
||
17 | ; no output |
||
18 | DEV_DEL_DISK equ 2 ; input = structure del_disk_struc, |
||
19 | ; no output |
||
20 | ; For all IOCTLs the driver returns one of the following error codes: |
||
21 | NO_ERROR equ 0 |
||
22 | ERROR_INVALID_IOCTL equ 1 ; unknown IOCTL code, wrong input/output size... |
||
23 | ERROR_INVALID_ID equ 2 ; .DiskId must be from 0 to 9 |
||
24 | ERROR_SIZE_TOO_LARGE equ 3 ; .DiskSize is too large |
||
25 | ERROR_SIZE_TOO_SMALL equ 4 ; .DiskSize is too small |
||
26 | ERROR_NO_MEMORY equ 5 ; memory allocation failed |
||
27 | |||
5044 | clevermous | 28 | include '../struct.inc' |
2644 | clevermous | 29 | |
30 | API_VERSION equ 1 |
||
31 | ; Input structures: |
||
5044 | clevermous | 32 | struct add_disk_struc |
33 | DiskSize dd ? ; disk size in sectors, 1 sector = 512 bytes |
||
34 | ; Note: DiskSize is the full size, including FAT service data. |
||
2644 | clevermous | 35 | ; Size for useful data is slightly less than this number. |
5044 | clevermous | 36 | DiskId db ? ; from 0 to 9 |
37 | ends |
||
38 | struct del_disk_struc |
||
39 | DiskId db ? ; from 0 to 9 |
||
40 | ends |
||
2644 | clevermous | 41 | |
42 | max_num_disks equ 10 |
||
43 | |||
5044 | clevermous | 44 | ; standard driver stuff; version of driver model = 5 |
45 | format PE DLL native 0.05 |
||
2644 | clevermous | 46 | |
47 | DEBUG equ 0 |
||
48 | |||
5044 | clevermous | 49 | section '.flat' code readable writable executable |
50 | data fixups |
||
51 | end data |
||
52 | entry START |
||
53 | include '../proc32.inc' |
||
54 | include '../peimport.inc' |
||
55 | include '../macros.inc' |
||
2644 | clevermous | 56 | ; the start procedure (see the description above) |
57 | proc START |
||
58 | ; This procedure is called in two situations: |
||
59 | ; when the driver is loading and when the system is shutting down. |
||
60 | ; 1. Check that the driver is loading; do nothing unless so. |
||
61 | xor eax, eax ; set return value in case we will do nothing |
||
62 | cmp dword [esp+4], 1 |
||
63 | jne .nothing |
||
64 | ; 2. Register the driver in the system. |
||
5044 | clevermous | 65 | invoke RegService, my_service, service_proc |
2644 | clevermous | 66 | ; 3. Return the value returned by RegService back to the system. |
67 | .nothing: |
||
5044 | clevermous | 68 | retn |
2644 | clevermous | 69 | endp |
70 | |||
71 | ; Service procedure for the driver - handle all IOCTL requests for the driver. |
||
72 | ; The description of handled IOCTLs is located in the start of this file. |
||
73 | proc service_proc |
||
74 | ; 1. Save used registers to be stdcall. |
||
75 | ; Note: this shifts esp, so the first parameter [esp+4] becomes [esp+16]. |
||
76 | ; Note: edi is used not by this procedure itself, but by worker procedures. |
||
77 | push ebx esi edi |
||
78 | ; 2. Get parameter from the stack: [esp+16] is the first parameter, |
||
79 | ; pointer to IOCTL structure. |
||
80 | mov edx, [esp+16] ; edx -> IOCTL |
||
81 | ; 3. Set the return value to 'invalid IOCTL'. |
||
82 | ; Now, if one of conditions for IOCTL does not met, the code |
||
83 | ; can simply return the value already loaded. |
||
84 | mov al, ERROR_INVALID_IOCTL |
||
85 | ; 4. Get request code and select a handler for the code. |
||
86 | mov ecx, [edx+IOCTL.io_code] |
||
87 | test ecx, ecx ; check for SRV_GETVERSION |
||
88 | jnz .no.srv_getversion |
||
89 | ; 4. This is SRV_GETVERSION request, no input, 4 bytes output, API_VERSION. |
||
90 | ; 4a. Output size must be at least 4 bytes. |
||
91 | cmp [edx+IOCTL.out_size], 4 |
||
92 | jl .return |
||
93 | ; 4b. Write result to the output buffer. |
||
94 | mov eax, [edx+IOCTL.output] |
||
95 | mov dword [eax], API_VERSION |
||
96 | ; 4c. Return success. |
||
97 | xor eax, eax |
||
98 | jmp .return |
||
99 | .no.srv_getversion: |
||
100 | dec ecx ; check for DEV_ADD_DISK |
||
101 | jnz .no.dev_add_disk |
||
102 | ; 5. This is DEV_ADD_DISK request, input is add_disk_struc, output is 1 byte |
||
5044 | clevermous | 103 | ; 5a. Input size must be exactly sizeof.add_disk_struc bytes. |
104 | cmp [edx+IOCTL.inp_size], sizeof.add_disk_struc |
||
2644 | clevermous | 105 | jnz .return |
106 | ; 5b. Load input parameters and call the worker procedure. |
||
107 | mov eax, [edx+IOCTL.input] |
||
108 | movzx ebx, [eax+add_disk_struc.DiskId] |
||
109 | mov esi, [eax+add_disk_struc.DiskSize] |
||
110 | call add_disk |
||
111 | ; 5c. Return back to the caller the value from the worker procedure. |
||
112 | jmp .return |
||
113 | .no.dev_add_disk: |
||
114 | dec ecx ; check for DEV_DEL_DISK |
||
115 | jnz .return |
||
116 | ; 6. This is DEV_DEL_DISK request, input is del_disk_struc |
||
5044 | clevermous | 117 | ; 6a. Input size must be exactly sizeof.del_disk_struc bytes. |
118 | cmp [edx+IOCTL.inp_size], sizeof.del_disk_struc |
||
2644 | clevermous | 119 | jnz .return |
120 | ; 6b. Load input parameters and call the worker procedure. |
||
121 | mov eax, [edx+IOCTL.input] |
||
122 | movzx ebx, [eax+del_disk_struc.DiskId] |
||
123 | call del_disk |
||
124 | ; 6c. Return back to the caller the value from the worker procedure. |
||
125 | .return: |
||
126 | ; 7. Exit. |
||
127 | ; 7a. The code above returns a value in al for efficiency, |
||
128 | ; propagate it to eax. |
||
129 | movzx eax, al |
||
130 | ; 7b. Restore used registers to be stdcall. |
||
131 | pop edi esi ebx |
||
132 | ; 7c. Return, popping one argument. |
||
133 | retn 4 |
||
134 | endp |
||
135 | |||
136 | ; The worker procedure for DEV_ADD_DISK request. |
||
137 | ; Creates a memory-based disk of given size and formats it in FAT16/32. |
||
138 | ; Called with ebx = disk id, esi = disk size, |
||
139 | ; returns error code in al. |
||
140 | proc add_disk |
||
141 | ; 1. Check that disk id is correct and free. |
||
142 | ; Otherwise, return the corresponding error code. |
||
143 | mov al, ERROR_INVALID_ID |
||
144 | cmp ebx, max_num_disks |
||
145 | jae .return |
||
146 | cmp [disk_pointers+ebx*4], 0 |
||
147 | jnz .return |
||
148 | ; 2. Check that the size is reasonable. |
||
149 | ; Otherwise, return the corresponding error code. |
||
150 | mov al, ERROR_SIZE_TOO_LARGE |
||
151 | cmp esi, MAX_SIZE |
||
152 | ja .return |
||
153 | mov al, ERROR_SIZE_TOO_SMALL |
||
154 | cmp esi, MIN_FAT16_SIZE |
||
155 | jb .return |
||
156 | ; 3. Allocate memory for the disk, store the pointer in edi. |
||
157 | ; If failed, return the corresponding error code. |
||
158 | mov eax, esi |
||
159 | shl eax, 9 |
||
5044 | clevermous | 160 | invoke KernelAlloc, eax |
2644 | clevermous | 161 | mov edi, eax |
162 | test eax, eax |
||
163 | mov al, ERROR_NO_MEMORY |
||
164 | jz .return |
||
165 | ; 4. Store the pointer and the size in the global variables. |
||
166 | ; It is possible, though very unlikely, that two threads |
||
167 | ; have called this function in parallel with the same id, |
||
168 | ; so [disk_pointers+ebx*4] could be filled by another thread. |
||
169 | ; Play extra safe and store new value only if old value is zero. |
||
170 | xor eax, eax |
||
171 | lock cmpxchg [disk_pointers+ebx*4], edi |
||
172 | jz @f |
||
173 | ; Otherwise, free the allocated memory and return the corresponding error code. |
||
5044 | clevermous | 174 | invoke KernelFree, edi |
2644 | clevermous | 175 | mov al, ERROR_INVALID_ID |
176 | jmp .return |
||
177 | @@: |
||
178 | mov [disk_sizes+ebx*4], esi |
||
179 | ; 5. Call the worker procedure for formatting this disk. |
||
180 | ; It should not fail. |
||
181 | call format_disk |
||
182 | ; 6. Register the disk in the system. |
||
183 | ; 6a. Generate name as /tmp#, where # = ebx + '0'. Use two dwords in the stack. |
||
184 | push 0 |
||
185 | push 'tmp' |
||
186 | mov eax, esp ; eax points to 'tmp' + zero byte + zero dword |
||
187 | lea ecx, [ebx+'0'] ; ecx = digit |
||
188 | mov [eax+3], cl ; eax points to 'tmp#' + zero dword |
||
189 | ; 6b. Call the kernel API. Use disk id as 'userdata' parameter for callbacks. |
||
5044 | clevermous | 190 | invoke DiskAdd, disk_functions, eax, ebx, 0 |
2644 | clevermous | 191 | ; 6c. Restore the stack after 6a. |
192 | pop ecx ecx |
||
193 | ; 6c. Check the result. If DiskAdd has failed, cleanup and return |
||
194 | ; ERROR_NO_MEMORY, this is the most probable or even the only reason to fail. |
||
195 | test eax, eax |
||
196 | jnz @f |
||
197 | mov [disk_sizes+ebx*4], 0 |
||
198 | mov [disk_pointers+ebx*4], 0 |
||
5044 | clevermous | 199 | invoke KernelFree, edi |
2644 | clevermous | 200 | mov al, ERROR_NO_MEMORY |
201 | jmp .return |
||
202 | @@: |
||
203 | push eax |
||
204 | ; 6d. Notify the kernel that media is inserted. |
||
5044 | clevermous | 205 | invoke DiskMediaChanged, eax, 1 |
2644 | clevermous | 206 | ; 6e. Disk is fully configured; store its handle in the global variable |
207 | ; and return success. |
||
208 | pop [disk_handles+ebx*4] |
||
209 | xor eax, eax |
||
210 | ; 7. Return. |
||
211 | .return: |
||
212 | retn |
||
213 | endp |
||
214 | |||
215 | ; The worker procedure for DEV_DEL_DISK request. |
||
216 | ; Deletes a previously created memory-based disk. |
||
217 | ; Called with ebx = disk id, |
||
218 | ; returns error code in al. |
||
219 | proc del_disk |
||
220 | ; 1. Check that disk id is correct. |
||
221 | ; Otherwise, return the corresponding error code. |
||
222 | mov al, ERROR_INVALID_ID |
||
223 | cmp ebx, max_num_disks |
||
224 | jae .return |
||
225 | ; 2. Get the disk handle, simultaneously clearing the global variable. |
||
226 | xor edx, edx |
||
227 | xchg edx, [disk_handles+ebx*4] |
||
228 | ; 3. Check that the handle is non-zero. |
||
229 | ; Otherwise, return the corresponding error code. |
||
230 | test edx, edx |
||
231 | jz .return |
||
232 | ; 4. Delete the disk from the system. |
||
5044 | clevermous | 233 | invoke DiskDel, edx |
2644 | clevermous | 234 | ; 5. Return success. |
235 | ; Note that we can't free memory yet; it will be done in tmpdisk_close. |
||
236 | xor eax, eax |
||
237 | .return: |
||
238 | retn |
||
239 | endp |
||
240 | |||
241 | ; Include implementation of tmpdisk_* callbacks. |
||
242 | include 'tmpdisk_work.inc' |
||
243 | ; Include FAT-specific code. |
||
244 | include 'tmpdisk_fat.inc' |
||
245 | |||
246 | ; initialized data |
||
247 | align 4 |
||
248 | disk_functions: |
||
249 | dd disk_functions_end - disk_functions |
||
250 | dd tmpdisk_close |
||
251 | dd 0 ; no need in .closemedia |
||
252 | dd tmpdisk_querymedia |
||
253 | dd tmpdisk_read |
||
254 | dd tmpdisk_write |
||
255 | dd 0 ; no need in .flush |
||
256 | dd tmpdisk_adjust_cache_size |
||
257 | disk_functions_end: |
||
258 | ; disk_handles = array of values for Disk* kernel functions |
||
259 | label disk_handles dword |
||
260 | times max_num_disks dd 0 |
||
261 | ; disk_pointers = array of pointers to disk data |
||
262 | label disk_pointers dword |
||
263 | times max_num_disks dd 0 |
||
264 | ; disk_sizes = array of disk sizes |
||
265 | label disk_sizes dword |
||
266 | times max_num_disks dd 0 |
||
267 | |||
268 | my_service db 'tmpdisk',0 |