Subversion Repositories Kolibri OS

Compare Revisions

No changes between revisions

Regard whitespace Rev 5044 → Rev 5043

/kernel/trunk/data32.inc
148,7 → 148,7
msg_CR db 13,10,0
 
szHwMouse db 'ATI2D',0
szPS2MDriver db '/rd/1/drivers/PS2MOUSE.SYS',0
szPS2MDriver db 'PS2MOUSE',0
;szCOM_MDriver db 'COM_MOUSE',0
szVidintel db '/rd/1/drivers/vidintel.sys',0
szUSB db 'USB',0
/kernel/trunk/kernel.asm
1058,7 → 1058,7
 
; Load PS/2 mouse driver
 
stdcall load_pe_driver, szPS2MDriver, 0
stdcall load_driver, szPS2MDriver
 
mov esi, boot_setmouse
call boot_log
/kernel/trunk/drivers/vidintel.asm
0,0 → 1,465
; Stub of videodriver for Intel videocards.
; (c) CleverMouse
 
; When the start procedure gots control,
; it tries to detect preferred resolution,
; sets the detected resolution assuming 32-bpp VESA mode and exits
; (without registering a service).
; Detection can be overloaded with compile-time settings
; use_predefined_mode/predefined_width/predefined_height.
 
; set predefined resolution here
use_predefined_mode = 0;1
predefined_width = 0;1366
predefined_height = 0;768
 
; standard driver stuff
format MS COFF
 
DEBUG = 1
 
include 'proc32.inc'
include 'imports.inc'
 
public START
public version
 
section '.flat' code readable align 16
; the start procedure (see the description above)
START:
; 1. Detect device. Abort if not found.
push esi
call DetectDevice
test esi, esi
jz .return0
; 2. Detect optimal mode unless the mode is given explicitly. Abort if failed.
if use_predefined_mode = 0
call DetectMode
end if
cmp [width], 0
jz .return0_cleanup
; 3. Set the detected mode.
call SetMode
; 4. Cleanup and return.
.return0_cleanup:
stdcall FreeKernelSpace, esi
.return0:
pop esi
xor eax, eax
ret 4
 
; check that there is Intel videocard
; if so, map MMIO registers and set internal variables
; esi points to MMIO block; NULL means no device
DetectDevice:
; 1. Sanity check: check that we are dealing with Intel videocard.
; Integrated video device for Intel is always at PCI:0:2:0.
xor esi, esi ; initialize return value to NULL
; 1a. Get PCI VendorID and DeviceID.
push esi
push 10h
push esi
call PciRead32
; 1b. loword(eax) = ax = VendorID, hiword(eax) = DeviceID.
; Test whether we have Intel chipset.
cmp ax, 8086h
jnz .return
; 1c. Say hi including DeviceID.
shr eax, 10h
push edi
pusha
mov edi, pciid_text
call WriteWord
mov esi, hellomsg
call SysMsgBoardStr
popa
; 1d. Test whether we know this DeviceID.
; If this is the case, remember the position of the device in line of Intel cards;
; this knowledge will be useful later.
; Tested on devices with id: 8086:0046, partially 8086:2A02.
mov ecx, pciids_num
mov edi, pciids
repnz scasw
pop edi
jnz .return_unknown_pciid
sub ecx, pciids_num - 1
neg ecx
mov [deviceType], ecx
; 1e. Continue saying hi with positive intonation.
pusha
mov esi, knownmsg
call SysMsgBoardStr
popa
; 2. Prepare MMIO region to control the card.
; 2a. Read MMIO physical address from PCI config space.
push 10h
cmp ecx, i9xx_start
jae @f
mov byte [esp], 14h
@@:
push 10h
push esi
call PciRead32
; 2b. Mask out PCI region type, lower 4 bits.
and al, not 0xF
; 2c. Create virtual mapping of the physical memory.
push 1Bh
push 100000h
push eax
call MapIoMem
; 3. Return.
xchg esi, eax
.return:
ret
; 1f. If we do not know DeviceID, continue saying hi with negative intonation.
.return_unknown_pciid:
pusha
mov esi, unknownmsg
call SysMsgBoardStr
popa
ret
 
; Convert word in ax to hexadecimal text in edi, advance edi.
WriteWord:
; 1. Convert high byte.
push eax
mov al, ah
call WriteByte
pop eax
; 2. Convert low byte.
; Fall through to WriteByte; ret from WriteByte is ret from WriteWord too.
 
; Convert byte in al to hexadecimal text in edi, advance edi.
WriteByte:
; 1. Convert high nibble.
push eax
shr al, 4
call WriteNibble
pop eax
; 2. Convert low nibble.
and al, 0xF
; Fall through to WriteNibble; ret from WriteNibble is ret from WriteByte too.
 
; Convert nibble in al to hexadecimal text in edi, advance edi.
WriteNibble:
; Obvious, isn't it?
cmp al, 10
sbb al, 69h
das
stosb
ret
 
