-CC = gcc
+CC = gcc -m32
+LD = ld -m elf_i386
+OBJCOPY = objcopy
OPTFLAGS = -g -Os
-INCLUDES = -I./ -I../ -I../libfat/
-CFLAGS = -W -Wall $(OPTFLAGS) $(INCLUDES)
-LDFLAGS = -s
-NASM = nasm
+INCLUDES = -include code16.h -I. -I.. -I../libfat
+CFLAGS = -W -Wall -ffreestanding $(OPTFLAGS) $(INCLUDES)
+LDFLAGS = -T com16.ld
-SRCS =
-OBJS =
+SRCS = syslinux.c printf.c conio.c skipatou.c atou.c malloc.c free.c \
+ ../syslxmod.c ../bootsect_bin.c ../ldlinux_bin.c \
+ $(wildcard ../libfat/*.c)
+OBJS = crt0.o $(patsubst %.c,%.o,$(notdir $(SRCS)))
-.SUFFIXES: .c .o .i .s .S .asm .bin .lst .com
+.SUFFIXES: .c .o .i .s .S .elf .com
VPATH = .:..:../libfat
-all: syslinux.com
+all: installer
tidy:
- -rm -f *.o *.i *.s *.a .*.d *.lst
+ -rm -f *.o *.i *.s *.a .*.d *.elf
clean: tidy
-rm -f syslinux.com
spotless: clean
-rm -f *~
-installer:
+installer: syslinux.com
+
+syslinux.elf: $(OBJS)
+ $(LD) $(LDFLAGS) -o $@ $^
+
+syslinux.com: syslinux.elf
+ $(OBJCOPY) -O binary $< $@
+
+%.o: %.c
+ $(CC) -Wp,-MT,$@,-MD,.$@.d $(CFLAGS) -c -o $@ $<
+%.i: %.c
+ $(CC) $(CFLAGS) -E -o $@ $<
+%.s: %.c
+ $(CC) $(CFLAGS) -S -o $@ $<
+%.s: %.S
+ $(CC) $(CFLAGS) -D__ASSEMBLY__ -S -o $@ $<
+
+-include .*.d
+
-syslinux.com: syslinux.asm ../ldlinux.bss ../ldlinux.sys
-.asm.com:
- $(NASM) $(INCLUDES) -f bin -o $@ -l $*.lst $<
--- /dev/null
+#include "mystuff.h"
+
+unsigned int atou(const char *s)
+{
+ unsigned int i = 0;
+ while (isdigit(*s))
+ i = i*10 + (*s++ - '0');
+ return i;
+}
+
--- /dev/null
+/* Must be included first of all */
+__asm__ (".code16gcc");
--- /dev/null
+/*
+ * Linker script for COM16 binaries
+ */
+
+/* Script for -z combreloc: combine and sort reloc sections */
+OUTPUT_FORMAT("elf32-i386", "elf32-i386",
+ "elf32-i386")
+OUTPUT_ARCH(i386)
+EXTERN(_start)
+ENTRY(_start)
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ . = 0x100;
+ PROVIDE (__executable_start = .);
+
+ .init :
+ {
+ KEEP (*(.init))
+ } =0x90909090
+ .text :
+ {
+ *(.text .stub .text.* .gnu.linkonce.t.*)
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ } =0x90909090
+ .fini :
+ {
+ KEEP (*(.fini))
+ } =0x90909090
+ PROVIDE (__etext = .);
+ PROVIDE (_etext = .);
+ PROVIDE (etext = .);
+ .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
+ .rodata1 : { *(.rodata1) }
+
+ /* 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 :
+ {
+ KEEP (*(SORT(.ctors.*)))
+ KEEP (*(.ctors))
+ }
+ PROVIDE (__ctors_end = .);
+ PROVIDE (__dtors_start = .);
+ .dtors :
+ {
+ KEEP (*(SORT(.dtors.*)))
+ KEEP (*(.dtors))
+ }
+ PROVIDE (__dtors_end = .);
+
+ /* Adjust the address for the data segment. Avoid mixing code and
+ data within same 128-byte chunk. */
+ . = ALIGN(128);
+
+ .data :
+ {
+ *(.data .data.* .gnu.linkonce.d.*)
+ SORT(CONSTRUCTORS)
+ }
+ .data1 : { *(.data1) }
+ _edata = .;
+ PROVIDE (edata = .);
+ __bss_start = .;
+ .bss :
+ {
+ *(.dynbss)
+ *(.bss .bss.* .gnu.linkonce.b.*)
+ *(COMMON)
+ /* 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(32 / 8);
+ _end = .;
+ PROVIDE (end = .);
+
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+ /DISCARD/ : { *(.note.GNU-stack) }
+}
--- /dev/null
+#ident "$Id$"
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2001-2003 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.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * conio.c
+ *
+ * Output to the screen
+ */
+
+#include <stdarg.h>
+#include "mystuff.h"
+
+int putchar(int ch)
+{
+ asm("movb $0x02,%%ah ; int $0x21" : : "b" (ch));
+ return ch;
+}
+
+/* Note: doesn't put '\n' like the stdc version does */
+int puts(const char *s)
+{
+ int count = 0;
+
+ while ( *s ) {
+ putchar(*s);
+ count++;
+ s++;
+ }
+
+ return count;
+}
--- /dev/null
+ .code16
+
+ .section ".init","ax"
+ .globl _start
+ .type _start,@function
+_start:
+ # Clear the .bss
+ cld
+ xorl %eax,%eax
+ movw $__bss_start,%di
+ movw $_end+3,%cx
+ subw %di,%cx
+ shrw $2,%cx
+ rep ; stosl
+
+ # Compute argc and argv
+ movzbw 0x80,%cx
+ movl $0x81,%esi
+ movw $argv,%di
+
+ xorl %eax,%eax # Dummy argv[0]
+ stosl
+
+1:
+ # Skip any space or control character
+ jcxz 2f
+ lodsb
+ decw %cx
+ cmp $0x20,%al
+ ja 3f
+5:
+ movb $0,-1(%si)
+ jmp 1b
+
+3:
+ movl %esi,%eax
+ stosl
+4:
+ # Skip this word
+ jcxz 2f
+ lodsb
+ decw %cx
+ cmp $0x20,%al
+ ja 4b
+ jmp 5b
+2:
+ xorl %eax,%eax # Final null argv entry
+ stosl
+
+ # Initialize malloc
+ calll __init_memory_arena
+
+ # Now call main...
+ pushl $argv # Push argv
+ pushl %eax # Dummy argc (we dont look at it)
+ calll main
+
+ pushl %eax
+
+ .globl exit
+exit:
+ popl %eax
+ movb $0x4c,%ah # Terminate program
+ int $0x21
+
+ .section ".bss","aw"
+argv: .space 4*128
--- /dev/null
+#ifndef ERRNO_H
+#define ERRNO_H
+
+int errno;
+void perror(const char *);
+
+#endif /* ERRNO_H */
--- /dev/null
+/*
+ * free.c
+ *
+ * Very simple linked-list based malloc()/free().
+ */
+
+#include <stdlib.h>
+#include "malloc.h"
+
+static struct free_arena_header *
+__free_block(struct free_arena_header *ah)
+{
+ struct free_arena_header *pah, *nah;
+
+ pah = ah->a.prev;
+ nah = ah->a.next;
+ if ( pah->a.type == ARENA_TYPE_FREE &&
+ (char *)pah+pah->a.size == (char *)ah ) {
+ /* Coalesce into the previous block */
+ pah->a.size += ah->a.size;
+ pah->a.next = nah;
+ nah->a.prev = pah;
+
+#ifdef DEBUG_MALLOC
+ ah->a.type = ARENA_TYPE_DEAD;
+#endif
+
+ ah = pah;
+ pah = ah->a.prev;
+ } else {
+ /* Need to add this block to the free chain */
+ ah->a.type = ARENA_TYPE_FREE;
+
+ ah->next_free = __malloc_head.next_free;
+ ah->prev_free = &__malloc_head;
+ __malloc_head.next_free = ah;
+ ah->next_free->prev_free = ah;
+ }
+
+ /* In either of the previous cases, we might be able to merge
+ with the subsequent block... */
+ if ( nah->a.type == ARENA_TYPE_FREE &&
+ (char *)ah+ah->a.size == (char *)nah ) {
+ ah->a.size += nah->a.size;
+
+ /* Remove the old block from the chains */
+ nah->next_free->prev_free = nah->prev_free;
+ nah->prev_free->next_free = nah->next_free;
+ ah->a.next = nah->a.next;
+ nah->a.next->a.prev = ah;
+
+#ifdef DEBUG_MALLOC
+ nah->a.type = ARENA_TYPE_DEAD;
+#endif
+ }
+
+ /* Return the block that contains the called block */
+ return ah;
+}
+
+void free(void *ptr)
+{
+ struct free_arena_header *ah;
+
+ if ( !ptr )
+ return;
+
+ ah = (struct free_arena_header *)
+ ((struct arena_header *)ptr - 1);
+
+#ifdef DEBUG_MALLOC
+ assert( ah->a.type == ARENA_TYPE_USED );
+#endif
+
+ __free_block(ah);
+
+ /* Here we could insert code to return memory to the system. */
+}
--- /dev/null
+/*
+ * malloc.c
+ *
+ * Very simple linked-list based malloc()/free().
+ */
+
+#include <stdlib.h>
+#include "malloc.h"
+
+struct free_arena_header __malloc_head =
+{
+ {
+ ARENA_TYPE_HEAD,
+ 0,
+ &__malloc_head,
+ &__malloc_head,
+ },
+ &__malloc_head,
+ &__malloc_head
+};
+
+/* This is extern so it can be overridden by the user application */
+const size_t __stack_size = 4096;
+
+static inline size_t sp(void)
+{
+ uint16_t sp;
+ asm volatile("movw %%sp,%0" : "=rm" (sp));
+ return sp;
+}
+
+void __init_memory_arena(void)
+{
+ extern char _end[]; /* Symbol created by the linker */
+ struct free_arena_header *fp;
+ size_t start, total_space;
+
+ start = (size_t)ARENA_ALIGN_UP(_end);
+ total_space = sp() - start;
+
+ fp = (struct free_arena_header *)start;
+ fp->a.type = ARENA_TYPE_FREE;
+ fp->a.size = total_space - __stack_size;
+
+ /* Insert into chains */
+ fp->a.next = fp->a.prev = &__malloc_head;
+ fp->next_free = fp->prev_free = &__malloc_head;
+ __malloc_head.a.next = __malloc_head.a.prev = fp;
+ __malloc_head.next_free = __malloc_head.prev_free = fp;
+}
+
+static void *__malloc_from_block(struct free_arena_header *fp, size_t size)
+{
+ size_t fsize;
+ struct free_arena_header *nfp, *na;
+
+ fsize = fp->a.size;
+
+ /* We need the 2* to account for the larger requirements of a free block */
+ if ( fsize >= size+2*sizeof(struct arena_header) ) {
+ /* Bigger block than required -- split block */
+ nfp = (struct free_arena_header *)((char *)fp + size);
+ na = fp->a.next;
+
+ nfp->a.type = ARENA_TYPE_FREE;
+ nfp->a.size = fsize-size;
+ fp->a.type = ARENA_TYPE_USED;
+ fp->a.size = size;
+
+ /* Insert into all-block chain */
+ nfp->a.prev = fp;
+ nfp->a.next = na;
+ na->a.prev = nfp;
+ fp->a.next = nfp;
+
+ /* Replace current block on free chain */
+ nfp->next_free = fp->next_free;
+ nfp->prev_free = fp->prev_free;
+ fp->next_free->prev_free = nfp;
+ fp->prev_free->next_free = nfp;
+ } else {
+ /* Allocate the whole block */
+ fp->a.type = ARENA_TYPE_USED;
+
+ /* Remove from free chain */
+ fp->next_free->prev_free = fp->prev_free;
+ fp->prev_free->next_free = fp->next_free;
+ }
+
+ return (void *)(&fp->a + 1);
+}
+
+void *malloc(size_t size)
+{
+ struct free_arena_header *fp;
+
+ if ( size == 0 )
+ return NULL;
+
+ /* Add the obligatory arena header, and round up */
+ size = (size+2*sizeof(struct arena_header)-1) & ARENA_SIZE_MASK;
+
+ for ( fp = __malloc_head.next_free ; fp->a.type != ARENA_TYPE_HEAD ;
+ fp = fp->next_free ) {
+ if ( fp->a.size >= size ) {
+ /* Found fit -- allocate out of this block */
+ return __malloc_from_block(fp, size);
+ }
+ }
+
+ /* Nothing found... need to request a block from the kernel */
+ return NULL; /* No kernel to get stuff from */
+}
--- /dev/null
+/*
+ * malloc.h
+ *
+ * Internals for the memory allocator
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+
+/*
+ * This is the minimum chunk size we will ask the kernel for; this should
+ * be a multiple of the page size on all architectures.
+ */
+#define MALLOC_CHUNK_SIZE 65536
+#define MALLOC_CHUNK_MASK (MALLOC_CHUNK_SIZE-1)
+
+/*
+ * This structure should be a power of two. This becomes the
+ * alignment unit.
+ */
+struct free_arena_header;
+
+struct arena_header {
+ size_t type;
+ size_t size; /* Also gives the location of the next entry */
+ struct free_arena_header *next, *prev;
+};
+
+#ifdef DEBUG_MALLOC
+#define ARENA_TYPE_USED 0x64e69c70
+#define ARENA_TYPE_FREE 0x012d610a
+#define ARENA_TYPE_HEAD 0x971676b5
+#define ARENA_TYPE_DEAD 0xeeeeeeee
+#else
+#define ARENA_TYPE_USED 0
+#define ARENA_TYPE_FREE 1
+#define ARENA_TYPE_HEAD 2
+#endif
+
+#define ARENA_SIZE_MASK (~(sizeof(struct arena_header)-1))
+
+#define ARENA_ALIGN_UP(p) ((char *)(((uintptr_t)(p) + ARENA_SIZE_MASK) & ARENA_SIZE_MASK))
+#define ARENA_ALIGN_DOWN(p) ((char *)((uintptr_t)(p) & ARENA_SIZE_MASK))
+
+/*
+ * This structure should be no more than twice the size of the
+ * previous structure.
+ */
+struct free_arena_header {
+ struct arena_header a;
+ struct free_arena_header *next_free, *prev_free;
+};
+
+extern struct free_arena_header __malloc_head;
--- /dev/null
+#ifndef MYSTUFF_H
+#define MYSTUFF_H
+
+#define NULL ((void *)0)
+
+unsigned int skip_atou(const char **s);
+unsigned int atou(const char *s);
+
+static inline int
+isdigit(int ch)
+{
+ return (ch >= '0') && (ch <= '9');
+}
+
+#endif /* MYSTUFF_H */
--- /dev/null
+#include <stdio.h>
+#include <errno.h>
+
+void perror(const char *msg)
+{
+ printf("%s: error %s\n", msg, errno);
+}
+
--- /dev/null
+/*
+ * Oh, it's a waste of space, but oh-so-yummy for debugging. It's just
+ * initialization code anyway, so it doesn't take up space when we're
+ * actually running. This version of printf() does not include 64-bit
+ * support. "Live with it."
+ *
+ * Most of this code was shamelessly snarfed from the Linux kernel, then
+ * modified.
+ *
+ * FIX THIS: Replace printf() implementation with BSD/MIT-licensed one
+ * from klibc
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include "mystuff.h"
+
+static int strnlen(const char *s, int maxlen)
+{
+ const char *es = s;
+ while ( *es && maxlen ) {
+ es++; maxlen--;
+ }
+
+ return (es-s);
+}
+
+#define ZEROPAD 1 /* pad with zero */
+#define SIGN 2 /* unsigned/signed long */
+#define PLUS 4 /* show plus */
+#define SPACE 8 /* space if plus */
+#define LEFT 16 /* left justified */
+#define SPECIAL 32 /* 0x */
+#define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */
+
+#define do_div(n,base) ({ \
+int __res; \
+__res = ((unsigned long) n) % (unsigned) base; \
+n = ((unsigned long) n) / (unsigned) base; \
+__res; })
+
+static char * number(char * str, long num, int base, int size, int precision
+ ,int type)
+{
+ char c,sign,tmp[66];
+ const char *digits="0123456789abcdefghijklmnopqrstuvwxyz";
+ int i;
+
+ if (type & LARGE)
+ digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ if (type & LEFT)
+ type &= ~ZEROPAD;
+ if (base < 2 || base > 36)
+ return 0;
+ c = (type & ZEROPAD) ? '0' : ' ';
+ sign = 0;
+ if (type & SIGN) {
+ if (num < 0) {
+ sign = '-';
+ num = -num;
+ size--;
+ } else if (type & PLUS) {
+ sign = '+';
+ size--;
+ } else if (type & SPACE) {
+ sign = ' ';
+ size--;
+ }
+ }
+ if (type & SPECIAL) {
+ if (base == 16)
+ size -= 2;
+ else if (base == 8)
+ size--;
+ }
+ i = 0;
+ if (num == 0)
+ tmp[i++]='0';
+ else while (num != 0)
+ tmp[i++] = digits[do_div(num,base)];
+ if (i > precision)
+ precision = i;
+ size -= precision;
+ if (!(type&(ZEROPAD+LEFT)))
+ while(size-->0)
+ *str++ = ' ';
+ if (sign)
+ *str++ = sign;
+ if (type & SPECIAL) {
+ if (base==8)
+ *str++ = '0';
+ else if (base==16) {
+ *str++ = '0';
+ *str++ = digits[33];
+ }
+ }
+ if (!(type & LEFT))
+ while (size-- > 0)
+ *str++ = c;
+ while (i < precision--)
+ *str++ = '0';
+ while (i-- > 0)
+ *str++ = tmp[i];
+ while (size-- > 0)
+ *str++ = ' ';
+ return str;
+}
+
+/* Forward decl. needed for IP address printing stuff... */
+int sprintf(char * buf, const char *fmt, ...);
+
+int vsprintf(char *buf, const char *fmt, va_list args)
+{
+ int len;
+ unsigned long num;
+ int i, base;
+ char * str;
+ const char *s;
+
+ int flags; /* flags to number() */
+
+ int field_width; /* width of output field */
+ int precision; /* min. # of digits for integers; max
+ number of chars for from string */
+ int qualifier; /* 'h', 'l', or 'L' for integer fields */
+
+ for (str=buf ; *fmt ; ++fmt) {
+ if (*fmt != '%') {
+ *str++ = *fmt;
+ continue;
+ }
+
+ /* process flags */
+ flags = 0;
+ repeat:
+ ++fmt; /* this also skips first '%' */
+ switch (*fmt) {
+ case '-': flags |= LEFT; goto repeat;
+ case '+': flags |= PLUS; goto repeat;
+ case ' ': flags |= SPACE; goto repeat;
+ case '#': flags |= SPECIAL; goto repeat;
+ case '0': flags |= ZEROPAD; goto repeat;
+ }
+
+ /* get field width */
+ field_width = -1;
+ if (isdigit(*fmt))
+ field_width = skip_atou(&fmt);
+ else if (*fmt == '*') {
+ ++fmt;
+ /* it's the next argument */
+ field_width = va_arg(args, int);
+ if (field_width < 0) {
+ field_width = -field_width;
+ flags |= LEFT;
+ }
+ }
+
+ /* get the precision */
+ precision = -1;
+ if (*fmt == '.') {
+ ++fmt;
+ if (isdigit(*fmt))
+ precision = skip_atou(&fmt);
+ else if (*fmt == '*') {
+ ++fmt;
+ /* it's the next argument */
+ precision = va_arg(args, int);
+ }
+ if (precision < 0)
+ precision = 0;
+ }
+
+ /* get the conversion qualifier */
+ qualifier = -1;
+ if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {
+ qualifier = *fmt;
+ ++fmt;
+ }
+
+ /* default base */
+ base = 10;
+
+ switch (*fmt) {
+ case 'c':
+ if (!(flags & LEFT))
+ while (--field_width > 0)
+ *str++ = ' ';
+ *str++ = (unsigned char) va_arg(args, int);
+ while (--field_width > 0)
+ *str++ = ' ';
+ continue;
+
+ case 's':
+ s = va_arg(args, char *);
+ len = strnlen(s, precision);
+
+ if (!(flags & LEFT))
+ while (len < field_width--)
+ *str++ = ' ';
+ for (i = 0; i < len; ++i)
+ *str++ = *s++;
+ while (len < field_width--)
+ *str++ = ' ';
+ continue;
+
+ case 'p':
+ if (field_width == -1) {
+ field_width = 2*sizeof(void *);
+ flags |= ZEROPAD;
+ }
+ str = number(str,
+ (unsigned long) va_arg(args, void *), 16,
+ field_width, precision, flags);
+ continue;
+
+
+ case 'n':
+ if (qualifier == 'l') {
+ long * ip = va_arg(args, long *);
+ *ip = (str - buf);
+ } else {
+ int * ip = va_arg(args, int *);
+ *ip = (str - buf);
+ }
+ continue;
+
+ case '%':
+ *str++ = '%';
+ continue;
+
+ /* integer number formats - set up the flags and "break" */
+ case 'o':
+ base = 8;
+ break;
+
+ case 'X':
+ flags |= LARGE;
+ case 'x':
+ base = 16;
+ break;
+
+ case 'd':
+ case 'i':
+ flags |= SIGN;
+ case 'u':
+ break;
+
+ default:
+ *str++ = '%';
+ if (*fmt)
+ *str++ = *fmt;
+ else
+ --fmt;
+ continue;
+ }
+ if (qualifier == 'l')
+ num = va_arg(args, unsigned long);
+ else if (qualifier == 'h') {
+ num = (unsigned short) va_arg(args, int);
+ if (flags & SIGN)
+ num = (short) num;
+ } else if (flags & SIGN)
+ num = va_arg(args, int);
+ else
+ num = va_arg(args, unsigned int);
+ str = number(str, num, base, field_width, precision, flags);
+ }
+ *str = '\0';
+ return str-buf;
+}
+
+int sprintf(char * buf, const char *fmt, ...)
+{
+ va_list args;
+ int i;
+
+ va_start(args, fmt);
+ i=vsprintf(buf,fmt,args);
+ va_end(args);
+ return i;
+}
+
+int printf(const char *fmt, ...)
+{
+ char printf_buf[1024];
+ va_list args;
+ int printed;
+
+ va_start(args, fmt);
+ printed = vsprintf(printf_buf, fmt, args);
+ va_end(args);
+
+ puts(printf_buf);
+
+ return printed;
+}
--- /dev/null
+#include "mystuff.h"
+
+unsigned int skip_atou(const char **s)
+{
+ int i=0;
+
+ while (isdigit(**s))
+ i = i*10 + *((*s)++) - '0';
+ return i;
+}
--- /dev/null
+#ifndef STDIO_H
+#define STDIO_H
+
+#include <stdarg.h>
+#include <stdlib.h>
+
+typedef unsigned int off_t;
+
+int putchar(int);
+int puts(const char *);
+int sprintf(char * buf, const char *fmt, ...);
+int vsprintf(char *buf, const char *fmt, va_list args);
+int printf(const char *fmt, ...);
+
+#define stdin 0
+#define stdout 1
+#define stderr 2
+
+#define fprintf(x, y, ...) printf(y, ## __VA_ARGS__)
+
+#endif /* STDIO_H */
+
--- /dev/null
+#ifndef STDLIB_H
+#define STDLIB_H
+
+typedef int ssize_t;
+typedef unsigned int size_t;
+
+void __attribute__((noreturn)) exit(int);
+
+void *malloc(size_t);
+void free(void *);
+
+#endif
--- /dev/null
+/*
+ * string.h
+ */
+
+#ifndef _STRING_H
+#define _STRING_H
+
+/* Standard routines */
+static inline void *memcpy(void *__d, const void *__s, unsigned int __n)
+{
+ asm volatile("cld ; rep ; movsb"
+ : "+D" (__d), "+S" (__s), "+c" (__n));
+ return __d;
+}
+
+static inline void *memset(void *__d, int __c, unsigned int __n)
+{
+ asm volatile("cld ; rep ; stosb"
+ : "+D" (__d), "+a" (__c), "+c" (__n));
+ return __d;
+}
+
+#define strcpy(a,b) __builtin_strcpy(a,b)
+#define strlen(a) __builtin_strlen(a)
+
+/* This only returns true or false */
+static inline int memcmp(const void *__m1, const void *__m2, unsigned int __n)
+{
+ _Bool rv;
+ asm volatile("cld ; repe ; cmpsb ; setne %0"
+ : "=abd" (rv), "+D" (__m1), "+S" (__m2), "+c" (__n));
+ return rv;
+}
+
+#endif /* _STRING_H */
+++ /dev/null
-; -*- fundamental -*- (asm-mode sucks)
-; $Id$
-; -----------------------------------------------------------------------
-;
-; Copyright 1998-2004 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.
-;
-; -----------------------------------------------------------------------
-
-;
-; syslinux.asm
-;
-; DOS installer for SYSLINUX
-;
-
- absolute 0
-pspInt20: resw 1
-pspNextParagraph: resw 1
- resb 1 ; reserved
-pspDispatcher: resb 5
-pspTerminateVector: resd 1
-pspControlCVector: resd 1
-pspCritErrorVector: resd 1
- resw 11 ; reserved
-pspEnvironment: resw 1
- resw 23 ; reserved
-pspFCB_1: resb 16
-pspFCB_2: resb 16
- resd 1 ; reserved
-pspCommandLen: resb 1
-pspCommandArg: resb 127
-
- section .text
- org 0100h
-_start:
- mov ax,3000h ; Get DOS version
- int 21h
- xchg al,ah
- mov [DOSVersion],ax
- cmp ax,0314h ; DOS 3.20 minimum
- jae dosver_ok
- mov dx,msg_ancient_err
- jmp die
-
- section .bss
- alignb 2
-DOSVersion: resw 1
-
- section .text
-;
-; Scan command line for a drive letter followed by a colon
-;
-dosver_ok:
- xor cx,cx
- mov si,pspCommandArg
- mov cl,[pspCommandLen]
-
-cmdscan1: jcxz bad_usage ; End of command line?
- lodsb ; Load character
- dec cx
- cmp al,' ' ; White space
- jbe cmdscan1
- cmp al,'-'
- je scan_option
- or al,020h ; -> lower case
- cmp al,'a' ; Check for letter
- jb bad_usage
- cmp al,'z'
- ja bad_usage
- sub al,'a' ; Convert to zero-based index
- mov [DriveNo],al ; Save away drive index
-
- section .bss
-DriveNo: resb 1
-
- section .text
-;
-; Got the leading letter, now the next character must be a colon
-;
-got_letter: jcxz bad_usage
- lodsb
- dec cx
- cmp al,':'
- jne bad_usage
-;
-; Got the colon; the rest better be whitespace
-;
-got_colon: jcxz got_cmdline
- lodsb
- dec cx
- cmp al,' '
- jbe got_colon
-;
-; We end up here if the command line doesn't parse
-;
-bad_usage: mov dx,msg_unfair
- jmp die
-
- section .data
-msg_unfair: db 'Usage: syslinux [-s] <drive>:', 0Dh, 0Ah, '$'
-
- section .text
-;
-; Scan for options after a - sign. The only recognized option right now
-; is -s.
-;
-scan_option: jcxz bad_usage
- lodsb
- dec cx
- cmp al,' '
- jbe cmdscan1
- or al,20h
- cmp al,'s'
- jne bad_usage
- push si ; make_stupid doesn't save these
- push cx
- call make_stupid ; Enable stupid boot sector
- pop cx
- pop si
- jmp short scan_option
-
-;
-; Parsed the command line OK. Check that the drive parameters are acceptable
-;
- struc DPB
-dpbDrive: resb 1
-dpbUnit: resb 1
-dpbSectorSize: resw 1
-dpbClusterMask: resb 1
-dpbClusterShift: resb 1
-dpbFirstFAT: resw 1
-dpbFATCount: resb 1
-dpbRootEntries: resw 1
-dpbFirstSector: resw 1
-dpbMaxCluster: resw 1
-dpbFATSize: resw 1
-dpbDirSector: resw 1
-dpbDriverAddr: resd 1
-dpbMedia: resb 1
-dpbFirstAccess: resb 1
-dpbNextDPB: resd 1
-dpbNextFree: resw 1
-dpbFreeCnt: resw 1
- endstruc
-
-got_cmdline:
- mov dl,[DriveNo]
- inc dl ; 1-based
- mov ah,32h
- int 21h ; Get Drive Parameter Block
-
- and al,al
- jnz filesystem_error
-
- cmp word [bx+dpbSectorSize],512 ; Sector size = 512 required
- jne sectorsize_error
-
- cmp byte [bx+dpbClusterShift],5 ; Max size = 16K = 2^5 sectors
- jna drive_ok
-
-hugeclust_error:
- mov dx,msg_hugeclust_err
- jmp die
-filesystem_error:
- mov dx,msg_filesystem_err
- jmp doserr
-sectorsize_error:
- mov dx,msg_sectorsize_err
- jmp die
-
-drive_ok:
- push cs
- pop ds
-
-;
-; Writing LDLINUX.SYS
-;
- section .data
-ldlinux_sys_str:
- db 'A:\LDLINUX.SYS', 0
- section .text
-
-write_file:
- ; 0. Set the correct filename
-
- mov al,[DriveNo]
- add byte [ldlinux_sys_str],al
-
- ; 1. If the file exists, strip its attributes and delete
-
- xor cx,cx ; Clear attributes
- mov dx,ldlinux_sys_str
- mov ax,4301h ; Set file attributes
- int 21h
-
- mov dx,ldlinux_sys_str
- mov ah,41h ; Delete file
- int 21h
-
- ; 2. Create LDLINUX.SYS and write data to it
-
- mov dx,ldlinux_sys_str
- xor cx,cx ; Normal file
- mov ah,3Ch ; Create file
- int 21h
- jc .file_write_error
- mov [FileHandle],ax
-
- mov bx,ax
- mov cx,ldlinux_size
- mov dx,LDLinuxSYS
- mov ah,40h ; Write data
- int 21h
- jc .file_write_error
- cmp ax,ldlinux_size
- je .no_file_write_error
-.file_write_error:
- mov dx, msg_fwrite_err
- jmp doserr
-.no_file_write_error:
-
- mov bx,[FileHandle]
- mov ah,3Eh ; Close file
- int 21h
-
- section .bss
-FileHandle: resw 1
-
- section .text
-
- ; 3. Set the readonly flag on LDLINUX.SYS
-
- mov dx,ldlinux_sys_str
- mov cx,1 ; Read only
- mov ax,4301h ; Set attributes
- int 21h
-
-;
-; Now, if we're on a recent Windows system we need to lock the device.
-; This call should have no effect on plain DOS.
-;
-lock_drive:
- cmp word [DOSVersion], 0700h ; Win9x/NT?
- jb .plain_dos ; Plain DOS -> no locking
-
- mov ax,440Dh ; Generic IOCTL
- mov bl,[DriveNo]
- inc bl ; 1-based
- mov bh,1 ; Lock level 1
- mov cx,084Ah ; Lock logical volume
- mov dx,01h ; Allow write mappings/allow new mappings
- pusha
- int 21h
- jc .disk_lock_error_nocleanup
- popa
-
- xor dx,dx
- inc bh ; Lock level 2
- pusha
- int 21h
- jc .disk_lock_error
- popa
-
- inc bh ; Lock level 3
- pusha
- int 21h
- jnc .done
-
-.disk_lock_error:
- xor cx,cx
- mov cl,bh
- dec cx
-.lock_cleanup:
- push cx
- mov ax, 440Dh
- mov bl,[DriveNo]
- inc bl
- mov cx,086Ah
- int 21h
- pop cx
- loop .lock_cleanup
-
-.disk_lock_error_nocleanup:
- popa
- mov dx, msg_lock_err
- jmp doserr
-
-.done:
- popa
-
-.plain_dos: ; Plain DOS -> no locking
-
-;
-; Now read the old boot sector and copy the superblock.
-;
- section .data
- align 4, db 0
-DISKIO equ $
-diStartSector: dd 0 ; Absolute sector 0
-diSectors: dw 1 ; One sector
-diBuffer: dw SectorBuffer ; Buffer offset
- dw 0 ; Buffer segment
-
- section .text
-read_bootsect:
- mov ax,cs ; Set DS <- CS
- mov ds,ax
-
- cmp word [DOSVersion],0400h ; DOS 4.00 has a new interface
- jae .new
-.old:
- mov bx,SectorBuffer
- mov cx,1 ; One sector
- jmp short .common
-.new:
- mov bx,DISKIO
- mov [bx+8],ax ; Buffer segment
- mov cx,-1
-.common:
- xor dx,dx ; Absolute sector 0
- mov al,[DriveNo]
- int 25h ; DOS absolute disk read
- pop ax ; Remove flags from stack
- jc disk_read_error
-
- mov si,SectorBuffer+3 ; Offset of superblock
- mov di,BootSector+3
- mov cx,59 ; Superblock = 59 bytes
- rep movsb ; Copy the superblock
- jmp short write_bootsect
-disk_read_error:
- mov dx,msg_read_err
- jmp doserr
-
-;
-; Writing boot sector
-;
-write_bootsect:
- cmp word [DOSVersion],0400h ; DOS 4.00 has a new interface
- jae .new
-.old:
- mov bx,BootSector
- mov cx,1 ; One sector
- jmp short .common
-.new:
- mov bx,DISKIO
- mov word [bx+6],BootSector
- mov cx,-1
-.common:
- xor dx,dx ; Absolute sector 0
- mov al,[DriveNo]
- int 26h ; DOS absolute disk write
- pop ax ; Remove flags from stack
- jc disk_write_error
-
-;
-; Unlock the disk if we had to lock it
-;
-unlock_disk:
- cmp word [DOSVersion], 0700h
- jb .plain_dos
-
- mov cx, 3 ; Need to release lock 3 times
-.loop:
- push cx
- mov ax,440Dh ; Generic IOCTL
- mov bl,[DriveNo]
- inc bl ; 1-based drive number
- mov cx,086Ah ; Unlock logical drive
- int 21h
- pop cx
- loop .loop
-
-.plain_dos: ; Plain DOS -> no locking
-
-all_done: mov ax,4C00h ; Exit good status
- int 21h
-;
-; Error routine jump
-;
-disk_write_error:
- mov dx,msg_write_err
-
-doserr:
- push cs
- pop ds
- push dx ; Error message
- push ax ; Error code
- mov dx, msg_error_sp
- mov ah,09h
- int 21h
- pop ax
-
- mov cx,4
- mov bx,hexdigits
- mov si,ax
-.digit:
- rol si,1
- rol si,1
- rol si,1
- rol si,1
- mov ax,si
- and al,0Fh
- xlatb
- mov ah,02h ; Display character
- mov dl,al
- int 21h
- loop .digit
-
- mov dx,msg_colon
- mov ah,09h
- int 21h
-
- jmp short die_common
-
- section .data
-hexdigits: db '0123456789ABCDEF'
-
- section .text
-die:
- push cs
- pop ds
- push dx
- mov dx, msg_error
- mov ah,09h
- int 21h
-
-die_common:
- pop dx ; Error message
-
- mov ah,09h ; Write string
- int 21h
-
- mov ax,4C01h ; Exit error status
- int 21h
-
-;
-; Patch the code to make it "stupid"
-;
-make_stupid:
- ; Only access one sector at a time
- mov word [BootSector+0x1FC],1
- ret
-
- section .data
-msg_error_sp: db 'ERROR $'
-msg_colon: db ': $'
-msg_error: db 'ERROR: $'
-msg_ancient_err: db 'DOS version 3.20 or later required', 0Dh, 0Ah, '$'
-msg_filesystem_err: db 'Filesystem not found on disk', 0Dh, 0Ah, '$'
-msg_sectorsize_err: db 'Sector sizes other than 512 bytes not supported', 0Dh, 0Ah, '$'
-msg_hugeclust_err: db 'Clusters larger than 16K not supported', 0Dh, 0Ah, '$'
-msg_read_err: db 'Boot sector read failed', 0Dh, 0Ah, '$'
-msg_write_err: db 'Boot sector write failed', 0Dh, 0Ah, '$'
-msg_fwrite_err: db 'LDLINUX.SYS write failed', 0Dh, 0Ah, '$'
-msg_lock_err: db 'Unable to lock drive for exclusive access', 0Dh, 0Ah, '$'
-
- section .data
- align 16, db 0
-BootSector: incbin "ldlinux.bss"
-LDLinuxSYS: incbin "ldlinux.sys"
-ldlinux_size: equ $-LDLinuxSYS
-
- section .bss
- alignb 16
-SectorBuffer: resb 512
--- /dev/null
+#ident "$Id$"
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 1998-2004 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.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * syslinux.c - Linux installer program for SYSLINUX
+ *
+ * Hacked up for DOS.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "mystuff.h"
+
+#include "syslinux.h"
+#include "libfat.h"
+
+const char *program = "syslinux"; /* Name of program */
+
+void __attribute__((noreturn)) usage(void)
+{
+ fprintf(stderr, "Usage: %s [-sf] drive:\n", program);
+ exit(1);
+}
+
+void __attribute__((noreturn)) die(const char *msg)
+{
+ fprintf(stderr, "%s: %s\n", program, msg);
+ exit(1);
+}
+
+/*
+ * read/write wrapper functions
+ */
+int open(const char *filename, int mode)
+{
+ uint16_t rv;
+ uint8_t err;
+
+ rv = 0x3D00 | mode;
+ asm volatile("int $0x21 ; setc %0"
+ : "=rm" (err), "+a" (rv)
+ : "d" (filename));
+ if ( err )
+ die("cannot open ldlinux.sys");
+
+ return rv;
+}
+
+void close(int fd)
+{
+ uint16_t rv = 0x3E00;
+
+ asm volatile("int $0x21"
+ : "+a" (rv)
+ : "b" (fd));
+
+ /* The only error MS-DOS returns for close is EBADF,
+ and we really don't care... */
+}
+
+ssize_t write_file(int fd, const void *buf, size_t count)
+{
+ uint16_t rv;
+ ssize_t done = 0;
+ uint8_t err;
+
+ while ( count ) {
+ rv = 0x4000;
+ asm volatile("int $0x21 ; setc %0"
+ : "=rm" (err), "+a" (rv)
+ : "b" (fd), "c" (count), "d" (buf));
+ if ( err || rv == 0 )
+ die("file write error");
+
+ done += rv;
+ count -= rv;
+ }
+
+ return done;
+}
+
+void write_device(int drive, const void *buf, size_t nsecs, unsigned int sector)
+{
+ uint8_t err;
+
+ asm volatile("int $0x26 ; setc %0 ; popfw"
+ : "=rm" (err)
+ : "a" (drive), "b" (buf), "c" (nsecs), "d" (sector));
+
+ if ( err )
+ die("sector write error");
+}
+
+void read_device(int drive, const void *buf, size_t nsecs, unsigned int sector)
+{
+ uint8_t err;
+
+ asm volatile("int $0x25 ; setc %0 ; popfw"
+ : "=rm" (err)
+ : "a" (drive), "b" (buf), "c" (nsecs), "d" (sector));
+
+ if ( err )
+ die("sector write error");
+}
+
+void set_attributes(const char *file, int attributes)
+{
+ uint8_t err;
+ uint16_t rv = 0x4301;
+
+ asm volatile("int $0x21 ; setc %0"
+ : "=rm" (err), "+a" (rv)
+ : "c" (attributes), "d" (file));
+
+ if ( err )
+ die("set attribute error");
+}
+
+/*
+ * Version of the read_device function suitable for libfat
+ */
+int libfat_xpread(void *pp, void *buf, size_t secsize, libfat_sector_t sector)
+{
+ read_device((int)pp, buf, 1, sector);
+ return secsize;
+}
+
+int main(int argc, char *argv[])
+{
+ static unsigned char sectbuf[512];
+ int dev_fd, fd;
+ static char ldlinux_name[] = "@:LDLINUX.SYS";
+ char **argp, *opt;
+ int force = 0; /* -f (force) option */
+ struct libfat_filesystem *fs;
+ libfat_sector_t s, *secp, sectors[65]; /* 65 is maximum possible */
+ int32_t ldlinux_cluster;
+ int nsectors;
+ char *device = NULL;
+
+ (void)argc; /* Unused */
+
+ for ( argp = argv+1 ; *argp ; argp++ ) {
+ if ( **argp == '-' ) {
+ opt = *argp + 1;
+ if ( !*opt )
+ usage();
+
+ while ( *opt ) {
+ if ( *opt == 's' ) {
+ syslinux_make_stupid(); /* Use "safe, slow and stupid" code */
+ } else if ( *opt == 'f' ) {
+ force = 1; /* Force install */
+ } else {
+ usage();
+ }
+ opt++;
+ }
+ } else {
+ if ( device )
+ usage();
+ device = *argp;
+ }
+ }
+
+ if ( !device )
+ usage();
+
+ /*
+ * Figure out which drive we're talking to
+ */
+ dev_fd = device[0] & 0x1F;
+ if ( !(device[0] & 0x40) || device[1] != ':' || device[2] )
+ usage();
+
+ read_device(dev_fd, sectbuf, 1, 0);
+
+ /*
+ * Check to see that what we got was indeed an MS-DOS boot sector/superblock
+ */
+ if(!syslinux_check_bootsect(sectbuf,device)) {
+ exit(1);
+ }
+
+ ldlinux_name[0] = dev_fd | 0x40;
+
+ set_attributes(ldlinux_name, 0);
+ fd = open(ldlinux_name, 2); /* Open for read/write access */
+ write_file(fd, syslinux_ldlinux, syslinux_ldlinux_len);
+ close(fd);
+ set_attributes(ldlinux_name, 0x27); /* ARCHIVE SYSTEM HIDDEN READONLY */
+
+ /*
+ * Now, use libfat to create a block map. This probably
+ * should be changed to use ioctl(...,FIBMAP,...) since
+ * this is supposed to be a simple, privileged version
+ * of the installer.
+ */
+ fs = libfat_open(libfat_xpread, (void *)dev_fd);
+ ldlinux_cluster = libfat_searchdir(fs, 0, "LDLINUX SYS", NULL);
+ secp = sectors;
+ nsectors = 0;
+ s = libfat_clustertosector(fs, ldlinux_cluster);
+ while ( s && nsectors < 65 ) {
+ *secp++ = s;
+ nsectors++;
+ s = libfat_nextsector(fs, s);
+ }
+ libfat_close(fs);
+
+ /*
+ * Patch ldlinux.sys and the boot sector
+ */
+ syslinux_patch(sectors, nsectors);
+
+ /*
+ * Write the now-patched first sector of ldlinux.sys
+ */
+ write_device(dev_fd, syslinux_ldlinux, 1, sectors[0]);
+
+ /*
+ * To finish up, write the boot sector
+ */
+
+ /* Read the superblock again since it might have changed while mounted */
+ read_device(dev_fd, sectbuf, 1, 0);
+
+ /* Copy the syslinux code into the boot sector */
+ syslinux_make_bootsect(sectbuf);
+
+ /* Write new boot sector */
+ write_device(dev_fd, sectbuf, 1, 0);
+
+ /* Done! */
+
+ return 0;
+}
+