Merge branch 'master' of git://git.ucc.asn.au/tpg/acess2
authorJohn Hodge <[email protected]>
Wed, 8 Feb 2012 06:59:58 +0000 (14:59 +0800)
committerJohn Hodge <[email protected]>
Wed, 8 Feb 2012 06:59:58 +0000 (14:59 +0800)
1  2 
KernelLand/Kernel/arch/armv7/mm_virt.c
KernelLand/Kernel/arch/armv7/proc.S
KernelLand/Kernel/arch/armv7/start.S
KernelLand/Kernel/drv/vterm.c
KernelLand/Modules/Display/Tegra2Vid/main.c
KernelLand/Modules/Display/Tegra2Vid/tegra2.h
KernelLand/Modules/Input/PS2KbMouse/pl050.c

index 0000000,460b334..90655b6
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,1078 +1,1078 @@@
 -#define TLBIMVA(addr) __asm__ __volatile__ ("mcr p15, 0, %0, c8, c7, 1;dsb;isb" : : "r" ((addr)&~0xFFF):"memory")
+ /*
+  * Acess2
+  * 
+  * ARM7 Virtual Memory Manager
+  * - arch/arm7/mm_virt.c
+  */
+ #define DEBUG 0
+ #include <acess.h>
+ #include <mm_virt.h>
+ #include <hal_proc.h>
+ #define TRACE_MAPS    0
+ #define AP_KRW_ONLY   1       // Kernel page
+ #define AP_KRO_ONLY   5       // Kernel RO page
+ #define AP_RW_BOTH    3       // Standard RW
+ #define AP_RO_BOTH    7       // COW Page
+ #define AP_RO_USER    2       // User RO Page
+ #define PADDR_MASK_LVL1       0xFFFFFC00
+ // === IMPORTS ===
+ extern Uint32 kernel_table0[];
+ // === TYPES ===
+ typedef struct
+ {
+       tPAddr  PhysAddr;
+       Uint8   Size;
+       Uint8   Domain;
+       BOOL    bExecutable;
+       BOOL    bGlobal;
+       BOOL    bShared;
+        int    AP;
+ } tMM_PageInfo;
+ //#define FRACTAL(table1, addr)       ((table1)[ (0xFF8/4*1024) + ((addr)>>20)])
+ #define FRACTAL(table1, addr) ((table1)[ (0xFF8/4*1024) + ((addr)>>22)])
+ #define USRFRACTAL(addr)      (*((Uint32*)(0x7FDFF000) + ((addr)>>22)))
+ #define TLBIALL()     __asm__ __volatile__ ("mcr p15, 0, %0, c8, c7, 0" : : "r" (0))
 -                              #warning "HACK: TLBIALL"
 -                              TLBIALL();
