
/**
 * @file: idt.c
 *
 * Tento modul implementuje nekolik rutin pro praci s obsahem tabuky
 * vektoru preruseni (IDT). Rutiny provadi operace s touto tabulkou
 * pouze na aktulalnim logickem procesoru (kazdy logicky procesor ma
 * vlastni tabulku vektoru preruseni).
 */

#include <ntddk.h>
#include "intcount.h"
#include "cr0trick.h"
#include "sync.h"
#include "idt.h"

/** Vytvori ULONG ze dvou shortu.
 *
 * Tato rutina je potrebna, protoze informace o adrese obsluzne rutiny
 * v deskriptoru tabulky vektoru preruseni je ulozena po castech na
 * dvou mistech.
 *
 * @param a Dolnich 16 bitu noveho ULONGu.
 * @param b Hornich 16 bitu noveho ULONGu.
 *
 * @return Funkce vraci ULONG vytvoreny z dvou shortu.
 */
static ULONG MAKELONG(unsigned short a, unsigned short b)
{
  return (ULONG) (a | (b << 16));
}


/** Vrati adresu obsluzne rutiny zadaneho proruseni.
 *
 * @param VectorNumber Index vektoru preruseni. Muze nabyvat hodnoty od
 * 0 do 255.
 *
 * @return Funkce vrati adresu obsluzne rutiny zadaneho preruseni.
 */
PVOID IdtGetIsrAddress(IN ULONG VectorNumber)
{
  IDTINFO IDTInfo;
  ULONG IDTBase;
  PIDTENTRY IDTEntry;
  PVOID Ret = NULL;
  KdPrint(("idt.c: IdtGetIsrAddress(VectorNumber=%u)\n", VectorNumber));

  // Zjisteni virtualni adresu a limit IDT aktualniho procesoru
  __asm sidt IDTInfo
  IDTBase = MAKELONG(IDTInfo.LowIDTBase, IDTInfo.HiIDTBase);
  // Vypocet adresy prislusneho deskriptoru a ziskani adresy obsluzne rutiny
  IDTEntry = (PIDTENTRY) (IDTBase + sizeof(IDTENTRY) * VectorNumber);
  Ret = (PVOID) MAKELONG(IDTEntry->LowIDTOffset, IDTEntry->HiIDTOffset);

  KdPrint(("idt.c: IdtGetIsrAddress(-):0x%p\n", Ret));
  return Ret;
}


/** Zmeni adresu obsluzne rutiny zadaneho preruseni.
 *
 * @param VectorNumber Cislo ciloveho preruseni. Muze nabyvat hodnot od
 * 0 do 255.
 * @param NewAddress Nova adresa obsluzne rutiny.
 *
 * @return Funkce vrati puvodni adresu obsluzne rutiny daneho vektoru
 * preruseni.
 */
VOID IdtModifyIsr(IN ULONG VectorNumber, IN PVOID NewAddress)
{
  IDTINFO IDTInfo;
  ULONG IDTBase;
  PIDTENTRY IDTEntry;
  KdPrint(("idt.c: IdtModifyIsr(VectorNumber=%u; NewAddress=0x%p)\n", VectorNumber, NewAddress));

  __asm sidt IDTInfo
  IDTBase = MAKELONG(IDTInfo.LowIDTBase, IDTInfo.HiIDTBase);
  IDTEntry = (PIDTENTRY) (IDTBase + sizeof(IDTENTRY) * VectorNumber);
  // IDT muze byt chranena proti zapisu, proto je pred samotnym prepsanim
  // adresy obsluzne rutiny nutne tuto ochranu na vypnout a po nem zase
  // zapnout.
  CR0TrickBegin();
  IDTEntry->LowIDTOffset = (USHORT)((ULONG)NewAddress & 0xFFFF);
  IDTEntry->HiIDTOffset = (USHORT)((ULONG)NewAddress >> 16);
  CR0TrickEnd();

  KdPrint(("idt.c: IdtModifyIsr(-)\n"));
  return;
}