if use_predefined_mode = 0
; detect resolution of the flat panel
DetectMode:
push esi edi
; 1. Get the location of block of GMBUS* registers.
; Starting with Ironlake, GMBUS* registers were moved.
add esi, 5100h
cmp [deviceType], ironlake_start
jb @f
add esi, 0xC0000
@@:
; 2. Initialize GMBUS engine.
mov edi, edid
mov ecx, 0x10000
@@:
test byte [esi+8+1], 80h
loopnz @b
jnz .fail
mov dword [esi], 3
test byte [esi+8+1], 4
jz .noreset
call ResetGMBus
jnz .fail
.noreset:
; 3. Send read command.
and dword [esi+20h], 0
mov dword [esi+4], 4E8000A1h
; 4. Wait for data, writing to the buffer as data arrive.
.getdata:
mov ecx, 0x10000
@@:
test byte [esi+8+1], 8
loopz @b
test byte [esi+8+1], 4
jz .dataok
call ResetGMBus
jmp .fail
.dataok:
mov eax, [esi+0Ch]
stosd
cmp edi, edid+80h
jb .getdata
; 5. Wait for bus idle.
mov ecx, 0x10000
@@:
test byte [esi+8+1], 2
loopnz @b
; 6. We got EDID; dump it if DEBUG.
if DEBUG
pusha
xor ecx, ecx
mov esi, edid
mov edi, edid_text
.dumploop:
lodsb
call WriteByte
mov al, ' '
stosb
inc cl
test cl, 15
jnz @f
mov byte [edi-1], 13
mov al, 10
stosb
@@:
test cl, cl
jns .dumploop
mov esi, edidmsg
call SysMsgBoardStr
popa
end if
; 7. Test whether EDID is good.
; 7a. Signature: 00 FF FF FF FF FF FF 00.
mov esi, edid
cmp dword [esi], 0xFFFFFF00
jnz .fail
cmp dword [esi+4], 0x00FFFFFF
jnz .fail
; 7b. Checksum must be zero.
xor edx, edx
mov ecx, 80h
@@:
lodsb
add dl, al
loop @b
jnz .fail
; 8. Get width and height from EDID.
xor eax, eax
mov ah, [esi-80h+3Ah]
shr ah, 4
mov al, [esi-80h+38h]
mov [width], eax
mov ah, [esi-80h+3Dh]
shr ah, 4
mov al, [esi-80h+3Bh]
mov [height], eax
; 9. Return.
.fail:
pop edi esi
ret
 
; reset bus, clear all errors
ResetGMBus:
; look into the PRM
mov dword [esi+4], 80000000h
mov dword [esi+4], 0
mov ecx, 0x10000
@@:
test byte [esi+8+1], 2
loopnz @b
ret
end if
 
; set resolution [width]*[height]
SetMode:
; 1. Program the registers of videocard.
; look into the PRM
cli
; or byte [esi+7000Ah], 0Ch ; PIPEACONF: disable Display+Cursor Planes
; or byte [esi+7100Ah], 0Ch ; PIPEBCONF: disable Display+Cursor Planes
xor eax, eax
xor edx, edx
cmp [deviceType], i965_start
jb @f
mov dl, 9Ch - 84h
@@:
; or byte [esi+71403h], 80h ; VGACNTRL: VGA Display Disable
and byte [esi+70080h], not 27h ; CURACNTR: disable cursor A
mov dword [esi+70084h], eax ; CURABASE: force write to CURA* regs
and byte [esi+700C0h], not 27h ; CURBCNTR: disable cursor B
mov dword [esi+700C4h], eax ; CURBBASE: force write to CURB* regs
and byte [esi+70183h], not 80h ; DSPACNTR: disable Primary A Plane
mov dword [esi+edx+70184h], eax ; DSPALINOFF/DSPASURF: force write to DSPA* regs
and byte [esi+71183h], not 80h ; DSPBCNTR: disable Primary B Plane
mov dword [esi+edx+71184h], eax ; DSPBLINOFF/DSPBSURF: force write to DSPB* regs
if 1
cmp [deviceType], ironlake_start
jae .disable_pipes
mov edx, 10000h
or byte [esi+70024h], 2 ; PIPEASTAT: clear VBLANK status
or byte [esi+71024h], 2 ; PIPEBSTAT: clear VBLANK status
.wait_vblank_preironlake1:
mov ecx, 1000h
loop $
test byte [esi+7000Bh], 80h ; PIPEACONF: pipe A active?
jz @f
test byte [esi+70024h], 2 ; PIPEASTAT: got VBLANK?
jz .wait_vblank_preironlake2
@@:
test byte [esi+7100Bh], 80h ; PIPEBCONF: pipe B active?
jz .disable_pipes
test byte [esi+71024h], 2 ; PIPEBSTAT: got VBLANK?
jnz .disable_pipes
.wait_vblank_preironlake2:
dec edx
jnz .wait_vblank_preironlake1
jmp .not_disabled
.disable_pipes:
end if
and byte [esi+7000Bh], not 80h ; PIPEACONF: disable pipe
and byte [esi+7100Bh], not 80h ; PIPEBCONF: disable pipe
cmp [deviceType], gen4_start
jb .wait_watching_scanline
; g45 and later: use special flag from PIPE*CONF
mov edx, 10000h
@@:
mov ecx, 1000h
loop $
test byte [esi+7000Bh], 40h ; PIPEACONF: wait until pipe disabled
jz @f
dec edx
jnz @b
jmp .not_disabled
@@:
test byte [esi+7100Bh], 40h ; PIPEBCONF: wait until pipe disabled
jz .disabled
mov ecx, 1000h
loop $
dec edx
jnz @b
jmp .not_disabled
; pineview and before: wait while scanline still changes
.wait_watching_scanline:
mov edx, 1000h
.dis1:
push dword [esi+71000h]
push dword [esi+70000h]
mov ecx, 10000h
loop $
pop eax
xor eax, [esi+70000h]
and eax, 1FFFh
pop eax
jnz .notdis1
xor eax, [esi+71000h]
and eax, 1FFFh
jz .disabled
.notdis1:
dec edx
jnz .dis1
.not_disabled:
sti
jmp .return
.disabled:
lea eax, [esi+61183h]
cmp [deviceType], ironlake_start
jb @f
add eax, 0xE0000 - 0x60000
@@:
lea edx, [esi+60000h]
test byte [eax], 40h
jz @f
add edx, 1000h
@@:
mov eax, [width]
dec eax
shl eax, 16
mov ax, word [height]
dec eax
mov dword [edx+1Ch], eax ; PIPEASRC: set source image size
ror eax, 16
mov dword [edx+10190h], eax ; for old cards
mov ecx, [width]
add ecx, 15
and ecx, not 15
shl ecx, 2
mov dword [edx+10188h], ecx ; DSPASTRIDE: set scanline length
mov dword [edx+10184h], 0 ; DSPALINOFF: force write to DSPA* registers
and byte [esi+61233h], not 80h ; PFIT_CONTROL: disable panel fitting
or byte [edx+1000Bh], 80h ; PIPEACONF: enable pipe
; and byte [edx+1000Ah], not 0Ch ; PIPEACONF: enable Display+Cursor Planes
or byte [edx+10183h], 80h ; DSPACNTR: enable Display Plane A
sti
; 2. Notify the kernel that resolution has changed.
call GetDisplay
mov edx, [width]
mov dword [eax+8], edx
mov edx, [height]
mov dword [eax+0Ch], edx
mov [eax+18h], ecx
mov eax, [width]
dec eax
dec edx
call SetScreen
.return:
ret
 
