Subversion Repositories Kolibri OS

Rev

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

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