[f000:fff0] 0xffff0: ljmp $0xf000,$0xe05b 0x0000fff0 in ?? () (gdb) si [f000:e05b] 0xfe05b: cmpl $0x0,%cs:0x70c8 0x0000e05b in ?? () (gdb) si [f000:e062] 0xfe062: jne 0xfd414 0x0000e062 in ?? () (gdb) si [f000:e066] 0xfe066: xor %dx,%dx 0x0000e066 in ?? () (gdb) si [f000:e068] 0xfe068: mov %dx,%ss 0x0000e068 in ?? () (gdb) si [f000:e06a] 0xfe06a: mov $0x7000,%esp 0x0000e06a in ?? () (gdb) si [f000:e070] 0xfe070: mov $0xf2d4e,%edx 0x0000e070 in ?? () (gdb) si [f000:e076] 0xfe076: jmp 0xfff00 0x0000e076 in ?? ()
Q: Examine the 8 words of memory at 0x00100000 at the point the BIOS enters the boot loader, and then again at the point the boot loader enters the kernel. Why are they different? What is there at the second breakpoint?
Q: What is the first instruction after the new mapping is established that would fail to work properly if the mapping weren’t in place?
初次出问题的指令:
TEXT - 1 lines
1
jmp *%eax
此时%eax储存的为0xf010002f,若初始使用的页表没有合理映射,可能会使跳转出问题。
Exercise 8
We have omitted a small fragment of code - the code necessary to print octal numbers using patterns of the form “%o”. Find and fill in this code fragment.
修改printfmt.c中的vprintfmt函数:
C - 6 lines
1 2 3 4 5 6
// (unsigned) octal case'o': // Replace this with your code. num = getuint(&ap, lflag); base = 8; goto number;
Q1: Explain the interface between printf.c and console.c. Specifically, what function does console.c export? How is this function used by printf.c?
console.c导出cputchar函数供printf.c中的putch函数使用:
C - 7 lines
1 2 3 4 5 6 7
// printf.c staticvoid putch(int ch, int *cnt) { cputchar(ch); // from console.c *cnt++; }
int x = 1, y = 3, z = 4; cprintf("x %d, y %x, z %d\n", x, y, z);
Q3.1: In the call to cprintf(), to what does fmt point? To what does ap point?
fmt指向字符串"x %d, y %x, z %d\n",也即8(%ebp)位置处的第一个参数。ap指向可变参数列表,也即12(%ebp)位置处的第二个参数
Q3.2: List (in order of execution) each call to cons_putc, va_arg, and vcprintf. For cons_putc, list its argument as well. For va_arg, list what ap points to before and after the call. For vcprintf list the values of its two arguments.
Q4: What is the output? Explain how this output is arrived at in the step-by-step manner of the previous exercise. Here’s an ASCII table that maps bytes to characters.
The output depends on that fact that the x86 is little-endian. If the x86 were instead big-endian what would you set i to in order to yield the same output? Would you need to change 57616 to a different value?
C - 2 lines
1 2
unsignedint i = 0x00646c72; cprintf("H%x Wo%s", 57616, &i);
输出为He110 World
57616的16进制表示为110,而十六进制数72,6c,64在ASCII码中对应的字符分别为r, l, d
若为大端法,则只需令i = 0x726c6400,无需改动57616
Q5: In the following code, what is going to be printed after ‘y=’? (note: the answer is not a specific value.) Why does this happen?
C - 1 lines
1
cprintf("x=%d y=%d", 3);
将会输出12(%ebp)处的值
Q6: Let’s say that GCC changed its calling convention so that it pushed arguments on the stack in declaration order, so that the last argument is pushed last. How would you have to change cprintf or its interface so that it would still be possible to pass it a variable number of arguments?
Q: Determine where the kernel initializes its stack, and exactly where in memory its stack is located. How does the kernel reserve space for its stack? And at which “end” of this reserved area is the stack pointer initialized to point to?
// What is the purpose of this? if (crt_pos >= CRT_SIZE) { int i;
memmove(crt_buf, crt_buf + CRT_COLS, (CRT_SIZE - CRT_COLS) * sizeof(uint16_t)); for (i = CRT_SIZE - CRT_COLS; i < CRT_SIZE; i++) crt_buf[i] = 0x0700 | ' '; crt_pos -= CRT_COLS; }
/* move that little blinky thing */ /* just move the cursor */ outb(addr_6845, 14); outb(addr_6845 + 1, crt_pos >> 8); outb(addr_6845, 15); outb(addr_6845 + 1, crt_pos); }
staticint isdigit(int c) { return c >= '0' && c <= '9'; }
staticint atoi(constchar* s) { int res = 0; for (int i = 0; isdigit(s[i]); ++i) res = res * 10 + (s[i] - '0'); return res; }
// Modify the VGA text-mode character attribute 'attr' // based on the parameter contained in the buf. staticvoid handle_ansi_esc_param(constchar* buf, int len, int* attr) { // white is light grey staticint ansi2cga[] = {0x0, 0x4, 0x2, 0xe, 0x1, 0x5, 0x3, 0x7}; int tmp_attr = *attr; int n = atoi(buf); if (n >= 30 && n <= 37) { tmp_attr = (tmp_attr & ~(0x0f)) | ansi2cga[n - 30]; } elseif (n >= 40 && n <= 47) { tmp_attr = (tmp_attr & ~(0xf0)) | (ansi2cga[n - 40] << 4); } elseif (n == 0) { tmp_attr = 0x07; } *attr = tmp_attr; }
// The max length of one parameter. // Emmmmmm... no body will input // a number parameter with length of 1023, probably. #define ESC_BUFSZ 1024
// If the character is '\033' (esc), then buffer the input // until get a whole ANSI Esc Seq (and update the attribute) // or get a false input midway. // Otherwise output it normally. // // Use a deterministic finite automata: // // [0]: '\033' => [1] // other => [0] + output the character // // [1]: '\033' => [1] // '[' => [2] // other => [0] // // [2]: digit => [3] + begin record the modification // other => [0] + discard the modification // // [3]: digit => [3] // ';' => [2] + record the modification of attribute // 'm' => [0] + update the attribute // other => [0] + discard the modification // staticvoid cga_putc(int c) { staticint state = 0; staticchar esc_buf[ESC_BUFSZ]; staticint esc_len = 0; staticint attr = 0; // default attribute. staticint esc_attr = 0;
switch(state) { case0: { if ((char)c == '\033') { state = 1; } else { cga_putc1((attr << 8) | (c & 0xff)); } break; } case1: { if ((char)c == '[') { esc_attr = attr; state = 2; } elseif ((char)c != '\033') { state = 0; } break; } case2: { if (isdigit(c)) { esc_buf[esc_len++] = (char)c; state = 3; } else { // discard modification esc_len = 0;
state = 0; } break; } case3: { if (isdigit(c)) { esc_buf[esc_len++] = (char)c; } elseif ((char)c == ';') { // record current modification esc_buf[esc_len++] = 0; handle_ansi_esc_param(esc_buf, esc_len, &esc_attr); esc_len = 0;
case'\b': // Change the behavior of backspace to support character insert. // Maintain the character in `crt_pos`. if (crt_pos > 0) { crt_pos--; // crt_buf[crt_pos] = (c & ~0xff) | ' '; } break;
将(在输入字符串尾部)退格的操作实现为:
C - 1 lines
1
cputchar('\b'), cputchar(' '), cputchar('\b');
user-terminal的backspace甚至会让光标回退到prompt之前。在gdb上截获输入的字符,发现在(我的)user-terminal上输入backspace得到的字符是DEL(0x7f),比空格' '的ASCII码更大。也就是说在原始版本的readline中,当i == 0时,在user-terminal输入backspace虽然不会进入到处理退格的语句中(判断条件为(c == '\b' || c == '\x7f') && i > 0),但是会进入到正常回显字符的语句中(判断条件为c >= ' ' && i < BUFLEN - 1),因此将正常回显字符的判断条件改为:
// Handle the extended ASCII code inputed by console inlinestaticvoidhandle_ext_ascii(int c); // Handle the escape sequence inputed by serial (user-terminal) inlinestaticvoidhandle_esc_seq(void);
// Move the cursor right inlinestaticvoidmove_right(void); // Move the cursor left inlinestaticvoidmove_left(void);
// Flush buffer's [cur, tail) to the displays // and move the cursor back inlinestaticvoidflush_buf(void);
// Insert char to current cursor inlinestaticvoidinsert_char(int c); // Remove current cursor's char inlinestaticvoidremove_char(void); // Terminate the input inlinestaticvoidend_input(void);
#define BUFLEN 1024 staticchar buf[BUFLEN];
// Current position of cursor staticint cur; // Tail of buffer staticint tail;
staticint echoing;
char * readline(constchar *prompt) { int c;
if (prompt != NULL) cprintf("%s", prompt);
cur = tail = 0; echoing = iscons(0); while (1) { c = getchar(); if (c < 0) { cprintf("read error: %e\n", c); returnNULL; } elseif ((c == '\b' || c == '\x7f') && cur > 0) { remove_char(); } elseif (c >= ' ' && c <= '~' && tail < BUFLEN-1) { // Must have c <= '~', // because DEL(0x7f) is larger than '~' // and it will be inputed when you push // 'backspace' in user-terminal insert_char(c); } elseif (c == '\n' || c == '\r') { end_input(); return buf; } elseif (c == '\x1b') { handle_esc_seq(); // only serial will input esc } elseif (c > '\x7f') { handle_ext_ascii(c); // only console will input extended ascii } } }
inlinestaticvoid flush_buf(void) { for (int i = cur; i < tail; ++i) cputchar(buf[i]); for (int i = cur; i < tail; ++i) cputchar('\b'); // cursor move back }
inlinestaticvoid insert_char(int c) { if (cur == tail) { tail++, buf[cur++] = c; if (echoing) cputchar(c); } else { // general case memmove(buf + cur + 1, buf + cur, tail - cur); buf[cur] = c, tail++; if (echoing) flush_buf(); move_right(); } }
inlinestaticvoid remove_char(void) { if (cur == tail) { cur--, tail--; if (echoing) cputchar('\b'), cputchar(' '), cputchar('\b'); } else { // general case memmove(buf + cur - 1, buf + cur, tail - cur); buf[tail - 1] = ' '; move_left(); if (echoing) flush_buf(); tail--; } }
inlinestaticvoid move_left(void) { if (cur > 0) { if (echoing) cputchar('\b'); cur--; } }
inlinestaticvoid move_right(void) { if (cur < tail) { if (echoing) cputchar(buf[cur]); cur++; } }
inlinestaticvoid end_input(void) { if (echoing) { for (; cur < tail; cputchar(buf[cur++])) /* move the cursor to the tail */; cputchar('\n'); } cur = tail; buf[tail] = 0; }
inlinestaticvoid handle_esc_seq(void) { char a, b = 0;
a = getchar(); if (a == '[') { switch(b = getchar()) { case ESC_LF: move_left(); return; case ESC_RT: move_right(); return; } } insert_char(a); if (b) insert_char(b); }