Subversion Repositories Kolibri OS

Rev

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

Rev Author Line No. Line
2142 serge 1
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2
;;                                                              ;;
2465 Serge 3
;; Copyright (C) KolibriOS team 2011-2012. All rights reserved. ;;
2142 serge 4
;; Distributed under terms of the GNU General Public License    ;;
5
;;                                                              ;;
6
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
7
 
3908 Serge 8
$Revision: 3742 $
2142 serge 9
 
10
; This function is intended to replace the old 'hd_read' function when
11
; [hdd_appl_data] = 0, so its input/output parameters are the same, except
12
; that it can't use the global variables 'hd_error' and 'hdd_appl_data'.
13
; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure
14
; eax is relative to partition start
15
; out: eax = error code; 0 = ok
16
fs_read32_sys:
3908 Serge 17
; Save ecx, set ecx to SysCache and let the common part do its work.
2142 serge 18
        push    ecx
19
        mov     ecx, [ebp+PARTITION.Disk]
20
        add     ecx, DISK.SysCache
21
        jmp     fs_read32_common
22
 
23
; This function is intended to replace the old 'hd_read' function when
24
; [hdd_appl_data] = 1, so its input/output parameters are the same, except
25
; that it can't use the global variables 'hd_error' and 'hdd_appl_data'.
26
; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure
27
; eax is relative to partition start
28
; out: eax = error code; 0 = ok
29
fs_read32_app:
3908 Serge 30
; Save ecx, set ecx to AppCache and let the common part do its work.
2142 serge 31
        push    ecx
32
        mov     ecx, [ebp+PARTITION.Disk]
33
        add     ecx, DISK.AppCache
34
 
35
; This label is the common part of fs_read32_sys and fs_read32_app.
36
fs_read32_common:
37
; 1. Check that the required sector is inside the partition. If no, return
38
; DISK_STATUS_END_OF_MEDIA.
39
        cmp     dword [ebp+PARTITION.Length+4], 0
40
        jnz     @f
41
        cmp     dword [ebp+PARTITION.Length], eax
42
        ja      @f
43
        mov     eax, DISK_STATUS_END_OF_MEDIA
44
        pop     ecx
45
        ret
46
@@:
47
; 2. Get the absolute sector on the disk.
2987 Serge 48
        push    edx esi
2142 serge 49
        xor     edx, edx
50
        add     eax, dword [ebp+PARTITION.FirstSector]
51
        adc     edx, dword [ebp+PARTITION.FirstSector+4]
52
; 3. If there is no cache for this disk, just pass the request to the driver.
53
        cmp     [ecx+DISKCACHE.pointer], 0
54
        jnz     .scancache
55
        push    1
56
        push    esp     ; numsectors
57
        push    edx     ; startsector
58
        push    eax     ; startsector
59
        push    ebx     ; buffer
2987 Serge 60
        mov     esi, [ebp+PARTITION.Disk]
2142 serge 61
        mov     al, DISKFUNC.read
62
        call    disk_call_driver
63
        pop     ecx
2987 Serge 64
        pop     esi edx
2142 serge 65
        pop     ecx
66
        ret
67
.scancache:
68
; 4. Scan the cache.
2987 Serge 69
        push    edi ecx ; scan cache
2142 serge 70
        push    edx eax
71
virtual at esp
72
.sector_lo      dd      ?
73
.sector_hi      dd      ?
74
.cache          dd      ?
75
end virtual
76
; The following code is inherited from hd_read. The differences are:
77
; all code is protected by the cache lock; instead of static calls
78
; to hd_read_dma/hd_read_pio/bd_read the dynamic call to DISKFUNC.read is used;
79
; sector is 64-bit, not 32-bit.
80
        call    mutex_lock
81
        mov     eax, [.sector_lo]
82
        mov     edx, [.sector_hi]
83
        mov     esi, [ecx+DISKCACHE.pointer]
84
        mov     ecx, [ecx+DISKCACHE.sad_size]
