Eu tenho que colocar o código de erro empurrado para a pilha por certas exceções antes de retornar do manipulador de interrupção?
-
20-08-2019 - |
Pergunta
Eu tenho carregado uma tabela IDT com 256 entradas, todos apontando para manipuladores semelhantes:
- para exceções 8 e 10-14, empurrar o número de exceção (essas exceções empurrar um código de erro automaticamente)
- para os outros, empurrar um código de erro "fictício" eo número de exceção;
- em seguida, saltar para um manipulador comum
Assim, quando o manipulador comum entra, a pilha está alinhada corretamente e contém a exceção / número de interrupção, o código de erro (que pode ser apenas um simulado), eflags, cs e EIP.
A minha pergunta diz respeito retornar do manipulador de interrupção. Eu uso iret
para retornar depois de tomar o número de exceção eo código de erro da pilha, mas isso não funciona para exceção nr 8; se eu deixar o código de erro na pilha, em seguida, ele retorna bem!
Perguntas:
- que eu tenho que deixar o código de erro na pilha para exceções que colocam o código de erro lá? Se assim for, como é que
iret
determinar se tem a pop um código de erro ou não? - assim que eu permitir que as interrupções Eu sempre obter exceção 8 (falha dupla), mas, em seguida, tudo corre bem (Eu estou desenvolvendo um sistema operacional hobby). É este o comportamento normal ou eu tenho um lugar bug?
Solução
Se a CPU empurrou um código de erro automaticamente, o manipulador deve pop-lo antes do iret
. A instrução iret
não sabe onde você está vindo, se é uma falha, uma armadilha ou uma interrupção externa. Ele sempre faz o mesmo, e ele assume que não há nenhum código de erro na pilha.
Citando o SDM (Software Manual do desenvolvedor), Volume 3, Capítulo 5, seção 5,13 intitulado Código de erro:
O código de erro é empurrado na pilha como um doubleword ou palavra (dependendo a interrupção padrão, armadilha, ou tarefa tamanho da janela). Para manter a pilha alinhada para empurrões doubleword, a metade superior do código de erro é reservado. Nota que o código de erro não é estalou quando a instrução IRET é executado para retornar de um manipulador de exceção, assim o manipulador deve remover o código de erro antes de executar um retorno.
Você pode encontrar Manual IA-32 Software do desenvolvedor aqui : http://www.intel.com/products/processor/manuals/
Volume 3 parte 1, capítulo 5, descreve exceção e manipulação de interrupção. Volume 2 Parte 1 tem a especificação para a instrução iret
.
Outras dicas
Eu escrevi um pequena OS x86 um tempo atrás. Dê uma olhada no arquivo ISR. asm no repositório cvs.
Observe como montamos os manipuladores, a maioria empurrar um dword manequim para a pilha para a conta para os poucos manipuladores que recebem automaticamente um código de erro empurrado. Então, quando nós retornamos através de um IRET sempre podemos assumir 2 dwords na pilha, independentemente da interrupção e executar um add esp, 8 antes do iret a coisas limpas-se muito bem.
Isso deve responder a sua primeira pergunta.
Quanto à sua segunda pergunta: A dupla falta quando você habilita interrupções, ... hmmm poderia ser um problema com a paginação se você não configurá-lo corretamente. Poderia ser uma outra coisa milhões também:)
Eu tive um problema semelhante com "duplas faltas" assim que permitiu interrupções. Bem, eles olhou como duplas faltas, mas eles realmente eram interrupções do timer!
duplas faltas são número de interrupção 8
.
Infelizmente, interrompe uma configuração sinais PIC padrão temporizador como número de interrupção (DEFAULT_PIC_BASE + TIMER_OFFSET)
= (8 + 0)
= 8
.
Masking todas as minhas interrupções PIC (até que eu estava pronto para configurar corretamente o PIC) silenciados estas interrupções do timer-fault-lookalike dupla.
(PICs exigem a CPU para reconhecer interrupções antes de produzir a próxima. Desde seu código não estava reconhecendo a interrupção inicial do timer, o PIC nunca lhe dei qualquer mais! É por isso que você só tem um, em vez de um zilhão poderia ter esperado.)
Eu tenho que deixar o código de erro na pilha para exceções que colocam o código de erro lá?
Como os outros mencionados, você tem que fazer qualquer um:
pop %eax
/* Do something with %eax */
iret
Ou se você deseja ignorar o código de erro:
add $4, %esp
iret
Se você não fizer isso, iret
irá interpretar o código de erro como o novo CS, e que são susceptíveis de obter uma falha de proteção geral, como mencionado em: Por que iret a partir de uma falha de página manipulador de gerar interrupção 13 (falha de proteção geral) eo código de erro 0x18 / a>
Minimal Trabalho desta página manipulador que eu criei para ilustrar isso. Tente comentar o pop
e vê-lo explodir.
Compare o acima com um erro Divisão excepção, que não a pop a pilha.
Note que se você simplesmente int $14
, nenhum byte extra é empurrado:. Isso só acontece na exceção real
manual Intel Volume 3 Guia de programação do Sistema - 325384-056US setembro 2015 Tabela 6-1. "Exceções de modo protegido e interrupções" coluna "Código de erro" contém a lista de interrupções que empurram o código de erro ou não.
38.9.2.2 "Códigos de erro Erro de página" explica o que os meios de erro.
Uma maneira elegante de lidar com isso é para empurrar um código 0
erro manequim na pilha para as interrupções que não fazem isso para fazer coisas uniforme. James Molloy tutorial faz exatamente isso .
O kernel Linux 4.2 parece estar a fazer algo semelhante. Sob arch / x86 / entry / entry64 .S ele modela interrupções com has_error_code
:
trace_idtentry page_fault do_page_fault has_error_code=1
e, em seguida, usa-lo no mesmo arquivo como:
.ifeq \has_error_code
pushq $-1 /* ORIG_RAX: no syscall to restart */
.endif
que faz o impulso quando has_error_code=0
.