align 4
hellomsg db 'Intel videocard detected, PciId=8086:'
pciid_text db '0000'
db ', which is ', 0
knownmsg db 'known',13,10,0
unknownmsg db 'unknown',13,10,0
 
if DEBUG
edidmsg db 'EDID successfully read:',13,10
edid_text rb 8*(16*3+1)
db 0
end if
 
version:
dd 0x50005
 
width dd predefined_width
height dd predefined_height
 
pciids:
dw 0x3577 ; i830m
dw 0x2562 ; 845g
dw 0x3582 ; i855gm
i865_start = ($ - pciids) / 2
dw 0x2572 ; i865g
i9xx_start = ($ - pciids) / 2
dw 0x2582 ; i915g
dw 0x258a ; e7221g (i915g)
dw 0x2592 ; i915gm
dw 0x2772 ; i945g
dw 0x27a2 ; i945gm
dw 0x27ae ; i945gme
i965_start = ($ - pciids) / 2
dw 0x2972 ; i946qz (i965g)
dw 0x2982 ; g35g (i965g)
dw 0x2992 ; i965q (i965g)
dw 0x29a2 ; i965g
dw 0x29b2 ; q35g
dw 0x29c2 ; g33g
dw 0x29d2 ; q33g
dw 0xa001 ; pineview
dw 0xa011 ; pineview
gen4_start = ($ - pciids) / 2
dw 0x2a02 ; i965gm
dw 0x2a12 ; i965gm
dw 0x2a42 ; gm45
dw 0x2e02 ; g45
dw 0x2e12 ; g45
dw 0x2e22 ; g45
dw 0x2e32 ; g45
dw 0x2e42 ; g45
dw 0x2e92 ; g45
ironlake_start = ($ - pciids) / 2
dw 0x0042 ; ironlake_d
dw 0x0046 ; ironlake_m
dw 0x0102 ; sandybridge_d
dw 0x0112 ; sandybridge_d
dw 0x0122 ; sandybridge_d
dw 0x0106 ; sandybridge_m
dw 0x0116 ; sandybridge_m
dw 0x0126 ; sandybridge_m
dw 0x010A ; sandybridge_d
pciids_num = ($ - pciids) / 2
 
align 4
deviceType dd ?
edid rb 0x80
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
/kernel/trunk/drivers/tmpdisk_fat.inc
0,0 → 1,327
; FAT-specific code for tmpdisk.asm.
; Formats a disk to FAT16 or FAT32, depending on size.
; Note: formatting is adjusted for memory-based disks. Although the resulting
; image is a valid FAT32 volume, it has no "spare" sectors, e.g. second copy
; of FAT or place for second sector of MS FAT32 bootloader.
 
; Some constants
FAT16_ROOTDIR_SECTORS = 16 ; can be changed, but why not?
; FAT16:
; 1 bootsector,
; min 0xFF5 sectors for data,
; min (0xFF5*2/512) = 16 sectors per FAT, we use only one copy,
; FAT16_ROOTDIR_SECTORS for root directory
MIN_FAT16_SIZE = 1 + 16 + FAT16_ROOTDIR_SECTORS + 0xFF5
; FAT32:
; 1 bootsector,
; 1 sector for fsinfo,
; min 0xFFF5 sectors for data,
; min (0xFFF5*4/512) = 512 sectors per FAT, we use only one copy
MIN_FAT32_SIZE = 1 + 1 + 512 + 0xFFF5
MAX_SIZE = 1 shl (30 - 9) ; 1G in 512-byte sectors
 
