
Raio-x de latência para hardware não documentado
Uma ferramenta exploratória para temporização de MMIO — meça o tempo de qualquer endereço físico e disseque o hardware a partir da latência.

Surpreendentemente útil para engenharia reversa de hardware, impressão digital de hipervisores, atividade de dispositivos por canal lateral, caracterização de registradores, calibração de gatilhos maliciosos e criação de instruções de máquina absurdamente longas.
O primeiro megabyte de memória física é um exemplo fácil para começar, mesmo que sua estrutura já seja bem conhecida.

As fronteiras de latência nos permitem dividir o espaço de endereçamento sem depender de layouts reportados.
sudo ./mmiotic --start-address 0x0 --end-address 0x100000 --stride 4 --count 20 --quiet-mmap0x9FFFF a ~380 cy, depois o buraco de vídeo de 128 KB, que se divide em dois em 0xB0000. A mudança na variância em 0x20000 pode indicar um page walk diferente para a página nula.A0000–AFFFF: lento e instável (996 cy, stdev 327), roteado para a Radeon ligada mas ociosa. B0000–BFFFF: rápido e rígido (780 cy, stdev 3.2, lê ffffffff), reivindicado por ninguém. Dados de aparência semelhante, mas uma diferença de 100x na variância.C0000–FFFFF: /proc/iomem diz "System ROM", mas o scan mostra latência de DRAM, não velocidade de flash — a BIOS é espelhada na DRAM.Faça um scan do espaço de configuração do root complex (00:00.0, 4 KB) em busca de outliers de latência contra um piso plano de ~675 ciclos:

A latência do mailbox é significativamente maior do que os dados ao redor, e os picos duplicados e as variâncias equivalentes nos permitem associar registradores funcionalmente semelhantes.
sudo ./mmiotic --start-address 0xf8000000 --end-address 0xf8001000 --stride 4 --count 2000xe4 (1218 cy, valor 80e3110b) e 0xa4 (1199 cy, valor deadbeef). e4 é um doorbell documentado; a4 não é documentado.e4 é público.deadbeef é lido como um sentinela de não inicializado/erro — mesmo mailbox, a4 não implementado ou alimentado com entrada inválida.Latências cuidadosamente selecionadas às vezes podem ser usadas em cenários incomuns de exploração (MCHAMMER, smiiiiiiiiiiiiiiii):

Aqui, uma Radeon ociosa e sem driver transforma uma leitura alinhada de 4 bytes em uma parada de ~100.000 ciclos.
sudo ./mmiotic --start-address 0x90e00000 --end-address 0x90e00100 --stride 4 --count 50 — varra o início do BAR da GPU (0x90e00000, 256 KB). 0x90e00008 custa 110.152 cy; 0x90e00018, 16 bytes adiante, volta a ser rápido a 1.523 — por registrador, não por página.sudo ./mmiotic --start-address 0x90e00000 --end-address 0x90e40000 --stride 4 --count 1 --binary — --binary sonda em ordem de inversão de bits, então toda a janela de 256 KB é coberta uniformemente após 1.012 sondas, mapeando um bloco contíguo de 48 KB onde todo registrador é lento.Idealmente, ler um endereço físico seria uma operação relativamente segura, mas mmiotic é especialmente bom em encontrar acidentalmente as exceções:

