Gene Cumm
2009-Feb-08 14:36 UTC
[syslinux] [PATCH 1/1] COMBOOT API: Add calls for directory functions; Implement for FAT; Try 2
From: Gene Cumm <gene.cumm at gmail.com> COMBOOT API: Add calls for directory functions; Implement most only for FAT (SYSLINUX). Uses INT 22h AX= 001Fh, 0020h, 0021h and 0022h to prepare for the COM32 C functions getcwd(), opendir(), readdir(), and closedir(), respectively. INT22h, AX=001Fh will return a valid value for all variants. INT22h, AX= 0020h, 0021h, and 0022h are only implemented for SYSLINUX while other variants will call comapi_err for these 3. Signed-off-by: Gene Cumm <gene.cumm at gmail.com> --- This is the second try at implementing these calls after feedback on the first. Adds an API call to obtain the current working directory. EXTLINUX will not return the correct value yet however SYSLINUX, ISOLINUX, and PXELINUX will return the correct value. For the moment, EXTLINUX will ONLY return "./" (null-terminated of course). In SYSLINUX and ISOLINUX, it presets the CurrentDirName string to "/" for safety. PXELINUX copies TFTP Prefix to CurrentDirName. Also adds API calls for opening a directory, reading one filename and some of its info and closing the directory. These API calls are only implemented in SYSLINUX for now. ISOLINUX will probably be next on my list. It uses the existing array Files for now but I can easily tweak it to use its own array (I never saw any response to my request for comments). With regards to character sets, this version should work effectively, given the patch and feedback from HPA. (Thank you again). I've fixed the special cases of the directories named "." and "..", based on the assumption that no short name shall begin with a '.' unless it is one of these two directories. The assumption is based on some material stating that '.' can not be in a short filename. Currently, I would classify this as BETA as it has had testing but only by me. I've been able to successfully handle short filenames, single entry long filenames, multi-entry filenames with an ending 0000h and multi-entry filenames ending on a directory entry line (32 bytes). Positive and negative testing results and constructive criticism are welcome. Based on current head plus the patch that HPA sent on Mon, Jan 12, 2009 at 2:59 PM EST, subject "Re: [syslinux] COMBOOT API: Add calls for directory functions; Implement for FAT.". I just tested and his patch does still apply to head without any modification needed. As before, I've checked it against the checkpatch.pl and it returned no errors. Is there a better way? linux:Documentation/CodingStyle alludes to GCC's documentation on RTL for styling. diff --git a/core/comboot.inc b/core/comboot.inc index 7210b8b..2ff5f33 100644 --- a/core/comboot.inc +++ b/core/comboot.inc @@ -1047,6 +1047,72 @@ comapi_kbdtable: stc ret +; +; INT 22h AX=001Fh Get current working directory +; +comapi_getcwd: + mov P_ES,cs + mov P_BX,CurrentDirName + clc + ret + +; +; INT 22h AX=0020h Open directory +; +%if IS_SYSLINUX +comapi_opendir: + push ds + mov ds,P_ES + mov si,P_SI + mov di,InitRD + call mangle_name + pop ds + call searchdir + jnz comapi_err ; Didn't find a directory + cmp eax,0 + jz comapi_err ; Found nothing + ;ZF is unset + call alloc_fill_dir + mov P_EAX,eax + mov P_CX,SECTOR_SIZE + mov P_SI,si + clc + ret +%else +comapi_opendir equ comapi_err +%endif + +; +; INT 22h AX=0021h Read directory +; +%if IS_SYSLINUX +comapi_readdir: + mov es,P_ES + mov di,P_DI + mov si,P_SI + call readdir + mov P_EAX,eax + mov P_DL,dl + mov P_EBX,ebx + mov P_SI,si + ret +%else +comapi_readdir equ comapi_err +%endif + +; +; INT 22h AX=0022h Close directory +; +%if IS_SYSLINUX +comapi_closedir: + mov si,P_SI + call close_dir + clc + ret +%else +comapi_closedir equ comapi_err +%endif + section .data %macro int21 2 @@ -1100,6 +1166,10 @@ int22_table: dw comapi_getadv ; 001C get pointer to ADV dw comapi_writeadv ; 001D write ADV to disk dw comapi_kbdtable ; 001E keyboard remapping table + dw comapi_getcwd ; 001F get current working directory + dw comapi_opendir ; 0020 open directory + dw comapi_readdir ; 0021 read directory + dw comapi_closedir ; 0022 close directory int22_count equ ($-int22_table)/2 APIKeyWait db 0 @@ -1124,3 +1194,4 @@ err_comlarge db 'COMBOOT image too large.', CR, LF, 0 alignb 4 DOSErrTramp resd 33 ; Error trampolines ConfigName resb FILENAME_MAX +CurrentDirName resb FILENAME_MAX diff --git a/core/extlinux.asm b/core/extlinux.asm index 24d0d92..c7a51e9 100644 --- a/core/extlinux.asm +++ b/core/extlinux.asm @@ -42,6 +42,9 @@ MAX_SYMLINKS equ 64 ; Maximum number of symlinks per lookup SYMLINK_SECTORS equ 2 ; Max number of sectors in a symlink ; (should be >= FILENAME_MAX) +ROOT_DIR_WORD equ 0x002F +CUR_DIR_DWORD equ 0x00002F2E + ; ; This is what we need to do when idle ; @@ -843,6 +846,8 @@ load_config: mov si,config_name ; Save config file name mov di,ConfigName call strcpy + mov dword [CurrentDirName],CUR_DIR_DWORD ; Write './',0,0 to the CurrentDirName + call build_curdir_str mov di,ConfigName call open @@ -1515,6 +1520,9 @@ getfssec: pop ebp ret +build_curdir_str: + ret + ; ----------------------------------------------------------------------------- ; Common modules ; ----------------------------------------------------------------------------- diff --git a/core/isolinux.asm b/core/isolinux.asm index 3b97005..2c6d970 100644 --- a/core/isolinux.asm +++ b/core/isolinux.asm @@ -36,6 +36,8 @@ MAX_OPEN equ (1 << MAX_OPEN_LG2) SECTOR_SHIFT equ 11 ; 2048 bytes/sector (El Torito requirement) SECTOR_SIZE equ (1 << SECTOR_SHIFT) +ROOT_DIR_WORD equ 0x002F + ; ; This is what we need to do when idle ; @@ -1147,15 +1149,33 @@ get_fs_structures: ; Look for an isolinux directory, and if found, ; make it the current directory instead of the root ; directory. + ; Also copy the name of the directory to CurrentDirName + mov word [CurrentDirName],ROOT_DIR_WORD ; Write '/',0 to the CurrentDirName mov di,boot_dir ; Search for /boot/isolinux mov al,02h + push di call searchdir_iso + pop di jnz .found_dir mov di,isolinux_dir mov al,02h ; Search for /isolinux + push di call searchdir_iso + pop di jz .no_isolinux_dir .found_dir: + ; Copy current directory name to CurrentDirName + push si + push di + mov si,di + mov di,CurrentDirName + call strcpy + mov byte [di],0 ;done in case it's not word aligned + dec di + mov byte [di],'/' + pop di + pop si + mov [CurrentDir+dir_len],eax mov eax,[si+file_left] mov [CurrentDir+dir_clust],eax diff --git a/core/ldlinux.asm b/core/ldlinux.asm index c7f6577..32a9d2d 100644 --- a/core/ldlinux.asm +++ b/core/ldlinux.asm @@ -44,6 +44,11 @@ MAX_OPEN equ (1 << MAX_OPEN_LG2) SECTOR_SHIFT equ 9 SECTOR_SIZE equ (1 << SECTOR_SHIFT) +DIRENT_SHIFT equ 5 +DIRENT_SIZE equ (1 << DIRENT_SHIFT) + +ROOT_DIR_WORD equ 0x002F + ; ; This is what we need to do when idle ; @@ -900,19 +906,40 @@ getfattype: mov si,config_name ; Save configuration file name mov di,ConfigName call strcpy + mov word [CurrentDirName],ROOT_DIR_WORD ; Write '/',0 to the CurrentDirName mov eax,[RootDir] ; Make the root directory ... mov [CurrentDir],eax ; ... the current directory mov di,syslinux_cfg1 + push di call open + pop di jnz .config_open mov di,syslinux_cfg2 + push di call open + pop di jnz .config_open mov di,syslinux_cfg3 + push di call open + pop di jz no_config_file .config_open: + push si + mov si,di + push si + mov di,CurrentDirName + ; This is inefficient as it will copy more than needed + ; but not by too much + call strcpy + mov ax,config_name ;Cut it down + pop si + sub ax,si + mov di,CurrentDirName + add di,ax + mov byte [di],0 + pop si mov eax,[PrevDir] ; Make the directory with syslinux.cfg ... mov [CurrentDir],eax ; ... the current directory @@ -945,6 +972,37 @@ allocate_file: ret ; +; alloc_fill_dir: +; Allocate then fill a file structure for a directory starting in +; sector EAX. +; +; Assumes DS == ES == CS. +; +; If successful: +; ZF clear +; SI = file pointer +; If unsuccessful +; ZF set +; EAX clobbered +; +alloc_fill_dir: + push bx + call allocate_file + jnz .alloc_failure +.found: + mov si,bx + mov [si+file_sector],eax ; Current sector + mov dword [si+file_bytesleft],0 ; Current offset + mov [si+file_left],eax ; Beginning sector + pop bx + ret + +.alloc_failure: + pop bx + xor eax,eax ; ZF <- 1 + ret + +; ; search_dos_dir: ; Search a specific directory for a pre-mangled filename in ; MangledBuf, in the directory starting in sector EAX. @@ -1191,6 +1275,18 @@ close_file: .closed: ret ; +; close_dir: +; Deallocates a directory structure (pointer in SI) +; Assumes CS == DS. +; +close_dir: + and si,si + jz .closed + mov dword [si],0 ; First dword == file_sector + xor si,si +.closed: ret + +; ; searchdir: ; ; Open a file @@ -1224,9 +1320,17 @@ searchdir: cmp al,'/' jne .findend .endpath: - xchg si,di + xchg si,di ; GRC: si begin; di end[ /]+1 pop eax ; <A> Current directory sector + ; GRC Here I need to check if di-1 = si which signifies + ; we have the desired directory in EAX + ; What about where the file name = "."; later + mov dx,di + dec dx + cmp dx,si + jz .founddir + mov [PrevDir],eax ; Remember last directory searched push di @@ -1263,12 +1367,257 @@ searchdir: xchg eax,[si+file_sector] ; Get sector number and free file structure jmp .pathwalk ; Walk the next bit of the path + ; Found the desired directory; ZF set but EAX not 0 +.founddir: + ret + .badfile: xor eax,eax mov [si],eax ; Free file structure .notfound: + xor eax,eax ; Zero out EAX + ret + +; +; readdir: Read one file from a directory +; +; ES:DI -> String buffer (filename) +; DS:SI -> Pointer to open_file_t +; DS Must be the SYSLINUX Data Segment +; +; Returns the file's name in the filename string buffer +; EAX returns the file size +; EBX returns the beginning sector (currently without offsetting) +; DL returns the file type +; The directory handle's data is incremented to reflect a name read. +; +readdir: + push ecx + push bp ; Using bp to transfer between segment registers + push si + push es + push fs ; Using fs to store the current es (from COMBOOT) + push gs + mov bp,es + mov fs,bp + cmp si,0 + jz .fail +.load_handle: + mov eax,[ds:si+file_sector] ; Current sector + mov ebx,[ds:si+file_bytesleft] ; Current offset + cmp eax,0 + jz .fail +.fetch_cache: + call getcachesector +.move_current: + add si,bx ; Resume last position in sector + mov ecx,SECTOR_SIZE ; 0 out high part + sub cx,bx + shr cx,5 ; Number of entries left +.scanentry: + cmp byte [gs:si],0 + jz .fail + cmp word [gs:si+11],0Fh ; Long filename + jne .short_entry + +.vfat_entry: + push eax + push ecx + push si + push di +.vfat_ln_info: ; Get info about the line that we're on + mov al,[gs:si] + test al,40h + jz .vfat_tail_ln + and al,03Fh + mov ah,1 ; On beginning line + jmp .vfat_ck_ln + +.vfat_tail_ln: ; VFAT tail line processing (later in VFAT, head in name) + test al,80h ; Invalid data? + jnz .vfat_abort + mov ah,0 ; Not on beginning line + cmp dl,al + jne .vfat_abort ; Is this the entry we need? + mov bl,[gs:si+13] + cmp bl,[VFATCsum] + jne .vfat_abort + jmp .vfat_cp_ln + +.vfat_ck_ln: ; Load this line's VFAT CheckSum + mov bl,[gs:si+13] + mov [VFATCsum],bl +.vfat_cp_ln: ; Copy VFAT line + dec al ; Store the next line we need + mov dx,ax ; Use DX to store the progress + mov bx,13 + mov ah,0 + mul bl ; Offset for DI + add di,ax ; Increment DI + inc si ; Align to the real characters + mov cx,13 ; 13 characters per VFAT DIRENT +.vfat_cp_chr: + gs lodsw ; Unicode here!! + mov bp,ds + mov es,bp + call ucs2_to_cp ; Convert to local codepage + mov bp,fs + mov es,bp + jc .vfat_abort ;-; Use short name if character not on codepage + stosb ; CAN NOT OVERRIDE es + cmp al,0 + jz .vfat_find_next ; Null-terminated string; don't process more + cmp cx,3 + je .vfat_adj_add2 + cmp cx,9 + jne .vfat_adj_add0 +.vfat_adj_add3: inc si +.vfat_adj_add2: inc si +.vfat_adj_add1: inc si +.vfat_adj_add0: + loop .vfat_cp_chr + cmp dh,1 ; Is this the first round? + jnz .vfat_find_next +.vfat_null_term: ; Need to null-terminate if first line as we rolled over the end + mov al,0 + stosb + +.vfat_find_next: ;Find the next part of the name + pop di + pop si + pop ecx + pop eax + cmp dl,0 + jz .vfat_find_info ; We're done with the name + add si,DIRENT_SIZE + dec cx + jnz .vfat_entry + call nextsector + jnc .vfat_entry ; CF is set if we're at end + jmp .fail +.vfat_find_info: ; Fetch next entry for the size/"INode" + add si,DIRENT_SIZE + dec cx + jnz .get_info + call nextsector + jnc .get_info ; CF is set if we're at end + jmp .fail +.vfat_abort: ; Something went wrong, skip + pop di + pop si + pop ecx + pop eax + jmp .skip_entry + +.short_entry: + test byte [gs:si+11],8 ; Ignore volume labels //HERE + jnz .skip_entry + mov edx,eax ;Save current sector + push cx + push si + push di + mov cx,8 +.short_file: + gs lodsb + cmp al,'.' + jz .short_dot +.short_file_loop: + cmp al,' ' + jz .short_skip_bs + stosb + loop .short_file_loop + jmp .short_period +.short_skip_bs: ; skip blank spaces in FILENAME (before EXT) + add si,cx + dec si +.short_period: + mov al,'.' + stosb + mov cx,3 +.short_ext: + gs lodsb + cmp al,' ' + jz .short_done + stosb + loop .short_ext + jmp .short_done +.short_dot: + stosb + gs lodsb + cmp al,' ' + jz .short_done + stosb +.short_done: + mov al,0 ; Null-terminate the short strings + stosb + pop di + pop si + pop cx + mov eax,edx +.get_info: + mov ebx,[gs:si+28] ; length + mov dl,[gs:si+11] ; type +.next_entry: + add si,DIRENT_SIZE + dec cx + jnz .store_offset + call nextsector + jnc .store_sect ; CF is set if we're at end + jmp .fail + +.skip_entry: + add si,DIRENT_SIZE + dec cx + jnz .scanentry + call nextsector + jnc .scanentry ; CF is set if we're at end + jmp .fail + +.store_sect: + pop gs + pop fs + pop es + pop si + mov [ds:si+file_sector],eax + mov eax,0 ; Now at beginning of new sector + jmp .success + +.store_offset: + pop gs + pop fs + pop es + pop si ; cx=num remain; SECTOR_SIZE-(cx*32)=cur pos + shl ecx,DIRENT_SHIFT + mov eax,SECTOR_SIZE + sub eax,ecx + and eax,0ffffh + +.success: + mov [ds:si+file_bytesleft],eax + ; "INode" number = ((CurSector-RootSector)*SECTOR_SIZE + Offset)/DIRENT_SIZE) + mov ecx,eax + mov eax,[ds:si+file_sector] + sub eax,[RootDir] + shl eax,SECTOR_SHIFT + add eax,ecx + shr eax,DIRENT_SHIFT + dec eax + xchg eax,ebx ; -> EBX=INode, EAX=FileSize + jmp .done + +.fail: + pop gs + pop fs + pop es + pop si + call close_dir xor eax,eax + stc +.done: + pop bp + pop ecx +.end: ret section .bss diff --git a/core/pxelinux.asm b/core/pxelinux.asm index aac1ec2..94f23af 100644 --- a/core/pxelinux.asm +++ b/core/pxelinux.asm @@ -704,6 +704,13 @@ prefix: test byte [DHCPMagic], 04h ; Did we get a path prefix option call writestr_early call crlf + ; Set CurrentDirName + push di + mov si,PathPrefix + mov di,CurrentDirName + call strcpy + pop di + ; ; Load configuration file ; diff --git a/doc/comboot.txt b/doc/comboot.txt index 5329502..387303d 100644 --- a/doc/comboot.txt +++ b/doc/comboot.txt @@ -921,3 +921,46 @@ AX=001Eh [3.74] Keyboard remapping table version, the format code is always 1 and the length is always 256. This version can be updated simply by overwriting the version in memory; this may not be true in the future. + + +AX=001Fh [BETA-3.74+] Get current working directory + Input: AX 0001Eh + Output: ES:BX null-terminated directory name string + + Returns the current working directory. For SYSLINUX, ISOLINUX, + and PXELINUX, this will be an absolute path. For EXTLINUX, it + currently returns "./". + + +AX=0020h [BETA-3.74+] Open directory + Input: AX 001Fh + ES:SI /-null-terminated directory name + Output: SI directory handle + EAX clobbered + + Open a directory for reading. Directory name must have a trailing + "/" before the null (otherwise, you're looking for a file)(This + may change as this is a BETA call). + + +AX=0021h [BETA-3.74+] Read directory + Input: AX 0020h + SI directory handle + ES:DI buffer for file name + Output: DL Type of file + SI directory handle, or 0 if end of directory was reached + EAX Size of file + EBX Inode of file + + Read one filename from the directory, incrementing the directory + structure at SI as appropriate, storing the filename into the buffer + at ES:DI, and returning the type of the file in DL, the file length + in EAX, the INode/file number in EBX and the updated directory handle. + + +AX=0022h [BETA-3.74+] Close directory + Input: AX 001Fh + SI directory handle + Output SI 0 + + Closes a directory.
H. Peter Anvin
2009-Feb-10 06:50 UTC
[syslinux] [PATCH 1/1] COMBOOT API: Add calls for directory functions; Implement for FAT; Try 2
Cool! I've put this on a "dir" branch; when I have reviewed and tested it a bit I'll hopefully merge it into the master branch. -hpa -- H. Peter Anvin, Intel Open Source Technology Center I work for Intel. I don't speak on their behalf.
Possibly Parallel Threads
- [PATCH 1/1] COMBOOT API: Add calls for directory functions; Implement for FAT
- [PATCH 1/1] COMBOOT API: Add get current working directory call to most
- [PATCH 1/1] COMBOOT API: Add get current working directory call to most (revised)
- [PATCH 1/1] SYSLINUX/COMBOOT: Abstract searchdir and fix the opendir call
- [PATCH] mboot using module path