Collection of tools to create, dump and manipulate Zen microcode updates. Currently supported are Zen1 and Zen2 microcode variants, and basic instructions: arithmetic, logic, shift, load, store, conditional microcode branch. See zen_base.yaml for field descriptions, operations and flags.
mov reg1, 0x4242
add reg1, reg3, 0x42
xor.zcZCnd reg1, reg3, 0x42
jz.z 0x1fc0
mov msr2:[reg15], reg14
mov.b reg14, ls:[reg15 + reg14 + 0x20]samples/ucodecontains microcode patch sources for the macro assembler and disassembler.samples/mcodehosts companion x86-64 code that calls into the patches, useful when testing end-to-end flows.
To install ZenUtils, use uv:
uv pip install git+https://github.com/AngryUEFI/ZenUtilsThis will install the command line tools and Python package. To add ZenUtils as a dependency to your own project using uv, run:
uv pip add git+https://github.com/AngryUEFI/ZenUtilsThis will add ZenUtils to your pyproject.toml for use as a library in your project.
Usage:
$ python zenutils/asm.py --arch Zen2 "add reg1, reg3, 0x42"
; 0x382F9C108C080042 0011100000101111100111000001000010001100000010000000000001000010
Inspect fields:
$ python zenutils/asm.py --arch Zen2 "add reg1, reg3, 0x42" -i
; 0x382F9C108C080042 0011100000101111100111000001000010001100000010000000000001000010
; imm16 : 66 0000000001000010
; imm_signed : 0 0
; imm_mode : 1 1
; rt : 0 00000
; rs : 3 00011
; rd : 1 00001
; rmod : 1 1
; read_zf : 0 0
; read_cf : 0 0
; write_zf : 0 0
; write_cf : 0 0
; native_flags: 0 0
; size_flags : 7 111
; load : 0 0
; store : 0 0
; operation : 95 01011111
; exec_unit : 7 111
Python usage:
from zenutils.arch.registry import Registry
from zenutils.asm import assemble_single
zen2_spec = Registry().get('Zen2')
word = assemble_single(zen2_spec, "add reg1, reg3, 0x42", label_addrs = {})
print(f"0x{word:016X}")The macro assembler converts microcode assembly files to microcode updates binaries. It supports labels and takes care of placing instructions and quads as needed. Use the -d flag to get text-based debug output instead.
Note The microcode updates must be signed (f.i., with Zentool) before they are accepted by Zen CPUs.
For microcode examples see samples/ucode. Usage:
$ python zenutils/masm.py samples/ucode/strlen.s -o cpu00870F10_strlen_shld.bin
; Packages used: 3 of 64
$ zentool resign cpu00870F10_strlen_shld.bin
Usage:
$ python zenutils/disasm.py --arch Zen2 0x382F9C108C080042
add reg1, reg3, 0x42
Inspect fields:
$ python zenutils/disasm.py --arch Zen2 0x382F9C108C080042 -i
; 0x382F9C108C080042 0011100000101111100111000001000010001100000010000000000001000010
; imm16 : 66 0000000001000010
; imm_signed : 0 0
; imm_mode : 1 1
; rt : 0 00000
; rs : 3 00011
; rd : 1 00001
; rmod : 1 1
; read_zf : 0 0
; read_cf : 0 0
; write_zf : 0 0
; write_cf : 0 0
; native_flags: 0 0
; size_flags : 7 111
; load : 0 0
; store : 0 0
; operation : 95 01011111
; exec_unit : 7 111
add reg1, reg3, 0x42
Disassemble a whole binary microcode update file:
$ python zenutils/disasm.py --arch Zen2 -u cpu00870F10_strlen_shld.bin
; Header
.date 0x07112025
.revision 0x08701040
.format 0x8004
...
; Match Register
.match_reg 0 0x00000420
.match_reg 1 0x00000000
.match_reg 2 0x00000000
...
; Instruction Packages
; Slot 0 @ 0x1fc0 (0x38501c1782000000 0x387f9c1000000000 0x387f9c1000000000 0x387f9c1000000000 0x00000001)
mov reg15, rax
nop
nop
nop
.sw_continue
; Slot 1 @ 0x1fc1 (0x286f20173c009800 0x38581c9039c00000 0x20021c2000081fc2 0x382f9c17bc080001 0x00121fc1)
mov.b reg14, ls:[reg15]
and.Z reg0, reg14, reg14
jz.z 0x1fc2
add reg15, reg15, 0x1
.sw_branch 0x1fc1 ; (immediately)
; Slot 2 @ 0x1fc2 (0x38281c19be000000 0x387f9c1000000000 0x387f9c1000000000 0x387f9c1000000000 0x03100082)
sub rbx, reg15, rax
nop
nop
nop
.sw_complete ; (immediately)
...