创建Avatar²需要的第二片信息是内存布局的规范。 Avatar持续对所有内存范围的跟踪并将生成的包含所有范围的内存映射送给独立的目标。
内存范围定义
增加一个内存范围很简单。假定存在一个叫’avatar’的Avatar²对象,使用下面这一行来创建0x40000000处大小为0x1000的基础内存区域就足够了:
dummy_range = avatar.add_memory_range(0x40000000, 0x1000)
内存范围是非常灵活的,在创建时允许多种多样的附加关键字,其中的一些可能仅仅被特定种类的目标使用。下面是所有独立目标创建内存范围时可能使用的关键字参数的列表。
Keyword | Description |
---|---|
name | 内存范围的可选名 |
permissions | 文本形式表示的权限。默认: ‘rwx’ |
file | 包括内存初始内容的文件路径 |
forwarded | 是否可访问要转发给特定目标所需的那个内存范围 |
forwarded_to | 如果启用了转发,则引用将处理内存访问的目标。 |
emulate | 对所给内存范围的avatar外设进行仿真 |
内存转发
Avatar²的一个核心特征就是执行与内存访问的分离。这允许在不同目标之间转发内存的访问。例如,一个固件镜像可以在一个模拟器中执行,而所有的内存访问可以被转发到真实物理设备。
转发规则自身在配置内存范围时就通过forwarded与forwarded_to参数设置好了。假设我们在QEMU中分析一个包含内存映射外设的物理设备。一个典型的内存范围配置如下:
mmio = avatar.add_memory_range(0x4000000, 0x10000, name='mmio',
permissions='rw-'
forwarded=True, forwarded_to=phys_device)
ram = avatar.add_memory_range(0x2000000, 0x1000000, name='ram',
permissions='rw-')
rom = avatar.add_memory_range(0x0800000, 0x1000000, name='rom',
file='./firmware.bin',
permissions='r-x')
Qemu-Target Peripheral Emulation Ranges
由于Qemu是一个完整的系统模拟器,他也可以模拟大量外设。逻辑上来说,Avatar²可以通过指定memmory range里的目标的特定关键词 ‘qemui_name’与’qemu_properties’参数来受益于此特性。
例如,一个非常普通的串口设备,可以在Qemu中仿真而不用转发I/O访问给物理设备,正如下面这个例子:
# Properties as required by qemu
serial_qproperties = {'type' : 'serial', 'value': 0, 'name':'chardev'}
serial = avatar.add_memory_range(0x40004c00, 0x100, name='usart',
qemu_name='stm32l1xx-usart',
qemu_properties=serial_qproperties,
permissions='rw-')
# Provide serial I/O via tcp
qemu.additional_args = ["-serial", "tcp::1234,server,nowait"]
Avatar² Peripheral Emulation Ranges
不幸的是,Qemu不支持所有现存的外设,也不支持每个使用Qemu目标的Avatar²设置。 因此,Avatar²允许用AvatarPeripheral类来指定用户定义的外设实现。
为此,需要两步:
从 AvatarPeripheral创建一个子类,它在__init__ 函数中定义定制读写处理程序。
将该类的引用传递给内存范围的模拟关键字。下面这个例子提供了一个HelloWorld外设的实现,它每次读取时返回了的字符串’Hello World’的另一部分。
from avatar2 import *
class HelloWorldPeripheral(AvatarPeripheral):
def hw_read(self, size):
ret = self.hello_world[:size]
self.hello_world = self.hello_world[size:] + self.hello_world[:size]
return ret
def nop_write(self, size, value):
return True
def __init__(self, name, address, size, **kwargs):
AvatarPeripheral.__init__(self, name, address, size)
self.hello_world='Hello World'
self.read_handler[0:size] = self.hw_read
self.write_handler[0:size] = self.nop_write
[...]
hw = avatar.add_memory_range(0x40004c00, 0x100, name='hello_world',
emulate=HelloWorldPeripheral, permissions='rw-')