85
        add     esi, 12
86
 
87
        mov     edi, 1
88
 
89
.hdreadcache:
90
 
91
        cmp     dword [esi+8], 0        ; empty
92
        je      .nohdcache
93
 
94
        cmp     [esi], eax      ; correct sector
95
        jne     .nohdcache
96
        cmp     [esi+4], edx    ; correct sector
97
        je      .yeshdcache
98
 
99
.nohdcache:
100
 
101
        add     esi, 12
102
        inc     edi
103
        dec     ecx
104
        jnz     .hdreadcache
105
 
106
        mov     esi, [.cache]
107
        call    find_empty_slot64       ; ret in edi
108
        test    eax, eax
109
        jnz     .read_done
110
 
111
        push    1
112
        push    esp
113
        push    edx
114
        push    [.sector_lo+12]
3232 Serge 115
        mov     ecx, [.cache+16]
2142 serge 116
        mov     eax, edi
117
        shl     eax, 9
118
        add     eax, [ecx+DISKCACHE.data]
119
        push    eax
120
        mov     esi, [ebp+PARTITION.Disk]
121
        mov     al, DISKFUNC.read
122
        call    disk_call_driver
123
        pop     ecx
124
        dec     ecx
125
        jnz     .read_done
126
 
127
        mov     ecx, [.cache]
128
        lea     eax, [edi*3]
129
        mov     esi, [ecx+DISKCACHE.pointer]
130
        lea     esi, [eax*4+esi]
131
 
132
        mov     eax, [.sector_lo]
133
        mov     edx, [.sector_hi]
134
        mov     [esi], eax      ; sector number
135
        mov     [esi+4], edx    ; sector number
136
        mov     dword [esi+8], 1; hd read - mark as same as in hd
137
 
138
.yeshdcache:
139
 
140
        mov     esi, edi
141
        mov     ecx, [.cache]
142
        shl     esi, 9
143
        add     esi, [ecx+DISKCACHE.data]
144
 
145
        mov     edi, ebx
146
        mov     ecx, 512/4
3555 Serge 147
        rep movsd               ; move data
2142 serge 148
        xor     eax, eax        ; successful read
149
.read_done:
150
        mov     ecx, [.cache]
151
        push    eax
152
        call    mutex_unlock
153
        pop     eax
154
        add     esp, 12
155
        pop     edi esi edx ecx
156
        ret
157
 
158
; This function is intended to replace the old 'hd_write' function when
159
; [hdd_appl_data] = 0, so its input/output parameters are the same, except
160
; that it can't use the global variables 'hd_error' and 'hdd_appl_data'.
161
; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure
162
; eax is relative to partition start
163
; out: eax = error code; 0 = ok
164
fs_write32_sys:
3908 Serge 165
; Save ecx, set ecx to SysCache and let the common part do its work.
2142 serge 166
        push    ecx
167
        mov     ecx, [ebp+PARTITION.Disk]
168
        add     ecx, DISK.SysCache
169
        jmp     fs_write32_common
170
 
171
; This function is intended to replace the old 'hd_write' function when
172
; [hdd_appl_data] = 1, so its input/output parameters are the same, except
173
; that it can't use the global variables 'hd_error' and 'hdd_appl_data'.
174
; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure
175
; eax is relative to partition start
176
; out: eax = error code; 0 = ok
177
fs_write32_app:
3908 Serge 178
; Save ecx, set ecx to AppCache and let the common part do its work.
2142 serge 179
        push    ecx
180
        mov     ecx, [ebp+PARTITION.Disk]
181
        add     ecx, DISK.AppCache
182
 
183
; This label is the common part of fs_read32_sys and fs_read32_app.
184
fs_write32_common:
185
; 1. Check that the required sector is inside the partition. If no, return
186
; DISK_STATUS_END_OF_MEDIA.
187
        cmp     dword [ebp+PARTITION.Length+4], 0
188
        jnz     @f
