Subversion Repositories Kolibri OS

Rev

Rev 5363 | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 5363 Rev 9734
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: 5363 $
8
$Revision: 9734 $
9
 
9
 
10
; Read/write functions try to do large operations,
10
; Read/write functions try to do large operations,
11
; it is significantly faster than several small operations.
11
; it is significantly faster than several small operations.
12
; This requires large buffers.
12
; This requires large buffers.
13
; We can't use input/output buffers directly - they can be controlled
13
; We can't use input/output buffers directly - they can be controlled
14
; by user-mode application, so they can be modified between the operation
14
; by user-mode application, so they can be modified between the operation
15
; and copying to/from cache, giving invalid data in cache.
15
; and copying to/from cache, giving invalid data in cache.
16
; It is unclear how to use cache directly, currently cache items are
16
; It is unclear how to use cache directly, currently cache items are
17
; allocated/freed sector-wise, so items for sequential sectors can be
17
; allocated/freed sector-wise, so items for sequential sectors can be
18
; scattered over all the cache.
18
; scattered over all the cache.
19
; So read/write functions allocate a temporary buffer which is
19
; So read/write functions allocate a temporary buffer which is
20
; 1) not greater than half of free memory and
20
; 1) not greater than half of free memory and
21
; 2) not greater than the following constant.
21
; 2) not greater than the following constant.
22
CACHE_MAX_ALLOC_SIZE = 4 shl 20
22
CACHE_MAX_ALLOC_SIZE = 4 shl 20
23
 
23
 
24
; Legacy interface for filesystems fs_{read,write}32_{sys,app}
24
; Legacy interface for filesystems fs_{read,write}32_{sys,app}
25
; gives only one sector for FS. However, per-sector reading is inefficient,
25
; gives only one sector for FS. However, per-sector reading is inefficient,
26
; so internally fs_read32_{sys,app} reads to the cache several sequential
26
; so internally fs_read32_{sys,app} reads to the cache several sequential
27
; sectors, hoping that they will be useful.
27
; sectors, hoping that they will be useful.
28
; Total number of sectors is given by the following constant.
28
; Total number of sectors is given by the following constant.
29
CACHE_LEGACY_READ_SIZE = 16
29
CACHE_LEGACY_READ_SIZE = 16
30
 
30
 
31
; This structure describes one item in the cache.
31
; This structure describes one item in the cache.
32
struct CACHE_ITEM
32
struct CACHE_ITEM
33
SectorLo        dd      ?       ; low 32 bits of sector
33
SectorLo        dd      ?       ; low 32 bits of sector
34
SectorHi        dd      ?       ; high 32 bits of sector
34
SectorHi        dd      ?       ; high 32 bits of sector
35
Status          dd      ?       ; one of CACHE_ITEM_*
35
Status          dd      ?       ; one of CACHE_ITEM_*
36
ends
36
ends
37
 
37
 
38
; Possible values for CACHE_ITEM_*
38
; Possible values for CACHE_ITEM_*
39
CACHE_ITEM_EMPTY = 0
39
CACHE_ITEM_EMPTY = 0
40
CACHE_ITEM_COPY = 1
40
CACHE_ITEM_COPY = 1
41
CACHE_ITEM_MODIFIED = 2
41
CACHE_ITEM_MODIFIED = 2
42
 
42
 
43
; Read several sequential sectors using cache #1.
43
; Read several sequential sectors using cache #1.
44
; in: edx:eax = start sector, relative to start of partition
44
; in: edx:eax = start sector, relative to start of partition
45
; in: ecx = number of sectors to read
45
; in: ecx = number of sectors to read
46
; in: ebx -> buffer
46
; in: ebx -> buffer
47
; in: ebp -> PARTITION
47
; in: ebp -> PARTITION
48
; out: eax = error code, 0 = ok
48
; out: eax = error code, 0 = ok
49
; out: ecx = number of sectors that were read
49
; out: ecx = number of sectors that were read
50
fs_read64_sys:
50
fs_read64_sys:
51
; Save ebx, set ebx to SysCache and let the common part do its work.
51
; Save ebx, set ebx to SysCache and let the common part do its work.
52
        push    ebx ebx
52
        push    ebx ebx
53
        mov     ebx, [ebp+PARTITION.Disk]
53
        mov     ebx, [ebp+PARTITION.Disk]
54
        add     ebx, DISK.SysCache
54
        add     ebx, DISK.SysCache
55
        jmp     fs_read64_common
55
        jmp     fs_read64_common
56
 
56
 
57
; Read several sequential sectors using cache #2.
57
; Read several sequential sectors using cache #2.
58
; in: edx:eax = start sector, relative to start of partition
58
; in: edx:eax = start sector, relative to start of partition
59
; in: ecx = number of sectors to read
59
; in: ecx = number of sectors to read
60
; in: edi -> buffer
60
; in: ebx -> buffer
61
; in: ebp -> PARTITION
61
; in: ebp -> PARTITION
62
; out: eax = error code, 0 = ok
62
; out: eax = error code, 0 = ok
63
; out: ecx = number of sectors that were read
63
; out: ecx = number of sectors that were read
64
fs_read64_app:
64
fs_read64_app:
65
; Save ebx, set ebx to AppCache and let the common part do its work.
65
; Save ebx, set ebx to AppCache and let the common part do its work.
66
        push    ebx ebx
66
        push    ebx ebx
67
        mov     ebx, [ebp+PARTITION.Disk]
67
        mov     ebx, [ebp+PARTITION.Disk]
68
        add     ebx, DISK.AppCache
68
        add     ebx, DISK.AppCache
69
 
69
 
70
; Common part of fs_read64_{app,sys}:
70
; Common part of fs_read64_{app,sys}:
71
; read several sequential sectors using the given cache.
71
; read several sequential sectors using the given cache.
72
fs_read64_common:
72
fs_read64_common:
73
; 1. Setup stack frame.
73
; 1. Setup stack frame.
74
        push    esi edi         ; save used registers to be stdcall
74
        push    esi edi         ; save used registers to be stdcall
75
        push    0               ; initialize .error_code
75
        push    0               ; initialize .error_code
76
        push    ebx edx eax ecx ecx     ; initialize stack variables
76
        push    ebx edx eax ecx ecx     ; initialize stack variables
77
virtual at esp
77
virtual at esp
78
.local_vars:
78
.local_vars:
79
.num_sectors_orig dd    ?
79
.num_sectors_orig dd    ?
80
; Number of sectors that should be read. Used to generate output value of ecx.
80
; Number of sectors that should be read. Used to generate output value of ecx.
81
.num_sectors    dd      ?
81
.num_sectors    dd      ?
82
; Number of sectors that remain to be read. Decreases from .num_sectors_orig to 0.
82
; Number of sectors that remain to be read. Decreases from .num_sectors_orig to 0.
83
.sector_lo      dd      ?       ; low 32 bits of the current sector
83
.sector_lo      dd      ?       ; low 32 bits of the current sector
84
.sector_hi      dd      ?       ; high 32 bits of the current sector
84
.sector_hi      dd      ?       ; high 32 bits of the current sector
85
.cache          dd      ?       ; pointer to DISKCACHE
85
.cache          dd      ?       ; pointer to DISKCACHE
86
.error_code     dd      ?       ; current status
86
.error_code     dd      ?       ; current status
87
.local_vars_size = $ - .local_vars
87
.local_vars_size = $ - .local_vars
88
.saved_regs     rd      2
88
.saved_regs     rd      2
89
.buffer         dd      ?       ; filled by fs_read64_{sys,app}
89
.buffer         dd      ?       ; filled by fs_read64_{sys,app}
90
end virtual
90
end virtual
91
; 2. Validate parameters against partition length:
91
; 2. Validate parameters against partition length:
92
; immediately return error if edx:eax are beyond partition end,
92
; immediately return error if edx:eax are beyond partition end,
93
; decrease .num_sectors and .num_sectors_orig, if needed,
93
; decrease .num_sectors and .num_sectors_orig, if needed,
94
; so that the entire operation fits in the partition limits.
94
; so that the entire operation fits in the partition limits.
95
        mov     eax, dword [ebp+PARTITION.Length]
95
        mov     eax, dword [ebp+PARTITION.Length]
96
        mov     edx, dword [ebp+PARTITION.Length+4]
96
        mov     edx, dword [ebp+PARTITION.Length+4]
97
        sub     eax, [.sector_lo]
97
        sub     eax, [.sector_lo]
98
        sbb     edx, [.sector_hi]
98
        sbb     edx, [.sector_hi]
99
        jb      .end_of_media
99
        jb      .end_of_media
100
        jnz     .no_end_of_media
100
        jnz     .no_end_of_media
101
        cmp     ecx, eax
101
        cmp     ecx, eax
102
        jbe     .no_end_of_media
102
        jbe     .no_end_of_media
103
; If .num_sectors got decreased, set status to DISK_STATUS_END_OF_MEDIA;
103
; If .num_sectors got decreased, set status to DISK_STATUS_END_OF_MEDIA;
104
; if all subsequent operations would be successful, this would become the final
104
; if all subsequent operations would be successful, this would become the final
105
; status, otherwise this would be rewritten by failed operation.
105
; status, otherwise this would be rewritten by failed operation.
106
        mov     [.num_sectors], eax
106
        mov     [.num_sectors], eax
107
        mov     [.num_sectors_orig], eax
107
        mov     [.num_sectors_orig], eax
108
        mov     [.error_code], DISK_STATUS_END_OF_MEDIA
108
        mov     [.error_code], DISK_STATUS_END_OF_MEDIA
109
.no_end_of_media:
109
.no_end_of_media:
110
; 3. If number of sectors to read is zero, either because zero-sectors operation
110
; 3. If number of sectors to read is zero, either because zero-sectors operation
111
; was requested or because it got decreased to zero due to partition limits,
111
; was requested or because it got decreased to zero due to partition limits,
112
; just return the current status.
112
; just return the current status.
113
        cmp     [.num_sectors], 0
113
        cmp     [.num_sectors], 0
114
        jz      .return
114
        jz      .return
115
; 4. Shift sector from partition-relative to absolute.
115
; 4. Shift sector from partition-relative to absolute.
116
        mov     eax, dword [ebp+PARTITION.FirstSector]
116
        mov     eax, dword [ebp+PARTITION.FirstSector]
117
        mov     edx, dword [ebp+PARTITION.FirstSector+4]
117
        mov     edx, dword [ebp+PARTITION.FirstSector+4]
118
        add     [.sector_lo], eax
118
        add     [.sector_lo], eax
119
        adc     [.sector_hi], edx
119
        adc     [.sector_hi], edx
120
; 5. If the cache is disabled, pass the request directly to the driver.
120
; 5. If the cache is disabled, pass the request directly to the driver.
121
        cmp     [ebx+DISKCACHE.pointer], 0
121
        cmp     [ebx+DISKCACHE.pointer], 0
122
        jz      .nocache
122
        jz      .nocache
123
; 6. Look for sectors in the cache, sequentially from the beginning.
123
; 6. Look for sectors in the cache, sequentially from the beginning.
124
; Stop at the first sector that is not in the cache
124
; Stop at the first sector that is not in the cache
125
; or when all sectors were read from the cache.
125
; or when all sectors were read from the cache.
126
; 6a. Acquire the lock.
126
; 6a. Acquire the lock.
127
        mov     ecx, [ebp+PARTITION.Disk]
127
        mov     ecx, [ebp+PARTITION.Disk]
128
        add     ecx, DISK.CacheLock
128
        add     ecx, DISK.CacheLock
129
        call    mutex_lock
129
        call    mutex_lock
130
.lookup_in_cache_loop:
130
.lookup_in_cache_loop:
131
; 6b. For each sector, call the lookup function without adding to the cache.
131
; 6b. For each sector, call the lookup function without adding to the cache.
132
        mov     eax, [.sector_lo]
132
        mov     eax, [.sector_lo]
133
        mov     edx, [.sector_hi]
133
        mov     edx, [.sector_hi]
134
        call    cache_lookup_read
134
        call    cache_lookup_read
135
; 6c. If it has failed, the sector is not in cache;
135
; 6c. If it has failed, the sector is not in cache;
136
; release the lock and go to 7.
136
; release the lock and go to 7.
137
        jc      .not_found_in_cache
137
        jc      .not_found_in_cache
138
; The sector is found in cache.
138
; The sector is found in cache.
139
; 6d. Copy data for the caller, advance [.buffer].
139
; 6d. Copy data for the caller, advance [.buffer].
140
        mov     esi, edi
140
        mov     esi, edi
141
        mov     edi, [.buffer]
141
        mov     edi, [.buffer]
142
        mov     eax, 1
142
        mov     eax, 1
143
        shl     eax, cl
143
        shl     eax, cl
144
        mov     ecx, eax
144
        mov     ecx, eax
145
        shr     ecx, 2
145
        shr     ecx, 2
146
        rep movsd
146
        rep movsd
147
        mov     [.buffer], edi
147
        mov     [.buffer], edi
148
; 6e. Advance the sector.
148
; 6e. Advance the sector.
149
        add     [.sector_lo], 1
149
        add     [.sector_lo], 1
150
        adc     [.sector_hi], 0
150
        adc     [.sector_hi], 0
151
; 6f. Decrement number of sectors left.
151
; 6f. Decrement number of sectors left.
152
; If all sectors were read, release the lock and return.
152
; If all sectors were read, release the lock and return.
153
        dec     [.num_sectors]
153
        dec     [.num_sectors]
154
        jnz     .lookup_in_cache_loop
154
        jnz     .lookup_in_cache_loop
155
; Release the lock acquired at 6a.
155
; Release the lock acquired at 6a.
156
        mov     ecx, [ebp+PARTITION.Disk]
156
        mov     ecx, [ebp+PARTITION.Disk]
157
        add     ecx, DISK.CacheLock
157
        add     ecx, DISK.CacheLock
158
        call    mutex_unlock
158
        call    mutex_unlock
159
.return:
159
.return:
160
        mov     eax, [.error_code]
160
        mov     eax, [.error_code]
161
        mov     ecx, [.num_sectors_orig]
161
        mov     ecx, [.num_sectors_orig]
162
        sub     ecx, [.num_sectors]
162
        sub     ecx, [.num_sectors]
163
.nothing:
163
.nothing:
164
        add     esp, .local_vars_size
164
        add     esp, .local_vars_size
165
        pop     edi esi ebx ebx    ; restore used registers to be stdcall
165
        pop     edi esi ebx ebx    ; restore used registers to be stdcall
166
        ret
166
        ret
167
.not_found_in_cache:
167
.not_found_in_cache:
168
; Release the lock acquired at 6a.
168
; Release the lock acquired at 6a.
169
        mov     ecx, [ebp+PARTITION.Disk]
169
        mov     ecx, [ebp+PARTITION.Disk]
170
        add     ecx, DISK.CacheLock
170
        add     ecx, DISK.CacheLock
171
        call    mutex_unlock
171
        call    mutex_unlock
