Subversion Repositories Kolibri OS

Rev

Rev 6827 | Rev 6844 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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