++#define TLBIMVA(addr) __asm__ __volatile__ ("mcr p15, 0, %0, c8, c7, 1;dsb;isb" : : "r" (((addr)&~0xFFF)|1):"memory")
+ #define DCCMVAC(addr) __asm__ __volatile__ ("mcr p15, 0, %0, c7, c10, 1" : : "r" ((addr)&~0xFFF))
+ // === PROTOTYPES ===
+ void  MM_int_GetTables(tVAddr VAddr, Uint32 **Table0, Uint32 **Table1);
+  int  MM_int_AllocateCoarse(tVAddr VAddr, int Domain);
+  int  MM_int_SetPageInfo(tVAddr VAddr, tMM_PageInfo *pi);
+  int  MM_int_GetPageInfo(tVAddr VAddr, tMM_PageInfo *pi);
+ tVAddr        MM_NewUserStack(void);
+ tPAddr        MM_AllocateZero(tVAddr VAddr);
+ tPAddr        MM_AllocateRootTable(void);
+ void  MM_int_CloneTable(Uint32 *DestEnt, int Table);
+ tPAddr        MM_Clone(void);
+ tVAddr        MM_NewKStack(int bGlobal);
+ void  MM_int_DumpTableEnt(tVAddr Start, size_t Len, tMM_PageInfo *Info);
+ //void        MM_DumpTables(tVAddr Start, tVAddr End);
+ void  MM_PageFault(Uint32 PC, Uint32 Addr, Uint32 DFSR, int bPrefetch);
+ // === GLOBALS ===
+ tPAddr        giMM_ZeroPage;
+ // === CODE ===
+ int MM_InitialiseVirtual(void)
+ {
+       return 0;
+ }
+ void MM_int_GetTables(tVAddr VAddr, Uint32 **Table0, Uint32 **Table1)
+ {
+       if(VAddr & 0x80000000) {
+               *Table0 = (void*)&kernel_table0;        // Level 0
+               *Table1 = (void*)MM_TABLE1KERN; // Level 1
+       }
+       else {
+               *Table0 = (void*)MM_TABLE0USER;
+               *Table1 = (void*)MM_TABLE1USER;
+       }
+ }
+ int MM_int_AllocateCoarse(tVAddr VAddr, int Domain)
+ {
+       Uint32  *table0, *table1;
+       Uint32  *desc;
+       tPAddr  paddr;
+       
+       ENTER("xVAddr iDomain", VAddr, Domain);
+       MM_int_GetTables(VAddr, &table0, &table1);
+       VAddr &= ~(0x400000-1); // 4MiB per "block", 1 Page
+       desc = &table0[ VAddr>>20];
+       LOG("desc = %p", desc);
+       
+       // table0: 4 bytes = 1 MiB
+       LOG("desc[0] = %x", desc[0]);
+       LOG("desc[1] = %x", desc[1]);
+       LOG("desc[2] = %x", desc[2]);
+       LOG("desc[3] = %x", desc[3]);
+       if( (desc[0] & 3) != 0 || (desc[1] & 3) != 0
+        || (desc[2] & 3) != 0 || (desc[3] & 3) != 0 )
+       {
+               // Error?
+               LEAVE('i', 1);
+               return 1;
+       }
+       paddr = MM_AllocPhys();
+       if( !paddr )
+       {
+               // Error
+               LEAVE('i', 2);
+               return 2;
+       }
+       
+       *desc = paddr | (Domain << 5) | 1;
+       desc[1] = desc[0] + 0x400;
+       desc[2] = desc[0] + 0x800;
+       desc[3] = desc[0] + 0xC00;
+       if( VAddr < 0x80000000 ) {
+               USRFRACTAL(VAddr) = paddr | 0x13;
+       }
+       else {
+               FRACTAL(table1, VAddr) = paddr | 0x13;
+       }
+       // TLBIALL 
+       TLBIALL();
+       
+       memset( (void*)&table1[ (VAddr >> 12) & ~(1024-1) ], 0, 0x1000 );
+       LEAVE('i', 0);
+       return 0;
+ }     
+ int MM_int_SetPageInfo(tVAddr VAddr, tMM_PageInfo *pi)
+ {
+       Uint32  *table0, *table1;
+       Uint32  *desc;
+       ENTER("pVAddr ppi", VAddr, pi);
+       MM_int_GetTables(VAddr, &table0, &table1);
+       desc = &table0[ VAddr >> 20 ];
+       LOG("desc = %p", desc);
+       switch(pi->Size)
+       {
+       case 12:        // Small Page
+       case 16:        // Large Page
+               LOG("Page");
+               if( (*desc & 3) == 0 ) {
+                       MM_int_AllocateCoarse( VAddr, pi->Domain );
+               }
+               desc = &table1[ VAddr >> 12 ];
+               LOG("desc (2) = %p", desc);
+               if( pi->Size == 12 )
+               {
+                       // Small page
+                       // - Error if overwriting a large page
+                       if( (*desc & 3) == 1 )  LEAVE_RET('i', 1);
+                       if( pi->PhysAddr == 0 ) {
+                               *desc = 0;
+                               TLBIMVA( VAddr );
+                               DCCMVAC( (tVAddr) desc );
 -                      #warning "HACK: TLBIALL"
 -                      TLBIALL();
++//                            #warning "HACK: TLBIALL"
++//                            TLBIALL();                              
+                               LEAVE('i', 0);
+                               return 0;
+                       }
+                       *desc = (pi->PhysAddr & 0xFFFFF000) | 2;
+                       if(!pi->bExecutable)    *desc |= 1;     // XN
+                       if(!pi->bGlobal)        *desc |= 1 << 11;       // nG
+                       if( pi->bShared)        *desc |= 1 << 10;       // S
+                       *desc |= (pi->AP & 3) << 4;     // AP
+                       *desc |= ((pi->AP >> 2) & 1) << 9;      // APX
+                       TLBIMVA( VAddr );       
++//                    #warning "HACK: TLBIALL"
++//                    TLBIALL();
+                       DCCMVAC( (tVAddr) desc );
+                       LEAVE('i', 0);
+                       return 0;
+               }
+               else
+               {
+                       // Large page
+                       Log_Warning("MMVirt", "TODO: Implement large pages in MM_int_SetPageInfo");
+               }
+               break;
+       case 20:        // Section or unmapped
+               Log_Warning("MMVirt", "TODO: Implement sections in MM_int_SetPageInfo");
+               break;
+       case 24:        // Supersection
+               // Error if not aligned
+               if( VAddr & 0xFFFFFF ) {
+                       LEAVE('i', 1);
+                       return 1;
+               }
+               if( (*desc & 3) == 0 || ((*desc & 3) == 2 && (*desc & (1 << 18)))  )
+               {
+                       if( pi->PhysAddr == 0 ) {
+                               *desc = 0;
+                       }
+                       else {
+                               // Apply
+                               *desc = pi->PhysAddr & 0xFF000000;
+ //                            *desc |= ((pi->PhysAddr >> 32) & 0xF) << 20;
+ //                            *desc |= ((pi->PhysAddr >> 36) & 0x7) << 5;
+                               *desc |= 2 | (1 << 18);
+                       }
+                       // TODO: Apply to all entries
+                       Log_Warning("MMVirt", "TODO: Apply changes to all entries of supersections");
+                       LEAVE('i', 0);
+                       return 0;
+               }
+               // TODO: What here?
+               Log_Warning("MMVirt", "TODO: 24-bit not on supersection?");
+               LEAVE('i', 1);
+               return 1;
+       }
+       LEAVE('i', 1);
+       return 1;
+ }
+ int MM_int_GetPageInfo(tVAddr VAddr, tMM_PageInfo *pi)
+ {
+       Uint32  *table0, *table1;
+       Uint32  desc;
+ //    LogF("MM_int_GetPageInfo: VAddr=%p, pi=%p\n", VAddr, pi);
+       
+       MM_int_GetTables(VAddr, &table0, &table1);
+       desc = table0[ VAddr >> 20 ];
+ //    if( VAddr > 0x90000000)
+ //            LOG("table0 desc(%p) = %x", &table0[ VAddr >> 20 ], desc);
+       
+       pi->bExecutable = 1;
+       pi->bGlobal = 0;
+       pi->bShared = 0;
+       pi->AP = 0;
+       switch( (desc & 3) )
+       {
+       // 0: Unmapped
+       case 0:
+               pi->PhysAddr = 0;
+               pi->Size = 20;
+               pi->Domain = 0;
+               return 1;
+       // 1: Coarse page table
+       case 1:
+               // Domain from top level table
+               pi->Domain = (desc >> 5) & 7;
+               // Get next level
+               desc = table1[ VAddr >> 12 ];
+ //            LOG("table1 desc(%p) = %x", &table1[ VAddr >> 12 ], desc);
+               switch( desc & 3 )
+               {
+               // 0: Unmapped
+               case 0: 
+                       pi->Size = 12;
+                       return 1;
+               // 1: Large Page (64KiB)
+               case 1:
+                       pi->Size = 16;
+                       pi->PhysAddr = desc & 0xFFFF0000;
+                       pi->AP = ((desc >> 4) & 3) | (((desc >> 9) & 1) << 2);
+                       pi->bExecutable = !(desc & 0x8000);
+                       pi->bShared = (desc >> 10) & 1;
+                       return 0;
+               // 2/3: Small page
+               case 2:
+               case 3:
+                       pi->Size = 12;
+                       pi->PhysAddr = desc & 0xFFFFF000;
+                       pi->bExecutable = !(desc & 1);
+                       pi->bGlobal = !(desc >> 11);
+                       pi->bShared = (desc >> 10) & 1;
+                       pi->AP = ((desc >> 4) & 3) | (((desc >> 9) & 1) << 2);
+                       return 0;
+               }
+               return 1;
+       
+       // 2: Section (or Supersection)
+       case 2:
+               if( desc & (1 << 18) ) {
+                       // Supersection
+                       pi->PhysAddr = desc & 0xFF000000;
+                       pi->PhysAddr |= (Uint64)((desc >> 20) & 0xF) << 32;
+                       pi->PhysAddr |= (Uint64)((desc >> 5) & 0x7) << 36;
+                       pi->Size = 24;
+                       pi->Domain = 0; // Supersections default to zero
+                       pi->AP = ((desc >> 10) & 3) | (((desc >> 15) & 1) << 2);
+                       return 0;
+               }
+               
+               // Section
+               pi->PhysAddr = desc & 0xFFF80000;
+               pi->Size = 20;
+               pi->Domain = (desc >> 5) & 7;
+               pi->AP = ((desc >> 10) & 3) | (((desc >> 15) & 1) << 2);
+               return 0;
+       // 3: Reserved (invalid)
+       case 3:
+               pi->PhysAddr = 0;
+               pi->Size = 20;
+               pi->Domain = 0;
+               return 2;
+       }
+       return 2;
+ }
+ // --- Exports ---
+ tPAddr MM_GetPhysAddr(tVAddr VAddr)
+ {
+       tMM_PageInfo    pi;
+       if( MM_int_GetPageInfo(VAddr, &pi) )
+               return 0;
+       return pi.PhysAddr | (VAddr & ((1 << pi.Size)-1));
+ }
+ Uint MM_GetFlags(tVAddr VAddr)
+ {
+       tMM_PageInfo    pi;
+        int    ret;
+       if( MM_int_GetPageInfo(VAddr, &pi) )
+               return 0;
+       ret = 0;
+       
+       switch(pi.AP)
+       {
+       case 0:
+               break;
+       case AP_KRW_ONLY:
+               ret |= MM_PFLAG_KERNEL;
+               break;
+       case AP_KRO_ONLY:
+               ret |= MM_PFLAG_KERNEL|MM_PFLAG_RO;
+               break;
+       case AP_RW_BOTH:
+               break;
+       case AP_RO_BOTH:
+               ret |= MM_PFLAG_COW;
+               break;
+       case AP_RO_USER:
+               ret |= MM_PFLAG_RO;
+               break;
+       }
+       if( pi.bExecutable )    ret |= MM_PFLAG_EXEC;
+       return ret;
+ }
+ void MM_SetFlags(tVAddr VAddr, Uint Flags, Uint Mask)
+ {
+       tMM_PageInfo    pi;
+       Uint    curFlags;
+       
+       if( MM_int_GetPageInfo(VAddr, &pi) )
+               return ;
+       
+       curFlags = MM_GetFlags(VAddr);
+       if( (curFlags & Mask) == Flags )
+               return ;
+       curFlags &= ~Mask;
+       curFlags |= Flags;
+       if( curFlags & MM_PFLAG_COW )
+               pi.AP = AP_RO_BOTH;
+       else
+       {
+               switch(curFlags & (MM_PFLAG_KERNEL|MM_PFLAG_RO) )
+               {
+               case 0:
+                       pi.AP = AP_RW_BOTH;     break;
+               case MM_PFLAG_KERNEL:
+                       pi.AP = AP_KRW_ONLY;    break;
+               case MM_PFLAG_RO:
+                       pi.AP = AP_RO_USER;     break;
+               case MM_PFLAG_KERNEL|MM_PFLAG_RO:
+                       pi.AP = AP_KRO_ONLY;    break;
+               }
+       }
+       
+       pi.bExecutable = !!(curFlags & MM_PFLAG_EXEC);
+       MM_int_SetPageInfo(VAddr, &pi);
+ }
+ int MM_IsValidBuffer(tVAddr Addr, size_t Size)
+ {
+       tMM_PageInfo    pi;
+        int    bUser = 0;
+       
+       Size += Addr & (PAGE_SIZE-1);
+       Addr &= ~(PAGE_SIZE-1);
+       if( MM_int_GetPageInfo(Addr, &pi) )     return 0;
+       Addr += PAGE_SIZE;
+       if(pi.AP != AP_KRW_ONLY && pi.AP != AP_KRO_ONLY)
+               bUser = 1;
+       while( Size >= PAGE_SIZE )
+       {
+               if( MM_int_GetPageInfo(Addr, &pi) )
+                       return 0;
+               if(bUser && (pi.AP == AP_KRW_ONLY || pi.AP == AP_KRO_ONLY))
+                       return 0;
+               Addr += PAGE_SIZE;
+               Size -= PAGE_SIZE;
+       }
+       
+       return 1;
+ }
+ int MM_Map(tVAddr VAddr, tPAddr PAddr)
+ {
+       tMM_PageInfo    pi = {0};
+       #if TRACE_MAPS
+       Log("MM_Map %P=>%p", PAddr, VAddr);
+       #endif
+       
+       pi.PhysAddr = PAddr;
+       pi.Size = 12;
+       if(VAddr < USER_STACK_TOP)
+               pi.AP = AP_RW_BOTH;
+       else
+               pi.AP = AP_KRW_ONLY;    // Kernel Read/Write
+       pi.bExecutable = 1;
+       if( MM_int_SetPageInfo(VAddr, &pi) ) {
+ //            MM_DerefPhys(pi.PhysAddr);
+               return 0;
+       }
+       return pi.PhysAddr;
+ }
+ tPAddr MM_Allocate(tVAddr VAddr)
+ {
+       tMM_PageInfo    pi = {0};
+       
+       ENTER("pVAddr", VAddr);
+       pi.PhysAddr = MM_AllocPhys();
+       if( pi.PhysAddr == 0 )  LEAVE_RET('i', 0);
+       pi.Size = 12;
+       if(VAddr < USER_STACK_TOP)
+               pi.AP = AP_RW_BOTH;
+       else
+               pi.AP = AP_KRW_ONLY;
+       pi.bExecutable = 0;
+       if( MM_int_SetPageInfo(VAddr, &pi) ) {
+               MM_DerefPhys(pi.PhysAddr);
+               LEAVE('i', 0);
+               return 0;
+       }
+       LEAVE('x', pi.PhysAddr);
+       return pi.PhysAddr;
+ }
+ tPAddr MM_AllocateZero(tVAddr VAddr)
+ {
+       if( !giMM_ZeroPage ) {
+               giMM_ZeroPage = MM_Allocate(VAddr);
+               MM_RefPhys(giMM_ZeroPage);
+               memset((void*)VAddr, 0, PAGE_SIZE);
+       }
+       else {
+               MM_RefPhys(giMM_ZeroPage);
+               MM_Map(VAddr, giMM_ZeroPage);
+       }
+       MM_SetFlags(VAddr, MM_PFLAG_COW, MM_PFLAG_COW);
+       return giMM_ZeroPage;
+ }
+ void MM_Deallocate(tVAddr VAddr)
+ {
+       tMM_PageInfo    pi;
+       
+       if( MM_int_GetPageInfo(VAddr, &pi) )    return ;
+       if( pi.PhysAddr == 0 )  return;
+       MM_DerefPhys(pi.PhysAddr);
+       
+       pi.PhysAddr = 0;
+       pi.AP = 0;
+       pi.bExecutable = 0;
+       MM_int_SetPageInfo(VAddr, &pi);
+ }
+ tPAddr MM_AllocateRootTable(void)
+ {
+       tPAddr  ret;
+       
+       ret = MM_AllocPhysRange(2, -1);
+       if( ret & 0x1000 ) {
+               MM_DerefPhys(ret);
+               MM_DerefPhys(ret+0x1000);
+               ret = MM_AllocPhysRange(3, -1);
+               if( ret & 0x1000 ) {
+                       MM_DerefPhys(ret);
+                       ret += 0x1000;
+ //                    Log("MM_AllocateRootTable: Second try not aligned, %P", ret);
+               }
+               else {
+                       MM_DerefPhys(ret + 0x2000);
+ //                    Log("MM_AllocateRootTable: Second try aligned, %P", ret);
+               }
+       }
+ //    else
+ //            Log("MM_AllocateRootTable: Got it in one, %P", ret);
+       return ret;
+ }
+ void MM_int_CloneTable(Uint32 *DestEnt, int Table)
+ {
+       tPAddr  table;
+       Uint32  *tmp_map;
+       Uint32  *cur = (void*)MM_TABLE1USER;
+ //    Uint32  *cur = &FRACTAL(MM_TABLE1USER,0);
+        int    i;
+       
+       table = MM_AllocPhys();
+       if(!table)      return ;
+       cur += 256*Table;
+       
+       tmp_map = (void*)MM_MapTemp(table);
+       
+       for( i = 0; i < 1024; i ++ )
+       {
+ //            Log_Debug("MMVirt", "cur[%i] (%p) = %x", Table*256+i, &cur[Table*256+i], cur[Table*256+i]);
+               switch(cur[i] & 3)
+               {
+               case 0: tmp_map[i] = 0; break;
+               case 1:
+                       tmp_map[i] = 0;
+                       Log_Error("MMVirt", "TODO: Support large pages in MM_int_CloneTable (%p)", (Table*256+i)*0x1000);
+                       // Large page?
+                       break;
+               case 2:
+               case 3:
+                       // Small page
+                       // - If full RW
+ //                    Debug("%p cur[%i] & 0x230 = 0x%x", Table*256*0x1000, i, cur[i] & 0x230);
+                       if( (cur[i] & 0x230) == 0x010 )
+                       {
+                               void    *dst, *src;
+                               tPAddr  newpage;
+                               newpage = MM_AllocPhys();
+                               src = (void*)( (Table*256+i)*0x1000 );
+                               dst = (void*)MM_MapTemp(newpage);
+ //                            Debug("Taking a copy of kernel page %p (%P)", src, cur[i] & ~0xFFF);
+                               memcpy(dst, src, PAGE_SIZE);
+                               MM_FreeTemp( (tVAddr)dst );
+                               tmp_map[i] = newpage | (cur[i] & 0xFFF);
+                       }
+                       else
+                       {
+                               if( (cur[i] & 0x230) == 0x030 )
+                                       cur[i] |= 0x200;        // Set to full RO (Full RO=COW, User RO = RO)
+                               tmp_map[i] = cur[i];
+                               MM_RefPhys( tmp_map[i] & ~0xFFF );
+                       }
+                       break;
+               }
+       }
+       MM_FreeTemp( (tVAddr) tmp_map );
+       DestEnt[0] = table + 0*0x400 + 1;
+       DestEnt[1] = table + 1*0x400 + 1;
+       DestEnt[2] = table + 2*0x400 + 1;
+       DestEnt[3] = table + 3*0x400 + 1;
+ }
+ tPAddr MM_Clone(void)
+ {
+       tPAddr  ret;
+       Uint32  *new_lvl1_1, *new_lvl1_2, *cur;
+       Uint32  *tmp_map;
+        int    i;
+ //    MM_DumpTables(0, KERNEL_BASE);
+       
+       ret = MM_AllocateRootTable();
+       cur = (void*)MM_TABLE0USER;
+       new_lvl1_1 = (void*)MM_MapTemp(ret);
+       new_lvl1_2 = (void*)MM_MapTemp(ret+0x1000);
+       tmp_map = new_lvl1_1;
+       for( i = 0; i < 0x800-4; i ++ )
+       {
+               // HACK! Ignore the original identity mapping
+               if( i == 0 && Threads_GetTID() == 0 ) {
+                       tmp_map[0] = 0;
+                       continue;
+               }
+               if( i == 0x400 )
+                       tmp_map = &new_lvl1_2[-0x400];
+               switch( cur[i] & 3 )
+               {
+               case 0: tmp_map[i] = 0; break;
+               case 1:
+                       MM_int_CloneTable(&tmp_map[i], i);
+                       i += 3; // Tables are alocated in blocks of 4
+                       break;
+               case 2:
+               case 3:
+                       Log_Error("MMVirt", "TODO: Support Sections/Supersections in MM_Clone (i=%i)", i);
+                       tmp_map[i] = 0;
+                       break;
+               }
+       }
+       // Allocate Fractal table
+       {
+                int    j, num;
+               tPAddr  tmp = MM_AllocPhys();
+               Uint32  *table = (void*)MM_MapTemp(tmp);
+               Uint32  sp;
+               register Uint32 __SP asm("sp");
+               // Map table to last 4MiB of user space
+               new_lvl1_2[0x3FC] = tmp + 0*0x400 + 1;
+               new_lvl1_2[0x3FD] = tmp + 1*0x400 + 1;
+               new_lvl1_2[0x3FE] = tmp + 2*0x400 + 1;
+               new_lvl1_2[0x3FF] = tmp + 3*0x400 + 1;
+               
+               tmp_map = new_lvl1_1;
+               for( j = 0; j < 512; j ++ )
+               {
+                       if( j == 256 )
+                               tmp_map = &new_lvl1_2[-0x400];
+                       if( (tmp_map[j*4] & 3) == 1 )
+                       {
+                               table[j] = tmp_map[j*4] & PADDR_MASK_LVL1;// 0xFFFFFC00;
+                               table[j] |= 0x813;      // nG, Kernel Only, Small page, XN
+                       }
+                       else
+                               table[j] = 0;
+               }
+               // Fractal
+               table[j++] = (ret + 0x0000) | 0x813;
+               table[j++] = (ret + 0x1000) | 0x813;
+               // Nuke the rest
+               for(      ; j < 1024; j ++ )
+                       table[j] = 0;
+               
+               // Get kernel stack bottom
+               sp = __SP & ~(MM_KSTACK_SIZE-1);
+               j = (sp / 0x1000) % 1024;
+               num = MM_KSTACK_SIZE/0x1000;
+ //            Log("num = %i, sp = %p, j = %i", num, sp, j);
+               
+               // Copy stack pages
+               for(; num--; j ++, sp += 0x1000)
+               {
+                       tVAddr  page;
+                       void    *tmp_page;
+                       
+                       page = MM_AllocPhys();
+ //                    Log("page = %P", page);
+                       table[j] = page | 0x813;
+                       tmp_page = (void*)MM_MapTemp(page);
+                       memcpy(tmp_page, (void*)sp, 0x1000);
+                       MM_FreeTemp( (tVAddr) tmp_page );
+               }
+               MM_FreeTemp( (tVAddr)table );
+       }
+       MM_FreeTemp( (tVAddr)new_lvl1_1 );
+       MM_FreeTemp( (tVAddr)new_lvl1_2 );
+ //    Log("MM_Clone: ret = %P", ret);
+       return ret;
+ }
+ void MM_ClearUser(void)
+ {
+        int    i, j;
+       const int       user_table_count = USER_STACK_TOP / (256*0x1000);
+       Uint32  *cur = (void*)MM_TABLE0USER;
+       Uint32  *tab;
+       
+ //    MM_DumpTables(0, 0x80000000);
+ //    Log("user_table_count = %i (as opposed to %i)", user_table_count, 0x800-4);
+       for( i = 0; i < user_table_count; i ++ )
+       {
+               switch( cur[i] & 3 )
+               {
+               case 0: break;  // Already unmapped
+               case 1: // Sub pages
+                       tab = (void*)(MM_TABLE1USER + i*256*sizeof(Uint32));
+                       for( j = 0; j < 1024; j ++ )
+                       {
+                               switch( tab[j] & 3 )
+                               {
+                               case 0: break;  // Unmapped
+                               case 1:
+                                       Log_Error("MMVirt", "TODO: Support large pages in MM_ClearUser");
+                                       break;
+                               case 2:
+                               case 3:
+                                       MM_DerefPhys( tab[j] & ~(PAGE_SIZE-1) );
+                                       break;
+                               }
+                       }
+                       MM_DerefPhys( cur[i] & ~(PAGE_SIZE-1) );
+                       cur[i+0] = 0;
+                       cur[i+1] = 0;
+                       cur[i+2] = 0;
+                       i += 3;
+                       break;
+               case 2:
+               case 3:
+                       Log_Error("MMVirt", "TODO: Implement sections/supersections in MM_ClearUser");
+                       break;
+               }
+               cur[i] = 0;
+       }
+       
+       // Final block of 4 tables are KStack
+       i = 0x800 - 4;
+       
+       // Clear out unused stacks
+       {
+               register Uint32 __SP asm("sp");
+                int    cur_stack_base = ((__SP & ~(MM_KSTACK_SIZE-1)) / PAGE_SIZE) % 1024;
+               tab = (void*)(MM_TABLE1USER + i*256*sizeof(Uint32));
+               
+               // First 512 is the Table1 mapping + 2 for Table0 mapping
+               for( j = 512+2; j < 1024; j ++ )
+               {
+                       // Skip current stack
+                       if( j == cur_stack_base ) {
+                               j += (MM_KSTACK_SIZE / PAGE_SIZE) - 1;
+                               continue ;
+                       }
+                       if( !(tab[j] & 3) )     continue;
+                       ASSERT( (tab[j] & 3) == 2 );
+                       MM_DerefPhys( tab[j] & ~(PAGE_SIZE) );
+                       tab[j] = 0;
+               }
+       }
+       
+ //    MM_DumpTables(0, 0x80000000);
+ }
+ tVAddr MM_MapTemp(tPAddr PAddr)
+ {
+       tVAddr  ret;
+       tMM_PageInfo    pi;
+       for( ret = MM_TMPMAP_BASE; ret < MM_TMPMAP_END - PAGE_SIZE; ret += PAGE_SIZE )
+       {
+               if( MM_int_GetPageInfo(ret, &pi) == 0 )
+                       continue;
+ //            Log("MapTemp %P at %p by %p", PAddr, ret, __builtin_return_address(0));
+               MM_RefPhys(PAddr);      // Counter the MM_Deallocate in FreeTemp
+               MM_Map(ret, PAddr);
+               
+               return ret;
+       }
+       Log_Warning("MMVirt", "MM_MapTemp: All slots taken");
+       return 0;
+ }
+ void MM_FreeTemp(tVAddr VAddr)
+ {
+       if( VAddr < MM_TMPMAP_BASE || VAddr >= MM_TMPMAP_END ) {
+               Log_Warning("MMVirt", "MM_FreeTemp: Passed an addr not from MM_MapTemp (%p)", VAddr);
+               return ;
+       }
+       
+       MM_Deallocate(VAddr);
+ }
+ tVAddr MM_MapHWPages(tPAddr PAddr, Uint NPages)
+ {
+       tVAddr  ret;
+        int    i;
+       tMM_PageInfo    pi;
+       ENTER("xPAddr iNPages", PAddr, NPages);
+       // Scan for a location
+       for( ret = MM_HWMAP_BASE; ret < MM_HWMAP_END - NPages * PAGE_SIZE; ret += PAGE_SIZE )
+       {
+ //            LOG("checking %p", ret);
+               // Check if there is `NPages` free pages
+               for( i = 0; i < NPages; i ++ )
+               {
+                       if( MM_int_GetPageInfo(ret + i*PAGE_SIZE, &pi) == 0 )
+                               break;
+               }
+               // Nope, jump to after the used page found and try again
+ //            LOG("i = %i, ==? %i", i, NPages);
+               if( i != NPages ) {
+                       ret += i * PAGE_SIZE;
+                       continue ;
+               }
+       
+               // Map the pages        
+               for( i = 0; i < NPages; i ++ )
+                       MM_Map(ret+i*PAGE_SIZE, PAddr+i*PAGE_SIZE);
+               // and return
+               LEAVE('p', ret);
+               return ret;
+       }
+       Log_Warning("MMVirt", "MM_MapHWPages: No space for a %i page block", NPages);
+       LEAVE('p', 0);
+       return 0;
+ }
+ tVAddr MM_AllocDMA(int Pages, int MaxBits, tPAddr *PAddr)
+ {
+       tPAddr  phys;
+       tVAddr  ret;
+       phys = MM_AllocPhysRange(Pages, MaxBits);
+       if(!phys) {
+               Log_Warning("MMVirt", "No space left for a %i page block (MM_AllocDMA)", Pages);
+               return 0;
+       }
+       
+       ret = MM_MapHWPages(phys, Pages);
+       *PAddr = phys;
+       return ret;
+ }
+ void MM_UnmapHWPages(tVAddr Vaddr, Uint Number)
+ {
+       Log_Error("MMVirt", "TODO: Implement MM_UnmapHWPages");
+ }
+ tVAddr MM_NewKStack(int bShared)
+ {
+       tVAddr  min_addr, max_addr;
+       tVAddr  addr, ofs;
+       if( bShared ) {
+               min_addr = MM_GLOBALSTACKS;
+               max_addr = MM_GLOBALSTACKS_END;
+       }
+       else {
+               min_addr = MM_KSTACK_BASE;
+               max_addr = MM_KSTACK_END;
+       }
+       // Locate a free slot
+       for( addr = min_addr; addr < max_addr; addr += MM_KSTACK_SIZE )
+       {
+               tMM_PageInfo    pi;
+               if( MM_int_GetPageInfo(addr+MM_KSTACK_SIZE-PAGE_SIZE, &pi) )    break;
+       }
+       // Check for an error   
+       if(addr >= max_addr) {
+               return 0;
+       }
+       // 1 guard page
+       for( ofs = PAGE_SIZE; ofs < MM_KSTACK_SIZE; ofs += PAGE_SIZE )
+       {
+               if( MM_Allocate(addr + ofs) == 0 )
+               {
+                       while(ofs)
+                       {
+                               ofs -= PAGE_SIZE;
+                               MM_Deallocate(addr + ofs);
+                       }
+                       Log_Warning("MMVirt", "MM_NewKStack: Unable to allocate");
+                       return 0;
+               }
+       }
+       return addr + ofs;
+ }
+ tVAddr MM_NewUserStack(void)
+ {
+       tVAddr  addr, ofs;
+       addr = USER_STACK_TOP - USER_STACK_SIZE;
+       if( MM_GetPhysAddr(addr + PAGE_SIZE) ) {
+               Log_Error("MMVirt", "Unable to create initial user stack, addr %p taken",
+                       addr + PAGE_SIZE
+                       );
+               return 0;
+       }
+       // 1 guard page
+       for( ofs = PAGE_SIZE; ofs < USER_STACK_SIZE; ofs += PAGE_SIZE )
+       {
+               tPAddr  rv;
+               if(ofs >= USER_STACK_SIZE - USER_STACK_COMM)
+                       rv = MM_Allocate(addr + ofs);
+               else
+                       rv = MM_AllocateZero(addr + ofs);
+               if(rv == 0)
+               {
+                       while(ofs)
+                       {
+                               ofs -= PAGE_SIZE;
+                               MM_Deallocate(addr + ofs);
+                       }
+                       Log_Warning("MMVirt", "MM_NewUserStack: Unable to allocate");
+                       return 0;
+               }
+               MM_SetFlags(addr+ofs, 0, MM_PFLAG_KERNEL);
+       }
+ //    Log("Return %p", addr + ofs);
+ //    MM_DumpTables(0, 0x80000000);
+       return addr + ofs;
+ }
+ void MM_int_DumpTableEnt(tVAddr Start, size_t Len, tMM_PageInfo *Info)
+ {
+       if( giMM_ZeroPage && Info->PhysAddr == giMM_ZeroPage )
+       {
+               Debug("%p => %8s - 0x%7x %i %x %s",
+                       Start, "ZERO", Len,
+                       Info->Domain, Info->AP,
+                       Info->bGlobal ? "G" : "nG"
+                       );
+       }
+       else
+       {
+               Debug("%p => %8x - 0x%7x %i %x %s",
+                       Start, Info->PhysAddr-Len, Len,
+                       Info->Domain, Info->AP,
+                       Info->bGlobal ? "G" : "nG"
+                       );
+       }
+ }
+ void MM_DumpTables(tVAddr Start, tVAddr End)
+ {
+       tVAddr  range_start = 0, addr;
+       tMM_PageInfo    pi, pi_old;
+        int    i = 0, inRange=0;
+       
+       memset(&pi_old, 0, sizeof(pi_old));
+       Debug("Page Table Dump (%p to %p):", Start, End);
+       range_start = Start;
+       for( addr = Start; i == 0 || (addr && addr < End); i = 1 )
+       {
+                int    rv;
+ //            Log("addr = %p", addr);
+               rv = MM_int_GetPageInfo(addr, &pi);
+               if( rv
+                || pi.Size != pi_old.Size
+                || pi.Domain != pi_old.Domain
+                || pi.AP != pi_old.AP
+                || pi.bGlobal != pi_old.bGlobal
+                || pi_old.PhysAddr != pi.PhysAddr )
+               {
+                       if(inRange) {
+                               MM_int_DumpTableEnt(range_start, addr - range_start, &pi_old);
+                       }
+                       addr &= ~((1 << pi.Size)-1);
+                       range_start = addr;
+               }
+               
+               pi_old = pi;
+               // Handle the zero page
+               if( !giMM_ZeroPage || pi_old.Size != 12 || pi_old.PhysAddr != giMM_ZeroPage )
+                       pi_old.PhysAddr += 1 << pi_old.Size;
+               addr += 1 << pi_old.Size;
+               inRange = (rv == 0);
+       }
+       if(inRange)
+               MM_int_DumpTableEnt(range_start, addr - range_start, &pi);
+       Debug("Done");
+ }
+ // NOTE: Runs in abort context, not much difference, just a smaller stack
+ void MM_PageFault(Uint32 PC, Uint32 Addr, Uint32 DFSR, int bPrefetch)
+ {
+        int    rv;
+       tMM_PageInfo    pi;
+       
+       rv = MM_int_GetPageInfo(Addr, &pi);
+       
+       // Check for COW
+       if( rv == 0 &&  pi.AP == AP_RO_BOTH )
+       {
+               pi.AP = AP_RW_BOTH;
+               if( giMM_ZeroPage && pi.PhysAddr == giMM_ZeroPage )
+               {
+                       tPAddr  newpage;
+                       newpage = MM_AllocPhys();
+                       if( !newpage ) {
+                               Log_Error("MMVirt", "Unable to allocate new page for COW of ZERO");
+                               for(;;);
+                       }
+                       
+                       #if TRACE_COW
+                       Log_Notice("MMVirt", "COW %p caused by %p, ZERO duped to %P (RefCnt(%i)--)", Addr, PC,
+                               newpage, MM_GetRefCount(pi.PhysAddr));
+                       #endif
+                       MM_DerefPhys(pi.PhysAddr);
+                       pi.PhysAddr = newpage;
+                       pi.AP = AP_RW_BOTH;
+                       MM_int_SetPageInfo(Addr, &pi);
+                       
+                       memset( (void*)(Addr & ~(PAGE_SIZE-1)), 0, PAGE_SIZE );
+                       return ;
+               }
+               else if( MM_GetRefCount(pi.PhysAddr) > 1 )
+               {
+                       // Duplicate the page
+                       tPAddr  newpage;
+                       void    *dst, *src;
+                       
+                       newpage = MM_AllocPhys();
+                       if(!newpage) {
+                               Log_Error("MMVirt", "Unable to allocate new page for COW");
+                               for(;;);
+                       }
+                       dst = (void*)MM_MapTemp(newpage);
+                       src = (void*)(Addr & ~(PAGE_SIZE-1));
+                       memcpy( dst, src, PAGE_SIZE );
+                       MM_FreeTemp( (tVAddr)dst );
+                       
+                       #if TRACE_COW
+                       Log_Notice("MMVirt", "COW %p caused by %p, %P duped to %P (RefCnt(%i)--)", Addr, PC,
+                               pi.PhysAddr, newpage, MM_GetRefCount(pi.PhysAddr));
+                       #endif
+                       MM_DerefPhys(pi.PhysAddr);
+                       pi.PhysAddr = newpage;
+               }
+               #if TRACE_COW
+               else {
+                       Log_Notice("MMVirt", "COW %p caused by %p, took last reference to %P",
+                               Addr, PC, pi.PhysAddr);
+               }
+               #endif
+               // Unset COW
+               pi.AP = AP_RW_BOTH;
+               MM_int_SetPageInfo(Addr, &pi);
+               return ;
+       }
+       
+       Log_Error("MMVirt", "Code at %p accessed %p (DFSR = 0x%x)%s", PC, Addr, DFSR,
+               (bPrefetch ? " - Prefetch" : "")
+               );
+       if( Addr < 0x80000000 )
+               MM_DumpTables(0, 0x80000000);
+       else
+               MM_DumpTables(0x80000000, -1);
+       for(;;);
+ }
index 0000000,531de29..8711dee
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,104 +1,104 @@@
 -      mov r1, #0
 -      mcrne p15, 0, r1, c8, c7, 0     @ TLBIALL - Invalidate all
