/*
 * Fiasco Thread Code
 * Shared between UX and native IA32.
 */
INTERFACE [ia32,amd64,ux]:

#include "trap_state.h"

class Idt_entry;

EXTENSION class Thread
{
private:
  /**
   * Return code segment used for exception reflection to user mode
   */
  static Mword exception_cs();

protected:
  static Trap_state::Handler nested_trap_handler FIASCO_FASTCALL;
};


//----------------------------------------------------------------------------
INTERFACE [ia32,amd64]:

class Trap_state;

EXTENSION class Thread
{
private:
  static int  (*int3_handler)(Trap_state*);
};


//----------------------------------------------------------------------------
IMPLEMENTATION [ia32,amd64,ux]:

#include "config.h"
#include "cpu.h"
#include "cpu_lock.h"
#include "gdt.h"
#include "idt.h"
#include "ipi.h"
#include "mem_layout.h"
#include "logdefs.h"
#include "paging.h"
#include "processor.h"		// for cli/sti
#include "regdefs.h"
#include "std_macros.h"
#include "thread.h"
#include "timer.h"
#include "trap_state.h"

Trap_state::Handler Thread::nested_trap_handler FIASCO_FASTCALL;

IMPLEMENT
Thread::Thread()
: Receiver(),
  Sender(0),   // select optimized version of constructor
  _pager(Thread_ptr::Invalid),
  _exc_handler(Thread_ptr::Invalid),
  _del_observer(0)
{
  assert (state(false) == Thread_invalid);

  inc_ref();
  _space.space(Kernel_task::kernel_task());

  if (Config::Stack_depth)
    std::memset((char*)this + sizeof(Thread), '5',
		Thread::Size-sizeof(Thread)-64);

  _magic          = magic;
  _recover_jmpbuf = 0;
  _timeout        = 0;

  *reinterpret_cast<void(**)()> (--_kernel_sp) = user_invoke;

  arch_init();

  state_add_dirty(Thread_dead, false);

  // ok, we're ready to go!
}

IMPLEMENT inline NEEDS[Thread::exception_triggered]
Mword
Thread::user_ip() const
{ return exception_triggered()?_exc_cont.ip():regs()->ip(); }

IMPLEMENT inline
Mword
Thread::user_flags() const
{ return regs()->flags(); }

/** Check if the pagefault occured at a special place: At some places in the
    kernel we want to ensure that a specific address is mapped. The regular
    case is "mapped", the exception or slow case is "not mapped". The fastest
    way to check this is to touch into the memory. If there is no mapping for
    the address we get a pagefault. Now the pagefault exception handler can
    recognize that situation by scanning the code. The trick is that the
    assembler instruction "andl $0xffffffff, %ss:(%ecx)" _clears_ the carry
    flag normally (see Intel reference manual). The pager wants to inform the
    code that there was a pagefault and therefore _sets_ the carry flag. So
    the code has only to check if the carry flag is set. If yes, there was
    a pagefault at this instruction.
    @param ip pagefault address */
IMPLEMENT inline
bool
Thread::pagein_tcb_request(Return_frame *regs)
{
  unsigned long new_ip = regs->ip();
  if (*(Unsigned8*)new_ip == 0x48) // REX.W
    new_ip += 1;

  register Unsigned16 op = *(Unsigned16*)new_ip;
  //LOG_MSG_3VAL(current(),"TCB", op, new_ip, 0);
  if ((op & 0xc0ff) == 0x8b) // Context::is_tcb_mapped() and Context::state()
    {
      regs->ip(new_ip + 2);
      // stack layout:
      //         user eip
      //         PF error code
      // reg =>  eax/rax
      //         ecx/rcx
      //         edx/rdx
      //         ...
      Mword *reg = ((Mword*)regs) - 2 - Return_frame::Pf_ax_offset;
#if 0
      LOG_MSG_3VAL(current(),"TCB", op, regs->ip(), (Mword)reg);
      LOG_MSG_3VAL(current(),"TCBX", reg[-3], reg[-4], reg[-5]);
      LOG_MSG_3VAL(current(),"TCB0", reg[0], reg[-1], reg[-2]);
      LOG_MSG_3VAL(current(),"TCB1", reg[1], reg[2], reg[3]);
#endif
      assert((op >> 11) <= 2);
      reg[-(op>>11)] = 0; // op==0 => eax, op==1 => ecx, op==2 => edx

      // tell program that a pagefault occured we cannot handle
      regs->flags(regs->flags() | 0x41); // set carry and zero flag in EFLAGS
      return true;
    }
  else if (*(Unsigned32*)regs->ip() == 0xff01f636) // used in shortcut.S
    {
      regs->ip(regs->ip() + 4);
      regs->flags(regs->flags() | 1);  // set carry flag in EFLAGS
      return true;
    }

  return false;
}