172
; The current sector is not present in the cache.
172
; The current sector is not present in the cache.
173
; Ask the driver to read all requested not-yet-read sectors,
173
; Ask the driver to read all requested not-yet-read sectors,
174
; put results in the cache.
174
; put results in the cache.
175
; Also, see the comment before the definition of CACHE_MAX_ALLOC_SIZE.
175
; Also, see the comment before the definition of CACHE_MAX_ALLOC_SIZE.
176
; 7. Allocate buffer for operations.
176
; 7. Allocate buffer for operations.
177
; Normally, create buffer that is sufficient for all remaining data.
177
; Normally, create buffer that is sufficient for all remaining data.
178
; However, for extra-large requests make an upper limit:
178
; However, for extra-large requests make an upper limit:
179
; do not use more than half of the free memory
179
; do not use more than half of the free memory
180
; or more than CACHE_MAX_ALLOC_SIZE bytes.
180
; or more than CACHE_MAX_ALLOC_SIZE bytes.
181
        mov     ecx, [ebx+DISKCACHE.sector_size_log]
181
        mov     ecx, [ebx+DISKCACHE.sector_size_log]
182
        mov     ebx, [pg_data.pages_free]
182
        mov     ebx, [pg_data.pages_free]
183
        shr     ebx, 1
183
        shr     ebx, 1
184
        jz      .nomemory
184
        jz      .nomemory
185
        cmp     ebx, CACHE_MAX_ALLOC_SIZE shr 12
185
        cmp     ebx, CACHE_MAX_ALLOC_SIZE shr 12
186
        jbe     @f
186
        jbe     @f
187
        mov     ebx, CACHE_MAX_ALLOC_SIZE shr 12
187
        mov     ebx, CACHE_MAX_ALLOC_SIZE shr 12
188
@@:
188
@@:
189
        shl     ebx, 12
189
        shl     ebx, 12
190
        shr     ebx, cl
190
        shr     ebx, cl
191
        jz      .nomemory
191
        jz      .nomemory
192
        cmp     ebx, [.num_sectors]
192
        cmp     ebx, [.num_sectors]
193
        jbe     @f
193
        jbe     @f
194
        mov     ebx, [.num_sectors]
194
        mov     ebx, [.num_sectors]
195
@@:
195
@@:
196
        mov     eax, ebx
196
        mov     eax, ebx
197
        shl     eax, cl
197
        shl     eax, cl
198
        stdcall kernel_alloc, eax
198
        stdcall kernel_alloc, eax
199
; If failed, return the appropriate error code.
199
; If failed, return the appropriate error code.
200
        test    eax, eax
200
        test    eax, eax
201
        jz      .nomemory
201
        jz      .nomemory
202
        mov     esi, eax
202
        mov     esi, eax
203
; Split the request to chunks that fit in the allocated buffer.
203
; Split the request to chunks that fit in the allocated buffer.
204
.read_loop:
204
.read_loop:
205
; 8. Get iteration size: either size of allocated buffer in sectors
205
; 8. Get iteration size: either size of allocated buffer in sectors
206
; or number of sectors left, select what is smaller.
206
; or number of sectors left, select what is smaller.
207
        cmp     ebx, [.num_sectors]
207
        cmp     ebx, [.num_sectors]
208
        jbe     @f
208
        jbe     @f
209
        mov     ebx, [.num_sectors]
209
        mov     ebx, [.num_sectors]
210
@@:
210
@@:
211
; 9. Create second portion of local variables.
211
; 9. Create second portion of local variables.
212
; Note that variables here and above are esp-relative;
212
; Note that variables here and above are esp-relative;
213
; it means that all addresses should be corrected when esp is changing.
213
; it means that all addresses should be corrected when esp is changing.
214
        push    ebx esi esi
214
        push    ebx esi esi
215
        push    ebx
215
        push    ebx
216
; In particular, num_sectors is now [.num_sectors+.local_vars2_size].
216
; In particular, num_sectors is now [.num_sectors+.local_vars2_size].
217
virtual at esp
217
virtual at esp
218
.local_vars2:
218
.local_vars2:
219
.current_num_sectors    dd      ?       ; number of sectors that were read
219
.current_num_sectors    dd      ?       ; number of sectors that were read
220
.current_buffer         dd      ?
220
.current_buffer         dd      ?
221
; pointer inside .allocated_buffer that points
221
; pointer inside .allocated_buffer that points
222
; to the beginning of not-processed data
222
; to the beginning of not-processed data
223
.allocated_buffer       dd      ?       ; saved in safe place
223
.allocated_buffer       dd      ?       ; saved in safe place
224
.iteration_size         dd      ?       ; saved in safe place
224
.iteration_size         dd      ?       ; saved in safe place
225
.local_vars2_size = $ - .local_vars2
225
.local_vars2_size = $ - .local_vars2
226
end virtual
226
end virtual
227
; 10. Call the driver, reading the next chunk.
227
; 10. Call the driver, reading the next chunk.
228
        push    esp     ; numsectors
228
        push    esp     ; numsectors
229
        push    [.sector_hi+.local_vars2_size+4] ; startsector
229
        push    [.sector_hi+.local_vars2_size+4] ; startsector
230
        push    [.sector_lo+.local_vars2_size+8] ; startsector
230
        push    [.sector_lo+.local_vars2_size+8] ; startsector
231
        push    esi     ; buffer
231
        push    esi     ; buffer
232
        mov     esi, [ebp+PARTITION.Disk]
232
        mov     esi, [ebp+PARTITION.Disk]
233
        mov     al, DISKFUNC.read
233
        mov     al, DISKFUNC.read
234
        call    disk_call_driver
234
        call    disk_call_driver
235
; If failed, save error code.
235
; If failed, save error code.
236
        test    eax, eax
236
        test    eax, eax
237
        jz      @f
237
        jz      @f
238
        mov     [.error_code+.local_vars2_size], eax
238
        mov     [.error_code+.local_vars2_size], eax
239
@@:
239
@@:
240
; 11. Copy data for the caller, advance .buffer.
240
; 11. Copy data for the caller, advance .buffer.
241
        cmp     [.current_num_sectors], 0
241
        cmp     [.current_num_sectors], 0
242
        jz      .copy_done
242
        jz      .copy_done
243
        mov     ebx, [.cache+.local_vars2_size]
243
        mov     ebx, [.cache+.local_vars2_size]
244
        mov     eax, [.current_num_sectors]
244
        mov     eax, [.current_num_sectors]
245
        mov     ecx, [ebx+DISKCACHE.sector_size_log]
245
        mov     ecx, [ebx+DISKCACHE.sector_size_log]
246
        shl     eax, cl
246
        shl     eax, cl
247
        mov     esi, [.allocated_buffer]
247
        mov     esi, [.allocated_buffer]
248
        mov     edi, [.buffer+.local_vars2_size]
248
        mov     edi, [.buffer+.local_vars2_size]
249
        mov     ecx, eax
249
        mov     ecx, eax
250
        shr     ecx, 2
250
        shr     ecx, 2
251
        rep movsd
251
        rep movsd
252
        mov     [.buffer+.local_vars2_size], edi
252
        mov     [.buffer+.local_vars2_size], edi
253
; 12. Copy data to the cache.
253
; 12. Copy data to the cache.
254
; 12a. Acquire the lock.
254
; 12a. Acquire the lock.
255
        mov     ecx, [ebp+PARTITION.Disk]
255
        mov     ecx, [ebp+PARTITION.Disk]
256
        add     ecx, DISK.CacheLock
256
        add     ecx, DISK.CacheLock
257
        call    mutex_lock
257
        call    mutex_lock
258
; 12b. Prepare for the loop: create a local variable that
258
; 12b. Prepare for the loop: create a local variable that
259
; stores number of sectors to be copied.
259
; stores number of sectors to be copied.
260
        push    [.current_num_sectors]
260
        push    [.current_num_sectors]
261
.store_to_cache:
261
.store_to_cache:
262
; 12c. For each sector, call the lookup function with adding to the cache, if not yet.
262
; 12c. For each sector, call the lookup function with adding to the cache, if not yet.
263
        mov     eax, [.sector_lo+.local_vars2_size+4]
263
        mov     eax, [.sector_lo+.local_vars2_size+4]
264
        mov     edx, [.sector_hi+.local_vars2_size+4]
264
        mov     edx, [.sector_hi+.local_vars2_size+4]
265
        call    cache_lookup_write
265
        call    cache_lookup_write
266
        test    eax, eax
266
        test    eax, eax
267
        jnz     .cache_error
267
        jnz     .cache_error
268
; 12d. If the sector was already present in the cache as modified,
268
; 12d. If the sector was already present in the cache as modified,
269
; data that were read at step 10 for this sector are obsolete,
269
; data that were read at step 10 for this sector are obsolete,
270
; so rewrite data for the caller from the cache.
270
; so rewrite data for the caller from the cache.
271
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
271
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
272
        jnz     .not_modified
272
        jnz     .not_modified
273
        mov     esi, edi
273
        mov     esi, edi
274
        mov     edi, [.buffer+.local_vars2_size+4]
274
        mov     edi, [.buffer+.local_vars2_size+4]
275
        mov     eax, [esp]
275
        mov     eax, [esp]
276
        shl     eax, cl
276
        shl     eax, cl
277
        sub     edi, eax
277
        sub     edi, eax
278
        mov     eax, 1
278
        mov     eax, 1
279
        shl     eax, cl
279
        shl     eax, cl
280
        mov     ecx, eax
280
        mov     ecx, eax
281
        shr     ecx, 2
281
        shr     ecx, 2
282
        rep movsd
282
        rep movsd
283
        add     [.current_buffer+4], eax
283
        add     [.current_buffer+4], eax
284
        jmp     .sector_done
284
        jmp     .sector_done
285
.not_modified:
285
.not_modified:
286
; 12e. For each not-modified sector,
286
; 12e. For each not-modified sector,
287
; copy data, mark the item as not-modified copy of the disk,
287
; copy data, mark the item as not-modified copy of the disk,
288
; advance .current_buffer and .sector_hi:.sector_lo to the next sector.
288
; advance .current_buffer and .sector_hi:.sector_lo to the next sector.
289
        mov     [esi+CACHE_ITEM.Status], CACHE_ITEM_COPY
289
        mov     [esi+CACHE_ITEM.Status], CACHE_ITEM_COPY
290
        mov     eax, 1
290
        mov     eax, 1
291
        shl     eax, cl
291
        shl     eax, cl
292
        mov     esi, [.current_buffer+4]
292
        mov     esi, [.current_buffer+4]
293
        mov     ecx, eax
293
        mov     ecx, eax
294
        shr     ecx, 2
294
        shr     ecx, 2
295
        rep movsd
295
        rep movsd
296
        mov     [.current_buffer+4], esi
296
        mov     [.current_buffer+4], esi
297
.sector_done:
297
.sector_done:
298
        add     [.sector_lo+.local_vars2_size+4], 1
298
        add     [.sector_lo+.local_vars2_size+4], 1
299
        adc     [.sector_hi+.local_vars2_size+4], 0
299
        adc     [.sector_hi+.local_vars2_size+4], 0
300
; 12f. Continue the loop 12c-12e until all sectors are read.
300
; 12f. Continue the loop 12c-12e until all sectors are read.
301
        dec     dword [esp]
301
        dec     dword [esp]
302
        jnz     .store_to_cache
302
        jnz     .store_to_cache
303
.cache_error:
303
.cache_error:
304
; 12g. Restore after the loop: pop the local variable.
304
; 12g. Restore after the loop: pop the local variable.
305
        pop     ecx
305
        pop     ecx
306
; 12h. Release the lock.
306
; 12h. Release the lock.
307
        mov     ecx, [ebp+PARTITION.Disk]
307
        mov     ecx, [ebp+PARTITION.Disk]
308
        add     ecx, DISK.CacheLock
308
        add     ecx, DISK.CacheLock
309
        call    mutex_unlock
309
        call    mutex_unlock
310
.copy_done:
310
.copy_done:
311
; 13. Remove portion of local variables created at step 9.
311
; 13. Remove portion of local variables created at step 9.
312
        pop     ecx
312
        pop     ecx
313
        pop     esi esi ebx
313
        pop     esi esi ebx
314
; 14. Continue iterations while number of sectors read by the driver
314
; 14. Continue iterations while number of sectors read by the driver
315
; is equal to number of sectors requested and there are additional sectors.
315
; is equal to number of sectors requested and there are additional sectors.
316
        cmp     ecx, ebx
316
        cmp     ecx, ebx
317
        jnz     @f
317
        jnz     @f
318
        sub     [.num_sectors], ebx
318
        sub     [.num_sectors], ebx
319
        jnz     .read_loop
319
        jnz     .read_loop
320
@@:
320
@@:
321
; 15. Free the buffer allocated at step 7 and return.
321
; 15. Free the buffer allocated at step 7 and return.
322
        stdcall kernel_free, esi
322
        stdcall kernel_free, esi
323
        jmp     .return
323
        jmp     .return
324
 
324
 
325
; Special branches:
325
; Special branches:
326
.nomemory:
326
.nomemory:
327
; memory allocation failed at step 7: return the corresponding error
327
; memory allocation failed at step 7: return the corresponding error
328
        mov     [.error_code], DISK_STATUS_NO_MEMORY
328
        mov     [.error_code], DISK_STATUS_NO_MEMORY
329
        jmp     .return
329
        jmp     .return
330
.nocache:
330
.nocache:
331
; step 5, after correcting number of sectors to fit in partition limits
331
; step 5, after correcting number of sectors to fit in partition limits
332
; and advancing partition-relative sector to absolute,
332
; and advancing partition-relative sector to absolute,
333
; sees that cache is disabled: pass corrected request to the driver
333
; sees that cache is disabled: pass corrected request to the driver
334
        lea     eax, [.num_sectors]
334
        lea     eax, [.num_sectors]
335
        push    eax             ; numsectors
335
        push    eax             ; numsectors
336
        push    [.sector_hi+4]  ; startsector
336
        push    [.sector_hi+4]  ; startsector
337
        push    [.sector_lo+8]  ; startsector
337
        push    [.sector_lo+8]  ; startsector
338
        push    [.buffer+12]    ; buffer
338
        push    [.buffer+12]    ; buffer
339
        mov     esi, [ebp+PARTITION.Disk]
339
        mov     esi, [ebp+PARTITION.Disk]
340
        mov     al, DISKFUNC.read
340
        mov     al, DISKFUNC.read
341
        call    disk_call_driver
341
        call    disk_call_driver
342
        test    eax, eax
342
        test    eax, eax
343
        jnz     @f
343
        jnz     @f
344
        mov     eax, [.error_code]
344
        mov     eax, [.error_code]
345
@@:
345
@@:
346
        mov     ecx, [.num_sectors]
346
        mov     ecx, [.num_sectors]
347
        jmp     .nothing
347
        jmp     .nothing
348
.end_of_media:
348
.end_of_media:
349
; requested sector is beyond the partition end: return the corresponding error
349
; requested sector is beyond the partition end: return the corresponding error
350
        mov     [.error_code], DISK_STATUS_END_OF_MEDIA
