2016-01-01 9 views
5

保護モードの割り込みを設定するプロセスは何ですか?保護モードでの割り込みの設定(x86)

Thisリンクは1がすべきと言う:

  • 割り込みディスクリプタ・テーブルのためのメイクスペース
  • そのスペースがあるCPUに知らせる(GDTチュートリアルを参照:lgdtとして非常に同じように動作LIDT)を
  • BIOSのデフォルトを使用しないようにPICに指示してください(PICチップのプログラミングを参照)
  • IRQと例外の両方について、ISRハンドラを2つ書きます(割り込みサービスルーチンを参照)
  • は(PICの)IRQマスクでサポートされているすべての割り込み

は、第三のステップは、私には意味がありませんを有効にする(私は見

  • 適切な記述子でISRハンドラのアドレスを入れてくださいthisリンクがありましたが、PICのことを伝えることについて何もありませんでした)ので、私はそれを無視して次の2つのステップを完了しました。しかし、割り込みの私の理解から、私が理解しなかったステップの両方は、PICコントローラからのハードウェア割り込みに関連し、IRQ 0のPITによって引き起こされた割り込みに影響を与えるべきではありません。

    コードを実行したところ、コンパイルは正常に完了し、仮想マシンで実行されましたが、割り込みは1回しか発生しませんでした。私はその後、PICにEOIを送信していないことを認識し、割り込みをさらに発生させないようにしました。ただし、iret命令の直前にmov al, 0x20out 0x20, alを追加すると、仮想マシンはクラッシュします。ここで

    は私のIDTだ:

    ; idt 
    idt_start : 
    
        dw 0x00   ; The interrupt handler is located at absolute address 0x00 
        dw CODE_SEG  ; CODE_SEG points to the GDT entry for code 
        db 0x0   ; The unused byte 
        db 0b11101001 ; 1110 Defines a 32 bit Interrupt gate, 0 is mandatory, privilege level = 0 (0b00), the last bit is one so that the CPU knows that the interrupt will be used 
        dw 0x00   ; The higher part of the offset (0x00) is 0x00 
    
    idt_end: 
    
    idt_descriptor : 
        dw idt_end - idt_start - 1 ; Size of our idt, always one less than the actual size 
        dd idt_start ; Start address of our idt 
    

    は、ここで(メモリ内の絶対位置は0x00にある)私の割り込みハンドラです:

    ISR_0: 
        push eax 
        add [0x300], byte 
        mov al, 0x20 
        out 0x20, al 
        pop eax 
        iret  
        times 512-($-$$) db 0 
    

    これは私が保護モードに入り、GDTをロードするために使用するコードですIDTをメモリに格納します。

    [bits 16] 
    
    switch_to_pm: 
    
        cli 
        lgdt [gdt_descriptor] 
        lidt [idt_descriptor] 
        mov eax, cr0 
        or eax, 1 
        mov cr0,eax 
        jmp CODE_SEG:init_pm 
    
    [bits 32] 
    
    init_pm : 
    
        mov ax, DATA_SEG 
        mov ds, ax 
        mov ss, ax 
        mov es, ax 
        mov fs, ax 
        mov gs, ax 
        mov ebp, 0x90000 
        mov esp, ebp 
        sti 
        call BEGIN_PM 
    

    私の主な機能(0x300の値をチェックする)は次のとおりです。

    void main() { 
        char iii[15]; 
        int * aa = (int *)0x300; 
        for (;;) 
        { 
         setCursor(0, 0); 
         print(itoab(*aa, iii)); 
        } 
    } 
    

    ところで、メモリダンプを使用して、すべてが正しいアドレスにロードされていることが確認されています。たとえば、0x300はコードを単純化するために使用されるメモリの自由な部分です。

  • +1

    第三のステップは、それらの標準BIOSのマッピングからの割り込み再マッピングの通常の手順を意味する:以下のように/asm/system.hに定義されています。通常IRQ 0-7はINT 8-15にマッピングされ、IRQ 8-15はINT 0x70-0x77にマッピングされます。多くのオペレーティングシステムでは、IRQ 0-7がCPU例外用に予約された範囲外のものに再マップされます。 –

    +0

    これは、PIT IRQ 0に接続されているINT 8はINT 8を生成します。INT 0は整数除算のオーバーフロー(ゼロ除算)例外です。 –

    +0

    ステップ3を実行して、最初の32個のIDTエントリの上にIRQを移動することを強くお勧めします。また、最初の32人のハンドラを置くことを強くお勧めします。問題が発生したかどうかを確認することができます。 [あなたの変数には揮発性のものを使う! –

    答えて

    6

    比較的小さいカーネル、つまりLinux 0.01がどのように機能するのかを見てみましょう。これは2回行われ

    割り込みディスクリプタ・テーブルのための

    • メイクスペースが(まあ、技術的には一度だけ):まず、ブートローダーは(パスが/boot/boot.sある)IDTRを初期化、保護モードにジャンプするとCPUは満足しています。次のようにIDTRの内容は次のとおりです。

      lidt  idt_48  | load idt with 0,0 
      

      さて、ジャンプを実行することができます。

      idt_48: 
          .word 0   | idt limit=0 
          .word 0,0  | idt base=0L 
      

      IDTRは、次のようにロードされます。
      IDTはありません。それは単なるダミーなので、カーネルのどこかでエラーは発生しません。

      その後、実際のIDTが初期化されます(パスは/boot/head.sです)。スペースは、次のように割り当てられている:

      _idt: .fill 256,8,0  # idt is uninitialized 
      
      • は、そのスペースがあるCPUに知らせる(GDTチュートリアルを参照:lgdtと非常に同じように動作lidt)を

      lidtが期待IDTRの内容を含む非線形アドレスである。その内容は次のようになります。

      idt_descr: 
          .word 256*8-1  # idt contains 256 entries 
          .long _idt 
      

      次のようにIDTRが初期化されます。

      lidt idt_descr 
      
      • はPICチップのプログラミングを参照してください(あなたはもはやBIOSのデフォルトを使用するPICを知らせます)

      @RossRidgeはお客様のコメントに記載されていますそれは、IRQ割り込みベクタ(IV)を再マッピングすることを意味します。
      PIC IVはIntel x86例外アドレスと重複しているため、それらの1つを再マップする必要があります。例外アドレスは固定配線されているため、PICベクタを再マップする必要があります。
      は、右はLinusによって、対応するコードの上にも、このコメントを参照してください。

      | well, that went ok, I hope. Now we have to reprogram the interrupts :-(
      | we put them right after the intel-reserved hardware interrupts, at 
      | int 0x20-0x2F. There they won't mess up anything. Sadly IBM really 
      | messed this up with the original PC, and they haven't been able to 
      | rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f, 
      | which is used for the internal hardware interrupts as well. We just 
      | have to reprogram the 8259's, and it isn't fun. 
      

      さて、ここで実際のコードです。その間のjmpは、CPUとPICを同期させるためのものであるため、CPUはPICがまだ受信できないデータを送信しません。これは、メモリに書き込むときの待機状態に匹敵します。つまり、CPUがメモリ/メモリアービタより高速の場合、次回にメモリにアクセスする前にある程度待たなければなりません。

      mov al,#0x11  | initialization sequence 
      out #0x20,al  | send it to 8259A-1 
      .word 0x00eb,0x00eb  | jmp $+2, jmp $+2 
      out #0xA0,al  | and to 8259A-2 
      .word 0x00eb,0x00eb 
      mov al,#0x20  | start of hardware int's (0x20) 
      out #0x21,al 
      .word 0x00eb,0x00eb 
      mov al,#0x28  | start of hardware int's 2 (0x28) 
      out #0xA1,al 
      .word 0x00eb,0x00eb 
      mov al,#0x04  | 8259-1 is master 
      out #0x21,al 
      .word 0x00eb,0x00eb 
      mov al,#0x02  | 8259-2 is slave 
      out #0xA1,al 
      .word 0x00eb,0x00eb 
      mov al,#0x01  | 8086 mode for both 
      out #0x21,al 
      .word 0x00eb,0x00eb 
      out #0xA1,al 
      .word 0x00eb,0x00eb 
      mov al,#0xFF  | mask off all interrupts for now 
      out #0x21,al 
      .word 0x00eb,0x00eb 
      out #0xA1,al 
      
      • の両方のIRQと例外の例外については

      のためのISRハンドラのカップルを(割り込みサービスルーチンを参照してください)書く、あなたは/kernel/traps.c/kernel/asm.sにハンドラコードを見つけることができます。
      いくつかの例外は、ハンドラにジャンプする前にスタック上のエラーコードをプッシュします。ハンドラに飛び越すか、iret命令が失敗します。また、ページフォルトは対応する仮想アドレスをcr2に書き込みます。
      IRQハンドラはシステム全体に分散しています。 -.-タイマーとディスク割り込みハンドラは/kernel/system_call.s、キーボード割り込みハンドラは/kernel/keyboard.sです。

      • 例外の初期化がtrap_init機能で/kernel/traps.cで行われ

      適切な記述子でISRハンドラのアドレスを入れてください:

      void trap_init(void) 
      { 
          int i; 
      
          set_trap_gate(0,&divide_error); 
          set_trap_gate(1,&debug); 
          set_trap_gate(2,&nmi); 
          set_system_gate(3,&int3); /* int3-5 can be called from all */ 
          set_system_gate(4,&overflow); 
          set_system_gate(5,&bounds); 
          set_trap_gate(6,&invalid_op); 
          set_trap_gate(7,&device_not_available); 
          set_trap_gate(8,&double_fault); 
          set_trap_gate(9,&coprocessor_segment_overrun); 
          set_trap_gate(10,&invalid_TSS); 
          set_trap_gate(11,&segment_not_present); 
          set_trap_gate(12,&stack_segment); 
          set_trap_gate(13,&general_protection); 
          set_trap_gate(14,&page_fault); 
          set_trap_gate(15,&reserved); 
          set_trap_gate(16,&coprocessor_error); 
          for (i=17;i<32;i++) 
           set_trap_gate(i,&reserved); 
      /* __asm__("movl $0x3ff000,%%eax\n\t" 
           "movl %%eax,%%db0\n\t" 
           "movl $0x000d0303,%%eax\n\t" 
           "movl %%eax,%%db7" 
           :::"ax");*/ 
      } 
      

      IRQハンドラエントリの初期化が複数のファイルにまたがって広がります。 sched_initから/kernel/sched.cまではタイマ割り込みハンドラのアドレスを初期化します。

      • これはマクロstimain関数で/init/main.cで行われる(PICの)IRQマスクのすべてのサポートされている割り込み

      を有効にします。

      #define sti() __asm__ ("sti"::) 
      
    +0

    詳細な回答ありがとうございます! 'int 32'を呼び出すとPIT割り込みハンドラが起動しますが、' sti'命令の後でもPIT割り込みは決して起動しません。 – DividedByZero

    関連する問題