189
        cmp     dword [ebp+PARTITION.Length], eax
190
        ja      @f
191
        mov     eax, DISK_STATUS_END_OF_MEDIA
192
        pop     ecx
193
        ret
194
@@:
2987 Serge 195
        push    edx esi
2142 serge 196
; 2. Get the absolute sector on the disk.
197
        xor     edx, edx
198
        add     eax, dword [ebp+PARTITION.FirstSector]
199
        adc     edx, dword [ebp+PARTITION.FirstSector+4]
200
; 3. If there is no cache for this disk, just pass request to the driver.
201
        cmp     [ecx+DISKCACHE.pointer], 0
202
        jnz     .scancache
203
        push    1
204
        push    esp     ; numsectors
205
        push    edx     ; startsector
206
        push    eax     ; startsector
207
        push    ebx     ; buffer
2987 Serge 208
        mov     esi, [ebp+PARTITION.Disk]
2142 serge 209
        mov     al, DISKFUNC.write
210
        call    disk_call_driver
211
        pop     ecx
2987 Serge 212
        pop     esi edx
2142 serge 213
        pop     ecx
214
        ret
215
.scancache:
216
; 4. Scan the cache.
2987 Serge 217
        push    edi ecx ; scan cache
2142 serge 218
        push    edx eax
219
virtual at esp
220
.sector_lo      dd      ?
221
.sector_hi      dd      ?
222
.cache          dd      ?
223
end virtual
224
; The following code is inherited from hd_write. The differences are:
225
; all code is protected by the cache lock;
226
; sector is 64-bit, not 32-bit.
227
        call    mutex_lock
228
 
229
        ; check if the cache already has the sector and overwrite it
230
        mov     eax, [.sector_lo]
231
        mov     edx, [.sector_hi]
232
        mov     esi, [ecx+DISKCACHE.pointer]
233
        mov     ecx, [ecx+DISKCACHE.sad_size]
234
        add     esi, 12
235
 
236
        mov     edi, 1
237
 
238
.hdwritecache:
239
        cmp     dword [esi+8], 0        ; if cache slot is empty
240
        je      .not_in_cache_write
241
 
242
        cmp     [esi], eax      ; if the slot has the sector
243
        jne     .not_in_cache_write
244
        cmp     [esi+4], edx    ; if the slot has the sector
245
        je      .yes_in_cache_write
246
 
247
.not_in_cache_write:
248
 
249
        add     esi, 12
250
        inc     edi
251
        dec     ecx
252
        jnz     .hdwritecache
253
 
254
        ; sector not found in cache
255
        ; write the block to a new location
256
 
257
        mov     esi, [.cache]
258
        call    find_empty_slot64       ; ret in edi
259
        test    eax, eax
260
        jne     .hd_write_access_denied
261
 
262
        mov     ecx, [.cache]
263
        lea     eax, [edi*3]
264
        mov     esi, [ecx+DISKCACHE.pointer]
265
        lea     esi, [eax*4+esi]
266
 
267
        mov     eax, [.sector_lo]
268
        mov     edx, [.sector_hi]
269
        mov     [esi], eax      ; sector number
270
        mov     [esi+4], edx    ; sector number
271
 
272
.yes_in_cache_write:
273
 
3232 Serge 274
        mov     dword [esi+8], 2        ; write - differs from hd
2142 serge 275
 
276
        shl     edi, 9
277
        mov     ecx, [.cache]
278
        add     edi, [ecx+DISKCACHE.data]
279
 
280
        mov     esi, ebx
281
        mov     ecx, 512/4
3555 Serge 282
        rep movsd               ; move data
2142 serge 283
        xor     eax, eax        ; success
284
.hd_write_access_denied:
285
        mov     ecx, [.cache]
286
        push    eax
287
        call    mutex_unlock
288
        pop     eax
289
        add     esp, 12
290
        pop     edi esi edx ecx
291
        ret
292
 