350
        mov     [.error_code], DISK_STATUS_END_OF_MEDIA
351
        jmp     .return
351
        jmp     .return
352
 
352
 
353
; Write several sequential sectors using cache #1.
353
; Write several sequential sectors using cache #1.
354
; in: edx:eax = start sector
354
; in: edx:eax = start sector
355
; in: ecx = number of sectors to write
355
; in: ecx = number of sectors to write
356
; in: ebx -> buffer
356
; in: ebx -> buffer
357
; in: ebp -> PARTITION
357
; in: ebp -> PARTITION
358
; out: eax = error code, 0 = ok
358
; out: eax = error code, 0 = ok
359
; out: ecx = number of sectors that were written
359
; out: ecx = number of sectors that were written
360
fs_write64_sys:
360
fs_write64_sys:
361
; Save ebx, set ebx to SysCache and let the common part do its work.
361
; Save ebx, set ebx to SysCache and let the common part do its work.
362
        push    ebx
362
        push    ebx
363
        mov     ebx, [ebp+PARTITION.Disk]
363
        mov     ebx, [ebp+PARTITION.Disk]
364
        add     ebx, DISK.SysCache
364
        add     ebx, DISK.SysCache
365
        jmp     fs_write64_common
365
        jmp     fs_write64_common
366
 
366
 
367
; Write several sequential sectors using cache #2.
367
; Write several sequential sectors using cache #2.
368
; in: edx:eax = start sector
368
; in: edx:eax = start sector
369
; in: ecx = number of sectors to write
369
; in: ecx = number of sectors to write
370
; in: ebx -> buffer
370
; in: ebx -> buffer
371
; in: ebp -> PARTITION
371
; in: ebp -> PARTITION
372
; out: eax = error code, 0 = ok
372
; out: eax = error code, 0 = ok
373
; out: ecx = number of sectors that were written
373
; out: ecx = number of sectors that were written
374
fs_write64_app:
374
fs_write64_app:
375
; Save ebx, set ebx to AppCache and let the common part do its work.
375
; Save ebx, set ebx to AppCache and let the common part do its work.
376
        push    ebx
376
        push    ebx
377
        mov     ebx, [ebp+PARTITION.Disk]
377
        mov     ebx, [ebp+PARTITION.Disk]
378
        add     ebx, DISK.AppCache
378
        add     ebx, DISK.AppCache
379
 
379
 
380
; Common part of fs_write64_{app,sys}:
380
; Common part of fs_write64_{app,sys}:
381
; write several sequential sectors using the given cache.
381
; write several sequential sectors using the given cache.
382
fs_write64_common:
382
fs_write64_common:
383
; 1. Setup stack frame.
383
; 1. Setup stack frame.
384
        push    esi edi         ; save used registers to be stdcall
384
        push    esi edi         ; save used registers to be stdcall
385
        push    0               ; initialize .error_code
385
        push    0               ; initialize .error_code
386
        push    edx eax ecx ecx ; initialize stack variables
386
        push    edx eax ecx ecx ; initialize stack variables
387
        push    [.buffer-4]     ; copy [.buffer] to [.cur_buffer]
387
        push    [.buffer-4]     ; copy [.buffer] to [.cur_buffer]
388
                                ; -4 is due to esp-relative addressing
388
                                ; -4 is due to esp-relative addressing
389
virtual at esp
389
virtual at esp
390
.local_vars:
390
.local_vars:
391
.cur_buffer     dd      ?       ; pointer to data that are currently copying
391
.cur_buffer     dd      ?       ; pointer to data that are currently copying
392
.num_sectors_orig dd    ?
392
.num_sectors_orig dd    ?
393
; Number of sectors that should be written. Used to generate output value of ecx.
393
; Number of sectors that should be written. Used to generate output value of ecx.
394
.num_sectors    dd      ?
394
.num_sectors    dd      ?
395
; Number of sectors that remain to be written.
395
; Number of sectors that remain to be written.
396
.sector_lo      dd      ?       ; low 32 bits of the current sector
396
.sector_lo      dd      ?       ; low 32 bits of the current sector
397
.sector_hi      dd      ?       ; high 32 bits of the current sector
397
.sector_hi      dd      ?       ; high 32 bits of the current sector
398
.error_code     dd      ?       ; current status
398
.error_code     dd      ?       ; current status
399
.local_vars_size = $ - .local_vars
399
.local_vars_size = $ - .local_vars
400
.saved_regs     rd      2
400
.saved_regs     rd      2
401
.buffer         dd      ?       ; filled by fs_write64_{sys,app}
401
.buffer         dd      ?       ; filled by fs_write64_{sys,app}
402
end virtual
402
end virtual
403
; 2. Validate parameters against partition length:
403
; 2. Validate parameters against partition length:
404
; immediately return error if edx:eax are beyond partition end,
404
; immediately return error if edx:eax are beyond partition end,
405
; decrease .num_sectors and .num_sectors_orig, if needed,
405
; decrease .num_sectors and .num_sectors_orig, if needed,
406
; so that the entire operation fits in the partition limits.
406
; so that the entire operation fits in the partition limits.
407
        mov     eax, dword [ebp+PARTITION.Length]
407
        mov     eax, dword [ebp+PARTITION.Length]
408
        mov     edx, dword [ebp+PARTITION.Length+4]
408
        mov     edx, dword [ebp+PARTITION.Length+4]
409
        sub     eax, [.sector_lo]
409
        sub     eax, [.sector_lo]
410
        sbb     edx, [.sector_hi]
410
        sbb     edx, [.sector_hi]
411
        jb      .end_of_media
411
        jb      .end_of_media
412
        jnz     .no_end_of_media
412
        jnz     .no_end_of_media
413
        cmp     ecx, eax
413
        cmp     ecx, eax
414
        jbe     .no_end_of_media
414
        jbe     .no_end_of_media
415
; If .num_sectors got decreased, set status to DISK_STATUS_END_OF_MEDIA;
415
; If .num_sectors got decreased, set status to DISK_STATUS_END_OF_MEDIA;
416
; if all subsequent operations would be successful, this would become the final
416
; if all subsequent operations would be successful, this would become the final
417
; status, otherwise this would be rewritten by failed operation.
417
; status, otherwise this would be rewritten by failed operation.
418
        mov     [.num_sectors], eax
418
        mov     [.num_sectors], eax
419
        mov     [.num_sectors_orig], eax
419
        mov     [.num_sectors_orig], eax
420
        mov     [.error_code], DISK_STATUS_END_OF_MEDIA
420
        mov     [.error_code], DISK_STATUS_END_OF_MEDIA
421
.no_end_of_media:
421
.no_end_of_media:
422
; 3. If number of sectors to write is zero, either because zero-sectors operation
422
; 3. If number of sectors to write is zero, either because zero-sectors operation
423
; was requested or because it got decreased to zero due to partition limits,
423
; was requested or because it got decreased to zero due to partition limits,
424
; just return the current status.
424
; just return the current status.
425
        cmp     [.num_sectors], 0
425
        cmp     [.num_sectors], 0
426
        jz      .return
426
        jz      .return
427
; 4. Shift sector from partition-relative to absolute.
427
; 4. Shift sector from partition-relative to absolute.
428
        mov     eax, dword [ebp+PARTITION.FirstSector]
428
        mov     eax, dword [ebp+PARTITION.FirstSector]
429
        mov     edx, dword [ebp+PARTITION.FirstSector+4]
429
        mov     edx, dword [ebp+PARTITION.FirstSector+4]
430
        add     [.sector_lo], eax
430
        add     [.sector_lo], eax
431
        adc     [.sector_hi], edx
431
        adc     [.sector_hi], edx
432
; 5. If the cache is disabled, pass the request directly to the driver.
432
; 5. If the cache is disabled, pass the request directly to the driver.
433
        cmp     [ebx+DISKCACHE.pointer], 0
433
        cmp     [ebx+DISKCACHE.pointer], 0
434
        jz      .nocache
434
        jz      .nocache
435
; 6. Store sectors in the cache, sequentially from the beginning.
435
; 6. Store sectors in the cache, sequentially from the beginning.
436
; 6a. Acquire the lock.
436
; 6a. Acquire the lock.
437
        mov     ecx, [ebp+PARTITION.Disk]
437
        mov     ecx, [ebp+PARTITION.Disk]
438
        add     ecx, DISK.CacheLock
438
        add     ecx, DISK.CacheLock
439
        call    mutex_lock
439
        call    mutex_lock
440
.lookup_in_cache_loop:
440
.lookup_in_cache_loop:
441
; 6b. For each sector, call the lookup function with adding to the cache, if not yet.
441
; 6b. For each sector, call the lookup function with adding to the cache, if not yet.
442
        mov     eax, [.sector_lo]
442
        mov     eax, [.sector_lo]
443
        mov     edx, [.sector_hi]
443
        mov     edx, [.sector_hi]
444
        call    cache_lookup_write
444
        call    cache_lookup_write
445
        test    eax, eax
445
        test    eax, eax
446
        jnz     .cache_error
446
        jnz     .cache_error
447
; 6c. For each sector, copy data, mark the item as modified and not saved,
447
; 6c. For each sector, copy data, mark the item as modified and not saved,
448
; advance .current_buffer to the next sector.
448
; advance .current_buffer to the next sector.
449
        mov     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
449
        mov     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
450
        mov     eax, 1
450
        mov     eax, 1
451
        shl     eax, cl
451
        shl     eax, cl
452
        mov     esi, [.cur_buffer]
452
        mov     esi, [.cur_buffer]
453
        mov     ecx, eax
453
        mov     ecx, eax
454
        shr     ecx, 2
454
        shr     ecx, 2
455
        rep movsd
455
        rep movsd
456
        mov     [.cur_buffer], esi
456
        mov     [.cur_buffer], esi
457
; 6d. Remove the sector from the other cache.
457
; 6d. Remove the sector from the other cache.
458
; Normally it should not be there, but prefetching could put to the app cache
458
; Normally it should not be there, but prefetching could put to the app cache
459
; data that normally should belong to the sys cache and vice versa.
459
; data that normally should belong to the sys cache and vice versa.
460
; Note: this requires that both caches must be protected by the same lock.
460
; Note: this requires that both caches must be protected by the same lock.
461
        mov     eax, [.sector_lo]
461
        mov     eax, [.sector_lo]
462
        mov     edx, [.sector_hi]
462
        mov     edx, [.sector_hi]
463
        push    ebx
463
        push    ebx
464
        sub     ebx, [ebp+PARTITION.Disk]
464
        sub     ebx, [ebp+PARTITION.Disk]
465
        xor     ebx, DISK.SysCache xor DISK.AppCache
465
        xor     ebx, DISK.SysCache xor DISK.AppCache
466
        add     ebx, [ebp+PARTITION.Disk]
466
        add     ebx, [ebp+PARTITION.Disk]
467
        call    cache_lookup_read
467
        call    cache_lookup_read
468
        jc      @f
468
        jc      @f
469
        mov     [esi+CACHE_ITEM.Status], CACHE_ITEM_EMPTY
469
        mov     [esi+CACHE_ITEM.Status], CACHE_ITEM_EMPTY
470
@@:
470
@@:
471
        pop     ebx
471
        pop     ebx
472
; 6e. Advance .sector_hi:.sector_lo to the next sector.
472
; 6e. Advance .sector_hi:.sector_lo to the next sector.
473
        add     [.sector_lo], 1
473
        add     [.sector_lo], 1
474
        adc     [.sector_hi], 0
474
        adc     [.sector_hi], 0
475
; 6f. Continue the loop at 6b-6e until all sectors are processed.
475
; 6f. Continue the loop at 6b-6e until all sectors are processed.
476
        dec     [.num_sectors]
476
        dec     [.num_sectors]
477
        jnz     .lookup_in_cache_loop
477
        jnz     .lookup_in_cache_loop
478
.unlock_return:
478
.unlock_return:
479
; 6g. Release the lock and return.
479
; 6g. Release the lock and return.
480
        mov     ecx, [ebp+PARTITION.Disk]
480
        mov     ecx, [ebp+PARTITION.Disk]
481
        add     ecx, DISK.CacheLock
481
        add     ecx, DISK.CacheLock
482
        call    mutex_unlock
482
        call    mutex_unlock
483
.return:
483
.return:
484
        mov     eax, [.error_code]
484
        mov     eax, [.error_code]
485
        mov     ecx, [.num_sectors_orig]
485
        mov     ecx, [.num_sectors_orig]
486
        sub     ecx, [.num_sectors]
486
        sub     ecx, [.num_sectors]
487
.nothing:
487
.nothing:
488
        add     esp, .local_vars_size
488
        add     esp, .local_vars_size
489
        pop     edi esi ebx
489
        pop     edi esi ebx
490
        ret
490
        ret
491
 
491
 
492
; Special branches:
492
; Special branches:
493
.cache_error:
493
.cache_error:
494
; error at flushing the cache while adding sector to the cache:
494
; error at flushing the cache while adding sector to the cache:
495
; return the error from the lookup function
495
; return the error from the lookup function
496
        mov     [.error_code], eax
496
        mov     [.error_code], eax
497
        jmp     .unlock_return
497
        jmp     .unlock_return
498
.end_of_media:
498
.end_of_media:
499
; requested sector is beyond the partition end: return the corresponding error
499
; requested sector is beyond the partition end: return the corresponding error
500
        mov     eax, DISK_STATUS_END_OF_MEDIA
500
        mov     eax, DISK_STATUS_END_OF_MEDIA
501
        xor     ecx, ecx
501
        xor     ecx, ecx
502
        jmp     .nothing
502
        jmp     .nothing
503
.nocache:
503
.nocache:
504
; step 5, after correcting number of sectors to fit in partition limits
504
; step 5, after correcting number of sectors to fit in partition limits
505
; and advancing partition-relative sector to absolute,
505
; and advancing partition-relative sector to absolute,
506
; sees that cache is disabled: pass corrected request to the driver
506
; sees that cache is disabled: pass corrected request to the driver
507
        lea     eax, [.num_sectors]
507
        lea     eax, [.num_sectors]
508
        push    eax             ; numsectors
508
        push    eax             ; numsectors
509
        push    [.sector_hi+4]  ; startsector
509
        push    [.sector_hi+4]  ; startsector
510
        push    [.sector_lo+8]  ; startsector
510
        push    [.sector_lo+8]  ; startsector
511
        push    [.buffer+12]    ; buffer
511
        push    [.buffer+12]    ; buffer
512
        mov     esi, [ebp+PARTITION.Disk]
512
        mov     esi, [ebp+PARTITION.Disk]
513
        mov     al, DISKFUNC.write
513
        mov     al, DISKFUNC.write
514
        call    disk_call_driver
514
        call    disk_call_driver
515
        mov     ecx, [.num_sectors]
515
        mov     ecx, [.num_sectors]
516
        jmp     .nothing
516
        jmp     .nothing
517
 
