// case for input & output for the same register.
asm ("leal (%0,%0,4), %0"
: "=r" (five_times_x) // %0
: "0" (x) // set the input register to be 0, that's also the output register.
);
// for clobber list: 一般情况明确写出来应该不需要添加在Clobber List中,GCC往往知道,主要
// 小心的是隐性地被修改的寄存器东东。
// 隐性修改影响到内存时还要加入volatile关键字,如果在asm的input和output中没有他们的话。
// 下面的这个case中假设_foo函数会用eax和ecx
asm ("movl %0,%%eax;
movl %1,%%ecx;
call _foo"
: /* no outputs */
: "g" (from), "g" (to)
: "eax", "ecx"
);
// 有时候的volatile关键字是防止被优化的,需要被小心谨慎地执行。
asm volatile (:::);
// 关键词设置: commonly used constraints.
// 1. register operand constraints (r)
+---+--------------------+
| r | Register(s) |
+---+--------------------+
| a | %eax, %ax, %al |
| b | %ebx, %bx, %bl |
| c | %ecx, %cx, %cl |
| d | %edx, %dx, %dl |
| S | %esi, %si |
| D | %edi, %di |
+---+--------------------+
// 2. Memory operand constraint (m)
// when the operands are in the memory, use m.
asm("sidt %0\n" : :"m"(loc)); // store idtable on 'loc' position in the memory.
// 3. digit constraints.
asm ("incl %0" :"=a"(var):"0"(var)); // 0 means the first register, here is eax.
Whenever a system call with three arguments is made, the macro shown above is used to make the call. The syscall number is placed in eax, then each parameters in ebx, ecx, edx. And finally "int 0x80" is the instruction which makes the system call work. The return value can be collected from eax.
The target architecture is set to "i8086".
[f000:fff0] 0xffff0: ljmp $0xf000,$0xe05b # strat from 0xffff0 as the cold reset vector.
0x0000fff0 in ?? () # 0xfe05b: the actual start of the POST code in the BIOS ROM.
[f000:e05b] 0xfe05b: cmpl $0x0,%cs:0x6ac8
0x0000e05b in ?? () # 看一下地址0xf6ac8, 但并没有研究出来这个东西到底是哪个dirty的小饼干
(gdb) x/w *0xf6ac8 # test for ROM memory.
0x0: 0x00000000
[f000:e062] 0xfe062: jne 0xfd2e1 # won't jump for zero.
0x0000e062 in ?? ()
[f000:e066] 0xfe066: xor %dx,%dx # 置零
0x0000e066 in ?? ()
[f000:e068] 0xfe068: mov %dx,%ss # ss will be set to zero.
0x0000e068 in ?? ()
[f000:e06a] 0xfe06a: mov $0x7000,%esp # esp set to 0x7000
0x0000e06a in ?? ()
[f000:e070] 0xfe070: mov $0xf34c2,%edx # 为什么要赋值?
0x0000e070 in ?? ()
[f000:e076] 0xfe076: jmp 0xfd15c # 跳走了!
0x0000e076 in ?? ()
[f000:d15c] 0xfd15c: mov %eax,%ecx # %ecx被赋值.
0x0000d15c in ?? ()
(gdb) i r eax
eax 0x0 0
[f000:d15f] 0xfd15f: cli # 清除中断位,IF, 我们可以看一下ELFLAG
0x0000d15f in ?? ()
(gdb) i r eflags
eflags 0x46 [ PF ZF ]
[f000:d160] 0xfd160: cld # clear DF, direction flag 0, when using string operation, ESI/EDI will increase.
0x0000d160 in ?? ()
(gdb) i r eflags
eflags 0x46 [ PF ZF ]
[f000:d161] 0xfd161: mov $0x8f,%eax # eax = 8f.
0x0000d161 in ?? ()
(gdb) i r eflags
eflags 0x46 [ PF ZF ]
[f000:d167] 0xfd167: out %al,$0x70 # output %al to 0x70 IO Port.
0x0000d167 in ?? ()
[f000:d169] 0xfd169: in $0x71,%al # input from port 71 to al
0x0000d169 in ?? ()
# in fact, in & out functions are for CMOS
# The CMOS memory exists outside of the normal address space and cannot
# contain directly executable code. It is reachable through IN and OUT
# commands at port number 70h (112d) and 71h (113d). To read a CMOS byte,
# an OUT to port 70h is executed with the address of the byte to be read and
# an IN from port 71h will then retrieve the requested information. The
# following BASIC fragment will read 128 CMOS bytes and print them to the
# screen in 8 rows of 16 values.
#
[f000:d16b] 0xfd16b: in $0x92,%al # input from port 92 to al
0x0000d16b in ?? ()
(gdb) i r al
al 0x0 0
[f000:d16d] 0xfd16d: or $0x2,%al # or.
0x0000d16d in ?? ()
[f000:d16f] 0xfd16f: out %al,$0x92 # output to 0x92 port.
0x0000d16f in ?? ()
[f000:d171] 0xfd171: lidtw %cs:0x6ab8 # They are commonly executed in real-address mode to allow processor initialization prior to switching to protected mode. load the number in.
0x0000d171 in ?? ()
[f000:d177] 0xfd177: lgdtw %cs:0x6a74 # Load Global/Interrupt Descriptor Table
0x0000d177 in ?? ()
[f000:d17d] 0xfd17d: mov %cr0,%eax # It seems that I can't get cr0 info using gdb.
0x0000d17d in ?? ()
[f000:d180] 0xfd180: or $0x1,%eax #
0x0000d180 in ?? ()
(gdb) i r eax
eax 0x60000010 1610612752
(gdb) si
[f000:d184] 0xfd184: mov %eax,%cr0
0x0000d184 in ?? ()
(gdb) i r eax
eax 0x60000011 1610612753
[f000:d187] 0xfd187: ljmpl $0x8,$0xfd18f // jump
0x0000d187 in ?? ()
(gdb) si
The target architecture is set to "i386". // world change!! Before this we are in real mode, but now we are in protected mode.
=> 0xfd18f: mov eax,0x10
0x000fd18f in ?? ()
阅读资料:
Background
The CMOS (complementary metal oxide semiconductor) memory is actually
a 64 or 128 byte battery-backed RAM memory module that is a part of the
system clock chip. Some IBM PS/2 models have the capability for a
2k (2048 byte) CMOS ROM Extension.
First used with clock-calender cards for the IBM PC-XT, when the PC/AT
(Advanced Technology) was introduced in 1985, the Motorola MC146818
became a part of the motherboard. Since the clock only uses fourteen of
the RAM bytes, the rest are available for storing system configuration data.
Interestingly, the original IBM-PC/AT (Advanced Technology) standard for
the region 10h-3Fh is nearly universal with one notable exception: The
IBM PS/2 systems deviate considerably (Note: AMSTRAD 8086 machines were
among the first to actively use the CMOS memory available and since they
*predate* the AT, do not follow the AT standard).
// Some code
// Read 'count' bytes at 'offset' from kernel into physical address 'pa'
// Might copy more than asked, because sectors mechanism. -> copy should be a multiples
// of sector size.
void readseg(uint32_t pa, uint32_t count, uint32_t offset) {
// .... skip some codes here ....
// ~(SECTSIZE - 1) => 0x200 => starts address from the multiples of 0x200.
pa &= ~(SECTSIZE - 1);
// .... skip some codes here ....
}
void readsect(void *dst, uint32_t offset) {
waitdisk(); // wait for disk ready or not.
// .... skip some codes here ....
outb(0x1f7, 0x20); // cmd 0x20 - read sectors.
waitdist();
// read a sector.
insl(0x1f0, dst, SECTSIZE/4);
}
void bootmain(void) {
readseg((uint32_t) ELFHDR, SECTSIZE*8, 0);
}
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.
这个练习甚至不用读代码,抄一下其它case就完事了,如果要加颜色看一下ANSI就好了,总之很简单。
Explain the interface between printf.c and console.c. Specifically, what function does console.c export? How is this function used by printf.c?
8 void *
7 memmove(void *dst, const void *src, size_t n)
6 {
5 const char *s;
4 char *d;
3
2 s = src;
1 d = dst;
195 if (s < d && s + n > d) {
1 s += n;
2 d += n;
3 while (n-- > 0)
4 *--d = *--s;
5 } else // our case.
6 while (n-- > 0)
7 *d++ = *s++; // copy from the first pixel. remove the first line of pixels of last plane.
8
9 return dst;
10 }
Trace the execution of the following code step-by-step:
int x = 1, y = 3, z = 4;
cprintf("x %d, y %x, z %d\n", x, y, z);
In the call to cprintf(), to what does fmt point? To what does ap point?
fmt points to "x %d, y %x, z %d\n", ap points to the list of [x, y, z].
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.
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?
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?
最早内核是在文件kern/entry.S中实现内核栈的建立。具体代码如下:
8 relocated:
7
6 # Clear the frame pointer register (EBP)
5 # so that once we get into debugging C code,
4 # stack backtraces will be terminated properly.
3 movl $0x0,%ebp # nuke frame pointer
2
1 # Set the stack pointer
77 movl $(bootstacktop),%esp
1
2 # now to C code
3 call i386_init
4
5 # Should never get here, but in case we do, just spin.
6 spin: jmp spin
7
8
9 .data
10 ###################################################################
11 # boot stack
12 ###################################################################
13 .p2align PGSHIFT # force page alignment
14 .globl bootstack
15 bootstack:
16 .space KSTKSIZE # Kernel Stack Size ensurance.
17 .globl bootstacktop
18 bootstacktop:N
To become familiar with the C calling conventions on the x86, find the address of the test_backtrace function in obj/kern/kernel.asm, set a breakpoint there, and examine what happens each time it gets called after the kernel starts. How many 32-bit words does each recursive nesting level of test_backtrace push on the stack, and what are those words?
Implement the backtrace function as specified above. Use the same format as in the example, since otherwise the grading script will be confused. When you think you have it working right, run make grade to see if its output conforms to what our grading script expects, and fix it if it doesn't. After you have handed in your Lab 1 code, you are welcome to change the output format of the backtrace function any way you like.