293
; This internal function is called from fs_read32_* and fs_write32_*. It is the
294
; analogue of find_empty_slot for 64-bit sectors.
295
find_empty_slot64:
296
;-----------------------------------------------------------
297
; find empty or read slot, flush cache if next 12.5% is used by write
298
; output : edi = cache slot
299
;-----------------------------------------------------------
300
.search_again:
301
        mov     ecx, [esi+DISKCACHE.sad_size]
302
        mov     edi, [esi+DISKCACHE.search_start]
303
        shr     ecx, 3
304
.search_for_empty:
305
        inc     edi
306
        cmp     edi, [esi+DISKCACHE.sad_size]
307
        jbe     .inside_cache
308
        mov     edi, 1
309
.inside_cache:
310
        lea     eax, [edi*3]
311
        shl     eax, 2
312
        add     eax, [esi+DISKCACHE.pointer]
313
        cmp     dword [eax+8], 2
314
        jb      .found_slot             ; it's empty or read
315
        dec     ecx
316
        jnz     .search_for_empty
2987 Serge 317
        stdcall write_cache64, [ebp+PARTITION.Disk] ; no empty slots found, write all
2142 serge 318
        test    eax, eax
319
        jne     .found_slot_access_denied
320
        jmp     .search_again           ; and start again
321
.found_slot:
322
        mov     [esi+DISKCACHE.search_start], edi
323
        xor     eax, eax        ; success
324
.found_slot_access_denied:
325
        ret
326
 
327
; This function is intended to replace the old 'write_cache' function.
2987 Serge 328
proc write_cache64 uses ecx edx esi edi, disk:dword
2142 serge 329
locals
3232 Serge 330
cache_chain_started     dd      0
2142 serge 331
cache_chain_size        dd      ?
332
cache_chain_pos         dd      ?
333
cache_chain_ptr         dd      ?
334
endl
3232 Serge 335
saved_esi_pos = 16+12 ; size of local variables + size of registers before esi
2142 serge 336
; If there is no cache for this disk, nothing to do.
337
        cmp     [esi+DISKCACHE.pointer], 0
338
        jz      .flush
339
;-----------------------------------------------------------
340
; write all changed sectors to disk
341
;-----------------------------------------------------------
342
 
343
        ; write difference ( 2 ) from cache to DISK
344
        mov     ecx, [esi+DISKCACHE.sad_size]
345
        mov     esi, [esi+DISKCACHE.pointer]
346
        add     esi, 12
347
        mov     edi, 1
348
.write_cache_more:
349
        cmp     dword [esi+8], 2        ; if cache slot is not different
350
        jne     .write_chain
351
        mov     dword [esi+8], 1        ; same as in hd
352
        mov     eax, [esi]
353
        mov     edx, [esi+4]            ; edx:eax = sector to write
3555 Serge 354
; Объединяем запись цепочки последовательных секторов в одно обращение к диску
2142 serge 355
        cmp     ecx, 1
356
        jz      .nonext
357
        cmp     dword [esi+12+8], 2
358
        jnz     .nonext
359
        push    eax edx
360
        add     eax, 1
361
        adc     edx, 0
362
        cmp     eax, [esi+12]
363
        jnz     @f
364
        cmp     edx, [esi+12+4]
365
@@:
366
        pop     edx eax
367
        jnz     .nonext
368
        cmp     [cache_chain_started], 1
369
        jz      @f
370
        mov     [cache_chain_started], 1
371
        mov     [cache_chain_size], 0
372
        mov     [cache_chain_pos], edi
373
        mov     [cache_chain_ptr], esi
374
@@:
375
        inc     [cache_chain_size]
376
        cmp     [cache_chain_size], 16
377
        jnz     .continue
378
        jmp     .write_chain
379
.nonext:
380
        call    .flush_cache_chain
381
        test    eax, eax
382
        jnz     .nothing
383
        mov     [cache_chain_size], 1
384
        mov     [cache_chain_ptr], esi
385
        call    .write_cache_sector
