6

我已经加载了一个包含 256 个条目的 idt 表,所有条目都指向类似的处理程序:

  • 对于异常 8 和 10-14,推送异常编号(这些异常会自动推送错误代码)
  • 对于其他人,推送“虚拟”错误代码和异常编号;
  • 然后跳转到一个通用处理程序

因此,当公共处理程序进入时,堆栈已正确对齐并包含异常/中断号、错误代码(可能只是一个伪代码)、eflags、cs 和 eip。

我的问题是关于从中断处理程序返回的。我使用iret从堆栈中取出异常编号和错误代码后返回,但这不适用于异常 nr 8;如果我将错误代码留在堆栈上,则返回正常!

问题:

  • 对于将错误代码放在那里的异常,我是否必须将错误代码留在堆栈上?如果是这样,如何iret确定它是否必须弹出错误代码?
  • 一旦我启用中断,我总是会得到异常 8(双重故障),但随后一切正常(我正在开发一个爱好操作系统)。这是正常行为还是我在某处有错误?
4

4 回答 4

13

如果 CPU 自动推送错误代码,则处理程序必须iret. 该iret指令不知道你来自哪里,如果它是一个错误,一个陷阱或一个外部中断。它总是做同样的事情,并且假设堆栈上没有错误代码。

引用 SDM(软件开发人员手册)第 3 卷第 5 章第 5.13 节标题为错误代码:

错误代码作为双字或字压入堆栈(取决于默认中断、陷阱或任务门大小)。为了使双字推送的堆栈保持对齐,保留错误代码的上半部分。请注意,执行 IRET 指令以从异常处理程序返回时不会弹出错误代码,因此处理程序必须在执行返回之前删除错误代码。

您可以在此处找到IA -32 软件开发人员手册 :http : //www.intel.com/products/processor/manuals/

第 3 卷第 1 部分第 5 章描述了异常和中断处理。第 2 卷第 1 部分有说明的规范iret

于 2009-01-29T13:50:57.160 回答
1

I wrote a small x86 OS a while back. Take a look at the file isr.asm in the cvs repository.

Notice how we set up the handlers, most push a dummy dword onto the stack to account for the few handlers that automatically get an error code pushed. Then when we return via an iret we can always assume 2 dwords on the stack irrespective of the interrupt and perform an add esp, 8 before the iret to clean things up nicely.

That should answer your first question.

As for your second question: A double fault when you enable interrupts, ...hmmm could be a problem with paging if you haven't set it up correctly. Could be a million other thing too :)

于 2009-01-29T14:02:10.773 回答
1

I had a similar problem with "double faults" as soon as I enabled interrupts. Well, they looked like double faults, but they really were timer interrupts!

Double faults are interrupt number 8.

Unfortunately, a default PIC configuration signals timer interrupts as interrupt number (DEFAULT_PIC_BASE + TIMER_OFFSET) = (8 + 0) = 8.

Masking out all my PIC interrupts (until I was ready to properly configure the PIC) silenced these double-fault-lookalike timer interrupts.

(PICs require the CPU to acknowledge interrupts before they produce the next one. Since your code wasn't acknowledging the initial timer interrupt, the PIC never gave you any more! That's why you only got one, rather than the zillion one might have expected.)

于 2015-01-25T19:53:26.313 回答
0

Do I have to leave the error code on the stack for exceptions that put the error code there?

As others mentioned, you have to do either:

pop %eax
/* Do something with %eax */
iret

Or if you want to ignore the error code:

add $4, %esp
iret

If you don't, iret will interpret the error code as the new CS, and you are likely to get a general protection fault as mentioned at: Why does iret from a page fault handler generate interrupt 13 (general protection fault) and error code 0x18?

Minimal Working this page handler that I've created to illustrate this. Try commenting out the pop and see it blow up.

Compare the above with a Division error exception which does not to pop the stack.

Note that if you do simply int $14, no extra byte gets pushed: this only happens on the actual exception.

Intel Manual Volume 3 System Programming Guide - 325384-056US September 2015 Table 6-1. "Protected-Mode Exceptions and Interrupts" column "Error Code" contains the list of interrupts that push the error code or not.

38.9.2.2 "Page Fault Error Codes" explains what the error means.

A neat way to deal with this is to push a dummy error code 0 on the stack for the interrupts that don't do this to make things uniform. James Molloy's tutorial does exactly that.

The Linux kernel 4.2 seems to do something similar. Under arch/x86/entry/entry64.S it models interrupts with has_error_code:

trace_idtentry page_fault do_page_fault has_error_code=1

and then uses it on the same file as:

.ifeq \has_error_code
pushq $-1 /* ORIG_RAX: no syscall to restart */
.endif

which does the push when has_error_code=0.

于 2015-10-28T18:08:08.033 回答