Por exemplo, na plataforma acima, uma leitura de 1 byte de 0xdc5003b0 — offset 0x3b0 de um BAR de registradores de iGPU Zen 4 sem driver (Região 5, 0xdc500000, 512 KB) — reinicia o sistema de forma confiável.
sudo ./mmiotic --address 0xdc5003b0 --size 1 --count 1. Sem saída; a máquina reinicia.0x3ac e 0x3b4 em ambos os lados leem 00000000 a ~3.200 cy e são inofensivos.0x3a0–0x3fc encontra 7 dwords venenosas intercaladas com 17 seguras.sudo busybox devmem 0xdc5003b0 32 e sudo dd if=/dev/mem bs=1 count=1 skip=$((0xdc5003b0)) derrubam a máquina se você preferir pular o intermediário.[!WARNING] Algumas plataformas serão reiniciadas quando regiões de endereço específicas forem sondadas pelo
mmiotic. Tenha cuidado onde você usa isso.
A base e o tamanho do ECAM são detectados automaticamente a partir do ACPI MCFG (/sys/firmware/acpi/tables/MCFG). Substitua com -M, --mmio-base / -Z, --mmio-size.
--find-target/--find-longest transformam a varredura em busca:
| Opção | Efeito |
|---|---|
-F, --find-target <s> | encontra um endereço cujo tempo de acesso atinja s segundos e então aumenta a largura (4b não alinhado → 8/16/32/64/512b) para elevá-lo ainda mais |
-G, --find-longest |
b/d/f.o.l: 00:02:00.00.4 address: 00000000f0100000 min: 312 max: 480 stdev: 2.1 dynamic: 0 value: 8086abcd
-T adiciona o sufixo cy às contagens de ciclos e insere um par de tempos min/max em escala (-N os imprime como nanossegundos brutos):
b/d/f.o.l: 00:02:00.00.4 address: 00000000f0100000 min: 312 cy max: 480 cy ( 104 ns/ 160 ns) stdev: 2.1 dynamic: 0 value: 8086abcd
Compile:
make
Executa como root para obter acesso a /dev/mem.
Se você vir: mmap ... Operation not permitted, tente iniciar com iomem=relaxed para remover CONFIG_STRICT_DEVMEM:
# /etc/default/grub
GRUB_CMDLINE_LINUX="iomem=relaxed nopat"
Então execute sudo update-grub e reinicie.
mmiotic tem sido usado para avançar uma variedade de pesquisas internas e externas, às vezes de maneiras inesperadas. Alguns exemplos de aplicações do mmiotic:
MCHAMMER: mmiotic calibra uma exceção de machine check atrasada para a entrega de precisão de sinais MC# em ambientes protegidos.
smiiiiiiiiiiiiiiii: mmiotic resolve a instrução de alta latência para quebrar o rendezvous do SMM.
The Assembly Hall of Shame: mmiotic é usado extensivamente para o importante problema da desotimização de desempenho.
mmiotic é um esforço de pesquisa de Christopher Domas (@xoreaxeaxeax).
| Opção | Nomes |
|---|
-b/-d/-f/-r | PCI --bus/--device/--function/--register — decodificados com base no ECAM |
-o, --offset <n> | offset a partir da base MMIO (ignora a decodificação B/D/F) |
-A, --address <n> | um endereço físico; define a base da região implicitamente |
-a/-z | --start-address/--end-address — uma faixa física; implica --scan |
| Opção | Efeito |
|---|
-S, --scan | percorre todos os endereços na região alvo |
-t, --stride <n> | passo entre endereços (padrão: 4) |
-n, --limit <n> | parar após este número de endereços |
--binary | sonda em ordem de inversão de bits — após k sondas a faixa tem cobertura uniforme com granularidade de ~range/k (veja o aviso acima) |
-x, --limited | faixa de registradores limitada (0x00–0xff vs 0x000–0xfff) |
-e, --skip | pula funções que leem 0xffffffff no offset 0 (dispositivos ausentes) |
-I, --iomem | escaneia todas as regiões de nível superior não–System-RAM em /proc/iomem |
-R, --ioregion <name> | escaneia regiões de /proc/iomem cujo rótulo corresponde a <name>, em qualquer profundidade |
| mesma busca, mas esgota todos os candidatos em vez de parar no primeiro resultado |
-B, --fallbacks <n> | candidatos levados para a fase de escalada (padrão: 10) |
| Opção | Efeito |
|---|
-s, --size <n> | bytes por acesso: 1 / 2 / 4 (padrão) / 8 / 16 (XMM) / 32 (YMM) / 64 (ZMM) / 512 (fxrstor). Tamanhos > 4 estão fora da especificação, mas funcionam no hardware testado; larguras AVX precisam de AVX / AVX-512F |
-c, --count <n> | amostras por endereço (padrão: 1); o mínimo é reportado |
-L, --lock | mede o tempo de um RMW com lock: lock xadd (tamanhos 1/2/4/8) ou lock cmpxchg16b (tamanho 16). Escreve o valor de volta |
-g, --gather | mede o tempo de um gather vetorizado; --size 16/32/64 escolhe XMM/YMM/ZMM (AVX-512) |
-w, --write-read | captura a latência de escrita medindo o tempo de uma leitura que deve observar uma escrita postada anteriormente — veja abaixo |
-E, --enter (--enter-2, --enter-3) | conduz uma rajada de até 30 acessos a partir de um único enter $0, $31 — veja abaixo |
-k, --continue | em uma falha de fxrstor, descarta essa amostra e continua a varredura |
| Opção | Efeito |
|---|
-C, --min-cycles <n> | exibe apenas resultados com min_cycles >= n |
-T, --time | mostra nanossegundos junto com os ciclos (detecta a frequência do TSC) |
-N, --nanosecond | com --time, imprime nanossegundos brutos, sem escala de unidades |
-P, --progress | progresso da varredura em tempo real no stderr |
-p, --processor <n> | fixa no CPU lógico n (padrão: 0) — reduz ruído do timer |
-u, --unbound | não fixa a um núcleo |
--lazy-mmap | mapeia janelas de 2 GB sob demanda; necessário para regiões > 2 GB |
--quiet-mmap | suprime o ruído de "mmap failed" de regiões inacessíveis |
| Campo | Significado |
|---|
b/d/f.o.l | barramento/dispositivo/função, offset do registrador, tamanho do acesso |
offset | offset a partir da base MMIO — substitui b/d/f.o.l quando o alvo veio de -o/-A/-a+-z |
address | endereço físico completo de 64 bits |
min/max | acesso mais rápido e mais lento observado, em ciclos |
stdev | desvio padrão das contagens de ciclos amostrados |
dynamic | 1 se o valor mudou em algum momento entre as amostras |
value | valor lido — para tamanhos acima de 8 bytes, os primeiros 32 bytes como qwords separados por espaços |
faults | anexado apenas se um acesso falhou |