~kernel/idt_v2.cr
.cr
Crystal
(text/x-crystal)
lib KernelShim
  fun outb(port : UInt16, value : UInt8)
  fun wait()
end

module IDT
  # Constants
  IDT_ADDRESS = 0xc0007100_u32
  KERNEL_CODE_SEGMENT_SELECTOR = 0x18_u16
  INTERRUPT_GATE = 0x8e_u8

  # Structs
  struct IDTEntry
    offset_low : UInt16
    selector : UInt16
    unused : UInt8
    type : UInt8
    offset_high : UInt16
  end

  struct IDTPointer
    size : UInt16
    address : UInt32
  end

  module Handlers
    def self.handle_division_by_zero_exception
      # handler code
    end

    def self.handle_debug_exception
      # handler code
    end

    def self.handle_non_maskable_exception
      # handler code
    end

    def self.handle_breakpoint_exception
      # handler code
    end

    def self.handle_overflow_exception
      # handler code
    end

    def self.handle_bound_range_exceeded_exception
      # handler code
    end

    def self.handle_invalid_opcode_exception
      # handler code
    end

    def self.handle_device_not_available_exception
      # handler code
    end

    def self.handle_double_fault_exception
      # handler code
    end

    def self.handle_invalid_tss_exception
      # handler code
    end

    def self.handle_segment_not_present_exception
      # handler code
    end

    def self.handle_stack_segment_fault_exception
      # handler code
    end

    def self.handle_general_protection_fault_exception
      # handler code
    end

    def self.handle_page_fault_exception
      # handler code
    end

    def self.handle_x87_floating_point_exception
      # handler code
    end

    def self.handle_alignment_check_exception
      # handler code
    end

    def self.handle_machine_check_exception
      # handler code
    end

    def self.handle_simd_floating_point_exception
      # handler code
    end

    def self.handle_virtualisation_exception
      # handler code
    end

    def self.handle_security_exception
      # handler code
    end

    def self.handle_interrupt_request_0
      # handler code
    end

    def self.handle_interrupt_request_1
      # handler code
    end

    def self.handle_interrupt_request_2
      # handler code
    end

    def self.handle_interrupt_request_3
      # handler code
    end

    def self.handle_interrupt_request_4
      # handler code
    end

    def self.handle_interrupt_request_5
      # handler code
    end

    def self.handle_interrupt_request_6
      # handler code
    end

    def self.handle_interrupt_request_7
      # handler code
    end

    def self.handle_interrupt_request_8
      # handler code
    end

    def self.handle_interrupt_request_9
      # handler code
    end

    def self.handle_interrupt_request_10
      # handler code
    end

    def self.handle_interrupt_request_11
      # handler code
    end

    def self.handle_interrupt_request_12
      # handler code
    end

    def self.handle_interrupt_request_13
      # handler code
    end

    def self.handle_interrupt_request_14
      # handler code
    end

    def self.handle_interrupt_request_15
      # handler code
    end
  end

  # Functions
  def self.remap_pic
    MASTER_PIC_COMMAND = 0x20_u8
    MASTER_PIC_DATA = 0x21_u8
    SLAVE_PIC_COMMAND = 0xa0_u8
    SLAVE_PIC_DATA = 0xa1_u8

    INIT_SEQUENCE_START = 0x11_u8
    MASTER_PIC_OFFSET = 0x20_u8
    SLAVE_PIC_OFFSET = 0x28_u8
    MODE_8086 = 0x01_u8

    KernelShim.outb(MASTER_PIC_COMMAND, INIT_SEQUENCE_START)
    KernelShim.wait()
    KernelShim.outb(SLAVE_PIC_COMMAND, INIT_SEQUENCE_START)
    KernelShim.wait()
    KernelShim.outb(MASTER_PIC_DATA, MASTER_PIC_OFFSET)
    KernelShim.wait()
    KernelShim.outb(SLAVE_PIC_DATA, SLAVE_PIC_OFFSET)
    KernelShim.wait()
    KernelShim.outb(MASTER_PIC_DATA, 0x04_u8) # Tells master there's a slave on IRQ2
    KernelShim.wait()
    KernelShim.outb(SLAVE_PIC_DATA, 0x02_u8) # Tells slave its cascade identity
    KernelShim.wait()
    KernelShim.outb(MASTER_PIC_DATA, MODE_8086)
    KernelShim.wait()
    KernelShim.outb(SLAVE_PIC_DATA, MODE_8086)
    KernelShim.wait()
    KernelShim.outb(MASTER_PIC_DATA, 0x00_u8) # Masks all interrupts (enable)
    KernelShim.wait()
    KernelShim.outb(SLAVE_PIC_DATA, 0x00_u8) # Masks all interrupts (enable)
    KernelShim.wait()
  end

  def self.set_up_idt_entries(idt_entries : IDTEntry*)
    handlers = [
      Handlers.handle_division_by_zero_exception,
      Handlers.handle_debug_exception,
      Handlers.handle_non_maskable_exception,
      Handlers.handle_breakpoint_exception,
      Handlers.handle_overflow_exception,
      Handlers.handle_bound_range_exceeded_exception,
      Handlers.handle_invalid_opcode_exception,
      Handlers.handle_device_not_available_exception,
      Handlers.handle_double_fault_exception,
      Handlers.handle_invalid_tss_exception,
      Handlers.handle_segment_not_present_exception,
      Handlers.handle_stack_segment_fault_exception,
      Handlers.handle_general_protection_fault_exception,
      Handlers.handle_page_fault_exception,
      Handlers.handle_x87_floating_point_exception,
      Handlers.handle_alignment_check_exception,
      Handlers.handle_machine_check_exception,
      Handlers.handle_simd_floating_point_exception,
      Handlers.handle_virtualisation_exception,
      Handlers.handle_security_exception,
      Handlers.handle_interrupt_request_0,
      Handlers.handle_interrupt_request_1,
      Handlers.handle_interrupt_request_2,
      Handlers.handle_interrupt_request_3,
      Handlers.handle_interrupt_request_4,
      Handlers.handle_interrupt_request_5,
      Handlers.handle_interrupt_request_6,
      Handlers.handle_interrupt_request_7,
      Handlers.handle_interrupt_request_8,
      Handlers.handle_interrupt_request_9,
      Handlers.handle_interrupt_request_10,
      Handlers.handle_interrupt_request_11,
      Handlers.handle_interrupt_request_12,
      Handlers.handle_interrupt_request_13,
      Handlers.handle_interrupt_request_14,
      Handlers.handle_interrupt_request_15,
    ]

    handlers.each_with_index do |handler, index|
      idt_entries[index].offset_low = (handler.address & 0x0000ffff).to_u16
      idt_entries[index].selector = KERNEL_CODE_SEGMENT_SELECTOR
      idt_entries[index].unused = 0_u8
      idt_entries[index].type = INTERRUPT_GATE
      idt_entries[index].offset_high = ((handler.address & 0xffff0000) >> 16).to_u16
    end
  end

  def self.load_idt(idt_entries : IDTEntry*)
    idt_pointer = IDTPointer.new
    idt_pointer.size = sizeof(IDTEntry) * 256
    idt_pointer.address = IDT_ADDRESS

    asm "lidt %{idt_pointer}" : : "m"(idt_pointer)
    asm "sti" ::
  end

  def self.init
    remap_pic
    idt_entries = Pointer(IDTEntry).new(IDT_ADDRESS)
    set_up_idt_entries(idt_entries)
    load_idt(idt_entries)
  end
end