Subversion Repositories Kolibri OS

Rev

Rev 5089 | Rev 5196 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 5089 Rev 5095
1
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2
;;                                                              ;;
2
;;                                                              ;;
3
;; Copyright (C) KolibriOS team 2011-2014. All rights reserved. ;;
3
;; Copyright (C) KolibriOS team 2011-2014. 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: 5089 $
8
$Revision: 5095 $
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
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: edi -> 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
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     ; 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     eax, [ebp+PARTITION.Disk]
626
        mov     eax, [ebp+PARTITION.Disk]
627
        cmp     dword [eax+DISK.MediaInfo.Capacity+4], 0
627
        cmp     dword [eax+DISK.MediaInfo.Capacity+4], 0
628
        jnz     @f
628
        jnz     @f
629
        cmp     dword [eax+DISK.MediaInfo.Capacity], 3 shl (20-9)
629
        cmp     dword [eax+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
        jmp     .found_in_cache
758
        jmp     .found_in_cache
759
 
759
 
760
; On error at steps 13-14, release the lock
760
; On error at steps 13-14, release the lock
761
; and pass the error to the caller.
761
; and pass the error to the caller.
762
.floppy_read_error:
762
.floppy_read_error:
763
        pop     ecx
763
        pop     ecx
764
.floppy_cache_error:
764
.floppy_cache_error:
765
        mov     ecx, [ebp+PARTITION.Disk]
765
        mov     ecx, [ebp+PARTITION.Disk]
766
        add     ecx, DISK.CacheLock
766
        add     ecx, DISK.CacheLock
767
        push    eax
767
        push    eax
768
        call    mutex_unlock
768
        call    mutex_unlock
769
        pop     eax
769
        pop     eax
770
        jmp     .return_eax
770
        jmp     .return_eax
771
 
771
 
772
; This function is intended to replace the old 'hd_write' function when
772
; This function is intended to replace the old 'hd_write' function when
773
; [hdd_appl_data] = 0, so its input/output parameters are the same, except
773
; [hdd_appl_data] = 0, so its input/output parameters are the same, except
774
; that it can't use the global variables 'hd_error' and 'hdd_appl_data'.
774
; that it can't use the global variables 'hd_error' and 'hdd_appl_data'.
775
; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure
775
; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure
776
; eax is relative to partition start
776
; eax is relative to partition start
777
; out: eax = error code; 0 = ok
777
; out: eax = error code; 0 = ok
778
fs_write32_sys:
778
fs_write32_sys:
779
; Just call the advanced function.
779
; Just call the advanced function.
780
        push    ecx edx
780
        push    ecx edx
781
        xor     edx, edx
781
        xor     edx, edx
782
        mov     ecx, 1
782
        mov     ecx, 1
783
        call    fs_write64_sys
783
        call    fs_write64_sys
784
        pop     edx ecx
784
        pop     edx ecx
785
        ret
785
        ret
786
 
786
 
787
; This function is intended to replace the old 'hd_write' function when
787
; This function is intended to replace the old 'hd_write' function when
788
; [hdd_appl_data] = 1, so its input/output parameters are the same, except
788
; [hdd_appl_data] = 1, so its input/output parameters are the same, except
789
; that it can't use the global variables 'hd_error' and 'hdd_appl_data'.
789
; that it can't use the global variables 'hd_error' and 'hdd_appl_data'.
790
; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure
790
; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure
791
; eax is relative to partition start
791
; eax is relative to partition start
792
; out: eax = error code; 0 = ok
792
; out: eax = error code; 0 = ok
793
fs_write32_app:
793
fs_write32_app:
794
; Just call the advanced function.
794
; Just call the advanced function.
795
        push    ecx edx
795
        push    ecx edx
796
        xor     edx, edx
796
        xor     edx, edx
797
        mov     ecx, 1
797
        mov     ecx, 1
798
        call    fs_write64_app
798
        call    fs_write64_app
799
        pop     edx ecx
799
        pop     edx ecx
800
        ret
800
        ret
801
 
801
 
802
; Lookup for the given sector in the given cache.
802
; Lookup for the given sector in the given cache.
803
; If the sector is not present, return error.
803
; If the sector is not present, return error.
804
; The caller must acquire the cache lock.
804
; The caller must acquire the cache lock.
805
; in: edx:eax = sector
805
; in: edx:eax = sector
806
; in: ebx -> DISKCACHE structure
806
; in: ebx -> DISKCACHE structure
807
; out: CF set if sector is not in cache
807
; out: CF set if sector is not in cache
808
; out: ecx = sector_size_log
808
; out: ecx = sector_size_log
809
; out: esi -> sector:status
809
; out: esi -> sector:status
810
; out: edi -> sector data
810
; out: edi -> sector data
811
proc cache_lookup_read
811
proc cache_lookup_read
812
        mov     esi, [ebx+DISKCACHE.pointer]
812
        mov     esi, [ebx+DISKCACHE.pointer]
813
        add     esi, sizeof.CACHE_ITEM
813
        add     esi, sizeof.CACHE_ITEM
814
 
814
 
815
        mov     edi, 1
815
        mov     edi, 1
816
 
816
 
817
.hdreadcache:
817
.hdreadcache:
818
 
818
 
819
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_EMPTY
819
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_EMPTY
820
        je      .nohdcache
820
        je      .nohdcache
821
 
821
 
822
        cmp     [esi+CACHE_ITEM.SectorLo], eax
822
        cmp     [esi+CACHE_ITEM.SectorLo], eax
823
        jne     .nohdcache
823
        jne     .nohdcache
824
        cmp     [esi+CACHE_ITEM.SectorHi], edx
824
        cmp     [esi+CACHE_ITEM.SectorHi], edx
825
        jne     .nohdcache
825
        jne     .nohdcache
826
        mov     ecx, [ebx+DISKCACHE.sector_size_log]
826
        mov     ecx, [ebx+DISKCACHE.sector_size_log]
827
        shl     edi, cl
827
        shl     edi, cl
828
        add     edi, [ebx+DISKCACHE.data]
828
        add     edi, [ebx+DISKCACHE.data]
829
        clc
829
        clc
830
        ret
830
        ret
831
 
831
 
832
.nohdcache:
832
.nohdcache:
833
 
833
 
834
        add     esi, sizeof.CACHE_ITEM
834
        add     esi, sizeof.CACHE_ITEM
835
        inc     edi
835
        inc     edi
836
        cmp     edi, [ebx+DISKCACHE.sad_size]
836
        cmp     edi, [ebx+DISKCACHE.sad_size]
837
        jbe     .hdreadcache
837
        jbe     .hdreadcache
838
        stc
838
        stc
839
        ret
839
        ret
840
endp
840
endp
841
 
841
 
842
; Lookup for the given sector in the given cache.
842
; Lookup for the given sector in the given cache.
843
; If the sector is not present, allocate space for it,
843
; If the sector is not present, allocate space for it,
844
; possibly flushing data.
844
; possibly flushing data.
845
; in: edx:eax = sector
845
; in: edx:eax = sector
846
; in: ebx -> DISKCACHE structure
846
; in: ebx -> DISKCACHE structure
847
; in: ebp -> PARTITION structure
847
; in: ebp -> PARTITION structure
848
; out: eax = error code
848
; out: eax = error code
849
; out: esi -> sector:status
849
; out: esi -> sector:status
850
; out: edi -> sector data
850
; out: edi -> sector data
851
proc cache_lookup_write
851
proc cache_lookup_write
852
        call    cache_lookup_read
852
        call    cache_lookup_read
853
        jnc     .return0
853
        jnc     .return0
854
        push    edx eax
854
        push    edx eax
855
;-----------------------------------------------------------
855
;-----------------------------------------------------------
856
; find empty or read slot, flush cache if next 12.5% is used by write
856
; find empty or read slot, flush cache if next 12.5% is used by write
857
; output : ecx = cache slot
857
; output : ecx = cache slot
858
;-----------------------------------------------------------
858
;-----------------------------------------------------------
859
; Note: the code is essentially inherited, so probably
859
; Note: the code is essentially inherited, so probably
860
; no analysis of efficiency were done.
860
; no analysis of efficiency were done.
861
; However, it works.
861
; However, it works.
862
.search_again:
862
.search_again:
863
        mov     eax, [ebx+DISKCACHE.sad_size]
863
        mov     eax, [ebx+DISKCACHE.sad_size]
864
        mov     ecx, [ebx+DISKCACHE.search_start]
864
        mov     ecx, [ebx+DISKCACHE.search_start]
865
        shr     eax, 3
865
        shr     eax, 3
866
        lea     esi, [ecx*sizeof.CACHE_ITEM/4]
866
        lea     esi, [ecx*sizeof.CACHE_ITEM/4]
867
        shl     esi, 2
867
        shl     esi, 2
868
        add     esi, [ebx+DISKCACHE.pointer]
868
        add     esi, [ebx+DISKCACHE.pointer]
869
.search_for_empty:
869
.search_for_empty:
870
        inc     ecx
870
        inc     ecx
871
        add     esi, sizeof.CACHE_ITEM
871
        add     esi, sizeof.CACHE_ITEM
872
        cmp     ecx, [ebx+DISKCACHE.sad_size]
872
        cmp     ecx, [ebx+DISKCACHE.sad_size]
873
        jbe     .inside_cache
873
        jbe     .inside_cache
874
        mov     ecx, 1
874
        mov     ecx, 1
875
        mov     esi, [ebx+DISKCACHE.pointer]
875
        mov     esi, [ebx+DISKCACHE.pointer]
876
        add     esi, sizeof.CACHE_ITEM
876
        add     esi, sizeof.CACHE_ITEM
877
.inside_cache:
877
.inside_cache:
878
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
878
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
879
        jb      .found_slot             ; it's empty or read
879
        jb      .found_slot             ; it's empty or read
880
        dec     eax
880
        dec     eax
881
        jnz     .search_for_empty
881
        jnz     .search_for_empty
882
        stdcall write_cache64, [ebp+PARTITION.Disk] ; no empty slots found, write all
882
        stdcall write_cache64, [ebp+PARTITION.Disk] ; no empty slots found, write all
883
        test    eax, eax
883
        test    eax, eax
884
        jne     .found_slot_access_denied
884
        jne     .found_slot_access_denied
885
        jmp     .search_again           ; and start again
885
        jmp     .search_again           ; and start again
886
.found_slot:
886
.found_slot:
887
        mov     [ebx+DISKCACHE.search_start], ecx
887
        mov     [ebx+DISKCACHE.search_start], ecx
888
        popd    [esi+CACHE_ITEM.SectorLo]
888
        popd    [esi+CACHE_ITEM.SectorLo]
889
        popd    [esi+CACHE_ITEM.SectorHi]
889
        popd    [esi+CACHE_ITEM.SectorHi]
890
        mov     [esi+CACHE_ITEM.Status], CACHE_ITEM_EMPTY
890
        mov     [esi+CACHE_ITEM.Status], CACHE_ITEM_EMPTY
891
        mov     edi, ecx
891
        mov     edi, ecx
892
        mov     ecx, [ebx+DISKCACHE.sector_size_log]
892
        mov     ecx, [ebx+DISKCACHE.sector_size_log]
893
        shl     edi, cl
893
        shl     edi, cl
894
        add     edi, [ebx+DISKCACHE.data]
894
        add     edi, [ebx+DISKCACHE.data]
895
.return0:
895
.return0:
896
        xor     eax, eax        ; success
896
        xor     eax, eax        ; success
897
        ret
897
        ret
898
.found_slot_access_denied:
898
.found_slot_access_denied:
899
        add     esp, 8
899
        add     esp, 8
900
        ret
900
        ret
901
endp
901
endp
902
 
902
 
903
; Flush the given cache.
903
; Flush the given cache.
904
; The caller must acquire the cache lock.
904
; The caller must acquire the cache lock.
905
; in: ebx -> DISKCACHE
905
; in: ebx -> DISKCACHE
906
; in: first argument in stdcall convention -> PARTITION
906
; in: first argument in stdcall convention -> PARTITION
907
proc write_cache64
907
proc write_cache64
908
; 1. Setup stack frame.
908
; 1. Setup stack frame.
909
        push    esi edi         ; save used registers to be stdcall
909
        push    esi edi         ; save used registers to be stdcall
910
        sub     esp, .local_vars_size   ; reserve space for local vars
910
        sub     esp, .local_vars_size   ; reserve space for local vars
911
virtual at esp
911
virtual at esp
912
.local_vars:
912
.local_vars:
913
.cache_end      dd      ?       ; item past the end of the cache
913
.cache_end      dd      ?       ; item past the end of the cache
914
.size_left      dd      ?       ; items left to scan
914
.size_left      dd      ?       ; items left to scan
915
.current_ptr    dd      ?       ; pointer to the current item
915
.current_ptr    dd      ?       ; pointer to the current item
916
;
916
;
917
; Write operations are coalesced in chains,
917
; Write operations are coalesced in chains,
918
; one chain describes a sequential interval of sectors,
918
; one chain describes a sequential interval of sectors,
919
; they can be sequential or scattered in the cache.
919
; they can be sequential or scattered in the cache.
920
.sequential     dd      ?
920
.sequential     dd      ?
921
; boolean variable, 1 if the current chain is sequential in the cache,
921
; boolean variable, 1 if the current chain is sequential in the cache,
922
; 0 if additional buffer is needed to perform the operation
922
; 0 if additional buffer is needed to perform the operation
923
.chain_start_pos dd     ?       ; data of chain start item
923
.chain_start_pos dd     ?       ; data of chain start item
924
.chain_start_ptr dd     ?       ; pointer to chain start item
924
.chain_start_ptr dd     ?       ; pointer to chain start item
925
.chain_size     dd      ?       ; chain size (thanks, C.O.)
925
.chain_size     dd      ?       ; chain size (thanks, C.O.)
926
.iteration_size dd      ?
926
.iteration_size dd      ?
927
; If the chain size is too large, split the operation to several iterations.
927
; If the chain size is too large, split the operation to several iterations.
928
; This is size in sectors for one iterations.
928
; This is size in sectors for one iterations.
929
.iteration_buffer dd    ?       ; temporary buffer for non-sequential chains
929
.iteration_buffer dd    ?       ; temporary buffer for non-sequential chains
930
.local_vars_size = $ - .local_vars
930
.local_vars_size = $ - .local_vars
931
                rd      2       ; saved registers
931
                rd      2       ; saved registers
932
                dd      ?       ; return address
932
                dd      ?       ; return address
933
.disk           dd      ?       ; first argument
933
.disk           dd      ?       ; first argument
934
end virtual
934
end virtual
935
; 1. If there is no cache for this disk, nothing to do, just return zero.
935
; 1. If there is no cache for this disk, nothing to do, just return zero.
936
        cmp     [ebx+DISKCACHE.pointer], 0
936
        cmp     [ebx+DISKCACHE.pointer], 0
937
        jz      .return0
937
        jz      .return0
938
; 2. Prepare for the loop: initialize current pointer and .size_left,
938
; 2. Prepare for the loop: initialize current pointer and .size_left,
939
; calculate .cache_end.
939
; calculate .cache_end.
940
        mov     ecx, [ebx+DISKCACHE.sad_size]
940
        mov     ecx, [ebx+DISKCACHE.sad_size]
941
        mov     [.size_left], ecx
941
        mov     [.size_left], ecx
942
        lea     ecx, [ecx*sizeof.CACHE_ITEM/4]
942
        lea     ecx, [ecx*sizeof.CACHE_ITEM/4]
943
        shl     ecx, 2
943
        shl     ecx, 2
944
        mov     esi, [ebx+DISKCACHE.pointer]
944
        mov     esi, [ebx+DISKCACHE.pointer]
945
        add     esi, sizeof.CACHE_ITEM
945
        add     esi, sizeof.CACHE_ITEM
946
        add     ecx, esi
946
        add     ecx, esi
947
        mov     [.cache_end], ecx
947
        mov     [.cache_end], ecx
948
; 3. Main loop: go over all items, go to 5 for every modified item.
948
; 3. Main loop: go over all items, go to 5 for every modified item.
949
.look:
949
.look:
950
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
950
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
951
        jz      .begin_write
951
        jz      .begin_write
952
.look_next:
952
.look_next:
953
        add     esi, sizeof.CACHE_ITEM
953
        add     esi, sizeof.CACHE_ITEM
954
        dec     [.size_left]
954
        dec     [.size_left]
955
        jnz     .look
955
        jnz     .look
956
; 4. Return success.
956
; 4. Return success.
957
.return0:
957
.return0:
958
        xor     eax, eax
958
        xor     eax, eax
959
.return:
959
.return:
960
        add     esp, .local_vars_size
960
        add     esp, .local_vars_size
961
        pop     edi esi         ; restore used registers to be stdcall
961
        pop     edi esi         ; restore used registers to be stdcall
962
        ret     4               ; return popping one argument
962
        ret     4               ; return popping one argument
963
.begin_write:
963
.begin_write:
964
; We have found a modified item.
964
; We have found a modified item.
965
; 5. Prepare for chain finding: save the current item, initialize chain variables.
965
; 5. Prepare for chain finding: save the current item, initialize chain variables.
966
        mov     [.current_ptr], esi
966
        mov     [.current_ptr], esi
967
; Initialize chain as sequential zero-length starting at the current item.
967
; Initialize chain as sequential zero-length starting at the current item.
968
        mov     [.chain_start_ptr], esi
968
        mov     [.chain_start_ptr], esi
969
        mov     eax, [ebx+DISKCACHE.sad_size]
969
        mov     eax, [ebx+DISKCACHE.sad_size]
970
        sub     eax, [.size_left]
970
        sub     eax, [.size_left]
971
        inc     eax
971
        inc     eax
972
        mov     ecx, [ebx+DISKCACHE.sector_size_log]
972
        mov     ecx, [ebx+DISKCACHE.sector_size_log]
973
        shl     eax, cl
973
        shl     eax, cl
974
        add     eax, [ebx+DISKCACHE.data]
974
        add     eax, [ebx+DISKCACHE.data]
975
        mov     [.chain_start_pos], eax
975
        mov     [.chain_start_pos], eax
976
        mov     [.chain_size], 0
976
        mov     [.chain_size], 0
977
        mov     [.sequential], 1
977
        mov     [.sequential], 1
978
; 6. Expand the chain backward.
978
; 6. Expand the chain backward.
979
; Note: the main loop in step 2 looks for items sequentially,
979
; Note: the main loop in step 2 looks for items sequentially,
980
; so the previous item is not modified. If the previous sector
980
; so the previous item is not modified. If the previous sector
981
; is present in the cache, it automatically makes the chain scattered.
981
; is present in the cache, it automatically makes the chain scattered.
982
; 6a. Calculate sector number: one before the sector for the current item.
982
; 6a. Calculate sector number: one before the sector for the current item.
983
        mov     eax, [esi+CACHE_ITEM.SectorLo]
983
        mov     eax, [esi+CACHE_ITEM.SectorLo]
984
        mov     edx, [esi+CACHE_ITEM.SectorHi]
984
        mov     edx, [esi+CACHE_ITEM.SectorHi]
985
        sub     eax, 1
985
        sub     eax, 1
986
        sbb     edx, 0
986
        sbb     edx, 0
987
.find_chain_start:
987
.find_chain_start:
988
; 6b. For each sector where the previous item does not expand the chain,
988
; 6b. For each sector where the previous item does not expand the chain,
989
; call the lookup function without adding to the cache.
989
; call the lookup function without adding to the cache.
990
        call    cache_lookup_read
990
        call    cache_lookup_read
991
; 6c. If the sector is not found in cache or is not modified, stop expanding
991
; 6c. If the sector is not found in cache or is not modified, stop expanding
992
; and advance to step 7.
992
; and advance to step 7.
993
        jc      .found_chain_start
993
        jc      .found_chain_start
994
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
994
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
995
        jnz     .found_chain_start
995
        jnz     .found_chain_start
996
; 6d. We have found a new block that expands the chain backwards.
996
; 6d. We have found a new block that expands the chain backwards.
997
; It makes the chain non-sequential.
997
; It makes the chain non-sequential.
998
; Normally, sectors come in sequential blocks, so try to look at previous items
998
; Normally, sectors come in sequential blocks, so try to look at previous items
999
; before returning to 6b; if there is a sequential block indeed, this saves some
999
; before returning to 6b; if there is a sequential block indeed, this saves some
1000
; time instead of many full-fledged lookups.
1000
; time instead of many full-fledged lookups.
1001
        mov     [.sequential], 0
1001
        mov     [.sequential], 0
1002
        mov     [.chain_start_pos], edi
1002
        mov     [.chain_start_pos], edi
1003
.look_backward:
1003
.look_backward:
1004
; 6e. For each sector, update chain start pos/ptr, decrement sector number,
1004
; 6e. For each sector, update chain start pos/ptr, decrement sector number,
1005
; look at the previous item.
1005
; look at the previous item.
1006
        mov     [.chain_start_ptr], esi
1006
        mov     [.chain_start_ptr], esi
1007
        inc     [.chain_size]
1007
        inc     [.chain_size]
1008
        sub     eax, 1
1008
        sub     eax, 1
1009
        sbb     edx, 0
1009
        sbb     edx, 0
1010
        sub     esi, sizeof.CACHE_ITEM
1010
        sub     esi, sizeof.CACHE_ITEM
1011
; If the previous item exists...
1011
; If the previous item exists...
1012
        cmp     esi, [ebx+DISKCACHE.pointer]
1012
        cmp     esi, [ebx+DISKCACHE.pointer]
1013
        jbe     .find_chain_start
1013
        jbe     .find_chain_start
1014
; ...describes the correct sector...
1014
; ...describes the correct sector...
1015
        cmp     [esi+CACHE_ITEM.SectorLo], eax
1015
        cmp     [esi+CACHE_ITEM.SectorLo], eax
1016
        jnz     .find_chain_start
1016
        jnz     .find_chain_start
1017
        cmp     [esi+CACHE_ITEM.SectorHi], edx
1017
        cmp     [esi+CACHE_ITEM.SectorHi], edx
1018
        jnz     .find_chain_start
1018
        jnz     .find_chain_start
1019
; ...and is modified...
1019
; ...and is modified...
1020
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
1020
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
1021
        jnz     .found_chain_start
1021
        jnz     .found_chain_start
1022
; ...expand the chain one sector backwards and continue the loop at 6e.
1022
; ...expand the chain one sector backwards and continue the loop at 6e.
1023
; Otherwise, advance to step 7 if the previous item describes the correct sector
1023
; Otherwise, advance to step 7 if the previous item describes the correct sector
1024
; but is not modified, and return to step 6b otherwise.
1024
; but is not modified, and return to step 6b otherwise.
1025
        mov     edi, 1
1025
        mov     edi, 1
1026
        shl     edi, cl
1026
        shl     edi, cl
1027
        sub     [.chain_start_pos], edi
1027
        sub     [.chain_start_pos], edi
1028
        jmp     .look_backward
1028
        jmp     .look_backward
1029
.found_chain_start:
1029
.found_chain_start:
1030
; 7. Expand the chain forward.
1030
; 7. Expand the chain forward.
1031
; 7a. Prepare for the loop at 7b:
1031
; 7a. Prepare for the loop at 7b:
1032
; set esi = pointer to current item, edx:eax = current sector.
1032
; set esi = pointer to current item, edx:eax = current sector.
1033
        mov     esi, [.current_ptr]
1033
        mov     esi, [.current_ptr]
1034
        mov     eax, [esi+CACHE_ITEM.SectorLo]
1034
        mov     eax, [esi+CACHE_ITEM.SectorLo]
1035
        mov     edx, [esi+CACHE_ITEM.SectorHi]
1035
        mov     edx, [esi+CACHE_ITEM.SectorHi]
1036
.look_forward:
1036
.look_forward:
1037
; 7b. First, look at the next item. If it describes the next sector:
1037
; 7b. First, look at the next item. If it describes the next sector:
1038
; if it is modified, expand the chain with that sector and continue this step,
1038
; if it is modified, expand the chain with that sector and continue this step,
1039
; if it is not modified, the chain is completed, so advance to step 8.
1039
; if it is not modified, the chain is completed, so advance to step 8.
1040
        inc     [.chain_size]
1040
        inc     [.chain_size]
1041
        add     eax, 1
1041
        add     eax, 1
1042
        adc     edx, 0
1042
        adc     edx, 0
1043
        add     esi, sizeof.CACHE_ITEM
1043
        add     esi, sizeof.CACHE_ITEM
1044
        cmp     esi, [.cache_end]
1044
        cmp     esi, [.cache_end]
1045
        jae     .find_chain_end
1045
        jae     .find_chain_end
1046
        cmp     [esi+CACHE_ITEM.SectorLo], eax
1046
        cmp     [esi+CACHE_ITEM.SectorLo], eax
1047
        jnz     .find_chain_end
1047
        jnz     .find_chain_end
1048
        cmp     [esi+CACHE_ITEM.SectorHi], edx
1048
        cmp     [esi+CACHE_ITEM.SectorHi], edx
1049
        jnz     .find_chain_end
1049
        jnz     .find_chain_end
1050
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
1050
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
1051
        jnz     .found_chain_end
1051
        jnz     .found_chain_end
1052
        jmp     .look_forward
1052
        jmp     .look_forward
1053
.find_chain_end:
1053
.find_chain_end:
1054
; 7c. Otherwise, call the lookup function.
1054
; 7c. Otherwise, call the lookup function.
1055
        call    cache_lookup_read
1055
        call    cache_lookup_read
1056
; 7d. If the next sector is present in the cache and is modified,
1056
; 7d. If the next sector is present in the cache and is modified,
1057
; mark the chain as non-sequential and continue to step 7b.
1057
; mark the chain as non-sequential and continue to step 7b.
1058
        jc      .found_chain_end
1058
        jc      .found_chain_end
1059
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
1059
        cmp     [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED
1060
        jnz     .found_chain_end
1060
        jnz     .found_chain_end
1061
        mov     [.sequential], 0
1061
        mov     [.sequential], 0
1062
        jmp     .look_forward
1062
        jmp     .look_forward
1063
.found_chain_end:
1063
.found_chain_end:
1064
; 8. Decide whether the chain is sequential or scattered.
1064
; 8. Decide whether the chain is sequential or scattered.
1065
; Advance to step 9 for sequential chains, go to step 10 for scattered chains.
1065
; Advance to step 9 for sequential chains, go to step 10 for scattered chains.
1066
        cmp     [.sequential], 0
1066
        cmp     [.sequential], 0
1067
        jz      .write_non_sequential
1067
        jz      .write_non_sequential
1068
.write_sequential:
1068
.write_sequential:
1069
; 9. Write a sequential chain to disk.
1069
; 9. Write a sequential chain to disk.
1070
; 9a. Pass the entire chain to the driver.
1070
; 9a. Pass the entire chain to the driver.
1071
        mov     eax, [.chain_start_ptr]
1071
        mov     eax, [.chain_start_ptr]
1072
        lea     ecx, [.chain_size]
1072
        lea     ecx, [.chain_size]
1073
        push    ecx     ; numsectors
1073
        push    ecx     ; numsectors
1074
        pushd   [eax+CACHE_ITEM.SectorHi]       ; startsector
1074
        pushd   [eax+CACHE_ITEM.SectorHi]       ; startsector
1075
        pushd   [eax+CACHE_ITEM.SectorLo]       ; startsector
1075
        pushd   [eax+CACHE_ITEM.SectorLo]       ; startsector
1076
        push    [.chain_start_pos+12]           ; buffer
1076
        push    [.chain_start_pos+12]           ; buffer
1077
        mov     esi, [ebp+PARTITION.Disk]
1077
        mov     esi, [ebp+PARTITION.Disk]
1078
        mov     al, DISKFUNC.write
1078
        mov     al, DISKFUNC.write
1079
        call    disk_call_driver
1079
        call    disk_call_driver
1080
; 9b. If failed, pass the error code to the driver.
1080
; 9b. If failed, pass the error code to the driver.
1081
        test    eax, eax
1081
        test    eax, eax
1082
        jnz     .return
1082
        jnz     .return
1083
; 9c. If succeeded, mark all sectors in the chain as not-modified,
1083
; 9c. If succeeded, mark all sectors in the chain as not-modified,
1084
; advance current item and number of items left to skip the chain.
1084
; advance current item and number of items left to skip the chain.
1085
        mov     esi, [.current_ptr]
1085
        mov     esi, [.current_ptr]
1086
        mov     eax, [.chain_size]
1086
        mov     eax, [.chain_size]
1087
        sub     [.size_left], eax
1087
        sub     [.size_left], eax
1088
@@:
1088
@@:
1089
        mov     [esi+CACHE_ITEM.Status], CACHE_ITEM_COPY
1089
        mov     [esi+CACHE_ITEM.Status], CACHE_ITEM_COPY
1090
        add     esi, sizeof.CACHE_ITEM
1090
        add     esi, sizeof.CACHE_ITEM
1091
        dec     eax
1091
        dec     eax
1092
        jnz     @b
1092
        jnz     @b
1093
; 9d. Continue the main loop at step 2 if there are more sectors.
1093
; 9d. Continue the main loop at step 2 if there are more sectors.
1094
; Return success otherwise.
1094
; Return success otherwise.
1095
        cmp     [.size_left], 0
1095
        cmp     [.size_left], 0
1096
        jnz     .look
1096
        jnz     .look
1097
        jmp     .return0
1097
        jmp     .return0
1098
.write_non_sequential:
1098
.write_non_sequential:
1099
; Write a non-sequential chain to the disk.
1099
; Write a non-sequential chain to the disk.
1100
; 10. Allocate a temporary buffer.
1100
; 10. Allocate a temporary buffer.
1101
; Use [.chain_size] sectors, but
1101
; Use [.chain_size] sectors, but
1102
; not greater than CACHE_MAX_ALLOC_SIZE bytes
1102
; not greater than CACHE_MAX_ALLOC_SIZE bytes
1103
; and not greater than half of free memory.
1103
; and not greater than half of free memory.
1104
        mov     eax, [pg_data.pages_free]
1104
        mov     eax, [pg_data.pages_free]
1105
        shr     eax, 1
1105
        shr     eax, 1
1106
        jz      .nomemory
1106
        jz      .nomemory
1107
        cmp     eax, CACHE_MAX_ALLOC_SIZE shr 12
1107
        cmp     eax, CACHE_MAX_ALLOC_SIZE shr 12
1108
        jbe     @f
1108
        jbe     @f
1109
        mov     eax, CACHE_MAX_ALLOC_SIZE shr 12
1109
        mov     eax, CACHE_MAX_ALLOC_SIZE shr 12
1110
@@:
1110
@@:
1111
        shl     eax, 12
1111
        shl     eax, 12
1112
        shr     eax, cl
1112
        shr     eax, cl
1113
        jz      .nomemory
1113
        jz      .nomemory
1114
        cmp     eax, [.chain_size]
1114
        cmp     eax, [.chain_size]
1115
        jbe     @f
1115
        jbe     @f
1116
        mov     eax, [.chain_size]
1116
        mov     eax, [.chain_size]
1117
@@:
1117
@@:
1118
        mov     [.iteration_size], eax
1118
        mov     [.iteration_size], eax
1119
        shl     eax, cl
1119
        shl     eax, cl
1120
        stdcall kernel_alloc, eax
1120
        stdcall kernel_alloc, eax
1121
        test    eax, eax
1121
        test    eax, eax
1122
        jz      .nomemory
1122
        jz      .nomemory
1123
        mov     [.iteration_buffer], eax
1123
        mov     [.iteration_buffer], eax
1124
.write_non_sequential_iteration:
1124
.write_non_sequential_iteration:
1125
; 11. Split the chain so that each iteration fits in the allocated buffer.
1125
; 11. Split the chain so that each iteration fits in the allocated buffer.
1126
; Iteration size is the minimum of chain size and allocated size.
1126
; Iteration size is the minimum of chain size and allocated size.
1127
        mov     eax, [.chain_size]
1127
        mov     eax, [.chain_size]
1128
        cmp     eax, [.iteration_size]
1128
        cmp     eax, [.iteration_size]
1129
        jae     @f
1129
        jae     @f
1130
        mov     [.iteration_size], eax
1130
        mov     [.iteration_size], eax
1131
@@:
1131
@@:
1132
; 12. Prepare arguments for the driver.
1132
; 12. Prepare arguments for the driver.
1133
        mov     esi, [.chain_start_ptr]
1133
        mov     esi, [.chain_start_ptr]
1134
        mov     edi, [.iteration_buffer]
1134
        mov     edi, [.iteration_buffer]
1135
        push    [.iteration_size]
1135
        push    [.iteration_size]
1136
        push    esp     ; numsectors
1136
        push    esp     ; numsectors
1137
        push    [esi+CACHE_ITEM.SectorHi]       ; startsector
1137
        push    [esi+CACHE_ITEM.SectorHi]       ; startsector
1138
        push    [esi+CACHE_ITEM.SectorLo]       ; startsector
1138
        push    [esi+CACHE_ITEM.SectorLo]       ; startsector
1139
        push    edi     ; buffer
1139
        push    edi     ; buffer
1140
; 13. Copy data from the cache to the temporary buffer,
1140
; 13. Copy data from the cache to the temporary buffer,
1141
; advancing chain_start pos/ptr and marking sectors as not-modified.
1141
; advancing chain_start pos/ptr and marking sectors as not-modified.
1142
; 13a. Prepare for the loop: push number of sectors to process.
1142
; 13a. Prepare for the loop: push number of sectors to process.
1143
        push    [.iteration_size+20]    ; temporary variable
1143
        push    [.iteration_size+20]    ; temporary variable
1144
.copy_loop:
1144
.copy_loop:
1145
; 13b. For each sector, copy the data.
1145
; 13b. For each sector, copy the data.
1146
; Note that edi is advanced automatically.
1146
; Note that edi is advanced automatically.
1147
        mov     esi, [.chain_start_pos+24]
1147
        mov     esi, [.chain_start_pos+24]
1148
        mov     ecx, [ebx+DISKCACHE.sector_size_log]
1148
        mov     ecx, [ebx+DISKCACHE.sector_size_log]
1149
        mov     eax, 1
1149
        mov     eax, 1
1150
        shl     eax, cl
1150
        shl     eax, cl
1151
        mov     ecx, eax
1151
        mov     ecx, eax
1152
        shr     ecx, 2
1152
        shr     ecx, 2
1153
        rep movsd
1153
        rep movsd
1154
        mov     ecx, eax ; keep for 13e
1154
        mov     ecx, eax ; keep for 13e
1155
; 13c. Mark the item as not-modified.
1155
; 13c. Mark the item as not-modified.
1156
        mov     esi, [.chain_start_ptr+24]
1156
        mov     esi, [.chain_start_ptr+24]
1157
        mov     [esi+CACHE_ITEM.Status], CACHE_ITEM_COPY
1157
        mov     [esi+CACHE_ITEM.Status], CACHE_ITEM_COPY
1158
; 13d. Check whether the next sector continues the chain.
1158
; 13d. Check whether the next sector continues the chain.
1159
; If so, advance to 13e. Otherwise, go to 13f.
1159
; If so, advance to 13e. Otherwise, go to 13f.
1160
        mov     eax, [esi+CACHE_ITEM.SectorLo]
1160
        mov     eax, [esi+CACHE_ITEM.SectorLo]
1161
        mov     edx, [esi+CACHE_ITEM.SectorHi]
1161
        mov     edx, [esi+CACHE_ITEM.SectorHi]
1162
        add     esi, sizeof.CACHE_ITEM
1162
        add     esi, sizeof.CACHE_ITEM
1163
        add     eax, 1
1163
        add     eax, 1
1164
        adc     edx, 0
1164
        adc     edx, 0
1165
        cmp     esi, [.cache_end+24]
1165
        cmp     esi, [.cache_end+24]
1166
        jae     .no_forward
1166
        jae     .no_forward
1167
        cmp     [esi+CACHE_ITEM.SectorLo], eax
1167
        cmp     [esi+CACHE_ITEM.SectorLo], eax
1168
        jnz     .no_forward
1168
        jnz     .no_forward
1169
        cmp     [esi+CACHE_ITEM.SectorHi], edx
1169
        cmp     [esi+CACHE_ITEM.SectorHi], edx
1170
        jnz     .no_forward
1170
        jnz     .no_forward
1171
; 13e. Increment position/pointer to the chain and
1171
; 13e. Increment position/pointer to the chain and
1172
; continue the loop.
1172
; continue the loop.
1173
        add     [.chain_start_pos+24], ecx
1173
        add     [.chain_start_pos+24], ecx
1174
        mov     [.chain_start_ptr+24], esi
1174
        mov     [.chain_start_ptr+24], esi
1175
        dec     dword [esp]
1175
        dec     dword [esp]
1176
        jnz     .copy_loop
1176
        jnz     .copy_loop
1177
        jmp     .copy_done
1177
        jmp     .copy_done
1178
.no_forward:
1178
.no_forward:
1179
; 13f. Call the lookup function without adding to the cache.
1179
; 13f. Call the lookup function without adding to the cache.
1180
; Update position/pointer with returned value.
1180
; Update position/pointer with returned value.
1181
; Note: for the last sector in the chain, edi/esi may contain
1181
; Note: for the last sector in the chain, edi/esi may contain
1182
; garbage; we are not going to use them in this case.
1182
; garbage; we are not going to use them in this case.
1183
        push    edi
1183
        push    edi
1184
        call    cache_lookup_read
1184
        call    cache_lookup_read
1185
        mov     [.chain_start_pos+28], edi
1185
        mov     [.chain_start_pos+28], edi
1186
        mov     [.chain_start_ptr+28], esi
1186
        mov     [.chain_start_ptr+28], esi
1187
        pop     edi
1187
        pop     edi
1188
        dec     dword [esp]
1188
        dec     dword [esp]
1189
        jnz     .copy_loop
1189
        jnz     .copy_loop
1190
.copy_done:
1190
.copy_done:
1191
; 13g. Restore the stack after 13a.
1191
; 13g. Restore the stack after 13a.
1192
        pop     ecx
1192
        pop     ecx
1193
; 14. Call the driver.
1193
; 14. Call the driver.
1194
        mov     esi, [ebp+PARTITION.Disk]
1194
        mov     esi, [ebp+PARTITION.Disk]
1195
        mov     al, DISKFUNC.write
1195
        mov     al, DISKFUNC.write
1196
        call    disk_call_driver
1196
        call    disk_call_driver
1197
        pop     ecx     ; numsectors
1197
        pop     ecx     ; numsectors
1198
; 15. If the driver has returned an error, free the buffer allocated at step 10
1198
; 15. If the driver has returned an error, free the buffer allocated at step 10
1199
; and pass the error to the caller.
1199
; and pass the error to the caller.
1200
; Otherwise, remove the processed part from the chain and continue iterations
1200
; Otherwise, remove the processed part from the chain and continue iterations
1201
; starting in step 11 if there are more data to process.
1201
; starting in step 11 if there are more data to process.
1202
        test    eax, eax
1202
        test    eax, eax
1203
        jnz     .nonsequential_error
1203
        jnz     .nonsequential_error
1204
        sub     [.chain_size], ecx
1204
        sub     [.chain_size], ecx
1205
        jnz     .write_non_sequential_iteration
1205
        jnz     .write_non_sequential_iteration
1206
; 16. The chain is written. Free the temporary buffer
1206
; 16. The chain is written. Free the temporary buffer
1207
; and continue the loop at step 2.
1207
; and continue the loop at step 2.
1208
        stdcall kernel_free, [.iteration_buffer]
1208
        stdcall kernel_free, [.iteration_buffer]
1209
        mov     esi, [.current_ptr]
1209
        mov     esi, [.current_ptr]
1210
        jmp     .look_next
1210
        jmp     .look_next
1211
.nonsequential_error:
1211
.nonsequential_error:
1212
        push    eax
1212
        push    eax
1213
        stdcall kernel_free, [.iteration_buffer+4]
1213
        stdcall kernel_free, [.iteration_buffer+4]
1214
        pop     eax
1214
        pop     eax
1215
        jmp     .return
1215
        jmp     .return
1216
.nomemory:
1216
.nomemory:
1217
        mov     eax, DISK_STATUS_NO_MEMORY
1217
        mov     eax, DISK_STATUS_NO_MEMORY
1218
        jmp     .return
1218
        jmp     .return
1219
endp
1219
endp
1220
 
1220
 
1221
; This internal function is called from disk_add to initialize the caching for
1221
; This internal function is called from disk_add to initialize the caching for
1222
; a new DISK.
1222
; a new DISK.
1223
; The algorithm is inherited from getcache.inc: take 1/32 part of the available
1223
; The algorithm is inherited from getcache.inc: take 1/32 part of the available
1224
; physical memory, round down to 8 pages, limit by 128K from below and by 1M
1224
; physical memory, round down to 8 pages, limit by 128K from below and by 1M
1225
; from above. Reserve 1/8 part of the cache for system data and 7/8 for app
1225
; from above. Reserve 1/8 part of the cache for system data and 7/8 for app
1226
; data.
1226
; data.
1227
; After the size is calculated, but before the cache is allocated, the device
1227
; After the size is calculated, but before the cache is allocated, the device
1228
; driver can adjust the size. In particular, setting size to zero disables
1228
; driver can adjust the size. In particular, setting size to zero disables
1229
; caching: there is no sense in a cache for a ramdisk. In fact, such action
1229
; caching: there is no sense in a cache for a ramdisk. In fact, such action
1230
; is most useful example of a non-trivial adjustment.
1230
; is most useful example of a non-trivial adjustment.
1231
; esi = pointer to DISK structure
1231
; esi = pointer to DISK structure
1232
disk_init_cache:
1232
disk_init_cache:
1233
; 1. Verify sector size. The code requires it to be a power of 2 not less than 4.
1233
; 1. Verify sector size. The code requires it to be a power of 2 not less than 4.
1234
; In the name of sanity check that sector size is not too small or too large.
1234
; In the name of sanity check that sector size is not too small or too large.
1235
        bsf     ecx, [esi+DISK.MediaInfo.SectorSize]
1235
        bsf     ecx, [esi+DISK.MediaInfo.SectorSize]
1236
        jz      .invalid_sector_size
1236
        jz      .invalid_sector_size
1237
        mov     eax, 1
1237
        mov     eax, 1
1238
        shl     eax, cl
1238
        shl     eax, cl
1239
        cmp     eax, [esi+DISK.MediaInfo.SectorSize]
1239
        cmp     eax, [esi+DISK.MediaInfo.SectorSize]
1240
        jnz     .invalid_sector_size
1240
        jnz     .invalid_sector_size
1241
        cmp     ecx, 6
1241
        cmp     ecx, 6
1242
        jb      .invalid_sector_size
1242
        jb      .invalid_sector_size
1243
        cmp     ecx, 14
1243
        cmp     ecx, 14
1244
        jbe     .normal_sector_size
1244
        jbe     .normal_sector_size
1245
.invalid_sector_size:
1245
.invalid_sector_size:
1246
        DEBUGF 1,'K : sector size %x is invalid\n',[esi+DISK.MediaInfo.SectorSize]
1246
        DEBUGF 1,'K : sector size %x is invalid\n',[esi+DISK.MediaInfo.SectorSize]
1247
        xor     eax, eax
1247
        xor     eax, eax
1248
        ret
1248
        ret
1249
.normal_sector_size:
1249
.normal_sector_size:
1250
        mov     [esi+DISK.SysCache.sector_size_log], ecx
1250
        mov     [esi+DISK.SysCache.sector_size_log], ecx
1251
        mov     [esi+DISK.AppCache.sector_size_log], ecx
1251
        mov     [esi+DISK.AppCache.sector_size_log], ecx
1252
; 2. Calculate the suggested cache size.
1252
; 2. Calculate the suggested cache size.
1253
; 2a. Get the size of free physical memory in pages.
1253
; 2a. Get the size of free physical memory in pages.
1254
        mov     eax, [pg_data.pages_free]
1254
        mov     eax, [pg_data.pages_free]
1255
; 2b. Use the value to calculate the size.
1255
; 2b. Use the value to calculate the size.
1256
        shl     eax, 12 - 5     ; 1/32 of it in bytes
1256
        shl     eax, 12 - 5     ; 1/32 of it in bytes
1257
        and     eax, -8*4096    ; round down to the multiple of 8 pages
1257
        and     eax, -8*4096    ; round down to the multiple of 8 pages
1258
; 2c. Force lower and upper limits.
1258
; 2c. Force lower and upper limits.
1259
        cmp     eax, 1024*1024
1259
        cmp     eax, 1024*1024
1260
        jb      @f
1260
        jb      @f
1261
        mov     eax, 1024*1024
1261
        mov     eax, 1024*1024
1262
@@:
1262
@@:
1263
        cmp     eax, 128*1024
1263
        cmp     eax, 128*1024
1264
        ja      @f
1264
        ja      @f
1265
        mov     eax, 128*1024
1265
        mov     eax, 128*1024
1266
@@:
1266
@@:
1267
; 2d. Give a chance to the driver to adjust the size.
1267
; 2d. Give a chance to the driver to adjust the size.
1268
        push    eax
1268
        push    eax
1269
        mov     al, DISKFUNC.adjust_cache_size
1269
        mov     al, DISKFUNC.adjust_cache_size
1270
        call    disk_call_driver
1270
        call    disk_call_driver
1271
; Cache size calculated.
1271
; Cache size calculated.
1272
        mov     [esi+DISK.cache_size], eax
1272
        mov     [esi+DISK.cache_size], eax
1273
        test    eax, eax
1273
        test    eax, eax
1274
        jz      .nocache
1274
        jz      .nocache
1275
; 3. Allocate memory for the cache.
1275
; 3. Allocate memory for the cache.
1276
; 3a. Call the allocator.
1276
; 3a. Call the allocator.
1277
        stdcall kernel_alloc, eax
1277
        stdcall kernel_alloc, eax
1278
        test    eax, eax
1278
        test    eax, eax
1279
        jnz     @f
1279
        jnz     @f
1280
; 3b. If it failed, say a message and return with eax = 0.
1280
; 3b. If it failed, say a message and return with eax = 0.
1281
        dbgstr 'no memory for disk cache'
1281
        dbgstr 'no memory for disk cache'
1282
        jmp     .nothing
1282
        jmp     .nothing
1283
@@:
1283
@@:
1284
; 4. Fill two DISKCACHE structures.
1284
; 4. Fill two DISKCACHE structures.
1285
        mov     [esi+DISK.SysCache.pointer], eax
1285
        mov     [esi+DISK.SysCache.pointer], eax
1286
        lea     ecx, [esi+DISK.CacheLock]
1286
        lea     ecx, [esi+DISK.CacheLock]
1287
        call    mutex_init
1287
        call    mutex_init
1288
; The following code is inherited from getcache.inc.
1288
; The following code is inherited from getcache.inc.
1289
        mov     edx, [esi+DISK.SysCache.pointer]
1289
        mov     edx, [esi+DISK.SysCache.pointer]
1290
        and     [esi+DISK.SysCache.search_start], 0
1290
        and     [esi+DISK.SysCache.search_start], 0
1291
        and     [esi+DISK.AppCache.search_start], 0
1291
        and     [esi+DISK.AppCache.search_start], 0
1292
        mov     eax, [esi+DISK.cache_size]
1292
        mov     eax, [esi+DISK.cache_size]
1293
        shr     eax, 3
1293
        shr     eax, 3
1294
        mov     [esi+DISK.SysCache.data_size], eax
1294
        mov     [esi+DISK.SysCache.data_size], eax
1295
        add     edx, eax
1295
        add     edx, eax
1296
        imul    eax, 7
1296
        imul    eax, 7
1297
        mov     [esi+DISK.AppCache.data_size], eax
1297
        mov     [esi+DISK.AppCache.data_size], eax
1298
        mov     [esi+DISK.AppCache.pointer], edx
1298
        mov     [esi+DISK.AppCache.pointer], edx
1299
 
1299
 
1300
        mov     eax, [esi+DISK.SysCache.data_size]
1300
        mov     eax, [esi+DISK.SysCache.data_size]
1301
        call    calculate_cache_slots
1301
        call    calculate_cache_slots
1302
        add     eax, [esi+DISK.SysCache.pointer]
1302
        add     eax, [esi+DISK.SysCache.pointer]
1303
        mov     [esi+DISK.SysCache.data], eax
1303
        mov     [esi+DISK.SysCache.data], eax
1304
        mov     [esi+DISK.SysCache.sad_size], ecx
1304
        mov     [esi+DISK.SysCache.sad_size], ecx
1305
 
1305
 
1306
        push    edi
1306
        push    edi
1307
        mov     edi, [esi+DISK.SysCache.pointer]
1307
        mov     edi, [esi+DISK.SysCache.pointer]
1308
        lea     ecx, [(ecx+1)*3]
1308
        lea     ecx, [(ecx+1)*3]
1309
        xor     eax, eax
1309
        xor     eax, eax
1310
        rep stosd
1310
        rep stosd
1311
        pop     edi
1311
        pop     edi
1312
 
1312
 
1313
        mov     eax, [esi+DISK.AppCache.data_size]
1313
        mov     eax, [esi+DISK.AppCache.data_size]
1314
        call    calculate_cache_slots
1314
        call    calculate_cache_slots
1315
        add     eax, [esi+DISK.AppCache.pointer]
1315
        add     eax, [esi+DISK.AppCache.pointer]
1316
        mov     [esi+DISK.AppCache.data], eax
1316
        mov     [esi+DISK.AppCache.data], eax
1317
        mov     [esi+DISK.AppCache.sad_size], ecx
1317
        mov     [esi+DISK.AppCache.sad_size], ecx
1318
 
1318
 
1319
        push    edi
1319
        push    edi
1320
        mov     edi, [esi+DISK.AppCache.pointer]
1320
        mov     edi, [esi+DISK.AppCache.pointer]
1321
        lea     ecx, [(ecx+1)*3]
1321
        lea     ecx, [(ecx+1)*3]
1322
        xor     eax, eax
1322
        xor     eax, eax
1323
        rep stosd
1323
        rep stosd
1324
        pop     edi
1324
        pop     edi
1325
 
1325
 
1326
; 5. Return with nonzero al.
1326
; 5. Return with nonzero al.
1327
        mov     al, 1
1327
        mov     al, 1
1328
; 6. Return.
1328
; 6. Return.
1329
.nothing:
1329
.nothing:
1330
        ret
1330
        ret
1331
; No caching is required for this driver. Zero cache pointers and return with
1331
; No caching is required for this driver. Zero cache pointers and return with
1332
; nonzero al.
1332
; nonzero al.
1333
.nocache:
1333
.nocache:
1334
        mov     [esi+DISK.SysCache.pointer], eax
1334
        mov     [esi+DISK.SysCache.pointer], eax
1335
        mov     [esi+DISK.AppCache.pointer], eax
1335
        mov     [esi+DISK.AppCache.pointer], eax
1336
        mov     al, 1
1336
        mov     al, 1
1337
        ret
1337
        ret
1338
 
1338
 
1339
calculate_cache_slots:
1339
calculate_cache_slots:
1340
        push    eax
1340
        push    eax
1341
        mov     ecx, [esi+DISK.MediaInfo.SectorSize]
1341
        mov     ecx, [esi+DISK.MediaInfo.SectorSize]
1342
        add     ecx, sizeof.CACHE_ITEM
1342
        add     ecx, sizeof.CACHE_ITEM
1343
        xor     edx, edx
1343
        xor     edx, edx
1344
        div     ecx
1344
        div     ecx
1345
        mov     ecx, eax
1345
        mov     ecx, eax
1346
        imul    eax, [esi+DISK.MediaInfo.SectorSize]
1346
        imul    eax, [esi+DISK.MediaInfo.SectorSize]
1347
        sub     [esp], eax
1347
        sub     [esp], eax
1348
        pop     eax
1348
        pop     eax
1349
        dec     ecx
1349
        dec     ecx
1350
        ret
1350
        ret
1351
 
1351
 
1352
 
1352
 
1353
; This internal function is called from disk_media_dereference to free the
1353
; This internal function is called from disk_media_dereference to free the
1354
; allocated cache, if there is one.
1354
; allocated cache, if there is one.
1355
; esi = pointer to DISK structure
1355
; esi = pointer to DISK structure
1356
disk_free_cache:
1356
disk_free_cache:
1357
; The algorithm is straightforward.
1357
; The algorithm is straightforward.
1358
        mov     eax, [esi+DISK.SysCache.pointer]
1358
        mov     eax, [esi+DISK.SysCache.pointer]
1359
        test    eax, eax
1359
        test    eax, eax
1360
        jz      .nothing
1360
        jz      .nothing
1361
        stdcall kernel_free, eax
1361
        stdcall kernel_free, eax
1362
.nothing:
1362
.nothing:
1363
        ret
1363
        ret
1364
 
1364
 
1365
; This function flushes all modified data from both caches for the given DISK.
1365
; This function flushes all modified data from both caches for the given DISK.
1366
; esi = pointer to DISK
1366
; esi = pointer to DISK
1367
disk_sync:
1367
disk_sync:
1368
; The algorithm is straightforward.
1368
; The algorithm is straightforward.
1369
        cmp     [esi+DISK.SysCache.pointer], 0
1369
        cmp     [esi+DISK.SysCache.pointer], 0
1370
        jz      .nothing
1370
        jz      .nothing
1371
        lea     ecx, [esi+DISK.CacheLock]
1371
        lea     ecx, [esi+DISK.CacheLock]
1372
        call    mutex_lock
1372
        call    mutex_lock
1373
        push    ebx
1373
        push    ebx
1374
        push    esi     ; for second write_cache64
1374
        push    esi     ; for second write_cache64
1375
        push    esi     ; for first write_cache64
1375
        push    esi     ; for first write_cache64
1376
        lea     ebx, [esi+DISK.SysCache]
1376
        lea     ebx, [esi+DISK.SysCache]
1377
        call    write_cache64
1377
        call    write_cache64
1378
        add     ebx, DISK.AppCache - DISK.SysCache
1378
        add     ebx, DISK.AppCache - DISK.SysCache
1379
        call    write_cache64
1379
        call    write_cache64
1380
        pop     ebx
1380
        pop     ebx
1381
        lea     ecx, [esi+DISK.CacheLock]
1381
        lea     ecx, [esi+DISK.CacheLock]
1382
        call    mutex_unlock
1382
        call    mutex_unlock
1383
.nothing:
1383
.nothing:
1384
        mov     al, DISKFUNC.flush
1384
        mov     al, DISKFUNC.flush
1385
        call    disk_call_driver
1385
        call    disk_call_driver
1386
        ret
1386
        ret