EMMY is a PC system emulator. It is intended for OS developers. It uses on-the-fly translation to achieve high performance. EMMY is primarily command-line driven. It uses the readline function when reading from the keyboard to make life easier. Command scripts can be created in files and executed for initialization purposes. EMMY provides the following environment: 1) A generic motherboard with RAM, CMOS timer, 8259 PICs, IOAPIC, ISA and PCI bus 2) Generic ATA disk and ATAPI CD, with PIO controller 3) Floppy drive & 8237 DMA controller 4) Character-cell 80x25 VGA screen and ASCII-only keyboard 5) SMP x86 CPUs (Pent Pros, FPU but no MMX, etc) 6) Realtek 8139 Ethernet card & PCI BIOS EMMY also incorporates a source-level debugger (GDB-like) for debugging a kernel. The kernel must be linked as an image provided in a file on the host system. Before starting EMMY, You will need to set these softlinks up so it will know where the output goes and where to get keyboard input from: ln -s /dev/pts/?? emmy_kbtty_tty ln -s /dev/pts/?? emmy_vgatty_tty The tty should be sized to 80x25, and put to sleep with a sleep 1000000 command or something like that. When EMMY starts, it needs to be configured. By default, you get 32Mb of RAM, one CPU and the boot device is the floppy. You still have to define the floppy, though, so it will know what kind of drive it is and what file to use: floppy 0 -144M -ro ../ozone/ozone/bootfloppy Here are other sample commands to configure it: bootdev hardisk idedev 0 -rw ../ozone/ozone/hd10meg.img idedev 1 -cd ../ozone/ozone/bootcd.iso ram 64 rtl8139 0 0/1 eth0 12-22-23-34-45-66 To display the configuration: config Once the configuration is set up, it is ready for action. When you use a command that requires the configuration to be finalized, EMMY will use the parameters given to set up the system. To get it going, just enter the 'run' command. However, there are many commands to use for debugging stuff. Like if there is a triple-fault, the emulator will print out a message and will stop. You can also stop execution at any time by pressing control-C. Breakpoints allow you to stop execution when a certain condition is attained. As of now, only the X86 cpu's support breakpoints. Some example of breakpoint commands are: break cpu0 (cs==0x10)&&((eip==0x400000)||(eip==0x118E0C)) break cpu0 (wmsz>0)?eax:edx break cpu0 eip==0xC272 The above commands simply stop execution when the given condition is met. The expression is a C-like syntax using conventional register names (plus others). Execution will continue until the given condition is met (evaluates non-zero). Sometimes, you want the breakpoint to happen, have some registers printed out and execution resumed. This command shows how to do that: break cpu0 (exv==0x0e)&&(cr0&1) ;; status cpu0 sveip cr2 eip inst@sveip ;; run This says, when the exception vector register (a pseudo register) gets set to 0x0E (pagefault), and we're in 32-bit mode (cr0<0>=1), print out sveip (the eip saved at beginning of the instruction), cr2 (the faulting address), eip (of next instruction, ie, the pagefault handler), and the disassembly of the instruction that caused the pagefault. Then resume execution. The double semicolons (;;) indicate the commands given are to be executed when the breakpoint attains. So here, the status command will print out the requested things about cpu0 then the run command will resume execution. You can also use DB for debugging. The x86 CPUs can operate in 3 basic stepping modes: 1) FAST - full-speed translation is performed, but a single step will typically execute a dozen instructions. 2) INTERP - instructions are interpreted one-at-a-time. This is useful when you want to step a single instruction at a time to see what is happening. It runs about 1/4th the speed of 'FAST' mode. 3) SINGLE - instructions are translated, but are executed one-at-a-time by taking a breakpoint on each one. This mode runs slowly, about 1/10th the speed of 'INTERP' mode. By default, the CPUs start in FAST mode. To change them, use the status command like this: status cpu0 step_fast status cpu0 step_interp status cpu0 step_single To see what mode it's in: status cpu0 step If a CPU is in FAST step mode, breakpoints will only be checked every 'several' instructions, so they may not be all that useful. If you have a need for an exact breakpoint, use INTERP mode. SINGLE mode is very slow (it uses int3's and sigtraps), and is typically useful only for debugging the translator itself, or to figure out why some code behaves differently between FAST and INTERP mode. COMMANDS bootcpu Selects which CPU is used for booting. The must be in the range 0..14, and less than the total number of CPUs. bootdev Selects which devicetype to use for booting. Valid things are cdrom, floppy, hardisk. The lowest numbered such device is selected. break Just 'break' by itself will list out any breakpoints that are currently defined. break List out just the given numbered breakpoint. break clear/disable/enable/test Perform the operation on the given breakpoint: clear - deleted the breakpoint disable - leaves it defined but ignores it enable - starts using it test - tests the condition to see if true (but does not execute associated commands) break [] [;; ]... Defines (or redefines) a breakpoint. is a device name as listed by the 'devices' command. config Displays the current configuration. cpus Defines the number of CPUs to have. The default is 1. The maximum is 15. devices Displays names of the currently configured devices. floppy <0..3> [-360K/-120M/-720K/-144M/-288M] [-ro/-rw] [] Defines, loads or unloads a floppy drive. First time used for a given number, it must indicate the type, such as -144M for a 1.44M drive. If a filename is given, the indicated file is loaded into the drive. If omitted, the drive will be marked unloaded. -ro or -rw may be given, the default is -rw. db [] This calls the DB symbolic debugger. If you supply a , the command will be executed and control returned back to Emmy. If you do not specify a DB command, control is turned over to the DB debugger prompt, where you can issue multiple commands. Return to the Emmy prompt by issuing a 'quit' command to the DB prompt. For multiprocessor emulation, DB views each CPU as a separate thread. To switch between CPUs, use DB's 'thread' command, giving the cpu number as the parameter. So to switch to what Emmy calls CPU 2, use 'thread 2'. DB assumes a flat 32-bit addressing space. All addresses given are assumed to be linear addresses. DB's breakpoints are implemented completely separately from Emmy's own breakpoints. While using DB, any breakpoints set by Emmy commands will be ignored. The breakpoints do not alter the runtime code by inserting int3's. They are done by stepping the code and checking the emulated EIP. This is done quickly so it does not affect execution time noticebly. The CPU's are forced to interpreter mode while in DB so no breakpoints are missed. A DB 'quit' command will exit back to the Emmy prompt so you can execute Emmy commands (like to examine control registers or such). DB's commands are very similar to GDB. Use the 'help' command as a guide. You must tell DB where the executable is on the host system. Use the 'file' command to do this. For example, you could put a command like this in your Emmy startup script: db file ../ozone/binaries/oz_kernel_486.elf That assumes the image was linked at a finite address. If you have a dynamic (driver) executable loaded, add on the base address it was loaded at: db file ../ozone/binaries/oz_dev_stego.elf:0x1F9000 idedev <0..3> [-cd/-ro/-rw] [] Defines, Loads or Unloads an hard disk or CDROM drive. If a filename is given, the indicated file is loaded into the drive. If omitted, the drive will be marked unloaded. -ro or -rw may be given, the default is -rw. -cd defines a CDROM (and implies read-only access). -ro or -rw (default) will set up an ATA drive. -cd will set up an ATAPI drive. ioapicid Defines the ID number for the IOAPIC. The default is 15. It should be .ge. the number of CPUs given in the 'cpus' command. pciirqs ... Defines the irq numbers to be assigned at boot time. The default is 9, 10, 12. In the command, separate the IRQ numbers with spaces. ram Defines the amount of RAM to have, in megabytes. The range is 16 to 1024. reset Resets everything to power on state. It will finalize configuration if not already. rtl8139 <0..3> / Define an rtl8139 ethernet card. You can have up to 4 of them, numbered 0 thru 3. Pcibus must be 0, pcidev must be in range 1 thru 19. The parameter says which Linux ethernet card to send stuff out on and to listen on for receive packets. The parameter says what MAC address to assign to the emulated device. You must be running as root in order for this to work. It won't communicate back to the host computer, it will only see other computers. run Start executing. Execution stops when either: 1) Control-C is pressed 2) A breakpoint is reached (and not resumed by an associated run command) 3) All devices (including CPUs) enter the halt state script ... Execute commands from the given files. status With no arguments, prints out the default status from all devices that support a status. status [] Prints status of the given device, optionally passing the given args. See description for the device to see if there are any args that can be passed. The X86 CPU's can take extensive arguments. step Steps the state of the given device. For a CPU, this would typically execute one (or more) instructions. For an IO device, it may move the transfer along a few bytes or request an interrupt. You can step the stepper, which will post an expired timer or a ready fd select to a device. Do a 'status stepper' to see what is ready to be posted. DEVICES Generally, there is an Emmy 'device' for each real-world device being emulated. You can list out the devices Emmy has defined with the 'devices' command. Devices can either be running or halted. Devices put themselves in one state or the other depending on their needs. For example, an X86 CPU will be halted when it executes an HLT instruction or gets a fatal error such as a triple fault. Otherwise, it will be in the run state. The floppy device will be in the run state while it is performing a transfer, otherwise it puts itself in the halt state. Devices that are in the run state will receive processing time by the run command. Any that are halted will not. If the run command finds it has no devices running (including the stepper), it will exit (as there is nothing that could possibly bring it out). You can also give a device that is in the run state an increment of processing by using the 'step' command. So you could step a cpu to advance it while leaving all other device as they are. ATAn Hard drives and CDROMs. CPUn The CPUs you have defined, numbered 0 through 14. The status command can have extensive arguments to display many aspects of the CPU and its environment. Arguments can consist of: 1) Register names (see table below) 2) EIPTRACE - prints out the CS:EIP SS:ESP of the last 64 steps 3) INST[@address[..address]] - prints out disassembly of instructions 4) LOG [ ] - logs status to a file 5) STEP_FAST, STEP_INTERP, STEP_SINGLE - sets the stepping mode 6) XLATE[@address[..address]] - prints out disassembly of instructions along with the corresponding translation, if any 7) general C-syntax-like expressions of registers, etc Register names: These real CPU registers: CR0,CR2,CR3,CR4 CS,DS,ES,FS,GS,SS DR0,DR1,DR2,DR3,DR6,DR7 EF (eflags) EAX,EBP,EBX,ECX,EDI,EDX,EIP,ESI,ESP FCT (floating-point control) FST (floating-point status) GDT,IDT (prints limit then base), LDT (prints limit, base and segno) ST0..ST7 (floating-point data registers) TSS (prints size.base[selector]bits) These pseudo registers: ASY - async register, indicates 'i' for pending IRQ, 'n' for pending NMI CYC - number of cycles (steps) made on this CPU EXL1 - emmy source line number that threw first exception EXL2 - emmy source line number that threw second exception EXL3 - emmy source line number that threw third exception EXV - exception vector of last instruction, or 0xFFFFFFFF if none ID - the CPU number (0..14) IOAD - IO address (port number) referenced in last instruction IOSZ - size of IO port referenced in last instruction NMI - nmi delivery state: "ena" if enabled; "inh" if inhibited RMLA,PA,SZ - memory linear address, physical address, size read in last instruction STATE - state of CPU: "run" if running "wfi" if waiting for interrupt (HLT instruction) "hfe" if halted on fatal error (triple fault) STEP - stepping mode: "fast" if translating "interp" if interpreting "single" single-stepped translating SVEIP - saved EIP at beginning of last instruction WMLA,PA,SZ - memory linear address, physical address, size written in last instruction C-like expressions: The C-like expressions can be used to print out complex things. One thing different to watch out for is they are evaluated from left to right (no precedence), so you may need parentheses. They accept the above registers as operands as well as decimal and hexadecimal constants. There are also suffix operators to read memory and IO ports: (address)vl - reads the longword at virtual (linear) address 'address' (address)pl - same thing, only at a physical address The first letter must be v, p or i (for IO port) The second letter must be b, w, or l to indicate byte, word or long So to display the first arg of a subroutine use (ebp+8)vl. (0x20)ib will display a byte from IO port 20. If there is a pagefault or other error accessing memory or IO port, you will get an error message but the state of the CPU will not be altered. Reading an IO port or a memory mapped IO register may have side-effects, depending on the definition of the device. The break command, when used with a cpu, uses the same expressions when evaluating its argument. If the result of the expression is non-zero, the breakpoint is considered to be attained, and the resultant action will be taken. Breakpoints are only examined between steps, so if the CPU is in FAST stepping mode, the breakpoints will not be checked after every instruction. Breakpoints are actually 'compiled' on the fly by the emulator so they have minimal impact on execution time. FLOPPYn Floppy disk drives, numbered 0 through 3. A status command will show what's going on with the drive. PIT This is the typical PC-type programmable interval timer (18.21Hz). It provides interrupts at a programmable rate. Only timer number 0 does any thing, the others are ignored. It provides interrupts at the true rate, so your software should be able to keep accurate time. STEPPER There is one built-in device called the stepper. The stepper device is a catch-all that works the internal timer queue and the select queue. If there are no other running devices in the system, the stepper will execute a select system call to wait for something to do. So, if your emulated program executes an HLT instruction and all peripherals are idle, EMMY will be in a select call and will not be eating up host cpu cycles. Displaying the status of the stepper device will show you which other objects are running and what devices are waiting for selects and timers.