Subversion Repositories Kolibri OS

Rev

Rev 4429 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  2. ;;                                                              ;;
  3. ;; Copyright (C) KolibriOS team 2013-2014. All rights reserved. ;;
  4. ;; Distributed under terms of the GNU General Public License    ;;
  5. ;;                                                              ;;
  6. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  7.  
  8. $Revision: 4850 $
  9.  
  10. ; Memory management for USB structures.
  11. ; Protocol layer uses the common kernel heap malloc/free.
  12. ; Hardware layer has special requirements:
  13. ; * memory blocks should be properly aligned
  14. ; * memory blocks should not cross page boundary
  15. ; Hardware layer allocates fixed-size blocks.
  16. ; Thus, the specific allocator is quite easy to write:
  17. ; allocate one page, split into blocks, maintain the single-linked
  18. ; list of all free blocks in each page.
  19.  
  20. ; Note: size must be a multiple of required alignment.
  21.  
  22. ; Data for one pool: dd pointer to the first page, MUTEX lock.
  23.  
  24. ; Allocator for fixed-size blocks: allocate a block.
  25. ; [ebx-4] = pointer to the first page, ebx = pointer to MUTEX structure.
  26. proc usb_allocate_common
  27.         push    edi     ; save used register to be stdcall
  28. virtual at esp
  29.         dd      ?       ; saved edi
  30.         dd      ?       ; return address
  31. .size   dd      ?
  32. end virtual
  33. ; 1. Take the lock.
  34.         mov     ecx, ebx
  35.         call    mutex_lock
  36. ; 2. Find the first allocated page with a free block, if any.
  37. ; 2a. Initialize for the loop.
  38.         mov     edx, ebx
  39. .pageloop:
  40. ; 2b. Get the next page, keeping the current in eax.
  41.         mov     eax, edx
  42.         mov     edx, [edx-4]
  43. ; 2c. If there is no next page, we're out of luck; go to 4.
  44.         test    edx, edx
  45.         jz      .newpage
  46.         add     edx, 0x1000
  47. @@:
  48. ; 2d. Get the pointer to the first free block on this page.
  49. ; If there is no free block, continue to 2b.
  50.         mov     eax, [edx-8]
  51.         test    eax, eax
  52.         jz      .pageloop
  53. ; 2e. Get the pointer to the next free block.
  54.         mov     ecx, [eax]
  55. ; 2f. Update the pointer to the first free block from eax to ecx.
  56. ; Normally [edx-8] still contains eax, if so, atomically set it to ecx
  57. ; and proceed to 3.
  58. ; However, the price of simplicity of usb_free_common (in particular, it
  59. ; doesn't take the lock) is that [edx-8] could (rarely) be changed while
  60. ; we processed steps 2d+2e. If so, return to 2d and retry.
  61.         lock cmpxchg [edx-8], ecx
  62.         jnz     @b
  63. .return:
  64. ; 3. Release the lock taken in step 1 and return.
  65.         push    eax
  66.         mov     ecx, ebx
  67.         call    mutex_unlock
  68.         pop     eax
  69.         pop     edi     ; restore used register to be stdcall
  70.         ret     4
  71. .newpage:
  72. ; 4. Allocate a new page.
  73.         push    eax
  74.         stdcall kernel_alloc, 0x1000
  75.         pop     edx
  76. ; If failed, say something to the debug board and return zero.
  77.         test    eax, eax
  78.         jz      .nomemory
  79. ; 5. Add the new page to the tail of list of allocated pages.
  80.         mov     [edx-4], eax
  81. ; 6. Initialize two service dwords in the end of page:
  82. ; first free block is (start of page) + (block size)
  83. ; (we will return first block at (start of page), so consider it allocated),
  84. ; no next page.
  85.         mov     edx, eax
  86.         lea     edi, [eax+0x1000-8]
  87.         add     edx, [.size]
  88.         mov     [edi], edx
  89.         and     dword [edi+4], 0
  90. ; 7. All blocks starting from edx are free; join them in a single-linked list.
  91. @@:
  92.         mov     ecx, edx
  93.         add     edx, [.size]
  94.         mov     [ecx], edx
  95.         cmp     edx, edi
  96.         jbe     @b
  97.         sub     ecx, [.size]
  98.         and     dword [ecx], 0
  99. ; 8. Return (start of page).
  100.         jmp     .return
  101. .nomemory:
  102.         dbgstr 'no memory for USB descriptor'
  103.         xor     eax, eax
  104.         jmp     .return
  105. endp
  106.  
  107. ; Allocator for fixed-size blocks: free a block.
  108. proc usb_free_common
  109.         push    ecx edx
  110. virtual at esp
  111.         rd      2       ; saved registers
  112.         dd      ?       ; return address
  113. .block  dd      ?
  114. end virtual
  115. ; Insert the given block to the head of free blocks in this page.
  116.         mov     ecx, [.block]
  117.         mov     edx, ecx
  118.         or      edx, 0xFFF
  119. @@:
  120.         mov     eax, [edx+1-8]
  121.         mov     [ecx], eax
  122.         lock cmpxchg [edx+1-8], ecx
  123.         jnz     @b
  124.         pop     edx ecx
  125.         ret     4
  126. endp
  127.  
  128. ; Helper procedure: translate physical address in ecx
  129. ; of some transfer descriptor to linear address.
  130. ; in: eax = address of first page
  131. proc usb_td_to_virt
  132. ; Traverse all pages used for transfer descriptors, looking for the one
  133. ; with physical address as in ecx.
  134. @@:
  135.         test    eax, eax
  136.         jz      .zero
  137.         push    eax
  138.         call    get_pg_addr
  139.         sub     eax, ecx
  140.         jz      .found
  141.         cmp     eax, -0x1000
  142.         ja      .found
  143.         pop     eax
  144.         mov     eax, [eax+0x1000-4]
  145.         jmp     @b
  146. .found:
  147. ; When found, combine page address from eax with page offset from ecx.
  148.         pop     eax
  149.         and     ecx, 0xFFF
  150.         add     eax, ecx
  151. .zero:
  152.         ret
  153. endp
  154.