aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--BUGS4
-rw-r--r--MCONFIG9
-rw-r--r--MCONFIG.build33
-rw-r--r--MCONFIG.embedded30
-rw-r--r--Makefile2
-rw-r--r--TODO22
-rwxr-xr-xcodepage/cptable.pl40
-rw-r--r--com32/MCONFIG35
-rw-r--r--com32/Makefile2
-rw-r--r--com32/include/com32.h13
-rw-r--r--com32/include/dev.h1
-rw-r--r--com32/include/dirent.h18
-rw-r--r--com32/include/netinet/in.h48
-rw-r--r--com32/include/string.h1
-rw-r--r--com32/include/sys/cpu.h26
-rw-r--r--com32/include/sys/dirent.h45
-rw-r--r--com32/include/syslinux/boot.h2
-rw-r--r--com32/include/syslinux/idle.h1
-rw-r--r--com32/include/syslinux/pmapi.h70
-rw-r--r--com32/include/syslinux/pxe.h485
-rw-r--r--com32/include/syslinux/pxe_api.h571
-rw-r--r--com32/lib/MCONFIG17
-rw-r--r--com32/lib/Makefile35
-rw-r--r--com32/lib/closedir.c30
-rw-r--r--com32/lib/com32.ld68
-rw-r--r--com32/lib/fprintf.c2
-rw-r--r--com32/lib/lmalloc.c57
-rw-r--r--com32/lib/lstrdup.c18
-rw-r--r--com32/lib/memmove.S31
-rw-r--r--com32/lib/opendir.c41
-rw-r--r--com32/lib/printf.c2
-rw-r--r--com32/lib/qsort.c4
-rw-r--r--com32/lib/readdir.c57
-rw-r--r--com32/lib/strpcpy.c20
-rw-r--r--com32/lib/sys/entry.S60
-rw-r--r--com32/lib/sys/file.h18
-rw-r--r--com32/lib/sys/fileclose.c11
-rw-r--r--com32/lib/sys/fileread.c51
-rw-r--r--com32/lib/sys/fstat.c4
-rw-r--r--com32/lib/sys/gpxe.c25
-rw-r--r--com32/lib/sys/open.c28
-rw-r--r--com32/lib/sys/openmem.c61
-rw-r--r--com32/lib/sys/readdir.c30
-rw-r--r--com32/lib/sys/stdcon_write.c19
-rw-r--r--com32/lib/sys/vesa/initvesa.c52
-rw-r--r--com32/lib/sys/zfile.c8
-rw-r--r--com32/lib/syslinux/idle.c24
-rw-r--r--com32/lib/syslinux/memscan.c9
-rw-r--r--com32/lib/syslinux/pxe_dns.c13
-rw-r--r--com32/lib/syslinux/pxe_get_cached.c37
-rw-r--r--com32/lib/syslinux/pxe_get_nic.c14
-rw-r--r--com32/lib/syslinux/run_command.c17
-rw-r--r--com32/lib/syslinux/runimage.c26
-rw-r--r--com32/lib/vdprintf.c77
-rw-r--r--com32/mboot/mboot.c2
-rw-r--r--com32/modules/Makefile4
-rw-r--r--com32/modules/dir.c177
-rw-r--r--com32/modules/host.c42
-rw-r--r--com32/rosh/rosh.c19
-rw-r--r--com32/rosh/rosh.h2
-rw-r--r--com32/sysdump/cpuid.c7
-rw-r--r--com32/sysdump/ctime.c7
-rw-r--r--com32/tools/.gitignore1
-rw-r--r--com32/tools/Makefile30
-rw-r--r--com32/tools/relocs.c693
-rw-r--r--core/Makefile79
-rw-r--r--core/abort.inc6
-rw-r--r--core/adv.inc22
-rw-r--r--core/bcopy32.inc297
-rw-r--r--core/bcopyxx.inc57
-rw-r--r--core/bios.inc8
-rw-r--r--core/bootsect.inc14
-rw-r--r--core/cache.inc114
-rw-r--r--core/call16.c27
-rw-r--r--core/callback.inc208
-rwxr-xr-xcore/checksumiso.pl51
-rw-r--r--core/cleanup.inc7
-rw-r--r--core/cmdline.inc9
-rw-r--r--core/codepage.S5
-rw-r--r--core/com32.inc431
-rw-r--r--core/comboot.inc197
-rw-r--r--core/common.inc25
-rw-r--r--core/config.inc6
-rw-r--r--core/configinit.inc2
-rw-r--r--core/conio.inc17
-rw-r--r--core/console.c22
-rw-r--r--core/cpuinit.inc99
-rw-r--r--core/diskfs.inc182
-rw-r--r--core/diskstart.inc126
-rw-r--r--core/dnsresolv.inc389
-rw-r--r--core/ext2_fs.inc183
-rw-r--r--core/extern.inc30
-rw-r--r--core/extlinux.asm915
-rw-r--r--core/font.inc6
-rw-r--r--core/fs/btrfs/btrfs.c696
-rw-r--r--core/fs/btrfs/btrfs.h292
-rw-r--r--core/fs/btrfs/crc32c.h50
-rw-r--r--core/fs/cache.c125
-rw-r--r--core/fs/chdir.c66
-rw-r--r--core/fs/diskio.c321
-rw-r--r--core/fs/ext2/bmap.c228
-rw-r--r--core/fs/ext2/ext2.c335
-rw-r--r--core/fs/ext2/ext2_fs.h310
-rw-r--r--core/fs/fat/fat.c826
-rw-r--r--core/fs/fat/fat_fs.h158
-rw-r--r--core/fs/fs.c413
-rw-r--r--core/fs/getfssec.c190
-rw-r--r--core/fs/iso9660/iso9660.c355
-rw-r--r--core/fs/iso9660/iso9660_fs.h51
-rw-r--r--core/fs/lib/close.c9
-rw-r--r--core/fs/lib/loadconfig.c24
-rw-r--r--core/fs/lib/mangle.c47
-rw-r--r--core/fs/loadhigh.c113
-rw-r--r--core/fs/newconfig.c41
-rw-r--r--core/fs/nonextextent.c13
-rw-r--r--core/fs/pxe/dhcp_option.c265
-rw-r--r--core/fs/pxe/dnsresolv.c352
-rw-r--r--core/fs/pxe/idle.c112
-rw-r--r--core/fs/pxe/portnum.c68
-rw-r--r--core/fs/pxe/pxe.c1694
-rw-r--r--core/fs/pxe/pxe.h236
-rw-r--r--core/fs/readdir.c50
-rw-r--r--core/getc.inc19
-rw-r--r--core/graphics.inc6
-rw-r--r--core/head.inc6
-rw-r--r--core/highmem.inc6
-rw-r--r--core/idle.c49
-rw-r--r--core/idle.inc47
-rw-r--r--core/include/cache.h23
-rw-r--r--core/include/codepage.h27
-rw-r--r--core/include/core.h74
-rw-r--r--core/include/ctype.h25
-rw-r--r--core/include/disk.h36
-rw-r--r--core/include/fs.h227
-rw-r--r--core/include/pmapi.h8
-rw-r--r--core/init.inc118
-rw-r--r--core/isolinux.asm615
-rw-r--r--core/kaboom.c16
-rw-r--r--core/kernel.inc3
-rw-r--r--core/layout.inc91
-rw-r--r--core/ldlinux.asm1410
-rw-r--r--core/loadhigh.inc78
-rw-r--r--core/localboot.inc6
-rw-r--r--core/lzo/enter.ash89
-rw-r--r--core/lzo/leave.ash114
-rw-r--r--core/lzo/lzo1c_d.ash184
-rw-r--r--core/lzo/lzo1f_d.ash176
-rw-r--r--core/lzo/lzo1x_d.ash401
-rw-r--r--core/lzo/lzo1x_f1.S63
-rw-r--r--core/lzo/lzo_asm.h287
-rw-r--r--core/macros.inc2
-rw-r--r--core/mem/free.c154
-rw-r--r--core/mem/init.c38
-rw-r--r--core/mem/malloc.c99
-rw-r--r--core/mem/malloc.h82
-rw-r--r--core/parsecmd.inc19
-rw-r--r--core/parseconfig.inc29
-rw-r--r--core/plaincon.inc2
-rw-r--r--core/pm.inc450
-rw-r--r--core/pmapi.c35
-rw-r--r--core/pmcall.inc70
-rw-r--r--core/prefix.inc17
-rw-r--r--core/printf.c20
-rw-r--r--core/pxeidle.inc122
-rw-r--r--core/pxelinux.asm2644
-rw-r--r--core/rllpack.c105
-rw-r--r--core/rllpack.inc164
-rw-r--r--core/runkernel.inc49
-rw-r--r--core/serirq.inc24
-rw-r--r--core/stack.inc20
-rw-r--r--core/strcasecmp.c11
-rw-r--r--core/strcpy.inc2
-rw-r--r--core/strecpy.inc28
-rw-r--r--core/strncasecmp.c24
-rw-r--r--core/syslinux.ld323
-rw-r--r--core/timer.inc46
-rw-r--r--core/ui.inc57
-rw-r--r--core/writedec.inc2
-rw-r--r--core/writehex.inc1
-rw-r--r--core/writestr.inc1
-rw-r--r--doc/comboot.txt138
-rw-r--r--dos/Makefile16
-rw-r--r--dos/argv.c4
-rw-r--r--dos/com16.ld130
-rw-r--r--dos/crt0.S29
-rw-r--r--dos/dosexe.ld131
-rw-r--r--dos/header.S54
-rw-r--r--dos/malloc.c32
-rw-r--r--dos/stdlib.h1
-rw-r--r--dos/syslinux.c72
-rw-r--r--extlinux/btrfs.h22
-rw-r--r--extlinux/fat.h62
-rw-r--r--extlinux/main.c641
-rwxr-xr-xlibinstaller/bin2c.pl4
-rw-r--r--libinstaller/setadv.c13
-rw-r--r--libinstaller/syslinux.h16
-rw-r--r--libinstaller/syslxint.h118
-rw-r--r--libinstaller/syslxmod.c334
-rw-r--r--linux/syslinux.c26
-rw-r--r--lzo/.gitignore1
-rw-r--r--lzo/LZO.TXT291
-rw-r--r--lzo/Makefile39
-rw-r--r--lzo/include/lzo/lzo1.h96
-rw-r--r--lzo/include/lzo/lzo1a.h96
-rw-r--r--lzo/include/lzo/lzo1b.h160
-rw-r--r--lzo/include/lzo/lzo1c.h160
-rw-r--r--lzo/include/lzo/lzo1f.h108
-rw-r--r--lzo/include/lzo/lzo1x.h177
-rw-r--r--lzo/include/lzo/lzo1y.h145
-rw-r--r--lzo/include/lzo/lzo1z.h150
-rw-r--r--lzo/include/lzo/lzo2a.h92
-rw-r--r--lzo/include/lzo/lzo_asm.h139
-rw-r--r--lzo/include/lzo/lzoconf.h417
-rw-r--r--lzo/include/lzo/lzodefs.h1807
-rw-r--r--lzo/include/lzo/lzoutil.h73
-rw-r--r--lzo/prepcore.c385
-rw-r--r--lzo/src/compr1b.h81
-rw-r--r--lzo/src/compr1c.h81
-rw-r--r--lzo/src/config1x.h118
-rw-r--r--lzo/src/lzo1_d.ch155
-rw-r--r--lzo/src/lzo1x_1.c50
-rw-r--r--lzo/src/lzo1x_1k.c50
-rw-r--r--lzo/src/lzo1x_1l.c50
-rw-r--r--lzo/src/lzo1x_1o.c50
-rw-r--r--lzo/src/lzo1x_9x.c881
-rw-r--r--lzo/src/lzo1x_c.ch351
-rw-r--r--lzo/src/lzo1x_d.ch466
-rw-r--r--lzo/src/lzo1x_d1.c46
-rw-r--r--lzo/src/lzo1x_d2.c46
-rw-r--r--lzo/src/lzo1x_d3.c108
-rw-r--r--lzo/src/lzo1x_o.c45
-rw-r--r--lzo/src/lzo1x_oo.ch366
-rw-r--r--lzo/src/lzo_conf.h323
-rw-r--r--lzo/src/lzo_crc.c167
-rw-r--r--lzo/src/lzo_dict.h316
-rw-r--r--lzo/src/lzo_dll.ch64
-rw-r--r--lzo/src/lzo_init.c176
-rw-r--r--lzo/src/lzo_mchw.ch242
-rw-r--r--lzo/src/lzo_ptr.c92
-rw-r--r--lzo/src/lzo_ptr.h154
-rw-r--r--lzo/src/lzo_str.c71
-rw-r--r--lzo/src/lzo_swd.ch707
-rw-r--r--lzo/src/lzo_util.c165
-rw-r--r--lzo/src/miniacc.h6553
-rw-r--r--lzo/src/stats1a.h137
-rw-r--r--lzo/src/stats1b.h142
-rw-r--r--lzo/src/stats1c.h61
-rw-r--r--memdisk/unzip.c2
-rw-r--r--modules/Makefile2
-rw-r--r--modules/gfxboot.asm1084
-rw-r--r--mtools/syslinux.c28
-rw-r--r--version2
-rw-r--r--win32/syslinux.c21
254 files changed, 32409 insertions, 10450 deletions
diff --git a/.gitignore b/.gitignore
index 7657c3d0..c7866759 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,6 +16,7 @@
*.map
*.o
*.orig
+*.raw
*.rej
*.s
*.sec
diff --git a/BUGS b/BUGS
deleted file mode 100644
index 9639da80..00000000
--- a/BUGS
+++ /dev/null
@@ -1,4 +0,0 @@
-Known bugs that have not yet been fixed:
-
-- PXELINUX: Some PXE stacks fail with routing enabled, some with
- routing disabled. Try both?
diff --git a/MCONFIG b/MCONFIG
index e9c16d38..87079a5f 100644
--- a/MCONFIG
+++ b/MCONFIG
@@ -32,15 +32,17 @@ BOOTDIR = /boot
EXTLINUXDIR = $(BOOTDIR)/extlinux
NASM = nasm
-NASMOPT = -O9999
+NASMOPT = -Ox
PERL = perl
+UPX = upx
CHMOD = chmod
CC = gcc
gcc_ok = $(shell tmpf=gcc_ok.$$$$.tmp; \
- if $(CC) $(1) -c $(topdir)/dummy.c -o $$tmpf 2>/dev/null ; \
+ if $(CC) $(GCCOPT) $(1) -c $(topdir)/dummy.c \
+ -o $$tmpf 2>/dev/null ; \
then echo '$(1)'; else echo '$(2)'; fi; \
rm -f $$tmpf)
@@ -71,3 +73,6 @@ MAKEDEPS = -Wp,-MT,$@,-MD,$(dir $@).$(notdir $@).d
# Dependencies that exclude system headers; use whenever we use
# header files from the platform.
UMAKEDEPS = -Wp,-MT,$@,-MMD,$(dir $@).$(notdir $@).d
+
+# Local additions, like -DDEBUG can go here
+-include $(topdir)/MCONFIG.local
diff --git a/MCONFIG.build b/MCONFIG.build
new file mode 100644
index 00000000..d1abff2a
--- /dev/null
+++ b/MCONFIG.build
@@ -0,0 +1,33 @@
+## -*- makefile -*- ------------------------------------------------------
+##
+## Copyright 2001-2008 H. Peter Anvin - All Rights Reserved
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+## Boston MA 02111-1307, USA; either version 2 of the License, or
+## (at your option) any later version; incorporated herein by reference.
+##
+## -----------------------------------------------------------------------
+
+##
+## Right now we don't distinguish between "build" system and the "host"
+## system, although we really should...
+##
+include $(topdir)/MCONFIG
+
+OPTFLAGS = -g -Os
+INCLUDES =
+CFLAGS = -W -Wall -Wno-sign-compare -D_FILE_OFFSET_BITS=64 \
+ $(OPTFLAGS) $(INCLUDES)
+LDFLAGS =
+LIBS =
+
+.SUFFIXES: .c .o .S .s .i .elf .com .bin .asm .lst .c32 .lss
+
+%.o: %.c
+ $(CC) $(UMAKEDEPS) $(CFLAGS) -c -o $@ $<
+%.i: %.c
+ $(CC) $(UMAKEDEPS) $(CFLAGS) -E -o $@ $<
+%.s: %.c
+ $(CC) $(UMAKEDEPS) $(CFLAGS) -S -o $@ $<
diff --git a/MCONFIG.embedded b/MCONFIG.embedded
index 4b42e0da..9f5846d7 100644
--- a/MCONFIG.embedded
+++ b/MCONFIG.embedded
@@ -16,22 +16,30 @@
include $(topdir)/MCONFIG
-GCCOPT := $(call gcc_ok,-m32,) \
- $(call gcc_ok,-ffreestanding,) \
- $(call gcc_ok,-fno-stack-protector,) \
- $(call gcc_ok,-falign-functions=0,-malign-functions=0) \
- $(call gcc_ok,-falign-jumps=0,-malign-jumps=0) \
- $(call gcc_ok,-falign-loops=0,-malign-loops=0) \
- $(call gcc_ok,-mpreferred-stack-boundary=2,) \
- $(call gcc_ok,-mincoming-stack-boundary=2,) \
- -march=i386 -Os -fomit-frame-pointer -mregparm=3 -DREGPARM=3 \
+GCCOPT := $(call gcc_ok,-m32,)
+GCCOPT += $(call gcc_ok,-ffreestanding,)
+GCCOPT += $(call gcc_ok,-fno-stack-protector,)
+GCCOPT += $(call gcc_ok,-fwrapv,)
+GCCOPT += $(call gcc_ok,-freg-struct-return,)
+GCCOPT += -march=i386 -Os -fomit-frame-pointer -mregparm=3 -DREGPARM=3 \
-msoft-float
+GCCOPT += $(call gcc_ok,-fno-exceptions,)
+GCCOPT += $(call gcc_ok,-fno-asynchronous-unwind-tables,)
+GCCOPT += $(call gcc_ok,-fno-strict-aliasing,)
+GCCOPT += $(call gcc_ok,-falign-functions=0,-malign-functions=0)
+GCCOPT += $(call gcc_ok,-falign-jumps=0,-malign-jumps=0)
+GCCOPT += $(call gcc_ok,-falign-labels=0,-malign-labels=0)
+GCCOPT += $(call gcc_ok,-falign-loops=0,-malign-loops=0)
+GCCOPT += $(call gcc_ok,-mpreferred-stack-boundary=2,)
+GCCOPT += $(call gcc_ok,-mincoming-stack-boundary=2,)
LIBGCC := $(shell $(CC) $(GCCOPT) --print-libgcc)
LD += -m elf_i386
-CFLAGS = $(GCCOPT) -g $(GCCWARN) -Wno-sign-compare $(OPTFLAGS) $(INCLUDES)
-SFLAGS = $(CFLAGS) -D__ASSEMBLY__
+
+# Note: use += for CFLAGS and SFLAGS in case something is set in MCONFIG.local
+CFLAGS += $(GCCOPT) -g $(GCCWARN) -Wno-sign-compare $(OPTFLAGS) $(INCLUDES)
+SFLAGS += $(CFLAGS) -D__ASSEMBLY__
.SUFFIXES: .c .o .S .s .i .elf .com .bin .asm .lst .c32 .lss
diff --git a/Makefile b/Makefile
index 2393faa6..fccd0f53 100644
--- a/Makefile
+++ b/Makefile
@@ -53,7 +53,7 @@ BOBJECTS = $(BTARGET) \
# Note: libinstaller is both a BSUBDIR and an ISUBDIR. It contains
# files that depend only on the B phase, but may have to be regenerated
# for "make installer".
-BSUBDIRS = codepage core memdisk modules com32 mbr memdump gpxe sample \
+BSUBDIRS = codepage com32 lzo core memdisk modules mbr memdump gpxe sample \
libinstaller dos win32 dosutil
ITARGET =
IOBJECTS = $(ITARGET) \
diff --git a/TODO b/TODO
deleted file mode 100644
index 530b4bfb..00000000
--- a/TODO
+++ /dev/null
@@ -1,22 +0,0 @@
-*** To do in the short term:
-
-- PXELINUX: Figure out localboot/idle problems.
-
-- PXELINUX: Support changing the default server and boot file prefix?
-
-- Library support for all the comboot system calls.
-
-- Deal with non-512-byte sectors (if I can get media which does...)
-
-
-*** Future projects:
-
-- Clean up the command-line parsing.
-
-- Cleaned up documentation, with a real man page.
-
-- API call to get directory listing.
-
-- COM32-based CLI.
-
-- Rewrite the filesystems to run in protected mode C code.
diff --git a/codepage/cptable.pl b/codepage/cptable.pl
index 05cfc3eb..e29cf006 100755
--- a/codepage/cptable.pl
+++ b/codepage/cptable.pl
@@ -82,7 +82,7 @@ open(CPOUT, '>', $cpout)
# Magic number, in anticipation of being able to load these
# files dynamically...
#
-print CPOUT pack("VV", 0x8fad232b, 0x9c295319);
+print CPOUT pack("VV", 0x58a8b3d4, 0x51d21eb1);
# Header fields available for future use...
print CPOUT pack("VVVVVV", 0, 0, 0, 0, 0, 0);
@@ -97,6 +97,7 @@ print CPOUT pack("VVVVVV", 0, 0, 0, 0, 0, 0);
# ... where @ytab is console codepage -> Unicode and
# %tabx is Unicode -> filesystem codepage.
#
+@uctab = (undef) x 256;
for ($i = 0; $i < 256; $i++) {
$uuc = $ucase{$ytab[$i]}; # Unicode upper case
if (defined($tabx{$uuc})) {
@@ -106,15 +107,44 @@ for ($i = 0; $i < 256; $i++) {
# Upper case equivalent stripped of accents
$u = $tabx{${$decomp{$uuc}}[0]};
} else {
- # No equivalent at all found. Set this to zero, which should
- # prevent shortname matching altogether (still making longname
- # matching possible, of course.)
- $u = 0;
+ # No equivalent at all found. Assume it is a lower-case-only
+ # character, like greek alpha in CP437.
+ $u = $i;
}
+ $uctab[$i] = $u;
print CPOUT pack("C", $u);
}
#
+# Self (shortname) lowercase table.
+# This depends both on the console codepage and the filesystem codepage;
+# the logical transcoding operation is:
+#
+# $taby{$lcase{$xtab[$i]}}
+#
+# ... where @ytab is console codepage -> Unicode and
+# %tabx is Unicode -> filesystem codepage.
+#
+@lctab = (undef) x 256;
+for ($i = 0; $i < 256; $i++) {
+ $llc = $lcase{$xtab[$i]}; # Unicode lower case
+ if (defined($l = $taby{$llc}) && $uctab[$l] == $i) {
+ # Straight-forward conversion
+ } elsif (defined($l = $tabx{${$decomp{$llc}}[0]}) && $uctab[$l] == $i) {
+ # Lower case equivalent stripped of accents
+ } else {
+ # No equivalent at all found. Find *anything* that matches the
+ # bijection criterion...
+ for ($l = 0; $l < 256; $l++) {
+ last if ($uctab[$l] == $i);
+ }
+ $l = $i if ($l == 256); # If nothing, we're screwed anyway...
+ }
+ $lctab[$i] = $l;
+ print CPOUT pack("C", $l);
+}
+
+#
# Unicode (longname) matching table.
# This only depends on the console codepage.
#
diff --git a/com32/MCONFIG b/com32/MCONFIG
index 0e152de3..0b774d4f 100644
--- a/com32/MCONFIG
+++ b/com32/MCONFIG
@@ -1,5 +1,5 @@
## -*- makefile -*- -------------------------------------------------------
-##
+##
## Copyright 2008-2009 H. Peter Anvin - All Rights Reserved
## Copyright 2009 Intel Corporation; author: H. Peter Anvin
##
@@ -17,17 +17,25 @@
include $(topdir)/MCONFIG
-GCCOPT := $(call gcc_ok,-std=gnu99,) \
- $(call gcc_ok,-m32,) \
- $(call gcc_ok,-fno-stack-protector,) \
- $(call gcc_ok,-falign-functions=0,-malign-functions=0) \
- $(call gcc_ok,-falign-jumps=0,-malign-jumps=0) \
- $(call gcc_ok,-falign-loops=0,-malign-loops=0) \
- $(call gcc_ok,-mpreferred-stack-boundary=2,) \
- $(call gcc_ok,-mincoming-stack-boundary=2,) \
- -march=i386 -Os -fomit-frame-pointer -mregparm=3 -DREGPARM=3
-
-com32 = $(topdir)/com32
+GCCOPT := $(call gcc_ok,-std=gnu99,)
+GCCOPT += $(call gcc_ok,-m32,)
+GCCOPT += $(call gcc_ok,-fno-stack-protector,)
+GCCOPT += $(call gcc_ok,-fwrapv,)
+GCCOPT += $(call gcc_ok,-freg-struct-return,)
+GCCOPT += -mregparm=3 -DREGPARM=3 -march=i386 -Os
+GCCOPT += $(call gcc_ok,-fPIE,-fPIC)
+GCCOPT += $(call gcc_ok,-fno-exceptions,)
+GCCOPT += $(call gcc_ok,-fno-asynchronous-unwind-tables,)
+GCCOPT += $(call gcc_ok,-fno-strict-aliasing,)
+GCCOPT += $(call gcc_ok,-falign-functions=0,-malign-functions=0)
+GCCOPT += $(call gcc_ok,-falign-jumps=0,-malign-jumps=0)
+GCCOPT += $(call gcc_ok,-falign-labels=0,-malign-labels=0)
+GCCOPT += $(call gcc_ok,-falign-loops=0,-malign-loops=0)
+GCCOPT += $(call gcc_ok,-mpreferred-stack-boundary=2,)
+GCCOPT += $(call gcc_ok,-incoming-stack-boundary=2,)
+
+com32 := $(topdir)/com32
+RELOCS := $(com32)/tools/relocs
ifneq ($(NOGPL),1)
GPLLIB = $(com32)/gpllib/libcom32gpl.a
@@ -47,7 +55,7 @@ SFLAGS = $(GCCOPT) $(GCCWARN) -march=i386 \
-I$(com32)/libutil/include -I$(com32)/include $(GPLINCLUDE)
COM32LD = $(com32)/lib/com32.ld
-LDFLAGS = -m elf_i386 -T $(COM32LD)
+LDFLAGS = -m elf_i386 --emit-relocs -T $(COM32LD)
LIBGCC := $(shell $(CC) $(GCCOPT) --print-libgcc)
LNXCFLAGS = -I$(com32)/libutil/include $(GCCWARN) -O -g -D_GNU_SOURCE
@@ -86,3 +94,4 @@ C_LNXLIBS = $(com32)/libutil/libutil_lnx.a
%.c32: %.elf
$(OBJCOPY) -O binary $< $@
+ $(RELOCS) $< >> $@ || ( rm -f $@ ; false )
diff --git a/com32/Makefile b/com32/Makefile
index 3821e581..4f95017a 100644
--- a/com32/Makefile
+++ b/com32/Makefile
@@ -1,4 +1,4 @@
-SUBDIRS = lib gpllib libutil modules mboot menu samples rosh cmenu \
+SUBDIRS = tools lib gpllib libutil modules mboot menu samples rosh cmenu \
hdt gfxboot sysdump
all tidy dist clean spotless install:
set -e; for d in $(SUBDIRS); do $(MAKE) -C $$d $@; done
diff --git a/com32/include/com32.h b/com32/include/com32.h
index 665fa0bf..6b142082 100644
--- a/com32/include/com32.h
+++ b/com32/include/com32.h
@@ -1,7 +1,7 @@
/* ----------------------------------------------------------------------- *
*
* Copyright 2002-2009 H. Peter Anvin - All Rights Reserved
- * Copyright 2009 Intel Corporation; author: H. Peter Anvin
+ * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
@@ -92,6 +92,8 @@ typedef struct {
#define EFLAGS_VIP 0x00100000
#define EFLAGS_ID 0x00200000
+struct com32_pmapi;
+
extern struct com32_sys_args {
uint32_t cs_sysargs;
char *cs_cmdline;
@@ -102,6 +104,7 @@ extern struct com32_sys_args {
int __cdecl (*cs_cfarcall)(uint32_t, const void *, uint32_t);
uint32_t cs_memsize;
const char *cs_name;
+ const struct com32_pmapi *cs_pm;
} __com32;
/*
@@ -115,6 +118,14 @@ int __cfarcall(uint16_t __cs, uint16_t __ip,
extern const com32sys_t __com32_zero_regs;
/*
+ * Lowmem allocation functions
+ */
+void *lmalloc(size_t);
+void *lzalloc(size_t);
+void lfree(void *);
+char *lstrdup(const char *);
+
+/*
* These functions convert between linear pointers in the range
* 0..0xFFFFF and real-mode style SEG:OFFS pointers. Note that a
* 32-bit linear pointer is not compatible with a SEG:OFFS pointer
diff --git a/com32/include/dev.h b/com32/include/dev.h
index 7809fb56..70b3165c 100644
--- a/com32/include/dev.h
+++ b/com32/include/dev.h
@@ -41,6 +41,7 @@ struct input_dev;
struct output_dev;
__extern int opendev(const struct input_dev *, const struct output_dev *, int);
+__extern int openmem(const void *, size_t, int);
/* Common generic devices */
diff --git a/com32/include/dirent.h b/com32/include/dirent.h
index d99b21fb..c4aca4f0 100644
--- a/com32/include/dirent.h
+++ b/com32/include/dirent.h
@@ -10,23 +10,7 @@
#include <stddef.h>
#include <sys/types.h>
-#ifndef NAME_MAX
-#define NAME_MAX 255
-#endif
-
-struct dirent {
- long d_ino; /* Inode/File number */
- off_t d_size; /* Size of file */
- mode_t d_mode; /* Type of file */
- char d_name[NAME_MAX + 1];
-};
-
-typedef struct {
- short dd_stat; /* status return from last lookup */
- uint16_t dd_fd;
- size_t dd_sect;
- char dd_name[NAME_MAX + 1]; /* directory */
-} DIR;
+#include <sys/dirent.h>
__extern DIR *opendir(const char *);
__extern struct dirent *readdir(DIR *);
diff --git a/com32/include/netinet/in.h b/com32/include/netinet/in.h
index 051cc08f..ccf04750 100644
--- a/com32/include/netinet/in.h
+++ b/com32/include/netinet/in.h
@@ -4,37 +4,47 @@
/* COM32 will be running on an i386 platform */
#include <stdint.h>
+#include <klibc/compiler.h>
-static inline uint16_t __htons(uint16_t v)
+#define __htons_macro(v) ((uint16_t) \
+ (((uint16_t)(v) << 8) | \
+ ((uint16_t)(v) >> 8)))
+
+static inline __constfunc uint16_t __htons(uint16_t v)
{
- return ((v) << 8) | ((v) >> 8);
+ return __htons_macro(v);
}
-#define htons(x) __htons(x)
-#define ntohs(x) __htons(x)
+#define htons(x) (__builtin_constant_p(x) ? __htons_macro(x) : __htons(x))
+#define ntohs(x) htons(x)
+
+#define __htonl_macro(v) ((uint32_t) \
+ ((((uint32_t)(v) & 0x000000ff) << 24) | \
+ (((uint32_t)(v) & 0x0000ff00) << 8) | \
+ (((uint32_t)(v) & 0x00ff0000) >> 8) | \
+ (((uint32_t)(v) & 0xff000000) >> 24)))
-static inline uint32_t __htonl(uint32_t v)
+static inline __constfunc uint32_t __htonl(uint32_t v)
{
- if (__builtin_constant_p(v)) {
- return (((v) & 0x000000ff) << 24) |
- (((v) & 0x0000ff00) << 8) |
- (((v) & 0x00ff0000) >> 8) | (((v) & 0xff000000) >> 24);
- } else {
-asm("xchgb %h0,%b0 ; roll $16,%0 ; xchgb %h0,%b0":"+abcd"(v));
- return v;
- }
+ asm("xchgb %h0,%b0 ; roll $16,%0 ; xchgb %h0,%b0"
+ : "+q" (v));
+ return v;
}
-#define htonl(x) __htonl(x)
-#define ntohl(x) __htonl(x)
+#define htonl(x) (__builtin_constant_p(x) ? __htonl_macro(x) : __htonl(x))
+#define ntohl(x) htonl(x)
+
+#define __htonq_macro(v) ((uint64_t) \
+ (((uint64_t)__htonl_macro((uint32_t)(v)) << 32) | \
+ (__htonl_macro((uint32_t)((uint64_t)(v) >> 32)))))
-static inline uint64_t __htonq(uint64_t v)
+static inline __constfunc uint64_t __htonq(uint64_t v)
{
- return ((uint64_t) __htonl(v) << 32) | __htonl(v >> 32);
+ return ((uint64_t)__htonl(v) << 32) | __htonl(v >> 32);
}
-#define htonq(x) __htonq(x)
-#define ntohq(x) __htonq(x)
+#define htonq(x) (__builtin_constant_p(x) ? __htonq_macro(x) : __htonq(x))
+#define ntohq(x) htonq(x)
typedef uint32_t in_addr_t;
typedef uint16_t in_port_t;
diff --git a/com32/include/string.h b/com32/include/string.h
index c964ee3b..af9792b6 100644
--- a/com32/include/string.h
+++ b/com32/include/string.h
@@ -23,7 +23,6 @@ __extern char *strcat(char *, const char *);
__extern char *strchr(const char *, int);
__extern int strcmp(const char *, const char *);
__extern char *strcpy(char *, const char *);
-__extern char *strpcpy(char *, const char *);
__extern size_t strcspn(const char *, const char *);
__extern char *strdup(const char *);
__extern char *strndup(const char *, size_t);
diff --git a/com32/include/sys/cpu.h b/com32/include/sys/cpu.h
index dfba02e7..a798a840 100644
--- a/com32/include/sys/cpu.h
+++ b/com32/include/sys/cpu.h
@@ -23,21 +23,26 @@ static inline void cpuid_count(uint32_t op, uint32_t cnt,
uint32_t * eax, uint32_t * ebx,
uint32_t * ecx, uint32_t * edx)
{
-asm("cpuid":"=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx)
+asm("pushl %%ebx ; cpuid ; movl %%ebx,%0 ; popl %%ebx":"=a"(*eax), "=SD"(*ebx), "=c"(*ecx),
+ "=d"(*edx)
: "a"(op), "c"(cnt));
}
static inline void cpuid(uint32_t op, uint32_t * eax, uint32_t * ebx,
uint32_t * ecx, uint32_t * edx)
{
- cpuid_count(op, 0, eax, ebx, ecx, edx);
+asm("pushl %%ebx ; cpuid ; movl %%ebx,%0 ; popl %%ebx":"=a"(*eax), "=SD"(*ebx), "=c"(*ecx),
+ "=d"(*edx)
+: "a"(op));
}
static inline __constfunc uint32_t cpuid_eax(uint32_t level)
{
uint32_t v;
-asm("cpuid": "=a"(v): "a"(level):"ebx", "ecx", "edx");
+asm("pushl %%ebx ; cpuid ; popl %%ebx":"=a"(v)
+: "a"(level)
+: "ecx", "edx");
return v;
}
@@ -45,7 +50,9 @@ static inline __constfunc uint32_t cpuid_ebx(uint32_t level)
{
uint32_t v;
-asm("cpuid": "=b"(v), "+a"(level): :"ecx", "edx");
+asm("pushl %%ebx ; cpuid ; movl %%ebx,%0 ; popl %%ebx":"=a"(v)
+: "a"(level)
+: "ecx", "edx");
return v;
}
@@ -53,7 +60,8 @@ static inline __constfunc uint32_t cpuid_ecx(uint32_t level)
{
uint32_t v;
-asm("cpuid": "=c"(v), "+a"(level): :"ebx", "edx");
+asm("pushl %%ebx ; cpuid ; popl %%ebx":"=c"(v), "+a"(level)
+: : "edx");
return v;
}
@@ -61,7 +69,8 @@ static inline __constfunc uint32_t cpuid_edx(uint32_t level)
{
uint32_t v;
-asm("cpuid": "=d"(v), "+a"(level): :"ebx", "ecx");
+asm("pushl %%ebx ; cpuid ; popl %%ebx":"=d"(v), "+a"(level)
+: : "ecx");
return v;
}
@@ -96,7 +105,10 @@ static inline void cpu_relax(void)
asm volatile ("rep ; nop");
}
-/* These are local cli/sti; not SMP-safe!!! */
+static inline void hlt(void)
+{
+ asm volatile ("hlt");
+}
static inline void cli(void)
{
diff --git a/com32/include/sys/dirent.h b/com32/include/sys/dirent.h
new file mode 100644
index 00000000..bb5e52c7
--- /dev/null
+++ b/com32/include/sys/dirent.h
@@ -0,0 +1,45 @@
+/*
+ * sys/dirent.h
+ */
+
+#ifndef DIRENT_H
+#define DIRENT_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#ifndef NAME_MAX
+#define NAME_MAX 255
+#endif
+
+struct dirent {
+ uint32_t d_ino;
+ uint32_t d_off;
+ uint16_t d_reclen;
+ uint16_t d_type;
+ char d_name[NAME_MAX + 1];
+};
+
+enum dirent_type {
+ DT_UNKNOWN = 0,
+ DT_FIFO = 1,
+ DT_CHR = 2,
+ DT_DIR = 4,
+ DT_BLK = 6,
+ DT_REG = 8,
+ DT_LNK = 10,
+ DT_SOCK = 12,
+ DT_WHT = 14,
+};
+
+/*
+ * Convert between stat structure mode types and directory types.
+ * The stat structure mode types are the same as in Linux.
+ */
+#define IFTODT(mode) (((mode) & 0170000) >> 12)
+#define DTTOIF(dt) ((dt) << 12)
+
+struct _DIR_;
+typedef struct _DIR_ DIR;
+
+#endif /* sys/dirent.h */
diff --git a/com32/include/syslinux/boot.h b/com32/include/syslinux/boot.h
index 87c05e99..21bea01a 100644
--- a/com32/include/syslinux/boot.h
+++ b/com32/include/syslinux/boot.h
@@ -37,7 +37,7 @@
#include <stdint.h>
#include <klibc/compiler.h>
-__noreturn syslinux_run_command(const char *);
+int syslinux_run_command(const char *);
__noreturn syslinux_run_default(void);
void syslinux_local_boot(uint16_t flags);
diff --git a/com32/include/syslinux/idle.h b/com32/include/syslinux/idle.h
index 4c5947b7..6a45236e 100644
--- a/com32/include/syslinux/idle.h
+++ b/com32/include/syslinux/idle.h
@@ -33,5 +33,6 @@
#define _SYSLINUX_IDLE_H
void syslinux_idle(void);
+void syslinux_reset_idle(void);
#endif
diff --git a/com32/include/syslinux/pmapi.h b/com32/include/syslinux/pmapi.h
new file mode 100644
index 00000000..f583deae
--- /dev/null
+++ b/com32/include/syslinux/pmapi.h
@@ -0,0 +1,70 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2002-2009 H. Peter Anvin - All Rights Reserved
+ * Copyright 2009 Intel Corporation; author: H. Peter Anvin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall
+ * be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * pmapi.h
+ *
+ * Definitions for the Syslinux 4 protected-mode ABI
+ */
+
+#ifndef _SYSLINUX_PMAPI_H
+#define _SYSLINUX_PMAPI_H
+
+#include <stddef.h>
+#include <inttypes.h>
+
+/*
+ * Note: add new members to this structure only at the end.
+ * The position of elements in this structure is an ABI.
+ */
+struct _DIR_;
+struct dirent;
+
+struct com32_filedata {
+ size_t size; /* File size */
+ int blocklg2; /* log2(block size) */
+ uint16_t handle; /* File handle */
+};
+
+struct com32_pmapi {
+ void *(*lmalloc)(size_t);
+ void (*lfree)(void *);
+
+ int (*open_file)(const char *, struct com32_filedata *);
+ size_t (*read_file)(uint16_t *, void *, size_t);
+ void (*close_file)(uint16_t);
+
+ struct _DIR_ *(*opendir)(const char *);
+ struct dirent *(*readdir)(struct _DIR_ *);
+ int (*closedir)(struct _DIR_ *);
+
+ void (*idle)(void);
+ void (*reset_idle)(void);
+};
+
+#endif /* _SYSLINUX_PMAPI_H */
diff --git a/com32/include/syslinux/pxe.h b/com32/include/syslinux/pxe.h
index 6e2a769b..4e8a3369 100644
--- a/com32/include/syslinux/pxe.h
+++ b/com32/include/syslinux/pxe.h
@@ -34,491 +34,10 @@
#ifndef _SYSLINUX_PXE_H
#define _SYSLINUX_PXE_H
-#include <stdint.h>
-#include <netinet/in.h>
-#include <klibc/compiler.h>
-#include <com32.h>
-
-/* PXE spec structures and definitions. These mostly follow the PXE
- spec, except when the PXE spec is unnecessarily stupid. Of course,
- that is most of the time. */
-
-/* Basic types; use Unix-like _t convention instead of SCREAMING; also
- re-use types we already have, like in_addr_t. */
-
-typedef uint16_t pxenv_status_t;
-
-#define MAC_ADDR_LEN 16
-typedef uint8_t mac_addr_t[MAC_ADDR_LEN];
-
-/* "Protected mode segment descriptor" according to PXE... */
-typedef struct {
- uint16_t segaddr;
- uint32_t physaddr;
- uint16_t segsize;
-} __packed pxe_segdesc_t;
-
-typedef far_ptr_t segoff16_t;
-
-typedef struct {
- uint8_t opcode;
-#define BOOTP_REQ 1
-#define BOOTP_REP 2
- uint8_t Hardware;
- uint8_t Hardlen;
- uint8_t Gatehops;
- uint32_t ident;
- uint16_t seconds;
- uint16_t Flags;
-#define BOOTP_BCAST 0x8000
- in_addr_t cip; /* Client IP address */
- in_addr_t yip; /* You IP address */
- in_addr_t sip; /* next server IP address */
- in_addr_t gip; /*relay agent IP address */
- mac_addr_t CAddr;
- uint8_t Sname[64];
- uint8_t bootfile[128];
- union {
-#define BOOTP_DHCPVEND 1024
- uint8_t d[BOOTP_DHCPVEND];
- struct {
- uint8_t magic[4];
-#define VM_RFC1048 0x63825363L
- uint32_t flags;
- uint8_t pad[56];
- } v;
- } vendor;
-} __packed pxe_bootp_t;
-
-/* Function calling structures and constants */
-
-typedef struct s_PXENV_GET_CACHED_INFO {
- pxenv_status_t Status;
- uint16_t PacketType;
-#define PXENV_PACKET_TYPE_DHCP_DISCOVER 1
-#define PXENV_PACKET_TYPE_DHCP_ACK 2
-#define PXENV_PACKET_TYPE_CACHED_REPLY 3
- uint16_t BufferSize;
- segoff16_t Buffer;
- uint16_t BufferLimit;
-} __packed t_PXENV_GET_CACHED_INFO;
-
-typedef struct s_PXENV_START_UNDI {
- pxenv_status_t Status;
- uint16_t AX;
- uint16_t BX;
- uint16_t DX;
- uint16_t DI;
- uint16_t ES;
-} __packed t_PXENV_START_UNDI;
-
-typedef struct s_PXENV_STOP_UNDI {
- pxenv_status_t Status;
-} __packed t_PXENV_STOP_UNDI;
-
-typedef struct s_PXENV_START_BASE {
- pxenv_status_t Status;
-} __packed t_PXENV_START_BASE;
-
-typedef struct s_PXENV_STOP_BASE {
- pxenv_status_t Status;
-} __packed t_PXENV_STOP_BASE;
-
-typedef struct s_PXENV_TFTP_OPEN {
- pxenv_status_t Status;
- in_addr_t ServerIPAddress;
- in_addr_t GatewayIPAddress;
- uint8_t FileName[128];
- in_port_t TFTPPort;
- uint16_t PacketSize;
-} __packed t_PXENV_TFTP_OPEN;
-
-typedef struct s_PXENV_TFTP_CLOSE {
- pxenv_status_t Status;
-} __packed t_PXENV_TFTP_CLOSE;
-
-typedef struct s_PXENV_TFTP_READ {
- pxenv_status_t Status;
- uint16_t PacketNumber;
- uint16_t BufferSize;
- segoff16_t Buffer;
-} __packed t_PXENV_TFTP_READ;
-
-typedef struct s_PXENV_TFTP_READ_FILE {
- pxenv_status_t Status;
- uint8_t FileName[128];
- uint32_t BufferSize;
- void *Buffer;
- in_addr_t ServerIPAddress;
- in_addr_t GatewayIPAddress;
- in_addr_t McastIPAddress;
- in_port_t TFTPClntPort;
- in_port_t TFTPSrvPort;
- uint16_t TFTPOpenTimeOut;
- uint16_t TFTPReopenDelay;
-} __packed t_PXENV_TFTP_READ_FILE;
-
-typedef struct s_PXENV_TFTP_GET_FSIZE {
- pxenv_status_t Status;
- in_addr_t ServerIPAddress;
- in_addr_t GatewayIPAddress;
- uint8_t FileName[128];
- uint32_t FileSize;
-} __packed t_PXENV_TFTP_GET_FSIZE;
-
-typedef struct s_PXENV_UDP_OPEN {
- pxenv_status_t status;
- in_addr_t src_ip;
-} __packed t_PXENV_UDP_OPEN;
-
-typedef struct s_PXENV_UDP_CLOSE {
- pxenv_status_t status;
-} __packed t_PXENV_UDP_CLOSE;
-
-typedef struct s_PXENV_UDP_WRITE {
- pxenv_status_t status;
- in_addr_t ip;
- in_addr_t gw;
- in_port_t src_port;
- in_port_t dst_port;
- uint16_t buffer_size;
- segoff16_t buffer;
-} __packed t_PXENV_UDP_WRITE;
-
-typedef struct s_PXENV_UDP_READ {
- pxenv_status_t status;
- in_addr_t src_ip;
- in_addr_t dest_ip;
- in_port_t s_port;
- in_port_t d_port;
- uint16_t buffer_size;
- segoff16_t buffer;
-} __packed t_PXENV_UDP_READ;
-
-typedef struct s_PXENV_UNDI_STARTUP {
- pxenv_status_t Status;
-} __packed t_PXENV_UNDI_STARTUP;
-
-typedef struct s_PXENV_UNDI_CLEANUP {
- pxenv_status_t Status;
-} __packed t_PXENV_UNDI_CLEANUP;
-
-typedef struct s_PXENV_UNDI_INITIALIZE {
- pxenv_status_t Status;
- void *ProtocolIni;
- uint8_t reserved[8];
-} __packed t_PXENV_UNDI_INITIALIZE;
-
-#define MAXNUM_MCADDR 8
-typedef struct s_PXENV_UNDI_MCAST_ADDRESS {
- uint16_t MCastAddrCount;
- mac_addr_t McastAddr[MAXNUM_MCADDR];
-} __packed t_PXENV_UNDI_MCAST_ADDRESS;
-
-typedef struct s_PXENV_UNDI_RESET {
- pxenv_status_t Status;
- t_PXENV_UNDI_MCAST_ADDRESS R_Mcast_Buf;
-} __packed t_PXENV_UNDI_RESET;
-
-typedef struct s_PXENV_UNDI_SHUTDOWN {
- pxenv_status_t Status;
-} __packed t_PXENV_UNDI_SHUTDOWN;
-
-typedef struct s_PXENV_UNDI_OPEN {
- pxenv_status_t Status;
- uint16_t OpenFlag;
- uint16_t PktFilter;
-#define FLTR_DIRECTED 0x0001
-#define FLTR_BRDCST 0x0002
-#define FLTR_PRMSCS 0x0004
-#define FLTR_SRC_RTG 0x0008
- t_PXENV_UNDI_MCAST_ADDRESS R_Mcast_Buf;
-} __packed t_PXENV_UNDI_OPEN;
-
-typedef struct s_PXENV_UNDI_CLOSE {
- pxenv_status_t Status;
-} __packed t_PXENV_UNDI_CLOSE;
-
-typedef struct s_PXENV_UNDI_TRANSMIT {
- pxenv_status_t Status;
- uint8_t Protocol;
-#define P_UNKNOWN 0
-#define P_IP 1
-#define P_ARP 2
-#define P_RARP 3
- uint8_t XmitFlag;
-#define XMT_DESTADDR 0x0000
-#define XMT_BROADCAST 0x0001
- segoff16_t DestAddr;
- segoff16_t TBD;
- uint32_t Reserved[2];
-} __packed t_PXENV_UNDI_TRANSMIT;
-#define MAX_DATA_BLKS 8
-typedef struct s_PXENV_UNDI_TBD {
- uint16_t ImmedLength;
- segoff16_t Xmit;
- uint16_t DataBlkCount;
- struct DataBlk {
- uint8_t TDPtrType;
- uint8_t TDRsvdByte;
- uint16_t TDDataLen;
- segoff16_t TDDataPtr;
- } DataBlock[MAX_DATA_BLKS];
-} __packed t_PXENV_UNDI_TBD;
-
-typedef struct s_PXENV_UNDI_SET_MCAST_ADDRESS {
- pxenv_status_t Status;
- t_PXENV_UNDI_MCAST_ADDRESS R_Mcast_Buf;
-} __packed t_PXENV_UNDI_SET_MCAST_ADDR;
-
-typedef struct s_PXENV_UNDI_SET_STATION_ADDRESS {
- pxenv_status_t Status;
- mac_addr_t StationAddress;
-} __packed t_PXENV_UNDI_SET_STATION_ADDR;
-
-typedef struct s_PXENV_UNDI_SET_PACKET_FILTER {
- pxenv_status_t Status;
- uint8_t filter;
-} __packed t_PXENV_UNDI_SET_PACKET_FILTER;
-
-typedef struct s_PXENV_UNDI_GET_INFORMATION {
- pxenv_status_t Status;
- uint16_t BaseIo;
- uint16_t IntNumber;
- uint16_t MaxTranUnit;
- uint16_t HwType;
-#define ETHER_TYPE 1
-#define EXP_ETHER_TYPE 2
-#define IEEE_TYPE 6
-#define ARCNET_TYPE 7
- uint16_t HwAddrLen;
- mac_addr_t CurrentNodeAddress;
- mac_addr_t PermNodeAddress;
- uint16_t ROMAddress;
- uint16_t RxBufCt;
- uint16_t TxBufCt;
-} __packed t_PXENV_UNDI_GET_INFORMATION;
-
-typedef struct s_PXENV_UNDI_GET_STATISTICS {
- pxenv_status_t Status;
- uint32_t XmtGoodFrames;
- uint32_t RcvGoodFrames;
- uint32_t RcvCRCErrors;
- uint32_t RcvResourceErrors;
-} __packed t_PXENV_UNDI_GET_STATISTICS;
-
-typedef struct s_PXENV_UNDI_CLEAR_STATISTICS {
- pxenv_status_t Status;
-} __packed t_PXENV_UNDI_CLEAR_STATISTICS;
-
-typedef struct s_PXENV_UNDI_INITIATE_DIAGS {
- pxenv_status_t Status;
-} __packed t_PXENV_UNDI_INITIATE_DIAGS;
-
-typedef struct s_PXENV_UNDI_FORCE_INTERRUPT {
- pxenv_status_t Status;
-} __packed t_PXENV_UNDI_FORCE_INTERRUPT;
-
-typedef struct s_PXENV_UNDI_GET_MCAST_ADDRESS {
- pxenv_status_t Status;
- in_addr_t InetAddr;
- mac_addr_t MediaAddr;
-} __packed t_PXENV_UNDI_GET_MCAST_ADDR;
-
-typedef struct s_PXENV_UNDI_GET_NIC_TYPE {
- pxenv_status_t Status;
- uint8_t NicType;
-#define PCI_NIC 2
-#define PnP_NIC 3
-#define CardBus_NIC 4
- union {
- struct {
- uint16_t Vendor_ID;
- uint16_t Dev_ID;
- uint8_t Base_Class;
- uint8_t Sub_Class;
- uint8_t Prog_Intf;
- uint8_t Rev;
- uint16_t BusDevFunc;
- uint16_t SubVendor_ID;
- uint16_t SubDevice_ID;
- } pci, cardbus;
- struct {
- uint32_t EISA_Dev_ID;
- uint8_t Base_Class;
- uint8_t Sub_Class;
- uint8_t Prog_Intf;
- uint16_t CardSelNum;
- } __packed pnp;
- } __packed info;
-} __packed t_PXENV_UNDI_GET_NIC_TYPE;
-
-typedef struct s_PXENV_UNDI_GET_IFACE_INFO {
- pxenv_status_t Status;
- uint8_t IfaceType[16];
- uint32_t LinkSpeed;
- uint32_t ServiceFlags;
- uint32_t Reserved[4];
-} __packed t_PXENV_UNDI_GET_NDIS_INFO;
-
-typedef struct s_PXENV_UNDI_GET_STATE {
-#define PXE_UNDI_GET_STATE_STARTED 1
-#define PXE_UNDI_GET_STATE_INITIALIZED 2
-#define PXE_UNDI_GET_STATE_OPENED 3
- pxenv_status_t Status;
- uint8_t UNDIstate;
-} __packed t_PXENV_UNDI_GET_STATE;
-
-typedef struct s_PXENV_UNDI_ISR {
- pxenv_status_t Status;
- uint16_t FuncFlag;
- uint16_t BufferLength;
- uint16_t FrameLength;
- uint16_t FrameHeaderLength;
- segoff16_t Frame;
- uint8_t ProtType;
- uint8_t PktType;
-} __packed t_PXENV_UNDI_ISR;
-#define PXENV_UNDI_ISR_IN_START 1
-#define PXENV_UNDI_ISR_IN_PROCESS 2
-#define PXENV_UNDI_ISR_IN_GET_NEXT 3
-/* One of these will be returned for
- PXENV_UNDI_ISR_IN_START */
-#define PXENV_UNDI_ISR_OUT_OURS 0
-#define PXENV_UNDI_USR_OUT_NOT_OURS 1
-/* One of these will be returned for
- PXENV_UNDI_ISR_IN_PROCESS and
- PXENV_UNDI_ISR_IN_GET_NEXT */
-#define PXENV_UNDI_ISR_OUT_DONE 0
-#define PXENV_UNDI_ISR_OUT_TRANSMIT 2
-#define PXENV_UNDI_ISR_OUT_RECEIVE 3
-#define PXENV_UNDI_ISR_OUT_BUSY 4
-
-/* Function numbers and error codes */
-
-#define PXENV_TFTP_OPEN 0x0020
-#define PXENV_TFTP_CLOSE 0x0021
-#define PXENV_TFTP_READ 0x0022
-#define PXENV_TFTP_READ_FILE 0x0023
-#define PXENV_TFTP_READ_FILE_PMODE 0x0024
-#define PXENV_TFTP_GET_FSIZE 0x0025
-
-#define PXENV_UDP_OPEN 0x0030
-#define PXENV_UDP_CLOSE 0x0031
-#define PXENV_UDP_READ 0x0032
-#define PXENV_UDP_WRITE 0x0033
-
-#define PXENV_START_UNDI 0x0000
-#define PXENV_UNDI_STARTUP 0x0001
-#define PXENV_UNDI_CLEANUP 0x0002
-#define PXENV_UNDI_INITIALIZE 0x0003
-#define PXENV_UNDI_RESET_NIC 0x0004
-#define PXENV_UNDI_SHUTDOWN 0x0005
-#define PXENV_UNDI_OPEN 0x0006
-#define PXENV_UNDI_CLOSE 0x0007
-#define PXENV_UNDI_TRANSMIT 0x0008
-#define PXENV_UNDI_SET_MCAST_ADDR 0x0009
-#define PXENV_UNDI_SET_STATION_ADDR 0x000A
-#define PXENV_UNDI_SET_PACKET_FILTER 0x000B
-#define PXENV_UNDI_GET_INFORMATION 0x000C
-#define PXENV_UNDI_GET_STATISTICS 0x000D
-#define PXENV_UNDI_CLEAR_STATISTICS 0x000E
-#define PXENV_UNDI_INITIATE_DIAGS 0x000F
-#define PXENV_UNDI_FORCE_INTERRUPT 0x0010
-#define PXENV_UNDI_GET_MCAST_ADDR 0x0011
-#define PXENV_UNDI_GET_NIC_TYPE 0x0012
-#define PXENV_UNDI_GET_IFACE_INFO 0x0013
-#define PXENV_UNDI_ISR 0x0014
-#define PXENV_STOP_UNDI 0x0015 /* Overlap...? */
-#define PXENV_UNDI_GET_STATE 0x0015 /* Overlap...? */
-
-#define PXENV_UNLOAD_STACK 0x0070
-#define PXENV_GET_CACHED_INFO 0x0071
-#define PXENV_RESTART_DHCP 0x0072
-#define PXENV_RESTART_TFTP 0x0073
-#define PXENV_MODE_SWITCH 0x0074
-#define PXENV_START_BASE 0x0075
-#define PXENV_STOP_BASE 0x0076
-
-#define PXENV_EXIT_SUCCESS 0x0000
-#define PXENV_EXIT_FAILURE 0x0001
-
-#define PXENV_STATUS_SUCCESS 0x00
-#define PXENV_STATUS_FAILURE 0x01
-#define PXENV_STATUS_BAD_FUNC 0x02
-#define PXENV_STATUS_UNSUPPORTED 0x03
-#define PXENV_STATUS_KEEP_UNDI 0x04
-#define PXENV_STATUS_KEEP_ALL 0x05
-#define PXENV_STATUS_OUT_OF_RESOURCES 0x06
-#define PXENV_STATUS_ARP_TIMEOUT 0x11
-#define PXENV_STATUS_UDP_CLOSED 0x18
-#define PXENV_STATUS_UDP_OPEN 0x19
-#define PXENV_STATUS_TFTP_CLOSED 0x1A
-#define PXENV_STATUS_TFTP_OPEN 0x1B
-#define PXENV_STATUS_MCOPY_PROBLEM 0x20
-#define PXENV_STATUS_BIS_INTEGRITY_FAILURE 0x21
-#define PXENV_STATUS_BIS_VALIDATE_FAILURE 0x22
-#define PXENV_STATUS_BIS_INIT_FAILURE 0x23
-#define PXENV_STATUS_BIS_SHUTDOWN_FAILURE 0x24
-#define PXENV_STATUS_BIS_GBOA_FAILURE 0x25
-#define PXENV_STATUS_BIS_FREE_FAILURE 0x26
-#define PXENV_STATUS_BIS_GSI_FAILURE 0x27
-#define PXENV_STATUS_BIS_BAD_CKSUM 0x28
-#define PXENV_STATUS_TFTP_CANNOT_ARP_ADDRESS 0x30
-#define PXENV_STATUS_TFTP_OPEN_TIMEOUT 0x32
-
-#define PXENV_STATUS_TFTP_UNKNOWN_OPCODE 0x33
-#define PXENV_STATUS_TFTP_READ_TIMEOUT 0x35
-#define PXENV_STATUS_TFTP_ERROR_OPCODE 0x36
-#define PXENV_STATUS_TFTP_CANNOT_OPEN_CONNECTION 0x38
-#define PXENV_STATUS_TFTP_CANNOT_READ_FROM_CONNECTION 0x39
-#define PXENV_STATUS_TFTP_TOO_MANY_PACKAGES 0x3A
-#define PXENV_STATUS_TFTP_FILE_NOT_FOUND 0x3B
-#define PXENV_STATUS_TFTP_ACCESS_VIOLATION 0x3C
-#define PXENV_STATUS_TFTP_NO_MCAST_ADDRESS 0x3D
-#define PXENV_STATUS_TFTP_NO_FILESIZE 0x3E
-#define PXENV_STATUS_TFTP_INVALID_PACKET_SIZE 0x3F
-#define PXENV_STATUS_DHCP_TIMEOUT 0x51
-#define PXENV_STATUS_DHCP_NO_IP_ADDRESS 0x52
-#define PXENV_STATUS_DHCP_NO_BOOTFILE_NAME 0x53
-#define PXENV_STATUS_DHCP_BAD_IP_ADDRESS 0x54
-#define PXENV_STATUS_UNDI_INVALID_FUNCTION 0x60
-#define PXENV_STATUS_UNDI_MEDIATEST_FAILED 0x61
-#define PXENV_STATUS_UNDI_CANNOT_INIT_NIC_FOR_MCAST 0x62
-#define PXENV_STATUS_UNDI_CANNOT_INITIALIZE_NIC 0x63
-#define PXENV_STATUS_UNDI_CANNOT_INITIALIZE_PHY 0x64
-#define PXENV_STATUS_UNDI_CANNOT_READ_CONFIG_DATA 0x65
-#define PXENV_STATUS_UNDI_CANNOT_READ_INIT_DATA 0x66
-#define PXENV_STATUS_UNDI_BAD_MAC_ADDRESS 0x67
-#define PXENV_STATUS_UNDI_BAD_EEPROM_CHECKSUM 0x68
-#define PXENV_STATUS_UNDI_ERROR_SETTING_ISR 0x69
-#define PXENV_STATUS_UNDI_INVALID_STATE 0x6A
-#define PXENV_STATUS_UNDI_TRANSMIT_ERROR 0x6B
-#define PXENV_STATUS_UNDI_INVALID_PARAMETER 0x6C
-#define PXENV_STATUS_BSTRAP_PROMPT_MENU 0x74
-#define PXENV_STATUS_BSTRAP_MCAST_ADDR 0x76
-#define PXENV_STATUS_BSTRAP_MISSING_LIST 0x77
-#define PXENV_STATUS_BSTRAP_NO_RESPONSE 0x78
-#define PXENV_STATUS_BSTRAP_FILE_TOO_BIG 0x79
-#define PXENV_STATUS_BINL_CANCELED_BY_KEYSTROKE 0xA0
-#define PXENV_STATUS_BINL_NO_PXE_SERVER 0xA1
-#define PXENV_STATUS_NOT_AVAILABLE_IN_PMODE 0xA2
-#define PXENV_STATUS_NOT_AVAILABLE_IN_RMODE 0xA3
-#define PXENV_STATUS_BUSD_DEVICE_NOT_SUPPORTED 0xB0
-#define PXENV_STATUS_LOADER_NO_FREE_BASE_MEMORY 0xC0
-#define PXENV_STATUS_LOADER_NO_BC_ROMID 0xC1
-#define PXENV_STATUS_LOADER_BAD_BC_ROMID 0xC2
-#define PXENV_STATUS_LOADER_BAD_BC_RUNTIME_IMAGE 0xC3
-#define PXENV_STATUS_LOADER_NO_UNDI_ROMID 0xC4
-#define PXENV_STATUS_LOADER_BAD_UNDI_ROMID 0xC5
-#define PXENV_STATUS_LOADER_BAD_UNDI_DRIVER_IMAGE 0xC6
-#define PXENV_STATUS_LOADER_NO_PXE_STRUCT 0xC8
-#define PXENV_STATUS_LOADER_NO_PXENV_STRUCT 0xC9
-#define PXENV_STATUS_LOADER_UNDI_START 0xCA
-#define PXENV_STATUS_LOADER_BC_START 0xCB
+#include <syslinux/pxe_api.h>
/* SYSLINUX-defined PXE utility functions */
-int pxe_get_cached_info(int level, void **buf, size_t * len);
+int pxe_get_cached_info(int level, void **buf, size_t *len);
int pxe_get_nic_type(t_PXENV_UNDI_GET_NIC_TYPE * gnt);
uint32_t pxe_dns(const char *hostname);
diff --git a/com32/include/syslinux/pxe_api.h b/com32/include/syslinux/pxe_api.h
new file mode 100644
index 00000000..27166b0b
--- /dev/null
+++ b/com32/include/syslinux/pxe_api.h
@@ -0,0 +1,571 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall
+ * be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * syslinux/pxe_api.h
+ *
+ * PXE type and constant definitions for SYSLINUX
+ */
+
+#ifndef _SYSLINUX_PXE_API_H
+#define _SYSLINUX_PXE_API_H
+
+#include <stdint.h>
+#include <netinet/in.h>
+#include <klibc/compiler.h>
+#include <com32.h>
+
+/* PXE spec structures and definitions. These mostly follow the PXE
+ spec, except when the PXE spec is unnecessarily stupid. Of course,
+ that is most of the time. */
+
+/* Basic types; use Unix-like _t convention instead of SCREAMING; also
+ re-use types we already have, like in_addr_t. */
+
+typedef uint16_t pxenv_status_t;
+
+#define MAC_ADDR_LEN 16
+typedef uint8_t mac_addr_t[MAC_ADDR_LEN];
+
+/* "Protected mode segment descriptor" according to PXE... */
+typedef struct {
+ uint16_t sel;
+ uint32_t base;
+ uint16_t size;
+} __packed pxe_segdesc_t;
+
+typedef far_ptr_t segoff16_t;
+
+typedef struct {
+ uint8_t opcode;
+#define BOOTP_REQ 1
+#define BOOTP_REP 2
+ uint8_t Hardware;
+ uint8_t Hardlen;
+ uint8_t Gatehops;
+ uint32_t ident;
+ uint16_t seconds;
+ uint16_t Flags;
+#define BOOTP_BCAST 0x8000
+ in_addr_t cip; /* Client IP address */
+ in_addr_t yip; /* You IP address */
+ in_addr_t sip; /* next server IP address */
+ in_addr_t gip; /*relay agent IP address */
+ mac_addr_t CAddr;
+ uint8_t Sname[64];
+ uint8_t bootfile[128];
+ union {
+#define BOOTP_DHCPVEND 1024
+ uint8_t d[BOOTP_DHCPVEND];
+ struct {
+ uint8_t magic[4];
+#define VM_RFC1048 0x63825363L
+ uint32_t flags;
+ uint8_t pad[56];
+ } v;
+ } vendor;
+} __packed pxe_bootp_t;
+
+/* Function calling structures and constants */
+
+typedef struct s_PXENV_GET_CACHED_INFO {
+ pxenv_status_t Status;
+ uint16_t PacketType;
+#define PXENV_PACKET_TYPE_DHCP_DISCOVER 1
+#define PXENV_PACKET_TYPE_DHCP_ACK 2
+#define PXENV_PACKET_TYPE_CACHED_REPLY 3
+ uint16_t BufferSize;
+ segoff16_t Buffer;
+ uint16_t BufferLimit;
+} __packed t_PXENV_GET_CACHED_INFO;
+
+typedef struct s_PXENV_START_UNDI {
+ pxenv_status_t Status;
+ uint16_t AX;
+ uint16_t BX;
+ uint16_t DX;
+ uint16_t DI;
+ uint16_t ES;
+} __packed t_PXENV_START_UNDI;
+
+typedef struct s_PXENV_STOP_UNDI {
+ pxenv_status_t Status;
+} __packed t_PXENV_STOP_UNDI;
+
+typedef struct s_PXENV_START_BASE {
+ pxenv_status_t Status;
+} __packed t_PXENV_START_BASE;
+
+typedef struct s_PXENV_STOP_BASE {
+ pxenv_status_t Status;
+} __packed t_PXENV_STOP_BASE;
+
+typedef struct s_PXENV_TFTP_OPEN {
+ pxenv_status_t Status;
+ in_addr_t ServerIPAddress;
+ in_addr_t GatewayIPAddress;
+ uint8_t FileName[128];
+ in_port_t TFTPPort;
+ uint16_t PacketSize;
+} __packed t_PXENV_TFTP_OPEN;
+
+typedef struct s_PXENV_TFTP_CLOSE {
+ pxenv_status_t Status;
+} __packed t_PXENV_TFTP_CLOSE;
+
+typedef struct s_PXENV_TFTP_READ {
+ pxenv_status_t Status;
+ uint16_t PacketNumber;
+ uint16_t BufferSize;
+ segoff16_t Buffer;
+} __packed t_PXENV_TFTP_READ;
+
+typedef struct s_PXENV_TFTP_READ_FILE {
+ pxenv_status_t Status;
+ uint8_t FileName[128];
+ uint32_t BufferSize;
+ void *Buffer;
+ in_addr_t ServerIPAddress;
+ in_addr_t GatewayIPAddress;
+ in_addr_t McastIPAddress;
+ in_port_t TFTPClntPort;
+ in_port_t TFTPSrvPort;
+ uint16_t TFTPOpenTimeOut;
+ uint16_t TFTPReopenDelay;
+} __packed t_PXENV_TFTP_READ_FILE;
+
+typedef struct s_PXENV_TFTP_GET_FSIZE {
+ pxenv_status_t Status;
+ in_addr_t ServerIPAddress;
+ in_addr_t GatewayIPAddress;
+ uint8_t FileName[128];
+ uint32_t FileSize;
+} __packed t_PXENV_TFTP_GET_FSIZE;
+
+typedef struct s_PXENV_UDP_OPEN {
+ pxenv_status_t status;
+ in_addr_t src_ip;
+} __packed t_PXENV_UDP_OPEN;
+
+typedef struct s_PXENV_UDP_CLOSE {
+ pxenv_status_t status;
+} __packed t_PXENV_UDP_CLOSE;
+
+typedef struct s_PXENV_UDP_WRITE {
+ pxenv_status_t status;
+ in_addr_t ip;
+ in_addr_t gw;
+ in_port_t src_port;
+ in_port_t dst_port;
+ uint16_t buffer_size;
+ segoff16_t buffer;
+} __packed t_PXENV_UDP_WRITE;
+
+typedef struct s_PXENV_UDP_READ {
+ pxenv_status_t status;
+ in_addr_t src_ip;
+ in_addr_t dest_ip;
+ in_port_t s_port;
+ in_port_t d_port;
+ uint16_t buffer_size;
+ segoff16_t buffer;
+} __packed t_PXENV_UDP_READ;
+
+typedef struct s_PXENV_UNDI_STARTUP {
+ pxenv_status_t Status;
+} __packed t_PXENV_UNDI_STARTUP;
+
+typedef struct s_PXENV_UNDI_CLEANUP {
+ pxenv_status_t Status;
+} __packed t_PXENV_UNDI_CLEANUP;
+
+typedef struct s_PXENV_UNDI_INITIALIZE {
+ pxenv_status_t Status;
+ void *ProtocolIni;
+ uint8_t reserved[8];
+} __packed t_PXENV_UNDI_INITIALIZE;
+
+#define MAXNUM_MCADDR 8
+typedef struct s_PXENV_UNDI_MCAST_ADDRESS {
+ uint16_t MCastAddrCount;
+ mac_addr_t McastAddr[MAXNUM_MCADDR];
+} __packed t_PXENV_UNDI_MCAST_ADDRESS;
+
+typedef struct s_PXENV_UNDI_RESET {
+ pxenv_status_t Status;
+ t_PXENV_UNDI_MCAST_ADDRESS R_Mcast_Buf;
+} __packed t_PXENV_UNDI_RESET;
+
+typedef struct s_PXENV_UNDI_SHUTDOWN {
+ pxenv_status_t Status;
+} __packed t_PXENV_UNDI_SHUTDOWN;
+
+typedef struct s_PXENV_UNDI_OPEN {
+ pxenv_status_t Status;
+ uint16_t OpenFlag;
+ uint16_t PktFilter;
+#define FLTR_DIRECTED 0x0001
+#define FLTR_BRDCST 0x0002
+#define FLTR_PRMSCS 0x0004
+#define FLTR_SRC_RTG 0x0008
+ t_PXENV_UNDI_MCAST_ADDRESS R_Mcast_Buf;
+} __packed t_PXENV_UNDI_OPEN;
+
+typedef struct s_PXENV_UNDI_CLOSE {
+ pxenv_status_t Status;
+} __packed t_PXENV_UNDI_CLOSE;
+
+typedef struct s_PXENV_UNDI_TRANSMIT {
+ pxenv_status_t Status;
+ uint8_t Protocol;
+#define P_UNKNOWN 0
+#define P_IP 1
+#define P_ARP 2
+#define P_RARP 3
+ uint8_t XmitFlag;
+#define XMT_DESTADDR 0x0000
+#define XMT_BROADCAST 0x0001
+ segoff16_t DestAddr;
+ segoff16_t TBD;
+ uint32_t Reserved[2];
+} __packed t_PXENV_UNDI_TRANSMIT;
+#define MAX_DATA_BLKS 8
+typedef struct s_PXENV_UNDI_TBD {
+ uint16_t ImmedLength;
+ segoff16_t Xmit;
+ uint16_t DataBlkCount;
+ struct DataBlk {
+ uint8_t TDPtrType;
+ uint8_t TDRsvdByte;
+ uint16_t TDDataLen;
+ segoff16_t TDDataPtr;
+ } DataBlock[MAX_DATA_BLKS];
+} __packed t_PXENV_UNDI_TBD;
+
+typedef struct s_PXENV_UNDI_SET_MCAST_ADDRESS {
+ pxenv_status_t Status;
+ t_PXENV_UNDI_MCAST_ADDRESS R_Mcast_Buf;
+} __packed t_PXENV_UNDI_SET_MCAST_ADDR;
+
+typedef struct s_PXENV_UNDI_SET_STATION_ADDRESS {
+ pxenv_status_t Status;
+ mac_addr_t StationAddress;
+} __packed t_PXENV_UNDI_SET_STATION_ADDR;
+
+typedef struct s_PXENV_UNDI_SET_PACKET_FILTER {
+ pxenv_status_t Status;
+ uint8_t filter;
+} __packed t_PXENV_UNDI_SET_PACKET_FILTER;
+
+typedef struct s_PXENV_UNDI_GET_INFORMATION {
+ pxenv_status_t Status;
+ uint16_t BaseIo;
+ uint16_t IntNumber;
+ uint16_t MaxTranUnit;
+ uint16_t HwType;
+#define ETHER_TYPE 1
+#define EXP_ETHER_TYPE 2
+#define IEEE_TYPE 6
+#define ARCNET_TYPE 7
+ uint16_t HwAddrLen;
+ mac_addr_t CurrentNodeAddress;
+ mac_addr_t PermNodeAddress;
+ uint16_t ROMAddress;
+ uint16_t RxBufCt;
+ uint16_t TxBufCt;
+} __packed t_PXENV_UNDI_GET_INFORMATION;
+
+typedef struct s_PXENV_UNDI_GET_STATISTICS {
+ pxenv_status_t Status;
+ uint32_t XmtGoodFrames;
+ uint32_t RcvGoodFrames;
+ uint32_t RcvCRCErrors;
+ uint32_t RcvResourceErrors;
+} __packed t_PXENV_UNDI_GET_STATISTICS;
+
+typedef struct s_PXENV_UNDI_CLEAR_STATISTICS {
+ pxenv_status_t Status;
+} __packed t_PXENV_UNDI_CLEAR_STATISTICS;
+
+typedef struct s_PXENV_UNDI_INITIATE_DIAGS {
+ pxenv_status_t Status;
+} __packed t_PXENV_UNDI_INITIATE_DIAGS;
+
+typedef struct s_PXENV_UNDI_FORCE_INTERRUPT {
+ pxenv_status_t Status;
+} __packed t_PXENV_UNDI_FORCE_INTERRUPT;
+
+typedef struct s_PXENV_UNDI_GET_MCAST_ADDRESS {
+ pxenv_status_t Status;
+ in_addr_t InetAddr;
+ mac_addr_t MediaAddr;
+} __packed t_PXENV_UNDI_GET_MCAST_ADDR;
+
+typedef struct s_PXENV_UNDI_GET_NIC_TYPE {
+ pxenv_status_t Status;
+ uint8_t NicType;
+#define PCI_NIC 2
+#define PnP_NIC 3
+#define CardBus_NIC 4
+ union {
+ struct {
+ uint16_t Vendor_ID;
+ uint16_t Dev_ID;
+ uint8_t Base_Class;
+ uint8_t Sub_Class;
+ uint8_t Prog_Intf;
+ uint8_t Rev;
+ uint16_t BusDevFunc;
+ uint16_t SubVendor_ID;
+ uint16_t SubDevice_ID;
+ } pci, cardbus;
+ struct {
+ uint32_t EISA_Dev_ID;
+ uint8_t Base_Class;
+ uint8_t Sub_Class;
+ uint8_t Prog_Intf;
+ uint16_t CardSelNum;
+ } __packed pnp;
+ } __packed info;
+} __packed t_PXENV_UNDI_GET_NIC_TYPE;
+
+typedef struct s_PXENV_UNDI_GET_IFACE_INFO {
+ pxenv_status_t Status;
+ uint8_t IfaceType[16];
+ uint32_t LinkSpeed;
+ uint32_t ServiceFlags;
+ uint32_t Reserved[4];
+} __packed t_PXENV_UNDI_GET_NDIS_INFO;
+
+typedef struct s_PXENV_UNDI_GET_STATE {
+#define PXE_UNDI_GET_STATE_STARTED 1
+#define PXE_UNDI_GET_STATE_INITIALIZED 2
+#define PXE_UNDI_GET_STATE_OPENED 3
+ pxenv_status_t Status;
+ uint8_t UNDIstate;
+} __packed t_PXENV_UNDI_GET_STATE;
+
+typedef struct s_PXENV_UNDI_ISR {
+ pxenv_status_t Status;
+ uint16_t FuncFlag;
+ uint16_t BufferLength;
+ uint16_t FrameLength;
+ uint16_t FrameHeaderLength;
+ segoff16_t Frame;
+ uint8_t ProtType;
+ uint8_t PktType;
+} __packed t_PXENV_UNDI_ISR;
+
+typedef struct s_PXENV_FILE_API_CHECK {
+ pxenv_status_t Status;
+ uint16_t Size;
+ uint32_t Magic;
+ uint32_t Provider;
+ uint32_t APIMask;
+ uint32_t Flags;
+} __packed t_PXENV_FILE_API_CHECK;
+
+typedef struct s_PXENV_FILE_READ {
+ pxenv_status_t Status;
+ uint16_t FileHandle;
+ uint16_t BufferSize;
+ segoff16_t Buffer;
+} __packed t_PXENV_FILE_READ;
+
+typedef struct s_PXENV_FILE_OPEN {
+ pxenv_status_t Status;
+ uint16_t FileHandle;
+ segoff16_t FileName;
+ uint32_t Reserved;
+} __packed t_PXENV_FILE_OPEN;
+
+typedef struct s_PXENV_FILE_CLOSE {
+ pxenv_status_t Status;
+ uint16_t FileHandle;
+} __packed t_PXENV_FILE_CLOSE;
+
+typedef struct s_PXENV_GET_FILE_SIZE {
+ pxenv_status_t Status;
+ uint16_t FileHandle;
+ uint32_t FileSize;
+} __packed t_PXENV_GET_FILE_SIZE;
+
+typedef struct s_PXENV_UNLOAD_STACK {
+ pxenv_status_t Status;
+ uint8_t reserved[10];
+} __packed t_PXENV_UNLOAD_STACK;
+
+#define PXENV_UNDI_ISR_IN_START 1
+#define PXENV_UNDI_ISR_IN_PROCESS 2
+#define PXENV_UNDI_ISR_IN_GET_NEXT 3
+/* One of these will be returned for
+ PXENV_UNDI_ISR_IN_START */
+#define PXENV_UNDI_ISR_OUT_OURS 0
+#define PXENV_UNDI_USR_OUT_NOT_OURS 1
+/* One of these will be returned for
+ PXENV_UNDI_ISR_IN_PROCESS and
+ PXENV_UNDI_ISR_IN_GET_NEXT */
+#define PXENV_UNDI_ISR_OUT_DONE 0
+#define PXENV_UNDI_ISR_OUT_TRANSMIT 2
+#define PXENV_UNDI_ISR_OUT_RECEIVE 3
+#define PXENV_UNDI_ISR_OUT_BUSY 4
+
+/* Function numbers and error codes */
+
+#define PXENV_TFTP_OPEN 0x0020
+#define PXENV_TFTP_CLOSE 0x0021
+#define PXENV_TFTP_READ 0x0022
+#define PXENV_TFTP_READ_FILE 0x0023
+#define PXENV_TFTP_READ_FILE_PMODE 0x0024
+#define PXENV_TFTP_GET_FSIZE 0x0025
+
+#define PXENV_UDP_OPEN 0x0030
+#define PXENV_UDP_CLOSE 0x0031
+#define PXENV_UDP_READ 0x0032
+#define PXENV_UDP_WRITE 0x0033
+
+#define PXENV_START_UNDI 0x0000
+#define PXENV_UNDI_STARTUP 0x0001
+#define PXENV_UNDI_CLEANUP 0x0002
+#define PXENV_UNDI_INITIALIZE 0x0003
+#define PXENV_UNDI_RESET_NIC 0x0004
+#define PXENV_UNDI_SHUTDOWN 0x0005
+#define PXENV_UNDI_OPEN 0x0006
+#define PXENV_UNDI_CLOSE 0x0007
+#define PXENV_UNDI_TRANSMIT 0x0008
+#define PXENV_UNDI_SET_MCAST_ADDR 0x0009
+#define PXENV_UNDI_SET_STATION_ADDR 0x000A
+#define PXENV_UNDI_SET_PACKET_FILTER 0x000B
+#define PXENV_UNDI_GET_INFORMATION 0x000C
+#define PXENV_UNDI_GET_STATISTICS 0x000D
+#define PXENV_UNDI_CLEAR_STATISTICS 0x000E
+#define PXENV_UNDI_INITIATE_DIAGS 0x000F
+#define PXENV_UNDI_FORCE_INTERRUPT 0x0010
+#define PXENV_UNDI_GET_MCAST_ADDR 0x0011
+#define PXENV_UNDI_GET_NIC_TYPE 0x0012
+#define PXENV_UNDI_GET_IFACE_INFO 0x0013
+#define PXENV_UNDI_ISR 0x0014
+#define PXENV_STOP_UNDI 0x0015 /* Overlap...? */
+#define PXENV_UNDI_GET_STATE 0x0015 /* Overlap...? */
+
+#define PXENV_UNLOAD_STACK 0x0070
+#define PXENV_GET_CACHED_INFO 0x0071
+#define PXENV_RESTART_DHCP 0x0072
+#define PXENV_RESTART_TFTP 0x0073
+#define PXENV_MODE_SWITCH 0x0074
+#define PXENV_START_BASE 0x0075
+#define PXENV_STOP_BASE 0x0076
+
+/* gPXE extensions... */
+#define PXENV_FILE_OPEN 0x00e0
+#define PXENV_FILE_CLOSE 0x00e1
+#define PXENV_FILE_SELECT 0x00e2
+#define PXENV_FILE_READ 0x00e3
+#define PXENV_GET_FILE_SIZE 0x00e4
+#define PXENV_FILE_EXEC 0x00e5
+#define PXENV_FILE_API_CHECK 0x00e6
+
+/* Exit codes */
+#define PXENV_EXIT_SUCCESS 0x0000
+#define PXENV_EXIT_FAILURE 0x0001
+
+/* Status codes */
+#define PXENV_STATUS_SUCCESS 0x00
+#define PXENV_STATUS_FAILURE 0x01
+#define PXENV_STATUS_BAD_FUNC 0x02
+#define PXENV_STATUS_UNSUPPORTED 0x03
+#define PXENV_STATUS_KEEP_UNDI 0x04
+#define PXENV_STATUS_KEEP_ALL 0x05
+#define PXENV_STATUS_OUT_OF_RESOURCES 0x06
+#define PXENV_STATUS_ARP_TIMEOUT 0x11
+#define PXENV_STATUS_UDP_CLOSED 0x18
+#define PXENV_STATUS_UDP_OPEN 0x19
+#define PXENV_STATUS_TFTP_CLOSED 0x1a
+#define PXENV_STATUS_TFTP_OPEN 0x1b
+#define PXENV_STATUS_MCOPY_PROBLEM 0x20
+#define PXENV_STATUS_BIS_INTEGRITY_FAILURE 0x21
+#define PXENV_STATUS_BIS_VALIDATE_FAILURE 0x22
+#define PXENV_STATUS_BIS_INIT_FAILURE 0x23
+#define PXENV_STATUS_BIS_SHUTDOWN_FAILURE 0x24
+#define PXENV_STATUS_BIS_GBOA_FAILURE 0x25
+#define PXENV_STATUS_BIS_FREE_FAILURE 0x26
+#define PXENV_STATUS_BIS_GSI_FAILURE 0x27
+#define PXENV_STATUS_BIS_BAD_CKSUM 0x28
+#define PXENV_STATUS_TFTP_CANNOT_ARP_ADDRESS 0x30
+#define PXENV_STATUS_TFTP_OPEN_TIMEOUT 0x32
+
+#define PXENV_STATUS_TFTP_UNKNOWN_OPCODE 0x33
+#define PXENV_STATUS_TFTP_READ_TIMEOUT 0x35
+#define PXENV_STATUS_TFTP_ERROR_OPCODE 0x36
+#define PXENV_STATUS_TFTP_CANNOT_OPEN_CONNECTION 0x38
+#define PXENV_STATUS_TFTP_CANNOT_READ_FROM_CONNECTION 0x39
+#define PXENV_STATUS_TFTP_TOO_MANY_PACKAGES 0x3a
+#define PXENV_STATUS_TFTP_FILE_NOT_FOUND 0x3b
+#define PXENV_STATUS_TFTP_ACCESS_VIOLATION 0x3c
+#define PXENV_STATUS_TFTP_NO_MCAST_ADDRESS 0x3d
+#define PXENV_STATUS_TFTP_NO_FILESIZE 0x3e
+#define PXENV_STATUS_TFTP_INVALID_PACKET_SIZE 0x3f
+#define PXENV_STATUS_DHCP_TIMEOUT 0x51
+#define PXENV_STATUS_DHCP_NO_IP_ADDRESS 0x52
+#define PXENV_STATUS_DHCP_NO_BOOTFILE_NAME 0x53
+#define PXENV_STATUS_DHCP_BAD_IP_ADDRESS 0x54
+#define PXENV_STATUS_UNDI_INVALID_FUNCTION 0x60
+#define PXENV_STATUS_UNDI_MEDIATEST_FAILED 0x61
+#define PXENV_STATUS_UNDI_CANNOT_INIT_NIC_FOR_MCAST 0x62
+#define PXENV_STATUS_UNDI_CANNOT_INITIALIZE_NIC 0x63
+#define PXENV_STATUS_UNDI_CANNOT_INITIALIZE_PHY 0x64
+#define PXENV_STATUS_UNDI_CANNOT_READ_CONFIG_DATA 0x65
+#define PXENV_STATUS_UNDI_CANNOT_READ_INIT_DATA 0x66
+#define PXENV_STATUS_UNDI_BAD_MAC_ADDRESS 0x67
+#define PXENV_STATUS_UNDI_BAD_EEPROM_CHECKSUM 0x68
+#define PXENV_STATUS_UNDI_ERROR_SETTING_ISR 0x69
+#define PXENV_STATUS_UNDI_INVALID_STATE 0x6a
+#define PXENV_STATUS_UNDI_TRANSMIT_ERROR 0x6b
+#define PXENV_STATUS_UNDI_INVALID_PARAMETER 0x6c
+#define PXENV_STATUS_BSTRAP_PROMPT_MENU 0x74
+#define PXENV_STATUS_BSTRAP_MCAST_ADDR 0x76
+#define PXENV_STATUS_BSTRAP_MISSING_LIST 0x77
+#define PXENV_STATUS_BSTRAP_NO_RESPONSE 0x78
+#define PXENV_STATUS_BSTRAP_FILE_TOO_BIG 0x79
+#define PXENV_STATUS_BINL_CANCELED_BY_KEYSTROKE 0xa0
+#define PXENV_STATUS_BINL_NO_PXE_SERVER 0xa1
+#define PXENV_STATUS_NOT_AVAILABLE_IN_PMODE 0xa2
+#define PXENV_STATUS_NOT_AVAILABLE_IN_RMODE 0xa3
+#define PXENV_STATUS_BUSD_DEVICE_NOT_SUPPORTED 0xb0
+#define PXENV_STATUS_LOADER_NO_FREE_BASE_MEMORY 0xc0
+#define PXENV_STATUS_LOADER_NO_BC_ROMID 0xc1
+#define PXENV_STATUS_LOADER_BAD_BC_ROMID 0xc2
+#define PXENV_STATUS_LOADER_BAD_BC_RUNTIME_IMAGE 0xc3
+#define PXENV_STATUS_LOADER_NO_UNDI_ROMID 0xc4
+#define PXENV_STATUS_LOADER_BAD_UNDI_ROMID 0xc5
+#define PXENV_STATUS_LOADER_BAD_UNDI_DRIVER_IMAGE 0xc6
+#define PXENV_STATUS_LOADER_NO_PXE_STRUCT 0xc8
+#define PXENV_STATUS_LOADER_NO_PXENV_STRUCT 0xc9
+#define PXENV_STATUS_LOADER_UNDI_START 0xca
+#define PXENV_STATUS_LOADER_BC_START 0xcb
+
+#endif /* _SYSLINUX_PXE_API_H */
diff --git a/com32/lib/MCONFIG b/com32/lib/MCONFIG
index 1ae83bc7..44278bd1 100644
--- a/com32/lib/MCONFIG
+++ b/com32/lib/MCONFIG
@@ -2,9 +2,20 @@
include $(topdir)/MCONFIG
-GCCOPT := $(call gcc_ok,-std=gnu99,) \
- $(call gcc_ok,-m32,) \
- $(call gcc_ok,-fno-stack-protector,) \
+GCCOPT := $(call gcc_ok,-std=gnu99,)
+GCCOPT += $(call gcc_ok,-m32,)
+GCCOPT += $(call gcc_ok,-fno-stack-protector,)
+GCCOPT += $(call gcc_ok,-fwrapv,)
+GCCOPT += $(call gcc_ok,-freg-struct-return,)
+GCCOPT += $(call gcc_ok,-fPIE,-fPIC)
+GCCOPT += $(call gcc_ok,-fno-exceptions,)
+GCCOPT += $(call gcc_ok,-fno-asynchronous-unwind-tables,)
+GCCOPT += $(call gcc_ok,-fno-strict-aliasing,)
+GCCOPT += $(call gcc_ok,-falign-functions=0,-malign-functions=0)
+GCCOPT += $(call gcc_ok,-falign-jumps=0,-malign-jumps=0)
+GCCOPT += $(call gcc_ok,-falign-labels=0,-malign-labels=0)
+GCCOPT += $(call gcc_ok,-falign-loops=0,-malign-loops=0)
+GCCOPT += $(call gcc_ok,-mpreferred-stack-boundary=2,)
INCLUDE = -I.
STRIP = strip --strip-all -R .comment -R .note
diff --git a/com32/lib/Makefile b/com32/lib/Makefile
index 250c3962..93643ce2 100644
--- a/com32/lib/Makefile
+++ b/com32/lib/Makefile
@@ -17,7 +17,7 @@ LIBOBJS = \
exit.o onexit.o \
perror.o printf.o puts.o qsort.o realloc.o seed48.o snprintf.o \
sprintf.o srand48.o sscanf.o stack.o strcasecmp.o strcat.o \
- strchr.o strcmp.o strcpy.o strpcpy.o strdup.o strlen.o \
+ strchr.o strcmp.o strcpy.o strdup.o strlen.o \
strerror.o strnlen.o \
strncasecmp.o strncat.o strncmp.o strncpy.o strndup.o \
stpcpy.o stpncpy.o \
@@ -27,11 +27,13 @@ LIBOBJS = \
asprintf.o vasprintf.o strlcpy.o strlcat.o \
vsscanf.o zalloc.o \
\
+ lmalloc.o lstrdup.o \
+ \
dprintf.o vdprintf.o \
\
- opendir.o readdir.o closedir.o getcwd.o chdir.o fdopendir.o \
+ sys/readdir.o getcwd.o chdir.o fdopendir.o \
\
- libgcc/__ashldi3.o libgcc/__udivdi3.o \
+ libgcc/__ashldi3.o libgcc/__udivdi3.o \
libgcc/__negdi2.o libgcc/__ashrdi3.o libgcc/__lshrdi3.o \
libgcc/__muldi3.o libgcc/__udivmoddi4.o libgcc/__umoddi3.o \
libgcc/__divdi3.o libgcc/__moddi3.o \
@@ -40,6 +42,7 @@ LIBOBJS = \
sys/entry.o sys/exit.o sys/argv.o sys/times.o \
sys/fileinfo.o sys/opendev.o sys/read.o sys/write.o sys/ftell.o \
sys/close.o sys/open.o sys/fileread.o sys/fileclose.o \
+ sys/openmem.o \
sys/isatty.o sys/fstat.o \
\
sys/zfile.o sys/zfopen.o \
@@ -117,6 +120,25 @@ LIBOBJS = \
syslinux/video/fontquery.o syslinux/video/forcetext.o \
syslinux/video/reportmode.o
+# These are the objects which are also imported into the core
+LIBCOREOBJS = \
+ memcpy.o mempcpy.o memset.o memcmp.o memmove.o \
+ strlen.o stpcpy.o strcpy.o strcmp.o strlcpy.o strlcat.o \
+ strchr.o strncmp.o strncpy.o \
+ \
+ snprintf.o sprintf.o vsnprintf.o \
+ \
+ dprintf.o vdprintf.o \
+ \
+ zalloc.o strdup.o \
+ \
+ sys/intcall.o sys/farcall.o sys/cfarcall.o sys/zeroregs.o \
+ \
+ libgcc/__ashldi3.o libgcc/__udivdi3.o \
+ libgcc/__negdi2.o libgcc/__ashrdi3.o libgcc/__lshrdi3.o \
+ libgcc/__muldi3.o libgcc/__udivmoddi4.o libgcc/__umoddi3.o \
+ libgcc/__divdi3.o libgcc/__moddi3.o
+
BINDIR = /usr/bin
LIBDIR = /usr/lib
DATADIR = /usr/share
@@ -124,13 +146,18 @@ AUXDIR = $(DATADIR)/syslinux
INCDIR = /usr/include
COM32DIR = $(AUXDIR)/com32
-all: libcom32.a
+all: libcom32.a libcomcore.a
libcom32.a : $(LIBOBJS)
rm -f $@
$(AR) cq $@ $^
$(RANLIB) $@
+libcomcore.a : $(LIBCOREOBJS)
+ rm -f $@
+ $(AR) cq $@ $^
+ $(RANLIB) $@
+
tidy dist clean:
rm -f sys/vesa/alphatbl.c
find . \( -name \*.o -o -name \*.a -o -name .\*.d -o -name \*.tmp \) -print0 | \
diff --git a/com32/lib/closedir.c b/com32/lib/closedir.c
deleted file mode 100644
index a2d11105..00000000
--- a/com32/lib/closedir.c
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * closedir.c
- */
-
-#include <dirent.h>
-#include <stdio.h>
-#include <errno.h>
-
-#include <com32.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdlib.h>
-
-int closedir(DIR * dir)
-{
- int rv;
- com32sys_t regs;
- if (dir == NULL) {
- rv = 0;
- } else {
- memset(&regs, 0, sizeof regs); /* ?Needed? */
- regs.eax.w[0] = 0x0022;
- regs.esi.w[0] = dir->dd_fd;
- __com32.cs_intcall(0x22, &regs, &regs);
- free(dir); /* garbage collection? */
- rv = 0;
- }
- return rv;
-}
diff --git a/com32/lib/com32.ld b/com32/lib/com32.ld
index 36d5b6ed..37ee46cf 100644
--- a/com32/lib/com32.ld
+++ b/com32/lib/com32.ld
@@ -11,8 +11,9 @@ ENTRY(_start)
SECTIONS
{
/* Read-only sections, merged into text segment: */
- . = 0x101000;
+ . = 0;
PROVIDE (__executable_start = .);
+ PROVIDE (_stext = .);
.init :
{
@@ -28,40 +29,52 @@ SECTIONS
{
KEEP (*(.fini))
} =0x90909090
- PROVIDE (__etext = .);
PROVIDE (_etext = .);
- PROVIDE (etext = .);
+
+ __rodata_start = .;
.rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
.rodata1 : { *(.rodata1) }
+ __rodata_end = .;
/* Ensure the __preinit_array_start label is properly aligned. We
could instead move the label definition inside the section, but
the linker would then create the section even if it turns out to
be empty, which isn't pretty. */
. = ALIGN(4);
- PROVIDE (__preinit_array_start = .);
- .preinit_array : { *(.preinit_array) }
- PROVIDE (__preinit_array_end = .);
- PROVIDE (__init_array_start = .);
- .init_array : { *(.init_array) }
- PROVIDE (__init_array_end = .);
- PROVIDE (__fini_array_start = .);
- .fini_array : { *(.fini_array) }
- PROVIDE (__fini_array_end = .);
- PROVIDE (__ctors_start = .);
- .ctors :
- {
+ .preinit_array : {
+ PROVIDE (__preinit_array_start = .);
+ *(.preinit_array)
+ PROVIDE (__preinit_array_end = .);
+ }
+ .init_array : {
+ PROVIDE (__init_array_start = .);
+ *(.init_array)
+ PROVIDE (__init_array_end = .);
+ }
+ .fini_array : {
+ PROVIDE (__fini_array_start = .);
+ *(.fini_array)
+ PROVIDE (__fini_array_end = .);
+ }
+ .ctors : {
+ PROVIDE (__ctors_start = .);
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
+ PROVIDE (__ctors_end = .);
}
- PROVIDE (__ctors_end = .);
- PROVIDE (__dtors_start = .);
- .dtors :
- {
+ .dtors : {
+ PROVIDE (__dtors_start = .);
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
+ PROVIDE (__dtors_end = .);
+ }
+
+ .got : {
+ PROVIDE (__got_start = .);
+ KEEP (*(.got.plt))
+ KEEP (*(.got))
+ PROVIDE (__got_end = .);
}
- PROVIDE (__dtors_end = .);
/* Adjust the address for the data segment. Avoid mixing code and
data within same 128-byte chunk. */
@@ -69,12 +82,14 @@ SECTIONS
.data :
{
- *(.data .data.* .gnu.linkonce.d.*)
+ _sdata = .;
+ KEEP(*(.data .data.* .gnu.linkonce.d.*))
SORT(CONSTRUCTORS)
+ *(.data1)
+ . = ALIGN(4);
+ _edata = .;
}
- .data1 : { *(.data1) }
- _edata = .;
- PROVIDE (edata = .);
+
__bss_start = .;
.bss :
{
@@ -84,11 +99,10 @@ SECTIONS
/* Align here to ensure that the .bss section occupies space up to
_end. Align after .bss to ensure correct alignment even if the
.bss section disappears because there are no input sections. */
- . = ALIGN(32 / 8);
+ . = ALIGN(4);
}
- . = ALIGN(32 / 8);
+ . = ALIGN(4);
_end = .;
- PROVIDE (end = .);
/* Stabs debugging sections. */
.stab 0 : { *(.stab) }
diff --git a/com32/lib/fprintf.c b/com32/lib/fprintf.c
index 36b86265..5eb8e079 100644
--- a/com32/lib/fprintf.c
+++ b/com32/lib/fprintf.c
@@ -5,8 +5,6 @@
#include <stdio.h>
#include <stdarg.h>
-#define BUFFER_SIZE 16384
-
int fprintf(FILE * file, const char *format, ...)
{
va_list ap;
diff --git a/com32/lib/lmalloc.c b/com32/lib/lmalloc.c
new file mode 100644
index 00000000..a646556c
--- /dev/null
+++ b/com32/lib/lmalloc.c
@@ -0,0 +1,57 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2010 Intel Corporation; author: H. Peter Anvin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall
+ * be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include <com32.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslinux/pmapi.h>
+
+void *lmalloc(size_t size)
+{
+ void *p;
+ p = __com32.cs_pm->lmalloc(size);
+ if (!p)
+ errno = ENOMEM;
+ return p;
+}
+
+void *lzalloc(size_t size)
+{
+ void *p;
+ p = __com32.cs_pm->lmalloc(size);
+ if (!p)
+ errno = ENOMEM;
+ else
+ memset(p, 0, size);
+ return p;
+}
+
+void lfree(void *ptr)
+{
+ __com32.cs_pm->lfree(ptr);
+}
diff --git a/com32/lib/lstrdup.c b/com32/lib/lstrdup.c
new file mode 100644
index 00000000..d11efe7e
--- /dev/null
+++ b/com32/lib/lstrdup.c
@@ -0,0 +1,18 @@
+/*
+ * lstrdup.c
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <com32.h>
+
+char *lstrdup(const char *s)
+{
+ int l = strlen(s) + 1;
+ char *d = lmalloc(l);
+
+ if (d)
+ memcpy(d, s, l);
+
+ return d;
+}
diff --git a/com32/lib/memmove.S b/com32/lib/memmove.S
index b7ac6767..e97299f2 100644
--- a/com32/lib/memmove.S
+++ b/com32/lib/memmove.S
@@ -1,6 +1,7 @@
/* ----------------------------------------------------------------------- *
*
* Copyright 2008 H. Peter Anvin - All Rights Reserved
+ * Copyright 2010 Intel Corporation; author: H. Peter Anvin
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
@@ -36,7 +37,7 @@
.type memmove,@function
.text
memmove:
- jecxz 3f
+ jecxz 4f
pushl %esi
pushl %edi
@@ -46,11 +47,12 @@ memmove:
movl %edx,%esi
cmpl %edi,%esi
- jb 1f
+ jb 2f
/* source >= dest, forwards move */
/* Initial alignment */
+1:
movl %edi,%edx
shrl $1,%edx
jnc 11f
@@ -81,14 +83,24 @@ memmove:
jz 15f
movsb
15:
- jmp 2f
+ /* Common exit stub */
+3:
+ popl %eax /* Return value */
+ popl %edi
+ popl %esi
+4:
+ ret
-1:
- /* source < dest, backwards move */
+2:
+ /* source < dest, backwards move if overlap */
+ leal -1(%ecx,%esi),%eax
+ cmpl %eax,%edi
+ ja 1b /* No overlap, after all... */
+
std
- leal -1(%ecx,%esi),%esi
leal -1(%ecx,%edi),%edi
+ movl %eax,%esi
/* Initial alignment */
movl %edi,%edx
@@ -129,11 +141,6 @@ memmove:
movsb
25:
cld
-2:
- popl %eax /* Return value */
- popl %edi
- popl %esi
-3:
- ret
+ jmp 3b
.size memmove, .-memmove
diff --git a/com32/lib/opendir.c b/com32/lib/opendir.c
deleted file mode 100644
index 21fe91d4..00000000
--- a/com32/lib/opendir.c
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * opendir.c
- */
-
-#include <dirent.h>
-#include <stdio.h>
-#include <errno.h>
-
-#include <com32.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdlib.h>
-
-DIR *opendir(const char *pathname)
-{
- DIR *newdir;
- com32sys_t regs;
-
- newdir = NULL;
-
- strlcpy(__com32.cs_bounce, pathname, __com32.cs_bounce_size);
-
- regs.eax.w[0] = 0x0020;
- regs.esi.w[0] = OFFS(__com32.cs_bounce);
- regs.es = SEG(__com32.cs_bounce);
-
- __com32.cs_intcall(0x22, &regs, &regs);
-
- if (!(regs.eflags.l & EFLAGS_CF)) {
- /* Initialization: malloc() then zero */
- newdir = calloc(1, sizeof(DIR));
- strcpy(newdir->dd_name, pathname);
- newdir->dd_fd = regs.esi.w[0];
- newdir->dd_sect = regs.eax.l;
- newdir->dd_stat = 0;
- }
-
- /* We're done */
- return newdir;
-}
diff --git a/com32/lib/printf.c b/com32/lib/printf.c
index a6f5b508..86c2b767 100644
--- a/com32/lib/printf.c
+++ b/com32/lib/printf.c
@@ -5,8 +5,6 @@
#include <stdio.h>
#include <stdarg.h>
-#define BUFFER_SIZE 16384
-
int printf(const char *format, ...)
{
va_list ap;
diff --git a/com32/lib/qsort.c b/com32/lib/qsort.c
index a67866d3..a9d646ce 100644
--- a/com32/lib/qsort.c
+++ b/com32/lib/qsort.c
@@ -6,6 +6,7 @@
*/
#include <stddef.h>
+#include <stdlib.h>
#include <string.h>
static inline size_t newgap(size_t gap)
@@ -27,6 +28,9 @@ void qsort(void *base, size_t nmemb, size_t size,
char *p1, *p2;
int swapped;
+ if (!nmemb)
+ return;
+
do {
gap = newgap(gap);
swapped = 0;
diff --git a/com32/lib/readdir.c b/com32/lib/readdir.c
deleted file mode 100644
index 3737d1ad..00000000
--- a/com32/lib/readdir.c
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * readdir.c
- */
-
-#include <dirent.h>
-#include <stdio.h>
-#include <errno.h>
-
-#include <com32.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdlib.h>
-
-struct dirent *readdir(DIR * dir)
-{
- struct dirent *newde;
- com32sys_t regs;
-
- newde = NULL;
- if ((dir != NULL) && (dir->dd_fd != 0) && (dir->dd_stat >= 0)) {
- memset(__com32.cs_bounce, 0, 32);
- memset(&regs, 0, sizeof(regs));
-
- regs.eax.w[0] = 0x0021;
- regs.esi.w[0] = dir->dd_fd;
- regs.edi.w[0] = OFFS(__com32.cs_bounce);
- regs.es = SEG(__com32.cs_bounce);
-
- __com32.cs_intcall(0x22, &regs, &regs);
-
-#if 0
- /* Don't do this as we won't be able to rewind. */
- dir->dd_fd = regs.esi.w[0]; /* Shouldn't be needed? */
-#endif
- if ((!(regs.eflags.l & EFLAGS_CF)) && (regs.esi.w[0] != 0)) {
- newde = calloc(1, sizeof(newde));
- if (newde != NULL) {
- strcpy(newde->d_name, __com32.cs_bounce);
- newde->d_mode = regs.edx.b[0];
- newde->d_size = regs.eax.l;
- newde->d_ino = regs.ebx.l;
- dir->dd_stat = 1;
- } else {
- dir->dd_stat = -2;
- errno = ENOMEM;
- }
- } else {
- dir->dd_stat = -1;
- errno = EIO; /* Is this the right nmber? */
- }
- } else {
- errno = EBADF;
- }
-
- return newde;
-}
diff --git a/com32/lib/strpcpy.c b/com32/lib/strpcpy.c
deleted file mode 100644
index a4fd2a06..00000000
--- a/com32/lib/strpcpy.c
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * strpcpy.c
- *
- * strpcpy() - strcpy() which returns a pointer to the final null
- */
-
-#include <string.h>
-
-char *strpcpy(char *dst, const char *src)
-{
- char *q = dst;
- const char *p = src;
- char ch;
-
- do {
- *q++ = ch = *p++;
- } while (ch);
-
- return q - 1;
-}
diff --git a/com32/lib/sys/entry.S b/com32/lib/sys/entry.S
index c34dbdf4..7bfde8b2 100644
--- a/com32/lib/sys/entry.S
+++ b/com32/lib/sys/entry.S
@@ -30,60 +30,84 @@
*/
/* Number of arguments in our version of the entry structure */
-#define COM32_ARGS 8
+#define COM32_ARGS 9
.section ".init","ax"
.globl _start
.type _start, @function
_start:
- /* This first instruction acts as COM32 magic number */
- movl $0x21cd4cff,%eax
+ /* This first instruction acts as COM32R magic number */
+ movl $0x21cd4cfe,%eax
/* Upwards string operations */
cld
+ /* Find our own location */
+ call 1f
+1: popl %ebx
+ addl $_GLOBAL_OFFSET_TABLE_ + (. - 1b), %ebx
+
+ /* Process relocations (which overlay the .bss segment) */
+ leal _edata@GOTOFF(%ebx),%esi
+ leal _start@GOTOFF(%ebx),%edx
+2: lodsl
+ andl %eax,%eax
+ jz 3f
+ addl %edx,(%eax,%edx)
+ jmp 2b
+3:
+ /* Relocate the GOT (is this right?) */
+ leal __got_start@GOTOFF(%ebx),%esi
+ leal __got_end@GOTOFF(%ebx),%edi
+4:
+ addl %edx,(%esi)
+ addl $4,%esi
+ cmpl %edi,%esi
+ jb 4b
+
/* Zero the .bss segment */
xorl %eax,%eax
- movl $__bss_start,%edi # Symbol provided by linker
- movl $_end+3,%ecx # Symbol provided by linker
+ leal __bss_start@GOTOFF(%ebx),%edi
+ leal _end+3@GOTOFF(%ebx),%ecx
subl %edi,%ecx
shrl $2,%ecx
rep ; stosl
/* Copy COM32 invocation parameters */
leal 4(%esp),%esi # Argument list
- movl $__com32,%edi
+ leal __com32@GOTOFF(%ebx),%edi
movl $(COM32_ARGS),%ecx
movl %esp,-4(%edi) # Save the initial stack ptr
cmpl (%esi),%ecx
- jbe 1f
+ jbe 5f
movl (%esi),%ecx
-1: inc %ecx # Copy the argument count, too
+5: inc %ecx # Copy the argument count, too
rep ; movsl
/* Parse the command line (assumes REGPARM) */
- movl __com32+4,%edx # Command line
- pushl %edx # Make space for argv
+ movl __com32+4@GOTOFF(%ebx),%edx # Command line
+ pushl %edx # Make space for argv
movl %esp,%eax
call __parse_argv
- pushl %eax # Save argc
+ pushl %eax # Save argc
/* Look for library initialization functions */
- movl $__ctors_start, %esi
-2:
- cmpl $__ctors_end, %esi
- jae 3f
+ leal __ctors_start@GOTOFF(%ebx),%esi
+ leal __ctors_end@GOTOFF(%ebx),%edi
+6:
+ cmpl %edi,%esi
+ jae 7f
call *(%esi)
addl $4,%esi
- jmp 2b
+ jmp 6b
/*
* Actually run main. This assumes REGPARM is used!!!!
*/
-3:
+7:
popl %eax # argc
popl %edx # argv
call main
- call *(__exit_handler)
+ call *__exit_handler@GOTOFF(%ebx)
hlt
.size _start, .-_start
diff --git a/com32/lib/sys/file.h b/com32/lib/sys/file.h
index fff91b19..e984f160 100644
--- a/com32/lib/sys/file.h
+++ b/com32/lib/sys/file.h
@@ -39,6 +39,7 @@
#include <sys/types.h>
#include <dev.h>
#include <fcntl.h>
+#include <syslinux/pmapi.h>
/* Device structure; contains the relevant operations */
@@ -56,18 +57,18 @@ struct input_dev {
uint16_t dev_magic; /* Magic number */
uint16_t flags; /* Flags */
int fileflags; /* Permitted file flags */
- ssize_t(*read) (struct file_info *, void *, size_t);
- int (*close) (struct file_info *);
- int (*open) (struct file_info *);
+ ssize_t (*read)(struct file_info *, void *, size_t);
+ int (*close)(struct file_info *);
+ int (*open)(struct file_info *);
};
struct output_dev {
uint16_t dev_magic; /* Magic number */
uint16_t flags; /* Flags */
int fileflags;
- ssize_t(*write) (struct file_info *, const void *, size_t);
- int (*close) (struct file_info *);
- int (*open) (struct file_info *);
+ ssize_t (*write)(struct file_info *, const void *, size_t);
+ int (*close)(struct file_info *);
+ int (*open)(struct file_info *);
const struct output_dev *fallback; /* Fallback option for certain consoles */
};
@@ -87,11 +88,8 @@ struct file_info {
/* Structure used for input blocking */
struct {
- int blocklg2; /* Blocksize log 2 */
+ struct com32_filedata fd;
size_t offset; /* Current file offset */
- size_t length; /* Total file length */
- uint16_t filedes; /* File descriptor */
- uint16_t _filler; /* Unused */
size_t nbytes; /* Number of bytes available in buffer */
char *datap; /* Current data pointer */
void *pvt; /* Private pointer for driver */
diff --git a/com32/lib/sys/fileclose.c b/com32/lib/sys/fileclose.c
index e005567e..e2c929f2 100644
--- a/com32/lib/sys/fileclose.c
+++ b/com32/lib/sys/fileclose.c
@@ -38,15 +38,8 @@
int __file_close(struct file_info *fp)
{
- com32sys_t regs;
-
- if (fp->i.filedes) {
- memset(&regs, 0, sizeof regs);
- regs.eax.w[0] = 0x0008; /* Close file */
- regs.esi.w[0] = fp->i.filedes;
-
- __com32.cs_intcall(0x22, &regs, NULL);
- }
+ if (fp->i.fd.handle)
+ __com32.cs_pm->close_file(fp->i.fd.handle);
return 0;
}
diff --git a/com32/lib/sys/fileread.c b/com32/lib/sys/fileread.c
index 54ff7115..aab99c80 100644
--- a/com32/lib/sys/fileread.c
+++ b/com32/lib/sys/fileread.c
@@ -34,32 +34,23 @@
#include <errno.h>
#include <string.h>
#include <com32.h>
+#include <syslinux/pmapi.h>
#include <minmax.h>
#include "file.h"
int __file_get_block(struct file_info *fp)
{
- com32sys_t ireg, oreg;
+ ssize_t bytes_read;
- memset(&ireg, 0, sizeof ireg);
- ireg.eax.w[0] = 0x0007; /* Read file */
- ireg.ebx.w[0] = OFFS(__com32.cs_bounce);
- ireg.es = SEG(__com32.cs_bounce);
- ireg.esi.w[0] = fp->i.filedes;
- ireg.ecx.w[0] = MAXBLOCK >> fp->i.blocklg2;
-
- __intcall(0x22, &ireg, &oreg);
-
- if (oreg.eflags.l & EFLAGS_CF) {
+ bytes_read = __com32.cs_pm->read_file(&fp->i.fd.handle, fp->i.buf,
+ MAXBLOCK >> fp->i.fd.blocklg2);
+ if (!bytes_read) {
errno = EIO;
return -1;
}
-
- fp->i.filedes = oreg.esi.w[0];
- fp->i.nbytes = oreg.ecx.l;
- fp->i.datap = fp->i.buf;
- memcpy(fp->i.buf, __com32.cs_bounce, fp->i.nbytes);
-
+
+ fp->i.nbytes = bytes_read;
+ fp->i.datap = fp->i.buf;
return 0;
}
@@ -71,22 +62,36 @@ ssize_t __file_read(struct file_info * fp, void *buf, size_t count)
while (count) {
if (fp->i.nbytes == 0) {
- if (fp->i.offset >= fp->i.length || !fp->i.filedes)
+ if (fp->i.offset >= fp->i.fd.size || !fp->i.fd.handle)
return n; /* As good as it gets... */
- if (__file_get_block(fp))
- return n ? n : -1;
+ if (count > MAXBLOCK) {
+ /* Large transfer: copy directly, without buffering */
+ ncopy = __com32.cs_pm->read_file(&fp->i.fd.handle, bufp,
+ count >> fp->i.fd.blocklg2);
+ if (!ncopy) {
+ errno = EIO;
+ return n ? n : -1;
+ }
+
+ goto got_data;
+ } else {
+ if (__file_get_block(fp))
+ return n ? n : -1;
+ }
}
ncopy = min(count, fp->i.nbytes);
memcpy(bufp, fp->i.datap, ncopy);
- n += ncopy;
- bufp += ncopy;
- count -= ncopy;
fp->i.datap += ncopy;
fp->i.offset += ncopy;
fp->i.nbytes -= ncopy;
+
+ got_data:
+ n += ncopy;
+ bufp += ncopy;
+ count -= ncopy;
}
return n;
diff --git a/com32/lib/sys/fstat.c b/com32/lib/sys/fstat.c
index 6a853933..0ce8cadb 100644
--- a/com32/lib/sys/fstat.c
+++ b/com32/lib/sys/fstat.c
@@ -45,14 +45,14 @@ int fstat(int fd, struct stat *buf)
}
if (fp->iop->flags & __DEV_FILE) {
- if (fp->i.length == (uint32_t) - 1) {
+ if (fp->i.fd.size == (uint32_t) - 1) {
/* File of unknown length, report it as a socket
(it probably really is, anyway!) */
buf->st_mode = S_IFSOCK | 0444;
buf->st_size = 0;
} else {
buf->st_mode = S_IFREG | 0444;
- buf->st_size = fp->i.length;
+ buf->st_size = fp->i.fd.size;
}
} else {
buf->st_mode = S_IFCHR | 0666;
diff --git a/com32/lib/sys/gpxe.c b/com32/lib/sys/gpxe.c
index fae03f8f..d86da42a 100644
--- a/com32/lib/sys/gpxe.c
+++ b/com32/lib/sys/gpxe.c
@@ -7,39 +7,44 @@ bool is_gpxe(void)
const struct syslinux_version *sv;
com32sys_t reg;
struct s_PXENV_FILE_CHECK_API *fca;
+ bool gpxe;
sv = syslinux_version();
if (sv->filesystem != SYSLINUX_FS_PXELINUX)
return false; /* Not PXELINUX */
- fca = __com32.cs_bounce;
- memset(fca, 0, sizeof *fca);
+ fca = lzalloc(sizeof *fca);
+ if (!fca)
+ return false;
fca->Size = sizeof *fca;
fca->Magic = 0x91d447b2;
memset(&reg, 0, sizeof reg);
reg.eax.w[0] = 0x0009;
reg.ebx.w[0] = 0x00e6; /* PXENV_FILE_API_CHECK */
- reg.edi.w[0] = OFFS(fca);
+ /* reg.edi.w[0] = OFFS(fca); */
reg.es = SEG(fca);
__intcall(0x22, &reg, &reg);
+ gpxe = true;
+
if (reg.eflags.l & EFLAGS_CF)
- return false; /* Cannot invoke PXE stack */
+ gpxe = false; /* Cannot invoke PXE stack */
if (reg.eax.w[0] || fca->Status)
- return false; /* PXE failure */
+ gpxe = false; /* PXE failure */
if (fca->Magic != 0xe9c17b20)
- return false; /* Incorrect magic */
+ gpxe = false; /* Incorrect magic */
if (fca->Size < sizeof *fca)
- return false; /* Short return */
+ gpxe = false; /* Short return */
+ /* XXX: The APIs to test for should be a passed-in option */
if (!(fca->APIMask & (1 << 5)))
- return false; /* No FILE EXEC */
+ gpxe = false; /* No FILE EXEC */
- return true;
+ lfree(fca);
+ return gpxe;
}
-
diff --git a/com32/lib/sys/open.c b/com32/lib/sys/open.c
index aac5e6f4..cb7c1b4d 100644
--- a/com32/lib/sys/open.c
+++ b/com32/lib/sys/open.c
@@ -41,7 +41,7 @@
extern ssize_t __file_read(struct file_info *, void *, size_t);
extern int __file_close(struct file_info *);
-static const struct input_dev file_dev = {
+const struct input_dev __file_dev = {
.dev_magic = __DEV_MAGIC,
.flags = __DEV_FILE | __DEV_INPUT,
.fileflags = O_RDONLY,
@@ -52,38 +52,20 @@ static const struct input_dev file_dev = {
int open(const char *pathname, int flags, ...)
{
- com32sys_t regs;
- int fd;
+ int fd, handle;
struct file_info *fp;
- fd = opendev(&file_dev, NULL, flags);
+ fd = opendev(&__file_dev, NULL, flags);
if (fd < 0)
return -1;
fp = &__file_info[fd];
- strlcpy(__com32.cs_bounce, pathname, __com32.cs_bounce_size);
-
- regs.eax.w[0] = 0x0006;
- regs.esi.w[0] = OFFS(__com32.cs_bounce);
- regs.es = SEG(__com32.cs_bounce);
-
- __com32.cs_intcall(0x22, &regs, &regs);
-
- if ((regs.eflags.l & EFLAGS_CF) || regs.esi.w[0] == 0) {
- close(fd);
- errno = ENOENT;
+ handle = __com32.cs_pm->open_file(pathname, &fp->i.fd);
+ if (handle < 0)
return -1;
- }
- {
- uint16_t blklg2;
- asm("bsrw %1,%0" : "=r" (blklg2) : "rm" (regs.ecx.w[0]));
- fp->i.blocklg2 = blklg2;
- }
- fp->i.length = regs.eax.l;
- fp->i.filedes = regs.esi.w[0];
fp->i.offset = 0;
fp->i.nbytes = 0;
diff --git a/com32/lib/sys/openmem.c b/com32/lib/sys/openmem.c
new file mode 100644
index 00000000..a56a4af8
--- /dev/null
+++ b/com32/lib/sys/openmem.c
@@ -0,0 +1,61 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2003-2008 H. Peter Anvin - All Rights Reserved
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall
+ * be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include <errno.h>
+#include <com32.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include "file.h"
+
+/*
+ * openmem.c
+ *
+ * Open a chunk of memory as if it was a file
+ */
+
+const struct input_dev __file_dev;
+
+int openmem(const void *base, size_t len, int flags)
+{
+ int fd;
+ struct file_info *fp;
+
+ fd = opendev(&__file_dev, NULL, flags);
+
+ if (fd < 0)
+ return -1;
+
+ fp = &__file_info[fd];
+
+ fp->i.fd.size = fp->i.nbytes = len;
+ fp->i.datap = (void *)base;
+ fp->i.fd.handle = 0; /* No actual file */
+ fp->i.offset = 0;
+
+ return fd;
+}
diff --git a/com32/lib/sys/readdir.c b/com32/lib/sys/readdir.c
new file mode 100644
index 00000000..d2a8c039
--- /dev/null
+++ b/com32/lib/sys/readdir.c
@@ -0,0 +1,30 @@
+/*
+ * readdir.c
+ */
+
+#include <dirent.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <com32.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+
+#include <syslinux/pmapi.h>
+
+DIR *opendir(const char *pathname)
+{
+ return __com32.cs_pm->opendir(pathname);
+}
+
+struct dirent *readdir(DIR *dir)
+{
+ return __com32.cs_pm->readdir(dir);
+}
+
+int closedir(DIR *dir)
+{
+ return __com32.cs_pm->closedir(dir);
+}
diff --git a/com32/lib/sys/stdcon_write.c b/com32/lib/sys/stdcon_write.c
index 8cc6b5f5..9cb2f7db 100644
--- a/com32/lib/sys/stdcon_write.c
+++ b/com32/lib/sys/stdcon_write.c
@@ -37,6 +37,23 @@
#include <minmax.h>
#include "file.h"
+#define BIOS_ROWS (*(uint8_t *)0x484) /* Minus one; if zero use 24 (= 25 lines) */
+#define BIOS_COLS (*(uint16_t *)0x44A)
+
+static int __stdcon_open(struct file_info *fp)
+{
+ fp->o.rows = BIOS_ROWS + 1;
+ fp->o.cols = BIOS_COLS;
+
+ /* Sanity check */
+ if (fp->o.rows < 12)
+ fp->o.rows = 24;
+ if (fp->o.cols < 40)
+ fp->o.cols = 80;
+
+ return 0;
+}
+
static ssize_t __stdcon_write(struct file_info *fp, const void *buf,
size_t count)
{
@@ -68,5 +85,5 @@ const struct output_dev dev_stdcon_w = {
.fileflags = O_WRONLY | O_CREAT | O_TRUNC | O_APPEND,
.write = __stdcon_write,
.close = NULL,
- .open = NULL,
+ .open = __stdcon_open,
};
diff --git a/com32/lib/sys/vesa/initvesa.c b/com32/lib/sys/vesa/initvesa.c
index 0a436f4c..9a1ae384 100644
--- a/com32/lib/sys/vesa/initvesa.c
+++ b/com32/lib/sys/vesa/initvesa.c
@@ -95,9 +95,13 @@ static int vesacon_set_mode(int x, int y)
com32sys_t rm;
uint8_t *rom_font;
uint16_t mode, bestmode, *mode_ptr;
+ struct vesa_info *vi;
struct vesa_general_info *gi;
struct vesa_mode_info *mi;
enum vesa_pixel_format pxf, bestpxf;
+ int err = 0;
+
+ debug("Hello, World!\r\n");
/* Free any existing data structures */
if (__vesacon_background) {
@@ -110,13 +114,15 @@ static int vesacon_set_mode(int x, int y)
}
/* Allocate space in the bounce buffer for these structures */
- gi = &((struct vesa_info *)__com32.cs_bounce)->gi;
- mi = &((struct vesa_info *)__com32.cs_bounce)->mi;
-
- debug("Hello, World!\r\n");
+ vi = lzalloc(sizeof *vi);
+ if (!vi) {
+ err = 10; /* Out of memory */
+ goto exit;
+ }
+ gi = &vi->gi;
+ mi = &vi->mi;
memset(&rm, 0, sizeof rm);
- memset(gi, 0, sizeof *gi);
gi->signature = VBE2_MAGIC; /* Get VBE2 extended data */
rm.eax.w[0] = 0x4F00; /* Get SVGA general information */
@@ -124,12 +130,18 @@ static int vesacon_set_mode(int x, int y)
rm.es = SEG(gi);
__intcall(0x10, &rm, &rm);
- if (rm.eax.w[0] != 0x004F)
- return 1; /* Function call failed */
- if (gi->signature != VESA_MAGIC)
- return 2; /* No magic */
- if (gi->version < 0x0102)
- return 3; /* VESA 1.2+ required */
+ if (rm.eax.w[0] != 0x004F) {
+ err = 1; /* Function call failed */
+ goto exit;
+ }
+ if (gi->signature != VESA_MAGIC) {
+ err = 2; /* No magic */
+ goto exit;
+ }
+ if (gi->version < 0x0102) {
+ err = 3; /* VESA 1.2+ required */
+ goto exit;
+ }
/* Copy general info */
memcpy(&__vesa_info.gi, gi, sizeof *gi);
@@ -232,8 +244,10 @@ static int vesacon_set_mode(int x, int y)
}
}
- if (bestpxf == PXF_NONE)
- return 4; /* No mode found */
+ if (bestpxf == PXF_NONE) {
+ err = 4; /* No mode found */
+ goto exit;
+ }
mi = &__vesa_info.mi;
mode = bestmode;
@@ -260,8 +274,10 @@ static int vesacon_set_mode(int x, int y)
mode |= 0x4000; /* Request linear framebuffer if supported */
rm.ebx.w[0] = mode;
__intcall(0x10, &rm, &rm);
- if (rm.eax.w[0] != 0x004F)
- return 9; /* Failed to set mode */
+ if (rm.eax.w[0] != 0x004F) {
+ err = 9; /* Failed to set mode */
+ goto exit;
+ }
__vesacon_background = calloc(mi->h_res*mi->v_res, 4);
__vesacon_shadowfb = calloc(mi->h_res*mi->v_res, 4);
@@ -279,7 +295,11 @@ static int vesacon_set_mode(int x, int y)
__vesacon_pixel_format = bestpxf;
- return 0;
+exit:
+ if (vi)
+ lfree(vi);
+
+ return err;
}
static int init_text_display(void)
diff --git a/com32/lib/sys/zfile.c b/com32/lib/sys/zfile.c
index a1213a94..0e6ba916 100644
--- a/com32/lib/sys/zfile.c
+++ b/com32/lib/sys/zfile.c
@@ -75,7 +75,7 @@ static int gzip_file_init(struct file_info *fp)
}
fp->iop = &gzip_file_dev;
- fp->i.length = -1; /* Unknown */
+ fp->i.fd.size = -1; /* Unknown */
return 0;
}
@@ -92,7 +92,7 @@ static ssize_t gzip_file_read(struct file_info *fp, void *ptr, size_t n)
zs->next_out = p;
zs->avail_out = n;
- if (!zs->avail_in && fp->i.filedes) {
+ if (!zs->avail_in && fp->i.fd.handle) {
if (__file_get_block(fp))
return nout ? nout : -1;
@@ -154,7 +154,9 @@ int zopen(const char *pathname, int flags, ...)
if (__file_get_block(fp))
goto err;
- if (fp->i.nbytes >= 14 && (uint8_t) fp->i.buf[0] == 037 && (uint8_t) fp->i.buf[1] == 0213 && /* gzip */
+ if (fp->i.nbytes >= 14 &&
+ (uint8_t) fp->i.buf[0] == 037 &&
+ (uint8_t) fp->i.buf[1] == 0213 && /* gzip */
fp->i.buf[2] == 8) /* deflate */
rv = gzip_file_init(fp);
else
diff --git a/com32/lib/syslinux/idle.c b/com32/lib/syslinux/idle.c
index 8a0d206c..ddaa7fcd 100644
--- a/com32/lib/syslinux/idle.c
+++ b/com32/lib/syslinux/idle.c
@@ -33,25 +33,15 @@
#include <stddef.h>
#include <com32.h>
-#include <sys/cpu.h>
+#include <syslinux/pmapi.h>
#include <syslinux/idle.h>
-void syslinux_idle(void)
+void syslinux_reset_idle(void)
{
- static int do_idle = 1;
- static const com32sys_t sys_idle = {
- .eax.l = 0x0013,
- };
- com32sys_t idle_result;
-
- /* This call isn't supported on SYSLINUX < 3.08, but all it does
- is return an error, so we don't care. */
-
- if (do_idle) {
- __intcall(0x22, &sys_idle, &idle_result);
-
- do_idle = ~idle_result.eflags.l & EFLAGS_CF;
- }
+ __com32.cs_pm->reset_idle();
+}
- cpu_relax();
+void syslinux_idle(void)
+{
+ __com32.cs_pm->idle();
}
diff --git a/com32/lib/syslinux/memscan.c b/com32/lib/syslinux/memscan.c
index 95580257..fc676cbf 100644
--- a/com32/lib/syslinux/memscan.c
+++ b/com32/lib/syslinux/memscan.c
@@ -51,7 +51,7 @@ int syslinux_scan_memory(scan_memory_callback_t callback, void *data)
{
static com32sys_t ireg;
com32sys_t oreg;
- struct e820_entry *e820buf = __com32.cs_bounce;
+ struct e820_entry *e820buf;
uint64_t start, len, maxlen;
int memfound = 0;
int rv;
@@ -74,13 +74,16 @@ int syslinux_scan_memory(scan_memory_callback_t callback, void *data)
return rv;
/* First try INT 15h AX=E820h */
+ e820buf = lzalloc(sizeof *e820buf);
+ if (!e820buf)
+ return -1;
+
ireg.eax.l = 0xe820;
ireg.edx.l = 0x534d4150;
ireg.ebx.l = 0;
ireg.ecx.l = sizeof(*e820buf);
ireg.es = SEG(e820buf);
ireg.edi.w[0] = OFFS(e820buf);
- memset(e820buf, 0, sizeof *e820buf);
do {
__intcall(0x15, &ireg, &oreg);
@@ -120,6 +123,8 @@ int syslinux_scan_memory(scan_memory_callback_t callback, void *data)
ireg.ebx.l = oreg.ebx.l;
} while (oreg.ebx.l);
+ lfree(e820buf);
+
if (memfound)
return 0;
diff --git a/com32/lib/syslinux/pxe_dns.c b/com32/lib/syslinux/pxe_dns.c
index 9ab95137..6620396f 100644
--- a/com32/lib/syslinux/pxe_dns.c
+++ b/com32/lib/syslinux/pxe_dns.c
@@ -48,21 +48,26 @@ uint32_t pxe_dns(const char *hostname)
unsigned char b[4];
uint32_t ip;
} q;
+ char *lm_hostname;
/* Is this a dot-quad? */
if (sscanf(hostname, "%hhu.%hhu.%hhu.%hhu",
&q.b[0], &q.b[1], &q.b[2], &q.b[3]) == 4)
return q.ip;
+ lm_hostname = lstrdup(hostname);
+ if (!lm_hostname)
+ return 0;
+
memset(&regs, 0, sizeof regs);
regs.eax.w[0] = 0x0010;
- regs.es = SEG(__com32.cs_bounce);
- regs.ebx.w[0] = OFFS(__com32.cs_bounce);
-
- strcpy((char *)__com32.cs_bounce, hostname);
+ regs.es = SEG(lm_hostname);
+ /* regs.ebx.w[0] = OFFS(lm_hostname); */
__intcall(0x22, &regs, &regs);
+ lfree(lm_hostname);
+
if (regs.eflags.l & EFLAGS_CF)
return 0;
diff --git a/com32/lib/syslinux/pxe_get_cached.c b/com32/lib/syslinux/pxe_get_cached.c
index 2e8349fe..47040378 100644
--- a/com32/lib/syslinux/pxe_get_cached.c
+++ b/com32/lib/syslinux/pxe_get_cached.c
@@ -42,40 +42,55 @@
or -1 on invocation failure */
int pxe_get_cached_info(int level, void **buf, size_t * len)
{
+ const int max_dhcp_packet = 2048;
com32sys_t regs;
- t_PXENV_GET_CACHED_INFO *gci = __com32.cs_bounce;
+ t_PXENV_GET_CACHED_INFO *gci;
void *bbuf, *nbuf;
+ int err;
+
+ gci = lmalloc(sizeof *gci + max_dhcp_packet);
+ if (!gci)
+ return -1;
memset(&regs, 0, sizeof regs);
regs.eax.w[0] = 0x0009;
regs.ebx.w[0] = PXENV_GET_CACHED_INFO;
regs.es = SEG(gci);
- regs.edi.w[0] = OFFS(gci);
+ /* regs.edi.w[0] = OFFS(gci); */
bbuf = &gci[1];
gci->Status = PXENV_STATUS_FAILURE;
gci->PacketType = level;
- gci->BufferSize = gci->BufferLimit = 65536 - sizeof(*gci);
+ gci->BufferSize = gci->BufferLimit = max_dhcp_packet;
gci->Buffer.seg = SEG(bbuf);
gci->Buffer.offs = OFFS(bbuf);
__intcall(0x22, &regs, &regs);
- if (regs.eflags.l & EFLAGS_CF)
- return -1;
+ if (regs.eflags.l & EFLAGS_CF) {
+ err = -1;
+ goto exit;
+ }
- if (gci->Status)
- return gci->Status;
+ if (gci->Status) {
+ err = gci->Status;
+ goto exit;
+ }
- nbuf = malloc(gci->BufferSize); /* malloc() does not use the bounce buffer */
- if (!nbuf)
- return -1;
+ nbuf = malloc(gci->BufferSize);
+ if (!nbuf) {
+ err = -1;
+ goto exit;
+ }
memcpy(nbuf, bbuf, gci->BufferSize);
*buf = nbuf;
*len = gci->BufferSize;
+ err = 0;
- return 0;
+exit:
+ lfree(gci);
+ return err;
}
diff --git a/com32/lib/syslinux/pxe_get_nic.c b/com32/lib/syslinux/pxe_get_nic.c
index 704a0d79..b301a75a 100644
--- a/com32/lib/syslinux/pxe_get_nic.c
+++ b/com32/lib/syslinux/pxe_get_nic.c
@@ -40,19 +40,25 @@
/* Returns the status code from PXE (0 on success),
or -1 on invocation failure */
-int pxe_get_nic_type(t_PXENV_UNDI_GET_NIC_TYPE * gnt)
+int pxe_get_nic_type(t_PXENV_UNDI_GET_NIC_TYPE *gnt)
{
com32sys_t regs;
+ t_PXENV_UNDI_GET_NIC_TYPE *lgnt;
+
+ lgnt = lzalloc(sizeof *lgnt);
+ if (!lgnt)
+ return -1;
memset(&regs, 0, sizeof regs);
regs.eax.w[0] = 0x0009;
regs.ebx.w[0] = PXENV_UNDI_GET_NIC_TYPE;
- regs.es = SEG(__com32.cs_bounce);
- regs.edi.w[0] = OFFS(__com32.cs_bounce);
+ regs.es = SEG(lgnt);
+ /* regs.edi.w[0] = OFFS(lgnt); */
__intcall(0x22, &regs, &regs);
- memcpy(gnt, __com32.cs_bounce, sizeof(t_PXENV_UNDI_GET_NIC_TYPE));
+ memcpy(gnt, lgnt, sizeof(t_PXENV_UNDI_GET_NIC_TYPE));
+ lfree(lgnt);
if (regs.eflags.l & EFLAGS_CF)
return -1;
diff --git a/com32/lib/syslinux/run_command.c b/com32/lib/syslinux/run_command.c
index 4693e16d..a0ac9a0d 100644
--- a/com32/lib/syslinux/run_command.c
+++ b/com32/lib/syslinux/run_command.c
@@ -30,18 +30,21 @@
#include <string.h>
#include <com32.h>
-__noreturn syslinux_run_command(const char *command)
+int syslinux_run_command(const char *command)
{
static com32sys_t ireg;
+ char *lm_command = lstrdup(command);
- strcpy(__com32.cs_bounce, command);
-
+ if (!lm_command)
+ return -1;
+
ireg.eax.w[0] = 0x0003;
- ireg.es = SEG(__com32.cs_bounce);
- ireg.ebx.w[0] = OFFS(__com32.cs_bounce);
+ ireg.es = SEG(lm_command);
+ /* ireg.ebx.w[0] = OFFS(lm_command); */
__intcall(0x22, &ireg, NULL);
- /* Should not return even on failure */
- for (;;) ;
+ /* Should not return even on failure, but in case... */
+ lfree(lm_command);
+ return -1;
}
diff --git a/com32/lib/syslinux/runimage.c b/com32/lib/syslinux/runimage.c
index 0184df37..29e9aadd 100644
--- a/com32/lib/syslinux/runimage.c
+++ b/com32/lib/syslinux/runimage.c
@@ -40,26 +40,32 @@ void syslinux_run_kernel_image(const char *filename, const char *cmdline,
uint32_t ipappend_flags, uint32_t type)
{
static com32sys_t ireg;
- char *bbfilename, *bbcmdline, *bbptr;
+ char *bbfilename = NULL;
+ char *bbcmdline = NULL;
int bytes;
- bbptr = __com32.cs_bounce;
+ bbfilename = lstrdup(filename);
+ if (!bbfilename)
+ goto fail;
- bytes = strlen(filename) + 1;
- memcpy(bbfilename = bbptr, filename, bytes);
- bbptr += bytes;
+ bbcmdline = lstrdup(cmdline);
+ if (!bbcmdline)
+ goto fail;
- bytes = strlen(cmdline) + 1;
- memcpy(bbcmdline = bbptr, filename, bytes);
- bbptr += bytes;
ireg.eax.w[0] = 0x0016;
ireg.ds = SEG(bbfilename);
- ireg.esi.w[0] = OFFS(bbfilename);
+ /* ireg.esi.w[0] = OFFS(bbfilename); */
ireg.es = SEG(bbcmdline);
- ireg.ebx.w[0] = OFFS(bbcmdline);
+ /* ireg.ebx.w[0] = OFFS(bbcmdline); */
ireg.ecx.l = ipappend_flags;
ireg.edx.l = type;
__intcall(0x22, &ireg, 0);
+
+fail:
+ if (bbcmdline)
+ lfree(bbcmdline);
+ if (bbfilename)
+ lfree(bbfilename);
}
diff --git a/com32/lib/vdprintf.c b/com32/lib/vdprintf.c
index ea9e0488..330279b3 100644
--- a/com32/lib/vdprintf.c
+++ b/com32/lib/vdprintf.c
@@ -14,15 +14,41 @@
#define DEBUG 1
#include <dprintf.h>
-#define BUFFER_SIZE 32768
+#define BUFFER_SIZE 4096
+enum serial_port_regs {
+ THR = 0,
+ RBR = 0,
+ DLL = 0,
+ DLM = 1,
+ IER = 1,
+ IIR = 2,
+ FCR = 2,
+ LCR = 3,
+ MCR = 4,
+ LSR = 5,
+ MSR = 6,
+ SCR = 7,
+};
static const uint16_t debug_base = 0x03f8; /* I/O base address */
+static void debug_putc(char c)
+{
+ if (c == '\n')
+ debug_putc('\r');
+
+ while ((inb(debug_base + LSR) & 0x20) == 0)
+ cpu_relax();
+ outb(c, debug_base + THR);
+}
+
void vdprintf(const char *format, va_list ap)
{
int rv;
char buffer[BUFFER_SIZE];
char *p;
+ static bool debug_init = false;
+ static bool debug_ok = false;
rv = vsnprintf(buffer, BUFFER_SIZE, format, ap);
@@ -37,10 +63,49 @@ void vdprintf(const char *format, va_list ap)
* if one is enabled or not (this means we don't have to enable the real
* serial console and therefore get conflicting output.)
*/
- p = buffer;
- while (rv--) {
- while ((inb(debug_base+5) & 0x20) == 0)
- cpu_relax();
- outb(*p++, debug_base);
+ if (__unlikely(!debug_init)) {
+ uint8_t dll, dlm, lcr;
+
+ debug_init = true;
+
+ cli();
+
+ /* Initialize the serial port to 115200 n81 with FIFOs enabled */
+ outb(0x83, debug_base + LCR);
+ outb(0x01, debug_base + DLL);
+ outb(0x00, debug_base + DLM);
+ (void)inb(debug_base + IER); /* Synchronize */
+ dll = inb(debug_base + DLL);
+ dlm = inb(debug_base + DLM);
+ lcr = inb(debug_base + LCR);
+
+ outb(0x03, debug_base + LCR);
+ (void)inb(debug_base + IER); /* Synchronize */
+
+ outb(0x00, debug_base + IER);
+ (void)inb(debug_base + IER); /* Synchronize */
+
+ sti();
+
+ if (dll != 0x01 || dlm != 0x00 || lcr != 0x83) {
+ /* No serial port present */
+ return;
+ }
+
+ outb(0x01, debug_base + FCR);
+ (void)inb(debug_base + IER); /* Synchronize */
+ if (inb(debug_base + IIR) < 0xc0) {
+ outb(0x00, debug_base + FCR); /* Disable non-functional FIFOs */
+ (void)inb(debug_base + IER); /* Synchronize */
+ }
+
+ debug_ok = true;
}
+
+ if (!debug_ok)
+ return;
+
+ p = buffer;
+ while (rv--)
+ debug_putc(*p++);
}
diff --git a/com32/mboot/mboot.c b/com32/mboot/mboot.c
index 915c7857..35450e03 100644
--- a/com32/mboot/mboot.c
+++ b/com32/mboot/mboot.c
@@ -135,7 +135,7 @@ static int get_modules(char **argv, struct module_data **mdp)
char *p;
mp->cmdline = p = malloc(arglen);
for (; *argp && strcmp(*argp, module_separator); argp++) {
- p = strpcpy(p, *argp);
+ p = stpcpy(p, *argp);
*p++ = ' ';
}
*--p = '\0';
diff --git a/com32/modules/Makefile b/com32/modules/Makefile
index 44bc1dfb..40df0c86 100644
--- a/com32/modules/Makefile
+++ b/com32/modules/Makefile
@@ -21,8 +21,8 @@ include ../MCONFIG
MODULES = chain.c32 config.c32 ethersel.c32 dmitest.c32 cpuidtest.c32 \
disk.c32 pcitest.c32 elf.c32 linux.c32 reboot.c32 pmload.c32 \
meminfo.c32 sdi.c32 sanboot.c32 ifcpu64.c32 vesainfo.c32 \
- kbdmap.c32 cmd.c32 vpdtest.c32 gpxecmd.c32 ifcpu.c32 \
- cpuid.c32
+ kbdmap.c32 cmd.c32 vpdtest.c32 host.c32 dir.c32 gpxecmd.c32 \
+ ifcpu.c32 cpuid.c32
TESTFILES =
diff --git a/com32/modules/dir.c b/com32/modules/dir.c
new file mode 100644
index 00000000..c9d93708
--- /dev/null
+++ b/com32/modules/dir.c
@@ -0,0 +1,177 @@
+/*
+ * Display directory contents
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <console.h>
+#include <string.h>
+#include <com32.h>
+#include <dirent.h>
+#include <minmax.h>
+#include <unistd.h>
+#include <getkey.h>
+
+static int rows, cols; /* Screen parameters */
+
+#define DIR_CHUNK 1024
+
+static const char *type_str(int type)
+{
+ switch (type) {
+ case DT_FIFO:
+ return "[fif]";
+ case DT_CHR:
+ return "[chr]";
+ case DT_DIR:
+ return "[dir]";
+ case DT_BLK:
+ return "[blk]";
+ case DT_UNKNOWN:
+ case DT_REG:
+ return "";
+ case DT_LNK:
+ return "[lnk]";
+ case DT_SOCK:
+ return "[sck]";
+ case DT_WHT:
+ return "[wht]";
+ default:
+ return "[???]";
+ }
+}
+
+static void free_dirents(struct dirent **dex, size_t n_de)
+{
+ size_t i;
+
+ for (i = 0; i < n_de; i++)
+ free(dex[i]);
+
+ free(dex);
+}
+
+static int compare_dirent(const void *p_de1, const void *p_de2)
+{
+ const struct dirent *de1 = *(const struct dirent **)p_de1;
+ const struct dirent *de2 = *(const struct dirent **)p_de2;
+ int ndir1, ndir2;
+
+ ndir1 = de1->d_type != DT_DIR;
+ ndir2 = de2->d_type != DT_DIR;
+
+ if (ndir1 != ndir2)
+ return ndir1 - ndir2;
+
+ return strcmp(de1->d_name, de2->d_name);
+}
+
+static int display_directory(const char *dirname)
+{
+ DIR *dir;
+ struct dirent *de;
+ struct dirent **dex = NULL;
+ size_t n_dex = 0, n_de = 0;
+ size_t i, j, k;
+ size_t nrows, ncols, perpage;
+ size_t endpage;
+ int maxlen = 0;
+ int pos, tpos, colwidth;
+
+ dir = opendir(dirname);
+ if (!dir) {
+ printf("Unable to read directory: %s\n", dirname);
+ return -1;
+ }
+
+ while ((de = readdir(dir)) != NULL) {
+ struct dirent *nde;
+
+ if (n_de >= n_dex) {
+ struct dirent **ndex;
+
+ ndex = realloc(dex, (n_dex + DIR_CHUNK) * sizeof *dex);
+ if (!ndex)
+ goto nomem;
+
+ dex = ndex;
+ n_dex += DIR_CHUNK;
+ }
+
+ nde = malloc(de->d_reclen);
+ if (!nde)
+ goto nomem;
+
+ memcpy(nde, de, de->d_reclen);
+ dex[n_de++] = nde;
+
+ maxlen = max(maxlen, de->d_reclen);
+ }
+
+ closedir(dir);
+
+ qsort(dex, n_de, sizeof *dex, compare_dirent);
+
+ maxlen -= offsetof(struct dirent, d_name) + 1;
+ ncols = (cols + 2)/(maxlen + 8);
+ ncols = min(ncols, n_de);
+ ncols = max(ncols, 1U);
+ colwidth = (cols + 2)/ncols;
+ perpage = ncols * (rows - 1);
+
+ for (i = 0; i < n_de; i += perpage) {
+ /* Rows on this page */
+ endpage = min(i+perpage, n_de);
+ nrows = ((endpage-i) + ncols - 1)/ncols;
+
+ for (j = 0; j < nrows; j++) {
+ pos = tpos = 0;
+ for (k = i+j; k < endpage; k += nrows) {
+ pos += printf("%*s%-5s %s",
+ (tpos - pos), "",
+ type_str(dex[k]->d_type),
+ dex[k]->d_name);
+ tpos += colwidth;
+ }
+ printf("\n");
+ }
+
+ if (endpage >= n_de)
+ break;
+
+ get_key(stdin, 0);
+ }
+
+ free_dirents(dex, n_de);
+ return 0;
+
+nomem:
+ closedir(dir);
+ printf("Out of memory error!\n");
+ free_dirents(dex, n_de);
+ return -1;
+}
+
+int main(int argc, char *argv[])
+{
+ int rv;
+
+ openconsole(&dev_rawcon_r, &dev_stdcon_w);
+
+ if (getscreensize(1, &rows, &cols)) {
+ /* Unknown screen size? */
+ rows = 24;
+ cols = 80;
+ }
+
+ if (argc < 2)
+ rv = display_directory(".");
+ else if (argc == 2)
+ rv = display_directory(argv[1]);
+ else {
+ printf("Usage: dir directory\n");
+ rv = 1;
+ }
+
+ return rv ? 1 : 0;
+}
+
diff --git a/com32/modules/host.c b/com32/modules/host.c
new file mode 100644
index 00000000..94ca876d
--- /dev/null
+++ b/com32/modules/host.c
@@ -0,0 +1,42 @@
+#include <stdio.h>
+#include <console.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <com32.h>
+
+static struct in_addr dnsresolve(const char *hostname)
+{
+ com32sys_t regs;
+ struct in_addr addr;
+
+ strcpy(__com32.cs_bounce, hostname);
+
+ regs.eax.w[0] = 0x0010;
+ regs.es = SEG(__com32.cs_bounce);
+ regs.ebx.w[0] = OFFS(__com32.cs_bounce);
+ __intcall(0x22, &regs, &regs);
+
+ addr.s_addr = regs.eax.l;
+ return addr;
+}
+
+int main(int argc, char *argv[])
+{
+ int i;
+ struct in_addr addr;
+
+ openconsole(&dev_null_r, &dev_stdcon_w);
+
+ for (i = 1; i < argc; i++) {
+ addr = dnsresolve(argv[i]);
+
+ printf("%-39s %08X %d.%d.%d.%d\n",
+ argv[i], ntohl(addr.s_addr),
+ ((uint8_t *)&addr.s_addr)[0],
+ ((uint8_t *)&addr.s_addr)[1],
+ ((uint8_t *)&addr.s_addr)[2],
+ ((uint8_t *)&addr.s_addr)[3]);
+ }
+
+ return 0;
+}
diff --git a/com32/rosh/rosh.c b/com32/rosh/rosh.c
index 720d8644..aa3e4532 100644
--- a/com32/rosh/rosh.c
+++ b/com32/rosh/rosh.c
@@ -389,12 +389,12 @@ void rosh_dir_arg(const char *ifilstr, const char *pwdstr)
ROSH_DEBUG("--'%s'\n", filestr);
}
fd = open(filestr, O_RDONLY);
- if (fd != -1) {
+ if (fd == -1) {
status = fstat(fd, &fdstat);
- if (S_ISDIR(fdstat.st_mode)) {
+ if (S_ISDIR(fdstat.st_mode)) {
ROSH_DEBUG("PATH '%s' is a directory\n", ifilstr);
d = fdopendir(fd);
- de = readdir(d);
+ de = readdir(d);
while (de != NULL) {
#ifdef DO_DEBUG
filestr2[0] = 0;
@@ -423,19 +423,18 @@ void rosh_dir_arg(const char *ifilstr, const char *pwdstr)
}
} else {
#ifdef __COM32__
- if (filestr[strlen(filestr) - 1] == SEP) {
+ if (filestr[strlen(filestr) - 1] == SEP) {
/* Directory */
filepos = 0;
- d = opendir(filestr);
+ d = opendir(filestr);
if (d != NULL) {
- printf("DIR:'%s' %8d %8d\n", d->dd_name, d->dd_fd,
- d->dd_sect);
+ //printf("DIR:'%s' %08x %8d\n", d->dd_name, (int)d->dd_sect, d->dd_offset);
de = readdir(d);
while (de != NULL) {
filepos++;
#ifdef DO_DEBUG
// if (strlen(de->d_name) > 25) de->d_name[25] = 0;
- switch (de->d_mode) {
+ switch (de->d_type) {
case 16:
ty = 'D';
break;
@@ -445,8 +444,6 @@ void rosh_dir_arg(const char *ifilstr, const char *pwdstr)
default:
ty = '*';
}
- printf("@%8d:%8d:%4d ", (int)de->d_ino, (int)de->d_size,
- de->d_mode);
#endif /* DO_DEBUG */
// printf("%s\n", de->d_name);
printf("'%s'\n", de->d_name);
@@ -454,11 +451,9 @@ void rosh_dir_arg(const char *ifilstr, const char *pwdstr)
// inchar = fgetc(stdin);
// fgets(instr, ROSH_CMD_SZ, stdin);
#endif /* DO_DEBUG */
- free(de);
de = readdir(d);
// if(filepos>15){ de = NULL; printf("Force Break\n");}
}
- printf("Dir.dd_fd: '%8d'\n", d->dd_fd);
closedir(d);
} else {
rosh_error(0, "dir:NULL", filestr);
diff --git a/com32/rosh/rosh.h b/com32/rosh/rosh.h
index 64b0564c..0c41bac9 100644
--- a/com32/rosh/rosh.h
+++ b/com32/rosh/rosh.h
@@ -47,6 +47,8 @@
#error SYSLINUX (I believe) requires __GNUC__
#endif /* __GNUC__ */
+#define DO_DEBUG 1
+
#ifdef DO_DEBUG
#define ROSH_DEBUG(f, ...) printf (f, ## __VA_ARGS__)
#ifdef DO_DEBUG2
diff --git a/com32/sysdump/cpuid.c b/com32/sysdump/cpuid.c
index 40b20618..ac80f229 100644
--- a/com32/sysdump/cpuid.c
+++ b/com32/sysdump/cpuid.c
@@ -39,11 +39,10 @@ static bool has_eflag(uint32_t flag)
return !!((f0^f1) & flag);
}
-static inline void get_cpuid(uint32_t eax, uint32_t ecx,
- struct cpuid_data *data)
+static void get_cpuid(uint32_t eax, uint32_t ecx, struct cpuid_data *data)
{
- asm("cpuid"
- : "=a" (data->eax), "=b" (data->ebx),
+ asm("pushl %%ebx ; cpuid ; movl %%ebx,%1 ; popl %%ebx"
+ : "=a" (data->eax), "=r" (data->ebx),
"=c" (data->ecx), "=d" (data->edx)
: "a" (eax), "c" (ecx));
}
diff --git a/com32/sysdump/ctime.c b/com32/sysdump/ctime.c
index 5af85666..56c8efb6 100644
--- a/com32/sysdump/ctime.c
+++ b/com32/sysdump/ctime.c
@@ -58,6 +58,13 @@ uint32_t posix_time(void)
mo += 12;
}
+ /*
+ * Just in case: if the month is nonsense, don't read off the end
+ * of the table...
+ */
+ if (mo-3 > 11)
+ return 0;
+
t = y*365 + y/4 - y/100 + y/400 + yday[mo-3] + d - 719469;
t *= 24;
t += h;
diff --git a/com32/tools/.gitignore b/com32/tools/.gitignore
new file mode 100644
index 00000000..b5397de4
--- /dev/null
+++ b/com32/tools/.gitignore
@@ -0,0 +1 @@
+/relocs
diff --git a/com32/tools/Makefile b/com32/tools/Makefile
new file mode 100644
index 00000000..e34296b4
--- /dev/null
+++ b/com32/tools/Makefile
@@ -0,0 +1,30 @@
+## -*- makefile -*- ------------------------------------------------------
+##
+## Copyright 2001-2008 H. Peter Anvin - All Rights Reserved
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+## Boston MA 02111-1307, USA; either version 2 of the License, or
+## (at your option) any later version; incorporated herein by reference.
+##
+## -----------------------------------------------------------------------
+
+topdir = ../..
+include $(topdir)/MCONFIG.build
+
+BINS = relocs
+
+all : $(BINS)
+
+relocs : relocs.o
+ $(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
+
+tidy dist clean spotless:
+ rm -f $(BINS)
+ rm -f *.o *.a .*.d
+ rm -f */*.o */*.a */.*.d
+
+install installer:
+
+-include .*.d */.*.d
diff --git a/com32/tools/relocs.c b/com32/tools/relocs.c
new file mode 100644
index 00000000..be57bf7e
--- /dev/null
+++ b/com32/tools/relocs.c
@@ -0,0 +1,693 @@
+/*
+ * This file is taken from the Linux kernel and is distributed under GPL v2.
+ */
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <elf.h>
+#include <byteswap.h>
+#define USE_BSD
+#include <endian.h>
+#include <regex.h>
+#include <sys/types.h>
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+static Elf32_Ehdr ehdr;
+static unsigned long reloc_count, reloc_idx;
+static unsigned long *relocs;
+
+struct section {
+ Elf32_Shdr shdr;
+ struct section *link;
+ Elf32_Sym *symtab;
+ Elf32_Rel *reltab;
+ char *strtab;
+};
+static struct section *secs;
+
+static void die(char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ exit(1);
+}
+
+/*
+ * Following symbols have been audited. Don't warn user about
+ * absolute relocations present w.r.t these symbols.
+ */
+
+/* True absolute relocations */
+
+static const char safe_abs_regex[] =
+"^(__.*_len|__.*_dwords)$";
+static regex_t safe_abs_regex_c;
+
+static int is_safe_abs_reloc(const char *sym_name)
+{
+ return !regexec(&safe_abs_regex_c, sym_name, 0, NULL, 0);
+}
+
+/* These are relative even though the linker marks them absolute */
+
+static const char safe_rel_regex[] =
+"^(__.*_start|__.*_end|_end|_[se](text|data))$";
+static regex_t safe_rel_regex_c;
+
+static int is_safe_rel_reloc(const char *sym_name)
+{
+ return !regexec(&safe_rel_regex_c, sym_name, 0, NULL, 0);
+}
+
+static void regex_init(void)
+{
+ char errbuf[128];
+ int err;
+
+ err = regcomp(&safe_abs_regex_c, safe_abs_regex,
+ REG_EXTENDED|REG_NOSUB);
+ if (err) {
+ regerror(err, &safe_abs_regex_c, errbuf, sizeof errbuf);
+ die("%s", errbuf);
+ }
+
+ err = regcomp(&safe_rel_regex_c, safe_rel_regex,
+ REG_EXTENDED|REG_NOSUB);
+ if (err) {
+ regerror(err, &safe_rel_regex_c, errbuf, sizeof errbuf);
+ die("%s", errbuf);
+ }
+}
+
+static const char *sym_type(unsigned type)
+{
+ static const char *type_name[] = {
+#define SYM_TYPE(X) [X] = #X
+ SYM_TYPE(STT_NOTYPE),
+ SYM_TYPE(STT_OBJECT),
+ SYM_TYPE(STT_FUNC),
+ SYM_TYPE(STT_SECTION),
+ SYM_TYPE(STT_FILE),
+ SYM_TYPE(STT_COMMON),
+ SYM_TYPE(STT_TLS),
+#undef SYM_TYPE
+ };
+ const char *name = "unknown sym type name";
+ if (type < ARRAY_SIZE(type_name)) {
+ name = type_name[type];
+ }
+ return name;
+}
+
+static const char *sym_bind(unsigned bind)
+{
+ static const char *bind_name[] = {
+#define SYM_BIND(X) [X] = #X
+ SYM_BIND(STB_LOCAL),
+ SYM_BIND(STB_GLOBAL),
+ SYM_BIND(STB_WEAK),
+#undef SYM_BIND
+ };
+ const char *name = "unknown sym bind name";
+ if (bind < ARRAY_SIZE(bind_name)) {
+ name = bind_name[bind];
+ }
+ return name;
+}
+
+static const char *sym_visibility(unsigned visibility)
+{
+ static const char *visibility_name[] = {
+#define SYM_VISIBILITY(X) [X] = #X
+ SYM_VISIBILITY(STV_DEFAULT),
+ SYM_VISIBILITY(STV_INTERNAL),
+ SYM_VISIBILITY(STV_HIDDEN),
+ SYM_VISIBILITY(STV_PROTECTED),
+#undef SYM_VISIBILITY
+ };
+ const char *name = "unknown sym visibility name";
+ if (visibility < ARRAY_SIZE(visibility_name)) {
+ name = visibility_name[visibility];
+ }
+ return name;
+}
+
+static const char *rel_type(unsigned type)
+{
+ static const char *type_name[] = {
+#define REL_TYPE(X) [X] = #X
+ REL_TYPE(R_386_NONE),
+ REL_TYPE(R_386_32),
+ REL_TYPE(R_386_PC32),
+ REL_TYPE(R_386_GOT32),
+ REL_TYPE(R_386_PLT32),
+ REL_TYPE(R_386_COPY),
+ REL_TYPE(R_386_GLOB_DAT),
+ REL_TYPE(R_386_JMP_SLOT),
+ REL_TYPE(R_386_RELATIVE),
+ REL_TYPE(R_386_GOTOFF),
+ REL_TYPE(R_386_GOTPC),
+#undef REL_TYPE
+ };
+ const char *name = NULL;
+ if (type < ARRAY_SIZE(type_name))
+ name = type_name[type];
+ if (!name)
+ name = "unknown";
+ return name;
+}
+
+static const char *sec_name(unsigned shndx)
+{
+ const char *sec_strtab;
+ const char *name;
+ sec_strtab = secs[ehdr.e_shstrndx].strtab;
+ name = "<noname>";
+ if (shndx < ehdr.e_shnum) {
+ name = sec_strtab + secs[shndx].shdr.sh_name;
+ }
+ else if (shndx == SHN_ABS) {
+ name = "ABSOLUTE";
+ }
+ else if (shndx == SHN_COMMON) {
+ name = "COMMON";
+ }
+ return name;
+}
+
+static const char *sym_name(const char *sym_strtab, Elf32_Sym *sym)
+{
+ const char *name;
+ name = "<noname>";
+ if (sym->st_name) {
+ name = sym_strtab + sym->st_name;
+ }
+ else {
+ name = sec_name(secs[sym->st_shndx].shdr.sh_name);
+ }
+ return name;
+}
+
+
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define le16_to_cpu(val) (val)
+#define le32_to_cpu(val) (val)
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+#define le16_to_cpu(val) bswap_16(val)
+#define le32_to_cpu(val) bswap_32(val)
+#endif
+
+static uint16_t elf16_to_cpu(uint16_t val)
+{
+ return le16_to_cpu(val);
+}
+
+static uint32_t elf32_to_cpu(uint32_t val)
+{
+ return le32_to_cpu(val);
+}
+
+static void read_ehdr(FILE *fp)
+{
+ if (fread(&ehdr, sizeof(ehdr), 1, fp) != 1) {
+ die("Cannot read ELF header: %s\n",
+ strerror(errno));
+ }
+ if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG) != 0) {
+ die("No ELF magic\n");
+ }
+ if (ehdr.e_ident[EI_CLASS] != ELFCLASS32) {
+ die("Not a 32 bit executable\n");
+ }
+ if (ehdr.e_ident[EI_DATA] != ELFDATA2LSB) {
+ die("Not a LSB ELF executable\n");
+ }
+ if (ehdr.e_ident[EI_VERSION] != EV_CURRENT) {
+ die("Unknown ELF version\n");
+ }
+ /* Convert the fields to native endian */
+ ehdr.e_type = elf16_to_cpu(ehdr.e_type);
+ ehdr.e_machine = elf16_to_cpu(ehdr.e_machine);
+ ehdr.e_version = elf32_to_cpu(ehdr.e_version);
+ ehdr.e_entry = elf32_to_cpu(ehdr.e_entry);
+ ehdr.e_phoff = elf32_to_cpu(ehdr.e_phoff);
+ ehdr.e_shoff = elf32_to_cpu(ehdr.e_shoff);
+ ehdr.e_flags = elf32_to_cpu(ehdr.e_flags);
+ ehdr.e_ehsize = elf16_to_cpu(ehdr.e_ehsize);
+ ehdr.e_phentsize = elf16_to_cpu(ehdr.e_phentsize);
+ ehdr.e_phnum = elf16_to_cpu(ehdr.e_phnum);
+ ehdr.e_shentsize = elf16_to_cpu(ehdr.e_shentsize);
+ ehdr.e_shnum = elf16_to_cpu(ehdr.e_shnum);
+ ehdr.e_shstrndx = elf16_to_cpu(ehdr.e_shstrndx);
+
+ if ((ehdr.e_type != ET_EXEC) && (ehdr.e_type != ET_DYN)) {
+ die("Unsupported ELF header type\n");
+ }
+ if (ehdr.e_machine != EM_386) {
+ die("Not for x86\n");
+ }
+ if (ehdr.e_version != EV_CURRENT) {
+ die("Unknown ELF version\n");
+ }
+ if (ehdr.e_ehsize != sizeof(Elf32_Ehdr)) {
+ die("Bad Elf header size\n");
+ }
+ if (ehdr.e_phentsize != sizeof(Elf32_Phdr)) {
+ die("Bad program header entry\n");
+ }
+ if (ehdr.e_shentsize != sizeof(Elf32_Shdr)) {
+ die("Bad section header entry\n");
+ }
+ if (ehdr.e_shstrndx >= ehdr.e_shnum) {
+ die("String table index out of bounds\n");
+ }
+}
+
+static void read_shdrs(FILE *fp)
+{
+ int i;
+ Elf32_Shdr shdr;
+
+ secs = calloc(ehdr.e_shnum, sizeof(struct section));
+ if (!secs) {
+ die("Unable to allocate %d section headers\n",
+ ehdr.e_shnum);
+ }
+ if (fseek(fp, ehdr.e_shoff, SEEK_SET) < 0) {
+ die("Seek to %d failed: %s\n",
+ ehdr.e_shoff, strerror(errno));
+ }
+ for (i = 0; i < ehdr.e_shnum; i++) {
+ struct section *sec = &secs[i];
+ if (fread(&shdr, sizeof shdr, 1, fp) != 1)
+ die("Cannot read ELF section headers %d/%d: %s\n",
+ i, ehdr.e_shnum, strerror(errno));
+ sec->shdr.sh_name = elf32_to_cpu(shdr.sh_name);
+ sec->shdr.sh_type = elf32_to_cpu(shdr.sh_type);
+ sec->shdr.sh_flags = elf32_to_cpu(shdr.sh_flags);
+ sec->shdr.sh_addr = elf32_to_cpu(shdr.sh_addr);
+ sec->shdr.sh_offset = elf32_to_cpu(shdr.sh_offset);
+ sec->shdr.sh_size = elf32_to_cpu(shdr.sh_size);
+ sec->shdr.sh_link = elf32_to_cpu(shdr.sh_link);
+ sec->shdr.sh_info = elf32_to_cpu(shdr.sh_info);
+ sec->shdr.sh_addralign = elf32_to_cpu(shdr.sh_addralign);
+ sec->shdr.sh_entsize = elf32_to_cpu(shdr.sh_entsize);
+ if (sec->shdr.sh_link < ehdr.e_shnum)
+ sec->link = &secs[sec->shdr.sh_link];
+ }
+
+}
+
+static void read_strtabs(FILE *fp)
+{
+ int i;
+ for (i = 0; i < ehdr.e_shnum; i++) {
+ struct section *sec = &secs[i];
+ if (sec->shdr.sh_type != SHT_STRTAB) {
+ continue;
+ }
+ sec->strtab = malloc(sec->shdr.sh_size);
+ if (!sec->strtab) {
+ die("malloc of %d bytes for strtab failed\n",
+ sec->shdr.sh_size);
+ }
+ if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0) {
+ die("Seek to %d failed: %s\n",
+ sec->shdr.sh_offset, strerror(errno));
+ }
+ if (fread(sec->strtab, 1, sec->shdr.sh_size, fp)
+ != sec->shdr.sh_size) {
+ die("Cannot read symbol table: %s\n",
+ strerror(errno));
+ }
+ }
+}
+
+static void read_symtabs(FILE *fp)
+{
+ int i,j;
+ for (i = 0; i < ehdr.e_shnum; i++) {
+ struct section *sec = &secs[i];
+ if (sec->shdr.sh_type != SHT_SYMTAB) {
+ continue;
+ }
+ sec->symtab = malloc(sec->shdr.sh_size);
+ if (!sec->symtab) {
+ die("malloc of %d bytes for symtab failed\n",
+ sec->shdr.sh_size);
+ }
+ if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0) {
+ die("Seek to %d failed: %s\n",
+ sec->shdr.sh_offset, strerror(errno));
+ }
+ if (fread(sec->symtab, 1, sec->shdr.sh_size, fp)
+ != sec->shdr.sh_size) {
+ die("Cannot read symbol table: %s\n",
+ strerror(errno));
+ }
+ for (j = 0; j < sec->shdr.sh_size/sizeof(Elf32_Sym); j++) {
+ Elf32_Sym *sym = &sec->symtab[j];
+ sym->st_name = elf32_to_cpu(sym->st_name);
+ sym->st_value = elf32_to_cpu(sym->st_value);
+ sym->st_size = elf32_to_cpu(sym->st_size);
+ sym->st_shndx = elf16_to_cpu(sym->st_shndx);
+ }
+ }
+}
+
+
+static void read_relocs(FILE *fp)
+{
+ int i,j;
+ for (i = 0; i < ehdr.e_shnum; i++) {
+ struct section *sec = &secs[i];
+ if (sec->shdr.sh_type != SHT_REL) {
+ continue;
+ }
+ sec->reltab = malloc(sec->shdr.sh_size);
+ if (!sec->reltab) {
+ die("malloc of %d bytes for relocs failed\n",
+ sec->shdr.sh_size);
+ }
+ if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0) {
+ die("Seek to %d failed: %s\n",
+ sec->shdr.sh_offset, strerror(errno));
+ }
+ if (fread(sec->reltab, 1, sec->shdr.sh_size, fp)
+ != sec->shdr.sh_size) {
+ die("Cannot read symbol table: %s\n",
+ strerror(errno));
+ }
+ for (j = 0; j < sec->shdr.sh_size/sizeof(Elf32_Rel); j++) {
+ Elf32_Rel *rel = &sec->reltab[j];
+ rel->r_offset = elf32_to_cpu(rel->r_offset);
+ rel->r_info = elf32_to_cpu(rel->r_info);
+ }
+ }
+}
+
+
+static void print_absolute_symbols(void)
+{
+ int i;
+ printf("Absolute symbols\n");
+ printf(" Num: Value Size Type Bind Visibility Name\n");
+ for (i = 0; i < ehdr.e_shnum; i++) {
+ struct section *sec = &secs[i];
+ char *sym_strtab;
+ Elf32_Sym *sh_symtab;
+ int j;
+
+ if (sec->shdr.sh_type != SHT_SYMTAB) {
+ continue;
+ }
+ sh_symtab = sec->symtab;
+ sym_strtab = sec->link->strtab;
+ for (j = 0; j < sec->shdr.sh_size/sizeof(Elf32_Sym); j++) {
+ Elf32_Sym *sym;
+ const char *name;
+ sym = &sec->symtab[j];
+ name = sym_name(sym_strtab, sym);
+ if (sym->st_shndx != SHN_ABS) {
+ continue;
+ }
+ printf("%5d %08x %5d %10s %10s %12s %s\n",
+ j, sym->st_value, sym->st_size,
+ sym_type(ELF32_ST_TYPE(sym->st_info)),
+ sym_bind(ELF32_ST_BIND(sym->st_info)),
+ sym_visibility(ELF32_ST_VISIBILITY(sym->st_other)),
+ name);
+ }
+ }
+ printf("\n");
+}
+
+static int print_absolute_relocs(FILE *f)
+{
+ int i, printed = 0;
+
+ for (i = 0; i < ehdr.e_shnum; i++) {
+ struct section *sec = &secs[i];
+ struct section *sec_applies, *sec_symtab;
+ char *sym_strtab;
+ Elf32_Sym *sh_symtab;
+ int j;
+ if (sec->shdr.sh_type != SHT_REL) {
+ continue;
+ }
+ sec_symtab = sec->link;
+ sec_applies = &secs[sec->shdr.sh_info];
+ if (!(sec_applies->shdr.sh_flags & SHF_ALLOC)) {
+ continue;
+ }
+ sh_symtab = sec_symtab->symtab;
+ sym_strtab = sec_symtab->link->strtab;
+ for (j = 0; j < sec->shdr.sh_size/sizeof(Elf32_Rel); j++) {
+ Elf32_Rel *rel;
+ Elf32_Sym *sym;
+ const char *name;
+ rel = &sec->reltab[j];
+ sym = &sh_symtab[ELF32_R_SYM(rel->r_info)];
+ name = sym_name(sym_strtab, sym);
+ if (sym->st_shndx != SHN_ABS) {
+ continue;
+ }
+
+ /* Absolute symbols are not relocated if bzImage is
+ * loaded at a non-compiled address. Display a warning
+ * to user at compile time about the absolute
+ * relocations present.
+ *
+ * User need to audit the code to make sure
+ * some symbols which should have been section
+ * relative have not become absolute because of some
+ * linker optimization or wrong programming usage.
+ *
+ * Before warning check if this absolute symbol
+ * relocation is harmless.
+ */
+ if (is_safe_abs_reloc(name) ||
+ is_safe_rel_reloc(name))
+ continue;
+
+ if (!printed) {
+ fprintf(f, "Unknown absolute relocations present\n");
+ fprintf(f, "Offset Info Type Sym.Value Sym.Name\n");
+ printed = 1;
+ }
+
+ fprintf(f, "%08x %08x %10s %08x %s\n",
+ rel->r_offset,
+ rel->r_info,
+ rel_type(ELF32_R_TYPE(rel->r_info)),
+ sym->st_value,
+ name);
+ }
+ }
+
+ if (printed)
+ fputc('\n', f);
+
+ return printed;
+}
+
+static void walk_relocs(void (*visit)(Elf32_Rel *rel, Elf32_Sym *sym))
+{
+ int i;
+ /* Walk through the relocations */
+ for (i = 0; i < ehdr.e_shnum; i++) {
+ char *sym_strtab;
+ Elf32_Sym *sh_symtab;
+ struct section *sec_applies, *sec_symtab;
+ int j;
+ struct section *sec = &secs[i];
+
+ if (sec->shdr.sh_type != SHT_REL) {
+ continue;
+ }
+ sec_symtab = sec->link;
+ sec_applies = &secs[sec->shdr.sh_info];
+ if (!(sec_applies->shdr.sh_flags & SHF_ALLOC)) {
+ continue;
+ }
+ sh_symtab = sec_symtab->symtab;
+ sym_strtab = sec_symtab->link->strtab;
+ for (j = 0; j < sec->shdr.sh_size/sizeof(Elf32_Rel); j++) {
+ Elf32_Rel *rel;
+ Elf32_Sym *sym;
+ unsigned r_type;
+ rel = &sec->reltab[j];
+ sym = &sh_symtab[ELF32_R_SYM(rel->r_info)];
+ r_type = ELF32_R_TYPE(rel->r_info);
+ /* Don't visit relocations to absolute symbols */
+ if (sym->st_shndx == SHN_ABS &&
+ !is_safe_rel_reloc(sym_name(sym_strtab, sym)))
+ continue;
+
+ switch (r_type) {
+ case R_386_NONE:
+ case R_386_PC32:
+ case R_386_GOTPC:
+ case R_386_GOTOFF:
+ case R_386_GOT32:
+ case R_386_PLT32:
+ /* Relative relocations don't need to
+ be adjusted */
+ break;
+ case R_386_32:
+ /* Visit relocations that need adjustment */
+ visit(rel, sym);
+ break;
+ default:
+ die("Unsupported relocation type: %s (%d)\n",
+ rel_type(r_type), r_type);
+ }
+ }
+ }
+}
+
+static void count_reloc(Elf32_Rel *rel, Elf32_Sym *sym)
+{
+ (void)rel; (void)sym;
+ reloc_count += 1;
+}
+
+static void collect_reloc(Elf32_Rel *rel, Elf32_Sym *sym)
+{
+ (void)sym;
+
+ /* Remember the address that needs to be adjusted. */
+ relocs[reloc_idx++] = rel->r_offset;
+}
+
+static int cmp_relocs(const void *va, const void *vb)
+{
+ const unsigned long *a, *b;
+ a = va; b = vb;
+ return (*a == *b)? 0 : (*a > *b)? 1 : -1;
+}
+
+static void emit_relocs(int as_text)
+{
+ int i;
+ /* Count how many relocations I have and allocate space for them. */
+ reloc_count = 0;
+ walk_relocs(count_reloc);
+ relocs = malloc(reloc_count * sizeof(relocs[0]));
+ if (!relocs) {
+ die("malloc of %d entries for relocs failed\n",
+ reloc_count);
+ }
+ /* Collect up the relocations */
+ reloc_idx = 0;
+ walk_relocs(collect_reloc);
+
+ /* Order the relocations for more efficient processing */
+ qsort(relocs, reloc_count, sizeof(relocs[0]), cmp_relocs);
+
+ /* Print the relocations */
+ if (as_text) {
+ /* Print the relocations in a form suitable that
+ * gas will like.
+ */
+ printf(".section \".data.reloc\",\"a\"\n");
+ printf(".balign 4\n");
+ for (i = 0; i < reloc_count; i++) {
+ printf("\t .long 0x%08lx\n", relocs[i]);
+ }
+ printf("\n");
+ }
+ else {
+ unsigned char buf[4];
+ /* Now print each relocation */
+ for (i = 0; i < reloc_count; i++) {
+ buf[0] = (relocs[i] >> 0) & 0xff;
+ buf[1] = (relocs[i] >> 8) & 0xff;
+ buf[2] = (relocs[i] >> 16) & 0xff;
+ buf[3] = (relocs[i] >> 24) & 0xff;
+ fwrite(buf, 4, 1, stdout);
+ }
+ /* Print a stop */
+ memset(buf, 0, sizeof buf);
+ fwrite(buf, 4, 1, stdout);
+ }
+}
+
+static void usage(void)
+{
+ die("relocs [--abs-syms |--abs-relocs | --text] vmlinux\n");
+}
+
+int main(int argc, char **argv)
+{
+ int show_absolute_syms, show_absolute_relocs;
+ int as_text;
+ const char *fname;
+ FILE *fp;
+ int i;
+ int err = 0;
+
+ show_absolute_syms = 0;
+ show_absolute_relocs = 0;
+ as_text = 0;
+ fname = NULL;
+ for (i = 1; i < argc; i++) {
+ char *arg = argv[i];
+ if (*arg == '-') {
+ if (strcmp(argv[1], "--abs-syms") == 0) {
+ show_absolute_syms = 1;
+ continue;
+ }
+
+ if (strcmp(argv[1], "--abs-relocs") == 0) {
+ show_absolute_relocs = 1;
+ continue;
+ }
+ else if (strcmp(argv[1], "--text") == 0) {
+ as_text = 1;
+ continue;
+ }
+ }
+ else if (!fname) {
+ fname = arg;
+ continue;
+ }
+ usage();
+ }
+ if (!fname) {
+ usage();
+ }
+
+
+ regex_init();
+
+ fp = fopen(fname, "r");
+ if (!fp) {
+ die("Cannot open %s: %s\n",
+ fname, strerror(errno));
+ }
+ read_ehdr(fp);
+ read_shdrs(fp);
+ read_strtabs(fp);
+ read_symtabs(fp);
+ read_relocs(fp);
+ if (show_absolute_syms) {
+ print_absolute_symbols();
+ return 0;
+ }
+ if (show_absolute_relocs) {
+ print_absolute_relocs(stdout);
+ return 0;
+ }
+ err = print_absolute_relocs(stderr);
+ emit_relocs(as_text);
+ return err;
+}
diff --git a/core/Makefile b/core/Makefile
index 65418c48..166f0e4e 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -1,7 +1,7 @@
## -----------------------------------------------------------------------
##
## Copyright 1998-2009 H. Peter Anvin - All Rights Reserved
-## Copyright 2009 Intel Corporation; author: H. Peter Anvin
+## Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
@@ -23,8 +23,8 @@ topdir = ..
include $(topdir)/MCONFIG.embedded
-include $(topdir)/version.mk
-OPTFLAGS =
-INCLUDES =
+OPTFLAGS =
+INCLUDES = -I./include -I$(com32)/include
# This is very similar to cp437; technically it's for Norway and Denmark,
# but it's unlikely the characters that are different will be used in
@@ -33,17 +33,32 @@ CODEPAGE = cp865
# The targets to build in this directory...
BTARGET = kwdhash.gen \
+ extlinux.bin extlinux.bss extlinux.sys \
ldlinux.bss ldlinux.sys ldlinux.bin \
- pxelinux.0 isolinux.bin isolinux-debug.bin \
- extlinux.bin extlinux.bss extlinux.sys
+ isolinux.bin isolinux-debug.bin pxelinux.0
# All primary source files for the main syslinux files
-NASMSRC = $(wildcard *.asm)
-NASMHDR = $(wildcard *.inc)
-CSRC = $(wildcard *.c)
-CHDR = $(wildcard *.h)
-OTHERSRC = keywords
-ALLSRC = $(NASMSRC) $(NASMHDR) $(CSRC) $(CHDR) $(OTHERSRC)
+NASMSRC := $(wildcard *.asm)
+NASMHDR := $(wildcard *.inc)
+CSRC := $(wildcard *.c */*.c */*/*.c)
+SSRC := $(wildcard *.S */*.S */*/*.S)
+CHDR := $(wildcard *.h)
+OTHERSRC := keywords
+ALLSRC = $(NASMSRC) $(NASMHDR) $(CSRC) $(SSRC) $(CHDR) $(OTHERSRC)
+
+COBJ := $(patsubst %.c,%.o,$(CSRC))
+SOBJ := $(patsubst %.S,%.o,$(SSRC))
+
+LIB = libcore.a
+LIBS = $(LIB) $(com32)/lib/libcomcore.a $(LIBGCC)
+LIBOBJS = $(COBJ) $(SOBJ)
+
+NASMDEBUG = -g -F dwarf
+NASMOPT += $(NASMDEBUG)
+
+PREPCORE = ../lzo/prepcore
+
+# CFLAGS += -DDEBUG=1
# The DATE is set on the make command line when building binaries for
# official release. Otherwise, substitute a hex string that is pretty much
@@ -62,26 +77,29 @@ kwdhash.gen: keywords genhash.pl
.PRECIOUS: %.elf
-# Standard rule for {isolinux,isolinux-debug}.bin
-iso%.bin: iso%.elf checksumiso.pl
- $(OBJCOPY) -O binary $< $@
- $(PERL) checksumiso.pl $@
+%.raw: %.elf
+ $(OBJCOPY) -O binary $< $(@:.bin=.raw)
-# Standard rule for {ldlinux,pxelinux,extlinux}.bin
-%.bin: %.elf
- $(OBJCOPY) -O binary $< $@
+%.bin: %.raw $(PREPCORE)
+ $(PREPCORE) $< $@
%.o: %.asm kwdhash.gen ../version.gen
- ( $(NASM) -M -DDEPEND $(NINCLUDE) -o $@ $< ; echo '' ) > .$@.d; true
- $(NASM) $(NASMOPT) -f elf -g -F stabs -DDATE_STR="'$(DATE)'" \
+ $(NASM) -f elf $(NASMOPT) -DDATE_STR="'$(DATE)'" \
-DHEXDATE="$(HEXDATE)" \
- -l $(@:.o=.lsr) -o $@ $<
+ -l $(@:.o=.lsr) -o $@ -MP -MD .$@.d $<
-%.elf: %.o syslinux.ld
- $(LD) $(LDFLAGS) -T syslinux.ld -M -o $@ $< > $(@:.elf=.map)
+%.elf: %.o $(LIBS) syslinux.ld
+ $(LD) $(LDFLAGS) -T syslinux.ld -M -o $@ $< \
+ --start-group $(LIBS) --end-group \
+ > $(@:.elf=.map)
$(OBJDUMP) -h $@ > $(@:.elf=.sec)
$(PERL) lstadjust.pl $(@:.elf=.lsr) $(@:.elf=.sec) $(@:.elf=.lst)
+$(LIB): $(LIBOBJS)
+ rm -f $@
+ $(AR) cq $@ $^
+ $(RANLIB) $@
+
pxelinux.0: pxelinux.bin
cp -f $< $@
@@ -97,12 +115,11 @@ extlinux.bss: extlinux.bin
extlinux.sys: extlinux.bin
dd if=$< of=$@ bs=512 skip=1
-# NASM prior to 2.03 wouldn't auto-generate this dependency...
-ldlinux.o: codepage.cp
-
codepage.cp: ../codepage/$(CODEPAGE).cp
cp -f $< $@
+codepage.o: codepage.S codepage.cp
+
install: installer
install-lib: installer
@@ -112,9 +129,11 @@ install-all: install install-lib
netinstall: installer
tidy dist:
- rm -f codepage.cp *.o *.elf stupid.* patch.offset .depend .*.d
- rm -f *.lsr *.lst *.map *.sec
- rm -f $(OBSOLETE)
+ rm -f codepage.cp *.o *.elf *.a stupid.* patch.offset .depend .*.d
+ rm -f *.elf.tmp *.sym
+ rm -f *.lsr *.lst *.map *.sec *.raw
+ rm -f */*.o */*/*.o */*.lst */*/*.lst */.*.d */*/.*.d
+ rm -f $(OBSOLETE) $(LIB)
clean: tidy
@@ -122,4 +141,4 @@ spotless: clean
rm -f $(BTARGET) *.bin *_bin.c
# Include dependencies file
--include .*.d
+-include .*.d */.*.d */*/.*.d
diff --git a/core/abort.inc b/core/abort.inc
index 5b16b9d9..9b181363 100644
--- a/core/abort.inc
+++ b/core/abort.inc
@@ -17,7 +17,7 @@
; Code to terminate a kernel load
;
- section .text
+ section .text16
;
; dot_pause: same as abort_check, except prints a dot, too
@@ -78,7 +78,7 @@ error_or_command:
jnz on_error
jmp enter_command
- section .data
+ section .data16
aborted_msg db ' aborted.', CR, LF, 0
- section .text
+ section .text16
diff --git a/core/adv.inc b/core/adv.inc
index d856a1a2..2dc16339 100644
--- a/core/adv.inc
+++ b/core/adv.inc
@@ -57,7 +57,7 @@ adv1:
.data resb ADV_LEN
.tail resd 1
.end equ $
- section .text
+ section .text16
;
; This is called after config file parsing, so we know
@@ -67,24 +67,26 @@ adv_init:
cmp byte [ADVDrive],-1
jne adv_read
-;%if IS_SYSLINUX || IS_MDSLINUX || IS_EXTLINUX
-%if IS_EXTLINUX ; Not yet implemented for the other derivatives
+%if IS_SYSLINUX || IS_EXTLINUX
+ cmp word [ADVSectors],2 ; Not present?
+ jb adv_verify
+
;
; Update pointers to default ADVs...
;
- mov bx,[LDLSectors]
+ mov bx,[DataSectors]
shl bx,2
mov ecx,[bsHidden]
- mov eax,[bx+SectorPtrs-8]
- mov edx,[bx+SectorPtrs-4]
+ mov eax,[bx+SectorPtrs] ; First ADV sector
+ mov edx,[bx+SectorPtrs+4] ; Second ADV sector
add eax,ecx
add edx,ecx
mov [ADVSec0],eax
mov [ADVSec1],edx
mov al,[DriveNumber]
mov [ADVDrive],al
+ jmp adv_read
%endif
- ; ** fall through to adv_verify **
;
; Initialize the ADV data structure in memory
@@ -492,12 +494,14 @@ adv_read_write:
stc
jmp .cb_done
- section .data
+ section .data16
alignz 4
ADVSec0 dd 0 ; Not specified
ADVSec1 dd 0 ; Not specified
ADVDrive db -1 ; No ADV defined
ADVCHSInfo db -1 ; We have CHS info for this drive
- section .bss
+ section .bss16
ADVOp resb 1
+
+ section .text16
diff --git a/core/bcopy32.inc b/core/bcopy32.inc
index c363373a..6537546b 100644
--- a/core/bcopy32.inc
+++ b/core/bcopy32.inc
@@ -29,7 +29,7 @@
;
bits 16
- section .text
+ section .text16
;
; bcopy:
@@ -39,17 +39,13 @@
; ESI - source pointer (-1 means do bzero rather than bcopy)
; EDI - target pointer
; ECX - byte count
-; DF - zero
;
; Outputs:
; ESI - first byte after source (garbage if ESI == -1 on entry)
; EDI - first byte after target
;
bcopy: jecxz .ret
- pushad
- push word pm_bcopy
- call simple_pm_call
- popad
+ pm_call pm_bcopy
add edi,ecx
add esi,ecx
.ret: ret
@@ -70,293 +66,8 @@ bcopy: jecxz .ret
; the entry point and src the mode (0 = pm, 1 = rm)
;
shuffle_and_boot_raw:
- push word pm_shuffle
- call simple_pm_call
- ; Never returns...
- jmp kaboom
-
-;
-; This routine is used to invoke a simple routine in 32-bit protected
-; mode (with 32-bit zero-based CS, DS, ES, and SS, with ESP pointing to the
-; real-mode stack even if the real-mode stack was in a nonzero SS.)
-;
-; No interrupt thunking services are provided; interrupts are disabled
-; for the duration of the routine. Don't run for too long at a time
-; unless you really mean it.
-;
-; Inputs:
-; On stack - pm entrypoint (IP only)
-; EAX, EBP preserved until real-mode exit
-; EBX, ECX, EDX, ESI and EDI passed to the called routine
-;
-; Outputs:
-; EAX, EBP restored from real-mode entry
-; All other registers as returned from called function
-; PM entrypoint cleaned off stack
-;
-simple_pm_call:
- push eax
- push ebp
- movzx ebp,sp ; BP is used as frame pointer
- pushfd ; Saves, among others, the IF flag
- push ds
- push es
- push fs
- push gs
-
- cli
- call enable_a20
-
- mov byte [cs:bcopy_gdt.TSS+5],89h ; Mark TSS unbusy
-
- ; Convert the stack segment to a base
- xor eax,eax
- mov ax,ss
- shl eax,4
- add ebp,eax ; EBP is now an absolute frame ptr
-
- ; Save the old segmented stack pointer
- mov [cs:.rm_esp],esp
- mov [cs:.rm_ss],ss
-
- o32 lgdt [cs:bcopy_gdt]
- mov eax,cr0
- or al,1
- mov cr0,eax ; Enter protected mode
- jmp PM_CS32:.in_pm
-
- bits 32
-.in_pm:
- mov ax,PM_DS32
- mov ss,eax
- lea esp,[ebp-8*4-2*4] ; Flat mode stack
- mov es,eax
- mov ds,eax
-
- ; Set fs, gs, tr, and ldtr in case we're on a virtual
- ; machine running on Intel VT hardware -- it can't
- ; deal with a partial transition, for no good reason.
-
- mov al,PM_DS16 ; Real-mode-like segment
- mov fs,eax
- mov gs,eax
- mov al,PM_TSS ; Intel VT really doesn't want
- ltr ax ; an invalid TR and LDTR, so give
- xor eax,eax ; it something that it can use...
- lldt ax ; (sigh)
-
- movzx eax,word [ebp+2*4+2]
- call eax ; Call actual routine
-
- jmp PM_CS16:.exit
- bits 16
-.exit:
- mov ax,PM_DS16 ; "Real-mode-like" data segment
- mov es,eax
- mov ds,eax
- mov ss,eax
-
- mov eax,cr0
- and al,~1
- mov cr0,eax ; Disable protected mode
- jmp 0:.in_rm
-
-.in_rm: ; Back in real mode
- lss esp,[cs:.rm_esp] ; Restore the stack
- pop gs
- pop fs
- pop es
- pop ds
-
- popfd ; Re-enables interrupts
- pop ebp
- pop eax
- ret 2 ; Drops the pm entry
-
- section .bss
- alignb 4
-.rm_esp resd 1
-.rm_ss resw 1
-
-
- section .text
-;
-; Routines to enable and disable (yuck) A20. These routines are gathered
-; from tips from a couple of sources, including the Linux kernel and
-; http://www.x86.org/. The need for the delay to be as large as given here
-; is indicated by Donnie Barnes of RedHat, the problematic system being an
-; IBM ThinkPad 760EL.
-;
-
- section .data
- alignz 2
-A20Ptr dw a20_dunno
-
- section .bss
- alignb 4
-A20Test resd 1 ; Counter for testing A20 status
-A20Tries resb 1 ; Times until giving up on A20
-
- section .text
-enable_a20:
- pushad
- mov byte [cs:A20Tries],255 ; Times to try to make this work
-
-try_enable_a20:
-
-;
-; First, see if we are on a system with no A20 gate, or the A20 gate
-; is already enabled for us...
-;
-a20_none:
- call a20_test
- jnz a20_done
- ; Otherwise, see if we had something memorized...
- jmp word [cs:A20Ptr]
-
-;
-; Next, try the BIOS (INT 15h AX=2401h)
-;
-a20_dunno:
-a20_bios:
- mov word [cs:A20Ptr], a20_bios
- mov ax,2401h
- pushf ; Some BIOSes muck with IF
- int 15h
- popf
-
- call a20_test
- jnz a20_done
-
-;
-; Enable the keyboard controller A20 gate
-;
-a20_kbc:
- mov dl, 1 ; Allow early exit
- call empty_8042
- jnz a20_done ; A20 live, no need to use KBC
-
- mov word [cs:A20Ptr], a20_kbc ; Starting KBC command sequence
-
- mov al,0D1h ; Write output port
- out 064h, al
- call empty_8042_uncond
-
- mov al,0DFh ; A20 on
- out 060h, al
- call empty_8042_uncond
-
- ; Apparently the UHCI spec assumes that A20 toggle
- ; ends with a null command (assumed to be for sychronization?)
- ; Put it here to see if it helps anything...
- mov al,0FFh ; Null command
- out 064h, al
- call empty_8042_uncond
-
- ; Verify that A20 actually is enabled. Do that by
- ; observing a word in low memory and the same word in
- ; the HMA until they are no longer coherent. Note that
- ; we don't do the same check in the disable case, because
- ; we don't want to *require* A20 masking (SYSLINUX should
- ; work fine without it, if the BIOS does.)
-.kbc_wait: push cx
- xor cx,cx
-.kbc_wait_loop:
- call a20_test
- jnz a20_done_pop
- loop .kbc_wait_loop
-
- pop cx
-;
-; Running out of options here. Final attempt: enable the "fast A20 gate"
-;
-a20_fast:
- mov word [cs:A20Ptr], a20_fast
- in al, 092h
- or al,02h
- and al,~01h ; Don't accidentally reset the machine!
- out 092h, al
-
-.fast_wait: push cx
- xor cx,cx
-.fast_wait_loop:
- call a20_test
- jnz a20_done_pop
- loop .fast_wait_loop
-
- pop cx
-
-;
-; Oh bugger. A20 is not responding. Try frobbing it again; eventually give up
-; and report failure to the user.
-;
- dec byte [cs:A20Tries]
- jnz a20_dunno ; Did we get the wrong type?
-
- mov si, err_a20
- jmp abort_load
-
- section .data
-err_a20 db CR, LF, 'A20 gate not responding!', CR, LF, 0
- section .text
-
-;
-; A20 unmasked, proceed...
-;
-a20_done_pop: pop cx
-a20_done: popad
- ret
-
-;
-; This routine tests if A20 is enabled (ZF = 0). This routine
-; must not destroy any register contents.
-;
-; The no-write early out avoids the io_delay in the (presumably common)
-; case of A20 already enabled (e.g. from a previous call.)
-;
-a20_test:
- push es
- push cx
- push eax
- mov cx,0FFFFh ; HMA = segment 0FFFFh
- mov es,cx
- mov eax,[cs:A20Test]
- mov cx,32 ; Loop count
- jmp .test ; First iteration = early out
-.wait: add eax,0x430aea41 ; A large prime number
- mov [cs:A20Test],eax
- io_delay ; Serialize, and fix delay
-.test: cmp eax,[es:A20Test+10h]
- loopz .wait
-.done: pop eax
- pop cx
- pop es
- ret
-
-;
-; Routine to empty the 8042 KBC controller. If dl != 0
-; then we will test A20 in the loop and exit if A20 is
-; suddenly enabled.
-;
-empty_8042_uncond:
- xor dl,dl
-empty_8042:
- call a20_test
- jz .a20_on
- and dl,dl
- jnz .done
-.a20_on: io_delay
- in al, 064h ; Status port
- test al,1
- jz .no_output
- io_delay
- in al, 060h ; Read input
- jmp short empty_8042
-.no_output:
- test al,2
- jnz empty_8042
- io_delay
-.done: ret
+ mov bx,pm_shuffle
+ jmp enter_pm
;
; The 32-bit copy and shuffle code is "special", so it is in its own file
diff --git a/core/bcopyxx.inc b/core/bcopyxx.inc
index 89ae4f40..c669b7a8 100644
--- a/core/bcopyxx.inc
+++ b/core/bcopyxx.inc
@@ -1,7 +1,7 @@
;; -----------------------------------------------------------------------
;;
;; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved
-;; Copyright 2009 Intel Corporation; author: H. Peter Anvin
+;; Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
;;
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
@@ -29,9 +29,8 @@
;
bits 32
- section .bcopyxx
+ section .bcopyxx.text
align 16
-bcopyxx_start equ $
;
; pm_bcopy:
;
@@ -95,10 +94,13 @@ pm_bcopy:
ret
.reverse:
- std ; Reverse copy
+ lea eax,[esi+ecx-1] ; Point to final byte
+ cmp edi,eax
+ ja .forward ; No overlap, do forward copy
- lea esi,[esi+ecx-1] ; Point to final byte
+ std ; Reverse copy
lea edi,[edi+ecx-1]
+ mov esi,eax
; Initial alignment
mov edx,edi
@@ -204,21 +206,23 @@ pm_bcopy:
; the entry point and src the mode (0 = pm, 1 = rm)
;
pm_shuffle:
+ cli ; End interrupt service (for good)
mov ebx,edi ; EBX <- descriptor list
lea edx,[edi+ecx+15] ; EDX <- where to relocate our code to
and edx,~15 ; Align 16 to benefit the GDT
call pm_bcopy
- mov edi,edx
- mov esi,bcopyxx_start
- mov ecx,bcopyxx_dwords
- lea eax,[edx+.safe-bcopyxx_start] ; Resume point
+ mov esi,__bcopyxx_start ; Absolute source address
+ mov edi,edx ; Absolute target address
+ sub edx,esi ; EDX <- address delta
+ mov ecx,__bcopyxx_dwords
+ lea eax,[edx+.safe] ; Resume point
; Relocate this code
rep movsd
jmp eax ; Jump to safe location
.safe:
; Give ourselves a safe stack
- lea esp,[edx+bcopyxx_stack+bcopyxx_end-bcopyxx_start]
- add edx,bcopy_gdt-bcopyxx_start
+ lea esp,[edx+bcopyxx_stack+__bcopyxx_end]
+ add edx,bcopy_gdt ; EDX <- new GDT
mov [edx+2],edx ; GDT self-pointer
lgdt [edx] ; Switch to local GDT
@@ -232,15 +236,16 @@ pm_shuffle:
call pm_bcopy
jmp .loop
.done:
+ lidt [edx+RM_IDT_ptr-bcopy_gdt] ; RM-like IDT
push ecx ; == 0, for cleaning the flags register
and esi,esi
- jz pm_shuffle_real_mode
+ jz pm_shuffle_16
popfd ; Clean the flags
jmp edi ; Protected mode entry
- ; We have a real-mode entry point, so we need to return
- ; to real mode. Note: EDX already points to the GDT.
-pm_shuffle_real_mode:
+ ; We have a 16-bit entry point, so we need to return
+ ; to 16-bit mode. Note: EDX already points to the GDT.
+pm_shuffle_16:
mov eax,edi
mov [edx+PM_CS16+2],ax
mov [edx+PM_DS16+2],ax
@@ -261,7 +266,9 @@ pm_shuffle_real_mode:
mov ss,edx
jmp PM_CS16:0
- align 16
+ section .bcopyxx.data
+
+ alignz 16
; GDT descriptor entry
%macro desc 1
bcopy_gdt.%1:
@@ -294,16 +301,6 @@ bcopy_gdt:
dd 00cf9300h ; present, dpl 0, cover all 4G
bcopy_gdt_size: equ $-bcopy_gdt
-
- alignz 4
-bcopyxx_end equ $ ; *Must* be dword-aligned!
-bcopyxx_len equ $-bcopyxx_start
-bcopyxx_dwords equ bcopyxx_len >> 2
-
-bcopyxx_stack equ 128 ; We want this much stack
- ; The +15 is for alignment
-bcopyxx_safe equ bcopyxx_len + bcopyxx_stack + 15
-
;
; Space for a dummy task state segment. It should never be actually
; accessed, but just in case it is, point to a chunk of memory that
@@ -311,5 +308,11 @@ bcopyxx_safe equ bcopyxx_len + bcopyxx_stack + 15
;
DummyTSS equ 0x580
+ align 4
+RM_IDT_ptr: dw 0FFFFh ; Length (nonsense, but matches CPU)
+ dd 0 ; Offset
+
+bcopyxx_stack equ 128 ; We want this much stack
+
bits 16
- section .text
+ section .text16
diff --git a/core/bios.inc b/core/bios.inc
index 987a2166..33a3cd4c 100644
--- a/core/bios.inc
+++ b/core/bios.inc
@@ -18,11 +18,17 @@
%ifndef _BIOS_INC
%define _BIOS_INC
+ global BIOS_fbm, BIOS_timer
- absolute 4*1Eh ; In the interrupt table
+ ; Interrupt vectors
+ absolute 4*1Ch
+BIOS_timer_hook resd 1
+
+ absolute 4*1Eh
fdctab equ $
fdctab1 resw 1
fdctab2 resw 1
+
absolute 0400h
serial_base resw 4 ; Base addresses for 4 serial ports
absolute 0413h
diff --git a/core/bootsect.inc b/core/bootsect.inc
index 0cf0c460..b4402f1c 100644
--- a/core/bootsect.inc
+++ b/core/bootsect.inc
@@ -36,13 +36,13 @@ SuperSize equ $+1
push word superblock_len_fat16
%endif
load_bootsec:
- mov edi,100000h
+ mov edi,free_high_memory
mov [trackbuf+4],edi ; Copy from this address
xor dx,dx ; No padding
mov bx,abort_check ; Don't print dots, but allow abort
call load_high
- sub edi,100000h
+ sub edi,free_high_memory
mov [trackbuf+8],edi ; Save length
mov eax,7C00h ; Entry point
@@ -54,7 +54,7 @@ load_bootsec:
; For a BSS boot sector we have to patch.
mov esi,superblock
- mov edi,100000h+(superblock-bootsec)
+ mov edi,free_high_memory+(superblock-bootsec)
call bcopy
%endif
push eax ; Save entry point
@@ -80,7 +80,7 @@ load_bootsec:
xor bx,bx
%elif IS_PXELINUX
mov byte [KeepPXE],03h ; Chainloading + keep PXE
- call reset_pxe
+ pm_call reset_pxe
lfs si,[InitStack]
; Put restore DS, EDX and ESI to the true initial values
mov bx,[fs:si+6]
@@ -143,7 +143,9 @@ replace_bootstrap_noclearmode:
jmp .stackok
%endif
.stdstack:
- mov di,7C00h-44
+ ; StackBuf is guaranteed to have 44 bytes free immediately
+ ; above it, and it will not interfere with our existing stack.
+ mov di,StackBuf
push di
mov cx,22 ; 44 bytes
rep stosw
@@ -232,4 +234,4 @@ replace_stub:
jmp 0:0
.csip equ $-4
- section .text
+ section .text16
diff --git a/core/cache.inc b/core/cache.inc
deleted file mode 100644
index 59755576..00000000
--- a/core/cache.inc
+++ /dev/null
@@ -1,114 +0,0 @@
-; -*- fundamental -*- ---------------------------------------------------
-;
-; Copyright 2004-2008 H. Peter Anvin - All Rights Reserved
-;
-; This program is free software; you can redistribute it and/or modify
-; it under the terms of the GNU General Public License as published by
-; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
-; Boston MA 02111-1307, USA; either version 2 of the License, or
-; (at your option) any later version; incorporated herein by reference.
-;
-; -----------------------------------------------------------------------
-
- section .text
-
- struc cptr
-.sector: resd 1 ; Sector number
-.prev: resw 1 ; LRU pointer to previous (less recent)
-.next: resw 1 ; LRU pointer to next (more recent)
- endstruc
-cptr_size_lg2 equ 3
-
-NCacheEntries equ 65536/SECTOR_SIZE
-
-;
-; initcache: Initialize the cache data structures
-;
-initcache:
- xor eax,eax ; We don't care about sector 0
- mov di,CachePtrs
- mov cx,NCacheEntries+1
- mov bx,CachePtrs+NCacheEntries*cptr_size ; "prev" pointer
-.loop:
- mov [di+cptr.sector],eax ; Zero sector number
- mov [di+cptr.prev],bx ; Previous pointer
- mov [bx+cptr.next],di ; Previous entry's next pointer
- mov bx,di
- add di,cptr_size
- loop .loop
- ret
-
-;
-; getcachesector: Check for a particular sector (EAX) in the sector cache,
-; and if it is already there, return a pointer in GS:SI
-; otherwise load it and return said pointer.
-;
-; Assumes CS == DS.
-;
-getcachesector:
- push cx
- push bx
- push di
- mov si,cache_seg
- mov gs,si
- mov si,CachePtrs+cptr_size ; Real sector cache pointers
- mov cx,NCacheEntries
-.search:
- cmp eax,[si]
- jz .hit
- add si,cptr_size
- loop .search
-
-.miss:
- TRACER 'M'
- ; Need to load it.
- push es
- push gs
- pop es
- mov bx,[CachePtrs+cptr.next] ; "Next most recent than head node"
- mov [bx+cptr.sector],eax
- mov si,bx
- sub bx,CachePtrs+cptr_size
- shl bx,SECTOR_SHIFT-cptr_size_lg2 ; Buffer address
- pushad
-%if IS_EXTLINUX
- call getonesec_ext
-%else
- call getonesec
-%endif
- popad
- pop es
-.hit:
- ; Update LRU, then compute buffer address
- TRACER 'H'
-
- ; Remove from current position in the list
- mov bx,[si+cptr.prev]
- mov di,[si+cptr.next]
- mov [bx+cptr.next],di
- mov [di+cptr.prev],bx
-
- ; Add to just before head node
- mov bx,[CachePtrs+cptr.prev]
- mov [si+cptr.prev],bx
- mov [bx+cptr.next],si
- mov [CachePtrs+cptr.prev],si
- mov word [si+cptr.next],CachePtrs
-
- sub si,CachePtrs+cptr_size
- shl si,SECTOR_SHIFT-cptr_size_lg2 ; Buffer address
-
- pop di
- pop bx
- pop cx
- ret
-
- section .bss
-
- ; Each CachePtr contains:
- ; - Block pointer
- ; - LRU previous pointer
- ; - LRU next pointer
- ; The first entry is the head node of the list
- alignb 4
-CachePtrs resb (NCacheEntries+1)*cptr_size
diff --git a/core/call16.c b/core/call16.c
new file mode 100644
index 00000000..86d70461
--- /dev/null
+++ b/core/call16.c
@@ -0,0 +1,27 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2009 Intel Corporation; author: H. Peter Anvin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston MA 02110-1301, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * call16.c
+ *
+ * Simple wrapper to call 16-bit core functions from 32-bit code
+ */
+
+#include <stddef.h>
+#include "core.h"
+
+const com32sys_t zero_regs; /* Common all-zero register set */
+
+void call16(void (*func)(void), const com32sys_t *ireg, com32sys_t *oreg)
+{
+ core_farcall((size_t)func, ireg, oreg);
+}
diff --git a/core/callback.inc b/core/callback.inc
new file mode 100644
index 00000000..a33b5825
--- /dev/null
+++ b/core/callback.inc
@@ -0,0 +1,208 @@
+;; -----------------------------------------------------------------------
+;;
+;; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved
+;; Copyright 2009 Intel Corporation; author: H. Peter Anvin
+;;
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+;; Boston MA 02111-1307, USA; either version 2 of the License, or
+;; (at your option) any later version; incorporated herein by reference.
+;;
+;; -----------------------------------------------------------------------
+
+;;
+;; callback.inc
+;;
+;; Callbacks from 32-bit mode to 16-bit mode
+;;
+
+;
+; 16-bit intcall/farcall handling code
+;
+
+;
+; 32-bit support code
+;
+ bits 32
+ section .text
+
+;
+; Intcall/farcall invocation. We manifest a structure on the real-mode stack,
+; containing the com32sys_t structure from <com32.h> as well as
+; the following entries (from low to high address):
+; - Target offset
+; - Target segment
+; - Return offset
+; - Return segment (== real mode cs == 0)
+; - Return flags
+;
+ global core_farcall
+core_farcall:
+ mov eax,[esp+1*4] ; CS:IP
+ jmp core_syscall
+
+ global core_intcall
+core_intcall:
+ movzx eax,byte [esp+1*4] ; INT number
+ mov eax,[eax*4] ; Get CS:IP from low memory
+
+core_syscall:
+ pushfd ; Save IF among other things...
+ push ebx
+ push ebp
+ push esi
+ push edi
+ push dword [CallbackSP]
+
+ cld
+
+ movzx edi,word [word RealModeSSSP]
+ movzx ebx,word [word RealModeSSSP+2]
+ sub edi,54 ; Allocate 54 bytes
+ mov [word RealModeSSSP],di
+ shl ebx,4
+ add edi,ebx ; Create linear address
+
+ mov esi,[esp+8*4] ; Source regs
+ xor ecx,ecx
+ mov cl,11 ; 44 bytes to copy
+ rep movsd
+
+ ; EAX is already set up to be CS:IP
+ stosd ; Save in stack frame
+ mov eax,.rm_return ; Return seg:offs
+ stosd ; Save in stack frame
+ mov eax,[edi-12] ; Return flags
+ and eax,0x200cd7 ; Mask (potentially) unsafe flags
+ mov [edi-12],eax ; Primary flags entry
+ stosw ; Return flags
+
+ mov bx,.rm
+ jmp enter_rm ; Go to real mode
+
+ bits 16
+ section .text16
+.rm:
+ pop gs
+ pop fs
+ pop es
+ pop ds
+ popad
+ popfd
+ mov [cs:CallbackSP],sp
+ retf ; Invoke routine
+
+.rm_return:
+ ; We clean up SP here because we don't know if the
+ ; routine returned with RET, RETF or IRET
+ mov sp,[cs:CallbackSP]
+ pushfd
+ pushad
+ push ds
+ push es
+ push fs
+ push gs
+ mov ebx,.pm_return
+ jmp enter_pm
+
+ ; On return, the 44-byte return structure is on the
+ ; real-mode stack, plus the 10 additional bytes used
+ ; by the target address (see above.)
+ bits 32
+ section .text
+.pm_return:
+ movzx esi,word [word RealModeSSSP]
+ movzx eax,word [word RealModeSSSP+2]
+ mov edi,[esp+9*4] ; Dest regs
+ shl eax,4
+ add esi,eax ; Create linear address
+ and edi,edi ; NULL pointer?
+ jnz .do_copy
+.no_copy: mov edi,esi ; Do a dummy copy-to-self
+.do_copy: xor ecx,ecx
+ mov cl,11 ; 44 bytes
+ rep movsd ; Copy register block
+
+ add dword [word RealModeSSSP],54
+ ; Remove from stack
+
+ pop dword [CallbackSP]
+ pop edi
+ pop esi
+ pop ebp
+ pop ebx
+ popfd
+ ret ; Return to 32-bit program
+
+;
+; Cfarcall invocation. We copy the stack frame to the real-mode stack,
+; followed by the return CS:IP and the CS:IP of the target function.
+;
+ global core_cfarcall
+core_cfarcall:
+ pushfd ; Save IF among other things...
+ push ebx
+ push ebp
+ push esi
+ push edi
+ push dword [CallbackSP]
+
+ cld
+ mov ecx,[esp+9*4] ; Size of stack frame
+
+ movzx edi,word [word RealModeSSSP]
+ movzx ebx,word [word RealModeSSSP+2]
+ mov [word CallbackSP],di
+ sub edi,ecx ; Allocate space for stack frame
+ and edi,~3 ; Round
+ sub edi,4*2 ; Return pointer, return value
+ mov [word RealModeSSSP],di
+ shl ebx,4
+ add edi,ebx ; Create linear address
+
+ mov eax,[esp+7*4] ; CS:IP
+ stosd ; Save to stack frame
+ mov eax,.rm_return ; Return seg:off
+ stosd
+ mov esi,[esp+8*4] ; Stack frame
+ mov eax,ecx ; Copy the stack frame
+ shr ecx,2
+ rep movsd
+ mov ecx,eax
+ and ecx,3
+ rep movsb
+
+ mov bx,.rm
+ jmp enter_rm
+
+ bits 16
+ section .text16
+.rm:
+ retf
+.rm_return:
+ mov sp,[cs:CallbackSP]
+ mov esi,eax
+ mov ebx,.pm_return
+ jmp enter_pm
+
+ bits 32
+ section .text
+.pm_return:
+ mov eax,esi
+ ; EDX already set up to be the RM return value
+ pop dword [CallbackSP]
+ pop ebx
+ pop ebp
+ pop esi
+ pop edi
+ popfd
+ ret
+
+ bits 16
+ section .bss16
+ alignb 4
+CallbackSP resd 1 ; SP saved during callback
+
+ bits 16
+ section .text16
diff --git a/core/checksumiso.pl b/core/checksumiso.pl
deleted file mode 100755
index 9b8c3ee5..00000000
--- a/core/checksumiso.pl
+++ /dev/null
@@ -1,51 +0,0 @@
-#!/usr/bin/perl
-#
-# Construct a checksum for isolinux*.bin, compatible
-# with an mkisofs boot-info-table
-#
-
-use bytes;
-use integer;
-
-($file) = @ARGV;
-
-open(FILE, '+<', $file) or die "$0: cannot open $file: $!\n";
-binmode FILE;
-
-@fstat = stat(FILE) or die "$0: stat $file: $!\n";
-if (!$fstat[7]) {
- die "$0: $file: cannot query length\n";
-}
-
-# Pad file to a multiple of 2048 bytes
-$frac = $fstat[7] % 2048;
-if ($frac) {
- seek(FILE,$fstat[7],0)
- or die "$0: $file: cannot seek to end\n";
- print FILE "\0" x (2048-$frac);
-}
-
-# Checksum the file post header
-if ( !seek(FILE,64,0) ) {
- die "$0: $file: cannot seek past header\n";
-}
-
-$csum = 0;
-$bytes = 64;
-while ( ($n = read(FILE, $dw, 4)) > 0 ) {
- $dw .= "\0\0\0\0"; # Pad to at least 32 bits
- ($v) = unpack("V", $dw);
- $csum = ($csum + $v) & 0xffffffff;
- $bytes += $n;
-}
-
-# Update header
-if ( !seek(FILE,16,0) ) {
- die "$0: $file: cannot seek to header\n";
-}
-
-print FILE pack("VV", $bytes, $csum);
-
-close(FILE);
-
-exit 0;
diff --git a/core/cleanup.inc b/core/cleanup.inc
index 063ed73e..300584c7 100644
--- a/core/cleanup.inc
+++ b/core/cleanup.inc
@@ -16,7 +16,7 @@
;; Some final tidying before jumping to a kernel or bootsector
;;
- section .text
+ section .text16
;
; cleanup_hardware:
;
@@ -49,6 +49,11 @@ cleanup_hardware:
int 10h
.no_vmware:
%endif
+
+ call comboot_cleanup_api
+
+ call timer_cleanup
+
popad
; If we enabled serial port interrupts, clean them up now
diff --git a/core/cmdline.inc b/core/cmdline.inc
index 642e5e14..7fa53816 100644
--- a/core/cmdline.inc
+++ b/core/cmdline.inc
@@ -19,6 +19,8 @@
;; Not used by plain kernel due to BOOT_IMAGE= etc.
;;
+ section .text16
+
;
; Assumes DS == CS
;
@@ -37,6 +39,7 @@ make_plain_cmdline:
dec di
mov [CmdLinePtr],di
+ mov byte [es:di],0 ; Null-terminate
pop es
ret
@@ -45,7 +48,9 @@ make_plain_cmdline:
; Actual IPAppend strings...
;
%if IS_PXELINUX
- section .data
+ extern IPOption, BOOTIFStr
+
+ section .data16
alignz 2
IPAppends dw IPOption
dw BOOTIFStr
@@ -60,7 +65,7 @@ numIPAppends equ 0
;
; Assumes DS == CS; pushes output to ES:DI
;
- section .text
+ section .text16
do_ip_append:
%ifndef DEPEND
diff --git a/core/codepage.S b/core/codepage.S
new file mode 100644
index 00000000..4f1d4836
--- /dev/null
+++ b/core/codepage.S
@@ -0,0 +1,5 @@
+ .section ".rodata","a"
+ .globl codepage
+codepage:
+ .incbin "codepage.cp"
+ .size codepage, .-codepage
diff --git a/core/com32.inc b/core/com32.inc
index 8c3f181a..111590c3 100644
--- a/core/com32.inc
+++ b/core/com32.inc
@@ -1,6 +1,7 @@
;; -----------------------------------------------------------------------
;;
-;; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
+;; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved
+;; Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
;;
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
@@ -16,6 +17,8 @@
;; Common code for running a COM32 image
;;
+ extern pm_api_vector
+
;
; Load a COM32 image. A COM32 image is the 32-bit analogue to a DOS
; .com file. A COM32 image is loaded at address 0x101000, with %esp
@@ -26,30 +29,9 @@
; "mov ax,0x4cff; int 0x21" in 16-bit mode. This will abort the
; program with an error if run in 16-bit mode.
;
+com32_entry equ free_high_memory
- ; We need to make this a proper section rather
- ; than using absolute numbers, in order to work
- ; around a bug in GNU ld 2.17, which is still in
- ; use as of this writing in the form of Debian
- ; 4.0 (etch).
- bits 32
- section .com32 exec write nobits align=16
-pm_idt equ 0x100000 ; Needs to be absolute...
- resb 4096
-pm_entry: ; Needs to not be...
-
- bits 16
- section .data
- alignz 2
-com32_pmidt:
- dw 8*256 ; Limit
- dd pm_idt ; Address
-
-com32_rmidt:
- dw 0ffffh ; Limit
- dd 0 ; Address
-
- section .text
+ section .text16
is_com32_image:
push si ; Save file handle
push eax ; Save file length
@@ -65,376 +47,99 @@ is_com32_image:
sub cx,si
fs rep movsb
- mov si,KernelCName
+ mov si,KernelName
mov di,Com32Name
call strcpy
call comboot_setup_api ; Set up the COMBOOT-style API
- mov edi,pm_entry ; Load address
+ mov edi,com32_entry ; Load address
pop eax ; File length
pop si ; File handle
xor dx,dx ; No padding
mov bx,abort_check ; Don't print dots, but allow abort
call load_high
-com32_start:
- mov ebx,com32_call_start ; Where to go in PM
-
-com32_enter_pm:
- cli
- mov ax,cs
- mov ds,ax
- mov [RealModeSSSP],sp
- mov [RealModeSSSP+2],ss
- cld
- call a20_test
- jnz .a20ok
- call enable_a20
+ mov esi,com32_entry
+ mov edi,trackbuf
+ mov ecx,5
+ call bcopy
+ cmp dword [trackbuf],0xcd4cfeb8
+ jne not_com32r
+ cmp byte [trackbuf+4],0x21
+ jne not_com32r
-.a20ok:
- mov byte [bcopy_gdt.TSS+5],89h ; Mark TSS unbusy
-
- lgdt [bcopy_gdt] ; We can use the same GDT just fine
- lidt [com32_pmidt] ; Set up the IDT
- mov eax,cr0
- or al,1
- mov cr0,eax ; Enter protected mode
- jmp PM_CS32:.in_pm
-
- bits 32
-.in_pm:
- xor eax,eax ; Available for future use...
- mov fs,eax
- mov gs,eax
- lldt ax
-
- mov al,PM_DS32 ; Set up data segments
- mov es,eax
- mov ds,eax
- mov ss,eax
-
- mov al,PM_TSS ; Be nice to Intel's VT by
- ltr ax ; giving it a valid TR
-
- mov esp,[PMESP] ; Load protmode %esp if available
- jmp ebx ; Go to where we need to go
+com32_start:
+ ;
+ ; Point the stack to the end of (permitted) high memory
+ ;
+ mov eax,[HighMemRsvd]
+ xor ax,ax ; Align to a 64K boundary
+ mov [PMESP],eax
+ mov ebx,.pm ; Where to go in PM
+ jmp enter_pm
;
; This is invoked right before the actually starting the COM32
; progam, in 32-bit mode...
;
-com32_call_start:
- ;
- ; Point the stack to the end of (permitted) high memory
- ;
- mov esp,[word HighMemRsvd]
- xor sp,sp ; Align to a 64K boundary
-
- ;
- ; Set up the protmode IDT and the interrupt jump buffers
- ; We set these up in the system area at 0x100000,
- ; but we could also put them beyond the stack.
- ;
- mov edi,pm_idt
-
- ; Form an interrupt gate descriptor
- mov eax,0x00200000+((pm_idt+8*256)&0x0000ffff)
- mov ebx,0x0000ee00+((pm_idt+8*256)&0xffff0000)
- xor ecx,ecx
- inc ch ; ecx <- 256
-
- push ecx
-.make_idt:
- stosd
- add eax,8
- xchg eax,ebx
- stosd
- xchg eax,ebx
- loop .make_idt
-
- pop ecx
-
- ; Each entry in the interrupt jump buffer contains
- ; the following instructions:
- ;
- ; 00000000 60 pushad
- ; 00000001 B0xx mov al,<interrupt#>
- ; 00000003 E9xxxxxxxx jmp com32_handle_interrupt
-
- mov eax,0e900b060h
- mov ebx,com32_handle_interrupt-(pm_idt+8*256+8)
-
-.make_ijb:
- stosd
- sub [edi-2],cl ; Interrupt #
- xchg eax,ebx
- stosd
- sub eax,8
- xchg eax,ebx
- loop .make_ijb
-
- ; Now everything is set up for interrupts...
+ bits 32
+ section .text
+.pm:
+ ; Set up the calling stack frame
+ push dword pm_api_vector
push dword Com32Name ; Module filename
push dword [HighMemSize] ; Memory managed by Syslinux
- push dword com32_cfarcall ; Cfarcall entry point
- push dword com32_farcall ; Farcall entry point
+ push dword core_cfarcall ; Cfarcall entry point
+ push dword core_farcall ; Farcall entry point
push dword (1 << 16) ; 64K bounce buffer
- push dword (xfer_buf_seg << 4) ; Bounce buffer address
- push dword com32_intcall ; Intcall entry point
+ push dword core_real_mode ; Bounce buffer address
+ push dword core_intcall ; Intcall entry point
push dword command_line ; Command line pointer
- push dword 8 ; Argument count
+ push dword 9 ; Argument count
sti ; Interrupts OK now
- call pm_entry ; Run the program...
+ call com32_entry ; Run the program...
; ... on return, fall through to com32_exit ...
-
com32_exit:
- mov bx,com32_done ; Return to command loop
-
-com32_enter_rm:
- cli
- cld
- mov [PMESP],esp ; Save exit %esp
- xor esp,esp ; Make sure the high bits are zero
- jmp PM_CS16:.in_pm16 ; Return to 16-bit mode first
+ mov bx,comboot_return
+ jmp enter_rm
bits 16
-.in_pm16:
- mov ax,PM_DS16 ; Real-mode-like segment
- mov es,ax
- mov ds,ax
- mov ss,ax
- mov fs,ax
- mov gs,ax
-
- lidt [com32_rmidt] ; Real-mode IDT (rm needs no GDT)
- mov eax,cr0
- and al,~1
- mov cr0,eax
- jmp 0:.in_rm
-
-.in_rm: ; Back in real mode
- mov ax,cs ; Set up sane segments
- mov ds,ax
- mov es,ax
- mov fs,ax
- mov gs,ax
- lss sp,[RealModeSSSP] ; Restore stack
- jmp bx ; Go to whereever we need to go...
-
-com32_done:
- sti
+ section .text16
+not_com32r:
+ mov si,KernelName
+ call writestr
+ mov si,not_com32r_msg
+ call writestr
jmp enter_command
-;
-; 16-bit support code
-;
- bits 16
-
-;
-; 16-bit interrupt-handling code
-;
-com32_int_rm:
- pushf ; Flags on stack
- push cs ; Return segment
- push word .cont ; Return address
- push dword edx ; Segment:offset of IVT entry
- retf ; Invoke IVT routine
-.cont: ; ... on resume ...
- mov ebx,com32_int_resume
- jmp com32_enter_pm ; Go back to PM
-
-;
-; 16-bit intcall/farcall handling code
-;
-com32_sys_rm:
- pop gs
- pop fs
- pop es
- pop ds
- popad
- popfd
- mov [cs:Com32SysSP],sp
- retf ; Invoke routine
-.return:
- ; We clean up SP here because we don't know if the
- ; routine returned with RET, RETF or IRET
- mov sp,[cs:Com32SysSP]
- pushfd
- pushad
- push ds
- push es
- push fs
- push gs
- mov ebx,com32_syscall.resume
- jmp com32_enter_pm
-
-;
-; 16-bit cfarcall handing code
-;
-com32_cfar_rm:
- retf
-.return:
- mov sp,[cs:Com32SysSP]
- mov [cs:RealModeEAX],eax
- mov ebx,com32_cfarcall.resume
- jmp com32_enter_pm
-
-;
-; 32-bit support code
-;
- bits 32
-
-;
-; This is invoked on getting an interrupt in protected mode. At
-; this point, we need to context-switch to real mode and invoke
-; the interrupt routine.
-;
-; When this gets invoked, the registers are saved on the stack and
-; AL contains the register number.
-;
-com32_handle_interrupt:
- movzx eax,al
- xor ebx,ebx ; Actually makes the code smaller
- mov edx,[ebx+eax*4] ; Get the segment:offset of the routine
- mov bx,com32_int_rm
- jmp com32_enter_rm ; Go to real mode
-
-com32_int_resume:
- popad
- iret
-
-;
-; Intcall/farcall invocation. We manifest a structure on the real-mode stack,
-; containing the com32sys_t structure from <com32.h> as well as
-; the following entries (from low to high address):
-; - Target offset
-; - Target segment
-; - Return offset
-; - Return segment (== real mode cs == 0)
-; - Return flags
-;
-com32_farcall:
- pushfd ; Save IF among other things...
- pushad ; We only need to save some, but...
-
- mov eax,[esp+10*4] ; CS:IP
- jmp com32_syscall
-
+ section .data16
+not_com32r_msg db ': not a COM32R image', CR, LF, 0
-com32_intcall:
- pushfd ; Save IF among other things...
- pushad ; We only need to save some, but...
-
- movzx eax,byte [esp+10*4] ; INT number
- mov eax,[eax*4] ; Get CS:IP from low memory
-
-com32_syscall:
- cld
-
- movzx edi,word [word RealModeSSSP]
- movzx ebx,word [word RealModeSSSP+2]
- sub edi,54 ; Allocate 54 bytes
- mov [word RealModeSSSP],di
- shl ebx,4
- add edi,ebx ; Create linear address
-
- mov esi,[esp+11*4] ; Source regs
- xor ecx,ecx
- mov cl,11 ; 44 bytes to copy
- rep movsd
-
- ; EAX is already set up to be CS:IP
- stosd ; Save in stack frame
- mov eax,com32_sys_rm.return ; Return seg:offs
- stosd ; Save in stack frame
- mov eax,[edi-12] ; Return flags
- and eax,0x200cd7 ; Mask (potentially) unsafe flags
- mov [edi-12],eax ; Primary flags entry
- stosw ; Return flags
-
- mov bx,com32_sys_rm
- jmp com32_enter_rm ; Go to real mode
-
- ; On return, the 44-byte return structure is on the
- ; real-mode stack, plus the 10 additional bytes used
- ; by the target address (see above.)
-.resume:
- movzx esi,word [word RealModeSSSP]
- movzx eax,word [word RealModeSSSP+2]
- mov edi,[esp+12*4] ; Dest regs
- shl eax,4
- add esi,eax ; Create linear address
- and edi,edi ; NULL pointer?
- jnz .do_copy
-.no_copy: mov edi,esi ; Do a dummy copy-to-self
-.do_copy: xor ecx,ecx
- mov cl,11 ; 44 bytes
- rep movsd ; Copy register block
-
- add dword [word RealModeSSSP],54 ; Remove from stack
-
- popad
- popfd
- ret ; Return to 32-bit program
-
-;
-; Cfarcall invocation. We copy the stack frame to the real-mode stack,
-; followed by the return CS:IP and the CS:IP of the target function.
-;
-com32_cfarcall:
- pushfd
- pushad
-
- cld
- mov ecx,[esp+12*4] ; Size of stack frame
-
- movzx edi,word [word RealModeSSSP]
- movzx ebx,word [word RealModeSSSP+2]
- mov [word Com32SysSP],di
- sub edi,ecx ; Allocate space for stack frame
- and edi,~3 ; Round
- sub edi,4*2 ; Return pointer, return value
- mov [word RealModeSSSP],di
- shl ebx,4
- add edi,ebx ; Create linear address
-
- mov eax,[esp+10*4] ; CS:IP
- stosd ; Save to stack frame
- mov eax,com32_cfar_rm.return ; Return seg:off
- stosd
- mov esi,[esp+11*4] ; Stack frame
- mov eax,ecx ; Copy the stack frame
- shr ecx,2
- rep movsd
- mov ecx,eax
- and ecx,3
- rep movsb
-
- mov bx,com32_cfar_rm
- jmp com32_enter_rm
-
-.resume:
- popad
- mov eax,[word RealModeEAX]
- popfd
- ret
-
- bits 16
-
- section .bss1
- alignb 4
-RealModeSSSP resd 1 ; Real-mode SS:SP
-RealModeEAX resd 1 ; Real mode EAX
-PMESP resd 1 ; Protected-mode ESP
-Com32SysSP resw 1 ; SP saved during COM32 syscall
+ ; Ersatz com32 invocation structure, to make libcom32
+ ; code run the same if linked to the core. This is in
+ ; the .data16 segment so HighMemSize can live here.
+ ;
+ ; Danger, Will Robinson: it's not clear the use of
+ ; core_xfer_buf is safe here.
+ global __entry_esp, __com32
+ alignz 4
+__entry_esp:
+ dd 0 ; Dummy to avoid _exit issues
+__com32:
+ dd 9 ; Argument count
+ dd 0 ; No command line
+ dd core_intcall ; Intcall entry point
+ dd 0 ; Bounce buffer address
+ dd 0 ; 64K bounce buffer
+ dd core_farcall ; Farcall entry point
+ dd core_cfarcall ; Cfarcall entry point
+HighMemSize dd 0 ; End of memory pointer (bytes)
+ dd 0 ; No module name
+ dd pm_api_vector ; Protected mode functions
section .uibss
-%if IS_SYSLINUX
-Com32Name resb FILENAME_MAX+2
-%else
Com32Name resb FILENAME_MAX
-%endif
- section .text
+ section .text16
diff --git a/core/comboot.inc b/core/comboot.inc
index f8a78531..7e4c3d6f 100644
--- a/core/comboot.inc
+++ b/core/comboot.inc
@@ -17,7 +17,7 @@
;; Common code for running a COMBOOT image
;;
- section .text
+ section .text16
; Parameter registers definition; this is the definition
; of the stack frame used by INT 21h and INT 22h.
@@ -63,7 +63,7 @@
; Looks like a COMBOOT image but too large
comboot_too_large:
- call close_file
+ pm_call pm_close_file
mov si,err_comlarge
call writestr
jmp enter_command
@@ -126,7 +126,7 @@ is_comboot_image:
mov bx,100h ; Load at <seg>:0100h
mov cx,10000h >> SECTOR_SHIFT
; Absolute maximum # of sectors
- call getfssec
+ pm_call getfssec
cmp ecx,65536-256-2 ; Maximum size
ja comboot_too_large
@@ -139,17 +139,9 @@ is_comboot_image:
jmp comboot_seg:100h ; Run it
-; Proper return vector
-; Note: this gets invoked both via INT 21h and directly via INT 20h.
-; We don't need to cld explicitly here, because comboot_exit does that
-; when invoking RESET_STACK_AND_SEGS.
-comboot_return: cli ; May not have a safe stack
- push enter_command ; Normal return to command prompt
- jmp comboot_exit
-
;
-; Set up the COMBOOT API interrupt vectors. This is also used
-; by the COM32 code.
+; Set up the COMBOOT API interrupt vectors. This is now done at
+; initialization time.
;
comboot_setup_api:
mov di,DOSErrTramp ; Error trampolines
@@ -178,11 +170,24 @@ comboot_setup_api:
loop .loop2
ret
- section .bss
+;
+; Restore the original state of the COMBOOT API vectors, and free
+; any low memory allocated by the comboot module.
+;
+comboot_cleanup_api:
+ pusha
+ mov si,DOSSaveVectors
+ mov di,4*20h
+ mov cx,20h
+ rep movsd ; Restore DOS-range vectors
+ popa
+ ret
+
+ section .bss16
alignb 4
DOSSaveVectors resd 32
- section .data
+ section .data16
%define comboot_err(x) (DOSErrTramp+4*((x)-20h))
comboot_vectors:
@@ -219,7 +224,7 @@ comboot_vectors:
dw comboot_err(3Eh) ; INT 3E = DOS FPU emulation
dw comboot_err(3Fh) ; INT 3F = DOS overlay manager
- section .text
+ section .text16
; INT 21h: generic DOS system call
comboot_int21: sti
@@ -284,25 +289,30 @@ comboot_bogus_tail:
call crlf
jmp enter_command
+; Proper return vector
+; Note: this gets invoked both via INT 21h and directly via INT 20h.
+; We don't need to cld explicitly here, because comboot_exit does that
+; when invoking RESET_STACK_AND_SEGS.
+comboot_return:
+ cli ; May not have a safe stack
+ push enter_command ; Normal return to command prompt
+ ; jmp comboot_exit
+
;
; Generic COMBOOT return to command line code
; stack -> where to go next
; CX -> message (for _msg version)
;
+ extern comboot_cleanup_lowmem
comboot_exit:
xor cx,cx
comboot_exit_msg:
pop bx ; Return address
- RESET_STACK_AND_SEGS SI ; Contains sti, cld
+ RESET_STACK_AND_SEGS si ; Contains sti, cld
+ pm_call comboot_cleanup_lowmem
call adjust_screen ; The COMBOOT program might have changed the screen
- pusha
- mov si,DOSSaveVectors
- mov di,4*20h
- mov cx,20h
- rep movsd ; Restore DOS-range vectors
- popa
jcxz .nomsg
- mov si,KernelCName
+ mov si,KernelName
call writestr
mov si,cx
call writestr
@@ -449,9 +459,9 @@ comapi_get_version:
mov P_ES,ds
; ES:SI -> version banner
- mov P_SI,syslinux_banner + 2 ; Skip leading CR LF
+ mov P_SI,syslinux_banner
; ES:DI -> copyright string
- mov P_DI,copyright_str + 1 ; Skip leading space
+ mov P_DI,copyright_str
comapi_nop:
clc
@@ -507,31 +517,18 @@ comapi_textmode:
; INT 22h AX=0006h Open file
;
comapi_open:
- call reset_idle
- push ds
- mov ds,P_ES
- mov si,P_SI
- mov di,InitRD
- call mangle_name
- pop ds
- call searchdir
- jz comapi_err
- mov P_EAX,eax
- mov P_CX,SECTOR_SIZE
- mov P_SI,si
- clc
+ pm_call pm_open_file
ret
;
; INT 22h AX=0007h Read file
;
comapi_read:
- call reset_idle
mov es,P_ES
mov bx,P_BX
mov si,P_SI
mov cx,P_CX
- call getfssec
+ pm_call getfssec
jnc .noteof
xor si,si ; SI <- 0 on EOF, CF <- 0
.noteof: mov P_SI,si
@@ -543,7 +540,7 @@ comapi_read:
;
comapi_close:
mov si,P_SI
- call close_file
+ pm_call pm_close_file
clc
ret
@@ -633,7 +630,7 @@ comapi_cleanup:
test dl,3
setnz [KeepPXE]
sub bp,sp ; unload_pxe may move the stack around
- call unload_pxe
+ pm_call unload_pxe
add bp,sp ; restore frame pointer...
%elif IS_SYSLINUX || IS_EXTLINUX
; Restore original FDC table
@@ -684,10 +681,11 @@ comapi_ipappend:
; INT 22h AX=0010h Resolve hostname
;
%if IS_PXELINUX
+ extern pxe_dns_resolv
comapi_dnsresolv:
mov ds,P_ES
mov si,P_BX
- call dns_resolv
+ pm_call pxe_dns_resolv
mov P_EAX,eax
clc
ret
@@ -695,7 +693,7 @@ comapi_dnsresolv:
comapi_dnsresolv equ comapi_err
%endif
- section .text
+ section .text16
;
; INT 22h AX=0011h Obsolete
@@ -742,30 +740,31 @@ comapi_runkernel:
cmp al,VK_TYPES-1
ja .error
mov [KernelType],al
+
+ ; It's not just possible, but quite likely, that ES:BX
+ ; points into real_mode_seg or xfer_buf_seg, so we
+ ; need to exercise some special care here... use
+ ; vk_append for temporary storage.
+ push ds
+ mov ds,P_ES
+ mov si,P_BX
+ mov di,vk_append
+ call strcpy
+ pop ds
+
push ds
mov ds,P_DS
mov si,P_SI
mov di,KernelName
- call mangle_name
+ pm_call pm_mangle_name
pop ds
- call searchdir
+ pm_call pm_searchdir
jz comapi_err
; The kernel image was found, so we can load it...
mov [Kernel_SI],si
mov [Kernel_EAX],eax
- ; It's not just possible, but quite likely, that ES:BX
- ; points into real_mode_seg or xfer_buf_seg, so we
- ; need to exercise some special care here... use
- ; trackbuf as an intermediary
- push ds
- mov ds,P_ES
- mov si,P_BX
- mov di,trackbuf
- call strcpy
- pop ds
-
%if IS_PXELINUX
mov al,P_CL
mov [IPAppend],al
@@ -778,10 +777,10 @@ comapi_runkernel:
push es
mov dx,real_mode_seg
mov es,dx
- mov si,trackbuf
+ mov si,vk_append
mov di,cmd_line_here
call strcpy
- mov byte [es:di-1],' ' ; Simulate APPEND
+ mov word [es:di-1],' ' ; Simulate APPEND: space plus null
pop es
mov [CmdLinePtr],di
mov word [CmdOptPtr],zero_string
@@ -831,7 +830,6 @@ comapi_userfont:
;
%if IS_SYSLINUX || IS_ISOLINUX || IS_EXTLINUX
comapi_readdisk:
- call reset_idle
mov esi,P_ESI ; Enforce ESI == EDI == 0, these
or esi,P_EDI ; are reserved for future expansion
jnz .err
@@ -895,69 +893,11 @@ comapi_getcwd:
ret
;
-; INT 22h AX=0020h Open directory
-;
-%if IS_SYSLINUX
-comapi_opendir:
- call reset_idle
- 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:
- call reset_idle
- 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
-
-;
; INT 22h AX=0023h Query shuffler size
;
comapi_shufsize:
- mov P_CX,bcopyxx_safe
+ ; +15 is padding to guarantee alignment
+ mov P_CX,__bcopyxx_len + 15
ret
;
@@ -970,7 +910,7 @@ comapi_shufraw:
mov ecx,P_ECX
jmp shuffle_and_boot_raw
- section .data
+ section .data16
%macro int21 2
db %1
@@ -1024,9 +964,9 @@ int22_table:
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
+ dw comapi_err ; 0020 open directory
+ dw comapi_err ; 0021 read directory
+ dw comapi_err ; 0022 close directory
dw comapi_shufsize ; 0023 query shuffler size
dw comapi_shufraw ; 0024 cleanup, shuffle and boot raw
int22_count equ ($-int22_table)/2
@@ -1049,8 +989,13 @@ feature_flags_len equ ($-feature_flags)
err_notdos db ': attempted DOS system call INT ',0
err_comlarge db 'COMBOOT image too large.', CR, LF, 0
- section .bss1
+ section .bss16
alignb 4
DOSErrTramp resd 33 ; Error trampolines
+
+ global ConfigName
ConfigName resb FILENAME_MAX
+%ifndef HAVE_CURRENTDIRNAME
+ global CurrentDirName
CurrentDirName resb FILENAME_MAX
+%endif
diff --git a/core/common.inc b/core/common.inc
new file mode 100644
index 00000000..7078011e
--- /dev/null
+++ b/core/common.inc
@@ -0,0 +1,25 @@
+;
+; Modules common to all derivatives. Do not include modules in this list
+; which have special section requirements (i.e. need to be in .init for
+; some derivatives.)
+;
+
+%include "getc.inc" ; getc et al
+%include "conio.inc" ; Console I/O
+%include "configinit.inc" ; Initialize configuration
+%include "parseconfig.inc" ; High-level config file handling
+%include "parsecmd.inc" ; Low-level config file handling
+%include "pm.inc" ; Protected mode
+%include "bcopy32.inc" ; 32-bit bcopy
+%include "loadhigh.inc" ; Load a file into high memory
+%include "font.inc" ; VGA font stuff
+%include "graphics.inc" ; VGA graphics
+%include "highmem.inc" ; High memory sizing
+%include "strcpy.inc" ; strcpy()
+%include "idle.inc" ; Idle handling
+%include "adv.inc" ; Auxillary Data Vector
+%include "timer.inc" ; Timer handling
+
+; Note: the prefix section is included late, to avoid problems with some
+; versions of NASM that had issues with forward references to EQU symbols.
+%include "prefix.inc" ; Prefix section for prepcore
diff --git a/core/config.inc b/core/config.inc
index 5a3d1c5a..269e13ec 100644
--- a/core/config.inc
+++ b/core/config.inc
@@ -32,6 +32,12 @@ MAX_FKEYS equ 12 ; Number of F-key help files
%assign HAS_LOCALBOOT 1
;
+; log2(Max filename size Including final null)
+;
+FILENAME_MAX_LG2 equ 8
+FILENAME_MAX equ (1 << FILENAME_MAX_LG2) ; Max mangled filename size
+
+;
; Version number definitinons
;
%include "../version.gen"
diff --git a/core/configinit.inc b/core/configinit.inc
index c6fdaf76..915e77f7 100644
--- a/core/configinit.inc
+++ b/core/configinit.inc
@@ -17,7 +17,7 @@
;; Initialize the configuration section
;;
- section .text
+ section .text16
reset_config:
call highmemsize
diff --git a/core/conio.inc b/core/conio.inc
index 701fcf05..b4505027 100644
--- a/core/conio.inc
+++ b/core/conio.inc
@@ -15,7 +15,7 @@
;; conio.inc
;;
;; Console I/O code, except:
-;; writechr, writestr_early - module-dependent
+;; writechr, writestr_early - module-dependent
;; writestr, crlf - writestr.inc
;; writehex* - writehex.inc
;;
@@ -24,7 +24,7 @@
; loadkeys: Load a LILO-style keymap; file is open on the top of the
; getc stack.
;
- section .text
+ section .text16
loadkeys:
mov cx,256
@@ -207,8 +207,8 @@ msg_viewimage:
mov byte [si],0 ; Zero-terminate filename
mov si,VGAFileBuf
mov di,VGAFileMBuf
- call mangle_name
- call open
+ pm_call pm_mangle_name
+ call core_open
jz msg_putcharnext ; Not there
call vgadisplayfile
; Fall through
@@ -388,7 +388,7 @@ debug_tracer: pushad
ret
%endif ; DEBUG_TRACERS
- section .data
+ section .data16
%if IS_ISOLINUX == 0 ; Defined elsewhere for ISOLINUX
crlf_msg db CR, LF
null_msg db 0
@@ -401,7 +401,7 @@ DisplayCon dw 01h ; Console display enabled
ScrollAttribute db 07h ; Grey on white (normal text color)
- section .bss
+ section .bss16
alignb 2
NextCharJump resw 1 ; Routine to interpret next print char
CursorDX equ $
@@ -413,7 +413,7 @@ VidRows resb 1 ; Rows on screen-1
; Serial console stuff; don't put this in .config becasue we don't want
; loading a new config file to undo this setting.
- section .data
+ section .data16
alignz 4
SerialPort dw 0 ; Serial port base (or 0 for no serial port)
BaudDivisor dw 115200/9600 ; Baud rate divisor
@@ -423,8 +423,9 @@ FlowInput db 0 ; Input bits for serial flow
FlowIgnore db 0 ; Ignore input unless these bits set
FlowDummy db 0 ; Unused
- section .bss
+ section .bss16
TextAttribute resb 1 ; Text attribute for message file
DisplayMask resb 1 ; Display modes mask
+ section .text16
%include "serirq.inc"
diff --git a/core/console.c b/core/console.c
new file mode 100644
index 00000000..282c57f5
--- /dev/null
+++ b/core/console.c
@@ -0,0 +1,22 @@
+#include <stddef.h>
+#include <com32.h>
+#include <stdio.h>
+#include <string.h>
+
+void myputchar(int c)
+{
+ static com32sys_t ireg;
+
+ if (c == '\n')
+ myputchar('\r');
+
+ ireg.eax.b[1] = 0x02;
+ ireg.edx.b[0] = c;
+ __intcall(0x21, &ireg, NULL);
+}
+
+void myputs(const char *str)
+{
+ while (*str)
+ myputchar(*str++);
+}
diff --git a/core/cpuinit.inc b/core/cpuinit.inc
deleted file mode 100644
index 4332fbc1..00000000
--- a/core/cpuinit.inc
+++ /dev/null
@@ -1,99 +0,0 @@
-;; -----------------------------------------------------------------------
-;;
-;; Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
-;; Copyright 2010 Intel Corporation; author: H. Peter Anvin
-;;
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
-;; Boston MA 02111-1307, USA; either version 2 of the License, or
-;; (at your option) any later version; incorporated herein by reference.
-;;
-;; -----------------------------------------------------------------------
-
-;;
-;; cpuinit.inc
-;;
-;; CPU-dependent initialization and related checks.
-;;
-
-check_escapes:
- mov ah,02h ; Check keyboard flags
- int 16h
- mov [KbdFlags],al ; Save for boot prompt check
- test al,04h ; Ctrl->skip 386 check
- jnz skip_checks
-
-;
-; Now check that there is sufficient low (DOS) memory
-;
-; NOTE: Linux doesn't use all of real_mode_seg, but we use the same
-; segment for COMBOOT images, which can use all 64K
-;
-dosram_k equ (real_mode_seg+0x1000) >> 6 ; Minimum DOS memory (K)
- int 12h
- cmp ax,dosram_k
- jae enough_ram
- mov si,err_noram
- call writestr_early
- jmp kaboom
-enough_ram:
-skip_checks:
-
-;
-; Detect old versions Xen HVM and disable halt
-; Xen HVM older than version 3.3 might be using vmxassist, which breaks
-; if HLT is executed in real mode.
-;
-; Note: in Syslinux 4, we should probably just execute the HLT in
-; protected mode instead.
-;
-check_xen:
- pushfd
- pushfd
- pop eax
- mov edx,eax
- xor eax,(1 << 21) ; ID flag
- push eax
- popfd
- pushfd
- pop eax
- popfd
- xor eax,edx
- and eax,(1 << 21)
- jz .not_xen ; No CPUID
-
- xor ebx,ebx
- xor ecx,ecx
- xor edx,edx
- mov eax,0x40000000
- cpuid
- cmp ebx,"XenV"
- jne .not_xen
- cmp ecx,"MMXe"
- jne .not_xen
- cmp edx,"nVMM"
- jne .not_xen
-
- ; We're on Xen...
- mov eax,0x40000001
- cpuid
- cmp eax,0x00030003
- jae .not_xen ; Xen >= 3.3, not affected
-
- ; We may be using vmxassist, so disable HLT
- mov byte [ForceNoHalt],1
-
-.not_xen:
-
- section .data
-err_noram db 'It appears your computer has less than '
- asciidec dosram_k
- db 'K of low ("DOS")'
- db CR, LF
- db 'RAM. Linux needs at least this amount to boot. If you get'
- db CR, LF
- db 'this message in error, hold down the Ctrl key while'
- db CR, LF
- db 'booting, and I will take your word for it.', CR, LF, 0
- section .text
diff --git a/core/diskfs.inc b/core/diskfs.inc
new file mode 100644
index 00000000..b8d03762
--- /dev/null
+++ b/core/diskfs.inc
@@ -0,0 +1,182 @@
+; -*- fundamental -*- (asm-mode sucks)
+; -----------------------------------------------------------------------
+;
+; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved
+; Copyright 2009 Intel Corporation; author: H. Peter Anvin
+;
+; This program is free software; you can redistribute it and/or modify
+; it under the terms of the GNU General Public License as published by
+; the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+; Boston MA 02110-1301, USA; either version 2 of the License, or
+; (at your option) any later version; incorporated herein by reference.
+;
+; -----------------------------------------------------------------------
+
+;
+; diskfs.inc
+;
+; Common code for conventional disk-based filesystems
+;
+
+;
+; Some semi-configurable constants... change on your own risk.
+;
+NULLFILE equ 0 ; Null character == empty filename
+NULLOFFSET equ 0 ; Position in which to look
+retry_count equ 16 ; How patient are we with the disk?
+%assign HIGHMEM_SLOP 0 ; Avoid this much memory near the top
+LDLINUX_MAGIC equ 0x3eb202fe ; A random number to identify ourselves with
+
+SECTOR_SHIFT equ 9
+SECTOR_SIZE equ (1 << SECTOR_SHIFT)
+
+;
+; The following structure is used for "virtual kernels"; i.e. LILO-style
+; option labels. The options we permit here are `kernel' and `append
+; Since there is no room in the bottom 64K for all of these, we
+; stick them in high memory and copy them down before we need them.
+;
+ struc vkernel
+vk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!**
+vk_rname: resb FILENAME_MAX ; Real name
+vk_appendlen: resw 1
+vk_type: resb 1 ; Type of file
+ alignb 4
+vk_append: resb max_cmd_len+1 ; Command line
+ alignb 4
+vk_end: equ $ ; Should be <= vk_size
+ endstruc
+
+
+
+; ---------------------------------------------------------------------------
+; BEGIN CODE
+; ---------------------------------------------------------------------------
+
+;
+; Memory below this point is reserved for the BIOS and the MBR
+;
+ section .earlybss
+ global trackbuf
+trackbufsize equ 8192
+trackbuf resb trackbufsize ; Track buffer goes here
+ ; ends at 2800h
+
+;
+; Common bootstrap code for disk-based derivatives
+;
+%include "diskstart.inc"
+
+
+;
+; Now, everything is "up and running"... patch kaboom for more
+; verbosity and using the full screen system
+;
+ ; E9 = JMP NEAR
+ mov di,kaboom.patch
+ mov al,0e9h
+ stosb
+ mov ax,kaboom2-2
+ sub ax,di
+ stosw
+
+;
+; Now we're all set to start with our *real* business. First load the
+; configuration file (if any) and parse it.
+;
+; In previous versions I avoided using 32-bit registers because of a
+; rumour some BIOSes clobbered the upper half of 32-bit registers at
+; random. I figure, though, that if there are any of those still left
+; they probably won't be trying to install Linux on them...
+;
+; The code is still ripe with 16-bitisms, though. Not worth the hassle
+; to take'm out. In fact, we may want to put them back if we're going
+; to boot ELKS at some point.
+;
+
+;
+; Load configuration file
+;
+ pm_call load_config
+ jz no_config_file
+
+;
+; Now we have the config file open. Parse the config file and
+; run the user interface.
+;
+%include "ui.inc"
+
+;
+;
+; kaboom2: once everything is loaded, replace the part of kaboom
+; starting with "kaboom.patch" with this part
+
+kaboom2:
+ mov si,err_bootfailed
+ call writestr
+ cmp byte [kaboom.again+1],18h ; INT 18h version?
+ je .int18
+ call getchar
+ call vgaclearmode
+ int 19h ; And try once more to boot...
+.norge: jmp short .norge ; If int 19h returned; this is the end
+.int18:
+ call vgaclearmode
+ int 18h
+.noreg: jmp short .noreg ; Nynorsk
+
+; -----------------------------------------------------------------------------
+; Common modules
+; -----------------------------------------------------------------------------
+
+%include "common.inc" ; Universal modules
+%include "plaincon.inc" ; writechr
+%include "writestr.inc" ; String output
+%include "writehex.inc" ; Hexadecimal output
+%include "localboot.inc" ; Disk-based local boot
+
+; -----------------------------------------------------------------------------
+; Begin data section
+; -----------------------------------------------------------------------------
+
+ section .data16
+copyright_str db ' Copyright (C) 1994-'
+ asciidec YEAR
+ db ' H. Peter Anvin et al', CR, LF, 0
+err_bootfailed db CR, LF, 'Boot failed: please change disks and press '
+ db 'a key to continue.', CR, LF, 0
+
+;
+; Config file keyword table
+;
+%include "keywords.inc"
+
+;
+; Extensions to search for (in *forward* order).
+;
+ alignz 4
+exten_table: db '.cbt' ; COMBOOT (specific)
+%if IS_SYSLINUX
+ db '.bss' ; Boot sector (add superblock)
+%endif
+ db '.bs', 0 ; Boot sector
+ db '.com' ; COMBOOT (same as DOS)
+ db '.c32' ; COM32
+exten_table_end:
+ dd 0, 0 ; Need 8 null bytes here
+
+;
+; Misc initialized (data) variables
+;
+%ifdef debug ; This code for debugging only
+debug_magic dw 0D00Dh ; Debug code sentinel
+%endif
+
+ alignz 4
+BufSafe dw trackbufsize/SECTOR_SIZE ; Clusters we can load into trackbuf
+BufSafeBytes dw trackbufsize ; = how many bytes?
+%ifndef DEPEND
+%if ( trackbufsize % SECTOR_SIZE ) != 0
+%error trackbufsize must be a multiple of SECTOR_SIZE
+%endif
+%endif
diff --git a/core/diskstart.inc b/core/diskstart.inc
index b8ab790c..c24b64ab 100644
--- a/core/diskstart.inc
+++ b/core/diskstart.inc
@@ -1,7 +1,7 @@
; -----------------------------------------------------------------------
;
; Copyright 1994-2009 H. Peter Anvin - All Rights Reserved
-; Copyright 2009 Intel Corporation; author: H. Peter Anvin
+; Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
;
; This program is free software; you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
@@ -18,11 +18,12 @@
;
; Expanded superblock
- section .bss
+ section .earlybss
alignb 8
SuperInfo resq 16 ; The first 16 bytes expanded 8 times
+DriveNumber resb 1
- section .text
+ section .init
;
; Some of the things that have to be saved very early are saved
; "close" to the initial stack pointer offset, in order to
@@ -33,6 +34,7 @@ PartInfo equ StackBuf ; Saved partition table entry
FloppyTable equ PartInfo+16 ; Floppy info table (must follow PartInfo)
OrigFDCTabPtr equ StackBuf-8 ; The 2nd high dword on the stack
OrigESDI equ StackBuf-4 ; The high dword on the stack
+StackHome equ OrigFDCTabPtr ; The start of the canonical stack
;
; Primary entry point. Tempting as though it may be, we can't put the
@@ -94,6 +96,7 @@ superblock_len_fat32 equ $-superblock+54
zb 54 ; Maximum needed size
superblock_max equ $-superblock
+ global SecPerClust
SecPerClust equ bxSecPerClust
;
; Note we don't check the constraints above now; we did that at install
@@ -250,6 +253,7 @@ getonesec:
; that is dead from that point; this saves space. However, please keep
; the order to dst,src to keep things sane.
;
+ global getlinsec
getlinsec:
add eax,[bsHidden] ; Add partition offset
xor edx,edx ; Zero-extend LBA (eventually allow 64 bits)
@@ -407,11 +411,12 @@ getlinsec_cbios:
;
; kaboom: write a message and bail out.
;
+ global kaboom
disk_error:
kaboom:
xor si,si
mov ss,si
- mov sp,StackBuf-4 ; Reset stack
+ mov sp,OrigFDCTabPtr ; Reset stack
mov ds,si ; Reset data segment
pop dword [fdctab] ; Restore FDC table
.patch: ; When we have full code, intercept here
@@ -451,6 +456,7 @@ bailmsg: db 'Boot error', 0Dh, 0Ah, 0
zb 1F8h-($-$$)
FirstSector dd 0xDEADBEEF ; Location of sector 1
+ global MaxTransfer
MaxTransfer dw 0x007F ; Max transfer size
; This field will be filled in 0xAA55 by the installer, but we abuse it
@@ -465,6 +471,7 @@ bootsignature dw kaboom.again-bootsec
; Start of LDLINUX.SYS
; ===========================================================================
+LDLINUX_SYS equ ($-$$)+TEXT_START
ldlinux_sys:
syslinux_banner db CR, LF, MY_NAME, ' ', VERSION_STR, ' ', DATE_STR, ' ', 0
@@ -478,19 +485,32 @@ ldlinux_magic dd LDLINUX_MAGIC
; This area is patched by the installer. It is found by looking for
; LDLINUX_MAGIC, plus 8 bytes.
;
+SUBVOL_MAX equ 256
+CURRENTDIR_MAX equ FILENAME_MAX
+
patch_area:
-LDLDwords dw 0 ; Total dwords starting at ldlinux_sys,
- ; not including ADVs
-LDLSectors dw 0 ; Number of sectors - (bootsec+this sec)
- ; but including any ADVs
+DataSectors dw 0 ; Number of sectors (not including bootsec)
+ADVSectors dw 0 ; Additional sectors for ADVs
+LDLDwords dd 0 ; Total dwords starting at ldlinux_sys,
CheckSum dd 0 ; Checksum starting at ldlinux_sys
; value = LDLINUX_MAGIC - [sum of dwords]
-%if IS_EXTLINUX
-CurrentDir dd 2 ; "Current" directory inode number
-%endif
+CurrentDirPtr dw CurrentDirName-LDLINUX_SYS ; Current directory name string
+CurrentDirLen dw CURRENTDIR_MAX
+SubvolPtr dw SubvolName-LDLINUX_SYS
+SubvolLen dw SUBVOL_MAX
+SecPtrOffset dw SectorPtrs-LDLINUX_SYS
+SecPtrCnt dw (SectorPtrsEnd - SectorPtrs) >> 2
-; Space for up to 64 sectors, the theoretical maximum
-SectorPtrs times 64 dd 0
+;
+; Installer pokes the base directory here. This is in .data16 so it
+; isn't actually located in the first sector.
+;
+%define HAVE_CURRENTDIRNAME
+ section .data16
+ global CurrentDirName, SubvolName
+CurrentDirName times CURRENTDIR_MAX db 0
+SubvolName times SUBVOL_MAX db 0
+ section .init
ldlinux_ent:
;
@@ -522,23 +542,25 @@ print_bios:
mov [BIOSName],si
call writestr_early
- section .bss
+ section .earlybss
%define HAVE_BIOSNAME 1
BIOSName resw 1
- section .text
+ section .init
;
; Now we read the rest of LDLINUX.SYS. Don't bother loading the first
; sector again, though.
;
load_rest:
- mov si,SectorPtrs
- mov bx,7C00h+2*SECTOR_SIZE ; Where we start loading
- mov cx,[LDLSectors]
+ lea esi,[SectorPtrs]
+ mov ebx,TEXT_START+2*SECTOR_SIZE ; Where we start loading
+ mov cx,[DataSectors]
+ dec cx ; Minus this sector
.get_chunk:
jcxz .done
- xor bp,bp
+ xor ebp,ebp
+ mov di,bx ; Low 64K of target address
lodsd ; First sector of this chunk
mov edx,eax
@@ -547,16 +569,27 @@ load_rest:
inc bp
dec cx
jz .chunk_ready
+ cmp ebx,esi ; Pointer we don't have yet?
+ jae .chunk_ready
inc edx ; Next linear sector
cmp [si],edx ; Does it match
jnz .chunk_ready ; If not, this is it
add si,4 ; If so, add sector to chunk
- jmp short .make_chunk
+ add di,SECTOR_SIZE ; Check for 64K segment wrap
+ jnz .make_chunk
.chunk_ready:
+ push ebx
+ push es
+ shr ebx,4 ; Convert to a segment
+ mov es,bx
+ xor bx,bx
+ xor edx,edx ; Zero-extend LBA
call getlinsecsr
- shl bp,SECTOR_SHIFT
- add bx,bp
+ pop es
+ pop ebx
+ shl ebp,SECTOR_SHIFT
+ add ebx,ebp
jmp .get_chunk
.done:
@@ -568,14 +601,23 @@ load_rest:
;
verify_checksum:
mov si,ldlinux_sys
- mov cx,[LDLDwords]
- mov edx,-LDLINUX_MAGIC
+ mov ecx,[LDLDwords]
+ mov eax,-LDLINUX_MAGIC
+ push ds
.checksum:
- lodsd
- add edx,eax
- loop .checksum
+ add eax,[si]
+ add si,4
+ jnz .nowrap
+ ; Handle segment wrap
+ mov dx,ds
+ add dx,1000h
+ mov ds,dx
+.nowrap:
+ dec ecx
+ jnz .checksum
+ pop ds
- and edx,edx ; Should be zero
+ and eax,eax ; Should be zero
jz all_read ; We're cool, go for it!
;
@@ -646,15 +688,23 @@ rl_checkpt equ $ ; Must be <= 8000h
rl_checkpt_off equ ($-$$)
%ifndef DEPEND
-%if rl_checkpt_off > 400h
+%if rl_checkpt_off > 3FCh ; Need one pointer in here
%error "Sector 1 overflow"
%endif
%endif
+; Sector pointers
+ alignz 4
+MaxInitDataSize equ 96 << 10
+MaxLMA equ TEXT_START+SECTOR_SIZE+MaxInitDataSize
+SectorPtrs times MaxInitDataSize >> SECTOR_SHIFT dd 0
+SectorPtrsEnd equ $
+
; ----------------------------------------------------------------------------
; End of code and data that have to be in the first sector
; ----------------------------------------------------------------------------
+ section .text16
all_read:
;
; Let the user (and programmer!) know we got this far. This used to be
@@ -680,7 +730,21 @@ expand_super:
stosd ; Store expanded byte
loop .loop
+
;
-; Fall through to the mainline code...
+; Common initialization code
;
- section .text
+%include "init.inc"
+
+ pushad
+ mov eax,ROOT_FS_OPS
+ movzx dx,byte [DriveNumber]
+ ; DH = 0: we are boot from disk not CDROM
+ mov ecx,[bsHidden]
+ ; Reserved for upper 32 bits of partition offset...
+ ; mov ebx,[bsHidden+4]
+ xor ebx,ebx
+ mov si,[bsHeads]
+ mov di,[bsSecPerTrack]
+ pm_call fs_init
+ popad
diff --git a/core/dnsresolv.inc b/core/dnsresolv.inc
deleted file mode 100644
index c2c429cb..00000000
--- a/core/dnsresolv.inc
+++ /dev/null
@@ -1,389 +0,0 @@
-; -*- fundamental -*-
-; -----------------------------------------------------------------------
-;
-; Copyright 2004-2008 H. Peter Anvin - All Rights Reserved
-;
-; This program is free software; you can redistribute it and/or modify
-; it under the terms of the GNU General Public License as published by
-; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
-; Bostom MA 02111-1307, USA; either version 2 of the License, or
-; (at your option) any later version; incorporated herein by reference.
-;
-; -----------------------------------------------------------------------
-
-;
-; dnsresolv.inc
-;
-; Very simple DNS resolver (assumes recursion-enabled DNS server;
-; this should be the normal thing for client-serving DNS servers.)
-;
-
-DNS_PORT equ htons(53) ; Default DNS port
-DNS_MAX_PACKET equ 512 ; Defined by protocol
-; TFTP uses the range 49152-57343
-DNS_LOCAL_PORT equ htons(60053) ; All local DNS queries come from this port #
-DNS_MAX_SERVERS equ 4 ; Max no of DNS servers
-
- section .text
-
-;
-; Turn a string in DS:SI into a DNS "label set" in ES:DI.
-; On return, DI points to the first byte after the label set,
-; and SI to the terminating byte.
-;
-; On return, DX contains the number of dots encountered.
-;
-dns_mangle:
- push ax
- push bx
- xor dx,dx
-.isdot:
- inc dx
- xor al,al
- mov bx,di
- stosb
-.getbyte:
- lodsb
- and al,al
- jz .endstring
- cmp al,':'
- jz .endstring
- cmp al,'.'
- je .isdot
- inc byte [es:bx]
- stosb
- jmp .getbyte
-.endstring:
- dec si
- dec dx ; We always counted one high
- cmp byte [es:bx],0
- jz .done
- xor al,al
- stosb
-.done:
- pop bx
- pop ax
- ret
-
-;
-; Compare two sets of DNS labels, in DS:SI and ES:DI; the one in SI
-; is allowed pointers relative to a packet in DNSRecvBuf.
-;
-; Assumes DS == ES. ZF = 1 if same; no registers changed.
-; (Note: change reference to [di] to [es:di] to remove DS == ES assumption)
-;
-dns_compare:
- pusha
-%if 0
-
-.label:
- lodsb
- cmp al,0C0h
- jb .noptr
- and al,03Fh ; Get pointer value
- mov ah,al ; ... in network byte order!
- lodsb
- mov si,DNSRecvBuf
- add si,ax
- jmp .label
-.noptr:
- cmp al,[di]
- jne .done ; Mismatch
- inc di
- movzx cx,al ; End label?
- and cx,cx ; ZF = 1 if match
- jz .done
-
- ; We have a string of bytes that need to match now
- repe cmpsb
- je .label
-
-.done:
-%else
- xor ax,ax
-%endif
- popa
- ret
-
-;
-; Skip past a DNS label set in DS:SI.
-;
-dns_skiplabel:
- push ax
- xor ax,ax ; AH == 0
-.loop:
- lodsb
- cmp al,0C0h ; Pointer?
- jae .ptr
- and al,al
- jz .done
- add si,ax
- jmp .loop
-.ptr:
- inc si ; Pointer is two bytes
-.done:
- pop ax
- ret
-
- ; DNS header format
- struc dnshdr
-.id: resw 1
-.flags: resw 1
-.qdcount: resw 1
-.ancount: resw 1
-.nscount: resw 1
-.arcount: resw 1
- endstruc
-
- ; DNS query
- struc dnsquery
-.qtype: resw 1
-.qclass: resw 1
- endstruc
-
- ; DNS RR
- struc dnsrr
-.type: resw 1
-.class: resw 1
-.ttl: resd 1
-.rdlength: resw 1
-.rdata: equ $
- endstruc
-
- section .bss2
- alignb 2
-DNSSendBuf resb DNS_MAX_PACKET
-DNSRecvBuf resb DNS_MAX_PACKET
-LocalDomain resb 256 ; Max possible length
-DNSServers resd DNS_MAX_SERVERS
-
- section .data
-pxe_udp_write_pkt_dns:
-.status: dw 0 ; Status
-.sip: dd 0 ; Server IP
-.gip: dd 0 ; Gateway IP
-.lport: dw DNS_LOCAL_PORT ; Local port
-.rport: dw DNS_PORT ; Remote port
-.buffersize: dw 0 ; Size of packet
-.buffer: dw DNSSendBuf, 0 ; off, seg of buffer
-
-pxe_udp_read_pkt_dns:
-.status: dw 0 ; Status
-.sip: dd 0 ; Source IP
-.dip: dd 0 ; Destination (our) IP
-.rport: dw DNS_PORT ; Remote port
-.lport: dw DNS_LOCAL_PORT ; Local port
-.buffersize: dw DNS_MAX_PACKET ; Max packet size
-.buffer: dw DNSRecvBuf, 0 ; off, seg of buffer
-
-LastDNSServer dw DNSServers
-
-; Actual resolver function
-; Points to a null-terminated or :-terminated string in DS:SI
-; and returns the name in EAX if it exists and can be found.
-; If EAX = 0 on exit, the lookup failed.
-;
-; No segment assumptions permitted.
-;
- section .text
-dns_resolv:
- push ds
- push es
- push di
- push bx
- push cx
- push dx
-
- push cs
- pop es ; ES <- CS
-
- ; First, build query packet
- mov di,DNSSendBuf+dnshdr.flags
- inc word [es:di-2] ; New query ID
- mov ax,htons(0100h) ; Recursion requested
- stosw
- mov ax,htons(1) ; One question
- stosw
- xor ax,ax ; No answers, NS or ARs
- stosw
- stosw
- stosw
-
- call dns_mangle ; Convert name to DNS labels
-
- push cs ; DS <- CS
- pop ds
-
- push si ; Save pointer to after DNS string
-
- ; Initialize...
- mov eax,[MyIP]
- mov [pxe_udp_read_pkt_dns.dip],eax
-
- and dx,dx
- jnz .fqdn ; If we have dots, assume it's FQDN
- dec di ; Remove final null
- mov si,LocalDomain
- call strcpy ; Uncompressed DNS label set so it ends in null
-.fqdn:
-
- mov ax,htons(1)
- stosw ; QTYPE = 1 = A
- stosw ; QCLASS = 1 = IN
-
- sub di,DNSSendBuf
- mov [pxe_udp_write_pkt_dns.buffersize],di
-
- ; Now, send it to the nameserver(s)
- ; Outer loop: exponential backoff
- ; Inner loop: scan the various DNS servers
-
- mov bx,TimeoutTable
-.backoff:
- movzx dx,byte [bx]
- mov si,DNSServers
-.servers:
- cmp si,[LastDNSServer]
- jb .moreservers
-
-.nomoreservers:
- inc bx
- cmp bx,TimeoutTableEnd
- jb .backoff
-
- xor eax,eax ; Nothing...
-.done:
- pop si
- pop dx
- pop cx
- pop bx
- pop di
- pop es
- pop ds
- ret
-
-.moreservers:
- lodsd ; EAX <- next server
- push si
- push bx
- push cx
- push dx
-
- mov word [pxe_udp_write_pkt_dns.status],0
-
- mov [pxe_udp_write_pkt_dns.sip],eax
- mov [pxe_udp_read_pkt_dns.sip],eax
- xor eax,[MyIP]
- and eax,[Netmask]
- jz .nogw
- mov eax,[Gateway]
-.nogw:
- mov [pxe_udp_write_pkt_dns.gip],eax
-
- mov di,pxe_udp_write_pkt_dns
- mov bx,PXENV_UDP_WRITE
- call pxenv
- jc .timeout ; Treat failed transmit as timeout
- cmp word [pxe_udp_write_pkt_dns.status],0
- jne .timeout
-
- mov cx,[BIOS_timer]
-.waitrecv:
- mov ax,[BIOS_timer]
- sub ax,cx
- cmp ax,dx
- jae .timeout
-
- mov word [pxe_udp_read_pkt_dns.status],0
- mov word [pxe_udp_read_pkt_dns.buffersize],DNS_MAX_PACKET
- mov di,pxe_udp_read_pkt_dns
- mov bx,PXENV_UDP_READ
- call pxenv
- and ax,ax
- jnz .waitrecv
- cmp [pxe_udp_read_pkt_dns.status],ax
- jnz .waitrecv
-
- ; Got a packet, deal with it...
- mov si,DNSRecvBuf
- lodsw
- cmp ax,[DNSSendBuf] ; ID
- jne .waitrecv ; Not ours
-
- lodsw ; flags
- xor al,80h ; Query#/Answer bit
- test ax,htons(0F80Fh)
- jnz .badness
-
- lodsw
- xchg ah,al ; ntohs
- mov cx,ax ; Questions echoed
- lodsw
- xchg ah,al ; ntohs
- push ax ; Replies
- lodsw ; NS records
- lodsw ; Authority records
-
- jcxz .qskipped
-.skipq:
- call dns_skiplabel ; Skip name
- add si,4 ; Skip question trailer
- loop .skipq
-
-.qskipped:
- pop cx ; Number of replies
- jcxz .badness
-
-.parseanswer:
- mov di,DNSSendBuf+dnshdr_size
- call dns_compare
- pushf
- call dns_skiplabel
- mov ax,[si+8] ; RDLENGTH
- xchg ah,al ; ntohs
- popf
- jnz .notsame
- cmp dword [si],htons(1)*0x10001 ; TYPE = A, CLASS = IN?
- jne .notsame
- cmp ax,4 ; RDLENGTH = 4?
- jne .notsame
- ;
- ; We hit paydirt here...
- ;
- mov eax,[si+10]
-.gotresult:
- add sp,8 ; Drop timeout information
- jmp .done
-
-.notsame:
- add si,10
- add si,ax
- loop .parseanswer
-
-.badness:
- ; We got back no data from this server.
- ; Unfortunately, for a recursive, non-authoritative
- ; query there is no such thing as an NXDOMAIN reply,
- ; which technically means we can't draw any
- ; conclusions. However, in practice that means the
- ; domain doesn't exist. If this turns out to be a
- ; problem, we may want to add code to go through all
- ; the servers before giving up.
-
- ; If the DNS server wasn't capable of recursion, and
- ; isn't capable of giving us an authoritative reply
- ; (i.e. neither AA or RA set), then at least try a
- ; different setver...
-
- test word [DNSRecvBuf+dnshdr.flags],htons(0480h)
- jz .timeout
-
- xor eax,eax
- jmp .gotresult
-
-.timeout:
- pop dx
- pop cx
- pop bx
- pop si
- jmp .servers
diff --git a/core/ext2_fs.inc b/core/ext2_fs.inc
deleted file mode 100644
index e84efb14..00000000
--- a/core/ext2_fs.inc
+++ /dev/null
@@ -1,183 +0,0 @@
-; -----------------------------------------------------------------------
-;
-; Copyright 1998-2008 H. Peter Anvin - All Rights Reserved
-;
-; This program is free software; you can redistribute it and/or modify
-; it under the terms of the GNU General Public License as published by
-; the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
-; USA; either version 2 of the License, or (at your option) any later
-; version; incorporated herein by reference.
-;
-; -----------------------------------------------------------------------
-
-;
-; ext2_fs.inc
-;
-; NASM include file for ext2fs data structures
-;
-
-%define EXT2_SUPER_MAGIC 0xEF53
-
-%define EXT2_GOOD_OLD_REV 0 ; The good old (original) format
-%define EXT2_DYNAMIC_REV 1 ; V2 format w/ dynamic inode sizes
-%define EXT2_GOOD_OLD_INODE_SIZE 128
-
-; Special inode numbers
-%define EXT2_BAD_INO 1 ; Bad blocks inode
-%define EXT2_ROOT_INO 2 ; Root inode
-%define EXT2_BOOT_LOADER_INO 5 ; Boot loader inode
-%define EXT2_UNDEL_DIR_INO 6 ; Undelete directory inode
-%define EXT3_RESIZE_INO 7 ; Reserved group descriptors inode
-%define EXT3_JOURNAL_INO 8 ; Journal inode
-
-; We're readonly, so we only care about incompat features.
-%define EXT2_FEATURE_INCOMPAT_COMPRESSION 0x0001
-%define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002
-%define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004
-%define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008
-%define EXT2_FEATURE_INCOMPAT_META_BG 0x0010
-%define EXT2_FEATURE_INCOMPAT_ANY 0xffffffff
-
-%define EXT2_NDIR_BLOCKS 12
-%define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS
-%define EXT2_DIND_BLOCK (EXT2_IND_BLOCK+1)
-%define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK+1)
-%define EXT2_N_BLOCKS (EXT2_TIND_BLOCK+1)
-
-;
-; File types and file modes
-;
-%define S_IFDIR 0040000o ; Directory
-%define S_IFCHR 0020000o ; Character device
-%define S_IFBLK 0060000o ; Block device
-%define S_IFREG 0100000o ; Regular file
-%define S_IFIFO 0010000o ; FIFO
-%define S_IFLNK 0120000o ; Symbolic link
-%define S_IFSOCK 0140000o ; Socket
-
-%define S_IFSHIFT 12
-
-%define T_IFDIR (S_IFDIR >> S_IFSHIFT)
-%define T_IFCHR (S_IFCHR >> S_IFSHIFT)
-%define T_IFBLK (S_IFBLK >> S_IFSHIFT)
-%define T_IFREG (S_IFREG >> S_IFSHIFT)
-%define T_IFIFO (S_IFIFO >> S_IFSHIFT)
-%define T_IFLNK (S_IFLNK >> S_IFSHIFT)
-%define T_IFSOCK (S_IFSOCK >> S_IFSHIFT)
-
-;
-; Structure definition for the ext2 superblock
-;
- struc ext2_super_block
-s_inodes_count resd 1 ; Inodes count
-s_blocks_count resd 1 ; Blocks count
-s_r_blocks_count resd 1 ; Reserved blocks count
-s_free_blocks_count resd 1 ; Free blocks count
-s_free_inodes_count resd 1 ; Free inodes count
-s_first_data_block resd 1 ; First Data Block
-s_log_block_size resd 1 ; Block size
-s_log_frag_size resd 1 ; Fragment size
-s_blocks_per_group resd 1 ; # Blocks per group
-s_frags_per_group resd 1 ; # Fragments per group
-s_inodes_per_group resd 1 ; # Inodes per group
-s_mtime resd 1 ; Mount time
-s_wtime resd 1 ; Write time
-s_mnt_count resw 1 ; Mount count
-s_max_mnt_count resw 1 ; Maximal mount count
-s_magic resw 1 ; Magic signature
-s_state resw 1 ; File system state
-s_errors resw 1 ; Behaviour when detecting errors
-s_minor_rev_level resw 1 ; minor revision level
-s_lastcheck resd 1 ; time of last check
-s_checkinterval resd 1 ; max. time between checks
-s_creator_os resd 1 ; OS
-s_rev_level resd 1 ; Revision level
-s_def_resuid resw 1 ; Default uid for reserved blocks
-s_def_resgid resw 1 ; Default gid for reserved blocks
-s_first_ino resd 1 ; First non-reserved inode
-s_inode_size resw 1 ; size of inode structure
-s_block_group_nr resw 1 ; block group # of this superblock
-s_feature_compat resd 1 ; compatible feature set
-s_feature_incompat resd 1 ; incompatible feature set
-s_feature_ro_compat resd 1 ; readonly-compatible feature set
-s_uuid resb 16 ; 128-bit uuid for volume
-s_volume_name resb 16 ; volume name
-s_last_mounted resb 64 ; directory where last mounted
-s_algorithm_usage_bitmap resd 1 ; For compression
-s_prealloc_blocks resb 1 ; Nr of blocks to try to preallocate
-s_prealloc_dir_blocks resb 1 ; Nr to preallocate for dirs
-s_padding1 resw 1
-s_reserved resd 204 ; Padding to the end of the block
- endstruc
-
-%ifndef DEPEND
-%if ext2_super_block_size != 1024
-%error "ext2_super_block definition bogus"
-%endif
-%endif
-
-;
-; Structure definition for the ext2 inode
-;
- struc ext2_inode
-i_mode resw 1 ; File mode
-i_uid resw 1 ; Owner Uid
-i_size resd 1 ; Size in bytes
-i_atime resd 1 ; Access time
-i_ctime resd 1 ; Creation time
-i_mtime resd 1 ; Modification time
-i_dtime resd 1 ; Deletion Time
-i_gid resw 1 ; Group Id
-i_links_count resw 1 ; Links count
-i_blocks resd 1 ; Blocks count
-i_flags resd 1 ; File flags
-l_i_reserved1 resd 1
-i_block resd EXT2_N_BLOCKS ; Pointer to blocks
-i_version resd 1 ; File version (for NFS)
-i_file_acl resd 1 ; File ACL
-i_dir_acl resd 1 ; Directory ACL
-i_faddr resd 1 ; Fragment address
-l_i_frag resb 1 ; Fragment number
-l_i_fsize resb 1 ; Fragment size
-i_pad1 resw 1
-l_i_reserved2 resd 2
- endstruc
-
-%ifndef DEPEND
-%if ext2_inode_size != 128
-%error "ext2_inode definition bogus"
-%endif
-%endif
-
-;
-; Structure definition for ext2 block group descriptor
-;
- struc ext2_group_desc
-bg_block_bitmap resd 1 ; Block bitmap block
-bg_inode_bitmap resd 1 ; Inode bitmap block
-bg_inode_table resd 1 ; Inode table block
-bg_free_blocks_count resw 1 ; Free blocks count
-bg_free_inodes_count resw 1 ; Free inodes count
-bg_used_dirs_count resw 1 ; Used inodes count
-bg_pad resw 1
-bg_reserved resd 3
- endstruc
-
-%ifndef DEPEND
-%if ext2_group_desc_size != 32
-%error "ext2_group_desc definition bogus"
-%endif
-%endif
-
-%define ext2_group_desc_lg2size 5
-
-;
-; Structure definition for ext2 directory entry
-;
- struc ext2_dir_entry
-d_inode resd 1 ; Inode number
-d_rec_len resw 1 ; Directory entry length
-d_name_len resb 1 ; Name length
-d_file_type resb 1 ; File type
-d_name equ $
- endstruc
diff --git a/core/extern.inc b/core/extern.inc
new file mode 100644
index 00000000..6110db0b
--- /dev/null
+++ b/core/extern.inc
@@ -0,0 +1,30 @@
+;
+; extern.inc
+;
+; Prototypes for external functions
+
+%ifndef EXTERN_INC
+%define EXTERN_INC
+
+ ; rllpack.c
+ extern rllpack, rllunpack
+
+ ; fs.c
+ extern fs_init, pm_searchdir, getfssec, pm_mangle_name, load_config
+ extern pm_open_file, pm_close_file
+
+ ; chdir.c
+ extern pm_realpath
+
+ ; readdir.c
+ extern opendir, readdir, closedir
+
+ ; newconfig.c
+ extern pm_is_config_file
+
+%if IS_PXELINUX
+ ; pxe.c
+ extern unload_pxe, reset_pxe
+%endif
+
+%endif ; EXTERN_INC
diff --git a/core/extlinux.asm b/core/extlinux.asm
index ac5fb6f0..95385765 100644
--- a/core/extlinux.asm
+++ b/core/extlinux.asm
@@ -18,914 +18,21 @@
%define IS_EXTLINUX 1
%include "head.inc"
-%include "ext2_fs.inc"
;
; Some semi-configurable constants... change on your own risk.
;
my_id equ extlinux_id
-; NASM 0.98.38 croaks if these are equ's rather than macros...
-FILENAME_MAX_LG2 equ 8 ; log2(Max filename size Including final null)
-FILENAME_MAX equ (1 << FILENAME_MAX_LG2) ; Max mangled filename size
-NULLFILE equ 0 ; Null character == empty filename
-NULLOFFSET equ 0 ; Position in which to look
-retry_count equ 16 ; How patient are we with the disk?
-%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)
-
-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
-
-;
-; The following structure is used for "virtual kernels"; i.e. LILO-style
-; option labels. The options we permit here are `kernel' and `append
-; Since there is no room in the bottom 64K for all of these, we
-; stick them in high memory and copy them down before we need them.
-;
- struc vkernel
-vk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!**
-vk_rname: resb FILENAME_MAX ; Real name
-vk_appendlen: resw 1
-vk_type: resb 1 ; Type of file
- alignb 4
-vk_append: resb max_cmd_len+1 ; Command line
- alignb 4
-vk_end: equ $ ; Should be <= vk_size
- endstruc
-
-;
-; File structure. This holds the information for each currently open file.
-;
- struc open_file_t
-file_bytesleft resd 1 ; Number of bytes left (0 = free)
-file_sector resd 1 ; Next linear sector to read
-file_in_sec resd 1 ; Sector where inode lives
-file_in_off resw 1
-file_mode resw 1
- endstruc
-
-%ifndef DEPEND
-%if (open_file_t_size & (open_file_t_size-1))
-%error "open_file_t is not a power of 2"
-%endif
-%endif
-
-; ---------------------------------------------------------------------------
-; BEGIN CODE
-; ---------------------------------------------------------------------------
-
-;
-; Memory below this point is reserved for the BIOS and the MBR
-;
- section .earlybss
-trackbufsize equ 8192
-trackbuf resb trackbufsize ; Track buffer goes here
- ; ends at 2800h
-
- section .bss
-SuperBlock resb 1024 ; ext2 superblock
-ClustSize resd 1 ; Bytes/cluster ("block")
-ClustMask resd 1 ; Sectors/cluster - 1
-PtrsPerBlock1 resd 1 ; Pointers/cluster
-PtrsPerBlock2 resd 1 ; (Pointers/cluster)^2
-DriveNumber resb 1 ; BIOS drive number
-ClustShift resb 1 ; Shift count for sectors/cluster
-ClustByteShift resb 1 ; Shift count for bytes/cluster
-
- alignb open_file_t_size
-Files resb MAX_OPEN*open_file_t_size
-
-;
-; Common bootstrap code for disk-based derivatives
-;
-%include "diskstart.inc"
-
-;
-; Load the real (ext2) superblock; 1024 bytes long at offset 1024
-;
- mov bx,SuperBlock
- mov eax,1024 >> SECTOR_SHIFT
- mov bp,ax
- call getlinsecsr
-
-;
-; Compute some values...
-;
- xor edx,edx
- inc edx
-
- ; s_log_block_size = log2(blocksize) - 10
- mov cl,[SuperBlock+s_log_block_size]
- add cl,10
- mov [ClustByteShift],cl
- mov eax,edx
- shl eax,cl
- mov [ClustSize],eax
-
- sub cl,SECTOR_SHIFT
- mov [ClustShift],cl
- shr eax,SECTOR_SHIFT
- mov [SecPerClust],eax
- dec eax
- mov [ClustMask],eax
-
- add cl,SECTOR_SHIFT-2 ; 4 bytes/pointer
- shl edx,cl
- mov [PtrsPerBlock1],edx
- shl edx,cl
- mov [PtrsPerBlock2],edx
-
-;
-; Common initialization code
-;
-%include "init.inc"
-%include "cpuinit.inc"
-
-;
-; Initialize the metadata cache
-;
- call initcache
-
-;
-; Now, everything is "up and running"... patch kaboom for more
-; verbosity and using the full screen system
-;
- ; E9 = JMP NEAR
- mov dword [kaboom.patch],0e9h+((kaboom2-(kaboom.patch+3)) << 8)
-
-;
-; Now we're all set to start with our *real* business. First load the
-; configuration file (if any) and parse it.
-;
-; In previous versions I avoided using 32-bit registers because of a
-; rumour some BIOSes clobbered the upper half of 32-bit registers at
-; random. I figure, though, that if there are any of those still left
-; they probably won't be trying to install Linux on them...
-;
-; The code is still ripe with 16-bitisms, though. Not worth the hassle
-; to take'm out. In fact, we may want to put them back if we're going
-; to boot ELKS at some point.
-;
-
-;
-; Load configuration file
-;
-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
- jz no_config_file
-
-;
-; Now we have the config file open. Parse the config file and
-; run the user interface.
-;
-%include "ui.inc"
-
-;
-; getlinsec_ext: same as getlinsec, except load any sector from the zero
-; block as all zeros; use to load any data derived
-; from an ext2 block pointer, i.e. anything *except the
-; superblock.*
-;
-getonesec_ext:
- mov bp,1
-
-getlinsec_ext:
- cmp eax,[SecPerClust]
- jae getlinsecsr ; Nothing fancy
-
- ; If we get here, at least part of what we want is in the
- ; zero block. Zero one sector at a time and loop.
- push eax
- push cx
- xchg di,bx
- xor eax,eax
- mov cx,SECTOR_SIZE >> 2
- rep stosd
- xchg di,bx
- pop cx
- pop eax
- inc eax
- dec bp
- jnz getlinsec_ext
- ret
-
-;
-; allocate_file: Allocate a file structure
-;
-; If successful:
-; ZF set
-; BX = file pointer
-; In unsuccessful:
-; ZF clear
-;
-allocate_file:
- TRACER 'a'
- push cx
- mov bx,Files
- mov cx,MAX_OPEN
-.check: cmp dword [bx], byte 0
- je .found
- add bx,open_file_t_size ; ZF = 0
- loop .check
- ; ZF = 0 if we fell out of the loop
-.found: pop cx
- ret
-;
-; open_inode:
-; Open a file indicated by an inode number in EAX
-;
-; NOTE: This file considers finding a zero-length file an
-; error. This is so we don't have to deal with that special
-; case elsewhere in the program (most loops have the test
-; at the end).
-;
-; If successful:
-; ZF clear
-; SI = file pointer
-; EAX = file length in bytes
-; ThisInode = the first 128 bytes of the inode
-; If unsuccessful
-; ZF set
-;
-; Assumes CS == DS == ES.
-;
-open_inode.allocate_failure:
- xor eax,eax
- pop bx
- pop di
- ret
-
-open_inode:
- push di
- push bx
- call allocate_file
- jnz .allocate_failure
-
- push cx
- push gs
- ; First, get the appropriate inode group and index
- dec eax ; There is no inode 0
- xor edx,edx
- mov [bx+file_sector],edx
- div dword [SuperBlock+s_inodes_per_group]
- ; EAX = inode group; EDX = inode within group
- push edx
-
- ; Now, we need the block group descriptor.
- ; To get that, we first need the relevant descriptor block.
-
- shl eax, ext2_group_desc_lg2size ; Get byte offset in desc table
- xor edx,edx
- div dword [ClustSize]
- ; eax = block #, edx = offset in block
- add eax,dword [SuperBlock+s_first_data_block]
- inc eax ; s_first_data_block+1
- mov cl,[ClustShift]
- shl eax,cl
- push edx
- shr edx,SECTOR_SHIFT
- add eax,edx
- pop edx
- and dx,SECTOR_SIZE-1
- call getcachesector ; Get the group descriptor
- add si,dx
- mov esi,[gs:si+bg_inode_table] ; Get inode table block #
- pop eax ; Get inode within group
- movzx edx, word [SuperBlock+s_inode_size]
- mul edx
- ; edx:eax = byte offset in inode table
- div dword [ClustSize]
- ; eax = block # versus inode table, edx = offset in block
- add eax,esi
- shl eax,cl ; Turn into sector
- push dx
- shr edx,SECTOR_SHIFT
- add eax,edx
- mov [bx+file_in_sec],eax
- pop dx
- and dx,SECTOR_SIZE-1
- mov [bx+file_in_off],dx
-
- call getcachesector
- add si,dx
- mov cx,EXT2_GOOD_OLD_INODE_SIZE >> 2
- mov di,ThisInode
- gs rep movsd
-
- mov ax,[ThisInode+i_mode]
- mov [bx+file_mode],ax
- mov eax,[ThisInode+i_size]
- mov [bx+file_bytesleft],eax
- mov si,bx
- and eax,eax ; ZF clear unless zero-length file
- pop gs
- pop cx
- pop bx
- pop di
- ret
-
- section .bss
- alignb 4
-ThisInode resb EXT2_GOOD_OLD_INODE_SIZE ; The most recently opened inode
-
- section .text
-;
-; 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
-
-;
-; searchdir:
-; Search the root directory for a pre-mangled filename in DS:DI.
-;
-; NOTE: This file considers finding a zero-length file an
-; error. This is so we don't have to deal with that special
-; case elsewhere in the program (most loops have the test
-; at the end).
-;
-; If successful:
-; ZF clear
-; SI = file pointer
-; DX:AX = EAX = file length in bytes
-; If unsuccessful
-; ZF set
-;
-; Assumes CS == DS == ES; *** IS THIS CORRECT ***?
-;
-searchdir:
- push bx
- push cx
- push bp
- mov byte [SymlinkCtr],MAX_SYMLINKS
-
- mov eax,[CurrentDir]
-.begin_path:
-.leadingslash:
- cmp byte [di],'/' ; Absolute filename?
- jne .gotdir
- mov eax,EXT2_ROOT_INO
- inc di ; Skip slash
- jmp .leadingslash
-.gotdir:
-
- ; At this point, EAX contains the directory inode,
- ; and DS:DI contains a pathname tail.
-.open:
- push eax ; Save directory inode
-
- call open_inode
- jz .missing ; If error, done
-
- mov cx,[si+file_mode]
- shr cx,S_IFSHIFT ; Get file type
-
- cmp cx,T_IFDIR
- je .directory
-
- add sp,4 ; Drop directory inode
-
- cmp cx,T_IFREG
- je .file
- cmp cx,T_IFLNK
- je .symlink
-
- ; Otherwise, something bad...
-.err:
- call close_file
-.err_noclose:
- xor eax,eax
- xor si,si
- cwd ; DX <- 0
-
-.done:
- and eax,eax ; Set/clear ZF
- pop bp
- pop cx
- pop bx
- ret
-
-.missing:
- add sp,4 ; Drop directory inode
- jmp .done
-
- ;
- ; It's a file.
- ;
-.file:
- cmp byte [di],0 ; End of path?
- je .done ; If so, done
- jmp .err ; Otherwise, error
-
- ;
- ; It's a directory.
- ;
-.directory:
- pop dword [ThisDir] ; Remember what directory we're searching
-
- cmp byte [di],0 ; More path?
- je .err ; If not, bad
-
-.skipslash: ; Skip redundant slashes
- cmp byte [di],'/'
- jne .readdir
- inc di
- jmp .skipslash
-
-.readdir:
- mov cx,[SecPerClust]
- push cx
- shl cx,SECTOR_SHIFT
- mov bx,trackbuf
- add cx,bx
- mov [EndBlock],cx
- pop cx
- push bx
- call getfssec
- pop bx
- pushf ; Save EOF flag
- push si ; Save filesystem pointer
-.getent:
- cmp bx,[EndBlock]
- jae .endblock
-
- push di
- cmp dword [bx+d_inode],0 ; Zero inode = void entry
- je .nope
-
- movzx cx,byte [bx+d_name_len]
- lea si,[bx+d_name]
- repe cmpsb
- je .maybe
-.nope:
- pop di
- add bx,[bx+d_rec_len]
- jmp .getent
-
-.endblock:
- pop si
- popf
- jnc .readdir ; There is more
- jmp .err ; Otherwise badness...
-
-.maybe:
- mov eax,[bx+d_inode]
-
- ; Does this match the end of the requested filename?
- cmp byte [di],0
- je .finish
- cmp byte [di],'/'
- jne .nope
-
- ; We found something; now we need to open the file
-.finish:
- pop bx ; Adjust stack (di)
- pop si
- call close_file ; Close directory
- pop bx ; Adjust stack (flags)
- jmp .open
-
- ;
- ; It's a symlink. We have to determine if it's a fast symlink
- ; (data stored in the inode) or not (data stored as a regular
- ; file.) Either which way, we start from the directory
- ; which we just visited if relative, or from the root directory
- ; if absolute, and append any remaining part of the path.
- ;
-.symlink:
- dec byte [SymlinkCtr]
- jz .err ; Too many symlink references
-
- cmp eax,SYMLINK_SECTORS*SECTOR_SIZE
- jae .err ; Symlink too long
-
- ; Computation for fast symlink, as defined by ext2/3 spec
- xor ecx,ecx
- cmp [ThisInode+i_file_acl],ecx
- setne cl ; ECX <- i_file_acl ? 1 : 0
- cmp [ThisInode+i_blocks],ecx
- jne .slow_symlink
-
- ; It's a fast symlink
-.fast_symlink:
- call close_file ; We've got all we need
- mov si,ThisInode+i_block
-
- push di
- mov di,SymlinkTmpBuf
- mov ecx,eax
- rep movsb
- pop si
-
-.symlink_finish:
- cmp byte [si],0
- je .no_slash
- mov al,'/'
- stosb
-.no_slash:
- mov bp,SymlinkTmpBufEnd
- call strecpy
- jc .err_noclose ; Buffer overflow
-
- ; Now copy it to the "real" buffer; we need to have
- ; two buffers so we avoid overwriting the tail on the
- ; next copy
- mov si,SymlinkTmpBuf
- mov di,SymlinkBuf
- push di
- call strcpy
- pop di
- mov eax,[ThisDir] ; Resume searching previous directory
- jmp .begin_path
-
-.slow_symlink:
- mov bx,SymlinkTmpBuf
- mov cx,SYMLINK_SECTORS
- call getfssec
- ; The EOF closed the file
-
- mov si,di ; SI = filename tail
- mov di,SymlinkTmpBuf
- add di,ax ; AX = file length
- jmp .symlink_finish
-
-
- section .bss
- alignb 4
-SymlinkBuf resb SYMLINK_SECTORS*SECTOR_SIZE+64
-SymlinkTmpBuf equ trackbuf
-SymlinkTmpBufEnd equ trackbuf+SYMLINK_SECTORS*SECTOR_SIZE+64
-ThisDir resd 1
-EndBlock resw 1
-SymlinkCtr resb 1
-
- section .text
-;
-; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed
-; to by ES:DI; ends on encountering any whitespace.
-; DI is preserved.
-;
-; This verifies that a filename is < FILENAME_MAX characters,
-; doesn't contain whitespace, zero-pads the output buffer,
-; and removes redundant slashes,
-; so "repe cmpsb" can do a compare, and the
-; path-searching routine gets a bit of an easier job.
-;
-; FIX: we may want to support \-escapes here (and this would
-; be the place.)
-;
-mangle_name:
- push di
- push bx
- xor ax,ax
- mov cx,FILENAME_MAX-1
- mov bx,di
-
-.mn_loop:
- lodsb
- cmp al,' ' ; If control or space, end
- jna .mn_end
- cmp al,ah ; Repeated slash?
- je .mn_skip
- xor ah,ah
- cmp al,'/'
- jne .mn_ok
- mov ah,al
-.mn_ok stosb
-.mn_skip: loop .mn_loop
-.mn_end:
- cmp bx,di ; At the beginning of the buffer?
- jbe .mn_zero
- cmp byte [di-1],'/' ; Terminal slash?
- jne .mn_zero
-.mn_kill: dec di ; If so, remove it
- inc cx
- jmp short .mn_end
-.mn_zero:
- inc cx ; At least one null byte
- xor ax,ax ; Zero-fill name
- rep stosb
- pop bx
- pop di
- ret ; Done
-
-;
-; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
-; filename to the conventional representation. This is needed
-; for the BOOT_IMAGE= parameter for the kernel.
-;
-; DS:SI -> input mangled file name
-; ES:DI -> output buffer
-;
-; On return, DI points to the first byte after the output name,
-; which is set to a null byte.
-;
-unmangle_name: call strcpy
- dec di ; Point to final null byte
- ret
-
-;
-;
-; kaboom2: once everything is loaded, replace the part of kaboom
-; starting with "kaboom.patch" with this part
-
-kaboom2:
- mov si,err_bootfailed
- call writestr
- cmp byte [kaboom.again+1],18h ; INT 18h version?
- je .int18
- call getchar
- call vgaclearmode
- int 19h ; And try once more to boot...
-.norge: jmp short .norge ; If int 19h returned; this is the end
-.int18:
- call vgaclearmode
- int 18h
-.noreg: jmp short .noreg ; Nynorsk
-
-
-;
-; linsector: Convert a linear sector index in a file to a linear sector number
-; EAX -> linear sector number
-; DS:SI -> open_file_t
-;
-; Returns next sector number in EAX; CF on EOF (not an error!)
-;
-linsector:
- push gs
- push ebx
- push esi
- push edi
- push ecx
- push edx
- push ebp
-
- push eax ; Save sector index
- mov cl,[ClustShift]
- shr eax,cl ; Convert to block number
- push eax
- mov eax,[si+file_in_sec]
- mov bx,si
- call getcachesector ; Get inode
- add si,[bx+file_in_off] ; Get *our* inode
- pop eax
- lea ebx,[i_block+4*eax]
- cmp eax,EXT2_NDIR_BLOCKS
- jb .direct
- mov ebx,i_block+4*EXT2_IND_BLOCK
- sub eax,EXT2_NDIR_BLOCKS
- mov ebp,[PtrsPerBlock1]
- cmp eax,ebp
- jb .ind1
- mov ebx,i_block+4*EXT2_DIND_BLOCK
- sub eax,ebp
- mov ebp,[PtrsPerBlock2]
- cmp eax,ebp
- jb .ind2
- mov ebx,i_block+4*EXT2_TIND_BLOCK
- sub eax,ebp
-
-.ind3:
- ; Triple indirect; eax contains the block no
- ; with respect to the start of the tind area;
- ; ebx contains the pointer to the tind block.
- xor edx,edx
- div dword [PtrsPerBlock2]
- ; EAX = which dind block, EDX = pointer within dind block
- push ax
- shr eax,SECTOR_SHIFT-2
- mov ebp,[gs:si+bx]
- shl ebp,cl
- add eax,ebp
- call getcachesector
- pop bx
- and bx,(SECTOR_SIZE >> 2)-1
- shl bx,2
- mov eax,edx ; The ind2 code wants the remainder...
-
-.ind2:
- ; Double indirect; eax contains the block no
- ; with respect to the start of the dind area;
- ; ebx contains the pointer to the dind block.
- xor edx,edx
- div dword [PtrsPerBlock1]
- ; EAX = which ind block, EDX = pointer within ind block
- push ax
- shr eax,SECTOR_SHIFT-2
- mov ebp,[gs:si+bx]
- shl ebp,cl
- add eax,ebp
- call getcachesector
- pop bx
- and bx,(SECTOR_SIZE >> 2)-1
- shl bx,2
- mov eax,edx ; The int1 code wants the remainder...
-
-.ind1:
- ; Single indirect; eax contains the block no
- ; with respect to the start of the ind area;
- ; ebx contains the pointer to the ind block.
- push ax
- shr eax,SECTOR_SHIFT-2
- mov ebp,[gs:si+bx]
- shl ebp,cl
- add eax,ebp
- call getcachesector
- pop bx
- and bx,(SECTOR_SIZE >> 2)-1
- shl bx,2
-
-.direct:
- mov ebx,[gs:bx+si] ; Get the pointer
-
- pop eax ; Get the sector index again
- shl ebx,cl ; Convert block number to sector
- and eax,[ClustMask] ; Add offset within block
- add eax,ebx
-
- pop ebp
- pop edx
- pop ecx
- pop edi
- pop esi
- pop ebx
- pop gs
- ret
-
-;
-; getfssec: Get multiple sectors from a file
-;
-; Same as above, except SI is a pointer to a open_file_t
-;
-; ES:BX -> Buffer
-; DS:SI -> Pointer to open_file_t
-; CX -> Sector count (0FFFFh = until end of file)
-; Must not exceed the ES segment
-; Returns CF=1 on EOF (not necessarily error)
-; On return ECX = number of bytes read
-; All arguments are advanced to reflect data read.
-;
-getfssec:
- push ebp
- push eax
- push edx
- push edi
-
- movzx ecx,cx
- push ecx ; Sectors requested read
- mov eax,[si+file_bytesleft]
- add eax,SECTOR_SIZE-1
- shr eax,SECTOR_SHIFT
- cmp ecx,eax ; Number of sectors left
- jbe .lenok
- mov cx,ax
-.lenok:
-.getfragment:
- mov eax,[si+file_sector] ; Current start index
- mov edi,eax
- call linsector
- push eax ; Fragment start sector
- mov edx,eax
- xor ebp,ebp ; Fragment sector count
-.getseccnt:
- inc bp
- dec cx
- jz .do_read
- xor eax,eax
- mov ax,es
- shl ax,4
- add ax,bx ; Now DI = how far into 64K block we are
- not ax ; Bytes left in 64K block
- inc eax
- shr eax,SECTOR_SHIFT ; Sectors left in 64K block
- cmp bp,ax
- jnb .do_read ; Unless there is at least 1 more sector room...
- inc edi ; Sector index
- inc edx ; Linearly next sector
- mov eax,edi
- call linsector
- ; jc .do_read
- cmp edx,eax
- je .getseccnt
-.do_read:
- pop eax ; Linear start sector
- pushad
- call getlinsec_ext
- popad
- push bp
- shl bp,9
- add bx,bp ; Adjust buffer pointer
- pop bp
- add [si+file_sector],ebp ; Next sector index
- jcxz .done
- jnz .getfragment
- ; Fall through
-.done:
- pop ecx ; Sectors requested read
- shl ecx,SECTOR_SHIFT
- sub [si+file_bytesleft],ecx
- jnbe .noteof ; CF=0 in this case
- add ecx,[si+file_bytesleft] ; Actual number of bytes read
- call close_file
- stc ; We hit EOF
-.noteof:
- pop edi
- pop edx
- pop eax
- pop ebp
- ret
-
-build_curdir_str:
- ret
-
-; -----------------------------------------------------------------------------
-; Common modules
-; -----------------------------------------------------------------------------
-
-%include "getc.inc" ; getc et al
-%include "conio.inc" ; Console I/O
-%include "plaincon.inc" ; writechr
-%include "writestr.inc" ; String output
-%include "writehex.inc" ; Hexadecimal output
-%include "configinit.inc" ; Initialize configuration
-%include "parseconfig.inc" ; High-level config file handling
-%include "parsecmd.inc" ; Low-level config file handling
-%include "bcopy32.inc" ; 32-bit bcopy
-%include "loadhigh.inc" ; Load a file into high memory
-%include "font.inc" ; VGA font stuff
-%include "graphics.inc" ; VGA graphics
-%include "highmem.inc" ; High memory sizing
-%include "strcpy.inc" ; strcpy()
-%include "strecpy.inc" ; strcpy with end pointer check
-%include "cache.inc" ; Metadata disk cache
-%include "idle.inc" ; Idle handling
-%include "adv.inc" ; Auxillary Data Vector
-%include "localboot.inc" ; Disk-based local boot
-
-; -----------------------------------------------------------------------------
-; Begin data section
-; -----------------------------------------------------------------------------
-
- section .data
-copyright_str db ' Copyright (C) 1994-'
- asciidec YEAR
- db ' H. Peter Anvin et al', CR, LF, 0
-err_bootfailed db CR, LF, 'Boot failed: please change disks and press '
- db 'a key to continue.', CR, LF, 0
-config_name db 'extlinux.conf',0 ; Unmangled form
-
-;
-; Config file keyword table
-;
-%include "keywords.inc"
-
-;
-; Extensions to search for (in *forward* order).
-;
- alignz 4
-exten_table: db '.cbt' ; COMBOOT (specific)
- db '.img' ; Disk image
- db '.bs', 0 ; Boot sector
- db '.com' ; COMBOOT (same as DOS)
- db '.c32' ; COM32
-exten_table_end:
- dd 0, 0 ; Need 8 null bytes here
-
-;
-; Misc initialized (data) variables
-;
-%ifdef debug ; This code for debugging only
-debug_magic dw 0D00Dh ; Debug code sentinel
-%endif
+ section .rodata
alignz 4
-BufSafe dw trackbufsize/SECTOR_SIZE ; Clusters we can load into trackbuf
-BufSafeBytes dw trackbufsize ; = how many bytes?
-%ifndef DEPEND
-%if ( trackbufsize % SECTOR_SIZE ) != 0
-%error trackbufsize must be a multiple of SECTOR_SIZE
-%endif
-%endif
+ROOT_FS_OPS:
+ extern vfat_fs_ops
+ dd vfat_fs_ops
+ extern ext2_fs_ops
+ dd ext2_fs_ops
+ extern btrfs_fs_ops
+ dd btrfs_fs_ops
+ dd 0
+
+%include "diskfs.inc"
diff --git a/core/font.inc b/core/font.inc
index 4090e5af..12236358 100644
--- a/core/font.inc
+++ b/core/font.inc
@@ -16,7 +16,7 @@
;; VGA font handling code
;;
- section .text
+ section .text16
;
; loadfont: Load a .psf font file and install it onto the VGA console
@@ -141,12 +141,12 @@ vidrows_ok: mov [VidRows],al
popa
ret
- section .data
+ section .data16
alignz 2
VGAFontSize dw 16 ; Defaults to 16 byte font
UserFont db 0 ; Using a user-specified font
- section .bss1
+ section .bss16
alignb 4
GXPixCols resw 1 ; Graphics mode pixel columns
GXPixRows resw 1 ; Graphics mode pixel rows
diff --git a/core/fs/btrfs/btrfs.c b/core/fs/btrfs/btrfs.c
new file mode 100644
index 00000000..72dcbe92
--- /dev/null
+++ b/core/fs/btrfs/btrfs.c
@@ -0,0 +1,696 @@
+/*
+ * btrfs.c -- readonly btrfs support for syslinux
+ * Some data structures are derivated from btrfs-tools-0.19 ctree.h
+ * Copyright 2009 Intel Corporation; author: alek.du@intel.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ * Boston MA 02111-1307, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <cache.h>
+#include <core.h>
+#include <disk.h>
+#include <fs.h>
+#include <dirent.h>
+#include "btrfs.h"
+
+/* compare function used for bin_search */
+typedef int (*cmp_func)(void *ptr1, void *ptr2);
+
+/* simple but useful bin search, used for chunk search and btree search */
+static int bin_search(void *ptr, int item_size, void *cmp_item, cmp_func func,
+ int min, int max, int *slot)
+{
+ int low = min;
+ int high = max;
+ int mid;
+ int ret;
+ unsigned long offset;
+ void *item;
+
+ while (low < high) {
+ mid = (low + high) / 2;
+ offset = mid * item_size;
+
+ item = ptr + offset;
+ ret = func(item, cmp_item);
+
+ if (ret < 0)
+ low = mid + 1;
+ else if (ret > 0)
+ high = mid;
+ else {
+ *slot = mid;
+ return 0;
+ }
+ }
+ *slot = low;
+ return 1;
+}
+
+static int cache_ready;
+static struct btrfs_chunk_map chunk_map;
+static struct btrfs_super_block sb;
+/* used for small chunk read for btrfs_read */
+#define RAW_BUF_SIZE 4096
+static u8 raw_buf[RAW_BUF_SIZE];
+static u64 fs_tree;
+
+static int btrfs_comp_chunk_map(struct btrfs_chunk_map_item *m1,
+ struct btrfs_chunk_map_item *m2)
+{
+ if (m1->logical > m2->logical)
+ return 1;
+ if (m1->logical < m2->logical)
+ return -1;
+ return 0;
+}
+
+/* insert a new chunk mapping item */
+static void insert_map(struct btrfs_chunk_map_item *item)
+{
+ int ret;
+ int slot;
+ int i;
+
+ if (chunk_map.map == NULL) { /* first item */
+ chunk_map.map_length = BTRFS_MAX_CHUNK_ENTRIES;
+ chunk_map.map = (struct btrfs_chunk_map_item *)
+ malloc(chunk_map.map_length * sizeof(*chunk_map.map));
+ chunk_map.map[0] = *item;
+ chunk_map.cur_length = 1;
+ return;
+ }
+ ret = bin_search(chunk_map.map, sizeof(*item), item,
+ (cmp_func)btrfs_comp_chunk_map, 0,
+ chunk_map.cur_length, &slot);
+ if (ret == 0)/* already in map */
+ return;
+ if (chunk_map.cur_length == BTRFS_MAX_CHUNK_ENTRIES) {
+ /* should be impossible */
+ printf("too many chunk items\n");
+ return;
+ }
+ for (i = chunk_map.cur_length; i > slot; i--)
+ chunk_map.map[i] = chunk_map.map[i-1];
+ chunk_map.map[slot] = *item;
+ chunk_map.cur_length++;
+}
+
+/*
+ * from sys_chunk_array or chunk_tree, we can convert a logical address to
+ * a physical address we can not support multi device case yet
+ */
+static u64 logical_physical(u64 logical)
+{
+ struct btrfs_chunk_map_item item;
+ int slot, ret;
+
+ item.logical = logical;
+ ret = bin_search(chunk_map.map, sizeof(*chunk_map.map), &item,
+ (cmp_func)btrfs_comp_chunk_map, 0,
+ chunk_map.cur_length, &slot);
+ if (ret == 0)
+ slot++;
+ else if (slot == 0)
+ return -1;
+ if (logical >=
+ chunk_map.map[slot-1].logical + chunk_map.map[slot-1].length)
+ return -1;
+ return chunk_map.map[slot-1].physical + logical -
+ chunk_map.map[slot-1].logical;
+}
+
+/* raw read from disk, offset and count are bytes */
+static int raw_read(struct fs_info *fs, char *buf, u64 offset, u64 count)
+{
+ struct disk *disk = fs->fs_dev->disk;
+ size_t max = RAW_BUF_SIZE >> disk->sector_shift;
+ size_t off, cnt, done, total;
+ sector_t sec;
+
+ total = count;
+ while (count > 0) {
+ sec = offset >> disk->sector_shift;
+ off = offset - (sec << disk->sector_shift);
+ done = disk->rdwr_sectors(disk, raw_buf, sec, max, 0);
+ if (done == 0)/* no data */
+ break;
+ cnt = (done << disk->sector_shift) - off;
+ if (cnt > count)
+ cnt = count;
+ memcpy(buf, raw_buf + off, cnt);
+ count -= cnt;
+ buf += cnt;
+ offset += cnt;
+ if (done != max)/* no enough sectors */
+ break;
+ }
+ return total - count;
+}
+
+/* cache read from disk, offset and count are bytes */
+static int cache_read(struct fs_info *fs, char *buf, u64 offset, u64 count)
+{
+ const char *cd;
+ size_t block_size = fs->fs_dev->cache_block_size;
+ size_t off, cnt, total;
+ block_t block;
+
+ total = count;
+ while (count > 0) {
+ block = offset / block_size;
+ off = offset % block_size;
+ cd = get_cache(fs->fs_dev, block);
+ if (!cd)
+ break;
+ cnt = block_size - off;
+ if (cnt > count)
+ cnt = count;
+ memcpy(buf, cd + off, cnt);
+ count -= cnt;
+ buf += cnt;
+ offset += cnt;
+ }
+ return total - count;
+}
+
+static int btrfs_read(struct fs_info *fs, char *buf, u64 offset, u64 count)
+{
+ if (cache_ready)
+ return cache_read(fs, buf, offset, count);
+ return raw_read(fs, buf, offset, count);
+}
+
+/* btrfs has several super block mirrors, need to calculate their location */
+static inline u64 btrfs_sb_offset(int mirror)
+{
+ u64 start = 16 * 1024;
+ if (mirror)
+ return start << (BTRFS_SUPER_MIRROR_SHIFT * mirror);
+ return BTRFS_SUPER_INFO_OFFSET;
+}
+
+/* find the most recent super block */
+static void btrfs_read_super_block(struct fs_info *fs)
+{
+ int i;
+ int ret;
+ u8 fsid[BTRFS_FSID_SIZE];
+ u64 offset;
+ u64 transid = 0;
+ struct btrfs_super_block buf;
+
+ /* find most recent super block */
+ for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
+ offset = btrfs_sb_offset(i);
+ ret = btrfs_read(fs, (char *)&buf, offset, sizeof(buf));
+ if (ret < sizeof(buf))
+ break;
+
+ if (buf.bytenr != offset ||
+ strncmp((char *)(&buf.magic), BTRFS_MAGIC,
+ sizeof(buf.magic)))
+ continue;
+
+ if (i == 0)
+ memcpy(fsid, buf.fsid, sizeof(fsid));
+ else if (memcmp(fsid, buf.fsid, sizeof(fsid)))
+ continue;
+
+ if (buf.generation > transid) {
+ memcpy(&sb, &buf, sizeof(sb));
+ transid = buf.generation;
+ }
+ }
+}
+
+static inline unsigned long btrfs_chunk_item_size(int num_stripes)
+{
+ return sizeof(struct btrfs_chunk) +
+ sizeof(struct btrfs_stripe) * (num_stripes - 1);
+}
+
+static void clear_path(struct btrfs_path *path)
+{
+ memset(path, 0, sizeof(*path));
+}
+
+static int btrfs_comp_keys(struct btrfs_disk_key *k1, struct btrfs_disk_key *k2)
+{
+ if (k1->objectid > k2->objectid)
+ return 1;
+ if (k1->objectid < k2->objectid)
+ return -1;
+ if (k1->type > k2->type)
+ return 1;
+ if (k1->type < k2->type)
+ return -1;
+ if (k1->offset > k2->offset)
+ return 1;
+ if (k1->offset < k2->offset)
+ return -1;
+ return 0;
+}
+
+/* compare keys but ignore offset, is useful to enumerate all same kind keys */
+static int btrfs_comp_keys_type(struct btrfs_disk_key *k1,
+ struct btrfs_disk_key *k2)
+{
+ if (k1->objectid > k2->objectid)
+ return 1;
+ if (k1->objectid < k2->objectid)
+ return -1;
+ if (k1->type > k2->type)
+ return 1;
+ if (k1->type < k2->type)
+ return -1;
+ return 0;
+}
+
+/* seach tree directly on disk ... */
+static int search_tree(struct fs_info *fs, u64 loffset,
+ struct btrfs_disk_key *key, struct btrfs_path *path)
+{
+ u8 buf[BTRFS_MAX_LEAF_SIZE];
+ struct btrfs_header *header = (struct btrfs_header *)buf;
+ struct btrfs_node *node = (struct btrfs_node *)buf;
+ struct btrfs_leaf *leaf = (struct btrfs_leaf *)buf;
+ int slot, ret;
+ u64 offset;
+
+ offset = logical_physical(loffset);
+ btrfs_read(fs, (char *)header, offset, sizeof(*header));
+ if (header->level) {/*node*/
+ btrfs_read(fs, (char *)&node->ptrs[0], offset + sizeof(*header),
+ sb.nodesize - sizeof(*header));
+ path->itemsnr[header->level] = header->nritems;
+ path->offsets[header->level] = loffset;
+ ret = bin_search(&node->ptrs[0], sizeof(struct btrfs_key_ptr),
+ key, (cmp_func)btrfs_comp_keys,
+ path->slots[header->level], header->nritems, &slot);
+ if (ret && slot > path->slots[header->level])
+ slot--;
+ path->slots[header->level] = slot;
+ ret = search_tree(fs, node->ptrs[slot].blockptr, key, path);
+ } else {/*leaf*/
+ btrfs_read(fs, (char *)&leaf->items, offset + sizeof(*header),
+ sb.leafsize - sizeof(*header));
+ path->itemsnr[header->level] = header->nritems;
+ path->offsets[0] = loffset;
+ ret = bin_search(&leaf->items[0], sizeof(struct btrfs_item),
+ key, (cmp_func)btrfs_comp_keys, path->slots[0],
+ header->nritems, &slot);
+ if (ret && slot > path->slots[header->level])
+ slot--;
+ path->slots[0] = slot;
+ path->item = leaf->items[slot];
+ btrfs_read(fs, (char *)&path->data,
+ offset + sizeof(*header) + leaf->items[slot].offset,
+ leaf->items[slot].size);
+ }
+ return ret;
+}
+
+/* return 0 if leaf found */
+static int next_leaf(struct fs_info *fs, struct btrfs_disk_key *key, struct btrfs_path *path)
+{
+ int slot;
+ int level = 1;
+
+ while (level < BTRFS_MAX_LEVEL) {
+ if (!path->itemsnr[level]) /* no more nodes */
+ return 1;
+ slot = path->slots[level] + 1;
+ if (slot >= path->itemsnr[level]) {
+ level++;
+ continue;;
+ }
+ path->slots[level] = slot;
+ path->slots[level-1] = 0; /* reset low level slots info */
+ search_tree(fs, path->offsets[level], key, path);
+ break;
+ }
+ if (level == BTRFS_MAX_LEVEL)
+ return 1;
+ return 0;
+}
+
+/* return 0 if slot found */
+static int next_slot(struct fs_info *fs, struct btrfs_disk_key *key, struct btrfs_path *path)
+{
+ int slot;
+
+ if (!path->itemsnr[0])
+ return 1;
+ slot = path->slots[0] + 1;
+ if (slot >= path->itemsnr[0])
+ return 1;
+ path->slots[0] = slot;
+ search_tree(fs, path->offsets[0], key, path);
+ return 0;
+}
+
+/*
+ * read chunk_array in super block
+ */
+static void btrfs_read_sys_chunk_array(void)
+{
+ struct btrfs_chunk_map_item item;
+ struct btrfs_disk_key *key;
+ struct btrfs_chunk *chunk;
+ int cur;
+
+ /* read chunk array in superblock */
+ cur = 0;
+ while (cur < sb.sys_chunk_array_size) {
+ key = (struct btrfs_disk_key *)(sb.sys_chunk_array + cur);
+ cur += sizeof(*key);
+ chunk = (struct btrfs_chunk *)(sb.sys_chunk_array + cur);
+ cur += btrfs_chunk_item_size(chunk->num_stripes);
+ /* insert to mapping table, ignore multi stripes */
+ item.logical = key->offset;
+ item.length = chunk->length;
+ item.devid = chunk->stripe.devid;
+ item.physical = chunk->stripe.offset;/*ignore other stripes */
+ insert_map(&item);
+ }
+}
+
+/* read chunk items from chunk_tree and insert them to chunk map */
+static void btrfs_read_chunk_tree(struct fs_info *fs)
+{
+ struct btrfs_disk_key search_key;
+ struct btrfs_chunk *chunk;
+ struct btrfs_chunk_map_item item;
+ struct btrfs_path path;
+
+ if (!(sb.flags & BTRFS_SUPER_FLAG_METADUMP)) {
+ if (sb.num_devices > 1)
+ printf("warning: only support single device btrfs\n");
+ /* read chunk from chunk_tree */
+ search_key.objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID;
+ search_key.type = BTRFS_CHUNK_ITEM_KEY;
+ search_key.offset = 0;
+ clear_path(&path);
+ search_tree(fs, sb.chunk_root, &search_key, &path);
+ do {
+ do {
+ if (btrfs_comp_keys_type(&search_key,
+ &path.item.key))
+ break;
+ chunk = (struct btrfs_chunk *)(path.data);
+ /* insert to mapping table, ignore stripes */
+ item.logical = path.item.key.offset;
+ item.length = chunk->length;
+ item.devid = chunk->stripe.devid;
+ item.physical = chunk->stripe.offset;
+ insert_map(&item);
+ } while (!next_slot(fs, &search_key, &path));
+ if (btrfs_comp_keys_type(&search_key, &path.item.key))
+ break;
+ } while (!next_leaf(fs, &search_key, &path));
+ }
+}
+
+static inline u64 btrfs_name_hash(const char *name, int len)
+{
+ return btrfs_crc32c((u32)~1, name, len);
+}
+
+static struct inode *btrfs_iget_by_inr(struct fs_info *fs, u64 inr)
+{
+ struct inode *inode;
+ struct btrfs_inode_item inode_item;
+ struct btrfs_disk_key search_key;
+ struct btrfs_path path;
+ int ret;
+
+ /* FIXME: some BTRFS inode member are u64, while our logical inode
+ is u32, we may need change them to u64 later */
+ search_key.objectid = inr;
+ search_key.type = BTRFS_INODE_ITEM_KEY;
+ search_key.offset = 0;
+ clear_path(&path);
+ ret = search_tree(fs, fs_tree, &search_key, &path);
+ if (ret)
+ return NULL;
+ inode_item = *(struct btrfs_inode_item *)path.data;
+ if (!(inode = alloc_inode(fs, inr, sizeof(struct btrfs_pvt_inode))))
+ return NULL;
+ inode->ino = inr;
+ inode->size = inode_item.size;
+ inode->mode = IFTODT(inode_item.mode);
+
+ if (inode->mode == DT_REG || inode->mode == DT_LNK) {
+ struct btrfs_file_extent_item extent_item;
+ u64 offset;
+
+ /* get file_extent_item */
+ search_key.type = BTRFS_EXTENT_DATA_KEY;
+ search_key.offset = 0;
+ clear_path(&path);
+ ret = search_tree(fs, fs_tree, &search_key, &path);
+ if (ret)
+ return NULL; /* impossible */
+ extent_item = *(struct btrfs_file_extent_item *)path.data;
+ if (extent_item.type == BTRFS_FILE_EXTENT_INLINE)/* inline file */
+ offset = path.offsets[0] + sizeof(struct btrfs_header)
+ + path.item.offset
+ + offsetof(struct btrfs_file_extent_item, disk_bytenr);
+ else
+ offset = extent_item.disk_bytenr;
+ PVT(inode)->offset = offset;
+ }
+ return inode;
+}
+
+static struct inode *btrfs_iget_root(struct fs_info *fs)
+{
+ /* BTRFS_FIRST_CHUNK_TREE_OBJECTID(256) actually is first OBJECTID for FS_TREE */
+ return btrfs_iget_by_inr(fs, BTRFS_FIRST_CHUNK_TREE_OBJECTID);
+}
+
+static struct inode *btrfs_iget(const char *name, struct inode *parent)
+{
+ struct fs_info *fs = parent->fs;
+ struct btrfs_disk_key search_key;
+ struct btrfs_path path;
+ struct btrfs_dir_item dir_item;
+ int ret;
+
+ search_key.objectid = parent->ino;
+ search_key.type = BTRFS_DIR_ITEM_KEY;
+ search_key.offset = btrfs_name_hash(name, strlen(name));
+ clear_path(&path);
+ ret = search_tree(fs, fs_tree, &search_key, &path);
+ if (ret)
+ return NULL;
+ dir_item = *(struct btrfs_dir_item *)path.data;
+
+ return btrfs_iget_by_inr(fs, dir_item.location.objectid);
+}
+
+static int btrfs_readlink(struct inode *inode, char *buf)
+{
+ btrfs_read(inode->fs, buf, logical_physical(PVT(inode)->offset), inode->size);
+ buf[inode->size] = '\0';
+ return inode->size;
+}
+
+static int btrfs_readdir(struct file *file, struct dirent *dirent)
+{
+ struct fs_info *fs = file->fs;
+ struct inode *inode = file->inode;
+ struct btrfs_disk_key search_key;
+ struct btrfs_path path;
+ struct btrfs_dir_item *dir_item;
+ int ret;
+
+ /*
+ * we use file->offset to store last search key.offset, will will search
+ * key that lower that offset, 0 means first search and we will search
+ * -1UL, which is the biggest possible key
+ */
+ search_key.objectid = inode->ino;
+ search_key.type = BTRFS_DIR_ITEM_KEY;
+ search_key.offset = file->offset - 1;
+ clear_path(&path);
+ ret = search_tree(fs, fs_tree, &search_key, &path);
+
+ if (ret) {
+ if (btrfs_comp_keys_type(&search_key, &path.item.key))
+ return -1;
+ }
+
+ dir_item = (struct btrfs_dir_item *)path.data;
+ file->offset = path.item.key.offset;
+ dirent->d_ino = dir_item->location.objectid;
+ dirent->d_off = file->offset;
+ dirent->d_reclen = offsetof(struct dirent, d_name)
+ + dir_item->name_len + 1;
+ dirent->d_type = IFTODT(dir_item->type);
+ memcpy(dirent->d_name, dir_item + 1, dir_item->name_len);
+ dirent->d_name[dir_item->name_len] = '\0';
+
+ return 0;
+}
+
+static int btrfs_next_extent(struct inode *inode, uint32_t lstart)
+{
+ struct btrfs_disk_key search_key;
+ struct btrfs_file_extent_item extent_item;
+ struct btrfs_path path;
+ int ret;
+ u64 offset;
+ struct fs_info *fs = inode->fs;
+ u32 sec_shift = SECTOR_SHIFT(fs);
+ u32 sec_size = SECTOR_SIZE(fs);
+
+ search_key.objectid = inode->ino;
+ search_key.type = BTRFS_EXTENT_DATA_KEY;
+ search_key.offset = lstart << sec_shift;
+ clear_path(&path);
+ ret = search_tree(fs, fs_tree, &search_key, &path);
+ if (ret) { /* impossible */
+ printf("btrfs: search extent data error!\n");
+ return 0;
+ }
+ extent_item = *(struct btrfs_file_extent_item *)path.data;
+
+ if (extent_item.type == BTRFS_FILE_EXTENT_INLINE) {/* inline file */
+ /* we fake a extent here, and PVT of inode will tell us */
+ offset = path.offsets[0] + sizeof(struct btrfs_header)
+ + path.item.offset
+ + offsetof(struct btrfs_file_extent_item, disk_bytenr);
+ inode->next_extent.len =
+ (inode->size + sec_size -1) >> sec_shift;
+ } else {
+ offset = extent_item.disk_bytenr + extent_item.offset;
+ inode->next_extent.len =
+ (extent_item.num_bytes + sec_size - 1) >> sec_shift;
+ }
+ inode->next_extent.pstart =
+ logical_physical(offset) >> sec_shift;
+ PVT(inode)->offset = offset;
+ return 0;
+}
+
+static uint32_t btrfs_getfssec(struct file *file, char *buf, int sectors,
+ bool *have_more)
+{
+ u32 ret;
+ struct fs_info *fs = file->fs;
+ u32 off = PVT(file->inode)->offset % SECTOR_SIZE(fs);
+ bool handle_inline = false;
+
+ if (off && !file->offset) {/* inline file first read patch */
+ file->inode->size += off;
+ handle_inline = true;
+ }
+ ret = generic_getfssec(file, buf, sectors, have_more);
+ if (!ret)
+ return ret;
+ off = PVT(file->inode)->offset % SECTOR_SIZE(fs);
+ if (handle_inline) {/* inline file patch */
+ ret -= off;
+ memcpy(buf, buf + off, ret);
+ }
+ return ret;
+}
+
+static void btrfs_get_fs_tree(struct fs_info *fs)
+{
+ struct btrfs_disk_key search_key;
+ struct btrfs_path path;
+ struct btrfs_root_item *tree;
+ bool subvol_ok = false;
+
+ /* check if subvol is filled by installer */
+ if (*SubvolName) {
+ search_key.objectid = BTRFS_FS_TREE_OBJECTID;
+ search_key.type = BTRFS_ROOT_REF_KEY;
+ search_key.offset = 0;
+ clear_path(&path);
+ if (search_tree(fs, sb.root, &search_key, &path))
+ next_slot(fs, &search_key, &path);
+ do {
+ do {
+ struct btrfs_root_ref *ref;
+
+ if (btrfs_comp_keys_type(&search_key,
+ &path.item.key))
+ break;
+ ref = (struct btrfs_root_ref *)path.data;
+ if (!strcmp((char*)(ref + 1), SubvolName)) {
+ subvol_ok = true;
+ break;
+ }
+ } while (!next_slot(fs, &search_key, &path));
+ if (subvol_ok)
+ break;
+ if (btrfs_comp_keys_type(&search_key, &path.item.key))
+ break;
+ } while (!next_leaf(fs, &search_key, &path));
+ if (!subvol_ok) /* should be impossible */
+ printf("no subvol found!\n");
+ }
+ /* find fs_tree from tree_root */
+ if (subvol_ok)
+ search_key.objectid = path.item.key.offset;
+ else /* "default" volume */
+ search_key.objectid = BTRFS_FS_TREE_OBJECTID;
+ search_key.type = BTRFS_ROOT_ITEM_KEY;
+ search_key.offset = -1;
+ clear_path(&path);
+ search_tree(fs, sb.root, &search_key, &path);
+ tree = (struct btrfs_root_item *)path.data;
+ fs_tree = tree->bytenr;
+}
+
+/* init. the fs meta data, return the block size shift bits. */
+static int btrfs_fs_init(struct fs_info *fs)
+{
+ struct disk *disk = fs->fs_dev->disk;
+
+ btrfs_init_crc32c();
+
+ fs->sector_shift = disk->sector_shift;
+ fs->sector_size = 1 << fs->sector_shift;
+ fs->block_shift = BTRFS_BLOCK_SHIFT;
+ fs->block_size = 1 << fs->block_shift;
+
+ btrfs_read_super_block(fs);
+ if (strncmp((char *)(&sb.magic), BTRFS_MAGIC, sizeof(sb.magic)))
+ return -1;
+ btrfs_read_sys_chunk_array();
+ btrfs_read_chunk_tree(fs);
+ btrfs_get_fs_tree(fs);
+ cache_ready = 1;
+
+ /* Initialize the block cache */
+ cache_init(fs->fs_dev, fs->block_shift);
+
+ return fs->block_shift;
+}
+
+const struct fs_ops btrfs_fs_ops = {
+ .fs_name = "btrfs",
+ .fs_flags = 0,
+ .fs_init = btrfs_fs_init,
+ .iget_root = btrfs_iget_root,
+ .iget = btrfs_iget,
+ .readlink = btrfs_readlink,
+ .getfssec = btrfs_getfssec,
+ .close_file = generic_close_file,
+ .mangle_name = generic_mangle_name,
+ .next_extent = btrfs_next_extent,
+ .readdir = btrfs_readdir,
+ .load_config = generic_load_config
+};
diff --git a/core/fs/btrfs/btrfs.h b/core/fs/btrfs/btrfs.h
new file mode 100644
index 00000000..aa1245b4
--- /dev/null
+++ b/core/fs/btrfs/btrfs.h
@@ -0,0 +1,292 @@
+#ifndef _BTRFS_H_
+#define _BTRFS_H_
+
+#include <stdint.h>
+#include <zconf.h>
+
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+/* type that store on disk, but it is same as cpu type for i386 arch */
+typedef u16 __le16;
+typedef u32 __le32;
+typedef u64 __le64;
+
+#include "crc32c.h"
+#define btrfs_crc32c crc32c_le
+
+#define BTRFS_SUPER_INFO_OFFSET (64 * 1024)
+#define BTRFS_SUPER_INFO_SIZE 4096
+#define BTRFS_MAX_LEAF_SIZE 4096
+#define BTRFS_BLOCK_SHIFT 12
+
+#define BTRFS_SUPER_MIRROR_MAX 3
+#define BTRFS_SUPER_MIRROR_SHIFT 12
+#define BTRFS_CSUM_SIZE 32
+#define BTRFS_FSID_SIZE 16
+#define BTRFS_LABEL_SIZE 256
+#define BTRFS_SYSTEM_CHUNK_ARRAY_SIZE 2048
+#define BTRFS_UUID_SIZE 16
+
+#define BTRFS_MAGIC "_BHRfS_M"
+
+#define BTRFS_SUPER_FLAG_METADUMP (1ULL << 33)
+
+#define BTRFS_DEV_ITEM_KEY 216
+#define BTRFS_CHUNK_ITEM_KEY 228
+#define BTRFS_ROOT_REF_KEY 156
+#define BTRFS_ROOT_ITEM_KEY 132
+#define BTRFS_EXTENT_DATA_KEY 108
+#define BTRFS_DIR_ITEM_KEY 84
+#define BTRFS_INODE_ITEM_KEY 1
+
+#define BTRFS_EXTENT_TREE_OBJECTID 2ULL
+#define BTRFS_FS_TREE_OBJECTID 5ULL
+
+#define BTRFS_FIRST_CHUNK_TREE_OBJECTID 256ULL
+
+#define BTRFS_FILE_EXTENT_INLINE 0
+#define BTRFS_FILE_EXTENT_REG 1
+#define BTRFS_FILE_EXTENT_PREALLOC 2
+
+#define BTRFS_MAX_LEVEL 8
+#define BTRFS_MAX_CHUNK_ENTRIES 256
+
+#define BTRFS_FT_REG_FILE 1
+#define BTRFS_FT_DIR 2
+#define BTRFS_FT_SYMLINK 7
+
+#define ROOT_DIR_WORD 0x002f
+
+struct btrfs_dev_item {
+ __le64 devid;
+ __le64 total_bytes;
+ __le64 bytes_used;
+ __le32 io_align;
+ __le32 io_width;
+ __le32 sector_size;
+ __le64 type;
+ __le64 generation;
+ __le64 start_offset;
+ __le32 dev_group;
+ u8 seek_speed;
+ u8 bandwidth;
+ u8 uuid[BTRFS_UUID_SIZE];
+ u8 fsid[BTRFS_UUID_SIZE];
+} __attribute__ ((__packed__));
+
+struct btrfs_super_block {
+ u8 csum[BTRFS_CSUM_SIZE];
+ /* the first 3 fields must match struct btrfs_header */
+ u8 fsid[BTRFS_FSID_SIZE]; /* FS specific uuid */
+ __le64 bytenr; /* this block number */
+ __le64 flags;
+
+ /* allowed to be different from the btrfs_header from here own down */
+ __le64 magic;
+ __le64 generation;
+ __le64 root;
+ __le64 chunk_root;
+ __le64 log_root;
+
+ /* this will help find the new super based on the log root */
+ __le64 log_root_transid;
+ __le64 total_bytes;
+ __le64 bytes_used;
+ __le64 root_dir_objectid;
+ __le64 num_devices;
+ __le32 sectorsize;
+ __le32 nodesize;
+ __le32 leafsize;
+ __le32 stripesize;
+ __le32 sys_chunk_array_size;
+ __le64 chunk_root_generation;
+ __le64 compat_flags;
+ __le64 compat_ro_flags;
+ __le64 incompat_flags;
+ __le16 csum_type;
+ u8 root_level;
+ u8 chunk_root_level;
+ u8 log_root_level;
+ struct btrfs_dev_item dev_item;
+
+ char label[BTRFS_LABEL_SIZE];
+
+ /* future expansion */
+ __le64 reserved[32];
+ u8 sys_chunk_array[BTRFS_SYSTEM_CHUNK_ARRAY_SIZE];
+} __attribute__ ((__packed__));
+
+struct btrfs_disk_key {
+ __le64 objectid;
+ u8 type;
+ __le64 offset;
+} __attribute__ ((__packed__));
+
+struct btrfs_stripe {
+ __le64 devid;
+ __le64 offset;
+ u8 dev_uuid[BTRFS_UUID_SIZE];
+} __attribute__ ((__packed__));
+
+struct btrfs_chunk {
+ __le64 length;
+ __le64 owner;
+ __le64 stripe_len;
+ __le64 type;
+ __le32 io_align;
+ __le32 io_width;
+ __le32 sector_size;
+ __le16 num_stripes;
+ __le16 sub_stripes;
+ struct btrfs_stripe stripe;
+} __attribute__ ((__packed__));
+
+struct btrfs_header {
+ /* these first four must match the super block */
+ u8 csum[BTRFS_CSUM_SIZE];
+ u8 fsid[BTRFS_FSID_SIZE]; /* FS specific uuid */
+ __le64 bytenr; /* which block this node is supposed to live in */
+ __le64 flags;
+
+ /* allowed to be different from the super from here on down */
+ u8 chunk_tree_uuid[BTRFS_UUID_SIZE];
+ __le64 generation;
+ __le64 owner;
+ __le32 nritems;
+ u8 level;
+} __attribute__ ((__packed__));
+
+struct btrfs_item {
+ struct btrfs_disk_key key;
+ __le32 offset;
+ __le32 size;
+} __attribute__ ((__packed__));
+
+struct btrfs_leaf {
+ struct btrfs_header header;
+ struct btrfs_item items[];
+} __attribute__ ((__packed__));
+
+struct btrfs_key_ptr {
+ struct btrfs_disk_key key;
+ __le64 blockptr;
+ __le64 generation;
+} __attribute__ ((__packed__));
+
+struct btrfs_node {
+ struct btrfs_header header;
+ struct btrfs_key_ptr ptrs[];
+} __attribute__ ((__packed__));
+
+/* remember how we get to a node/leaf */
+struct btrfs_path {
+ u64 offsets[BTRFS_MAX_LEVEL];
+ int itemsnr[BTRFS_MAX_LEVEL];
+ int slots[BTRFS_MAX_LEVEL];
+ /* remember last slot's item and data */
+ struct btrfs_item item;
+ u8 data[BTRFS_MAX_LEAF_SIZE];
+};
+
+/* store logical offset to physical offset mapping */
+struct btrfs_chunk_map_item {
+ u64 logical;
+ u64 length;
+ u64 devid;
+ u64 physical;
+};
+
+struct btrfs_chunk_map {
+ struct btrfs_chunk_map_item *map;
+ u32 map_length;
+ u32 cur_length;
+};
+
+struct btrfs_timespec {
+ __le64 sec;
+ __le32 nsec;
+} __attribute__ ((__packed__));
+
+struct btrfs_inode_item {
+ /* nfs style generation number */
+ __le64 generation;
+ /* transid that last touched this inode */
+ __le64 transid;
+ __le64 size;
+ __le64 nbytes;
+ __le64 block_group;
+ __le32 nlink;
+ __le32 uid;
+ __le32 gid;
+ __le32 mode;
+ __le64 rdev;
+ __le64 flags;
+
+ /* modification sequence number for NFS */
+ __le64 sequence;
+
+ /*
+ * a little future expansion, for more than this we can
+ * just grow the inode item and version it
+ */
+ __le64 reserved[4];
+ struct btrfs_timespec atime;
+ struct btrfs_timespec ctime;
+ struct btrfs_timespec mtime;
+ struct btrfs_timespec otime;
+} __attribute__ ((__packed__));
+
+struct btrfs_root_item {
+ struct btrfs_inode_item inode;
+ __le64 generation;
+ __le64 root_dirid;
+ __le64 bytenr;
+ __le64 byte_limit;
+ __le64 bytes_used;
+ __le64 last_snapshot;
+ __le64 flags;
+ __le32 refs;
+ struct btrfs_disk_key drop_progress;
+ u8 drop_level;
+ u8 level;
+} __attribute__ ((__packed__));
+
+struct btrfs_dir_item {
+ struct btrfs_disk_key location;
+ __le64 transid;
+ __le16 data_len;
+ __le16 name_len;
+ u8 type;
+} __attribute__ ((__packed__));
+
+struct btrfs_file_extent_item {
+ __le64 generation;
+ __le64 ram_bytes;
+ u8 compression;
+ u8 encryption;
+ __le16 other_encoding; /* spare for later use */
+ u8 type;
+ __le64 disk_bytenr;
+ __le64 disk_num_bytes;
+ __le64 offset;
+ __le64 num_bytes;
+} __attribute__ ((__packed__));
+
+struct btrfs_root_ref {
+ __le64 dirid;
+ __le64 sequence;
+ __le16 name_len;
+} __attribute__ ((__packed__));
+
+/*
+ * btrfs private inode information
+ */
+struct btrfs_pvt_inode {
+ uint64_t offset;
+};
+
+#define PVT(i) ((struct btrfs_pvt_inode *)((i)->pvt))
+
+#endif
diff --git a/core/fs/btrfs/crc32c.h b/core/fs/btrfs/crc32c.h
new file mode 100644
index 00000000..2c317384
--- /dev/null
+++ b/core/fs/btrfs/crc32c.h
@@ -0,0 +1,50 @@
+/*
+ * Copied from Linux kernel crypto/crc32c.c
+ * Copyright (c) 2004 Cisco Systems, Inc.
+ * Copyright (c) 2008 Herbert Xu <herbert@gondor.apana.org.au>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ */
+
+/*
+ * This is the CRC-32C table
+ * Generated with:
+ * width = 32 bits
+ * poly = 0x1EDC6F41
+ * reflect input bytes = true
+ * reflect output bytes = true
+ */
+
+static u32 crc32c_table[256];
+
+/*
+ * Steps through buffer one byte at at time, calculates reflected
+ * crc using table.
+ */
+
+static inline u32 crc32c_le(u32 crc, const char *data, size_t length)
+{
+ while (length--)
+ crc = crc32c_table[(u8)(crc ^ *data++)] ^ (crc >> 8);
+
+ return crc;
+}
+
+static inline void btrfs_init_crc32c(void)
+{
+ int i, j;
+ u32 v;
+ const u32 poly = 0x82F63B78; /* Bit-reflected CRC32C polynomial */
+
+ for (i = 0; i < 256; i++) {
+ v = i;
+ for (j = 0; j < 8; j++) {
+ v = (v >> 1) ^ ((v & 1) ? poly : 0);
+ }
+ crc32c_table[i] = v;
+ }
+}
diff --git a/core/fs/cache.c b/core/fs/cache.c
new file mode 100644
index 00000000..0d7891be
--- /dev/null
+++ b/core/fs/cache.c
@@ -0,0 +1,125 @@
+/*
+ * core/cache.c: A simple LRU-based cache implementation.
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <dprintf.h>
+#include "core.h"
+#include "cache.h"
+
+
+/*
+ * Initialize the cache data structres. the _block_size_shift_ specify
+ * the block size, which is 512 byte for FAT fs of the current
+ * implementation since the block(cluster) size in FAT is a bit big.
+ *
+ */
+void cache_init(struct device *dev, int block_size_shift)
+{
+ struct cache *prev, *cur;
+ char *data = dev->cache_data;
+ struct cache *head, *cache;
+ int i;
+
+ dev->cache_block_size = 1 << block_size_shift;
+
+ if (dev->cache_size < dev->cache_block_size + 2*sizeof(struct cache)) {
+ dev->cache_head = NULL;
+ return; /* Cache unusably small */
+ }
+
+ /* We need one struct cache for the headnode plus one for each block */
+ dev->cache_entries =
+ (dev->cache_size - sizeof(struct cache))/
+ (dev->cache_block_size + sizeof(struct cache));
+
+ dev->cache_head = head = (struct cache *)
+ (data + (dev->cache_entries << block_size_shift));
+ cache = dev->cache_head + 1; /* First cache descriptor */
+
+ head->prev = &cache[dev->cache_entries-1];
+ head->next->prev = dev->cache_head;
+ head->block = -1;
+ head->data = NULL;
+
+ prev = head;
+
+ for (i = 0; i < dev->cache_entries; i++) {
+ cur = &cache[i];
+ cur->data = data;
+ cur->block = -1;
+ cur->prev = prev;
+ prev->next = cur;
+ data += dev->cache_block_size;
+ prev = cur++;
+ }
+}
+
+/*
+ * Lock a block permanently in the cache
+ */
+void cache_lock_block(struct cache *cs)
+{
+ cs->prev->next = cs->next;
+ cs->next->prev = cs->prev;
+
+ cs->next = cs->prev = NULL;
+}
+
+/*
+ * Check for a particular BLOCK in the block cache,
+ * and if it is already there, just do nothing and return;
+ * otherwise pick a victim block and update the LRU link.
+ */
+struct cache *_get_cache_block(struct device *dev, block_t block)
+{
+ struct cache *head = dev->cache_head;
+ struct cache *cs;
+ int i;
+
+ cs = dev->cache_head + 1;
+
+ for (i = 0; i < dev->cache_entries; i++) {
+ if (cs->block == block)
+ goto found;
+ cs++;
+ }
+
+ /* Not found, pick a victim */
+ cs = head->next;
+
+found:
+ /* Move to the end of the LRU chain, unless the block is already locked */
+ if (cs->next) {
+ cs->prev->next = cs->next;
+ cs->next->prev = cs->prev;
+
+ cs->prev = head->prev;
+ head->prev->next = cs;
+ cs->next = head;
+ head->prev = cs;
+ }
+
+ return cs;
+}
+
+/*
+ * Check for a particular BLOCK in the block cache,
+ * and if it is already there, just do nothing and return;
+ * otherwise load it from disk and update the LRU link.
+ * Return the data pointer.
+ */
+const void *get_cache(struct device *dev, block_t block)
+{
+ struct cache *cs;
+
+ cs = _get_cache_block(dev, block);
+ if (cs->block != block) {
+ cs->block = block;
+ getoneblk(dev->disk, cs->data, block, dev->cache_block_size);
+ }
+
+ return cs->data;
+}
diff --git a/core/fs/chdir.c b/core/fs/chdir.c
new file mode 100644
index 00000000..bfce9bce
--- /dev/null
+++ b/core/fs/chdir.c
@@ -0,0 +1,66 @@
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include "fs.h"
+#include "cache.h"
+
+/*
+ * Convert a relative pathname to an absolute pathname
+ * In the future this might also resolve symlinks...
+ */
+void pm_realpath(com32sys_t *regs)
+{
+ const char *src = MK_PTR(regs->ds, regs->esi.w[0]);
+ char *dst = MK_PTR(regs->es, regs->edi.w[0]);
+
+ realpath(dst, src, FILENAME_MAX);
+}
+
+size_t realpath(char *dst, const char *src, size_t bufsize)
+{
+ if (this_fs->fs_ops->realpath) {
+ return this_fs->fs_ops->realpath(this_fs, dst, src, bufsize);
+ } else {
+ /* Filesystems with "common" pathname resolution */
+ return snprintf(dst, bufsize, "%s%s",
+ src[0] == '/' ? "" : this_fs->cwd_name,
+ src);
+ }
+}
+
+int chdir(const char *src)
+{
+ int rv;
+ struct file *file;
+ char *p;
+
+ if (this_fs->fs_ops->chdir)
+ return this_fs->fs_ops->chdir(this_fs, src);
+
+ /* Otherwise it is a "conventional filesystem" */
+ rv = searchdir(src);
+ if (rv < 0)
+ return rv;
+
+ file = handle_to_file(rv);
+ if (file->inode->mode != DT_DIR) {
+ _close_file(file);
+ return -1;
+ }
+
+ put_inode(this_fs->cwd);
+ this_fs->cwd = get_inode(file->inode);
+ _close_file(file);
+
+ /* Save the current working directory */
+ realpath(this_fs->cwd_name, src, CURRENTDIR_MAX);
+ p = strchr(this_fs->cwd_name, '\0');
+
+ /* Make sure the cwd_name ends in a slash, it's supposed to be a prefix */
+ if (p < this_fs->cwd_name + CURRENTDIR_MAX - 1 &&
+ (p == this_fs->cwd_name || p[1] != '/')) {
+ p[0] = '/';
+ p[1] = '\0';
+ }
+ return 0;
+}
diff --git a/core/fs/diskio.c b/core/fs/diskio.c
new file mode 100644
index 00000000..6afba219
--- /dev/null
+++ b/core/fs/diskio.c
@@ -0,0 +1,321 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+#include <klibc/compiler.h>
+#include <core.h>
+#include <fs.h>
+#include <disk.h>
+
+#define RETRY_COUNT 6
+
+static uint16_t MaxTransfer = 1 << (16 - 9);
+
+static int chs_rdwr_sectors(struct disk *disk, void *buf,
+ sector_t lba, size_t count, bool is_write)
+{
+ char *ptr = buf;
+ char *tptr;
+ size_t chunk, freeseg;
+ int sector_shift = disk->sector_shift;
+ uint32_t xlba = lba + disk->part_start; /* Truncated LBA (CHS is << 2 TB) */
+ uint32_t t;
+ uint16_t c, h, s;
+ com32sys_t ireg, oreg;
+ size_t done = 0;
+ size_t bytes;
+ int retry;
+
+ memset(&ireg, 0, sizeof ireg);
+
+ ireg.eax.b[1] = 0x02 + is_write;
+ ireg.edx.b[0] = disk->disk_number;
+
+ while (count) {
+ chunk = count;
+ if (chunk > MaxTransfer)
+ chunk = MaxTransfer;
+
+ freeseg = (0x10000 - ((size_t)ptr & 0xffff)) >> sector_shift;
+
+ if ((size_t)buf <= 0xf0000 && freeseg) {
+ /* Can do a direct load */
+ tptr = ptr;
+ } else {
+ /* Either accessing high memory or we're crossing a 64K line */
+ tptr = core_xfer_buf;
+ freeseg = (0x10000 - ((size_t)tptr & 0xffff)) >> sector_shift;
+ }
+ if (chunk > freeseg)
+ chunk = freeseg;
+
+ bytes = chunk << sector_shift;
+
+ if (tptr != ptr && is_write)
+ memcpy(tptr, ptr, bytes);
+
+ s = xlba % disk->s;
+ t = xlba / disk->s;
+ h = t % disk->h;
+ c = t / disk->h;
+
+ ireg.eax.b[0] = chunk;
+ ireg.ecx.b[1] = c;
+ ireg.ecx.b[0] = ((c & 0x300) >> 2) | (s+1);
+ ireg.edx.b[1] = h;
+ ireg.ebx.w[0] = OFFS(tptr);
+ ireg.es = SEG(tptr);
+
+ retry = RETRY_COUNT;
+
+ for (;;) {
+ __intcall(0x13, &ireg, &oreg);
+ if (!(oreg.eflags.l & EFLAGS_CF))
+ break;
+ if (retry--)
+ continue;
+
+ /* if we are reading ONE sector and go here, just make it _faile_ */
+ chunk = chunk == 1 ? 0 : ((chunk+1) >> 1);
+ if (chunk) {
+ MaxTransfer = chunk;
+ retry = RETRY_COUNT;
+ ireg.eax.b[0] = chunk;
+ continue;
+ }
+ return done; /* Failure */
+ }
+
+ bytes = chunk << sector_shift;
+
+ if (tptr != ptr && !is_write)
+ memcpy(ptr, tptr, bytes);
+
+ ptr += bytes;
+ xlba += chunk;
+ count -= chunk;
+ done += chunk;
+ }
+
+ return done;
+}
+
+struct edd_rdwr_packet {
+ uint16_t size;
+ uint16_t blocks;
+ far_ptr_t buf;
+ uint64_t lba;
+};
+
+static int edd_rdwr_sectors(struct disk *disk, void *buf,
+ sector_t lba, size_t count, bool is_write)
+{
+ static __lowmem struct edd_rdwr_packet pkt;
+ char *ptr = buf;
+ char *tptr;
+ size_t chunk, freeseg;
+ int sector_shift = disk->sector_shift;
+ com32sys_t ireg, oreg;
+ size_t done = 0;
+ size_t bytes;
+ int retry;
+
+ memset(&ireg, 0, sizeof ireg);
+
+ ireg.eax.b[1] = 0x42 + is_write;
+ ireg.edx.b[0] = disk->disk_number;
+ ireg.ds = SEG(&pkt);
+ ireg.esi.w[0] = OFFS(&pkt);
+
+ lba += disk->part_start;
+ while (count) {
+ chunk = count;
+ if (chunk > MaxTransfer)
+ chunk = MaxTransfer;
+
+ freeseg = (0x10000 - ((size_t)ptr & 0xffff)) >> sector_shift;
+
+ if ((size_t)buf <= 0xf0000 && freeseg) {
+ /* Can do a direct load */
+ tptr = ptr;
+ } else {
+ /* Either accessing high memory or we're crossing a 64K line */
+ tptr = core_xfer_buf;
+ freeseg = (0x10000 - ((size_t)tptr & 0xffff)) >> sector_shift;
+ }
+ if (chunk > freeseg)
+ chunk = freeseg;
+
+ bytes = chunk << sector_shift;
+
+ if (tptr != ptr && is_write)
+ memcpy(tptr, ptr, bytes);
+
+ pkt.size = sizeof pkt;
+ pkt.blocks = chunk;
+ pkt.buf = FAR_PTR(tptr);
+ pkt.lba = lba;
+
+ retry = RETRY_COUNT;
+
+ for (;;) {
+ __intcall(0x13, &ireg, &oreg);
+ if (!(oreg.eflags.l & EFLAGS_CF))
+ break;
+ if (retry--)
+ continue;
+ chunk = chunk == 1 ? 0 : ((chunk+1) >> 1);
+ if (chunk) {
+ MaxTransfer = chunk;
+ retry = RETRY_COUNT;
+ pkt.blocks = chunk;
+ continue;
+ }
+ /*** XXX: Consider falling back to CHS here?! ***/
+ printf("reading sectors error(EDD)\n");
+ return done; /* Failure */
+ }
+
+ bytes = chunk << sector_shift;
+
+ if (tptr != ptr && !is_write)
+ memcpy(ptr, tptr, bytes);
+
+ ptr += bytes;
+ lba += chunk;
+ count -= chunk;
+ done += chunk;
+ }
+ return done;
+}
+struct edd_disk_params {
+ uint16_t len;
+ uint16_t flags;
+ uint32_t phys_c;
+ uint32_t phys_h;
+ uint32_t phys_s;
+ uint64_t sectors;
+ uint16_t sector_size;
+ far_ptr_t dpte;
+ uint16_t devpath_key;
+ uint8_t devpath_len;
+ uint8_t _pad1[3];
+ char bus_type[4];
+ char if_type[8];
+ uint8_t if_path[8];
+ uint8_t dev_path[8];
+ uint8_t _pad2;
+ uint8_t devpath_csum;
+} __attribute__((packed));
+
+static inline bool is_power_of_2(uint32_t x)
+{
+ return !(x & (x-1));
+}
+
+static int ilog2(uint32_t num)
+{
+ int i = 0;
+
+ if (!is_power_of_2(num)) {
+ printf("ERROR: the num must be power of 2 when conveting to log2\n");
+ return 0;
+ }
+ while (num >>= 1)
+ i++;
+ return i;
+}
+
+void getoneblk(struct disk *disk, char *buf, block_t block, int block_size)
+{
+ int sec_per_block = block_size / disk->sector_size;
+
+ disk->rdwr_sectors(disk, buf, block * sec_per_block, sec_per_block, 0);
+}
+
+
+struct disk *disk_init(uint8_t devno, bool cdrom, sector_t part_start,
+ uint16_t bsHeads, uint16_t bsSecPerTrack)
+{
+ static struct disk disk;
+ static __lowmem struct edd_disk_params edd_params;
+ com32sys_t ireg, oreg;
+ bool ebios = cdrom;
+ int sector_size = cdrom ? 2048 : 512;
+
+ memset(&ireg, 0, sizeof ireg);
+
+ /* Get EBIOS support */
+ ireg.eax.b[1] = 0x41;
+ ireg.ebx.w[0] = 0x55aa;
+ ireg.edx.b[0] = devno;
+ ireg.eflags.b[0] = 0x3; /* CF set */
+
+ __intcall(0x13, &ireg, &oreg);
+
+ if (cdrom || (!(oreg.eflags.l & EFLAGS_CF) &&
+ oreg.ebx.w[0] == 0xaa55 && (oreg.ecx.b[0] & 1))) {
+ ebios = true;
+
+ /* Query EBIOS parameters */
+ edd_params.len = sizeof edd_params;
+
+ ireg.eax.b[1] = 0x48;
+ ireg.ds = SEG(&edd_params);
+ ireg.esi.w[0] = OFFS(&edd_params);
+ __intcall(0x13, &ireg, &oreg);
+
+ if (!(oreg.eflags.l & EFLAGS_CF) && oreg.eax.b[1] == 0) {
+ if (edd_params.len < sizeof edd_params)
+ memset((char *)&edd_params + edd_params.len, 0,
+ sizeof edd_params - edd_params.len);
+ if (edd_params.sector_size >= 512 &&
+ is_power_of_2(edd_params.sector_size))
+ sector_size = edd_params.sector_size;
+ }
+ }
+
+ /* CBIOS parameters */
+ disk.h = bsHeads;
+ disk.s = bsSecPerTrack;
+
+ if ((int8_t)devno < 0) {
+ /* Get hard disk geometry from BIOS */
+
+ ireg.eax.b[1] = 0x08;
+ __intcall(0x13, &ireg, &oreg);
+
+ if (!(oreg.eflags.l & EFLAGS_CF)) {
+ disk.h = oreg.edx.b[1] + 1;
+ disk.s = oreg.ecx.b[0] & 63;
+ }
+ }
+
+ disk.disk_number = devno;
+ disk.type = ebios;
+ disk.sector_size = sector_size;
+ disk.sector_shift = ilog2(sector_size);
+ disk.part_start = part_start;
+ disk.rdwr_sectors = ebios ? edd_rdwr_sectors : chs_rdwr_sectors;
+
+ return &disk;
+}
+
+
+/*
+ * Initialize the device structure.
+ *
+ * NOTE: the disk cache needs to be revamped to support multiple devices...
+ */
+struct device * device_init(uint8_t devno, bool cdrom, sector_t part_start,
+ uint16_t bsHeads, uint16_t bsSecPerTrack)
+{
+ static struct device dev;
+ static __hugebss char diskcache[128*1024];
+
+ dev.disk = disk_init(devno, cdrom, part_start, bsHeads, bsSecPerTrack);
+
+ dev.cache_data = diskcache;
+ dev.cache_size = sizeof diskcache;
+
+ return &dev;
+}
diff --git a/core/fs/ext2/bmap.c b/core/fs/ext2/bmap.c
new file mode 100644
index 00000000..ef2bf649
--- /dev/null
+++ b/core/fs/ext2/bmap.c
@@ -0,0 +1,228 @@
+/*
+ * The logical block -> physical block routine.
+ *
+ * Copyright (C) 2009 Liu Aleaxander -- All rights reserved. This file
+ * may be redistributed under the terms of the GNU Public License.
+ */
+
+#include <stdio.h>
+#include <dprintf.h>
+#include <fs.h>
+#include <disk.h>
+#include <cache.h>
+#include "ext2_fs.h"
+
+static const struct ext4_extent_header *
+ext4_find_leaf(struct fs_info *fs, const struct ext4_extent_header *eh,
+ block_t block)
+{
+ struct ext4_extent_idx *index;
+ block_t blk;
+ int i;
+
+ while (1) {
+ if (eh->eh_magic != EXT4_EXT_MAGIC)
+ return NULL;
+ if (eh->eh_depth == 0)
+ return eh;
+
+ index = EXT4_FIRST_INDEX(eh);
+ for (i = 0; i < (int)eh->eh_entries; i++) {
+ if (block < index[i].ei_block)
+ break;
+ }
+ if (--i < 0)
+ return NULL;
+
+ blk = index[i].ei_leaf_hi;
+ blk = (blk << 32) + index[i].ei_leaf_lo;
+ eh = get_cache(fs->fs_dev, blk);
+ }
+}
+
+/* handle the ext4 extents to get the phsical block number */
+/* XXX: still need to handle sparse files with extents */
+static block_t
+bmap_extent(struct inode *inode, uint32_t block, size_t *nblocks)
+{
+ struct fs_info *fs = inode->fs;
+ const struct ext4_extent_header *leaf;
+ const struct ext4_extent *ext;
+ int i;
+ block_t start;
+
+ leaf = ext4_find_leaf(fs, &PVT(inode)->i_extent_hdr, block);
+ if (!leaf) {
+ printf("ERROR, extent leaf not found\n");
+ return 0;
+ }
+
+ ext = EXT4_FIRST_EXTENT(leaf);
+ for (i = 0; i < leaf->eh_entries; i++) {
+ if (block < ext[i].ee_block)
+ break;
+ }
+ if (--i < 0) {
+ printf("ERROR, not find the right block\n");
+ return 0;
+ }
+
+ /* got it */
+ block -= ext[i].ee_block;
+ if (block >= ext[i].ee_len)
+ return 0;
+ start = ((block_t)ext[i].ee_start_hi << 32) + ext[i].ee_start_lo;
+
+ if (nblocks)
+ *nblocks = ext[i].ee_len - block;
+
+ return start + block;
+}
+
+/*
+ * Scan forward in a range of blocks to see if they are contiguous,
+ * then return the initial value.
+ */
+static uint32_t
+scan_set_nblocks(const uint32_t *map, unsigned int count, size_t *nblocks)
+{
+ uint32_t blk = *map;
+
+ if (nblocks) {
+ uint32_t skip = blk ? 1 : 0;
+ uint32_t next = blk + skip;
+ size_t cnt = 1;
+
+ while (--count) {
+ map++;
+ if (*map == next) {
+ cnt++;
+ next += skip;
+ } else {
+ break;
+ }
+ }
+
+ *nblocks = cnt;
+ }
+
+ return blk;
+}
+
+/*
+ * The actual indirect block map handling - the block passed in should
+ * be relative to the beginning of the particular block hierarchy.
+ */
+static block_t
+bmap_indirect(struct fs_info *fs, uint32_t start, uint32_t block,
+ int levels, size_t *nblocks)
+{
+ int addr_shift = BLOCK_SHIFT(fs) - 2;
+ uint32_t addr_count = 1 << addr_shift;
+ const uint32_t *blk = NULL;
+ uint32_t index = 0;
+
+ while (levels--) {
+ if (!start) {
+ if (nblocks)
+ *nblocks = addr_count << (levels * addr_shift);
+ return 0;
+ }
+ blk = get_cache(fs->fs_dev, start);
+ index = (block >> (levels * addr_shift)) & (addr_count - 1);
+ start = blk[index];
+ }
+
+ return scan_set_nblocks(blk + index, addr_count - index, nblocks);
+}
+
+/*
+ * Handle the traditional block map, like indirect, double indirect
+ * and triple indirect
+ */
+static block_t
+bmap_traditional(struct inode *inode, block_t block, size_t *nblocks)
+{
+ struct fs_info *fs = inode->fs;
+ const uint32_t addr_per_block = BLOCK_SIZE(fs) >> 2;
+ const int shft_per_block = BLOCK_SHIFT(fs) - 2;
+ const uint32_t direct_blocks = EXT2_NDIR_BLOCKS;
+ const uint32_t indirect_blocks = addr_per_block;
+ const uint32_t double_blocks = addr_per_block << shft_per_block;
+ const uint32_t triple_blocks = double_blocks << shft_per_block;
+
+ /* direct blocks */
+ if (block < direct_blocks)
+ return scan_set_nblocks(&PVT(inode)->i_block[block],
+ direct_blocks - block, nblocks);
+
+ /* indirect blocks */
+ block -= direct_blocks;
+ if (block < indirect_blocks)
+ return bmap_indirect(fs, PVT(inode)->i_block[EXT2_IND_BLOCK],
+ block, 1, nblocks);
+
+ /* double indirect blocks */
+ block -= indirect_blocks;
+ if (block < double_blocks)
+ return bmap_indirect(fs, PVT(inode)->i_block[EXT2_DIND_BLOCK],
+ block, 2, nblocks);
+
+ /* triple indirect block */
+ block -= double_blocks;
+ if (block < triple_blocks)
+ return bmap_indirect(fs, PVT(inode)->i_block[EXT2_TIND_BLOCK],
+ block, 3, nblocks);
+
+ /* This can't happen... */
+ return 0;
+}
+
+
+/**
+ * Map the logical block to physic block where the file data stores.
+ * In EXT4, there are two ways to handle the map process, extents and indirect.
+ * EXT4 uses a inode flag to mark extent file and indirect block file.
+ *
+ * @fs: the fs_info structure.
+ * @inode: the inode structure.
+ * @block: the logical block to be mapped.
+ * @nblocks: optional pointer to number of contiguous blocks (low estimate)
+ * @retrun: the physical block number.
+ *
+ */
+block_t ext2_bmap(struct inode *inode, block_t block, size_t *nblocks)
+{
+ block_t ret;
+
+ if (inode->flags & EXT4_EXTENTS_FLAG)
+ ret = bmap_extent(inode, block, nblocks);
+ else
+ ret = bmap_traditional(inode, block, nblocks);
+
+ return ret;
+}
+
+
+/*
+ * Next extent for getfssec
+ */
+int ext2_next_extent(struct inode *inode, uint32_t lstart)
+{
+ struct fs_info *fs = inode->fs;
+ int blktosec = BLOCK_SHIFT(fs) - SECTOR_SHIFT(fs);
+ int blkmask = (1 << blktosec) - 1;
+ block_t block;
+ size_t nblocks = 0;
+
+ block = ext2_bmap(inode, lstart >> blktosec, &nblocks);
+
+ if (!block)
+ inode->next_extent.pstart = EXTENT_ZERO;
+ else
+ inode->next_extent.pstart =
+ ((sector_t)block << blktosec) | (lstart & blkmask);
+
+ inode->next_extent.len = (nblocks << blktosec) - (lstart & blkmask);
+ return 0;
+}
diff --git a/core/fs/ext2/ext2.c b/core/fs/ext2/ext2.c
new file mode 100644
index 00000000..716670c6
--- /dev/null
+++ b/core/fs/ext2/ext2.c
@@ -0,0 +1,335 @@
+#include <dprintf.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/dirent.h>
+#include <minmax.h>
+#include "cache.h"
+#include "core.h"
+#include "disk.h"
+#include "fs.h"
+#include "ext2_fs.h"
+
+/*
+ * Convert an ext2 file type to the global values
+ */
+static enum dirent_type ext2_cvt_type(unsigned int d_file_type)
+{
+ static const enum dirent_type inode_type[] = {
+ DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR,
+ DT_BLK, DT_FIFO, DT_SOCK, DT_LNK,
+ };
+
+ if (d_file_type > sizeof inode_type / sizeof *inode_type)
+ return DT_UNKNOWN;
+ else
+ return inode_type[d_file_type];
+}
+
+/*
+ * get the group's descriptor of group_num
+ */
+static const struct ext2_group_desc *
+ext2_get_group_desc(struct fs_info *fs, uint32_t group_num)
+{
+ struct ext2_sb_info *sbi = EXT2_SB(fs);
+ uint32_t desc_block, desc_index;
+ const struct ext2_group_desc *desc_data_block;
+
+ if (group_num >= sbi->s_groups_count) {
+ printf ("ext2_get_group_desc"
+ "block_group >= groups_count - "
+ "block_group = %d, groups_count = %d",
+ group_num, sbi->s_groups_count);
+
+ return NULL;
+ }
+
+ desc_block = group_num / sbi->s_desc_per_block;
+ desc_index = group_num % sbi->s_desc_per_block;
+
+ desc_block += sbi->s_first_data_block + 1;
+
+ desc_data_block = get_cache(fs->fs_dev, desc_block);
+ return &desc_data_block[desc_index];
+}
+
+/*
+ * Unlike strncmp, ext2_match_entry returns 1 for success, 0 for failure.
+ */
+static inline bool ext2_match_entry(const char *name, size_t len,
+ const struct ext2_dir_entry * de)
+{
+ if (!de->d_inode)
+ return false;
+ if (len != de->d_name_len)
+ return false;
+ return !memcmp(name, de->d_name, len);
+}
+
+
+/*
+ * p is at least 6 bytes before the end of page
+ */
+static inline struct ext2_dir_entry *ext2_next_entry(struct ext2_dir_entry *p)
+{
+ return (struct ext2_dir_entry *)((char*)p + p->d_rec_len);
+}
+
+/*
+ * Map a logical sector and load it into the cache
+ */
+static const void *
+ext2_get_cache(struct inode *inode, block_t lblock)
+{
+ block_t pblock = ext2_bmap(inode, lblock, NULL);
+ return get_cache(inode->fs->fs_dev, pblock);
+}
+
+/*
+ * find a dir entry, return it if found, or return NULL.
+ */
+static const struct ext2_dir_entry *
+ext2_find_entry(struct fs_info *fs, struct inode *inode, const char *dname)
+{
+ block_t index = 0;
+ uint32_t i = 0, offset, maxoffset;
+ const struct ext2_dir_entry *de;
+ const char *data;
+ size_t dname_len = strlen(dname);
+
+ while (i < inode->size) {
+ data = ext2_get_cache(inode, index++);
+ offset = 0;
+ maxoffset = min(BLOCK_SIZE(fs), i-inode->size);
+
+ /* The smallest possible size is 9 bytes */
+ while (offset < maxoffset-8) {
+ de = (const struct ext2_dir_entry *)(data + offset);
+ if (de->d_rec_len > maxoffset - offset)
+ break;
+
+ if (ext2_match_entry(dname, dname_len, de))
+ return de;
+
+ offset += de->d_rec_len;
+ }
+ i += BLOCK_SIZE(fs);
+ }
+
+ return NULL;
+}
+
+static const struct ext2_inode *
+ext2_get_inode(struct fs_info *fs, int inr)
+{
+ const struct ext2_group_desc *desc;
+ const char *data;
+ uint32_t inode_group, inode_offset;
+ uint32_t block_num, block_off;
+
+ inr--;
+ inode_group = inr / EXT2_INODES_PER_GROUP(fs);
+ inode_offset = inr % EXT2_INODES_PER_GROUP(fs);
+ desc = ext2_get_group_desc(fs, inode_group);
+ if (!desc)
+ return NULL;
+
+ block_num = desc->bg_inode_table +
+ inode_offset / EXT2_INODES_PER_BLOCK(fs);
+ block_off = inode_offset % EXT2_INODES_PER_BLOCK(fs);
+
+ data = get_cache(fs->fs_dev, block_num);
+
+ return (const struct ext2_inode *)
+ (data + block_off * EXT2_SB(fs)->s_inode_size);
+}
+
+static void fill_inode(struct inode *inode, const struct ext2_inode *e_inode)
+{
+ inode->mode = IFTODT(e_inode->i_mode);
+ inode->size = e_inode->i_size;
+ inode->atime = e_inode->i_atime;
+ inode->ctime = e_inode->i_ctime;
+ inode->mtime = e_inode->i_mtime;
+ inode->dtime = e_inode->i_dtime;
+ inode->blocks = e_inode->i_blocks;
+ inode->flags = e_inode->i_flags;
+ inode->file_acl = e_inode->i_file_acl;
+ memcpy(PVT(inode)->i_block, e_inode->i_block, sizeof PVT(inode)->i_block);
+}
+
+static struct inode *ext2_iget_by_inr(struct fs_info *fs, uint32_t inr)
+{
+ const struct ext2_inode *e_inode;
+ struct inode *inode;
+
+ e_inode = ext2_get_inode(fs, inr);
+ if (!(inode = alloc_inode(fs, inr, sizeof(struct ext2_pvt_inode))))
+ return NULL;
+ fill_inode(inode, e_inode);
+
+ return inode;
+}
+
+static struct inode *ext2_iget_root(struct fs_info *fs)
+{
+ return ext2_iget_by_inr(fs, EXT2_ROOT_INO);
+}
+
+static struct inode *ext2_iget(const char *dname, struct inode *parent)
+{
+ const struct ext2_dir_entry *de;
+ struct fs_info *fs = parent->fs;
+
+ de = ext2_find_entry(fs, parent, dname);
+ if (!de)
+ return NULL;
+
+ return ext2_iget_by_inr(fs, de->d_inode);
+}
+
+/*
+ * Read the entire contents of an inode into a memory buffer
+ */
+static int cache_get_file(struct inode *inode, void *buf, size_t bytes)
+{
+ struct fs_info *fs = inode->fs;
+ size_t block_size = BLOCK_SIZE(fs);
+ uint32_t index = 0; /* Logical block number */
+ size_t chunk;
+ const char *data;
+ char *p = buf;
+
+ if (inode->size > bytes)
+ bytes = inode->size;
+
+ while (bytes) {
+ chunk = min(bytes, block_size);
+ data = ext2_get_cache(inode, index++);
+ memcpy(p, data, chunk);
+
+ bytes -= chunk;
+ p += chunk;
+ }
+
+ return 0;
+}
+
+static int ext2_readlink(struct inode *inode, char *buf)
+{
+ struct fs_info *fs = inode->fs;
+ int sec_per_block = 1 << (fs->block_shift - fs->sector_shift);
+ bool fast_symlink;
+
+ if (inode->size > BLOCK_SIZE(fs))
+ return -1; /* Error! */
+
+ fast_symlink = (inode->file_acl ? sec_per_block : 0) == inode->blocks;
+ if (fast_symlink)
+ memcpy(buf, PVT(inode)->i_block, inode->size);
+ else
+ cache_get_file(inode, buf, inode->size);
+
+ return inode->size;
+}
+
+/*
+ * Read one directory entry at a time
+ */
+static int ext2_readdir(struct file *file, struct dirent *dirent)
+{
+ struct fs_info *fs = file->fs;
+ struct inode *inode = file->inode;
+ const struct ext2_dir_entry *de;
+ const char *data;
+ block_t index = file->offset >> fs->block_shift;
+
+ if (file->offset >= inode->size)
+ return -1; /* End of file */
+
+ data = ext2_get_cache(inode, index);
+ de = (const struct ext2_dir_entry *)
+ (data + (file->offset & (BLOCK_SIZE(fs) - 1)));
+
+ dirent->d_ino = de->d_inode;
+ dirent->d_off = file->offset;
+ dirent->d_reclen = offsetof(struct dirent, d_name) + de->d_name_len + 1;
+ dirent->d_type = ext2_cvt_type(de->d_file_type);
+ memcpy(dirent->d_name, de->d_name, de->d_name_len);
+ dirent->d_name[de->d_name_len] = '\0';
+
+ file->offset += de->d_rec_len; /* Update for next reading */
+
+ return 0;
+}
+
+/*
+ * init. the fs meta data, return the block size bits.
+ */
+static int ext2_fs_init(struct fs_info *fs)
+{
+ struct disk *disk = fs->fs_dev->disk;
+ struct ext2_sb_info *sbi;
+ struct ext2_super_block sb;
+ struct cache *cs;
+
+ /* read the super block */
+ disk->rdwr_sectors(disk, &sb, 2, 2, 0);
+
+ /* check if it is ext2, since we also support btrfs now */
+ if (sb.s_magic != EXT2_SUPER_MAGIC)
+ return -1;
+
+ sbi = malloc(sizeof(*sbi));
+ if (!sbi) {
+ malloc_error("ext2_sb_info structure");
+ return -1;
+ }
+ fs->fs_info = sbi;
+
+ if (sb.s_magic != EXT2_SUPER_MAGIC) {
+ printf("ext2 mount error: it's not a EXT2/3/4 file system!\n");
+ return 0;
+ }
+
+ fs->sector_shift = disk->sector_shift;
+ fs->block_shift = sb.s_log_block_size + 10;
+ fs->sector_size = 1 << fs->sector_shift;
+ fs->block_size = 1 << fs->block_shift;
+
+ sbi->s_inodes_per_group = sb.s_inodes_per_group;
+ sbi->s_blocks_per_group = sb.s_blocks_per_group;
+ sbi->s_inodes_per_block = BLOCK_SIZE(fs) / sb.s_inode_size;
+ if (sb.s_desc_size < sizeof(struct ext2_group_desc))
+ sb.s_desc_size = sizeof(struct ext2_group_desc);
+ sbi->s_desc_per_block = BLOCK_SIZE(fs) / sb.s_desc_size;
+ sbi->s_groups_count = (sb.s_blocks_count - sb.s_first_data_block
+ + EXT2_BLOCKS_PER_GROUP(fs) - 1)
+ / EXT2_BLOCKS_PER_GROUP(fs);
+ sbi->s_first_data_block = sb.s_first_data_block;
+ sbi->s_inode_size = sb.s_inode_size;
+
+ /* Initialize the cache, and force block zero to all zero */
+ cache_init(fs->fs_dev, fs->block_shift);
+ cs = _get_cache_block(fs->fs_dev, 0);
+ memset(cs->data, 0, fs->block_size);
+ cache_lock_block(cs);
+
+ return fs->block_shift;
+}
+
+const struct fs_ops ext2_fs_ops = {
+ .fs_name = "ext2",
+ .fs_flags = FS_THISIND | FS_USEMEM,
+ .fs_init = ext2_fs_init,
+ .searchdir = NULL,
+ .getfssec = generic_getfssec,
+ .close_file = generic_close_file,
+ .mangle_name = generic_mangle_name,
+ .load_config = generic_load_config,
+ .iget_root = ext2_iget_root,
+ .iget = ext2_iget,
+ .readlink = ext2_readlink,
+ .readdir = ext2_readdir,
+ .next_extent = ext2_next_extent,
+};
diff --git a/core/fs/ext2/ext2_fs.h b/core/fs/ext2/ext2_fs.h
new file mode 100644
index 00000000..8adc9bbe
--- /dev/null
+++ b/core/fs/ext2/ext2_fs.h
@@ -0,0 +1,310 @@
+#ifndef __EXT2_FS_H
+#define __EXT2_FS_H
+
+#include <stdint.h>
+
+#define EXT2_SUPER_MAGIC 0xEF53
+
+#define EXT2_GOOD_OLD_REV 0 // The good old (original) format
+#define EXT2_DYNAMIC_REV 1 // V2 format w/ dynamic inode sizes
+#define EXT2_GOOD_OLD_INODE_SIZE 128
+
+// Special inode numbers
+#define EXT2_BAD_INO 1 // Bad blocks inode
+#define EXT2_ROOT_INO 2 // Root inode
+#define EXT2_BOOT_LOADER_INO 5 // Boot loader inode
+#define EXT2_UNDEL_DIR_INO 6 // Undelete directory inode
+#define EXT3_RESIZE_INO 7 // Reserved group descriptors inode
+#define EXT3_JOURNAL_INO 8 // Journal inode
+
+// We're readonly, so we only care about incompat features.
+#define EXT2_FEATURE_INCOMPAT_COMPRESSION 0x0001
+#define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002
+#define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004
+#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008
+#define EXT2_FEATURE_INCOMPAT_META_BG 0x0010
+#define EXT2_FEATURE_INCOMPAT_ANY 0xffffffff
+
+#define EXT2_NDIR_BLOCKS 12
+#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS
+#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK+1)
+#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK+1)
+#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK+1)
+
+
+/* for EXT4 extent */
+#define EXT4_EXT_MAGIC 0xf30a
+#define EXT4_EXTENTS_FLAG 0x00080000
+
+/*
+ * File types and file modes
+ */
+#define S_IFDIR 0040000 // Directory
+#define S_IFCHR 0020000 // Character device
+#define S_IFBLK 0060000 // Block device
+#define S_IFREG 0100000 // Regular file
+#define S_IFIFO 0010000 // FIFO
+#define S_IFLNK 0120000 // Symbolic link
+#define S_IFSOCK 0140000 // Socket
+
+#define S_IFSHIFT 12
+
+#define T_IFDIR (S_IFDIR >> S_IFSHIFT)
+#define T_IFCHR (S_IFCHR >> S_IFSHIFT)
+#define T_IFBLK (S_IFBLK >> S_IFSHIFT)
+#define T_IFREG (S_IFREG >> S_IFSHIFT)
+#define T_IFIFO (S_IFIFO >> S_IFSHIFT)
+#define T_IFLNK (S_IFLNK >> S_IFSHIFT)
+#define T_IFSOCK (S_IFSOCK >> S_IFSHIFT)
+
+
+#define ext2_group_desc_lg2size 5
+
+
+
+/*
+ * super block structure:
+ * include/linux/ext2_fs.h
+ */
+struct ext2_super_block {
+ uint32_t s_inodes_count; /* Inodes count */
+ uint32_t s_blocks_count; /* Blocks count */
+ uint32_t s_r_blocks_count; /* Reserved blocks count */
+ uint32_t s_free_blocks_count; /* Free blocks count */
+ uint32_t s_free_inodes_count; /* Free inodes count */
+ uint32_t s_first_data_block; /* First Data Block */
+ uint32_t s_log_block_size; /* Block size */
+ uint32_t s_log_frag_size; /* Fragment size */
+ uint32_t s_blocks_per_group; /* # Blocks per group */
+ uint32_t s_frags_per_group; /* # Fragments per group */
+ uint32_t s_inodes_per_group; /* # Inodes per group */
+ uint32_t s_mtime; /* Mount time */
+ uint32_t s_wtime; /* Write time */
+ uint16_t s_mnt_count; /* Mount count */
+ int16_t s_max_mnt_count; /* Maximal mount count */
+ uint16_t s_magic; /* Magic signature */
+ uint16_t s_state; /* File system state */
+ uint16_t s_errors; /* Behaviour when detecting errors */
+ uint16_t s_minor_rev_level;
+ uint32_t s_lastcheck; /* time of last check */
+ uint32_t s_checkinterval; /* max. time between checks */
+ uint32_t s_creator_os; /* OS */
+ uint32_t s_rev_level; /* Revision level */
+ uint16_t s_def_resuid; /* Default uid for reserved blocks */
+ uint16_t s_def_resgid; /* Default gid for reserved blocks */
+
+ uint32_t s_first_ino; /* First non-reserved inode */
+ uint16_t s_inode_size; /* size of inode structure */
+ uint16_t s_block_group_nr; /* block group # of this superblock */
+ uint32_t s_feature_compat; /* compatible feature set */
+ uint32_t s_feature_incompat; /* incompatible feature set */
+ uint32_t s_feature_ro_compat; /* readonly-compatible feature set */
+ uint8_t s_uuid[16]; /* 128-bit uuid for volume */
+ char s_volume_name[16]; /* volume name */
+ char s_last_mounted[64]; /* directory where last mounted */
+ uint32_t s_algorithm_usage_bitmap; /* For compression */
+ uint8_t s_prealloc_blocks; /* Nr of blocks to try to preallocate*/
+ uint8_t s_prealloc_dir_blocks;
+ uint16_t s_reserved_gdt_blocks; /* Per group desc for online growth */
+ /*
+ * Journaling support valid if EXT4_FEATURE_COMPAT_HAS_JOURNAL set.
+ */
+ uint8_t s_journal_uuid[16]; /* uuid of journal superblock */
+ uint32_t s_journal_inum; /* inode number of journal file */
+ uint32_t s_journal_dev; /* device number of journal file */
+ uint32_t s_last_orphan; /* start of list of inodes to delete */
+ uint32_t s_hash_seed[4]; /* HTREE hash seed */
+ uint8_t s_def_hash_version; /* Default hash version to use */
+ uint8_t s_reserved_char_pad;
+ uint16_t s_desc_size; /* size of group descriptor */
+ uint32_t s_default_mount_opts;
+ uint32_t s_first_meta_bg; /* First metablock block group */
+ uint32_t s_mkfs_time; /* When the filesystem was created */
+ uint32_t s_jnl_blocks[17]; /* Backup of the journal inode */
+ /* 64bit support valid if EXT4_FEATURE_COMPAT_64BIT */
+ uint32_t s_blocks_count_hi; /* Blocks count */
+ uint32_t s_r_blocks_count_hi; /* Reserved blocks count */
+ uint32_t s_free_blocks_count_hi;/* Free blocks count */
+ uint16_t s_min_extra_isize; /* All inodes have at least # bytes */
+ uint16_t s_want_extra_isize; /* New inodes should reserve # bytes */
+ uint32_t s_flags; /* Miscellaneous flags */
+ uint16_t s_raid_stride; /* RAID stride */
+ uint16_t s_mmp_interval; /* # seconds to wait in MMP checking */
+ uint64_t s_mmp_block; /* Block for multi-mount protection */
+ uint32_t s_raid_stripe_width; /* blocks on all data disks (N*stride)*/
+ uint8_t s_log_groups_per_flex; /* FLEX_BG group size */
+ uint8_t s_reserved_char_pad2;
+ uint16_t s_reserved_pad;
+ uint32_t s_reserved[162]; /* Padding to the end of the block */
+};
+
+/*******************************************************************************
+#ifndef DEPEND
+#if ext2_super_block_size != 1024
+#error ext2_super_block definition bogus
+#endif
+#endif
+*******************************************************************************/
+
+/*
+ * ext2 group desc structure:
+ */
+struct ext2_group_desc {
+ uint32_t bg_block_bitmap; /* Blocks bitmap block */
+ uint32_t bg_inode_bitmap; /* Inodes bitmap block */
+ uint32_t bg_inode_table; /* Inodes table block */
+ uint16_t bg_free_blocks_count; /* Free blocks count */
+ uint16_t bg_free_inodes_count; /* Free inodes count */
+ uint16_t bg_used_dirs_count; /* Directories count */
+ uint16_t bg_pad;
+ uint32_t bg_reserved[3];
+};
+
+/*******************************************************************************
+#ifndef DEPEND
+#if ext2_group_desc_size != 32
+#error ext2_group_desc definition bogus
+#endif
+#endif
+*******************************************************************************/
+
+
+/*
+ * ext2 inode structure:
+ */
+struct ext2_inode {
+ uint16_t i_mode; /* File mode */
+ uint16_t i_uid; /* Owner Uid */
+ uint32_t i_size; /* 4: Size in bytes */
+ uint32_t i_atime; /* Access time */
+ uint32_t i_ctime; /* 12: Creation time */
+ uint32_t i_mtime; /* Modification time */
+ uint32_t i_dtime; /* 20: Deletion Time */
+ uint16_t i_gid; /* Group Id */
+ uint16_t i_links_count; /* 24: Links count */
+ uint32_t i_blocks; /* Blocks count */
+ uint32_t i_flags; /* 32: File flags */
+ uint32_t l_i_reserved1;
+ uint32_t i_block[EXT2_N_BLOCKS]; /* 40: Pointers to blocks */
+ uint32_t i_version; /* File version (for NFS) */
+ uint32_t i_file_acl; /* File ACL */
+ uint32_t i_dir_acl; /* Directory ACL */
+ uint32_t i_faddr; /* Fragment address */
+ uint8_t l_i_frag; /* Fragment number */
+ uint8_t l_i_fsize; /* Fragment size */
+ uint16_t i_pad1;
+ uint32_t l_i_reserved2[2];
+};
+
+/*******************************************************************************
+#ifndef DEPEND
+#if ext2_inode_size != 128
+#error ext2_inode definition bogus
+#endif
+#endif
+*******************************************************************************/
+
+
+#define EXT2_NAME_LEN 255
+struct ext2_dir_entry {
+ unsigned int d_inode; /* Inode number */
+ unsigned short d_rec_len; /* Directory entry length */
+ unsigned char d_name_len; /* Name length */
+ unsigned char d_file_type;
+ char d_name[EXT2_NAME_LEN]; /* File name */
+};
+
+/*******************************************************************************
+#define EXT2_DIR_PAD 4
+#define EXT2_DIR_ROUND (EXT2_DIR_PAD - 1)
+#define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \
+ ~EXT2_DIR_ROUND)
+*******************************************************************************/
+
+
+
+
+
+
+/*
+ * This is the extent on-disk structure.
+ * It's used at the bottom of the tree.
+ */
+struct ext4_extent {
+ uint32_t ee_block; /* first logical block extent covers */
+ uint16_t ee_len; /* number of blocks covered by extent */
+ uint16_t ee_start_hi; /* high 16 bits of physical block */
+ uint32_t ee_start_lo; /* low 32 bits of physical block */
+};
+
+/*
+ * This is index on-disk structure.
+ * It's used at all the levels except the bottom.
+ */
+struct ext4_extent_idx {
+ uint32_t ei_block; /* index covers logical blocks from 'block' */
+ uint32_t ei_leaf_lo; /* pointer to the physical block of the next *
+ * level. leaf or next index could be there */
+ uint16_t ei_leaf_hi; /* high 16 bits of physical block */
+ uint16_t ei_unused;
+};
+
+/*
+ * Each block (leaves and indexes), even inode-stored has header.
+ */
+struct ext4_extent_header {
+ uint16_t eh_magic; /* probably will support different formats */
+ uint16_t eh_entries; /* number of valid entries */
+ uint16_t eh_max; /* capacity of store in entries */
+ uint16_t eh_depth; /* has tree real underlying blocks? */
+ uint32_t eh_generation; /* generation of the tree */
+};
+
+
+
+#define EXT4_FIRST_EXTENT(header) ( (struct ext4_extent *)(header + 1) )
+#define EXT4_FIRST_INDEX(header) ( (struct ext4_extent_idx *) (header + 1) )
+
+
+/*
+ * The ext2 super block information in memory
+ */
+struct ext2_sb_info {
+ uint32_t s_inodes_per_block;/* Number of inodes per block */
+ uint32_t s_inodes_per_group;/* Number of inodes in a group */
+ uint32_t s_blocks_per_group;/* Number of blocks in a group */
+ uint32_t s_desc_per_block; /* Number of group descriptors per block */
+ uint32_t s_groups_count; /* Number of groups in the fs */
+ uint32_t s_first_data_block; /* First Data Block */
+ int s_inode_size;
+};
+
+static inline struct ext2_sb_info *EXT2_SB(struct fs_info *fs)
+{
+ return fs->fs_info;
+}
+
+#define EXT2_BLOCKS_PER_GROUP(fs) (EXT2_SB(fs)->s_blocks_per_group)
+#define EXT2_INODES_PER_GROUP(fs) (EXT2_SB(fs)->s_inodes_per_group)
+#define EXT2_INODES_PER_BLOCK(fs) (EXT2_SB(fs)->s_inodes_per_block)
+#define EXT2_DESC_PER_BLOCK(fs) (EXT2_SB(fs)->s_desc_per_block)
+
+/*
+ * ext2 private inode information
+ */
+struct ext2_pvt_inode {
+ union {
+ uint32_t i_block[EXT2_N_BLOCKS];
+ struct ext4_extent_header i_extent_hdr;
+ };
+};
+
+#define PVT(i) ((struct ext2_pvt_inode *)((i)->pvt))
+
+/*
+ * functions
+ */
+block_t ext2_bmap(struct inode *, block_t, size_t *);
+int ext2_next_extent(struct inode *, uint32_t);
+
+#endif /* ext2_fs.h */
diff --git a/core/fs/fat/fat.c b/core/fs/fat/fat.c
new file mode 100644
index 00000000..029c2d4a
--- /dev/null
+++ b/core/fs/fat/fat.c
@@ -0,0 +1,826 @@
+#include <dprintf.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/dirent.h>
+#include <cache.h>
+#include <core.h>
+#include <disk.h>
+#include <fs.h>
+#include <klibc/compiler.h>
+#include "codepage.h"
+#include "fat_fs.h"
+
+static struct inode * new_fat_inode(struct fs_info *fs)
+{
+ struct inode *inode = alloc_inode(fs, 0, sizeof(struct fat_pvt_inode));
+ if (!inode)
+ malloc_error("inode structure");
+
+ return inode;
+}
+
+
+/*
+ * Check for a particular sector in the FAT cache
+ */
+static const void *get_fat_sector(struct fs_info *fs, sector_t sector)
+{
+ return get_cache(fs->fs_dev, FAT_SB(fs)->fat + sector);
+}
+
+static uint32_t get_next_cluster(struct fs_info *fs, uint32_t clust_num)
+{
+ uint32_t next_cluster = 0;
+ sector_t fat_sector;
+ uint32_t offset;
+ int lo, hi;
+ uint32_t sector_mask = SECTOR_SIZE(fs) - 1;
+ const uint8_t *data;
+
+ switch(FAT_SB(fs)->fat_type) {
+ case FAT12:
+ offset = clust_num + (clust_num >> 1);
+ fat_sector = offset >> SECTOR_SHIFT(fs);
+ offset &= sector_mask;
+ data = get_fat_sector(fs, fat_sector);
+ if (offset == sector_mask) {
+ /*
+ * we got the end of the one fat sector,
+ * but we have just one byte and we need two,
+ * so store the low part, then read the next fat
+ * sector, read the high part, then combine it.
+ */
+ lo = data[offset];
+ data = get_fat_sector(fs, fat_sector + 1);
+ hi = data[0];
+ next_cluster = (hi << 8) + lo;
+ } else {
+ next_cluster = *(const uint16_t *)(data + offset);
+ }
+
+ if (clust_num & 0x0001)
+ next_cluster >>= 4; /* cluster number is ODD */
+ else
+ next_cluster &= 0x0fff; /* cluster number is EVEN */
+ break;
+
+ case FAT16:
+ offset = clust_num << 1;
+ fat_sector = offset >> SECTOR_SHIFT(fs);
+ offset &= sector_mask;
+ data = get_fat_sector(fs, fat_sector);
+ next_cluster = *(const uint16_t *)(data + offset);
+ break;
+
+ case FAT32:
+ offset = clust_num << 2;
+ fat_sector = offset >> SECTOR_SHIFT(fs);
+ offset &= sector_mask;
+ data = get_fat_sector(fs, fat_sector);
+ next_cluster = *(const uint32_t *)(data + offset);
+ next_cluster &= 0x0fffffff;
+ break;
+ }
+
+ return next_cluster;
+}
+
+static int fat_next_extent(struct inode *inode, uint32_t lstart)
+{
+ struct fs_info *fs = inode->fs;
+ struct fat_sb_info *sbi = FAT_SB(fs);
+ uint32_t mcluster = lstart >> sbi->clust_shift;
+ uint32_t lcluster;
+ uint32_t pcluster;
+ uint32_t tcluster;
+ uint32_t cluster_size = UINT32_C(1) << sbi->clust_shift;
+ sector_t data_area = sbi->data;
+
+ tcluster = (inode->size + cluster_size - 1) >> sbi->clust_shift;
+ if (mcluster >= tcluster)
+ goto err; /* Requested cluster beyond end of file */
+
+ if (inode->next_extent.len) {
+ if (inode->next_extent.pstart < data_area)
+ goto err; /* Root directory has only one extent */
+ lcluster = (inode->next_extent.lstart + inode->next_extent.len)
+ >> sbi->clust_shift;
+ pcluster = ((inode->next_extent.pstart + inode->next_extent.len
+ - data_area) >> sbi->clust_shift) + 2;
+
+ if (lcluster > mcluster) {
+ lcluster = 0;
+ pcluster = PVT(inode)->start_cluster;
+ }
+ } else {
+ lcluster = 0;
+ pcluster = PVT(inode)->start_cluster;
+ }
+
+ for (;;) {
+ if (pcluster-2 >= sbi->clusters) {
+ inode->size = lcluster << sbi->clust_shift;
+ goto err;
+ }
+
+ if (lcluster >= mcluster)
+ break;
+
+ lcluster++;
+ pcluster = get_next_cluster(fs, pcluster);
+ }
+
+ inode->next_extent.pstart =
+ ((sector_t)(pcluster-2) << sbi->clust_shift) + data_area;
+ inode->next_extent.len = cluster_size;
+ lcluster++;
+
+ while (lcluster < tcluster) {
+ uint32_t xcluster;
+ xcluster = get_next_cluster(fs, pcluster);
+ if (xcluster != pcluster+1)
+ break; /* Not contiguous */
+ pcluster = xcluster;
+ inode->next_extent.len += cluster_size;
+ lcluster++;
+ }
+ return 0;
+
+err:
+ return -1;
+}
+
+static sector_t get_next_sector(struct fs_info* fs, uint32_t sector)
+{
+ struct fat_sb_info *sbi = FAT_SB(fs);
+ sector_t data_area = sbi->data;
+ sector_t data_sector;
+ uint32_t cluster;
+ int clust_shift = sbi->clust_shift;
+
+ if (sector < data_area) {
+ /* Root directory sector... */
+ sector++;
+ if (sector >= data_area)
+ sector = 0; /* Ran out of root directory, return EOF */
+ return sector;
+ }
+
+ data_sector = sector - data_area;
+ if ((data_sector + 1) & sbi->clust_mask) /* Still in the same cluster */
+ return sector + 1; /* Next sector inside cluster */
+
+ /* get a new cluster */
+ cluster = data_sector >> clust_shift;
+ cluster = get_next_cluster(fs, cluster + 2) - 2;
+
+ if (cluster >= sbi->clusters)
+ return 0;
+
+ /* return the start of the new cluster */
+ sector = (cluster << clust_shift) + data_area;
+ return sector;
+}
+
+/*
+ * Here comes the place I don't like VFAT fs most; if we need seek
+ * the file to the right place, we need get the right sector address
+ * from begining everytime! Since it's a kind a signle link list, we
+ * need to traver from the head-node to find the right node in that list.
+ *
+ * What a waste of time!
+ */
+static sector_t get_the_right_sector(struct file *file)
+{
+ struct inode *inode = file->inode;
+ uint32_t sector_pos = file->offset >> SECTOR_SHIFT(file->fs);
+ uint32_t where;
+ sector_t sector;
+
+ if (sector_pos < PVT(inode)->offset) {
+ /* Reverse seek */
+ where = 0;
+ sector = PVT(inode)->start;
+ } else {
+ where = PVT(inode)->offset;
+ sector = PVT(inode)->here;
+ }
+
+ while (where < sector_pos) {
+ sector = get_next_sector(file->fs, sector);
+ where++;
+ }
+
+ PVT(inode)->offset = sector_pos;
+ PVT(inode)->here = sector;
+
+ return sector;
+}
+
+/*
+ * Get the next sector in sequence
+ */
+static sector_t next_sector(struct file *file)
+{
+ struct inode *inode = file->inode;
+ sector_t sector = get_next_sector(file->fs, PVT(inode)->here);
+ PVT(inode)->offset++;
+ PVT(inode)->here = sector;
+
+ return sector;
+}
+
+/*
+ * Mangle a filename pointed to by src into a buffer pointed to by dst;
+ * ends on encountering any whitespace.
+ *
+ */
+static void vfat_mangle_name(char *dst, const char *src)
+{
+ char *p = dst;
+ char c;
+ int i = FILENAME_MAX -1;
+
+ /*
+ * Copy the filename, converting backslash to slash and
+ * collapsing duplicate separators.
+ */
+ while (not_whitespace(c = *src)) {
+ if (c == '\\')
+ c = '/';
+
+ if (c == '/') {
+ if (src[1] == '/' || src[1] == '\\') {
+ src++;
+ i--;
+ continue;
+ }
+ }
+ i--;
+ *dst++ = *src++;
+ }
+
+ /* Strip terminal slashes or whitespace */
+ while (1) {
+ if (dst == p)
+ break;
+ if (*(dst-1) == '/' && dst-1 == p) /* it's the '/' case */
+ break;
+ if ((*(dst-1) != '/') && (*(dst-1) != '.'))
+ break;
+
+ dst--;
+ i++;
+ }
+
+ i++;
+ for (; i > 0; i --)
+ *dst++ = '\0';
+}
+
+/*
+ * Mangle a normal style string to DOS style string.
+ */
+static void mangle_dos_name(char *mangle_buf, const char *src)
+{
+ int i;
+ unsigned char c;
+
+ i = 0;
+ while (i < 11) {
+ c = *src++;
+
+ if ((c <= ' ') || (c == '/'))
+ break;
+
+ if (c == '.') {
+ while (i < 8)
+ mangle_buf[i++] = ' ';
+ i = 8;
+ continue;
+ }
+
+ c = codepage.upper[c];
+ if (i == 0 && c == 0xe5)
+ c = 0x05; /* Special hack for the first byte only! */
+
+ mangle_buf[i++] = c;
+ }
+ while (i < 11)
+ mangle_buf[i++] = ' ';
+
+ mangle_buf[i] = '\0';
+}
+
+/*
+ * Match a string name against a longname. "len" is the number of
+ * codepoints in the input; including padding.
+ *
+ * Returns true on match.
+ */
+static bool vfat_match_longname(const char *str, const uint16_t *match,
+ int len)
+{
+ unsigned char c = -1; /* Nonzero: we have not yet seen NUL */
+ uint16_t cp;
+
+ dprintf("Matching: %s\n", str);
+
+ while (len) {
+ cp = *match++;
+ len--;
+ if (!cp)
+ break;
+ c = *str++;
+ if (cp != codepage.uni[0][c] && cp != codepage.uni[1][c])
+ return false; /* Also handles c == '\0' */
+ }
+
+ /* This should have been the end of the matching string */
+ if (*str)
+ return false;
+
+ /* Any padding entries must be FFFF */
+ while (len--)
+ if (*match++ != 0xffff)
+ return false;
+
+ return true;
+}
+
+/*
+ * Convert an UTF-16 longname to the system codepage; return
+ * the length on success or -1 on failure.
+ */
+static int vfat_cvt_longname(char *entry_name, const uint16_t *long_name)
+{
+ struct unicache {
+ uint16_t utf16;
+ uint8_t cp;
+ };
+ static struct unicache unicache[256];
+ struct unicache *uc;
+ uint16_t cp;
+ unsigned int c;
+ char *p = entry_name;
+
+ do {
+ cp = *long_name++;
+ uc = &unicache[cp % 256];
+
+ if (__likely(uc->utf16 == cp)) {
+ *p++ = uc->cp;
+ } else {
+ for (c = 0; c < 512; c++) {
+ /* This is a bit hacky... */
+ if (codepage.uni[0][c] == cp) {
+ uc->utf16 = cp;
+ *p++ = uc->cp = (uint8_t)c;
+ goto found;
+ }
+ }
+ return -1; /* Impossible character */
+ found:
+ ;
+ }
+ } while (cp);
+
+ return (p-entry_name)-1;
+}
+
+static void copy_long_chunk(uint16_t *buf, const struct fat_dir_entry *de)
+{
+ const struct fat_long_name_entry *le =
+ (const struct fat_long_name_entry *)de;
+
+ memcpy(buf, le->name1, 5 * 2);
+ memcpy(buf + 5, le->name2, 6 * 2);
+ memcpy(buf + 11, le->name3, 2 * 2);
+}
+
+static uint8_t get_checksum(const char *dir_name)
+{
+ int i;
+ uint8_t sum = 0;
+
+ for (i = 11; i; i--)
+ sum = ((sum & 1) << 7) + (sum >> 1) + (uint8_t)*dir_name++;
+ return sum;
+}
+
+
+/* compute the first sector number of one dir where the data stores */
+static inline sector_t first_sector(struct fs_info *fs,
+ const struct fat_dir_entry *dir)
+{
+ const struct fat_sb_info *sbi = FAT_SB(fs);
+ sector_t first_clust;
+ sector_t sector;
+
+ first_clust = (dir->first_cluster_high << 16) + dir->first_cluster_low;
+ sector = ((first_clust - 2) << sbi->clust_shift) + sbi->data;
+
+ return sector;
+}
+
+static inline enum dirent_type get_inode_mode(uint8_t attr)
+{
+ return (attr & FAT_ATTR_DIRECTORY) ? DT_DIR : DT_REG;
+}
+
+
+static struct inode *vfat_find_entry(const char *dname, struct inode *dir)
+{
+ struct fs_info *fs = dir->fs;
+ struct inode *inode;
+ const struct fat_dir_entry *de;
+ struct fat_long_name_entry *long_de;
+
+ char mangled_name[12];
+ uint16_t long_name[260]; /* == 20*13 */
+ int long_len;
+
+ sector_t dir_sector = PVT(dir)->start;
+ uint8_t vfat_init, vfat_next, vfat_csum = 0;
+ uint8_t id;
+ int slots;
+ int entries;
+ int checksum;
+ int long_match = 0;
+
+ slots = (strlen(dname) + 12) / 13;
+ if (slots > 20)
+ return NULL; /* Name too long */
+
+ slots |= 0x40;
+ vfat_init = vfat_next = slots;
+ long_len = slots*13;
+
+ /* Produce the shortname version, in case we need it. */
+ mangle_dos_name(mangled_name, dname);
+
+ while (dir_sector) {
+ de = get_cache(fs->fs_dev, dir_sector);
+ entries = 1 << (fs->sector_shift - 5);
+
+ while (entries--) {
+ if (de->name[0] == 0)
+ return NULL;
+
+ if (de->attr == 0x0f) {
+ /*
+ * It's a long name entry.
+ */
+ long_de = (struct fat_long_name_entry *)de;
+ id = long_de->id;
+ if (id != vfat_next)
+ goto not_match;
+
+ if (id & 0x40) {
+ /* get the initial checksum value */
+ vfat_csum = long_de->checksum;
+ id &= 0x3f;
+ long_len = id * 13;
+
+ /* ZERO the long_name buffer */
+ memset(long_name, 0, sizeof long_name);
+ } else {
+ if (long_de->checksum != vfat_csum)
+ goto not_match;
+ }
+
+ vfat_next = --id;
+
+ /* got the long entry name */
+ copy_long_chunk(long_name + id*13, de);
+
+ /*
+ * If we got the last entry, check it.
+ * Or, go on with the next entry.
+ */
+ if (id == 0) {
+ if (!vfat_match_longname(dname, long_name, long_len))
+ goto not_match;
+ long_match = 1;
+ }
+ de++;
+ continue; /* Try the next entry */
+ } else {
+ /*
+ * It's a short entry
+ */
+ if (de->attr & 0x08) /* ignore volume labels */
+ goto not_match;
+
+ if (long_match) {
+ /*
+ * We already have a VFAT long name match. However, the
+ * match is only valid if the checksum matches.
+ */
+ checksum = get_checksum(de->name);
+ if (checksum == vfat_csum)
+ goto found; /* Got it */
+ } else {
+ if (!memcmp(mangled_name, de->name, 11))
+ goto found;
+ }
+ }
+
+ not_match:
+ vfat_next = vfat_init;
+ long_match = 0;
+
+ de++;
+ }
+
+ /* Try with the next sector */
+ dir_sector = get_next_sector(fs, dir_sector);
+ }
+ return NULL; /* Nothing found... */
+
+found:
+ inode = new_fat_inode(fs);
+ inode->size = de->file_size;
+ PVT(inode)->start_cluster =
+ (de->first_cluster_high << 16) + de->first_cluster_low;
+ PVT(inode)->start = PVT(inode)->here = first_sector(fs, de);
+ inode->mode = get_inode_mode(de->attr);
+
+ return inode;
+}
+
+static struct inode *vfat_iget_root(struct fs_info *fs)
+{
+ struct inode *inode = new_fat_inode(fs);
+ int root_size = FAT_SB(fs)->root_size;
+
+ /*
+ * For FAT32, the only way to get the root directory size is to
+ * follow the entire FAT chain to the end... which seems pointless.
+ */
+ PVT(inode)->start_cluster = FAT_SB(fs)->root_cluster;
+ inode->size = root_size ? root_size << fs->sector_shift : ~0;
+ PVT(inode)->start = PVT(inode)->here = FAT_SB(fs)->root;
+ inode->mode = DT_DIR;
+
+ return inode;
+}
+
+static struct inode *vfat_iget(const char *dname, struct inode *parent)
+{
+ return vfat_find_entry(dname, parent);
+}
+
+static int vfat_readdir(struct file *file, struct dirent *dirent)
+{
+ struct fs_info *fs = file->fs;
+ const struct fat_dir_entry *de;
+ const char *data;
+ const struct fat_long_name_entry *long_de;
+
+ sector_t sector = get_the_right_sector(file);
+
+ uint16_t long_name[261]; /* == 20*13 + 1 (to guarantee null) */
+ char filename[261];
+ int name_len = 0;
+
+ uint8_t vfat_init, vfat_next, vfat_csum;
+ uint8_t id;
+ int entries_left;
+ bool long_entry = false;
+ int sec_off = file->offset & ((1 << fs->sector_shift) - 1);
+
+ data = get_cache(fs->fs_dev, sector);
+ de = (const struct fat_dir_entry *)(data + sec_off);
+ entries_left = ((1 << fs->sector_shift) - sec_off) >> 5;
+
+ vfat_next = vfat_csum = 0xff;
+
+ while (1) {
+ while (entries_left--) {
+ if (de->name[0] == 0)
+ return -1; /* End of directory */
+ if ((uint8_t)de->name[0] == 0xe5)
+ goto invalid;
+
+ if (de->attr == 0x0f) {
+ /*
+ * It's a long name entry.
+ */
+ long_de = (struct fat_long_name_entry *)de;
+ id = long_de->id;
+
+ if (id & 0x40) {
+ /* init vfat_csum and vfat_init */
+ vfat_csum = long_de->checksum;
+ id &= 0x3f;
+ if (id >= 20)
+ goto invalid; /* Too long! */
+
+ vfat_init = id;
+
+ /* ZERO the long_name buffer */
+ memset(long_name, 0, sizeof long_name);
+ } else {
+ if (long_de->checksum != vfat_csum || id != vfat_next)
+ goto invalid;
+ }
+
+ vfat_next = --id;
+
+ /* got the long entry name */
+ copy_long_chunk(long_name + id*13, de);
+
+ if (id == 0) {
+ name_len = vfat_cvt_longname(filename, long_name);
+ if (name_len > 0 && name_len < sizeof(dirent->d_name))
+ long_entry = true;
+ }
+
+ goto next;
+ } else {
+ /*
+ * It's a short entry
+ */
+ if (de->attr & 0x08) /* ignore volume labels */
+ goto invalid;
+
+ if (long_entry && get_checksum(de->name) == vfat_csum) {
+ /* Got a long entry */
+ } else {
+ /* Use the shortname */
+ int i;
+ uint8_t c;
+ char *p = filename;
+
+ for (i = 0; i < 8; i++) {
+ c = de->name[i];
+ if (c == ' ')
+ break;
+ if (de->lcase & LCASE_BASE)
+ c = codepage.lower[c];
+ *p++ = c;
+ }
+ if (de->name[8] != ' ') {
+ *p++ = '.';
+ for (i = 8; i < 11; i++) {
+ c = de->name[i];
+ if (c == ' ')
+ break;
+ if (de->lcase & LCASE_EXT)
+ c = codepage.lower[c];
+ *p++ = c;
+ }
+ }
+ *p = '\0';
+ name_len = p - filename;
+ }
+ goto got; /* Got something one way or the other */
+ }
+
+ invalid:
+ long_entry = false;
+ next:
+ de++;
+ file->offset += sizeof(struct fat_dir_entry);
+ }
+
+ /* Try with the next sector */
+ sector = next_sector(file);
+ if (!sector)
+ return -1;
+ de = get_cache(fs->fs_dev, sector);
+ entries_left = 1 << (fs->sector_shift - 5);
+ }
+
+got:
+ name_len++; /* Include final null */
+ dirent->d_ino = de->first_cluster_low | (de->first_cluster_high << 16);
+ dirent->d_off = file->offset;
+ dirent->d_reclen = offsetof(struct dirent, d_name) + name_len;
+ dirent->d_type = get_inode_mode(de->attr);
+ memcpy(dirent->d_name, filename, name_len);
+
+ file->offset += sizeof(*de); /* Update for next reading */
+
+ return 0;
+}
+
+/* Load the config file, return 1 if failed, or 0 */
+static int vfat_load_config(void)
+{
+ const char *search_directories[] = {
+ "/boot/syslinux",
+ "/syslinux",
+ "/",
+ NULL
+ };
+ com32sys_t regs;
+ int i;
+
+ /* If installed by extlinux, try the extlinux filename */
+ if (*CurrentDirName && !generic_load_config())
+ return 0;
+
+ for (i = 0; search_directories[i]; i++) {
+ memset(&regs, 0, sizeof regs);
+ snprintf(ConfigName, FILENAME_MAX, "%s/syslinux.cfg",
+ search_directories[i]);
+ regs.edi.w[0] = OFFS_WRT(ConfigName, 0);
+ call16(core_open, &regs, &regs);
+ if (!(regs.eflags.l & EFLAGS_ZF))
+ break;
+ }
+ if (!search_directories[i])
+ return -1;
+
+ /* Set the current working directory */
+ chdir(search_directories[i]);
+ return 0;
+}
+
+static inline __constfunc uint32_t bsr(uint32_t num)
+{
+ asm("bsrl %1,%0" : "=r" (num) : "rm" (num));
+ return num;
+}
+
+/* init. the fs meta data, return the block size in bits */
+static int vfat_fs_init(struct fs_info *fs)
+{
+ struct fat_bpb fat;
+ struct fat_sb_info *sbi;
+ struct disk *disk = fs->fs_dev->disk;
+ int sectors_per_fat;
+ uint32_t clusters;
+ sector_t total_sectors;
+
+ fs->sector_shift = fs->block_shift = disk->sector_shift;
+ fs->sector_size = 1 << fs->sector_shift;
+ fs->block_size = 1 << fs->block_shift;
+
+ disk->rdwr_sectors(disk, &fat, 0, 1, 0);
+
+ /* XXX: Find better sanity checks... */
+ if (!fat.bxResSectors || !fat.bxFATs)
+ return -1;
+ sbi = malloc(sizeof(*sbi));
+ if (!sbi)
+ malloc_error("fat_sb_info structure");
+ fs->fs_info = sbi;
+
+ sectors_per_fat = fat.bxFATsecs ? : fat.fat32.bxFATsecs_32;
+ total_sectors = fat.bxSectors ? : fat.bsHugeSectors;
+
+ sbi->fat = fat.bxResSectors;
+ sbi->root = sbi->fat + sectors_per_fat * fat.bxFATs;
+ sbi->root_size = root_dir_size(fs, &fat);
+ sbi->data = sbi->root + sbi->root_size;
+
+ sbi->clust_shift = bsr(fat.bxSecPerClust);
+ sbi->clust_byte_shift = sbi->clust_shift + fs->sector_shift;
+ sbi->clust_mask = fat.bxSecPerClust - 1;
+ sbi->clust_size = fat.bxSecPerClust << fs->sector_shift;
+
+ clusters = (total_sectors - sbi->data) >> sbi->clust_shift;
+ if (clusters <= 0xff4) {
+ sbi->fat_type = FAT12;
+ } else if (clusters <= 0xfff4) {
+ sbi->fat_type = FAT16;
+ } else {
+ sbi->fat_type = FAT32;
+
+ if (clusters > 0x0ffffff4)
+ clusters = 0x0ffffff4; /* Maximum possible */
+
+ if (fat.fat32.extended_flags & 0x80) {
+ /* Non-mirrored FATs, we need to read the active one */
+ sbi->fat += (fat.fat32.extended_flags & 0x0f) * sectors_per_fat;
+ }
+
+ /* FAT32: root directory is a cluster chain */
+ sbi->root = sbi->data
+ + ((fat.fat32.root_cluster-2) << sbi->clust_shift);
+ }
+ sbi->clusters = clusters;
+
+ /* Initialize the cache */
+ cache_init(fs->fs_dev, fs->block_shift);
+
+ return fs->block_shift;
+}
+
+const struct fs_ops vfat_fs_ops = {
+ .fs_name = "vfat",
+ .fs_flags = FS_USEMEM | FS_THISIND,
+ .fs_init = vfat_fs_init,
+ .searchdir = NULL,
+ .getfssec = generic_getfssec,
+ .close_file = generic_close_file,
+ .mangle_name = vfat_mangle_name,
+ .load_config = vfat_load_config,
+ .readdir = vfat_readdir,
+ .iget_root = vfat_iget_root,
+ .iget = vfat_iget,
+ .next_extent = fat_next_extent,
+};
diff --git a/core/fs/fat/fat_fs.h b/core/fs/fat/fat_fs.h
new file mode 100644
index 00000000..7ea3db85
--- /dev/null
+++ b/core/fs/fat/fat_fs.h
@@ -0,0 +1,158 @@
+#ifndef FAT_FS_H
+#define FAT_FS_H
+
+#include <stdint.h>
+
+#define FAT_DIR_ENTRY_SIZE 32
+#define DIRENT_SHIFT 5
+
+#define FAT_ATTR_READ_ONLY 0x01
+#define FAT_ATTR_HIDDEN 0x02
+#define FAT_ATTR_SYSTEM 0x04
+#define FAT_ATTR_VOLUME_ID 0x08
+#define FAT_ATTR_DIRECTORY 0x10
+#define FAT_ATTR_ARCHIVE 0x20
+
+#define FAT_MAXFILE 256
+
+#define FAT_ATTR_LONG_NAME (FAT_ATTR_READ_ONLY \
+ | FAT_ATTR_HIDDEN \
+ | FAT_ATTR_SYSTEM \
+ | FAT_ATTR_VOLUME_ID)
+
+#define FAT_ATTR_VALID (FAT_ATTR_READ_ONLY \
+ | FAT_ATTR_HIDDEN \
+ | FAT_ATTR_SYSTEM \
+ | FAT_ATTR_DIRECTORY \
+ | FAT_ATTR_ARCHIVE)
+
+enum fat_type{ FAT12, FAT16, FAT32 };
+
+/*
+ * The fat file system structures
+ */
+
+struct fat_bpb {
+ uint8_t jmp_boot[3];
+ uint8_t oem_name[8];
+ uint16_t sector_size;
+ uint8_t bxSecPerClust;
+ uint16_t bxResSectors;
+ uint8_t bxFATs;
+ uint16_t bxRootDirEnts;
+ uint16_t bxSectors;
+ uint8_t media;
+ uint16_t bxFATsecs;
+ uint16_t sectors_per_track;
+ uint16_t num_heads;
+ uint32_t num_hidden_sectors;
+ uint32_t bsHugeSectors;
+
+ union {
+ struct {
+ uint8_t num_ph_drive;
+ uint8_t reserved;
+ uint8_t boot_sig;
+ uint32_t num_serial;
+ uint8_t label[11];
+ uint8_t fstype[8];
+ } __attribute__ ((packed)) fat12_16;
+
+ struct {
+ uint32_t bxFATsecs_32;
+ uint16_t extended_flags;
+ uint16_t fs_version;
+ uint32_t root_cluster;
+ uint16_t fs_info;
+ uint16_t backup_boot_sector;
+ uint8_t reserved[12];
+ uint8_t num_ph_drive;
+ uint8_t reserved1;
+ uint8_t boot_sig;
+ uint32_t num_serial;
+ uint8_t label[11];
+ uint8_t fstype[8];
+ } __attribute__ ((packed)) fat32;
+
+ } __attribute__ ((packed));
+
+ uint8_t pad[422]; /* padding to 512 Bytes (one sector) */
+
+} __attribute__ ((packed));
+
+/*
+ * The fat file system info in memory
+ */
+struct fat_sb_info {
+ sector_t fat; /* The FAT region */
+ sector_t root; /* The root dir region */
+ sector_t data; /* The data region */
+
+ uint32_t clusters; /* Total number of clusters */
+ uint32_t root_cluster; /* Cluster number for (FAT32) root dir */
+ int root_size; /* The root dir size in sectors */
+
+ int clust_shift; /* based on sectors */
+ int clust_byte_shift; /* based on bytes */
+ int clust_mask; /* sectors per cluster mask */
+ int clust_size;
+
+ int fat_type;
+} __attribute__ ((packed));
+
+struct fat_dir_entry {
+ char name[11];
+ uint8_t attr;
+ uint8_t lcase;
+ uint8_t c_time_tenth;
+ uint16_t c_time;
+ uint16_t c_date;
+ uint16_t a_date;
+ uint16_t first_cluster_high;
+ uint16_t w_time;
+ uint16_t w_date;
+ uint16_t first_cluster_low;
+ uint32_t file_size;
+} __attribute__ ((packed));
+
+#define LCASE_BASE 8 /* basename is lower case */
+#define LCASE_EXT 16 /* extension is lower case */
+
+struct fat_long_name_entry {
+ uint8_t id;
+ uint16_t name1[5];
+ uint8_t attr;
+ uint8_t reserved;
+ uint8_t checksum;
+ uint16_t name2[6];
+ uint16_t first_cluster;
+ uint16_t name3[2];
+} __attribute__ ((packed));
+
+static inline struct fat_sb_info *FAT_SB(struct fs_info *fs)
+{
+ return fs->fs_info;
+}
+
+/*
+ * Count the root dir size in sectors
+ */
+static inline int root_dir_size(struct fs_info *fs, struct fat_bpb *fat)
+{
+ return (fat->bxRootDirEnts + SECTOR_SIZE(fs)/32 - 1)
+ >> (SECTOR_SHIFT(fs) - 5);
+}
+
+/*
+ * FAT private inode information
+ */
+struct fat_pvt_inode {
+ uint32_t start_cluster; /* Starting cluster address */
+ sector_t start; /* Starting sector */
+ sector_t offset; /* Current sector offset */
+ sector_t here; /* Sector corresponding to offset */
+};
+
+#define PVT(i) ((struct fat_pvt_inode *)((i)->pvt))
+
+#endif /* fat_fs.h */
diff --git a/core/fs/fs.c b/core/fs/fs.c
new file mode 100644
index 00000000..6ea74bf8
--- /dev/null
+++ b/core/fs/fs.c
@@ -0,0 +1,413 @@
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <dprintf.h>
+#include "fs.h"
+#include "cache.h"
+
+/* The currently mounted filesystem */
+struct fs_info *this_fs = NULL; /* Root filesystem */
+
+/* Actual file structures (we don't have malloc yet...) */
+struct file files[MAX_OPEN];
+
+/* Symlink hard limits */
+#define MAX_SYMLINK_CNT 20
+#define MAX_SYMLINK_BUF 4096
+
+/*
+ * Get a new inode structure
+ */
+struct inode *alloc_inode(struct fs_info *fs, uint32_t ino, size_t data)
+{
+ struct inode *inode = zalloc(sizeof(struct inode) + data);
+ if (inode) {
+ inode->fs = fs;
+ inode->ino = ino;
+ inode->refcnt = 1;
+ }
+ return inode;
+}
+
+/*
+ * 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->fs)
+ return file;
+ file++;
+ }
+
+ return NULL;
+}
+
+/*
+ * Close and free a file structure
+ */
+static inline void free_file(struct file *file)
+{
+ memset(file, 0, sizeof *file);
+}
+
+void _close_file(struct file *file)
+{
+ if (file->fs)
+ file->fs->fs_ops->close_file(file);
+ free_file(file);
+}
+
+/*
+ * Convert between a 16-bit file handle and a file structure
+ */
+
+void load_config(void)
+{
+ int err;
+
+ err = this_fs->fs_ops->load_config();
+
+ if (err)
+ printf("ERROR: No configuration file found\n");
+}
+
+void pm_mangle_name(com32sys_t *regs)
+{
+ const char *src = MK_PTR(regs->ds, regs->esi.w[0]);
+ char *dst = MK_PTR(regs->es, regs->edi.w[0]);
+
+ mangle_name(dst, src);
+}
+
+void mangle_name(char *dst, const char *src)
+{
+ this_fs->fs_ops->mangle_name(dst, src);
+}
+
+void getfssec(com32sys_t *regs)
+{
+ int sectors;
+ bool have_more;
+ uint32_t bytes_read;
+ char *buf;
+ struct file *file;
+ uint16_t handle;
+
+ sectors = regs->ecx.w[0];
+
+ 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 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;
+}
+
+size_t pmapi_read_file(uint16_t *handle, void *buf, size_t sectors)
+{
+ bool have_more;
+ size_t bytes_read;
+ struct file *file;
+
+ file = handle_to_file(*handle);
+ bytes_read = file->fs->fs_ops->getfssec(file, buf, sectors, &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);
+ *handle = 0;
+ }
+
+ return bytes_read;
+}
+
+void pm_searchdir(com32sys_t *regs)
+{
+ char *name = MK_PTR(regs->ds, regs->edi.w[0]);
+ int rv;
+
+ rv = searchdir(name);
+ if (rv < 0) {
+ regs->esi.w[0] = 0;
+ regs->eax.l = 0;
+ regs->eflags.l |= EFLAGS_ZF;
+ } else {
+ regs->esi.w[0] = rv;
+ regs->eax.l = handle_to_file(rv)->inode->size;
+ regs->eflags.l &= ~EFLAGS_ZF;
+ }
+}
+
+int searchdir(const char *name)
+{
+ struct inode *inode = NULL;
+ struct inode *parent = NULL;
+ struct file *file;
+ char *pathbuf = NULL;
+ char *part, *p, echar;
+ int symlink_count = MAX_SYMLINK_CNT;
+
+ if (!(file = alloc_file()))
+ goto err_no_close;
+ file->fs = this_fs;
+
+ /* if we have ->searchdir method, call it */
+ if (file->fs->fs_ops->searchdir) {
+ file->fs->fs_ops->searchdir(name, file);
+
+ if (file->inode)
+ return file_to_handle(file);
+ else
+ goto err;
+ }
+
+ /* else, try the generic-path-lookup method */
+
+ parent = get_inode(this_fs->cwd);
+ p = pathbuf = strdup(name);
+ if (!pathbuf)
+ goto err;
+
+ do {
+ got_link:
+ if (*p == '/') {
+ put_inode(parent);
+ parent = get_inode(this_fs->root);
+ }
+
+ do {
+ inode = get_inode(parent);
+
+ while (*p == '/')
+ p++;
+
+ if (!*p)
+ break;
+
+ part = p;
+ while ((echar = *p) && echar != '/')
+ p++;
+ *p++ = '\0';
+
+ if (part[0] != '.' || part[1] != '\0') {
+ inode = this_fs->fs_ops->iget(part, parent);
+ if (!inode)
+ goto err;
+ if (inode->mode == DT_LNK) {
+ char *linkbuf, *q;
+ int name_len = echar ? strlen(p) : 0;
+ int total_len = inode->size + name_len + 2;
+ int link_len;
+
+ if (!this_fs->fs_ops->readlink ||
+ --symlink_count == 0 || /* limit check */
+ total_len > MAX_SYMLINK_BUF)
+ goto err;
+
+ linkbuf = malloc(total_len);
+ if (!linkbuf)
+ goto err;
+
+ link_len = this_fs->fs_ops->readlink(inode, linkbuf);
+ if (link_len <= 0) {
+ free(linkbuf);
+ goto err;
+ }
+
+ q = linkbuf + link_len;
+
+ if (echar) {
+ if (link_len > 0 && q[-1] != '/')
+ *q++ = '/';
+
+ memcpy(q, p, name_len+1);
+ } else {
+ *q = '\0';
+ }
+
+ free(pathbuf);
+ p = pathbuf = linkbuf;
+ put_inode(inode);
+ inode = NULL;
+ goto got_link;
+ }
+
+ put_inode(parent);
+ parent = NULL;
+
+ if (!echar)
+ break;
+
+ if (inode->mode != DT_DIR)
+ goto err;
+
+ parent = inode;
+ inode = NULL;
+ }
+ } while (echar);
+ } while (0);
+
+ free(pathbuf);
+ pathbuf = NULL;
+ put_inode(parent);
+ parent = NULL;
+
+ if (!inode)
+ goto err;
+
+ file->inode = inode;
+ file->offset = 0;
+
+ dprintf("File %s -> %p (inode %p) len %u\n", name, file,
+ inode, inode->size);
+
+ return file_to_handle(file);
+
+err:
+ if (inode)
+ put_inode(inode);
+ if (parent)
+ put_inode(parent);
+ if (pathbuf)
+ free(pathbuf);
+ _close_file(file);
+err_no_close:
+ return -1;
+}
+
+int open_file(const char *name, struct com32_filedata *filedata)
+{
+ int rv;
+ struct file *file;
+ char mangled_name[FILENAME_MAX];
+
+ mangle_name(mangled_name, name);
+ rv = searchdir(mangled_name);
+
+ if (rv >= 0) {
+ file = handle_to_file(rv);
+ filedata->size = file->inode->size;
+ filedata->blocklg2 = SECTOR_SHIFT(file->fs);
+ filedata->handle = rv;
+ }
+ return rv;
+}
+
+void pm_open_file(com32sys_t *regs)
+{
+ int rv;
+ struct file *file;
+ const char *name = MK_PTR(regs->es, regs->esi.w[0]);
+ char mangled_name[FILENAME_MAX];
+
+ mangle_name(mangled_name, name);
+ rv = searchdir(mangled_name);
+ if (rv < 0) {
+ regs->eflags.l |= EFLAGS_CF;
+ } else {
+ file = handle_to_file(rv);
+ regs->eflags.l &= ~EFLAGS_CF;
+ regs->eax.l = file->inode->size;
+ regs->ecx.w[0] = SECTOR_SIZE(file->fs);
+ regs->esi.w[0] = rv;
+ }
+}
+
+void close_file(uint16_t handle)
+{
+ struct file *file;
+
+ if (handle) {
+ file = handle_to_file(handle);
+ _close_file(file);
+ }
+}
+
+void pm_close_file(com32sys_t *regs)
+{
+ close_file(regs->esi.w[0]);
+}
+
+/*
+ * it will do:
+ * initialize the memory management function;
+ * set up the vfs fs structure;
+ * initialize the device structure;
+ * invoke the fs-specific init function;
+ * initialize the cache if we need one;
+ * finally, get the current inode for relative path looking.
+ *
+ */
+void fs_init(com32sys_t *regs)
+{
+ static struct fs_info fs; /* The actual filesystem buffer */
+ uint8_t disk_devno = regs->edx.b[0];
+ uint8_t disk_cdrom = regs->edx.b[1];
+ sector_t disk_offset = regs->ecx.l | ((sector_t)regs->ebx.l << 32);
+ uint16_t disk_heads = regs->esi.w[0];
+ uint16_t disk_sectors = regs->edi.w[0];
+ int blk_shift = -1;
+ struct device *dev = NULL;
+ /* ops is a ptr list for several fs_ops */
+ const struct fs_ops **ops = (const struct fs_ops **)regs->eax.l;
+
+ /* Initialize malloc() */
+ mem_init();
+
+ /* Default name for the root directory */
+ fs.cwd_name[0] = '/';
+
+ while ((blk_shift < 0) && *ops) {
+ /* set up the fs stucture */
+ fs.fs_ops = *ops;
+
+ /*
+ * This boldly assumes that we don't mix FS_NODEV filesystems
+ * with FS_DEV filesystems...
+ */
+ if (fs.fs_ops->fs_flags & FS_NODEV) {
+ fs.fs_dev = NULL;
+ } else {
+ if (!dev)
+ dev = device_init(disk_devno, disk_cdrom, disk_offset,
+ disk_heads, disk_sectors);
+ fs.fs_dev = dev;
+ }
+ /* invoke the fs-specific init code */
+ blk_shift = fs.fs_ops->fs_init(&fs);
+ ops++;
+ }
+ if (blk_shift < 0) {
+ printf("No valid file system found!\n");
+ while (1)
+ ;
+ }
+ this_fs = &fs;
+
+ /* initialize the cache */
+ if (fs.fs_dev && fs.fs_dev->cache_data)
+ cache_init(fs.fs_dev, blk_shift);
+
+ /* start out in the root directory */
+ if (fs.fs_ops->iget_root) {
+ fs.root = fs.fs_ops->iget_root(&fs);
+ fs.cwd = get_inode(fs.root);
+ }
+}
diff --git a/core/fs/getfssec.c b/core/fs/getfssec.c
new file mode 100644
index 00000000..3d62d4e6
--- /dev/null
+++ b/core/fs/getfssec.c
@@ -0,0 +1,190 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2010 Intel Corporation; author: H. Peter Anvin
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall
+ * be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * getfssec.c
+ *
+ * Generic getfssec implementation for disk-based filesystems, which
+ * support the next_extent() method.
+ *
+ * The expected semantics of next_extent are as follows:
+ *
+ * The second argument will contain the initial sector number to be
+ * mapped. The routine is expected to populate
+ * inode->next_extent.pstart and inode->next_extent.len (the caller
+ * will store the initial sector number into inode->next_extent.lstart
+ * on return.)
+ *
+ * If inode->next_extent.len != 0 on entry then the routine is allowed
+ * to assume inode->next_extent contains valid data from the previous
+ * usage, which can be used for optimization purposes.
+ *
+ * If the filesystem can map the entire file as a single extent
+ * (e.g. iso9660), then the filesystem can simply insert the extent
+ * information into inode->next_extent at searchdir/iget time, and leave
+ * next_extent() as NULL.
+ *
+ * Note: the filesystem driver is not required to do extent coalescing,
+ * if that is difficult to do; this routine will perform extent lookahead
+ * and coalescing.
+ */
+
+#include <dprintf.h>
+#include <minmax.h>
+#include "fs.h"
+
+static inline sector_t next_psector(sector_t psector, uint32_t skip)
+{
+ if (EXTENT_SPECIAL(psector))
+ return psector;
+ else
+ return psector + skip;
+}
+
+static inline sector_t next_pstart(const struct extent *e)
+{
+ return next_psector(e->pstart, e->len);
+}
+
+
+static void get_next_extent(struct inode *inode)
+{
+ /* The logical start address that we care about... */
+ uint32_t lstart = inode->this_extent.lstart + inode->this_extent.len;
+
+ if (inode->fs->fs_ops->next_extent(inode, lstart))
+ inode->next_extent.len = 0; /* ERROR */
+ inode->next_extent.lstart = lstart;
+
+ dprintf("Extent: inode %p @ %u start %llu len %u\n",
+ inode, inode->next_extent.lstart,
+ inode->next_extent.pstart, inode->next_extent.len);
+}
+
+uint32_t generic_getfssec(struct file *file, char *buf,
+ int sectors, bool *have_more)
+{
+ struct inode *inode = file->inode;
+ struct fs_info *fs = file->fs;
+ struct disk *disk = fs->fs_dev->disk;
+ uint32_t bytes_read = 0;
+ uint32_t bytes_left = inode->size - file->offset;
+ uint32_t sectors_left =
+ (bytes_left + SECTOR_SIZE(fs) - 1) >> SECTOR_SHIFT(fs);
+ uint32_t lsector;
+
+ if (sectors > sectors_left)
+ sectors = sectors_left;
+
+ if (!sectors)
+ return 0;
+
+ lsector = file->offset >> SECTOR_SHIFT(fs);
+ dprintf("Offset: %u lsector: %u\n", file->offset, lsector);
+
+ if (lsector < inode->this_extent.lstart ||
+ lsector >= inode->this_extent.lstart + inode->this_extent.len) {
+ /* inode->this_extent unusable, maybe next_extent is... */
+ inode->this_extent = inode->next_extent;
+ }
+
+ if (lsector < inode->this_extent.lstart ||
+ lsector >= inode->this_extent.lstart + inode->this_extent.len) {
+ /* Still nothing useful... */
+ inode->this_extent.lstart = lsector;
+ inode->this_extent.len = 0;
+ } else {
+ /* We have some usable information */
+ uint32_t delta = lsector - inode->this_extent.lstart;
+ inode->this_extent.lstart = lsector;
+ inode->this_extent.len -= delta;
+ inode->this_extent.pstart
+ = next_psector(inode->this_extent.pstart, delta);
+ }
+
+ dprintf("this_extent: lstart %u pstart %llu len %u\n",
+ inode->this_extent.lstart,
+ inode->this_extent.pstart,
+ inode->this_extent.len);
+
+ while (sectors) {
+ uint32_t chunk;
+ size_t len;
+
+ while (sectors > inode->this_extent.len) {
+ if (!inode->next_extent.len ||
+ inode->next_extent.lstart !=
+ inode->this_extent.lstart + inode->this_extent.len)
+ get_next_extent(inode);
+
+ if (!inode->this_extent.len) {
+ /* Doesn't matter if it's contiguous... */
+ inode->this_extent = inode->next_extent;
+ } else if (inode->next_extent.len &&
+ inode->next_extent.pstart == next_pstart(&inode->this_extent)) {
+ /* Coalesce extents and loop */
+ inode->this_extent.len += inode->next_extent.len;
+ } else {
+ /* Discontiguous extents */
+ break;
+ }
+ }
+
+ dprintf("this_extent: lstart %u pstart %llu len %u\n",
+ inode->this_extent.lstart,
+ inode->this_extent.pstart,
+ inode->this_extent.len);
+
+ chunk = min(sectors, inode->this_extent.len);
+ len = chunk << SECTOR_SHIFT(fs);
+
+ dprintf(" I/O: inode %p @ %u start %llu len %u\n",
+ inode, inode->this_extent.lstart,
+ inode->this_extent.pstart, chunk);
+
+ if (inode->this_extent.pstart == EXTENT_ZERO) {
+ memset(buf, 0, len);
+ } else {
+ disk->rdwr_sectors(disk, buf, inode->this_extent.pstart, chunk, 0);
+ inode->this_extent.pstart += chunk;
+ }
+
+ buf += len;
+ sectors -= chunk;
+ bytes_read += len;
+ inode->this_extent.lstart += chunk;
+ inode->this_extent.len -= chunk;
+ }
+
+ bytes_read = min(bytes_read, bytes_left);
+ file->offset += bytes_read;
+
+ if (have_more)
+ *have_more = bytes_read < bytes_left;
+
+ return bytes_read;
+}
diff --git a/core/fs/iso9660/iso9660.c b/core/fs/iso9660/iso9660.c
new file mode 100644
index 00000000..4c568bf6
--- /dev/null
+++ b/core/fs/iso9660/iso9660.c
@@ -0,0 +1,355 @@
+#include <dprintf.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/dirent.h>
+#include <core.h>
+#include <cache.h>
+#include <disk.h>
+#include <fs.h>
+#include "iso9660_fs.h"
+
+/* Convert to lower case string */
+static inline char iso_tolower(char c)
+{
+ if (c >= 'A' && c <= 'Z')
+ c += 0x20;
+
+ return c;
+}
+
+static struct inode *new_iso_inode(struct fs_info *fs)
+{
+ return alloc_inode(fs, 0, sizeof(struct iso9660_pvt_inode));
+}
+
+static inline struct iso_sb_info *ISO_SB(struct fs_info *fs)
+{
+ return fs->fs_info;
+}
+
+/*
+ * Mangle a filename pointed to by src into a buffer pointed
+ * to by dst; ends on encountering any whitespace.
+ * dst is preserved.
+ *
+ * This verifies that a filename is < FilENAME_MAX characters,
+ * doesn't contain whitespace, zero-pads the output buffer,
+ * and removes trailing dots and redumndant slashes, so "repe
+ * cmpsb" can do a compare, and the path-searching routine gets
+ * a bit of an easier job.
+ *
+ */
+static void iso_mangle_name(char *dst, const char *src)
+{
+ char *p = dst;
+ int i = FILENAME_MAX - 1;
+
+ while (not_whitespace(*src)) {
+ if ( *src == '/' ) {
+ if ( *(src+1) == '/' ) {
+ i--;
+ src++;
+ continue;
+ }
+ }
+
+ *dst++ = *src ++;
+ i--;
+ }
+
+ while ( 1 ) {
+ if ( dst == p )
+ break;
+
+ if ( (*(dst-1) != '.') && (*(dst-1) != '/') )
+ break;
+
+ dst --;
+ i ++;
+ }
+
+ i ++;
+ for (; i > 0; i -- )
+ *dst++ = '\0';
+}
+
+static size_t iso_convert_name(char *dst, const char *src, int len)
+{
+ char *p = dst;
+ char c;
+
+ if (len == 1) {
+ switch (*src) {
+ case 1:
+ *p++ = '.';
+ /* fall through */
+ case 0:
+ *p++ = '.';
+ goto done;
+ default:
+ /* nothing special */
+ break;
+ }
+ }
+
+ while (len-- && (c = *src++)) {
+ if (c == ';') /* Remove any filename version suffix */
+ break;
+ *p++ = iso_tolower(c);
+ }
+
+ /* Then remove any terminal dots */
+ while (p > dst+1 && p[-1] == '.')
+ p--;
+
+done:
+ *p = '\0';
+ return p - dst;
+}
+
+/*
+ * Unlike strcmp, it does return 1 on match, or reutrn 0 if not match.
+ */
+static bool iso_compare_name(const char *de_name, size_t len,
+ const char *file_name)
+{
+ char iso_file_name[256];
+ char *p = iso_file_name;
+ char c1, c2;
+ size_t i;
+
+ i = iso_convert_name(iso_file_name, de_name, len);
+ dprintf("Compare: \"%s\" to \"%s\" (len %zu)\n",
+ file_name, iso_file_name, i);
+
+ do {
+ c1 = *p++;
+ c2 = iso_tolower(*file_name++);
+
+ /* compare equal except for case? */
+ if (c1 != c2)
+ return false;
+ } while (c1);
+
+ return true;
+}
+
+/*
+ * Find a entry in the specified dir with name _dname_.
+ */
+static const struct iso_dir_entry *
+iso_find_entry(const char *dname, struct inode *inode)
+{
+ struct fs_info *fs = inode->fs;
+ block_t dir_block = PVT(inode)->lba;
+ int i = 0, offset = 0;
+ const char *de_name;
+ int de_name_len, de_len;
+ const struct iso_dir_entry *de;
+ const char *data = NULL;
+
+ dprintf("iso_find_entry: \"%s\"\n", dname);
+
+ while (1) {
+ if (!data) {
+ dprintf("Getting block %d from block %llu\n", i, dir_block);
+ if (++i > inode->blocks)
+ return NULL; /* End of directory */
+ data = get_cache(fs->fs_dev, dir_block++);
+ offset = 0;
+ }
+
+ de = (const struct iso_dir_entry *)(data + offset);
+ de_len = de->length;
+ offset += de_len;
+
+ /* Make sure we have a full directory entry */
+ if (de_len < 33 || offset > BLOCK_SIZE(fs)) {
+ /*
+ * Zero = end of sector, or corrupt directory entry
+ *
+ * ECMA-119:1987 6.8.1.1: "Each Directory Record shall end
+ * in the Logical Sector in which it begins.
+ */
+ data = NULL;
+ continue;
+ }
+
+ de_name_len = de->name_len;
+ de_name = de->name;
+ if (iso_compare_name(de_name, de_name_len, dname)) {
+ dprintf("Found.\n");
+ return de;
+ }
+ }
+}
+
+static inline enum dirent_type get_inode_mode(uint8_t flags)
+{
+ return (flags & 0x02) ? DT_DIR : DT_REG;
+}
+
+static struct inode *iso_get_inode(struct fs_info *fs,
+ const struct iso_dir_entry *de)
+{
+ struct inode *inode = new_iso_inode(fs);
+ int blktosec = BLOCK_SHIFT(fs) - SECTOR_SHIFT(fs);
+
+ if (!inode)
+ return NULL;
+
+ dprintf("Getting inode for: %.*s\n", de->name_len, de->name);
+
+ inode->mode = get_inode_mode(de->flags);
+ inode->size = de->size_le;
+ PVT(inode)->lba = de->extent_le;
+ inode->blocks = (inode->size + BLOCK_SIZE(fs) - 1) >> BLOCK_SHIFT(fs);
+
+ /* We have a single extent for all data */
+ inode->next_extent.pstart = de->extent_le << blktosec;
+ inode->next_extent.len = inode->blocks << blktosec;
+
+ return inode;
+}
+
+static struct inode *iso_iget_root(struct fs_info *fs)
+{
+ const struct iso_dir_entry *root = &ISO_SB(fs)->root;
+
+ return iso_get_inode(fs, root);
+}
+
+static struct inode *iso_iget(const char *dname, struct inode *parent)
+{
+ const struct iso_dir_entry *de;
+
+ dprintf("iso_iget %p %s\n", parent, dname);
+
+ de = iso_find_entry(dname, parent);
+ if (!de)
+ return NULL;
+
+ return iso_get_inode(parent->fs, de);
+}
+
+static int iso_readdir(struct file *file, struct dirent *dirent)
+{
+ struct fs_info *fs = file->fs;
+ struct inode *inode = file->inode;
+ const struct iso_dir_entry *de;
+ const char *data = NULL;
+
+ while (1) {
+ size_t offset = file->offset & (BLOCK_SIZE(fs) - 1);
+
+ if (!data) {
+ uint32_t i = file->offset >> BLOCK_SHIFT(fs);
+ if (i >= inode->blocks)
+ return -1;
+ data = get_cache(fs->fs_dev, PVT(inode)->lba + i);
+ }
+ de = (const struct iso_dir_entry *)(data + offset);
+
+ if (de->length < 33 || offset + de->length > BLOCK_SIZE(fs)) {
+ file->offset = (file->offset + BLOCK_SIZE(fs))
+ & ~(BLOCK_SIZE(fs) - 1); /* Start of the next block */
+ data = NULL;
+ continue;
+ }
+ break;
+ }
+
+ dirent->d_ino = 0; /* Inode number is invalid to ISO fs */
+ dirent->d_off = file->offset;
+ dirent->d_type = get_inode_mode(de->flags);
+ dirent->d_reclen = offsetof(struct dirent, d_name) + 1 +
+ iso_convert_name(dirent->d_name, de->name, de->name_len);
+
+ file->offset += de->length; /* Update for next reading */
+
+ return 0;
+}
+
+/* Load the config file, return 1 if failed, or 0 */
+static int iso_load_config(void)
+{
+ const char *search_directories[] = {
+ "/boot/isolinux",
+ "/isolinux",
+ "/",
+ NULL
+ };
+ com32sys_t regs;
+ int i;
+
+ for (i = 0; search_directories[i]; i++) {
+ memset(&regs, 0, sizeof regs);
+ snprintf(ConfigName, FILENAME_MAX, "%s/isolinux.cfg",
+ search_directories[i]);
+ regs.edi.w[0] = OFFS_WRT(ConfigName, 0);
+ call16(core_open, &regs, &regs);
+ if (!(regs.eflags.l & EFLAGS_ZF))
+ break;
+ }
+ if (!search_directories[i])
+ return -1;
+
+ /* Set the current working directory */
+ chdir(search_directories[i]);
+ return 0;
+}
+
+static int iso_fs_init(struct fs_info *fs)
+{
+ struct iso_sb_info *sbi;
+ char pvd[2048]; /* Primary Volume Descriptor */
+ uint32_t pvd_lba;
+ struct disk *disk = fs->fs_dev->disk;
+ int blktosec;
+
+ sbi = malloc(sizeof(*sbi));
+ if (!sbi) {
+ malloc_error("iso_sb_info structure");
+ return 1;
+ }
+ fs->fs_info = sbi;
+
+ /*
+ * XXX: handling iso9660 in hybrid mode on top of a 4K-logical disk
+ * will really, really hurt...
+ */
+ fs->sector_shift = fs->fs_dev->disk->sector_shift;
+ fs->block_shift = 11; /* A CD-ROM block is always 2K */
+ fs->sector_size = 1 << fs->sector_shift;
+ fs->block_size = 1 << fs->block_shift;
+ blktosec = fs->block_shift - fs->sector_shift;
+
+ pvd_lba = iso_boot_info.pvd;
+ if (!pvd_lba)
+ pvd_lba = 16; /* Default if not otherwise defined */
+
+ disk->rdwr_sectors(disk, pvd, (sector_t)pvd_lba << blktosec,
+ 1 << blktosec, false);
+ memcpy(&sbi->root, pvd + ROOT_DIR_OFFSET, sizeof(sbi->root));
+
+ /* Initialize the cache */
+ cache_init(fs->fs_dev, fs->block_shift);
+
+ return fs->block_shift;
+}
+
+
+const struct fs_ops iso_fs_ops = {
+ .fs_name = "iso",
+ .fs_flags = FS_USEMEM | FS_THISIND,
+ .fs_init = iso_fs_init,
+ .searchdir = NULL,
+ .getfssec = generic_getfssec,
+ .close_file = generic_close_file,
+ .mangle_name = iso_mangle_name,
+ .load_config = iso_load_config,
+ .iget_root = iso_iget_root,
+ .iget = iso_iget,
+ .readdir = iso_readdir,
+ .next_extent = no_next_extent,
+};
diff --git a/core/fs/iso9660/iso9660_fs.h b/core/fs/iso9660/iso9660_fs.h
new file mode 100644
index 00000000..a365fa1a
--- /dev/null
+++ b/core/fs/iso9660/iso9660_fs.h
@@ -0,0 +1,51 @@
+#ifndef ISO9660_FS_H
+#define ISO9660_FS_H
+
+#include <klibc/compiler.h>
+#include <stdint.h>
+
+/* Boot info table */
+struct iso_boot_info {
+ uint32_t pvd; /* LBA of primary volume descriptor */
+ uint32_t file; /* LBA of boot file */
+ uint32_t length; /* Length of boot file */
+ uint32_t csum; /* Checksum of boot file */
+ uint32_t reserved[10]; /* Currently unused */
+};
+
+extern struct iso_boot_info iso_boot_info; /* In isolinux.asm */
+
+/* The root dir entry offset in the primary volume descriptor */
+#define ROOT_DIR_OFFSET 156
+
+struct iso_dir_entry {
+ uint8_t length; /* 00 */
+ uint8_t ext_attr_length; /* 01 */
+ uint32_t extent_le; /* 02 */
+ uint32_t extent_be; /* 06 */
+ uint32_t size_le; /* 0a */
+ uint32_t size_be; /* 0e */
+ uint8_t date[7]; /* 12 */
+ uint8_t flags; /* 19 */
+ uint8_t file_unit_size; /* 1a */
+ uint8_t interleave; /* 1b */
+ uint16_t volume_sequence_number_le; /* 1c */
+ uint16_t volume_sequence_number_be; /* 1e */
+ uint8_t name_len; /* 20 */
+ char name[0]; /* 21 */
+} __packed;
+
+struct iso_sb_info {
+ struct iso_dir_entry root;
+};
+
+/*
+ * iso9660 private inode information
+ */
+struct iso9660_pvt_inode {
+ uint32_t lba; /* Starting LBA of file data area*/
+};
+
+#define PVT(i) ((struct iso9660_pvt_inode *)((i)->pvt))
+
+#endif /* iso9660_fs.h */
diff --git a/core/fs/lib/close.c b/core/fs/lib/close.c
new file mode 100644
index 00000000..279598bc
--- /dev/null
+++ b/core/fs/lib/close.c
@@ -0,0 +1,9 @@
+#include "fs.h"
+
+void generic_close_file(struct file *file)
+{
+ if (file->inode) {
+ file->offset = 0;
+ put_inode(file->inode);
+ }
+}
diff --git a/core/fs/lib/loadconfig.c b/core/fs/lib/loadconfig.c
new file mode 100644
index 00000000..9318c1c3
--- /dev/null
+++ b/core/fs/lib/loadconfig.c
@@ -0,0 +1,24 @@
+#include <dprintf.h>
+#include <stdio.h>
+#include <string.h>
+#include <core.h>
+#include <fs.h>
+
+/*
+ * Standard version of load_config for extlinux-installed filesystems
+ */
+int generic_load_config(void)
+{
+ com32sys_t regs;
+
+ chdir(CurrentDirName);
+ realpath(ConfigName, "extlinux.conf", FILENAME_MAX);
+
+ dprintf("Config = %s\n", ConfigName);
+
+ memset(&regs, 0, sizeof regs);
+ regs.edi.w[0] = OFFS_WRT(ConfigName, 0);
+ call16(core_open, &regs, &regs);
+
+ return (regs.eflags.l & EFLAGS_ZF) ? -1 : 0;
+}
diff --git a/core/fs/lib/mangle.c b/core/fs/lib/mangle.c
new file mode 100644
index 00000000..813099fb
--- /dev/null
+++ b/core/fs/lib/mangle.c
@@ -0,0 +1,47 @@
+/**
+ * mangle_name:
+ *
+ * Mangle a filename pointed to by src into a buffer pointed
+ * to by dst; ends on encountering any whitespace.
+ * dst is preserved.
+ *
+ * This verifies that a filename is < FILENAME_MAX characters,
+ * doesn't contain whitespace, zero-pads the output buffer,
+ * and removes redundant slashes.
+ *
+ */
+
+#include <string.h>
+#include "fs.h"
+
+void generic_mangle_name(char *dst, const char *src)
+{
+ char *p = dst;
+ int i = FILENAME_MAX-1;
+
+ while (not_whitespace(*src)) {
+ if (*src == '/') {
+ if (src[1] == '/') {
+ src++;
+ i--;
+ continue;
+ }
+ }
+ i--;
+ *dst++ = *src++;
+ }
+
+ while (1) {
+ if (dst == p)
+ break;
+ if (dst[-1] != '/')
+ break;
+
+ dst--;
+ i++;
+ }
+
+ i++;
+ for (; i > 0; i --)
+ *dst++ = '\0';
+}
diff --git a/core/fs/loadhigh.c b/core/fs/loadhigh.c
new file mode 100644
index 00000000..e365b1a3
--- /dev/null
+++ b/core/fs/loadhigh.c
@@ -0,0 +1,113 @@
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Copyright 1994-2009 H. Peter Anvin - All Rights Reserved
+ * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ * Boston MA 02111-1307, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * loadhigh.c
+ *
+ * An alternate interface to getfssec.
+ *
+ * Inputs: SI = file handle/cluster pointer
+ * EDI = target address in high memory
+ * EAX = maximum number of bytes to load
+ * DX = zero-padding mask (e.g. 0003h for pad to dword)
+ * BX = 16-bit subroutine to call at the top of each loop
+ * (to print status and check for abort)
+ * EBP = maximum load address
+ *
+ * Outputs: SI = file handle/cluster pointer
+ * EBX = first untouched address (not including padding)
+ * EDI = first untouched address (including padding)
+ * CF = reached EOF
+ * OF = ran out of high memory
+ */
+
+#include <com32.h>
+#include <minmax.h>
+#include "core.h"
+#include "fs.h"
+
+#define MAX_CHUNK (1 << 20) /* 1 MB */
+
+void pm_load_high(com32sys_t *regs)
+{
+ struct fs_info *fs;
+ uint32_t bytes;
+ uint32_t zero_mask;
+ bool have_more;
+ uint32_t bytes_read;
+ char *buf, *limit;
+ struct file *file;
+ uint32_t sector_mask;
+ size_t pad;
+
+ bytes = regs->eax.l;
+ zero_mask = regs->edx.w[0];
+ buf = (char *)regs->edi.l;
+ limit = (char *)(regs->ebp.l & ~zero_mask);
+ file = handle_to_file(regs->esi.w[0]);
+ fs = file->fs;
+
+ regs->eflags.l &= ~(EFLAGS_CF|EFLAGS_OF|EFLAGS_AF|
+ EFLAGS_PF|EFLAGS_ZF|EFLAGS_SF);
+
+ sector_mask = SECTOR_SIZE(fs) - 1;
+
+ while (bytes) {
+ uint32_t sectors;
+ uint32_t chunk;
+
+ if (buf + SECTOR_SIZE(fs) > limit) {
+ /* Can't fit even one more sector in... */
+ regs->eflags.l |= EFLAGS_OF;
+ break;
+ }
+
+ chunk = bytes;
+
+ if (regs->ebx.w[0]) {
+ call16((void (*)(void))(size_t)regs->ebx.w[0], &zero_regs, NULL);
+ chunk = min(chunk, MAX_CHUNK);
+ }
+
+ if (chunk > (((char *)limit - buf) & ~sector_mask))
+ chunk = ((char *)limit - buf) & ~sector_mask;
+
+ sectors = (chunk + sector_mask) >> SECTOR_SHIFT(fs);
+ bytes_read = fs->fs_ops->getfssec(file, buf, sectors, &have_more);
+
+ if (bytes_read > chunk)
+ bytes_read = chunk;
+
+ buf += bytes_read;
+ bytes -= bytes_read;
+
+ if (!have_more) {
+ /*
+ * If we reach EOF, the filesystem driver will have already closed
+ * the underlying file... this really should be cleaner.
+ */
+ _close_file(file);
+ regs->esi.w[0] = 0;
+ regs->eflags.l |= EFLAGS_CF;
+ break;
+ }
+ }
+
+ pad = (size_t)buf & zero_mask;
+ if (pad)
+ memset(buf, 0, pad);
+
+ regs->ebx.l = (size_t)buf;
+ regs->edi.l = (size_t)buf + pad;
+}
diff --git a/core/fs/newconfig.c b/core/fs/newconfig.c
new file mode 100644
index 00000000..58c47a51
--- /dev/null
+++ b/core/fs/newconfig.c
@@ -0,0 +1,41 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2010 Intel Corporation; author: H. Peter Anvin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston MA 02110-1301, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * newconfig.c
+ *
+ * Load a new configuration file
+ */
+
+#include "core.h"
+#include "fs.h"
+
+void pm_is_config_file(com32sys_t *regs)
+{
+ char target_cwd[FILENAME_MAX];
+ const char *p;
+
+ (void)regs;
+
+ /* Save configuration file as an absolute path for posterity */
+ realpath(ConfigName, KernelName, FILENAME_MAX);
+
+ /* If we got anything on the command line, do a chdir */
+ p = cmd_line;
+ while (*p && !not_whitespace(*p))
+ p++;
+
+ if (*p) {
+ mangle_name(target_cwd, p);
+ chdir(target_cwd);
+ }
+}
diff --git a/core/fs/nonextextent.c b/core/fs/nonextextent.c
new file mode 100644
index 00000000..0c1ce2ce
--- /dev/null
+++ b/core/fs/nonextextent.c
@@ -0,0 +1,13 @@
+#include "fs.h"
+
+/*
+ * Use this routine for the next_extent() pointer when we never should
+ * be calling next_extent(), e.g. iso9660.
+ */
+int no_next_extent(struct inode *inode, uint32_t lstart)
+{
+ (void)inode;
+ (void)lstart;
+
+ return -1;
+}
diff --git a/core/fs/pxe/dhcp_option.c b/core/fs/pxe/dhcp_option.c
new file mode 100644
index 00000000..e18b605d
--- /dev/null
+++ b/core/fs/pxe/dhcp_option.c
@@ -0,0 +1,265 @@
+#include <stdio.h>
+#include <string.h>
+#include <core.h>
+#include <sys/cpu.h>
+#include "pxe.h"
+
+char LocalDomain[256];
+
+int over_load;
+uint8_t uuid_type;
+char uuid[17];
+
+static void parse_dhcp_options(void *, int, uint8_t);
+
+static void subnet_mask(void *data, int opt_len)
+{
+ if (opt_len != 4)
+ return;
+ net_mask = *(uint32_t *)data;
+}
+
+static void router(void *data, int opt_len)
+{
+ if (opt_len != 4)
+ return;
+ gate_way = *(uint32_t *)data;
+}
+
+static void dns_servers(void *data, int opt_len)
+{
+ int num = opt_len >> 2;
+ int i;
+
+ if (num > DNS_MAX_SERVERS)
+ num = DNS_MAX_SERVERS;
+
+ for (i = 0; i < num; i++) {
+ dns_server[i] = *(uint32_t *)data;
+ data += 4;
+ }
+
+#if 0
+ /*
+ * if you find you got no corret DNS server, you can add
+ * it here manually. BUT be carefull the DNS_MAX_SERVERS
+ */
+ if (i < DNS_MAX_SERVERS ) {
+ dns_server[i++] = your_master_dns_server;
+ dns_server[i++] = your_second_dns_server;
+ }
+#endif
+}
+
+static void local_domain(void *data, int opt_len)
+{
+ char *p = (char *)data + opt_len;
+ char *ld = LocalDomain;
+ char end = *p;
+
+ *p = '\0'; /* Zero-terminate option */
+ dns_mangle(&ld, data);
+ *p = end; /* Restore ending byte */
+}
+
+static void vendor_encaps(void *data, int opt_len)
+{
+ /* Only recongnize PXELINUX options */
+ parse_dhcp_options(data, opt_len, 208);
+}
+
+static void option_overload(void *data, int opt_len)
+{
+ if (opt_len != 1)
+ return;
+ over_load = *(uint8_t *)data;
+}
+
+
+static void server(void *data, int opt_len)
+{
+ uint32_t ip;
+
+ if (opt_len != 4)
+ return;
+
+ if (server_ip)
+ return;
+
+ ip = *(uint32_t *)data;
+ if (ip_ok(ip))
+ server_ip = ip;
+}
+
+static void client_identifier(void *data, int opt_len)
+{
+ if (opt_len > MAC_MAX || opt_len < 2 ||
+ MAC_len != (opt_len >> 8) ||
+ *(uint8_t *)data != MAC_type)
+ return;
+
+ opt_len --;
+ MAC_len = opt_len & 0xff;
+ memcpy(MAC, data+1, opt_len);
+ MAC[opt_len] = 0;
+}
+
+static void bootfile_name(void *data, int opt_len)
+{
+ strncpy(boot_file, data, opt_len);
+ boot_file[opt_len] = 0;
+}
+
+static void uuid_client_identifier(void *data, int opt_len)
+{
+ int type = *(uint8_t *)data;
+ if (opt_len != 17 || type != 0 || have_uuid)
+ return;
+
+ have_uuid = true;
+ uuid_type = type;
+ memcpy(uuid, data+1, 16);
+ uuid[16] = 0;
+}
+
+static void pxelinux_configfile(void *data, int opt_len)
+{
+ DHCPMagic |= 2;
+ strncpy(ConfigName, data, opt_len);
+ ConfigName[opt_len] = 0;
+}
+
+static void pxelinux_pathprefix(void *data, int opt_len)
+{
+ DHCPMagic |= 4;
+ strncpy(path_prefix, data, opt_len);
+ path_prefix[opt_len] = 0;
+}
+
+static void pxelinux_reboottime(void *data, int opt_len)
+{
+ if ((opt_len && 0xff) != 4)
+ return ;
+
+ RebootTime = ntohl(*(uint32_t *)data);
+ DHCPMagic |= 8; /* Got reboot time */
+}
+
+
+struct dhcp_options {
+ int opt_num;
+ void (*fun) (void *, int);
+};
+
+static struct dhcp_options dhcp_opts[] = {
+ {1, subnet_mask},
+ {3, router},
+ {6, dns_servers},
+ {15, local_domain},
+ {43, vendor_encaps},
+ {52, option_overload},
+ {54, server},
+ {61, client_identifier},
+ {67, bootfile_name},
+ {97, uuid_client_identifier},
+ {209, pxelinux_configfile},
+ {210, pxelinux_pathprefix},
+ {211, pxelinux_reboottime}
+};
+
+/*
+ * Parse a sequence of DHCP options, pointed to by _option_;
+ * -- some DHCP servers leave option fields unterminated
+ * in violation of the spec.
+ *
+ * filter contains the minimum value for the option to recognize
+ * -- this is used to restrict parsing to PXELINUX-specific options only.
+ */
+static void parse_dhcp_options(void *option, int size, uint8_t opt_filter)
+{
+ uint8_t opt_num;
+ uint8_t opt_len;
+ int opt_entries = sizeof(dhcp_opts) / sizeof(dhcp_opts[0]);
+ int i = 0;
+ char *p = option;
+ struct dhcp_options *opt;
+
+ while (size--) {
+ opt_num = *p++;
+
+ if (!size)
+ break;
+ if (opt_num == 0)
+ continue;
+ if (opt_num == 0xff)
+ break;
+
+ /* Anything else will have a lenght filed */
+ opt_len = *p++; /* c <- option lenght */
+ size = size - opt_len - 1;
+ if (size < 0)
+ break;
+ if (opt_num < opt_filter) { /* Is the option value valid */
+ option += opt_len; /* Try next */
+ continue;
+ }
+
+ opt = dhcp_opts;
+ for (i = 0; i < opt_entries; i++) {
+ if (opt_num == opt->opt_num) {
+ opt->fun(p, opt_len);
+ break;
+ }
+ opt ++;
+ }
+
+ /* parse next */
+ p += opt_len;
+ }
+}
+
+/*
+ * parse_dhcp
+ *
+ * Parse a DHCP packet. This includes dealing with "overloaded"
+ * option fields (see RFC 2132, section 9.3)
+ *
+ * This should fill in the following global variables, if the
+ * information is present:
+ *
+ * MyIP - client IP address
+ * server_ip - boot server IP address
+ * net_mask - network mask
+ * gate_way - default gateway router IP
+ * boot_file - boot file name
+ * DNSServers - DNS server IPs
+ * LocalDomain - Local domain name
+ * MAC_len, MAC - Client identifier, if MAC_len == 0
+ *
+ * This assumes the DHCP packet is in "trackbuf".
+ *
+ */
+void parse_dhcp(int pkt_len)
+{
+ struct bootp_t *dhcp = (struct bootp_t *)trackbuf;
+ int opt_len;
+
+ over_load = 0;
+ if (ip_ok(dhcp->yip))
+ MyIP = dhcp->yip;
+
+ if (ip_ok(dhcp->sip))
+ server_ip = dhcp->sip;
+
+ opt_len = (char *)dhcp + pkt_len - (char *)&dhcp->options;
+ if (opt_len && (dhcp->option_magic == BOOTP_OPTION_MAGIC))
+ parse_dhcp_options(&dhcp->options, opt_len, 0);
+
+ if (over_load & 1)
+ parse_dhcp_options(&dhcp->bootfile, 128, 0);
+ else if (dhcp->bootfile[0])
+ strcpy(boot_file, dhcp->bootfile);
+
+ if (over_load & 2)
+ parse_dhcp_options(dhcp->sname, 64, 0);
+}
diff --git a/core/fs/pxe/dnsresolv.c b/core/fs/pxe/dnsresolv.c
new file mode 100644
index 00000000..df7f33c1
--- /dev/null
+++ b/core/fs/pxe/dnsresolv.c
@@ -0,0 +1,352 @@
+#include <stdio.h>
+#include <string.h>
+#include <core.h>
+#include "pxe.h"
+
+/* DNS CLASS values we care about */
+#define CLASS_IN 1
+
+/* DNS TYPE values we care about */
+#define TYPE_A 1
+#define TYPE_CNAME 5
+
+/*
+ * The DNS header structure
+ */
+struct dnshdr {
+ uint16_t id;
+ uint16_t flags;
+ /* number of entries in the question section */
+ uint16_t qdcount;
+ /* number of resource records in the answer section */
+ uint16_t ancount;
+ /* number of name server resource records in the authority records section*/
+ uint16_t nscount;
+ /* number of resource records in the additional records section */
+ uint16_t arcount;
+} __attribute__ ((packed));
+
+/*
+ * The DNS query structure
+ */
+struct dnsquery {
+ uint16_t qtype;
+ uint16_t qclass;
+} __attribute__ ((packed));
+
+/*
+ * The DNS Resource recodes structure
+ */
+struct dnsrr {
+ uint16_t type;
+ uint16_t class;
+ uint32_t ttl;
+ uint16_t rdlength; /* The lenght of this rr data */
+ char rdata[];
+} __attribute__ ((packed));
+
+
+uint32_t dns_server[DNS_MAX_SERVERS] = {0, };
+
+
+/*
+ * Turn a string in _src_ into a DNS "label set" in _dst_; returns the
+ * number of dots encountered. On return, *dst is updated.
+ */
+int dns_mangle(char **dst, const char *p)
+{
+ char *q = *dst;
+ char *count_ptr;
+ char c;
+ int dots = 0;
+
+ count_ptr = q;
+ *q++ = 0;
+
+ while (1) {
+ c = *p++;
+ if (c == 0 || c == ':')
+ break;
+ if (c == '.') {
+ dots++;
+ count_ptr = q;
+ *q++ = 0;
+ continue;
+ }
+
+ *count_ptr += 1;
+ *q++ = c;
+ }
+
+ if (*count_ptr)
+ *q++ = 0;
+
+ /* update the strings */
+ *dst = q;
+ return dots;
+}
+
+
+/*
+ * Compare two sets of DNS labels, in _s1_ and _s2_; the one in _s2_
+ * is allowed pointers relative to a packet in buf.
+ *
+ */
+static bool dns_compare(const void *s1, const void *s2, const void *buf)
+{
+ const uint8_t *q = s1;
+ const uint8_t *p = s2;
+ unsigned int c0, c1;
+
+ while (1) {
+ c0 = p[0];
+ if (c0 >= 0xc0) {
+ /* Follow pointer */
+ c1 = p[1];
+ p = (const uint8_t *)buf + ((c0 - 0xc0) << 8) + c1;
+ } else if (c0) {
+ c0++; /* Include the length byte */
+ if (memcmp(q, p, c0))
+ return false;
+ q += c0;
+ p += c0;
+ } else {
+ return *q == 0;
+ }
+ }
+}
+
+/*
+ * Copy a DNS label into a buffer, considering the possibility that we might
+ * have to follow pointers relative to "buf".
+ * Returns a pointer to the first free byte *after* the terminal null.
+ */
+static void *dns_copylabel(void *dst, const void *src, const void *buf)
+{
+ uint8_t *q = dst;
+ const uint8_t *p = src;
+ unsigned int c0, c1;
+
+ while (1) {
+ c0 = p[0];
+ if (c0 >= 0xc0) {
+ /* Follow pointer */
+ c1 = p[1];
+ p = (const uint8_t *)buf + ((c0 - 0xc0) << 8) + c1;
+ } else if (c0) {
+ c0++; /* Include the length byte */
+ memcpy(q, p, c0);
+ p += c0;
+ q += c0;
+ } else {
+ *q++ = 0;
+ return q;
+ }
+ }
+}
+
+/*
+ * Skip past a DNS label set in DS:SI
+ */
+static char *dns_skiplabel(char *label)
+{
+ uint8_t c;
+
+ while (1) {
+ c = *label++;
+ if (c >= 0xc0)
+ return ++label; /* pointer is two bytes */
+ if (c == 0)
+ return label;
+ label += c;
+ }
+}
+
+/*
+ * Actual resolver function
+ * Points to a null-terminated or :-terminated string in _name_
+ * and returns the ip addr in _ip_ if it exists and can be found.
+ * If _ip_ = 0 on exit, the lookup failed. _name_ will be updated
+ *
+ * XXX: probably need some caching here.
+ */
+uint32_t dns_resolv(const char *name)
+{
+ static char __lowmem DNSSendBuf[PKTBUF_SIZE];
+ static char __lowmem DNSRecvBuf[PKTBUF_SIZE];
+ char *p;
+ int err;
+ int dots;
+ int same;
+ int rd_len;
+ int ques, reps; /* number of questions and replies */
+ uint8_t timeout;
+ const uint8_t *timeout_ptr = TimeoutTable;
+ uint32_t oldtime;
+ uint32_t srv;
+ uint32_t *srv_ptr = dns_server;
+ struct dnshdr *hd1 = (struct dnshdr *)DNSSendBuf;
+ struct dnshdr *hd2 = (struct dnshdr *)DNSRecvBuf;
+ struct dnsquery *query;
+ struct dnsrr *rr;
+ static __lowmem struct s_PXENV_UDP_WRITE udp_write;
+ static __lowmem struct s_PXENV_UDP_READ udp_read;
+ uint16_t local_port;
+ uint32_t result = 0;
+
+ /* Get a local port number */
+ local_port = get_port();
+
+ /* First, fill the DNS header struct */
+ hd1->id++; /* New query ID */
+ hd1->flags = htons(0x0100); /* Recursion requested */
+ hd1->qdcount = htons(1); /* One question */
+ hd1->ancount = 0; /* No answers */
+ hd1->nscount = 0; /* No NS */
+ hd1->arcount = 0; /* No AR */
+
+ p = DNSSendBuf + sizeof(struct dnshdr);
+ dots = dns_mangle(&p, name); /* store the CNAME */
+
+ if (!dots) {
+ p--; /* Remove final null */
+ /* Uncompressed DNS label set so it ends in null */
+ strcpy(p, LocalDomain);
+ }
+
+ /* Fill the DNS query packet */
+ query = (struct dnsquery *)p;
+ query->qtype = htons(TYPE_A);
+ query->qclass = htons(CLASS_IN);
+ p += sizeof(struct dnsquery);
+
+ /* Now send it to name server */
+ timeout_ptr = TimeoutTable;
+ timeout = *timeout_ptr++;
+ while (srv_ptr < dns_server + DNS_MAX_SERVERS) {
+ srv = *srv_ptr++;
+ if (!srv)
+ continue; /* just move on before runing the time out */
+ udp_write.status = 0;
+ udp_write.ip = srv;
+ udp_write.gw = ((srv ^ MyIP) & net_mask) ? gate_way : 0;
+ udp_write.src_port = local_port;
+ udp_write.dst_port = DNS_PORT;
+ udp_write.buffer_size = p - DNSSendBuf;
+ udp_write.buffer = FAR_PTR(DNSSendBuf);
+ err = pxe_call(PXENV_UDP_WRITE, &udp_write);
+ if (err || udp_write.status != 0)
+ continue;
+
+ oldtime = jiffies();
+ while (1) {
+ udp_read.status = 0;
+ udp_read.src_ip = srv;
+ udp_read.dest_ip = MyIP;
+ udp_read.s_port = DNS_PORT;
+ udp_read.d_port = local_port;
+ udp_read.buffer_size = DNS_MAX_PACKET;
+ udp_read.buffer = FAR_PTR(DNSRecvBuf);
+ err = pxe_call(PXENV_UDP_READ, &udp_read);
+ if (err || udp_read.status)
+ continue;
+
+ /* Got a packet, deal with it... */
+ if (hd2->id == hd1->id)
+ break;
+
+ if (jiffies()-oldtime >= timeout) {
+ /* time out */
+ timeout = *timeout_ptr++;
+ if (!timeout)
+ goto done; /* All time ticks run out */
+ else
+ goto again;
+ }
+ }
+ if ((hd2->flags ^ 0x80) & htons(0xf80f))
+ goto badness;
+
+ ques = htons(hd2->qdcount); /* Questions */
+ reps = htons(hd2->ancount); /* Replies */
+ p = DNSRecvBuf + sizeof(struct dnshdr);
+ while (ques--) {
+ p = dns_skiplabel(p); /* Skip name */
+ p += 4; /* Skip question trailer */
+ }
+
+ /* Parse the replies */
+ while (reps--) {
+ same = dns_compare(DNSSendBuf + sizeof(struct dnshdr),
+ p, DNSRecvBuf);
+ p = dns_skiplabel(p);
+ rr = (struct dnsrr *)p;
+ rd_len = ntohs(rr->rdlength);
+ if (same && ntohs(rr->class) == CLASS_IN) {
+ switch (ntohs(rr->type)) {
+ case TYPE_A:
+ if (rd_len == 4) {
+ result = *(uint32_t *)rr->rdata;
+ goto done;
+ }
+ break;
+ case TYPE_CNAME:
+ dns_copylabel(DNSSendBuf + sizeof(struct dnshdr),
+ rr->rdata, DNSRecvBuf);
+ /*
+ * We should probably rescan the packet from the top
+ * here, and technically we might have to send a whole
+ * new request here...
+ */
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* not the one we want, try next */
+ p += sizeof(struct dnsrr) + rd_len;
+ }
+
+ badness:
+ /*
+ *
+ ; We got back no data from this server.
+ ; Unfortunately, for a recursive, non-authoritative
+ ; query there is no such thing as an NXDOMAIN reply,
+ ; which technically means we can't draw any
+ ; conclusions. However, in practice that means the
+ ; domain doesn't exist. If this turns out to be a
+ ; problem, we may want to add code to go through all
+ ; the servers before giving up.
+
+ ; If the DNS server wasn't capable of recursion, and
+ ; isn't capable of giving us an authoritative reply
+ ; (i.e. neither AA or RA set), then at least try a
+ ; different setver...
+ */
+ if (hd2->flags == htons(0x480))
+ continue;
+
+ break; /* failed */
+
+ again:
+ continue;
+ }
+
+done:
+ free_port(local_port); /* Return port number to the free pool */
+
+ return result;
+}
+
+
+/*
+ * the one should be called from ASM file
+ */
+void pxe_dns_resolv(com32sys_t *regs)
+{
+ const char *name = MK_PTR(regs->ds, regs->esi.w[0]);
+
+ regs->eax.l = dns_resolv(name);
+}
diff --git a/core/fs/pxe/idle.c b/core/fs/pxe/idle.c
new file mode 100644
index 00000000..0538b163
--- /dev/null
+++ b/core/fs/pxe/idle.c
@@ -0,0 +1,112 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2008 H. Peter Anvin - All Rights Reserved
+ * Copyright 2009 Intel Corporation; author: H. Peter Anvin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston MA 02110-1301, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include <stdio.h>
+#include <string.h>
+#include <core.h>
+#include <fs.h>
+#include <minmax.h>
+#include <sys/cpu.h>
+#include "pxe.h"
+
+static int pxe_idle_poll(void)
+{
+ static __lowmem char junk_pkt[PKTBUF_SIZE];
+ static __lowmem t_PXENV_UDP_READ read_buf;
+
+ memset(&read_buf, 0, sizeof read_buf);
+
+ read_buf.src_ip = 0; /* Any destination */
+ read_buf.dest_ip = MyIP;
+ read_buf.s_port = 0; /* Any source port */
+ read_buf.d_port = htons(9); /* Discard port (not used...) */
+ read_buf.buffer_size = sizeof junk_pkt;
+ read_buf.buffer = FAR_PTR(junk_pkt);
+
+ pxe_call(PXENV_UDP_READ, &read_buf);
+
+ return 0;
+}
+
+static uint32_t pxe_detect_nic_type(void)
+{
+ static __lowmem t_PXENV_UNDI_GET_NIC_TYPE nic_type;
+
+ if (pxe_call(PXENV_UNDI_GET_NIC_TYPE, &nic_type))
+ return -1; /* Unknown NIC */
+
+ if (nic_type.NicType != PCI_NIC && nic_type.NicType != CardBus_NIC)
+ return -1; /* Not a PCI NIC */
+
+ /*
+ * Return VID:DID as a single number, with the VID in the high word
+ * -- this is opposite from the usual order, but it makes it easier to
+ * enforce that the table is sorted.
+ */
+ return (nic_type.info.pci.Vendor_ID << 16) + nic_type.info.pci.Dev_ID;
+}
+
+#define PCI_DEV(vid, did) (((vid) << 16) + (did))
+
+/* This array should be sorted!! */
+static const uint32_t pxe_need_idle_drain[] =
+{
+ /*
+ * Older Broadcom NICs: they need receive calls on idle to avoid
+ * FIFO stalls.
+ */
+ PCI_DEV(0x14e4, 0x1659), /* BCM5721 */
+ PCI_DEV(0x14e4, 0x165a), /* BCM5722 */
+ PCI_DEV(0x14e4, 0x165b), /* BCM5723 */
+ PCI_DEV(0x14e4, 0x1668), /* BCM5714 */
+ PCI_DEV(0x14e4, 0x1669), /* BCM5714S */
+ PCI_DEV(0x14e4, 0x166a), /* BCM5780 */
+ PCI_DEV(0x14e4, 0x1673), /* BCM5755M */
+ PCI_DEV(0x14e4, 0x1674), /* BCM5756ME */
+ PCI_DEV(0x14e4, 0x1678), /* BCM5715 */
+ PCI_DEV(0x14e4, 0x1679), /* BCM5715S */
+ PCI_DEV(0x14e4, 0x167b), /* BCM5755 */
+};
+
+void pxe_idle_init(void)
+{
+ uint32_t dev_id = pxe_detect_nic_type();
+ int l, h;
+ bool found;
+
+ l = 0;
+ h = sizeof pxe_need_idle_drain / sizeof pxe_need_idle_drain[0] - 1;
+
+ found = false;
+ while (h >= l) {
+ int x = (l+h) >> 1;
+ uint32_t id = pxe_need_idle_drain[x];
+
+ if (id == dev_id) {
+ found = true;
+ break;
+ } else if (id < dev_id) {
+ l = x+1;
+ } else {
+ h = x-1;
+ }
+ }
+
+ if (found)
+ idle_hook_func = pxe_idle_poll;
+}
+
+void pxe_idle_cleanup(void)
+{
+ idle_hook_func = NULL;
+}
diff --git a/core/fs/pxe/portnum.c b/core/fs/pxe/portnum.c
new file mode 100644
index 00000000..19af0cd0
--- /dev/null
+++ b/core/fs/pxe/portnum.c
@@ -0,0 +1,68 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2010 Intel Corporation; author: H. Peter Anvin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston MA 02110-1301, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <netinet/in.h>
+#include "pxe.h"
+
+/* Port number bitmap - port numbers 49152 (0xc000) to 57343 (0xefff) */
+#define PORT_NUMBER_BASE 49152
+#define PORT_NUMBER_COUNT 8192 /* Power of 2, please */
+static uint32_t port_number_bitmap[PORT_NUMBER_COUNT/32];
+static uint16_t first_port_number /* = 0 */;
+
+/*
+ * Bitmap functions
+ */
+static bool test_bit(const uint32_t *bitmap, int32_t index)
+{
+ uint8_t st;
+ asm("btl %2,%1 ; setc %0" : "=qm" (st) : "m" (*bitmap), "r" (index));
+ return st;
+}
+
+static void set_bit(uint32_t *bitmap, int32_t index)
+{
+ asm volatile("btsl %1,%0" : "+m" (*bitmap) : "r" (index) : "memory");
+}
+
+static void clr_bit(uint32_t *bitmap, int32_t index)
+{
+ asm volatile("btcl %1,%0" : "+m" (*bitmap) : "r" (index) : "memory");
+}
+
+/*
+ * Get and free a port number (host byte order)
+ */
+uint16_t get_port(void)
+{
+ uint16_t port;
+
+ do {
+ port = first_port_number++;
+ first_port_number &= PORT_NUMBER_COUNT - 1;
+ } while (test_bit(port_number_bitmap, port));
+
+ set_bit(port_number_bitmap, port);
+ return htons(port + PORT_NUMBER_BASE);
+}
+
+void free_port(uint16_t port)
+{
+ port = ntohs(port) - PORT_NUMBER_BASE;
+
+ if (port >= PORT_NUMBER_COUNT)
+ return;
+
+ clr_bit(port_number_bitmap, port);
+}
diff --git a/core/fs/pxe/pxe.c b/core/fs/pxe/pxe.c
new file mode 100644
index 00000000..011ef293
--- /dev/null
+++ b/core/fs/pxe/pxe.c
@@ -0,0 +1,1694 @@
+#include <dprintf.h>
+#include <stdio.h>
+#include <string.h>
+#include <core.h>
+#include <fs.h>
+#include <minmax.h>
+#include <sys/cpu.h>
+#include "pxe.h"
+
+#define GPXE 1
+
+uint32_t server_ip = 0; /* IP address of boot server */
+uint32_t net_mask = 0; /* net_mask of this subnet */
+uint32_t gate_way = 0; /* Default router */
+uint16_t real_base_mem; /* Amount of DOS memory after freeing */
+
+uint8_t MAC[MAC_MAX]; /* Actual MAC address */
+uint8_t MAC_len; /* MAC address len */
+uint8_t MAC_type; /* MAC address type */
+
+char __bss16 BOOTIFStr[7+3*(MAC_MAX+1)];
+#define MAC_str (BOOTIFStr+7)
+
+char boot_file[256]; /* From DHCP */
+char path_prefix[256]; /* From DHCP */
+char dot_quad_buf[16];
+
+static bool has_gpxe;
+static uint32_t gpxe_funcs;
+static uint8_t uuid_dashes[] = {4, 2, 2, 2, 6, 0};
+bool have_uuid = false;
+
+/* Common receive buffer */
+static __lowmem char packet_buf[PKTBUF_SIZE] __aligned(16);
+
+const uint8_t TimeoutTable[] = {
+ 2, 2, 3, 3, 4, 5, 6, 7, 9, 10, 12, 15, 18, 21, 26, 31, 37, 44,
+ 53, 64, 77, 92, 110, 132, 159, 191, 229, 255, 255, 255, 255, 0
+};
+
+struct tftp_options {
+ const char *str_ptr; /* string pointer */
+ size_t offset; /* offset into socket structre */
+};
+
+#define IFIELD(x) offsetof(struct inode, x)
+#define PFIELD(x) (offsetof(struct inode, pvt) + \
+ offsetof(struct pxe_pvt_inode, x))
+
+static const struct tftp_options tftp_options[] =
+{
+ { "tsize", IFIELD(size) },
+ { "blksize", PFIELD(tftp_blksize) },
+};
+static const int tftp_nopts = sizeof tftp_options / sizeof tftp_options[0];
+
+static void tftp_error(struct inode *file, uint16_t errnum,
+ const char *errstr);
+
+/*
+ * Allocate a local UDP port structure and assign it a local port number.
+ * Return the inode pointer if success, or null if failure
+ */
+static struct inode *allocate_socket(struct fs_info *fs)
+{
+ struct inode *inode = alloc_inode(fs, 0, sizeof(struct pxe_pvt_inode));
+
+ if (!inode) {
+ malloc_error("socket structure");
+ } else {
+ struct pxe_pvt_inode *socket = PVT(inode);
+ socket->tftp_localport = get_port();
+ }
+
+ return inode;
+}
+
+static void free_socket(struct inode *inode)
+{
+ struct pxe_pvt_inode *socket = PVT(inode);
+
+ free_port(socket->tftp_localport);
+ free_inode(inode);
+}
+
+#if GPXE
+static void gpxe_close_file(struct inode *inode)
+{
+ struct pxe_pvt_inode *socket = PVT(inode);
+ static __lowmem struct s_PXENV_FILE_CLOSE file_close;
+
+ file_close.FileHandle = socket->tftp_remoteport;
+ pxe_call(PXENV_FILE_CLOSE, &file_close);
+}
+#endif
+
+static void pxe_close_file(struct file *file)
+{
+ struct inode *inode = file->inode;
+ struct pxe_pvt_inode *socket = PVT(inode);
+
+ if (!socket->tftp_goteof) {
+#if GPXE
+ if (socket->tftp_localport == 0xffff) {
+ gpxe_close_file(inode);
+ } else
+#endif
+ if (socket->tftp_localport != 0) {
+ tftp_error(inode, 0, "No error, file close");
+ }
+ }
+
+ free_socket(inode);
+}
+
+/**
+ * Take a nubmer of bytes in memory and convert to lower-case hxeadecimal
+ *
+ * @param: dst, output buffer
+ * @param: src, input buffer
+ * @param: count, number of bytes
+ *
+ */
+static void lchexbytes(char *dst, const void *src, int count)
+{
+ uint8_t half;
+ uint8_t c;
+ const uint8_t *s = src;
+
+ for(; count > 0; count--) {
+ c = *s++;
+ half = ((c >> 4) & 0x0f) + '0';
+ *dst++ = half > '9' ? (half + 'a' - '9' - 1) : half;
+
+ half = (c & 0x0f) + '0';
+ *dst++ = half > '9' ? (half + 'a' - '9' - 1) : half;
+ }
+}
+
+/*
+ * just like the lchexbytes, except to upper-case
+ *
+ */
+static void uchexbytes(char *dst, const void *src, int count)
+{
+ uint8_t half;
+ uint8_t c;
+ const uint8_t *s = src;
+
+ for(; count > 0; count--) {
+ c = *s++;
+ half = ((c >> 4) & 0x0f) + '0';
+ *dst++ = half > '9' ? (half + 'A' - '9' - 1) : half;
+
+ half = (c & 0x0f) + '0';
+ *dst++ = half > '9' ? (half + 'A' - '9' - 1) : half;
+ }
+}
+
+/*
+ * Parse a single hexadecimal byte, which must be complete (two
+ * digits). This is used in URL parsing.
+ */
+static int hexbyte(const char *p)
+{
+ if (!is_hex(p[0]) || !is_hex(p[1]))
+ return -1;
+ else
+ return (hexval(p[0]) << 4) + hexval(p[1]);
+}
+
+/*
+ * Tests an IP address in _ip_ for validity; return with 0 for bad, 1 for good.
+ * We used to refuse class E, but class E addresses are likely to become
+ * assignable unicast addresses in the near future.
+ *
+ */
+int ip_ok(uint32_t ip)
+{
+ if (ip == -1 || /* Refuse the all-one address */
+ (ip & 0xff) == 0 || /* Refuse network zero */
+ (ip & 0xff) == 0xff || /* Refuse loopback */
+ (ip & 0xf0) == 0xe0 ) /* Refuse class D */
+ return 0;
+
+ return 1;
+}
+
+
+/*
+ * Take an IP address (in network byte order) in _ip_ and
+ * output a dotted quad string to _dst_, returns the length
+ * of the dotted quad ip string.
+ *
+ */
+static int gendotquad(char *dst, uint32_t ip)
+{
+ int part;
+ int i = 0, j;
+ char temp[4];
+ char *p = dst;
+
+ for (; i < 4; i++) {
+ j = 0;
+ part = ip & 0xff;
+ do {
+ temp[j++] = (part % 10) + '0';
+ }while(part /= 10);
+ for (; j > 0; j--)
+ *p++ = temp[j-1];
+ *p++ = '.';
+
+ ip >>= 8;
+ }
+ /* drop the last dot '.' and zero-terminate string*/
+ *(--p) = 0;
+
+ return p - dst;
+}
+
+/*
+ * parse the ip_str and return the ip address with *res.
+ * return the the string address after the ip string
+ *
+ */
+static const char *parse_dotquad(const char *ip_str, uint32_t *res)
+{
+ const char *p = ip_str;
+ uint8_t part = 0;
+ uint32_t ip = 0;
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ while (is_digit(*p)) {
+ part = part * 10 + *p - '0';
+ p++;
+ }
+ if (i != 3 && *p != '.')
+ return NULL;
+
+ ip = (ip << 8) | part;
+ part = 0;
+ p++;
+ }
+ p--;
+
+ *res = ip;
+ return p;
+}
+
+/*
+ * the ASM pxenv function wrapper, return 1 if error, or 0
+ *
+ */
+int pxe_call(int opcode, void *data)
+{
+ extern void pxenv(void);
+ com32sys_t regs;
+
+#if 0
+ printf("pxe_call op %04x data %p\n", opcode, data);
+#endif
+
+ memset(&regs, 0, sizeof regs);
+ regs.ebx.w[0] = opcode;
+ regs.es = SEG(data);
+ regs.edi.w[0] = OFFS(data);
+ call16(pxenv, &regs, &regs);
+
+ return regs.eflags.l & EFLAGS_CF; /* CF SET if fail */
+}
+
+/**
+ * Send an ERROR packet. This is used to terminate a connection.
+ *
+ * @inode: Inode structure
+ * @errnum: Error number (network byte order)
+ * @errstr: Error string (included in packet)
+ */
+static void tftp_error(struct inode *inode, uint16_t errnum,
+ const char *errstr)
+{
+ static __lowmem struct {
+ uint16_t err_op;
+ uint16_t err_num;
+ char err_msg[64];
+ } __packed err_buf;
+ static __lowmem struct s_PXENV_UDP_WRITE udp_write;
+ int len = min(strlen(errstr), sizeof(err_buf.err_msg)-1);
+ struct pxe_pvt_inode *socket = PVT(inode);
+
+ err_buf.err_op = TFTP_ERROR;
+ err_buf.err_num = errnum;
+ memcpy(err_buf.err_msg, errstr, len);
+ err_buf.err_msg[len] = '\0';
+
+ udp_write.src_port = socket->tftp_localport;
+ udp_write.dst_port = socket->tftp_remoteport;
+ udp_write.ip = socket->tftp_remoteip;
+ udp_write.gw = ((udp_write.ip ^ MyIP) & net_mask) ? gate_way : 0;
+ udp_write.buffer = FAR_PTR(&err_buf);
+ udp_write.buffer_size = 4 + len + 1;
+
+ /* If something goes wrong, there is nothing we can do, anyway... */
+ pxe_call(PXENV_UDP_WRITE, &udp_write);
+}
+
+
+/**
+ * Send ACK packet. This is a common operation and so is worth canning.
+ *
+ * @param: inode, Inode pointer
+ * @param: ack_num, Packet # to ack (network byte order)
+ *
+ */
+static void ack_packet(struct inode *inode, uint16_t ack_num)
+{
+ int err;
+ static __lowmem uint16_t ack_packet_buf[2];
+ static __lowmem struct s_PXENV_UDP_WRITE udp_write;
+ struct pxe_pvt_inode *socket = PVT(inode);
+
+ /* Packet number to ack */
+ ack_packet_buf[0] = TFTP_ACK;
+ ack_packet_buf[1] = ack_num;
+ udp_write.src_port = socket->tftp_localport;
+ udp_write.dst_port = socket->tftp_remoteport;
+ udp_write.ip = socket->tftp_remoteip;
+ udp_write.gw = ((udp_write.ip ^ MyIP) & net_mask) ? gate_way : 0;
+ udp_write.buffer = FAR_PTR(ack_packet_buf);
+ udp_write.buffer_size = 4;
+
+ err = pxe_call(PXENV_UDP_WRITE, &udp_write);
+#if 0
+ printf("sent %s\n", err ? "FAILED" : "OK");
+#endif
+}
+
+
+/**
+ * Get a DHCP packet from the PXE stack into the trackbuf
+ *
+ * @param: type, packet type
+ * @return: buffer size
+ *
+ */
+static int pxe_get_cached_info(int type)
+{
+ int err;
+ static __lowmem struct s_PXENV_GET_CACHED_INFO get_cached_info;
+ printf(" %02x", type);
+
+ get_cached_info.Status = 0;
+ get_cached_info.PacketType = type;
+ get_cached_info.BufferSize = 8192;
+ get_cached_info.Buffer = FAR_PTR(trackbuf);
+ err = pxe_call(PXENV_GET_CACHED_INFO, &get_cached_info);
+ if (err) {
+ printf("PXE API call failed, error %04x\n", err);
+ kaboom();
+ }
+
+ return get_cached_info.BufferSize;
+}
+
+
+/*
+ * Return the type of pathname passed.
+ */
+enum pxe_path_type {
+ PXE_RELATIVE, /* No :: or URL */
+ PXE_HOMESERVER, /* Starting with :: */
+ PXE_TFTP, /* host:: */
+ PXE_URL_TFTP, /* tftp:// */
+ PXE_URL, /* Absolute URL syntax */
+};
+
+static enum pxe_path_type pxe_path_type(const char *str)
+{
+ const char *p;
+
+ p = str;
+
+ while (1) {
+ switch (*p) {
+ case ':':
+ if (p[1] == ':') {
+ if (p == str)
+ return PXE_HOMESERVER;
+ else
+ return PXE_TFTP;
+ } else if (p > str && p[1] == '/' && p[2] == '/') {
+ if (strncasecmp(str, "tftp://", 7))
+ return PXE_URL_TFTP;
+ else
+ return PXE_URL;
+ }
+
+ /* else fall through */
+ case '/': case '!': case '@': case '#': case '%':
+ case '^': case '&': case '*': case '(': case ')':
+ case '[': case ']': case '{': case '}': case '\\':
+ case '|': case '=': case '`': case '~': case '\'':
+ case '\"': case ';': case '>': case '<': case '?':
+ case '\0':
+ /* Any of these characters terminate the colon search */
+ return PXE_RELATIVE;
+ default:
+ break;
+ }
+ p++;
+ }
+}
+
+#if GPXE
+
+/**
+ * Get a fresh packet from a gPXE socket
+ * @param: inode -> Inode pointer
+ *
+ */
+static void get_packet_gpxe(struct inode *inode)
+{
+ struct pxe_pvt_inode *socket = PVT(inode);
+ static __lowmem struct s_PXENV_FILE_READ file_read;
+ int err;
+
+ while (1) {
+ file_read.FileHandle = socket->tftp_remoteport;
+ file_read.Buffer = FAR_PTR(packet_buf);
+ file_read.BufferSize = PKTBUF_SIZE;
+ err = pxe_call(PXENV_FILE_READ, &file_read);
+ if (!err) /* successed */
+ break;
+
+ if (file_read.Status != PXENV_STATUS_TFTP_OPEN)
+ kaboom();
+ }
+
+ memcpy(socket->tftp_pktbuf, packet_buf, file_read.BufferSize);
+
+ socket->tftp_dataptr = socket->tftp_pktbuf;
+ socket->tftp_bytesleft = file_read.BufferSize;
+ socket->tftp_filepos += file_read.BufferSize;
+
+ if (socket->tftp_bytesleft == 0)
+ inode->size = socket->tftp_filepos;
+
+ /* if we're done here, close the file */
+ if (inode->size > socket->tftp_filepos)
+ return;
+
+ /* Got EOF, close it */
+ socket->tftp_goteof = 1;
+ gpxe_close_file(inode);
+}
+#endif /* GPXE */
+
+
+/*
+ * mangle a filename pointed to by _src_ into a buffer pointed
+ * to by _dst_; ends on encountering any whitespace.
+ *
+ */
+static void pxe_mangle_name(char *dst, const char *src)
+{
+ size_t len = FILENAME_MAX-1;
+
+ while (len-- && not_whitespace(*src))
+ *dst++ = *src++;
+
+ *dst = '\0';
+}
+
+/*
+ * Get a fresh packet if the buffer is drained, and we haven't hit
+ * EOF yet. The buffer should be filled immediately after draining!
+ */
+static void fill_buffer(struct inode *inode)
+{
+ int err;
+ int last_pkt;
+ const uint8_t *timeout_ptr = TimeoutTable;
+ uint8_t timeout;
+ uint16_t buffersize;
+ uint32_t oldtime;
+ void *data = NULL;
+ static __lowmem struct s_PXENV_UDP_READ udp_read;
+ struct pxe_pvt_inode *socket = PVT(inode);
+
+ if (socket->tftp_bytesleft || socket->tftp_goteof)
+ return;
+
+#if GPXE
+ if (socket->tftp_localport == 0xffff) {
+ get_packet_gpxe(inode);
+ return;
+ }
+#endif
+
+ /*
+ * Start by ACKing the previous packet; this should cause
+ * the next packet to be sent.
+ */
+ timeout_ptr = TimeoutTable;
+ timeout = *timeout_ptr++;
+ oldtime = jiffies();
+
+ ack_again:
+ ack_packet(inode, socket->tftp_lastpkt);
+
+ while (timeout) {
+ udp_read.buffer = FAR_PTR(packet_buf);
+ udp_read.buffer_size = PKTBUF_SIZE;
+ udp_read.src_ip = socket->tftp_remoteip;
+ udp_read.dest_ip = MyIP;
+ udp_read.s_port = socket->tftp_remoteport;
+ udp_read.d_port = socket->tftp_localport;
+ err = pxe_call(PXENV_UDP_READ, &udp_read);
+ if (err) {
+ uint32_t now = jiffies();
+
+ if (now-oldtime >= timeout) {
+ oldtime = now;
+ timeout = *timeout_ptr++;
+ if (!timeout)
+ break;
+ }
+ continue;
+ }
+
+ if (udp_read.buffer_size < 4) /* Bad size for a DATA packet */
+ continue;
+
+ data = packet_buf;
+ if (*(uint16_t *)data != TFTP_DATA) /* Not a data packet */
+ continue;
+
+ /* If goes here, recevie OK, break */
+ break;
+ }
+
+ /* time runs out */
+ if (timeout == 0)
+ kaboom();
+
+ last_pkt = socket->tftp_lastpkt;
+ last_pkt = ntohs(last_pkt); /* Host byte order */
+ last_pkt++;
+ last_pkt = htons(last_pkt); /* Network byte order */
+ if (*(uint16_t *)(data + 2) != last_pkt) {
+ /*
+ * Wrong packet, ACK the packet and try again.
+ * This is presumably because the ACK got lost,
+ * so the server just resent the previous packet.
+ */
+#if 0
+ printf("Wrong packet, wanted %04x, got %04x\n", \
+ htons(last_pkt), htons(*(uint16_t *)(data+2)));
+#endif
+ goto ack_again;
+ }
+
+ /* It's the packet we want. We're also EOF if the size < blocksize */
+ socket->tftp_lastpkt = last_pkt; /* Update last packet number */
+ buffersize = udp_read.buffer_size - 4; /* Skip TFTP header */
+ memcpy(socket->tftp_pktbuf, packet_buf + 4, buffersize);
+ socket->tftp_dataptr = socket->tftp_pktbuf;
+ socket->tftp_filepos += buffersize;
+ socket->tftp_bytesleft = buffersize;
+ if (buffersize < socket->tftp_blksize) {
+ /* it's the last block, ACK packet immediately */
+ ack_packet(inode, *(uint16_t *)(data + 2));
+
+ /* Make sure we know we are at end of file */
+ inode->size = socket->tftp_filepos;
+ socket->tftp_goteof = 1;
+ }
+}
+
+
+/**
+ * getfssec: Get multiple clusters from a file, given the starting cluster.
+ * In this case, get multiple blocks from a specific TCP connection.
+ *
+ * @param: fs, the fs_info structure address, in pxe, we don't use this.
+ * @param: buf, buffer to store the read data
+ * @param: openfile, TFTP socket pointer
+ * @param: blocks, 512-byte block count; 0FFFFh = until end of file
+ *
+ * @return: the bytes read
+ *
+ */
+static uint32_t pxe_getfssec(struct file *file, char *buf,
+ int blocks, bool *have_more)
+{
+ struct inode *inode = file->inode;
+ struct pxe_pvt_inode *socket = PVT(inode);
+ int count = blocks;
+ int chunk;
+ int bytes_read = 0;
+
+ count <<= TFTP_BLOCKSIZE_LG2;
+ while (count) {
+ fill_buffer(inode); /* If we have no 'fresh' buffer, get it */
+ if (!socket->tftp_bytesleft)
+ break;
+
+ chunk = count;
+ if (chunk > socket->tftp_bytesleft)
+ chunk = socket->tftp_bytesleft;
+ socket->tftp_bytesleft -= chunk;
+ memcpy(buf, socket->tftp_dataptr, chunk);
+ socket->tftp_dataptr += chunk;
+ buf += chunk;
+ bytes_read += chunk;
+ count -= chunk;
+ }
+
+
+ if (socket->tftp_bytesleft || (socket->tftp_filepos < inode->size)) {
+ fill_buffer(inode);
+ *have_more = 1;
+ } else if (socket->tftp_goteof) {
+ /*
+ * The socket is closed and the buffer drained; the caller will
+ * call close_file and therefore free the socket.
+ */
+ *have_more = 0;
+ }
+
+ return bytes_read;
+}
+
+/**
+ * Open a TFTP connection to the server
+ *
+ * @param:filename, the file we wanna open
+ *
+ * @out: open_file_t structure, stores in file->open_file
+ * @ouT: the lenght of this file, stores in file->file_len
+ *
+ */
+static void pxe_searchdir(const char *filename, struct file *file)
+{
+ struct fs_info *fs = file->fs;
+ struct inode *inode;
+ struct pxe_pvt_inode *socket;
+ char *buf;
+ const char *np;
+ char *p;
+ char *options;
+ char *data;
+ static __lowmem struct s_PXENV_UDP_WRITE udp_write;
+ static __lowmem struct s_PXENV_UDP_READ udp_read;
+ static __lowmem struct s_PXENV_FILE_OPEN file_open;
+ static const char rrq_tail[] = "octet\0""tsize\0""0\0""blksize\0""1408";
+ static __lowmem char rrq_packet_buf[2+2*FILENAME_MAX+sizeof rrq_tail];
+ const struct tftp_options *tftp_opt;
+ int i = 0;
+ int err;
+ int buffersize;
+ const uint8_t *timeout_ptr;
+ uint32_t timeout;
+ uint32_t oldtime;
+ uint16_t tid;
+ uint16_t opcode;
+ uint16_t blk_num;
+ uint32_t ip = 0;
+ uint32_t opdata, *opdata_ptr;
+ enum pxe_path_type path_type;
+ char fullpath[2*FILENAME_MAX];
+ uint16_t server_port = TFTP_PORT; /* TFTP server port */
+
+ inode = file->inode = NULL;
+
+ buf = rrq_packet_buf;
+ *(uint16_t *)buf = TFTP_RRQ; /* TFTP opcode */
+ buf += 2;
+
+ path_type = pxe_path_type(filename);
+ if (path_type == PXE_RELATIVE) {
+ snprintf(fullpath, sizeof fullpath, "%s%s", fs->cwd_name, filename);
+ path_type = pxe_path_type(filename = fullpath);
+ }
+
+ switch (path_type) {
+ case PXE_RELATIVE: /* Really shouldn't happen... */
+ case PXE_URL:
+ buf = stpcpy(buf, filename);
+ ip = server_ip; /* Default server */
+ break;
+
+ case PXE_HOMESERVER:
+ buf = stpcpy(buf, filename+2);
+ ip = server_ip;
+ break;
+
+ case PXE_TFTP:
+ np = strchr(filename, ':');
+ buf = stpcpy(buf, np+2);
+ if (parse_dotquad(filename, &ip) != np)
+ ip = dns_resolv(filename);
+ break;
+
+ case PXE_URL_TFTP:
+ np = filename + 7;
+ while (*np && *np != '/' && *np != ':')
+ np++;
+ if (np > filename + 7) {
+ if (parse_dotquad(filename, &ip) != np)
+ ip = dns_resolv(filename);
+ }
+ if (*np == ':') {
+ np++;
+ server_port = 0;
+ while (*np >= '0' && *np <= '9')
+ server_port = server_port * 10 + *np++ - '0';
+ server_port = server_port ? htons(server_port) : TFTP_PORT;
+ }
+ if (*np == '/')
+ np++; /* Do *NOT* eat more than one slash here... */
+ /*
+ * The ; is because of a quirk in the TFTP URI spec (RFC
+ * 3617); it is to be followed by TFTP modes, which we just ignore.
+ */
+ while (*np && *np != ';') {
+ int v;
+ if (*np == '%' && (v = hexbyte(np+1)) > 0) {
+ *buf++ = v;
+ np += 3;
+ } else {
+ *buf++ = *np++;
+ }
+ }
+ break;
+ }
+
+ if (!ip)
+ return; /* No server */
+
+ buf++; /* Point *past* the final NULL */
+ memcpy(buf, rrq_tail, sizeof rrq_tail);
+ buf += sizeof rrq_tail;
+
+ inode = allocate_socket(fs);
+ if (!inode)
+ return; /* Allocation failure */
+ socket = PVT(inode);
+
+ timeout_ptr = TimeoutTable; /* Reset timeout */
+ timeout = *timeout_ptr;
+ oldtime = jiffies();
+
+sendreq:
+#if GPXE
+ if (path_type == PXE_URL) {
+ if (has_gpxe) {
+ file_open.Status = PXENV_STATUS_BAD_FUNC;
+ file_open.FileName = FAR_PTR(rrq_packet_buf + 2);
+ err = pxe_call(PXENV_FILE_OPEN, &file_open);
+ if (err)
+ goto done;
+
+ socket->tftp_localport = -1;
+ socket->tftp_remoteport = file_open.FileHandle;
+ inode->size = -1;
+ goto done;
+ } else {
+ static bool already = false;
+ if (!already) {
+ printf("URL syntax, but gPXE extensions not detected, "
+ "trying plain TFTP...\n");
+ already = true;
+ }
+ }
+ }
+#endif /* GPXE */
+
+ socket->tftp_remoteip = ip;
+ tid = socket->tftp_localport; /* TID(local port No) */
+ udp_write.buffer = FAR_PTR(rrq_packet_buf);
+ udp_write.ip = ip;
+ udp_write.gw = ((udp_write.ip ^ MyIP) & net_mask) ? gate_way : 0;
+ udp_write.src_port = tid;
+ udp_write.dst_port = server_port;
+ udp_write.buffer_size = buf - rrq_packet_buf;
+ err = pxe_call(PXENV_UDP_WRITE, &udp_write);
+
+ /* If the WRITE call fails, we let the timeout take care of it... */
+
+wait_pkt:
+ for (;;) {
+ buf = packet_buf;
+ udp_read.buffer = FAR_PTR(buf);
+ udp_read.buffer_size = PKTBUF_SIZE;
+ udp_read.dest_ip = MyIP;
+ udp_read.d_port = tid;
+ err = pxe_call(PXENV_UDP_READ, &udp_read);
+ if (err) {
+ uint32_t now = jiffies();
+ if (now-oldtime >= timeout)
+ goto failure;
+ continue;
+ }
+
+ /* Make sure the packet actually came from the server */
+ if (udp_read.src_ip == socket->tftp_remoteip)
+ break;
+ }
+
+ socket->tftp_remoteport = udp_read.s_port;
+
+ /* filesize <- -1 == unknown */
+ inode->size = -1;
+ /* Default blksize unless blksize option negotiated */
+ socket->tftp_blksize = TFTP_BLOCKSIZE;
+ buffersize = udp_read.buffer_size - 2; /* bytes after opcode */
+ if (buffersize < 0)
+ goto failure; /* Garbled reply */
+
+ /*
+ * Get the opcode type, and parse it
+ */
+ opcode = *(uint16_t *)packet_buf;
+ switch (opcode) {
+ case TFTP_ERROR:
+ inode->size = 0;
+ break; /* ERROR reply; don't try again */
+
+ case TFTP_DATA:
+ /*
+ * If the server doesn't support any options, we'll get a
+ * DATA reply instead of OACK. Stash the data in the file
+ * buffer and go with the default value for all options...
+ *
+ * We got a DATA packet, meaning no options are
+ * suported. Save the data away and consider the
+ * length undefined, *unless* this is the only
+ * data packet...
+ */
+ buffersize -= 2;
+ if (buffersize < 0)
+ goto failure;
+ data = packet_buf + 2;
+ blk_num = *(uint16_t *)data;
+ data += 2;
+ if (blk_num != htons(1))
+ goto failure;
+ socket->tftp_lastpkt = blk_num;
+ if (buffersize > TFTP_BLOCKSIZE)
+ goto err_reply; /* Corrupt */
+ else if (buffersize < TFTP_BLOCKSIZE) {
+ /*
+ * This is the final EOF packet, already...
+ * We know the filesize, but we also want to
+ * ack the packet and set the EOF flag.
+ */
+ inode->size = buffersize;
+ socket->tftp_goteof = 1;
+ ack_packet(inode, blk_num);
+ }
+
+ socket->tftp_bytesleft = buffersize;
+ socket->tftp_dataptr = socket->tftp_pktbuf;
+ memcpy(socket->tftp_pktbuf, data, buffersize);
+ break;
+
+ case TFTP_OACK:
+ /*
+ * Now we need to parse the OACK packet to get the transfer
+ * and packet sizes.
+ */
+
+ options = packet_buf + 2;
+ p = options;
+
+ while (buffersize) {
+ const char *opt = p;
+
+ /*
+ * If we find an option which starts with a NUL byte,
+ * (a null option), we're either seeing garbage that some
+ * TFTP servers add to the end of the packet, or we have
+ * no clue how to parse the rest of the packet (what is
+ * an option name and what is a value?) In either case,
+ * discard the rest.
+ */
+ if (!*opt)
+ goto done;
+
+ while (buffersize) {
+ if (!*p)
+ break; /* Found a final null */
+ *p++ |= 0x20;
+ buffersize--;
+ }
+ if (!buffersize)
+ break; /* Unterminated option */
+
+ /* Consume the terminal null */
+ p++;
+ buffersize--;
+
+ if (!buffersize)
+ break; /* No option data */
+
+ /*
+ * Parse option pointed to by options; guaranteed to be
+ * null-terminated
+ */
+ tftp_opt = tftp_options;
+ for (i = 0; i < tftp_nopts; i++) {
+ if (!strcmp(opt, tftp_opt->str_ptr))
+ break;
+ tftp_opt++;
+ }
+ if (i == tftp_nopts)
+ goto err_reply; /* Non-negotitated option returned,
+ no idea what it means ...*/
+
+ /* get the address of the filed that we want to write on */
+ opdata_ptr = (uint32_t *)((char *)inode + tftp_opt->offset);
+ opdata = 0;
+
+ /* do convert a number-string to decimal number, just like atoi */
+ while (buffersize--) {
+ uint8_t d = *p++;
+ if (d == '\0')
+ break; /* found a final null */
+ d -= '0';
+ if (d > 9)
+ goto err_reply; /* Not a decimal digit */
+ opdata = opdata*10 + d;
+ }
+ *opdata_ptr = opdata;
+ }
+ break;
+
+ default:
+ printf("TFTP unknown opcode %d\n", ntohs(opcode));
+ goto err_reply;
+ }
+
+done:
+ if (!inode->size) {
+ free_socket(inode);
+ return;
+ }
+ file->inode = inode;
+ return;
+
+err_reply:
+ /* Build the TFTP error packet */
+ tftp_error(inode, TFTP_EOPTNEG, "TFTP protocol error");
+ printf("TFTP server sent an incomprehesible reply\n");
+ kaboom();
+
+failure:
+ if (jiffies() - oldtime < timeout)
+ goto wait_pkt;
+
+ /* Otherwise, we need to try again */
+ timeout_ptr++;
+ if (*timeout_ptr)
+ goto sendreq; /* Try again */
+}
+
+
+/*
+ * Store standard filename prefix
+ */
+static void get_prefix(void)
+{
+ int len;
+ char *p;
+ char c;
+
+ if (!(DHCPMagic & 0x04)) {
+ /* No path prefix option, derive from boot file */
+
+ strlcpy(path_prefix, boot_file, sizeof path_prefix);
+ len = strlen(path_prefix);
+ p = &path_prefix[len - 1];
+
+ while (len--) {
+ c = *p--;
+ c |= 0x20;
+
+ c = (c >= '0' && c <= '9') ||
+ (c >= 'a' && c <= 'z') ||
+ (c == '.' || c == '-');
+ if (!c)
+ break;
+ };
+
+ if (len < 0)
+ p --;
+
+ *(p + 2) = 0; /* Zero-terminate after delimiter */
+ }
+
+ printf("TFTP prefix: %s\n", path_prefix);
+ chdir(path_prefix);
+}
+
+/*
+ * realpath for PXE
+ */
+static size_t pxe_realpath(struct fs_info *fs, char *dst, const char *src,
+ size_t bufsize)
+{
+ enum pxe_path_type path_type = pxe_path_type(src);
+
+ return snprintf(dst, bufsize, "%s%s",
+ path_type == PXE_RELATIVE ? fs->cwd_name : "", src);
+}
+
+/*
+ * chdir for PXE
+ */
+static int pxe_chdir(struct fs_info *fs, const char *src)
+{
+ /* The cwd for PXE is just a text prefix */
+ enum pxe_path_type path_type = pxe_path_type(src);
+
+ if (path_type == PXE_RELATIVE)
+ strlcat(fs->cwd_name, src, sizeof fs->cwd_name);
+ else
+ strlcpy(fs->cwd_name, src, sizeof fs->cwd_name);
+
+ dprintf("cwd = \"%s\"\n", fs->cwd_name);
+ return 0;
+}
+
+ /*
+ * try to load a config file, if found, return 1, or return 0
+ *
+ */
+static int try_load(char *config_name)
+{
+ com32sys_t regs;
+
+ printf("Trying to load: %-50s ", config_name);
+ pxe_mangle_name(KernelName, config_name);
+
+ memset(&regs, 0, sizeof regs);
+ regs.edi.w[0] = OFFS_WRT(KernelName, 0);
+ call16(core_open, &regs, &regs);
+ if (regs.eflags.l & EFLAGS_ZF) {
+ printf("\r");
+ return 0;
+ } else {
+ printf("ok\n");
+ return 1;
+ }
+}
+
+
+/* Load the config file, return 1 if failed, or 0 */
+static int pxe_load_config(void)
+{
+ const char *cfgprefix = "pxelinux.cfg/";
+ const char *default_str = "default";
+ char *config_file;
+ char *last;
+ char *p;
+ uint8_t *uuid_ptr;
+ int tries = 8;
+
+ get_prefix();
+ if (DHCPMagic & 0x02) {
+ /* We got a DHCP option, try it first */
+ if (try_load(boot_file))
+ return 0;
+ }
+
+ /*
+ * Have to guess config file name ...
+ */
+ memcpy(ConfigName, cfgprefix, strlen(cfgprefix));
+ config_file = ConfigName + strlen(cfgprefix);
+
+ /* Try loading by UUID */
+ if (have_uuid) {
+ uuid_ptr = uuid_dashes;
+ p = config_file;
+ while (*uuid_ptr) {
+ int len = *uuid_ptr;
+ char *src = uuid;
+
+ lchexbytes(p, src, len);
+ p += len * 2;
+ src += len;
+ uuid_ptr++;
+ *p++ = '-';
+ }
+ /* Remove last dash and zero-terminate */
+ *--p = '\0';
+ if (try_load(ConfigName))
+ return 0;
+ }
+
+ /* Try loading by MAC address */
+ strcpy(config_file, MAC_str);
+ if (try_load(ConfigName))
+ return 0;
+
+ /* Nope, try hexadecimal IP prefixes... */
+ uchexbytes(config_file, (uint8_t *)&MyIP, 4); /* Convet to hex string */
+ last = &config_file[8];
+ while (tries) {
+ *last = '\0'; /* Zero-terminate string */
+ if (try_load(ConfigName))
+ return 0;
+ last--; /* Drop one character */
+ tries--;
+ };
+
+ /* Final attempt: "default" string */
+ strcpy(config_file, default_str);
+ if (try_load(ConfigName))
+ return 0;
+
+ printf("%-68s\n", "Unable to locate configuration file");
+ kaboom();
+}
+
+/*
+ * Generate the botif string, and the hardware-based config string
+ */
+static void make_bootif_string(void)
+{
+ const uint8_t *src;
+ char *dst = BOOTIFStr;
+ int i;
+
+ dst += sprintf(dst, "BOOTIF=%02x", MAC_type);
+ src = MAC;
+ for (i = MAC_len; i; i--)
+ dst += sprintf(dst, "-%02x", *src++);
+}
+
+/*
+ * Generate an ip=<client-ip>:<boot-server-ip>:<gw-ip>:<netmask>
+ * option into IPOption based on a DHCP packet in trackbuf.
+ *
+ */
+char __bss16 IPOption[3+4*16];
+
+static void genipopt(void)
+{
+ char *p = IPOption;
+
+ p = stpcpy(p, "ip=");
+
+ p += gendotquad(p, MyIP);
+ *p++ = ':';
+
+ p += gendotquad(p, server_ip);
+ *p++ = ':';
+
+ p += gendotquad(p, gate_way);
+ *p++ = ':';
+
+ gendotquad(p, net_mask);
+}
+
+
+/* Generate ip= option and print the ip adress */
+static void ip_init(void)
+{
+ uint32_t ip = MyIP;
+
+ genipopt();
+ gendotquad(dot_quad_buf, ip);
+
+ ip = ntohl(ip);
+ printf("My IP address seems to be %08X %s\n", ip, dot_quad_buf);
+ printf("%s\n", IPOption);
+}
+
+/*
+ * Validity check on possible !PXE structure in buf
+ * return 1 for success, 0 for failure.
+ *
+ */
+static int is_pxe(const void *buf)
+{
+ const struct pxe_t *pxe = buf;
+ const uint8_t *p = buf;
+ int i = pxe->structlength;
+ uint8_t sum = 0;
+
+ if (i < sizeof(struct pxe_t) ||
+ memcmp(pxe->signature, "!PXE", 4))
+ return 0;
+
+ while (i--)
+ sum += *p++;
+
+ return sum == 0;
+}
+
+/*
+ * Just like is_pxe, it checks PXENV+ structure
+ *
+ */
+static int is_pxenv(const void *buf)
+{
+ const struct pxenv_t *pxenv = buf;
+ const uint8_t *p = buf;
+ int i = pxenv->length;
+ uint8_t sum = 0;
+
+ /* The pxeptr field isn't present in old versions */
+ if (i < offsetof(struct pxenv_t, pxeptr) ||
+ memcmp(pxenv->signature, "PXENV+", 6))
+ return 0;
+
+ while (i--)
+ sum += *p++;
+
+ return sum == 0;
+}
+
+
+
+/*
+ * memory_scan_for_pxe_struct:
+ * memory_scan_for_pxenv_struct:
+ *
+ * If none of the standard methods find the !PXE/PXENV+ structure,
+ * look for it by scanning memory.
+ *
+ * return the corresponding pxe structure if found, or NULL;
+ */
+static const void *memory_scan(uintptr_t start, int (*func)(const void *))
+{
+ const char *ptr;
+
+ /* Scan each 16 bytes of conventional memory before the VGA region */
+ for (ptr = (const char *)start; ptr < (const char *)0xA0000; ptr += 16) {
+ if (func(ptr))
+ return ptr; /* found it! */
+ ptr += 16;
+ }
+ return NULL;
+}
+
+static const struct pxe_t *memory_scan_for_pxe_struct(void)
+{
+ extern uint16_t BIOS_fbm; /* Starting segment */
+
+ return memory_scan(BIOS_fbm << 10, is_pxe);
+}
+
+static const struct pxenv_t *memory_scan_for_pxenv_struct(void)
+{
+ return memory_scan(0x10000, is_pxenv);
+}
+
+/*
+ * Find the !PXE structure; we search for the following, in order:
+ *
+ * a. !PXE structure as SS:[SP + 4]
+ * b. PXENV+ structure at [ES:BX]
+ * c. INT 1Ah AX=0x5650 -> PXENV+
+ * d. Search memory for !PXE
+ * e. Search memory for PXENV+
+ *
+ * If we find a PXENV+ structure, we try to find a !PXE structure from
+ * if if the API version is 2.1 or later
+ *
+ */
+static int pxe_init(bool quiet)
+{
+ extern void pxe_int1a(void);
+ char plan = 'A';
+ uint16_t seg, off;
+ uint16_t code_seg, code_len;
+ uint16_t data_seg, data_len;
+ const char *base = GET_PTR(InitStack);
+ com32sys_t regs;
+ const char *type;
+ const struct pxenv_t *pxenv;
+ const struct pxe_t *pxe;
+
+ /* Assume API version 2.1 */
+ APIVer = 0x201;
+
+ /* Plan A: !PXE structure as SS:[SP + 4] */
+ off = *(const uint16_t *)(base + 48);
+ seg = *(const uint16_t *)(base + 50);
+ pxe = MK_PTR(seg, off);
+ if (is_pxe(pxe))
+ goto have_pxe;
+
+ /* Plan B: PXENV+ structure at [ES:BX] */
+ plan++;
+ off = *(const uint16_t *)(base + 24); /* Original BX */
+ seg = *(const uint16_t *)(base + 4); /* Original ES */
+ pxenv = MK_PTR(seg, off);
+ if (is_pxenv(pxenv))
+ goto have_pxenv;
+
+ /* Plan C: PXENV+ structure via INT 1Ah AX=5650h */
+ plan++;
+ memset(&regs, 0, sizeof regs);
+ regs.eax.w[0] = 0x5650;
+ call16(pxe_int1a, &regs, &regs);
+ if (!(regs.eflags.l & EFLAGS_CF) && (regs.eax.w[0] == 0x564e)) {
+ pxenv = MK_PTR(regs.es, regs.ebx.w[0]);
+ if (is_pxenv(pxenv))
+ goto have_pxenv;
+ }
+
+ /* Plan D: !PXE memory scan */
+ plan++;
+ if ((pxe = memory_scan_for_pxe_struct()))
+ goto have_pxe;
+
+ /* Plan E: PXENV+ memory scan */
+ plan++;
+ if ((pxenv = memory_scan_for_pxenv_struct()))
+ goto have_pxenv;
+
+ /* Found nothing at all !! */
+ if (!quiet)
+ printf("No !PXE or PXENV+ API found; we're dead...\n");
+ return -1;
+
+ have_pxenv:
+ APIVer = pxenv->version;
+ if (!quiet)
+ printf("Found PXENV+ structure\nPXE API version is %04x\n", APIVer);
+
+ /* if the API version number is 0x0201 or higher, use the !PXE structure */
+ if (APIVer >= 0x201) {
+ if (pxenv->length >= sizeof(struct pxenv_t)) {
+ pxe = GET_PTR(pxenv->pxeptr);
+ if (is_pxe(pxe))
+ goto have_pxe;
+ /*
+ * Nope, !PXE structure missing despite API 2.1+, or at least
+ * the pointer is missing. Do a last-ditch attempt to find it
+ */
+ if ((pxe = memory_scan_for_pxe_struct()))
+ goto have_pxe;
+ }
+ APIVer = 0x200; /* PXENV+ only, assume version 2.00 */
+ }
+
+ /* Otherwise, no dice, use PXENV+ structure */
+ data_len = pxenv->undidatasize;
+ data_seg = pxenv->undidataseg;
+ code_len = pxenv->undicodesize;
+ code_seg = pxenv->undicodeseg;
+ PXEEntry = pxenv->rmentry;
+ type = "PXENV+";
+ goto have_entrypoint;
+
+ have_pxe:
+ data_len = pxe->seg[PXE_Seg_UNDIData].size;
+ data_seg = pxe->seg[PXE_Seg_UNDIData].sel;
+ code_len = pxe->seg[PXE_Seg_UNDICode].size;
+ code_seg = pxe->seg[PXE_Seg_UNDICode].sel;
+ PXEEntry = pxe->entrypointsp;
+ type = "!PXE";
+
+ have_entrypoint:
+ if (!quiet) {
+ printf("%s entry point found (we hope) at %04X:%04X via plan %c\n",
+ type, PXEEntry.seg, PXEEntry.offs, plan);
+ printf("UNDI code segment at %04X len %04X\n", code_seg, code_len);
+ printf("UNDI data segment at %04X len %04X\n", data_seg, data_len);
+ }
+
+ code_seg = code_seg + ((code_len + 15) >> 4);
+ data_seg = data_seg + ((data_len + 15) >> 4);
+
+ real_base_mem = max(code_seg, data_seg) >> 6; /* Convert to kilobytes */
+
+ return 0;
+}
+
+/*
+ * See if we have gPXE
+ */
+static void gpxe_init(void)
+{
+ int err;
+ static __lowmem struct s_PXENV_FILE_API_CHECK api_check;
+
+ if (APIVer >= 0x201) {
+ api_check.Size = sizeof api_check;
+ api_check.Magic = 0x91d447b2;
+ err = pxe_call(PXENV_FILE_API_CHECK, &api_check);
+ if (!err && api_check.Magic == 0xe9c17b20)
+ gpxe_funcs = api_check.APIMask;
+ }
+
+ /* Necessary functions for us to use the gPXE file API */
+ has_gpxe = (~gpxe_funcs & 0x4b) == 0;
+}
+
+/*
+ * Initialize UDP stack
+ *
+ */
+static void udp_init(void)
+{
+ int err;
+ static __lowmem struct s_PXENV_UDP_OPEN udp_open;
+ udp_open.src_ip = MyIP;
+ err = pxe_call(PXENV_UDP_OPEN, &udp_open);
+ if (err || udp_open.status) {
+ printf("Failed to initialize UDP stack ");
+ printf("%d\n", udp_open.status);
+ kaboom();
+ }
+}
+
+
+/*
+ * Network-specific initialization
+ */
+static void network_init(void)
+{
+ struct bootp_t *bp = (struct bootp_t *)trackbuf;
+ int pkt_len;
+
+ *LocalDomain = 0; /* No LocalDomain received */
+
+ /*
+ * Get the DHCP client identifiers (query info 1)
+ */
+ printf("Getting cached packet ");
+ pkt_len = pxe_get_cached_info(1);
+ parse_dhcp(pkt_len);
+ /*
+ * We don't use flags from the request packet, so
+ * this is a good time to initialize DHCPMagic...
+ * Initialize it to 1 meaning we will accept options found;
+ * in earlier versions of PXELINUX bit 0 was used to indicate
+ * we have found option 208 with the appropriate magic number;
+ * we no longer require that, but MAY want to re-introduce
+ * it in the future for vendor encapsulated options.
+ */
+ *(char *)&DHCPMagic = 1;
+
+ /*
+ * Get the BOOTP/DHCP packet that brought us file (and an IP
+ * address). This lives in the DHCPACK packet (query info 2)
+ */
+ pkt_len = pxe_get_cached_info(2);
+ parse_dhcp(pkt_len);
+ /*
+ * Save away MAC address (assume this is in query info 2. If this
+ * turns out to be problematic it might be better getting it from
+ * the query info 1 packet
+ */
+ MAC_len = bp->hardlen > 16 ? 0 : bp->hardlen;
+ MAC_type = bp->hardware;
+ memcpy(MAC, bp->macaddr, MAC_len);
+
+ /*
+ * Get the boot file and other info. This lives in the CACHED_REPLY
+ * packet (query info 3)
+ */
+ pkt_len = pxe_get_cached_info(3);
+ parse_dhcp(pkt_len);
+ printf("\n");
+
+ make_bootif_string();
+ ip_init();
+
+ /*
+ * Check to see if we got any PXELINUX-specific DHCP options; in particular,
+ * if we didn't get the magic enable, do not recognize any other options.
+ */
+ if ((DHCPMagic & 1) == 0)
+ DHCPMagic = 0;
+
+ udp_init();
+}
+
+/*
+ * Initialize pxe fs
+ *
+ */
+static int pxe_fs_init(struct fs_info *fs)
+{
+ (void)fs; /* drop the compile warning message */
+
+ /* This block size is actually arbitrary... */
+ fs->sector_shift = fs->block_shift = TFTP_BLOCKSIZE_LG2;
+ fs->sector_size = fs->block_size = 1 << TFTP_BLOCKSIZE_LG2;
+
+ /* This block size is actually arbitrary... */
+ fs->sector_shift = fs->block_shift = TFTP_BLOCKSIZE_LG2;
+ fs->sector_size = fs->block_size = 1 << TFTP_BLOCKSIZE_LG2;
+
+ /* Find the PXE stack */
+ if (pxe_init(false))
+ kaboom();
+
+ /* See if we also have a gPXE stack */
+ gpxe_init();
+
+ /* Network-specific initialization */
+ network_init();
+
+ /* Initialize network-card-specific idle handling */
+ pxe_idle_init();
+
+ /* Our name for the root */
+ strcpy(fs->cwd_name, "::");
+
+ return 0;
+}
+
+/*
+ * Look to see if we are on an EFI CSM system. Some EFI
+ * CSM systems put the BEV stack in low memory, which means
+ * a return to the PXE stack will crash the system. However,
+ * INT 18h works reliably, so in that case hack the stack and
+ * point the "return address" to an INT 18h instruction.
+ *
+ * Hack the stack instead of the much simpler "just invoke INT 18h
+ * if we want to reset", so that chainloading other NBPs will work.
+ *
+ * This manipulates the real-mode InitStack directly. It relies on this
+ * *not* being a currently active stack, i.e. the former
+ * USE_PXE_PROVIDED_STACK no longer works.
+ */
+extern far_ptr_t InitStack;
+
+struct efi_struct {
+ uint32_t magic;
+ uint8_t csum;
+ uint8_t len;
+} __attribute__((packed));
+#define EFI_MAGIC (('$' << 24)+('E' << 16)+('F' << 8)+'I')
+
+static inline bool is_efi(const struct efi_struct *efi)
+{
+ /*
+ * We don't verify the checksum, because it seems some CSMs leave
+ * it at zero, sigh...
+ */
+ return (efi->magic == EFI_MAGIC) && (efi->len >= 83);
+}
+
+static void install_efi_csm_hack(void)
+{
+ static const uint8_t efi_csm_hack[] =
+ {
+ 0xcd, 0x18, /* int $0x18 */
+ 0xea, 0xf0, 0xff, 0x00, 0xf0, /* ljmpw $0xf000,$0xfff0 */
+ 0xf4 /* hlt */
+ };
+ uint16_t *retcode;
+
+ retcode = GET_PTR(*(far_ptr_t *)((char *)GET_PTR(InitStack) + 44));
+
+ /* Don't do this if the return already points to int $0x18 */
+ if (*retcode != 0x18cd) {
+ uint32_t efi_ptr;
+ bool efi = false;
+
+ for (efi_ptr = 0xe0000 ; efi_ptr < 0x100000 ; efi_ptr += 16) {
+ if (is_efi((const struct efi_struct *)efi_ptr)) {
+ efi = true;
+ break;
+ }
+ }
+
+ if (efi) {
+ uint8_t *src = GET_PTR(InitStack);
+ uint8_t *dst = src - sizeof efi_csm_hack;
+
+ memmove(dst, src, 52);
+ memcpy(dst+52, efi_csm_hack, sizeof efi_csm_hack);
+ InitStack.offs -= sizeof efi_csm_hack;
+
+ /* Clobber the return address */
+ *(uint16_t *)(dst+44) = OFFS_WRT(dst+52, InitStack.seg);
+ *(uint16_t *)(dst+46) = InitStack.seg;
+ }
+ }
+}
+
+int reset_pxe(void)
+{
+ static __lowmem struct s_PXENV_UDP_CLOSE udp_close;
+ extern void gpxe_unload(void);
+ int err = 0;
+
+ pxe_idle_cleanup();
+
+ pxe_call(PXENV_UDP_CLOSE, &udp_close);
+
+ if (gpxe_funcs & 0x80) {
+ /* gPXE special unload implemented */
+ call16(gpxe_unload, &zero_regs, NULL);
+
+ /* Locate the actual vendor stack... */
+ err = pxe_init(true);
+ }
+
+ install_efi_csm_hack();
+ return err;
+}
+
+/*
+ * This function unloads the PXE and UNDI stacks and
+ * unclaims the memory.
+ */
+void unload_pxe(void)
+{
+ /* PXE unload sequences */
+ static const uint8_t new_api_unload[] = {
+ PXENV_UNDI_SHUTDOWN, PXENV_UNLOAD_STACK, PXENV_STOP_UNDI, 0
+ };
+ static const uint8_t old_api_unload[] = {
+ PXENV_UNDI_SHUTDOWN, PXENV_UNLOAD_STACK, PXENV_UNDI_CLEANUP, 0
+ };
+
+ uint8_t api;
+ const uint8_t *api_ptr;
+ uint16_t flag = 0;
+ int err;
+ size_t int_addr;
+ static __lowmem struct s_PXENV_UNLOAD_STACK unload_stack;
+
+ dprintf("FBM before unload = %d\n", BIOS_fbm);
+
+ err = reset_pxe();
+
+ dprintf("FBM after reset_pxe = %d, err = %d\n", BIOS_fbm, err);
+
+ /* If we want to keep PXE around, we still need to reset it */
+ if (KeepPXE || err)
+ return;
+
+ api_ptr = major_ver(APIVer) >= 2 ? new_api_unload : old_api_unload;
+ while((api = *api_ptr++)) {
+ memset(&unload_stack, 0, sizeof unload_stack);
+ err = pxe_call(api, &unload_stack);
+ if (err || unload_stack.Status != PXENV_STATUS_SUCCESS) {
+ dprintf("PXE unload API call %04x failed\n", api);
+ goto cant_free;
+ }
+ }
+
+ flag = 0xff00;
+ if (real_base_mem <= BIOS_fbm) { /* Santiy check */
+ dprintf("FBM %d < real_base_mem %d\n", BIOS_fbm, real_base_mem);
+ goto cant_free;
+ }
+ flag++;
+
+ /* Check that PXE actually unhooked the INT 0x1A chain */
+ int_addr = (size_t)GET_PTR(*(far_ptr_t *)(4 * 0x1a));
+ int_addr >>= 10;
+ if (int_addr >= real_base_mem || int_addr < BIOS_fbm) {
+ BIOS_fbm = real_base_mem;
+ dprintf("FBM after unload_pxe = %d\n", BIOS_fbm);
+ return;
+ }
+
+ dprintf("Can't free FBM, real_base_mem = %d, FBM = %d, INT 1A = %08x (%d)\n",
+ real_base_mem, BIOS_fbm, *(uint32_t *)(4 * 0x1a), int_addr);
+
+cant_free:
+
+ printf("Failed to free base memory error %04x-%08x\n",
+ flag, *(uint32_t *)(4 * 0x1a));
+ return;
+}
+
+const struct fs_ops pxe_fs_ops = {
+ .fs_name = "pxe",
+ .fs_flags = FS_NODEV,
+ .fs_init = pxe_fs_init,
+ .searchdir = pxe_searchdir,
+ .chdir = pxe_chdir,
+ .realpath = pxe_realpath,
+ .getfssec = pxe_getfssec,
+ .close_file = pxe_close_file,
+ .mangle_name = pxe_mangle_name,
+ .load_config = pxe_load_config,
+};
diff --git a/core/fs/pxe/pxe.h b/core/fs/pxe/pxe.h
new file mode 100644
index 00000000..e801aea5
--- /dev/null
+++ b/core/fs/pxe/pxe.h
@@ -0,0 +1,236 @@
+/* -----------------------------------------------------------------------
+ *
+ * Copyright 1999-2008 H. Peter Anvin - All Rights Reserved
+ * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ * Boston MA 02111-1307, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * pxe.h
+ *
+ * PXE opcodes
+ *
+ */
+#ifndef PXE_H
+#define PXE_H
+
+#include <syslinux/pxe_api.h>
+#include "fs.h" /* For MAX_OPEN, should go away */
+
+/*
+ * Some basic defines...
+ */
+#define TFTP_PORT htons(69) /* Default TFTP port */
+#define TFTP_BLOCKSIZE_LG2 9
+#define TFTP_BLOCKSIZE (1 << TFTP_BLOCKSIZE_LG2)
+#define PKTBUF_SIZE 2048 /* */
+
+#define is_digit(c) (((c) >= '0') && ((c) <= '9'))
+#define major_ver(v) (((v) >> 8) && 0xff)
+
+static inline bool is_hex(char c)
+{
+ return (c >= '0' && c <= '9') ||
+ (c >= 'A' && c <= 'F') ||
+ (c >= 'a' && c <= 'f');
+}
+
+static inline int hexval(char c)
+{
+ return (c >= 'A') ? (c & ~0x20) - 'A' + 10 : (c - '0');
+}
+
+/*
+ * TFTP operation codes
+ */
+#define TFTP_RRQ htons(1) // Read rest
+#define TFTP_WRQ htons(2) // Write rest
+#define TFTP_DATA htons(3) // Data packet
+#define TFTP_ACK htons(4) // ACK packet
+#define TFTP_ERROR htons(5) // ERROR packet
+#define TFTP_OACK htons(6) // OACK packet
+
+/*
+ * TFTP error codes
+ */
+#define TFTP_EUNDEF htons(0) // Unspecified error
+#define TFTP_ENOTFOUND htons(1) // File not found
+#define TFTP_EACCESS htons(2) // Access violation
+#define TFTP_ENOSPACE htons(3) // Disk full
+#define TFTP_EBADOP htons(4) // Invalid TFTP operation
+#define TFTP_EBADID htons(5) // Unknown transfer
+#define TFTP_EEXISTS htons(6) // File exists
+#define TFTP_ENOUSER htons(7) // No such user
+#define TFTP_EOPTNEG htons(8) // Option negotiation failure
+
+
+#define BOOTP_OPTION_MAGIC htonl(0x63825363)
+#define MAC_MAX 32
+
+/* Defines for DNS */
+#define DNS_PORT htons(53) /* Default DNS port */
+#define DNS_MAX_PACKET 512 /* Defined by protocol */
+#define DNS_MAX_SERVERS 4 /* Max no of DNS servers */
+
+
+/*
+ * structures
+ */
+
+struct pxenv_t {
+ uint8_t signature[6]; /* PXENV+ */
+ uint16_t version;
+ uint8_t length;
+ uint8_t checksum;
+ segoff16_t rmentry;
+ uint32_t pmoffset;
+ uint16_t pmselector;
+ uint16_t stackseg;
+ uint16_t stacksize;
+ uint16_t bc_codeseg;
+ uint16_t bc_codesize;
+ uint16_t bc_dataseg;
+ uint16_t bc_datasize;
+ uint16_t undidataseg;
+ uint16_t undidatasize;
+ uint16_t undicodeseg;
+ uint16_t undicodesize;
+ segoff16_t pxeptr;
+} __packed;
+
+struct pxe_t {
+ uint8_t signature[4]; /* !PXE */
+ uint8_t structlength;
+ uint8_t structcksum;
+ uint8_t structrev;
+ uint8_t _pad1;
+ segoff16_t undiromid;
+ segoff16_t baseromid;
+ segoff16_t entrypointsp;
+ segoff16_t entrypointesp;
+ segoff16_t statuscallout;
+ uint8_t _pad2;
+ uint8_t segdesccnt;
+ uint16_t firstselector;
+ pxe_segdesc_t seg[7];
+} __packed;
+
+enum pxe_segments {
+ PXE_Seg_Stack = 0,
+ PXE_Seg_UNDIData = 1,
+ PXE_Seg_UNDICode = 2,
+ PXE_Seg_UNDICodeWrite = 3,
+ PXE_Seg_BC_Data = 4,
+ PXE_Seg_BC_Code = 5,
+ PXE_Seg_BC_CodeWrite = 6
+};
+
+struct bootp_t {
+ uint8_t opcode; /* BOOTP/DHCP "opcode" */
+ uint8_t hardware; /* ARP hreadware type */
+ uint8_t hardlen; /* Hardware address length */
+ uint8_t gatehops; /* Used by forwarders */
+ uint32_t ident; /* Transaction ID */
+ uint16_t seconds; /* Seconds elapsed */
+ uint16_t flags; /* Broadcast flags */
+ uint32_t cip; /* Cient IP */
+ uint32_t yip; /* "Your" IP */
+ uint32_t sip; /* Next Server IP */
+ uint32_t gip; /* Relay agent IP */
+ uint8_t macaddr[16]; /* Client MAC address */
+ uint8_t sname[64]; /* Server name (optional) */
+ char bootfile[128]; /* Boot file name */
+ uint32_t option_magic; /* Vendor option magic cookie */
+ uint8_t options[1260]; /* Vendor options */
+} __attribute__ ((packed));
+
+/*
+ * Our inode private information -- this includes the packet buffer!
+ */
+struct pxe_pvt_inode {
+ uint16_t tftp_localport; /* Local port number (0=not in us)*/
+ uint16_t tftp_remoteport; /* Remote port number */
+ uint32_t tftp_remoteip; /* Remote IP address */
+ uint32_t tftp_filepos; /* bytes downloaded (includeing buffer) */
+ uint32_t tftp_blksize; /* Block size for this connection(*) */
+ uint16_t tftp_bytesleft; /* Unclaimed data bytes */
+ uint16_t tftp_lastpkt; /* Sequence number of last packet (NBO) */
+ char *tftp_dataptr; /* Pointer to available data */
+ uint8_t tftp_goteof; /* 1 if the EOF packet received */
+ uint8_t tftp_unused[3]; /* Currently unused */
+ char tftp_pktbuf[PKTBUF_SIZE];
+} __attribute__ ((packed));
+
+#define PVT(i) ((struct pxe_pvt_inode *)((i)->pvt))
+
+/*
+ * Variable externs
+ */
+extern uint32_t server_ip;
+extern uint32_t MyIP;
+extern uint32_t net_mask;
+extern uint32_t gate_way;
+extern uint16_t server_port;
+
+extern uint8_t MAC[];
+extern char BOOTIFStr[];
+extern uint8_t MAC_len;
+extern uint8_t MAC_type;
+
+extern uint8_t DHCPMagic;
+extern uint32_t RebootTime;
+
+extern char boot_file[];
+extern char path_prefix[];
+extern char LocalDomain[];
+
+extern char IPOption[];
+extern char dot_quad_buf[];
+
+extern uint32_t dns_server[];
+
+extern uint16_t real_base_mem;
+extern uint16_t APIVer;
+extern far_ptr_t PXEEntry;
+extern uint8_t KeepPXE;
+
+extern far_ptr_t InitStack;
+
+extern bool have_uuid;
+extern uint8_t uuid_type;
+extern char uuid[];
+
+extern uint16_t BIOS_fbm;
+extern const uint8_t TimeoutTable[];
+
+
+/*
+ * functions
+ */
+
+/* pxe.c */
+int ip_ok(uint32_t);
+int pxe_call(int, void *);
+
+/* dhcp_options.c */
+void parse_dhcp(int);
+
+/* dnsresolv.c */
+int dns_mangle(char **, const char *);
+uint32_t dns_resolv(const char *);
+
+/* idle.c */
+void pxe_idle_init(void);
+void pxe_idle_cleanup(void);
+
+/* socknum.c */
+uint16_t get_port(void);
+void free_port(uint16_t port);
+
+#endif /* pxe.h */
diff --git a/core/fs/readdir.c b/core/fs/readdir.c
new file mode 100644
index 00000000..d20fc33b
--- /dev/null
+++ b/core/fs/readdir.c
@@ -0,0 +1,50 @@
+#include <stdio.h>
+#include <string.h>
+#include <sys/dirent.h>
+#include "fs.h"
+#include "core.h"
+
+/*
+ * Open a directory
+ */
+DIR *opendir(const char *path)
+{
+ int rv;
+
+ rv = searchdir(path);
+ if (rv < 0)
+ return NULL;
+
+ /* XXX: check for a directory handle here */
+ return (DIR *)handle_to_file(rv);
+}
+
+/*
+ * Read one directory entry at one time.
+ */
+struct dirent *readdir(DIR *dir)
+{
+ static struct dirent buf;
+ struct file *dd_dir = (struct file *)dir;
+ int rv = -1;
+
+ if (dd_dir) {
+ if (dd_dir->fs->fs_ops->readdir) {
+ rv = dd_dir->fs->fs_ops->readdir(dd_dir, &buf);
+ }
+ }
+
+ return rv < 0 ? NULL : &buf;
+}
+
+/*
+ * Close a directory
+ */
+int closedir(DIR *dir)
+{
+ struct file *dd_dir = (struct file *)dir;
+ _close_file(dd_dir);
+ return 0;
+}
+
+
diff --git a/core/getc.inc b/core/getc.inc
index bb12047b..efe60de3 100644
--- a/core/getc.inc
+++ b/core/getc.inc
@@ -60,8 +60,9 @@ getc_file_lg2 equ 4 ; Size of getc_file as a power of 2
;
; close: Output: CF set if nothing open
;
-open:
- call searchdir
+ global core_open
+core_open:
+ pm_call pm_searchdir
jz openfd.ret
openfd:
push bx
@@ -82,11 +83,11 @@ openfd:
.ret: ret
.stack_full:
- call close_file
+ pm_call pm_close_file
xor ax,ax ; ZF <- 1
pop bx
ret
-
+
getc:
push bx
push si
@@ -135,7 +136,7 @@ getc:
mov [di+gc_bufbytes],si ; In case SI == 0
jz .empty
mov cx,bytes_per_getc >> SECTOR_SHIFT
- call getfssec
+ pm_call getfssec
mov [di+gc_bufbytes],cx
mov [di+gc_file],si
jcxz .empty
@@ -177,7 +178,7 @@ close:
push si
mov bx,[CurrentGetC]
mov si,[bx+gc_file]
- call close_file
+ pm_call pm_close_file
add bx,getc_file_size
mov [CurrentGetC],bx
pop si
@@ -321,7 +322,7 @@ parseint:
.isk: shl ebx,10 ; * 2^10
jmp .fini
- section .bss1
+ section .bss16
alignb 4
NumBuf resb 15 ; Buffer to load number
NumBufEnd resb 1 ; Last byte in NumBuf
@@ -329,14 +330,14 @@ NumBufEnd resb 1 ; Last byte in NumBuf
GetCStack resb getc_file_size*MAX_GETC
.end equ $
- section .data
+ section .data16
CurrentGetC dw GetCStack.end ; GetCStack empty
;
; unhexchar: Convert a hexadecimal digit in AL to the equivalent number;
; return CF=1 if not a hex digit
;
- section .text
+ section .text16
unhexchar:
cmp al,'0'
jb .ret ; If failure, CF == 1 already
diff --git a/core/graphics.inc b/core/graphics.inc
index 26925e9a..a8d28515 100644
--- a/core/graphics.inc
+++ b/core/graphics.inc
@@ -21,7 +21,7 @@
;
; Assumes CS == DS == ES.
;
- section .text
+ section .text16
vgadisplayfile:
; This is a cheap and easy way to make sure the screen is
@@ -322,7 +322,7 @@ vgacursorcommon:
ret
- section .data
+ section .data16
; Map colors to consecutive DAC registers
linear_color db 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0
@@ -330,7 +330,7 @@ linear_color db 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0
; of this byte.
UsingVGA db 0
- section .bss2
+ section .bss16
alignb 4
LSSHeader equ $
LSSMagic resd 1 ; Magic number
diff --git a/core/head.inc b/core/head.inc
index 7117b4ee..18ce132c 100644
--- a/core/head.inc
+++ b/core/head.inc
@@ -20,9 +20,15 @@
%ifndef _HEAD_INC
%define _HEAD_INC
+%if __NASM_MAJOR__ < 2
+ %error "NASM 2.00 or later required to compile correctly"
+%endif
+
%include "macros.inc"
%include "config.inc"
%include "layout.inc"
+%include "pmcall.inc"
+%include "extern.inc"
%include "kernel.inc"
%include "bios.inc"
%include "tracers.inc"
diff --git a/core/highmem.inc b/core/highmem.inc
index 1f8349bd..ea386ffc 100644
--- a/core/highmem.inc
+++ b/core/highmem.inc
@@ -17,7 +17,7 @@
;; mem= command on the command line while booting a new kernel.
;;
- section .text
+ section .text16
;
; This is set up as a subroutine; it will set up the global variable
@@ -150,9 +150,9 @@ got_highmem:
pop es
ret ; Done!
- section .bss
+ section .bss16
alignb 4
E820Buf resd 5 ; INT 15:E820 data buffer
E820Mem resd 1 ; Memory detected by E820
E820Max resd 1 ; Is E820 memory capped?
-HighMemSize resd 1 ; End of memory pointer (bytes)
+; HighMemSize is defined in com32.inc
diff --git a/core/idle.c b/core/idle.c
new file mode 100644
index 00000000..3f57393b
--- /dev/null
+++ b/core/idle.c
@@ -0,0 +1,49 @@
+/* -*- fundamental -*- ---------------------------------------------------
+ *
+ * Copyright 2008 H. Peter Anvin - All Rights Reserved
+ * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston MA 02110-1301, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * idle.c:
+ *
+ * This function provided protected-mode access to the idle handling.
+ * It needs to be carefully coordinated with idle.inc, which provides
+ * idle services to real-mode code.
+ */
+
+#include "core.h"
+#include <sys/cpu.h>
+
+#define TICKS_TO_IDLE 4 /* Also in idle.inc */
+
+extern uint32_t _IdleTimer;
+extern uint16_t NoHalt;
+
+int (*idle_hook_func)(void);
+
+void reset_idle(void)
+{
+ _IdleTimer = jiffies();
+}
+
+void __idle(void)
+{
+ if (jiffies() - _IdleTimer < TICKS_TO_IDLE)
+ return;
+
+ if (idle_hook_func && idle_hook_func())
+ return; /* Nonzero return = do not idle */
+
+ if (NoHalt)
+ cpu_relax();
+ else
+ hlt();
+}
diff --git a/core/idle.inc b/core/idle.inc
index dc8d2045..9677c822 100644
--- a/core/idle.inc
+++ b/core/idle.inc
@@ -1,7 +1,7 @@
;; -*- fundamental -*- ---------------------------------------------------
;;
;; Copyright 2008 H. Peter Anvin - All Rights Reserved
-;; Copyright 2009 Intel Corporation; author: H. Peter Anvin
+;; Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
;;
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
@@ -11,19 +11,19 @@
;;
;; -----------------------------------------------------------------------
- section .text
-TICKS_TO_IDLE equ 4
+ section .text16
+TICKS_TO_IDLE equ 4 ; Also in idle.c
reset_idle:
- push ax
- mov ax,[cs:BIOS_timer]
- mov [cs:IdleTimer],ax
- pop ax
+ push eax
+ mov eax,[cs:__jiffies]
+ mov [cs:_IdleTimer],eax
+ pop eax
sti ; Guard against BIOS/PXE brokenness...
ret
do_idle:
- push ax
+ push eax
push ds
push es
mov ax,cs
@@ -54,27 +54,28 @@ do_idle:
pop si
sti
.ok:
- mov ax,[BIOS_timer]
- sub ax,[IdleTimer]
- cmp ax,TICKS_TO_IDLE
+ ; Don't spend time jumping to PM unless we're actually idle...
+
+ mov eax,[__jiffies]
+ sub eax,[_IdleTimer]
+ cmp eax,TICKS_TO_IDLE
jb .done
- call [IdleHook]
- cmp dword [NoHalt],0
- jne .done
- hlt
+
+ extern __idle
+ pm_call __idle
.done:
pop es
pop ds
- pop ax
+ pop eax
.ret: ret
- section .data
- alignb 4
-NoHalt dw 0 ; NoHalt set by user
-ForceNoHalt dw 0 ; NoHalt forced by hardware config
-IdleHook dw do_idle.ret
+ section .data16
+ alignz 4
+ global _IdleTimer
+_IdleTimer dd 0
+ global NoHalt
+NoHalt dw 0
hlt_err db 'ERROR: idle with IF=0', CR, LF, 0
- section .bss
-IdleTimer resw 1
+ section .text16
diff --git a/core/include/cache.h b/core/include/cache.h
new file mode 100644
index 00000000..1f451afd
--- /dev/null
+++ b/core/include/cache.h
@@ -0,0 +1,23 @@
+#ifndef _CACHE_H
+#define _CACHE_H
+
+#include <stdint.h>
+#include <com32.h>
+#include "disk.h"
+#include "fs.h"
+
+/* The cache structure */
+struct cache {
+ block_t block;
+ struct cache *prev;
+ struct cache *next;
+ void *data;
+};
+
+/* functions defined in cache.c */
+void cache_init(struct device *, int);
+const void *get_cache(struct device *, block_t);
+struct cache *_get_cache_block(struct device *, block_t);
+void cache_lock_block(struct cache *);
+
+#endif /* cache.h */
diff --git a/core/include/codepage.h b/core/include/codepage.h
new file mode 100644
index 00000000..a24d90f5
--- /dev/null
+++ b/core/include/codepage.h
@@ -0,0 +1,27 @@
+/*
+ * Codepage data structure as generated by cptable.pl
+ */
+#ifndef CODEPAGE_H
+#define CODEPAGE_H
+
+#include <stdint.h>
+
+#define CODEPAGE_MAGIC UINT64_C(0x51d21eb158a8b3d4)
+
+struct codepage {
+ uint64_t magic;
+ uint32_t reserved[6];
+
+ uint8_t upper[256]; /* Codepage upper case table */
+ uint8_t lower[256]; /* Codepage lower case table */
+
+ /*
+ * The primary Unicode match is the same case, i.e. A -> A,
+ * the secondary Unicode match is the opposite case, i.e. A -> a.
+ */
+ uint16_t uni[2][256]; /* Primary and alternate Unicode matches */
+};
+
+extern const struct codepage codepage;
+
+#endif /* CODEPAGE_H */
diff --git a/core/include/core.h b/core/include/core.h
new file mode 100644
index 00000000..eb7bfcdb
--- /dev/null
+++ b/core/include/core.h
@@ -0,0 +1,74 @@
+#ifndef CORE_H
+#define CORE_H
+
+#include <klibc/compiler.h>
+#include <com32.h>
+#include <syslinux/pmapi.h>
+
+extern char core_xfer_buf[65536];
+extern char core_cache_buf[65536];
+extern char trackbuf[];
+extern char CurrentDirName[];
+extern char SubvolName[];
+extern char ConfigName[];
+extern char KernelName[];
+extern char cmd_line[];
+extern char ConfigFile[];
+
+/* diskstart.inc isolinux.asm*/
+extern void getlinsec(void);
+
+/* getc.inc */
+extern void core_open(void);
+
+/* hello.c */
+extern void myputs(const char*);
+
+/* idle.c */
+extern int (*idle_hook_func)(void);
+extern void __idle(void);
+extern void reset_idle(void);
+
+/* mem/malloc.c, mem/free.c, mem/init.c */
+extern void *malloc(size_t);
+extern void *lmalloc(size_t);
+extern void *pmapi_lmalloc(size_t);
+extern void *zalloc(size_t);
+extern void free(void *);
+extern void mem_init(void);
+
+void __cdecl core_intcall(uint8_t, const com32sys_t *, com32sys_t *);
+void __cdecl core_farcall(uint32_t, const com32sys_t *, com32sys_t *);
+int __cdecl core_cfarcall(uint32_t, const void *, uint32_t);
+
+extern const com32sys_t zero_regs;
+void call16(void (*)(void), const com32sys_t *, com32sys_t *);
+
+/*
+ * __lowmem is in the low 1 MB; __bss16 in the low 64K
+ */
+#define __lowmem __attribute__((nocommon,section(".lowmem")))
+#define __bss16 __attribute__((nocommon,section(".bss16")))
+
+/*
+ * Section for very large aligned objects, not zeroed on startup
+ */
+#define __hugebss __attribute__((nocommon,section(".hugebss"),aligned(4096)))
+
+/*
+ * Death! The macro trick is to avoid symbol conflict with
+ * the real-mode symbol kaboom.
+ */
+__noreturn _kaboom(void);
+#define kaboom() _kaboom()
+
+/*
+ * Basic timer function...
+ */
+extern volatile uint32_t __jiffies;
+static inline uint32_t jiffies(void)
+{
+ return __jiffies;
+}
+
+#endif /* CORE_H */
diff --git a/core/include/ctype.h b/core/include/ctype.h
new file mode 100644
index 00000000..5c6d4cb4
--- /dev/null
+++ b/core/include/ctype.h
@@ -0,0 +1,25 @@
+#ifndef CTYPE_H
+#define CTYPE_H
+
+/*
+ * Small subset of <ctype.h> for parsing uses, only handles ASCII
+ * and passes the rest through.
+ */
+
+static inline int toupper(int c)
+{
+ if (c >= 'a' && c <= 'z')
+ c -= 0x20;
+
+ return c;
+}
+
+static inline int tolower(int c)
+{
+ if (c >= 'A' && c <= 'Z')
+ c += 0x20;
+
+ return c;
+}
+
+#endif /* CTYPE_H */
diff --git a/core/include/disk.h b/core/include/disk.h
new file mode 100644
index 00000000..da6555ae
--- /dev/null
+++ b/core/include/disk.h
@@ -0,0 +1,36 @@
+#ifndef DISK_H
+#define DISK_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+typedef uint64_t sector_t;
+typedef uint64_t block_t;
+
+/*
+ * struct disk: contains the information about a specific disk and also
+ * contains the I/O function.
+ */
+struct disk {
+ uint8_t disk_number; /* in BIOS style */
+ uint8_t type; /* CHS or EDD */
+ uint16_t sector_size; /* gener512B or 2048B */
+ uint8_t sector_shift;
+
+ uint8_t h, s; /* CHS geometry */
+ uint8_t pad;
+
+ sector_t part_start; /* the start address of this partition(in sectors) */
+
+ int (*rdwr_sectors)(struct disk *, void *, sector_t, size_t, bool);
+};
+
+extern void read_sectors(char *, sector_t, int);
+extern void getoneblk(struct disk *, char *, block_t, int);
+
+/* diskio.c */
+struct disk *disk_init(uint8_t, bool, sector_t, uint16_t, uint16_t);
+struct device *device_init(uint8_t, bool, sector_t, uint16_t, uint16_t);
+
+#endif /* DISK_H */
diff --git a/core/include/fs.h b/core/include/fs.h
new file mode 100644
index 00000000..f1d35bbb
--- /dev/null
+++ b/core/include/fs.h
@@ -0,0 +1,227 @@
+#ifndef FS_H
+#define FS_H
+
+#include <stddef.h>
+#include <stdbool.h>
+#include <string.h>
+#include <com32.h>
+#include <stdio.h>
+#include <sys/dirent.h>
+#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)
+
+#define FILENAME_MAX_LG2 8
+#define FILENAME_MAX (1 << FILENAME_MAX_LG2)
+
+#define CURRENTDIR_MAX FILENAME_MAX
+
+#define BLOCK_SIZE(fs) ((fs)->block_size)
+#define BLOCK_SHIFT(fs) ((fs)->block_shift)
+#define SECTOR_SIZE(fs) ((fs)->sector_size)
+#define SECTOR_SHIFT(fs) ((fs)->sector_shift)
+
+struct fs_info {
+ const struct fs_ops *fs_ops;
+ struct device *fs_dev;
+ void *fs_info; /* The fs-specific information */
+ int sector_shift, sector_size;
+ int block_shift, block_size;
+ struct inode *root, *cwd; /* Root and current directories */
+ char cwd_name[CURRENTDIR_MAX]; /* Current directory by name */
+};
+
+extern struct fs_info *this_fs;
+
+struct dirent; /* Directory entry structure */
+struct file;
+enum fs_flags {
+ FS_NODEV = 1 << 0,
+ FS_USEMEM = 1 << 1, /* If we need a malloc routine, set it */
+ FS_THISIND = 1 << 2, /* Set cwd based on config file location */
+};
+
+struct fs_ops {
+ /* in fact, we use fs_ops structure to find the right fs */
+ const char *fs_name;
+ enum fs_flags fs_flags;
+
+ int (*fs_init)(struct fs_info *);
+ void (*searchdir)(const char *, struct file *);
+ uint32_t (*getfssec)(struct file *, char *, int, bool *);
+ void (*close_file)(struct file *);
+ void (*mangle_name)(char *, const char *);
+ size_t (*realpath)(struct fs_info *, char *, const char *, size_t);
+ int (*chdir)(struct fs_info *, const char *);
+ int (*load_config)(void);
+
+ struct inode * (*iget_root)(struct fs_info *);
+ struct inode * (*iget)(const char *, struct inode *);
+ int (*readlink)(struct inode *, char *);
+
+ /* the _dir_ stuff */
+ int (*readdir)(struct file *, struct dirent *);
+
+ int (*next_extent)(struct inode *, uint32_t);
+};
+
+/*
+ * Extent structure: contains the mapping of some chunk of a file
+ * that is contiguous on disk.
+ */
+struct extent {
+ sector_t pstart; /* Physical start sector */
+ uint32_t lstart; /* Logical start sector */
+ uint32_t len; /* Number of contiguous sectors */
+};
+
+/* Special sector numbers used for struct extent.pstart */
+#define EXTENT_ZERO ((sector_t)-1) /* All-zero extent */
+#define EXTENT_VOID ((sector_t)-2) /* Invalid information */
+
+#define EXTENT_SPECIAL(x) ((x) >= EXTENT_VOID)
+
+/*
+ * The inode structure, including the detail file information
+ */
+struct inode {
+ struct fs_info *fs; /* The filesystem this inode is associated with */
+ int refcnt;
+ int mode; /* FILE , DIR or SYMLINK */
+ uint32_t size;
+ uint32_t blocks; /* How many blocks the file take */
+ uint32_t ino; /* Inode number */
+ uint32_t atime; /* Access time */
+ uint32_t mtime; /* Modify time */
+ uint32_t ctime; /* Create time */
+ uint32_t dtime; /* Delete time */
+ uint32_t flags;
+ uint32_t file_acl;
+ struct extent this_extent, next_extent;
+ char pvt[0]; /* Private filesystem data */
+};
+
+struct file {
+ struct fs_info *fs;
+ uint32_t offset; /* for next read */
+ struct inode *inode; /* The file-specific information */
+};
+
+enum dev_type {CHS, EDD};
+
+/*
+ * Struct device contains:
+ * the pointer points to the disk structure,
+ * the cache stuff.
+ */
+struct cache;
+
+struct device {
+ struct disk *disk;
+
+ /* the cache stuff */
+ char *cache_data;
+ struct cache *cache_head;
+ uint16_t cache_block_size;
+ uint16_t cache_entries;
+ uint32_t cache_size;
+};
+
+/*
+ * Our definition of "not whitespace"
+ */
+static inline bool not_whitespace(char c)
+{
+ return (unsigned char)c > ' ';
+}
+
+/*
+ * Inode allocator/deallocator
+ */
+struct inode *alloc_inode(struct fs_info *fs, uint32_t ino, size_t data);
+static inline void free_inode(struct inode * inode)
+{
+ free(inode);
+}
+
+static inline struct inode *get_inode(struct inode *inode)
+{
+ inode->refcnt++;
+ return inode;
+}
+static inline void put_inode(struct inode *inode)
+{
+ if (! --inode->refcnt)
+ free(inode);
+}
+
+static inline void malloc_error(char *obj)
+{
+ printf("Out of memory: can't allocate memory for %s\n", obj);
+ kaboom();
+}
+
+/*
+ * File handle conversion functions
+ */
+extern struct file files[];
+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;
+}
+
+/* fs.c */
+void pm_mangle_name(com32sys_t *);
+void pm_searchdir(com32sys_t *);
+void mangle_name(char *, const char *);
+int searchdir(const char *name);
+void _close_file(struct file *);
+size_t pmapi_read_file(uint16_t *handle, void *buf, size_t sectors);
+int open_file(const char *name, struct com32_filedata *filedata);
+void pm_open_file(com32sys_t *);
+void close_file(uint16_t handle);
+void pm_close_file(com32sys_t *);
+
+/* chdir.c */
+void pm_realpath(com32sys_t *regs);
+size_t realpath(char *dst, const char *src, size_t bufsize);
+int chdir(const char *src);
+
+/* readdir.c */
+DIR *opendir(const char *pathname);
+struct dirent *readdir(DIR *dir);
+int closedir(DIR *dir);
+
+/*
+ * Generic functions that filesystem drivers may choose to use
+ */
+
+/* mangle.c */
+void generic_mangle_name(char *, const char *);
+
+/* loadconfig.c */
+int generic_load_config(void);
+
+/* close.c */
+void generic_close_file(struct file *file);
+
+/* getfssec.c */
+uint32_t generic_getfssec(struct file *file, char *buf,
+ int sectors, bool *have_more);
+
+/* nonextextent.c */
+int no_next_extent(struct inode *, uint32_t);
+
+#endif /* FS_H */
diff --git a/core/include/pmapi.h b/core/include/pmapi.h
new file mode 100644
index 00000000..57d2e6f7
--- /dev/null
+++ b/core/include/pmapi.h
@@ -0,0 +1,8 @@
+#ifndef PMAPI_H
+#define PMAPI_H
+
+#include <syslinux/pmapi.h>
+
+size_t pmapi_read_file(uint16_t *, void *, size_t);
+
+#endif /* PMAPI_H */
diff --git a/core/init.inc b/core/init.inc
index 0b213ace..e06ca96f 100644
--- a/core/init.inc
+++ b/core/init.inc
@@ -2,6 +2,7 @@
; -----------------------------------------------------------------------
;
; Copyright 2004-2008 H. Peter Anvin - All Rights Reserved
+; Copyright 2009 Intel Corporation; author: H. Peter Anvin
;
; This program is free software; you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
@@ -17,10 +18,20 @@
; Common initialization code (inline)
;
- section .text
+ section .text16
common_init:
- ; Now set up screen parameters
- call adjust_screen
+ ; Initialize PM invocation framework
+ call pm_init
+
+ ; Decompress PM code to its target location
+ pm_call pm_decompress
+ cmp eax,__pm_code_len
+ jne kaboom
+
+;
+; Initialize timer
+;
+ call timer_init
;
; Initialize configuration information
@@ -28,20 +39,95 @@ common_init:
call reset_config
;
-; Clear Files structures
+; Set up the COMBOOT APIs
+;
+ call comboot_setup_api
+
+;
+; Now set up screen parameters
+;
+ call adjust_screen
+
+;
+; CPU-dependent initialization and related checks.
+;
+check_escapes:
+ mov ah,02h ; Check keyboard flags
+ int 16h
+ mov [KbdFlags],al ; Save for boot prompt check
+ test al,04h ; Ctrl->skip 386 check
+ jnz skip_checks
+
+;
+; Now check that there is sufficient low (DOS) memory
+;
+; NOTE: Linux doesn't use all of real_mode_seg, but we use the same
+; segment for COMBOOT images, which can use all 64K
+;
+ int 12h
+ mov edx,__lowmem_heap + min_lowmem_heap + 1023
+ shr edx,10
+ cmp ax,dx
+ jae enough_ram
+ mov ax,dx
+ mov si,err_noram
+ mov cl,10
+ div cl
+ add [si+err_noram.size-err_noram+2],ah
+ cbw
+ div cl
+ add [si+err_noram.size-err_noram],ax
+ call writestr_early
+ jmp kaboom
+enough_ram:
+skip_checks:
+
+ section .data16
+err_noram db 'It appears your computer has less than '
+.size db '000'
+ db 'K of low ("DOS")'
+ db CR, LF
+ db 'RAM. Syslinux needs at least this amount to boot. If you get'
+ db CR, LF
+ db 'this message in error, hold down the Ctrl key while'
+ db CR, LF
+ db 'booting, and I will take your word for it.', CR, LF, 0
+
+ section .text16
+;
+; The code to decompress the PM code and initialize other segments.
;
- mov di,Files
- mov cx,(MAX_OPEN*open_file_t_size)/4
+ extern _lzo1x_decompress_asm_fast
+
+ section .textnr
+ bits 32
+pm_decompress:
+ push 0 ; Space for decompressed size
+ push esp ; Pointer to previous word
+ push __pm_code_start ; Target address
+ push dword [lzo_data_size] ; Compressed size
+ push dword __pm_code_lma
+ call _lzo1x_decompress_asm_fast
+ add esp,16
+ pop RM_EAX ; Decompressed size
+
+ ; Zero bss sections (but not .earlybss, since it may
+ ; contain already-live data.)
xor eax,eax
+ mov edi,__bss_start
+ mov ecx,__bss_dwords
+ rep stosd
+ mov edi,__bss16_start
+ mov ecx,__bss16_dwords
rep stosd
+ mov edi,__high_clear_start ; .uibss, .auxseg, .lowmem
+ mov ecx,__high_clear_dwords
+ rep stosd
+
+ ret
+
+ section .data16
+lzo_data_size dd 0 ; filled in by compressor
-%if IS_PXELINUX
- mov di,Files+tftp_pktbuf
- mov cx,MAX_OPEN
-.setbufptr:
- mov [di],ax
- add di,open_file_t_size
- add ax,PKTBUF_SIZE
- loop .setbufptr
-%endif
- section .text ; This is an inline file...
+ section .text16
+ bits 16
diff --git a/core/isolinux.asm b/core/isolinux.asm
index 23429bdf..d1d5bf8d 100644
--- a/core/isolinux.asm
+++ b/core/isolinux.asm
@@ -26,14 +26,10 @@
; Some semi-configurable constants... change on your own risk.
;
my_id equ isolinux_id
-FILENAME_MAX_LG2 equ 8 ; log2(Max filename size Including final null)
-FILENAME_MAX equ (1 << FILENAME_MAX_LG2)
NULLFILE equ 0 ; Zero byte == null file name
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)
@@ -72,12 +68,6 @@ file_left resd 1 ; Number of sectors left
%endif
%endif
- struc dir_t
-dir_lba resd 1 ; Directory start (LBA)
-dir_len resd 1 ; Length in bytes
-dir_clust resd 1 ; Length in clusters
- endstruc
-
; ---------------------------------------------------------------------------
; BEGIN CODE
; ---------------------------------------------------------------------------
@@ -86,18 +76,15 @@ dir_clust resd 1 ; Length in clusters
; Memory below this point is reserved for the BIOS and the MBR
;
section .earlybss
+ global trackbuf
trackbufsize equ 8192
trackbuf resb trackbufsize ; Track buffer goes here
; ends at 2800h
; Some of these are touched before the whole image
- ; is loaded. DO NOT move this to .uibss.
- section .bss2
+ ; is loaded. DO NOT move this to .bss16/.uibss.
+ section .earlybss
alignb 4
-ISOFileName resb 64 ; ISO filename canonicalization buffer
-ISOFileNameEnd equ $
-CurrentDir resb dir_t_size ; Current directory
-RootDir resb dir_t_size ; Root directory
FirstSecSum resd 1 ; Checksum of bytes 64-2048
ImageDwords resd 1 ; isolinux.bin size, dwords
InitStack resd 1 ; Initial stack pointer (SS:SP)
@@ -190,10 +177,7 @@ dsp_dummy: resb 1 ; Scratch, safe to overwrite
_spec_end equ $
_spec_len equ _spec_end - _spec_start
- alignb open_file_t_size
-Files resb MAX_OPEN*open_file_t_size
-
- section .text
+ section .init
;;
;; Primary entry point. Because BIOSes are buggy, we only load the first
;; CD-ROM sector (2K) of the file, so the number one priority is actually
@@ -203,6 +187,7 @@ StackBuf equ STACK_TOP-44 ; 44 bytes needed for
; the bootsector chainloading
; code!
OrigESDI equ StackBuf-4 ; The high dword on the stack
+StackHome equ OrigESDI
bootsec equ $
@@ -215,6 +200,8 @@ _start: ; Far jump makes sure we canonicalize the address
; -boot-info-table option. If not, the values in this
; table are default values that we can use to get us what
; we need, at least under a certain set of assumptions.
+ global iso_boot_info
+iso_boot_info:
bi_pvd: dd 16 ; LBA of primary volume descriptor
bi_file: dd 0 ; LBA of boot file
bi_length: dd 0xdeadbeef ; Length of boot file
@@ -410,8 +397,8 @@ found_file:
sub eax,SECTOR_SIZE-3 ; ... minus sector loaded
shr eax,2 ; bytes->dwords
mov [ImageDwords],eax ; boot file dwords
- add eax,(2047 >> 2)
- shr eax,9 ; dwords->sectors
+ add eax,((SECTOR_SIZE-1) >> 2)
+ shr eax,SECTOR_SHIFT-2 ; dwords->sectors
mov [ImageSectors],ax ; boot file sectors
mov eax,[bi_file] ; Address of code to load
@@ -423,49 +410,76 @@ found_file:
call crlf
%endif
- ; Just in case some BIOSes have problems with
- ; segment wraparound, use the normalized address
- mov bx,((7C00h+2048) >> 4)
+ ; Load the rest of the file. However, just in case there
+ ; are still BIOSes with 64K wraparound problems, we have to
+ ; take some extra precautions. Since the normal load
+ ; address (TEXT_START) is *not* 2K-sector-aligned, we round
+ ; the target address upward to a sector boundary,
+ ; and then move the entire thing down as a unit.
+MaxLMA equ 384*1024 ; Reasonable limit (384K)
+
+ mov bx,((TEXT_START+2*SECTOR_SIZE-1) & ~(SECTOR_SIZE-1)) >> 4
+ mov bp,[ImageSectors]
+ push bx ; Load segment address
+
+.more:
+ push bx ; Segment address
+ push bp ; Sector count
mov es,bx
+ mov cx,0xfff
+ and bx,cx
+ inc cx
+ sub cx,bx
+ shr cx,SECTOR_SHIFT - 4
+ jnz .notaligned
+ mov cx,0x10000 >> SECTOR_SHIFT ; Full 64K segment possible
+.notaligned:
+ cmp bp,cx
+ jbe .ok
+ mov bp,cx
+.ok:
xor bx,bx
- mov bp,[ImageSectors]
-%ifdef DEBUG_MESSAGES
- push ax
- mov si,size_msg
- call writemsg
- mov ax,bp
- call writehex4
- call crlf
- pop ax
-%endif
+ push bp
call getlinsec
+ pop cx
+ mov dx,cx
+ pop bp
+ pop bx
- push ds
- pop es
+ shl cx,SECTOR_SHIFT - 4
+ add bx,cx
+ sub bp,dx
+ jnz .more
-%ifdef DEBUG_MESSAGES
- mov si,loaded_msg
- call writemsg
-%endif
-
- ; Verify the checksum on the loaded image.
-verify_image:
- mov si,7C00h+2048
- mov bx,es
+ ; Move the image into place, and also verify the
+ ; checksum
+ pop ax ; Load segment address
+ mov bx,(TEXT_START + SECTOR_SIZE) >> 4
mov ecx,[ImageDwords]
mov edi,[FirstSecSum] ; First sector checksum
-.loop es lodsd
- add edi,eax
+ xor si,si
+
+move_verify_image:
+.setseg:
+ mov ds,ax
+ mov es,bx
+.loop:
+ mov edx,[si]
+ add edi,edx
dec ecx
+ mov [es:si],edx
jz .done
- and si,si
+ add si,4
jnz .loop
- ; SI wrapped around, advance ES
+ add ax,1000h
add bx,1000h
- mov es,bx
- jmp short .loop
-.done: mov ax,ds
+ jmp .setseg
+.done:
+ mov ax,cs
+ mov ds,ax
mov es,ax
+
+ ; Verify the checksum on the loaded image.
cmp [bi_csum],edi
je integrity_ok
@@ -697,9 +711,7 @@ writemsg: push ax
;
writechr:
- jmp near writechr_simple ; 3-byte jump
-
-writechr_simple:
+.simple:
pushfd
pushad
mov ah,0Eh
@@ -747,6 +759,7 @@ getonesec:
; ES:BX - Target buffer
; BP - Sector count
;
+ global getlinsec
getlinsec: jmp word [cs:GetlinsecPtr]
%ifndef DEBUG_MESSAGES
@@ -1012,6 +1025,7 @@ xint13: mov byte [RetryCount],retry_count
; kaboom: write a message and bail out. Wait for a user keypress,
; then do a hard reboot.
;
+ global kaboom
disk_error:
kaboom:
RESET_STACK_AND_SEGS AX
@@ -1044,8 +1058,6 @@ startup_msg: db 'Starting up, DL = ', 0
spec_ok_msg: db 'Loaded spec packet OK, drive = ', 0
secsize_msg: db 'Sector size ', 0
offset_msg: db 'Main image LBA = ', 0
-size_msg: db 'Sectors to load = ', 0
-loaded_msg: db 'Loaded boot image, verifying...', CR, LF, 0
verify_msg: db 'Image checksum verified.', CR, LF, 0
allread_msg db 'Main image read, jumping to main code...', CR, LF, 0
%endif
@@ -1081,6 +1093,7 @@ bios_ebios: dw getlinsec_ebios, bios_ebios_str
%endif
; Maximum transfer size
+ global MaxTransfer
MaxTransfer dw 127 ; Hard disk modes
MaxTransferCD dw 32 ; CD mode
@@ -1094,6 +1107,8 @@ rl_checkpt equ $ ; Must be <= 800h
; End of code and data that have to be in the first sector
; ----------------------------------------------------------------------------
+ section .text16
+
all_read:
; Test tracers
@@ -1104,10 +1119,14 @@ all_read:
; Common initialization code
;
%include "init.inc"
-%include "cpuinit.inc"
; Patch the writechr routine to point to the full code
- mov word [writechr+1], writechr_full-(writechr+3)
+ mov di,writechr
+ mov al,0e9h
+ stosb
+ mov ax,writechr_full-2
+ sub ax,di
+ stosw
; Tell the user we got this far...
%ifndef DEBUG_MESSAGES ; Gets messy with debugging on
@@ -1135,95 +1154,31 @@ all_read:
; (which will be at 16 only for a single-session disk!); from the PVD
; we should be able to find the rest of what we need to know.
;
-get_fs_structures:
- mov eax,[bi_pvd]
- mov bx,trackbuf
- call getonesec
+ pushad
+ mov eax,ROOT_FS_OPS
+ mov dl,[DriveNumber]
+ cmp word [BIOSType],bios_cdrom
+ sete dh ; 1 for cdrom, 0 for hybrid mode
+ mov ecx,[bsHidden]
+ mov ebx,[bsHidden+4]
+ mov si,[bsHeads]
+ mov di,[bsSecPerTrack]
+ pm_call fs_init
+ popad
- mov eax,[trackbuf+156+2]
- mov [RootDir+dir_lba],eax
- mov [CurrentDir+dir_lba],eax
-%ifdef DEBUG_MESSAGES
- mov si,dbg_rootdir_msg
- call writemsg
- call writehex8
- call crlf
-%endif
- mov eax,[trackbuf+156+10]
- mov [RootDir+dir_len],eax
- mov [CurrentDir+dir_len],eax
- add eax,SECTOR_SIZE-1
- shr eax,SECTOR_SHIFT
- mov [RootDir+dir_clust],eax
- mov [CurrentDir+dir_clust],eax
-
- ; 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
+ section .rodata
+ alignz 4
+ROOT_FS_OPS:
+ extern iso_fs_ops
+ dd iso_fs_ops
+ dd 0
- mov [CurrentDir+dir_len],eax
- mov eax,[si+file_left]
- mov [CurrentDir+dir_clust],eax
- xor eax,eax ; Free this file pointer entry
- xchg eax,[si+file_sector]
- mov [CurrentDir+dir_lba],eax
-%ifdef DEBUG_MESSAGES
- push si
- mov si,dbg_isodir_msg
- call writemsg
- pop si
- call writehex8
- call crlf
-%endif
-.no_isolinux_dir:
+ section .text16
;
; Locate the configuration file
;
-load_config:
-%ifdef DEBUG_MESSAGES
- mov si,dbg_config_msg
- call writemsg
-%endif
-
- mov si,config_name
- mov di,ConfigName
- call strcpy
-
- mov di,ConfigName
- call open
- jz no_config_file ; Not found or empty
-
-%ifdef DEBUG_MESSAGES
- mov si,dbg_configok_msg
- call writemsg
-%endif
+ pm_call load_config
;
; Now we have the config file open. Parse the config file and
@@ -1264,7 +1219,7 @@ is_disk_image:
mov bx,trackbuf
mov cx,1 ; Load 1 sector
- call getfssec
+ pm_call getfssec
cmp word [trackbuf+510],0aa55h ; Boot signature
jne .bad_image ; Image not bootable
@@ -1361,391 +1316,23 @@ is_disk_image:
mov al,bl
.done_sector: 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_left
- xor si,si
-.closed: ret
-
-;
-; searchdir:
-;
-; Open a file
-;
-; On entry:
-; DS:DI = filename
-; If successful:
-; ZF clear
-; SI = file pointer
-; EAX = file length in bytes
-; If unsuccessful
-; ZF set
-;
-; Assumes CS == DS == ES, and trashes BX and CX.
-;
-; searchdir_iso is a special entry point for ISOLINUX only. In addition
-; to the above, searchdir_iso passes a file flag mask in AL. This is useful
-; for searching for directories.
-;
-alloc_failure:
- xor ax,ax ; ZF <- 1
- ret
-
-searchdir:
- xor al,al
-searchdir_iso:
- mov [ISOFlags],al
- TRACER 'S'
- call allocate_file ; Temporary file structure for directory
- jnz alloc_failure
- push es
- push ds
- pop es ; ES = DS
- mov si,CurrentDir
- cmp byte [di],'/' ; If filename begins with slash
- jne .not_rooted
- inc di ; Skip leading slash
- mov si,RootDir ; Reference root directory instead
-.not_rooted:
- mov eax,[si+dir_clust]
- mov [bx+file_left],eax
- shl eax,SECTOR_SHIFT
- mov [bx+file_bytesleft],eax
- mov eax,[si+dir_lba]
- mov [bx+file_sector],eax
- mov edx,[si+dir_len]
-
-.look_for_slash:
- mov ax,di
-.scan:
- mov cl,[di]
- inc di
- and cl,cl
- jz .isfile
- cmp cl,'/'
- jne .scan
- mov [di-1],byte 0 ; Terminate at directory name
- mov cl,02h ; Search for directory
- xchg cl,[ISOFlags]
-
- push di ; Save these...
- push cx
-
- ; Create recursion stack frame...
- push word .resume ; Where to "return" to
- push es
-.isfile: xchg ax,di
-
-.getsome:
- ; Get a chunk of the directory
- ; This relies on the fact that ISOLINUX doesn't change SI
- mov si,trackbuf
- TRACER 'g'
- pushad
- xchg bx,si
- mov cx,[BufSafe]
- call getfssec
- popad
-
-.compare:
- movzx eax,byte [si] ; Length of directory entry
- cmp al,33
- jb .next_sector
- TRACER 'c'
- mov cl,[si+25]
- xor cl,[ISOFlags]
- test cl, byte 8Eh ; Unwanted file attributes!
- jnz .not_file
- pusha
- movzx cx,byte [si+32] ; File identifier length
- add si,byte 33 ; File identifier offset
- TRACER 'i'
- call iso_compare_names
- popa
- je .success
-.not_file:
- sub edx,eax ; Decrease bytes left
- jbe .failure
- add si,ax ; Advance pointer
-
-.check_overrun:
- ; Did we finish the buffer?
- cmp si,trackbuf+trackbufsize
- jb .compare ; No, keep going
-
- jmp short .getsome ; Get some more directory
-
-.next_sector:
- ; Advance to the beginning of next sector
- lea ax,[si+SECTOR_SIZE-1]
- and ax,~(SECTOR_SIZE-1)
- sub ax,si
- jmp short .not_file ; We still need to do length checks
-
-.failure: xor eax,eax ; ZF = 1
- mov [bx+file_sector],eax
- pop es
- ret
-
-.success:
- mov eax,[si+2] ; Location of extent
- mov [bx+file_sector],eax
- mov eax,[si+10] ; Data length
- mov [bx+file_bytesleft],eax
- push eax
- add eax,SECTOR_SIZE-1
- shr eax,SECTOR_SHIFT
- mov [bx+file_left],eax
- pop eax
- jz .failure ; Empty file?
- ; ZF = 0
- mov si,bx
- pop es
- ret
-.resume: ; We get here if we were only doing part of a lookup
- ; This relies on the fact that .success returns bx == si
- xchg edx,eax ; Directory length in edx
- pop cx ; Old ISOFlags
- pop di ; Next filename pointer
- mov byte [di-1], '/' ; Restore slash
- mov [ISOFlags],cl ; Restore the flags
- jz .failure ; Did we fail? If so fail for real!
- jmp .look_for_slash ; Otherwise, next level
-
-;
-; allocate_file: Allocate a file structure
-;
-; If successful:
-; ZF set
-; BX = file pointer
-; In unsuccessful:
-; ZF clear
-;
-allocate_file:
- TRACER 'a'
- push cx
- mov bx,Files
- mov cx,MAX_OPEN
-.check: cmp dword [bx], byte 0
- je .found
- add bx,open_file_t_size ; ZF = 0
- loop .check
- ; ZF = 0 if we fell out of the loop
-.found: pop cx
- ret
-
-;
-; iso_compare_names:
-; Compare the names DS:SI and DS:DI and report if they are
-; equal from an ISO 9660 perspective. SI is the name from
-; the filesystem; CX indicates its length, and ';' terminates.
-; DI is expected to end with a null.
-;
-; Note: clobbers AX, CX, SI, DI; assumes DS == ES == base segment
-;
-
-iso_compare_names:
- ; First, terminate and canonicalize input filename
- push di
- mov di,ISOFileName
-.canon_loop: jcxz .canon_end
- lodsb
- dec cx
- cmp al,';'
- je .canon_end
- and al,al
- je .canon_end
- stosb
- cmp di,ISOFileNameEnd-1 ; Guard against buffer overrun
- jb .canon_loop
-.canon_end:
- cmp di,ISOFileName
- jbe .canon_done
- cmp byte [di-1],'.' ; Remove terminal dots
- jne .canon_done
- dec di
- jmp short .canon_end
-.canon_done:
- mov [di],byte 0 ; Null-terminate string
- pop di
- mov si,ISOFileName
-.compare:
- lodsb
- mov ah,[di]
- inc di
- and ax,ax
- jz .success ; End of string for both
- and al,al ; Is either one end of string?
- jz .failure ; If so, failure
- and ah,ah
- jz .failure
- or ax,2020h ; Convert to lower case
- cmp al,ah
- je .compare
-.failure: and ax,ax ; ZF = 0 (at least one will be nonzero)
-.success: ret
-
-;
-; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed
-; to by ES:DI; ends on encountering any whitespace.
-; DI is preserved.
-;
-; This verifies that a filename is < FILENAME_MAX characters,
-; doesn't contain whitespace, zero-pads the output buffer,
-; and removes trailing dots and redundant slashes,
-; so "repe cmpsb" can do a compare, and the
-; path-searching routine gets a bit of an easier job.
-;
-mangle_name:
- push di
- push bx
- xor ax,ax
- mov cx,FILENAME_MAX-1
- mov bx,di
-
-.mn_loop:
- lodsb
- cmp al,' ' ; If control or space, end
- jna .mn_end
- cmp al,ah ; Repeated slash?
- je .mn_skip
- xor ah,ah
- cmp al,'/'
- jne .mn_ok
- mov ah,al
-.mn_ok stosb
-.mn_skip: loop .mn_loop
-.mn_end:
- cmp bx,di ; At the beginning of the buffer?
- jbe .mn_zero
- cmp byte [es:di-1],'.' ; Terminal dot?
- je .mn_kill
- cmp byte [es:di-1],'/' ; Terminal slash?
- jne .mn_zero
-.mn_kill: dec di ; If so, remove it
- inc cx
- jmp short .mn_end
-.mn_zero:
- inc cx ; At least one null byte
- xor ax,ax ; Zero-fill name
- rep stosb
- pop bx
- pop di
- ret ; Done
-
-;
-; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
-; filename to the conventional representation. This is needed
-; for the BOOT_IMAGE= parameter for the kernel.
-;
-; DS:SI -> input mangled file name
-; ES:DI -> output buffer
-;
-; On return, DI points to the first byte after the output name,
-; which is set to a null byte.
-;
-unmangle_name: call strcpy
- dec di ; Point to final null byte
- ret
-
-;
-; getfssec: Get multiple clusters from a file, given the file pointer.
-;
-; On entry:
-; ES:BX -> Buffer
-; SI -> File pointer
-; CX -> Cluster count
-; On exit:
-; SI -> File pointer (or 0 on EOF)
-; CF = 1 -> Hit EOF
-; ECX -> Bytes actually read
-;
-getfssec:
- TRACER 'F'
- push ds
- push cs
- pop ds ; DS <- CS
-
- movzx ecx,cx
- cmp ecx,[si+file_left]
- jna .ok_size
- mov ecx,[si+file_left]
-.ok_size:
-
- pushad
- mov eax,[si+file_sector]
- mov bp,cx
- TRACER 'l'
- call getlinsec
- popad
-
- ; ECX[31:16] == 0 here...
- add [si+file_sector],ecx
- sub [si+file_left],ecx
- shl ecx,SECTOR_SHIFT ; Convert to bytes
- cmp ecx,[si+file_bytesleft]
- jb .not_all
- mov ecx,[si+file_bytesleft]
-.not_all: sub [si+file_bytesleft],ecx
- jnz .ret ; CF = 0 in this case...
- push eax
- xor eax,eax
- mov [si+file_sector],eax ; Unused
- mov si,ax
- pop eax
- stc
-.ret:
- pop ds
- TRACER 'f'
- ret
; -----------------------------------------------------------------------------
; Common modules
; -----------------------------------------------------------------------------
-%include "getc.inc" ; getc et al
-%include "conio.inc" ; Console I/O
-%include "configinit.inc" ; Initialize configuration
-%include "parseconfig.inc" ; High-level config file handling
-%include "parsecmd.inc" ; Low-level config file handling
-%include "bcopy32.inc" ; 32-bit bcopy
-%include "loadhigh.inc" ; Load a file into high memory
-%include "font.inc" ; VGA font stuff
-%include "graphics.inc" ; VGA graphics
-%include "highmem.inc" ; High memory sizing
-%include "strcpy.inc" ; strcpy()
+%include "common.inc" ; Universal modules
%include "rawcon.inc" ; Console I/O w/o using the console functions
-%include "idle.inc" ; Idle handling
-%include "adv.inc" ; Auxillary Data Vector
%include "localboot.inc" ; Disk-based local boot
; -----------------------------------------------------------------------------
; Begin data section
; -----------------------------------------------------------------------------
- section .data
-
-default_str db 'default', 0
-default_len equ ($-default_str)
-boot_dir db '/boot' ; /boot/isolinux
-isolinux_dir db '/isolinux', 0
-config_name db 'isolinux.cfg', 0
+ section .data16
err_disk_image db 'Cannot load disk image (invalid file)?', CR, LF, 0
-%ifdef DEBUG_MESSAGES
-dbg_rootdir_msg db 'Root directory at LBA = ', 0
-dbg_isodir_msg db 'isolinux directory at LBA = ', 0
-dbg_config_msg db 'About to load config file...', CR, LF, 0
-dbg_configok_msg db 'Configuration file opened...', CR, LF, 0
-%endif
-
;
; Config file keyword table
;
diff --git a/core/kaboom.c b/core/kaboom.c
new file mode 100644
index 00000000..d639915a
--- /dev/null
+++ b/core/kaboom.c
@@ -0,0 +1,16 @@
+/*
+ * kaboom.c
+ */
+
+#include "core.h"
+
+#undef kaboom
+
+__noreturn _kaboom(void)
+{
+ extern void kaboom(void);
+ call16(kaboom, &zero_regs, NULL);
+ /* Do this if kaboom somehow returns... */
+ for (;;)
+ asm volatile("hlt");
+}
diff --git a/core/kernel.inc b/core/kernel.inc
index 5e1c7a39..245cd6db 100644
--- a/core/kernel.inc
+++ b/core/kernel.inc
@@ -62,6 +62,9 @@ linux_fdctab resb 12
cmd_line_here equ $ ; F800 Should be out of the way
endstruc
+ global cmd_line
+cmd_line equ core_real_mode + cmd_line_here
+
;
; Old kernel command line signature
;
diff --git a/core/layout.inc b/core/layout.inc
index 19b50579..dab27dde 100644
--- a/core/layout.inc
+++ b/core/layout.inc
@@ -27,7 +27,7 @@ BSS_START equ 0800h
TEXT_START equ 7C00h
;
-; Stack layout
+; 16-bit stack layout
;
; PXELINUX: There are apparently some AMI BIOSes in the field which
; put their BEV stack somewhere below 7C00h (and therefore don't
@@ -50,17 +50,27 @@ STACK_BASE equ STACK_TOP - STACK_LEN
LATEBSS_START equ 0B800h
;
+; 32-bit stack layout
+;
+ global STACK32_LEN
+STACK32_LEN equ 64*1024
+
+ section .stack nobits write align=4096
+ resb STACK32_LEN
+
+;
; The various sections and their relationship
;
; Use .earlybss for things that MUST be in low memory.
- section .earlybss nobits
+ section .earlybss nobits write
section .config write progbits align=4
section .replacestub exec write progbits align=16
+ section .gentextnr exec write nobits align=16
- ; Use .bss for things that doesn't have to be in low memory;
- ; with .bss1 and .bss2 to offload. .earlybss should be used
- ; for things that absolutely have to be below 0x7c00.
- section .bss write nobits align=16
+ ; Use .bss16 for things that doesn't have to be in low memory;
+ ; .earlybss should be used for things that absolutely have
+ ; to be below 0x7c00.
+ section .bss16 write nobits align=16
%if 0 ; IS_PXELINUX
; Warning here: RBFG build 22 randomly overwrites
@@ -72,11 +82,12 @@ LATEBSS_START equ 0B800h
RBFG_brainfuck: resb 2048 ; Bigger than an Ethernet packet...
%endif
- section .bss2 write nobits align=16
-
- section .text exec write progbits align=16
- section .bcopyxx exec write progbits align=16
- section .data write progbits align=16
+ section .init exec write progbits align=1
+ section .text16 exec write progbits align=1
+ section .textnr exec nowrite progbits align=1
+ section .bcopyxx.text exec nowrite progbits align=16
+ section .bcopyxx.data noexec write progbits align=16
+ section .data16 noexec write progbits align=16
section .adv write nobits align=512
@@ -86,36 +97,41 @@ RBFG_brainfuck: resb 2048 ; Bigger than an Ethernet packet...
; the spillover from the last fractional sector load.
section .uibss write nobits align=16
- ; Normal bss...
- section .bss1 write nobits align=16
-
; Symbols from linker script
%macro SECINFO 1
extern __%1_start, __%1_lma, __%1_end
extern __%1_len, __%1_dwords
%endmacro
+ SECINFO bss16
+ SECINFO uibss
SECINFO config
SECINFO replacestub
+ SECINFO bcopyxx
+
+ SECINFO pm_code
+ SECINFO high_clear
+
+ SECINFO bss
+
+ extern free_high_memory
global _start
- section .text
+ section .text16
;
; Segment assignments in the bottom 640K
; Keep the low-memory footprint as small as possible... overrun is a hard
; failure!
;
-; 0000h - main code/data segment (and BIOS segment)
-
-xfer_buf_seg equ 1000h
-aux_seg equ 2000h
serial_buf_size equ 4096 ; Should be a power of 2
;
; Contents of aux_seg
;
+ extern aux_seg ; Actual segment assigned by linker
+
struc aux
.fontbuf resb 8192
.serial resb serial_buf_size
@@ -123,22 +139,33 @@ serial_buf_size equ 4096 ; Should be a power of 2
alignb 4096 ; Align the next segment to 4K
endstruc
-aux_seg_end equ aux_seg + (aux_size >> 4)
+ section .auxseg write nobits align=16
+auxseg resb aux_size
;
-; Bounce buffer for I/O to high mem
-; Note: we keep all the segments page-aligned, even if that probably
-; is somewhat excessive. Sector alignment is obligatory, however.
+; Transfer buffer segment: guaranteed to be aligned 64K, used for disk I/O
+; One symbol for the segment number, one for the absolute address
;
+ extern xfer_buf_seg
+ section .xfer_buf write nobits align=65536
+ global core_xfer_buf
+core_xfer_buf resb 65536
-%if IS_ISOLINUX
-; ISOLINUX doesn't have a block cache yet
-real_mode_seg equ aux_seg_end
-%else
-cache_seg equ aux_seg_end ; 64K area for metadata cache
-real_mode_seg equ cache_seg + 1000h
+;
+; Segment for the real mode code (needed as long as we have a in-kernel
+; loader and/or COM16 support.
+; One symbol for the segment number, one for the absolute address
+;
+ extern real_mode_seg
+ section .real_mode write nobits align=65536
+ global core_real_mode
+core_real_mode resb 65536
+comboot_seg equ real_mode_seg ; COMBOOT image loading zone
-pktbuf_seg equ cache_seg ; PXELINUX packet buffers
-%endif
+;
+; At the very end, the lowmem heap
+;
+ extern __lowmem_heap
+min_lowmem_heap equ 65536
-comboot_seg equ real_mode_seg ; COMBOOT image loading zone
+ section .text16
diff --git a/core/ldlinux.asm b/core/ldlinux.asm
index 0808e6e8..23540ea5 100644
--- a/core/ldlinux.asm
+++ b/core/ldlinux.asm
@@ -22,1417 +22,19 @@
;
; ****************************************************************************
-%ifndef IS_MDSLINUX
%define IS_SYSLINUX 1
-%endif
%include "head.inc"
;
; Some semi-configurable constants... change on your own risk.
;
my_id equ syslinux_id
-FILENAME_MAX_LG2 equ 6 ; log2(Max filename size Including final null)
-FILENAME_MAX equ (1<<FILENAME_MAX_LG2) ; Max mangled filename size
-NULLFILE equ 0 ; First char space == null filename
-NULLOFFSET equ 0 ; Position in which to look
-retry_count equ 16 ; How patient are we with the disk?
-%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)
-
-DIRENT_SHIFT equ 5
-DIRENT_SIZE equ (1 << DIRENT_SHIFT)
-
-ROOT_DIR_WORD equ 0x002F
-
-;
-; The following structure is used for "virtual kernels"; i.e. LILO-style
-; option labels. The options we permit here are `kernel' and `append
-; Since there is no room in the bottom 64K for all of these, we
-; stick them in high memory and copy them down before we need them.
-;
- struc vkernel
-vk_vname: resb FILENAME_MAX ; Virtual name **MUST BE FIRST!**
-vk_rname: resb FILENAME_MAX ; Real name
-vk_appendlen: resw 1
-vk_type: resb 1 ; Type of file
- alignb 4
-vk_append: resb max_cmd_len+1 ; Command line
- alignb 4
-vk_end: equ $ ; Should be <= vk_size
- endstruc
-
-;
-; File structure. This holds the information for each currently open file.
-;
- struc open_file_t
-file_sector resd 1 ; Sector pointer (0 = structure free)
-file_bytesleft resd 1 ; Number of bytes left
-file_left resd 1 ; Number of sectors le