386
        test    eax, eax
387
        jnz     .nothing
388
        jmp     .continue
389
.write_chain:
390
        call    .flush_cache_chain
391
        test    eax, eax
392
        jnz     .nothing
393
.continue:
394
        add     esi, 12
395
        inc     edi
396
        dec     ecx
397
        jnz     .write_cache_more
398
        call    .flush_cache_chain
399
        test    eax, eax
400
        jnz     .nothing
401
.flush:
2987 Serge 402
        mov     esi, [disk]
2142 serge 403
        mov     al, DISKFUNC.flush
404
        call    disk_call_driver
405
.nothing:
406
        ret
407
 
408
.flush_cache_chain:
409
        xor     eax, eax
410
        cmp     [cache_chain_started], eax
411
        jz      @f
412
        call    .write_cache_chain
413
        mov     [cache_chain_started], 0
414
@@:
415
        retn
416
 
417
.write_cache_sector:
418
        mov     [cache_chain_size], 1
419
        mov     [cache_chain_pos], edi
420
.write_cache_chain:
421
        pusha
422
        mov     edi, [cache_chain_pos]
3232 Serge 423
        mov     ecx, [ebp-saved_esi_pos]
2142 serge 424
        shl     edi, 9
425
        add     edi, [ecx+DISKCACHE.data]
426
        mov     ecx, [cache_chain_size]
427
        push    ecx
428
        push    esp     ; numsectors
429
        mov     eax, [cache_chain_ptr]
430
        pushd   [eax+4]
431
        pushd   [eax]   ; startsector
432
        push    edi     ; buffer
433
        mov     esi, [ebp]
434
        mov     esi, [esi+PARTITION.Disk]
435
        mov     al, DISKFUNC.write
436
        call    disk_call_driver
437
        pop     ecx
438
        mov     [esp+28], eax
439
        popa
440
        retn
441
endp
442
 
443
; This internal function is called from disk_add to initialize the caching for
444
; a new DISK.
445
; The algorithm is inherited from getcache.inc: take 1/32 part of the available
446
; physical memory, round down to 8 pages, limit by 128K from below and by 1M
447
; from above. Reserve 1/8 part of the cache for system data and 7/8 for app
448
; data.
449
; After the size is calculated, but before the cache is allocated, the device
450
; driver can adjust the size. In particular, setting size to zero disables
451
; caching: there is no sense in a cache for a ramdisk. In fact, such action
452
; is most useful example of a non-trivial adjustment.
453
; esi = pointer to DISK structure
454
disk_init_cache:
455
; 1. Calculate the suggested cache size.
456
; 1a. Get the size of free physical memory in pages.
457
        mov     eax, [pg_data.pages_free]
458
; 1b. Use the value to calculate the size.
459
        shl     eax, 12 - 5     ; 1/32 of it in bytes
460
        and     eax, -8*4096    ; round down to the multiple of 8 pages
461
; 1c. Force lower and upper limits.
462
        cmp     eax, 1024*1024
463
        jb      @f
464
        mov     eax, 1024*1024
465
@@:
466
        cmp     eax, 128*1024
467
        ja      @f
468
        mov     eax, 128*1024
469
@@:
470
; 1d. Give a chance to the driver to adjust the size.
471
        push    eax
472
        mov     al, DISKFUNC.adjust_cache_size
473
        call    disk_call_driver
474
; Cache size calculated.
475
        mov     [esi+DISK.cache_size], eax
476
        test    eax, eax
477
        jz      .nocache
478
; 2. Allocate memory for the cache.
479
; 2a. Call the allocator.
480
        stdcall kernel_alloc, eax
481
        test    eax, eax
482
        jnz     @f
483
; 2b. If it failed, say a message and return with eax = 0.
484
        dbgstr 'no memory for disk cache'
485
        jmp     .nothing
486
@@:
487
; 3. Fill two DISKCACHE structures.
488
        mov     [esi+DISK.SysCache.pointer], eax
