DOSSEG .MODEL LARGE include disk.inc include partit.inc .DATA extrn _x86BootCode:far .DATA? extrn PartitionList:dword extrn PartitionListCount:word .CODE ASSUME ds:NOTHING extrn _malloc:far extrn _free:far extrn _qsort:far extrn _ReadDisk:far extrn _WriteDisk:far extrn _GetDiskInfoByHandle:far .386 ;++ ; ; INT ; _far ; MakePartitionAtStartOfDisk( ; IN HDISK DiskHandle, ; OUT FPVOID SectorBuffer, ; IN ULONG MinimumSectorCount, ; IN UINT PartitionClass, ; IN BYTE SystemId OPTIONAL ; ); ; ; Routine Description: ; ; This routine makes a new primary partition of at least ; a given number of sectors. The partition may be larger ; to keep it aligned on the proper cyl/track boundary. ; ; Only int13 disk units are supported. ; ; Arguments: ; ; DiskHandle - supplies a disk handle as returned by OpenDisk(). ; ; SectorBuffer - supplies a pointer to a buffer suitable for use ; for i/o of a single sector. This buffer must not ; cross a DMA boundary, but the caller is responsible ; for ensuring this. ; ; MinimumSectorCount - supplies the minimum number of sectors ; that the partition should contain. ; ; PartitionClass - supplies a value indicating what class of system id ; should be used for the partition. ; ; PARTCLASS_FAT - creates a type 1, 4, or 6, or e partition ; depending on size and availability of xint13 ; services for the drive. ; ; PARTCLASS_FAT32 - creates a type b or c partition depending ; on availability of xint13 services for the drive. ; ; PARTCLASS_NTFS - creates a type 7 partition. ; ; PARTCLASS_OTHER - create a partition whose type is given by ; the SystemId parameter. ; ; SystemId - if PartitionClass is PARTCLASS_OTHER, supplies the ; system id for the partition. Ignored otherwise. ; ; Return Value: ; ; Partition id of newly created partition, or -1 if failure. ; ;-- DiskHandle equ dword ptr [bp+6] DiskHandlel equ word ptr [bp+6] DiskHandleh equ word ptr [bp+8] SectorBuffer equ dword ptr [bp+10] SectorBufferl equ word ptr [bp+10] SectorBufferh equ word ptr [bp+12] MinimumSectorCount equ dword ptr [bp+14] MinimumSectorCountl equ word ptr [bp+14] MinimumSectorCounth equ word ptr [bp+16] PartitionClass equ word ptr [bp+18] SystemId equ byte ptr [bp+20] ExtSecCnth equ word ptr [bp-2] ExtSecCntl equ word ptr [bp-4] ExtSecCnt equ dword ptr [bp-4] Cylinders equ word ptr [bp-6] Heads equ word ptr [bp-8] SectorsPerTrack equ byte ptr [bp-9] Int13UnitNumber equ byte ptr [bp-10] SectorsPerCylinder equ word ptr [bp-12] DiskSizeh equ word ptr [bp-14] DiskSizel equ word ptr [bp-16] DiskSize equ dword ptr [bp-16] DiskId equ word ptr [bp-18] Tableh equ word ptr [bp-20] Tablel equ word ptr [bp-22] Table equ dword ptr [bp-22] FreeStarth equ word ptr [bp-24] FreeStartl equ word ptr [bp-26] FreeStart equ dword ptr [bp-26] START_AND_SIZE STRUC PartStartl dw ? PartStarth dw ? PartSizel dw ? PartSizeh dw ? START_AND_SIZE ENDS public _MakePartitionAtStartOfDisk _MakePartitionAtStartOfDisk proc far push bp mov bp,sp sub sp,26 push ds push es push bx push si push di ; ; Get disk info. This also makes sure the handle is open, ; calculates the number of sectors we can address on the disk, ; and the number of sectors in a cylinder. ; call near ptr pGetDiskValues jnc @f mov ax,0ffffh jc done2 ; ; Allocate a buffer for our table. ; Make sure ds addresses DGROUP for crt ; @@: push ds push DGROUP pop ds push 5 * SIZE START_AND_SIZE call _malloc add sp,2 pop ds mov cx,ax or cx,dx jnz @f dec ax ; ax = -1 for error return jmp done2 @@: mov Tableh,dx mov Tablel,ax ; ; Read sector 0 of the disk. ; push SectorBuffer push 1 push 0 push 0 push DiskHandle call _ReadDisk add sp,14 cmp ax,0 jne @f dec ax jmp done1 ; ax = -1 for error exit ; ; Make sure the MBR is valid. ; @@: lds si,SectorBuffer cmp word ptr [si+510],0aa55h je @f mov ax,0ffffh jmp done1 ; ; Zero out the start/offset table. ; @@: les di,Table ; es:di -> local start/size table mov ax,0 mov cx,4*(SIZE START_AND_SIZE) cld rep stosb ; ; Build the start/offset table. ; mov cx,4 ; cx = loop count add si,1beh ; ds:si -> start of partition table mov di,Tablel ; es:di -> local start/size table nextent: cmp byte ptr [si+4],0 jnz @f add si,16 loop nextent jmp short chkfull @@: add si,8 ; ds:si -> relative sector field movsw ; start low movsw ; start high movsw ; count low movsw ; count high inc al ; remember number of used entries loop nextent ; ; See if the partition table is full. ; chkfull: cmp al,4 jne @f mov ax,0ffffh je done1 ; ; Sort the local start/size table. Before calling crt ; make sure ds addresses DGROUP. ; @@: push ds push DGROUP pop ds push ax ; save table length push SEG CompareStartSize push OFFSET CompareStartSize ; compare routine pointer push SIZE START_AND_SIZE ; size of each element push ax ; number of elements to sort push Table ; array of elements to sort call _qsort add sp,12 pop bx ; restore table length pop ds ; ; Put a "fence" entry at the end of the table ; for space at the end of the disk. ; mov al,SIZE START_AND_SIZE mul bl ; ax = byte offset to fence entry inc bl ; account for fence entry les di,Table add di,ax ; es:di -> fence entry mov ax,DiskSizel mov es:[di].PartStartl,ax mov ax,DiskSizeh mov es:[di].PartStarth,ax mov es:[di].PartSizel,1 mov es:[di].PartSizeh,0 ; ; Initialize for loop. The first space starts on the first sector ; of the second head. ; mov al,SectorsPerTrack cbw mov FreeStartl,ax mov FreeStarth,0 mov cl,bl xor ch,ch ; cx = # entries in table les di,Table ; es:di -> start/offset table jmp short aligned1 ; ; Get the start of the partition and align to cylinder boundary ; nextspace: mov dx,FreeStarth mov ax,FreeStartl ; dx:ax = start of free space div SectorsPerCylinder ; dx = sector within cylinder cmp dx,0 ; already aligned? jz aligned1 ; yes mov bx,SectorsPerCylinder sub bx,dx ; bx = adjustment to next boundary add FreeStartl,bx adc FreeStarth,0 ; FreeStart now aligned aligned1: mov dx,es:[di].PartStarth mov ax,es:[di].PartStartl ; dx:ax = start of partition sub ax,FreeStartl sbb dx,FreeStarth ; dx:ax = size of free space push ax push dx ; ; Now make sure the end of the free space is aligned. ; add ax,FreeStartl adc dx,FreeStarth ; dx:ax = first sector past free space div SectorsPerCylinder ; dx = sector within cylinder (may be 0) mov bx,dx pop dx pop ax ; dx:ax = size of free space sub ax,bx sbb dx,0 ; dx:ax = size of aligned free space js nextspace1 ; just in case ; ; Check the free space to see if it's large enough. ; cmp dx,MinimumSectorCounth ja makepart ; it's large enough jb nextspace1 cmp ax,MinimumSectorCountl jae makepart nextspace1: mov ax,es:[di].PartStartl add ax,es:[di].PartSizel mov FreeStartl,ax mov ax,es:[di].PartStarth adc ax,es:[di].PartSizeh mov FreeStarth,ax ; next space is after this partition add di,SIZE START_AND_SIZE ; point at next table entry loop nextspace mov ax,cx ; no room, set ax for error return dec ax jmp done1 ; ; If we get here, we've found a free space that will work ; for our partition. ; ; FreeStart has the start of the free space. ; makepart: mov ax,FreeStartl add ax,MinimumSectorCountl mov dx,FreeStarth adc dx,MinimumSectorCounth div SectorsPerCylinder ; dx = sector within cylinder cmp dx,0 ; aligned already? jz @f ; yes mov bx,SectorsPerCylinder sub bx,dx ; bx = adjustment to next cyl boundary add MinimumSectorCountl,bx adc MinimumSectorCounth,0 ; ; Now MinimumSectorCount has the actual sector count. ; Find a free table entry. We know there is one or else ; we'd have errored out a while ago. ; @@: lds si,SectorBuffer add si,1beh ; ds:si -> partition table @@: cmp byte ptr [si+4],0 jz makepart1 add si,16 jmp short @b makepart1: mov ax,FreeStartl mov dx,FreeStarth mov [si+8],ax mov [si+10],dx add si,1 call pMakeCHS mov ax,MinimumSectorCountl mov dx,MinimumSectorCounth mov [si+11],ax mov [si+13],dx add ax,FreeStartl adc dx,FreeStarth sub ax,1 sbb dx,0 add si,4 call pMakeCHS ; al = xint13 required flag sub si,1 ; ds:si -> system id byte ; ; Figure out the partition id. ; al is xint13 required flag ; mov dx,PartitionClass mov ah,SystemId call pDetermineSystemId mov [si],al ; store away system id sub si,4 ; ds:si -> partition table entry call pNewPartitionRecord cmp ax,0ffffh je done1 ; error, bail now ; ; Phew. Write out the master boot sector. ; push ax ; save partition id push SectorBuffer push 1 push 0 push 0 push DiskHandle call _WriteDisk add sp,14 ; ax set for return pop dx ; dx = partition id cmp ax,0 ; error? jne @f ; no dec ax ; ax = -1 for error return jmp short done1 @@: mov ax,dx ; ax = partition id for return done1: push ax push Table push DGROUP pop ds ; address DGROUP for crt call _free add sp,4 pop ax done2: pop di pop si pop bx pop es pop ds leave retf _MakePartitionAtStartOfDisk endp Comparand1 equ dword ptr [bp+6] Comparand2 equ dword ptr [bp+10] CompareStartSize proc far push bp mov bp,sp push ds push es push di push si lds si,Comparand1 les di,Comparand2 mov dx,es:[di].PartStarth mov ax,es:[di].PartStartl cmp [si].PartStarth,dx jb less1 ja greater1 cmp [si].PartStartl,ax jb less1 ja greater1 xor ax,ax jmp short compdone less1: mov ax,0ffffh jmp short compdone greater1: mov ax,1 compdone: pop si pop di pop es pop ds leave retf CompareStartSize endp ;++ ; ; INT ; _far ; MakePartitionAtEndOfEmptyDisk( ; IN HDISK DiskHandle, ; OUT FPVOID SectorBuffer, ; IN ULONG MinimumSectorCount, ; IN BOOL NewMasterBootCode ; ); ; ; Routine Description: ; ; This routine creates an partition at the very end of a disk. ; The disk MUST be completely empty. If no valid MBR exists, ; one will be created. ; ; The partition will be made active. ; ; Arguments: ; ; DiskHandle - supplies handle to open disk, from OpenDisk(). ; ; SectorBuffer - supplies a pointer to a buffer suitable for use ; for i/o of a single sector. This buffer must not ; cross a DMA boundary, but the caller is responsible ; for ensuring this. ; ; MinimumSectorCount - supplies minimum number of sectors ; for the partition. The actual size may be larger ; to account for rounding, etc. ; ; This routine does NOT properly deal with partitions ; that start on cylinder 0 or are larger than the size ; of the disk! The caller must ensure that these cases ; do not occur. ; ; NewMasterBootCode - if non-0, then new master boot code will ; be written regardless of the current state of the MBR. ; ; Return Value: ; ; Partition id of newly created partition. ; -1 means failure ; ;-- DiskHandle equ dword ptr [bp+6] DiskHandlel equ word ptr [bp+6] DiskHandleh equ word ptr [bp+8] SectorBuffer equ dword ptr [bp+10] SectorBufferl equ word ptr [bp+10] SectorBufferh equ word ptr [bp+12] MinimumSectorCount equ dword ptr [bp+14] MinimumSectorCountl equ word ptr [bp+14] MinimumSectorCounth equ word ptr [bp+16] NewMasterBootCode equ word ptr [bp+18] ExtSecCnth equ word ptr [bp-2] ExtSecCntl equ word ptr [bp-4] ExtSecCnt equ dword ptr [bp-4] Cylinders equ word ptr [bp-6] Heads equ word ptr [bp-8] SectorsPerTrack equ byte ptr [bp-9] Int13UnitNumber equ byte ptr [bp-10] SectorsPerCylinder equ word ptr [bp-12] DiskSizeh equ word ptr [bp-14] DiskSizel equ word ptr [bp-16] DiskSize equ dword ptr [bp-16] DiskId equ word ptr [bp-18] public _MakePartitionAtEndOfEmptyDisk _MakePartitionAtEndOfEmptyDisk proc far push bp mov bp,sp sub sp,18 push ds push es push bx push si push di ; ; Get disk info. This also makes sure the handle is open, ; calculates the number of sectors we can address on the disk, ; and the number of sectors in a cylinder. ; call near ptr pGetDiskValues jnc @f mov ax,0ffffh jc mpaod_4 @@: ; ; Read the mbr. ; push SectorBuffer push 1 push 0 push 0 push DiskHandle call _ReadDisk add sp,14 cmp ax,0 jne @f dec ax ; ax = -1 for error return jmp mpaod_4 ; ; Check the mbr. If not valid, make it valid. ; @@: les di,SectorBuffer cmp NewMasterBootCode,0 jnz makembr ; caller wants new master boot code cmp byte ptr es:[di+510],055h jne makembr cmp byte ptr es:[di+511],0aah je mbrok makembr: mov byte ptr es:[di+510],055h mov byte ptr es:[di+511],0aah mov si,offset DGROUP:_x86BootCode mov cx,1b8h/2 ; don't overwrite NTFT sig or table rep movsw mbrok: ; ; Make sure all entries are empty. ; lds si,SectorBuffer add si,1beh mov cx,0 mov ax,cx dec ax ; ax = -1 cmp [si+04h],cl jnz mpaod_4 cmp [si+14h],cl jnz mpaod_4 cmp [si+24h],cl jnz mpaod_4 cmp [si+34h],cl jnz mpaod_4 ; ; Calculate the starting sector. We don't worry about aligning ; the end sector because in the conventional int13 case we ; calculated the disk size based on cylinder count, which means ; it is guaranteed to be aligned; in the xint13 case CHS isn't ; even relevent and in any case there won't be any partitions ; on the disk after this one. ; mov ax,DiskSizel sub ax,MinimumSectorCountl mov dx,DiskSizeh sbb dx,MinimumSectorCounth ; dx:ax = start sector of partition mov [si+8],ax mov [si+10],dx ; save in partition table div SectorsPerCylinder ; ax = cyl, dx = sector within cyl sub [si+8],dx ; note: dx is zero if already aligned sbb [si+10],cx ; partition table has aligned start mov ax,MinimumSectorCountl add ax,dx ; grow to account for alignment mov [si+12],ax mov ax,MinimumSectorCounth adc ax,cx mov [si+14],ax ; put adjusted size in partition table ; ; Make the partition active. ; mov byte ptr [si],80h ; ; Fill in CHS values for the partition ; mov ax,[si+8] mov dx,[si+10] ; dx:ax = start sector inc si ; ds:si -> start CHS in part table call pMakeCHS mov ax,[si+7] add ax,[si+11] mov dx,[si+9] adc dx,[si+13] sub ax,1 sbb dx,0 ; dx:ax = last sector add si,4 ; ds:si -> end CHS in part table call pMakeCHS ; al = overflow flag ; ; Figure out the system id. ; al is xint13 required flag ; mov dx,PARTCLASS_FAT call pDetermineSystemId mov [si-1],al ; store away system id ; ; Build a partition record for this guy and add to list. ; sub si,5 ; ds:si -> partition table entry call pNewPartitionRecord cmp ax,0ffffh je mpaod_4 ; error, ax set for return ; ; The mbr is ready, write it out. ; push ax push SectorBuffer push 1 push 0 push 0 push DiskHandle call _WriteDisk add sp,14 mov dx,ax pop ax ; ax = partition id cmp dx,0 ; write error? jnz mpaod_4 ; no, ax = partition id for exit mov ax,0ffffh ; set ax for error return mpaod_4: pop di pop si pop bx pop es pop ds leave retf _MakePartitionAtEndOfEmptyDisk endp ;++ ; ; INT ; _far ; ReinitializePartitionTable( ; IN HDISK DiskHandle, ; OUT FPVOID SectorBuffer ; ); ; ; Routine Description: ; ; This routine wipes a disk completely clean by clearning ; the partition table and writing new boot code. ; ; NOTE: after this routine, partition ids may change! ; ; Arguments: ; ; DiskHandle - supplies handle to open disk, from OpenDisk(). ; ; SectorBuffer - supplies a pointer to a buffer suitable for use ; for i/o of a single sector. This buffer must not ; cross a DMA boundary, but the caller is responsible ; for ensuring this. ; ; Return Value: ; ; The new number of total partitions, -1 if error ; ;-- DiskHandle equ dword ptr [bp+6] SectorBuffer equ dword ptr [bp+10] public _ReinitializePartitionTable _ReinitializePartitionTable proc far push bp mov bp,sp push ds push es push si push di pushf ; ; This is very simple. Move template boot code ; into the sector buffer and write it out. ; les di,SectorBuffer push DGROUP pop ds mov si,OFFSET DGROUP:_x86BootCode mov cx,512/2 cld rep movsw push SectorBuffer push 1 push 0 push 0 push DiskHandle call _WriteDisk add sp,14 cmp ax,0 jnz rpt2 dec ax ; ax = -1 for return jmp short rpt8 ; ; Now go through the partition list, removing all entries that were ; on this disk. ; rpt2: les di,DiskHandle mov bx,es:[di].DiskInfoDiskId ; bx = id of disk we're wiping mov dx,[PartitionListCount] mov si,OFFSET DGROUP:PartitionList ; ds:si = &PartitionList .errnz PartInfoNext rpt3: mov cx,[si].PartInfoNextl or cx,[si].PartInfoNexth jz rpt5 ; done les di,[si].PartInfoNext ; es:di -> current, ds:si -> prev cmp es:[di].PartInfoDiskId,bx ; partition on disk we wiped? jne rpt4 ; no dec dx ; one fewer total partitions mov ax,es:[di].PartInfoNextl mov [si].PartInfoNextl,ax mov ax,es:[di].PartInfoNexth mov [si].PartInfoNexth,ax ; prev->next = current->next push bx push ds push DGROUP pop ds ; make sure we're addressing DGROUP push es push di call _free ; free(current) add sp,4 pop ds pop bx ; restore disk id jmp short rpt3 rpt4: mov ax,es mov ds,ax mov si,di ; prev = current jmp short rpt3 rpt5: push DGROUP pop ds mov [PartitionListCount],dx ; save new count mov ax,dx ; ax = return value ; ; Now reassign partition ids so they are contiguous. ; mov si,OFFSET DGROUP:PartitionList rpt6: mov cx,[si].PartInfoNextl or cx,[si].PartInfoNexth jz rpt8 dec dx lds si,[si].PartInfoNext mov [si].PartInfoDiskId,dx jmp short rpt6 rpt8: popf pop di pop si pop es pop ds leave retf _ReinitializePartitionTable endp ; ; dx:ax = sector to translate ; ds:si -> CHS bytes ; pMakeCHS proc near div SectorsPerCylinder ; ax = cylinder, dx = sector in cyl cmp ax,Cylinders ; overflow? jb chsok ; no, continue push 1 ; overflow flag mov ax,Cylinders dec ax ; store max cylinder in table jmp short chs1 chsok: push 0 ; no overflow flag chs1: mov cx,ax xchg cl,ch shl cl,6 ; ; small divide is acceptable here since head is 1-255, sector is ; 1-63 (and thus max sector within cyl is (63*256)-1). ; mov ax,dx div SectorsPerTrack ; al = head, ah = sector inc ah ; sector is 1-based or cl,ah ; cx has cyl/sector in int13 format storechs: mov [si+1],cx ; store in partition table entry mov [si],al ; store head in partition table entry pop ax ; return overflow flag ret pMakeCHS endp ; ; ds:si -> partition table entry with lba and sysid fields filled in ; preserves none ; returns new part ordinal or -1 ; pNewPartitionRecord proc near ; ; Allocate a new partition record ; push ds push si push DGROUP pop ds mov si,OFFSET DGROUP:PartitionListCount push [si] ; save count for later inc word ptr [si] push SIZE PART_INFO call _malloc add sp,2 pop bx ; restore partition count pop si pop ds ; ds:si -> partition table entry mov cx,ax or cx,dx jnz @f ; malloc ok mov ax,0ffffh jz npr_done ; return failure @@: push bx mov cx,DGROUP mov es,cx mov di,OFFSET DGROUP:PartitionList ; es:di = &PartitionList .errnz PartInfoNext mov cx,es:[di] ; get current head of list in bx:cx mov bx,es:[di+2] mov es:[di],ax mov es:[di+2],dx ; insert new record at head of list mov es,dx mov di,ax ; es:di -> new record mov es:[di].PartInfoNextl,cx mov es:[di].PartInfoNexth,bx mov ax,DiskId mov es:[di].PartInfoDiskId,ax pop ax ; partition ordinal, also return val mov es:[di].PartInfoOrdinal,ax mov cx,[si+8] mov es:[di].PartInfoStartSectorl,cx mov cx,[si+10] mov es:[di].PartInfoStartSectorh,cx mov cx,[si+12] mov es:[di].PartInfoSectorCountl,cx mov cx,[si+14] mov es:[di].PartInfoSectorCounth,cx mov cl,[si+4] mov es:[di].PartInfoSystemId,cl ; ; Indicate partition not open ; mov es:[di].PartInfoPartOpen,0 mov es:[di].PartInfoDiskHandlel,0 mov es:[di].PartInfoDiskHandleh,0 npr_done: ret pNewPartitionRecord endp ; ; No params, nested in top-level routines above ; pGetDiskValues proc near ; ; Get disk info. This also makes sure the handle is open. ; push ss lea bx,DiskId push bx push ss lea bx,ExtSecCnt push bx push ss lea bx,Cylinders push bx push ss lea bx,Heads push bx push ss lea bx,SectorsPerTrack push bx push ss lea bx,Int13UnitNumber push bx push DiskHandle call _GetDiskInfoByHandle add sp,28 cmp ax,0 jnz @f stc ret ; ; Calculate the number of sectors we can address on the disk. ; Also precalculate the number of sectors in a cylinder. ; @@: mov al,SectorsPerTrack cbw mul Heads ; ax = sectors per cylinder, dx = 0 mov SectorsPerCylinder,ax cmp ExtSecCntl,dx jnz usexcnt cmp ExtSecCnth,dx jnz usexcnt mul Cylinders ; dx:ax = sectors on disk jmp short storsize usexcnt: mov dx,ExtSecCnth mov ax,ExtSecCntl storsize: mov DiskSizeh,dx mov DiskSizel,ax clc gv3: ret pGetDiskValues endp ; ; No params, nested in top-level routines above ; al = xint13 required flag ; dx = partition class ; ah = sysid if unknown class ; ; returns system id in al ; pDetermineSystemId proc near cmp dx,PARTCLASS_FAT jne tryfat32 cmp al,0 je @f mov al,0eh ; type e = fat xint13 jmp short gotsysid @@: cmp MinimumSectorCounth,0 jne bigfat cmp MinimumSectorCountl,32680 jb fat12 mov al,4 ; type 4 = fat16 jmp short gotsysid fat12: mov al,1 ; type 1 = fat12 jmp short gotsysid bigfat: mov al,6 ; type 6 = huge fat jmp short gotsysid tryfat32: cmp dx,PARTCLASS_FAT32 jne tryntfs cmp al,0 jne @f mov al,0bh ; type b = fat32 non-xint13 jmp short gotsysid @@: mov al,0ch ; type c = fat32 xint13 jmp short gotsysid tryntfs: cmp dx,PARTCLASS_NTFS jne othersys mov al,7 ; type 7 = ntfs jmp short gotsysid othersys: mov al,ah gotsysid: ret pDetermineSystemId endp end