Subversion Repositories Kolibri OS

Rev

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

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