1 /*
2 * Copyright (c) 2017-2021, Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included
12 * in all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
21 */
22 //!
23 //! \file mos_context_specific.cpp
24 //! \brief Container for Linux/Android specific parameters shared across different GPU contexts of the same device instance
25 //!
26
27 #include "mos_os.h"
28 #include "mos_util_debug.h"
29 #include "mos_resource_defs.h"
30 #include <unistd.h>
31 #include <dlfcn.h>
32 #include "hwinfo_linux.h"
33 #include <stdlib.h>
34
35 #ifndef ANDROID
36 #include <sys/ipc.h>
37 #include <sys/shm.h>
38 #include <sys/sem.h>
39 #include <sys/types.h>
40 #include <time.h>
41 #endif
42
43 #if MOS_MEDIASOLO_SUPPORTED
44 #include "mos_os_solo.h"
45 #endif // MOS_MEDIASOLO_SUPPORTED
46 #include "mos_solo_generic.h"
47
48 #include "mos_context_specific.h"
49 #include "mos_gpucontextmgr.h"
50 #include "mos_cmdbufmgr.h"
51
OsContextSpecific()52 OsContextSpecific::OsContextSpecific()
53 {
54 for (int i = 0; i < MOS_GPU_CONTEXT_MAX; i++)
55 {
56 m_GpuContextHandle[i] = MOS_GPU_CONTEXT_INVALID_HANDLE;
57 }
58
59 MOS_OS_FUNCTION_ENTER;
60 }
61
~OsContextSpecific()62 OsContextSpecific::~OsContextSpecific()
63 {
64 MOS_OS_FUNCTION_ENTER;
65 }
66
67 #ifndef ANDROID
LockSemaphore(int32_t semid)68 MOS_STATUS OsContextSpecific::LockSemaphore(int32_t semid)
69 {
70 struct sembuf op[2];
71 op[0].sem_num = 0; // wait for [0] to be 0
72 op[0].sem_op = 0;
73 op[0].sem_flg = 0;
74 op[1].sem_num = 0;
75 op[1].sem_op = 1; // increment
76 op[1].sem_flg = SEM_UNDO;
77
78 if (semid < 0)
79 {
80 return MOS_STATUS_UNKNOWN;
81 }
82
83 if (semop(semid, op, 2) < 0)
84 {
85 return MOS_STATUS_UNKNOWN;
86 }
87
88 return MOS_STATUS_SUCCESS;
89 }
90
UnLockSemaphore(int32_t semid)91 MOS_STATUS OsContextSpecific::UnLockSemaphore(int32_t semid)
92 {
93 struct sembuf op;
94 op.sem_num = 0;
95 op.sem_op = -1; // decrement back to 0
96 op.sem_flg = SEM_UNDO;
97
98 if (semid < 0)
99 {
100 return MOS_STATUS_UNKNOWN;
101 }
102
103 if (semop(semid, &op, 1) < 0)
104 {
105 return MOS_STATUS_UNKNOWN;
106 }
107
108 return MOS_STATUS_SUCCESS;
109 }
110
ShmAttachedNumber(unsigned int shmid)111 int16_t OsContextSpecific::ShmAttachedNumber(unsigned int shmid)
112 {
113 struct shmid_ds buf;
114 MOS_ZeroMemory(&buf, sizeof(buf));
115
116 if (shmctl(shmid, IPC_STAT, &buf) < 0)
117 {
118 return -1;
119 }
120
121 return buf.shm_nattch;
122 }
123
DestroySemaphore(unsigned int semid)124 MOS_STATUS OsContextSpecific::DestroySemaphore(unsigned int semid)
125 {
126 int32_t nwait = 0;
127
128 nwait = semctl(semid, 0, GETZCNT, 0);
129
130 if (nwait > 0)
131 {
132 return MOS_STATUS_SUCCESS;
133 }
134
135 if (semctl(semid, 0, IPC_RMID, nullptr) < 0)
136 {
137 return MOS_STATUS_UNKNOWN;
138 }
139
140 return MOS_STATUS_SUCCESS;
141 }
142
ConnectCreateShm(long key,uint32_t size,int32_t * pShmid,void ** ppShm)143 MOS_STATUS OsContextSpecific::ConnectCreateShm(long key, uint32_t size, int32_t *pShmid, void* *ppShm)
144 {
145 MOS_STATUS eStatus = MOS_STATUS_SUCCESS;
146 MOS_OS_CHK_NULL_RETURN(pShmid);
147 MOS_OS_CHK_NULL_RETURN(ppShm);
148
149 struct shmid_ds buf;
150 int32_t shmid = 0;
151 key_t key_value = (key_t)key;
152 void *shmptr = nullptr;
153 MOS_ZeroMemory(&buf, sizeof(buf));
154
155 shmid = shmget(key_value, size, IPC_CREAT | 0666);
156 if (shmid < 0)
157 {
158 return MOS_STATUS_UNKNOWN;
159 }
160
161 shmptr = shmat(shmid, 0, 0);
162 if (shmptr == MOS_LINUX_SHM_INVALID)
163 {
164 DetachDestroyShm(shmid, shmptr);
165 return MOS_STATUS_UNKNOWN;
166 }
167
168 if (shmctl(shmid, IPC_STAT, &buf) < 0)
169 {
170 // can't get any status info
171 DetachDestroyShm(shmid, shmptr);
172 return MOS_STATUS_UNKNOWN;
173 }
174
175 *ppShm = shmptr;
176 *pShmid = shmid;
177
178 return MOS_STATUS_SUCCESS;
179 }
180
DetachDestroyShm(int32_t shmid,void * pShm)181 MOS_STATUS OsContextSpecific::DetachDestroyShm(int32_t shmid, void *pShm)
182 {
183 MOS_STATUS eStatus = MOS_STATUS_SUCCESS;
184 MOS_OS_CHK_NULL_RETURN(pShm);
185
186 struct shmid_ds buf;
187 MOS_ZeroMemory(&buf, sizeof(buf));
188
189 if (shmid < 0)
190 {
191 return MOS_STATUS_UNKNOWN;
192 }
193
194 if (pShm != MOS_LINUX_SHM_INVALID && shmdt(pShm) < 0)
195 {
196 return MOS_STATUS_UNKNOWN;
197 }
198
199 if (shmctl(shmid, IPC_STAT, &buf) < 0)
200 {
201 return MOS_STATUS_UNKNOWN;
202 }
203
204 if (buf.shm_nattch == 0)
205 {
206 if (shmctl(shmid, IPC_RMID, nullptr) < 0)
207 {
208 return MOS_STATUS_UNKNOWN;
209 }
210 }
211 return MOS_STATUS_SUCCESS;
212 }
213
ConnectCreateSemaphore(long key,int32_t * pSemid)214 MOS_STATUS OsContextSpecific::ConnectCreateSemaphore(long key, int32_t *pSemid)
215 {
216 MOS_STATUS eStatus = MOS_STATUS_SUCCESS;
217 MOS_OS_CHK_NULL_RETURN(pSemid);
218
219 int32_t semid = 0;
220 struct sembuf sop;
221 struct semid_ds buf;
222 key_t key_value = (key_t)key;
223 int32_t val = 0;
224
225 MOS_ZeroMemory(&sop, sizeof(sop));
226 MOS_ZeroMemory(&buf, sizeof(buf));
227
228 semid = semget(key, 1, IPC_CREAT | IPC_EXCL | 0666);
229
230 if (semid != MOS_LINUX_IPC_INVALID_ID)
231 {
232 // initialize it to 0
233 if (semctl(semid, 0, SETVAL, val) == -1)
234 {
235 return MOS_STATUS_UNKNOWN;
236 }
237
238 // Perform a "no-op" semaphore operation - changes sem_otime
239 // so other processes can see we've initialized the set.
240 sop.sem_num = 0;
241 sop.sem_op = 0; //Wait for value to equal 0
242 sop.sem_flg = 0;
243 if (semop(semid, &sop, 1) == -1)
244 {
245 return MOS_STATUS_UNKNOWN;
246 }
247 }
248 else
249 {
250 // errno EEXIST
251 semid = semget(key, 1, 0666);
252 if (semid == MOS_LINUX_IPC_INVALID_ID)
253 {
254 return MOS_STATUS_UNKNOWN;
255 }
256 }
257
258 *pSemid = semid;
259
260 return MOS_STATUS_SUCCESS;
261 }
262
CreateIPC()263 MOS_STATUS OsContextSpecific::CreateIPC()
264 {
265 MOS_STATUS eStatus;
266
267 m_semId = MOS_LINUX_IPC_INVALID_ID;
268 m_shmId = MOS_LINUX_IPC_INVALID_ID;
269 m_shm = MOS_LINUX_SHM_INVALID;
270 if (m_apoMosEnabled)
271 {
272 return MOS_STATUS_SUCCESS;
273 }
274
275 struct semid_ds buf;
276 MOS_ZeroMemory(&buf, sizeof(buf));
277 //wait and retry till to get a valid semphore
278 for(int i = 0; i < MOS_LINUX_SEM_MAX_TRIES; i++)
279 {
280 ConnectCreateSemaphore(m_dualVdboxKey, &m_semId);
281
282 if (m_semId == MOS_LINUX_IPC_INVALID_ID)
283 {
284 return MOS_STATUS_UNKNOWN;
285 }
286 //check wether the semid is initialized or not
287 if (semctl(m_semId, 0, IPC_STAT, &buf) == -1)
288 {
289 return MOS_STATUS_UNKNOWN;
290 }
291 if (buf.sem_otime != 0)
292 {
293 break;
294 }
295
296 MosUtilities::MosSleep(1); //wait and retry
297 }
298
299 LockSemaphore(m_semId);
300 eStatus = ConnectCreateShm(m_dualVdboxKey, sizeof(VDBOX_WORKLOAD), &(m_shmId), &(m_shm));
301 UnLockSemaphore(m_semId);
302 MOS_CHK_STATUS_SAFE(eStatus);
303
304 finish:
305 return eStatus;
306 }
307
DestroyIPC()308 void OsContextSpecific::DestroyIPC()
309 {
310 if (m_apoMosEnabled)
311 {
312 return;
313 }
314
315 if (MOS_LINUX_IPC_INVALID_ID != m_semId)
316 {
317 int16_t iAttachedNum = 0;
318
319 if (MOS_LINUX_IPC_INVALID_ID != m_shmId)
320 {
321 LockSemaphore(m_semId);
322 iAttachedNum = ShmAttachedNumber(m_shmId);
323
324 DetachDestroyShm(m_shmId, m_shm);
325 m_shmId = MOS_LINUX_IPC_INVALID_ID;
326 m_shm = MOS_LINUX_SHM_INVALID;
327
328 if (iAttachedNum)
329 {
330 --iAttachedNum;
331 }
332 UnLockSemaphore(m_semId);
333 }
334 }
335 }
336
CreateSSEUIPC()337 MOS_STATUS OsContextSpecific::CreateSSEUIPC()
338 {
339 MOS_STATUS eStatus = MOS_STATUS_SUCCESS;
340
341 m_sseuSemId = MOS_LINUX_IPC_INVALID_ID;
342 m_sseuShmId = MOS_LINUX_IPC_INVALID_ID;
343 m_sseuShm = MOS_LINUX_SHM_INVALID;
344 if (m_apoMosEnabled)
345 {
346 return MOS_STATUS_SUCCESS;
347 }
348 return eStatus;
349 }
350
DestroySSEUIPC()351 void OsContextSpecific::DestroySSEUIPC()
352 {
353 if (m_apoMosEnabled)
354 {
355 return;
356 }
357 if (MOS_LINUX_IPC_INVALID_ID != m_sseuSemId)
358 {
359 short iAttachedNum = 0;
360
361 if (MOS_LINUX_IPC_INVALID_ID != m_sseuShmId)
362 {
363 LockSemaphore(m_sseuSemId);
364 iAttachedNum = ShmAttachedNumber(m_sseuShmId);
365
366 DetachDestroyShm(m_sseuShmId, m_sseuShm);
367 m_sseuShmId = MOS_LINUX_IPC_INVALID_ID;
368 m_sseuShm = MOS_LINUX_SHM_INVALID;
369
370 if (iAttachedNum)
371 {
372 --iAttachedNum;
373 }
374 UnLockSemaphore(m_sseuSemId);
375 }
376 }
377 }
378
SetSliceCount(uint32_t * pSliceCount)379 void OsContextSpecific::SetSliceCount(uint32_t *pSliceCount)
380 {
381 if (pSliceCount == nullptr)
382 MOS_OS_ASSERTMESSAGE("pSliceCount is NULL.");
383 }
384
385 #endif //#ifndef ANDROID
386
GetGpuPriority(int32_t * pPriority)387 void OsContextSpecific::GetGpuPriority(int32_t* pPriority)
388 {
389 uint64_t priority = 0;
390 mos_get_context_param(m_intelContext, 0, I915_CONTEXT_PARAM_PRIORITY, &priority);
391 *pPriority = (int32_t)priority;
392 }
393
SetGpuPriority(int32_t priority)394 void OsContextSpecific::SetGpuPriority(int32_t priority)
395 {
396 int ret = 0;
397 ret = mos_set_context_param(m_intelContext, 0, I915_CONTEXT_PARAM_PRIORITY,(uint64_t)priority);
398 if (ret != 0)
399 {
400 MOS_OS_NORMALMESSAGE("Warning: failed to set the gpu priority, errno is %d", ret);
401 }
402 }
403
Init(PMOS_CONTEXT pOsDriverContext)404 MOS_STATUS OsContextSpecific::Init(PMOS_CONTEXT pOsDriverContext)
405 {
406 uint32_t iDeviceId = 0;
407 MOS_STATUS eStatus;
408 uint32_t i = 0;
409
410 MOS_OS_FUNCTION_ENTER;
411
412 eStatus = MOS_STATUS_SUCCESS;
413
414 if (GetOsContextValid() == false)
415 {
416 if( nullptr == pOsDriverContext ||
417 nullptr == pOsDriverContext->bufmgr ||
418 0 > pOsDriverContext->fd )
419 {
420 MOS_OS_ASSERT(false);
421 return MOS_STATUS_INVALID_HANDLE;
422 }
423 m_apoMosEnabled = pOsDriverContext->m_apoMosEnabled;
424 m_bufmgr = pOsDriverContext->bufmgr;
425 m_gpuContextMgr = static_cast<GpuContextMgr *>(pOsDriverContext->m_gpuContextMgr);
426 m_cmdBufMgr = static_cast<CmdBufMgr *>(pOsDriverContext->m_cmdBufMgr);
427 m_fd = pOsDriverContext->fd;
428 MOS_SecureMemcpy(&m_perfData, sizeof(PERF_DATA), pOsDriverContext->pPerfData, sizeof(PERF_DATA));
429 mos_bufmgr_enable_reuse(pOsDriverContext->bufmgr);
430 m_pGmmClientContext = pOsDriverContext->pGmmClientContext;
431 m_auxTableMgr = pOsDriverContext->m_auxTableMgr;
432
433 // DDI layer can pass over the DeviceID.
434 iDeviceId = pOsDriverContext->iDeviceId;
435 if (0 == iDeviceId)
436 {
437 PLATFORM platformInfo;
438 MEDIA_FEATURE_TABLE skuTable;
439 MEDIA_WA_TABLE waTable;
440 MEDIA_SYSTEM_INFO gtSystemInfo;
441
442 MOS_ZeroMemory(&platformInfo, sizeof(platformInfo));
443 skuTable.reset();
444 waTable.reset();
445 MOS_ZeroMemory(>SystemInfo, sizeof(gtSystemInfo));
446 eStatus = HWInfo_GetGfxInfo(pOsDriverContext->fd, pOsDriverContext->bufmgr, &platformInfo, &skuTable, &waTable, >SystemInfo, pOsDriverContext->m_userSettingPtr);
447 if (eStatus != MOS_STATUS_SUCCESS)
448 {
449 MOS_OS_ASSERTMESSAGE("Fatal error - unsuccesfull Sku/Wa/GtSystemInfo initialization");
450 return eStatus;
451 }
452
453 MOS_SecureMemcpy(&m_platformInfo, sizeof(PLATFORM), &platformInfo, sizeof(PLATFORM));
454 MOS_SecureMemcpy(&m_gtSystemInfo, sizeof(MEDIA_SYSTEM_INFO), >SystemInfo, sizeof(MEDIA_SYSTEM_INFO));
455
456 pOsDriverContext->iDeviceId = platformInfo.usDeviceID;
457 m_skuTable = skuTable;
458 m_waTable = waTable;
459
460 pOsDriverContext->m_skuTable = skuTable;
461 pOsDriverContext->m_waTable = waTable;
462 pOsDriverContext->m_gtSystemInfo = gtSystemInfo;
463 pOsDriverContext->m_platform = platformInfo;
464
465 MOS_OS_NORMALMESSAGE("DeviceID was created DeviceID = %d, platform product %d", iDeviceId, platformInfo.eProductFamily);
466 }
467 else
468 {
469 // pOsDriverContext's parameters were passed by CmCreateDevice.
470 // Get SkuTable/WaTable/systemInfo/platform from OSDriver directly.
471 MOS_SecureMemcpy(&m_platformInfo, sizeof(PLATFORM), &(pOsDriverContext->m_platform), sizeof(PLATFORM));
472 MOS_SecureMemcpy(&m_gtSystemInfo, sizeof(MEDIA_SYSTEM_INFO), &(pOsDriverContext->m_gtSystemInfo), sizeof(MEDIA_SYSTEM_INFO));
473
474 m_skuTable = pOsDriverContext->m_skuTable;
475 m_waTable = pOsDriverContext->m_waTable;
476 }
477
478 m_use64BitRelocs = true;
479 m_useSwSwizzling = pOsDriverContext->bSimIsActive || MEDIA_IS_SKU(&m_skuTable, FtrUseSwSwizzling);
480 m_tileYFlag = MEDIA_IS_SKU(&m_skuTable, FtrTileY);
481
482 MOS_TraceEventExt(EVENT_GPU_CONTEXT_CREATE, EVENT_TYPE_START,
483 &eStatus, sizeof(eStatus), nullptr, 0);
484 if (!Mos_Solo_IsEnabled(nullptr) && MEDIA_IS_SKU(&m_skuTable,FtrContextBasedScheduling))
485 {
486 m_intelContext = mos_context_create_ext(pOsDriverContext->bufmgr,0, pOsDriverContext->m_protectedGEMContext);
487 if (m_intelContext)
488 {
489 m_intelContext->vm_id = mos_vm_create(pOsDriverContext->bufmgr);
490 if (m_intelContext->vm_id == INVALID_VM)
491 {
492 MOS_OS_ASSERTMESSAGE("Failed to create vm.\n");
493 return MOS_STATUS_UNKNOWN;
494 }
495 }
496 }
497 else //use legacy context create ioctl for pre-gen11 platforms
498 {
499 m_intelContext = mos_context_create(pOsDriverContext->bufmgr);
500 if (m_intelContext)
501 {
502 m_intelContext->vm_id = INVALID_VM;
503 }
504 }
505 MOS_TraceEventExt(EVENT_GPU_CONTEXT_CREATE, EVENT_TYPE_END,
506 &m_intelContext, sizeof(void *),
507 &eStatus, sizeof(eStatus));
508
509 if (m_intelContext == nullptr)
510 {
511 MOS_OS_ASSERTMESSAGE("Failed to create drm intel context");
512 return MOS_STATUS_UNKNOWN;
513 }
514
515 m_isAtomSOC = IS_ATOMSOC(iDeviceId);
516
517 #ifndef ANDROID
518
519 if ((m_gtSystemInfo.VDBoxInfo.IsValid) && (m_gtSystemInfo.VDBoxInfo.NumberOfVDBoxEnabled > 1))
520 {
521 m_kmdHasVCS2 = true;
522 }
523 else
524 {
525 m_kmdHasVCS2 = false;
526 }
527
528 if (m_kmdHasVCS2)
529 {
530 eStatus = CreateIPC();
531 if (eStatus != MOS_STATUS_SUCCESS)
532 {
533 MOS_OS_ASSERTMESSAGE("Fatal error - create IPC failed");
534 return eStatus;
535 }
536 }
537
538 eStatus = CreateSSEUIPC();
539 if (eStatus != MOS_STATUS_SUCCESS)
540 {
541 MOS_OS_ASSERTMESSAGE("Fatal error - Failed to create shared memory for SSEU configuration.");
542 return eStatus;
543 }
544 #endif
545
546 m_transcryptedKernels = nullptr;
547 m_transcryptedKernelsSize = 0;
548
549 // For Media Memory compression
550 m_mediaMemDecompState = pOsDriverContext->ppMediaMemDecompState;
551 m_memoryDecompress = pOsDriverContext->pfnMemoryDecompress;
552 m_mediaMemCopy = pOsDriverContext->pfnMediaMemoryCopy;
553 m_mediaMemCopy2D = pOsDriverContext->pfnMediaMemoryCopy2D;
554 m_mosContext = pOsDriverContext;
555
556 m_noParsingAssistanceInKmd = true;
557 m_numNalUnitBytesIncluded = MOS_NAL_UNIT_LENGTH - MOS_NAL_UNIT_STARTCODE_LENGTH;
558
559 // Init reset count for the context
560 uint32_t dwResetCount = 0;
561 mos_get_reset_stats(m_intelContext, &dwResetCount, nullptr, nullptr);
562 m_gpuResetCount = dwResetCount;
563 m_gpuActiveBatch = 0;
564 m_gpuPendingBatch = 0;
565
566 m_usesPatchList = true;
567 m_usesGfxAddress = false;
568
569 m_inlineCodecStatusUpdate = true;
570
571 SetOsContextValid(true);
572 }
573 return eStatus;
574 }
575
Destroy()576 void OsContextSpecific::Destroy()
577 {
578 MOS_OS_FUNCTION_ENTER;
579
580 if (GetOsContextValid() == true)
581 {
582 // APO MOS will destory each stream's GPU context at different place
583 if (!m_apoMosEnabled)
584 {
585 for (auto i = 0; i < MOS_GPU_CONTEXT_MAX; i++)
586 {
587 if (m_GpuContextHandle[i] != MOS_GPU_CONTEXT_INVALID_HANDLE)
588 {
589 if (m_gpuContextMgr == nullptr)
590 {
591 MOS_OS_ASSERTMESSAGE("GpuContextMgr is null when destroy GpuContext");
592 break;
593 }
594 auto gpuContext = m_gpuContextMgr->GetGpuContext(m_GpuContextHandle[i]);
595 if (gpuContext == nullptr)
596 {
597 MOS_OS_ASSERTMESSAGE("cannot find the gpuContext corresponding to the active gpuContextHandle");
598 continue;
599 }
600 m_gpuContextMgr->DestroyGpuContext(gpuContext);
601 }
602 }
603 }
604
605 #ifndef ANDROID
606 if (m_kmdHasVCS2)
607 {
608 DestroyIPC();
609 }
610 DestroySSEUIPC();
611 #endif
612 m_skuTable.reset();
613 m_waTable.reset();
614 MOS_TraceEventExt(EVENT_GPU_CONTEXT_DESTROY, EVENT_TYPE_START,
615 &m_intelContext, sizeof(void *), nullptr, 0);
616 if (m_intelContext && m_intelContext->vm_id != INVALID_VM)
617 {
618 mos_vm_destroy(m_intelContext->bufmgr, m_intelContext->vm_id);
619 m_intelContext->vm_id = INVALID_VM;
620 }
621 if (m_intelContext)
622 {
623 mos_context_destroy(m_intelContext);
624 }
625 MOS_TraceEventExt(EVENT_GPU_CONTEXT_DESTROY, EVENT_TYPE_END,
626 nullptr, 0, nullptr, 0);
627 SetOsContextValid(false);
628 }
629 }
630
631