The RemoteBIOS Project.
Note: My native language isn't English, i have never learned,
please ignore my ugly English. :)
Urbez Santana i Roma, Jan 2008
Introduction.
The RemoteBIOS project, are a tool for write a binary ROM file, called remotebios.bin, that aids the developer for construct a BIOS, for a mainboard, for example for embedded platforms, or Coreboot project.
The tool, makes possible to run scripts, that manages all hardware important routines, for example, rdmsr,wrmsr, outb, inb, write_pci, read_pci, mov_cr0, etc, remotely trough an Serial Port connection to the Mainboard.
With this tool, you can for example, write a script (a *.c program) that is executed in a developer machine, and the Mainboard with RemoteBIOS. For example, for testing, with a raminit, some times you need test if the routine works, if not, change and test now, etc. The tool, admits, trough a console, make interactive commands, also used by the scripts, for test and show the state of the Mainboard.
It is very risked to write continuously the EEPROM or Flash ROM, for test these type of programs, and slow, in the case, that you need a external Programmer.
If you write RemoteBIOS one time, you can make any type of tests and send for example trough Serial Port ELF binaries and then execute it, and if not works, reset the Mainboard, and test now, with the changes that you have in your program.
If you write a BIOS embedded program, with this, you can test your ELF application, and finally, use the same way to create RemoteBIOS, and build your Final BIOS.
If you are developing Coreboot for your mainboard, you can test, it, simply send Coreboot_ram elf file, to the RemoteBIOS, and then execute it, and test why not works, for example. This not have a remote debugger but, I am writing one that will appears in the future, i hope :)
Requirements.
At the actual time, you need a Mainboard that have Serial Port, if your mainboard not have Serial Port, you must substitute them, with another basic protocol, for example Parallel port, or SMBUS, with the appropriate hardware, and then, write the routines for show the messages, in the file 'serial.c'
A mainboard with a CPU that have at minimum MMX with extended instructions or with SSE, SSE2, and early CPU's. If you not have MMX, with the instructions 'pextrw' and 'pinsrw', you must written your code in xmmstack, with the pay of use a MMX register only for transfer with 'punpckhdq'.
Why need MMX???
Because the remotebios project are written in C, not all in assembler, and I known only a C compiler without use stack, that is part of Coreboot project, and with the limitations of romcc, RemoteBIOS cannot compile with them.
I have write a tool that RemoteBIOS uses, for compile your C program with GCC, and use the assembler *.s output of gcc, to eliminate all stack references, trough MMX registers or XMM registers, simulating a stack. Yes a little stack, but, is enough for some programs.
With MMX extended, you can use 64 bytes of stack, and with SSE2 128 bytes.
With SSE is possible to have 128 bytes of stack, but I have not implemented.
With SSE2 is possible to use 128+64 (XMM and MMX registers) but I have not implemented yet.
All of these last possibilities, come later in the future.
Exist another little limitation with 'xmmstack' program, that I explain too later in the document.
The RemoteBIOS project are developed with GNU C, and a linux machine.
Is not in my head to port to another platform.
You must have gcc 4.x.x, if possible.
For configure, 'dialog' program.
For connect to the RemoteBIOS trough Serial Port as a terminal, is
recommended have 'minicom' program.
For test the remotebios.bin locally is also recommended have 'bochs'
program, if you will modify RemoteBIOS.
Configure.
You can configure the project, just running the program:
./configure
in the project directory.
This configuration tool, ask you for:
*The language, yet only 'English', 'Catalan' and 'Spanish'.
*The serial port type.
Normally the Serial port, not need any initialization that the PC80 typical initialization but, for example VIA southbrigdes, need trough PNP activate, and then the PC80 initialization.
Another Serial Port, that not work, with PC80 directly, or not are VIA, you must write your 'serial.c' code, it is easy, not?
The serial port, or another system for connect with RemoteBIOS are crucial, you are welcome for write a new communication way, if your board not have Serial Port. If your mainboard no have a simple way for communication, sorry, you cannot use RemoteBIOS.
Configure....
*The BIOS ROM size.
Depends of the chipset of your BIOS, you have more or minus size for writing the remotebios.bin
64 Kbytes are the minimum required for RemoteBIOS, but can fit in more little ROMs.
*Reserved ROM size for the remotebios.bin program.
It is asked, because, RemoteBIOS can attach with self, up to 10 ELF programs.
This can be utile if you have consolidated a ELF program that works fine, and must
go to next stage of testing with your mainboard, and will you flash your ROM for a second time for gain time.
The normal way, is send the ELF aplication, trough 'bioscp' program, and then,
execute it, with the command 'load' of remotebios, with 'minicom' terminal for example.
The rest of free ROM space, are reserved for these ELF files, for example, if you
have a 128 Kbytes ROM, and you reserve 64K for remotebios, you have 64K for your programs.
*Adding ELF files to remotebios.
Just type the path, of the ELF binary, and configure adds it to the remotebios if you accept finally the configuration.
*Finish the configuration.
If you answer YES, you change the actual configuration, for the specified in ./configure.
Then you can compile the program, and test it :)
Compiling the remotebios aplication.
Simply type 'make' at the RemoteBIOS path.
If you have errors, sorry, normally you must not have.
You can compile also the 'bioscp' and the 'biossh' scripts, simply enter into
respective subdirectories, and type 'make'.
Finally if you will test a ELF example program that you can send with 'bioscp'
run 'make' into the subdirectory 'test'
The remotebios.bin
If you want test the remotebios.bin, before write any ROM, you can use Bochs x86 emulator, compiled with support of SSE2 for familiarize with the application, and how works, for be prepared to use in the real ROM.
You can use another program that Bochs, but I explain, how with them.
When you start bochs, go to option 3 (Edit options), then change to the 5 (Memory Options), and now the option 2, for change the Path of the BIOS, and type the path of the remotebios.bin binary, and the start of the bios (0x100000-ROMSIZE).
Exit from this menu, and go to the option 11 (Serial & Parallel Ports), and configure the serial port to 1 to the device '/dev/pty3' (term mode) for example, or another pty.
Exit all menus, and save the configuration, and go to the simulation (option 5). If you have debugger enabled, start with continue command.
Ok, the remotebios.bin is working, and you can test it, with the command:
minicom -p /dev/ttyp3 if you have used /dev/ptyp3 in bochs.
The next text appears to the minicom terminal:
|
BIOS Test Program. Write 'help' for help, and 'help command' for each command. Cmd: |
If you have start minicom, before bochs start, you can see exactly the last information, if not, you not see any, until you press a Enter, and remotebios show to you the 'Cmd: '
propmt.
Here you can type 'help', and the help, puts to the Serial terminal, the list of commands:
|
This help. You can also help commands with 'help <command>' List of Commands: help wpci rpci getb getw getl putb putw putl boot dump rmsr wmsr rcr0 wcr0 rcr2 wcr2 rcr3 wcr3 rcr4 wcr4 inpb inpw inpl outb outw outl cpid exec puts gets load invd copy test recv |
You can see that all commands are four characters, yes :) not a coincidence.
Here is the time of explain each command:
Note that all commands must be typed in lowercase
REMOTE COMMANDS.
When you are manually connected to RemoteBIOS trough the serial port, you can type the commands that follow this paragraph, but you be careful with the use, this is not a real readline prompt, only you can only correct the command that you are typing, with backspace, or the parameters, but alone each, you cannot correct all of the line, when you type the space for separate the parameters, you can only erase the last parameter. This could be dangerous if you are using commands of the type 'wpci' or 'wmsr', you must be sure before you press the space. This system will be better in the future. Now the comands:
help
Syntax: help
Shows the list of commands.
Syntax: help <command>
Shows a short explanation of the command and syntax.
wpci
Syntax: wpci <size> <cfgreg> <value>
Writes to the PCI configuration register, the specified value.
size, must be 1,2 or 4 (bytes that you want to write to the pci register).
cfgreg, are in hex, and represents the BUS, DEVICE, FN and REG.
for example, if (BUS,DEV,FN,REG)=(0,2,3,4f) then you must write
134f in this parameters, each value with the size in bits that are specified
in the PCI standard configuration. 8 bits REG, 3 bits FN, etc.
value, are in hex, and is the value that you write in this Register of PCI.
rpci
Syntax: rpci <size> <cfgreg>
Reads from the PCI configuration register, and shows it.
size, must be 1,2 or 4 (bytes that you want to write to the pci register).
cfgreg, are in hex, and represents the BUS, DEVICE, FN and REG.
for example, if (BUS,DEV,FN,REG)=(0,2,3,4f) then you must write
134f in this parameters, each value with the size in bits that are specified
in the PCI standard configuration. 8 bits REG, 3 bits FN, etc.
The result are in hex.
Note that all integers are in hex, I not say more that the values are in hex, and without the typical 0x of C syntax.
Remote Commands....
getb
getw
getl
Syntax: getb <addr>
Reads from the Physical Address space a byte/word/longword. For example if you use one address of the ROM space, you can show the contents of the ROM, and If you have initialized the RAM, with a script, you can read the RAM, or one mapped PCI device.
putb
putw
putl
Syntax: putb <addr> <value>
Writes to the Physical Address space a byte/word/longword. For example if you use one address of the RAM space, you must have initialized the RAM first with one script.
boot
Syntax: boot
Forces a reboot, with a exception, not all CPU's do the reboot, I must make a new code for boot, better, with a triple fault, or a invalid opcode.
boot
Syntax: dump <pci_device>
Shows all contents of a PCI device. The 256 bytes are written in 16 lines of 16 bytes.
The pci_device, must be in form of the bit size of fields (BUS,DEV,FN) in hexadecimal.
For example If you write:
|
Cmd: dump 88 00:06112732870010020000010600008000 10:00000000000000000000000000000000 20:00000000000000000000000000000000 30:00000000C00000000000000000000000 40:00800000000000000C20000004000000 50:001D0000000000002000000100000000 60:00000000000000040000000000000000 70:00000000000000000000000020000000 80:00045000000000000100000000000000 90:00E6390080400F00100A000000000000 A0:00000000000000000000000000000000 B0:00000000000000000000000000000000 C0:01000200000000000000000000000000 D0:01000000000000000000000000000000 E0:00000000000000000000000000000000 F0:0000000000000B000000000000000000 |
The last contents are of the “00:11.0 ISA bridge” of one mainboard.
Remote Commands....
rmsr
Syntax: rmsr <ix>
Reads the MSR register specified with <ix>, and outputs to the terminal the contents of the register.
wmsr
Syntax: wmsr <ix> <hi> <lo>
Writes to a MSR register specified with <ix>, the value, decomposed in <hi> and <lo> longwords. In the configuration of a lot of VGA's and the CPU, are necessary.
rcr0
rcr2
rcr3
rcr4
Syntax: rdcr[0|2|3|4]
Reads the Configuration Register CRx of the CPU.
wcr0
wcr2
wcr3
wcr4
Syntax: wrcr[0|2|3|4] <value>
Writes a longword in the Configuration Register CRx of the CPU.
You can disable the Protected Mode bit of CR0, but the remotebios is
not affected.
You can enable SSE instructions with CR4, and all that these registers implement.
inpb
inpw
inpl
Syntax: inp[b|w|l] <addr>
Reads from the IO space, at the 16 bits address, one byte, word, or longword.
outb
outw
outl
Syntax: out[b|w|l] <addr> <value>
Writes to the IO space, at the 16 bits address, one byte, word, or longword.
cpid
Syntax: cpid <ix>
Executes the CPUID instruction, with the index IX.
Shows the 4 longwords of the CPUID in order by most significant to less.
Remote Commands....
exec
Syntax: exec <esp> <eip>
Jumps to a ELF binary or a another routine. Note that the RAM must be initalized
if you will jump to the RAM.
puts
Syntax: puts <addr> <val> <val> ....... [ESC]
Writes longwords to the Physical address, and ends if you press the ESC key.
Be careful not write a <num> value too high, if you do that, is possible
gets
Syntax: gets <num> <addr>
Reads a num of longwords of the Physical address.
that you not see the end of the output, and then you must reboot the mainboard.
Hehehehehe. I have some times exchanged the num and the address :)))
load
Syntax: load <num or address>
If <num> are 0..9 the remotebios tries to install the ELFs binaries that you have included in remotebios. And then executes the binary.
Normally you not have included any ELF binary in remotebios, and goes nothing.
If <num> are greater than 9, then tries to install a ELF binary located in the address
specified, and then executes it. If the memory no contains a ELF i386 binary, remotebios goes nothing, and returns to the prompt 'Cmd:'
NOTE, that you must have initialized the RAM, with a script (use the 'biossh' subdirectory to generate one script).
You can send a ELF binary to the RAM, with the 'bioscp' program, located at one
arbitrary location, and then, load the ELF and execute it, with the 'load' command.
invd
Syntax: invd
Executes a 'wbinvd' instruction, to invalidate the cache, and write back.
Initialize to a value a portion of RAM:
Remote Commands....
copy
Syntax: copy <dst> <src> <len>
Makes a memcpy, for test the RAM memory, or for another purposes, for example
if you type:
|
putl 100000 0 copy 100004 100000 10000 |
Then you have initialized the memory 100000 to 110000 with zeroes.
Is useful to test if RAM works in conjuntion with 'test' command copying the contents of the rom to the RAM, and test if are the same.
Also you can show the memory copied with 'gets' command.
test
Syntax: test <dst> <src> <len>
Makes a 'memcmp', for compare regions of the memory, returns 1 if are equal
and 0, if not.
recv
Syntax: recv
Enters in Receive MODE, this mode uses the stack, because the utility
is for write in RAM your programs. This are designed for use with 'bioscp'
You can show the prompt “Recv: “ instead 'Cmd: '.
With 'exit' you can return the normal mode.
With 'help' you can give a little help.
With 'pack <num>' you can specify the number of packets before the RemoteBIOS
sends to you a 6bit checksum. I recommend for more speed use a value of 6 or 8.
With 'addr <address>' you can indicate where in RAM are the copy.
With 'data ' you initiate the transfer. With 'ESC' key, you can exit transfering.
if you send a 'SPACE' key, indicates that the checksum are wrong, and must repeat
the entire last packet.
Each packet consist in 4 characters, in a conversion of 3 bytes to 4 readable characters. The formula are that each 6 bit of the 24 bits (3 bytes,4 elements) are converted in 4 characters, adding 40 a '(' character to the 6bit value.
The last command, that 'bioscp' realizes are the 'csum' command for test if the
complete transmission are okey or wrong.
At the moment, these are all of the commands implemented.
You are free, to add that you needed. But normally these are the typical, that
RemoteBIOS needed for have control of all Hardware in your mainboard, remotely.
The BIOS-SH Remote scripts.
The program 'biossh' are written, for have true speed, writing your test routines, in C,
that the biossh Library allows. For example you can initialize the MTRR registers, for
have more performance in the execution of commands in the slow ROM.
I recommend that 'mtrr', must be the first script that your run, and then, the Memory initialization.
Here are 3 samples of C scripts:
scripts/mtrr.c
This scripts, initializes the Memory Type Range Registers, and is useful if you will that the remotebios program works better, using the cache in ROM.
scripts/cn_raminit.c
This sampe, is a raminit for a VIA CN10000 Mainboard, this works, and I hope that
works on another C7/CN700 mainboards. This scripts are a extraction of the recently work with CN700 from Corey Osgood in Coreboot, but have 2 differences,
that are needed for the type of RAM that I use (Kingstom), the SDRAM init, works only with the 20 bit active. (In the function sdram_end_init() I have added configuration of the Brigde device (0,1,0), for mark the secondary BUS at byte 0x19 and 0x1a. This is necessary because FILO autodetects the PCI using this byte, and
hangs if it isn't correct. The RAM_COMMAND_MRS, are all changed with the 20 bit
active, if not are activated, with the concrete RAM that I use, does not work.
scripts/via_ide.c
This activates the IDE, for access to the Primary Master IDE device.
The BIOS-SH Remote scripts....
If you will make a new script, create in the directory 'scripts' of the subdirectory 'biossh' your *.c script. When you will compile it, use 'make' from the 'biossh' directory, or type 'make my_new_script' where my_new_script are the name of the new script created.
In the Script, you can use the Remote Call Functions, that works remotely with RemoteBIOS:
void out_port(int sz,uint16_t addr,uint32_t val) ;
Writes into RemoteBIOS mainboard a byte/word/longword ('sz'=1,2,4) into
the IO address 'addr' the value val.
uint32_t in_port(int sz,uint16_t addr) ;
Reads from the IO space at address 'addr', one byte/word/longword depends on
the value of sz, 1, 2 or 4 (bytes).
void cpuid(uint32_t ix,uint32_t *a,uint32_t *b,uint32_t *c,uint32_t *d) ;
Reads the result of a execution of the CPUID command.
void rdmsr(uint32_t ix,uint32_t *hi,uint32_t *lo) ;
Reads from a MSR register from the Remote CPU, and stores the results in
the referenced parameters 'hi' and 'lo'.
void wrmsr(uint32_t ix,uint32_t hi,uint32_t lo) ;
The reverse function of rdmsr, writes the 'hi' and 'lo' to the Index MSR specified by
the parameter 'ix'
uint32_t read_cr(int cr) ;
Reads from the Config Register CR0, CR2, CR3 or CR4, if 'cr' are (0, 2, 3 and 4 respectively)
void write_cr(int cr,uint32_t val) ;
Writes to the Config Register CR0, CR2, CR3 or CR4.
uint32_t read_phys(int sz,uint32_t addr) ;
Reads from the memory space, one/two or four bytes, specified in 'sz'.
The BIOS-SH Remote scripts....
void write_phys(int sz,uint32_t addr,uint32_t val) ;
Writes to the memory space, one/two or four bytes, specified in 'sz'.
int pci_read_config(int pcidev,int reg,int len) ;
Reads from the PCI Configuration Register, 'reg', of the device 'pcidev'. In the 'len'
you tell the number of bytes that you will read, (only 1,2 and 4).
You can use the macro: PCIDEV(bus,dev,fn) that facilitates the task.
void pci_write_config(int pcidev,int reg, int val,int len) ;
Writes to the PCI Configuration Register, 'reg', of the device 'pcidev'. In the 'len'
you tell the number of bytes that you will written, (only 1,2 and 4).
void wbinvd() ;
Executes a wbinvd in the remote CPU.
void enable_cache() ;
Enables the cache, using CR0 and the instruction wbinvd
void disable_cache() ;
Changes the apropiate CR0 bit for that. Yes this is not needed, you have the write_cr function :)
Finally, you can write your routines in the script, or change the code 'biossh.c' if you needed.
Parameters when you execute your script:
./script -d <serial_device> -t <fifo_size> -h --help
With -d, you can change the default /dev/ttyS0 serial device for /dev/ttyUSB0 if you use
a USB serial port, or you can use /dev/ttypX, if you are testing with bochs or another.
With -t, you can change the number of max Serial packet.
With -h and --help, you give a cutre help
The BIOS-CP Utility program.
With 'bioscp' you can send to the RemoteBIOS, a complete binary, if you will test it, then
you can use 'minicom' for connect to the RemoteBIOS, and use the 'exec' function if you have send a RAW binary.
The normal use, are sending ELF binary's, for example, you can test with the 'test' example given in the project directory.
You can use 'test' as a base of one bigger BIOS embedded project, and write the definitive program.
Remember, that you must have run a script that initializes the memory, except if you use an emulator.
It is highly recommended, that you make a 'strip' command in your ELF binary, before you send to the RemoteBIOS.
Syntax of 'bioscp'
bioscp <file.elf> -a <hex address ram> [-l length] [-o offset] [-d <serial device>]
[-t <fifo_size>]
You must specify a address that not are the same that the ELF binary, will be installed, use
'objdump -x' command, if you want wise where the ELF will be ubicated.
The file.elf are complete send, to the RemoteBIOS, at the addres that you specify, with
-a parameter. With -l you can change the number of bytes that you will send.
And with -o you can change the offset that will start from your elf file.
The parameter -d permits you change the default serial device (/dev/ttyS0)
With -t, you can specify, the size of serial packet (default 4)
Be sure that you compile your ELF binary without the standard C library's, and have all simbols, using the -nostdlib and -static flags, with gcc.
You have an example in the directory 'test'.
You can test Coreboot here, if you compile your Coreboot version, you can use
the generated “coreboot_ram” that is the ELF file, that the stage1 of Coreboot, copies to the RAM, and jumps into.
With bioscp, you can send, the binary (strip them first) into the address 0x100000 for example (./bioscp -a 200000 -d /dev/ttyUSB0 -p 6 coreboot_ram). Then you can use minicom, for execute the instruction 'Cmd: load 200000', and RemoteBIOS automatically detects the ELF binary, and installs it, then, call to it.
This is a good way to debug and test your Coreboot, without write the flash.
Note that wold be better, if we have a remote debugger.
If you will use FILO with coreboot that you testing, remember, that coreboot, tests the ROM section CONFIG_ROM_PAYLOAD_START each 16 bytes.
For that, if you will, you can add FILO in the ./configure, and be sure that you reserve
the size for RemoteBIOS, that are the same that reserves Coreboot for it's payload.
See the value in coreboot.map, if the value are the same that are in the file rom_addr.inc'.
.ROM_EXES = 0xfffd0000;
in rom_addr.inc for example and...
fffd0000 A CONFIG_ROM_PAYLOAD_START
in the coreboot.map file.
I'm writing a ELF binary, for debug remotely, for perfect development of the Coreboot or Embedded programs, or start programs. But sorry, since is not finished.
You can make if you will, a emulator with debugger, and make a program that runs your Program, in the emulator, and executes the hardware instructions in RemoteBIOS, for debugging purposes.
Note: I must write a better documentation.
Improve the xmmstack program.
Make a binary for FAST communication trough Serial Port, for better performance with 'bioscp'
Terminate the remote debugger, I will be happy.
The XMM-STACK Utility program.
The intention, of this utility, are build the RemoteBIOS project ROM, with a code that not uses the stack, like ROMCC from Coreboot. But I cannot compile succesfully the RemoteBIOS with the ROMCC utility, although it is a great program.
I have used the output of the GCC in i386 or pentium assembler, with the obligatory options: -fomit-frame-pointer -fno-stack-protector.
With these options, I have the ESP and EBP registers plus DR0,DR1,DR2 and DR3
for manage a virtual stack with XMM registers or MMX.
These utility can be used for another purposes that RemoteBIOS. But, you must known
what you can do in C program, and what not.
The XMM-STACK Utility program....
Important, xmmstack does not allow non readonly pointers, if you want
return more than one integer in a function you cannot write this:
int your_function(int *ret1,int *ret2)
My first version of xmmstack does not follow stack assignations, and
you cannot write in *ret1 or *ret2. For return
3 integers you must use a struct:
typedef struct {int ret0,int ret1,int ret2} retA;
retA your_function()
Be careful with "asm" statements, if the asm statement returns info,
you must use "asm volatile", if not, gcc, can optimize wrongly your
code.
The stack overflows jumps to a halt function, i must in the next version
accept a global function for reinit the stack and print one error, if your
program consumes too stack memory, it halt if overflow the stack.
Remember, i hope that the most mainboards have the MMX extensions
minimum, but the mainboards that not have MMXE, not work with xmmstack.
If you use xmmstack -xmm , you must activate the bit 9 in CR4 for have
the SSE2 extensions. With xmmstack -mmx (remotebios use only -mmx) normally is enough, if you control that your program not use excesive stack. Yet the only way for wise if have enough stack, is run the program, sorry.
Remember that if you use -mmx, you cannot use floats and doubles in your
code.
Note that with SSE2, we can implement one stack with XMM and MMX registers, and have a amount of 8*4+8*8 bytes, and if you have a AMD, use the 16 registers, but in the moment, are no needed for this application, but, if you need it, you can implement.
END OF DOCUMENT