; Initializes FATxx structures on the disk.
; Called with edi = pointer to disk data, esi = size of disk.
proc format_disk
; Determine FAT type and jump to the corresponding handler.
cmp esi, MIN_FAT32_SIZE
jae format_disk_fat32
; Fall through to format_disk_fat16.
endp
 
; Structure of FAT16 bootsector. Field names are from MS spec.
struc FAT16BOOT
{
.BS_jmpBoot rb 3
.BS_OEMName rb 8
.BPB_BytsPerSec dw ?
.BPB_SecsPerClus db ?
.BPB_RsvdSecCnt dw ?
.BPB_NumFATs db ?
.BPB_RootEntCnt dw ?
.BPB_TotSec16 dw ?
.BPB_Media db ?
.BPB_FATSz16 dw ?
.BPB_SecPerTrk dw ?
.BPB_NumHeads dw ?
.BPB_HiddSec dd ?
.BPB_TotSec32 dd ?
.BS_DrvNum db ?
.BS_Reserved1 db ?
.BS_BootSig db ?
.BS_VolID dd ?
.BS_VolLab rb 11
.BS_FilSysType rb 8
}
virtual at 0
FAT16BOOT FAT16BOOT
end virtual
 
; Initializes FAT16 structures on the disk.
; Called with edi = pointer to disk data, esi = size of disk.
format_disk_fat16:
; 1. Calculate number of clusters.
; 1a. There are fixed-sized areas and there are data+FAT;
; every cluster uses 512 bytes in data area and 2 bytes in FAT area.
lea eax, [esi-1-FAT16_ROOTDIR_SECTORS]
; two following lines are equivalent to edx = floor(eax*512/514)
mov ecx, 0xFF00FF01
mul ecx ; edx = number of clusters
; 1b. Force the number be less than 0xfff5.
mov eax, 0xFFF4
cmp edx, eax
jb @f
mov edx, eax
@@:
; 2. Zero all system areas on the disk.
lea ecx, [256*(1+FAT16_ROOTDIR_SECTORS)+edx+255]
and ecx, not 255
shr ecx, 1
xor eax, eax
push edi
rep stosd
pop edi
; 3. Generate the bootsector.
; 3a. Copy static stub.
push esi edi
mov esi, fat16bootsector_stub
mov ecx, fat16bootsector_stub_size
rep movsb
pop edi esi
mov word [edi+510], 0xAA55
; 3b. Set fields which depend on size.
cmp esi, 0x10000
jae .size_is_32bit
mov [edi+FAT16BOOT.BPB_TotSec16], si
jmp .size_written
.size_is_32bit:
mov [edi+FAT16BOOT.BPB_TotSec32], esi
.size_written:
lea eax, [edx+255]
shr eax, 8
mov [edi+FAT16BOOT.BPB_FATSz16], ax
; 3c. Generate volume ID.
call generate_volume_id
mov [edi+FAT16BOOT.BS_VolID], eax
; 4. Initialize FAT.
mov dword [edi+512], 0xFFFFFFF8
; 5. Return.
ret
 
; Structure of FAT32 bootsector. Field names are from MS spec.
struc FAT32BOOT
{
.BS_jmpBoot rb 3
.BS_OEMName rb 8
.BPB_BytsPerSec dw ?
.BPB_SecsPerClus db ?
.BPB_RsvdSecCnt dw ?
.BPB_NumFATs db ?
.BPB_RootEntCnt dw ?
.BPB_TotSec16 dw ?
.BPB_Media db ?
.BPB_FATSz16 dw ?
.BPB_SecPerTrk dw ?
.BPB_NumHeads dw ?
.BPB_HiddSec dd ?
.BPB_TotSec32 dd ?
.BPB_FATSz32 dd ?
.BPB_ExtFlags dw ?
.BPB_FSVer dw ?
.BPB_RootClus dd ?
.BPB_FSInfo dw ?
.BPB_BkBootSec dw ?
.BPB_Reserved rb 12
.BS_DrvNum db ?
.BS_Reserved1 db ?
.BS_BootSig db ?
.BS_VolID dd ?
.BS_VolLab rb 11
.BS_FilSysType rb 8
}
virtual at 0
FAT32BOOT FAT32BOOT
end virtual
 
; Initializes FAT32 structures on the disk.
; Called with edi = pointer to disk data, esi = size of disk.
format_disk_fat32:
; 1. Calculate number of clusters.
; 1a. There is fixed-sized area and there are data+FAT;
; every cluster uses 512 bytes in data area and 4 bytes in FAT area.
lea eax, [esi-1-1]
; two following lines are equivalent to edx=floor(eax*512/516) if eax<10000000h
mov ecx, 0xFE03F810
mul ecx ; edx = number of clusters
; 2. Zero all system areas on the disk and first cluster of data,
; used for root directory.
lea ecx, [128*(1+1+1)+edx+127]
and ecx, not 127
xor eax, eax
push edi
rep stosd
pop edi
; 3. Generate the bootsector.
; 3a. Copy static stub.
push esi edi
mov esi, fat32bootsector_stub
mov ecx, fat32bootsector_stub_size
rep movsb
pop edi esi
mov word [edi+510], 0xAA55
; 3b. Set fields which depend on size.
mov [edi+FAT32BOOT.BPB_TotSec32], esi
lea eax, [edx+127]
shr eax, 7
mov [edi+FAT32BOOT.BPB_FATSz32], eax
; 3c. Generate volume ID.
call generate_volume_id
mov [edi+FAT32BOOT.BS_VolID], eax
; 4. Initialize fsinfo sector.
mov dword [edi+512], 'RRaA'
mov dword [edi+512+484], 'rrAa'
dec edx ; one cluster is occupied by root dir
mov dword [edi+512+488], edx ; free count
mov byte [edi+512+492], 3 ; first free cluster
mov word [edi+512+510], 0xAA55
; 5. Initialize FAT.
mov dword [edi+512*2], 0x0FFFFFF8
mov dword [edi+512*2+4], 0x0FFFFFFF
mov dword [edi+512*2+8], 0x0FFFFFFF
; 6. Return.
ret
 
