Я изучаю код BIOS на моей машине (x86_64 Linux, IvyBridge).Я использую следующую процедуру для выгрузки кода BIOS:
$ sudo cat /proc/iomem | grep ROM
000f0000-000fffff : System ROM
$ sudo dd if=/dev/mem of=bios.dump bs=1M count=1
Затем я использую radare2
, чтобы прочитать и разобрать двоичный дамп:
$ r2 -b 16 bios.dump
[0000:0000]> s 0xffff0
[f000:fff0]> pd 3
: f000:fff0 0f09 wbinvd
`=< f000:fff2 e927f5 jmp 0xff51c
f000:fff5 0000 add byte [bx + si], al
Я знаю, что инициализация процессора x86 всегда начинаетсяс 16-битной средой 8086, и первая команда, которая должна быть выполнена, находится в f000:fff0
, то есть 0xffff0
.Поэтому я иду в это место и разбираю код.
К моему удивлению, первая инструкция - WBINVD
, чья функциональность заключается в аннулировании кэша, который, кажется, не имеет значения, когдапроцессор включен или сброшен.Я ожидал бы, что первая инструкция будет просто jmp
с меньшим адресом памяти.
Почему существует WBINVD
до jmp
?
Я уже искал соответствующую часть руководств Intel, том 3, глава 9 «Управление процессорами и инициализация», но это не так »Не могу ничего сказать о WBINVD
.Я также искал некоторые онлайн-ресурсы, но не нашел никакого объяснения.
Редактировать для получения дополнительной информации:
После выполнения инструкции jmp
для 0xff51c
код более интересен;он выполняет самопроверку:
[f000:f51c]> pd
f000:f51c dbe3 fninit
f000:f51e 0f6ec0 movd mm0, eax
f000:f521 6631c0 xor eax, eax
f000:f524 8ec0 mov es, ax
f000:f526 8cc8 mov ax, cs
f000:f528 8ed8 mov ds, ax
f000:f52a b800f0 mov ax, 0xf000
f000:f52d 8ec0 mov es, ax
f000:f52f 6726a0f0ff00. mov al, byte es:[0xfff0] ; [0xfff0:1]=0
f000:f536 3cea cmp al, 0xea
,=< f000:f538 750f jne 0xff549
| f000:f53a b91b00 mov cx, 0x1b
| f000:f53d 0f32 rdmsr ; check BSP (Boot Strap Processor) flag, if set, loop back to 0xffff0; otherwise, infinite hlt
| f000:f53f f6c401 test ah, 1
,==< f000:f542 7441 je 0xff585
,===< f000:f544 eaf0ff00f0 ljmp 0xf000:0xfff0
||`-> f000:f549 b001 mov al, 1
|| f000:f54b e680 out 0x80, al
|| f000:f54d 66be8cfdffff mov esi, 0xfffffd8c ; 4294966668
|| f000:f553 662e0f0114 lgdt cs:[si]
|| f000:f558 0f20c0 mov eax, cr0
|| f000:f55b 6683c803 or eax, 3
|| f000:f55f 0f22c0 mov cr0, eax
|| f000:f562 0f20e0 mov eax, cr4
|| f000:f565 660d00060000 or eax, 0x600
|| f000:f56b 0f22e0 mov cr4, eax
|| f000:f56e b81800 mov ax, 0x18
|| f000:f571 8ed8 mov ds, ax
|| f000:f573 8ec0 mov es, ax
|| f000:f575 8ee0 mov fs, ax
|| f000:f577 8ee8 mov gs, ax
|| f000:f579 8ed0 mov ss, ax
|| f000:f57b 66be92fdffff mov esi, 0xfffffd92 ; 4294966674
|| f000:f581 662eff2c ljmp cs:[si]
|`.-> f000:f585 fa cli
| : f000:f586 f4 hlt
| `=< f000:f587 ebfc jmp 0xff585
Чтобы сделать вывод о странности, этот код BIOS читает себя в 0xffff0
и сравнивает байт с 0xea
, который в точности является кодом операции для дальнего перехода:
f000:f52a b800f0 mov ax, 0xf000
f000:f52d 8ec0 mov es, ax
f000:f52f 6726a0f0ff00. mov al, byte es:[0xfff0] ; [0xfff0:1]=0
f000:f536 3cea cmp al, 0xea
Если он обнаружит, что код на 0xffff0
- это большой скачок, то он пойдет в бесконечный цикл.
Точнее, точки доступа (процессоры приложений) будут зацикливаться бесконечно.по команде hlt
, в то время как BSP (загрузочный процессор) вернется к началу 0xffff0
.Поскольку код в 0xffff0
не будет изменен, мы можем заключить, что BSP всегда найдет байт 0xea
и никогда не выйдет из цикла.
Так какова цель этогопроверка?Я с трудом могу поверить, что это наивная попытка предотвратить модификацию.