extern "C" FIASCO_FASTCALL
void
thread_restore_exc_state()
{
  current_thread()->restore_exc_state();
}

PRIVATE static
void
Thread::print_page_fault_error(Mword e)
{
  printf("%lx", e);
}

/**
 * The global trap handler switch.
 * This function handles CPU-exception reflection, emulation of CPU 
 * instructions (LIDT, WRMSR, RDMSR), int3 debug messages, 
 * kernel-debugger invocation, and thread crashes (if a trap cannot be 
 * handled).
 * @param state trap state
 * @return 0 if trap has been consumed by handler;
 *          -1 if trap could not be handled.
 */
PUBLIC
int
Thread::handle_slow_trap(Trap_state *ts)
{
  Address ip;
  int from_user = ts->cs() & 3;

  if (EXPECT_FALSE(ts->_trapno == 0xee)) //debug IPI
    {
      Ipi::eoi(Ipi::Debug, cpu());
      goto generic_debug;
    }

  if (from_user && _space.user_mode())
    {
      if (ts->_trapno == 14 && Kmem::is_io_bitmap_page_fault(ts->_cr2))
        {
	  ts->_trapno = 13;
	  ts->_err    = 0;
        }

      if (send_exception(ts))
	goto success;
    }

  // XXX We might be forced to raise an excepton. In this case, our return
  // CS:IP points to leave_by_trigger_exception() which will trigger the
  // exception just before returning to userland. But if we were inside an
  // IPC while we was ex-regs'd, we will generate the 'exception after the
  // syscall' _before_ we leave the kernel.
  if (ts->_trapno == 13 && (ts->_err & 6) == 6)
    goto check_exception;

  LOG_TRAP;

  if (!check_trap13_kernel (ts))
    return 0;

  if (EXPECT_FALSE(!from_user))
    {
      // get also here if a pagefault was not handled by the user level pager
      if (ts->_trapno == 14)
	goto check_exception;

      goto generic_debug;      // we were in kernel mode -- nothing to emulate
    }

  if (EXPECT_FALSE(ts->_trapno == 2))
    goto generic_debug;        // NMI always enters kernel debugger

  if (EXPECT_FALSE(ts->_trapno == 0xffffffff))
    goto generic_debug;        // debugger interrupt

  check_f00f_bug(ts);

  // so we were in user mode -- look for something to emulate

  // We continue running with interrupts off -- no sti() here. But
  // interrupts may be enabled by the pagefault handler if we get a
  // pagefault in peek_user().

  // Set up exception handling.  If we suffer an un-handled user-space
  // page fault, kill the thread.
  jmp_buf pf_recovery;
  unsigned error;
  if (EXPECT_FALSE ((error = setjmp(pf_recovery)) != 0) )
    {
      WARN("%p killed:\n"
           "\033[1mUnhandled page fault, code=%08x\033[m\n", this, error);
      goto fail_nomsg;
    }

  _recover_jmpbuf = &pf_recovery;

  switch (handle_io_page_fault(ts))
    {
    case 1: goto success;
    case 2: goto fail;
    default: break;
    }

  ip = ts->ip();

  // just print out some warning, we do the normal exception handling
  handle_sysenter_trap(ts, ip, from_user);
  _recover_jmpbuf = 0;

check_exception:

  // send exception IPC if requested
  if (send_exception(ts))
    goto success;

  // backward compatibility cruft: check for those insane "int3" debug
  // messaging command sequences
  if (ts->_trapno == 3)
    {
      if (int3_handler && int3_handler(ts))
	goto success;

      goto generic_debug;
    }

  // privileged tasks also may invoke the kernel debugger with a debug
  // exception
  if (ts->_trapno == 1)
    goto generic_debug;


fail:
  // can't handle trap -- kill the thread
  WARN("%p killed:\n"
       "\033[1mUnhandled trap \033[m\n", this);

fail_nomsg:
  if ((int)Config::Warn_level >= Warning)
    ts->dump();

  halt();

success:
  _recover_jmpbuf = 0;
  return 0;

generic_debug:
  _recover_jmpbuf = 0;

  if (!nested_trap_handler)
    return handle_not_nested_trap(ts);

  return call_nested_trap_handler(ts);
}