; Generate volume serial number, which should try to be unique for each volume.
; Use CMOS date+time, copy-pasted from fat32.inc.
generate_volume_id:
call get_time_for_file
mov cx, ax
call get_date_for_file
shl eax, 16
mov ax, cx
ret
 
; Three following procedures are copy-pasted from fat32.inc.
bcd2bin:
;----------------------------------
; input : AL=BCD number (eg. 0x11)
; output : AH=0
; AL=decimal number (eg. 11)
;----------------------------------
xor ah, ah
shl ax, 4
shr al, 4
aad
ret
 
get_date_for_file:
;-----------------------------------------------------
; Get date from CMOS and pack day,month,year in AX
; DATE bits 0..4 : day of month 0..31
; 5..8 : month of year 1..12
; 9..15 : count of years from 1980
;-----------------------------------------------------
mov al, 0x7 ;day
out 0x70, al
in al, 0x71
call bcd2bin
ror eax, 5
 
mov al, 0x8 ;month
out 0x70, al
in al, 0x71
call bcd2bin
ror eax, 4
 
mov al, 0x9 ;year
out 0x70, al
in al, 0x71
call bcd2bin
add ax, 20 ;because CMOS return only the two last
;digit (eg. 2000 -> 00 , 2001 -> 01) and we
rol eax, 9 ;need the difference with 1980 (eg. 2001-1980)
ret
 
 
get_time_for_file:
;-----------------------------------------------------
; Get time from CMOS and pack hour,minute,second in AX
; TIME bits 0..4 : second (the low bit is lost)
; 5..10 : minute 0..59
; 11..15 : hour 0..23
;-----------------------------------------------------
mov al, 0x0 ;second
out 0x70, al
in al, 0x71
call bcd2bin
ror eax, 6
 
mov al, 0x2 ;minute
out 0x70, al
in al, 0x71
call bcd2bin
ror eax, 6
 
mov al, 0x4 ;hour
out 0x70, al
in al, 0x71
call bcd2bin
rol eax, 11
ret
 
; some data
fat16bootsector_stub:
db 0EBh, 3Ch, 90h ; BS_jmpBoot
db 'KOLIBRI ' ; BS_OEMName
dw 512 ; BPB_BytsPerSec
db 1 ; BPB_SecsPerClus
dw 1 ; BPB_RsvdSecCnt
db 1 ; BPB_NumFATs
dw FAT16_ROOTDIR_SECTORS*16 ; BPB_RootEntCnt
dw 0 ; BPB_TotSec16, filled in format_disk_fat16
db 0F8h ; BPB_Media
dw 0 ; BPB_FATSz16, filled in format_disk_fat16
dw 32 ; BPB_SecPerTrk
dw 128 ; BPB_NumHeads
dd 0 ; BPB_HiddSec
dd 0 ; BPB_TotSec32, filled in format_disk_fat16
db 80h ; BS_DrvNum
db 0 ; BS_Reserved1
db 29h ; BS_BootSig
dd 0 ; BS_VolID, filled in format_disk_fat16
db 'NO NAME ' ; BS_VolLab
db 'FAT16 ' ; BS_FilSysType
; just in case add some meaningful bytes if someone tries to boot
db 0CDh, 19h, 0EBh, 0FEh ; int 19h, jmp $
fat16bootsector_stub_size = $ - fat16bootsector_stub
fat32bootsector_stub:
db 0EBh, 58h, 90h ; BS_jmpBoot
db 'KOLIBRI ' ; BS_OEMName
dw 512 ; BPB_BytsPerSec
db 1 ; BPB_SecsPerClus
dw 2 ; BPB_RsvdSecCnt
db 1 ; BPB_NumFATs
dw 0 ; BPB_RootEntCnt
dw 0 ; BPB_TotSec16
db 0F8h ; BPB_Media
dw 0 ; BPB_FATSz16
dw 32 ; BPB_SecPerTrk
dw 128 ; BPB_NumHeads
dd 0 ; BPB_HiddSec
dd 0 ; BPB_TotSec32, filled in format_disk_fat32
dd 0 ; BPB_FATSz32, filled in format_disk_fat32
dw 0 ; BPB_ExtFlags
dw 0 ; BPB_FSVer
dd 2 ; BPB_RootClus
dw 1 ; BPB_FSInfo
dw 0 ; BPB_BkBootSec
rb 12 ; BPB_Reserved
db 80h ; BS_DrvNum
db 0 ; BS_Reserved1
db 29h ; BS_BootSig
dd 0 ; BS_VolID, filled in format_disk_fat32
db 'NO NAME ' ; BS_VolLab
db 'FAT32 ' ; BS_FilSysType
; same bytes as in fat16bootsector_stub
db 0CDh, 19h, 0EBh, 0FEh ; int 19h, jmp $
fat32bootsector_stub_size = $ - fat32bootsector_stub
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
/kernel/trunk/drivers/tmpdisk.asm
0,0 → 1,295
; Disk driver to create FAT16/FAT32 memory-based temporary disk aka RAM disk.
; (c) CleverMouse
 
