lab-1(mit-6.828)
文章目录 13 节
Exercise 3
- At what point does the processor start executing 32-bit code? What exactly causes the switch from 16- to 32-bit mode?
从这一行开始执行32行代码

开启保护模式,开启32位模式

- What is the last instruction of the boot loader executed, and what is the first instruction of the kernel it just loaded?
bootloader执行的最后一行代码

kernel的第一条指令

- Where is the first instruction of the kernel?
同上
- How does the boot loader decide how many sectors it must read in order to fetch the entire kernel from disk? Where does it find this information?
bootloader首先读取除了启动扇区(第0扇区)之外的前8个扇区

这次读取的大小已经足够了,足够读入内核的ELF header到内存中,有了ELF header之后,可以通过ELF header的信息获取Program header,Program header记录了要读入部分的信息,根据这个信息,bootloader将整个内核装载进入内核

Exercise 7
目前,还没有开启分页,无法访问0xf0100000

接下来,我们开启分页,可以看到这两个地址实际映射的内存是一样的

Exercise 8
这个部分参照其他16进制的实现,我们可以得到以下代码

following question:
- Explain the interface between
printf.candconsole.c. Specifically, what function doesconsole.cexport? How is this function used byprintf.c?
console.c导出了 putch 这个函数

printf.c中通过函数指针的方式调用这个函数

