|
1 // Copyright (c) 1994-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of the License "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // e32\memmodel\epoc\moving\mprocess.cpp |
|
15 // |
|
16 // |
|
17 |
|
18 #include "memmodel.h" |
|
19 #include "cache_maintenance.h" |
|
20 #include "mmboot.h" |
|
21 |
|
22 #define iMState iWaitLink.iSpare1 |
|
23 |
|
24 _LIT(KDollarDat,"$DAT"); |
|
25 _LIT(KLitDllDollarData,"DLL$DATA"); |
|
26 |
|
27 /******************************************** |
|
28 * Process |
|
29 ********************************************/ |
|
30 void DMemModelProcess::Destruct() |
|
31 { |
|
32 NKern::LockSystem(); |
|
33 if (this==TheCurrentAddressSpace) |
|
34 TheCurrentAddressSpace=NULL; |
|
35 if (this==TheCurrentVMProcess) |
|
36 TheCurrentVMProcess=NULL; |
|
37 if (this==TheCurrentDataSectionProcess) |
|
38 TheCurrentDataSectionProcess=NULL; |
|
39 if (this==TheCompleteDataSectionProcess) |
|
40 TheCompleteDataSectionProcess=NULL; |
|
41 NKern::UnlockSystem(); |
|
42 DProcess::Destruct(); |
|
43 } |
|
44 |
|
45 TInt DMemModelProcess::NewChunk(DChunk*& aChunk, SChunkCreateInfo& aInfo, TLinAddr& aRunAddr) |
|
46 { |
|
47 aChunk=NULL; |
|
48 DMemModelChunk* pC=NULL; |
|
49 TInt r=GetNewChunk(pC,aInfo); |
|
50 if (r!=KErrNone) |
|
51 { |
|
52 if (pC) |
|
53 pC->Close(NULL); |
|
54 return r; |
|
55 } |
|
56 if (aInfo.iForceFixed || iAttributes & DMemModelProcess::EFixedAddress) |
|
57 pC->iAttributes |= DMemModelChunk::EFixedAddress; |
|
58 if (!aInfo.iGlobal && (iAttributes & DMemModelProcess::EPrivate)!=0) |
|
59 pC->iAttributes |= DMemModelChunk::EPrivate; |
|
60 if (pC->iChunkType==EDll || pC->iChunkType==EUserCode || pC->iChunkType==EUserSelfModCode || pC->iChunkType==EKernelCode) |
|
61 pC->iAttributes |= (DMemModelChunk::EFixedAddress|DMemModelChunk::ECode); |
|
62 pC->iOwningProcess=(aInfo.iGlobal)?NULL:this; |
|
63 r=pC->Create(aInfo); |
|
64 if (r==KErrNone && (aInfo.iOperations & SChunkCreateInfo::EAdjust)) |
|
65 { |
|
66 if (aInfo.iRunAddress!=0) |
|
67 pC->SetFixedAddress(aInfo.iRunAddress,aInfo.iPreallocated); |
|
68 if (aInfo.iPreallocated==0) |
|
69 { |
|
70 if (pC->iAttributes & DChunk::EDisconnected) |
|
71 { |
|
72 r=pC->Commit(aInfo.iInitialBottom,aInfo.iInitialTop-aInfo.iInitialBottom); |
|
73 } |
|
74 else if (pC->iAttributes & DChunk::EDoubleEnded) |
|
75 { |
|
76 r=pC->AdjustDoubleEnded(aInfo.iInitialBottom,aInfo.iInitialTop); |
|
77 } |
|
78 else |
|
79 { |
|
80 r=pC->Adjust(aInfo.iInitialTop); |
|
81 } |
|
82 } |
|
83 if (r==KErrNone && pC->iHomeRegionBase==0 && (pC->iAttributes&DMemModelChunk::EFixedAddress)!=0) |
|
84 { |
|
85 r=pC->Reserve(0); |
|
86 aRunAddr=(TLinAddr)pC->Base(); |
|
87 } |
|
88 } |
|
89 if (r==KErrNone && (aInfo.iOperations & SChunkCreateInfo::EAdd)) |
|
90 { |
|
91 if (pC->iAttributes & DMemModelChunk::ECode) |
|
92 Mmu::Get().SyncCodeMappings(); |
|
93 if (pC->iChunkType!=EUserCode) |
|
94 { |
|
95 r=WaitProcessLock(); |
|
96 if (r==KErrNone) |
|
97 { |
|
98 r=AddChunk(pC,aRunAddr,EFalse); |
|
99 SignalProcessLock(); |
|
100 } |
|
101 } |
|
102 else |
|
103 aRunAddr=(TLinAddr)pC->Base(); // code chunks always fixed address |
|
104 } |
|
105 if (r==KErrNone) |
|
106 { |
|
107 pC->iDestroyedDfc = aInfo.iDestroyedDfc; |
|
108 aChunk=(DChunk*)pC; |
|
109 } |
|
110 else |
|
111 pC->Close(NULL); // NULL since chunk can't have been added to process |
|
112 return r; |
|
113 } |
|
114 |
|
115 TInt DMemModelProcess::DoCreate(TBool aKernelProcess, TProcessCreateInfo& aInfo) |
|
116 { |
|
117 __KTRACE_OPT(KPROC,Kern::Printf("DMemModelProcess::DoCreate %O",this)); |
|
118 |
|
119 if (aKernelProcess) |
|
120 iAttributes=ESupervisor|EFixedAddress|EPrivate; |
|
121 else if (aInfo.iAttr & ECodeSegAttFixed) |
|
122 iAttributes=EFixedAddress|EPrivate; |
|
123 else |
|
124 iAttributes=0; |
|
125 if ((iAttributes & ESupervisor)==0 && (iAttributes & EFixedAddress)!=0) |
|
126 { |
|
127 CheckForFixedAccess(); |
|
128 } |
|
129 return KErrNone; |
|
130 } |
|
131 |
|
132 TInt DMemModelProcess::CreateDataBssStackArea(TProcessCreateInfo& aInfo) |
|
133 { |
|
134 __KTRACE_OPT(KPROC,Kern::Printf("DMemModelProcess::CreateDataBssStackArea %O",this)); |
|
135 TInt dataBssSize=Mmu::RoundToPageSize(aInfo.iTotalDataSize); |
|
136 TInt maxSize=dataBssSize+PP::MaxStackSpacePerProcess; |
|
137 TBool fixed=(iAttributes & EFixedAddress); |
|
138 |
|
139 __KTRACE_OPT(KPROC,Kern::Printf("DataBssSize=%x, chunk max size %x",dataBssSize,maxSize)); |
|
140 |
|
141 SChunkCreateInfo cinfo; |
|
142 cinfo.iGlobal=EFalse; |
|
143 cinfo.iAtt=TChunkCreate::EDisconnected; |
|
144 cinfo.iForceFixed=EFalse; |
|
145 cinfo.iOperations=SChunkCreateInfo::EAdjust|SChunkCreateInfo::EAdd; |
|
146 cinfo.iType=EUserData; |
|
147 cinfo.iMaxSize=maxSize; |
|
148 cinfo.iInitialBottom=0; |
|
149 cinfo.iInitialTop=dataBssSize; |
|
150 cinfo.iPreallocated=0; |
|
151 cinfo.iName.Set(KDollarDat); |
|
152 cinfo.iOwner=this; |
|
153 if (fixed && dataBssSize!=0 && aInfo.iCodeLoadAddress) |
|
154 { |
|
155 const TRomImageHeader& rih=*(const TRomImageHeader*)aInfo.iCodeLoadAddress; |
|
156 cinfo.iRunAddress=rih.iDataBssLinearBase; |
|
157 } |
|
158 else |
|
159 cinfo.iRunAddress=0; |
|
160 TInt r=NewChunk((DChunk*&)iDataBssStackChunk,cinfo,iDataBssRunAddress); |
|
161 return r; |
|
162 } |
|
163 |
|
164 TInt DMemModelProcess::AddChunk(DChunk* aChunk,TBool isReadOnly) |
|
165 { |
|
166 DMemModelChunk* pC=(DMemModelChunk*)aChunk; |
|
167 TInt r=WaitProcessLock(); |
|
168 if (r==KErrNone) |
|
169 { |
|
170 TInt pos=0; |
|
171 r=ChunkIndex(pC,pos); |
|
172 TLinAddr dataSectionBase=0; |
|
173 if (r==0) // Found the chunk in this process, just up its count |
|
174 { |
|
175 iChunks[pos].iAccessCount++; |
|
176 __KTRACE_OPT(KPROC,Kern::Printf("DMemModelProcess::AddChunk %08x to %08x (Access count incremented to %d)",aChunk,this,iChunks[pos].iAccessCount)); |
|
177 SignalProcessLock(); |
|
178 return KErrNone; |
|
179 } |
|
180 r=AddChunk(pC,dataSectionBase,isReadOnly); |
|
181 SignalProcessLock(); |
|
182 } |
|
183 __KTRACE_OPT(KPROC,Kern::Printf("DMemModelProcess::AddChunk returns %d",r)); |
|
184 return r; |
|
185 } |
|
186 |
|
187 void FlushBeforeChunkMove(DMemModelChunk* aChunk) |
|
188 { |
|
189 Mmu& m = Mmu::Get(); |
|
190 TUint32 ff=Mmu::EFlushDMove|Mmu::EFlushDPermChg; |
|
191 if (aChunk->iAttributes & DMemModelChunk::ECode) // assumption here that code chunks don't move |
|
192 ff |= Mmu::EFlushIPermChg; |
|
193 m.GenericFlush(ff); |
|
194 } |
|
195 |
|
196 TInt DMemModelProcess::AddChunk(DMemModelChunk* aChunk, TLinAddr& aDataSectionBase, TBool isReadOnly) |
|
197 { |
|
198 // |
|
199 // Must hold the process $LOCK mutex before calling this |
|
200 // |
|
201 __KTRACE_OPT(KPROC,Kern::Printf("DMemModelProcess::AddChunk %08x to %08x (for first time)",aChunk,this)); |
|
202 TInt r=AllocateDataSectionBase(*((DMemModelChunk*)aChunk),(TUint&)aDataSectionBase); |
|
203 if(r!=KErrNone) |
|
204 return r; |
|
205 |
|
206 if (iNumChunks==KMaxChunksInProcess) |
|
207 return KErrOverflow; // too many chunks in the process |
|
208 |
|
209 SChunkInfo *pC=iChunks; |
|
210 SChunkInfo *pE=pC+iNumChunks-1; |
|
211 NKern::LockSystem(); |
|
212 while(pE>=pC && TUint(pE->iDataSectionBase)>TUint(aDataSectionBase)) |
|
213 { |
|
214 pE[1]=pE[0]; |
|
215 pE--; |
|
216 } |
|
217 pC=pE+1; |
|
218 pC->iDataSectionBase=aDataSectionBase; |
|
219 pC->isReadOnly=isReadOnly; |
|
220 pC->iAccessCount=1; |
|
221 pC->iChunk=aChunk; |
|
222 iNumChunks++; |
|
223 |
|
224 if(!(iAttributes&ESupervisor)) |
|
225 { |
|
226 TInt attribs=aChunk->iAttributes; |
|
227 if (!(attribs&DMemModelChunk::EFixedAddress)) |
|
228 { |
|
229 iNumMovingChunks++; |
|
230 iAttributes |= EMoving; |
|
231 } |
|
232 |
|
233 if (attribs&DMemModelChunk::EFixedAccess) |
|
234 { |
|
235 NKern::UnlockSystem(); |
|
236 AddFixedAccessChunk(aChunk); |
|
237 goto done; // FINISHED |
|
238 } |
|
239 |
|
240 iAttributes |= EVariableAccess; |
|
241 if (attribs & DMemModelChunk::ECode) |
|
242 { |
|
243 iNumNonFixedAccessCodeChunks++; |
|
244 iAttributes |= EVariableCode; |
|
245 } |
|
246 if (++iNumNonFixedAccessChunks==1) |
|
247 { |
|
248 NKern::UnlockSystem(); |
|
249 DoAttributeChange(); // change process from fixed to variable access |
|
250 NKern::LockSystem(); |
|
251 } |
|
252 |
|
253 if (this!=TheCurrentThread->iOwningProcess) |
|
254 { |
|
255 // Adding chunk to another process |
|
256 if (this==TheCurrentDataSectionProcess && !(attribs&DMemModelChunk::EFixedAddress)) |
|
257 TheCompleteDataSectionProcess=NULL; // just set partial state change flag and leave chunk alone |
|
258 if (this==TheCurrentAddressSpace) |
|
259 TheCurrentAddressSpace=NULL; |
|
260 NKern::UnlockSystem(); |
|
261 goto done; // FINISHED |
|
262 } |
|
263 |
|
264 // Adding chunk to currently active user process |
|
265 { |
|
266 TheCurrentAddressSpace=NULL; |
|
267 Mmu& m = Mmu::Get(); |
|
268 TUint32 ff=0; // flush flags |
|
269 DMemModelChunk::TChunkState state=isReadOnly?DMemModelChunk::ERunningRO:DMemModelChunk::ERunningRW; |
|
270 if (attribs&DMemModelChunk::EFixedAddress) |
|
271 { |
|
272 // Fixed address chunk, just change permissions |
|
273 ff|=aChunk->ApplyTopLevelPermissions(state); |
|
274 } |
|
275 else if (this==TheCurrentDataSectionProcess) |
|
276 { |
|
277 // Moving chunk. |
|
278 // This process is already in the data section, so just move the chunk down. |
|
279 // Must do flushing first |
|
280 TheCompleteDataSectionProcess=NULL; |
|
281 FlushBeforeChunkMove(aChunk); |
|
282 aChunk->MoveToRunAddress(aDataSectionBase,state); // idempotent |
|
283 TheCompleteDataSectionProcess=this; |
|
284 } |
|
285 else if (iNumMovingChunks==1) |
|
286 { |
|
287 // The first moving chunk being added to a process with the data section occupied by another process. |
|
288 // This is the problematic case - we must displace the other process from the data section. |
|
289 // However we must allow preemption after each chunk is moved. Note that if a reschedule does |
|
290 // occur the necessary chunk moves will have been done by the scheduler, so we can finish |
|
291 // immediately. |
|
292 // Must do cache flushing first |
|
293 m.GenericFlush(Mmu::EFlushDMove); |
|
294 if (TheCurrentDataSectionProcess) |
|
295 { |
|
296 if (TheCurrentDataSectionProcess->iAttributes & EVariableCode) |
|
297 ff |= Mmu::EFlushIPermChg; |
|
298 SChunkInfo* pOtherProcChunks=TheCurrentDataSectionProcess->iChunks; |
|
299 SChunkInfo* pEndOtherProcChunks=pOtherProcChunks+TheCurrentDataSectionProcess->iNumChunks; |
|
300 NKern::FlashSystem(); |
|
301 // if a reschedule occurs, TheCompleteDataSectionProcess will become equal to this |
|
302 while (TheCompleteDataSectionProcess!=this && pOtherProcChunks<pEndOtherProcChunks) |
|
303 { |
|
304 DMemModelChunk *pChunk=pOtherProcChunks->iChunk; |
|
305 pChunk->MoveToHomeSection(); |
|
306 ++pOtherProcChunks; |
|
307 TheCompleteDataSectionProcess=NULL; |
|
308 NKern::FlashSystem(); |
|
309 } |
|
310 } |
|
311 if (TheCompleteDataSectionProcess!=this) |
|
312 { |
|
313 if (attribs & DMemModelChunk::ECode) |
|
314 ff |= Mmu::EFlushIPermChg; |
|
315 aChunk->MoveToRunAddress(aDataSectionBase,state); |
|
316 TheCurrentDataSectionProcess=this; |
|
317 TheCompleteDataSectionProcess=this; |
|
318 } |
|
319 } |
|
320 TheCurrentAddressSpace=this; |
|
321 TheCurrentVMProcess=this; |
|
322 if (ff) |
|
323 m.GenericFlush(ff); |
|
324 } |
|
325 } |
|
326 NKern::UnlockSystem(); |
|
327 done: |
|
328 __KTRACE_OPT(KPROC,Kern::Printf("Added array entry for %x",aDataSectionBase)); |
|
329 __KTRACE_OPT(KPROC,Kern::Printf("Chunks maxsize %x",pC->iChunk->MaxSize())); |
|
330 __DEBUG_EVENT(EEventUpdateProcess, this); |
|
331 return KErrNone; |
|
332 } |
|
333 |
|
334 TInt DMemModelProcess::AllocateDataSectionBase(DMemModelChunk& aChunk, TUint& aBase) |
|
335 { |
|
336 __KTRACE_OPT(KPROC,Kern::Printf("DMemModelProcess::AllocateDataSectionBase")); |
|
337 aBase=0; |
|
338 if ((aChunk.iAttributes & DMemModelChunk::EPrivate) && this!=aChunk.iOwningProcess) |
|
339 return KErrAccessDenied; |
|
340 if (aChunk.iAttributes & DMemModelChunk::EFixedAddress) |
|
341 { |
|
342 aBase=aChunk.iHomeRegionBase; |
|
343 return KErrNone; |
|
344 } |
|
345 Mmu& m = Mmu::Get(); |
|
346 TLinAddr base=0; |
|
347 TLinAddr maxBase=0; |
|
348 switch (aChunk.iChunkType) |
|
349 { |
|
350 case EUserData: |
|
351 base=m.iDataSectionBase; |
|
352 maxBase=m.iDllDataBase; |
|
353 break; |
|
354 case EUserCode: |
|
355 case EUserSelfModCode: |
|
356 MM::Panic(MM::EUserCodeNotFixed); |
|
357 break; |
|
358 case EDllData: |
|
359 aBase=m.iDllDataBase; |
|
360 return KErrNone; |
|
361 default: |
|
362 __KTRACE_OPT(KPANIC,Kern::Printf("DMemModelProcess::AllocateDataSectionBase BadChunkType %d",aChunk.iChunkType)); |
|
363 return KErrAccessDenied; |
|
364 } |
|
365 |
|
366 TLinAddr lastBase=base; |
|
367 SChunkInfo *pS=iChunks; |
|
368 SChunkInfo *pE=pS+iNumChunks; |
|
369 while (pS<pE) |
|
370 { |
|
371 TLinAddr thisBase=pS->iDataSectionBase; |
|
372 __KTRACE_OPT(KPROC,Kern::Printf("Chunk already at %x",thisBase)); |
|
373 if (thisBase>=maxBase) |
|
374 break; |
|
375 if (thisBase>=base) // Within the range we are allocating |
|
376 { |
|
377 TInt gap=thisBase-lastBase; |
|
378 if (gap>=aChunk.MaxSize()) |
|
379 break; |
|
380 lastBase=thisBase+pS->iChunk->MaxSize(); |
|
381 } |
|
382 pS++; |
|
383 } |
|
384 if (lastBase+aChunk.MaxSize()>maxBase) |
|
385 { |
|
386 __KTRACE_OPT(KPROC,Kern::Printf("ERROR - none allocated, out of memory")); |
|
387 return KErrNoMemory; |
|
388 } |
|
389 aBase=lastBase; |
|
390 __KTRACE_OPT(KPROC,Kern::Printf("User allocated %x",aBase)); |
|
391 return KErrNone; |
|
392 } |
|
393 |
|
394 TUint8* DMemModelProcess::DataSectionBase(DMemModelChunk* aChunk) |
|
395 { |
|
396 // this can't be called after $LOCK is deleted |
|
397 Kern::MutexWait(*iProcessLock); |
|
398 TInt pos=0; |
|
399 TInt r=ChunkIndex(aChunk,pos); |
|
400 if (r==0) // Found the chunk |
|
401 { |
|
402 TUint8* answer=((TUint8*)iChunks[pos].iDataSectionBase); |
|
403 __KTRACE_OPT(KPROC,Kern::Printf("DMemModelProcess::DataSectionBase %x",answer)); |
|
404 Kern::MutexSignal(*iProcessLock); |
|
405 return answer; |
|
406 } |
|
407 __KTRACE_OPT(KPROC,Kern::Printf("DMemModelProcess::DataSectionBase chunk %08x not present in %08x",aChunk,this)); |
|
408 Kern::MutexSignal(*iProcessLock); |
|
409 return(NULL); |
|
410 } |
|
411 |
|
412 void DMemModelProcess::DoRemoveChunk(TInt aIndex) |
|
413 { |
|
414 // Must be called with process $LOCK mutex held |
|
415 __DEBUG_EVENT(EEventUpdateProcess, this); |
|
416 DMemModelChunk* chunk = iChunks[aIndex].iChunk; |
|
417 Mmu& m = Mmu::Get(); |
|
418 NKern::LockSystem(); |
|
419 TInt attribs=chunk->iAttributes; |
|
420 __KTRACE_OPT(KPROC,Kern::Printf("Removing Chunk attribs=%08x, Process attribs=%08x",attribs,iAttributes)); |
|
421 if (!(attribs&DMemModelChunk::EFixedAccess)) |
|
422 { |
|
423 // Must leave chunk in process chunk list until we have flushed the cache if necessary |
|
424 if (this==TheCurrentVMProcess && (attribs&DMemModelChunk::EFixedAddress)) |
|
425 { |
|
426 TUint32 ff=chunk->ApplyTopLevelPermissions(DMemModelChunk::ENotRunning); |
|
427 m.GenericFlush(ff); |
|
428 // the system must now remain locked until the chunk is removed from the process chunk list |
|
429 } |
|
430 if (this==TheCurrentDataSectionProcess && !(attribs&DMemModelChunk::EFixedAddress)) |
|
431 { |
|
432 // must do cache flush first |
|
433 FlushBeforeChunkMove(chunk); // preemptible, but on return cache is free of chunk data |
|
434 chunk->MoveToHomeSection(); |
|
435 // the system must now remain locked until the chunk is removed from the process chunk list |
|
436 } |
|
437 } |
|
438 |
|
439 // Remove the chunk from the process chunk list |
|
440 SChunkInfo *pD=iChunks+aIndex; |
|
441 SChunkInfo *pS=iChunks+aIndex+1; |
|
442 SChunkInfo *pE=iChunks+iNumChunks; |
|
443 while(pS<pE) |
|
444 *pD++=*pS++; |
|
445 iNumChunks--; |
|
446 |
|
447 // Update the process attribute flags |
|
448 if (!(attribs&DMemModelChunk::EFixedAddress)) |
|
449 { |
|
450 if (--iNumMovingChunks==0) |
|
451 iAttributes &= ~EMoving; |
|
452 } |
|
453 if (!(attribs&DMemModelChunk::EFixedAccess)) |
|
454 { |
|
455 if ((attribs&DMemModelChunk::ECode) && --iNumNonFixedAccessCodeChunks==0) |
|
456 iAttributes &= ~EVariableCode; |
|
457 if (this==TheCurrentDataSectionProcess && !(iAttributes&EMoving)) |
|
458 { |
|
459 TheCurrentDataSectionProcess=NULL; |
|
460 TheCompleteDataSectionProcess=NULL; |
|
461 } |
|
462 if (--iNumNonFixedAccessChunks==0) |
|
463 { |
|
464 iAttributes &= ~EVariableAccess; |
|
465 if (this==TheCurrentVMProcess) |
|
466 { |
|
467 TheCurrentVMProcess=NULL; |
|
468 TheCurrentAddressSpace=NULL; |
|
469 } |
|
470 NKern::UnlockSystem(); |
|
471 DoAttributeChange(); // change process from variable to fixed access |
|
472 } |
|
473 else |
|
474 NKern::UnlockSystem(); |
|
475 } |
|
476 else |
|
477 { |
|
478 NKern::UnlockSystem(); |
|
479 RemoveFixedAccessChunk(chunk); |
|
480 } |
|
481 } |
|
482 |
|
483 /** |
|
484 Final chance for process to release resources during its death. |
|
485 |
|
486 Called with process $LOCK mutex held (if it exists). |
|
487 This mutex will not be released before it is deleted. |
|
488 I.e. no other thread will ever hold the mutex again. |
|
489 */ |
|
490 void DMemModelProcess::FinalRelease() |
|
491 { |
|
492 // Clean up any left over chunks (such as SharedIo buffers) |
|
493 while(iNumChunks) |
|
494 DoRemoveChunk(0); |
|
495 } |
|
496 |
|
497 void DMemModelProcess::RemoveChunk(DMemModelChunk *aChunk) |
|
498 { |
|
499 // note that this can't be called after the process $LOCK mutex has been deleted |
|
500 // since it can only be called by a thread in this process doing a handle close or |
|
501 // dying, or by the process handles array being deleted due to the process dying, |
|
502 // all of which happen before $LOCK is deleted. |
|
503 __KTRACE_OPT(KPROC,Kern::Printf("DMemModelProcess::RemoveChunk %08x from %08x",aChunk,this)); |
|
504 Kern::MutexWait(*iProcessLock); |
|
505 TInt pos=0; |
|
506 TInt r=ChunkIndex(aChunk,pos); |
|
507 __KTRACE_OPT(KPROC,if(r) Kern::Printf("Chunk lookup failed with %d",r)); |
|
508 if (r==0) // Found the chunk |
|
509 { |
|
510 __KTRACE_OPT(KPROC,Kern::Printf("Chunk access count %d",iChunks[pos].iAccessCount)); |
|
511 if (--iChunks[pos].iAccessCount==0) |
|
512 DoRemoveChunk(pos); |
|
513 } |
|
514 Kern::MutexSignal(*iProcessLock); |
|
515 } |
|
516 |
|
517 TInt DMemModelProcess::ChunkIndex(DMemModelChunk* aChunk,TInt& aPos) |
|
518 { |
|
519 if (aChunk==NULL) |
|
520 return(KErrNotFound); |
|
521 TInt i=0; |
|
522 SChunkInfo *pC=iChunks; |
|
523 SChunkInfo *pE=pC+iNumChunks; |
|
524 while(pC<pE && (pC->iChunk!=aChunk)) |
|
525 { |
|
526 pC++; |
|
527 i++; |
|
528 } |
|
529 if (pC==pE) |
|
530 return KErrNotFound; |
|
531 aPos=i; |
|
532 return KErrNone; |
|
533 } |
|
534 |
|
535 void DMemModelProcess::RemoveDllData() |
|
536 // |
|
537 // Call with CodeSegLock held |
|
538 // |
|
539 { |
|
540 Kern::SafeClose((DObject*&)iDllDataChunk, this); |
|
541 } |
|
542 |
|
543 TInt DMemModelProcess::CreateDllDataChunk() |
|
544 // |
|
545 // Call with CodeSegLock held |
|
546 // |
|
547 { |
|
548 __KTRACE_OPT(KDLL,Kern::Printf("DMemModelProcess %O CreateDllDataChunk",this)); |
|
549 Mmu& m = Mmu::Get(); |
|
550 SChunkCreateInfo c; |
|
551 c.iGlobal=EFalse; |
|
552 c.iAtt=TChunkCreate::EDisconnected; |
|
553 c.iForceFixed=EFalse; |
|
554 c.iOperations=SChunkCreateInfo::EAdjust|SChunkCreateInfo::EAdd; |
|
555 c.iRunAddress=0; |
|
556 c.iPreallocated=0; |
|
557 c.iType=EDllData; |
|
558 c.iMaxSize=(iAttributes&EFixedAddress) ? 1 : m.iMaxDllDataSize; // minimal size for fixed processes |
|
559 c.iName.Set(KLitDllDollarData); |
|
560 c.iOwner=this; |
|
561 c.iInitialBottom=0; |
|
562 c.iInitialTop=0; |
|
563 TLinAddr runAddr; |
|
564 return NewChunk((DChunk*&)iDllDataChunk,c,runAddr); |
|
565 } |
|
566 |
|
567 void DMemModelProcess::FreeDllDataChunk() |
|
568 { |
|
569 iDllDataChunk->Close(this); |
|
570 iDllDataChunk=NULL; |
|
571 } |
|
572 |
|
573 TInt DMemModelProcess::CommitDllData(TLinAddr aBase, TInt aSize) |
|
574 { |
|
575 __KTRACE_OPT(KDLL,Kern::Printf("DMemModelProcess %O CommitDllData %08x+%x",this,aBase,aSize)); |
|
576 TInt r=KErrNone; |
|
577 if (!iDllDataChunk) |
|
578 r=CreateDllDataChunk(); |
|
579 if (r==KErrNone) |
|
580 { |
|
581 Mmu& m = Mmu::Get(); |
|
582 TLinAddr dll_data_base=(iAttributes & EFixedAddress) ? (TLinAddr)iDllDataChunk->Base() |
|
583 : TLinAddr(m.iDllDataBase); |
|
584 TInt offset=aBase-dll_data_base; |
|
585 __ASSERT_ALWAYS(TUint32(offset)<TUint32(iDllDataChunk->iMaxSize),MM::Panic(MM::ECommitInvalidDllDataAddress)); |
|
586 r=iDllDataChunk->Commit(offset, aSize); |
|
587 if (r!=KErrNone && iDllDataChunk->iSize==0) |
|
588 FreeDllDataChunk(); |
|
589 } |
|
590 __KTRACE_OPT(KDLL,Kern::Printf("CommitDllData returns %d",r)); |
|
591 return r; |
|
592 } |
|
593 |
|
594 void DMemModelProcess::DecommitDllData(TLinAddr aBase, TInt aSize) |
|
595 { |
|
596 __KTRACE_OPT(KDLL,Kern::Printf("DMemModelProcess %O DecommitDllData %08x+%x",this,aBase,aSize)); |
|
597 Mmu& m = Mmu::Get(); |
|
598 TLinAddr dll_data_base=(iAttributes & EFixedAddress) ? (TLinAddr)iDllDataChunk->Base() |
|
599 : TLinAddr(m.iDllDataBase); |
|
600 TInt offset=aBase-dll_data_base; |
|
601 TInt r=iDllDataChunk->Decommit(offset, aSize); |
|
602 __ASSERT_ALWAYS(r==KErrNone,MM::Panic(MM::EDecommitInvalidDllDataAddress)); |
|
603 if (iDllDataChunk->iSize==0) |
|
604 FreeDllDataChunk(); |
|
605 } |
|
606 |
|
607 TInt DMemModelProcess::MapCodeSeg(DCodeSeg* aSeg) |
|
608 { |
|
609 DMemModelCodeSeg& seg=*(DMemModelCodeSeg*)aSeg; |
|
610 __KTRACE_OPT(KDLL,Kern::Printf("Process %O MapCodeSeg %C", this, aSeg)); |
|
611 TBool kernel_only=( (seg.iAttr&(ECodeSegAttKernel|ECodeSegAttGlobal)) == ECodeSegAttKernel ); |
|
612 if (kernel_only && !(iAttributes&ESupervisor)) |
|
613 return KErrNotSupported; |
|
614 if (seg.iAttr&ECodeSegAttKernel || seg.iDataAllocBase==-1) |
|
615 return KErrNone; // no extra mappings needed for kernel code or code with fixed data address |
|
616 TInt r=KErrNone; |
|
617 if (seg.IsDll()) |
|
618 { |
|
619 TInt total_data_size; |
|
620 TLinAddr data_base; |
|
621 seg.GetDataSizeAndBase(total_data_size, data_base); |
|
622 if (r==KErrNone && total_data_size) |
|
623 { |
|
624 TInt size=Mmu::RoundToPageSize(total_data_size); |
|
625 r=CommitDllData(data_base, size); |
|
626 } |
|
627 } |
|
628 return r; |
|
629 } |
|
630 |
|
631 void DMemModelProcess::UnmapCodeSeg(DCodeSeg* aSeg) |
|
632 { |
|
633 DMemModelCodeSeg& seg=*(DMemModelCodeSeg*)aSeg; |
|
634 __KTRACE_OPT(KDLL,Kern::Printf("Process %O UnmapCodeSeg %C", this, aSeg)); |
|
635 if (seg.iAttr&ECodeSegAttKernel || seg.iDataAllocBase==-1) |
|
636 return; // no extra mappings needed for kernel code or code with fixed data address |
|
637 if (seg.IsDll()) |
|
638 { |
|
639 TInt total_data_size; |
|
640 TLinAddr data_base; |
|
641 seg.GetDataSizeAndBase(total_data_size, data_base); |
|
642 if (total_data_size) |
|
643 DecommitDllData(data_base, Mmu::RoundToPageSize(total_data_size)); |
|
644 } |
|
645 } |
|
646 |
|
647 TInt DMemModelProcess::NewShPool(DShPool*& /* aPool */, TShPoolCreateInfo& /* aInfo */) |
|
648 { |
|
649 return KErrNotSupported; |
|
650 } |
|
651 |
|
652 TInt DThread::RawRead(const TAny* aSrc, TAny* aDest, TInt aLength, TInt aFlags, TIpcExcTrap* aExcTrap) |
|
653 // |
|
654 // Read from the thread's process. |
|
655 // aSrc is run address of memory to read. The memory is in aThread's address space. |
|
656 // aDest is the address of destination. The memory is in the current process's address space. |
|
657 // aExcTrap, exception trap object to be updated if the actual memory access is performed on another memory area. It happens |
|
658 // when reading is performed in chunks or if home adress is read instead of the provided run address. |
|
659 // Enter and return with system locked. |
|
660 { |
|
661 const TUint8* pS=(const TUint8*)aSrc; |
|
662 TUint8* pD=(TUint8*)aDest; |
|
663 const TUint8* pC=NULL; |
|
664 TBool check=ETrue; |
|
665 TBool suspect=EFalse; |
|
666 DThread* pT=TheCurrentThread; |
|
667 while (aLength) |
|
668 { |
|
669 if (check) |
|
670 { |
|
671 suspect=((aFlags & KCheckLocalAddress) && !MM::CurrentAddress(pT,pD,aLength,ETrue)); |
|
672 if (iMState==EDead) |
|
673 return KErrDied; |
|
674 pC=(const TUint8*)MM::CurrentAddress(this,pS,aLength,EFalse); |
|
675 __KTRACE_OPT(KTHREAD2,Kern::Printf("DThread::RawRead %08x<-[%08x::%08x]%08x+%x",pD,this,pS,pC,aLength)); |
|
676 if (!pC) |
|
677 return KErrBadDescriptor; |
|
678 } |
|
679 TInt len=Min(aLength,K::MaxMemCopyInOneGo); |
|
680 if (aExcTrap) |
|
681 { |
|
682 aExcTrap->iSize = (len + 2*(sizeof(TInt32)-1));//+6 is for the worst case. We do not have to be precise here. |
|
683 aExcTrap->iRemoteBase = (TLinAddr)pC & ~(sizeof(TInt32)-1); |
|
684 if (aExcTrap->iLocalBase) |
|
685 aExcTrap->iLocalBase = (TLinAddr)pD & ~(sizeof(TInt32)-1); |
|
686 __KTRACE_OPT(KTHREAD2,Kern::Printf("DThread::RawRead exc. update: %08x %08x %08x",aExcTrap->iLocalBase,aExcTrap->iRemoteBase,aExcTrap->iSize)); |
|
687 } |
|
688 |
|
689 #ifdef __DEMAND_PAGING__ |
|
690 XTRAP_PAGING_START(check); |
|
691 CHECK_PAGING_SAFE; |
|
692 #endif |
|
693 |
|
694 suspect?(void)umemput(pD,pC,len):(void)memcpy(pD,pC,len); |
|
695 |
|
696 #ifdef __DEMAND_PAGING__ |
|
697 XTRAP_PAGING_END; |
|
698 if(check<0) |
|
699 return check; // paging error caused by bad client (I.e. 'this' thread was bad) |
|
700 if(check) |
|
701 { |
|
702 __KTRACE_OPT(KTHREAD2,Kern::Printf("DThread::RawRead paging trap, suspect %d, dest %08x, source %08x, length %d\n", suspect, pD, pC, len)); |
|
703 continue; |
|
704 } |
|
705 #endif |
|
706 |
|
707 pD+=len; |
|
708 pS+=len; |
|
709 pC+=len; |
|
710 aLength-=len; |
|
711 if (aLength) |
|
712 check=NKern::FlashSystem(); |
|
713 } |
|
714 return KErrNone; |
|
715 } |
|
716 |
|
717 TInt DThread::RawWrite(const TAny* aDest, const TAny* aSrc, TInt aLength, TInt aFlags, DThread* aOriginatingThread, TIpcExcTrap* aExcTrap) |
|
718 // |
|
719 // Write to the thread's process. |
|
720 // aDest is run address of memory to write. It resides in this thread's address space. |
|
721 // aSrc is address of the source buffer. It resides in the current process's address space. |
|
722 // aOriginatingThread is the thread on behalf of which this operation is performed (eg client of device driver). |
|
723 // Enter and return with system locked |
|
724 // aExcTrap, exception trap object to be updated if the actual memory access is performed on another memory area. It happens |
|
725 // when reading is performed in chunks or if home adress is read instead of the provided run address. |
|
726 // |
|
727 { |
|
728 TUint8* pD=(TUint8*)aDest; |
|
729 const TUint8* pS=(const TUint8*)aSrc; |
|
730 TUint8* pC=NULL; |
|
731 TBool check=ETrue; |
|
732 TBool suspect=EFalse; |
|
733 DThread* pT=TheCurrentThread; |
|
734 DThread* pO=aOriginatingThread; |
|
735 if (!pO) |
|
736 pO=pT; |
|
737 DProcess* pF=K::TheFileServerProcess; |
|
738 TBool special=(iOwningProcess==pF && pO->iOwningProcess==pF); |
|
739 while (aLength) |
|
740 { |
|
741 if (check) |
|
742 { |
|
743 suspect=((aFlags & KCheckLocalAddress) && !MM::CurrentAddress(pT,pS,aLength,EFalse)); |
|
744 if (iMState==EDead) |
|
745 return KErrDied; |
|
746 pC=(TUint8*)MM::CurrentAddress(this,pD,aLength,ETrue); |
|
747 __KTRACE_OPT(KTHREAD2,Kern::Printf("DThread::RawRead [%08x::%08x]%08x<-%08x+%x",this,pD,pC,pS,aLength)); |
|
748 if (!pC) |
|
749 { |
|
750 if (special) |
|
751 pC=pD; |
|
752 else |
|
753 return KErrBadDescriptor; |
|
754 } |
|
755 } |
|
756 TInt len=Min(aLength,K::MaxMemCopyInOneGo); |
|
757 if (aExcTrap) |
|
758 { |
|
759 aExcTrap->iSize = (len + 2*(sizeof(TInt32)-1));//+6 is for the worst case. We do not have to be precise here. |
|
760 aExcTrap->iRemoteBase = (TLinAddr)pC & ~(sizeof(TInt32)-1); |
|
761 if (aExcTrap->iLocalBase) |
|
762 aExcTrap->iLocalBase = (TLinAddr)pS & ~(sizeof(TInt32)-1); |
|
763 __KTRACE_OPT(KTHREAD2,Kern::Printf("DThread::RawWrite exc. update %08x %08x %08x",aExcTrap->iLocalBase,aExcTrap->iRemoteBase,aExcTrap->iSize)); |
|
764 } |
|
765 |
|
766 #ifdef __DEMAND_PAGING__ |
|
767 XTRAP_PAGING_START(check); |
|
768 // Must check that it is safe to page, unless we are reading from unpaged ROM in which case |
|
769 // we allow it. umemget does this anyway, so we just need to check if suspect is not set. |
|
770 if (!suspect) |
|
771 { |
|
772 CHECK_PAGING_SAFE_RANGE((TLinAddr)aSrc, aLength); |
|
773 CHECK_DATA_PAGING_SAFE_RANGE((TLinAddr)aDest, aLength); |
|
774 } |
|
775 #endif |
|
776 |
|
777 suspect?(void)umemget(pC,pS,len):(void)memcpy(pC,pS,len); |
|
778 |
|
779 #ifdef __DEMAND_PAGING__ |
|
780 XTRAP_PAGING_END |
|
781 if(check<0) |
|
782 return check; // paging error caused by bad client (I.e. 'this' thread was bad) |
|
783 if(check) |
|
784 { |
|
785 __KTRACE_OPT(KTHREAD2,Kern::Printf("DThread::RawWrite paging trap, suspect %d, dest %08x, src %08x, length %d\n", suspect, pC, pD, len)); |
|
786 continue; |
|
787 } |
|
788 #endif |
|
789 |
|
790 pD+=len; |
|
791 pS+=len; |
|
792 pC+=len; |
|
793 aLength-=len; |
|
794 if (aLength) |
|
795 check=NKern::FlashSystem(); |
|
796 } |
|
797 return KErrNone; |
|
798 } |
|
799 |
|
800 #ifdef __DEBUGGER_SUPPORT__ |
|
801 |
|
802 TInt CodeModifier::SafeWriteCode(DProcess* aProcess, TLinAddr aAddress, TInt aSize, TUint aValue, void* aOldValue) |
|
803 { |
|
804 //Set exception handler. Make sure the boundaries cover the worst case (aSize = 4) |
|
805 TIpcExcTrap xt; |
|
806 xt.iLocalBase=0; |
|
807 xt.iRemoteBase=(TLinAddr)aAddress&~3; //word aligned. |
|
808 xt.iSize=sizeof(TInt); |
|
809 xt.iDir=1; |
|
810 NKern::LockSystem(); |
|
811 TInt r=xt.Trap(NULL); |
|
812 if (r==0) |
|
813 { |
|
814 r = WriteCode(aAddress, aSize, aValue, aOldValue); |
|
815 xt.UnTrap(); |
|
816 } |
|
817 NKern::UnlockSystem(); |
|
818 return r; |
|
819 } |
|
820 |
|
821 TInt CodeModifier::WriteCode(TLinAddr aAddress, TInt aSize, TUint aValue, void* aOldValue) |
|
822 { |
|
823 TUint userChunkBase = (TUint)MM::UserCodeChunk->Base(); |
|
824 TRomHeader romHeader = Epoc::RomHeader(); |
|
825 |
|
826 if (!((aAddress >= romHeader.iRomBase ) && (aAddress < (romHeader.iRomBase + romHeader.iUncompressedSize)))) //if not in ROM |
|
827 if ( (aAddress<userChunkBase) || (aAddress) > (userChunkBase+MM::UserCodeChunk->MaxSize()) ) //and not in non-XIP code |
|
828 return KErrBadDescriptor; |
|
829 |
|
830 // if page was moved by defrag there may be a cache line with the |
|
831 // wrong, old physical address, so we must invalidate this first. |
|
832 InternalCache::Invalidate(KCacheSelectD, (TLinAddr)aAddress, 4); |
|
833 |
|
834 //Copy data and clean/invalidate caches with interrupts disabled. |
|
835 TInt irq=NKern::DisableAllInterrupts(); |
|
836 switch(aSize) |
|
837 { |
|
838 case 1: |
|
839 *(TUint8*) aOldValue = *(TUint8*)aAddress; |
|
840 *(TUint8*) aAddress = (TUint8)aValue; |
|
841 break; |
|
842 case 2: |
|
843 *(TUint16*) aOldValue = *(TUint16*)aAddress; |
|
844 *(TUint16*) aAddress = (TUint16)aValue; |
|
845 break; |
|
846 default://It is 4 otherwise |
|
847 *(TUint32*) aOldValue = *(TUint32*)aAddress; |
|
848 *(TUint32*) aAddress = (TUint32)aValue; |
|
849 break; |
|
850 }; |
|
851 CacheMaintenance::CodeChanged(aAddress, aSize, CacheMaintenance::ECodeModifier); |
|
852 NKern::RestoreInterrupts(irq); |
|
853 |
|
854 return KErrNone; |
|
855 } |
|
856 #endif //__DEBUGGER_SUPPORT__ |
|
857 |
|
858 TInt DThread::ReadAndParseDesHeader(const TAny* aSrc, TDesHeader& aDest) |
|
859 // |
|
860 // Read the header of a remote descriptor. |
|
861 // Enter and return with system locked |
|
862 // |
|
863 { |
|
864 TInt r=KErrBadDescriptor; |
|
865 DThread* thread = TheCurrentThread; |
|
866 TRawDesHeader& header = (TRawDesHeader&)aDest; |
|
867 |
|
868 #ifdef __DEMAND_PAGING__ |
|
869 retry: |
|
870 TInt pagingFault; |
|
871 XTRAP_PAGING_START(pagingFault); |
|
872 CHECK_PAGING_SAFE; |
|
873 thread->iIpcClient = this; |
|
874 #endif |
|
875 |
|
876 const TUint32* pS=(const TUint32*)MM::CurrentAddress(this,aSrc,sizeof(TDesC8),EFalse); |
|
877 if (pS && KErrNone==Kern::SafeRead(pS,&header[0],sizeof(TUint32))) |
|
878 { |
|
879 TInt type=header[0]>>KShiftDesType8; |
|
880 static const TUint8 LengthLookup[16]={4,8,12,8,12,0,0,0,0,0,0,0,0,0,0,0}; |
|
881 TInt len=LengthLookup[type]; |
|
882 if(len>(TInt)sizeof(TUint32)) |
|
883 { |
|
884 if(KErrNone==Kern::SafeRead(pS+1,&header[1],len-sizeof(TUint32))) |
|
885 r = type; |
|
886 // else, bad descriptor |
|
887 } |
|
888 else if(len) |
|
889 r = type; |
|
890 // else, bad descriptor |
|
891 } |
|
892 |
|
893 #ifdef __DEMAND_PAGING__ |
|
894 thread->iIpcClient = NULL; |
|
895 XTRAP_PAGING_END; |
|
896 if(pagingFault<0) |
|
897 return pagingFault; // paging error caused by bad client (I.e. 'this' thread was bad) |
|
898 if(pagingFault) |
|
899 goto retry; |
|
900 #endif |
|
901 |
|
902 return K::ParseDesHeader(aSrc, header, aDest); |
|
903 } |
|
904 |
|
905 DMemModelChunk* ChunkFromAddress(DThread* aThread, const TAny* aAddress) |
|
906 { |
|
907 DMemModelProcess* pP = (DMemModelProcess*)aThread->iOwningProcess; |
|
908 DMemModelProcess::SChunkInfo* pS=pP->iChunks; |
|
909 DMemModelProcess::SChunkInfo* pC=pS+pP->iNumChunks; |
|
910 while(--pC>=pS && TUint(pC->iDataSectionBase)>TUint(aAddress)) {}; |
|
911 if(pC<pS) |
|
912 return 0; |
|
913 return pC->iChunk; |
|
914 } |
|
915 |
|
916 /** |
|
917 Open a shared chunk in which a remote address range is located. |
|
918 */ |
|
919 DChunk* DThread::OpenSharedChunk(const TAny* aAddress, TBool aWrite, TInt& aOffset) |
|
920 { |
|
921 NKern::LockSystem(); |
|
922 |
|
923 DMemModelProcess* pP = (DMemModelProcess*)iOwningProcess; |
|
924 DMemModelProcess::SChunkInfo* pS=pP->iChunks; |
|
925 DMemModelProcess::SChunkInfo* pC=pS+pP->iNumChunks; |
|
926 while(--pC>=pS && TUint(pC->iDataSectionBase)>TUint(aAddress)) {}; |
|
927 if(pC>=pS) |
|
928 { |
|
929 DMemModelChunk* chunk = pC->iChunk; |
|
930 if(chunk->iChunkType==ESharedKernelSingle || chunk->iChunkType==ESharedKernelMultiple) |
|
931 { |
|
932 TInt offset = (TInt)aAddress-(TInt)chunk->Base(); |
|
933 if(TUint(offset)<TUint(chunk->iMaxSize) && chunk->Open()==KErrNone) |
|
934 { |
|
935 aOffset = offset; |
|
936 NKern::UnlockSystem(); |
|
937 return chunk; |
|
938 } |
|
939 } |
|
940 } |
|
941 NKern::UnlockSystem(); |
|
942 return 0; |
|
943 } |
|
944 |
|
945 TInt DThread::PrepareMemoryForDMA(const TAny* aLinAddr, TInt aSize, TPhysAddr* aPhysicalPageList) |
|
946 { |
|
947 if ((iOwningProcess->iAttributes & DMemModelProcess::EFixedAddress )==0) |
|
948 return KErrNotSupported; |
|
949 Mmu& m=(Mmu&)*MmuBase::TheMmu; |
|
950 return m.PreparePagesForDMA((TLinAddr)aLinAddr, aSize, aPhysicalPageList); |
|
951 } |
|
952 |
|
953 TInt DThread::ReleaseMemoryFromDMA(const TAny* aLinAddr, TInt aSize, TPhysAddr* aPhysicalPageList) |
|
954 { |
|
955 if ((iOwningProcess->iAttributes & DMemModelProcess::EFixedAddress )==0) |
|
956 return KErrNotSupported; |
|
957 TInt pageCount = (((TInt)aLinAddr & KPageMask) + aSize + KPageMask) >> KPageShift; |
|
958 Mmu& m=(Mmu&)*MmuBase::TheMmu; |
|
959 return m.ReleasePagesFromDMA(aPhysicalPageList, pageCount); |
|
960 } |