+ /*
+  * Acess2 ARM
+  * - By John Hodge (thePowersGang)
+  *
+  * arch/arm7/proc.S
+  * - Process management assembly
+  */
+ #include "include/assembly.h"
+ .globl KernelThreadHeader
+ @ SP+12: Argument 1
+ @ SP+8: Argument Count
+ @ SP+4: Function
+ @ SP+0: Thread Pointer
+ KernelThreadHeader:
+       ldr r0, [sp],#4
+       @ TODO: Do something with the thread pointer
+       
+       ldr r4, [sp],#4 @ Function
+       @ Get argument
+       ldr r0, [sp],#4
+       blx r4
+       
+       ldr r0, =0
+       bl Threads_Exit
+       b .
+ .globl SwitchTask
+ @ R0: New stack
+ @ R1: Pointer to where to save old stack
+ @ R2: New IP
+ @ R3: Pointer to save old IP
+ @ SP+0: New address space
+ SwitchTask:
+       push {r4-r12,lr}
+       @ Save IP       
+       ldr r4, =.return
+       str r4, [r3]
+       @ Save SP
+       str sp, [r1]
+       @ Only update TTBR0 if the task has an explicit address space
+       ldr r1, [sp,#4*10]
+       tst r1, r1
+       mcrne p15, 0, r1, c2, c0, 0     @ Set TTBR0 to r0
++#     mov r1, #1
++      mcrne p15, 0, r1, c8, c7, 0     @ TLBIALL - Invalid user space
+       @ Restore SP
+       mov sp, r0
+       bx r2
+ .return:
+       pop {r4-r12,pc}
+ .extern MM_Clone
+ .extern MM_DumpTables
+ .globl Proc_CloneInt
+ Proc_CloneInt:
+       @ R0: SP Destination
+       @ R1: Mem Destination
+       push {r4-r12,lr}
+       mov r4, r1      @ Save mem destination
+       str sp, [r0]    @ Save SP to SP dest
+       bl MM_Clone
+       str r0, [r4]    @ Save clone return to Mem Dest
+       ldr r0, =Proc_CloneInt_new
+       pop {r4-r12,pc}
+ Proc_CloneInt_new:
+       mov r0, #0
+       pop {r4-r12,pc}
+ @ R0: New user SP
+ @ Return: Old user SP
+ .globl Proc_int_SwapUserSP
+ Proc_int_SwapUserSP:
+       cps #31 @ Go to system mode
+       mov r1, sp
+       tst r0, r0      @ Only update if non-zero
+       movne sp, r0
+       mov r0, r1
+       cps #19
+       mov pc, lr
+ .section .usertext, "ax"
+ .globl Proc_int_DropToUser
+ @ R0: User IP
+ @ R1: User SP
+ Proc_int_DropToUser:
+       cps #16
+       mov sp, r1
+       mov pc, r0
+ .section .rodata
+ csProc_CloneInt_NewTaskMessage:
+       .asciz "New task PC=%p, R4=%p, sp=%p"
+ csProc_CloneInt_OldTaskMessage:
+       .asciz "Parent task PC=%p, R4=%p, SP=%p"
index 0000000,8d9f3e4..113c8a4
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,367 +1,366 @@@
 -      mvn r1, #1 << 2
 -      and r0, r0, r1
+ #include "include/assembly.h"
+ #include "include/options.h"
+ @
+ @ Exception defs taken from ARM DDI 0406B
+ @ 
+ .section .init
+ interrupt_vector_table:
+ ivt_reset:    b _start        @ 0x00 Reset
+ ivt_undef:    b Undef_Handler @ 0x04 #UD
+ ivt_svc:      b SVC_Handler   @ 0x08 SVC (used to be called SWI)
+ ivt_prefetch: b PrefetchAbort @ 0x0C Prefetch abort
+ ivt_data:     b DataAbort     @ 0x10 Data abort
+ ivt_unused:   b .             @ 0x14 Not Used
+ ivt_irq:      b IRQHandler    @ 0x18 IRQ
+ ivt_fiq:      b .             @ 0x1C FIQ (Fast interrupt)
+ .globl _start
+ _start:
+       ldr r2, =UART0_PADDR
+       mov r1, #'A'
+       str r1, [r2]    
+       ldr r0, =kernel_table0-KERNEL_BASE
+       mcr p15, 0, r0, c2, c0, 1       @ Set TTBR1 to r0
+       mcr p15, 0, r0, c2, c0, 0       @ Set TTBR0 to r0 too (for identity)
+       mov r1, #'c'
+       str r1, [r2]    
+       mov r0, #1
+       mcr p15, 0, r0, c2, c0, 2       @ Set TTCR to 1 (50/50 split)
+       mov r1, #'e'
+       str r1, [r2]    
+       
+       mov r0, #3
+       mcr p15, 0, r0, c3, c0, 0       @ Set Domain 0 to Manager
+       mov r1, #'s'
+       str r1, [r2]    
+       @ Enable VMSA
+       mrc p15, 0, r0, c1, c0, 0
+       orr r0, r0, #1
+       orr r0, r0, #1 << 23
 -      @ HACK! Disable caching
 -      mrc p15, 0, r1, c1, c0, 0
+       mcr p15, 0, r0, c1, c0, 0
++      @ HACK: Set ASID to non zero
++      mov r0, #1
++      MCR p15,0,r0,c13,c0,1
+       ldr r2, =0xF1000000
+       mov r1, #'s'
+       str r1, [r2]    
+       @ Enable access faults on domains 0 & 1
+       mov r0, #0x55   @ 01010101b
+       mcr p15, 0, r0, c3, c0, 0
+       mov r1, #'2'
+       str r1, [r2]    
+       @
+       @ Check for security extensions
+       @
+       mrc p15, 0, r0, c0, c1, 1
+       and r0, #0xF0
+       @ - Present
+       ldrne r0,=KERNEL_BASE
+       mcrne p15, 0, r0, c12, c0, 0    @ Set the VBAR (brings exceptions into high memory)
+       @ - Absent
+       mrceq p15, 0, r0, c1, c0, 0     @ Set SCTLR.V
+       orreq r0, #0x2000
+       mcreq p15, 0, r0, c1, c0, 0
+       mov r1, #'-'
+       str r1, [r2]    
+       @ Prepare for interrupts
+       cps #18 @ IRQ Mode
+       ldr sp, =irqstack+0x1000        @ Set up stack
+       cps #23 @ Abort Mode
+       ldr sp, =abortstack+0x1000
+       cps #19
+       mov r1, #'a'
+       str r1, [r2]    
+       mov r1, #'r'
+       str r1, [r2]    
+       mov r1, #'m'
+       str r1, [r2]    
+       mov r1, #13
+       str r1, [r2]    
+       mov r1, #10
+       str r1, [r2]    
+ .extern bss_start
+ .extern bss_size_div_4
+ .zero_bss:
+       ldr r0, =bss_start
+       ldr r1, =bss_end
+       mov r3, #0
+ .zero_bss_loop:
+       str r3, [r0],#4
+       cmp r0, r1
+       bls .zero_bss_loop
+ .goto_c:
+       ldr sp, =0x80000000-8   @ Set up stack (top of user range)
+       ldr r0, =kmain
+       mov pc, r0
+ 1:    b 1b    @ Infinite loop
+ .comm irqstack, 0x1000        @ ; 4KiB Stack
+ .comm abortstack, 0x1000      @ ; 4KiB Stack
+ .extern SyscallHandler
+ SVC_Handler:
+ @     sub lr, #4
+       srsdb sp!, #19  @ Save state to stack
+       cpsie ifa, #19  @ Ensure we're in supervisor with interrupts enabled (should already be there)
+       push {r0-r12}
+       ldr r4, [lr,#-4]
+       mvn r5, #0xFF000000
+       and r4, r5
+       tst r4, #0x1000 
+       bne .arm_specifics
+       push {r4}
+       mov r0, sp
+       ldr r4, =SyscallHandler
+       blx r4
+       
+ @     ldr r0, =csSyscallPrintRetAddr
+ @     ldr r1, [sp,#9*4+5*4]
+ @     ldr r4, =Log
+ @     blx r4
+       
+       pop {r2}        @ errno
+       pop {r0,r1}     @ Ret/RetHi
+       add sp, #2*4    @ Saved r2/r3
+       pop {r4-r12}
+       rfeia sp!       @ Pop state (actually RFEFD)
+ .arm_specifics:
+       and r4, #0xFF
+       mov r0, r4      @ Number
+       mov r1, sp      @ Arguments
+       
+       ldr r4, =ARMv7_int_HandleSyscalls
+       blx r4
+       add sp, #4*4
+       pop {r4-r12}
+       rfeia sp!
+ .globl gpIRQHandler
+ gpIRQHandler: .long   0
+ IRQ_saved_sp: .long   0
+ IRQ_saved_lr:         .long   0
+ .globl IRQHandler
+ IRQHandler:
+       sub lr, #4      @ Adjust LR to the correct value
+       srsdb sp!, #19  @ Switch to supervisor mode (DDI0406B D1.6.5) (actually SRSFD)
+       cps #19
+       PUSH_GPRS
+ @     ldr r0, =csIRQ_Tag
+ @     ldr r1, =csIRQ_Fmt
+ @     ldr r4, =Log_Debug
+ @     blx r4
+       
+       @ Call the registered handler
+       ldr r0, gpIRQHandler
+       blx r0
+       @ Restore CPU state
+       POP_GPRS
+       cpsie i
+       rfeia sp!       @ Pop state (actually RFEFD)
+       bx lr
+ .globl DataAbort
+ DataAbort:
+       sub lr, #8      @ Adjust LR to the correct value
+       srsdb sp!, #23  @ Switch to supervisor mode (DDI0406B D1.6.5) (actually SRSFD)
+ @     cpsid ifa, #19
+       PUSH_GPRS
+       mov r3, #0      @ not a prefetch abort
+       mrc p15, 0, r2, c5, c0, 0       @ Read DFSR (Data Fault Status Register) to R2
+       mrc p15, 0, r1, c6, c0, 0       @ Read DFAR (Data Fault Address Register) into R1
+       mov r0, lr      @ PC
+       ldr r4, =MM_PageFault
+       blx r4
+       POP_GPRS
+       rfeia sp!       @ Pop state (actually RFEFD)
+ .globl PrefetchAbort
+ PrefetchAbort:
+       sub lr, #4      @ Adjust LR to the correct value
+       srsdb sp!, #23  @ Switch to supervisor mode (DDI0406B D1.6.5) (actually SRSFD)
+ @     cpsid ifa, #19
+       PUSH_GPRS
+       ldr r0, =csAbort_Tag
+       ldr r1, =csPrefetchAbort_Fmt
+ #     mov r2, lr
+       mrc p15, 0, r2, c6, c0, 2       @ Read IFAR (Instruction Fault Address Register) into R3
+       mrc p15, 0, r3, c5, c0, 1       @ Read IFSR (Instruction Fault Status Register) into R3
+       ldr r5, =Log_Error
+       blx r5
+ .loop:
+       wfi
+       b .loop
+ .globl Undef_Handler
+ Undef_Handler:
+       wfi
+       b Undef_Handler
+ .section .rodata
+ csIRQ_Tag:
+ csAbort_Tag:
+       .asciz "ARMv7"
+ csIRQ_Fmt:
+       .asciz "IRQ"
+ csDataAbort_Fmt:
+       .asciz "Data Abort - %p accessed %p, DFSR=%x Unk:%x Unk:%x"
+ csPrefetchAbort_Fmt:
+       .asciz "Prefetch Abort at %p, IFSR=%x"
+ csSyscallPrintRetAddr:
+       .asciz "Syscall ret to %p"
+ .section .padata
+ .globl kernel_table0
+ kernel_table0:
+       .long 0x00000402        @ Identity map the first 1 MiB
+       .rept 0x7FC - 1
+               .long 0
+       .endr
+       .long user_table1_map + 0x000 - KERNEL_BASE + 1 @ 0x7FC00000
+       .long user_table1_map + 0x400 - KERNEL_BASE + 1 @ 0x7FD00000
+       .long user_table1_map + 0x800 - KERNEL_BASE + 1 @ KStacks
+       .long user_table1_map + 0xC00 - KERNEL_BASE + 1
+       @ 0x80000000 - User/Kernel split
+       .long 0x00000402        @ Map first 4 MiB to 2GiB (KRW only)
+       .long 0x00100402        @ 
+       .long 0x00200402        @ 
+       .long 0x00300402        @ 
+       .rept 0xF00 - 0x800 - 4
+               .long 0
+       .endr
+ #if PCI_PADDR
+       .long PCI_PADDR +  0*(1 << 20) + 0x402  @ Map PCI config space
+       .long PCI_PADDR +  1*(1 << 20) + 0x402
+       .long PCI_PADDR +  2*(1 << 20) + 0x402
+       .long PCI_PADDR +  3*(1 << 20) + 0x402
+       .long PCI_PADDR +  4*(1 << 20) + 0x402
+       .long PCI_PADDR +  5*(1 << 20) + 0x402
+       .long PCI_PADDR +  6*(1 << 20) + 0x402
+       .long PCI_PADDR +  7*(1 << 20) + 0x402
+       .long PCI_PADDR +  8*(1 << 20) + 0x402
+       .long PCI_PADDR +  9*(1 << 20) + 0x402
+       .long PCI_PADDR + 10*(1 << 20) + 0x402
+       .long PCI_PADDR + 11*(1 << 20) + 0x402
+       .long PCI_PADDR + 12*(1 << 20) + 0x402
+       .long PCI_PADDR + 13*(1 << 20) + 0x402
+       .long PCI_PADDR + 14*(1 << 20) + 0x402
+       .long PCI_PADDR + 15*(1 << 20) + 0x402
+ #else
+       .rept 16
+               .long 0
+       .endr
+ #endif
+       .long hwmap_table_0 + 0x000 - KERNEL_BASE + 1
+       .long hwmap_table_0 + 0x400 - KERNEL_BASE + 1
+       .long hwmap_table_0 + 0x800 - KERNEL_BASE + 1
+       .long hwmap_table_0 + 0xC00 - KERNEL_BASE + 1
+       .rept 0xFF8 - 0xF00 - 16 - 4
+               .long 0
+       .endr
+       @ Page fractals
+       .long kernel_table1_map + 0x000 - KERNEL_BASE + 1
+       .long kernel_table1_map + 0x400 - KERNEL_BASE + 1
+       .long kernel_table1_map + 0x800 - KERNEL_BASE + 1
+       .long kernel_table1_map + 0xC00 - KERNEL_BASE + 1
+       .long kernel_exception_map + 0x000 - KERNEL_BASE + 1
+       .long kernel_exception_map + 0x400 - KERNEL_BASE + 1
+       .long kernel_exception_map + 0x800 - KERNEL_BASE + 1
+       .long kernel_exception_map + 0xC00 - KERNEL_BASE + 1
+ @ PID0 user table
+ .globl user_table1_map
+ @ User table1 data table (only the first half is needed)
+ @ - Abused to provide kernel stacks in the unused half of the table
+ user_table1_map:      @ Size = 4KiB (only 2KiB used)
+       .rept 0x800/4-1
+               .long 0
+       .endr
+       .long user_table1_map - KERNEL_BASE + 0x13      @ ...1FF000 = 0x7FDFF000
+       @ Kernel stack zone
+       .long kernel_table0 + 0x0000 - KERNEL_BASE + 0x13       @ ...200000 = 0x7FE00000
+       .long kernel_table0 + 0x1000 - KERNEL_BASE + 0x13       @ ...201000 = 0x7FE01000
+       .rept (0x800/4)-(MM_KSTACK_SIZE/0x1000)-2
+               .long 0
+       .endr
+       #if MM_KSTACK_SIZE != 0x2000
+       #error Kernel stack size not changed in start.S
+       #endif
+       .long stack + 0x0000 - KERNEL_BASE + 0x13       @ Kernel Stack
+       .long stack + 0x1000 - KERNEL_BASE + 0x13       @ 
+ .globl kernel_table1_map
+ kernel_table1_map:    @ Size = 4KiB
+       .rept (0xF00+16)/4
+               .long 0
+       .endr
+       .long hwmap_table_0 - KERNEL_BASE + 0x13
+       .rept 0xFF8/4 - (0xF00+16)/4 - 1
+               .long 0
+       .endr
+       .long kernel_table1_map - KERNEL_BASE + 0x13
+       .long kernel_exception_map - KERNEL_BASE + 0x13
+ @ Hardware mappings 
+ .globl hwmap_table_0
+ hwmap_table_0:
+       .long UART0_PADDR + 0x13        @ UART0
+       .rept 1024 - 1
+               .long 0
+       .endr
+ .globl kernel_exception_map
+ kernel_exception_map:
+       @ Padding
+       .rept 1024-256
+               .long 0
+       .endr
+       @ Align to nearly the end
+       .rept 256-16
+               .long   0
+       .endr
+       .long 0x212     @ Map first page for exceptions (Kernel RO, Execute)
+       .rept 16-1-2
+               .long 0
+       .endr
+       .long gUsertextPhysStart + 0x22 @ User .text (User RO, Kernel RW, because both is COW)
+       .long 0
+       
+ .section .padata
+ stack:
+       .space MM_KSTACK_SIZE, 0        @ Original kernel stack
+ // vim: ts=8 ft=armv7
index 0000000,2ae38ee..71c4d4a
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,764 +1,767 @@@
 -MODULE_DEFINE(0, VERSION, VTerm, VT_Install, NULL, DEFAULT_INPUT, NULL);
+ /*
+  * Acess2 Virtual Terminal Driver
+  */
+ #define DEBUG 0
+ #include "vterm.h"
+ #include <fs_devfs.h>
+ #include <modules.h>
+ #include <api_drv_keyboard.h>
+ #include <api_drv_video.h>
+ #include <errno.h>
+ #include <semaphore.h>
+ // === CONSTANTS ===
+ #define VERSION       ((0<<8)|(50))
+ #define       NUM_VTS 8
+ //#define DEFAULT_OUTPUT      "BochsGA"
+ #define DEFAULT_OUTPUT        "Vesa"
+ #define FALLBACK_OUTPUT       "x86_VGAText"
+ #define DEFAULT_INPUT "PS2Keyboard"
+ #define       DEFAULT_WIDTH   640
+ #define       DEFAULT_HEIGHT  480
+ #define DEFAULT_SCROLLBACK    2       // 2 Screens of text + current screen
+ //#define DEFAULT_SCROLLBACK  0
+ // === TYPES ===
+ // === IMPORTS ===
+ extern void   Debug_SetKTerminal(const char *File);
+ // === PROTOTYPES ===
+  int  VT_Install(char **Arguments);
+ char  *VT_ReadDir(tVFS_Node *Node, int Pos);
+ tVFS_Node     *VT_FindDir(tVFS_Node *Node, const char *Name);
+  int  VT_Root_IOCtl(tVFS_Node *Node, int Id, void *Data);
+ Uint64        VT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);
+ Uint64        VT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer);
+  int  VT_Terminal_IOCtl(tVFS_Node *Node, int Id, void *Data);
+ // === CONSTANTS ===
+ // === GLOBALS ===
++MODULE_DEFINE(0, VERSION, VTerm, VT_Install, NULL, NULL);
+ tVFS_NodeType gVT_RootNodeType = {
+       .TypeName = "VTerm Root",
+       .ReadDir = VT_ReadDir,
+       .FindDir = VT_FindDir,
+       .IOCtl = VT_Root_IOCtl
+       };
+ tVFS_NodeType gVT_TermNodeType = {
+       .TypeName = "VTerm",
+       .Read = VT_Read,
+       .Write = VT_Write,
+       .IOCtl = VT_Terminal_IOCtl
+       };
+ tDevFS_Driver gVT_DrvInfo = {
+       NULL, "VTerm",
+       {
+       .Flags = VFS_FFLAG_DIRECTORY,
+       .Size = NUM_VTS,
+       .Inode = -1,
+       .NumACLs = 0,
+       .Type = &gVT_RootNodeType
+       }
+ };
+ // --- Terminals ---
+ tVTerm        gVT_Terminals[NUM_VTS];
+  int  giVT_CurrentTerminal = 0;
+ tVTerm        *gpVT_CurTerm = &gVT_Terminals[0];
+ // --- Video State ---
+ short giVT_RealWidth  = DEFAULT_WIDTH;        //!< Screen Width
+ short giVT_RealHeight = DEFAULT_HEIGHT;       //!< Screen Height
+  int  giVT_Scrollback = DEFAULT_SCROLLBACK;
+ // --- Driver Handles ---
+ char  *gsVT_OutputDevice = NULL;
+ char  *gsVT_InputDevice = NULL;
+  int  giVT_OutputDevHandle = -2;
+  int  giVT_InputDevHandle = -2;
+ // === CODE ===
+ /**
+  * \fn int VT_Install(char **Arguments)
+  * \brief Installs the Virtual Terminal Driver
+  */
+ int VT_Install(char **Arguments)
+ {
+        int    i;
+       
+       // Scan Arguments
+       if(Arguments)
+       {
+               char    **args;
+               const char      *arg;
+               for(args = Arguments; (arg = *args); args++ )
+               {
+                       char    data[strlen(arg)+1];
+                       char    *opt = data;
+                       char    *val;
+                       
+                       val = strchr(arg, '=');
+                       strcpy(data, arg);
+                       if( val ) {
+                               data[ val - arg ] = '\0';
+                               val ++;
+                       }
+                       Log_Debug("VTerm", "Argument '%s'", arg);
+                       
+                       if( strcmp(opt, "Video") == 0 ) {
+                               if( !gsVT_OutputDevice )
+                                       gsVT_OutputDevice = strdup(val);
+                       }
+                       else if( strcmp(opt, "Input") == 0 ) {
+                               if( !gsVT_InputDevice )
+                                       gsVT_InputDevice = strdup(val);
+                       }
+                       else if( strcmp(opt, "Width") == 0 ) {
+                               giVT_RealWidth = atoi( val );
+                       }
+                       else if( strcmp(opt, "Height") == 0 ) {
+                               giVT_RealHeight = atoi( val );
+                       }
+                       else if( strcmp(opt, "Scrollback") == 0 ) {
+                               giVT_Scrollback = atoi( val );
+                       }
+               }
+       }
+       
+       // Apply Defaults
+       if(!gsVT_OutputDevice)  gsVT_OutputDevice = (char*)DEFAULT_OUTPUT;
+       else if( Module_EnsureLoaded( gsVT_OutputDevice ) )     gsVT_OutputDevice = (char*)DEFAULT_OUTPUT;
+       if( Module_EnsureLoaded( gsVT_OutputDevice ) )  gsVT_OutputDevice = (char*)FALLBACK_OUTPUT;
+       if( Module_EnsureLoaded( gsVT_OutputDevice ) ) {
+               Log_Error("VTerm", "Fallback video '%s' is not avaliable, giving up", FALLBACK_OUTPUT);
+               return MODULE_ERR_MISC;
+       }
+       
+       if(!gsVT_InputDevice)   gsVT_InputDevice = (char*)DEFAULT_INPUT;
+       else if( Module_EnsureLoaded( gsVT_InputDevice ) )      gsVT_InputDevice = (char*)DEFAULT_INPUT;
++      if( Module_EnsureLoaded( gsVT_OutputDevice ) ) {
++              Log_Error("VTerm", "Fallback input '%s' is not avaliable, input will not be avaliable", DEFAULT_INPUT);
++      }
+       
+       // Create device paths
+       {
+               char    *tmp;
+               tmp = malloc( 9 + strlen(gsVT_OutputDevice) + 1 );
+               strcpy(tmp, "/Devices/");
+               strcpy(&tmp[9], gsVT_OutputDevice);
+               gsVT_OutputDevice = tmp;
+               tmp = malloc( 9 + strlen(gsVT_InputDevice) + 1 );
+               strcpy(tmp, "/Devices/");
+               strcpy(&tmp[9], gsVT_InputDevice);
+               gsVT_InputDevice = tmp;
+       }
+       
+       Log_Log("VTerm", "Using '%s' as output", gsVT_OutputDevice);
+       Log_Log("VTerm", "Using '%s' as input", gsVT_InputDevice);
+       
+       VT_InitOutput();
+       VT_InitInput();
+       
+       // Create Nodes
+       for( i = 0; i < NUM_VTS; i++ )
+       {
+               gVT_Terminals[i].Mode = TERM_MODE_TEXT;
+               gVT_Terminals[i].Flags = 0;
+ //            gVT_Terminals[i].Flags = VT_FLAG_HIDECSR;       //HACK - Stop all those memcpy calls
+               gVT_Terminals[i].CurColour = DEFAULT_COLOUR;
+               gVT_Terminals[i].WritePos = 0;
+               gVT_Terminals[i].AltWritePos = 0;
+               gVT_Terminals[i].ViewPos = 0;
+               gVT_Terminals[i].ReadingThread = -1;
+               gVT_Terminals[i].ScrollHeight = 0;
+               
+               // Initialise
+               VT_int_ChangeMode( &gVT_Terminals[i],
+                       TERM_MODE_TEXT, giVT_RealWidth, giVT_RealHeight );
+               
+               gVT_Terminals[i].Name[0] = '0'+i;
+               gVT_Terminals[i].Name[1] = '\0';
+               gVT_Terminals[i].Node.Inode = i;
+               gVT_Terminals[i].Node.ImplPtr = &gVT_Terminals[i];
+               gVT_Terminals[i].Node.NumACLs = 0;      // Only root can open virtual terminals
+       
+               gVT_Terminals[i].Node.Type = &gVT_TermNodeType; 
+ //            Semaphore_Init(&gVT_Terminals[i].InputSemaphore, 0, MAX_INPUT_CHARS8, "VTerm", gVT_Terminals[i].Name);
+       }
+       
+       // Add to DevFS
+       DevFS_AddDevice( &gVT_DrvInfo );
+       
+       // Set kernel output to VT0
+       Debug_SetKTerminal("/Devices/VTerm/0");
+       
+       return MODULE_ERR_OK;
+ }
+ /**
+  * \brief Set the video resolution
+  * \param Width       New screen width
+  * \param Height      New screen height
+  */
+ void VT_SetResolution(int Width, int Height)
+ {
+       tVideo_IOCtl_Mode       mode = {0};
+        int    tmp;
+        int    i;
+       
+       // Create the video mode
+       mode.width = Width;
+       mode.height = Height;
+       mode.bpp = 32;
+       mode.flags = 0;
+       
+       // Set video mode
+       VFS_IOCtl( giVT_OutputDevHandle, VIDEO_IOCTL_FINDMODE, &mode );
+       tmp = mode.id;
+       if( Width != mode.width || Height != mode.height )
+       {
+               Log_Warning("VTerm",
+                       "Selected resolution (%ix%i is not supported) by the device, using (%ix%i)",
+                       giVT_RealWidth, giVT_RealHeight,
+                       mode.width, mode.height
+                       );
+               giVT_RealWidth = mode.width;
+               giVT_RealHeight = mode.height;
+       }
+       VFS_IOCtl( giVT_OutputDevHandle, VIDEO_IOCTL_GETSETMODE, &tmp );
+       
+       // Resize text terminals if needed
+       if( gVT_Terminals[0].Text && (giVT_RealWidth != mode.width || giVT_RealHeight != mode.height) )
+       {
+                int    newBufSize = (giVT_RealWidth/giVT_CharWidth)
+                                       *(giVT_RealHeight/giVT_CharHeight)
+                                       *(giVT_Scrollback+1);
+               //tVT_Char      *tmp;
+               // Resize the text terminals
+               Log_Debug("VTerm", "Resizing terminals to %ix%i",
+                       giVT_RealWidth/giVT_CharWidth, giVT_RealHeight/giVT_CharHeight);
+               for( i = 0; i < NUM_VTS; i ++ )
+               {
+                       if( gVT_Terminals[i].Mode != TERM_MODE_TEXT )   continue;
+                       
+                       gVT_Terminals[i].TextWidth = giVT_RealWidth/giVT_CharWidth;
+                       gVT_Terminals[i].TextHeight = giVT_RealHeight/giVT_CharHeight;
+                       gVT_Terminals[i].ScrollHeight = gVT_Terminals[i].TextHeight;
+                       
+                       gVT_Terminals[i].Text = realloc(
+                               gVT_Terminals[i].Text,
+                               newBufSize*sizeof(tVT_Char)
+                               );
+               }
+       }
+ }
+ /**
+  * \fn char *VT_ReadDir(tVFS_Node *Node, int Pos)
+  * \brief Read from the VTerm Directory
+  */
+ char *VT_ReadDir(tVFS_Node *Node, int Pos)
+ {
+       if(Pos < 0)     return NULL;
+       if(Pos >= NUM_VTS)      return NULL;
+       return strdup( gVT_Terminals[Pos].Name );
+ }
+ /**
+  * \fn tVFS_Node *VT_FindDir(tVFS_Node *Node, const char *Name)
+  * \brief Find an item in the VTerm directory
+  * \param Node        Root node
+  * \param Name        Name (number) of the terminal
+  */
+ tVFS_Node *VT_FindDir(tVFS_Node *Node, const char *Name)
+ {
+        int    num;
+       
+       ENTER("pNode sName", Node, Name);
+       
+       // Open the input and output files if needed
+       if(giVT_OutputDevHandle == -2)  VT_InitOutput();
+       if(giVT_InputDevHandle == -2)   VT_InitInput();
+       
+       // Sanity check name
+       if(Name[0] < '0' || Name[0] > '9' || Name[1] != '\0') {
+               LEAVE('n');
+               return NULL;
+       }
+       // Get index
+       num = Name[0] - '0';
+       if(num >= NUM_VTS) {
+               LEAVE('n');
+               return NULL;
+       }
+       // Return node
+       LEAVE('p', &gVT_Terminals[num].Node);
+       return &gVT_Terminals[num].Node;
+ }
+ /**
+  * \fn int VT_Root_IOCtl(tVFS_Node *Node, int Id, void *Data)
+  * \brief Control the VTerm Driver
+  */
+ int VT_Root_IOCtl(tVFS_Node *Node, int Id, void *Data)
+ {
+        int    len;
+       switch(Id)
+       {
+       case DRV_IOCTL_TYPE:    return DRV_TYPE_MISC;
+       case DRV_IOCTL_IDENT:   memcpy(Data, "VT\0\0", 4);      return 0;
+       case DRV_IOCTL_VERSION: return VERSION;
+       case DRV_IOCTL_LOOKUP:  return 0;
+       
+       case 4: // Get Video Driver
+               if(Data)        strcpy(Data, gsVT_OutputDevice);
+               return strlen(gsVT_OutputDevice);
+       
+       case 5: // Set Video Driver
+               if(!Data)       return -EINVAL;
+               if(Threads_GetUID() != 0)       return -EACCES;
+               
+               len = strlen(Data);
+               
+               // TODO: Check if the string used is a heap string
+               
+               free(gsVT_OutputDevice);
+               
+               gsVT_OutputDevice = malloc(len+1);
+               strcpy(gsVT_OutputDevice, Data);
+               
+               VFS_Close(giVT_OutputDevHandle);
+               giVT_OutputDevHandle = -1;
+               
+               VT_InitOutput();
+               return 1;
+       }
+       return 0;
+ }
+ /**
+  * \brief Read from a virtual terminal
+  */
+ Uint64 VT_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)
+ {
+        int    pos = 0;
+        int    avail;
+       tVTerm  *term = &gVT_Terminals[ Node->Inode ];
+       Uint32  *codepoint_buf = Buffer;
+       Uint32  *codepoint_in;
+       
+       Mutex_Acquire( &term->ReadingLock );
+       
+       // Check current mode
+       switch(term->Mode)
+       {
+       // Text Mode (UTF-8)
+       case TERM_MODE_TEXT:
+               VT_int_UpdateCursor(term, 1);
+       
+               VFS_SelectNode(Node, VFS_SELECT_READ, NULL, "VT_Read (UTF-8)");
+               
+               avail = term->InputWrite - term->InputRead;
+               if(avail < 0)
+                       avail += MAX_INPUT_CHARS8;
+               if(avail > Length - pos)
+                       avail = Length - pos;
+               
+               while( avail -- )
+               {
+                       ((char*)Buffer)[pos] = term->InputBuffer[term->InputRead];
+                       pos ++;
+                       term->InputRead ++;
+                       while(term->InputRead >= MAX_INPUT_CHARS8)
+                               term->InputRead -= MAX_INPUT_CHARS8;
+               }
+               break;
+       
+       //case TERM_MODE_FB:
+       // Other - UCS-4
+       default:
+               VFS_SelectNode(Node, VFS_SELECT_READ, NULL, "VT_Read (UCS-4)");
+               
+               avail = term->InputWrite - term->InputRead;
+               if(avail < 0)
+                       avail += MAX_INPUT_CHARS32;
+               Length /= 4;
+               if(avail > Length - pos)
+                       avail = Length - pos;
+               
+               codepoint_in = (void*)term->InputBuffer;
+               codepoint_buf = Buffer;
+               
+               while( avail -- )
+               {
+                       codepoint_buf[pos] = codepoint_in[term->InputRead];
+                       pos ++;
+                       term->InputRead ++;
+                       while(term->InputRead >= MAX_INPUT_CHARS32)
+                               term->InputRead -= MAX_INPUT_CHARS32;
+               }
+               pos *= 4;
+               break;
+       }
+       
+       // Mark none avaliable if buffer empty
+       if( term->InputRead == term->InputWrite )
+               VFS_MarkAvaliable(&term->Node, 0);
+       
+       term->ReadingThread = -1;
+ //    VT_int_UpdateCursor(term, term->Mode == TERM_MODE_TEXT);
+       Mutex_Release( &term->ReadingLock );
+       
+       return pos;
+ }
+ /**
+  * \fn Uint64 VT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)
+  * \brief Write to a virtual terminal
+  */
+ Uint64 VT_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, const void *Buffer)
+ {
+       tVTerm  *term = &gVT_Terminals[ Node->Inode ];
+        int    size;
+       
+       // Write
+       switch( term->Mode )
+       {
+       // Print Text
+       case TERM_MODE_TEXT:
+               VT_int_PutString(term, Buffer, Length);
+               break;
+       
+       // Framebuffer :)
+       case TERM_MODE_FB:
+               // - Sanity Checking
+               size = term->Width*term->Height*4;
+               if( Offset > size ) {
+                       Log_Notice("VTerm", "VT_Write: Offset (0x%llx) > FBSize (0x%x)",
+                               Offset, size);
+                       return 0;
+               }
+               if( Offset + Length > size ) {
+                       Log_Notice("VTerm", "VT_Write: Offset+Length (0x%llx) > FBSize (0x%x)",
+                               Offset+Length, size);
+                       Length = size - Offset;
+               }
+               
+               // Update screen if needed
+               if( Node->Inode == giVT_CurrentTerminal )
+               {
+                       if( giVT_RealHeight > term->Height )
+                               Offset += (giVT_RealHeight - term->Height) / 2 * term->Width * 4;
+                       // Handle undersized virtual terminals
+                       if( giVT_RealWidth > term->Width )
+                       {
+                               // No? :( Well, just center it
+                                int    x, y, w, h;
+                               Uint    dst_ofs;
+                               // TODO: Fix to handle the final line correctly?
+                               x = Offset/4;   y = x / term->Width;    x %= term->Width;
+                               w = Length/4+x; h = w / term->Width;    w %= term->Width;
+                               
+                               // Center
+                               x += (giVT_RealWidth - term->Width) / 2;
+                               dst_ofs = (x + y * giVT_RealWidth) * 4;
+                               while(h--)
+                               {
+                                       VFS_WriteAt( giVT_OutputDevHandle,
+                                               dst_ofs,
+                                               term->Width * 4,
+                                               Buffer
+                                               );
+                                       Buffer = (void*)( (Uint)Buffer + term->Width*4 );
+                                       dst_ofs += giVT_RealWidth * 4;
+                               }
+                               return 0;
+                       }
+                       else
+                       {
+                               return VFS_WriteAt( giVT_OutputDevHandle, Offset, Length, Buffer );
+                       }
+               }
+               else
+               {
+                       if( !term->Buffer )
+                               term->Buffer = malloc( term->Width * term->Height * 4 );
+                       // Copy to the local cache
+                       memcpy( (char*)term->Buffer + (Uint)Offset, Buffer, Length );
+               }
+               break;
+       // Just pass on (for now)
+       // TODO: Handle locally too to ensure no information is lost on
+       //       VT Switch (and to isolate terminals from each other)
+       case TERM_MODE_2DACCEL:
+       //case TERM_MODE_3DACCEL:
+               if( Node->Inode == giVT_CurrentTerminal )
+               {
+                       VFS_Write( giVT_OutputDevHandle, Length, Buffer );
+               }
+               break;
+       }
+       
+       return 0;
+ }
+ /**
+  * \fn int VT_Terminal_IOCtl(tVFS_Node *Node, int Id, void *Data)
+  * \brief Call an IO Control on a virtual terminal
+  */
+ int VT_Terminal_IOCtl(tVFS_Node *Node, int Id, void *Data)
+ {
+        int    *iData = Data;
+        int    ret;
+       tVTerm  *term = Node->ImplPtr;
+       ENTER("pNode iId pData", Node, Id, Data);
+       
+       if(Id >= DRV_IOCTL_LOOKUP) {
+               // Only root can fiddle with graphics modes
+               // TODO: Remove this and replace with user ownership
+               if( Threads_GetUID() != 0 )     return -1;
+       }
+       
+       switch(Id)
+       {
+       // --- Core Defined
+       case DRV_IOCTL_TYPE:
+               LEAVE('i', DRV_TYPE_TERMINAL);
+               return DRV_TYPE_TERMINAL;
+       case DRV_IOCTL_IDENT:
+               memcpy(Data, "VT\0\0", 4);
+               LEAVE('i', 0);
+               return 0;
+       case DRV_IOCTL_VERSION:
+               LEAVE('x', VERSION);
+               return VERSION;
+       case DRV_IOCTL_LOOKUP:
+               LEAVE('i', 0);
+               return 0;
+       
+       // Get/Set the mode (and apply any changes)
+       case TERM_IOCTL_MODETYPE:
+               if(Data != NULL)
+               {
+                       if( CheckMem(Data, sizeof(int)) == 0 ) {
+                               LEAVE('i', -1);
+                               return -1;
+                       }
+                       Log_Log("VTerm", "VTerm %i mode set to %i", (int)Node->Inode, *iData);
+                       
+                       // Update mode if needed
+                       if( term->Mode != *iData || term->NewWidth || term->NewHeight)
+                       {
+                               // Adjust for text mode
+                               if( *iData == TERM_MODE_TEXT ) {
+                                       term->NewHeight *= giVT_CharHeight;
+                                       term->NewWidth *= giVT_CharWidth;
+                               }
+                               // Fill unchanged dimensions
+                               if(term->NewHeight == 0)        term->NewHeight = term->Height;
+                               if(term->NewWidth == 0) term->NewWidth = term->Width;
+                               // Set new mode
+                               VT_int_ChangeMode(term, *iData, term->NewWidth, term->NewHeight);
+                               // Clear unapplied dimensions
+                               term->NewWidth = 0;
+                               term->NewHeight = 0;
+                       }
+                       
+                       // Update the screen dimensions
+                       if(Node->Inode == giVT_CurrentTerminal)
+                               VT_SetTerminal( giVT_CurrentTerminal );
+               }
+               LEAVE('i', term->Mode);
+               return term->Mode;
+       
+       // Get/set the terminal width
+       case TERM_IOCTL_WIDTH:
+               if(Data != NULL) {
+                       if( CheckMem(Data, sizeof(int)) == 0 ) {
+                               LEAVE('i', -1);
+                               return -1;
+                       }
+                       term->NewWidth = *iData;
+               }
+               if( term->NewWidth )
+                       ret = term->NewWidth;
+               else if( term->Mode == TERM_MODE_TEXT )
+                       ret = term->TextWidth;
+               else
+                       ret = term->Width;
+               LEAVE('i', ret);
+               return ret;
+       
+       // Get/set the terminal height
+       case TERM_IOCTL_HEIGHT:
+               if(Data != NULL) {
+                       if( CheckMem(Data, sizeof(int)) == 0 ) {
+                               LEAVE('i', -1);
+                               return -1;
+                       }
+                       term->NewHeight = *iData;
+               }
+               if( term->NewHeight )
+                       ret = term->NewHeight;
+               else if( term->Mode == TERM_MODE_TEXT )
+                       ret = term->TextHeight;
+               else
+                       ret = term->Height;
+               LEAVE('i', ret);
+               return ret;
+       
+       case TERM_IOCTL_FORCESHOW:
+               Log_Log("VTerm", "Thread %i forced VTerm %i to be shown",
+                       Threads_GetTID(), (int)Node->Inode);
+               VT_SetTerminal( Node->Inode );
+               LEAVE('i', 1);
+               return 1;
+       
+       case TERM_IOCTL_GETSETCURSOR:
+               if(Data != NULL)
+               {
+                       tVideo_IOCtl_Pos        *pos = Data;
+                       if( !CheckMem(Data, sizeof(*pos)) ) {
+                               errno = -EINVAL;
+                               LEAVE('i', -1);
+                               return -1;
+                       }
+               
+                       if( term->Mode == TERM_MODE_TEXT )
+                       {
+                               if(term->Flags & VT_FLAG_ALTBUF)
+                                       term->AltWritePos = pos->x + pos->y * term->TextWidth;
+                               else
+                                       term->WritePos = pos->x + pos->y * term->TextWidth + term->ViewPos;
+                               VT_int_UpdateCursor(term, 0);
+                       }
+                       else
+                       {
+                               term->VideoCursorX = pos->x;
+                               term->VideoCursorY = pos->y;
+                               VT_int_UpdateCursor(term, 1);
+                       }
+               }
+               ret = (term->Flags & VT_FLAG_ALTBUF) ? term->AltWritePos : term->WritePos-term->ViewPos;
+               LEAVE('i', ret);
+               return ret;
+       case TERM_IOCTL_SETCURSORBITMAP: {
+               tVideo_IOCtl_Bitmap     *bmp = Data;
+               if( Data == NULL )
+               {
+                       free( term->VideoCursor );
+                       term->VideoCursor = NULL;
+                       LEAVE('i', 0);
+                       return 0;
+               }
+               // Sanity check bitmap
+               if( !CheckMem(bmp, sizeof(tVideo_IOCtl_Bitmap)) ) {
+                       Log_Notice("VTerm", "%p in TERM_IOCTL_SETCURSORBITMAP invalid", bmp);
+                       errno = -EINVAL;
+                       LEAVE_RET('i', -1);
+               }
+               if( !CheckMem(bmp->Data, bmp->W*bmp->H*sizeof(Uint32)) ) {
+                       Log_Notice("VTerm", "%p in TERM_IOCTL_SETCURSORBITMAP invalid", bmp);
+                       errno = -EINVAL;
+                       LEAVE_RET('i', -1);
+               }
+               // Reallocate if needed
+               if(term->VideoCursor)
+               {
+                       if(bmp->W * bmp->H != term->VideoCursor->W * term->VideoCursor->H) {
+                               free(term->VideoCursor);
+                               term->VideoCursor = NULL;
+                       }
+               }
+               if(!term->VideoCursor) {
+                       term->VideoCursor = malloc(sizeof(tVideo_IOCtl_Pos) + bmp->W*bmp->H*sizeof(Uint32));
+                       if(!term->VideoCursor) {
+                               Log_Error("VTerm", "Unable to allocate memory for cursor");
+                               errno = -ENOMEM;
+                               LEAVE_RET('i', -1);
+                       }
+               }
+               
+               memcpy(term->VideoCursor, bmp, sizeof(tVideo_IOCtl_Pos) + bmp->W*bmp->H*sizeof(Uint32));
+       
+               Log_Debug("VTerm", "Set VT%i's cursor to %p %ix%i",
+                       (int)term->Node.Inode, bmp, bmp->W, bmp->H);
+               if(gpVT_CurTerm == term)
+                       VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETCURSORBITMAP, term->VideoCursor);
+       
+               LEAVE('i', 0);
+               return 0; }
+       }
+       LEAVE('i', -1);
+       return -1;
+ }
+ /**
+  * \fn void VT_SetTerminal(int ID)
+  * \brief Set the current terminal
+  */
+ void VT_SetTerminal(int ID)
+ {
+       // Copy the screen state
+       if( ID != giVT_CurrentTerminal && gpVT_CurTerm->Mode != TERM_MODE_TEXT )
+       {
+               if( !gpVT_CurTerm->Buffer )
+                       gpVT_CurTerm->Buffer = malloc( gpVT_CurTerm->Width*gpVT_CurTerm->Height*4 );
+               if( gpVT_CurTerm->Width < giVT_RealWidth )
+               {
+                        int    line;
+                       Uint    ofs = 0;
+                       Uint32  *dest = gpVT_CurTerm->Buffer;
+                       // Slower scanline copy
+                       for( line = 0; line < gpVT_CurTerm->Height; line ++ )
+                       {
+                               VFS_ReadAt(giVT_OutputDevHandle, ofs, gpVT_CurTerm->Width*4, dest);
+                               ofs += giVT_RealWidth * 4;
+                               dest += gpVT_CurTerm->Width;
+                       }
+               }
+               else
+               {
+                       VFS_ReadAt(giVT_OutputDevHandle,
+                               0, gpVT_CurTerm->Height*giVT_RealWidth*4,
+                               gpVT_CurTerm->Buffer
+                               );
+               }
+       }
+       // Update current terminal ID
+       Log_Log("VTerm", "Changed terminal from %i to %i", giVT_CurrentTerminal, ID);
+       giVT_CurrentTerminal = ID;
+       gpVT_CurTerm = &gVT_Terminals[ID];
+       
+       if( gpVT_CurTerm->Mode == TERM_MODE_TEXT )
+       {
+               VT_SetMode( VIDEO_BUFFMT_TEXT );
+       }
+       else
+       {
+               // Update the cursor image
+               if(gpVT_CurTerm->VideoCursor)
+                       VFS_IOCtl(giVT_OutputDevHandle, VIDEO_IOCTL_SETCURSORBITMAP, gpVT_CurTerm->VideoCursor);
+               VT_SetMode( VIDEO_BUFFMT_FRAMEBUFFER );
+       }
+       
+       if(gpVT_CurTerm->Buffer)
+       {
+               // TODO: Handle non equal sized
+               VFS_WriteAt(
+                       giVT_OutputDevHandle,
+                       0,
+                       gpVT_CurTerm->Width*gpVT_CurTerm->Height*sizeof(Uint32),
+                       gpVT_CurTerm->Buffer
+                       );
+       }
+       
+       VT_int_UpdateCursor(gpVT_CurTerm, 1);
+       // Update the screen
+       VT_int_UpdateScreen(gpVT_CurTerm, 1);
+ }
index 0000000,d9405be..72ac697
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,326 +1,343 @@@
 -      \r
