Skip to content

Commit

Permalink
Merge pull request #1551 from panda-re/sw_support
Browse files Browse the repository at this point in the history
Updating and adding architecture support in pypanda.
  • Loading branch information
hpreslier authored Nov 4, 2024
2 parents 1fcdf85 + 706c088 commit 097b4aa
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 47 deletions.
3 changes: 2 additions & 1 deletion hw/avatar/configurable_machine.c
Original file line number Diff line number Diff line change
Expand Up @@ -438,8 +438,9 @@ static void set_entry_point(QDict *conf, THISCPU *cpuu)
cpuu->env.active_tc.PC = entry;

#elif defined(TARGET_PPC)
cpuu->env.nip = entry;
//Not implemented yet
fprintf(stderr, "Not yet implemented- can't start execution at 0x%x\n", entry);
//fprintf(stderr, "Not yet implemented- can't start execution at 0x%x\n", entry);
#endif

}
Expand Down
141 changes: 97 additions & 44 deletions panda/python/core/pandare/arch.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ def __init__(self, panda):
regnames = ["X0", "X1", "X2", "X3", "X4", "X5", "X6", "X7",
"XR", "X9", "X10", "X11", "X12", "X13", "X14",
"X15", "IP0", "IP1", "PR", "X19", "X20", "X21",
"X22", "X23", "X24", "X25", "X26", "X27", "X27",
"X22", "X23", "X24", "X25", "X26", "X27",
"X28", "FP", "LR", "SP"]

self.reg_sp = regnames.index("SP")
Expand Down Expand Up @@ -601,6 +601,7 @@ def set_retval(self, cpu, val, convention='default', failure=False):

return super().set_retval(cpu, val, convention)


class Mips64Arch(MipsArch):
'''
Register names and accessors for MIPS64. Inherits from MipsArch for everything
Expand Down Expand Up @@ -629,67 +630,71 @@ def __init__(self, panda):
# note names must be stored uppercase for get/set reg to work case-insensitively
self.registers = {regnames[idx].upper(): idx for idx in range(len(regnames)) }

class X86Arch(PandaArch):
class PowerPCArch(PandaArch):
'''
Register names and accessors for x86
Register names and accessors for ppc
'''

def __init__(self, panda):
def __init__(self, panda):
super().__init__(panda)
regnames = ['EAX', 'ECX', 'EDX', 'EBX', 'ESP', 'EBP', 'ESI', 'EDI']
# XXX Note order is A C D B, because that's how qemu does it . See target/i386/cpu.h

# Note we don't set self.call_conventions because stack-based arg get/set is
# not yet supported
self.reg_retval = {"default": "EAX",
"syscall": "EAX",
"linux_kernel": "EAX"}

self.call_conventions = {"cdecl": [f"stack_{x}" for x in range(20)], # 20: arbitrary but big
"syscall": ["EAX", "EBX", "ECX", "EDX", "ESI", "EDI", "EBP"],
"linux_kernel": ["EAX", "EDX", "ECX", "stack_3", "stack_4", "stack_5", "stack_6"]}
self.call_conventions['default'] = self.call_conventions['cdecl']

self.reg_sp = regnames.index('ESP')
self.registers = {regnames[idx]: idx for idx in range(len(regnames)) }

regnames = ["r0", "sp", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11",
"r12", "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", "r21", "r22",
"r23", "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31"]
self.reg_sp = regnames.index('sp')
self.registers = {regnames[idx].upper(): idx for idx in range(len(regnames)) }
self.registers_crf = ["CR0", "CR1", "CR2", "CR3", "CR4", "CR5", "CR6", "CR7"]

def get_pc(self, cpu):
'''
Overloaded function to return the x86 current program counter
Overloaded function to return the ppc current program counter
'''
return cpu.env_ptr.eip
return cpu.env_ptr.nip

def set_pc(self, cpu, val):
'''
Overloaded function to set the x86 program counter
Overloaded function to set the ppc program counter
'''
cpu.env_ptr.eip = val
cpu.env_ptr.nip = val

def _get_reg_val(self, cpu, reg):
'''
Return an x86 register
Return a ppc register
'''
return cpu.env_ptr.regs[reg]
return cpu.env_ptr.gpr[reg]

def _set_reg_val(self, cpu, reg, val):
'''
Set an x86 register
Set an x86_64 register
'''
cpu.env_ptr.regs[reg] = val
cpu.env_ptr.gpr[reg] = val

def get_reg(self, cpu, reg):

reg = reg.upper()
env = cpu.env_ptr
if reg == "LR":
return env.lr
elif reg == "CTR":
return env.ctr
elif reg in self.registers_crf:
return env.crf[self.registers_crf.index(reg)]
else:
return super().get_reg(cpu, reg)


def set_reg(self, cpu, reg, val):
reg = reg.upper()
env = cpu.env_ptr

if reg == "LR":
env.lr = val
elif reg == "CTR":
env.ctr = val
elif reg in self.registers_crf:
env.crf[self.registers_crf.index(reg)] = val
else:
super().set_reg(cpu, reg, val)

def get_return_value(self, cpu):
'''
.. Deprecated:: use get_retval
'''
return self.get_retval(cpu)

def get_return_address(self,cpu):
'''
looks up where ret will go
'''
esp = self.get_reg(cpu,"ESP")
return self.panda.virtual_memory_read(cpu,esp,4,fmt='int')

class X86_64Arch(PandaArch):
'''
Expand Down Expand Up @@ -722,6 +727,7 @@ def __init__(self, panda):
self.reg_names_short = ['AX', 'CX', 'DX', 'BX', 'SP', 'BP', 'SI', 'DI']
self.reg_names_byte = ['AL', 'CL', 'DL', 'BL', 'AH', 'CH', 'DH', 'BH']
self.seg_names = ['ES', 'CS', 'SS', 'DS', 'FS', 'GS']
self.reg_names_mmr = ['LDT', 'TR', 'GDT', 'IDT']

