Kernel - Fixed page fault Proc_ReturnToUser
[tpg/acess2.git] / Kernel / arch / x86_64 / mm_virt.c
1 /*
2  * Acess2 x86_64 Port
3  * 
4  * Virtual Memory Manager
5  */
6 #define DEBUG   0
7 #include <acess.h>
8 #include <mm_virt.h>
9 #include <proc.h>
10
11 // === CONSTANTS ===
12 #define PML4_SHIFT      39
13 #define PDP_SHIFT       30
14 #define PDIR_SHIFT      21
15 #define PTAB_SHIFT      12
16
17 #define PADDR_MASK      0x7FFFFFFF##FFFFF000
18 #define PAGE_MASK       (((Uint)1 << 36)-1)
19 #define TABLE_MASK      (((Uint)1 << 27)-1)
20 #define PDP_MASK        (((Uint)1 << 18)-1)
21 #define PML4_MASK       (((Uint)1 << 9)-1)
22
23 #define PF_PRESENT      0x1
24 #define PF_WRITE        0x2
25 #define PF_USER         0x4
26 #define PF_LARGE        0x0
27 #define PF_COW          0x200
28 #define PF_PAGED        0x400
29 #define PF_NX           0x80000000##00000000
30
31 // === MACROS ===
32 #define PAGETABLE(idx)  (*((tPAddr*)MM_FRACTAL_BASE+((idx)&PAGE_MASK)))
33 #define PAGEDIR(idx)    PAGETABLE((MM_FRACTAL_BASE>>12)+((idx)&TABLE_MASK))
34 #define PAGEDIRPTR(idx) PAGEDIR((MM_FRACTAL_BASE>>21)+((idx)&PDP_MASK))
35 #define PAGEMAPLVL4(idx)        PAGEDIRPTR((MM_FRACTAL_BASE>>30)+((idx)&PML4_MASK))
36
37 #define INVLPG(__addr)  __asm__ __volatile__ ("invlpg (%0)"::"r"(__addr));
38
39 // === PROTOTYPES ===
40 void    MM_InitVirt(void);
41 void    MM_FinishVirtualInit(void);
42 void    MM_PageFault(tVAddr Addr, Uint ErrorCode, tRegs *Regs);
43 void    MM_DumpTables(tVAddr Start, tVAddr End);
44  int    MM_Map(tVAddr VAddr, tPAddr PAddr);
45  int    MM_GetPageEntry(tVAddr Addr, tPAddr *Phys, Uint *Flags);
46
47 // === GLOBALS ===
48
49 // === CODE ===
50 void MM_InitVirt(void)
51 {
52         MM_DumpTables(0, -1L);
53 }
54
55 void MM_FinishVirtualInit(void)
56 {
57 }
58
59 /**
60  * \brief Called on a page fault
61  */
62 void MM_PageFault(tVAddr Addr, Uint ErrorCode, tRegs *Regs)
63 {
64         // TODO: Copy on Write
65         #if 0
66         if( gaPageDir  [Addr>>22] & PF_PRESENT
67          && gaPageTable[Addr>>12] & PF_PRESENT
68          && gaPageTable[Addr>>12] & PF_COW )
69         {
70                 tPAddr  paddr;
71                 if(MM_GetRefCount( gaPageTable[Addr>>12] & ~0xFFF ) == 1)
72                 {
73                         gaPageTable[Addr>>12] &= ~PF_COW;
74                         gaPageTable[Addr>>12] |= PF_PRESENT|PF_WRITE;
75                 }
76                 else
77                 {
78                         //Log("MM_PageFault: COW - MM_DuplicatePage(0x%x)", Addr);
79                         paddr = MM_DuplicatePage( Addr );
80                         MM_DerefPhys( gaPageTable[Addr>>12] & ~0xFFF );
81                         gaPageTable[Addr>>12] &= PF_USER;
82                         gaPageTable[Addr>>12] |= paddr|PF_PRESENT|PF_WRITE;
83                 }
84                 
85                 INVLPG( Addr & ~0xFFF );
86                 return;
87         }
88         #endif
89         
90         // If it was a user, tell the thread handler
91         if(ErrorCode & 4) {
92                 Warning("%s %s %s memory%s",
93                         (ErrorCode&4?"User":"Kernel"),
94                         (ErrorCode&2?"write to":"read from"),
95                         (ErrorCode&1?"bad/locked":"non-present"),
96                         (ErrorCode&16?" (Instruction Fetch)":"")
97                         );
98                 Warning("User Pagefault: Instruction at %04x:%08x accessed %p",
99                         Regs->CS, Regs->RIP, Addr);
100                 __asm__ __volatile__ ("sti");   // Restart IRQs
101 //              Threads_SegFault(Addr);
102                 return ;
103         }
104         
105         // Kernel #PF
106         Debug_KernelPanic();
107         // -- Check Error Code --
108         if(ErrorCode & 8)
109                 Warning("Reserved Bits Trashed!");
110         else
111         {
112                 Warning("%s %s %s memory%s",
113                         (ErrorCode&4?"User":"Kernel"),
114                         (ErrorCode&2?"write to":"read from"),
115                         (ErrorCode&1?"bad/locked":"non-present"),
116                         (ErrorCode&16?" (Instruction Fetch)":"")
117                         );
118         }
119         
120         Log("Code at %p accessed %p", Regs->RIP, Addr);
121         // Print Stack Backtrace
122 //      Error_Backtrace(Regs->RIP, Regs->RBP);
123         
124         MM_DumpTables(0, -1);
125         
126         __asm__ __volatile__ ("cli");
127         for( ;; )
128                 HALT();
129 }
130
131 /**
132  * \brief Dumps the layout of the page tables
133  */
134 void MM_DumpTables(tVAddr Start, tVAddr End)
135 {
136         const tPAddr    CHANGEABLE_BITS = 0xFF8;
137         const tPAddr    MASK = ~CHANGEABLE_BITS;        // Physical address and access bits
138         tVAddr  rangeStart = 0;
139         tPAddr  expected = CHANGEABLE_BITS;     // MASK is used because it's not a vaild value
140         tVAddr  curPos;
141         Uint    page;
142         
143         Log("Table Entries: (%p to %p)", Start, End);
144         
145         End &= (1L << 48) - 1;
146         
147         Start >>= 12;   End >>= 12;
148         
149         for(page = Start, curPos = Start<<12;
150                 page < End;
151                 curPos += 0x1000, page++)
152         {
153                 if( curPos == 0x800000000000L )
154                         curPos = 0xFFFF800000000000L;
155                 
156                 //Debug("&PAGEMAPLVL4(%i page>>27) = %p", page>>27, &PAGEMAPLVL4(page>>27));
157                 //Debug("&PAGEDIRPTR(%i page>>18) = %p", page>>18, &PAGEDIRPTR(page>>18));
158                 //Debug("&PAGEDIR(%i page>>9) = %p", page>>9, &PAGEDIR(page>>9));
159                 //Debug("&PAGETABLE(%i page) = %p", page, &PAGETABLE(page));
160                 
161                 // End of a range
162                 if(
163                         !(PAGEMAPLVL4(page>>27) & PF_PRESENT)
164                 ||      !(PAGEDIRPTR(page>>18) & PF_PRESENT)
165                 ||      !(PAGEDIR(page>>9) & PF_PRESENT)
166                 ||  !(PAGETABLE(page) & PF_PRESENT)
167                 ||  (PAGETABLE(page) & MASK) != expected)
168                 {                       
169                         if(expected != CHANGEABLE_BITS) {
170                                 Log("%016x-0x%016x => %013x-%013x (%c%c%c%c)",
171                                         rangeStart, curPos - 1,
172                                         PAGETABLE(rangeStart>>12) & ~0xFFF,
173                                         (expected & ~0xFFF) - 1,
174                                         (expected & PF_PAGED ? 'p' : '-'),
175                                         (expected & PF_COW ? 'C' : '-'),
176                                         (expected & PF_USER ? 'U' : '-'),
177                                         (expected & PF_WRITE ? 'W' : '-')
178                                         );
179                                 expected = CHANGEABLE_BITS;
180                         }
181                         if( !(PAGEMAPLVL4(page>>27) & PF_PRESENT) ) {
182                                 page += (1 << 27) - 1;
183                                 curPos += (1L << 39) - 0x1000;
184                                 //Debug("pml4 ent unset (page = 0x%x now)", page);
185                                 continue;
186                         }
187                         if( !(PAGEDIRPTR(page>>18) & PF_PRESENT) ) {
188                                 page += (1 << 18) - 1;
189                                 curPos += (1L << 30) - 0x1000;
190                                 //Debug("pdp ent unset (page = 0x%x now)", page);
191                                 continue;
192                         }
193                         if( !(PAGEDIR(page>>9) & PF_PRESENT) ) {
194                                 page += (1 << 9) - 1;
195                                 curPos += (1L << 21) - 0x1000;
196                                 //Debug("pd ent unset (page = 0x%x now)", page);
197                                 continue;
198                         }
199                         if( !(PAGETABLE(page) & PF_PRESENT) )   continue;
200                         
201                         expected = (PAGETABLE(page) & MASK);
202                         rangeStart = curPos;
203                 }
204                 if(expected != CHANGEABLE_BITS)
205                         expected += 0x1000;
206         }
207         
208         if(expected != CHANGEABLE_BITS) {
209                 Log("%016x-%016x => %013x-%013x (%s%s%s%s)",
210                         rangeStart, curPos - 1,
211                         PAGETABLE(rangeStart>>12) & ~0xFFF,
212                         (expected & ~0xFFF) - 1,
213                         (expected & PF_PAGED ? "p" : "-"),
214                         (expected & PF_COW ? "C" : "-"),
215                         (expected & PF_USER ? "U" : "-"),
216                         (expected & PF_WRITE ? "W" : "-")
217                         );
218                 expected = 0;
219         }
220 }
221
222 /**
223  * \brief Map a physical page to a virtual one
224  */
225 int MM_Map(tVAddr VAddr, tPAddr PAddr)
226 {
227         tPAddr  tmp;
228         
229         ENTER("xVAddr xPAddr", VAddr, PAddr);
230         
231         // Check that the page hasn't been mapped already
232         {
233                 Uint    flags;
234                  int    ret;
235                 ret = MM_GetPageEntry(VAddr, &tmp, &flags);
236                 if( flags & PF_PRESENT ) {
237                         LEAVE('i', 0);
238                         return 0;
239                 }
240         }
241         
242         // Check PML4
243         if( !(PAGEMAPLVL4(VAddr >> 39) & 1) )
244         {
245                 tmp = MM_AllocPhys();
246                 if(!tmp) {
247                         LEAVE('i', 0);
248                         return 0;
249                 }
250                 PAGEMAPLVL4(VAddr >> 39) = tmp | 3;
251                 INVLPG( &PAGEDIRPTR( (VAddr>>39)<<9 ) );
252                 memset( &PAGEDIRPTR( (VAddr>>39)<<9 ), 0, 4096 );
253         }
254         
255         // Check PDP
256         if( !(PAGEDIRPTR(VAddr >> 30) & 1) )
257         {
258                 tmp = MM_AllocPhys();
259                 if(!tmp) {
260                         LEAVE('i', 0);
261                         return 0;
262                 }
263                 PAGEDIRPTR(VAddr >> 30) = tmp | 3;
264                 INVLPG( &PAGEDIR( (VAddr>>30)<<9 ) );
265                 memset( &PAGEDIR( (VAddr>>30)<<9 ), 0, 0x1000 );
266         }
267         
268         // Check Page Dir
269         if( !(PAGEDIR(VAddr >> 21) & 1) )
270         {
271                 tmp = MM_AllocPhys();
272                 if(!tmp) {
273                         LEAVE('i', 0);
274                         return 0;
275                 }
276                 PAGEDIR(VAddr >> 21) = tmp | 3;
277                 INVLPG( &PAGETABLE( (VAddr>>21)<<9 ) );
278                 memset( &PAGETABLE( (VAddr>>21)<<9 ), 0, 4096 );
279         }
280         
281         // Check if this virtual address is already mapped
282         if( PAGETABLE(VAddr >> PTAB_SHIFT) & 1 ) {
283                 LEAVE('i', 0);
284                 return 0;
285         }
286         
287         PAGETABLE(VAddr >> PTAB_SHIFT) = PAddr | 3;
288         
289         INVLPG( VAddr );
290
291         LEAVE('i', 1);  
292         return 1;
293 }
294
295 /**
296  * \brief Removed a mapped page
297  */
298 void MM_Unmap(tVAddr VAddr)
299 {
300         // Check PML4
301         if( !(PAGEMAPLVL4(VAddr >> 39) & 1) )   return ;
302         // Check PDP
303         if( !(PAGEDIRPTR(VAddr >> 30) & 1) )    return ;
304         // Check Page Dir
305         if( !(PAGEDIR(VAddr >> 21) & 1) )       return ;
306         
307         PAGETABLE(VAddr >> PTAB_SHIFT) = 0;
308         INVLPG( VAddr );
309 }
310
311 /**
312  * \brief Allocate a block of memory at the specified virtual address
313  */
314 tPAddr MM_Allocate(tVAddr VAddr)
315 {
316         tPAddr  ret;
317         
318         ENTER("xVAddr", VAddr);
319         
320         // NOTE: This is hack, but I like my dumps to be neat
321         #if 1
322         if( !MM_Map(VAddr, 0) ) // Make sure things are allocated
323         {
324                 Warning("MM_Allocate: Unable to map, tables did not initialise");
325                 LEAVE('i', 0);
326                 return 0;
327         }
328         MM_Unmap(VAddr);
329         #endif
330         
331         ret = MM_AllocPhys();
332         LOG("ret = %x", ret);
333         if(!ret) {
334                 LEAVE('i', 0);
335                 return 0;
336         }
337         
338         if( !MM_Map(VAddr, ret) )
339         {
340                 Warning("MM_Allocate: Unable to map. Strange, we should have errored earlier");
341                 MM_DerefPhys(ret);
342                 LEAVE('i');
343                 return 0;
344         }
345         
346         LEAVE('x', ret);
347         return ret;
348 }
349
350 void MM_Deallocate(tVAddr VAddr)
351 {
352         tPAddr  phys;
353         
354         phys = MM_GetPhysAddr(VAddr);
355         if(!phys)       return ;
356         
357         MM_Unmap(VAddr);
358         
359         MM_DerefPhys(phys);
360 }
361
362 /**
363  * \brief Get the page table entry of a virtual address
364  * \param Addr  Virtual Address
365  * \param Phys  Location to put the physical address
366  * \param Flags Flags on the entry (set to zero if unmapped)
367  * \return Size of the entry (in address bits) - 12 = 4KiB page
368  */
369 int MM_GetPageEntry(tVAddr Addr, tPAddr *Phys, Uint *Flags)
370 {
371         if(!Phys || !Flags)     return 0;
372         
373         // Check if the PML4 entry is present
374         if( !(PAGEMAPLVL4(Addr >> 39) & 1) ) {
375                 *Phys = 0;
376                 *Flags = 0;
377                 return 39;
378         }
379         // - Check for large page
380         if( PAGEMAPLVL4(Addr >> 39) & PF_LARGE ) {
381                 *Phys = PAGEMAPLVL4(Addr >> 39) & ~0xFFF;
382                 *Flags = PAGEMAPLVL4(Addr >> 39) & 0xFFF;
383                 return 39;
384         }
385         
386         // Check the PDP entry
387         if( !(PAGEDIRPTR(Addr >> 30) & 1) ) {
388                 *Phys = 0;
389                 *Flags = 0;
390                 return 30;
391         }
392         // - Check for large page
393         if( PAGEDIRPTR(Addr >> 30) & PF_LARGE ) {
394                 *Phys = PAGEDIRPTR(Addr >> 30) & ~0xFFF;
395                 *Flags = PAGEDIRPTR(Addr >> 30) & 0xFFF;
396                 return 30;
397         }
398         
399         // Check PDIR Entry
400         if( !(PAGEDIR(Addr >> 21) & 1) ) {
401                 *Phys = 0;
402                 *Flags = 0;
403                 return 21;
404         }
405         // - Check for large page
406         if( PAGEDIR(Addr >> 21) & PF_LARGE ) {
407                 *Phys = PAGEDIR(Addr >> 21) & ~0xFFF;
408                 *Flags = PAGEDIR(Addr >> 21) & 0xFFF;
409                 return 21;
410         }
411         
412         // And, check the page table entry
413         if( !(PAGETABLE(Addr >> PTAB_SHIFT) & 1) ) {
414                 *Phys = 0;
415                 *Flags = 0;
416         }
417         else {
418                 *Phys = PAGETABLE(Addr >> PTAB_SHIFT) & ~0xFFF;
419                 *Flags = PAGETABLE(Addr >> PTAB_SHIFT) & 0xFFF;
420         }
421         return 12;
422 }
423
424 /**
425  * \brief Get the physical address of a virtual location
426  */
427 tPAddr MM_GetPhysAddr(tVAddr Addr)
428 {
429         tPAddr  ret;
430         Uint    flags;
431         
432         MM_GetPageEntry(Addr, &ret, &flags);
433         
434         return ret | (Addr & 0xFFF);
435 }
436
437 /**
438  * \brief Sets the flags on a page
439  */
440 void MM_SetFlags(tVAddr VAddr, Uint Flags, Uint Mask)
441 {
442         tPAddr  *ent;
443         
444         // Validity Check
445         if( !(PAGEMAPLVL4(VAddr >> 39) & 1) )
446                 return ;
447         if( !(PAGEDIRPTR(VAddr >> 30) & 1) )
448                 return ;
449         if( !(PAGEDIR(VAddr >> 21) & 1) )
450                 return ;
451         if( !(PAGETABLE(VAddr >> 12) & 1) )
452                 return ;
453         
454         // Set Flags
455         ent = &PAGETABLE(VAddr >> 12);
456         
457         // Read-Only
458         if( Mask & MM_PFLAG_RO )
459         {
460                 if( Flags & MM_PFLAG_RO ) {
461                         *ent &= ~PF_WRITE;
462                 }
463                 else {
464                         *ent |= PF_WRITE;
465                 }
466         }
467         
468         // Kernel
469         if( Mask & MM_PFLAG_KERNEL )
470         {
471                 if( Flags & MM_PFLAG_KERNEL ) {
472                         *ent &= ~PF_USER;
473                 }
474                 else {
475                         *ent |= PF_USER;
476                 }
477         }
478         
479         // Copy-On-Write
480         if( Mask & MM_PFLAG_COW )
481         {
482                 if( Flags & MM_PFLAG_COW ) {
483                         *ent &= ~PF_WRITE;
484                         *ent |= PF_COW;
485                 }
486                 else {
487                         *ent &= ~PF_COW;
488                         *ent |= PF_WRITE;
489                 }
490         }
491         
492         // Execute
493         if( Mask & MM_PFLAG_EXEC )
494         {
495                 if( Flags & MM_PFLAG_EXEC ) {
496                         *ent &= ~PF_NX;
497                 }
498                 else {
499                         *ent |= PF_NX;
500                 }
501         }
502 }
503
504 /**
505  * \brief Get the flags applied to a page
506  */
507 Uint MM_GetFlags(tVAddr VAddr)
508 {
509         tPAddr  *ent;
510         Uint    ret = 0;
511         
512         // Validity Check
513         if( !(PAGEMAPLVL4(VAddr >> 39) & 1) )
514                 return 0;
515         if( !(PAGEDIRPTR(VAddr >> 30) & 1) )
516                 return 0;
517         if( !(PAGEDIR(VAddr >> 21) & 1) )
518                 return 0;
519         if( !(PAGETABLE(VAddr >> 12) & 1) )
520                 return 0;
521         
522         // Set Flags
523         ent = &PAGETABLE(VAddr >> 12);
524         
525         // Read-Only
526         if( !(*ent & PF_WRITE) )        ret |= MM_PFLAG_RO;
527         // Kernel
528         if( !(*ent & PF_USER) ) ret |= MM_PFLAG_KERNEL;
529         // Copy-On-Write
530         if( *ent & PF_COW )     ret |= MM_PFLAG_COW;    
531         // Execute
532         if( !(*ent & PF_NX) )   ret |= MM_PFLAG_EXEC;
533         
534         return ret;
535 }
536
537 // --- Hardware Mappings ---
538 /**
539  * \brief Map a range of hardware pages
540  */
541 tVAddr MM_MapHWPages(tPAddr PAddr, Uint Number)
542 {
543         Log_KernelPanic("MM", "TODO: Implement MM_MapHWPages");
544         return 0;
545 }
546
547 /**
548  * \brief Free a range of hardware pages
549  */
550 void MM_UnmapHWPages(tVAddr VAddr, Uint Number)
551 {
552         Log_KernelPanic("MM", "TODO: Implement MM_UnmapHWPages");
553 }
554
555 // --- Tempory Mappings ---
556 tVAddr MM_MapTemp(tPAddr PAddr)
557 {
558         Log_KernelPanic("MM", "TODO: Implement MM_MapTemp");
559         return 0;
560 }
561
562 void MM_FreeTemp(tVAddr VAddr)
563 {
564         Log_KernelPanic("MM", "TODO: Implement MM_FreeTemp");
565         return ;
566 }
567
568
569 // --- Address Space Clone --
570 tPAddr MM_Clone(void)
571 {
572         tPAddr  ret;
573         
574         // #1 Create a copy of the PML4
575         ret = MM_AllocPhys();
576         if(!ret)        return 0;
577         
578         Log_KernelPanic("MM", "TODO: Implement MM_Clone");
579         
580         // #2 Alter the fractal pointer
581         // #3 Set Copy-On-Write to all user pages
582         // #4 Return
583         return 0;
584 }
585
586 void MM_ClearUser(void)
587 {
588         tVAddr  addr = 0;
589         // #1 Traverse the structure < 2^47, Deref'ing all pages
590         // #2 Free tables/dirs/pdps once they have been cleared
591         
592         for( addr = 0; addr < 0x800000000000; )
593         {
594                 if( PAGEMAPLVL4(addr >> PML4_SHIFT) & 1 )
595                 {
596                         if( PAGEDIRPTR(addr >> PDP_SHIFT) & 1 )
597                         {
598                                 if( PAGEDIR(addr >> PDIR_SHIFT) & 1 )
599                                 {
600                                         // Page
601                                         if( PAGETABLE(addr >> PTAB_SHIFT) & 1 ) {
602                                                 MM_DerefPhys( PAGETABLE(addr >> PTAB_SHIFT) & PADDR_MASK );
603                                                 PAGETABLE(addr >> PTAB_SHIFT) = 0;
604                                         }
605                                         addr += 1 << PTAB_SHIFT;
606                                         // Dereference the PDIR Entry
607                                         if( (addr + (1 << PTAB_SHIFT)) >> PDIR_SHIFT != (addr >> PDIR_SHIFT) ) {
608                                                 MM_DerefPhys( PAGEMAPLVL4(addr >> PDIR_SHIFT) & PADDR_MASK );
609                                                 PAGEDIR(addr >> PDIR_SHIFT) = 0;
610                                         }
611                                 }
612                                 else {
613                                         addr += 1 << PDIR_SHIFT;
614                                         continue;
615                                 }
616                                 // Dereference the PDP Entry
617                                 if( (addr + (1 << PDIR_SHIFT)) >> PDP_SHIFT != (addr >> PDP_SHIFT) ) {
618                                         MM_DerefPhys( PAGEMAPLVL4(addr >> PDP_SHIFT) & PADDR_MASK );
619                                         PAGEDIRPTR(addr >> PDP_SHIFT) = 0;
620                                 }
621                         }
622                         else {
623                                 addr += 1 << PDP_SHIFT;
624                                 continue;
625                         }
626                         // Dereference the PML4 Entry
627                         if( (addr + (1 << PDP_SHIFT)) >> PML4_SHIFT != (addr >> PML4_SHIFT) ) {
628                                 MM_DerefPhys( PAGEMAPLVL4(addr >> PML4_SHIFT) & PADDR_MASK );
629                                 PAGEMAPLVL4(addr >> PML4_SHIFT) = 0;
630                         }
631                 }
632                 else {
633                         addr += (tVAddr)1 << PML4_SHIFT;
634                         continue;
635                 }
636         }
637 }
638
639 tVAddr MM_NewWorkerStack(void)
640 {
641         Log_KernelPanic("MM", "TODO: Implement MM_NewWorkerStack");
642         return 0;
643 }
644
645 /**
646  * \brief Allocate a new kernel stack
647  */
648 tVAddr MM_NewKStack(void)
649 {
650         tVAddr  base = MM_KSTACK_BASE;
651         Uint    i;
652         for( ; base < MM_KSTACK_TOP; base += KERNEL_STACK_SIZE )
653         {
654                 if(MM_GetPhysAddr(base) != 0)
655                         continue;
656                 
657                 //Log("MM_NewKStack: Found one at %p", base + KERNEL_STACK_SIZE);
658                 for( i = 0; i < KERNEL_STACK_SIZE; i += 0x1000)
659                         MM_Allocate(base+i);
660                 
661                 return base + KERNEL_STACK_SIZE;
662         }
663         Log_Warning("MM", "MM_NewKStack - No address space left\n");
664         return 0;
665 }

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