2434 Serge 489
        lea     ecx, [esi+DISK.SysCache.mutex]
2142 serge 490
        call    mutex_init
2434 Serge 491
        lea     ecx, [esi+DISK.AppCache.mutex]
2142 serge 492
        call    mutex_init
493
; The following code is inherited from getcache.inc.
494
        mov     edx, [esi+DISK.SysCache.pointer]
495
        and     [esi+DISK.SysCache.search_start], 0
496
        and     [esi+DISK.AppCache.search_start], 0
497
        mov     eax, [esi+DISK.cache_size]
498
        shr     eax, 3
499
        mov     [esi+DISK.SysCache.data_size], eax
500
        add     edx, eax
501
        imul    eax, 7
502
        mov     [esi+DISK.AppCache.data_size], eax
503
        mov     [esi+DISK.AppCache.pointer], edx
504
 
505
        mov     eax, [esi+DISK.SysCache.data_size]
506
        push    ebx
3232 Serge 507
        call    calculate_for_hd64
2142 serge 508
        pop     ebx
509
        add     eax, [esi+DISK.SysCache.pointer]
510
        mov     [esi+DISK.SysCache.data], eax
511
        mov     [esi+DISK.SysCache.sad_size], ecx
512
 
513
        push    edi
514
        mov     edi, [esi+DISK.SysCache.pointer]
515
        lea     ecx, [ecx*3]
516
        xor     eax, eax
3555 Serge 517
        rep stosd
2142 serge 518
        pop     edi
519
 
520
        mov     eax, [esi+DISK.AppCache.data_size]
521
        push    ebx
3232 Serge 522
        call    calculate_for_hd64
2142 serge 523
        pop     ebx
524
        add     eax, [esi+DISK.AppCache.pointer]
525
        mov     [esi+DISK.AppCache.data], eax
526
        mov     [esi+DISK.AppCache.sad_size], ecx
527
 
528
        push    edi
529
        mov     edi, [esi+DISK.AppCache.pointer]
530
        lea     ecx, [ecx*3]
531
        xor     eax, eax
3555 Serge 532
        rep stosd
2142 serge 533
        pop     edi
534
 
535
; 4. Return with nonzero al.
536
        mov     al, 1
537
; 5. Return.
538
.nothing:
539
        ret
540
; No caching is required for this driver. Zero cache pointers and return with
541
; nonzero al.
542
.nocache:
543
        mov     [esi+DISK.SysCache.pointer], eax
544
        mov     [esi+DISK.AppCache.pointer], eax
545
        mov     al, 1
546
        ret
547
 
3232 Serge 548
calculate_for_hd64:
549
        push    eax
550
        mov     ebx, eax
551
        shr     eax, 9
552
        lea     eax, [eax*3]
553
        shl     eax, 2
554
        sub     ebx, eax
555
        shr     ebx, 9
556
        mov     ecx, ebx
557
        shl     ebx, 9
558
        pop     eax
559
        sub     eax, ebx
560
        dec     ecx
561
        ret
562
 
563
 
2142 serge 564
; This internal function is called from disk_media_dereference to free the
565
; allocated cache, if there is one.
566
; esi = pointer to DISK structure
567
disk_free_cache:
568
; The algorithm is straightforward.
569
        mov     eax, [esi+DISK.SysCache.pointer]
570
        test    eax, eax
571
        jz      .nothing
572
        stdcall kernel_free, eax
573
.nothing:
574
        ret
2987 Serge 575
 
576
; This function flushes all modified data from both caches for the given DISK.
577
; esi = pointer to DISK
578
disk_sync:
579
; The algorithm is straightforward.
580
        push    esi
581
        push    esi     ; for second write_cache64
582
        push    esi     ; for first write_cache64
583
        add     esi, DISK.SysCache
584
        call    write_cache64
585
        add     esi, DISK.AppCache - DISK.SysCache
586
        call    write_cache64
587
        pop     esi
588
        ret