Fixed VM8086/Vesa memory allocation bug
[tpg/acess2.git] / Kernel / arch / x86 / vm8086.c
1 /*
2  * Acess2 VM8086 Driver
3  * - By John Hodge (thePowersGang)
4  */
5 #include <acess.h>
6 #include <vm8086.h>
7 #include <modules.h>
8
9 // === CONSTANTS ===
10 #define VM8086_MAGIC_CS 0xFFFF
11 #define VM8086_MAGIC_IP 0x0010
12 #define VM8086_STACK_SEG        0x9F00
13 #define VM8086_STACK_OFS        0x0AFE
14 enum eVM8086_Opcodes
15 {
16         VM8086_OP_PUSHF = 0x9C,
17         VM8086_OP_POPF  = 0x9D,
18         VM8086_OP_INT_I = 0xCD,
19         VM8086_OP_IRET  = 0xCF,
20         VM8086_OP_IN_AD = 0xEC,
21         VM8086_OP_IN_ADX= 0xED
22 };
23 #define VM8086_PAGES_PER_INST   4
24
25 // === IMPORTS ===
26  int    Proc_Clone(Uint *Err, Uint Flags);
27
28 // === TYPES ===
29 struct sVM8086_InternalData
30 {
31         struct {
32                 Uint32  Bitmap; // 32 sections = 128 byte blocks
33                 tVAddr  VirtBase;
34                 tPAddr  PhysAddr;
35         }       AllocatedPages[VM8086_PAGES_PER_INST];
36 };
37
38 // === PROTOTYPES ===
39  int    VM8086_Install(char **Arguments);
40 void    VM8086_GPF(tRegs *Regs);
41 tVM8086 *VM8086_Init(void);
42
43 // === GLOBALS ===
44 MODULE_DEFINE(0, 0x100, VM8086, VM8086_Install, NULL, NULL);
45 tSpinlock       glVM8086_Process;
46 tPID    gVM8086_WorkerPID;
47 tTID    gVM8086_CallingThread;
48 tVM8086 * volatile gpVM8086_State;
49
50 // === FUNCTIONS ===
51 int VM8086_Install(char **Arguments)
52 {
53         tPID    pid;    
54         
55         // Create BIOS Call process
56         pid = Proc_Clone(NULL, CLONE_VM);
57         if(pid == -1)
58         {
59                 Log_Error("VM8086", "Unable to clone kernel into VM8086 worker");
60                 return MODULE_ERR_MISC;
61         }
62         if(pid == 0)
63         {
64                 Uint    *stacksetup;    // Initialising Stack
65                 Uint16  *rmstack;       // Real Mode Stack
66                  int    i;
67                  
68                 // Set Image Name
69                 Threads_SetName("VM8086");
70                 
71                 // Map ROM Area
72                 for(i=0xA0;i<0x100;i++) {
73                         MM_Map( i * 0x1000, i * 0x1000 );
74                         //MM_SetFlags( i * 0x1000, MM_PFLAG_RO, MM_PFLAG_RO );  // Set Read Only
75                 }
76                 MM_Map( 0, 0 ); // IVT / BDA
77                 for(i=0x70;i<0x80;i++) {
78                         MM_Map( i * 0x1000, i * 0x1000 );       MM_DerefPhys( i * 0x1000 );
79                 }
80                 MM_Map( 0x9F000, 0x9F000 );     // Stack / EBDA
81                 MM_Allocate( 0x100000 );        // System Stack / Stub
82                 
83                 *(Uint8*)(0x100000) = VM8086_OP_IRET;
84                 *(Uint8*)(0x100001) = 0x07;     // POP ES
85                 *(Uint8*)(0x100002) = 0x1F;     // POP DS
86                 *(Uint8*)(0x100003) = 0xCB;     // RET FAR
87                 
88                 rmstack = (Uint16*)(VM8086_STACK_SEG*16 + VM8086_STACK_OFS);
89                 *rmstack-- = 0xFFFF;    //CS
90                 *rmstack-- = 0x0010;    //IP
91                 
92                 // Setup Stack
93                 stacksetup = (Uint*)0x101000;
94                 *--stacksetup = VM8086_STACK_SEG;       // GS
95                 *--stacksetup = VM8086_STACK_SEG;       // FS
96                 *--stacksetup = VM8086_STACK_SEG;       // DS
97                 *--stacksetup = VM8086_STACK_SEG;       // ES
98                 *--stacksetup = VM8086_STACK_SEG;       // SS
99                 *--stacksetup = VM8086_STACK_OFS-2;     // SP
100                 *--stacksetup = 0x20202;        // FLAGS
101                 *--stacksetup = 0xFFFF; // CS
102                 *--stacksetup = 0x10;   // IP
103                 *--stacksetup = 0xAAAA; // AX
104                 *--stacksetup = 0xCCCC; // CX
105                 *--stacksetup = 0xDDDD; // DX
106                 *--stacksetup = 0xBBBB; // BX
107                 *--stacksetup = 0x5454; // SP
108                 *--stacksetup = 0xB4B4; // BP
109                 *--stacksetup = 0x5151; // SI
110                 *--stacksetup = 0xD1D1; // DI
111                 *--stacksetup = 0x20|3; // DS - Kernel
112                 *--stacksetup = 0x20|3; // ES - Kernel
113                 *--stacksetup = 0x20|3; // FS
114                 *--stacksetup = 0x20|3; // GS
115                 __asm__ __volatile__ (
116                 "mov %%eax,%%esp;\n\t"  // Set stack pointer
117                 "pop %%gs;\n\t"
118                 "pop %%fs;\n\t"
119                 "pop %%es;\n\t"
120                 "pop %%ds;\n\t"
121                 "popa;\n\t"
122                 "iret;\n\t" : : "a" (stacksetup));
123                 for(;;);        // Shouldn't be reached
124         }
125         
126         gVM8086_WorkerPID = pid;
127         Log_Log("VM8086", "gVM8086_WorkerPID = %i", pid);
128         Threads_Yield();        // Yield to allow the child to initialise
129         
130         return MODULE_ERR_OK;
131 }
132
133 void VM8086_GPF(tRegs *Regs)
134 {
135         Uint8   opcode;
136         
137         //Log_Log("VM8086", "GPF - %04x:%04x", Regs->cs, Regs->eip);
138         
139         if(Regs->eip == VM8086_MAGIC_IP && Regs->cs == VM8086_MAGIC_CS
140         && Threads_GetPID() == gVM8086_WorkerPID)
141         {
142                 if( gpVM8086_State ) {
143                         gpVM8086_State->AX = Regs->eax; gpVM8086_State->CX = Regs->ecx;
144                         gpVM8086_State->DX = Regs->edx; gpVM8086_State->BX = Regs->ebx;
145                         gpVM8086_State->BP = Regs->ebp;
146                         gpVM8086_State->SI = Regs->esi; gpVM8086_State->DI = Regs->edi;
147                         gpVM8086_State->DS = Regs->ds;  gpVM8086_State->ES = Regs->es;
148                         gpVM8086_State = NULL;
149                         Threads_WakeTID(gVM8086_CallingThread);
150                 }
151                 
152                 //Log_Log("VM8086", "Waiting for something to do");
153                 __asm__ __volatile__ ("sti");
154                 // Wait for a new task
155                 while(!gpVM8086_State) {
156                         Threads_Sleep();
157                         //Log_Log("VM8086", "gpVM8086_State = %p", gpVM8086_State);
158                 }
159                 
160                 //Log_Log("VM8086", "We have a task (%p)", gpVM8086_State);
161                 Regs->esp -= 2; *(Uint16*)( (Regs->ss<<4) + (Regs->esp&0xFFFF) ) = VM8086_MAGIC_CS;
162                 Regs->esp -= 2; *(Uint16*)( (Regs->ss<<4) + (Regs->esp&0xFFFF) ) = VM8086_MAGIC_IP;
163                 Regs->esp -= 2; *(Uint16*)( (Regs->ss<<4) + (Regs->esp&0xFFFF) ) = gpVM8086_State->CS;
164                 Regs->esp -= 2; *(Uint16*)( (Regs->ss<<4) + (Regs->esp&0xFFFF) ) = gpVM8086_State->IP;
165                 Regs->esp -= 2; *(Uint16*)( (Regs->ss<<4) + (Regs->esp&0xFFFF) ) = gpVM8086_State->DS;
166                 Regs->esp -= 2; *(Uint16*)( (Regs->ss<<4) + (Regs->esp&0xFFFF) ) = gpVM8086_State->ES;
167                 
168                 // Set Registers
169                 Regs->eip = 0x11;       Regs->cs = 0xFFFF;
170                 Regs->eax = gpVM8086_State->AX; Regs->ecx = gpVM8086_State->CX;
171                 Regs->edx = gpVM8086_State->DX; Regs->ebx = gpVM8086_State->BX;
172                 Regs->esi = gpVM8086_State->SI; Regs->edi = gpVM8086_State->DI;
173                 Regs->ebp = gpVM8086_State->BP;
174                 Regs->ds = 0x23;        Regs->es = 0x23;
175                 Regs->fs = 0x23;        Regs->gs = 0x23;
176                 return ;
177         }
178         
179         opcode = *(Uint8*)( KERNEL_BASE + (Regs->cs*16) + (Regs->eip) );
180         Regs->eip ++;
181         switch(opcode)
182         {
183         case VM8086_OP_PUSHF:   //PUSHF
184                 Regs->esp -= 2;
185                 *(Uint16*)( Regs->ss*16 + (Regs->esp&0xFFFF) ) = Regs->eflags & 0xFFFF;
186                 #if TRACE_EMU
187                 Log_Debug("VM8086", "Emulated PUSHF");
188                 #endif
189                 break;
190         case VM8086_OP_POPF:    //POPF
191                 Regs->eflags &= 0xFFFF0002;
192                 Regs->eflags |= *(Uint16*)( Regs->ss*16 + (Regs->esp&0xFFFF) ) & 0xFFFD;        // Changing IF is not allowed
193                 Regs->esp += 2;
194                 #if TRACE_EMU
195                 Log_Debug("VM8086", "Emulated POPF");
196                 #endif
197                 break;
198         
199         case VM8086_OP_INT_I:   //INT imm8
200                 {
201                  int    id;
202                 id = *(Uint8*)( Regs->cs*16 +(Regs->eip&0xFFFF));
203                 Regs->eip ++;
204                 
205                 Regs->esp -= 2; *(Uint16*)( Regs->ss*16 + (Regs->esp&0xFFFF) ) = Regs->cs;
206                 Regs->esp -= 2; *(Uint16*)( Regs->ss*16 + (Regs->esp&0xFFFF) ) = Regs->eip;
207                 
208                 Regs->cs = *(Uint16*)(4*id + 2);
209                 Regs->eip = *(Uint16*)(4*id);
210                 #if TRACE_EMU
211                 Log_Debug("VM8086", "Emulated INT 0x%x", id);
212                 #endif
213                 }
214                 break;
215         
216         case VM8086_OP_IRET:    //IRET
217                 Regs->eip = *(Uint16*)( Regs->ss*16 + (Regs->esp&0xFFFF) );     Regs->esp += 2;
218                 Regs->cs  = *(Uint16*)( Regs->ss*16 + (Regs->esp&0xFFFF) );     Regs->esp += 2;
219                 #if TRACE_EMU
220                 Log_Debug("VM8086", "IRET to %04x:%04x", Regs->cs, Regs->eip);
221                 #endif
222                 break;
223         
224         
225         case VM8086_OP_IN_AD:   //IN AL, DX
226                 Regs->eax &= 0xFFFFFF00;
227                 Regs->eax |= inb(Regs->edx&0xFFFF);
228                 #if TRACE_EMU
229                 Log_Debug("VM8086", "Emulated IN AL, DX (Port 0x%x)\n", Regs->edx&0xFFFF);
230                 #endif
231                 break;
232         case VM8086_OP_IN_ADX:  //IN AX, DX
233                 Regs->eax &= 0xFFFF0000;
234                 Regs->eax |= inw(Regs->edx&0xFFFF);
235                 #if TRACE_EMU
236                 Log_Debug("VM8086", "Emulated IN AX, DX (Port 0x%x)\n", Regs->edx&0xFFFF);
237                 #endif
238                 break;
239                 
240         case 0xEE:      //OUT DX, AL
241                 outb(Regs->edx&0xFFFF, Regs->eax&0xFF);
242                 #if TRACE_EMU
243                 Log_Debug("VM8086", "Emulated OUT DX, AL (*0x%04x = 0x%02x)\n", Regs->edx&0xFFFF, Regs->eax&0xFF);
244                 #endif
245                 break;
246         case 0xEF:      //OUT DX, AX
247                 outw(Regs->edx&0xFFFF, Regs->eax&0xFFFF);
248                 #if TRACE_EMU
249                 Log_Debug("VM8086", "Emulated OUT DX, AX (*0x%04x = 0x%04x)\n", Regs->edx&0xFFFF, Regs->eax&0xFFFF);
250                 #endif
251                 break;
252                 
253         // TODO: Decide on allowing VM8086 Apps to enable/disable interrupts
254         case 0xFA:      //CLI
255                 break;
256         case 0xFB:      //STI
257                 break;
258         
259         case 0x66:
260                 Log_Warning("VM8086", "Code at %04x:%04x attempted to use an operand override, ignored",
261                         Regs->cs, Regs->eip);
262                 break;
263         
264         default:
265                 Log_Error("VM8086", "Error - Unknown opcode %02x caused a GPF at %04x:%04x",
266                         opcode, Regs->cs, Regs->eip);
267                 // Force an end to the call
268                 Regs->cs = VM8086_MAGIC_CS;
269                 Regs->eip = VM8086_MAGIC_IP;
270                 break;
271         }
272 }
273
274 /**
275  * \brief Create an instance of the VM8086 Emulator
276  */
277 tVM8086 *VM8086_Init(void)
278 {
279         tVM8086 *ret;
280         ret = calloc( 1, sizeof(tVM8086) + sizeof(struct sVM8086_InternalData) );
281         ret->Internal = (void*)((tVAddr)ret + sizeof(tVM8086));
282         return ret;
283 }
284
285 void VM8086_Free(tVM8086 *State)
286 {
287          int    i;
288         for( i = VM8086_PAGES_PER_INST; i --; )
289                 MM_UnmapHWPages( State->Internal->AllocatedPages[i].VirtBase, 1);
290         free(State);
291 }
292
293 void *VM8086_Allocate(tVM8086 *State, int Size, Uint16 *Segment, Uint16 *Offset)
294 {
295          int    i, j, base = 0;
296          int    nBlocks, rem;
297         
298         Size = (Size + 127) & ~127;
299         nBlocks = Size / 128;
300         
301         if(Size > 4096) return NULL;
302         
303         for( i = 0; i < VM8086_PAGES_PER_INST; i++ )
304         {
305                 if( State->Internal->AllocatedPages[i].VirtBase == 0 )  continue;
306                 
307                 
308                 //Log_Debug("VM8086", "AllocatedPages[%i].Bitmap = 0b%b", i, State->Internal->AllocatedPages[i].Bitmap);
309                 
310                 rem = nBlocks;
311                 base = 0;
312                 // Scan the bitmap for a free block
313                 for( j = 0; j < 32; j++ ) {
314                         if( State->Internal->AllocatedPages[i].Bitmap & (1 << j) ) {
315                                 base = j;
316                                 rem = nBlocks;
317                         }
318                         else {
319                                 rem --;
320                                 if(rem == 0)    // Goodie, there's a gap
321                                 {
322                                         for( j = 0; j < nBlocks; j++ )
323                                                 State->Internal->AllocatedPages[i].Bitmap |= 1 << (base + j);
324                                         *Segment = State->Internal->AllocatedPages[i].PhysAddr / 16 + base * 8;
325                                         *Offset = 0;
326                                         //Log_Debug("VM8086", "Allocated at #%i,%04x", i, base*128);
327                                         return (void*)( State->Internal->AllocatedPages[i].VirtBase + base * 128 );
328                                 }
329                         }
330                 }
331         }
332         
333         // No pages with free space?, allocate a new one
334         for( i = 0; i < VM8086_PAGES_PER_INST; i++ )
335         {
336                 if( State->Internal->AllocatedPages[i].VirtBase == 0 )  break;
337         }
338         // Darn, we can't allocate any more
339         if( i == VM8086_PAGES_PER_INST ) {
340                 Log_Warning("VM8086", "Out of pages in %p", State);
341                 return NULL;
342         }
343         
344         State->Internal->AllocatedPages[i].VirtBase = MM_AllocDMA(
345                 1, 20, &State->Internal->AllocatedPages[i].PhysAddr);
346         State->Internal->AllocatedPages[i].Bitmap = 0;
347                 
348         for( j = 0; j < nBlocks; j++ )
349                 State->Internal->AllocatedPages[i].Bitmap |= 1 << j;
350         //Log_Debug("VM8086", "AllocatedPages[%i].Bitmap = 0b%b", i, State->Internal->AllocatedPages[i].Bitmap);
351         *Segment = State->Internal->AllocatedPages[i].PhysAddr / 16;
352         *Offset = 0;
353         return (void*) State->Internal->AllocatedPages[i].VirtBase;
354 }
355
356 void *VM8086_GetPointer(tVM8086 *State, Uint16 Segment, Uint16 Offset)
357 {
358         return (void*)( KERNEL_BASE + Segment*16 + Offset );
359 }
360
361 void VM8086_Int(tVM8086 *State, Uint8 Interrupt)
362 {
363         State->IP = *(Uint16*)(KERNEL_BASE+4*Interrupt);
364         State->CS = *(Uint16*)(KERNEL_BASE+4*Interrupt+2);
365         
366         LOCK( &glVM8086_Process );
367         
368         gpVM8086_State = State;
369         gVM8086_CallingThread = Threads_GetTID();
370         Threads_WakeTID( gVM8086_WorkerPID );
371         while( gpVM8086_State != NULL )
372                 Threads_Sleep();
373         
374         RELEASE( &glVM8086_Process );
375 }

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