/**
 * The low-level page fault handler called from entry.S.  We're invoked with
 * interrupts turned off.  Apart from turning on interrupts in almost
 * all cases (except for kernel page faults in TCB area), just forwards
 * the call to Thread::handle_page_fault().
 * @param pfa page-fault virtual address
 * @param error_code CPU error code
 * @return true if page fault could be resolved, false otherwise
 */
extern "C" FIASCO_FASTCALL
int
thread_page_fault(Address pfa, Mword error_code, Address ip, Mword flags,
		  Return_frame *regs)
{

  // XXX: need to do in a different way, if on debug stack e.g.
#if 0
  // If we're in the GDB stub -- let generic handler handle it
  if (EXPECT_FALSE (!in_context_area((void*)Proc::stack_pointer())))
    return false;
#endif

  // Pagefault in user mode or interrupts were enabled
  if (PF::is_usermode_error(error_code))
    {
      if (current_thread()->vcpu_pagefault(pfa, error_code, ip))
        return 1;

      Proc::sti();
    }
  else if(flags & EFLAGS_IF)
    Proc::sti();

  // Pagefault in kernel mode and interrupts were disabled
  else
    {
      // page fault in kernel memory region
      if (Kmem::is_kmem_page_fault(pfa, error_code))
	{
	  // We've interrupted a context in the kernel with disabled interrupts,
	  // the page fault address is in the kernel region, the error code is
	  // "not mapped" (as opposed to "access error"), and the region is
	  // actually valid (that is, mapped in Kmem's shared page directory,
	  // just not in the currently active page directory)
	  // Remain cli'd !!!
	}
      else if (!Kmem::is_kmem_page_fault(pfa, error_code))
	{
          // No error -- just enable interrupts.
	  Proc::sti();
	}
      else
	{
          // Error: We interrupted a cli'd kernel context touching kernel space
	  if (!Thread::log_page_fault())
	    printf("*P[%lx,%lx,%lx] ", pfa, error_code & 0xffff, ip);

	  kdb_ke ("page fault in cli mode");
	}
    }

  return current_thread()->handle_page_fault(pfa, error_code, ip, regs);
}

/** The catch-all trap entry point.  Called by assembly code when a 
    CPU trap (that's not specially handled, such as system calls) occurs.
    Just forwards the call to Thread::handle_slow_trap().
    @param state trap state
    @return 0 if trap has been consumed by handler;
           -1 if trap could not be handled.
 */
extern "C" FIASCO_FASTCALL
int
thread_handle_trap(Trap_state *ts, unsigned)
{
  return current_thread()->handle_slow_trap(ts);
}


//
// Public services
//

IMPLEMENT inline
bool
Thread::handle_sigma0_page_fault(Address pfa)
{
  size_t size;

  // Check if mapping a superpage doesn't exceed the size of physical memory
  if (Cpu::have_superpages()
     // Some distributions do not allow to mmap below a certain threshold
     // (like 64k on Ubuntu 8.04) so we cannot map a superpage at 0 if
     // we're Fiasco-UX
      && (!Config::Is_ux || !(pfa < Config::SUPERPAGE_SIZE)))
    {
      pfa &= Config::SUPERPAGE_MASK;
      size = Config::SUPERPAGE_SIZE;
    }
  else
    {
      pfa &= Config::PAGE_MASK;
      size = Config::PAGE_SIZE;
    }

  return mem_space()->v_insert(Mem_space::Phys_addr(pfa), Mem_space::Addr(pfa),
                               Mem_space::Size(size),
                               Mem_space::Page_writable
                               | Mem_space::Page_user_accessible)
    != Mem_space::Insert_err_nomem;
}

