Rev 10051 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
2288 | clevermous | 1 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
2 | ;; ;; |
||
10051 | ace_dent | 3 | ;; Copyright (C) KolibriOS team 2011-2024. All rights reserved. ;; |
2288 | clevermous | 4 | ;; Distributed under terms of the GNU General Public License ;; |
5 | ;; ;; |
||
6 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
||
7 | |||
8 | |||
9 | ; ============================================================================= |
||
10 | ; ================================= Constants ================================= |
||
11 | ; ============================================================================= |
||
12 | ; Error codes for callback functions. |
||
13 | DISK_STATUS_OK = 0 ; success |
||
14 | DISK_STATUS_GENERAL_ERROR = -1; if no other code is suitable |
||
15 | DISK_STATUS_INVALID_CALL = 1 ; invalid input parameters |
||
16 | DISK_STATUS_NO_MEDIA = 2 ; no media present |
||
17 | DISK_STATUS_END_OF_MEDIA = 3 ; end of media while reading/writing data |
||
4437 | clevermous | 18 | DISK_STATUS_NO_MEMORY = 4 ; insufficient memory for driver operation |
2288 | clevermous | 19 | ; Driver flags. Represent bits in DISK.DriverFlags. |
20 | DISK_NO_INSERT_NOTIFICATION = 1 |
||
21 | ; Media flags. Represent bits in DISKMEDIAINFO.Flags. |
||
22 | DISK_MEDIA_READONLY = 1 |
||
23 | |||
24 | ; If too many partitions are detected,there is probably an error on the disk. |
||
25 | ; 256 partitions should be enough for any reasonable use. |
||
26 | ; Also, the same number is limiting the number of MBRs to process; if |
||
27 | ; too many MBRs are visible,there probably is a loop in the MBR structure. |
||
28 | MAX_NUM_PARTITIONS = 256 |
||
29 | |||
30 | ; ============================================================================= |
||
31 | ; ================================ Structures ================================= |
||
32 | ; ============================================================================= |
||
33 | ; This structure defines all callback functions for working with the physical |
||
34 | ; device. They are implemented by a driver. Objects with this structure reside |
||
35 | ; in a driver. |
||
2381 | hidnplayr | 36 | struct DISKFUNC |
37 | strucsize dd ? |
||
2288 | clevermous | 38 | ; Size of the structure. This field is intended for possible extensions of |
39 | ; this structure. If a new function is added to this structure and a driver |
||
40 | ; implements an old version, the caller can detect this by checking .strucsize, |
||
41 | ; so the driver remains compatible. |
||
2381 | hidnplayr | 42 | close dd ? |
2288 | clevermous | 43 | ; The pointer to the function which frees all driver-specific resources for |
44 | ; the disk. |
||
45 | ; Optional, may be NULL. |
||
46 | ; void close(void* userdata); |
||
2381 | hidnplayr | 47 | closemedia dd ? |
2288 | clevermous | 48 | ; The pointer to the function which informs the driver that the kernel has |
49 | ; finished all processing with the current media. If media is removed, the |
||
50 | ; driver should decline all requests to that media with DISK_STATUS_NO_MEDIA, |
||
51 | ; even if new media is inserted, until this function is called. If media is |
||
52 | ; removed, a new call to 'disk_media_changed' is not allowed until this |
||
53 | ; function is called. |
||
54 | ; Optional, may be NULL (if media is not removable). |
||
55 | ; void closemedia(void* userdata); |
||
2381 | hidnplayr | 56 | querymedia dd ? |
2288 | clevermous | 57 | ; The pointer to the function which determines capabilities of the media. |
58 | ; int querymedia(void* userdata, DISKMEDIAINFO* info); |
||
59 | ; Return value: one of DISK_STATUS_* |
||
2381 | hidnplayr | 60 | read dd ? |
2288 | clevermous | 61 | ; The pointer to the function which reads data from the device. |
62 | ; int read(void* userdata, void* buffer, __int64 startsector, int* numsectors); |
||
63 | ; input: *numsectors = number of sectors to read |
||
64 | ; output: *numsectors = number of sectors which were successfully read |
||
65 | ; Return value: one of DISK_STATUS_* |
||
2381 | hidnplayr | 66 | write dd ? |
2288 | clevermous | 67 | ; The pointer to the function which writes data to the device. |
68 | ; Optional, may be NULL. |
||
69 | ; int write(void* userdata, void* buffer, __int64 startsector, int* numsectors); |
||
70 | ; input: *numsectors = number of sectors to write |
||
71 | ; output: *numsectors = number of sectors which were successfully written |
||
72 | ; Return value: one of DISK_STATUS_* |
||
2381 | hidnplayr | 73 | flush dd ? |
2288 | clevermous | 74 | ; The pointer to the function which flushes the internal device cache. |
75 | ; Optional, may be NULL. |
||
76 | ; int flush(void* userdata); |
||
77 | ; Return value: one of DISK_STATUS_* |
||
78 | ; Note that read/write are called by the cache manager, so a driver should not |
||
79 | ; create a software cache. This function is implemented for flushing a hardware |
||
80 | ; cache, if it exists. |
||
2381 | hidnplayr | 81 | adjust_cache_size dd ? |
2288 | clevermous | 82 | ; The pointer to the function which returns the cache size for this device. |
83 | ; Optional, may be NULL. |
||
7727 | dunkaist | 84 | ; unsigned int adjust_cache_size(void* userdata, unsigned int suggested_size); |
2288 | clevermous | 85 | ; Return value: 0 = disable cache, otherwise = used cache size in bytes. |
10053 | Doczom | 86 | LoadTray dd ? |
87 | ; This pointer to the function which load and unload tray drive. |
||
88 | ; Optional, may be NULL |
||
89 | ; int LoadTray(void* userdata, int flags); |
||
90 | ; flags: |
||
91 | ; 0 - load |
||
92 | ; 1 - unload |
||
93 | ; Return value: one of DISK_STATUS_* |
||
2288 | clevermous | 94 | ends |
95 | |||
96 | ; This structure holds information on a medium. |
||
97 | ; Objects with this structure are allocated by the kernel as a part of the DISK |
||
98 | ; structure and are filled by a driver in the 'querymedia' callback. |
||
2381 | hidnplayr | 99 | struct DISKMEDIAINFO |
100 | Flags dd ? |
||
2288 | clevermous | 101 | ; Combination of DISK_MEDIA_* bits. |
2381 | hidnplayr | 102 | SectorSize dd ? |
2288 | clevermous | 103 | ; Size of the sector. |
2381 | hidnplayr | 104 | Capacity dq ? |
2288 | clevermous | 105 | ; Size of the media in sectors. |
10053 | Doczom | 106 | LastSessionSector dd ? |
107 | ; Number last session sectors for CDFS |
||
2288 | clevermous | 108 | ends |
109 | |||
110 | ; This structure represents the disk cache. To follow the old implementation, |
||
111 | ; there are two distinct caches for a disk, one for "system" data,and the other |
||
112 | ; for "application" data. |
||
2381 | hidnplayr | 113 | struct DISKCACHE |
2288 | clevermous | 114 | ; The following fields are inherited from data32.inc:cache_ideX. |
2381 | hidnplayr | 115 | pointer dd ? |
116 | data_size dd ? ; unused |
||
117 | data dd ? |
||
118 | sad_size dd ? |
||
119 | search_start dd ? |
||
5089 | clevermous | 120 | sector_size_log dd ? |
2288 | clevermous | 121 | ends |
122 | |||
123 | ; This structure represents a disk device and its media for the kernel. |
||
124 | ; This structure is allocated by the kernel in the 'disk_add' function, |
||
125 | ; freed in the 'disk_dereference' function. |
||
2381 | hidnplayr | 126 | struct DISK |
2288 | clevermous | 127 | ; Fields of disk object |
2381 | hidnplayr | 128 | Next dd ? |
129 | Prev dd ? |
||
2288 | clevermous | 130 | ; All disk devices are linked in one list with these two fields. |
131 | ; Head of the list is the 'disk_list' variable. |
||
2381 | hidnplayr | 132 | Functions dd ? |
2288 | clevermous | 133 | ; Pointer to the 'DISKFUNC' structure with driver functions. |
2381 | hidnplayr | 134 | Name dd ? |
2288 | clevermous | 135 | ; Pointer to the string used for accesses through the global filesystem. |
2381 | hidnplayr | 136 | UserData dd ? |
2288 | clevermous | 137 | ; This field is passed to all callback functions so a driver can decide which |
138 | ; physical device is addressed. |
||
2381 | hidnplayr | 139 | DriverFlags dd ? |
2288 | clevermous | 140 | ; Bitfield. Currently only DISK_NO_INSERT_NOTIFICATION bit is defined. |
141 | ; If it is set, the driver will never issue 'disk_media_changed' notification |
||
142 | ; with argument set to true, so the kernel must try to detect media during |
||
143 | ; requests from the file system. |
||
2381 | hidnplayr | 144 | RefCount dd ? |
2288 | clevermous | 145 | ; Count of active references to this structure. One reference is kept during |
146 | ; the lifetime of the structure between 'disk_add' and 'disk_del'. |
||
147 | ; Another reference is taken during any filesystem operation for this disk. |
||
148 | ; One reference is added if media is inserted. |
||
149 | ; The structure is destroyed when the reference count decrements to zero: |
||
150 | ; this usually occurs in 'disk_del', but can be delayed to the end of last |
||
151 | ; filesystem operation, if one is active. |
||
2381 | hidnplayr | 152 | MediaLock MUTEX |
2288 | clevermous | 153 | ; Lock to protect the MEDIA structure. See the description after |
154 | ; 'disk_list_mutex' for the locking strategy. |
||
155 | ; Fields of media object |
||
2381 | hidnplayr | 156 | MediaInserted db ? |
2288 | clevermous | 157 | ; 0 if media is not inserted, nonzero otherwise. |
2381 | hidnplayr | 158 | MediaUsed db ? |
2288 | clevermous | 159 | ; 0 if media fields are not used, nonzero otherwise. If .MediaRefCount is |
160 | ; nonzero, this field is nonzero too; however, when .MediaRefCount goes |
||
161 | ; to zero, there is some time interval during which media object is still used. |
||
3460 | clevermous | 162 | dw ? ; padding |
2288 | clevermous | 163 | ; The following fields are not valid unless either .MediaInserted is nonzero |
164 | ; or they are accessed from a code which has obtained the reference when |
||
165 | ; .MediaInserted was nonzero. |
||
2381 | hidnplayr | 166 | MediaRefCount dd ? |
2288 | clevermous | 167 | ; Count of active references to the media object. One reference is kept during |
168 | ; the lifetime of the media between two calls to 'disk_media_changed'. |
||
169 | ; Another reference is taken during any filesystem operation for this media. |
||
170 | ; The callback 'closemedia' is called when the reference count decrements to |
||
171 | ; zero: this usually occurs in 'disk_media_changed', but can be delayed to the |
||
172 | ; end of the last filesystem operation, if one is active. |
||
2381 | hidnplayr | 173 | MediaInfo DISKMEDIAINFO |
2288 | clevermous | 174 | ; This field keeps information on the current media. |
2381 | hidnplayr | 175 | NumPartitions dd ? |
2288 | clevermous | 176 | ; Number of partitions on this media. |
2381 | hidnplayr | 177 | Partitions dd ? |
2288 | clevermous | 178 | ; Pointer to array of .NumPartitions pointers to PARTITION structures. |
2381 | hidnplayr | 179 | cache_size dd ? |
2288 | clevermous | 180 | ; inherited from cache_ideX_size |
4437 | clevermous | 181 | CacheLock MUTEX |
182 | ; Lock to protect both caches. |
||
2381 | hidnplayr | 183 | SysCache DISKCACHE |
184 | AppCache DISKCACHE |
||
2288 | clevermous | 185 | ; Two caches for the disk. |
186 | ends |
||
187 | |||
188 | ; This structure represents one partition for the kernel. This is a base |
||
189 | ; template, the actual contents after common fields is determined by the |
||
190 | ; file system code for this partition. |
||
2381 | hidnplayr | 191 | struct PARTITION |
192 | FirstSector dq ? |
||
2288 | clevermous | 193 | ; First sector of the partition. |
2381 | hidnplayr | 194 | Length dq ? |
2288 | clevermous | 195 | ; Length of the partition in sectors. |
2381 | hidnplayr | 196 | Disk dd ? |
2288 | clevermous | 197 | ; Pointer to parent DISK structure. |
2381 | hidnplayr | 198 | FSUserFunctions dd ? |
2288 | clevermous | 199 | ; Handlers for the sysfunction 70h. This field is a pointer to the following |
3742 | clevermous | 200 | ; array. The first dword is pointer to disconnect handler. |
201 | ; The first dword is a number of supported subfunctions, other dwords |
||
2288 | clevermous | 202 | ; point to handlers of corresponding subfunctions. |
203 | ; ...fs-specific data may follow... |
||
204 | ends |
||
205 | |||
206 | ; This is an external structure, it represents an entry in the partition table. |
||
2381 | hidnplayr | 207 | struct PARTITION_TABLE_ENTRY |
208 | Bootable db ? |
||
2288 | clevermous | 209 | ; 80h = bootable partition, 0 = non-bootable partition, other values = invalid |
2381 | hidnplayr | 210 | FirstHead db ? |
211 | FirstSector db ? |
||
212 | FirstTrack db ? |
||
2288 | clevermous | 213 | ; Coordinates of first sector in CHS. |
2381 | hidnplayr | 214 | Type db ? |
2288 | clevermous | 215 | ; Partition type, one of predefined constants. 0 = empty, several types denote |
216 | ; extended partition (see process_partition_table_entry), we are not interested |
||
217 | ; in other values. |
||
2381 | hidnplayr | 218 | LastHead db ? |
219 | LastSector db ? |
||
220 | LastTrack db ? |
||
2288 | clevermous | 221 | ; Coordinates of last sector in CHS. |
2381 | hidnplayr | 222 | FirstAbsSector dd ? |
2288 | clevermous | 223 | ; Coordinate of first sector in LBA. |
2381 | hidnplayr | 224 | Length dd ? |
2288 | clevermous | 225 | ; Length of the partition in sectors. |
226 | ends |
||
227 | |||
6827 | dunkaist | 228 | ; GUID Partition Table Header, UEFI 2.6, Table 18 |
229 | struct GPTH |
||
230 | Signature rb 8 |
||
231 | ; 'EFI PART' |
||
232 | Revision dd ? |
||
233 | ; 0x00010000 |
||
234 | HeaderSize dd ? |
||
235 | ; Size of this header in bytes, must fit to one sector. |
||
236 | HeaderCRC32 dd ? |
||
237 | ; Set this field to zero, compute CRC32 via 0xEDB88320, compare. |
||
238 | Reserved dd ? |
||
6828 | dunkaist | 239 | ; Must be zero. |
6827 | dunkaist | 240 | MyLBA dq ? |
241 | ; LBA of the sector containing this GPT header. |
||
242 | AlternateLBA dq ? |
||
243 | ; LBA of the sector containing the other GPT header. |
||
244 | ; AlternateLBA of Primary GPTH points to Backup one and vice versa. |
||
245 | FirstUsableLBA dq ? |
||
246 | ; Only sectors between first and last UsableLBA may form partitions |
||
247 | LastUsableLBA dq ? |
||
248 | DiskGUID rb 16 |
||
249 | ; Globally Unique IDentifier |
||
250 | PartitionEntryLBA dq ? |
||
251 | ; First LBA of Partition Entry Array. |
||
252 | ; Length in bytes is computed as a product of two following fields. |
||
253 | NumberOfPartitionEntries dd ? |
||
254 | ; Actual number of partitions depends on the contents of Partition Entry Array. |
||
255 | ; A partition entry is unused if zeroed. |
||
256 | SizeOfPartitionEntry dd ? ; in bytes |
||
257 | PartitionEntryArrayCRC32 dd ? |
||
258 | ; Same CRC as for GPT header. |
||
259 | ends |
||
260 | |||
261 | ; GPT Partition Entry, UEFI 2.6, Table 19 |
||
262 | struct GPE |
||
263 | PartitionTypeGUID rb 16 |
||
264 | UniquePartitionGUID rb 16 |
||
265 | StartingLBA dq ? |
||
266 | EndingLBA dq ? |
||
267 | ; Length in sectors is EndingLBA - StartingLBA + 1. |
||
268 | Attributes dq ? |
||
269 | PartitionName rb 72 |
||
270 | ends |
||
271 | |||
2288 | clevermous | 272 | ; ============================================================================= |
273 | ; ================================ Global data ================================ |
||
274 | ; ============================================================================= |
||
275 | iglobal |
||
276 | ; The pseudo-item for the list of all DISK structures. |
||
277 | ; Initialized to the empty list. |
||
278 | disk_list: |
||
279 | dd disk_list |
||
280 | dd disk_list |
||
281 | endg |
||
282 | uglobal |
||
283 | ; This mutex guards all operations with the global list of DISK structures. |
||
284 | disk_list_mutex MUTEX |
||
285 | ; * There are two dependent objects, a disk and a media. In the simplest case, |
||
286 | ; disk and media are both non-removable. However, in the general case both |
||
287 | ; can be removed at any time, simultaneously or only media,and this makes things |
||
288 | ; complicated. |
||
289 | ; * For efficiency, both disk and media objects are located in the one |
||
290 | ; structure named DISK. However, logically they are different. |
||
291 | ; * The following operations use data of disk object: adding (disk_add); |
||
292 | ; deleting (disk_del); filesystem (fs_lfn which eventually calls |
||
293 | ; dyndisk_handler or dyndisk_enum_root). |
||
294 | ; * The following operations use data of media object: adding/removing |
||
295 | ; (disk_media_changed); filesystem (fs_lfn which eventually calls |
||
296 | ; dyndisk_handler; dyndisk_enum_root doesn't work with media). |
||
297 | ; * Notifications disk_add, disk_media_changed, disk_del are synchronized |
||
298 | ; between themselves, this is a requirement for the driver. However, file |
||
299 | ; system operations are asynchronous, can be issued at any time by any |
||
300 | ; thread. |
||
301 | ; * We must prevent a situation when a filesystem operation thinks that the |
||
302 | ; object is still valid but in fact the notification has destroyed the |
||
303 | ; object. So we keep a reference counter for both disk and media and destroy |
||
304 | ; the object when this counter goes to zero. |
||
305 | ; * The driver must know when it is safe to free driver-allocated resources. |
||
306 | ; The object can be alive even after death notification has completed. |
||
307 | ; We use special callbacks to satisfy both assertions: 'close' for the disk |
||
308 | ; and 'closemedia' for the media. The destruction of the object includes |
||
309 | ; calling the corresponding callback. |
||
310 | ; * Each filesystem operation keeps one reference for the disk and one |
||
311 | ; reference for the media. Notification disk_del forces notification on the |
||
312 | ; media death, so the reference counter for the disk is always not less than |
||
313 | ; the reference counter for the media. |
||
314 | ; * Two operations "get the object" and "increment the reference counter" can |
||
315 | ; not be done simultaneously. We use a mutex to guard the consistency here. |
||
316 | ; It must be a part of the container for the object, so that this mutex can |
||
317 | ; be acquired as a part of getting the object from the container. The |
||
318 | ; container for disk object is the global list, and this list is guarded by |
||
319 | ; 'disk_list_mutex'. The container for media object is the disk object, and |
||
320 | ; the corresponding mutex is DISK.MediaLock. |
||
321 | ; * Notifications do not change the data of objects, they can only remove |
||
322 | ; objects. Thus we don't need another synchronization at this level. If two |
||
323 | ; filesystem operations are referencing the same filesystem data, this is |
||
324 | ; better resolved at the level of the filesystem. |
||
325 | endg |
||
326 | |||
327 | iglobal |
||
5089 | clevermous | 328 | ; The function 'disk_scan_partitions' needs three sector-sized buffers for |
2288 | clevermous | 329 | ; MBR, bootsector and fs-temporary sector data. It can not use the static |
330 | ; buffers always, since it can be called for two or more disks in parallel. |
||
331 | ; However, this case is not typical. We reserve three static 512-byte buffers |
||
332 | ; and a flag that these buffers are currently used. If 'disk_scan_partitions' |
||
333 | ; detects that the buffers are currently used, it allocates buffers from the |
||
5089 | clevermous | 334 | ; heap. Also, the heap is used when sector size is other than 512. |
2288 | clevermous | 335 | ; The flag is implemented as a global dword variable. When the static buffers |
336 | ; are not used, the value is -1. When the static buffers are used, the value |
||
337 | ; is normally 0 and temporarily can become greater. The function increments |
||
338 | ; this value. If the resulting value is zero, it uses the buffers and |
||
339 | ; decrements the value when the job is done. Otherwise, it immediately |
||
340 | ; decrements the value and uses buffers from the heap, allocated in the |
||
341 | ; beginning and freed in the end. |
||
342 | partition_buffer_users dd -1 |
||
343 | endg |
||
344 | uglobal |
||
345 | ; The static buffers for MBR, bootsector and fs-temporary sector data. |
||
346 | align 16 |
||
347 | mbr_buffer rb 512 |
||
348 | bootsect_buffer rb 512 |
||
349 | fs_tmp_buffer rb 512 |
||
350 | endg |
||
351 | |||
352 | iglobal |
||
353 | ; This is the array of default implementations of driver callbacks. |
||
354 | ; Same as DRIVERFUNC structure except for the first field; all functions must |
||
355 | ; have the default implementations. |
||
356 | align 4 |
||
357 | disk_default_callbacks: |
||
358 | dd disk_default_close |
||
359 | dd disk_default_closemedia |
||
360 | dd disk_default_querymedia |
||
361 | dd disk_default_read |
||
362 | dd disk_default_write |
||
363 | dd disk_default_flush |
||
364 | dd disk_default_adjust_cache_size |
||
365 | endg |
||
366 | |||
367 | ; ============================================================================= |
||
368 | ; ================================= Functions ================================= |
||
369 | ; ============================================================================= |
||
370 | |||
371 | ; This function registers a disk device. |
||
372 | ; This includes: |
||
373 | ; - allocating an internal structure describing this device; |
||
374 | ; - registering this structure in the global filesystem. |
||
375 | ; The function initializes the disk as if there is no media. If a media is |
||
376 | ; present, the function 'disk_media_changed' should be called after this |
||
377 | ; function succeeds. |
||
378 | ; Parameters: |
||
379 | ; [esp+4] = pointer to DISKFUNC structure with the callbacks |
||
380 | ; [esp+8] = pointer to name (ASCIIZ string) |
||
381 | ; [esp+12] = userdata to be passed to the callbacks as is. |
||
382 | ; [esp+16] = flags, bitfield. Currently only DISK_NO_INSERT_NOTIFICATION bit |
||
383 | ; is defined. |
||
384 | ; Return value: |
||
385 | ; NULL = operation has failed |
||
386 | ; non-NULL = handle of the disk. This handle can be used |
||
387 | ; in the operations with other Disk* functions. |
||
388 | ; The handle is the pointer to the internal structure DISK. |
||
389 | disk_add: |
||
390 | push ebx esi ; save used registers to be stdcall |
||
391 | ; 1. Allocate the DISK structure. |
||
392 | ; 1a. Call the heap manager. |
||
3598 | clevermous | 393 | movi eax, sizeof.DISK |
2288 | clevermous | 394 | call malloc |
395 | ; 1b. Check the result. If allocation failed, return (go to 9) with eax = 0. |
||
396 | test eax, eax |
||
397 | jz .nothing |
||
398 | ; 2. Copy the disk name to the DISK structure. |
||
399 | ; 2a. Get length of the name, including the terminating zero. |
||
400 | mov ebx, [esp+8+8] ; ebx = pointer to name |
||
401 | push eax ; save allocated pointer to DISK |
||
402 | xor eax, eax ; the argument of malloc() is in eax |
||
403 | @@: |
||
404 | inc eax |
||
405 | cmp byte [ebx+eax-1], 0 |
||
406 | jnz @b |
||
3681 | clevermous | 407 | ; 2b. Call the heap manager. |
2288 | clevermous | 408 | call malloc |
409 | ; 2c. Check the result. If allocation failed, go to 7. |
||
410 | pop esi ; restore allocated pointer to DISK |
||
411 | test eax, eax |
||
412 | jz .free |
||
413 | ; 2d. Store the allocated pointer to the DISK structure. |
||
414 | mov [esi+DISK.Name], eax |
||
415 | ; 2e. Copy the name. |
||
416 | @@: |
||
417 | mov dl, [ebx] |
||
418 | mov [eax], dl |
||
419 | inc ebx |
||
420 | inc eax |
||
421 | test dl, dl |
||
422 | jnz @b |
||
423 | ; 3. Copy other arguments of the function to the DISK structure. |
||
424 | mov eax, [esp+4+8] |
||
425 | mov [esi+DISK.Functions], eax |
||
426 | mov eax, [esp+12+8] |
||
427 | mov [esi+DISK.UserData], eax |
||
428 | mov eax, [esp+16+8] |
||
429 | mov [esi+DISK.DriverFlags], eax |
||
430 | ; 4. Initialize other fields of the DISK structure. |
||
431 | ; Media is not inserted, reference counter is 1. |
||
432 | lea ecx, [esi+DISK.MediaLock] |
||
433 | call mutex_init |
||
434 | xor eax, eax |
||
435 | mov dword [esi+DISK.MediaInserted], eax |
||
3460 | clevermous | 436 | mov [esi+DISK.MediaRefCount], eax |
2288 | clevermous | 437 | inc eax |
438 | mov [esi+DISK.RefCount], eax |
||
439 | ; The DISK structure is initialized. |
||
440 | ; 5. Insert the new structure to the global list. |
||
441 | ; 5a. Acquire the mutex. |
||
442 | mov ecx, disk_list_mutex |
||
443 | call mutex_lock |
||
444 | ; 5b. Insert item to the tail of double-linked list. |
||
445 | mov edx, disk_list |
||
446 | list_add_tail esi, edx ;esi= new edx= list head |
||
447 | ; 5c. Release the mutex. |
||
448 | call mutex_unlock |
||
449 | ; 6. Return with eax = pointer to DISK. |
||
450 | xchg eax, esi |
||
451 | jmp .nothing |
||
452 | .free: |
||
453 | ; Memory allocation for DISK structure succeeded, but for disk name failed. |
||
454 | ; 7. Free the DISK structure. |
||
455 | xchg eax, esi |
||
456 | call free |
||
457 | ; 8. Return with eax = 0. |
||
458 | xor eax, eax |
||
459 | .nothing: |
||
460 | ; 9. Return. |
||
461 | pop esi ebx ; restore used registers to be stdcall |
||
462 | ret 16 ; purge 4 dword arguments to be stdcall |
||
463 | |||
464 | ; This function deletes a disk device from the global filesystem. |
||
465 | ; This includes: |
||
466 | ; - removing a media including all partitions; |
||
467 | ; - deleting this structure from the global filesystem; |
||
468 | ; - dereferencing the DISK structure and possibly destroying it. |
||
469 | ; Parameters: |
||
470 | ; [esp+4] = handle of the disk, i.e. the pointer to the DISK structure. |
||
471 | ; Return value: none. |
||
472 | disk_del: |
||
473 | push esi ; save used registers to be stdcall |
||
474 | ; 1. Force media to be removed. If the media is already removed, the |
||
475 | ; call does nothing. |
||
2643 | clevermous | 476 | mov esi, [esp+4+4] ; esi = handle of the disk |
2288 | clevermous | 477 | stdcall disk_media_changed, esi, 0 |
478 | ; 2. Delete the structure from the global list. |
||
479 | ; 2a. Acquire the mutex. |
||
480 | mov ecx, disk_list_mutex |
||
481 | call mutex_lock |
||
482 | ; 2b. Delete item from double-linked list. |
||
483 | mov eax, [esi+DISK.Next] |
||
484 | mov edx, [esi+DISK.Prev] |
||
485 | mov [eax+DISK.Prev], edx |
||
486 | mov [edx+DISK.Next], eax |
||
487 | ; 2c. Release the mutex. |
||
488 | call mutex_unlock |
||
489 | ; 3. The structure still has one reference created in disk_add. Remove this |
||
490 | ; reference. If there are no other references, disk_dereference will free the |
||
491 | ; structure. |
||
492 | call disk_dereference |
||
493 | ; 4. Return. |
||
494 | pop esi ; restore used registers to be stdcall |
||
495 | ret 4 ; purge 1 dword argument to be stdcall |
||
496 | |||
497 | ; This is an internal function which removes a previously obtained reference |
||
498 | ; to the disk. If this is the last reference, this function lets the driver |
||
499 | ; finalize all associated data, and afterwards frees the DISK structure. |
||
500 | ; esi = pointer to DISK structure |
||
501 | disk_dereference: |
||
502 | ; 1. Decrement reference counter. Use atomic operation to correctly handle |
||
503 | ; possible simultaneous calls. |
||
504 | lock dec [esi+DISK.RefCount] |
||
505 | ; 2. If the result is nonzero, there are other references, so nothing to do. |
||
506 | ; In this case, return (go to 4). |
||
507 | jnz .nothing |
||
508 | ; 3. If we are here, we just removed the last reference and must destroy the |
||
509 | ; disk object. |
||
510 | ; 3a. Call the driver. |
||
511 | mov al, DISKFUNC.close |
||
512 | stdcall disk_call_driver |
||
513 | ; 3b. Free the structure. |
||
514 | xchg eax, esi |
||
3202 | clevermous | 515 | push ebx |
2288 | clevermous | 516 | call free |
3202 | clevermous | 517 | pop ebx |
2288 | clevermous | 518 | ; 4. Return. |
519 | .nothing: |
||
520 | ret |
||
521 | |||
522 | ; This is an internal function which removes a previously obtained reference |
||
523 | ; to the media. If this is the last reference, this function calls 'closemedia' |
||
524 | ; callback to signal the driver that the processing has finished and it is safe |
||
525 | ; to inform about a new media. |
||
526 | ; esi = pointer to DISK structure |
||
527 | disk_media_dereference: |
||
528 | ; 1. Decrement reference counter. Use atomic operation to correctly handle |
||
529 | ; possible simultaneous calls. |
||
530 | lock dec [esi+DISK.MediaRefCount] |
||
531 | ; 2. If the result is nonzero, there are other references, so nothing to do. |
||
532 | ; In this case, return (go to 4). |
||
533 | jnz .nothing |
||
534 | ; 3. If we are here, we just removed the last reference and must destroy the |
||
535 | ; media object. |
||
536 | ; Note that the same place inside the DISK structure is reused for all media |
||
537 | ; objects, so we must guarantee that reusing does not happen while freeing. |
||
538 | ; Reusing is only possible when someone processes a new media. There are two |
||
539 | ; mutually exclusive variants: |
||
540 | ; * driver issues media insert notifications (DISK_NO_INSERT_NOTIFICATION bit |
||
541 | ; in DISK.DriverFlags is not set). In this case, we require from the driver |
||
542 | ; that such notification (except for the first one) can occur only after a |
||
543 | ; call to 'closemedia' callback. |
||
544 | ; * driver does not issue media insert notifications. In this case, the kernel |
||
545 | ; itself must sometimes check whether media is inserted. We have the flag |
||
546 | ; DISK.MediaUsed, visible to the kernel. This flag signals to the other parts |
||
547 | ; of kernel that the way is free. |
||
548 | ; In the first case other parts of the kernel do not use DISK.MediaUsed, so it |
||
549 | ; does not matter when this flag is cleared. In the second case this flag must |
||
550 | ; be cleared after all other actions, including call to 'closemedia'. |
||
551 | ; 3a. Free all partitions. |
||
552 | push esi edi |
||
553 | mov edi, [esi+DISK.NumPartitions] |
||
554 | mov esi, [esi+DISK.Partitions] |
||
555 | test edi, edi |
||
556 | jz .nofree |
||
557 | .freeloop: |
||
558 | lodsd |
||
3742 | clevermous | 559 | mov ecx, [eax+PARTITION.FSUserFunctions] |
560 | call dword [ecx] |
||
2288 | clevermous | 561 | dec edi |
562 | jnz .freeloop |
||
563 | .nofree: |
||
564 | pop edi esi |
||
565 | ; 3b. Free the cache. |
||
566 | call disk_free_cache |
||
567 | ; 3c. Call the driver. |
||
568 | mov al, DISKFUNC.closemedia |
||
569 | stdcall disk_call_driver |
||
570 | ; 3d. Clear the flag. |
||
571 | mov [esi+DISK.MediaUsed], 0 |
||
572 | .nothing: |
||
573 | ret |
||
574 | |||
575 | ; This function is called by the driver and informs the kernel that the media |
||
576 | ; has changed. If the media is non-removable, it is called exactly once |
||
577 | ; immediately after 'disk_add' and once from 'disk_del'. |
||
578 | ; Parameters: |
||
579 | ; [esp+4] = handle of the disk, i.e. the pointer to the DISK structure. |
||
580 | ; [esp+8] = new status of the media: zero = no media, nonzero = media inserted. |
||
581 | disk_media_changed: |
||
582 | push ebx esi edi ; save used registers to be stdcall |
||
583 | ; 1. Remove the existing media, if it is present. |
||
584 | mov esi, [esp+4+12] ; esi = pointer to DISK |
||
585 | ; 1a. Check whether it is present. Since DISK.MediaInserted is changed only |
||
586 | ; in this function and calls to this function are synchronized, no lock is |
||
587 | ; required for checking. |
||
588 | cmp [esi+DISK.MediaInserted], 0 |
||
589 | jz .noremove |
||
590 | ; We really need to remove the media. |
||
591 | ; 1b. Acquire mutex. |
||
592 | lea ecx, [esi+DISK.MediaLock] |
||
593 | call mutex_lock |
||
594 | ; 1c. Clear the flag. |
||
595 | mov [esi+DISK.MediaInserted], 0 |
||
596 | ; 1d. Release mutex. |
||
597 | call mutex_unlock |
||
598 | ; 1e. Remove the "lifetime" reference and possibly destroy the structure. |
||
599 | call disk_media_dereference |
||
600 | .noremove: |
||
601 | ; 2. Test whether there is new media. |
||
602 | cmp dword [esp+8+12], 0 |
||
603 | jz .noinsert |
||
604 | ; Yep, there is. |
||
605 | ; 3. Process the new media. We assume that all media fields are available to |
||
606 | ; use, see comments in 'disk_media_dereference' (this covers using by previous |
||
607 | ; media referencers) and note that calls to this function are synchronized |
||
608 | ; (this covers using by new media referencers). |
||
609 | ; 3a. Call the 'querymedia' callback. |
||
610 | ; .Flags are set to zero for possible future extensions. |
||
611 | lea edx, [esi+DISK.MediaInfo] |
||
10053 | Doczom | 612 | and [edx+DISKMEDIAINFO.LastSessionSector], 0 |
2288 | clevermous | 613 | and [edx+DISKMEDIAINFO.Flags], 0 |
614 | mov al, DISKFUNC.querymedia |
||
615 | stdcall disk_call_driver, edx |
||
616 | ; 3b. Check the result of the callback. Abort if it failed. |
||
617 | test eax, eax |
||
618 | jnz .noinsert |
||
619 | ; 3c. Allocate the cache unless disabled by the driver. Abort if failed. |
||
620 | call disk_init_cache |
||
621 | test al, al |
||
622 | jz .noinsert |
||
623 | ; 3d. Acquire the lifetime reference for the media object. |
||
624 | inc [esi+DISK.MediaRefCount] |
||
625 | ; 3e. Scan for partitions. Ignore result; the list of partitions is valid even |
||
626 | ; on errors. |
||
627 | call disk_scan_partitions |
||
628 | ; 3f. Media is inserted and available for use. |
||
629 | inc [esi+DISK.MediaInserted] |
||
630 | .noinsert: |
||
631 | ; 4. Return. |
||
632 | pop edi esi ebx ; restore used registers to be stdcall |
||
633 | ret 8 ; purge 2 dword arguments to be stdcall |
||
634 | |||
635 | ; This function is a thunk for all functions of a disk driver. |
||
636 | ; It checks whether the referenced function is implemented in the driver. |
||
637 | ; If so, this function jumps to the function in the driver. |
||
638 | ; Otherwise, it jumps to the default implementation. |
||
639 | ; al = offset of function in the DISKFUNC structure; |
||
640 | ; esi = pointer to the DISK structure; |
||
641 | ; stack is the same as for the corresponding function except that the |
||
642 | ; first parameter (void* userdata) is prepended automatically. |
||
643 | disk_call_driver: |
||
644 | movzx eax, al ; eax = offset of function in the DISKFUNC structure |
||
645 | ; 1. Prepend the first argument to the stack. |
||
646 | pop ecx ; ecx = return address |
||
647 | push [esi+DISK.UserData] ; add argument |
||
648 | push ecx ; save return address |
||
649 | ; 2. Check that the required function is inside the table. If not, go to 5. |
||
650 | mov ecx, [esi+DISK.Functions] |
||
651 | cmp eax, [ecx+DISKFUNC.strucsize] |
||
652 | jae .default |
||
653 | ; 3. Check that the required function is implemented. If not, go to 5. |
||
654 | mov ecx, [ecx+eax] |
||
655 | test ecx, ecx |
||
656 | jz .default |
||
657 | ; 4. Jump to the required function. |
||
658 | jmp ecx |
||
659 | .default: |
||
660 | ; 5. Driver does not implement the required function; use default implementation. |
||
661 | jmp dword [disk_default_callbacks+eax-4] |
||
662 | |||
663 | ; The default implementation of DISKFUNC.querymedia. |
||
664 | disk_default_querymedia: |
||
3598 | clevermous | 665 | movi eax, DISK_STATUS_INVALID_CALL |
2288 | clevermous | 666 | ret 8 |
667 | |||
668 | ; The default implementation of DISKFUNC.read and DISKFUNC.write. |
||
669 | disk_default_read: |
||
670 | disk_default_write: |
||
3598 | clevermous | 671 | movi eax, DISK_STATUS_INVALID_CALL |
2288 | clevermous | 672 | ret 20 |
673 | |||
674 | ; The default implementation of DISKFUNC.close, DISKFUNC.closemedia and |
||
675 | ; DISKFUNC.flush. |
||
676 | disk_default_close: |
||
677 | disk_default_closemedia: |
||
678 | disk_default_flush: |
||
679 | xor eax, eax |
||
680 | ret 4 |
||
681 | |||
682 | ; The default implementation of DISKFUNC.adjust_cache_size. |
||
683 | disk_default_adjust_cache_size: |
||
3164 | clevermous | 684 | mov eax, [esp+8] |
685 | ret 8 |
||
2288 | clevermous | 686 | |
687 | ; This is an internal function called from 'disk_media_changed' when a new media |
||
688 | ; is detected. It creates the list of partitions for the media. |
||
689 | ; If media is not partitioned, then the list consists of one partition which |
||
690 | ; covers all the media. |
||
691 | ; esi = pointer to the DISK structure. |
||
692 | disk_scan_partitions: |
||
693 | ; 1. Initialize .NumPartitions and .Partitions fields as zeros: empty list. |
||
694 | and [esi+DISK.NumPartitions], 0 |
||
695 | and [esi+DISK.Partitions], 0 |
||
5089 | clevermous | 696 | ; 2. Acquire the buffer for MBR and bootsector tests. See the comment before |
2288 | clevermous | 697 | ; the 'partition_buffer_users' variable. |
5089 | clevermous | 698 | mov eax, [esi+DISK.MediaInfo.SectorSize] |
699 | cmp eax, 512 |
||
700 | jnz @f |
||
2288 | clevermous | 701 | mov ebx, mbr_buffer ; assume the global buffer is free |
702 | lock inc [partition_buffer_users] |
||
703 | jz .buffer_acquired ; yes, it is free |
||
704 | lock dec [partition_buffer_users] ; no, we must allocate |
||
5089 | clevermous | 705 | @@: |
706 | lea eax, [eax*3] |
||
707 | stdcall kernel_alloc, eax |
||
2288 | clevermous | 708 | test eax, eax |
709 | jz .nothing |
||
710 | xchg eax, ebx |
||
711 | .buffer_acquired: |
||
712 | ; MBR/EBRs are organized in the chain. We use a loop over MBR/EBRs, but no |
||
713 | ; more than MAX_NUM_PARTITION times. |
||
5089 | clevermous | 714 | ; 3. Prepare things for the loop. |
2288 | clevermous | 715 | ; ebp will hold the sector number for current MBR/EBR. |
716 | ; [esp] will hold the sector number for current extended partition, if there |
||
717 | ; is one. |
||
718 | ; [esp+4] will hold the counter that prevents long loops. |
||
719 | push ebp ; save ebp |
||
720 | push MAX_NUM_PARTITIONS ; the counter of max MBRs to process |
||
721 | xor ebp, ebp ; start from sector zero |
||
722 | push ebp ; no extended partition yet |
||
5089 | clevermous | 723 | ; 4. MBR is 512 bytes long. If sector size is less than 512 bytes, |
6827 | dunkaist | 724 | ; assume no MBR, no partitions and go to 11. |
5089 | clevermous | 725 | cmp [esi+DISK.MediaInfo.SectorSize], 512 |
726 | jb .notmbr |
||
2288 | clevermous | 727 | .new_mbr: |
728 | ; 5. Read the current sector. |
||
729 | ; Note that 'read' callback operates with 64-bit sector numbers, so we must |
||
730 | ; push additional zero as a high dword of sector number. |
||
731 | mov al, DISKFUNC.read |
||
732 | push 1 |
||
733 | stdcall disk_call_driver, ebx, ebp, 0, esp |
||
734 | pop ecx |
||
735 | ; 6. If the read has failed, abort the loop. |
||
736 | dec ecx |
||
737 | jnz .mbr_failed |
||
738 | ; 7. Check the MBR/EBR signature. If it is wrong, abort the loop. |
||
739 | ; Soon we will access the partition table which starts at ebx+0x1BE, |
||
740 | ; so we can fill its address right now. If we do it now, then the addressing |
||
741 | ; [ecx+0x40] is shorter than [ebx+0x1fe]: one-byte offset vs 4-bytes offset. |
||
742 | lea ecx, [ebx+0x1be] ; ecx -> partition table |
||
743 | cmp word [ecx+0x40], 0xaa55 |
||
9941 | Doczom | 744 | jnz .notmbr |
2288 | clevermous | 745 | ; 8. The MBR is treated differently from EBRs. For MBR we additionally need to |
6827 | dunkaist | 746 | ; execute step 10 and possibly step 11. |
2288 | clevermous | 747 | test ebp, ebp |
748 | jnz .mbr |
||
6827 | dunkaist | 749 | ; 9. Handle GUID Partition Table |
750 | ; 9a. Check if MBR is protective |
||
751 | call is_protective_mbr |
||
752 | jnz .no_gpt |
||
753 | ; 9b. If so, try to scan GPT headers |
||
754 | call disk_scan_gpt |
||
755 | ; 9c. If any GPT header is valid, ignore MBR |
||
756 | jz .done |
||
757 | ; Otherwise process legacy/protective MBR |
||
758 | .no_gpt: |
||
2288 | clevermous | 759 | ; The partition table can be present or not present. In the first case, we just |
760 | ; read the MBR. In the second case, we just read the bootsector for a |
||
761 | ; filesystem. |
||
762 | ; The following algorithm is used to distinguish between these cases. |
||
763 | ; A. If at least one entry of the partition table is invalid, this is |
||
764 | ; a bootsector. See the description of 'is_partition_table_entry' for |
||
765 | ; definition of validity. |
||
766 | ; B. If all entries are empty (filesystem type field is zero) and the first |
||
767 | ; byte is jmp opcode (0EBh or 0E9h), this is a bootsector which happens to |
||
768 | ; have zeros in the place of partition table. |
||
769 | ; C. Otherwise, this is an MBR. |
||
6827 | dunkaist | 770 | ; 10. Test for MBR vs bootsector. |
771 | ; 10a. Check entries. If any is invalid, go to 11 (rule A). |
||
2288 | clevermous | 772 | call is_partition_table_entry |
773 | jc .notmbr |
||
774 | add ecx, 10h |
||
775 | call is_partition_table_entry |
||
776 | jc .notmbr |
||
777 | add ecx, 10h |
||
778 | call is_partition_table_entry |
||
779 | jc .notmbr |
||
780 | add ecx, 10h |
||
781 | call is_partition_table_entry |
||
782 | jc .notmbr |
||
6827 | dunkaist | 783 | ; 10b. Check types of the entries. If at least one is nonzero, go to 12 (rule C). |
2288 | clevermous | 784 | mov al, [ecx-30h+PARTITION_TABLE_ENTRY.Type] |
785 | or al, [ecx-20h+PARTITION_TABLE_ENTRY.Type] |
||
786 | or al, [ecx-10h+PARTITION_TABLE_ENTRY.Type] |
||
787 | or al, [ecx+PARTITION_TABLE_ENTRY.Type] |
||
788 | jnz .mbr |
||
6827 | dunkaist | 789 | ; 10c. Empty partition table or bootsector with many zeroes? (rule B) |
2288 | clevermous | 790 | cmp byte [ebx], 0EBh |
791 | jz .notmbr |
||
792 | cmp byte [ebx], 0E9h |
||
793 | jnz .mbr |
||
794 | .notmbr: |
||
6827 | dunkaist | 795 | ; 11. This is not an MBR. The media is not partitioned. Create one partition |
2288 | clevermous | 796 | ; which covers all the media and abort the loop. |
797 | stdcall disk_add_partition, 0, 0, \ |
||
3742 | clevermous | 798 | dword [esi+DISK.MediaInfo.Capacity], dword [esi+DISK.MediaInfo.Capacity+4], esi |
2288 | clevermous | 799 | jmp .done |
800 | .mbr: |
||
6827 | dunkaist | 801 | ; 12. Process all entries of the new MBR/EBR |
2288 | clevermous | 802 | lea ecx, [ebx+0x1be] ; ecx -> partition table |
803 | push 0 ; assume no extended partition |
||
804 | call process_partition_table_entry |
||
805 | add ecx, 10h |
||
806 | call process_partition_table_entry |
||
807 | add ecx, 10h |
||
808 | call process_partition_table_entry |
||
809 | add ecx, 10h |
||
810 | call process_partition_table_entry |
||
811 | pop ebp |
||
6827 | dunkaist | 812 | ; 13. Test whether we found a new EBR and should continue the loop. |
813 | ; 13a. If there was no next EBR, return. |
||
2288 | clevermous | 814 | test ebp, ebp |
815 | jz .done |
||
816 | ; Ok, we have EBR. |
||
6827 | dunkaist | 817 | ; 13b. EBRs addresses are relative to the start of extended partition. |
2288 | clevermous | 818 | ; For simplicity, just abort if an 32-bit overflow occurs; large disks |
819 | ; are most likely partitioned with GPT, not MBR scheme, since the precise |
||
820 | ; calculation here would increase limit just twice at the price of big |
||
821 | ; compatibility problems. |
||
822 | pop eax ; load extended partition |
||
823 | add ebp, eax |
||
824 | jc .mbr_failed |
||
6827 | dunkaist | 825 | ; 13c. If extended partition has not yet started, start it. |
2288 | clevermous | 826 | test eax, eax |
827 | jnz @f |
||
828 | mov eax, ebp |
||
829 | @@: |
||
6827 | dunkaist | 830 | ; 13d. If the limit is not exceeded, continue the loop. |
2288 | clevermous | 831 | dec dword [esp] |
832 | push eax ; store extended partition |
||
833 | jnz .new_mbr |
||
834 | .mbr_failed: |
||
835 | .done: |
||
6827 | dunkaist | 836 | ; 14. Cleanup after the loop. |
2288 | clevermous | 837 | pop eax ; not important anymore |
838 | pop eax ; not important anymore |
||
839 | pop ebp ; restore ebp |
||
6827 | dunkaist | 840 | ; 15. Release the buffer. |
841 | ; 15a. Test whether it is the global buffer or we have allocated it. |
||
2288 | clevermous | 842 | cmp ebx, mbr_buffer |
843 | jz .release_partition_buffer |
||
6827 | dunkaist | 844 | ; 15b. If we have allocated it, free it. |
2288 | clevermous | 845 | xchg eax, ebx |
846 | call free |
||
847 | jmp .nothing |
||
6827 | dunkaist | 848 | ; 15c. Otherwise, release reference. |
2288 | clevermous | 849 | .release_partition_buffer: |
850 | lock dec [partition_buffer_users] |
||
851 | .nothing: |
||
6827 | dunkaist | 852 | ; 16. Return. |
2288 | clevermous | 853 | ret |
854 | |||
6827 | dunkaist | 855 | |
856 | ; This function is called from disk_scan_partitions to validate and parse |
||
857 | ; primary and backup GPTs. |
||
858 | proc disk_scan_gpt |
||
6828 | dunkaist | 859 | push ecx |
6827 | dunkaist | 860 | ; Scan primary GPT (second sector) |
861 | stdcall scan_gpt, 1, 0 |
||
862 | test eax, eax |
||
863 | ; There is no code to restore backup GPT if it's corrupt. |
||
864 | ; Therefore just exit if Primary GPT has been parsed successfully. |
||
865 | jz .exit |
||
866 | DEBUGF 1, 'K : Primary GPT is corrupt, trying backup one\n' |
||
867 | mov eax, dword[esi+DISK.MediaInfo.Capacity+0] |
||
868 | mov edx, dword[esi+DISK.MediaInfo.Capacity+4] |
||
869 | sub eax, 1 |
||
870 | sbb edx, 0 |
||
871 | ; Scan backup GPT (last sector) |
||
872 | stdcall scan_gpt, eax, edx |
||
873 | test eax, eax |
||
874 | jz .exit |
||
875 | DEBUGF 1, 'K : Backup GPT is also corrupt, fallback to legacy MBR\n' |
||
876 | .exit: |
||
877 | ; Return value is ZF |
||
6828 | dunkaist | 878 | pop ecx |
6827 | dunkaist | 879 | ret |
880 | endp |
||
881 | |||
882 | |||
883 | ; This function is called from disk_scan_gpt to process a single GPT. |
||
884 | proc scan_gpt _mylba:qword |
||
885 | locals |
||
886 | GPEA_len dd ? ; Length of GPT Partition Entry Array in bytes |
||
887 | endl |
||
888 | push ebx edi |
||
889 | ; Allocalte memory for GPT header |
||
890 | mov eax, [esi+DISK.MediaInfo.SectorSize] |
||
891 | stdcall kernel_alloc, eax |
||
892 | test eax, eax |
||
893 | jz .fail |
||
894 | ; Save pointer to stack, just in case |
||
895 | push eax |
||
896 | mov ebx, eax |
||
897 | ; Read GPT header |
||
898 | mov al, DISKFUNC.read |
||
899 | push 1 |
||
900 | stdcall disk_call_driver, ebx, dword[_mylba+0], dword[_mylba+4], esp |
||
901 | pop ecx |
||
902 | test eax, eax |
||
903 | jnz .fail_free_gpt |
||
904 | ; Check signature |
||
905 | cmp dword[ebx+GPTH.Signature+0], 'EFI ' |
||
906 | jnz .fail_free_gpt |
||
907 | cmp dword[ebx+GPTH.Signature+4], 'PART' |
||
908 | jnz .fail_free_gpt |
||
909 | ; Check Revision |
||
910 | cmp [ebx+GPTH.Revision], 0x00010000 |
||
911 | jnz .fail_free_gpt |
||
912 | ; Compute and check CRC32 |
||
913 | xor edx, edx |
||
914 | xchg edx, [ebx+GPTH.HeaderCRC32] |
||
915 | mov eax, -1 |
||
916 | stdcall crc_32, 0xEDB88320, ebx, [ebx+GPTH.HeaderSize] |
||
917 | xor eax, -1 |
||
918 | cmp eax, edx |
||
919 | jnz .fail_free_gpt |
||
920 | ; Reserved must be zero |
||
921 | cmp [ebx+GPTH.Reserved], 0 |
||
922 | jnz .fail_free_gpt |
||
923 | ; MyLBA of GPT header at LBA X must equal X |
||
924 | mov eax, dword[ebx+GPTH.MyLBA+0] |
||
925 | mov edx, dword[ebx+GPTH.MyLBA+4] |
||
926 | cmp eax, dword[_mylba+0] |
||
927 | jnz .fail_free_gpt |
||
928 | cmp edx, dword[_mylba+4] |
||
929 | jnz .fail_free_gpt |
||
930 | ; Capacity - MyLBA = AlternateLBA |
||
931 | mov eax, dword[esi+DISK.MediaInfo.Capacity+0] |
||
932 | mov edx, dword[esi+DISK.MediaInfo.Capacity+4] |
||
933 | sub eax, dword[_mylba+0] |
||
934 | sbb edx, dword[_mylba+4] |
||
935 | cmp eax, dword[ebx+GPTH.AlternateLBA+0] |
||
6844 | dunkaist | 936 | jnz .fail_free_gpt |
6827 | dunkaist | 937 | cmp edx, dword[ebx+GPTH.AlternateLBA+4] |
6844 | dunkaist | 938 | jnz .fail_free_gpt |
6827 | dunkaist | 939 | |
940 | ; Compute GPT Partition Entry Array (GPEA) length in bytes |
||
941 | mov eax, [ebx+GPTH.NumberOfPartitionEntries] |
||
942 | mul [ebx+GPTH.SizeOfPartitionEntry] |
||
943 | test edx, edx ; far too big |
||
944 | jnz .fail_free_gpt |
||
945 | ; Round up to sector boundary |
||
946 | mov ecx, [esi+DISK.MediaInfo.SectorSize] ; power of two |
||
947 | dec ecx |
||
948 | add eax, ecx |
||
949 | jc .fail_free_gpt ; too big |
||
950 | not ecx |
||
951 | and eax, ecx |
||
952 | ; We will need this length to compute CRC32 of GPEA |
||
953 | mov [GPEA_len], eax |
||
954 | ; Allocate memory for GPEA |
||
955 | stdcall kernel_alloc, eax |
||
956 | test eax, eax |
||
957 | jz .fail_free_gpt |
||
958 | ; Save to not juggle with registers |
||
959 | push eax |
||
960 | mov edi, eax |
||
961 | mov eax, [GPEA_len] |
||
962 | xor edx, edx |
||
963 | ; Get the number of sectors GPEA fits into |
||
964 | div [esi+DISK.MediaInfo.SectorSize] |
||
965 | push eax ; esp = pointer to the number of sectors |
||
966 | mov al, DISKFUNC.read |
||
967 | stdcall disk_call_driver, edi, dword[ebx+GPTH.PartitionEntryLBA+0], \ |
||
968 | dword[ebx+GPTH.PartitionEntryLBA+4], esp |
||
969 | test eax, eax |
||
6828 | dunkaist | 970 | pop eax |
6827 | dunkaist | 971 | jnz .fail_free_gpea_gpt |
972 | ; Compute and check CRC32 of GPEA |
||
973 | mov eax, -1 |
||
974 | stdcall crc_32, 0xEDB88320, edi, [GPEA_len] |
||
975 | xor eax, -1 |
||
7270 | dunkaist | 976 | cmp eax, [ebx+GPTH.PartitionEntryArrayCRC32] |
6827 | dunkaist | 977 | jnz .fail_free_gpea_gpt |
978 | |||
979 | ; Process partitions, skip zeroed ones. |
||
980 | .next_gpe: |
||
981 | xor eax, eax |
||
982 | mov ecx, [ebx+GPTH.SizeOfPartitionEntry] |
||
983 | repz scasb |
||
984 | jz .skip |
||
985 | add edi, ecx |
||
986 | sub edi, [ebx+GPTH.SizeOfPartitionEntry] |
||
987 | ; Length of a partition in sectors is EndingLBA - StartingLBA + 1 |
||
988 | mov eax, dword[edi+GPE.EndingLBA+0] |
||
989 | mov edx, dword[edi+GPE.EndingLBA+4] |
||
990 | sub eax, dword[edi+GPE.StartingLBA+0] |
||
991 | sbb edx, dword[edi+GPE.StartingLBA+4] |
||
992 | add eax, 1 |
||
993 | adc edx, 0 |
||
7270 | dunkaist | 994 | push ebx |
995 | mov ebx, [ebp-8] ; three-sectors-sized buffer |
||
6827 | dunkaist | 996 | stdcall disk_add_partition, dword[edi+GPE.StartingLBA+0], \ |
997 | dword[edi+GPE.StartingLBA+4], eax, edx, esi |
||
7270 | dunkaist | 998 | pop ebx |
6827 | dunkaist | 999 | add edi, [ebx+GPTH.SizeOfPartitionEntry] |
1000 | .skip: |
||
1001 | dec [ebx+GPTH.NumberOfPartitionEntries] |
||
1002 | jnz .next_gpe |
||
1003 | |||
1004 | ; Pointers to GPT header and GPEA are on the stack |
||
1005 | stdcall kernel_free |
||
1006 | stdcall kernel_free |
||
1007 | pop edi ebx |
||
1008 | xor eax, eax |
||
1009 | ret |
||
1010 | .fail_free_gpea_gpt: |
||
1011 | stdcall kernel_free |
||
1012 | .fail_free_gpt: |
||
1013 | stdcall kernel_free |
||
1014 | .fail: |
||
1015 | pop edi ebx |
||
1016 | xor eax, eax |
||
1017 | inc eax |
||
1018 | ret |
||
1019 | endp |
||
1020 | |||
1021 | ; ecx = pointer to partition records array (MBR + 446) |
||
1022 | is_protective_mbr: |
||
1023 | push ecx edi |
||
1024 | xor eax, eax |
||
6828 | dunkaist | 1025 | cmp [ecx-2], ax |
6827 | dunkaist | 1026 | jnz .exit |
1027 | ; Partition record 0 has specific fields |
||
6828 | dunkaist | 1028 | cmp [ecx+0], al |
6827 | dunkaist | 1029 | jnz .exit |
1030 | cmp byte[ecx+4], 0xEE |
||
1031 | jnz .exit |
||
1032 | cmp dword[ecx+8], 1 |
||
1033 | jnz .exit |
||
7546 | dunkaist | 1034 | mov edi, -1 |
1035 | cmp [ecx+12], edi |
||
1036 | jz @f |
||
1037 | add edi, dword[esi+DISK.MediaInfo.Capacity+0] |
||
1038 | cmp [ecx+12], edi |
||
1039 | jnz .exit |
||
6844 | dunkaist | 1040 | @@: |
6827 | dunkaist | 1041 | ; Check that partition records 1-3 are filled with zero |
1042 | lea edi, [ecx+16] |
||
1043 | mov ecx, 16*3/2 ; 3 partitions |
||
1044 | repz scasw |
||
1045 | .exit: |
||
1046 | pop edi ecx |
||
1047 | ; Return value is ZF |
||
1048 | ret |
||
1049 | |||
2288 | clevermous | 1050 | ; This is an internal function called from disk_scan_partitions. It checks |
1051 | ; whether the entry pointed to by ecx is a valid entry of partition table. |
||
1052 | ; The entry is valid if the first byte is 0 or 80h, the first sector plus the |
||
1053 | ; length is less than twice the size of media. Multiplication by two is |
||
1054 | ; required since the size mentioned in the partition table can be slightly |
||
1055 | ; greater than the real size. |
||
1056 | is_partition_table_entry: |
||
1057 | ; 1. Check .Bootable field. |
||
1058 | mov al, [ecx+PARTITION_TABLE_ENTRY.Bootable] |
||
1059 | and al, 7Fh |
||
1060 | jnz .invalid |
||
1061 | ; 3. Calculate first sector + length. Note that .FirstAbsSector is relative |
||
1062 | ; to the MBR/EBR, so the real sum is ebp + .FirstAbsSector + .Length. |
||
1063 | mov eax, ebp |
||
1064 | xor edx, edx |
||
1065 | add eax, [ecx+PARTITION_TABLE_ENTRY.FirstAbsSector] |
||
1066 | adc edx, 0 |
||
1067 | add eax, [ecx+PARTITION_TABLE_ENTRY.Length] |
||
1068 | adc edx, 0 |
||
1069 | ; 4. Divide by two. |
||
1070 | shr edx, 1 |
||
1071 | rcr eax, 1 |
||
1072 | ; 5. Compare with capacity. If the subtraction (edx:eax) - .Capacity does not |
||
1073 | ; overflow, this is bad. |
||
1074 | sub eax, dword [esi+DISK.MediaInfo.Capacity] |
||
1075 | sbb edx, dword [esi+DISK.MediaInfo.Capacity+4] |
||
1076 | jnc .invalid |
||
1077 | .valid: |
||
1078 | ; 5. Return success: CF is cleared. |
||
1079 | clc |
||
1080 | ret |
||
1081 | .invalid: |
||
1082 | ; 6. Return fail: CF is set. |
||
1083 | stc |
||
1084 | ret |
||
1085 | |||
1086 | ; This is an internal function called from disk_scan_partitions. It processes |
||
1087 | ; the entry pointed to by ecx. |
||
1088 | ; * If the entry is invalid, just ignore this entry. |
||
1089 | ; * If the type is zero, just ignore this entry. |
||
1090 | ; * If the type is one of types for extended partition, store the address |
||
1091 | ; of this partition as the new MBR in [esp+4]. |
||
1092 | ; * Otherwise, add the partition to the list of partitions for this disk. |
||
1093 | ; We don't use the type from the entry to identify the file system; |
||
1094 | ; fs-specific checks do this more reliably. |
||
1095 | process_partition_table_entry: |
||
1096 | ; 1. Check for valid entry. If invalid, return (go to 5). |
||
1097 | call is_partition_table_entry |
||
1098 | jc .nothing |
||
1099 | ; 2. Check for empty entry. If invalid, return (go to 5). |
||
1100 | mov al, [ecx+PARTITION_TABLE_ENTRY.Type] |
||
1101 | test al, al |
||
1102 | jz .nothing |
||
1103 | ; 3. Check for extended partition. If extended, go to 6. |
||
1104 | irp type,\ |
||
1105 | 0x05,\ ; DOS: extended partition |
||
1106 | 0x0f,\ ; WIN95: extended partition, LBA-mapped |
||
1107 | 0xc5,\ ; DRDOS/secured: extended partition |
||
1108 | 0xd5 ; Old Multiuser DOS secured: extended partition |
||
1109 | { |
||
1110 | cmp al, type |
||
1111 | jz .extended |
||
1112 | } |
||
1113 | ; 4. If we are here, that is a normal partition. Add it to the list. |
||
1114 | ; Note that the first sector is relative to MBR/EBR. |
||
1115 | mov eax, ebp |
||
1116 | xor edx, edx |
||
1117 | add eax, [ecx+PARTITION_TABLE_ENTRY.FirstAbsSector] |
||
1118 | adc edx, 0 |
||
1119 | push ecx |
||
1120 | stdcall disk_add_partition, eax, edx, \ |
||
3742 | clevermous | 1121 | [ecx+PARTITION_TABLE_ENTRY.Length], 0, esi |
2288 | clevermous | 1122 | pop ecx |
1123 | .nothing: |
||
1124 | ; 5. Return. |
||
1125 | ret |
||
1126 | .extended: |
||
1127 | ; 6. If we are here, that is an extended partition. Store the address. |
||
1128 | mov eax, [ecx+PARTITION_TABLE_ENTRY.FirstAbsSector] |
||
1129 | mov [esp+4], eax |
||
1130 | ret |
||
1131 | |||
1132 | ; This is an internal function called from disk_scan_partitions and |
||
1133 | ; process_partition_table_entry. It adds one partition to the list of |
||
1134 | ; partitions for the media. |
||
3742 | clevermous | 1135 | ; Important note: start, length, disk MUST be present and |
1136 | ; MUST be in the same order as in PARTITION structure. |
||
1137 | ; esi duplicates [disk]. |
||
1138 | proc disk_add_partition stdcall uses ebx edi, start:qword, length:qword, disk:dword |
||
2288 | clevermous | 1139 | ; 1. Check that this partition will not exceed the limit on total number. |
1140 | cmp [esi+DISK.NumPartitions], MAX_NUM_PARTITIONS |
||
1141 | jae .nothing |
||
1142 | ; 2. Check that this partition does not overlap with any already registered |
||
1143 | ; partition. Since any file system assumes that the disk data will not change |
||
1144 | ; outside of its control, such overlap could be destructive. |
||
1145 | ; Since the number of partitions is usually very small and is guaranteed not |
||
1146 | ; to be large, the simple linear search is sufficient. |
||
1147 | ; 2a. Prepare the loop: edi will point to the current item of .Partitions |
||
1148 | ; array, ecx will be the current item, ebx will hold number of items left. |
||
1149 | mov edi, [esi+DISK.Partitions] |
||
1150 | mov ebx, [esi+DISK.NumPartitions] |
||
1151 | test ebx, ebx |
||
1152 | jz .partitionok |
||
1153 | .scan_existing: |
||
1154 | ; 2b. Get the next partition. |
||
1155 | mov ecx, [edi] |
||
1156 | add edi, 4 |
||
1157 | ; The range [.FirstSector, .FirstSector+.Length) must be either entirely to |
||
1158 | ; the left of [start, start+length) or entirely to the right. |
||
1159 | ; 2c. Subtract .FirstSector - start. The possible overflow distinguish between |
||
1160 | ; cases "to the left" (2e) and "to the right" (2d). |
||
1161 | mov eax, dword [ecx+PARTITION.FirstSector] |
||
1162 | mov edx, dword [ecx+PARTITION.FirstSector+4] |
||
1163 | sub eax, dword [start] |
||
1164 | sbb edx, dword [start+4] |
||
1165 | jb .less |
||
1166 | ; 2d. .FirstSector is greater than or equal to start. Check that .FirstSector |
||
1167 | ; is greater than or equal to start+length; the subtraction |
||
1168 | ; (.FirstSector-start) - length must not cause overflow. Go to 2g if life is |
||
1169 | ; good or to 2f in the other case. |
||
1170 | sub eax, dword [length] |
||
1171 | sbb edx, dword [length+4] |
||
1172 | jb .overlap |
||
1173 | jmp .next_existing |
||
1174 | .less: |
||
1175 | ; 2e. .FirstSector is less than start. Check that .FirstSector+.Length is less |
||
1176 | ; than or equal to start. If the addition (.FirstSector-start) + .Length does |
||
1177 | ; not cause overflow, then .FirstSector + .Length is strictly less than start; |
||
1178 | ; since the equality is also valid, use decrement preliminarily. Go to 2g or |
||
1179 | ; 2f depending on the overflow. |
||
1180 | sub eax, 1 |
||
1181 | sbb edx, 0 |
||
1182 | add eax, dword [ecx+PARTITION.Length] |
||
1183 | adc edx, dword [ecx+PARTITION.Length+4] |
||
1184 | jnc .next_existing |
||
1185 | .overlap: |
||
1186 | ; 2f. The partition overlaps with previously registered partition. Say warning |
||
1187 | ; and return with nothing done. |
||
1188 | dbgstr 'two partitions overlap, ignoring the last one' |
||
1189 | jmp .nothing |
||
1190 | .next_existing: |
||
1191 | ; 2g. The partition does not overlap with the current partition. Continue the |
||
1192 | ; loop. |
||
1193 | dec ebx |
||
1194 | jnz .scan_existing |
||
1195 | .partitionok: |
||
1196 | ; 3. The partition has passed tests. Reallocate the partitions array for a new |
||
1197 | ; entry. |
||
1198 | ; 3a. Call the allocator. |
||
1199 | mov eax, [esi+DISK.NumPartitions] |
||
1200 | inc eax ; one more entry |
||
1201 | shl eax, 2 ; each entry is dword |
||
1202 | call malloc |
||
1203 | ; 3b. Test the result. If failed, return with nothing done. |
||
1204 | test eax, eax |
||
1205 | jz .nothing |
||
1206 | ; 3c. Copy the old array to the new array. |
||
1207 | mov edi, eax |
||
1208 | push esi |
||
1209 | mov ecx, [esi+DISK.NumPartitions] |
||
1210 | mov esi, [esi+DISK.Partitions] |
||
1211 | rep movsd |
||
1212 | pop esi |
||
1213 | ; 3d. Set the field in the DISK structure to the new array. |
||
1214 | xchg [esi+DISK.Partitions], eax |
||
1215 | ; 3e. Free the old array. |
||
1216 | call free |
||
1217 | ; 4. Recognize the file system. |
||
1218 | ; 4a. Call the filesystem recognizer. It will allocate the PARTITION structure |
||
1219 | ; with possible filesystem-specific fields. |
||
1220 | call disk_detect_partition |
||
1221 | ; 4b. Check return value. If zero, return with list not changed; so far only |
||
1222 | ; the array was reallocated, this is ok for other code. |
||
1223 | test eax, eax |
||
1224 | jz .nothing |
||
1225 | ; 5. Insert the new partition to the list. |
||
1226 | stosd |
||
1227 | inc [esi+DISK.NumPartitions] |
||
1228 | ; 6. Return. |
||
1229 | .nothing: |
||
1230 | ret |
||
1231 | endp |
||
1232 | |||
1233 | ; This is an internal function called from disk_add_partition. |
||
1234 | ; It tries to recognize the file system on the partition and allocates the |
||
1235 | ; corresponding PARTITION structure with filesystem-specific fields. |
||
1236 | disk_detect_partition: |
||
1237 | ; This function inherits the stack frame from disk_add_partition. In stdcall |
||
1238 | ; with ebp-based frame arguments start from ebp+8, since [ebp]=saved ebp |
||
1239 | ; and [ebp+4]=return address. |
||
1240 | virtual at ebp+8 |
||
1241 | .start dq ? |
||
1242 | .length dq ? |
||
3742 | clevermous | 1243 | .disk dd ? |
2288 | clevermous | 1244 | end virtual |
3742 | clevermous | 1245 | ; 1. Read the bootsector to the buffer. |
2643 | clevermous | 1246 | ; When disk_add_partition is called, ebx contains a pointer to |
3742 | clevermous | 1247 | ; a three-sectors-sized buffer. This function saves ebx in the stack |
2643 | clevermous | 1248 | ; immediately before ebp. |
3742 | clevermous | 1249 | mov ebx, [ebp-4] ; get buffer |
5089 | clevermous | 1250 | add ebx, [esi+DISK.MediaInfo.SectorSize] ; advance over MBR data to bootsector data |
3742 | clevermous | 1251 | add ebp, 8 ; ebp points to part of PARTITION structure |
1252 | xor eax, eax ; first sector of the partition |
||
1253 | call fs_read32_sys |
||
2643 | clevermous | 1254 | ; 2. Run tests for all supported filesystems. If at least one test succeeded, |
1255 | ; go to 4. |
||
3742 | clevermous | 1256 | ; For tests: |
1257 | ; ebp -> first three fields of PARTITION structure, .start, .length, .disk; |
||
1258 | ; [esp] = error code after bootsector read: 0 = ok, otherwise = failed, |
||
1259 | ; ebx points to the buffer for bootsector, |
||
5089 | clevermous | 1260 | ; ebx+[esi+DISK.MediaInfo.SectorSize] points to sector-sized buffer that can be used for anything. |
9930 | Doczom | 1261 | |
1262 | ; lock fs list |
||
1263 | |||
9894 | Doczom | 1264 | mov ecx, [fs_list] |
1265 | @@: |
||
1266 | cmp ecx, fs_list |
||
1267 | jz @f |
||
1268 | |||
1269 | push ecx eax |
||
1270 | call dword[ecx + FileSystem.Creat_part] |
||
1271 | pop ecx |
||
1272 | test eax, eax |
||
1273 | jnz .success |
||
1274 | |||
9930 | Doczom | 1275 | mov eax, ecx |
1276 | pop ecx |
||
9894 | Doczom | 1277 | mov ecx, [ecx] |
1278 | jmp @b |
||
1279 | @@: |
||
1280 | push eax |
||
2643 | clevermous | 1281 | call fat_create_partition |
1282 | test eax, eax |
||
1283 | jnz .success |
||
9734 | sober_dev | 1284 | call exFAT_create_partition |
1285 | test eax, eax |
||
1286 | jnz .success |
||
3742 | clevermous | 1287 | call ntfs_create_partition |
1288 | test eax, eax |
||
1289 | jnz .success |
||
1290 | call ext2_create_partition |
||
1291 | test eax, eax |
||
1292 | jnz .success |
||
3913 | dunkaist | 1293 | call xfs_create_partition |
1294 | test eax, eax |
||
1295 | jnz .success |
||
10053 | Doczom | 1296 | call iso9660_create_partition |
1297 | test eax, eax |
||
1298 | jnz .success |
||
2643 | clevermous | 1299 | ; 3. No file system has recognized the volume, so just allocate the PARTITION |
2288 | clevermous | 1300 | ; structure without extra fields. |
3598 | clevermous | 1301 | movi eax, sizeof.PARTITION |
2288 | clevermous | 1302 | call malloc |
1303 | test eax, eax |
||
1304 | jz .nothing |
||
3742 | clevermous | 1305 | mov edx, dword [ebp+PARTITION.FirstSector] |
2288 | clevermous | 1306 | mov dword [eax+PARTITION.FirstSector], edx |
3742 | clevermous | 1307 | mov edx, dword [ebp+PARTITION.FirstSector+4] |
2288 | clevermous | 1308 | mov dword [eax+PARTITION.FirstSector+4], edx |
3742 | clevermous | 1309 | mov edx, dword [ebp+PARTITION.Length] |
2288 | clevermous | 1310 | mov dword [eax+PARTITION.Length], edx |
3742 | clevermous | 1311 | mov edx, dword [ebp+PARTITION.Length+4] |
2288 | clevermous | 1312 | mov dword [eax+PARTITION.Length+4], edx |
2643 | clevermous | 1313 | mov [eax+PARTITION.Disk], esi |
3742 | clevermous | 1314 | mov [eax+PARTITION.FSUserFunctions], default_fs_functions |
2643 | clevermous | 1315 | .success: |
2288 | clevermous | 1316 | .nothing: |
3742 | clevermous | 1317 | sub ebp, 8 ; restore ebp |
2643 | clevermous | 1318 | ; 4. Return with eax = pointer to PARTITION or NULL. |
1319 | pop ecx |
||
9930 | Doczom | 1320 | |
1321 | ; unlock fs list |
||
2288 | clevermous | 1322 | ret |
1323 | |||
3742 | clevermous | 1324 | iglobal |
1325 | align 4 |
||
1326 | default_fs_functions: |
||
1327 | dd free |
||
9043 | dunkaist | 1328 | dd (default_fs_functions_end - default_fs_functions - 4) / 4 |
1329 | dd 0 |
||
1330 | dd 0 |
||
1331 | dd 0 |
||
1332 | dd 0 |
||
1333 | dd 0 |
||
1334 | dd default_fs_get_file_info |
||
1335 | default_fs_functions_end: |
||
3742 | clevermous | 1336 | endg |
1337 | |||
9043 | dunkaist | 1338 | proc default_fs_get_file_info uses edi |
1339 | movi eax, ERROR_UNSUPPORTED_FS |
||
1340 | cmp byte[esi], 0 |
||
1341 | jnz .done |
||
1342 | movi ecx, 40 ; len of BDFE without filename |
||
1343 | cmp [ebx+f70s5arg.xflags], 0 |
||
1344 | jz @f |
||
1345 | add ecx, 2 ; volume label requested, space for utf16 terminator |
||
1346 | @@: |
||
1347 | mov ebx, [ebx+f70s5arg.buf] |
||
1348 | stdcall is_region_userspace, ebx, ecx |
||
1349 | movi eax, ERROR_MEMORY_POINTER |
||
9045 | dunkaist | 1350 | jnz .done |
9043 | dunkaist | 1351 | mov edi, ebx |
1352 | xor eax, eax |
||
1353 | rep stosb |
||
1354 | mov [ebx+bdfe.attr], 0x10 ; directory flag |
||
1355 | mov word[ebx+bdfe.name], 0 ; word because of possible utf16 |
||
1356 | mov eax, dword[ebp+PARTITION.Length+DQ.lo] |
||
1357 | mov edx, dword[ebp+PARTITION.Length+DQ.hi] |
||
1358 | mov ecx, [ebp+PARTITION.Disk] |
||
1359 | mov ecx, [ecx+DISK.MediaInfo.SectorSize] |
||
1360 | bsf ecx, ecx |
||
1361 | shld edx, eax, cl |
||
1362 | shl eax, cl |
||
1363 | mov [ebx+bdfe.size.lo], eax |
||
1364 | mov [ebx+bdfe.size.hi], edx |
||
1365 | xor eax, eax |
||
1366 | .done: |
||
1367 | ret |
||
1368 | endp |
||
1369 | |||
2288 | clevermous | 1370 | ; This function is called from file_system_lfn. |
1371 | ; This handler gets the control each time when fn 70 is called |
||
1372 | ; with unknown item of root subdirectory. |
||
6468 | pathoswith | 1373 | ; in: esi = ebp -> path string |
2288 | clevermous | 1374 | ; out: if the handler processes path, it must not return in file_system_lfn, |
1375 | ; but instead pop return address and return directly to the caller |
||
1376 | ; otherwise simply return |
||
1377 | dyndisk_handler: |
||
9734 | sober_dev | 1378 | ; DEBUGF 1, "K : FS Input Path:%s\n",esi |
2288 | clevermous | 1379 | push ebx edi ; save registers used in file_system_lfn |
1380 | ; 1. Acquire the mutex. |
||
1381 | mov ecx, disk_list_mutex |
||
1382 | call mutex_lock |
||
1383 | ; 2. Loop over the list of DISK structures. |
||
1384 | ; 2a. Initialize. |
||
1385 | mov ebx, disk_list |
||
1386 | .scan: |
||
1387 | ; 2b. Get the next item. |
||
1388 | mov ebx, [ebx+DISK.Next] |
||
1389 | ; 2c. Check whether the list is done. If so, go to 3. |
||
1390 | cmp ebx, disk_list |
||
1391 | jz .notfound |
||
1392 | ; 2d. Compare names. If names match, go to 5. |
||
1393 | mov edi, [ebx+DISK.Name] |
||
1394 | push esi |
||
1395 | @@: |
||
1396 | ; esi points to the name from fs operation; it is terminated by zero or slash. |
||
1397 | lodsb |
||
1398 | test al, al |
||
1399 | jz .eoin_dec |
||
1400 | cmp al, '/' |
||
1401 | jz .eoin |
||
1402 | ; edi points to the disk name. |
||
1403 | inc edi |
||
1404 | ; edi points to lowercase name, this is a requirement for the driver. |
||
1405 | ; Characters at esi can have any register. Lowercase the current character. |
||
1406 | ; This lowercasing works for latin letters and digits; since the disk name |
||
1407 | ; should not contain other symbols, this is ok. |
||
1408 | or al, 20h |
||
1409 | cmp al, [edi-1] |
||
1410 | jz @b |
||
1411 | .wrongname: |
||
1412 | ; 2f. Names don't match. Continue the loop. |
||
1413 | pop esi |
||
1414 | jmp .scan |
||
1415 | .notfound: |
||
1416 | ; The loop is done and no name matches. |
||
1417 | ; 3. Release the mutex. |
||
1418 | call mutex_unlock |
||
1419 | ; 4. Return normally. |
||
1420 | pop edi ebx ; restore registers used in file_system_lfn |
||
1421 | ret |
||
1422 | ; part of 2d: the name matches partially, but we must check that this is full |
||
1423 | ; equality. |
||
1424 | .eoin_dec: |
||
1425 | dec esi |
||
1426 | .eoin: |
||
1427 | cmp byte [edi], 0 |
||
1428 | jnz .wrongname |
||
1429 | ; We found the addressed DISK structure. |
||
1430 | ; 5. Reference the disk. |
||
1431 | lock inc [ebx+DISK.RefCount] |
||
1432 | ; 6. Now we are sure that the DISK structure is not going to die at least |
||
1433 | ; while we are working with it, so release the global mutex. |
||
1434 | call mutex_unlock |
||
2643 | clevermous | 1435 | pop ecx ; pop from the stack saved value of esi |
2288 | clevermous | 1436 | ; 7. Acquire the mutex for media object. |
1437 | pop edi ; restore edi |
||
1438 | lea ecx, [ebx+DISK.MediaLock] |
||
1439 | call mutex_lock |
||
1440 | ; 8. Get the media object. If it is not NULL, reference it. |
||
1441 | xor edx, edx |
||
1442 | cmp [ebx+DISK.MediaInserted], dl |
||
1443 | jz @f |
||
1444 | mov edx, ebx |
||
1445 | inc [ebx+DISK.MediaRefCount] |
||
1446 | @@: |
||
1447 | ; 9. Now we are sure that the media object, if it exists, is not going to die |
||
1448 | ; at least while we are working with it, so release the mutex for media object. |
||
1449 | call mutex_unlock |
||
1450 | mov ecx, ebx |
||
1451 | pop ebx eax ; restore ebx, pop return address |
||
1452 | ; 10. Check whether the fs operation wants to enumerate partitions (go to 11) |
||
1453 | ; or work with some concrete partition (go to 12). |
||
1454 | cmp byte [esi], 0 |
||
1455 | jnz .haspartition |
||
1456 | ; 11. The fs operation wants to enumerate partitions. |
||
6875 | pathoswith | 1457 | ; Check whether the media is inserted. |
2288 | clevermous | 1458 | mov esi, fs_dyndisk_next_nomedia |
1459 | test edx, edx |
||
1460 | jz @f |
||
1461 | mov esi, fs_dyndisk_next |
||
6875 | pathoswith | 1462 | @@: ; Let the procedure from fs_lfn.inc do the job. |
2288 | clevermous | 1463 | jmp file_system_lfn.maindir_noesi |
6468 | pathoswith | 1464 | |
6845 | pathoswith | 1465 | .root: |
1466 | pop ecx edx |
||
1467 | xor eax, eax |
||
1468 | cmp byte [ebx], 9 |
||
1469 | jz .cleanup_ecx |
||
6468 | pathoswith | 1470 | .access_denied: |
6845 | pathoswith | 1471 | movi eax, ERROR_ACCESS_DENIED |
1472 | .cleanup_ecx: |
||
1473 | mov [esp+32], eax |
||
6468 | pathoswith | 1474 | mov esi, ecx ; disk*dereference assume that esi points to DISK |
1475 | test edx, edx ; if there are no media, we didn't reference it |
||
1476 | jz @f |
||
1477 | call disk_media_dereference |
||
1478 | @@: |
||
1479 | call disk_dereference |
||
1480 | stdcall kernel_free, ebp |
||
1481 | ret |
||
1482 | |||
1483 | .dyndisk_cleanup: |
||
6845 | pathoswith | 1484 | pop ecx edx |
1485 | movi eax, ERROR_FILE_NOT_FOUND |
||
1486 | jmp .cleanup_ecx |
||
6468 | pathoswith | 1487 | |
2288 | clevermous | 1488 | .haspartition: |
1489 | ; 12. The fs operation has specified some partition. |
||
6845 | pathoswith | 1490 | push edx ecx |
6464 | pathoswith | 1491 | xor eax, eax |
1492 | lodsb |
||
1493 | sub eax, '0' |
||
1494 | jz .dyndisk_cleanup |
||
1495 | cmp eax, 10 |
||
1496 | jnc .dyndisk_cleanup |
||
1497 | mov ecx, eax |
||
1498 | lodsb |
||
1499 | cmp eax, '/' |
||
1500 | jz @f |
||
1501 | test eax, eax |
||
1502 | jnz .dyndisk_cleanup |
||
1503 | dec esi |
||
1504 | @@: |
||
6845 | pathoswith | 1505 | cmp byte [esi], 0 |
1506 | jnz @f |
||
10053 | Doczom | 1507 | ; partition info |
6845 | pathoswith | 1508 | cmp byte [ebx], 1 |
1509 | jz @f |
||
1510 | cmp byte [ebx], 5 |
||
1511 | jnz .root |
||
1512 | @@: |
||
2288 | clevermous | 1513 | dec ecx ; convert to zero-based partition index |
6464 | pathoswith | 1514 | pop edx ; edx = pointer to DISK, dword [esp] = NULL or edx |
4273 | clevermous | 1515 | ; If the driver does not support insert notifications and we are the only fs |
1516 | ; operation with this disk, ask the driver whether the media |
||
1517 | ; was inserted/removed/changed. Otherwise, assume that media status is valid. |
||
1518 | test byte [edx+DISK.DriverFlags], DISK_NO_INSERT_NOTIFICATION |
||
1519 | jz .media_accurate |
||
10053 | Doczom | 1520 | |
4273 | clevermous | 1521 | push ecx esi |
1522 | mov esi, edx |
||
1523 | cmp dword [esp+8], 0 |
||
1524 | jz .test_no_media |
||
1525 | cmp [esi+DISK.MediaRefCount], 2 |
||
1526 | jnz .media_accurate_pop |
||
10053 | Doczom | 1527 | |
4273 | clevermous | 1528 | lea edx, [esi+DISK.MediaInfo] |
1529 | and [edx+DISKMEDIAINFO.Flags], 0 |
||
1530 | mov al, DISKFUNC.querymedia |
||
1531 | stdcall disk_call_driver, edx |
||
2288 | clevermous | 1532 | test eax, eax |
4273 | clevermous | 1533 | jz .media_accurate_pop |
10053 | Doczom | 1534 | |
4273 | clevermous | 1535 | stdcall disk_media_dereference ; drop our reference so that disk_media_changed could close the media |
1536 | stdcall disk_media_changed, esi, 0 |
||
1537 | and dword [esp+8], 0 ; no media |
||
10053 | Doczom | 1538 | ;jmp .media_accurate_pop |
4273 | clevermous | 1539 | .test_no_media: |
1540 | stdcall disk_media_changed, esi, 1 ; issue fake notification |
||
6468 | pathoswith | 1541 | ; if querymedia() inside disk_media_changed returns error, the notification is ignored |
4273 | clevermous | 1542 | cmp [esi+DISK.MediaInserted], 0 |
1543 | jz .media_accurate_pop |
||
1544 | lock inc [esi+DISK.MediaRefCount] |
||
1545 | mov dword [esp+8], esi |
||
10053 | Doczom | 1546 | |
4273 | clevermous | 1547 | .media_accurate_pop: |
1548 | mov edx, esi |
||
1549 | pop esi ecx |
||
10053 | Doczom | 1550 | |
4273 | clevermous | 1551 | .media_accurate: |
1552 | pop eax |
||
1553 | test eax, eax |
||
2288 | clevermous | 1554 | jz .nomedia |
1555 | cmp ecx, [edx+DISK.NumPartitions] |
||
6468 | pathoswith | 1556 | jae .notfound2 |
2643 | clevermous | 1557 | mov eax, [edx+DISK.Partitions] |
1558 | mov eax, [eax+ecx*4] |
||
1559 | mov edi, [eax+PARTITION.FSUserFunctions] |
||
1560 | mov ecx, [ebx] |
||
3742 | clevermous | 1561 | cmp [edi+4], ecx |
2643 | clevermous | 1562 | jbe .unsupported |
9043 | dunkaist | 1563 | cmp dword[edi+8+ecx*4], 0 ; user function not implemented |
1564 | jz .unsupported |
||
6917 | pathoswith | 1565 | pushd edx ebp eax [edi+8+ecx*4] |
1566 | cmp ecx, 10 |
||
1567 | jnz .callFS |
||
1568 | or ecx, -1 |
||
1569 | mov edi, esi |
||
1570 | xor eax, eax |
||
1571 | repnz scasb |
||
1572 | mov edx, edi |
||
1573 | dec edi |
||
1574 | mov al, '/' |
||
1575 | std |
||
1576 | repnz scasb |
||
1577 | cld |
||
1578 | inc edi |
||
1579 | mov [edi], ah |
||
1580 | mov ebp, [current_slot] |
||
1581 | add ebp, APPDATA.cur_dir |
||
1582 | pushd ebx esi edx [ebp] ebp edi |
||
1583 | sub esi, 2 |
||
1584 | mov [ebp], esi |
||
1585 | mov edi, edx |
||
1586 | mov esi, [ebx+16] |
||
1587 | mov eax, [ebx+20] |
||
1588 | cmp eax, 4 |
||
1589 | jc @f |
||
1590 | xor eax, eax |
||
1591 | @@: |
||
1592 | call getFullPath |
||
1593 | pop edi ebp |
||
1594 | mov byte [edi], '/' |
||
1595 | popd [ebp] edi esi ebx |
||
7040 | pathoswith | 1596 | add edi, 2 |
6917 | pathoswith | 1597 | test eax, eax |
1598 | jz .errorRename |
||
7040 | pathoswith | 1599 | cmp byte [edi], 0 |
1600 | jz .errorRename |
||
6917 | pathoswith | 1601 | .callFS: |
1602 | pop eax ebp |
||
1603 | call eax |
||
1604 | pop ebp edx |
||
2643 | clevermous | 1605 | mov dword [esp+20], ebx |
2288 | clevermous | 1606 | .cleanup: |
6845 | pathoswith | 1607 | mov dword [esp+32], eax |
2288 | clevermous | 1608 | mov esi, edx |
1609 | call disk_media_dereference |
||
6468 | pathoswith | 1610 | @@: |
2288 | clevermous | 1611 | call disk_dereference |
6468 | pathoswith | 1612 | stdcall kernel_free, ebp |
2288 | clevermous | 1613 | ret |
6468 | pathoswith | 1614 | |
1615 | .unsupported: |
||
6845 | pathoswith | 1616 | movi eax, ERROR_UNKNOWN_FS |
6468 | pathoswith | 1617 | cmp edi, default_fs_functions |
1618 | jz .cleanup |
||
6845 | pathoswith | 1619 | movi eax, ERROR_UNSUPPORTED_FS |
2643 | clevermous | 1620 | jmp .cleanup |
6468 | pathoswith | 1621 | |
6917 | pathoswith | 1622 | .errorRename: |
1623 | pop eax eax ebp edx |
||
6468 | pathoswith | 1624 | .notfound2: |
6845 | pathoswith | 1625 | movi eax, ERROR_FILE_NOT_FOUND |
2288 | clevermous | 1626 | jmp .cleanup |
6468 | pathoswith | 1627 | |
2288 | clevermous | 1628 | .nomedia: |
1629 | test ecx, ecx |
||
6468 | pathoswith | 1630 | jnz .notfound2 |
2288 | clevermous | 1631 | mov dword [esp+32], ERROR_DEVICE |
1632 | mov esi, edx |
||
6468 | pathoswith | 1633 | jmp @b |
1634 | |||
1635 | ; This is a callback for enumerating partitions called from |
||
1636 | ; file_system_lfn.maindir in the case of inserted media. |
||
1637 | ; It just increments eax until DISK.NumPartitions reached and then |
||
1638 | ; cleans up. |
||
1639 | fs_dyndisk_next: |
||
1640 | mov ecx, [esp+8] |
||
1641 | cmp eax, [ecx+DISK.NumPartitions] |
||
1642 | jae .nomore |
||
1643 | inc eax |
||
1644 | clc |
||
1645 | ret |
||
1646 | .nomore: |
||
1647 | pusha |
||
1648 | mov esi, ecx |
||
1649 | call disk_media_dereference |
||
2288 | clevermous | 1650 | call disk_dereference |
6468 | pathoswith | 1651 | popa |
1652 | stc |
||
2288 | clevermous | 1653 | ret |
1654 | |||
6468 | pathoswith | 1655 | ; This is a callback for enumerating partitions called from |
1656 | ; file_system_lfn.maindir in the case of missing media. |
||
1657 | ; In this case we create one pseudo-partition. |
||
1658 | fs_dyndisk_next_nomedia: |
||
1659 | cmp eax, 1 |
||
1660 | jae .nomore |
||
1661 | inc eax |
||
1662 | clc |
||
1663 | ret |
||
1664 | .nomore: |
||
1665 | mov ecx, [esp+8] |
||
1666 | pusha |
||
1667 | mov esi, ecx |
||
1668 | call disk_dereference |
||
1669 | popa |
||
1670 | stc |
||
1671 | ret |
||
1672 | |||
2288 | clevermous | 1673 | ; This function is called from file_system_lfn. |
1674 | ; This handler is called when virtual root is enumerated |
||
1675 | ; and must return all items which can be handled by this. |
||
1676 | ; It is called several times, first time with eax=0 |
||
1677 | ; in: eax = 0 for first call, previously returned value for subsequent calls |
||
1678 | ; out: eax = 0 => no more items |
||
1679 | ; eax != 0 => buffer pointed to by edi contains name of item |
||
1680 | dyndisk_enum_root: |
||
1681 | push edx ; save register used in file_system_lfn |
||
1682 | mov ecx, disk_list_mutex ; it will be useful |
||
1683 | ; 1. If this is the first call, acquire the mutex and initialize. |
||
1684 | test eax, eax |
||
1685 | jnz .notfirst |
||
1686 | call mutex_lock |
||
1687 | mov eax, disk_list |
||
1688 | .notfirst: |
||
1689 | ; 2. Get next item. |
||
1690 | mov eax, [eax+DISK.Next] |
||
1691 | ; 3. If there are no more items, go to 6. |
||
1692 | cmp eax, disk_list |
||
1693 | jz .last |
||
1694 | ; 4. Copy name from the DISK structure to edi. |
||
1695 | push eax esi |
||
1696 | mov esi, [eax+DISK.Name] |
||
1697 | @@: |
||
1698 | lodsb |
||
1699 | stosb |
||
1700 | test al, al |
||
1701 | jnz @b |
||
1702 | pop esi eax |
||
1703 | ; 5. Return with eax = item. |
||
1704 | pop edx ; restore register used in file_system_lfn |
||
1705 | ret |
||
1706 | .last: |
||
1707 | ; 6. Release the mutex and return with eax = 0. |
||
1708 | call mutex_unlock |
||
1709 | xor eax, eax |
||
1710 | pop edx ; restore register used in file_system_lfn |
||
1711 | ret |
||
10053 | Doczom | 1712 | |
1713 | ; void fastcall eject_disk_media(const char* str, uint32_t code); |
||
1714 | ; for load/eject drive |
||
1715 | ; ecx -> asciiz string disk name (sd0, usbhd0) |
||
1716 | ; edx = code command(0 - LoadTray 1-EjectTray) |
||
1717 | eject_disk_media: |
||
1718 | push esi edx ecx |
||
1719 | |||
1720 | mov ecx, disk_list_mutex |
||
1721 | call mutex_lock |
||
1722 | ; find disk ejected |
||
1723 | mov edx, disk_list |
||
1724 | .next: |
||
1725 | mov edx, [edx] |
||
1726 | cmp edx, disk_list |
||
1727 | jz .nf |
||
1728 | |||
1729 | mov ecx, [edx + DISK.Name] |
||
1730 | mov esi, [esp] |
||
1731 | @@: |
||
1732 | lodsb |
||
1733 | test al, al |
||
1734 | jz @f |
||
1735 | |||
1736 | or al, 0x20 |
||
1737 | cmp al, [ecx] |
||
1738 | jnz .next |
||
1739 | inc ecx |
||
1740 | jmp @b |
||
1741 | @@: |
||
1742 | cmp byte[ecx], al |
||
1743 | jnz .next |
||
1744 | |||
1745 | mov ecx, disk_list_mutex |
||
1746 | call mutex_unlock |
||
1747 | ; found disk |
||
1748 | pop ecx edx esi |
||
1749 | |||
1750 | and edx, 1b |
||
1751 | mov al, DISKFUNC.LoadTray |
||
1752 | stdcall disk_call_driver, edx ; ecx - code command |
||
1753 | ret |
||
1754 | |||
1755 | .nf: |
||
1756 | mov ecx, disk_list_mutex |
||
1757 | call mutex_unlock |
||
1758 | pop ecx edx esi |
||
1759 | ret |
||
1760 | |||
1761 | ; for disk emulation PARTITION structure |
||
1762 | ; int32_t find_part(char* esi) esi -> name disk and partition |
||
1763 | ; return number partition or zero for disk and |
||
1764 | ;find_part: |
||
1765 | ; mov ecx, disk_list_mutex |
||
1766 | ; |
||
1767 | ; ret |