AMD нужно было немного места для добавления новых кодов операций для префиксов REX
и некоторых других новых инструкций при разработке 64-разрядных расширений x86. Они изменили значение некоторых кодов операций на эти новые инструкции.
Некоторые из инструкций были просто краткими формами существующих инструкций или иным образом не являлись необходимыми. PUSHA
была одной из жертв. Непонятно, почему они запретили PUSHA
, хотя, похоже, он не перекрывает новые коды операций. Возможно, они зарезервированы для кодов операций PUSHA
и POPA
для будущего использования, поскольку они полностью избыточны и не будут работать быстрее и не будут происходить достаточно часто в коде, чтобы иметь значение.
Порядок PUSHA
был порядком кодирования команды: eax
, ecx
, edx
, ebx
, esp
, ebp
, esi
, edi
. Обратите внимание, что он избыточно нажал esp
! Вам нужно знать esp
, чтобы найти данные, которые он нажал!
Если вы конвертируете код из 64-битного кода, код PUSHA
все равно не годится, вам нужно обновить его, чтобы протолкнуть новые регистры r8
через r15
. Вам также необходимо сохранить и восстановить гораздо большее состояние SSE, xmm8
thru xmm15
. Предполагая, что вы собираетесь забить их.
Если код обработчика прерываний является просто заглушкой, которая пересылает код C, вам не нужно сохранять все регистры. Можно предположить, что компилятор C будет генерировать код, который будет сохранять rbx
, rbp
, rsi
, rdi
и r12
thru r15
. Вам нужно только сохранить и восстановить rax
, rcx
, rdx
и r8
thru r11
. (Примечание: на Linux или других платформах System V ABI компилятор будет сохранять rbx
, rbp
, r12
- r15
, вы можете ожидать, что rsi
и rdi
забиты) .
Регистры сегментов не содержат значения в длинном режиме (если прерванный поток работает в режиме 32-битной совместимости, вы должны сохранить регистры сегментов, спасибо ughoavgfhw). Фактически, они избавились от большей части сегментации в длинном режиме, но FS
все еще зарезервирован для операционных систем, чтобы использовать в качестве базового адреса для локальных данных потока. Значение самого регистра не имеет значения, база FS
и GS
устанавливаются через MSR 0xC0000100
и 0xC0000101
. Предполагая, что вы не будете использовать FS
, вам не нужно об этом беспокоиться, просто помните, что любые локальные данные потока, к которым обращается код C, могут использовать TLS любого случайного потока. Будьте осторожны с этим, поскольку библиотеки времени выполнения C используют TLS для некоторых функций (например, strtok обычно использует TLS).
Загрузка значения в FS
или GS
(даже в пользовательском режиме) приведет к перезаписи MSR FSBASE
или GSBASE
. Поскольку некоторые операционные системы используют GS
в качестве «процессорного локального» хранилища (им нужен способ иметь указатель на структуру для каждого ЦП), им нужно хранить его где-то, что не будет засорено загрузкой GS
в пользователе Режим. Чтобы решить эту проблему, для регистра GSBASE
зарезервированы две MSR: одна активная и одна скрытая. В режиме ядра GSBASE
ядра хранится в обычной GSBASE
MSR, а база режима пользователя находится в другой (скрытой) GSBASE
MSR. При переключении контекста из режима ядра в контекст пользовательского режима, а также при сохранении контекста режима пользователя и переходе в режим ядра код переключения контекста должен выполнить инструкцию SWAPGS, которая меняет значения видимого и скрытого GSBASE
MSR. Поскольку ядро GSBASE
безопасно скрыто в другой MSR в пользовательском режиме, код режима пользователя не может заглушить ядро GSBASE
путем загрузки значения в GS
. Когда ЦП снова входит в режим ядра, код сохранения контекста выполнит SWAPGS
и восстановит ядро GSBASE
.