diff --git a/kernel/branches/Kolibri-acpi/fs/disk.inc b/kernel/branches/Kolibri-acpi/blkdev/disk.inc similarity index 92% rename from kernel/branches/Kolibri-acpi/fs/disk.inc rename to kernel/branches/Kolibri-acpi/blkdev/disk.inc index 3dce6e3dc4..a8b6ce1b39 100644 --- a/kernel/branches/Kolibri-acpi/fs/disk.inc +++ b/kernel/branches/Kolibri-acpi/blkdev/disk.inc @@ -5,6 +5,8 @@ ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +$Revision: 2140 $ + ; ============================================================================= ; ================================= Constants ================================= ; ============================================================================= @@ -76,6 +78,11 @@ struct DISKFUNC ; Note that read/write are called by the cache manager, so a driver should not ; create a software cache. This function is implemented for flushing a hardware ; cache, if it exists. +.adjust_cache_size dd ? +; The pointer to the function which returns the cache size for this device. +; Optional, may be NULL. +; unsigned int adjust_cache_size(unsigned int suggested_size); +; Return value: 0 = disable cache, otherwise = used cache size in bytes. ends ; This structure holds an information about a media. @@ -90,6 +97,20 @@ struct DISKMEDIAINFO ; Size of the media in sectors. ends +; This structure represents disk cache. To follow the old implementation, +; there are two distinct caches for a disk, one for "system" data, other +; for "application" data. +struct DISKCACHE +.Lock MUTEX +; Lock to protect the cache. +; The following fields are inherited from data32.inc:cache_ideX. +.pointer rd 1 +.data_size rd 1 ; not use +.data rd 1 +.sad_size rd 1 +.search_start rd 1 +ends + ; This structure represents a disk device and its media for the kernel. ; This structure is allocated by the kernel in the 'disk_add' function, ; freed in the 'disk_dereference' function. @@ -146,6 +167,11 @@ struct DISK ; Number of partitions on this media. .Partitions dd ? ; Pointer to array of .NumPartitions pointers to PARTITION structures. +.cache_size dd ? +; inherited from cache_ideX_size +.SysCache DISKCACHE +.AppCache DISKCACHE +; Two caches for the disk. ends ; This structure represents one partition for the kernel. This is a base @@ -156,6 +182,8 @@ struct PARTITION ; First sector of the partition. .Length dq ? ; Length of the partition in sectors. +.Disk dd ? +; Pointer to parent DISK structure. .FSUserFunctions dd ? ; Handlers for the sysfunction 70h. This field is a pointer to the following ; array. The first dword is a number of supported subfunctions, other dwords @@ -242,12 +270,13 @@ disk_list_mutex MUTEX endg iglobal -; The function 'disk_scan_partitions' needs two 512-byte buffers for -; MBR and bootsectors data. It can not use the static buffers always, -; since it can be called for two or more disks in parallel. However, this -; case is not typical. We reserve two static 512-byte buffers and a flag -; that these buffers are currently used. If 'disk_scan_partitions' detects that -; the buffers are currently used, it allocates buffers from the heap. +; The function 'disk_scan_partitions' needs three 512-byte buffers for +; MBR, bootsector and fs-temporary sector data. It can not use the static +; buffers always, since it can be called for two or more disks in parallel. +; However, this case is not typical. We reserve three static 512-byte buffers +; and a flag that these buffers are currently used. If 'disk_scan_partitions' +; detects that the buffers are currently used, it allocates buffers from the +; heap. ; The flag is implemented as a global dword variable. When the static buffers ; are not used, the value is -1. When the static buffers are used, the value ; is normally 0 and temporarily can become greater. The function increments @@ -258,10 +287,11 @@ iglobal partition_buffer_users dd -1 endg uglobal -; The static buffers for MBR and bootsectors data. +; The static buffers for MBR, bootsector and fs-temporary sector data. align 16 mbr_buffer rb 512 bootsect_buffer rb 512 +fs_tmp_buffer rb 512 endg iglobal @@ -276,6 +306,7 @@ disk_default_callbacks: dd disk_default_read dd disk_default_write dd disk_default_flush + dd disk_default_adjust_cache_size endg ; ============================================================================= @@ -312,45 +343,44 @@ disk_add: jz .nothing ; 2. Copy disk name to the DISK structure. ; 2a. Get length of the name, including the terminating zero. - mov esi, [esp+8+8] ; esi = pointer to name + mov ebx, [esp+8+8] ; ebx = pointer to name push eax ; save allocated pointer to DISK xor eax, eax ; the argument of malloc() is in eax @@: inc eax - cmp byte [esi+eax-1], 0 + cmp byte [ebx+eax-1], 0 jnz @b ; 2b. Call the heap manager. call malloc ; 2c. Check the result. If allocation failed, go to 7. - pop ebx ; restore allocated pointer to DISK + pop esi ; restore allocated pointer to DISK test eax, eax jz .free ; 2d. Store the allocated pointer to the DISK structure. - mov [ebx+DISK.Name], eax + mov [esi+DISK.Name], eax ; 2e. Copy the name. @@: - mov dl, [esi] + mov dl, [ebx] mov [eax], dl - inc esi + inc ebx inc eax test dl, dl jnz @b ; 3. Copy other arguments of the function to the DISK structure. mov eax, [esp+4+8] - mov [ebx+DISK.Functions], eax + mov [esi+DISK.Functions], eax mov eax, [esp+12+8] - mov [ebx+DISK.UserData], eax + mov [esi+DISK.UserData], eax mov eax, [esp+16+8] - mov [ebx+DISK.DriverFlags], eax + mov [esi+DISK.DriverFlags], eax ; 4. Initialize other fields of the DISK structure. -; Media is not inserted, initialized state of mutex is zero, -; reference counter is 1. - lea ecx, [ebx+DISK.MediaLock] +; Media is not inserted, reference counter is 1. + lea ecx, [esi+DISK.MediaLock] call mutex_init xor eax, eax - mov dword [ebx+DISK.MediaInserted], eax + mov dword [esi+DISK.MediaInserted], eax inc eax - mov [ebx+DISK.RefCount], eax + mov [esi+DISK.RefCount], eax ; The DISK structure is initialized. ; 5. Insert the new structure to the global list. ; 5a. Acquire the mutex. @@ -358,16 +388,16 @@ disk_add: call mutex_lock ; 5b. Insert item to the tail of double-linked list. mov edx, disk_list - list_add_tail ebx, edx ;ebx= new edx= list head + list_add_tail esi, edx ;esi= new edx= list head ; 5c. Release the mutex. - call mutex_unlock + call mutex_unlock ; 6. Return with eax = pointer to DISK. - xchg eax, ebx + xchg eax, esi jmp .nothing .free: ; Memory allocation for DISK structure succeeded, but for disk name failed. ; 7. Free the DISK structure. - xchg eax, ebx + xchg eax, esi call free ; 8. Return with eax = 0. xor eax, eax @@ -474,10 +504,12 @@ lock dec [esi+DISK.MediaRefCount] jnz .freeloop .nofree: pop edi esi -; 3b. Call the driver. +; 3b. Free the cache. + call disk_free_cache +; 3c. Call the driver. mov al, DISKFUNC.closemedia stdcall disk_call_driver -; 3c. Clear the flag. +; 3d. Clear the flag. mov [esi+DISK.MediaUsed], 0 .nothing: ret @@ -525,12 +557,16 @@ disk_media_changed: ; 3b. Check the result of the callback. Abort if it failed. test eax, eax jnz .noinsert -; 3c. Acquire the lifetime reference for the media object. +; 3c. Allocate the cache unless disabled by the driver. Abort if failed. + call disk_init_cache + test al, al + jz .noinsert +; 3d. Acquire the lifetime reference for the media object. inc [esi+DISK.MediaRefCount] -; 3d. Scan for partitions. Ignore result; the list of partitions is valid even +; 3e. Scan for partitions. Ignore result; the list of partitions is valid even ; on errors. call disk_scan_partitions -; 3e. Media is inserted and available for use. +; 3f. Media is inserted and available for use. inc [esi+DISK.MediaInserted] .noinsert: ; 4. Return. @@ -586,6 +622,11 @@ disk_default_flush: xor eax, eax ret 4 +; The default implementation of DISKFUNC.adjust_cache_size. +disk_default_adjust_cache_size: + mov eax, [esp+4] + ret 4 + ; This is an internal function called from 'disk_media_changed' when new media ; is detected. It creates the list of partitions for the media. ; If media is not partitioned, then the list consists of one partition which @@ -609,7 +650,7 @@ disk_scan_partitions: lock inc [partition_buffer_users] jz .buffer_acquired ; yes, it is free lock dec [partition_buffer_users] ; no, we must allocate - stdcall kernel_alloc, 1024 + stdcall kernel_alloc, 512*3 test eax, eax jz .nothing xchg eax, ebx @@ -712,6 +753,7 @@ lock dec [partition_buffer_users] ; no, we must allocate ; compatibility problems. pop eax ; load extended partition add ebp, eax + jc .mbr_failed ; 12c. If extended partition has not yet started, start it. test eax, eax jnz @f @@ -849,7 +891,7 @@ proc disk_add_partition stdcall uses ebx edi, start:qword, length:qword ; The range [.FirstSector, .FirstSector+.Length) must be either entirely to ; the left of [start, start+length) or entirely to the right. ; 2c. Subtract .FirstSector - start. The possible overflow distinguish between -; cases "to the left" (2?) and "to the right" (2d). +; cases "to the left" (2e) and "to the right" (2d). mov eax, dword [ecx+PARTITION.FirstSector] mov edx, dword [ecx+PARTITION.FirstSector+4] sub eax, dword [start] diff --git a/kernel/branches/Kolibri-acpi/blkdev/disk_cache.inc b/kernel/branches/Kolibri-acpi/blkdev/disk_cache.inc new file mode 100644 index 0000000000..b3d0140582 --- /dev/null +++ b/kernel/branches/Kolibri-acpi/blkdev/disk_cache.inc @@ -0,0 +1,592 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision: 2140 $ + +; This function is intended to replace the old 'hd_read' function when +; [hdd_appl_data] = 0, so its input/output parameters are the same, except +; that it can't use the global variables 'hd_error' and 'hdd_appl_data'. +; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure +; eax is relative to partition start +; out: eax = error code; 0 = ok +fs_read32_sys: +; Compatibility hack: if PARTITION.Disk is 'old', there is no DISK structure, +; this request should be processed by hd_read. + cmp [ebp+PARTITION.Disk], 'old' + jnz @f + mov [hdd_appl_data], 0 + call hd_read + mov [hdd_appl_data], 1 ; restore to default state + ret +@@: +; In the normal case, save ecx, set ecx to SysCache and let the common part +; do its work. + push ecx + mov ecx, [ebp+PARTITION.Disk] + add ecx, DISK.SysCache + jmp fs_read32_common + +; This function is intended to replace the old 'hd_read' function when +; [hdd_appl_data] = 1, so its input/output parameters are the same, except +; that it can't use the global variables 'hd_error' and 'hdd_appl_data'. +; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure +; eax is relative to partition start +; out: eax = error code; 0 = ok +fs_read32_app: +; Compatibility hack: if PARTITION.Disk is 'old', there is no DISK structure, +; this request should be processed by hd_read. + cmp [ebp+PARTITION.Disk], 'old' + jnz @f + mov [hdd_appl_data], 1 + jmp hd_read +@@: +; In the normal case, save ecx, set ecx to AppCache and let the common part +; do its work. + push ecx + mov ecx, [ebp+PARTITION.Disk] + add ecx, DISK.AppCache + +; This label is the common part of fs_read32_sys and fs_read32_app. +fs_read32_common: +; 1. Check that the required sector is inside the partition. If no, return +; DISK_STATUS_END_OF_MEDIA. + cmp dword [ebp+PARTITION.Length+4], 0 + jnz @f + cmp dword [ebp+PARTITION.Length], eax + ja @f + mov eax, DISK_STATUS_END_OF_MEDIA + pop ecx + ret +@@: +; 2. Get the absolute sector on the disk. + push edx + xor edx, edx + add eax, dword [ebp+PARTITION.FirstSector] + adc edx, dword [ebp+PARTITION.FirstSector+4] +; 3. If there is no cache for this disk, just pass the request to the driver. + cmp [ecx+DISKCACHE.pointer], 0 + jnz .scancache + push 1 + push esp ; numsectors + push edx ; startsector + push eax ; startsector + push ebx ; buffer + mov al, DISKFUNC.read + call disk_call_driver + pop ecx + pop edx + pop ecx + ret +.scancache: +; 4. Scan the cache. + push esi edi ecx ; scan cache + push edx eax +virtual at esp +.sector_lo dd ? +.sector_hi dd ? +.cache dd ? +end virtual +; The following code is inherited from hd_read. The differences are: +; all code is protected by the cache lock; instead of static calls +; to hd_read_dma/hd_read_pio/bd_read the dynamic call to DISKFUNC.read is used; +; sector is 64-bit, not 32-bit. + call mutex_lock + mov eax, [.sector_lo] + mov edx, [.sector_hi] + mov esi, [ecx+DISKCACHE.pointer] + mov ecx, [ecx+DISKCACHE.sad_size] + add esi, 12 + + mov edi, 1 + +.hdreadcache: + + cmp dword [esi+8], 0 ; empty + je .nohdcache + + cmp [esi], eax ; correct sector + jne .nohdcache + cmp [esi+4], edx ; correct sector + je .yeshdcache + +.nohdcache: + + add esi, 12 + inc edi + dec ecx + jnz .hdreadcache + + mov esi, [.cache] + call find_empty_slot64 ; ret in edi + test eax, eax + jnz .read_done + + push 1 + push esp + push edx + push [.sector_lo+12] + mov ecx, [.cache] + mov eax, edi + shl eax, 9 + add eax, [ecx+DISKCACHE.data] + push eax + mov esi, [ebp+PARTITION.Disk] + mov al, DISKFUNC.read + call disk_call_driver + pop ecx + dec ecx + jnz .read_done + + mov ecx, [.cache] + lea eax, [edi*3] + mov esi, [ecx+DISKCACHE.pointer] + lea esi, [eax*4+esi] + + mov eax, [.sector_lo] + mov edx, [.sector_hi] + mov [esi], eax ; sector number + mov [esi+4], edx ; sector number + mov dword [esi+8], 1; hd read - mark as same as in hd + +.yeshdcache: + + mov esi, edi + mov ecx, [.cache] + shl esi, 9 + add esi, [ecx+DISKCACHE.data] + + mov edi, ebx + mov ecx, 512/4 + rep movsd ; move data + xor eax, eax ; successful read +.read_done: + mov ecx, [.cache] + push eax + call mutex_unlock + pop eax + add esp, 12 + pop edi esi edx ecx + ret + +; This function is intended to replace the old 'hd_write' function when +; [hdd_appl_data] = 0, so its input/output parameters are the same, except +; that it can't use the global variables 'hd_error' and 'hdd_appl_data'. +; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure +; eax is relative to partition start +; out: eax = error code; 0 = ok +fs_write32_sys: +; Compatibility hack: if PARTITION.Disk is 'old', there is no DISK structure, +; this request should be processed by hd_write. + cmp [ebp+PARTITION.Disk], 'old' + jnz @f + mov [hdd_appl_data], 0 + call hd_write + mov [hdd_appl_data], 1 ; restore to default state + ret +@@: +; In the normal case, save ecx, set ecx to SysCache and let the common part +; do its work. + push ecx + mov ecx, [ebp+PARTITION.Disk] + add ecx, DISK.SysCache + jmp fs_write32_common + +; This function is intended to replace the old 'hd_write' function when +; [hdd_appl_data] = 1, so its input/output parameters are the same, except +; that it can't use the global variables 'hd_error' and 'hdd_appl_data'. +; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure +; eax is relative to partition start +; out: eax = error code; 0 = ok +fs_write32_app: +; Compatibility hack: if PARTITION.Disk is 'old', there is no DISK structure, +; this request should be processed by hd_write. + cmp [ebp+PARTITION.Disk], 'old' + jnz @f + mov [hdd_appl_data], 1 + jmp hd_write +@@: +; In the normal case, save ecx, set ecx to AppCache and let the common part +; do its work. + push ecx + mov ecx, [ebp+PARTITION.Disk] + add ecx, DISK.AppCache + +; This label is the common part of fs_read32_sys and fs_read32_app. +fs_write32_common: +; 1. Check that the required sector is inside the partition. If no, return +; DISK_STATUS_END_OF_MEDIA. + cmp dword [ebp+PARTITION.Length+4], 0 + jnz @f + cmp dword [ebp+PARTITION.Length], eax + ja @f + mov eax, DISK_STATUS_END_OF_MEDIA + pop ecx + ret +@@: + push edx +; 2. Get the absolute sector on the disk. + xor edx, edx + add eax, dword [ebp+PARTITION.FirstSector] + adc edx, dword [ebp+PARTITION.FirstSector+4] +; 3. If there is no cache for this disk, just pass request to the driver. + cmp [ecx+DISKCACHE.pointer], 0 + jnz .scancache + push 1 + push esp ; numsectors + push edx ; startsector + push eax ; startsector + push ebx ; buffer + mov al, DISKFUNC.write + call disk_call_driver + pop ecx + pop edx + pop ecx + ret +.scancache: +; 4. Scan the cache. + push esi edi ecx ; scan cache + push edx eax +virtual at esp +.sector_lo dd ? +.sector_hi dd ? +.cache dd ? +end virtual +; The following code is inherited from hd_write. The differences are: +; all code is protected by the cache lock; +; sector is 64-bit, not 32-bit. + call mutex_lock + + ; check if the cache already has the sector and overwrite it + mov eax, [.sector_lo] + mov edx, [.sector_hi] + mov esi, [ecx+DISKCACHE.pointer] + mov ecx, [ecx+DISKCACHE.sad_size] + add esi, 12 + + mov edi, 1 + +.hdwritecache: + cmp dword [esi+8], 0 ; if cache slot is empty + je .not_in_cache_write + + cmp [esi], eax ; if the slot has the sector + jne .not_in_cache_write + cmp [esi+4], edx ; if the slot has the sector + je .yes_in_cache_write + +.not_in_cache_write: + + add esi, 12 + inc edi + dec ecx + jnz .hdwritecache + + ; sector not found in cache + ; write the block to a new location + + mov esi, [.cache] + call find_empty_slot64 ; ret in edi + test eax, eax + jne .hd_write_access_denied + + mov ecx, [.cache] + lea eax, [edi*3] + mov esi, [ecx+DISKCACHE.pointer] + lea esi, [eax*4+esi] + + mov eax, [.sector_lo] + mov edx, [.sector_hi] + mov [esi], eax ; sector number + mov [esi+4], edx ; sector number + +.yes_in_cache_write: + + mov dword [esi+4], 2 ; write - differs from hd + + shl edi, 9 + mov ecx, [.cache] + add edi, [ecx+DISKCACHE.data] + + mov esi, ebx + mov ecx, 512/4 + rep movsd ; move data + xor eax, eax ; success +.hd_write_access_denied: + mov ecx, [.cache] + push eax + call mutex_unlock + pop eax + add esp, 12 + pop edi esi edx ecx + ret + +; This internal function is called from fs_read32_* and fs_write32_*. It is the +; analogue of find_empty_slot for 64-bit sectors. +find_empty_slot64: +;----------------------------------------------------------- +; find empty or read slot, flush cache if next 12.5% is used by write +; output : edi = cache slot +;----------------------------------------------------------- +.search_again: + mov ecx, [esi+DISKCACHE.sad_size] + mov edi, [esi+DISKCACHE.search_start] + shr ecx, 3 +.search_for_empty: + inc edi + cmp edi, [esi+DISKCACHE.sad_size] + jbe .inside_cache + mov edi, 1 +.inside_cache: + lea eax, [edi*3] + shl eax, 2 + add eax, [esi+DISKCACHE.pointer] + cmp dword [eax+8], 2 + jb .found_slot ; it's empty or read + dec ecx + jnz .search_for_empty + call write_cache64 ; no empty slots found, write all + test eax, eax + jne .found_slot_access_denied + jmp .search_again ; and start again +.found_slot: + mov [esi+DISKCACHE.search_start], edi + xor eax, eax ; success +.found_slot_access_denied: + ret + +; This function is intended to replace the old 'write_cache' function. +proc write_cache64 uses ecx edx esi edi +locals +cache_chain_started dd ? +cache_chain_size dd ? +cache_chain_pos dd ? +cache_chain_ptr dd ? +endl +; If there is no cache for this disk, nothing to do. + cmp [esi+DISKCACHE.pointer], 0 + jz .flush +;----------------------------------------------------------- +; write all changed sectors to disk +;----------------------------------------------------------- + + ; write difference ( 2 ) from cache to DISK + mov ecx, [esi+DISKCACHE.sad_size] + mov esi, [esi+DISKCACHE.pointer] + add esi, 12 + mov edi, 1 +.write_cache_more: + cmp dword [esi+8], 2 ; if cache slot is not different + jne .write_chain + mov dword [esi+8], 1 ; same as in hd + mov eax, [esi] + mov edx, [esi+4] ; edx:eax = sector to write +; Объединяем запись цепочки последовательных секторов в одно обращение к диску + cmp ecx, 1 + jz .nonext + cmp dword [esi+12+8], 2 + jnz .nonext + push eax edx + add eax, 1 + adc edx, 0 + cmp eax, [esi+12] + jnz @f + cmp edx, [esi+12+4] +@@: + pop edx eax + jnz .nonext + cmp [cache_chain_started], 1 + jz @f + mov [cache_chain_started], 1 + mov [cache_chain_size], 0 + mov [cache_chain_pos], edi + mov [cache_chain_ptr], esi +@@: + inc [cache_chain_size] + cmp [cache_chain_size], 16 + jnz .continue + jmp .write_chain +.nonext: + call .flush_cache_chain + test eax, eax + jnz .nothing + mov [cache_chain_size], 1 + mov [cache_chain_ptr], esi + call .write_cache_sector + test eax, eax + jnz .nothing + jmp .continue +.write_chain: + call .flush_cache_chain + test eax, eax + jnz .nothing +.continue: + add esi, 12 + inc edi + dec ecx + jnz .write_cache_more + call .flush_cache_chain + test eax, eax + jnz .nothing +.flush: + mov esi, [ebp] + mov esi, [esi+PARTITION.Disk] + mov al, DISKFUNC.flush + call disk_call_driver +.nothing: + ret + +.flush_cache_chain: + xor eax, eax + cmp [cache_chain_started], eax + jz @f + call .write_cache_chain + mov [cache_chain_started], 0 +@@: + retn + +.write_cache_sector: + mov [cache_chain_size], 1 + mov [cache_chain_pos], edi +.write_cache_chain: + pusha + mov edi, [cache_chain_pos] + mov ecx, [ebp-12] + shl edi, 9 + add edi, [ecx+DISKCACHE.data] + mov ecx, [cache_chain_size] + push ecx + push esp ; numsectors + mov eax, [cache_chain_ptr] + pushd [eax+4] + pushd [eax] ; startsector + push edi ; buffer + mov esi, [ebp] + mov esi, [esi+PARTITION.Disk] + mov al, DISKFUNC.write + call disk_call_driver + pop ecx + mov [esp+28], eax + popa + retn +endp + +; This internal function is called from disk_add to initialize the caching for +; a new DISK. +; The algorithm is inherited from getcache.inc: take 1/32 part of the available +; physical memory, round down to 8 pages, limit by 128K from below and by 1M +; from above. Reserve 1/8 part of the cache for system data and 7/8 for app +; data. +; After the size is calculated, but before the cache is allocated, the device +; driver can adjust the size. In particular, setting size to zero disables +; caching: there is no sense in a cache for a ramdisk. In fact, such action +; is most useful example of a non-trivial adjustment. +; esi = pointer to DISK structure +disk_init_cache: +; 1. Calculate the suggested cache size. +; 1a. Get the size of free physical memory in pages. + mov eax, [pg_data.pages_free] +; 1b. Use the value to calculate the size. + shl eax, 12 - 5 ; 1/32 of it in bytes + and eax, -8*4096 ; round down to the multiple of 8 pages +; 1c. Force lower and upper limits. + cmp eax, 1024*1024 + jb @f + mov eax, 1024*1024 +@@: + cmp eax, 128*1024 + ja @f + mov eax, 128*1024 +@@: +; 1d. Give a chance to the driver to adjust the size. + push eax + mov al, DISKFUNC.adjust_cache_size + call disk_call_driver +; Cache size calculated. + mov [esi+DISK.cache_size], eax + test eax, eax + jz .nocache +; 2. Allocate memory for the cache. +; 2a. Call the allocator. + stdcall kernel_alloc, eax + test eax, eax + jnz @f +; 2b. If it failed, say a message and return with eax = 0. + dbgstr 'no memory for disk cache' + jmp .nothing +@@: +; 3. Fill two DISKCACHE structures. + mov [esi+DISK.SysCache.pointer], eax + lea ecx, [esi+DISK.SysCache.Lock] + call mutex_init + lea ecx, [esi+DISK.AppCache.Lock] + call mutex_init +; The following code is inherited from getcache.inc. + mov edx, [esi+DISK.SysCache.pointer] + and [esi+DISK.SysCache.search_start], 0 + and [esi+DISK.AppCache.search_start], 0 + mov eax, [esi+DISK.cache_size] + shr eax, 3 + mov [esi+DISK.SysCache.data_size], eax + add edx, eax + imul eax, 7 + mov [esi+DISK.AppCache.data_size], eax + mov [esi+DISK.AppCache.pointer], edx + + mov eax, [esi+DISK.SysCache.data_size] + push ebx + call calculate_for_hd + pop ebx + add eax, [esi+DISK.SysCache.pointer] + mov [esi+DISK.SysCache.data], eax + mov [esi+DISK.SysCache.sad_size], ecx + + push edi + mov edi, [esi+DISK.SysCache.pointer] + lea ecx, [ecx*3] + xor eax, eax + rep stosd + pop edi + + mov eax, [esi+DISK.AppCache.data_size] + push ebx + call calculate_for_hd + pop ebx + add eax, [esi+DISK.AppCache.pointer] + mov [esi+DISK.AppCache.data], eax + mov [esi+DISK.AppCache.sad_size], ecx + + push edi + mov edi, [esi+DISK.AppCache.pointer] + lea ecx, [ecx*3] + xor eax, eax + rep stosd + pop edi + +; 4. Return with nonzero al. + mov al, 1 +; 5. Return. +.nothing: + ret +; No caching is required for this driver. Zero cache pointers and return with +; nonzero al. +.nocache: + mov [esi+DISK.SysCache.pointer], eax + mov [esi+DISK.AppCache.pointer], eax + mov al, 1 + ret + +; This internal function is called from disk_media_dereference to free the +; allocated cache, if there is one. +; esi = pointer to DISK structure +disk_free_cache: +; The algorithm is straightforward. + mov eax, [esi+DISK.SysCache.pointer] + test eax, eax + jz .nothing + stdcall kernel_free, eax +.nothing: + ret diff --git a/kernel/branches/Kolibri-acpi/boot/boot_fat12.asm b/kernel/branches/Kolibri-acpi/boot/boot_fat12.asm new file mode 100644 index 0000000000..4d6219f41e --- /dev/null +++ b/kernel/branches/Kolibri-acpi/boot/boot_fat12.asm @@ -0,0 +1,302 @@ +; FAT12 boot sector for Kolibri OS +; +; Copyright (C) Alex Nogueira Teixeira +; Copyright (C) Diamond +; Copyright (C) Dmitry Kartashov aka shurf +; +; Distributed under GPL, see file COPYING for details +; +; Version 1.0 + +lf equ 0ah +cr equ 0dh + +pos_read_tmp equ 0700h ;position for temporary read +boot_program equ 07c00h ;position for boot code +seg_read_kernel equ 01000h ;segment to kernel read + + jmp start_program + nop + +; Boot Sector and BPB Structure + BS_OEMName db 'KOLIBRI ' ; db 8 + BPB_BytsPerSec dw 512 ; bytes per sector + BPB_SecPerClus db 1 ; sectors per cluster + BPB_RsvdSecCnt dw 1 ; number of reserver sectors + BPB_NumFATs db 2 ; count of FAT data structures + BPB_RootEntCnt dw 224 ; count of 32-byte dir. entries (224*32 = 14 sectors) + BPB_TotSec16 dw 2880 ; count of sectors on the volume (2880 for 1.44 mbytes disk) + BPB_Media db 0f0h ; f0 - used for removable media + BPB_FATSz16 dw 9 ; count of sectors by one copy of FAT + BPB_SecPerTrk dw 18 ; sectors per track + BPB_NumHeads dw 2 ; number of heads + BPB_HiddSec dd 0 ; count of hidden sectors + BPB_TotSec32 dd 0 ; count of sectors on the volume (if > 65535) + BS_DrvNum db 0 ; int 13h drive number + BS_Reserved db 0 ; reserved + BS_BootSig db 29h ; Extended boot signature + BS_VolID dd 0 ; Volume serial number + BS_VolLab db 'KOLIBRI ' ; Volume label (db 11) + BS_FilSysType db 'FAT12 ' ; file system type (db 8) + +start_program: + + xor ax,ax + mov ss,ax + mov sp,boot_program + push ss + pop ds + + ; print loading string + mov si,loading+boot_program +loop_loading: + lodsb + or al,al + jz read_root_directory + mov ah,0eh + mov bx,7 + int 10h + jmp loop_loading + +read_root_directory: + push ss + pop es + + ; calculate some disk parameters + ; - beginning sector of RootDir + mov ax,word [BPB_FATSz16+boot_program] + xor cx,cx + mov cl,byte [BPB_NumFATs+boot_program] + mul cx + add ax,word [BPB_RsvdSecCnt+boot_program] + mov word [FirstRootDirSecNum+boot_program],ax ; 19 + mov si,ax + + ; - count of sectors in RootDir + mov bx,word [BPB_BytsPerSec+boot_program] + mov cl,5 ; divide ax by 32 + shr bx,cl ; bx = directory entries per sector + mov ax,word [BPB_RootEntCnt+boot_program] + xor dx,dx + div bx + mov word [RootDirSecs+boot_program],ax ; 14 + + ; - data start + add si,ax ; add beginning sector of RootDir and count sectors in RootDir + mov word [data_start+boot_program],si ; 33 + ; reading root directory + ; al=count root dir sectrors !!!! TODO: al, max 255 sectors !!!! + mov ah,2 ; read + push ax + + mov ax,word [FirstRootDirSecNum+boot_program] + call conv_abs_to_THS ; convert abs sector (AX) to BIOS T:H:S (track:head:sector) + pop ax + mov bx,pos_read_tmp ; es:bx read buffer + call read_sector + + mov si,bx ; read buffer address: es:si + mov ax,[RootDirSecs+boot_program] + mul word [BPB_BytsPerSec+boot_program] + add ax,si ; AX = end of root dir. in buffer pos_read_tmp + + ; find kernel file in root directory +loop_find_dir_entry: + push si + mov cx,11 + mov di,kernel_name+boot_program + rep cmpsb ; compare es:si and es:di, cx bytes long + pop si + je found_kernel_file + add si,32 ; next dir. entry + cmp si,ax ; end of directory + jb loop_find_dir_entry + +file_error_message: + mov si,error_message+boot_program + +loop_error_message: + lodsb + or al,al + jz freeze_pc + mov ah,0eh + mov bx,7 + int 10h + jmp loop_error_message + +freeze_pc: + jmp $ ; endless loop + + ; === KERNEL FOUND. LOADING... === + +found_kernel_file: + mov bp,[si+01ah] ; first cluster of kernel file + ; + mov [cluster1st+boot_program],bp ; starting cluster of kernel file + ; <\diamond> + + ; reading first FAT table + mov ax,word [BPB_RsvdSecCnt+boot_program] ; begin first FAT abs sector number + call conv_abs_to_THS ; convert abs sector (AX) to BIOS T:H:S (track:head:sector) + mov bx,pos_read_tmp ; es:bx read position + mov ah,2 ; ah=2 (read) + mov al, byte [BPB_FATSz16+boot_program] ; FAT size in sectors (TODO: max 255 sectors) + call read_sector + jc file_error_message ; read error + + mov ax,seg_read_kernel + mov es,ax + xor bx,bx ; es:bx = 1000h:0000h + + + ; reading kernel file +loop_obtains_kernel_data: + ; read one cluster of file + call obtain_cluster + jc file_error_message ; read error + + ; add one cluster length to segment:offset + push bx + mov bx,es + mov ax,word [BPB_BytsPerSec+boot_program] ;\ + movsx cx,byte [BPB_SecPerClus+boot_program] ; | !!! TODO: !!! + mul cx ; | out this from loop !!! + shr ax,4 ;/ + add bx,ax + mov es,bx + pop bx + + mov di,bp + shr di,1 + pushf + add di,bp ; di = bp * 1.5 + add di,pos_read_tmp + mov ax,[di] ; read next entry from FAT-chain + popf + jc move_4_right + and ax,0fffh + jmp verify_end_sector +move_4_right: + mov cl,4 + shr ax,cl +verify_end_sector: + cmp ax,0ff8h ; last cluster + jae execute_kernel + mov bp,ax + jmp loop_obtains_kernel_data + +execute_kernel: + ; + mov ax,'KL' + push 0 + pop ds + mov si,loader_block+boot_program + ; + push word seg_read_kernel + push word 0 + retf ; jmp far 1000:0000 + + +;------------------------------------------ + ; loading cluster from file to es:bx +obtain_cluster: + ; bp - cluster number to read + ; carry = 0 -> read OK + ; carry = 1 -> read ERROR + + ; print one dot + push bx + mov ax,0e2eh ; ah=0eh (teletype), al='.' + xor bh,bh + int 10h + pop bx + +writesec: + ; convert cluster number to sector number + mov ax,bp ; data cluster to read + sub ax,2 + xor dx,dx + mov dl,byte [BPB_SecPerClus+boot_program] + mul dx + add ax,word [data_start+boot_program] + + call conv_abs_to_THS ; convert abs sector (AX) to BIOS T:H:S (track:head:sector) +patchhere: + mov ah,2 ; ah=2 (read) + mov al,byte [BPB_SecPerClus+boot_program] ; al=(one cluster) + call read_sector + retn +;------------------------------------------ + +;------------------------------------------ + ; read sector from disk +read_sector: + push bp + mov bp,20 ; try 20 times +newread: + dec bp + jz file_error_message + push ax bx cx dx + int 13h + pop dx cx bx ax + jc newread + pop bp + retn +;------------------------------------------ + ; convert abs. sector number (AX) to BIOS T:H:S + ; sector number = (abs.sector%BPB_SecPerTrk)+1 + ; pre.track number = (abs.sector/BPB_SecPerTrk) + ; head number = pre.track number%BPB_NumHeads + ; track number = pre.track number/BPB_NumHeads + ; Return: cl - sector number + ; ch - track number + ; dl - drive number (0 = a:) + ; dh - head number +conv_abs_to_THS: + push bx + mov bx,word [BPB_SecPerTrk+boot_program] + xor dx,dx + div bx + inc dx + mov cl, dl ; cl = sector number + mov bx,word [BPB_NumHeads+boot_program] + xor dx,dx + div bx + ; !!!!!!! ax = track number, dx = head number + mov ch,al ; ch=track number + xchg dh,dl ; dh=head number + mov dl,0 ; dl=0 (drive 0 (a:)) + pop bx + retn +;------------------------------------------ + +loading db cr,lf,'Starting system ',00h +error_message db 13,10 +kernel_name db 'KERNEL MNT ?',cr,lf,00h +FirstRootDirSecNum dw ? +RootDirSecs dw ? +data_start dw ? + +; +write1st: + push cs + pop ds + mov byte [patchhere+1+boot_program], 3 ; change ah=2 to ah=3 + mov bp,[cluster1st+boot_program] + push 1000h + pop es + xor bx,bx + call writesec + mov byte [patchhere+1+boot_program], 2 ; change back ah=3 to ah=2 + retf +cluster1st dw ? +loader_block: + db 1 + dw 0 + dw write1st+boot_program + dw 0 +; <\diamond> + +times 0x1fe-$ db 00h + + db 55h,0aah ;boot signature diff --git a/kernel/branches/Kolibri-acpi/core/exports.inc b/kernel/branches/Kolibri-acpi/core/exports.inc index e41b73b9d8..baddeeb27a 100644 --- a/kernel/branches/Kolibri-acpi/core/exports.inc +++ b/kernel/branches/Kolibri-acpi/core/exports.inc @@ -167,6 +167,13 @@ kernel_export: dd szStrchr , strchr dd szStrrchr , strrchr + dd szDiskAdd , disk_add + dd szDiskDel , disk_del + dd szDiskMediaChanged, disk_media_changed + + dd szTimerHS , timer_hs + dd szCancelTimerHS , cancel_timer_hs + exp_lfb: dd szLFBAddress , 0 dd 0 ;terminator, must be zero diff --git a/kernel/branches/Kolibri-acpi/docs/drivers_api.txt b/kernel/branches/Kolibri-acpi/docs/drivers_api.txt index cdfe47a880..24c155c776 100644 --- a/kernel/branches/Kolibri-acpi/docs/drivers_api.txt +++ b/kernel/branches/Kolibri-acpi/docs/drivers_api.txt @@ -53,6 +53,10 @@ struc DISKFUNC ; Flushes the hardware cache, if it exists. Note that a driver should not ; implement a software cache for read/write, since they are called from the ; kernel cache manager. + .adjust_cache_size dd ? +; unsigned int adjust_cache_size(unsigned int suggested_size); +; Optional. +; Returns the cache size for this device in bytes. 0 = disable cache. } struc DISKMEDIAINFO { diff --git a/kernel/branches/Kolibri-acpi/init.inc b/kernel/branches/Kolibri-acpi/init.inc index 6bf6bf8708..a1ad30fafe 100644 --- a/kernel/branches/Kolibri-acpi/init.inc +++ b/kernel/branches/Kolibri-acpi/init.inc @@ -432,3 +432,4 @@ proc test_cpu endp + diff --git a/kernel/branches/Kolibri-acpi/kernel.asm b/kernel/branches/Kolibri-acpi/kernel.asm index 3071f74a57..fd32d27396 100644 --- a/kernel/branches/Kolibri-acpi/kernel.asm +++ b/kernel/branches/Kolibri-acpi/kernel.asm @@ -905,8 +905,6 @@ first_app_found: push 1 pop dword [CURRENT_TASK] ; set OS task fisrt -; stdcall enable_irq, 1 - ; SET KEYBOARD PARAMETERS mov al, 0xf6 ; reset keyboard, scan enabled call kb_write @@ -1048,6 +1046,7 @@ osloop: call checkidle call check_fdd_motor_status call check_ATAPI_device_event + call check_timers jmp osloop ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; diff --git a/kernel/branches/Kolibri-acpi/kernel32.inc b/kernel/branches/Kolibri-acpi/kernel32.inc index cbb12ad27c..56930f556e 100644 --- a/kernel/branches/Kolibri-acpi/kernel32.inc +++ b/kernel/branches/Kolibri-acpi/kernel32.inc @@ -235,7 +235,8 @@ include "gui/button.inc" ; file system -include "fs/disk.inc" ; support for plug-n-play disks +include "blkdev/disk.inc" ; support for plug-n-play disks +include "blkdev/disk_cache.inc" ; caching for plug-n-play disks include "fs/fs.inc" ; syscall include "fs/fat32.inc" ; read / write for fat32 filesystem include "fs/ntfs.inc" ; read / write for ntfs filesystem