def _get_segment_register(self, env, seg_name):
seg_idx = self.seg_names.index(seg_name)
Expand Down Expand Up @@ -765,6 +771,20 @@ def set_pc(self, cpu, val):
'''
cpu.env_ptr.eip = val

def _get_mmr_val(self, cpu, reg):
reg = reg.lower()
sc = getattr(cpu.env_ptr, reg)
return (sc.selector, sc.base, sc.limit, sc.flags)

def _set_mmr_val(self, cpu, reg, val):
reg = reg.lower()
selector, base, limit, flags = val
sc = getattr(cpu.env_ptr, reg)
sc.selector = selector
sc.base = base
sc.limit = limit
sc.flags = flags

def _get_reg_val(self, cpu, reg):
'''
Return an x86_64 register
Expand Down Expand Up @@ -802,8 +822,12 @@ def get_reg(self, cpu, reg):

reg = reg.upper()
env = cpu.env_ptr
if reg in self.reg_names_mmr:
return self._get_mmr_val(cpu, reg)
if reg in self.seg_names:
return self._get_segment_register(env, reg)
elif reg in ['EFLAGS', 'RFLAGS']:
return env.eflags
elif reg in ['RIP', 'PC', 'EIP']:
pc = self.get_pc(cpu) # changes reg to 'IP' and re-calls this
if reg == 'EIP':
Expand Down Expand Up @@ -856,13 +880,17 @@ def set_reg(self, cpu, reg, val):
reg = reg.upper()
env = cpu.env_ptr

if reg in ['ES', 'CS', 'SS', 'DS', 'FS', 'GS']:
if reg in self.reg_names_mmr:
return self._set_mmr_val(cpu, reg, val)
elif reg in self.seg_names:
self._set_segment_register(env, reg, val)
elif reg in ['EFLAGS', 'RFLAGS']:
env.eflags = val
elif reg in ['RIP', 'PC']:
return self.set_pc(cpu, val) # changes reg to 'IP' and re-calls this
elif reg.startswith('XMM'):
#env.xmm_regs[int(reg[3:])] = val
raise NotImplementedError("XMM registers unsupported")
env.xmm_regs[int(reg[3:])] = val
#raise NotImplementedError("XMM registers unsupported")
elif reg.startswith('MM'):
raise NotImplementedError("MM registers unsupported")
elif reg.startswith('YMM'):
Expand Down Expand Up @@ -890,3 +918,28 @@ def set_reg(self, cpu, reg, val):
self._set_general_purpose_register(env, reg, val, mask)
else:
super().set_reg(cpu, reg, val)


