Gxemul将内存空间分为两部分管理,一部分是物理内存,地址分配到0x00000000,另一部分是device的地址,注册device的时候由device自己指定空间
1 创建物理内存
memory.cc memory_new
{
初始化物理内存参数
mem->physical_max = physical_max;
mem->dev_dyntrans_alignment = 4095;
创建物理内存
mem->pagetable = (unsigned char *) mmap(NULL, s,
PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
if (mem->pagetable == NULL) {
CHECK_ALLOCATION(mem->pagetable = malloc(s));
memset(mem->pagetable, 0, s);
}
初始化device可占用的地址空间
mem->mmap_dev_minaddr = 0xffffffffffffffffULL;
mem->mmap_dev_maxaddr = 0;
}
2 内存地址转换
在cpu_new时已经将v2p_translate设置为translate_v2p_mmu3k,translate_v2p_mmu3k在memory_mips.cc中生成
#define TRANSLATE_ADDRESS translate_v2p_mmu3k
#define V2P_MMU3K
#include "memory_mips_v2p.cc"
#undef TRANSLATE_ADDRESS
#undef V2P_MMU3K
int TRANSLATE_ADDRESS(struct cpu *cpu, uint64_t vaddr, uint64_t *return_paddr, int flags)
{
//获取cpu所在状态
cp0 = cpu->cd.mips.coproc[0];
status = cp0->reg[COP0_STATUS];
if (status & MIPS1_SR_KU_CUR)
ksu = KSU_USER;
else
ksu = KSU_KERNEL;
//32bit判断
if (x_64 == 0) {
vaddr = (int32_t) vaddr;
xuseg_top = 0x7fffffff;
/* (Actually useg for R2000/R3000) */
}
if (vaddr <= xuseg_top) {
KUSEG: 低2G必须使用MMU
use_tlb = 1;
} else {
if (ksu == KSU_KERNEL) {
内核态,直接取低512M
/* kseg0, kseg1: */
if (vaddr >= (uint64_t)0xffffffff80000000ULL &&
vaddr <= (uint64_t)0xffffffffbfffffffULL) {
*return_paddr = vaddr & 0x1fffffff;
return 2;
}
/* other segments: KSEG2 必须使用MMU*/
use_tlb = 1;
} else {
不是内核态,但访问了对应的地址,将会产生excption
use_tlb = 0;
}
}
if (use_tlb)
{
使用MMU
}
//内存地址excption
mips_cpu_exception(cpu, exccode, tlb_refill, vaddr, 0, vaddr_vpn2, vaddr_asid, x_64);
}
3 注册device占用内存空间
device需要内存空间用于安排自己的寄存器和FIFO等
memory.cc memory_device_register
{
//查找注册的地址是否可用
for (i=0; i<mem->n_mmapped_devices; i++) {
if (i == 0 && baseaddr + len <= mem->devices[i].baseaddr)
newi = i;
if (i > 0 && baseaddr + len <= mem->devices[i].baseaddr &&
baseaddr >= mem->devices[i-1].endaddr)
newi = i;
if (i == mem->n_mmapped_devices - 1 &&
baseaddr >= mem->devices[i].endaddr)
newi = i + 1;
/* If this is not colliding with device i, then continue: */
if (baseaddr + len <= mem->devices[i].baseaddr)
continue;
if (baseaddr >= mem->devices[i].endaddr)
continue;
fatal("\nERROR! \"%s\" collides with device %i (\"%s\")!\n",
device_name, i, mem->devices[i].name);
exit(1);
}
注册device内存空间信息及访问函数
mem->devices[newi].baseaddr = baseaddr;
mem->devices[newi].endaddr = baseaddr + len;
mem->devices[newi].length = len;
mem->devices[newi].flags = flags;
mem->devices[newi].dyntrans_data = dyntrans_data;
mem->devices[newi].dyntrans_write_low = (uint64_t)-1;
mem->devices[newi].dyntrans_write_high = 0;
mem->devices[newi].f = f;
mem->devices[newi].extra = extra;
mem->devices[newi].m = NULL;
//更新device内存空间范围
if (baseaddr < mem->mmap_dev_minaddr)
mem->mmap_dev_minaddr = baseaddr & ~mem->dev_dyntrans_alignment;
if (baseaddr + len > mem->mmap_dev_maxaddr)
mem->mmap_dev_maxaddr = (((baseaddr + len) - 1) | mem->dev_dyntrans_alignment) + 1;
}
4 访问内存
mips_memory_rw 由generate_tail.c 自动生成的tmp_mips_tail.c结合mempry_rw.cc产生:
#define MEMORY_RW mips_memory_rw
#define MEM_MIPS
#include "memory_rw.cc"
#undef MEM_MIPS
#undef MEMORY_RW
memory_rw.cc int MEMORY_RW(struct cpu *cpu, struct memory *mem, uint64_t vaddr, unsigned char *data, size_t len, int writeflag, int misc_flags)
{
计算出要访问的物理地址
cpu->translate_v2p(cpu, vaddr, &paddr,
(writeflag? FLAG_WRITEFLAG : 0) +
(no_exceptions? FLAG_NOEXCEPTIONS : 0)
+ (misc_flags & MEMORY_USER_ACCESS)
+ (cache==CACHE_INSTRUCTION? FLAG_INSTR : 0))
if (paddr >= mem->mmap_dev_minaddr && paddr < mem->mmap_dev_maxaddr)
{
属于device的地址,在device中寻找,device地址在memory_device_register已经注册,找到后使用注册的device access函数访问
}
if (paddr >= mem->physical_max)
{
不在物理内存中,属于PROM不做处理
if ((paddr & 0xffffc00000ULL) == 0x1fc00000) {
/* Ok, this is PROM stuff */
}
}
//转换为虚拟机中的内存地址
memblock = memory_paddr_to_hostaddr(mem, paddr & ~offset_mask, writeflag);
获取对应数据,并做读写操作
cpu->update_translation_table
cpu->invalidate_code_translation
if (writeflag == MEM_WRITE)
memcpy(memblock + offset, data, len);
else
memcpy(data, memblock + offset, len);
}