PRIVATE static inline
void
Thread::save_fpu_state_to_utcb(Trap_state *, Utcb *)
{}

/* return 1 if this exception should be sent, return 0 if not
 */
PUBLIC inline NEEDS["trap_state.h"]
int
Thread::send_exception_arch(Trap_state *ts)
{
  // Do not send exception IPC but return 'not for us' if thread is a normal
  // thread (not alien) and it's a debug trap,
  // debug traps for aliens are always reflected as exception IPCs
  if (!(state() & Thread_alien)
      && (ts->_trapno == 1 || ts->_trapno == 3))
    return 0; // we do not handle this

  if (ts->_trapno == 3)
    {
      if (state() & Thread_dis_alien)
	{
	  state_del(Thread_dis_alien);
	  return 0; // no exception
	}

      // set IP back on the int3 instruction
      ts->ip(ts->ip() - 1);
    }

  return 1; // make it an exception
}


//----------------------------------------------------------------------------
IMPLEMENTATION [ia32 || amd64]:

PROTECTED inline
void
Thread::vcpu_resume_user_arch()
{}

//----------------------------------------------------------------------------
IMPLEMENTATION [ux]:

PROTECTED inline
void
Thread::vcpu_resume_user_arch()
{
  switch_gdt_user_entries(this);
}

//----------------------------------------------------------------------------
IMPLEMENTATION [ux || amd64]:

IMPLEMENT inline NEEDS[Thread::exception_triggered]
void
Thread::user_ip(Mword ip)
{
  if (exception_triggered())
    _exc_cont.ip(ip);
  else
    {
      Entry_frame *r = regs();
      r->ip(ip);
    }
}

//----------------------------------------------------------------------------
IMPLEMENTATION [(ia32,amd64,ux) && !io]:

PRIVATE inline
int
Thread::handle_io_page_fault(Trap_state *)
{ return 0; }

PRIVATE inline
bool
Thread::get_ioport(Address /*eip*/, Trap_state * /*ts*/,
                   unsigned * /*port*/, unsigned * /*size*/)
{ return false; }


//---------------------------------------------------------------------------
IMPLEMENTATION[ia32 || amd64]:

#include "fpu.h"
#include "fpu_alloc.h"
#include "fpu_state.h"
#include "gdt.h"
#include "globalconfig.h"
#include "idt.h"
#include "simpleio.h"
#include "static_init.h"
#include "terminate.h"

int (*Thread::int3_handler)(Trap_state*);
DEFINE_PER_CPU Per_cpu<Thread::Dbg_stack> Thread::dbg_stack;

STATIC_INITIALIZER_P (int3_handler_init, KDB_INIT_PRIO);

static
void
int3_handler_init()
{
  Thread::set_int3_handler(Thread::handle_int3);
}

IMPLEMENT static inline NEEDS ["gdt.h"]
Mword
Thread::exception_cs()
{
  return Gdt::gdt_code_user | Gdt::Selector_user;
}

/**
 * The ia32 specific part of the thread constructor.
 */
PRIVATE inline NEEDS ["gdt.h"]
void
Thread::arch_init()
{
  // clear out user regs that can be returned from the thread_ex_regs
  // system call to prevent covert channel
  Entry_frame *r = regs();
  r->flags(EFLAGS_IOPL_K | EFLAGS_IF | 2);	// ei
  r->cs(Gdt::gdt_code_user | Gdt::Selector_user);
  r->ss(Gdt::gdt_data_user | Gdt::Selector_user);

  r->sp(0);
  // after cs initialisation as ip() requires proper cs
  r->ip(0);
}


/** A C interface for Context::handle_fpu_trap, callable from assembly code.
    @relates Context
 */
// The "FPU not available" trap entry point
extern "C"
int
thread_handle_fputrap()
{
  LOG_TRAP_N(7);

  return current_thread()->switchin_fpu();
}

