The information covered in this tutorial will be slightly useful for Assignment 1, however the tutorial's main focus will be material that is relevant to Kernel Projects 1 & 2.
source /u/cs452/public/env.cshIf your shell is bash, run
source /u/cs452/public/env.shThis should set up everything. You probably want to put the appropriate line in your .cshrc or .bashrc respectively.
/u/cs452/public/examples/cs452-demoYou can copy the files in this directory to a private directory of your own in order to do some experimentation. The directory contains:
Using Newlib is the same as using the regular C library. You simply include the header and link with the library. Because GCC doesn't use Newlib by default, we include the above compilation and linking flags. See the gcc man page or documentation for more info about them.
Here is what happens when you compile the "Hello world" sample program.
[cs452@lagrange]...public/examples/cs452-demo> pwd /u3/cs452/public/examples/cs452-demo [cs452@lagrange]...public/examples/cs452-demo> make /u3/cs452/i586-3.3.3//bin/i586-elf-g++ -nostdinc -nostdinc++ -nodefaultlibs -nostdlib -O6 -Wall -I. -I/u3/cs452/i586-3.3.3//include -I/u3/cs452/i586-3.3.3//i586-elf/include -I/u3/cs452/i586-3.3.3//include/cs452 -I/u3/cs452/i586-3.3.3//lib/gcc-lib/i586-elf/3.3.3/include/ -c main.cc -o main.o main.cc: In function `int main(long unsigned int, long unsigned int)': main.cc:10: warning: unused variable `int i' /u3/cs452/i586-3.3.3//bin/i586-elf-g++ -nostdinc -nostdinc++ -nodefaultlibs -nostdlib -O6 -Wall -I. -I/u3/cs452/i586-3.3.3//include -I/u3/cs452/i586-3.3.3//i586-elf/include -I/u3/cs452/i586-3.3.3//include/cs452 -I/u3/cs452/i586-3.3.3//lib/gcc-lib/i586-elf/3.3.3/include/ -c serial.cc -o serial.o /u3/cs452/i586-3.3.3//bin/i586-elf-g++ -nostdinc -nostdinc++ -nodefaultlibs -nostdlib -O6 -Wall -I. -I/u3/cs452/i586-3.3.3//include -I/u3/cs452/i586-3.3.3//i586-elf/include -I/u3/cs452/i586-3.3.3//include/cs452 -I/u3/cs452/i586-3.3.3//lib/gcc-lib/i586-elf/3.3.3/include/ -c video.cc -o video.o /u3/cs452/i586-3.3.3//bin/i586-elf-g++ -nostdinc -nostdinc++ -nodefaultlibs -nostdlib -O6 -Wall -I. -I/u3/cs452/i586-3.3.3//include -I/u3/cs452/i586-3.3.3//i586-elf/include -I/u3/cs452/i586-3.3.3//include/cs452 -I/u3/cs452/i586-3.3.3//lib/gcc-lib/i586-elf/3.3.3/include/ -c stubs.cc -o stubs.o stubs.cc: In function `clock_t times(tms*)': stubs.cc:120: warning: return of negative value `-1' to `clock_t' stubs.cc:120: warning: argument of negative value `-1' to `long unsigned int' stubs.cc: In function `void _exit(int)': stubs.cc:18: warning: `noreturn' function does return /u3/cs452/i586-3.3.3//bin/i586-elf-gcc -nostdinc -nostdinc++ -nodefaultlibs -nostdlib -O6 -Wall -I. -I/u3/cs452/i586-3.3.3//include -I/u3/cs452/i586-3.3.3//i586-elf/include -I/u3/cs452/i586-3.3.3//include/cs452 -I/u3/cs452/i586-3.3.3//lib/gcc-lib/i586-elf/3.3.3/include/ -c multiboot.S -o multiboot.o /u3/cs452/i586-3.3.3//bin/i586-elf-g++ -nostdinc -nostdinc++ -nodefaultlibs -nostdlib -O6 -Wall -I. -I/u3/cs452/i586-3.3.3//include -I/u3/cs452/i586-3.3.3//i586-elf/include -I/u3/cs452/i586-3.3.3//include/cs452 -I/u3/cs452/i586-3.3.3//lib/gcc-lib/i586-elf/3.3.3/include/ -Wl,-T,loader.cpp.x multiboot.o main.o serial.o video.o stubs.o -L/u3/cs452/i586-3.3.3//lib -lstdc++ -lgcc -lc -o cs452_demo /u3/cs452/i586-3.3.3//bin/i586-elf-strip cs452_demo -o cs452_demo-stripped
[cs452@lagrange]...public/examples/cs452-demo> make post Acquiring lock for grub listing Got lock Reading grub entries Cleaning up kernel/module list Adding entry: ['cs452_demo'] Saving grub list Copying your files to /software/undergrad.math/data/cs452-sysfiles/cs452/ All doneIn the /software/undergrad.math/data/cs452-sysfiles/ directory 452-post will create a directory with your username (if it doesn't already exist) and copy your "kernel" and any modules to it. The "kernel" is just the application Grub will initially boot. The string "Hello World" is how your application will show up in the Grub listing. Looking at the file /software/undergrad.math/data/cs452-sysfiles/.grub.lst we see it now contains:
title cs452: Hello World root (nd) kernel /software/undergrad.math/data/cs452-sysfiles/cs452/cs452_demoPlus whatever else was in the listing before. Entries from 452-post will be removed if the kernel or any modules the Grub listing entry points to are removed.
For more detailed information about the architecture and about processor instructions, you will need access to a 486 (or 386+) microprocessor manual. There is one in the lab, but you may want your own so that you don't have to share. The one I like is entitled The 80386 book, by Ross P. Nelson. It is much more readable than the book in the lab. (This book is copyright 1988 by Microsoft Press, ISBN 1-55615-138-1.) You can also refer to Chapter 3, Software Information, of your course notes.
Intel processor manuals may also be found at http://www.x86.org/intel.doc/586manuals.htm.
The GNU Assembler, gas, uses a different syntax from what you will likely find in any x86 reference manual, and the two-operand instructions have the source and destinations in the opposite order. Here are the types of the gas instructions:
opcode (e.g., pushal) opcode operand (e.g., pushl %edx) opcode source,dest (e.g., movl %edx,%eax) (e.g., addl %edx,%eax)
Where there are two operands, the rightmost one is the destination. The
leftmost one is the source.
For example,
movl %edx, %eax
means
Move the contents of the edx register into the eax
register.
For another example,
addl %edx,%eax
means
Add the contents of the edx and eax registers,
and place the sum in the eax register.
Included in the syntactic differences between gas and Intel assemblers is that all register names used as operands must be preceeded by a percent (%) sign, and instruction names usually end in either "l", "w", or "b", indicating the size of the operands: long (32 bits), word (16 bits), or byte (8 bits), respectively. For our purposes, we will usually be using the "l" (long) suffix.
Here are the important processor registers:
EAX,EBX,ECX,EDX - "general purpose", more or less interchangeable EBP - used to access data on stack - when this register is used to specify an address, SS is used implicitly ESI,EDI - index registers, relative to DS,ES respectively SS,DS,CS,ES,FS,GS - segment registers - (when Intel went from the 286 to the 386, they figured that providing more segment registers would be more useful to programmers than providing more general- purpose registers... now, they have an essentially RISC processor with only _FOUR_ GPRs!) - these are all only 16 bits in size EIP - program counter (instruction pointer), relative to CS ESP - stack pointer, relative to SS EFLAGS - condition codes, a.k.a. flags
i486 addresses are formed from a segment base address plus an offset. To compute an absolute memory address, the i486 figures out which segment register is being used, and uses the value in that segment register as an index into the global descriptor table (GDT). The entry in the GDT tells (among other things) what the absolute address of the start of the segment is. The processor takes this base address and adds on the offset to come up with the final absolute address for an operation. You'll be able to look in a 486 manual for more information about this or about the GDT's organization.
i486 has 6 16-bit segment registers, listed here in order of importance:
To explain the contradiction in the above paragraph with the GDT we note that once your kernel is running, you're in protected mode. To get into protected mode Grub had to set up a GDT. Once in the kernel though, until the processor executes an instruction that looks at the GDT (such as when performing a context switch), the GDTR doesn't need to be valid. The Multiboot specification states that we can't assume the GDTR points to anything valid, which is why you need to create your own GDT as soon as possible. This is only necessary for your actual kernel, not your first assignment.
The x86 architecture supports different addressing modes for the operands. A discussion of all modes is out of the scope of this tutorial, and you may refer to your favourite x86 reference manual for a painfully-detailed discussion of them. Segment registers are special, you can't do a
movw seg-reg, seg-regYou can, however, do
movw seg-reg,memory movw memory,seg-reg movw seg-reg,reg movw reg,seg-regNote: If you movw %ss,%ax, then you should xorl %eax,%eax first to clear the high-order 16 bits of %eax, so you can work with long values.
mov (especially with segment registers) - e.g.,: movw %es,%ax movl %cs:4,%esp movw _processControlBlock,%cs - note: mov's do NOT set flags pushl, popl - push/pop long pushal, popal - push/pop EAX,EBX,ECX,EDX,ESP,EBP,ESI,EDI call (jumps to piece of code, saves return address on stack) e.g., call _cFunction int - call a software interrupt ret (returns from piece of code entered due to call instruction) iretl (returns from piece of code entered due to hardware or software interrupt) sti, cli - set/clear the interrupt bit to enable/disable interrupts respectively
The way to mix C and assembly language is outlined on page 33 of your Course Notes. You use the "asm" directive. To access C-language variables from inside of assembly language, you simply use the C identifier name as a memory operand. These variables cannot be local to a procedure, and also cannot be static inside a procedure. They must be global (but can be static global). The newline characters are necessary.
unsigned long a1, r; void junk( void ) { asm( "pushl %eax \n" "pushl %ebx \n" "movl $100,%eax \n" "movl a1,%ebx \n" "int $69 \n" "movl %eax,r \n" "popl %ebx \n" "popl %eax \n" ); }
This example does the following: