ducky.cpu package

Module contents

class ducky.cpu.CPU(machine, cpuid, memory_controller, cores=1)[source]

Bases: ducky.interfaces.ISnapshotable, ducky.interfaces.IMachineWorker

boot()[source]
die(exc)[source]
halt()[source]
load_state(state)[source]
on_core_alive(core)[source]

Triggered when one of cores goes alive.

on_core_halted(core)[source]

Signal CPU that one of cores is no longer alive.

on_core_running(core)[source]

Signal CPU that one of cores is now running.

on_core_suspended(core)[source]

Signal CPU that one of cores is now suspended.

save_state(parent)[source]
suspend()[source]
wake_up()[source]
class ducky.cpu.CPUCore(coreid, cpu, memory_controller)[source]

Bases: ducky.interfaces.ISnapshotable, ducky.interfaces.IMachineWorker

This class represents the main workhorse, one of CPU cores. Reads instructions, executes them, has registers, caches, handles interrupts, ...

Parameters:
  • coreid (int) – id of this core. Usually, it’s its serial number but it has no special meaning.
  • cpu (ducky.cpu.CPU) – CPU that owns this core.
  • memory_controller (ducky.mm.MemoryController) – use this controller to access main memory.
FP()[source]
IP()[source]
REG(reg)[source]
SP()[source]
_CPUCore__get_flags()
_CPUCore__set_flags(flags)
_enter_exception(index, *args)[source]

Prepare CPU for handling exception routine. CPU core loads new IP and SP from proper entry of EVT. Old SP and FLAGS are saved on the exception stack, and new call frame is created. Privileged mode flag is set, hardware interrupt flag is cleared.

Then, if exception provides its routine with some arguments, these arguments are pushed on the stack.

Exception stack layout then looks like this (original stack is left untouched):

+---------+ <= EVT SP
|   SP    |
+---------+
|  FLAGS  |
+---------+
|   IP    |
+---------+
|   FP    |
+---------+ <= FP
|   arg1  |
+---------+
|   ...   |
+---------+
|   argN  |
+---------+ <= SP
|   ...   |
+---------+
Parameters:
  • index (int) – exception ID - EVT index.
  • args (u32_t) – if present, these values will be pushed onto the stack.
_exit_exception()[source]

Restore CPU state after running an exception routine. Call frame is destroyed, registers are restored. Clearing routine arguments is responsibility of the routine.

_get_instruction_set()[source]
_handle_exception(exc, index, *args)[source]

This method provides CPU exception classes with a simple recipe on how to deal with the exception:

  • tell processor to start exception dance,
  • if the exception is raised again, tell processor to plan double fault routine,
  • and if yet another exception is raised, halt the core.
_handle_python_exception(exc)[source]
_raw_pop()[source]

Pop value from stack. 4 byte number is read from address in SP, then SP is incremented by four.

Returns:popped value
Return type:u32
_raw_push(val)[source]

Push value on stack. SP is decremented by four, and value is written at this new address.

Parameters:val (u32) – value to be pushed
_set_instruction_set(instr_set)[source]
boot()[source]
change_runnable_state(alive=None, running=None, idle=None)[source]
check_protected_ins()[source]

Raise AccessViolationError if core is not running in privileged mode.

This method should be used by instruction handlers that require privileged mode, e.g. protected instructions.

Raises:ducky.errors.PrivilegedInstructionError – if the core is not in privileged mode
create_frame()[source]

Creates new call stack frame, by performing the following operations:

  • push IP
  • push FP
  • set FP to SP (i.e. SP before this method + 2 pushes)

Stack layout then looks like this:

+---------+
|   IPx   |
+---------+
|   FPx   |
+---------+ <= FPy
|   ...   |
+---------+ <= original SP
|   IPy   |
+---------+
|   FPy   |
+---------+ <= SP, FP
|   ...   |
+---------+

FP then points to the newly created frame, to the saved FP in particular, and this saved FP points to its predecesor, thus forming a chain.

destroy_frame()[source]

Destroys current call stack frame by popping values from the stack, reversing the list of operations performed by ducky.cpu.CPUCore.create_frame():

  • pop FP
  • pop IP

After this, FP points to the frame from which the instruction that created the currently destroyed frame was executed, and restored IP points to the next instruction.

Raises:InvalidFrameError – if frame checking is enabled, current SP is compared with saved FP to see, if the stack was clean before leaving the frame. This error indicated there is some value left on stack when ret or retint were executed. Usually, this signals missing pop to complement one of previous ``push``es.
die(exc)[source]
do_step(ip, regset)[source]
flags
halt()[source]
has_coprocessor(name)[source]
init_debug_set()[source]
instruction_set
irq(index)[source]

This is a wrapper for _enter_exception, for device drivers to call when hardware interrupt arrives.

Parameters:index (int) – exception ID - EVT index
load_state(state)[source]
pop(*regs)[source]
pop_flags()[source]
pop_frame()[source]
push(*regs)[source]
push_flags()[source]
reset(new_ip=0)[source]

Reset core’s state. All registers are set to zero, all flags are set to zero, except HWINT flag which is set to one, and IP is set to requested value.

Parameters:new_ip (u32_t) – new IP value, defaults to zero
run()[source]
save_state(parent)[source]
step()[source]

Perform one “step” - fetch next instruction, increment IP, and execute instruction’s code (see inst_* methods)

suspend()[source]
wake_up()[source]
class ducky.cpu.CPUCoreState[source]

Bases: ducky.snapshot.SnapshotNode

class ducky.cpu.CPUState(*fields)[source]

Bases: ducky.snapshot.SnapshotNode

get_core_state_by_id(coreid)[source]
get_core_states()[source]
class ducky.cpu.CoreFlags[source]

Bases: ducky.util.Flags

_flags = ['privileged', 'hwint_allowed', 'equal', 'zero', 'overflow', 'sign']
_labels = 'PHEZOS'
ducky.cpu.DEFAULT_CORE_INST_CACHE_SIZE = 256

Default size of core instruction cache, in instructions.

ducky.cpu.DEFAULT_EVT_ADDRESS = 0

Default EVT address

ducky.cpu.DEFAULT_PT_ADDRESS = 65536

Default PT address

class ducky.cpu.InstructionCache_Base(mmu, *args, **kwargs)[source]

Bases: ducky.util.LoggingCapable, dict

Simple instruction cache class, based on a dictionary, with a limited size.

Parameters:core (ducky.cpu.CPUCore) – CPU core that owns this cache.
class ducky.cpu.InstructionCache_Full(mmu, *args, **kwargs)[source]

Bases: ducky.util.LoggingCapable, list

Simple instruction cache class, based on a list, with unlimited size.

Parameters:core (ducky.cpu.CPUCore) – CPU core that owns this cache.
clear()[source]
class ducky.cpu.InterruptVector(ip=0, sp=0)[source]

Bases: object

Interrupt vector table entry.

SIZE = 8
static load(core, addr)[source]
class ducky.cpu.MMU(core, memory_controller)[source]

Bases: ducky.interfaces.ISnapshotable

Memory management unit (aka MMU) provides a single point handling all core’s memory operations. All memory reads and writes must go through this unit, which is then responsible for all translations, access control, and caching.

Parameters:
  • core (ducky.cpu.CPUCore) – parent core.
  • memory_controller (ducky.mm.MemoryController) – memory controller that provides access to the main memory.
  • memory.force-aligned-access (bool) – if set, MMU will disallow unaligned reads and writes. False by default.
  • cpu.pt-address (int) – base address of page table. ducky.cpu.DEFAULT_PT_ADDRESS by default.
  • cpu.pt-enabled (bool) – if set, CPU core will start with page table enabled. False by default.
_check_access(access, addr, align=None)[source]

Check attempted access against several criteria:

  • PT is enabled - disabled PT implies different set of read/write methods that don’t use this method to check access
  • access alignment if correct alignment is required
  • privileged access implies granted access
  • corresponding PTE settings
Parameters:
  • accessread, write or execute.
  • addr (int) – memory address.
  • align (int) – if set, operation is expected to be aligned to this boundary.
Raises:
_debug_wrapper_read(reader, *args, **kwargs)[source]
_debug_wrapper_write(writer, *args, **kwargs)[source]
_fetch_instr(addr)[source]

Read instruction from memory. This method is responsible for the real job of fetching instructions and filling the cache.

Parameters:addr (u24) – absolute address to read from
Returns:instruction
Return type:InstBinaryFormat_Master
_fetch_instr_jit(addr)[source]

Read instruction from memory. This method is responsible for the real job of fetching instructions and filling the cache.

Parameters:addr (u24) – absolute address to read from
Returns:instruction
Return type:InstBinaryFormat_Master
_get_pg_ops_dict(address)[source]
_get_pg_ops_list(address)[source]
_get_pt_enabled()[source]
_get_pte(addr)[source]

Find out PTE for particular physical address. If PTE is not in internal PTE cache, it is fetched from PTE table.

Parameters:addr (int) – memory address.
_nopt_read_u16(addr)[source]
_nopt_read_u32(addr, not_execute=True)[source]
_nopt_read_u8(addr)[source]
_nopt_write_u16(addr, value)[source]
_nopt_write_u32(addr, value)[source]
_nopt_write_u8(addr, value)[source]
_pt_read_u16(addr)[source]
_pt_read_u32(addr, not_execute=True)[source]
_pt_read_u8(addr)[source]
_pt_write_u16(addr, value)[source]
_pt_write_u32(addr, value)[source]
_pt_write_u8(addr, value)[source]
_set_access_methods()[source]

Set parent core’s memory-access methods to proper shortcuts. Methods named MEM_{IN,OUT}{8,16,32} will be set to corresponding MMU methods.

_set_pt_enabled(value)[source]
halt()[source]
pt_enabled
release_ptes()[source]

Clear internal PTE cache.

reset()[source]

Reset MMU. PT will be disabled, and all internal caches will be flushed.

class ducky.cpu.StackFrame(sp, ip)[source]

Bases: object

ducky.cpu.do_log_cpu_core_state(core, logger=None, disassemble=True, inst_set=None)[source]

Log state of a CPU core. Content of its registers, and other interesting or useful internal variables are logged.

Parameters:
  • core (ducky.cpu.CPUCore) – core whose state should be logged.
  • logger – called for each line of output to actualy log it. By default, core’s ducky.cpu.CPUCore.DEBUG() method is used.
ducky.cpu.log_cpu_core_state(*args, **kwargs)[source]

This is a wrapper for ducky.cpu.do_log_cpu_core_state function. Its main purpose is to be removed when debug mode is not set, therefore all debug calls of ducky.cpu.do_log_cpu_core_state will disappear from code, making such code effectively “quiet”.