简体   繁体   English

无法修改数据段寄存器。 尝试时抛出一般保护错误

[英]Cannot modify data segment register. When tried General Protection Error is thrown

I have been trying to create an ISR handler following this tutorial by James Molloy but I got stuck.我一直在尝试按照 James Molloy 的本教程创建一个 ISR 处理程序,但我被卡住了。 Whenever I throw a software interrupt, general purpose registers and the data segment register is pushed onto the stack with the variables automatically pushed by the CPU.每当我抛出软件中断时,通用寄存器和数据段寄存器都会被推送到堆栈中,而 CPU 会自动推送变量。 Then the data segment is changed to the value of 0x10 (Kernel Data Segment Descriptor) so the privilege levels are changed.然后将数据段更改为 0x10(内核数据段描述符)的值,从而更改权限级别。 Then after the handler returns those values are pop ed.然后在处理程序返回后,这些值被pop But whenever the value in ds is changed a GPE is thrown with the error code 0x2544 and after a few seconds the VM restarts.但是,每当ds的值发生更改时,都会抛出错误代码为 0x2544 的 GPE,几秒钟后 VM 会重新启动。 (linker and compiler i386-elf-gcc , assembler nasm) (链接器和编译器 i386-elf-gcc ,汇编器 nasm)

I tried placing hlt instructions in between instructions to locate which instruction was throwing the GPE.我尝试在指令之间放置hlt指令以定位哪个指令正在抛出 GPE。 After that I was able to find out that the the `mov ds,ax' instruction.在那之后,我能够找到“mov ds,ax”指令。 I tried various things like removing the stack which was initialized by the bootstrap code to deleting the privilege changing parts of the code.我尝试了各种方法,例如删除由引导程序代码初始化的堆栈以删除代码的权限更改部分。 The only way I can return from the common stub is to remove the parts of my code which change the privilege levels but as I want to move towards user mode I still want them to stay.我可以从公共存根返回的唯一方法是删除更改权限级别的代码部分,但是当我想转向用户模式时,我仍然希望它们保留。

Here is my common stub:这是我的常见存根:

isr_common_stub:
    pusha                    ; Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax
    xor eax,eax
    mov ax, ds               ; Lower 16-bits of eax = ds.
    push eax                 ; save the data segment descriptor

    mov ax, 0x10  ; load the kernel data segment descriptor
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax

    call isr_handler

    xor eax,eax
    pop eax
    mov ds, ax ; This is the instruction everything fails;
    mov es, ax
    mov fs, ax
    mov gs, ax
    popa
    iret

My ISR handler macros:我的 ISR 处理程序宏:

extern isr_handler

%macro ISR_NOERRCODE 1
  global isr%1        ; %1 accesses the first parameter.
  isr%1:
    cli
    push byte 0
    push %1
    jmp isr_common_stub
%endmacro

%macro ISR_ERRCODE 1
  global isr%1
  isr%1:
    cli
    push byte %1
    jmp isr_common_stub
%endmacro
ISR_NOERRCODE 0
ISR_NOERRCODE 1
ISR_NOERRCODE 2
ISR_NOERRCODE 3
...

My C handler which results in "Received interrupt: 0xD err. code 0x2544"我的 C 处理程序导致“收到中断:0xD 错误。代码 0x2544”

#include <stdio.h>
#include <isr.h>
#include <tty.h>

void isr_handler(registers_t regs) {
    printf("ds: %x \n" ,regs.ds);
    printf("Received interrupt: %x with err. code: %x \n", regs.int_no, regs.err_code);
}

And my main function:而我的主要功能:

void kmain(struct multiboot *mboot_ptr) {
    descinit(); // Sets up IDT and GDT
    ttyinit(TTY0); // Sets up the VGA Framebuffer
    asm volatile ("int $0x1"); // Triggers a software interrupt
    printf("Wow"); // After that its supposed to print this
}

As you can see the code was supposed to output,正如你所看到的代码应该输出,

ds: 0x10
Received interrupt: 0x1 with err. code: 0

but results in,但结果是,

...
ds: 0x10
Received interrupt: 0xD with err. code: 0x2544

ds: 0x10
Received interrupt: 0xD with err. code: 0x2544
...

Which goes on until the VM restarts itself.这一直持续到 VM 自行重新启动。

What am I doing wrong?我究竟做错了什么?

The code isn't complete but I'm going to guess what you are seeing is a result of a well known bug in James Molloy's OSDev tutorial.代码并不完整,但我猜你看到的是 James Molloy 的 OSDev 教程中一个众所周知的错误的结果。 The OSDev community has compiled a list of known bugs in an errata list . OSDev 社区在勘误表中编制了一份已知错误列表 I recommend reviewing and fixing all the bugs mentioned there.我建议查看并修复那里提到的所有错误。 Specifically in this case I believe the bug that is causing problems is this one:特别是在这种情况下,我认为导致问题的错误是这个:

Problem: Interrupt handlers corrupt interrupted state问题:中断处理程序破坏了中断状态

This article previously told you to know the ABI.这篇文章之前告诉你要了解ABI。 If you do you will see a huge problem in the interrupt.s suggested by the tutorial: It breaks the ABI for structure passing!如果你这样做了,你会在教程建议的中断中看到一个巨大的问题:它破坏了结构传递的 ABI! It creates an instance of the struct registers on the stack and then passes it by value to the isr_handler function and then assumes the structure is intact afterwards.它在堆栈上创建结构寄存器的实例,然后按值将其传递给 isr_handler 函数,然后假设结构完好无损。 However, the function parameters on the stack belongs to the function and it is allowed to trash these values as it sees fit (if you need to know whether the compiler actually does this, you are thinking the wrong way, but it actually does).但是,堆栈上的函数参数属于该函数,并且可以按照它认为合适的方式丢弃这些值(如果您需要知道编译器是否真的这样做了,那么您的想法是错误的,但它确实这样做了)。 There are two ways around this.有两种方法可以解决这个问题。 The most practical method is to pass the structure as a pointer instead, which allows you to explicitly edit the register state when needed - very useful for system calls, without having the compiler randomly doing it for you.最实用的方法是将结构作为指针传递,这允许您在需要时显式编辑寄存器状态 - 对于系统调用非常有用,而无需编译器随机为您执行。 The compiler can still edit the pointer on the stack when it's not specifically needed.当不是特别需要时,编译器仍然可以编辑堆栈上的指针。 The second option is to make another copy the structure and pass that第二种选择是制作另一个副本结构并传递它

The problem is that the 32-bit System V ABI doesn't guarantee that data passed by value will be unmodified on the stack!问题是 32 位 System V ABI 不能保证按值传递的数据在堆栈上不会被修改! The compiler is free to reuse that memory for whatever purposes it chooses.编译器可以自由地为它选择的任何目的重用该内存。 The compiler probably generated code that trashed the area on the stack where DS is stored.编译器可能生成的代码破坏了堆栈上存储DS的区域。 When DS was set with the bogus value it crashed.DS设置为虚假值时,它崩溃了。 What you should be doing is passing by reference rather than value.你应该做的是通过引用而不是值传递。 I'd recommend these code changes in the assembly code:我建议在汇编代码中更改这些代码:

irq_common_stub:
    pusha
    mov ax, ds
    push eax
    mov ax, 0x10 ;0x10
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    push esp                 ; At this point ESP is a pointer to where GS (and the rest
                             ; of the interrupt handler state resides)
                             ; Push ESP as 1st parameter as it's a 
                             ; pointer to a registers_t  
    call irq_handler
    pop ebx                  ; Remove the saved ESP on the stack. Efficient to just pop it 
                             ; into any register. You could have done: add esp, 4 as well
    pop ebx
    mov ds, bx
    mov es, bx
    mov fs, bx
    mov gs, bx
    popa
    add esp, 8
    sti
    iret

And then modify irq_handler to use registers_t *regs instead of registers_t regs :然后修改irq_handler使用registers_t *regs而不是registers_t regs

void irq_handler(registers_t *regs) {
    if (regs->int_no >= 40) port_byte_out(0xA0, 0x20);
    port_byte_out(0x20, 0x20);

    if (interrupt_handlers[regs->int_no] != 0) {
        interrupt_handlers[regs->int_no](*regs);
    }
    else
    {
        klog("ISR: Unhandled IRQ%u!\n", regs->int_no);
    }
}

I'd actually recommend each interrupt handler take a pointer to registers_t to avoid unnecessary copying.我实际上建议每个中断处理程序都使用一个指向registers_t的指针以避免不必要的复制。 If your interrupt handlers and the interrupt_handlers array used function that took registers_t * as the parameter (instead of registers_t ) then you'd modify the code:如果您的中断处理程序和interrupt_handlers数组使用了以registers_t *作为参数(而不是registers_t )的函数,那么您需要修改代码:

interrupt_handlers[r->int_no](*regs); 

to be:成为:

interrupt_handlers[r->int_no](regs);

Important : You have to make these same type of changes for your ISR handlers as well.重要提示:您还必须对ISR 处理程序进行这些相同类型的更改。 Both the IRQ and ISR handlers and associated code have this same problem. IRQ 和 ISR 处理程序以及相关代码都存在同样的问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 内联汇编错误:在重新加载“asm”时在 class 'GENERAL_REGS' 中找不到寄存器 - inline assembly error: can't find a register in class 'GENERAL_REGS' while reloading 'asm' 避免使用 .data 段 - Avoid the use of .data segment 如何解决C中内联汇编中的错误:“在重新加载“ asm”时找不到类“ GENERAL_REGS”中的寄存器” - how to solve error in inline assembly in C: 'can't find a register in class 'GENERAL_REGS' while reloading 'asm'' 尝试安装python软件包,但遇到“找不到-lgcc_s”错误 - tried to install a python package but encountered “cannot find -lgcc_s” error 如何在ASM内联语句中请求通用寄存器? - How to ask for a general purpose register in an asm inline statement? -Os标志,使通用寄存器正常工作是必需的 - -Os flag necessary to make general purpose register behave normally 尝试执行[:digit:]或\\ d之类的简单操作时会抛出regex_error - regex_error being thrown when trying to do simple things like [:digit:] or \d ARM汇编:重新加载“ asm”时无法在类“ GENERAL_REGS”中找到寄存器 - ARM assembly: can’t find a register in class ‘GENERAL_REGS’ while reloading ‘asm’ 了解C中数据bss段的size命令 - understanding size command for data bss segment in C 添加printf会增加数据段的大小 - adding printf increases the size of data segment
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM