From e5b229c776a5de62121a18a981119484d367ad90 Mon Sep 17 00:00:00 2001 From: hpa Date: Wed, 15 Dec 2004 12:34:42 +0000 Subject: [PATCH] Convert the DOS installer to C like everything else. --- dos/Makefile | 46 ++++-- dos/atou.c | 10 ++ dos/code16.h | 2 + dos/com16.ld | 127 +++++++++++++++ dos/conio.c | 41 +++++ dos/crt0.S | 67 ++++++++ dos/errno.h | 7 + dos/free.c | 78 +++++++++ dos/malloc.c | 113 +++++++++++++ dos/malloc.h | 54 +++++++ dos/mystuff.h | 15 ++ dos/perror.c | 8 + dos/printf.c | 297 +++++++++++++++++++++++++++++++++++ dos/skipatou.c | 10 ++ dos/stdio.h | 22 +++ dos/stdlib.h | 12 ++ dos/string.h | 35 +++++ dos/syslinux.asm | 471 ------------------------------------------------------- dos/syslinux.c | 250 +++++++++++++++++++++++++++++ 19 files changed, 1180 insertions(+), 485 deletions(-) create mode 100644 dos/atou.c create mode 100644 dos/code16.h create mode 100644 dos/com16.ld create mode 100644 dos/conio.c create mode 100644 dos/crt0.S create mode 100644 dos/errno.h create mode 100644 dos/free.c create mode 100644 dos/malloc.c create mode 100644 dos/malloc.h create mode 100644 dos/mystuff.h create mode 100644 dos/perror.c create mode 100644 dos/printf.c create mode 100644 dos/skipatou.c create mode 100644 dos/stdio.h create mode 100644 dos/stdlib.h create mode 100644 dos/string.h delete mode 100644 dos/syslinux.asm create mode 100644 dos/syslinux.c diff --git a/dos/Makefile b/dos/Makefile index 5319daf..947c1c8 100644 --- a/dos/Makefile +++ b/dos/Makefile @@ -1,21 +1,24 @@ -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 @@ -23,9 +26,24 @@ clean: tidy 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 $< diff --git a/dos/atou.c b/dos/atou.c new file mode 100644 index 0000000..6d65106 --- /dev/null +++ b/dos/atou.c @@ -0,0 +1,10 @@ +#include "mystuff.h" + +unsigned int atou(const char *s) +{ + unsigned int i = 0; + while (isdigit(*s)) + i = i*10 + (*s++ - '0'); + return i; +} + diff --git a/dos/code16.h b/dos/code16.h new file mode 100644 index 0000000..ceb1600 --- /dev/null +++ b/dos/code16.h @@ -0,0 +1,2 @@ +/* Must be included first of all */ +__asm__ (".code16gcc"); diff --git a/dos/com16.ld b/dos/com16.ld new file mode 100644 index 0000000..08a1e95 --- /dev/null +++ b/dos/com16.ld @@ -0,0 +1,127 @@ +/* + * 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) } +} diff --git a/dos/conio.c b/dos/conio.c new file mode 100644 index 0000000..460858f --- /dev/null +++ b/dos/conio.c @@ -0,0 +1,41 @@ +#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 +#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; +} diff --git a/dos/crt0.S b/dos/crt0.S new file mode 100644 index 0000000..95a4dc5 --- /dev/null +++ b/dos/crt0.S @@ -0,0 +1,67 @@ + .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 diff --git a/dos/errno.h b/dos/errno.h new file mode 100644 index 0000000..30aa046 --- /dev/null +++ b/dos/errno.h @@ -0,0 +1,7 @@ +#ifndef ERRNO_H +#define ERRNO_H + +int errno; +void perror(const char *); + +#endif /* ERRNO_H */ diff --git a/dos/free.c b/dos/free.c new file mode 100644 index 0000000..aa17080 --- /dev/null +++ b/dos/free.c @@ -0,0 +1,78 @@ +/* + * free.c + * + * Very simple linked-list based malloc()/free(). + */ + +#include +#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. */ +} diff --git a/dos/malloc.c b/dos/malloc.c new file mode 100644 index 0000000..93a51bb --- /dev/null +++ b/dos/malloc.c @@ -0,0 +1,113 @@ +/* + * malloc.c + * + * Very simple linked-list based malloc()/free(). + */ + +#include +#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 */ +} diff --git a/dos/malloc.h b/dos/malloc.h new file mode 100644 index 0000000..57b7652 --- /dev/null +++ b/dos/malloc.h @@ -0,0 +1,54 @@ +/* + * malloc.h + * + * Internals for the memory allocator + */ + +#include +#include + +/* + * 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; diff --git a/dos/mystuff.h b/dos/mystuff.h new file mode 100644 index 0000000..83f072f --- /dev/null +++ b/dos/mystuff.h @@ -0,0 +1,15 @@ +#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 */ diff --git a/dos/perror.c b/dos/perror.c new file mode 100644 index 0000000..ff8d5f2 --- /dev/null +++ b/dos/perror.c @@ -0,0 +1,8 @@ +#include +#include + +void perror(const char *msg) +{ + printf("%s: error %s\n", msg, errno); +} + diff --git a/dos/printf.c b/dos/printf.c new file mode 100644 index 0000000..efd63cf --- /dev/null +++ b/dos/printf.c @@ -0,0 +1,297 @@ +/* + * 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 +#include +#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; +} diff --git a/dos/skipatou.c b/dos/skipatou.c new file mode 100644 index 0000000..4c663f6 --- /dev/null +++ b/dos/skipatou.c @@ -0,0 +1,10 @@ +#include "mystuff.h" + +unsigned int skip_atou(const char **s) +{ + int i=0; + + while (isdigit(**s)) + i = i*10 + *((*s)++) - '0'; + return i; +} diff --git a/dos/stdio.h b/dos/stdio.h new file mode 100644 index 0000000..ffc9b3f --- /dev/null +++ b/dos/stdio.h @@ -0,0 +1,22 @@ +#ifndef STDIO_H +#define STDIO_H + +#include +#include + +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 */ + diff --git a/dos/stdlib.h b/dos/stdlib.h new file mode 100644 index 0000000..45b76a5 --- /dev/null +++ b/dos/stdlib.h @@ -0,0 +1,12 @@ +#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 diff --git a/dos/string.h b/dos/string.h new file mode 100644 index 0000000..cee8e2e --- /dev/null +++ b/dos/string.h @@ -0,0 +1,35 @@ +/* + * 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 */ diff --git a/dos/syslinux.asm b/dos/syslinux.asm deleted file mode 100644 index bb2df31..0000000 --- a/dos/syslinux.asm +++ /dev/null @@ -1,471 +0,0 @@ -; -*- 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] :', 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 diff --git a/dos/syslinux.c b/dos/syslinux.c new file mode 100644 index 0000000..051912b --- /dev/null +++ b/dos/syslinux.c @@ -0,0 +1,250 @@ +#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 +#include +#include +#include +#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; +} + -- 2.7.4