genode/base-codezero/tool/gen_romfs
2011-12-22 16:19:25 +01:00

203 lines
5.6 KiB
Python
Executable File

#!/usr/bin/python
import os, re, getopt, sys
from stat import ST_SIZE
from subprocess import PIPE, Popen
verbose = 0
# return address of 4K page following the spefified address
def round_page(addr):
page_size = 0x1000
return (addr + page_size) & ~(page_size - 1)
def first_free_addr_after_program(elf, cross_prefix = ""):
try:
objdump = cross_prefix + "objdump"
objdump_output = Popen([objdump, "-p", elf],
stdout=PIPE).communicate()[0]
except OSError:
print "Error: execution of " + objdump + " failed, invalid cross-tool prefix?"
exit(3)
#
# The output of 'objdump -p' contains the list of program segments. Each
# segment has two lines of text, the first containing the 'vaddr' value and
# the latter containing the 'memsz' value. For each line, we match for both
# 'vaddr' and 'memsz' fields. When observing a line with a 'memsz' field,
# we know that the previous line contained the corresponding 'vaddr' and
# that the end address of the segment is the sum of the current 'vaddr'
# and 'memsz' values.
#
max_end_addr = 0
for line in objdump_output.splitlines():
match_vaddr = re.compile(".*vaddr (0x[0-9a-f]*).*").match(line)
match_memsz = re.compile(".*memsz (0x[0-9a-f]*).*").match(line)
if (match_vaddr):
vaddr = int(match_vaddr.group(1), 0)
if (match_memsz):
memsz = int(match_memsz.group(1), 0)
max_end_addr = max(max_end_addr, vaddr + memsz)
# align the first free address at the next page boundary
return round_page(max_end_addr)
def generate_modules_asm(modules):
"""
Generate assembly code aggregating boot-module data from specified files.
The generated assembly code looks as follows:
/*
* The ELF image consists only of a data section. At file offset 0, there
* is a magic cookie that core validates when accessing the ROM fs. It is
* followed by the end address of the meta data.
*/
.section .data
.string "GROM" /* magic cookie used by core to identify a ROM fs image*/
.long header_end /* end of ROM fs meta data */
/*
* Each module is represented by a struct of 3 long values. The first
* value is pointer to the module name. A null-pointer marks the end of
* the module list.
*/
.long mod1_name /* pointer to the null-terminated module name */
.long mod1_start /* pointer to the module data */
.long mod1_end - mod1_start /* size of the module data */
.long 0
/*
* For each module, there exists a null-terminated string labeled with
* 'mod<index>_name' referenced by the module list above.
*/
mod1_name:
.string "name of data module"
.byte 0
header_end:
/*
* The data of each module must be aligned at a page boundary to enable
* the mapping of individual modules to different address spaces.
*/
.align 4096
mod1_start: .incbin "data"
mod1_end:
"""
asm_src = ""
# header
asm_src += ".section .data\nmodule_list:\n"
asm_src += ".ascii \"GROM\"\n"
asm_src += ".long header_end\n"
# module list
i = 1
for module in modules:
asm_src += ".long mod" + str(i) + "_name\n"
asm_src += ".long mod" + str(i) + "_start\n"
asm_src += ".long mod" + str(i) + "_end - mod" + str(i) + "_start\n"
i = i + 1
asm_src += ".long 0\n"
# module names
i = 1
for module in modules:
asm_src += "mod" + str(i) + "_name: .string \"" + os.path.split(module)[1] + "\"; .byte 0\n"
i = i + 1
asm_src += "header_end:\n"
# module data
i = 1
for module in modules:
asm_src += ".p2align 12,0\n"
asm_src += "mod" + str(i) + "_start: .incbin \"" + module + "\"; "
asm_src += "mod" + str(i) + "_end:\n"
i = i + 1
return asm_src
instructions = """
usage: gen_romfs [-v] [-p <cross-prefix>] -c <core-elf> -o <output> [modules ...]
Generates Genode ROM file system as ELF file loadable into a Codezero container
-c|--core ELF binary of Genode's core
-o|--output name of ELF image to generate
-p|--prefix cross toolchain prefix
-v|--verbose print details about generated ROM file systemn
"""
def usage():
print instructions
def user_error(message):
print "Error: " + message
usage
sys.exit(2)
# default values for command-line arguments
cross_prefix = ""
core_elf = ""
dst_elf = ""
# parse command line arguments
try:
opts, modules = getopt.getopt(sys.argv[1:],
"c:o:p:v",
["core=", "output=", "prefix=", "verbose"])
except getopt.GetoptError:
usage()
sys.exit(2)
for opt, arg in opts:
if opt in ("-c", "--core"):
core_elf = arg
elif opt in ("-o", "--output"):
dst_elf = arg
elif opt in ("-p", "--prefix"):
cross_prefix = arg
elif opt in ("-v", "--verbose"):
verbose = 1
else:
user_error("invalid argument \"" + arg + "\"")
# validate arguments
if (core_elf == ""): user_error("no core binary specified")
if (len(modules) == 0): user_error("no modules specified")
if (dst_elf == ""): user_error("no output file spefied")
# determine destination address of the modules ELF image
modules_start_addr = first_free_addr_after_program(core_elf, cross_prefix)
if (verbose):
print "module address: " + hex(modules_start_addr)
# generate assembly code aggregating the module data
asm_src = generate_modules_asm(modules)
if (verbose):
print "generated assember code:"
for line in asm_src.splitlines():
print " " + line
# invoke assembler and linker through the gcc front end
gcc_cmd = [cross_prefix + "gcc",
"-nostdlib",
"-x", "assembler",
"-Wl,--entry=0",
"-Wl,--section-start=.data=" + hex(modules_start_addr),
"-o", dst_elf,
"-"]
if (verbose):
print "gcc command line:"
print " " + ' '.join(gcc_cmd)
Popen(gcc_cmd, stdin=PIPE).communicate(asm_src)[0]