--- /dev/null
-#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(;;);
+ }
+
--- /dev/null
- 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"
--- /dev/null
- 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
+
--- /dev/null
-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);
+ }
--- /dev/null
- \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
--- /dev/null
- {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
+
--- /dev/null
- 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];
+ }
+