; Note: in the ideal world, a disk driver should not care about a file system
; on it. In the current world, however, there is no way to format a disk in
; FAT, so this part of file-system-specific operations is included in the
; driver.
 
; When this driver is loading, it registers itself in the system and does
; nothing more. When loaded, this driver controls pseudo-disk devices
; named /tmp#/, where # is a digit from 0 to 9. The driver does not create
; any device by itself, waiting for instructions from an application.
; The driver responds to the following IOCTLs from a control application:
SRV_GETVERSION equ 0 ; input ignored,
; output = dword API_VERSION
DEV_ADD_DISK equ 1 ; input = structure add_disk_struc,
; no output
DEV_DEL_DISK equ 2 ; input = structure del_disk_struc,
; no output
; For all IOCTLs the driver returns one of the following error codes:
NO_ERROR equ 0
ERROR_INVALID_IOCTL equ 1 ; unknown IOCTL code, wrong input/output size...
ERROR_INVALID_ID equ 2 ; .DiskId must be from 0 to 9
ERROR_SIZE_TOO_LARGE equ 3 ; .DiskSize is too large
ERROR_SIZE_TOO_SMALL equ 4 ; .DiskSize is too small
ERROR_NO_MEMORY equ 5 ; memory allocation failed
 
 
API_VERSION equ 1
; Input structures:
struc add_disk_struc
{
.DiskSize dd ? ; disk size in sectors, 1 sector = 512 bytes
; Note: .DiskSize is the full size, including FAT service data.
; Size for useful data is slightly less than this number.
.DiskId db ? ; from 0 to 9
.sizeof:
}
virtual at 0
add_disk_struc add_disk_struc
end virtual
struc del_disk_struc
{
.DiskId db ? ; from 0 to 9
.sizeof:
}
virtual at 0
del_disk_struc del_disk_struc
end virtual
 
max_num_disks equ 10
 
; standard driver stuff
format MS COFF
 
DEBUG equ 0
include 'proc32.inc'
include 'imports.inc'
 
public START
public version
 
struc IOCTL
{
.handle dd ?
.io_code dd ?
.input dd ?
.inp_size dd ?
.output dd ?
.out_size dd ?
}
 
virtual at 0
IOCTL IOCTL
end virtual
 
section '.flat' code readable align 16
; the start procedure (see the description above)
proc START
; This procedure is called in two situations:
; when the driver is loading and when the system is shutting down.
; 1. Check that the driver is loading; do nothing unless so.
xor eax, eax ; set return value in case we will do nothing
cmp dword [esp+4], 1
jne .nothing
; 2. Register the driver in the system.
stdcall RegService, my_service, service_proc
; 3. Return the value returned by RegService back to the system.
.nothing:
retn 4
endp
 
; Service procedure for the driver - handle all IOCTL requests for the driver.
; The description of handled IOCTLs is located in the start of this file.
proc service_proc
; 1. Save used registers to be stdcall.
; Note: this shifts esp, so the first parameter [esp+4] becomes [esp+16].
; Note: edi is used not by this procedure itself, but by worker procedures.
push ebx esi edi
; 2. Get parameter from the stack: [esp+16] is the first parameter,
; pointer to IOCTL structure.
mov edx, [esp+16] ; edx -> IOCTL
; 3. Set the return value to 'invalid IOCTL'.
; Now, if one of conditions for IOCTL does not met, the code
; can simply return the value already loaded.
mov al, ERROR_INVALID_IOCTL
; 4. Get request code and select a handler for the code.
mov ecx, [edx+IOCTL.io_code]
test ecx, ecx ; check for SRV_GETVERSION
jnz .no.srv_getversion
; 4. This is SRV_GETVERSION request, no input, 4 bytes output, API_VERSION.
; 4a. Output size must be at least 4 bytes.
cmp [edx+IOCTL.out_size], 4
jl .return
; 4b. Write result to the output buffer.
mov eax, [edx+IOCTL.output]
mov dword [eax], API_VERSION
; 4c. Return success.
xor eax, eax
jmp .return
.no.srv_getversion:
dec ecx ; check for DEV_ADD_DISK
jnz .no.dev_add_disk
; 5. This is DEV_ADD_DISK request, input is add_disk_struc, output is 1 byte
; 5a. Input size must be exactly add_disk_struc.sizeof bytes.
cmp [edx+IOCTL.inp_size], add_disk_struc.sizeof
jnz .return
; 5b. Load input parameters and call the worker procedure.
mov eax, [edx+IOCTL.input]
movzx ebx, [eax+add_disk_struc.DiskId]
mov esi, [eax+add_disk_struc.DiskSize]
call add_disk
; 5c. Return back to the caller the value from the worker procedure.
jmp .return
.no.dev_add_disk:
dec ecx ; check for DEV_DEL_DISK
jnz .return
; 6. This is DEV_DEL_DISK request, input is del_disk_struc
; 6a. Input size must be exactly del_disk_struc.sizeof bytes.
cmp [edx+IOCTL.inp_size], del_disk_struc.sizeof
jnz .return
; 6b. Load input parameters and call the worker procedure.
mov eax, [edx+IOCTL.input]
movzx ebx, [eax+del_disk_struc.DiskId]
call del_disk
; 6c. Return back to the caller the value from the worker procedure.
.return:
; 7. Exit.
; 7a. The code above returns a value in al for efficiency,
; propagate it to eax.
movzx eax, al
; 7b. Restore used registers to be stdcall.
pop edi esi ebx
; 7c. Return, popping one argument.
retn 4
endp
 
