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