PUBLIC static inline
void
Thread::set_int3_handler(int (*handler)(Trap_state *ts))
{
  int3_handler = handler;
}

/**
 * Default handle for int3 extensions if JDB is disabled. If the JDB is
 * available, Jdb::handle_int3_threadctx is called instead.
 * @return 0 not handled, wait for user response
 *         1 successfully handled
 */
PUBLIC static
int
Thread::handle_int3(Trap_state *ts)
{
  Mem_space *s   = current()->mem_space();
  int from_user  = ts->cs() & 3;
  Address   ip   = ts->ip();
  Unsigned8 todo = s->peek((Unsigned8*)ip, from_user);
  Unsigned8 *str;
  int len;
  char c;

  switch (todo)
    {
    case 0xeb: // jmp == enter_kdebug()
      len = s->peek((Unsigned8*)(ip+1), from_user);
      str = (Unsigned8*)(ip + 2);

      putstr("KDB: ");
      if (len > 0)
	{
	  for (; len; len--)
            putchar(s->peek(str++, from_user));
	}
      putchar('\n');
      return 0; // => Jdb

    case 0x90: // nop == l4kd_display()
      if (          s->peek((Unsigned8*)(ip+1), from_user)  != 0xeb /*jmp*/
	  || (len = s->peek((Unsigned8*)(ip+2), from_user)) <= 0)
	return 0; // => Jdb

      str = (Unsigned8*)(ip + 3);
      for (; len; len--)
	putchar(s->peek(str++, from_user));
      break;

    case 0x3c: // cmpb
      todo = s->peek((Unsigned8*)(ip+1), from_user);
      switch (todo)
	{
	case  0: // l4kd_outchar
	  putchar(ts->value() & 0xff);
	  break;
        case  1: // l4kd_outnstring
	  str = (Unsigned8*)ts->value();
          len = ts->value4();
	  for(; len > 0; len--)
	    putchar(s->peek(str++, from_user));
	  break;
	case  2: // l4kd_outstr
	  str = (Unsigned8*)ts->value();
	  for (; (c=s->peek(str++, from_user)); )
            putchar(c);
	  break;
	case  5: // l4kd_outhex32
	  printf("%08lx", ts->value() & 0xffffffff);
	  break;
	case  6: // l4kd_outhex20
	  printf("%05lx", ts->value() & 0xfffff);
	  break;
	case  7: // l4kd_outhex16
	  printf("%04lx", ts->value() & 0xffff);
	  break;
	case  8: // l4kd_outhex12
	  printf("%03lx", ts->value() & 0xfff);
	  break;
	case  9: // l4kd_outhex8
	  printf("%02lx", ts->value() & 0xff);
	  break;
	case 11: // l4kd_outdec
	  printf("%ld", ts->value());
	  break;
	case 31: // Watchdog
	  switch (ts->value2())
	    {
	    case 1:
	      // enable watchdog
	      Watchdog::user_enable();
	      break;
	    case 2:
	      // disable watchdog
	      Watchdog::user_disable();
	      break;
	    case 3:
	      // user takes over the control of watchdog and is from now on
	      // responsible for calling "I'm still alive" events (function 5)
	      Watchdog::user_takeover_control();
	      break;
	    case 4:
	      // user returns control of watchdog to kernel
	      Watchdog::user_giveback_control();
              break;
	    case 5:
	      // I'm still alive
	      Watchdog::touch();
	      break;
	    }
	  break;

	default: // ko
	  if (todo < ' ')
	    return 0; // => Jdb

	  putchar(todo);
	  break;
	}
      break;

    default:
      return 0; // => Jdb
    }

  return 1;
}


PRIVATE inline
void
Thread::check_f00f_bug(Trap_state *ts)
{
  // If we page fault on the IDT, it must be because of the F00F bug.
  // Figure out exception slot and raise the corresponding exception.
  // XXX: Should we also modify the error code?
  if (ts->_trapno == 14		// page fault?
      && ts->_cr2 >= Idt::idt()
      && ts->_cr2 <  Idt::idt() + Idt::_idt_max * 8)
    ts->_trapno = (ts->_cr2 - Idt::idt()) / 8;
}