+ /**\r
+  * main.c\r
+  * - Driver core\r
+  */\r
+ #define DEBUG 0\r
+ #define VERSION       ((0<<8)|10)\r
+ #include <acess.h>\r
+ #include <errno.h>\r
+ #include <modules.h>\r
+ #include <vfs.h>\r
+ #include <fs_devfs.h>\r
+ #include <drv_pci.h>\r
+ #include <api_drv_video.h>\r
+ #include <lib/keyvalue.h>\r
+ #include <options.h>  // ARM Arch\r
+ #include "tegra2.h"\r
\r
+ #define ABS(a)        ((a)>0?(a):-(a))\r
\r
+ // === PROTOTYPES ===\r
+ // Driver\r
+  int  Tegra2Vid_Install(char **Arguments);\r
+ void  Tegra2Vid_Uninstall();\r
+ // Internal\r
+ // Filesystem\r
+ Uint64        Tegra2Vid_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer);\r
+ Uint64        Tegra2Vid_Write(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer);\r
+  int  Tegra2Vid_IOCtl(tVFS_Node *node, int id, void *data);\r
+ // -- Internals\r
+  int  Tegra2Vid_int_SetMode(int Mode);\r
\r
+ // === GLOBALS ===\r
+ MODULE_DEFINE(0, VERSION, Tegra2Vid, Tegra2Vid_Install, NULL, NULL);\r
+ tDevFS_Driver gTegra2Vid_DriverStruct = {\r
+       NULL, "Tegra2Vid",\r
+       {\r
+       .Read = Tegra2Vid_Read,\r
+       .Write = Tegra2Vid_Write,\r
+       .IOCtl = Tegra2Vid_IOCtl\r
+       }\r
+ };\r
+ // -- Options\r
+ tPAddr        gTegra2Vid_PhysBase = TEGRA2VID_BASE;\r
+  int  gbTegra2Vid_IsVersatile = 1;\r
+ // -- KeyVal parse rules\r
+ const tKeyVal_ParseRules      gTegra2Vid_KeyValueParser = {\r
+       NULL,\r
+       {\r
+               {"Base", "P", &gTegra2Vid_PhysBase},\r
+               {NULL, NULL, NULL}\r
+       }\r
+ };\r
+ // -- Driver state\r
+  int  giTegra2Vid_CurrentMode = 0;\r
+  int  giTegra2Vid_BufferMode;\r
+ size_t        giTegra2Vid_FramebufferSize;\r
+ Uint32        *gpTegra2Vid_IOMem;\r
+ tPAddr        gTegra2Vid_FramebufferPhys;\r
+ void  *gpTegra2Vid_Framebuffer;\r
+ // -- Misc\r
+ tDrvUtil_Video_BufInfo        gTegra2Vid_DrvUtil_BufInfo;\r
+ tVideo_IOCtl_Pos      gTegra2Vid_CursorPos;\r
\r
+ // === CODE ===\r
+ /**\r
+  */\r
+ int Tegra2Vid_Install(char **Arguments)\r
+ {\r
++      return MODULE_ERR_NOTNEEDED;\r
+ //    KeyVal_Parse(&gTegra2Vid_KeyValueParser, Arguments);\r
\r
+       gpTegra2Vid_IOMem = (void*)MM_MapHWPages(gTegra2Vid_PhysBase, 256/4);\r
+       {\r
+               Log_Debug("Tegra2Vid", "Display CMD Registers");\r
+               for( int i = 0x000; i <= 0x01A; i ++ )\r
+                       Log_Debug("Tegra2Vid", "[0x%03x] = 0x%08x", i, gpTegra2Vid_IOMem[i]);\r
+               for( int i = 0x028; i <= 0x043; i ++ )\r
+                       Log_Debug("Tegra2Vid", "[0x%03x] = 0x%08x", i, gpTegra2Vid_IOMem[i]);\r
+               Log_Debug("Tegra2Vid", "Display COM Registers");\r
+               for( int i = 0x300; i <= 0x329; i ++ )\r
+                       Log_Debug("Tegra2Vid", "[0x%03x] = 0x%08x", i, gpTegra2Vid_IOMem[i]);\r
+               Log_Debug("Tegra2Vid", "Display DISP Registers");\r
+               for( int i = 0x400; i <= 0x446; i ++ )\r
+                       Log_Debug("Tegra2Vid", "[0x%03x] = 0x%08x", i, gpTegra2Vid_IOMem[i]);\r
+               for( int i = 0x480; i <= 0x484; i ++ )\r
+                       Log_Debug("Tegra2Vid", "[0x%03x] = 0x%08x", i, gpTegra2Vid_IOMem[i]);\r
+               for( int i = 0x4C0; i <= 0x4C1; i ++ )\r
+                       Log_Debug("Tegra2Vid", "[0x%03x] = 0x%08x", i, gpTegra2Vid_IOMem[i]);\r
\r
+               Log_Debug("Tegra2Vid", "WINC_A Registers");\r
+               for( int i = 0x700; i <= 0x714; i ++ )\r
+                       Log_Debug("Tegra2Vid", "[0x%03x] = 0x%08x", i, gpTegra2Vid_IOMem[i]);\r
+               Log_Debug("Tegra2Vid", "WINBUF_A");\r
+               for( int i = 0x800; i <= 0x80A; i ++ )\r
+                       Log_Debug("Tegra2Vid", "[0x%03x] = 0x%08x", i, gpTegra2Vid_IOMem[i]);\r
+       }\r
+ //    return 1;\r
 -      memset(gpTegra2Vid_Framebuffer, 0x1F, 0x1000);\r
++\r
++      // HACK!!!\r
++//    {\r
++//            int     w = 1980, h = 1080;\r
++//            gpTegra2Vid_IOMem[DC_DISP_DISP_ACTIVE_0] = (h << 16) | w;\r
++//            gpTegra2Vid_IOMem[DC_WIN_A_SIZE_0] = (h << 16) | w;\r
++//            gpTegra2Vid_IOMem[DC_WIN_A_PRESCALED_SIZE_0] = (h << 16) | w;\r
++//    }\r
++\r
+       giTegra2Vid_FramebufferSize =\r
+               (gpTegra2Vid_IOMem[DC_WIN_A_SIZE_0]&0xFFFF)\r
+               *(gpTegra2Vid_IOMem[DC_WIN_A_SIZE_0]>>16)*4;\r
\r
+       Log_Debug("Tegra2Vid", "giTegra2Vid_FramebufferSize = 0x%x", giTegra2Vid_FramebufferSize);\r
+       gpTegra2Vid_Framebuffer = MM_MapHWPages(\r
+               gpTegra2Vid_IOMem[DC_WINBUF_A_START_ADDR_0],\r
+               (giTegra2Vid_FramebufferSize+PAGE_SIZE-1)/PAGE_SIZE\r
+               );\r
 -      *(Uint32*)(gpTegra2Vid_IOMem + DC_WIN_A_SIZE_0) = (mode->H << 16) | mode->W;\r
++      memset(gpTegra2Vid_Framebuffer, 0xFF, 0x1000);\r
++\r
++      gpTegra2Vid_IOMem[DC_WIN_A_WIN_OPTIONS_0] &= ~0x40;\r
++      gpTegra2Vid_IOMem[DC_WIN_A_COLOR_DEPTH_0] = 12; // Could be 13 (BGR/RGB)\r
++      gTegra2Vid_DrvUtil_BufInfo.Width = 1024;\r
++      gTegra2Vid_DrvUtil_BufInfo.Height = 768;\r
++      gTegra2Vid_DrvUtil_BufInfo.Pitch = 1024*4;\r
++      gTegra2Vid_DrvUtil_BufInfo.Depth = 32;\r
++      gTegra2Vid_DrvUtil_BufInfo.Framebuffer = gpTegra2Vid_Framebuffer;\r
\r
\r
+ //    Tegra2Vid_int_SetMode(4);\r
\r
+       DevFS_AddDevice( &gTegra2Vid_DriverStruct );\r
\r
+       return 0;\r
+ }\r
\r
+ /**\r
+  * \brief Clean up resources for driver unloading\r
+  */\r
+ void Tegra2Vid_Uninstall()\r
+ {\r
+ }\r
\r
+ /**\r
+  * \brief Read from the framebuffer\r
+  */\r
+ Uint64 Tegra2Vid_Read(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer)\r
+ {\r
+       return 0;\r
+ }\r
\r
+ /**\r
+  * \brief Write to the framebuffer\r
+  */\r
+ Uint64 Tegra2Vid_Write(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)\r
+ {\r
+       gTegra2Vid_DrvUtil_BufInfo.BufferFormat = giTegra2Vid_BufferMode;\r
+       return DrvUtil_Video_WriteLFB(&gTegra2Vid_DrvUtil_BufInfo, Offset, Length, Buffer);\r
+ }\r
\r
+ const char *csaTegra2Vid_IOCtls[] = {DRV_IOCTLNAMES, DRV_VIDEO_IOCTLNAMES, NULL};\r
\r
+ /**\r
+  * \brief Handle messages to the device\r
+  */\r
+ int Tegra2Vid_IOCtl(tVFS_Node *Node, int ID, void *Data)\r
+ {\r
+        int    ret = -2;\r
+       ENTER("pNode iID pData", Node, ID, Data);\r
+       \r
+       switch(ID)\r
+       {\r
+       BASE_IOCTLS(DRV_TYPE_VIDEO, "PL110", VERSION, csaTegra2Vid_IOCtls);\r
\r
+       case VIDEO_IOCTL_SETBUFFORMAT:\r
+               DrvUtil_Video_RemoveCursor( &gTegra2Vid_DrvUtil_BufInfo );\r
+               ret = giTegra2Vid_BufferMode;\r
+               if(Data)        giTegra2Vid_BufferMode = *(int*)Data;\r
+               if(gTegra2Vid_DrvUtil_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT)\r
+                       DrvUtil_Video_SetCursor( &gTegra2Vid_DrvUtil_BufInfo, &gDrvUtil_TextModeCursor );\r
+               break;\r
+       \r
+       case VIDEO_IOCTL_GETSETMODE:\r
+               if(Data)\r
+               {\r
+                        int    newMode;\r
+                       \r
+                       if( !CheckMem(Data, sizeof(int)) )\r
+                               LEAVE_RET('i', -1);\r
+                       \r
+                       newMode = *(int*)Data;\r
+                       \r
+                       if(newMode < 0 || newMode >= ciTegra2Vid_ModeCount)\r
+                               LEAVE_RET('i', -1);\r
\r
+                       if(newMode != giTegra2Vid_CurrentMode)\r
+                       {\r
+                               giTegra2Vid_CurrentMode = newMode;\r
+                               Tegra2Vid_int_SetMode( newMode );\r
+                       }\r
+               }\r
+               ret = giTegra2Vid_CurrentMode;\r
+               break;\r
+       \r
+       case VIDEO_IOCTL_FINDMODE:\r
+               {\r
+               tVideo_IOCtl_Mode *mode = Data;\r
+                int    closest, closestArea, reqArea = 0;\r
+               if(!Data || !CheckMem(Data, sizeof(tVideo_IOCtl_Mode)))\r
+                       LEAVE_RET('i', -1);\r
+               if( mode->bpp != 32 )\r
+                       LEAVE_RET('i', 0);\r
+               if( mode->flags != 0 )\r
+                       LEAVE_RET('i', 0);\r
\r
+               ret = 0;\r
\r
+               for( int i = 0; i < ciTegra2Vid_ModeCount; i ++ )\r
+               {\r
+                        int    area;\r
+                       if(mode->width == caTegra2Vid_Modes[i].W && mode->height == caTegra2Vid_Modes[i].H) {\r
+                               mode->id = i;\r
+                               ret = 1;\r
+                               break;\r
+                       }\r
+                       \r
+                       area = caTegra2Vid_Modes[i].W * caTegra2Vid_Modes[i].H;\r
+                       if(!reqArea) {\r
+                               reqArea = mode->width * mode->height;\r
+                               closest = i;\r
+                               closestArea = area;\r
+                       }\r
+                       else if( ABS(area - reqArea) < ABS(closestArea - reqArea) ) {\r
+                               closest = i;\r
+                               closestArea = area;\r
+                       }\r
+               }\r
+               \r
+               if( ret == 0 )\r
+               {\r
+                       mode->id = closest;\r
+                       ret = 1;\r
+               }\r
+               mode->width = caTegra2Vid_Modes[mode->id].W;\r
+               mode->height = caTegra2Vid_Modes[mode->id].H;\r
+               break;\r
+               }\r
+       \r
+       case VIDEO_IOCTL_MODEINFO:\r
+               {\r
+               tVideo_IOCtl_Mode *mode = Data;\r
+               if(!Data || !CheckMem(Data, sizeof(tVideo_IOCtl_Mode)))\r
+                       LEAVE_RET('i', -1);\r
+               if(mode->id < 0 || mode->id >= ciTegra2Vid_ModeCount)\r
+                       LEAVE_RET('i', 0);\r
+               \r
\r
+               mode->bpp = 32;\r
+               mode->flags = 0;\r
+               mode->width = caTegra2Vid_Modes[mode->id].W;\r
+               mode->height = caTegra2Vid_Modes[mode->id].H;\r
\r
+               ret = 1;\r
+               break;\r
+               }\r
+       \r
+       case VIDEO_IOCTL_SETCURSOR:\r
+               if( !Data || !CheckMem(Data, sizeof(tVideo_IOCtl_Pos)) )\r
+                       LEAVE_RET('i', -1);\r
\r
+               DrvUtil_Video_RemoveCursor( &gTegra2Vid_DrvUtil_BufInfo );\r
+               \r
+               gTegra2Vid_CursorPos = *(tVideo_IOCtl_Pos*)Data;\r
+               if(gTegra2Vid_DrvUtil_BufInfo.BufferFormat == VIDEO_BUFFMT_TEXT)\r
+                       DrvUtil_Video_DrawCursor(\r
+                               &gTegra2Vid_DrvUtil_BufInfo,\r
+                               gTegra2Vid_CursorPos.x*giVT_CharWidth,\r
+                               gTegra2Vid_CursorPos.y*giVT_CharHeight\r
+                               );\r
+               else\r
+                       DrvUtil_Video_DrawCursor(\r
+                               &gTegra2Vid_DrvUtil_BufInfo,\r
+                               gTegra2Vid_CursorPos.x,\r
+                               gTegra2Vid_CursorPos.y\r
+                               );\r
+               break;\r
+       \r
+       default:\r
+               LEAVE('i', -2);\r
+               return -2;\r
+       }\r
+       \r
+       LEAVE('i', ret);\r
+       return ret;\r
+ }\r
\r
+ //\r
+ //\r
+ //\r
\r
+ int Tegra2Vid_int_SetMode(int Mode)\r
+ {\r
+       const struct sTegra2_Disp_Mode  *mode = &caTegra2Vid_Modes[Mode];\r
+        int    w = mode->W, h = mode->H;       // Horizontal/Vertical Active\r
+       *(Uint32*)(gpTegra2Vid_IOMem + DC_DISP_FRONT_PORCH_0) = (mode->VFP << 16) | mode->HFP; \r
+       *(Uint32*)(gpTegra2Vid_IOMem + DC_DISP_SYNC_WIDTH_0)  = (mode->HS << 16)  | mode->HS;\r
+       *(Uint32*)(gpTegra2Vid_IOMem + DC_DISP_BACK_PORCH_0)  = (mode->VBP << 16) | mode->HBP;\r
+       *(Uint32*)(gpTegra2Vid_IOMem + DC_DISP_DISP_ACTIVE_0) = (mode->H << 16)   | mode->W;\r
\r
+       *(Uint32*)(gpTegra2Vid_IOMem + DC_WIN_A_POSITION_0) = 0;\r
 -      *(Uint32*)(gpTegra2Vid_IOMem + DC_WIN_A_PRESCALED_SIZE_0) = (mode->H << 16) | mode->W;\r
++      *(Uint32*)(gpTegra2Vid_IOMem + DC_WIN_A_SIZE_0) = (h << 16) | w;\r
+       *(Uint32*)(gpTegra2Vid_IOMem + DC_DISP_DISP_COLOR_CONTROL_0) = 0x8;     // BASE888\r
+       *(Uint32*)(gpTegra2Vid_IOMem + DC_WIN_A_COLOR_DEPTH_0) = 12;    // Could be 13 (BGR/RGB)\r
++      *(Uint32*)(gpTegra2Vid_IOMem + DC_WIN_A_PRESCALED_SIZE_0) = (h << 16) | w;\r
\r
+       Log_Debug("Tegra2Vid", "Mode %i (%ix%i) selected", Mode, w, h);\r
\r
+       if( !gpTegra2Vid_Framebuffer || w*h*4 != giTegra2Vid_FramebufferSize )\r
+       {\r
+               if( gpTegra2Vid_Framebuffer )\r
+               {\r
+                       // TODO: Free framebuffer for reallocation\r
+               }\r
\r
+               giTegra2Vid_FramebufferSize = w*h*4;            \r
\r
+               gpTegra2Vid_Framebuffer = (void*)MM_AllocDMA(\r
+                       (giTegra2Vid_FramebufferSize + PAGE_SIZE-1) / PAGE_SIZE,\r
+                       32,\r
+                       &gTegra2Vid_FramebufferPhys\r
+                       );\r
+               // TODO: Catch allocation failures\r
+               Log_Debug("Tegra2Vid", "0x%x byte framebuffer at %p (%P phys)",\r
+                               giTegra2Vid_FramebufferSize,\r
+                               gpTegra2Vid_Framebuffer,\r
+                               gTegra2Vid_FramebufferPhys\r
+                               );\r
+               \r
+               // Tell hardware\r
+               *(Uint32*)(gpTegra2Vid_IOMem + DC_WINBUF_A_START_ADDR_0) = gTegra2Vid_FramebufferPhys;\r
+               *(Uint32*)(gpTegra2Vid_IOMem + DC_WINBUF_A_ADDR_V_OFFSET_0) = 0;        // Y offset\r
+               *(Uint32*)(gpTegra2Vid_IOMem + DC_WINBUF_A_ADDR_H_OFFSET_0) = 0;        // X offset\r
+       }\r
\r
+       return 0;\r
+ }\r
index 0000000,a3f126b..c36b05e
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,75 +1,174 @@@
 -      {720,  487,  16,33,   63, 33,   59, 133},       // NTSC 2
 -      {720,  576,  12,33,   63, 33,   69, 193},       // PAL 2 (VFP shown as 2/33, used 33)
 -      {720,  483,  16, 6,   63,  6,   59,  30},       // 480p
 -      {1280, 720,  70, 5,  804,  6,  220,  20},       // 720p
 -      {1920,1080,  44, 4,  884,  5,  148,  36},       // 1080p