; The worker procedure for DEV_ADD_DISK request.
; Creates a memory-based disk of given size and formats it in FAT16/32.
; Called with ebx = disk id, esi = disk size,
; returns error code in al.
proc add_disk
; 1. Check that disk id is correct and free.
; Otherwise, return the corresponding error code.
mov al, ERROR_INVALID_ID
cmp ebx, max_num_disks
jae .return
cmp [disk_pointers+ebx*4], 0
jnz .return
; 2. Check that the size is reasonable.
; Otherwise, return the corresponding error code.
mov al, ERROR_SIZE_TOO_LARGE
cmp esi, MAX_SIZE
ja .return
mov al, ERROR_SIZE_TOO_SMALL
cmp esi, MIN_FAT16_SIZE
jb .return
; 3. Allocate memory for the disk, store the pointer in edi.
; If failed, return the corresponding error code.
mov eax, esi
shl eax, 9
stdcall KernelAlloc, eax
mov edi, eax
test eax, eax
mov al, ERROR_NO_MEMORY
jz .return
; 4. Store the pointer and the size in the global variables.
; It is possible, though very unlikely, that two threads
; have called this function in parallel with the same id,
; so [disk_pointers+ebx*4] could be filled by another thread.
; Play extra safe and store new value only if old value is zero.
xor eax, eax
lock cmpxchg [disk_pointers+ebx*4], edi
jz @f
; Otherwise, free the allocated memory and return the corresponding error code.
stdcall KernelFree, edi
mov al, ERROR_INVALID_ID
jmp .return
@@:
mov [disk_sizes+ebx*4], esi
; 5. Call the worker procedure for formatting this disk.
; It should not fail.
call format_disk
; 6. Register the disk in the system.
; 6a. Generate name as /tmp#, where # = ebx + '0'. Use two dwords in the stack.
push 0
push 'tmp'
mov eax, esp ; eax points to 'tmp' + zero byte + zero dword
lea ecx, [ebx+'0'] ; ecx = digit
mov [eax+3], cl ; eax points to 'tmp#' + zero dword
; 6b. Call the kernel API. Use disk id as 'userdata' parameter for callbacks.
stdcall DiskAdd, disk_functions, eax, ebx, 0
; 6c. Restore the stack after 6a.
pop ecx ecx
; 6c. Check the result. If DiskAdd has failed, cleanup and return
; ERROR_NO_MEMORY, this is the most probable or even the only reason to fail.
test eax, eax
jnz @f
mov [disk_sizes+ebx*4], 0
mov [disk_pointers+ebx*4], 0
stdcall KernelFree, edi
mov al, ERROR_NO_MEMORY
jmp .return
@@:
push eax
; 6d. Notify the kernel that media is inserted.
stdcall DiskMediaChanged, eax, 1
; 6e. Disk is fully configured; store its handle in the global variable
; and return success.
pop [disk_handles+ebx*4]
xor eax, eax
; 7. Return.
.return:
retn
endp
 
; The worker procedure for DEV_DEL_DISK request.
; Deletes a previously created memory-based disk.
; Called with ebx = disk id,
; returns error code in al.
proc del_disk
; 1. Check that disk id is correct.
; Otherwise, return the corresponding error code.
mov al, ERROR_INVALID_ID
cmp ebx, max_num_disks
jae .return
; 2. Get the disk handle, simultaneously clearing the global variable.
xor edx, edx
xchg edx, [disk_handles+ebx*4]
; 3. Check that the handle is non-zero.
; Otherwise, return the corresponding error code.
test edx, edx
jz .return
; 4. Delete the disk from the system.
stdcall DiskDel, edx
; 5. Return success.
; Note that we can't free memory yet; it will be done in tmpdisk_close.
xor eax, eax
.return:
retn
endp
 
; Include implementation of tmpdisk_* callbacks.
include 'tmpdisk_work.inc'
; Include FAT-specific code.
include 'tmpdisk_fat.inc'
 
; initialized data
align 4
disk_functions:
dd disk_functions_end - disk_functions
dd tmpdisk_close
dd 0 ; no need in .closemedia
dd tmpdisk_querymedia
dd tmpdisk_read
dd tmpdisk_write
dd 0 ; no need in .flush
dd tmpdisk_adjust_cache_size
disk_functions_end:
; disk_handles = array of values for Disk* kernel functions
label disk_handles dword
times max_num_disks dd 0
; disk_pointers = array of pointers to disk data
label disk_pointers dword
times max_num_disks dd 0
; disk_sizes = array of disk sizes
label disk_sizes dword
times max_num_disks dd 0
 
