From df1682350b49e77d2bf43276353cc4141f6b8efb Mon Sep 17 00:00:00 2001 From: John Wang Date: Wed, 31 Jan 2024 21:39:12 +0800 Subject: [PATCH] early printk --- Makefile | 5 +- arch/Makefile | 2 +- arch/x86/kernel/early_printk.c | 34 ++- arch/x86/kernel/head64.c | 9 +- include/asm/div64.h | 14 + include/asm/posix_types_64.h | 2 + include/os/ctype.h | 12 + include/os/kernel.h | 3 + include/os/string.h | 3 + include/os/types.h | 10 + lib/string.c | 13 +- lib/vsprintf.c | 476 ++++++++++++++++++++++++++++++++- 12 files changed, 565 insertions(+), 18 deletions(-) create mode 100644 include/asm/div64.h create mode 100644 include/os/ctype.h diff --git a/Makefile b/Makefile index 881a652..2a0c7eb 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,7 @@ export TARGET_ISO=HelloOS.iso export CONFIG_x86_64=y export CC=gcc export AS=as +export LD=ld export HIDE=@ export DEBUG=-g @@ -18,10 +19,10 @@ cleanall: $(HIDE)make -C arch cleanall run: - qemu-system-x86_64 -m 1G -cdrom arch/$(TARGET_ISO) -nographic + qemu-system-x86_64 -m 1G -cdrom arch/$(TARGET_ISO) -curses debug: - qemu-system-x86_64 -m 1G -cdrom arch/$(TARGET_ISO) -s -S -nographic + qemu-system-x86_64 -m 1G -cdrom arch/$(TARGET_ISO) -s -S -curses kill: @ps aux | grep [q]emu | awk '{print $$2}' | xargs kill diff --git a/arch/Makefile b/arch/Makefile index 3e57884..d7540a4 100644 --- a/arch/Makefile +++ b/arch/Makefile @@ -22,7 +22,7 @@ $(TARGET_ISO): clean $(kernel_target) $(HIDE)rm -rf build_dir $(kernel_target): compile - $(HIDE)ld -b elf64-x86-64 -z muldefs -o $(kernel_target) -Map=./kernel.map -T $(lds) $(shell find $(SRCS) -name "*.o") + $(HIDE)$(LD) -b elf64-x86-64 -z muldefs -o $(kernel_target) -Map=./kernel.map -T $(lds) $(shell find $(SRCS) -name "*.o") compile: $(HIDE)for dir in $(SRCS); do \ diff --git a/arch/x86/kernel/early_printk.c b/arch/x86/kernel/early_printk.c index 07ec51f..968ca5f 100644 --- a/arch/x86/kernel/early_printk.c +++ b/arch/x86/kernel/early_printk.c @@ -7,13 +7,37 @@ #include #define VGABASE (PAGE_OFFSET + 0xb8000) +static long max_ypos = 25, max_xpos = 80; +static long current_ypos = 25, current_xpos; void early_vga_write(struct console *con, const char *str, unsigned n) { - unsigned short *dst = (unsigned short*)VGABASE; - for (int i = 0; i < 1000; i++) { - writew((0x7 << 8) | (unsigned short) 'h', dst); - dst++; - } + char c; + int i, k, j; + + while ((c = *str++) != '\0' && n-- > 0) { + // 向上滚动一行 + if (current_ypos >= max_ypos) { + for (k = 1, j = 0; k < max_ypos; k++, j++) { + for (i = 0; i < max_xpos; i++) { + writew(readw((void*)(VGABASE+2*(max_xpos*k+i))), (void*)(VGABASE + 2*(max_xpos*j + i))); + } + } + // 将最后一行设置空 + for (i = 0; i < max_xpos; i++) + writew(0x720, (void*)(VGABASE + 2*(max_xpos*j + i))); + current_ypos = max_ypos-1; + } + if (c == '\n') { + current_xpos = 0; + current_ypos++; + } else if (c != '\r') { + writew(((0x7 << 8) | (unsigned short) c), (void*)(VGABASE + 2*(max_xpos*current_ypos + current_xpos++))); + if (current_xpos >= max_xpos) { + current_xpos = 0; + current_ypos++; + } + } + } } static struct console early_vga_console = { diff --git a/arch/x86/kernel/head64.c b/arch/x86/kernel/head64.c index 02500f1..ddf7ced 100644 --- a/arch/x86/kernel/head64.c +++ b/arch/x86/kernel/head64.c @@ -5,13 +5,16 @@ #include void clear_bss() { - // memset(__bss_start, 0, - // (unsigned long) __bss_stop - (unsigned long) __bss_start); + memset(__bss_start, 0, + (unsigned long) __bss_stop - (unsigned long) __bss_start); } void x86_64_start_kernel() { - early_printk("hello world"); + early_printk("x86_64_start_kernel running..."); + + // bss 段清零 + clear_bss(); start_kernel(); } \ No newline at end of file diff --git a/include/asm/div64.h b/include/asm/div64.h new file mode 100644 index 0000000..1a77fd9 --- /dev/null +++ b/include/asm/div64.h @@ -0,0 +1,14 @@ +#ifndef _ASM_DIV64_H +#define _ASM_DIV64_H + +#include + +# define do_div(n,base) ({ \ + uint32_t __base = (base); \ + uint32_t __rem; \ + __rem = ((uint64_t)(n)) % __base; \ + (n) = ((uint64_t)(n)) / __base; \ + __rem; \ + }) + +#endif \ No newline at end of file diff --git a/include/asm/posix_types_64.h b/include/asm/posix_types_64.h index 7912d5c..1496968 100644 --- a/include/asm/posix_types_64.h +++ b/include/asm/posix_types_64.h @@ -4,4 +4,6 @@ typedef unsigned long __kernel_size_t; typedef long __kernel_ssize_t; +typedef long __kernel_ptrdiff_t; + #endif \ No newline at end of file diff --git a/include/os/ctype.h b/include/os/ctype.h new file mode 100644 index 0000000..8571a70 --- /dev/null +++ b/include/os/ctype.h @@ -0,0 +1,12 @@ +#ifndef _OS_CTYPE_H +#define _OS_CTYPE_H + +static inline int isdigit(char c) { + return c >= '0' && c <= '9'; +} + +static inline int isalnum(char c) { + return c >= 'a' && c <= 'z' || (c >= 'A' && c <= 'Z'); +} + +#endif \ No newline at end of file diff --git a/include/os/kernel.h b/include/os/kernel.h index 4c72587..75f4d10 100644 --- a/include/os/kernel.h +++ b/include/os/kernel.h @@ -10,4 +10,7 @@ extern void __attribute__((format(printf, 1, 2))) extern int vscnprintf(char *buf, size_t size, const char *fmt, va_list args) __attribute__ ((format (printf, 3, 0))); +extern int vsnprintf(char *buf, size_t size, const char *fmt, va_list args) + __attribute__ ((format (printf, 3, 0))); + #endif \ No newline at end of file diff --git a/include/os/string.h b/include/os/string.h index f7363d4..4da1697 100644 --- a/include/os/string.h +++ b/include/os/string.h @@ -6,5 +6,8 @@ #ifndef __HAVE_ARCH_MEMSET extern void *memset(void *,int,__kernel_size_t); #endif +#ifndef __HAVE_ARCH_STRNLEN +extern __kernel_size_t strnlen(const char *,__kernel_size_t); +#endif #endif \ No newline at end of file diff --git a/include/os/types.h b/include/os/types.h index 9c135ce..ae19c54 100644 --- a/include/os/types.h +++ b/include/os/types.h @@ -13,4 +13,14 @@ typedef __kernel_size_t size_t; typedef __kernel_ssize_t ssize_t; #endif +#ifndef _PTRDIFF_T +#define _PTRDIFF_T +typedef __kernel_ptrdiff_t ptrdiff_t; +#endif + +typedef __u8 uint8_t; +typedef __u16 uint16_t; +typedef __u32 uint32_t; +typedef __u64 uint64_t; + #endif \ No newline at end of file diff --git a/lib/string.c b/lib/string.c index df364e9..d632864 100644 --- a/lib/string.c +++ b/lib/string.c @@ -1,12 +1,21 @@ #include #ifndef __HAVE_ARCH_MEMSET -void *memset(void *s, int c, size_t count) -{ +void *memset(void *s, int c, size_t count) { char *xs = s; while (count--) *xs++ = c; return s; } +#endif + +#ifndef __HAVE_ARCH_STRNLEN +__kernel_size_t strnlen(const char *s, __kernel_size_t count) { + const char *sc; + + for (sc = s; count-- && *sc != '\0'; ++sc) + /* nothing */; + return sc - s; +} #endif \ No newline at end of file diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 4dadbcd..25a1308 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -1,10 +1,476 @@ #include + #include +#include +#include +#include + +#include +#include + +static int skip_atoi(const char **s) { + int i=0; + + while (isdigit(**s)) + i = i*10 + *((*s)++) - '0'; + return i; +} int vscnprintf(char *buf, size_t size, const char *fmt, va_list args) { - int i = 0; - for (; fmt[i]; i++) { - buf[i] = fmt[i]; - } - return i; + int i; + + i = vsnprintf(buf,size,fmt,args); + return (i >= size) ? (size - 1) : i; +} + +static char* put_dec_trunc(char *buf, unsigned q) +{ + unsigned d3, d2, d1, d0; + d1 = (q>>4) & 0xf; + d2 = (q>>8) & 0xf; + d3 = (q>>12); + + d0 = 6*(d3 + d2 + d1) + (q & 0xf); + q = (d0 * 0xcd) >> 11; + d0 = d0 - 10*q; + *buf++ = d0 + '0'; /* least significant digit */ + d1 = q + 9*d3 + 5*d2 + d1; + if (d1 != 0) { + q = (d1 * 0xcd) >> 11; + d1 = d1 - 10*q; + *buf++ = d1 + '0'; /* next digit */ + + d2 = q + 2*d2; + if ((d2 != 0) || (d3 != 0)) { + q = (d2 * 0xd) >> 7; + d2 = d2 - 10*q; + *buf++ = d2 + '0'; /* next digit */ + + d3 = q + 4*d3; + if (d3 != 0) { + q = (d3 * 0xcd) >> 11; + d3 = d3 - 10*q; + *buf++ = d3 + '0'; /* next digit */ + if (q != 0) + *buf++ = q + '0'; /* most sign. digit */ + } + } + } + return buf; +} + +static char* put_dec_full(char *buf, unsigned q) +{ + /* BTW, if q is in [0,9999], 8-bit ints will be enough, */ + /* but anyway, gcc produces better code with full-sized ints */ + unsigned d3, d2, d1, d0; + d1 = (q>>4) & 0xf; + d2 = (q>>8) & 0xf; + d3 = (q>>12); + + /* Possible ways to approx. divide by 10 */ + /* gcc -O2 replaces multiply with shifts and adds */ + // (x * 0xcd) >> 11: 11001101 - shorter code than * 0x67 (on i386) + // (x * 0x67) >> 10: 1100111 + // (x * 0x34) >> 9: 110100 - same + // (x * 0x1a) >> 8: 11010 - same + // (x * 0x0d) >> 7: 1101 - same, shortest code (on i386) + + d0 = 6*(d3 + d2 + d1) + (q & 0xf); + q = (d0 * 0xcd) >> 11; + d0 = d0 - 10*q; + *buf++ = d0 + '0'; + d1 = q + 9*d3 + 5*d2 + d1; + q = (d1 * 0xcd) >> 11; + d1 = d1 - 10*q; + *buf++ = d1 + '0'; + + d2 = q + 2*d2; + q = (d2 * 0xd) >> 7; + d2 = d2 - 10*q; + *buf++ = d2 + '0'; + + d3 = q + 4*d3; + q = (d3 * 0xcd) >> 11; /* - shorter code */ + /* q = (d3 * 0x67) >> 10; - would also work */ + d3 = d3 - 10*q; + *buf++ = d3 + '0'; + *buf++ = q + '0'; + return buf; +} +/* No inlining helps gcc to use registers better */ +static char* put_dec(char *buf, unsigned long long num) { + while (1) { + unsigned rem; + if (num < 100000) + return put_dec_trunc(buf, num); + rem = do_div(num, 100000); + buf = put_dec_full(buf, rem); + } +} + +#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 SMALL 32 /* Must be 32 == 0x20 */ +#define SPECIAL 64 /* 0x */ + +static char *number(char *buf, char *end, unsigned long long num, int base, int size, int precision, int type) +{ + /* we are called with base 8, 10 or 16, only, thus don't need "G..." */ + static const char digits[16] = "0123456789ABCDEF"; /* "GHIJKLMNOPQRSTUVWXYZ"; */ + + char tmp[66]; + char sign; + char locase; + int need_pfx = ((type & SPECIAL) && base != 10); + int i; + + /* locase = 0 or 0x20. ORing digits or letters with 'locase' + * produces same digits or (maybe lowercased) letters */ + locase = (type & SMALL); + if (type & LEFT) + type &= ~ZEROPAD; + sign = 0; + if (type & SIGN) { + if ((signed long long) num < 0) { + sign = '-'; + num = - (signed long long) num; + size--; + } else if (type & PLUS) { + sign = '+'; + size--; + } else if (type & SPACE) { + sign = ' '; + size--; + } + } + if (need_pfx) { + size--; + if (base == 16) + size--; + } + + /* generate full string in tmp[], in reverse order */ + i = 0; + if (num == 0) + tmp[i++] = '0'; + /* Generic code, for any base: + else do { + tmp[i++] = (digits[do_div(num,base)] | locase); + } while (num != 0); + */ + else if (base != 10) { /* 8 or 16 */ + int mask = base - 1; + int shift = 3; + if (base == 16) shift = 4; + do { + tmp[i++] = (digits[((unsigned char)num) & mask] | locase); + num >>= shift; + } while (num); + } else { /* base 10 */ + i = put_dec(tmp, num) - tmp; + } + + /* printing 100 using %2d gives "100", not "00" */ + if (i > precision) + precision = i; + /* leading space padding */ + size -= precision; + if (!(type & (ZEROPAD+LEFT))) { + while(--size >= 0) { + if (buf < end) + *buf = ' '; + ++buf; + } + } + /* sign */ + if (sign) { + if (buf < end) + *buf = sign; + ++buf; + } + /* "0x" / "0" prefix */ + if (need_pfx) { + if (buf < end) + *buf = '0'; + ++buf; + if (base == 16) { + if (buf < end) + *buf = ('X' | locase); + ++buf; + } + } + /* zero or space padding */ + if (!(type & LEFT)) { + char c = (type & ZEROPAD) ? '0' : ' '; + while (--size >= 0) { + if (buf < end) + *buf = c; + ++buf; + } + } + /* hmm even more zero padding? */ + while (i <= --precision) { + if (buf < end) + *buf = '0'; + ++buf; + } + /* actual digits of result */ + while (--i >= 0) { + if (buf < end) + *buf = tmp[i]; + ++buf; + } + /* trailing space padding */ + while (--size >= 0) { + if (buf < end) + *buf = ' '; + ++buf; + } + return buf; +} + +static char *string(char *buf, char *end, char *s, int field_width, int precision, int flags) { + int len, i; + + if ((unsigned long)s < PAGE_SIZE) + s = ""; + + len = strnlen(s, precision); + + if (!(flags & LEFT)) { + while (len < field_width--) { + if (buf < end) + *buf = ' '; + ++buf; + } + } + for (i = 0; i < len; ++i) { + if (buf < end) + *buf = *s; + ++buf; ++s; + } + while (len < field_width--) { + if (buf < end) + *buf = ' '; + ++buf; + } + return buf; } + +static char *pointer(const char *fmt, char *buf, char *end, void *ptr, int field_width, int precision, int flags) +{ + flags |= SMALL; + if (field_width == -1) { + field_width = 2*sizeof(void *); + flags |= ZEROPAD; + } + return number(buf, end, (unsigned long) ptr, 16, field_width, precision, flags); +} + +int vsnprintf(char *buf, size_t size, const char *fmt, va_list args) { + unsigned long long num; + int base; + char *str, *end, c; + + 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 */ + /* 'z' support added 23/7/1999 S.H. */ + /* 'z' changed to 'Z' --davidm 1/25/99 */ + /* 't' added for ptrdiff_t */ + + str = buf; + end = buf + size; + + /* Make sure end is always >= buf */ + if (end < buf) { + end = ((void *)-1); + size = end - buf; + } + + for (; *fmt ; ++fmt) { + if (*fmt != '%') { + if (str < end) + *str = *fmt; + ++str; + 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' || + *fmt =='Z' || *fmt == 'z' || *fmt == 't') { + qualifier = *fmt; + ++fmt; + if (qualifier == 'l' && *fmt == 'l') { + qualifier = 'L'; + ++fmt; + } + } + + /* default base */ + base = 10; + + switch (*fmt) { + case 'c': + if (!(flags & LEFT)) { + while (--field_width > 0) { + if (str < end) + *str = ' '; + ++str; + } + } + c = (unsigned char) va_arg(args, int); + if (str < end) + *str = c; + ++str; + while (--field_width > 0) { + if (str < end) + *str = ' '; + ++str; + } + continue; + + case 's': + str = string(str, end, va_arg(args, char *), field_width, precision, flags); + continue; + + case 'p': + str = pointer(fmt+1, str, end, + va_arg(args, void *), + field_width, precision, flags); + /* Skip all alphanumeric pointer suffixes */ + while (isalnum(fmt[1])) + fmt++; + continue; + + case 'n': + /* FIXME: + * What does C99 say about the overflow case here? */ + if (qualifier == 'l') { + long * ip = va_arg(args, long *); + *ip = (str - buf); + } else if (qualifier == 'Z' || qualifier == 'z') { + size_t * ip = va_arg(args, size_t *); + *ip = (str - buf); + } else { + int * ip = va_arg(args, int *); + *ip = (str - buf); + } + continue; + + case '%': + if (str < end) + *str = '%'; + ++str; + continue; + + /* integer number formats - set up the flags and "break" */ + case 'o': + base = 8; + break; + + case 'x': + flags |= SMALL; + case 'X': + base = 16; + break; + + case 'd': + case 'i': + flags |= SIGN; + case 'u': + break; + + default: + if (str < end) + *str = '%'; + ++str; + if (*fmt) { + if (str < end) + *str = *fmt; + ++str; + } else { + --fmt; + } + continue; + } + if (qualifier == 'L') + num = va_arg(args, long long); + else if (qualifier == 'l') { + num = va_arg(args, unsigned long); + if (flags & SIGN) + num = (signed long) num; + } else if (qualifier == 'Z' || qualifier == 'z') { + num = va_arg(args, size_t); + } else if (qualifier == 't') { + num = va_arg(args, ptrdiff_t); + } else if (qualifier == 'h') { + num = (unsigned short) va_arg(args, int); + if (flags & SIGN) + num = (signed short) num; + } else { + num = va_arg(args, unsigned int); + if (flags & SIGN) + num = (signed int) num; + } + str = number(str, end, num, base, + field_width, precision, flags); + } + if (size > 0) { + if (str < end) + *str = '\0'; + else + end[-1] = '\0'; + } + /* the trailing null byte doesn't count towards the total */ + return str-buf; +} \ No newline at end of file