+ /*
+  * Acess2 NVidia Tegra2 Display Driver
+  * - By John Hodge (thePowersGang)
+  *
+  * tegra2.h
+  * - Driver definitions
+  */
+ #ifndef _TEGRA2_DISP_H_
+ #define _TEGRA2_DISP_H_
+ #define TEGRA2VID_BASE        0x54200000      // 0x40000 Large (256 KB)
+ const struct sTegra2_Disp_Mode
+ {
+       Uint16  W,   H;
+       Uint16  HFP, VFP;
+       Uint16  HS,  VS;
+       Uint16  HBP, VBP;
+ }     caTegra2Vid_Modes[] = {
+       // TODO: VESA timings
 -
++//    {720,  487,  16,33,   63, 33,   59, 133},       // NTSC 2
++//    {720,  576,  12,33,   63, 33,   69, 193},       // PAL 2 (VFP shown as 2/33, used 33)
++//    {720,  483,  16, 6,   63,  6,   59,  30},       // 480p
++//    {1280, 720,  70, 5,  804,  6,  220,  20},       // 720p
++//    {1920,1080,  44, 4,  884,  5,  148,  36},       // 1080p
+       // TODO: Can all but HA/VA be constant and those select the resolution?
+ };
+ const int ciTegra2Vid_ModeCount = sizeof(caTegra2Vid_Modes)/sizeof(caTegra2Vid_Modes[0]);
+ enum eTegra2_Disp_Regs
+ {
++      DC_CMD_STATE_CONTROL_0 = 0x041,
++      DC_CMD_DISPLAY_WINDOW_HEADER_0, // 042
++      DC_CMD_REG_ACT_CONTROL_0,       // 043
++
++      DC_COM_CRC_CONTROL_0 = 0x300,
++      DC_COM_CRC_CHECKSUM_0,          // 301
++      DC_COM_PIN_OUTPUT_ENABLE0_0,    // 302
++      DC_COM_PIN_OUTPUT_ENABLE1_0,    // 303
++      DC_COM_PIN_OUTPUT_ENABLE2_0,    // 304
++      DC_COM_PIN_OUTPUT_ENABLE3_0,    // 305
++      DC_COM_PIN_OUTPUT_POLARITY0_0,  // 306
++      DC_COM_PIN_OUTPUT_POLARITY1_0,  // 307
++      DC_COM_PIN_OUTPUT_POLARITY2_0,  // 308
++      DC_COM_PIN_OUTPUT_POLARITY3_0,  // 309
++      DC_COM_PIN_OUTPUT_DATA0_0,      // 30A
++      DC_COM_PIN_OUTPUT_DATA1_0,      // 30B
++      DC_COM_PIN_OUTPUT_DATA2_0,      // 30C
++      DC_COM_PIN_OUTPUT_DATA3_0,      // 30D
++      DC_COM_PIN_INPUT_ENABLE0_0,     // 30E
++      DC_COM_PIN_INPUT_ENABLE1_0,     // 30F
++      DC_COM_PIN_INPUT_ENABLE2_0,     // 310
++      DC_COM_PIN_INPUT_ENABLE3_0,     // 311
++      DC_COM_PIN_INPUT_DATA0_0,       // 312
++      DC_COM_PIN_INPUT_DATA1_0,       // 313
++      DC_COM_PIN_OUTPUT_SELECT0_0,    // 314
++      DC_COM_PIN_OUTPUT_SELECT1_0,    // 315
++      DC_COM_PIN_OUTPUT_SELECT2_0,    // 316
++      DC_COM_PIN_OUTPUT_SELECT3_0,    // 317
++      DC_COM_PIN_OUTPUT_SELECT4_0,    // 318
++      DC_COM_PIN_OUTPUT_SELECT5_0,    // 319
++      DC_COM_PIN_OUTPUT_SELECT6_0,    // 31A
++      DC_COM_PIN_MISC_CONTROL_0,      // 31B
++      // TODO: Complete
++
+       DC_DISP_DISP_SIGNAL_OPTIONS0_0 = 0x400,
+       DC_DISP_DISP_SIGNAL_OPTIONS1_0, // 401
+       DC_DISP_DISP_WIN_OPTIONS_0,     // 402
+       DC_DISP_MEM_HIGH_PRIORITY_0,    // 403
+       DC_DISP_MEM_HIGH_PRIORITY_TIMER_0,      // 404
+       DC_DISP_DISP_TIMING_OPTIONS_0,  // 405
+       DC_DISP_REF_TO_SYNC_0,          // 406 (TrimSlice 0x0001 000B)
+       DC_DISP_SYNC_WIDTH_0,           // 407 (TrimSlice 0x0004 003A)
+       DC_DISP_BACK_PORCH_0,           // 408 (TrimSlice 0x0004 003A)
+       DC_DISP_DISP_ACTIVE_0,          // 409 (TrimSlice 0x0300 0400)
+       DC_DISP_FRONT_PORCH_0,          // 40A (TrimSlice 0x0004 003A)
 -      DC_DISP_DISP_COLOR_CONTROL_0 = 0x430,
 -      
+       DC_DISP_H_PULSE0_CONTROL_0,     // 40B
++      DC_DISP_H_PULSE0_POSITION_A_0,  // 40C
++      DC_DISP_H_PULSE0_POSITION_B_0,  // 40D
++      DC_DISP_H_PULSE0_POSITION_C_0,  // 40E
++      DC_DISP_H_PULSE0_POSITION_D_0,  // 40F
++      DC_DISP_H_PULSE1_CONTROL_0,     // 410
++      DC_DISP_H_PULSE1_POSITION_A_0,  // 411
++      DC_DISP_H_PULSE1_POSITION_B_0,  // 412
++      DC_DISP_H_PULSE1_POSITION_C_0,  // 413
++      DC_DISP_H_PULSE1_POSITION_D_0,  // 414
++      DC_DISP_H_PULSE2_CONTROL_0,     // 415
++      DC_DISP_H_PULSE2_POSITION_A_0,  // 416
++      DC_DISP_H_PULSE2_POSITION_B_0,  // 417
++      DC_DISP_H_PULSE2_POSITION_C_0,  // 418
++      DC_DISP_H_PULSE2_POSITION_D_0,  // 419
++      DC_DISP_V_PULSE0_CONTROL_0,     // 41A
++      DC_DISP_V_PULSE0_POSITION_A_0,  // 41B
++      DC_DISP_V_PULSE0_POSITION_B_0,  // 41C
++      DC_DISP_V_PULSE0_POSITION_C_0,  // 41D
++      DC_DISP_V_PULSE1_CONTROL_0,     // 41E
++      DC_DISP_V_PULSE1_POSITION_A_0,  // 41F
++      DC_DISP_V_PULSE1_POSITION_B_0,  // 420
++      DC_DISP_V_PULSE1_POSITION_C_0,  // 421
++      DC_DISP_V_PULSE2_CONTROL_0,     // 422
++      DC_DISP_V_PULSE2_POSITION_A_0,  // 423
++      DC_DISP_V_PULSE3_CONTROL_0,     // 424
++      DC_DISP_V_PULSE3_POSITION_A_0,  // 425
++      DC_DISP_M0_CONTROL_0,           // 426
++      DC_DISP_M1_CONTROL_0,           // 427
++      DC_DISP_DI_CONTROL_0,           // 428
++      DC_DISP_PP_CONTROL_0,           // 429
++      DC_DISP_PP_SELECT_A_0,          // 42A
++      DC_DISP_PP_SELECT_B_0,          // 42B
++      DC_DISP_PP_SELECT_C_0,          // 42C
++      DC_DISP_PP_SELECT_D_0,          // 42D
++      DC_DISP_DISP_CLOCK_CONTROL_0,   // 42E
++      DC_DISP_DISP_INTERFACE_CONTROL_0,//42F
++      DC_DISP_DISP_COLOR_CONTROL_0,   // 430
++      DC_DISP_SHIFT_CLOCK_OPTIONS_0,  // 431
++      DC_DISP_DATA_ENABLE_OPTIONS_0,  // 432
++      DC_DISP_SERIAL_INTERFACE_OPTIONS_0,     // 433
++      DC_DISP_LCD_SPI_OPTIONS_0,      // 434
++      DC_DISP_BORDER_COLOR_0,         // 435
++      DC_DISP_COLOR_KEY0_LOWER_0,     // 436
++      DC_DISP_COLOR_KEY0_UPPER_0,     // 437
++      DC_DISP_COLOR_KEY1_LOWER_0,     // 438
++      DC_DISP_COLOR_KEY1_UPPER_0,     // 439
++      _DC_DISP_UNUSED_43A,
++      _DC_DISP_UNUSED_43B,
++      DC_DISP_CURSOR_FOREGROUND_0,    // 43C - IMPORTANT
++      DC_DISP_CURSOR_BACKGROUND_0,    // 43D - IMPORTANT
++      DC_DISP_CURSOR_START_ADDR_0,    // 43E - IMPORTANT
++      DC_DISP_CURSOR_START_ADDR_NS_0, // 43F - IMPORTANT
++      DC_DISP_CURSOR_POSITION_0,      // 440 - IMPORTANT
++      DC_DISP_CURSOR_POSITION_NS_0,   // 441 - IMPORTANT
++      DC_DISP_INIT_SEQ_CONTROL_0,     // 442
++      DC_DISP_SPI_INIT_SEQ_DATA_A_0,  // 443
++      DC_DISP_SPI_INIT_SEQ_DATA_B_0,  // 444
++      DC_DISP_SPI_INIT_SEQ_DATA_C_0,  // 445
++      DC_DISP_SPI_INIT_SEQ_DATA_D_0,  // 446
++
++      DC_DISP_DC_MCCIF_FIFOCTRL_0 = 0x480,
++      DC_DISP_MCCIF_DISPLAY0A_HYST_0, // 481
++      DC_DISP_MCCIF_DISPLAY0B_HYST_0, // 482
++      DC_DISP_MCCIF_DISPLAY0C_HYST_0, // 483
++      DC_DISP_MCCIF_DISPLAY1B_HYST_0, // 484
++
++      DC_DISP_DAC_CRT_CTRL_0 = 0x4C0,
++      DC_DISP_DISP_MISC_CONTROL_0,    // 4C1
+       DC_WINC_A_COLOR_PALETTE_0 = 0x500,
+       DC_WINC_A_PALETTE_COLOR_EXT_0 = 0x600,
+       DC_WIN_A_WIN_OPTIONS_0 = 0x700,
+       DC_WIN_A_BYTE_SWAP_0,           // 701
+       DC_WIN_A_BUFFER_CONTROL_0,      // 702
+       DC_WIN_A_COLOR_DEPTH_0,         // 703
+       DC_WIN_A_POSITION_0,            // 704
+       DC_WIN_A_SIZE_0,                // 705 (TrimSlice 0x0300 0400)
+       DC_WIN_A_PRESCALED_SIZE_0,
+       DC_WIN_A_H_INITIAL_DDA_0,
+       DC_WIN_A_V_INITIAL_DDA_0,
+       DC_WIN_A_DDA_INCREMENT_0,
+       DC_WIN_A_LINE_STRIDE_0,
+       DC_WIN_A_BUF_STRIDE_0,
+       DC_WIN_A_BUFFER_ADDR_MODE_0,
+       DC_WIN_A_DV_CONTROL_0,
+       DC_WIN_A_BLEND_NOKEY_0,
+       
+       DC_WINBUF_A_START_ADDR_0 = 0x800,
+       DC_WINBUF_A_START_ADDR_NS_0,
+       DC_WINBUF_A_ADDR_H_OFFSET_0,
+       DC_WINBUF_A_ADDR_H_OFFSET_NS_0,
+       DC_WINBUF_A_ADDR_V_OFFSET_0,
+       DC_WINBUF_A_ADDR_V_OFFSET_NS_0,
+ };
+ #endif
index 0000000,839c0bd..a8dd074
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,129 +1,129 @@@
 -      PL050_WriteMouseData(0xD4);
 -      PL050_WriteMouseData(0xF4);     // Enable Packets