PRIVATE inline
unsigned
Thread::check_io_bitmap_delimiter_fault(Trap_state *ts)
{
  // check for page fault at the byte following the IO bitmap
  if (ts->_trapno == 14           // page fault?
      && (ts->_err & 4) == 0         // in supervisor mode?
      && ts->ip() < Kmem::mem_user_max   // delimiter byte accessed?
      && (ts->_cr2 == Mem_layout::Io_bitmap + Mem_layout::Io_port_max / 8))
    {
      // page fault in the first byte following the IO bitmap
      // map in the cpu_page read_only at the place
      Mem_space::Status result =
	mem_space()->v_insert(
	    Mem_space::Phys_addr(mem_space()->virt_to_phys_s0((void*)Kmem::io_bitmap_delimiter_page())),
	    Mem_space::Addr::create(Mem_layout::Io_bitmap + Mem_layout::Io_port_max / 8),
	    Mem_space::Size::create(Config::PAGE_SIZE),
	    Pt_entry::global());

      switch (result)
	{
	case Mem_space::Insert_ok:
	  return 1; // success
	case Mem_space::Insert_err_nomem:
	  // kernel failure, translate this into a general protection
	  // violation and hope that somebody handles it
	  ts->_trapno = 13;
	  ts->_err    =  0;
	  return 0; // fail
	default:
	  // no other error code possible
	  assert (false);
	}
    }

  return 1;
}

PRIVATE inline
bool
Thread::handle_sysenter_trap(Trap_state *ts, Address eip, bool from_user)
{
  if (EXPECT_FALSE
      ((ts->_trapno == 6 || ts->_trapno == 13)
       && (ts->_err & 0xffff) == 0
       && (eip < Kmem::mem_user_max - 2)
       && (mem_space()->peek((Unsigned16*) eip, from_user)) == 0x340f))
    {
      // somebody tried to do sysenter on a machine without support for it
      WARN("tcb=%p killed:\n"
	   "\033[1;31mSYSENTER not supported on this machine\033[0m",
	   this);

      if (Cpu::have_sysenter())
	// GP exception if sysenter is not correctly set up..
        WARN("MSR_SYSENTER_CS: %llx", Cpu::rdmsr(MSR_SYSENTER_CS));
      else
	// We get UD exception on processors without SYSENTER/SYSEXIT.
        WARN("SYSENTER/EXIT not available.");

      return false;
    }

  return true;
}

PRIVATE inline
int
Thread::handle_not_nested_trap(Trap_state *ts)
{
  // no kernel debugger present
  printf(" %p IP=" L4_PTR_FMT " Trap=%02lx [Ret/Esc]\n",
	 this, ts->ip(), ts->_trapno);

  int r;
  // cannot use normal getchar because it may block with hlt and irq's
  // are off here
  while ((r = Kconsole::console()->getchar(false)) == -1)
    Proc::pause();

  if (r == '\033')
    terminate (1);

  return 0;
}

PROTECTED inline
int
Thread::sys_control_arch(Utcb *)
{
  return 0;
}

//---------------------------------------------------------------------------
IMPLEMENTATION [(ia32 | amd64) & (debug | kdb) & !mp]:

PRIVATE static inline unsigned Thread::dbg_find_cpu() { return 0; }

//---------------------------------------------------------------------------
IMPLEMENTATION [(ia32 | amd64) & (debug | kdb) & mp]:

#include "apic.h"

PRIVATE static inline NEEDS["apic.h"]
unsigned
Thread::dbg_find_cpu()
{
  unsigned long phys_cpu = Apic::get_id();
  unsigned log_cpu = Apic::find_cpu(phys_cpu);
  if (log_cpu == ~0U)
    {
      printf("Trap on unknown CPU phys_id=%lx\n", phys_cpu);
      log_cpu = 0;
    }
  return log_cpu;
}


//---------------------------------------------------------------------------
IMPLEMENTATION [(ia32 |amd64) & !(debug | kdb)]:

/** There is no nested trap handler if both jdb and kdb are disabled.
 * Important: We don't need the nested_handler_stack here.
 */
PRIVATE static inline
int
Thread::call_nested_trap_handler(Trap_state *)
{ return -1; }