517
 
518
; Legacy. Use fs_read64_sys instead.
518
; Legacy. Use fs_read64_sys instead.
519
; This function is intended to replace the old 'hd_read' function when
519
; This function is intended to replace the old 'hd_read' function when
520
; [hdd_appl_data] = 0, so its input/output parameters are the same, except
520
; [hdd_appl_data] = 0, so its input/output parameters are the same, except
521
; that it can't use the global variables 'hd_error' and 'hdd_appl_data'.
521
; that it can't use the global variables 'hd_error' and 'hdd_appl_data'.
522
; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure
522
; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure
523
; eax is relative to partition start
523
; eax is relative to partition start
524
; out: eax = error code; 0 = ok
524
; out: eax = error code; 0 = ok
525
fs_read32_sys:
525
fs_read32_sys:
526
; Save ebx, set ebx to SysCache and let the common part do its work.
526
; Save ebx, set ebx to SysCache and let the common part do its work.
527
        push    ebx
527
        push    ebx
528
        mov     ebx, [ebp+PARTITION.Disk]
528
        mov     ebx, [ebp+PARTITION.Disk]
529
        add     ebx, DISK.SysCache
529
        add     ebx, DISK.SysCache
530
        jmp     fs_read32_common
530
        jmp     fs_read32_common
531
 
531
 
532
; Legacy. Use fs_read64_app instead.
532
; Legacy. Use fs_read64_app instead.
533
; This function is intended to replace the old 'hd_read' function when
533
; This function is intended to replace the old 'hd_read' function when
534
; [hdd_appl_data] = 1, so its input/output parameters are the same, except
534
; [hdd_appl_data] = 1, so its input/output parameters are the same, except
535
; that it can't use the global variables 'hd_error' and 'hdd_appl_data'.
535
; that it can't use the global variables 'hd_error' and 'hdd_appl_data'.
536
; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure
536
; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure
537
; eax is relative to partition start
537
; eax is relative to partition start
538
; out: eax = error code; 0 = ok
538
; out: eax = error code; 0 = ok
539
fs_read32_app:
539
fs_read32_app:
540
; Save ebx, set ebx to AppCache and let the common part do its work.
540
; Save ebx, set ebx to AppCache and let the common part do its work.
541
        push    ebx
541
        push    ebx
542
        mov     ebx, [ebp+PARTITION.Disk]
542
        mov     ebx, [ebp+PARTITION.Disk]
543
        add     ebx, DISK.AppCache
543
        add     ebx, DISK.AppCache
544
 
544
 
545
; This label is the common part of fs_read32_sys and fs_read32_app.
545
; This label is the common part of fs_read32_sys and fs_read32_app.
546
fs_read32_common:
546
fs_read32_common:
547
; 1. Check that the required sector is inside the partition. If no, return
547
; 1. Check that the required sector is inside the partition. If no, return
548
; DISK_STATUS_END_OF_MEDIA.
548
; DISK_STATUS_END_OF_MEDIA.
549
        cmp     dword [ebp+PARTITION.Length+4], 0
549
        cmp     dword [ebp+PARTITION.Length+4], 0
550
        jnz     @f
550
        jnz     @f
551
        cmp     dword [ebp+PARTITION.Length], eax
551
        cmp     dword [ebp+PARTITION.Length], eax
552
        ja      @f
552
        ja      @f
553
        mov     eax, DISK_STATUS_END_OF_MEDIA
553
        mov     eax, DISK_STATUS_END_OF_MEDIA
554
        pop     ebx
554
        pop     ebx
555
        ret
555
        ret
556
@@:
556
@@:
557
; 2. Get the absolute sector on the disk.
557
; 2. Get the absolute sector on the disk.
558
        push    ecx edx esi edi
558
        push    ecx edx esi edi
559
        xor     edx, edx
559
        xor     edx, edx
560
        add     eax, dword [ebp+PARTITION.FirstSector]
560
        add     eax, dword [ebp+PARTITION.FirstSector]
561
        adc     edx, dword [ebp+PARTITION.FirstSector+4]
561
        adc     edx, dword [ebp+PARTITION.FirstSector+4]
562
; 3. If there is no cache for this disk, just pass the request to the driver.
562
; 3. If there is no cache for this disk, just pass the request to the driver.
563
        cmp     [ebx+DISKCACHE.pointer], 0
563
        cmp     [ebx+DISKCACHE.pointer], 0
564
        jnz     .scancache
564
        jnz     .scancache
565
        push    1
565
        push    1
566
        push    esp     ; numsectors
566
        push    esp     ; numsectors
567
        push    edx     ; startsector
567
        push    edx     ; startsector
568
        push    eax     ; startsector
568
        push    eax     ; startsector
569
        pushd   [esp+32]; buffer
569
        pushd   [esp+32]; buffer
570
        mov     esi, [ebp+PARTITION.Disk]
570
        mov     esi, [ebp+PARTITION.Disk]
571
        mov     al, DISKFUNC.read
571
        mov     al, DISKFUNC.read
572
        call    disk_call_driver
572
        call    disk_call_driver
573
        pop     ecx
573
        pop     ecx
574
        pop     edi esi edx ecx
574
        pop     edi esi edx ecx
575
        pop     ebx
575
        pop     ebx
576
        ret
576
        ret
577
.scancache:
577
.scancache:
578
        push    ebx edx eax
578
        push    ebx edx eax
579
virtual at esp
579
virtual at esp
580
.local_vars:
580
.local_vars:
581
.sector_lo      dd      ?
581
.sector_lo      dd      ?
582
.sector_hi      dd      ?
582
.sector_hi      dd      ?
583
.cache          dd      ?
583
.cache          dd      ?
584
.local_vars_size = $ - .local_vars
584
.local_vars_size = $ - .local_vars
585
.saved_regs     rd      4
585
.saved_regs     rd      4
586
.buffer         dd      ?
586
.buffer         dd      ?
587
end virtual
587
end virtual
588
; 4. Scan for the requested sector in the cache.
588
; 4. Scan for the requested sector in the cache.
589
; If found, copy the data and return.
589
; If found, copy the data and return.
590
; 4a. Acquire the lock.
590
; 4a. Acquire the lock.
591
        mov     ecx, [ebp+PARTITION.Disk]
591
        mov     ecx, [ebp+PARTITION.Disk]
592
        add     ecx, DISK.CacheLock
592
        add     ecx, DISK.CacheLock
593
        call    mutex_lock
593
        call    mutex_lock
594
; 4b. Call the lookup function without adding to the cache.
594
; 4b. Call the lookup function without adding to the cache.
595
        mov     eax, [.sector_lo]
595
        mov     eax, [.sector_lo]
596
        mov     edx, [.sector_hi]
596
        mov     edx, [.sector_hi]
597
        call    cache_lookup_read
597
        call    cache_lookup_read
598
; If not found, go to 5.
598
; If not found, go to 5.
599
        jc      .not_found_in_cache
599
        jc      .not_found_in_cache
600
.found_in_cache:
600
.found_in_cache:
601
; 4c. Copy the data.
601
; 4c. Copy the data.
602
        mov     esi, edi
602
        mov     esi, edi
603
        mov     edi, [.buffer]
603
        mov     edi, [.buffer]
604
        mov     eax, 1
604
        mov     eax, 1
605
        shl     eax, cl
605
        shl     eax, cl
606
        mov     ecx, eax
606
        mov     ecx, eax
607
        shr     ecx, 2
607
        shr     ecx, 2
608
        rep movsd
608
        rep movsd
609
; 4d. Release the lock and return success.
609
; 4d. Release the lock and return success.
610
        mov     ecx, [ebp+PARTITION.Disk]
610
        mov     ecx, [ebp+PARTITION.Disk]
611
        add     ecx, DISK.CacheLock
611
        add     ecx, DISK.CacheLock
612
        call    mutex_unlock
612
        call    mutex_unlock
613
.return:
613
.return:
614
        xor     eax, eax
614
        xor     eax, eax
615
.return_eax:
615
.return_eax:
616
        add     esp, .local_vars_size
616
        add     esp, .local_vars_size
617
        pop     edi esi edx ecx
617
        pop     edi esi edx ecx
618
        pop     ebx
618
        pop     ebx
619
        ret
619
        ret