+ /*
+  * Acess2
+  * - By thePowersGang (John Hodge)
+  *
+  * PL050 (or comaptible) Driver
+  */
+ #define DEBUG 1
+ #include <acess.h>
+ #include "common.h"
+ // === CONSTANTS ===
+ #define PL050_TXBUSY  0x20
+ // === PROTOTYPES ===
+ void  PL050_Init(Uint32 KeyboardBase, Uint8 KeyboardIRQ, Uint32 MouseBase, Uint8 MouseIRQ);
+ void  PL050_KeyboardHandler(int IRQ, void *Ptr);
+ void  PL050_MouseHandler(int IRQ, void *Ptr);
+ void  PL050_EnableMouse(void);
+ static inline void    PL050_WriteMouseData(Uint8 data);
+ static inline void    PL050_WriteKeyboardData(Uint8 data);
+ static inline Uint8   PL050_ReadMouseData(void);
+ static inline Uint8   PL050_ReadKeyboardData(void);
+ // === GLOBALS ===
+ Uint32        *gpPL050_KeyboardBase;
+ Uint32        *gpPL050_MouseBase;
+ // === CODE ===
+ void PL050_Init(Uint32 KeyboardBase, Uint8 KeyboardIRQ, Uint32 MouseBase, Uint8 MouseIRQ)
+ {
+       if( KeyboardBase ) {
+               LOG("KeyboardBase = 0x%x", KeyboardBase);
+               gpPL050_KeyboardBase = (void*)MM_MapHWPages(KeyboardBase, 1);
+               LOG("gpPL050_KeyboardBase = %p", gpPL050_KeyboardBase);
+               IRQ_AddHandler(KeyboardIRQ, PL050_KeyboardHandler, NULL);
+               gpPL050_KeyboardBase[0] = 0x10;
+       }
+       if( MouseBase ) {
+               gpPL050_MouseBase = (void*)MM_MapHWPages(MouseBase, 1);
+               IRQ_AddHandler(MouseIRQ, PL050_MouseHandler, NULL);
+               gpPL050_MouseBase[0] = 0x10;
+       }
+ }
+ void PL050_KeyboardHandler(int IRQ, void *Ptr)
+ {
+       Uint8   scancode;
+       scancode = PL050_ReadKeyboardData();
+       KB_HandleScancode( scancode );
+ }
+ void PL050_MouseHandler(int IRQ, void *Ptr)
+ {
+       PS2Mouse_HandleInterrupt( PL050_ReadMouseData() );
+ }
+ void PL050_SetLEDs(Uint8 leds)
+ {
+       PL050_WriteKeyboardData(0xED);
+       PL050_WriteKeyboardData(leds);
+ }
+ void PL050_EnableMouse(void)
+ {
+       Log_Log("PL050", "Enabling Mouse...");
+       
+       //PL050_WriteMouseData(0xD4);
+       //PL050_WriteMouseData(0xF6);   // Set Default Settings
++//    PL050_WriteMouseData(0xD4);
++//    PL050_WriteMouseData(0xF4);     // Enable Packets
+       LOG("Done");
+ }
+ static inline void PL050_WriteMouseData(Uint8 Data)
+ {
+        int    timeout = 10000;
+       if( !gpPL050_MouseBase ) {
+               Log_Error("PL050", "Mouse disabled (gpPL050_MouseBase = NULL)");
+               return ;
+       }
+       ENTER("xData", Data);
+       while( --timeout && (gpPL050_MouseBase[1] & PL050_TXBUSY) );
+       if(timeout)
+               gpPL050_MouseBase[2] = Data;
+       else
+               Log_Error("PL050", "Write to mouse timed out");
+       LEAVE('-');
+ }
+ static inline Uint8 PL050_ReadMouseData(void)
+ {
+       if( !gpPL050_MouseBase ) {
+               Log_Error("PL050", "Mouse disabled (gpPL050_MouseBase = NULL)");
+               return 0;
+       }
+       return gpPL050_MouseBase[2];
+ }
+ static inline void PL050_WriteKeyboardData(Uint8 Data)
+ {
+        int    timeout = 10000;
+       if( !gpPL050_KeyboardBase ) {
+               Log_Error("PL050", "Keyboard disabled (gpPL050_KeyboardBase = NULL)");
+               return ;
+       }
+       while( --timeout && gpPL050_KeyboardBase[1] & PL050_TXBUSY );
+       if(timeout)
+               gpPL050_KeyboardBase[2] = Data;
+       else
+               Log_Error("PL050", "Write to keyboard timed out");
+ }
+ static inline Uint8 PL050_ReadKeyboardData(void)
+ {
+       if( !gpPL050_KeyboardBase ) {
+               Log_Error("PL050", "Keyboard disabled (gpPL050_KeyboardBase = NULL)");
+               return 0;
+       }
+       return gpPL050_KeyboardBase[2];
+ }

UCC git Repository :: git.ucc.asn.au