version dd 0x00060006
my_service db 'tmpdisk',0
 
; uninitialized data
; actually, not used here
;section '.data' data readable writable align 16 ; standard driver stuff
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property
/kernel/trunk/drivers/tmpdisk_work.inc
0,0 → 1,144
; Callbacks which implement tmpdisk-specific disk functions for tmpdisk.asm.
 
; The first argument of every callback is .userdata = userdata arg of AddDisk.
; For tmpdisk, .userdata is the disk id, one of 0,...,max_num_disks-1.
 
DISK_STATUS_OK = 0 ; success
DISK_STATUS_GENERAL_ERROR = -1; if no other code is suitable
DISK_STATUS_INVALID_CALL = 1 ; invalid input parameters
DISK_STATUS_NO_MEDIA = 2 ; no media present
DISK_STATUS_END_OF_MEDIA = 3 ; end of media while reading/writing data
 
; The last function that is called for the given disk. The kernel calls it when
; the kernel has finished all operations with the disk and it is safe to free
; all driver-specific data identified by 'userdata'.
proc tmpdisk_close
virtual at esp+4
.userdata dd ?
end virtual
; Free the memory for disk and zero global variables.
mov edx, [.userdata]
mov [disk_sizes+edx*4], 0
xor eax, eax
xchg eax, [disk_pointers+edx*4]
stdcall KernelFree, eax
retn 4
endp
 
struc DISKMEDIAINFO
{
.flags dd ?
DISK_MEDIA_READONLY = 1
.sectorsize dd ?
.capacity dq ?
}
virtual at 0
DISKMEDIAINFO DISKMEDIAINFO
end virtual
 
; Returns information about disk media.
proc tmpdisk_querymedia
virtual at esp+4
.userdata dd ?
.info dd ?
end virtual
; Media is always present, sector size is always 512 bytes,
; the size of disk in sectors is stored in a global variable.
mov edx, [.userdata]
mov ecx, [.info]
mov [ecx+DISKMEDIAINFO.flags], 0
mov [ecx+DISKMEDIAINFO.sectorsize], 512
mov eax, [disk_sizes+edx*4]
mov dword [ecx+DISKMEDIAINFO.capacity], eax
mov dword [ecx+DISKMEDIAINFO.capacity+4], 0
; Return zero as an indicator of success.
xor eax, eax
retn 8
endp
 
; Reads one or more sectors from the device.
tmpdisk_read:
xor edx, edx ; 0 = reading
jmp tmpdisk_readwrite
 
; Writes one or more sectors to the device.
tmpdisk_write:
mov dl, 1 ; 1 = writing
; Fall through to tmpdisk_readwrite.
 
; Common procedure for reading and writing.
; dl = 0 for reading, dl = 1 for writing.
; Arguments of tmpdisk_read and tmpdisk_write are the same,
; they continue to be stack arguments of this procedure.
proc tmpdisk_readwrite \
userdata:dword, \
buffer:dword, \
start_sector:qword, \
numsectors_ptr:dword
; 1. Save used registers to be stdcall.
push esi edi
mov esi, [userdata]
mov edi, [numsectors_ptr]
; 1. Determine number of sectors to be transferred.
; This is either the requested number of sectors or number of sectors
; up to the disk boundary, depending of what is less.
xor ecx, ecx
; 1a. Test whether [start_sector] is less than [disk_sizes] for selected disk.
; If so, calculate number of sectors between [start_sector] and [disk_sizes].
; Otherwise, the actual number of sectors is zero.
cmp dword [start_sector+4], ecx
jnz .got_number
mov eax, [disk_sizes+esi*4]
sub eax, dword [start_sector]
jbe .got_number
; 1b. Get the requested number of sectors.
mov ecx, [edi]
; 1c. If it is greater than number of sectors calculated in 1a, use the value
; from 1a.
cmp ecx, eax
jb .got_number
mov ecx, eax
.got_number:
; 2. Compare the actual number of sectors with requested. If they are
; equal, set eax (it will be the returned value) to zero. Otherwise,
; use DISK_STATUS_END_OF_MEDIA.
xor eax, eax
cmp ecx, [edi]
jz @f
mov al, DISK_STATUS_END_OF_MEDIA
@@:
; 3. Store the actual number of sectors.
mov [edi], ecx
; 4. Calculate source and destination addresses.
mov edi, dword [start_sector]
shl edi, 9
add edi, [disk_pointers+esi*4]
mov esi, [buffer]
; 5. Calculate number of dwords to be transferred.
shl ecx, 9-2
; 6. Now esi = [buffer], edi = pointer inside disk.
; This is normal for write operations;
; exchange esi and edi for read operations.
test dl, dl
jnz @f
xchg esi, edi
@@:
; 7. Copy data.
rep movsd
; 8. Restore used registers to be stdcall and return.
; The value in eax was calculated in step 2.
pop edi esi
ret
endp
 
; The kernel calls this function when initializing cache subsystem for
; the media. This call allows the driver to adjust the cache size.
proc tmpdisk_adjust_cache_size
virtual at esp+4
.userdata dd ?
.suggested_size dd ?
end virtual
; Since tmpdisk does not need cache, just return 0.
xor eax, eax
retn 8
endp
Property changes:
Added: svn:eol-style
+native
\ No newline at end of property