- Explain the following from
console.c
1 if (crt_pos >= CRT_SIZE) {
2 int i;
3 memmove(crt_buf, crt_buf + CRT_COLS, (CRT_SIZE - CRT_COLS) * sizeof(uint16_t));
4 for (i = CRT_SIZE - CRT_COLS; i < CRT_SIZE; i++)
5 crt_buf[i] = 0x0700 | ' ';
6 crt_pos -= CRT_COLS;
7 }
简单来说,这里的代码的作用是当需要显示的内容超过屏幕时,将整体的内容上移一行
Challenge
控制命令
我们常用的printf函数输出来的颜色是终端的配色。如果想要输出不同的颜色进行区分,就需要用到printf的控制命令:\033[m。
控制命令以\033[开头,以m结尾,而中间则是属性码,属性代码之间使用;分隔,如\033[1;34;42m。而属性代码的含义见下面的表格。
printf属性代码
这里列举了三类属性代码,当然这只是一部分。
通用格式控制
| 属性代码 | 功能 |
|---|---|
| 0 | 重置所有属性 |
| 1 | 高亮/加粗 |
| 2 | 暗淡 |
| 4 | 下划线 |
| 5 | 闪烁 |
| 7 | 反转 |
| 8 | 隐藏 |
前景色
| 属性代码 | 颜色 |
|---|---|
| 30 | 黑色 |
| 31 | 红色 |
| 32 | 绿色 |
| 33 | 黄色 |
| 34 | 蓝色 |
| 35 | 品红 |
| 36 | 青色 |
背景色
| 属性代码 | 颜色 |
|---|---|
| 40 | 黑色 |
| 41 | 红色 |
| 42 | 绿色 |
| 43 | 黄色 |
| 44 | 蓝色 |
| 45 | 品红 |
| 46 | 青色 |
根据如上ANSI转义序列的标准,我实现了部分颜色,修改部分的代码如下
printfmt.c中的vprintfmt函数修改如下
#define RESET 0
#define BOLD 1
#define FBLACK 30
#define FRED 31
#define FGREEN 32
#define FYELLOW 33
#define FBLUE 34
#define FDEFAULT 39
#define BBLACK 40
#define BRED 41
#define BGREEN 42
#define BYELLOW 43
#define BBLUE 44
#define BDEFAULT 49
#define ESC '\033'
#define START '['
#define END 'm'
#define DELIMITER ';'
// default black background, white foreground
static int color = 0x0700;
int
parse_str2int(const char *s, int start, int end){
int result = 0;
for(int i = start ; i < end ; i++){
result *= 10;
result += s[i] - '0';
}
return result;
}
void set_color(int r){
// cprintf("r : %d\n", r);
if(r / 10 == 3){
color &= 0xf0ff;
}else if(r / 10 == 4){
color &= 0x0fff;
}
switch (r){
case RESET:
color = 0x0700;
break;
case BOLD:
color |= 0x0800;
break;
case FBLACK:
color |= 0x0000;
break;
case FBLUE:
color |= 0x0100;
break;
case FGREEN:
color |= 0x0200;
break;
case FRED:
color |= 0x0400;
break;
case FYELLOW:
color |= 0x0600;
break;
case FDEFAULT:
color |= 0x0700;
break;
case BBLACK:
color |= 0x0000;
break;
case BBLUE:
color |= 0x1000;
break;
case BGREEN:
color |= 0x2000;
break;
case BRED:
color |= 0x4000;
break;
case BYELLOW:
color |= 0x6000;
break;
case BDEFAULT:
color |= 0x7000;
break;
default:
break;
}
// cprintf("color : %x\n", color);
}
void
vprintfmt(void (*putch)(int, void*), void *putdat, const char *fmt, va_list ap)
{
register const char *p;
register int ch, err;
unsigned long long num;
int base, lflag, width, precision, altflag;
char padc;
while (1) {
while ((ch = *(unsigned char *) fmt++) != '%') {
if (ch == '\0')
return;
if (ch == ESC) {
if(*fmt++ != START)
continue;
const char * str = fmt;
int startidx = 0;
while ((ch = *(unsigned char *) fmt++) != END){
if(ch == '\0')
return;
}
int endidx = fmt - str - 1;
int s = startidx;
int e = endidx;
for(int i = startidx ; i < endidx ; i++){
if(str[i] == DELIMITER){
e = i;
int result = parse_str2int(str, s, e);
s = i + 1;
set_color(result);
}
}
int result = parse_str2int(str, s, endidx);
set_color(result);
putch(color, putdat);
continue;
}
putch(ch, putdat);
}
// Process a %-escape sequence
padc = ' ';
width = -1;
precision = -1;
lflag = 0;
altflag = 0;
reswitch:
switch (ch = *(unsigned char *) fmt++) {
// flag to pad on the right
case '-':
padc = '-';
goto reswitch;
// flag to pad with 0's instead of spaces
case '0':
padc = '0';
goto reswitch;
// width field
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
for (precision = 0; ; ++fmt) {
precision = precision * 10 + ch - '0';
ch = *fmt;
if (ch < '0' || ch > '9')
break;
}
goto process_precision;
case '*':
precision = va_arg(ap, int);
goto process_precision;
case '.':
if (width < 0)
width = 0;
goto reswitch;
case '#':
altflag = 1;
goto reswitch;
process_precision:
if (width < 0)
width = precision, precision = -1;
goto reswitch;
// long flag (doubled for long long)
case 'l':
lflag++;
goto reswitch;
// character
case 'c':
putch(va_arg(ap, int), putdat);
break;
// error message
case 'e':
err = va_arg(ap, int);
if (err < 0)
err = -err;
if (err >= MAXERROR || (p = error_string[err]) == NULL)
printfmt(putch, putdat, "error %d", err);
else
printfmt(putch, putdat, "%s", p);
break;
// string
case 's':
if ((p = va_arg(ap, char *)) == NULL)
p = "(null)";
if (width > 0 && padc != '-')
for (width -= strnlen(p, precision); width > 0; width--)
putch(padc, putdat);
for (; (ch = *p++) != '\0' && (precision < 0 || --precision >= 0); width--)
if (altflag && (ch < ' ' || ch > '~'))
putch('?', putdat);
else
putch(ch, putdat);
for (; width > 0; width--)
putch(' ', putdat);
break;
// (signed) decimal
case 'd':
num = getint(&ap, lflag);
if ((long long) num < 0) {
putch('-', putdat);
num = -(long long) num;
}
base = 10;
goto number;
// unsigned decimal
case 'u':
num = getuint(&ap, lflag);
base = 10;
goto number;
// (unsigned) octal
case 'o':
// Replace this with your code.
num = getuint(&ap, lflag);
base = 8;
break;
// pointer
case 'p':
putch('0', putdat);
putch('x', putdat);
num = (unsigned long long)
(uintptr_t) va_arg(ap, void *);
base = 16;
goto number;
// (unsigned) hexadecimal
case 'x':
num = getuint(&ap, lflag);
base = 16;
number:
printnum(putch, putdat, num, base, width, padc);
break;
// escaped '%' character
case '%':
putch(ch, putdat);
break;
// unrecognized escape sequence - just print it literally
default:
putch('%', putdat);
for (fmt--; fmt[-1] != '%'; fmt--)
/* do nothing */;
break;
}
}
}
console.c的cga_putc函数修改如下
static int color = 0x0700;
static void
cga_putc(int c)
{
// if no attribute given, then use black on white
if (!(c & ~0xFF)){
c |= color;
}else{
color = c;
return;
}
switch (c & 0xff) {
case '\b':
if (crt_pos > 0) {
crt_pos--;
crt_buf[crt_pos] = (c & ~0xff) | ' ';
}
break;
case '\n':
crt_pos += CRT_COLS;
/* fallthru */
case '\r':
crt_pos -= (crt_pos % CRT_COLS);
break;
case '\t':
cons_putc(' ');
cons_putc(' ');
cons_putc(' ');
cons_putc(' ');
cons_putc(' ');
break;
default:
crt_buf[crt_pos++] = c; /* write the character */
break;
}
// 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 */
outb(addr_6845, 14);
outb(addr_6845 + 1, crt_pos >> 8);
outb(addr_6845, 15);
outb(addr_6845 + 1, crt_pos);
}
最后测试部分代码和实现效果如下

打印出红色前景+绿色背景+高亮

Exercise 9
栈的结束地址是data段的开始,大小为8个页,32KB


Exercise 10
下面是C语言函数调用时栈的内存图

Exercise 11
在entry.S中将ebp初始化为0,说明当ebp为0时栈帧就结束了,根据练习10的模型,可以遍历所有的栈帧
int
mon_backtrace(int argc, char **argv, struct Trapframe *tf)
{
// Your code here.
uint32_t ebp,eip;
for(ebp = read_ebp();ebp != 0 ; ebp = *((uint32_t*)ebp)){
eip = *(uint32_t*)(ebp + 4);
cprintf("ebp %x eip %x args ",ebp,eip);
for(int i = 2 ; i < 7 ; i++){
cprintf("%08x ",*((uint32_t*)(ebp + i * 4)));
}
cprintf("\n");
}
return 0;
}
可以得到输出如下

Exercise 12
这个练习只需要添加调试信息的打印即可
int
mon_backtrace(int argc, char **argv, struct Trapframe *tf)
{
// Your code here.
uint32_t ebp,eip;
for(ebp = read_ebp();ebp != 0 ; ebp = *((uint32_t*)ebp)){
eip = *(uint32_t*)(ebp + 4);
cprintf("ebp %x eip %x args ",ebp,eip);
for(int i = 2 ; i < 7 ; i++){
cprintf("%08x ",*((uint32_t*)(ebp + i * 4)));
}
cprintf("\n");
struct Eipdebuginfo info;
debuginfo_eip(eip,&info);
cprintf("\t%s:%d: %.*s+%d\n",info.eip_file,info.eip_line,info.eip_fn_namelen,info.eip_fn_name,info.eip_fn_addr);
}
return 0;
}