Subversion Repositories Kolibri OS

Rev

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

  1. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  2. ;;                                                              ;;
  3. ;; Copyright (C) KolibriOS team 2011-2012. All rights reserved. ;;
  4. ;; Distributed under terms of the GNU General Public License    ;;
  5. ;;                                                              ;;
  6. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  7.  
  8. $Revision: 3742 $
  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. ; Save ecx, set ecx to SysCache and let the common part do its work.
  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:
  30. ; Save ecx, set ecx to AppCache and let the common part do its work.
  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.
  48.         push    edx esi
  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
  60.         mov     esi, [ebp+PARTITION.Disk]
  61.         mov     al, DISKFUNC.read
  62.         call    disk_call_driver
  63.         pop     ecx
  64.         pop     esi edx
  65.         pop     ecx
  66.         ret
  67. .scancache:
  68. ; 4. Scan the cache.
  69.         push    edi ecx ; scan cache
  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]
  115.         mov     ecx, [.cache+16]
  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
  147.         rep movsd               ; move data
  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:
  165. ; Save ecx, set ecx to SysCache and let the common part do its work.
  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:
  178. ; Save ecx, set ecx to AppCache and let the common part do its work.
  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. @@:
  195.         push    edx esi
  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
  208.         mov     esi, [ebp+PARTITION.Disk]
  209.         mov     al, DISKFUNC.write
  210.         call    disk_call_driver
  211.         pop     ecx
  212.         pop     esi edx
  213.         pop     ecx
  214.         ret
  215. .scancache:
  216. ; 4. Scan the cache.
  217.         push    edi ecx ; scan cache
  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.  
  274.         mov     dword [esi+8], 2        ; write - differs from hd
  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
  282.         rep movsd               ; move data
  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
  317.         stdcall write_cache64, [ebp+PARTITION.Disk] ; no empty slots found, write all
  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.
  328. proc write_cache64 uses ecx edx esi edi, disk:dword
  329. locals
  330. cache_chain_started     dd      0
  331. cache_chain_size        dd      ?
  332. cache_chain_pos         dd      ?
  333. cache_chain_ptr         dd      ?
  334. endl
  335. saved_esi_pos = 16+12 ; size of local variables + size of registers before esi
  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
  354. ; Объединяем запись цепочки последовательных секторов в одно обращение к диску
  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:
  402.         mov     esi, [disk]
  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]
  423.         mov     ecx, [ebp-saved_esi_pos]
  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
  489.         lea     ecx, [esi+DISK.SysCache.mutex]
  490.         call    mutex_init
  491.         lea     ecx, [esi+DISK.AppCache.mutex]
  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
  507.         call    calculate_for_hd64
  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
  517.         rep stosd
  518.         pop     edi
  519.  
  520.         mov     eax, [esi+DISK.AppCache.data_size]
  521.         push    ebx
  522.         call    calculate_for_hd64
  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
  532.         rep stosd
  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.  
  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.  
  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
  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
  589.