diff options
author | hpa <hpa> | 2003-12-10 20:43:14 +0000 |
---|---|---|
committer | hpa <hpa> | 2003-12-10 20:43:14 +0000 |
commit | 614094b18a80ba39557beda64c7b732779d0b5fb (patch) | |
tree | a9d03d0d682db2f6c20a8e63ca9a7d400443bca3 /sample | |
parent | e7d427ff062f5844b36f3d029020ab06ed71d090 (diff) | |
download | syslinux-614094b18a80ba39557beda64c7b732779d0b5fb.tar.gz |
Redistribute the library code; initial work on generic chainloader
Diffstat (limited to 'sample')
-rw-r--r-- | sample/Makefile | 4 | ||||
-rw-r--r-- | sample/atou.c | 23 | ||||
-rw-r--r-- | sample/chain.c | 269 | ||||
-rw-r--r-- | sample/conio.c | 319 | ||||
-rw-r--r-- | sample/printf.c | 305 |
5 files changed, 600 insertions, 320 deletions
diff --git a/sample/Makefile b/sample/Makefile index 900acbd6..fcb8f4c7 100644 --- a/sample/Makefile +++ b/sample/Makefile @@ -27,11 +27,11 @@ OBJCOPY = objcopy PPMTOLSS16 = ../ppmtolss16 LIB = libcom32.a -LIBOBJS = conio.o +LIBOBJS = conio.o atou.o printf.o .SUFFIXES: .lss .c .o .elf .c32 -all: syslogo.lss comecho.com hello.c32 hello2.c32 filetest.c32 c32echo.c32 fd.c32 +all: syslogo.lss comecho.com hello.c32 hello2.c32 filetest.c32 c32echo.c32 fd.c32 chain.c32 %.o: %.S $(CC) $(SFLAGS) -c -o $@ $< diff --git a/sample/atou.c b/sample/atou.c new file mode 100644 index 00000000..0ea1bfe4 --- /dev/null +++ b/sample/atou.c @@ -0,0 +1,23 @@ +static inline int +isdigit(int ch) +{ + return (ch >= '0') && (ch <= '9'); +} + +unsigned int skip_atou(const char **s) +{ + int i=0; + + while (isdigit(**s)) + i = i*10 + *((*s)++) - '0'; + return i; +} + +unsigned int atou(const char *s) +{ + unsigned int i = 0; + while (isdigit(*s)) + i = i*10 + (*s++ - '0'); + return i; +} + diff --git a/sample/chain.c b/sample/chain.c new file mode 100644 index 00000000..21a0ab88 --- /dev/null +++ b/sample/chain.c @@ -0,0 +1,269 @@ +#ident "$Id$" +/* ----------------------------------------------------------------------- * + * + * Copyright 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, + * Bostom MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * hd.c + * + * Chainload a hard disk (currently rather braindead.) + * + * Usage: hd disk# [partition] + * + * ... e.g. "hd 0 1" will boot the first partition on the first hard disk. + * + * Only partitions 1-4 supported at this time; 0 = boot MBR (default.) + */ + +#include <com32.h> +#define NULL ((void *)0) + +int printf(const char *, ...); +int puts(const char *); +unsigned int skip_atou(char * const *); + +static inline void error(const char *msg) +{ + puts(msg); +} + +static inline int isspace(char x) +{ + return (x == ' ') || (x >= '\b' && x <= '\r'); +} + +/* + * Call int 13h, but with retry on failure. Especially floppies need this. + */ +int int13_retry(const com32sys_t *inreg, com32sys_t *outreg) +{ + int retry = 6; /* Number of retries */ + com32sys_t tmpregs; + + if ( !outreg ) outreg = &tmpregs; + + while ( retry ) { + __com32.cs_intcall(0x13, inreg, outreg); + if ( (outreg->eflags.l & 1) == 0 ) + return 0; /* CF=0, OK */ + } + + return -1; /* Error */ +} + +/* + * Query disk parameters and EBIOS availability for a particular disk. + */ +struct diskinfo { + int disk; + int head; + int sect; + int ebios; +} disk_info; + +int get_disk_params(int disk) +{ + com32sys_t getparm, parm, getebios, ebios; + + memset(&getparm, 0, sizeof getparm); + memset(&getebios, 0, sizeof getebios); + memset(&disk_info, 0, sizeof disk_info); + + disk_info.disk = disk; + + if ( disk & 0x80 ) { + /* Get disk parameters -- hard drives only */ + + getparm.eax.b[1] = 0x08; + getparm.edx.b[0] = disk; + if ( int13_retry(&getparm, &parm) ) + return -1; + + disk_info.head = parm.edx.b[1]+1; + disk_info.sect = parm.ecx.b[0] & 0x3f; + + /* Get EBIOS support */ + + getebios.eax.w[0] = 0x4100; + getebios.edx.b[0] = disk; + getebios.ebx.w[0] = 0x55aa; + getebios.eflags.b[0] = 0x3; /* CF set */ + if ( !int13_retry(&getebios, &ebios) ) { + if ( ebios.ebx.w[0] == 0xaa55 && + (ebios.ecx.b[0] & 1) ) + disk_info.ebios = 1; + } + } + + return 0; +} + +/* + * Get a disk block; buf is REQUIRED TO BE IN LOW MEMORY. + */ +struct ebios_dapa { + uint16_t len; + uint16_t count; + uint16_t off; + uint16_t seg; + uint64_t lba; +} *dapa; + +int read_sector(void *buf, unsigned int lba) +{ + com32sys_t inreg; + + memset(&inreg, 0, sizeof inreg); + + if ( disk_info.ebios ) { + dapa->len = sizeof(*dapa); + dapa->count = 1; /* 1 sector */ + dapa->off = OFFS(buf); + dapa->seg = SEG(buf); + dapa->lba = lba; + + inreg.esi.w[0] = OFFS(dapa); + inreg.ds = SEG(dapa); + inreg.edx.b[0] = disk_info.disk; + inreg.eax.b[1] = 0x42; /* Extended read */ + } else { + unsigned int c, h, s, t; + + s = (lba % disk_info.sect) + 1; + t = lba / disk_info.sect; /* Track = head*cyl */ + h = t % disk_info.head; + c = t / disk_info.head; + + if ( s > 63 || h > 256 || c > 1023 ) + return -1; + + inreg.eax.w[0] = 0x0201; /* Read one sector */ + inreg.ecx.b[1] = c & 0xff; + inreg.ecx.b[0] = s + (c >> 6); + inreg.edx.b[1] = h; + inreg.edx.b[0] = disk_info.disk; + inreg.ebx.w[0] = OFFS(buf); + inreg.es = SEG(buf); + } + + return int13_retry(&inreg, NULL); +} + + +/* A DOS partition table entry */ +struct part_entry { + uint8_t active_flag; /* 0x80 if "active" */ + uint8_t start_head; + uint8_t start_sect; + uint8_t start_cyl; + uint8_t ostype; + uint8_t end_head; + uint8_t end_sect; + uint8_t end_cyl; + uint32_t start_lba; + uint32_t length; +} __attribute__((packed)); + +int __start(void) +{ + char *mbr, *boot_sector; + struct part_entry *partinfo; + char *cmdline = __com32.cs_cmdline; + int hd, drive, whichpart; + static com32sys_t inreg; /* In bss, so zeroed automatically */ + int retry; + + /* Parse command line */ + while ( isspace(*cmdline) ) + cmdline++; + + hd = 0; + if ( (cmdline[0] == 'h' || cmdline[0] == 'f') && + cmdline[1] == 'd' ) { + hd = cmdline[0] == 'h'; + cmdline += 2; + } + drive = (hd ? 0x80 : 0) | skip_atou(&cmdline); + whichpart = 0; /* Default */ + + if ( isspace(*cmdline) ) { + while ( isspace(*cmdline) ) + cmdline++; + + whichpart = skip_atou(&cmdline); + } + + if ( !(drive & 0x80) && whichpart != 0 ) { + error("Partitions not supported on floppy drives\n"); + goto bail; + } + + /* Divvy up the bounce buffer. To keep things sector- + aligned, give the EBIOS DAPA the first sector, then + the MBR next, and the rest is used for the partition- + chasing stack. */ + dapa = (struct ebios_dapa *)__com32.cs_bounce; + mbr = (char *)__com32.cs_bounce + 512; + + /* Get the MBR */ + if ( get_disk_params(drive) ) { + error("Cannot get disk parameters\n"); + goto bail; + } + + if ( read_sector(mbr, 0) ) { + error("Cannot read MBR\n"); + goto bail; + } + + if ( whichpart == 0 ) { + /* Boot the MBR */ + partinfo = NULL; + boot_sector = mbr; + } else if ( whichpart <= 4 ) { + /* Boot a primary partition */ + partinfo = &((struct part_entry *)(mbr + 0x1be))[whichpart-1]; + boot_sector = mbr+512; + } else { + /* Boot a logical partition */ + error("Logical partitions not implemented yet\n"); + goto bail; + } + + /* Do the actual chainloading */ + if ( partinfo ) { + /* Actually read the boot sector */ + if ( read_sector(boot_sector, partinfo->start_lba) ) { + error("Cannot read boot sector\n"); + goto bail; + } + + /* 0x7BE is the canonical place for the first partition entry. */ + inreg.esi.w[0] = 0x7be; + memcpy((char *)0x7be, partinfo, sizeof(*partinfo)); + } + inreg.eax.w[0] = 0x000d; /* Clean up and chain boot */ + inreg.edx.w[0] = 0; /* Should be 3 for "keeppxe" */ + inreg.edi.l = (uint32_t)boot_sector; + inreg.ecx.l = 512; /* One sector */ + inreg.ebx.b[0] = drive; /* DL = drive no */ + + __com32.cs_intcall(0x22, &inreg, NULL); + + /* If we get here, badness happened */ + error("Chainboot failed!\n"); + +bail: + return 255; +} + + + diff --git a/sample/conio.c b/sample/conio.c index 9126bc7a..24688a9a 100644 --- a/sample/conio.c +++ b/sample/conio.c @@ -15,9 +15,6 @@ * conio.c * * Output to the screen - * - * FIX THIS: Replace printf() implementation with BSD/MIT-licensed one - * from klibc */ #include <com32.h> @@ -43,6 +40,7 @@ int putchar(int ch) return ch; } +/* Note: doesn't put '\n' like the stdc version does */ int puts(const char *s) { int count = 0; @@ -55,318 +53,3 @@ int puts(const char *s) return count; } - -/* - * 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. - */ - -static inline int -isdigit(int ch) -{ - return (ch >= '0') && (ch <= '9'); -} - -static int skip_atoi(const char **s) -{ - int i=0; - - while (isdigit(**s)) - i = i*10 + *((*s)++) - '0'; - return i; -} - -unsigned int atou(const char *s) -{ - unsigned int i = 0; - while (isdigit(*s)) - i = i*10 + (*s++ - '0'); - return i; -} - -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_atoi(&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_atoi(&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/sample/printf.c b/sample/printf.c new file mode 100644 index 00000000..68717f3d --- /dev/null +++ b/sample/printf.c @@ -0,0 +1,305 @@ +/* + * 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> + +static inline int +isdigit(int ch) +{ + return (ch >= '0') && (ch <= '9'); +} + +unsigned int skip_atou(const char **s); +unsigned int atou(const char *s); + +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; +} + |