620
.not_found_in_cache:
620
.not_found_in_cache:
621
; 5. Decide whether we need to prefetch further sectors.
621
; 5. Decide whether we need to prefetch further sectors.
622
; If so, advance to 6. If not, go to 13.
622
; If so, advance to 6. If not, go to 13.
623
; Assume that devices < 3MB are floppies which are slow
623
; Assume that devices < 3MB are floppies which are slow
624
; (ramdisk does not have a cache, so we don't even get here for ramdisk).
624
; (ramdisk does not have a cache, so we don't even get here for ramdisk).
625
; This is a dirty hack, but the entire function is somewhat hacky. Use fs_read64*.
625
; This is a dirty hack, but the entire function is somewhat hacky. Use fs_read64*.
626
        mov     ecx, [ebp+PARTITION.Disk]
626
        mov     ecx, [ebp+PARTITION.Disk]
627
        cmp     dword [ecx+DISK.MediaInfo.Capacity+4], 0
627
        cmp     dword [ecx+DISK.MediaInfo.Capacity+4], 0
628
        jnz     @f
628
        jnz     @f
629
        cmp     dword [ecx+DISK.MediaInfo.Capacity], 3 shl (20-9)
629
        cmp     dword [ecx+DISK.MediaInfo.Capacity], 3 shl (20-9)
630
        jb      .floppy
630
        jb      .floppy
631
@@:
631
@@:
632
; We want to prefetch CACHE_LEGACY_READ_SIZE sectors.
632
; We want to prefetch CACHE_LEGACY_READ_SIZE sectors.
633
; 6. Release the lock acquired at step 4a.
633
; 6. Release the lock acquired at step 4a.
634
        mov     ecx, [ebp+PARTITION.Disk]
634
        mov     ecx, [ebp+PARTITION.Disk]
635
        add     ecx, DISK.CacheLock
635
        add     ecx, DISK.CacheLock
636
        call    mutex_unlock
636
        call    mutex_unlock
637
; 7. Allocate buffer for CACHE_LEGACY_READ_SIZE sectors.
637
; 7. Allocate buffer for CACHE_LEGACY_READ_SIZE sectors.
638
        mov     eax, CACHE_LEGACY_READ_SIZE
638
        mov     eax, CACHE_LEGACY_READ_SIZE
639
        mov     ecx, [ebx+DISKCACHE.sector_size_log]
639
        mov     ecx, [ebx+DISKCACHE.sector_size_log]
640
        shl     eax, cl
640
        shl     eax, cl
641
        stdcall kernel_alloc, eax
641
        stdcall kernel_alloc, eax
642
; If failed, return the corresponding error code.
642
; If failed, return the corresponding error code.
643
        test    eax, eax
643
        test    eax, eax
644
        jz      .nomemory
644
        jz      .nomemory
645
; 8. Create second portion of local variables.
645
; 8. Create second portion of local variables.
646
        push    eax eax
646
        push    eax eax
647
        push    CACHE_LEGACY_READ_SIZE
647
        push    CACHE_LEGACY_READ_SIZE
648
virtual at esp
648
virtual at esp
649
.local_vars2:
649
.local_vars2:
650
.num_sectors            dd      ?       ; number of sectors left
650
.num_sectors            dd      ?       ; number of sectors left
651
.current_buffer         dd      ?       ; pointer to data that are currently copying
651
.current_buffer         dd      ?       ; pointer to data that are currently copying
652
.allocated_buffer       dd      ?       ; saved at safe place
652
.allocated_buffer       dd      ?       ; saved at safe place
653
.local_vars2_size = $ - .local_vars2
653
.local_vars2_size = $ - .local_vars2
654
end virtual
654
end virtual
655
; 9. Call the driver to read CACHE_LEGACY_READ_SIZE sectors.
655
; 9. Call the driver to read CACHE_LEGACY_READ_SIZE sectors.
656
        push    esp     ; numsectors
656
        push    esp     ; numsectors
657
        push    [.sector_hi+.local_vars2_size+4]        ; startsector
657
        push    [.sector_hi+.local_vars2_size+4]        ; startsector
658
        push    [.sector_lo+.local_vars2_size+8]        ; startsector
658
        push    [.sector_lo+.local_vars2_size+8]        ; startsector
659
        push    eax     ; buffer
659
        push    eax     ; buffer
660
        mov     esi, [ebp+PARTITION.Disk]
660
        mov     esi, [ebp+PARTITION.Disk]
661
        mov     al, DISKFUNC.read
661
        mov     al, DISKFUNC.read
662
        call    disk_call_driver
662
        call    disk_call_driver
663
; Note: we're ok if at least one sector is read,
663
; Note: we're ok if at least one sector is read,
664
; read error somewhere after that just limits data to be put in cache.
664
; read error somewhere after that just limits data to be put in cache.
665
        cmp     [.num_sectors], 0
665
        cmp     [.num_sectors], 0
666
        jz      .read_error
666
        jz      .read_error
667
; 10. Copy data for the caller.
667
; 10. Copy data for the caller.
668
        mov     esi, [.allocated_buffer]
668
        mov     esi, [.allocated_buffer]
669
        mov     edi, [.buffer+.local_vars2_size]
669
        mov     edi, [.buffer+.local_vars2_size]
670
        mov     ecx, [ebx+DISKCACHE.sector_size_log]
670
        mov     ecx, [ebx+DISKCACHE.sector_size_log]
671
        mov     eax, 1
671
        mov     eax, 1
672
        shl     eax, cl
672
        shl     eax, cl
673
        mov     ecx, eax
673
        mov     ecx, eax
674
        shr     ecx, 2
674
        shr     ecx, 2
675
        rep movsd
675
        rep movsd
676
; 11. Store all sectors that were successfully read to the cache.
676
; 11. Store all sectors that were successfully read to the cache.
677
; 11a. Acquire the lock.
677
; 11a. Acquire the lock.
678
        mov     ecx, [ebp+PARTITION.Disk]
678
        mov     ecx, [ebp+PARTITION.Disk]
679
        add     ecx, DISK.CacheLock
679
        add     ecx, DISK.CacheLock
680
        call    mutex_lock
680
        call    mutex_lock
681
.store_to_cache:
681
.store_to_cache:
682
; 11b. For each sector, call the lookup function with adding to the cache, if not yet.
682
; 11b. For each sector, call the lookup function with adding to the cache, if not yet.
683
        mov     eax, [.sector_lo+.local_vars2_size]
683
        mov     eax, [.sector_lo+.local_vars2_size]
684
        mov     edx, [.sector_hi+.local_vars2_size]
684
        mov     edx, [.sector_hi+.local_vars2_size]
685
        call    cache_lookup_write
685
        call    cache_lookup_write
686
        test    eax, eax
686
        test    eax, eax
687
        jnz     .cache_error
687
        jnz     .cache_error
688
; 11c. Ignore sectors marked as modified: for them the cache is more recent that disk data.
688
; 11c. Ignore sectors marked as modified: for them the cache is more recent that disk data.
689
        mov     eax, 1
689
        mov     eax, 1
690
        shl     eax, cl
690
        shl     eax, cl
691
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
691
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
692
        jnz     .not_modified
692
        jnz     .not_modified
693
        add     [.current_buffer], eax
693
        add     [.current_buffer], eax
694
        jmp     .sector_done
694
        jmp     .sector_done
695
.not_modified:
695
.not_modified:
696
; 11d. For each sector, copy data, mark the item as not-modified copy of the disk,
696
; 11d. For each sector, copy data, mark the item as not-modified copy of the disk,
697
; advance .current_buffer and .sector_hi:.sector_lo to the next sector.
697
; advance .current_buffer and .sector_hi:.sector_lo to the next sector.
698
        mov     [esi+CACHE_ITEM.Status], CACHE_ITEM_COPY
698
        mov     [esi+CACHE_ITEM.Status], CACHE_ITEM_COPY
699
        mov     esi, [.current_buffer]
699
        mov     esi, [.current_buffer]
700
        mov     ecx, eax
700
        mov     ecx, eax
701
        shr     ecx, 2
701
        shr     ecx, 2
702
        rep movsd
702
        rep movsd
703
        mov     [.current_buffer], esi
703
        mov     [.current_buffer], esi
704
.sector_done:
704
.sector_done:
705
        add     [.sector_lo+.local_vars2_size], 1
705
        add     [.sector_lo+.local_vars2_size], 1
706
        adc     [.sector_hi+.local_vars2_size], 0
706
        adc     [.sector_hi+.local_vars2_size], 0
707
; 11e. Continue the loop at 11b-11d until all sectors are processed.
707
; 11e. Continue the loop at 11b-11d until all sectors are processed.
708
        dec     [.num_sectors]
708
        dec     [.num_sectors]
709
        jnz     .store_to_cache
709
        jnz     .store_to_cache
710
.cache_error:
710
.cache_error:
711
; 11f. Release the lock.
711
; 11f. Release the lock.
712
        mov     ecx, [ebp+PARTITION.Disk]
712
        mov     ecx, [ebp+PARTITION.Disk]
713
        add     ecx, DISK.CacheLock
713
        add     ecx, DISK.CacheLock
714
        call    mutex_unlock
714
        call    mutex_unlock
715
.copy_done:
715
.copy_done:
716
; 12. Remove portion of local variables created at step 8,
716
; 12. Remove portion of local variables created at step 8,
717
; free the buffer allocated at step 7 and return.
717
; free the buffer allocated at step 7 and return.
718
        pop     ecx ecx
718
        pop     ecx ecx
719
        stdcall kernel_free
719
        stdcall kernel_free
720
        jmp     .return
720
        jmp     .return
721
.read_error:
721
.read_error:
722
; If no sectors were read, free the buffer allocated at step 7
722
; If no sectors were read, free the buffer allocated at step 7
723
; and pass the error to the caller.
723
; and pass the error to the caller.
724
        push    eax
724
        push    eax
725
        stdcall kernel_free, [.allocated_buffer+4]
725
        stdcall kernel_free, [.allocated_buffer+4]
726
        pop     eax
726
        pop     eax
727
        add     esp, .local_vars2_size
727
        add     esp, .local_vars2_size
728
        jmp     .return_eax
728
        jmp     .return_eax
729
.nomemory:
729
.nomemory:
730
        mov     eax, DISK_STATUS_NO_MEMORY
730
        mov     eax, DISK_STATUS_NO_MEMORY
731
        jmp     .return_eax
731
        jmp     .return_eax
732
.floppy:
732
.floppy:
733
; We don't want to prefetch anything, just read one sector.
733
; We don't want to prefetch anything, just read one sector.
734
; We are still holding the lock acquired at step 4a.
734
; We are still holding the lock acquired at step 4a.
735
; 13. Call the lookup function adding sector to the cache.
735
; 13. Call the lookup function adding sector to the cache.
736
        call    cache_lookup_write
736
        call    cache_lookup_write
737
        test    eax, eax
737
        test    eax, eax
738
        jnz     .floppy_cache_error
738
        jnz     .floppy_cache_error
739
        push    esi
739
        push    esi
740
 
740
 
741
; 14. Call the driver to read one sector.
741
; 14. Call the driver to read one sector.
742
        push    1
742
        push    1
743
        push    esp
743
        push    esp
744
        push    edx
744
        push    edx
745
        push    [.sector_lo+16]
745
        push    [.sector_lo+16]
746
        push    edi
746
        push    edi
747
        mov     esi, [ebp+PARTITION.Disk]
747
        mov     esi, [ebp+PARTITION.Disk]
748
        mov     al, DISKFUNC.read
748
        mov     al, DISKFUNC.read
749
        call    disk_call_driver
749
        call    disk_call_driver
750
        pop     ecx
750
        pop     ecx
751
        dec     ecx
751
        dec     ecx
752
        jnz     .floppy_read_error
752
        jnz     .floppy_read_error
753
; 15. Get the slot and pointer to the cache item,
753
; 15. Get the slot and pointer to the cache item,
754
; change the status to not-modified copy of the disk
754
; change the status to not-modified copy of the disk
755
; and go to 4c.
755
; and go to 4c.
756
        pop     esi
756
        pop     esi
757
        mov     [esi+CACHE_ITEM.Status], CACHE_ITEM_COPY
757
        mov     [esi+CACHE_ITEM.Status], CACHE_ITEM_COPY
758
        mov     ecx, [ebx+DISKCACHE.sector_size_log]
758
        mov     ecx, [ebx+DISKCACHE.sector_size_log]
759
        jmp     .found_in_cache
759
        jmp     .found_in_cache
760
 
760
 
761
; On error at steps 13-14, release the lock
761
; On error at steps 13-14, release the lock
762
; and pass the error to the caller.
762
; and pass the error to the caller.
763
.floppy_read_error:
763
.floppy_read_error:
764
        pop     ecx
764
        pop     ecx
765
.floppy_cache_error:
765
.floppy_cache_error:
766
        mov     ecx, [ebp+PARTITION.Disk]
766
        mov     ecx, [ebp+PARTITION.Disk]
767
        add     ecx, DISK.CacheLock
767
        add     ecx, DISK.CacheLock
768
        push    eax
768
        push    eax
769
        call    mutex_unlock
769
        call    mutex_unlock
770
        pop     eax
770
        pop     eax
771
        jmp     .return_eax
771
        jmp     .return_eax
772
 
772
 
773
; This function is intended to replace the old 'hd_write' function when
773
; This function is intended to replace the old 'hd_write' function when
774
; [hdd_appl_data] = 0, so its input/output parameters are the same, except
774
; [hdd_appl_data] = 0, so its input/output parameters are the same, except
775
; that it can't use the global variables 'hd_error' and 'hdd_appl_data'.
775
; that it can't use the global variables 'hd_error' and 'hdd_appl_data'.
776
; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure
776
; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure
777
; eax is relative to partition start
777
; eax is relative to partition start
778
; out: eax = error code; 0 = ok
778
; out: eax = error code; 0 = ok
779
fs_write32_sys:
779
fs_write32_sys:
780
; Just call the advanced function.
780
; Just call the advanced function.
781
        push    ecx edx
781
        push    ecx edx
782
        xor     edx, edx
782
        xor     edx, edx
783
        mov     ecx, 1
783
        mov     ecx, 1
784
        call    fs_write64_sys
784
        call    fs_write64_sys
785
        pop     edx ecx
785
        pop     edx ecx
786
        ret
786
        ret
787
 
787
 
788
; This function is intended to replace the old 'hd_write' function when
788
; This function is intended to replace the old 'hd_write' function when
789
; [hdd_appl_data] = 1, so its input/output parameters are the same, except
789
; [hdd_appl_data] = 1, so its input/output parameters are the same, except
790
; that it can't use the global variables 'hd_error' and 'hdd_appl_data'.
790
; that it can't use the global variables 'hd_error' and 'hdd_appl_data'.
791
; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure
791
; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure
792
; eax is relative to partition start
792
; eax is relative to partition start
793
; out: eax = error code; 0 = ok
793
; out: eax = error code; 0 = ok
794
fs_write32_app:
794
fs_write32_app:
795
; Just call the advanced function.
795
; Just call the advanced function.
796
        push    ecx edx
796
        push    ecx edx
797
        xor     edx, edx
797
        xor     edx, edx
798
        mov     ecx, 1
798
        mov     ecx, 1
799
        call    fs_write64_app
799
        call    fs_write64_app
800
        pop     edx ecx
800
        pop     edx ecx
801
        ret
801
        ret
802
 
802
 
803
; Lookup for the given sector in the given cache.
803
; Lookup for the given sector in the given cache.
804
; If the sector is not present, return error.
804
; If the sector is not present, return error.
805
; The caller must acquire the cache lock.
805
; The caller must acquire the cache lock.
806
; in: edx:eax = sector
806
; in: edx:eax = sector
807
; in: ebx -> DISKCACHE structure
807
; in: ebx -> DISKCACHE structure
808
; out: CF set if sector is not in cache
808
; out: CF set if sector is not in cache
809
; out: ecx = sector_size_log
809
; out: ecx = sector_size_log
810
; out: esi -> sector:status
810
; out: esi -> sector:status
811
; out: edi -> sector data
811
; out: edi -> sector data
812
proc cache_lookup_read
812
proc cache_lookup_read
813
        mov     esi, [ebx+DISKCACHE.pointer]
813
        mov     esi, [ebx+DISKCACHE.pointer]
814
        add     esi, sizeof.CACHE_ITEM
814
        add     esi, sizeof.CACHE_ITEM
815
 
815
 
816
        mov     edi, 1
816
        mov     edi, 1
817
 
817
 
818
.hdreadcache:
818
.hdreadcache:
819
 
819
 
820
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_EMPTY
820
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_EMPTY
821
        je      .nohdcache
821
        je      .nohdcache
822
 
822
 
823
        cmp     [esi+CACHE_ITEM.SectorLo], eax
823
        cmp     [esi+CACHE_ITEM.SectorLo], eax
824
        jne     .nohdcache
824
        jne     .nohdcache
825
        cmp     [esi+CACHE_ITEM.SectorHi], edx
825
        cmp     [esi+CACHE_ITEM.SectorHi], edx
826
        jne     .nohdcache
826
        jne     .nohdcache
827
        mov     ecx, [ebx+DISKCACHE.sector_size_log]
827
        mov     ecx, [ebx+DISKCACHE.sector_size_log]
828
        shl     edi, cl
828
        shl     edi, cl
829
        add     edi, [ebx+DISKCACHE.data]
829
        add     edi, [ebx+DISKCACHE.data]
830
        clc
830
        clc
831
        ret
831
        ret
832
 
832
 
833
.nohdcache:
833
.nohdcache:
834
 
834
 
835
        add     esi, sizeof.CACHE_ITEM
835
        add     esi, sizeof.CACHE_ITEM
836
        inc     edi
836
        inc     edi
837
        cmp     edi, [ebx+DISKCACHE.sad_size]
837
        cmp     edi, [ebx+DISKCACHE.sad_size]
838
        jbe     .hdreadcache
838
        jbe     .hdreadcache
839
        stc
839
        stc
840
        ret
840
        ret
841
endp
841
endp
842
 
842
 
843
; Lookup for the given sector in the given cache.
843
; Lookup for the given sector in the given cache.
844
; If the sector is not present, allocate space for it,
844
; If the sector is not present, allocate space for it,
845
; possibly flushing data.
845
; possibly flushing data.
846
; in: edx:eax = sector
846
; in: edx:eax = sector
847
; in: ebx -> DISKCACHE structure
847
; in: ebx -> DISKCACHE structure
848
; in: ebp -> PARTITION structure
848
; in: ebp -> PARTITION structure
849
; out: eax = error code
849
; out: eax = error code
850
; out: esi -> sector:status
850
; out: esi -> sector:status
851
; out: edi -> sector data
851
; out: edi -> sector data
852
proc cache_lookup_write
852
proc cache_lookup_write
853
        call    cache_lookup_read
853
        call    cache_lookup_read
854
        jnc     .return0
854
        jnc     .return0
855
        push    edx eax
855
        push    edx eax
856
;-----------------------------------------------------------
856
;-----------------------------------------------------------
857
; find empty or read slot, flush cache if next 12.5% is used by write
857
; find empty or read slot, flush cache if next 12.5% is used by write
858
; output : ecx = cache slot
858
; output : ecx = cache slot
859
;-----------------------------------------------------------
859
;-----------------------------------------------------------
860
; Note: the code is essentially inherited, so probably
860
; Note: the code is essentially inherited, so probably
861
; no analysis of efficiency were done.
861
; no analysis of efficiency were done.
862
; However, it works.
862
; However, it works.
863
.search_again:
863
.search_again:
864
        mov     eax, [ebx+DISKCACHE.sad_size]
864
        mov     eax, [ebx+DISKCACHE.sad_size]
865
        mov     ecx, [ebx+DISKCACHE.search_start]
865
        mov     ecx, [ebx+DISKCACHE.search_start]
866
        shr     eax, 3
866
        shr     eax, 3
867
        lea     esi, [ecx*sizeof.CACHE_ITEM/4]
867
        lea     esi, [ecx*sizeof.CACHE_ITEM/4]
868
        shl     esi, 2
868
        shl     esi, 2
869
        add     esi, [ebx+DISKCACHE.pointer]
869
        add     esi, [ebx+DISKCACHE.pointer]
870
.search_for_empty:
870
.search_for_empty:
871
        inc     ecx
871
        inc     ecx
872
        add     esi, sizeof.CACHE_ITEM
872
        add     esi, sizeof.CACHE_ITEM
873
        cmp     ecx, [ebx+DISKCACHE.sad_size]
873
        cmp     ecx, [ebx+DISKCACHE.sad_size]
874
        jbe     .inside_cache
874
        jbe     .inside_cache
875
        mov     ecx, 1
875
        mov     ecx, 1
876
        mov     esi, [ebx+DISKCACHE.pointer]
876
        mov     esi, [ebx+DISKCACHE.pointer]
877
        add     esi, sizeof.CACHE_ITEM
877
        add     esi, sizeof.CACHE_ITEM
878
.inside_cache:
878
.inside_cache:
879
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
879
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
880
        jb      .found_slot             ; it's empty or read
880
        jb      .found_slot             ; it's empty or read
881
        dec     eax
881
        dec     eax
882
        jnz     .search_for_empty
882
        jnz     .search_for_empty
883
        stdcall write_cache64, [ebp+PARTITION.Disk] ; no empty slots found, write all
883
        stdcall write_cache64, [ebp+PARTITION.Disk] ; no empty slots found, write all
884
        test    eax, eax
884
        test    eax, eax
885
        jne     .found_slot_access_denied
885
        jne     .found_slot_access_denied
886
        jmp     .search_again           ; and start again
886
        jmp     .search_again           ; and start again
887
.found_slot:
887
.found_slot:
888
        mov     [ebx+DISKCACHE.search_start], ecx
888
        mov     [ebx+DISKCACHE.search_start], ecx
889
        popd    [esi+CACHE_ITEM.SectorLo]
889
        popd    [esi+CACHE_ITEM.SectorLo]
890
        popd    [esi+CACHE_ITEM.SectorHi]
890
        popd    [esi+CACHE_ITEM.SectorHi]
891
        mov     [esi+CACHE_ITEM.Status], CACHE_ITEM_EMPTY
891
        mov     [esi+CACHE_ITEM.Status], CACHE_ITEM_EMPTY
892
        mov     edi, ecx
892
        mov     edi, ecx
893
        mov     ecx, [ebx+DISKCACHE.sector_size_log]
893
        mov     ecx, [ebx+DISKCACHE.sector_size_log]
894
        shl     edi, cl
894
        shl     edi, cl
895
        add     edi, [ebx+DISKCACHE.data]
895
        add     edi, [ebx+DISKCACHE.data]
896
.return0:
896
.return0:
897
        xor     eax, eax        ; success
897
        xor     eax, eax        ; success
898
        ret
898
        ret
899
.found_slot_access_denied:
899
.found_slot_access_denied:
900
        add     esp, 8
900
        add     esp, 8
901
        ret
901
        ret
902
endp
902
endp
903
 
903
 
904
; Flush the given cache.
904
; Flush the given cache.
905
; The caller must acquire the cache lock.
905
; The caller must acquire the cache lock.
906
; in: ebx -> DISKCACHE
906
; in: ebx -> DISKCACHE
907
; in: first argument in stdcall convention -> PARTITION
907
; in: first argument in stdcall convention -> PARTITION
908
proc write_cache64
908
proc write_cache64
909
; 1. Setup stack frame.
909
; 1. Setup stack frame.
910
        push    esi edi         ; save used registers to be stdcall
910
        push    esi edi         ; save used registers to be stdcall
911
        sub     esp, .local_vars_size   ; reserve space for local vars
911
        sub     esp, .local_vars_size   ; reserve space for local vars
912
virtual at esp
912
virtual at esp
913
.local_vars:
913
.local_vars:
914
.cache_end      dd      ?       ; item past the end of the cache
914
.cache_end      dd      ?       ; item past the end of the cache
915
.size_left      dd      ?       ; items left to scan
915
.size_left      dd      ?       ; items left to scan
916
.current_ptr    dd      ?       ; pointer to the current item
916
.current_ptr    dd      ?       ; pointer to the current item
917
;
917
;
918
; Write operations are coalesced in chains,
918
; Write operations are coalesced in chains,
919
; one chain describes a sequential interval of sectors,
919
; one chain describes a sequential interval of sectors,
920
; they can be sequential or scattered in the cache.
920
; they can be sequential or scattered in the cache.
921
.sequential     dd      ?
921
.sequential     dd      ?
922
; boolean variable, 1 if the current chain is sequential in the cache,
922
; boolean variable, 1 if the current chain is sequential in the cache,
923
; 0 if additional buffer is needed to perform the operation
923
; 0 if additional buffer is needed to perform the operation
924
.chain_start_pos dd     ?       ; data of chain start item
924
.chain_start_pos dd     ?       ; data of chain start item
925
.chain_start_ptr dd     ?       ; pointer to chain start item
925
.chain_start_ptr dd     ?       ; pointer to chain start item
926
.chain_size     dd      ?       ; chain size (thanks, C.O.)
926
.chain_size     dd      ?       ; chain size (thanks, C.O.)
927
.iteration_size dd      ?
927
.iteration_size dd      ?
928
; If the chain size is too large, split the operation to several iterations.
928
; If the chain size is too large, split the operation to several iterations.
929
; This is size in sectors for one iterations.
929
; This is size in sectors for one iterations.
930
.iteration_buffer dd    ?       ; temporary buffer for non-sequential chains
930
.iteration_buffer dd    ?       ; temporary buffer for non-sequential chains
931
.local_vars_size = $ - .local_vars
931
.local_vars_size = $ - .local_vars
932
                rd      2       ; saved registers
932
                rd      2       ; saved registers
933
                dd      ?       ; return address
933
                dd      ?       ; return address
934
.disk           dd      ?       ; first argument
934
.disk           dd      ?       ; first argument
935
end virtual
935
end virtual
936
; 1. If there is no cache for this disk, nothing to do, just return zero.
936
; 1. If there is no cache for this disk, nothing to do, just return zero.
937
        cmp     [ebx+DISKCACHE.pointer], 0
937
        cmp     [ebx+DISKCACHE.pointer], 0
938
        jz      .return0
938
        jz      .return0
939
; 2. Prepare for the loop: initialize current pointer and .size_left,
939
; 2. Prepare for the loop: initialize current pointer and .size_left,
940
; calculate .cache_end.
940
; calculate .cache_end.
941
        mov     ecx, [ebx+DISKCACHE.sad_size]
941
        mov     ecx, [ebx+DISKCACHE.sad_size]
942
        mov     [.size_left], ecx
942
        mov     [.size_left], ecx
943
        lea     ecx, [ecx*sizeof.CACHE_ITEM/4]
943
        lea     ecx, [ecx*sizeof.CACHE_ITEM/4]
944
        shl     ecx, 2
944
        shl     ecx, 2
945
        mov     esi, [ebx+DISKCACHE.pointer]
945
        mov     esi, [ebx+DISKCACHE.pointer]
946
        add     esi, sizeof.CACHE_ITEM
946
        add     esi, sizeof.CACHE_ITEM
947
        add     ecx, esi
947
        add     ecx, esi
948
        mov     [.cache_end], ecx
948
        mov     [.cache_end], ecx
949
; 3. Main loop: go over all items, go to 5 for every modified item.
949
; 3. Main loop: go over all items, go to 5 for every modified item.
950
.look:
950
.look:
951
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
951
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
952
        jz      .begin_write
952
        jz      .begin_write
953
.look_next:
953
.look_next:
954
        add     esi, sizeof.CACHE_ITEM
954
        add     esi, sizeof.CACHE_ITEM
955
        dec     [.size_left]
955
        dec     [.size_left]
956
        jnz     .look
956
        jnz     .look
957
; 4. Return success.
957
; 4. Return success.
958
.return0:
958
.return0:
959
        xor     eax, eax
959
        xor     eax, eax
960
.return:
960
.return:
961
        add     esp, .local_vars_size
961
        add     esp, .local_vars_size
962
        pop     edi esi         ; restore used registers to be stdcall
962
        pop     edi esi         ; restore used registers to be stdcall
963
        ret     4               ; return popping one argument
963
        ret     4               ; return popping one argument
964
.begin_write:
964
.begin_write:
965
; We have found a modified item.
965
; We have found a modified item.
966
; 5. Prepare for chain finding: save the current item, initialize chain variables.
966
; 5. Prepare for chain finding: save the current item, initialize chain variables.
967
        mov     [.current_ptr], esi
967
        mov     [.current_ptr], esi
968
; Initialize chain as sequential zero-length starting at the current item.
968
; Initialize chain as sequential zero-length starting at the current item.
969
        mov     [.chain_start_ptr], esi
969
        mov     [.chain_start_ptr], esi
970
        mov     eax, [ebx+DISKCACHE.sad_size]
970
        mov     eax, [ebx+DISKCACHE.sad_size]
971
        sub     eax, [.size_left]
971
        sub     eax, [.size_left]
972
        inc     eax
972
        inc     eax
973
        mov     ecx, [ebx+DISKCACHE.sector_size_log]
973
        mov     ecx, [ebx+DISKCACHE.sector_size_log]
974
        shl     eax, cl
974
        shl     eax, cl
975
        add     eax, [ebx+DISKCACHE.data]
975
        add     eax, [ebx+DISKCACHE.data]
976
        mov     [.chain_start_pos], eax
976
        mov     [.chain_start_pos], eax
977
        mov     [.chain_size], 0
977
        mov     [.chain_size], 0
978
        mov     [.sequential], 1
978
        mov     [.sequential], 1
979
; 6. Expand the chain backward.
979
; 6. Expand the chain backward.
980
; Note: the main loop in step 2 looks for items sequentially,
980
; Note: the main loop in step 2 looks for items sequentially,
981
; so the previous item is not modified. If the previous sector
981
; so the previous item is not modified. If the previous sector
982
; is present in the cache, it automatically makes the chain scattered.
982
; is present in the cache, it automatically makes the chain scattered.
983
; 6a. Calculate sector number: one before the sector for the current item.
983
; 6a. Calculate sector number: one before the sector for the current item.
984
        mov     eax, [esi+CACHE_ITEM.SectorLo]
984
        mov     eax, [esi+CACHE_ITEM.SectorLo]
985
        mov     edx, [esi+CACHE_ITEM.SectorHi]
985
        mov     edx, [esi+CACHE_ITEM.SectorHi]
986
        sub     eax, 1
986
        sub     eax, 1
987
        sbb     edx, 0
987
        sbb     edx, 0
988
.find_chain_start:
988
.find_chain_start:
989
; 6b. For each sector where the previous item does not expand the chain,
989
; 6b. For each sector where the previous item does not expand the chain,
990
; call the lookup function without adding to the cache.
990
; call the lookup function without adding to the cache.
991
        call    cache_lookup_read
991
        call    cache_lookup_read
992
; 6c. If the sector is not found in cache or is not modified, stop expanding
992
; 6c. If the sector is not found in cache or is not modified, stop expanding
993
; and advance to step 7.
993
; and advance to step 7.
994
        jc      .found_chain_start
994
        jc      .found_chain_start
995
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
995
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
996
        jnz     .found_chain_start
996
        jnz     .found_chain_start
997
; 6d. We have found a new block that expands the chain backwards.
997
; 6d. We have found a new block that expands the chain backwards.
998
; It makes the chain non-sequential.
998
; It makes the chain non-sequential.
999
; Normally, sectors come in sequential blocks, so try to look at previous items
999
; Normally, sectors come in sequential blocks, so try to look at previous items
1000
; before returning to 6b; if there is a sequential block indeed, this saves some
1000
; before returning to 6b; if there is a sequential block indeed, this saves some
1001
; time instead of many full-fledged lookups.
1001
; time instead of many full-fledged lookups.
1002
        mov     [.sequential], 0
1002
        mov     [.sequential], 0
1003
        mov     [.chain_start_pos], edi
1003
        mov     [.chain_start_pos], edi
1004
.look_backward:
1004
.look_backward:
1005
; 6e. For each sector, update chain start pos/ptr, decrement sector number,
1005
; 6e. For each sector, update chain start pos/ptr, decrement sector number,
1006
; look at the previous item.
1006
; look at the previous item.
1007
        mov     [.chain_start_ptr], esi
1007
        mov     [.chain_start_ptr], esi
1008
        inc     [.chain_size]
1008
        inc     [.chain_size]
1009
        sub     eax, 1
1009
        sub     eax, 1
1010
        sbb     edx, 0
1010
        sbb     edx, 0
1011
        sub     esi, sizeof.CACHE_ITEM
1011
        sub     esi, sizeof.CACHE_ITEM
1012
; If the previous item exists...
1012
; If the previous item exists...
1013
        cmp     esi, [ebx+DISKCACHE.pointer]
1013
        cmp     esi, [ebx+DISKCACHE.pointer]
1014
        jbe     .find_chain_start
1014
        jbe     .find_chain_start
1015
; ...describes the correct sector...
1015
; ...describes the correct sector...
1016
        cmp     [esi+CACHE_ITEM.SectorLo], eax
1016
        cmp     [esi+CACHE_ITEM.SectorLo], eax
1017
        jnz     .find_chain_start
1017
        jnz     .find_chain_start
1018
        cmp     [esi+CACHE_ITEM.SectorHi], edx
1018
        cmp     [esi+CACHE_ITEM.SectorHi], edx
1019
        jnz     .find_chain_start
1019
        jnz     .find_chain_start
1020
; ...and is modified...
1020
; ...and is modified...
1021
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
1021
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
1022
        jnz     .found_chain_start
1022
        jnz     .found_chain_start
1023
; ...expand the chain one sector backwards and continue the loop at 6e.
1023
; ...expand the chain one sector backwards and continue the loop at 6e.
1024
; Otherwise, advance to step 7 if the previous item describes the correct sector
1024
; Otherwise, advance to step 7 if the previous item describes the correct sector
1025
; but is not modified, and return to step 6b otherwise.
1025
; but is not modified, and return to step 6b otherwise.
1026
        mov     edi, 1
1026
        mov     edi, 1
1027
        shl     edi, cl
1027
        shl     edi, cl
1028
        sub     [.chain_start_pos], edi
1028
        sub     [.chain_start_pos], edi
1029
        jmp     .look_backward
1029
        jmp     .look_backward
1030
.found_chain_start:
1030
.found_chain_start:
1031
; 7. Expand the chain forward.
1031
; 7. Expand the chain forward.
1032
; 7a. Prepare for the loop at 7b:
1032
; 7a. Prepare for the loop at 7b:
1033
; set esi = pointer to current item, edx:eax = current sector.
1033
; set esi = pointer to current item, edx:eax = current sector.
1034
        mov     esi, [.current_ptr]
1034
        mov     esi, [.current_ptr]
1035
        mov     eax, [esi+CACHE_ITEM.SectorLo]
1035
        mov     eax, [esi+CACHE_ITEM.SectorLo]
1036
        mov     edx, [esi+CACHE_ITEM.SectorHi]
1036
        mov     edx, [esi+CACHE_ITEM.SectorHi]
1037
.look_forward:
1037
.look_forward:
1038
; 7b. First, look at the next item. If it describes the next sector:
1038
; 7b. First, look at the next item. If it describes the next sector:
1039
; if it is modified, expand the chain with that sector and continue this step,
1039
; if it is modified, expand the chain with that sector and continue this step,
1040
; if it is not modified, the chain is completed, so advance to step 8.
1040
; if it is not modified, the chain is completed, so advance to step 8.
1041
        inc     [.chain_size]
1041
        inc     [.chain_size]
1042
        add     eax, 1
1042
        add     eax, 1
1043
        adc     edx, 0
1043
        adc     edx, 0
1044
        add     esi, sizeof.CACHE_ITEM
1044
        add     esi, sizeof.CACHE_ITEM
1045
        cmp     esi, [.cache_end]
1045
        cmp     esi, [.cache_end]
1046
        jae     .find_chain_end
1046
        jae     .find_chain_end
1047
        cmp     [esi+CACHE_ITEM.SectorLo], eax
1047
        cmp     [esi+CACHE_ITEM.SectorLo], eax
1048
        jnz     .find_chain_end
1048
        jnz     .find_chain_end
1049
        cmp     [esi+CACHE_ITEM.SectorHi], edx
1049
        cmp     [esi+CACHE_ITEM.SectorHi], edx
1050
        jnz     .find_chain_end
1050
        jnz     .find_chain_end
1051
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
1051
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
1052
        jnz     .found_chain_end
1052
        jnz     .found_chain_end
1053
        jmp     .look_forward
1053
        jmp     .look_forward
1054
.find_chain_end:
1054
.find_chain_end:
1055
; 7c. Otherwise, call the lookup function.
1055
; 7c. Otherwise, call the lookup function.
1056
        call    cache_lookup_read
1056
        call    cache_lookup_read
1057
; 7d. If the next sector is present in the cache and is modified,
1057
; 7d. If the next sector is present in the cache and is modified,
1058
; mark the chain as non-sequential and continue to step 7b.
1058
; mark the chain as non-sequential and continue to step 7b.
1059
        jc      .found_chain_end
1059
        jc      .found_chain_end
1060
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
1060
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
1061
        jnz     .found_chain_end
1061
        jnz     .found_chain_end
1062
        mov     [.sequential], 0
1062
        mov     [.sequential], 0
1063
        jmp     .look_forward
1063
        jmp     .look_forward
1064
.found_chain_end:
1064
.found_chain_end:
1065
; 8. Decide whether the chain is sequential or scattered.
1065
; 8. Decide whether the chain is sequential or scattered.
1066
; Advance to step 9 for sequential chains, go to step 10 for scattered chains.
1066
; Advance to step 9 for sequential chains, go to step 10 for scattered chains.
1067
        cmp     [.sequential], 0
1067
        cmp     [.sequential], 0
1068
        jz      .write_non_sequential
1068
        jz      .write_non_sequential
1069
.write_sequential:
1069
.write_sequential:
1070
; 9. Write a sequential chain to disk.
1070
; 9. Write a sequential chain to disk.
1071
; 9a. Pass the entire chain to the driver.
1071
; 9a. Pass the entire chain to the driver.
1072
        mov     eax, [.chain_start_ptr]
1072
        mov     eax, [.chain_start_ptr]
1073
        lea     ecx, [.chain_size]
1073
        lea     ecx, [.chain_size]
1074
        push    ecx     ; numsectors
1074
        push    ecx     ; numsectors
1075
        pushd   [eax+CACHE_ITEM.SectorHi]       ; startsector
1075
        pushd   [eax+CACHE_ITEM.SectorHi]       ; startsector
1076
        pushd   [eax+CACHE_ITEM.SectorLo]       ; startsector
1076
        pushd   [eax+CACHE_ITEM.SectorLo]       ; startsector
1077
        push    [.chain_start_pos+12]           ; buffer
1077
        push    [.chain_start_pos+12]           ; buffer
1078
        mov     esi, [ebp+PARTITION.Disk]
1078
        mov     esi, [ebp+PARTITION.Disk]
1079
        mov     al, DISKFUNC.write
1079
        mov     al, DISKFUNC.write
1080
        call    disk_call_driver
1080
        call    disk_call_driver
1081
; 9b. If failed, pass the error code to the driver.
1081
; 9b. If failed, pass the error code to the driver.
1082
        test    eax, eax
1082
        test    eax, eax
1083
        jnz     .return
1083
        jnz     .return
1084
; 9c. If succeeded, mark all sectors in the chain as not-modified,
1084
; 9c. If succeeded, mark all sectors in the chain as not-modified,
1085
; advance current item and number of items left to skip the chain.
1085
; advance current item and number of items left to skip the chain.
1086
        mov     esi, [.current_ptr]
1086
        mov     esi, [.current_ptr]
1087
        mov     eax, [.chain_size]
1087
        mov     eax, [.chain_size]
1088
        sub     [.size_left], eax
1088
        sub     [.size_left], eax
1089
@@:
1089
@@:
1090
        mov     [esi+CACHE_ITEM.Status], CACHE_ITEM_COPY
1090
        mov     [esi+CACHE_ITEM.Status], CACHE_ITEM_COPY
1091
        add     esi, sizeof.CACHE_ITEM
1091
        add     esi, sizeof.CACHE_ITEM
1092
        dec     eax
1092
        dec     eax
1093
        jnz     @b
1093
        jnz     @b
1094
; 9d. Continue the main loop at step 2 if there are more sectors.
1094
; 9d. Continue the main loop at step 2 if there are more sectors.
1095
; Return success otherwise.
1095
; Return success otherwise.
1096
        cmp     [.size_left], 0
1096
        cmp     [.size_left], 0
1097
        jnz     .look
1097
        jnz     .look
1098
        jmp     .return0
1098
        jmp     .return0
1099
.write_non_sequential:
1099
.write_non_sequential:
1100
; Write a non-sequential chain to the disk.
1100
; Write a non-sequential chain to the disk.
1101
; 10. Allocate a temporary buffer.
1101
; 10. Allocate a temporary buffer.
1102
; Use [.chain_size] sectors, but
1102
; Use [.chain_size] sectors, but
1103
; not greater than CACHE_MAX_ALLOC_SIZE bytes
1103
; not greater than CACHE_MAX_ALLOC_SIZE bytes
1104
; and not greater than half of free memory.
1104
; and not greater than half of free memory.
1105
        mov     eax, [pg_data.pages_free]
1105
        mov     eax, [pg_data.pages_free]
1106
        shr     eax, 1
1106
        shr     eax, 1
1107
        jz      .nomemory
1107
        jz      .nomemory
1108
        cmp     eax, CACHE_MAX_ALLOC_SIZE shr 12
1108
        cmp     eax, CACHE_MAX_ALLOC_SIZE shr 12
1109
        jbe     @f
1109
        jbe     @f
1110
        mov     eax, CACHE_MAX_ALLOC_SIZE shr 12
1110
        mov     eax, CACHE_MAX_ALLOC_SIZE shr 12
1111
@@:
1111
@@:
1112
        shl     eax, 12
1112
        shl     eax, 12
1113
        shr     eax, cl
1113
        shr     eax, cl
1114
        jz      .nomemory
1114
        jz      .nomemory
1115
        cmp     eax, [.chain_size]
1115
        cmp     eax, [.chain_size]
1116
        jbe     @f
1116
        jbe     @f
1117
        mov     eax, [.chain_size]
1117
        mov     eax, [.chain_size]
1118
@@:
1118
@@:
1119
        mov     [.iteration_size], eax
1119
        mov     [.iteration_size], eax
1120
        shl     eax, cl
1120
        shl     eax, cl
1121
        stdcall kernel_alloc, eax
1121
        stdcall kernel_alloc, eax
1122
        test    eax, eax
1122
        test    eax, eax
1123
        jz      .nomemory
1123
        jz      .nomemory
1124
        mov     [.iteration_buffer], eax
1124
        mov     [.iteration_buffer], eax
1125
.write_non_sequential_iteration:
1125
.write_non_sequential_iteration:
1126
; 11. Split the chain so that each iteration fits in the allocated buffer.
1126
; 11. Split the chain so that each iteration fits in the allocated buffer.
1127
; Iteration size is the minimum of chain size and allocated size.
1127
; Iteration size is the minimum of chain size and allocated size.
1128
        mov     eax, [.chain_size]
1128
        mov     eax, [.chain_size]
1129
        cmp     eax, [.iteration_size]
1129
        cmp     eax, [.iteration_size]
1130
        jae     @f
1130
        jae     @f
1131
        mov     [.iteration_size], eax
1131
        mov     [.iteration_size], eax
1132
@@:
1132
@@:
1133
; 12. Prepare arguments for the driver.
1133
; 12. Prepare arguments for the driver.
1134
        mov     esi, [.chain_start_ptr]
1134
        mov     esi, [.chain_start_ptr]
1135
        mov     edi, [.iteration_buffer]
1135
        mov     edi, [.iteration_buffer]
1136
        push    [.iteration_size]
1136
        push    [.iteration_size]
1137
        push    esp     ; numsectors
1137
        push    esp     ; numsectors
1138
        push    [esi+CACHE_ITEM.SectorHi]       ; startsector
1138
        push    [esi+CACHE_ITEM.SectorHi]       ; startsector
1139
        push    [esi+CACHE_ITEM.SectorLo]       ; startsector
1139
        push    [esi+CACHE_ITEM.SectorLo]       ; startsector
1140
        push    edi     ; buffer
1140
        push    edi     ; buffer
1141
; 13. Copy data from the cache to the temporary buffer,
1141
; 13. Copy data from the cache to the temporary buffer,
1142
; advancing chain_start pos/ptr and marking sectors as not-modified.
1142
; advancing chain_start pos/ptr and marking sectors as not-modified.
1143
; 13a. Prepare for the loop: push number of sectors to process.
1143
; 13a. Prepare for the loop: push number of sectors to process.
1144
        push    [.iteration_size+20]    ; temporary variable
1144
        push    [.iteration_size+20]    ; temporary variable
1145
.copy_loop:
1145
.copy_loop:
1146
; 13b. For each sector, copy the data.
1146
; 13b. For each sector, copy the data.
1147
; Note that edi is advanced automatically.
1147
; Note that edi is advanced automatically.
1148
        mov     esi, [.chain_start_pos+24]
1148
        mov     esi, [.chain_start_pos+24]
1149
        mov     ecx, [ebx+DISKCACHE.sector_size_log]
1149
        mov     ecx, [ebx+DISKCACHE.sector_size_log]
1150
        mov     eax, 1
1150
        mov     eax, 1
1151
        shl     eax, cl
1151
        shl     eax, cl
1152
        mov     ecx, eax
1152
        mov     ecx, eax
1153
        shr     ecx, 2
1153
        shr     ecx, 2
1154
        rep movsd
1154
        rep movsd
1155
        mov     ecx, eax ; keep for 13e
1155
        mov     ecx, eax ; keep for 13e
1156
; 13c. Mark the item as not-modified.
1156
; 13c. Mark the item as not-modified.
1157
        mov     esi, [.chain_start_ptr+24]
1157
        mov     esi, [.chain_start_ptr+24]
1158
        mov     [esi+CACHE_ITEM.Status], CACHE_ITEM_COPY
1158
        mov     [esi+CACHE_ITEM.Status], CACHE_ITEM_COPY
1159
; 13d. Check whether the next sector continues the chain.
1159
; 13d. Check whether the next sector continues the chain.
1160
; If so, advance to 13e. Otherwise, go to 13f.
1160
; If so, advance to 13e. Otherwise, go to 13f.
1161
        mov     eax, [esi+CACHE_ITEM.SectorLo]
1161
        mov     eax, [esi+CACHE_ITEM.SectorLo]
1162
        mov     edx, [esi+CACHE_ITEM.SectorHi]
1162
        mov     edx, [esi+CACHE_ITEM.SectorHi]
1163
        add     esi, sizeof.CACHE_ITEM
1163
        add     esi, sizeof.CACHE_ITEM
1164
        add     eax, 1
1164
        add     eax, 1
1165
        adc     edx, 0
1165
        adc     edx, 0
1166
        cmp     esi, [.cache_end+24]
1166
        cmp     esi, [.cache_end+24]
1167
        jae     .no_forward
1167
        jae     .no_forward
1168
        cmp     [esi+CACHE_ITEM.SectorLo], eax
1168
        cmp     [esi+CACHE_ITEM.SectorLo], eax
1169
        jnz     .no_forward
1169
        jnz     .no_forward
1170
        cmp     [esi+CACHE_ITEM.SectorHi], edx
1170
        cmp     [esi+CACHE_ITEM.SectorHi], edx
1171
        jnz     .no_forward
1171
        jnz     .no_forward
1172
; 13e. Increment position/pointer to the chain and
1172
; 13e. Increment position/pointer to the chain and
1173
; continue the loop.
1173
; continue the loop.
1174
        add     [.chain_start_pos+24], ecx
1174
        add     [.chain_start_pos+24], ecx
1175
        mov     [.chain_start_ptr+24], esi
1175
        mov     [.chain_start_ptr+24], esi
1176
        dec     dword [esp]
1176
        dec     dword [esp]
1177
        jnz     .copy_loop
1177
        jnz     .copy_loop
1178
        jmp     .copy_done
1178
        jmp     .copy_done
1179
.no_forward:
1179
.no_forward:
1180
; 13f. Call the lookup function without adding to the cache.
1180
; 13f. Call the lookup function without adding to the cache.
1181
; Update position/pointer with returned value.
1181
; Update position/pointer with returned value.
1182
; Note: for the last sector in the chain, edi/esi may contain
1182
; Note: for the last sector in the chain, edi/esi may contain
1183
; garbage; we are not going to use them in this case.
1183
; garbage; we are not going to use them in this case.
1184
        push    edi
1184
        push    edi
1185
        call    cache_lookup_read
1185
        call    cache_lookup_read
1186
        mov     [.chain_start_pos+28], edi
1186
        mov     [.chain_start_pos+28], edi
1187
        mov     [.chain_start_ptr+28], esi
1187
        mov     [.chain_start_ptr+28], esi
1188
        pop     edi
1188
        pop     edi
1189
        dec     dword [esp]
1189
        dec     dword [esp]
1190
        jnz     .copy_loop
1190
        jnz     .copy_loop
1191
.copy_done:
1191
.copy_done:
1192
; 13g. Restore the stack after 13a.
1192
; 13g. Restore the stack after 13a.
1193
        pop     ecx
1193
        pop     ecx
1194
; 14. Call the driver.
1194
; 14. Call the driver.
1195
        mov     esi, [ebp+PARTITION.Disk]
1195
        mov     esi, [ebp+PARTITION.Disk]
1196
        mov     al, DISKFUNC.write
1196
        mov     al, DISKFUNC.write
1197
        call    disk_call_driver
1197
        call    disk_call_driver
1198
        pop     ecx     ; numsectors
1198
        pop     ecx     ; numsectors
1199
; 15. If the driver has returned an error, free the buffer allocated at step 10
1199
; 15. If the driver has returned an error, free the buffer allocated at step 10
1200
; and pass the error to the caller.
1200
; and pass the error to the caller.
1201
; Otherwise, remove the processed part from the chain and continue iterations
1201
; Otherwise, remove the processed part from the chain and continue iterations
1202
; starting in step 11 if there are more data to process.
1202
; starting in step 11 if there are more data to process.
1203
        test    eax, eax
1203
        test    eax, eax
1204
        jnz     .nonsequential_error
1204
        jnz     .nonsequential_error
1205
        sub     [.chain_size], ecx
1205
        sub     [.chain_size], ecx
1206
        jnz     .write_non_sequential_iteration
1206
        jnz     .write_non_sequential_iteration
1207
; 16. The chain is written. Free the temporary buffer
1207
; 16. The chain is written. Free the temporary buffer
1208
; and continue the loop at step 2.
1208
; and continue the loop at step 2.
1209
        stdcall kernel_free, [.iteration_buffer]
1209
        stdcall kernel_free, [.iteration_buffer]
1210
        mov     esi, [.current_ptr]
1210
        mov     esi, [.current_ptr]
1211
        jmp     .look_next
1211
        jmp     .look_next
1212
.nonsequential_error:
1212
.nonsequential_error:
1213
        push    eax
1213
        push    eax
1214
        stdcall kernel_free, [.iteration_buffer+4]
1214
        stdcall kernel_free, [.iteration_buffer+4]
1215
        pop     eax
1215
        pop     eax
1216
        jmp     .return
1216
        jmp     .return
1217
.nomemory:
1217
.nomemory:
1218
        mov     eax, DISK_STATUS_NO_MEMORY
1218
        mov     eax, DISK_STATUS_NO_MEMORY
1219
        jmp     .return
1219
        jmp     .return
1220
endp
1220
endp
1221
 
1221
 
1222
; This internal function is called from disk_add to initialize the caching for
1222
; This internal function is called from disk_add to initialize the caching for
1223
; a new DISK.
1223
; a new DISK.
1224
; The algorithm is inherited from getcache.inc: take 1/32 part of the available
1224
; The algorithm is inherited from getcache.inc: take 1/32 part of the available
1225
; physical memory, round down to 8 pages, limit by 128K from below and by 1M
1225
; physical memory, round down to 8 pages, limit by 128K from below and by 1M
1226
; from above. Reserve 1/8 part of the cache for system data and 7/8 for app
1226
; from above. Reserve 1/8 part of the cache for system data and 7/8 for app
1227
; data.
1227
; data.
1228
; After the size is calculated, but before the cache is allocated, the device
1228
; After the size is calculated, but before the cache is allocated, the device
1229
; driver can adjust the size. In particular, setting size to zero disables
1229
; driver can adjust the size. In particular, setting size to zero disables
1230
; caching: there is no sense in a cache for a ramdisk. In fact, such action
1230
; caching: there is no sense in a cache for a ramdisk. In fact, such action
1231
; is most useful example of a non-trivial adjustment.
1231
; is most useful example of a non-trivial adjustment.
1232
; esi = pointer to DISK structure
1232
; esi = pointer to DISK structure
1233
disk_init_cache:
1233
disk_init_cache:
1234
; 1. Verify sector size. The code requires it to be a power of 2 not less than 4.
1234
; 1. Verify sector size. The code requires it to be a power of 2 not less than 4.
1235
; In the name of sanity check that sector size is not too small or too large.
1235
; In the name of sanity check that sector size is not too small or too large.
1236
        bsf     ecx, [esi+DISK.MediaInfo.SectorSize]
1236
        bsf     ecx, [esi+DISK.MediaInfo.SectorSize]
1237
        jz      .invalid_sector_size
1237
        jz      .invalid_sector_size
1238
        mov     eax, 1
1238
        mov     eax, 1
1239
        shl     eax, cl
1239
        shl     eax, cl
1240
        cmp     eax, [esi+DISK.MediaInfo.SectorSize]
1240
        cmp     eax, [esi+DISK.MediaInfo.SectorSize]
1241
        jnz     .invalid_sector_size
1241
        jnz     .invalid_sector_size
1242
        cmp     ecx, 6
1242
        cmp     ecx, 6
1243
        jb      .invalid_sector_size
1243
        jb      .invalid_sector_size
1244
        cmp     ecx, 14
1244
        cmp     ecx, 14
1245
        jbe     .normal_sector_size
1245
        jbe     .normal_sector_size
1246
.invalid_sector_size:
1246
.invalid_sector_size:
1247
        DEBUGF 1,'K : sector size %x is invalid\n',[esi+DISK.MediaInfo.SectorSize]
1247
        DEBUGF 1,'K : sector size %x is invalid\n',[esi+DISK.MediaInfo.SectorSize]
1248
        xor     eax, eax
1248
        xor     eax, eax
1249
        ret
1249
        ret
1250
.normal_sector_size:
1250
.normal_sector_size:
1251
        mov     [esi+DISK.SysCache.sector_size_log], ecx
1251
        mov     [esi+DISK.SysCache.sector_size_log], ecx
1252
        mov     [esi+DISK.AppCache.sector_size_log], ecx
1252
        mov     [esi+DISK.AppCache.sector_size_log], ecx
1253
; 2. Calculate the suggested cache size.
1253
; 2. Calculate the suggested cache size.
1254
; 2a. Get the size of free physical memory in pages.
1254
; 2a. Get the size of free physical memory in pages.
1255
        mov     eax, [pg_data.pages_free]
1255
        mov     eax, [pg_data.pages_free]
1256
; 2b. Use the value to calculate the size.
1256
; 2b. Use the value to calculate the size.
1257
        shl     eax, 12 - 5     ; 1/32 of it in bytes
1257
        shl     eax, 12 - 5     ; 1/32 of it in bytes
1258
        and     eax, -8*4096    ; round down to the multiple of 8 pages
1258
        and     eax, -8*4096    ; round down to the multiple of 8 pages
1259
; 2c. Force lower and upper limits.
1259
; 2c. Force lower and upper limits.
1260
        cmp     eax, 1024*1024
1260
        cmp     eax, 1024*1024
1261
        jb      @f
1261
        jb      @f
1262
        mov     eax, 1024*1024
1262
        mov     eax, 1024*1024
1263
@@:
1263
@@:
1264
        cmp     eax, 128*1024
1264
        cmp     eax, 128*1024
1265
        ja      @f
1265
        ja      @f
1266
        mov     eax, 128*1024
1266
        mov     eax, 128*1024
1267
@@:
1267
@@:
1268
; 2d. Give a chance to the driver to adjust the size.
1268
; 2d. Give a chance to the driver to adjust the size.
1269
        push    eax
1269
        push    eax
1270
        mov     al, DISKFUNC.adjust_cache_size
1270
        mov     al, DISKFUNC.adjust_cache_size
1271
        call    disk_call_driver
1271
        call    disk_call_driver
1272
; Cache size calculated.
1272
; Cache size calculated.
1273
        mov     [esi+DISK.cache_size], eax
1273
        mov     [esi+DISK.cache_size], eax
1274
        test    eax, eax
1274
        test    eax, eax
1275
        jz      .nocache
1275
        jz      .nocache
1276
; 3. Allocate memory for the cache.
1276
; 3. Allocate memory for the cache.
1277
; 3a. Call the allocator.
1277
; 3a. Call the allocator.
1278
        stdcall kernel_alloc, eax
1278
        stdcall kernel_alloc, eax
1279
        test    eax, eax
1279
        test    eax, eax
1280
        jnz     @f
1280
        jnz     @f
1281
; 3b. If it failed, say a message and return with eax = 0.
1281
; 3b. If it failed, say a message and return with eax = 0.
1282
        dbgstr 'no memory for disk cache'
1282
        dbgstr 'no memory for disk cache'
1283
        jmp     .nothing
1283
        jmp     .nothing
1284
@@:
1284
@@:
1285
; 4. Fill two DISKCACHE structures.
1285
; 4. Fill two DISKCACHE structures.
1286
        mov     [esi+DISK.SysCache.pointer], eax
1286
        mov     [esi+DISK.SysCache.pointer], eax
1287
        lea     ecx, [esi+DISK.CacheLock]
1287
        lea     ecx, [esi+DISK.CacheLock]
1288
        call    mutex_init
1288
        call    mutex_init
1289
; The following code is inherited from getcache.inc.
1289
; The following code is inherited from getcache.inc.
1290
        mov     edx, [esi+DISK.SysCache.pointer]
1290
        mov     edx, [esi+DISK.SysCache.pointer]
1291
        and     [esi+DISK.SysCache.search_start], 0
1291
        and     [esi+DISK.SysCache.search_start], 0
1292
        and     [esi+DISK.AppCache.search_start], 0
1292
        and     [esi+DISK.AppCache.search_start], 0
1293
        mov     eax, [esi+DISK.cache_size]
1293
        mov     eax, [esi+DISK.cache_size]
1294
        shr     eax, 3
1294
        shr     eax, 3
1295
        mov     [esi+DISK.SysCache.data_size], eax
1295
        mov     [esi+DISK.SysCache.data_size], eax
1296
        add     edx, eax
1296
        add     edx, eax
1297
        imul    eax, 7
1297
        imul    eax, 7
1298
        mov     [esi+DISK.AppCache.data_size], eax
1298
        mov     [esi+DISK.AppCache.data_size], eax
1299
        mov     [esi+DISK.AppCache.pointer], edx
1299
        mov     [esi+DISK.AppCache.pointer], edx
1300
 
1300
 
1301
        mov     eax, [esi+DISK.SysCache.data_size]
1301
        mov     eax, [esi+DISK.SysCache.data_size]
1302
        call    calculate_cache_slots
1302
        call    calculate_cache_slots
1303
        add     eax, [esi+DISK.SysCache.pointer]
1303
        add     eax, [esi+DISK.SysCache.pointer]
1304
        mov     [esi+DISK.SysCache.data], eax
1304
        mov     [esi+DISK.SysCache.data], eax
1305
        mov     [esi+DISK.SysCache.sad_size], ecx
1305
        mov     [esi+DISK.SysCache.sad_size], ecx
1306
 
1306
 
1307
        push    edi
1307
        push    edi
1308
        mov     edi, [esi+DISK.SysCache.pointer]
1308
        mov     edi, [esi+DISK.SysCache.pointer]
1309
        lea     ecx, [(ecx+1)*3]
1309
        lea     ecx, [(ecx+1)*3]
1310
        xor     eax, eax
1310
        xor     eax, eax
1311
        rep stosd
1311
        rep stosd
1312
        pop     edi
1312
        pop     edi
1313
 
1313
 
1314
        mov     eax, [esi+DISK.AppCache.data_size]
1314
        mov     eax, [esi+DISK.AppCache.data_size]
1315
        call    calculate_cache_slots
1315
        call    calculate_cache_slots
1316
        add     eax, [esi+DISK.AppCache.pointer]
1316
        add     eax, [esi+DISK.AppCache.pointer]
1317
        mov     [esi+DISK.AppCache.data], eax
1317
        mov     [esi+DISK.AppCache.data], eax
1318
        mov     [esi+DISK.AppCache.sad_size], ecx
1318
        mov     [esi+DISK.AppCache.sad_size], ecx
1319
 
1319
 
1320
        push    edi
1320
        push    edi
1321
        mov     edi, [esi+DISK.AppCache.pointer]
1321
        mov     edi, [esi+DISK.AppCache.pointer]
1322
        lea     ecx, [(ecx+1)*3]
1322
        lea     ecx, [(ecx+1)*3]
1323
        xor     eax, eax
1323
        xor     eax, eax
1324
        rep stosd
1324
        rep stosd
1325
        pop     edi
1325
        pop     edi
1326
 
1326
 
1327
; 5. Return with nonzero al.
1327
; 5. Return with nonzero al.
1328
        mov     al, 1
1328
        mov     al, 1
1329
; 6. Return.
1329
; 6. Return.
1330
.nothing:
1330
.nothing:
1331
        ret
1331
        ret
1332
; No caching is required for this driver. Zero cache pointers and return with
1332
; No caching is required for this driver. Zero cache pointers and return with
1333
; nonzero al.
1333
; nonzero al.
1334
.nocache:
1334
.nocache:
1335
        mov     [esi+DISK.SysCache.pointer], eax
1335
        mov     [esi+DISK.SysCache.pointer], eax
1336
        mov     [esi+DISK.AppCache.pointer], eax
1336
        mov     [esi+DISK.AppCache.pointer], eax
1337
        mov     al, 1
1337
        mov     al, 1
1338
        ret
1338
        ret
1339
 
1339
 
1340
calculate_cache_slots:
1340
calculate_cache_slots:
1341
        push    eax
1341
        push    eax
1342
        mov     ecx, [esi+DISK.MediaInfo.SectorSize]
1342
        mov     ecx, [esi+DISK.MediaInfo.SectorSize]
1343
        add     ecx, sizeof.CACHE_ITEM
1343
        add     ecx, sizeof.CACHE_ITEM
1344
        xor     edx, edx
1344
        xor     edx, edx
1345
        div     ecx
1345
        div     ecx
1346
        mov     ecx, eax
1346
        mov     ecx, eax
1347
        imul    eax, [esi+DISK.MediaInfo.SectorSize]
1347
        imul    eax, [esi+DISK.MediaInfo.SectorSize]
1348
        sub     [esp], eax
1348
        sub     [esp], eax
1349
        pop     eax
1349
        pop     eax
1350
        dec     ecx
1350
        dec     ecx
1351
        ret
1351
        ret
1352
 
1352
 
1353
 
1353
 
1354
; This internal function is called from disk_media_dereference to free the
1354
; This internal function is called from disk_media_dereference to free the
1355
; allocated cache, if there is one.
1355
; allocated cache, if there is one.
1356
; esi = pointer to DISK structure
1356
; esi = pointer to DISK structure
1357
disk_free_cache:
1357
disk_free_cache:
1358
; The algorithm is straightforward.
1358
; The algorithm is straightforward.
1359
        mov     eax, [esi+DISK.SysCache.pointer]
1359
        mov     eax, [esi+DISK.SysCache.pointer]
1360
        test    eax, eax
1360
        test    eax, eax
1361
        jz      .nothing
1361
        jz      .nothing
1362
        stdcall kernel_free, eax
1362
        stdcall kernel_free, eax
1363
.nothing:
1363
.nothing:
1364
        ret
1364
        ret
1365
 
1365
 
1366
; This function flushes all modified data from both caches for the given DISK.
1366
; This function flushes all modified data from both caches for the given DISK.
1367
; esi = pointer to DISK
1367
; esi = pointer to DISK
1368
disk_sync:
1368
disk_sync:
1369
; The algorithm is straightforward.
1369
; The algorithm is straightforward.
1370
        cmp     [esi+DISK.SysCache.pointer], 0
1370
        cmp     [esi+DISK.SysCache.pointer], 0
1371
        jz      .nothing
1371
        jz      .nothing
1372
        lea     ecx, [esi+DISK.CacheLock]
1372
        lea     ecx, [esi+DISK.CacheLock]
1373
        call    mutex_lock
1373
        call    mutex_lock
1374
        push    ebx
1374
        push    ebx
1375
        push    esi     ; for second write_cache64
1375
        push    esi     ; for second write_cache64
1376
        push    esi     ; for first write_cache64
1376
        push    esi     ; for first write_cache64
1377
        lea     ebx, [esi+DISK.SysCache]
1377
        lea     ebx, [esi+DISK.SysCache]
1378
        call    write_cache64
1378
        call    write_cache64
1379
        add     ebx, DISK.AppCache - DISK.SysCache
1379
        add     ebx, DISK.AppCache - DISK.SysCache
1380
        call    write_cache64
1380
        call    write_cache64
1381
        pop     ebx
1381
        pop     ebx
1382
        lea     ecx, [esi+DISK.CacheLock]
1382
        lea     ecx, [esi+DISK.CacheLock]
1383
        call    mutex_unlock
1383
        call    mutex_unlock
1384
.nothing:
1384
.nothing:
1385
        mov     al, DISKFUNC.flush
1385
        mov     al, DISKFUNC.flush
1386
        call    disk_call_driver
1386
        call    disk_call_driver
1387
        ret
1387
        ret