class X86Arch(X86_64Arch):
'''
Register names and accessors for x86
'''

def __init__(self, panda):
super().__init__(panda)
regnames = ['EAX', 'ECX', 'EDX', 'EBX', 'ESP', 'EBP', 'ESI', 'EDI']
# XXX Note order is A C D B, because that's how qemu does it . See target/i386/cpu.h

# Note we don't set self.call_conventions because stack-based arg get/set is
# not yet supported
self.reg_retval = {"default": "EAX",
"syscall": "EAX",
"linux_kernel": "EAX"}

self.call_conventions = {"cdecl": [f"stack_{x}" for x in range(20)], # 20: arbitrary but big
"syscall": ["EAX", "EBX", "ECX", "EDX", "ESI", "EDI", "EBP"],
"linux_kernel": ["EAX", "EDX", "ECX", "stack_3", "stack_4", "stack_5", "stack_6"]}
self.call_conventions['default'] = self.call_conventions['cdecl']

self.reg_sp = regnames.index('ESP')
self.registers = {regnames[idx]: idx for idx in range(len(regnames)) }
4 changes: 3 additions & 1 deletion panda/python/core/pandare/panda.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
from .asyncthread import AsyncThread
from .qcows_internal import Qcows
from .qemu_logging import QEMU_Log_Manager
from .arch import ArmArch, Aarch64Arch, MipsArch, Mips64Arch, X86Arch, X86_64Arch
from .arch import ArmArch, Aarch64Arch, MipsArch, Mips64Arch, X86Arch, X86_64Arch, PowerPCArch
from .cosi import Cosi
from dataclasses import dataclass

Expand Down Expand Up @@ -150,6 +150,8 @@ def __init__(self, arch="i386", mem="128M",
self.arch = MipsArch(self)
elif self.arch_name in ["mips64", "mips64el"]:
self.arch = Mips64Arch(self)
elif self.arch_name in ["ppc"]:
self.arch = PowerPCArch(self)
else:
raise ValueError(f"Unsupported architecture {self.arch_name}")
self.bits, self.endianness, self.register_size = self.arch._determine_bits()
Expand Down
15 changes: 14 additions & 1 deletion target/ppc/translate_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -9814,6 +9814,7 @@ static void ppc_cpu_realizefn(DeviceState *dev, Error **errp)
error_propagate(errp, local_err);
return;
}
cpu_reset(cs);

#if !defined(CONFIG_USER_ONLY)
cpu->cpu_dt_id = (cs->cpu_index / smp_threads) * max_smt
Expand Down Expand Up @@ -10247,7 +10248,19 @@ const char *ppc_cpu_lookup_alias(const char *alias)

PowerPCCPU *cpu_ppc_init(const char *cpu_model)
{
return POWERPC_CPU(cpu_generic_init(TYPE_POWERPC_CPU, cpu_model));
ObjectClass *cpu_oc;
Object *cpuobj;

cpu_oc = cpu_class_by_name(TYPE_POWERPC_CPU, cpu_model);
if (cpu_oc == NULL) {
error_report("Unable to find CPU definition: %s", cpu_model);
exit(1);
}
cpuobj = object_new(object_class_get_name(cpu_oc));
object_property_set_bool(cpuobj, true, "realized", &error_fatal);
return POWERPC_CPU(cpuobj);

//return POWERPC_CPU(cpu_generic_init(TYPE_POWERPC_CPU, cpu_model));
}

/* Sort by PVR, ordering special case "host" last. */
Expand Down

0 comments on commit 097b4aa

Please sign in to comment.