; Looks like a COMBOOT image but too large
comboot_too_large:
- call close_file
+ pm_call close_file
mov si,err_comlarge
call writestr
jmp enter_command
;
comapi_close:
mov si,P_SI
- call close_file
+ pm_call close_file
clc
ret
%assign HIGHMEM_SLOP 0 ; Avoid this much memory near the top
LDLINUX_MAGIC equ 0x3eb202fe ; A random number to identify ourselves with
-MAX_OPEN_LG2 equ 6 ; log2(Max number of open files)
-MAX_OPEN equ (1 << MAX_OPEN_LG2)
-
SECTOR_SHIFT equ 9
SECTOR_SIZE equ (1 << SECTOR_SHIFT)
#define FILENAME_MAX_LG2 8
#define FILENAME_MAX (1 << FILENAME_MAX_LG2)
-/* The size of open_file_t in extlinux is double of in others */
-#define MAX_OPEN_LG2 (6 - 1)
-#define MAX_OPEN (1 << MAX_OPEN_LG2)
#define MAX_SYMLINKS 64
#define SYMLINK_SECTORS 2
uint32_t pad[3]; /* pad to 2^5 == 0x20 bytes */
};
-static struct open_file_t __bss16 Files[MAX_OPEN];
+static struct open_file_t Files[MAX_OPEN];
static char SymlinkBuf[SYMLINK_SECTORS * SECTOR_SIZE + 64];
/**
- * close_file:
+ * ext2_close_file:
*
* Deallocates a file structure point by FILE
*
* @param: file, the file structure we want deallocate
*
*/
-static void close_file(struct open_file_t *file)
+static inline void close_pvt(struct open_file_t *of)
{
- if (file)
- file->file_bytesleft = 0;
+ of->file_bytesleft = 0;
}
+static void ext2_close_file(struct file *file)
+{
+ close_pvt(file->open_file);
+}
/**
* mangle_name:
* @return: ECX(of regs), number of bytes read
*
*/
-static uint32_t ext2_getfssec(struct fs_info *fs, char *buf,
- void *open_file, int sectors, int *have_more)
+static uint32_t ext2_getfssec(struct file *gfile, char *buf,
+ int sectors, bool *have_more)
{
int sector_left, next_sector, sector_idx;
int frag_start, con_sec_cnt;
int bytes_read = sectors << SECTOR_SHIFT;
- struct open_file_t *file = (struct open_file_t *)open_file;
+ struct open_file_t *file = gfile->open_file;
+ struct fs_info *fs = gfile->fs;
sector_left = (file->file_bytesleft + SECTOR_SIZE - 1) >> SECTOR_SHIFT;
if (sectors > sector_left)
sector_idx ++;
next_sector ++;
- }while(next_sector == linsector(fs, sector_idx));
+ } while (next_sector == linsector(fs, sector_idx));
#if 0
printf("You are reading data stored at sector --0x%x--0x%x\n",
getlinsec_ext(fs, buf, frag_start, con_sec_cnt);
buf += con_sec_cnt << 9;
file->file_sector += con_sec_cnt; /* next sector index */
- }while(sectors);
+ } while(sectors);
if (bytes_read >= file->file_bytesleft) {
bytes_read = file->file_bytesleft;
- *have_more = 0;
- } else
- *have_more = 1;
+ *have_more = 0;
+ } else {
+ *have_more = 1;
+ }
file->file_bytesleft -= bytes_read;
return bytes_read;
* find a dir entry, if find return it or return NULL
*
*/
-static struct ext2_dir_entry* find_dir_entry(struct fs_info *fs, struct open_file_t *file,char *filename)
+static struct ext2_dir_entry* find_dir_entry(struct fs_info *fs,
+ struct open_file_t *file,
+ char *filename)
{
- int have_more;
+ bool have_more;
char *EndBlock = trackbuf + (SecPerClust << SECTOR_SHIFT);;
struct ext2_dir_entry *de;
+ struct file xfile;
+
+ /* Fake out a VFS file structure */
+ xfile.fs = fs;
+ xfile.open_file = file;
/* read a clust at a time */
- ext2_getfssec(fs, trackbuf, file, SecPerClust, &have_more);
+ ext2_getfssec(&xfile, trackbuf, SecPerClust, &have_more);
de = (struct ext2_dir_entry *)trackbuf;
while (1) {
if ((char *)de >= (char *)EndBlock) {
if (!have_more)
return NULL;
- ext2_getfssec(fs, trackbuf, file,SecPerClust,&have_more);
+ ext2_getfssec(&xfile, trackbuf, SecPerClust, &have_more);
de = (struct ext2_dir_entry *)trackbuf;
}
}
-static char* do_symlink(struct fs_info *fs, struct open_file_t *file,
- uint32_t file_len, char *filename)
+static char *do_symlink(struct fs_info *fs, struct open_file_t *file,
+ uint32_t file_len, char *filename)
{
- int flag, have_more;
+ int flag;
+ bool have_more;
char *SymlinkTmpBuf = trackbuf;
char *lnk_end;
- char *SymlinkTmpBufEnd = trackbuf + SYMLINK_SECTORS * SECTOR_SIZE+64;
+ char *SymlinkTmpBufEnd = trackbuf + SYMLINK_SECTORS * SECTOR_SIZE+64;
+ struct file xfile;
+ xfile.fs = fs;
+ xfile.open_file = file;
flag = this_inode.i_file_acl ? SecPerClust : 0;
if (this_inode.i_blocks == flag) {
/* fast symlink */
- close_file(file); /* we've got all we need */
+ close_pvt(file); /* we've got all we need */
memcpy(SymlinkTmpBuf, this_inode.i_block, file_len);
lnk_end = SymlinkTmpBuf + file_len;
} else {
/* slow symlink */
- ext2_getfssec(fs, SymlinkTmpBuf,file,SYMLINK_SECTORS,&have_more);
+ ext2_getfssec(&xfile, SymlinkTmpBuf, SYMLINK_SECTORS, &have_more);
lnk_end = SymlinkTmpBuf + file_len;
}
inr = de->d_inode;
filename += de->d_name_len;
- close_file(open_file);
+ close_pvt(open_file);
goto open;
}
/* Otherwise, something bad ... */
err:
- close_file(open_file);
+ close_pvt(open_file);
err_noclose:
file_len = 0;
open_file = NULL;
const struct fs_ops ext2_fs_ops = {
.fs_name = "ext2",
+ .fs_flags = 0,
.fs_init = ext2_fs_init,
.searchdir = ext2_searchdir,
.getfssec = ext2_getfssec,
+ .close_file = ext2_close_file,
.mangle_name = ext2_mangle_name,
.unmangle_name = ext2_unmangle_name,
.load_config = ext2_load_config
; fs.c
extern fs_init, searchdir, getfssec, mangle_name, load_config
- extern unmangle_name
+ extern unmangle_name, close_file
%if IS_SYSLINUX
; fat.c
#define FILENAME_MAX_LG2 8
#define FILENAME_MAX (1 << FILENAME_MAX_LG2)
-#define MAX_OPEN_LG2 6
-#define MAX_OPEN (1 << MAX_OPEN_LG2)
#define ROOT_DIR_WORD 0x002f
/* file structure. This holds the information for each currently open file */
uint32_t file_left; /* number of sectors left */
};
-static struct open_file_t __bss16 Files[MAX_OPEN];
+static struct open_file_t Files[MAX_OPEN];
extern uint8_t SecPerClust;
/* Deallocates a file structure */
-static void close_file(struct open_file_t *file)
+static inline void close_pvt(struct open_file_t *of)
{
- if ( file )
- file->file_sector = 0;
+ of->file_sector = 0;
}
+static void vfat_close_file(struct file *file)
+{
+ close_pvt(file->open_file);
+}
/* Deallocates a directory structure */
* @return: number of bytes read
*
*/
-static uint32_t vfat_getfssec(struct fs_info *fs, char *buf,
- void *open_file, int sectors, int *have_more)
+static uint32_t vfat_getfssec(struct file *gfile, char *buf, int sectors,
+ bool *have_more)
{
uint32_t bytes_read = sectors << SECTOR_SHIFT;
- struct open_file_t *file = (struct open_file_t *)open_file;
+ struct open_file_t *file = gfile->open_file;
+ struct fs_info *fs = gfile->fs;
if ( sectors > file->file_left )
sectors = file->file_left;
char *p;
struct open_file_t *open_file = NULL;
- if (file->fs != this_fs)
- this_fs = file->fs;
+ this_fs = file->fs;
dir_sector = CurrentDir;
if ( *filename == '/' ) {
mangle_dos_name(MangleBuf, filename);
/* close it before open a new dir file */
- close_file(open_file);
+ if (open_file)
+ close_pvt(open_file);
open_file = search_dos_dir(file->fs, MangleBuf, dir_sector, &file_len, &attr);
- if (! open_file)
+ if (!open_file)
goto fail;
dir_sector = open_file->file_sector;
open_file->file_left = ( file_len + SECTOR_SIZE -1 ) >> SECTOR_SHIFT;
}
- file->file_len = file_len;
- file->open_file = (void *)open_file;
+ file->file_len = file_len;
+ file->open_file = open_file;
}
static void vfat_load_config(com32sys_t *regs)
{
- char syslinux_cfg1[] = "/boot/syslinux/syslinux.cfg";
- char syslinux_cfg2[] = "/syslinux/syslinux.cfg";
- char syslinux_cfg3[] = "/syslinux.cfg";
- char config_name[] = "syslinux.cfg";
+ static const char syslinux_cfg1[] = "/boot/syslinux/syslinux.cfg";
+ static const char syslinux_cfg2[] = "/syslinux/syslinux.cfg";
+ static const char syslinux_cfg3[] = "/syslinux.cfg";
+ static const char config_name[] = "syslinux.cfg";
-
- char *syslinux_cfg[]= {syslinux_cfg1, syslinux_cfg2, syslinux_cfg3};
+ const char * const syslinux_cfg[] =
+ { syslinux_cfg1, syslinux_cfg2, syslinux_cfg3 };
com32sys_t oregs;
int i = 0;
CurrentDir = PrevDir;
}
-static inline void bsr(uint8_t *res, int num)
+static inline __constfunc uint32_t bsr(uint32_t num)
{
- int i = 0;
- while (num >>= 1)
- i ++;
- *res = i;
+ asm("bsrl %1,%0" : "=r" (num) : "rm" (num));
+ return num;
}
/* init. the fs meta data, return the block size in bits */
RootDirSize = (fat.bxRootDirEnts+SECTOR_SIZE/32-1) >> (SECTOR_SHIFT-5);
DataArea = RootDirArea + RootDirSize;
- bsr(&ClustShift, fat.bxSecPerClust);
+ ClustShift = bsr(fat.bxSecPerClust);
ClustByteShift = ClustShift + SECTOR_SHIFT;
ClustMask = fat.bxSecPerClust - 1;
ClustSize = fat.bxSecPerClust << SECTOR_SHIFT;
const struct fs_ops vfat_fs_ops = {
.fs_name = "vfat",
+ .fs_flags = 0,
.fs_init = vfat_fs_init,
.searchdir = vfat_searchdir,
.getfssec = vfat_getfssec,
+ .close_file = vfat_close_file,
.mangle_name = vfat_mangle_name,
.unmangle_name = vfat_unmangle_name,
.load_config = vfat_load_config
#include "fs.h"
#include "cache.h"
-
-/* The this fs pointer */
+/* The currently mounted filesystem */
struct fs_info *this_fs = NULL;
struct fs_info fs;
+/* Actual file structures (we don't have malloc yet...) */
+static struct file Files[MAX_OPEN];
+
+/*
+ * Get an empty file structure
+ */
+static struct file *alloc_file(void)
+{
+ int i;
+ struct file *file = Files;
+
+ for (i = 0; i < MAX_OPEN; i++) {
+ if (!file->open_file)
+ return file;
+ }
+
+ return NULL;
+}
+
+/*
+ * Close and free a file structure
+ */
+static inline void free_file(struct file *file)
+{
+ memset(file, 0, sizeof *file);
+}
+
+static void _close_file(struct file *file)
+{
+ if (file->open_file)
+ file->fs->fs_ops->close_file(file);
+ free_file(file);
+}
+
+/*
+ * Convert between a 16-bit file handle and a file structure
+ */
+static inline uint16_t file_to_handle(struct file *file)
+{
+ return file ? (file - Files)+1 : 0;
+}
+static inline struct file *handle_to_file(uint16_t handle)
+{
+ return handle ? &Files[handle-1] : NULL;
+}
void load_config(com32sys_t *regs)
{
void mangle_name(com32sys_t *regs)
{
- char *src = (char *)MK_PTR(regs->ds, regs->esi.w[0]);
- char *dst = (char *)MK_PTR(regs->es, regs->edi.w[0]);
+ char *src = MK_PTR(regs->ds, regs->esi.w[0]);
+ char *dst = MK_PTR(regs->es, regs->edi.w[0]);
this_fs->fs_ops->mangle_name(dst, src);
}
void getfssec(com32sys_t *regs)
{
int sectors;
- int have_more;
+ bool have_more;
uint32_t bytes_read;
char *buf;
- struct file file;
+ struct file *file;
+ uint16_t handle;
sectors = regs->ecx.w[0];
- buf = (char *)MK_PTR(regs->es, regs->ebx.w[0]);
- file.open_file = MK_PTR(regs->ds, regs->esi.w[0]);
- file.fs = this_fs;
- bytes_read = this_fs->fs_ops->getfssec(file.fs, buf, file.open_file, sectors, &have_more);
+ handle = regs->esi.w[0];
+ file = handle_to_file(handle);
+
+ buf = MK_PTR(regs->es, regs->ebx.w[0]);
+ bytes_read = file->fs->fs_ops->getfssec(file, buf, sectors, &have_more);
- /* if we reach the EOF, set the si to be NULL */
- if (!have_more)
+ /*
+ * If we reach EOF, the filesystem driver will have already closed
+ * the underlying file... this really should be cleaner.
+ */
+ if (!have_more) {
+ _close_file(file);
regs->esi.w[0] = 0;
+ }
regs->ecx.l = bytes_read;
}
void searchdir(com32sys_t *regs)
{
char *filename = (char *)MK_PTR(regs->ds, regs->edi.w[0]);;
- struct file file;
+ struct file *file;
-#if 1
+#if 0
printf("filename: %s\n", filename);
#endif
- memset(&file, 0, sizeof file);
- file.fs = this_fs;
-
- this_fs->fs_ops->searchdir(filename, &file);
- regs->esi.w[0] = OFFS_WRT(file.open_file, 0);
- regs->eax.l = file.file_len;
- if (file.open_file)
- regs->eflags.l &= ~EFLAGS_ZF;
- else
- regs->eflags.l |= EFLAGS_ZF;
+ file = alloc_file();
+
+ if (file) {
+ file->fs = this_fs;
+ file->fs->fs_ops->searchdir(filename, file);
+
+ if (file->open_file) {
+ regs->esi.w[0] = file_to_handle(file);
+ regs->eax.l = file->file_len;
+ regs->eflags.l &= ~EFLAGS_ZF;
+ return;
+ }
+ }
+
+ /* failure... */
+ regs->esi.w[0] = 0;
+ regs->eax.l = 0;
+ regs->eflags.l |= EFLAGS_ZF;
}
+void close_file(com32sys_t *regs)
+{
+ uint16_t handle = regs->esi.w[0];
+ struct file *file;
+
+ if (handle) {
+ file = handle_to_file(handle);
+ _close_file(file);
+ }
+}
/*
* it will do:
void fs_init(com32sys_t *regs)
{
int blk_shift;
- struct fs_ops *ops = (struct fs_ops*)regs->eax.l;
-
+ const struct fs_ops *ops = (const struct fs_ops *)regs->eax.l;
+
/* set up the fs stucture */
- fs.fs_name = ops->fs_name;
fs.fs_ops = ops;
- if (!strcmp(fs.fs_name, "pxe"))
- fs.fs_dev = NULL;
+
+ /* this is a total hack... */
+ if (ops->fs_flags & FS_NODEV)
+ fs.fs_dev = NULL; /* Non-device filesystem */
else
- fs.fs_dev = device_init(regs->edx.b[0], regs->edx.b[1], regs->ecx.l, \
- regs->esi.w[0], regs->edi.w[0]);
+ fs.fs_dev = device_init(regs->edx.b[0], regs->edx.b[1], regs->ecx.l,
+ regs->esi.w[0], regs->edi.w[0]);
+
this_fs = &fs;
/* invoke the fs-specific init code */
.ret: ret
.stack_full:
- call close_file
+ pm_call close_file
xor ax,ax ; ZF <- 1
pop bx
ret
-;
-; close_file:
-; Deallocates a file structure (pointer in SI)
-; Assumes CS == DS.
-;
-close_file:
- and si,si
- jz .closed
- mov dword [si],0 ; First dword == file_bytesleft
- xor si,si
-.closed: ret
-
-
getc:
push bx
push si
push si
mov bx,[CurrentGetC]
mov si,[bx+gc_file]
- call close_file
+ pm_call close_file
add bx,getc_file_size
mov [CurrentGetC],bx
pop si
#include "core.h"
#include "disk.h"
+/*
+ * Maximum number of open files. This is *currently* constrained by the
+ * fact that PXE needs to be able to fit all its packet buffers into a
+ * 64K segment; this should be fixed by moving the packet buffers to high
+ * memory.
+ */
+#define MAX_OPEN_LG2 5
+#define MAX_OPEN (1 << MAX_OPEN_LG2)
+
struct fs_info {
- char *fs_name;
- struct fs_ops *fs_ops;
+ const struct fs_ops *fs_ops;
struct device *fs_dev;
};
+struct open_file_t; /* Filesystem private structure */
+
struct file {
- void* open_file; /* points to the fs-specific open_file_t */
- struct fs_info *fs;
+ struct open_file_t *open_file; /* Filesystem private data */
+ struct fs_info *fs;
uint32_t file_len;
};
+enum fs_flags {
+ FS_NODEV = 1,
+};
struct fs_ops {
/* in fact, we use fs_ops structure to find the right fs */
- char *fs_name;
+ const char *fs_name;
+ enum fs_flags fs_flags;
int (*fs_init)(struct fs_info *);
void (*searchdir)(char *, struct file *);
- uint32_t (*getfssec)(struct fs_info *, char *, void * , int, int *);
+ uint32_t (*getfssec)(struct file *, char *, int, bool *);
+ void (*close_file)(struct file *);
void (*mangle_name)(char *, char *);
int (*unmangle_name)(char *, char *);
void (*load_config)(com32sys_t *);
#define FILENAME_MAX_LG2 8
#define FILENAME_MAX (1 << FILENAME_MAX_LG2)
-#define MAX_OPEN_LG2 6
-#define MAX_OPEN (1 << MAX_OPEN_LG2)
#define ISO_SECTOR_SHIFT 11
#define ISO_SECTOR_SIZE (1 << ISO_SECTOR_SHIFT)
#define ROOT_DIR_WORD 0x002f
uint32_t file_left;
};
-static struct open_file_t __bss16 Files[MAX_OPEN];
+static struct open_file_t Files[MAX_OPEN];
struct dir_t {
uint32_t dir_lba; /* Directory start (LBA) */
* Deallocates a file structure
*
*/
-static void close_file(struct open_file_t *file)
+static inline void close_pvt(struct open_file_t *file)
{
- if (file)
- file->file_sector = 0;
+ file->file_sector = 0;
+}
+
+static void iso_close_file(struct file *file)
+{
+ close_pvt(file->open_file);
}
/**
*res = dir;
/* we can close it now */
- close_file(file);
+ close_pvt(file);
/* Mark we got a directory */
return 1;
goto found;
}
err:
- close_file(open_file);
+ close_pvt(open_file);
file_len = 0;
open_file = NULL;
CurrentDir.dir_len = open_file->file_bytesleft;
CurrentDir.dir_clust = open_file->file_left;
CurrentDir.dir_lba = open_file->file_sector;
- close_file(open_file);
+ close_pvt(open_file);
#ifdef DEBUG
printf("isolinux directory at LBA = 0x%x\n", CurrentDir.dir_lba);
const struct fs_ops iso_fs_ops = {
.fs_name = "iso",
+ .fs_flags = 0,
.fs_init = iso_fs_init,
.searchdir = iso_searchdir,
.getfssec = iso_getfssec,
+ .close_file = iso_close_file,
.mangle_name = iso_mangle_name,
.unmangle_name = iso_unmangle_name,
.load_config = iso_load_config
NULLOFFSET equ 0 ; Position in which to look
retry_count equ 6 ; How patient are we with the BIOS?
%assign HIGHMEM_SLOP 128*1024 ; Avoid this much memory near the top
-MAX_OPEN_LG2 equ 6 ; log2(Max number of open files)
-MAX_OPEN equ (1 << MAX_OPEN_LG2)
SECTOR_SHIFT equ 11 ; 2048 bytes/sector (El Torito requirement)
SECTOR_SIZE equ (1 << SECTOR_SHIFT)
#include <minmax.h>
#include <sys/cpu.h>
-#define MAX_OPEN_LG2 5
-#define MAX_OPEN (1 << MAX_OPEN_LG2)
#define FILENAME_MAX_LG2 7
#define FILENAME_MAX (1 << FILENAME_MAX_LG2)
#define GPXE 1
#define USE_PXE_PROVIDED_STACK 0
-static struct open_file_t __bss16 Files[MAX_OPEN];
+static struct open_file_t Files[MAX_OPEN];
static char *err_nopxe = "No !PXE or PXENV+ API found; we're dead...\n";
static char *err_pxefailed = "PXE API call failed, error ";
*/
static void free_socket(struct open_file_t *file)
{
- /* tftp_pktbuf is not cleared */
+ /* tftp_nextport and tftp_pktbuf are not cleared */
memset(file, 0, offsetof(struct open_file_t, tftp_nextport));
}
+static void pxe_close_file(struct file *file)
+{
+ /*
+ * XXX: we really should see if the connection is open as send
+ * a courtesy ERROR packet so the server knows the connection is
+ * dead.
+ */
+ free_socket(file->open_file);
+}
+
/**
* Take a nubmer of bytes in memory and convert to lower-case hxeadecimal
*
* @return: the bytes read
*
*/
-static uint32_t pxe_getfssec(struct fs_info *fs, char *buf,
- void *open_file, int blocks, int *have_more)
+static uint32_t pxe_getfssec(struct file *gfile, char *buf,
+ int blocks, bool *have_more)
{
- struct open_file_t *file = (struct open_file_t *)open_file;
+ struct open_file_t *file = gfile->open_file;
int count = blocks;
int chunk;
int bytes_read = 0;
- sti();
- fs = NULL; /* drop the compile warning message */
-
count <<= TFTP_BLOCKSIZE_LG2;
while (count) {
fill_buffer(file); /* If we have no 'fresh' buffer, get it */
*have_more = 1;
} else if (file->tftp_goteof) {
/*
- * The socket is closed and the buffer dranined
- * close socket structure and re-init for next user
+ * The socket is closed and the buffer drained; the caller will
+ * call close_file and therefore free the socket.
*/
- free_socket(file);
*have_more = 0;
}
const struct fs_ops pxe_fs_ops = {
.fs_name = "pxe",
+ .fs_flags = FS_NODEV,
.fs_init = pxe_fs_init,
.searchdir = pxe_searchdir,
.getfssec = pxe_getfssec,
+ .close_file = pxe_close_file,
.mangle_name = pxe_mangle_name,
.unmangle_name = pxe_unmangle_name,
.load_config = pxe_load_config
NULLOFFSET equ 4 ; Position in which to look
REBOOT_TIME equ 5*60 ; If failure, time until full reset
%assign HIGHMEM_SLOP 128*1024 ; Avoid this much memory near the top
-MAX_OPEN_LG2 equ 5 ; log2(Max number of open sockets)
-MAX_OPEN equ (1 << MAX_OPEN_LG2)
-PKTBUF_SIZE equ (65536/MAX_OPEN) ; Per-socket packet buffer size
TFTP_PORT equ htons(69) ; Default TFTP port
-; Desired TFTP block size
-; For Ethernet MTU is normally 1500. Unfortunately there seems to
-; be a fair number of networks with "substandard" MTUs which break.
-; The code assumes TFTP_LARGEBLK <= 2K.
-TFTP_MTU equ 1440
-TFTP_LARGEBLK equ (TFTP_MTU-20-8-4) ; MTU - IP hdr - UDP hdr - TFTP hdr
-; Standard TFTP block size
TFTP_BLOCKSIZE_LG2 equ 9 ; log2(bytes/block)
TFTP_BLOCKSIZE equ (1 << TFTP_BLOCKSIZE_LG2)
dw 0 ; SS of base stack
KeepPXE db 0 ; Should PXE be kept around?
-;
-; TFTP commands
-;
-tftp_tail db 'octet', 0 ; Octet mode
-tsize_str db 'tsize' ,0 ; Request size
-tsize_len equ ($-tsize_str)
- db '0', 0
-blksize_str db 'blksize', 0 ; Request large blocks
-blksize_len equ ($-blksize_str)
- asciidec TFTP_LARGEBLK
- db 0
-tftp_tail_len equ ($-tftp_tail)
-
alignz 2
;
-; Options negotiation parsing table (string pointer, string len, offset
-; into socket structure)
-;
-tftp_opt_table:
- dw tsize_str, tsize_len, tftp_filesize
- dw blksize_str, blksize_len, tftp_blksize
-tftp_opts equ ($-tftp_opt_table)/6
-
-;
; Error packet to return on TFTP protocol error
; Most of our errors are OACK parsing errors, so use that error code
;