mirror of https://gitlab.com/nakst/essence
move ArchStartupApplicationProcessors to x86_pc.cpp
This commit is contained in:
parent
ed038d1059
commit
cd51bf4d6c
|
@ -189,10 +189,6 @@ void MMArchFinalizeVAS(MMSpace *space) {
|
||||||
KernelPanic("Unimplemented!\n");
|
KernelPanic("Unimplemented!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void ArchStartupApplicationProcessors() {
|
|
||||||
// TODO.
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MMArchHandlePageFault(uintptr_t address, uint32_t flags) {
|
bool MMArchHandlePageFault(uintptr_t address, uint32_t flags) {
|
||||||
address &= ~(K_PAGE_SIZE - 1);
|
address &= ~(K_PAGE_SIZE - 1);
|
||||||
bool forSupervisor = flags & MM_HANDLE_PAGE_FAULT_FOR_SUPERVISOR;
|
bool forSupervisor = flags & MM_HANDLE_PAGE_FAULT_FOR_SUPERVISOR;
|
||||||
|
|
|
@ -21,10 +21,12 @@
|
||||||
[global ProcessorFakeTimerInterrupt]
|
[global ProcessorFakeTimerInterrupt]
|
||||||
[global ProcessorSetAddressSpace]
|
[global ProcessorSetAddressSpace]
|
||||||
[global ProcessorFlushCodeCache]
|
[global ProcessorFlushCodeCache]
|
||||||
|
[global ProcessorAPStartup]
|
||||||
[global GetLocalStorage]
|
[global GetLocalStorage]
|
||||||
[global GetCurrentThread]
|
[global GetCurrentThread]
|
||||||
[global ArchSwitchContext]
|
[global ArchSwitchContext]
|
||||||
[global processorGDTR]
|
[global processorGDTR]
|
||||||
|
[global gdt_data]
|
||||||
[global timeStampCounterSynchronizationValue]
|
[global timeStampCounterSynchronizationValue]
|
||||||
|
|
||||||
[extern ArchNextTimer]
|
[extern ArchNextTimer]
|
||||||
|
@ -551,3 +553,35 @@ ProcessorSetAddressSpace:
|
||||||
ProcessorFlushCodeCache:
|
ProcessorFlushCodeCache:
|
||||||
wbinvd
|
wbinvd
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
SynchronizeTimeStampCounter:
|
||||||
|
; TODO
|
||||||
|
ret
|
||||||
|
|
||||||
|
[bits 16]
|
||||||
|
ProcessorAPStartup: ; This function must be less than 4KB in length (see drivers/acpi.cpp)
|
||||||
|
mov ax,0x1000
|
||||||
|
mov ds,ax
|
||||||
|
mov byte [0xFC0],1 ; Indicate we've started.
|
||||||
|
mov eax,[0xFF0]
|
||||||
|
mov cr3,eax
|
||||||
|
lgdt [0x1000 + gdt_data.gdt - gdt_data]
|
||||||
|
mov eax,cr0
|
||||||
|
or eax,1
|
||||||
|
mov cr0,eax
|
||||||
|
jmp 0x8:dword (.pmode - ProcessorAPStartup + 0x10000)
|
||||||
|
[bits 32]
|
||||||
|
.pmode:
|
||||||
|
mov eax,0x10
|
||||||
|
mov ds,ax
|
||||||
|
mov es,ax
|
||||||
|
mov ss,ax
|
||||||
|
lgdt [gdt_data.gdt]
|
||||||
|
mov esp,[0x10FD0]
|
||||||
|
call SetupProcessor1
|
||||||
|
call SynchronizeTimeStampCounter
|
||||||
|
mov edi,[0x10FB0]
|
||||||
|
push edi
|
||||||
|
call SetupProcessor2
|
||||||
|
mov byte [0x10FC0],2 ; Indicate the BSP can start the next processor.
|
||||||
|
jmp ProcessorReady
|
||||||
|
|
|
@ -62,15 +62,10 @@ struct MMArchVAS {
|
||||||
|
|
||||||
uint8_t coreL1Commit[(0xFFFF800200000000 - 0xFFFF800100000000) >> (/* ENTRIES_PER_PAGE_TABLE_BITS */ 9 + K_PAGE_BITS + 3)];
|
uint8_t coreL1Commit[(0xFFFF800200000000 - 0xFFFF800100000000) >> (/* ENTRIES_PER_PAGE_TABLE_BITS */ 9 + K_PAGE_BITS + 3)];
|
||||||
|
|
||||||
extern "C" void gdt_data();
|
|
||||||
extern "C" void processorGDTR();
|
|
||||||
|
|
||||||
extern "C" uint64_t ProcessorReadCR3();
|
|
||||||
extern "C" uintptr_t ProcessorGetRSP();
|
extern "C" uintptr_t ProcessorGetRSP();
|
||||||
extern "C" uintptr_t ProcessorGetRBP();
|
extern "C" uintptr_t ProcessorGetRBP();
|
||||||
extern "C" uint64_t ProcessorReadMXCSR();
|
extern "C" uint64_t ProcessorReadMXCSR();
|
||||||
extern "C" void ProcessorInstallTSS(uint32_t *gdt, uint32_t *tss);
|
extern "C" void ProcessorInstallTSS(uint32_t *gdt, uint32_t *tss);
|
||||||
extern "C" void ProcessorAPStartup();
|
|
||||||
|
|
||||||
extern "C" void SSSE3Framebuffer32To24Copy(volatile uint8_t *destination, volatile uint8_t *source, size_t pixelGroups);
|
extern "C" void SSSE3Framebuffer32To24Copy(volatile uint8_t *destination, volatile uint8_t *source, size_t pixelGroups);
|
||||||
extern "C" uintptr_t _KThreadTerminate;
|
extern "C" uintptr_t _KThreadTerminate;
|
||||||
|
@ -655,105 +650,6 @@ EsError ArchApplyRelocation(uintptr_t type, uint8_t *buffer, uintptr_t offset, u
|
||||||
return ES_SUCCESS;
|
return ES_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ArchStartupApplicationProcessors() {
|
|
||||||
// TODO How do we know that this address is usable?
|
|
||||||
#define AP_TRAMPOLINE 0x10000
|
|
||||||
|
|
||||||
KEvent delay = {};
|
|
||||||
|
|
||||||
uint8_t *startupData = (uint8_t *) (LOW_MEMORY_MAP_START + AP_TRAMPOLINE);
|
|
||||||
|
|
||||||
// Put the trampoline code in memory.
|
|
||||||
EsMemoryCopy(startupData, (void *) ProcessorAPStartup, 0x1000); // Assume that the AP trampoline code <=4KB.
|
|
||||||
|
|
||||||
// Put the paging table location at AP_TRAMPOLINE + 0xFF0.
|
|
||||||
*((uint64_t *) (startupData + 0xFF0)) = ProcessorReadCR3();
|
|
||||||
|
|
||||||
// Put the 64-bit GDTR at AP_TRAMPOLINE + 0xFE0.
|
|
||||||
EsMemoryCopy(startupData + 0xFE0, (void *) processorGDTR, 0x10);
|
|
||||||
|
|
||||||
// Put the GDT at AP_TRAMPOLINE + 0x1000.
|
|
||||||
EsMemoryCopy(startupData + 0x1000, (void *) gdt_data, 0x1000);
|
|
||||||
|
|
||||||
// Put the startup flag at AP_TRAMPOLINE + 0xFC0
|
|
||||||
uint8_t volatile *startupFlag = (uint8_t *) (LOW_MEMORY_MAP_START + AP_TRAMPOLINE + 0xFC0);
|
|
||||||
|
|
||||||
// Temporarily identity map 2 pages in at 0x10000.
|
|
||||||
MMArchMapPage(kernelMMSpace, AP_TRAMPOLINE, AP_TRAMPOLINE, MM_MAP_PAGE_COMMIT_TABLES_NOW);
|
|
||||||
MMArchMapPage(kernelMMSpace, AP_TRAMPOLINE + 0x1000, AP_TRAMPOLINE + 0x1000, MM_MAP_PAGE_COMMIT_TABLES_NOW);
|
|
||||||
|
|
||||||
for (uintptr_t i = 0; i < acpi.processorCount; i++) {
|
|
||||||
ArchCPU *processor = acpi.processors + i;
|
|
||||||
if (processor->bootProcessor) continue;
|
|
||||||
|
|
||||||
// Allocate state for the processor.
|
|
||||||
NewProcessorStorage storage = AllocateNewProcessorStorage(processor);
|
|
||||||
|
|
||||||
// Clear the startup flag.
|
|
||||||
*startupFlag = 0;
|
|
||||||
|
|
||||||
// Put the stack at AP_TRAMPOLINE + 0xFD0, and the address of the NewProcessorStorage at AP_TRAMPOLINE + 0xFB0.
|
|
||||||
void *stack = (void *) ((uintptr_t) MMStandardAllocate(kernelMMSpace, 0x1000, MM_REGION_FIXED) + 0x1000);
|
|
||||||
*((void **) (startupData + 0xFD0)) = stack;
|
|
||||||
*((NewProcessorStorage **) (startupData + 0xFB0)) = &storage;
|
|
||||||
|
|
||||||
KernelLog(LOG_INFO, "ACPI", "starting processor", "Starting processor %d with local storage %x...\n", i, storage.local);
|
|
||||||
|
|
||||||
// Send an INIT IPI.
|
|
||||||
ProcessorDisableInterrupts(); // Don't be interrupted between writes...
|
|
||||||
LapicWriteRegister(0x310 >> 2, processor->apicID << 24);
|
|
||||||
LapicWriteRegister(0x300 >> 2, 0x4500);
|
|
||||||
ProcessorEnableInterrupts();
|
|
||||||
KEventWait(&delay, 10);
|
|
||||||
|
|
||||||
// Send a startup IPI.
|
|
||||||
ProcessorDisableInterrupts();
|
|
||||||
LapicWriteRegister(0x310 >> 2, processor->apicID << 24);
|
|
||||||
LapicWriteRegister(0x300 >> 2, 0x4600 | (AP_TRAMPOLINE >> K_PAGE_BITS));
|
|
||||||
ProcessorEnableInterrupts();
|
|
||||||
for (uintptr_t i = 0; i < 100 && *startupFlag == 0; i++) KEventWait(&delay, 1);
|
|
||||||
|
|
||||||
if (*startupFlag) {
|
|
||||||
// The processor started correctly.
|
|
||||||
} else {
|
|
||||||
// Send a startup IPI, again.
|
|
||||||
ProcessorDisableInterrupts();
|
|
||||||
LapicWriteRegister(0x310 >> 2, processor->apicID << 24);
|
|
||||||
LapicWriteRegister(0x300 >> 2, 0x4600 | (AP_TRAMPOLINE >> K_PAGE_BITS));
|
|
||||||
ProcessorEnableInterrupts();
|
|
||||||
for (uintptr_t i = 0; i < 1000 && *startupFlag == 0; i++) KEventWait(&delay, 1); // Wait longer this time.
|
|
||||||
|
|
||||||
if (*startupFlag) {
|
|
||||||
// The processor started correctly.
|
|
||||||
} else {
|
|
||||||
// The processor could not be started.
|
|
||||||
KernelLog(LOG_ERROR, "ACPI", "processor startup failure",
|
|
||||||
"ACPIInitialise - Could not start processor %d\n", processor->processorID);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// EsPrint("Startup flag 1 reached!\n");
|
|
||||||
|
|
||||||
for (uintptr_t i = 0; i < 10000 && *startupFlag != 2; i++) KEventWait(&delay, 1);
|
|
||||||
|
|
||||||
if (*startupFlag == 2) {
|
|
||||||
// The processor started!
|
|
||||||
} else {
|
|
||||||
// The processor did not report it completed initilisation, worringly.
|
|
||||||
// Don't let it continue.
|
|
||||||
|
|
||||||
KernelLog(LOG_ERROR, "ACPI", "processor startup failure",
|
|
||||||
"ACPIInitialise - Could not initialise processor %d\n", processor->processorID);
|
|
||||||
|
|
||||||
// TODO Send IPI to stop the processor.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove the identity pages needed for the trampoline code.
|
|
||||||
MMArchUnmapPages(kernelMMSpace, AP_TRAMPOLINE, 2, ES_FLAGS_DEFAULT);
|
|
||||||
}
|
|
||||||
|
|
||||||
#include <kernel/terminal.cpp>
|
#include <kernel/terminal.cpp>
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
109
arch/x86_pc.cpp
109
arch/x86_pc.cpp
|
@ -2,6 +2,10 @@
|
||||||
|
|
||||||
extern "C" uint64_t ProcessorReadCR3();
|
extern "C" uint64_t ProcessorReadCR3();
|
||||||
|
|
||||||
|
extern "C" void gdt_data();
|
||||||
|
extern "C" void processorGDTR();
|
||||||
|
extern "C" void ProcessorAPStartup();
|
||||||
|
|
||||||
struct MSIHandler {
|
struct MSIHandler {
|
||||||
KIRQHandler callback;
|
KIRQHandler callback;
|
||||||
void *context;
|
void *context;
|
||||||
|
@ -576,7 +580,7 @@ uintptr_t ArchFindRootSystemDescriptorPointer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t ArchGetTimeFromPITMs() {
|
uint64_t ArchGetTimeFromPITMs() {
|
||||||
// TODO This isn't working on real hardware, but ArchDelay1Ms is?
|
// TODO This isn't working on real hardware, but EarlyDelay1Ms is?
|
||||||
|
|
||||||
// NOTE This will only work if called at least once every 50 ms.
|
// NOTE This will only work if called at least once every 50 ms.
|
||||||
// (The PIT only stores a 16-bit counter, which is depleted every 50 ms.)
|
// (The PIT only stores a 16-bit counter, which is depleted every 50 ms.)
|
||||||
|
@ -602,7 +606,7 @@ uint64_t ArchGetTimeFromPITMs() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ArchDelay1Ms() {
|
void EarlyDelay1Ms() {
|
||||||
ProcessorOut8(IO_PIT_COMMAND, 0x30);
|
ProcessorOut8(IO_PIT_COMMAND, 0x30);
|
||||||
ProcessorOut8(IO_PIT_DATA, 0xA9);
|
ProcessorOut8(IO_PIT_DATA, 0xA9);
|
||||||
ProcessorOut8(IO_PIT_DATA, 0x04);
|
ProcessorOut8(IO_PIT_DATA, 0x04);
|
||||||
|
@ -693,7 +697,7 @@ void ArchInitialise() {
|
||||||
ProcessorDisableInterrupts();
|
ProcessorDisableInterrupts();
|
||||||
uint64_t start = ProcessorReadTimeStamp();
|
uint64_t start = ProcessorReadTimeStamp();
|
||||||
LapicWriteRegister(0x380 >> 2, (uint32_t) -1);
|
LapicWriteRegister(0x380 >> 2, (uint32_t) -1);
|
||||||
for (int i = 0; i < 8; i++) ArchDelay1Ms(); // Average over 8ms
|
for (int i = 0; i < 8; i++) EarlyDelay1Ms(); // Average over 8ms
|
||||||
acpi.lapicTicksPerMs = ((uint32_t) -1 - LapicReadRegister(0x390 >> 2)) >> 4;
|
acpi.lapicTicksPerMs = ((uint32_t) -1 - LapicReadRegister(0x390 >> 2)) >> 4;
|
||||||
EsRandomAddEntropy(LapicReadRegister(0x390 >> 2));
|
EsRandomAddEntropy(LapicReadRegister(0x390 >> 2));
|
||||||
uint64_t end = ProcessorReadTimeStamp();
|
uint64_t end = ProcessorReadTimeStamp();
|
||||||
|
@ -988,3 +992,102 @@ bool KRegisterIRQ(intptr_t line, KIRQHandler handler, void *context, const char
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ArchStartupApplicationProcessors() {
|
||||||
|
// TODO How do we know that this address is usable?
|
||||||
|
#define AP_TRAMPOLINE 0x10000
|
||||||
|
|
||||||
|
KEvent delay = {};
|
||||||
|
|
||||||
|
uint8_t *startupData = (uint8_t *) (LOW_MEMORY_MAP_START + AP_TRAMPOLINE);
|
||||||
|
|
||||||
|
// Put the trampoline code in memory.
|
||||||
|
EsMemoryCopy(startupData, (void *) ProcessorAPStartup, 0x1000); // Assume that the AP trampoline code <=4KB.
|
||||||
|
|
||||||
|
// Put the paging table location at AP_TRAMPOLINE + 0xFF0.
|
||||||
|
*((uint64_t *) (startupData + 0xFF0)) = ProcessorReadCR3();
|
||||||
|
|
||||||
|
// Put the 64-bit GDTR at AP_TRAMPOLINE + 0xFE0.
|
||||||
|
EsMemoryCopy(startupData + 0xFE0, (void *) processorGDTR, 0x10);
|
||||||
|
|
||||||
|
// Put the GDT at AP_TRAMPOLINE + 0x1000.
|
||||||
|
EsMemoryCopy(startupData + 0x1000, (void *) gdt_data, 0x1000);
|
||||||
|
|
||||||
|
// Put the startup flag at AP_TRAMPOLINE + 0xFC0
|
||||||
|
uint8_t volatile *startupFlag = (uint8_t *) (LOW_MEMORY_MAP_START + AP_TRAMPOLINE + 0xFC0);
|
||||||
|
|
||||||
|
// Temporarily identity map 2 pages in at 0x10000.
|
||||||
|
MMArchMapPage(kernelMMSpace, AP_TRAMPOLINE, AP_TRAMPOLINE, MM_MAP_PAGE_COMMIT_TABLES_NOW);
|
||||||
|
MMArchMapPage(kernelMMSpace, AP_TRAMPOLINE + 0x1000, AP_TRAMPOLINE + 0x1000, MM_MAP_PAGE_COMMIT_TABLES_NOW);
|
||||||
|
|
||||||
|
for (uintptr_t i = 0; i < acpi.processorCount; i++) {
|
||||||
|
ArchCPU *processor = acpi.processors + i;
|
||||||
|
if (processor->bootProcessor) continue;
|
||||||
|
|
||||||
|
// Allocate state for the processor.
|
||||||
|
NewProcessorStorage storage = AllocateNewProcessorStorage(processor);
|
||||||
|
|
||||||
|
// Clear the startup flag.
|
||||||
|
*startupFlag = 0;
|
||||||
|
|
||||||
|
// Put the stack at AP_TRAMPOLINE + 0xFD0, and the address of the NewProcessorStorage at AP_TRAMPOLINE + 0xFB0.
|
||||||
|
void *stack = (void *) ((uintptr_t) MMStandardAllocate(kernelMMSpace, 0x1000, MM_REGION_FIXED) + 0x1000);
|
||||||
|
*((void **) (startupData + 0xFD0)) = stack;
|
||||||
|
*((NewProcessorStorage **) (startupData + 0xFB0)) = &storage;
|
||||||
|
|
||||||
|
KernelLog(LOG_INFO, "ACPI", "starting processor", "Starting processor %d with local storage %x...\n", i, storage.local);
|
||||||
|
|
||||||
|
// Send an INIT IPI.
|
||||||
|
ProcessorDisableInterrupts(); // Don't be interrupted between writes...
|
||||||
|
LapicWriteRegister(0x310 >> 2, processor->apicID << 24);
|
||||||
|
LapicWriteRegister(0x300 >> 2, 0x4500);
|
||||||
|
ProcessorEnableInterrupts();
|
||||||
|
KEventWait(&delay, 10);
|
||||||
|
|
||||||
|
// Send a startup IPI.
|
||||||
|
ProcessorDisableInterrupts();
|
||||||
|
LapicWriteRegister(0x310 >> 2, processor->apicID << 24);
|
||||||
|
LapicWriteRegister(0x300 >> 2, 0x4600 | (AP_TRAMPOLINE >> K_PAGE_BITS));
|
||||||
|
ProcessorEnableInterrupts();
|
||||||
|
for (uintptr_t i = 0; i < 100 && *startupFlag == 0; i++) KEventWait(&delay, 1);
|
||||||
|
|
||||||
|
if (*startupFlag) {
|
||||||
|
// The processor started correctly.
|
||||||
|
} else {
|
||||||
|
// Send a startup IPI, again.
|
||||||
|
ProcessorDisableInterrupts();
|
||||||
|
LapicWriteRegister(0x310 >> 2, processor->apicID << 24);
|
||||||
|
LapicWriteRegister(0x300 >> 2, 0x4600 | (AP_TRAMPOLINE >> K_PAGE_BITS));
|
||||||
|
ProcessorEnableInterrupts();
|
||||||
|
for (uintptr_t i = 0; i < 1000 && *startupFlag == 0; i++) KEventWait(&delay, 1); // Wait longer this time.
|
||||||
|
|
||||||
|
if (*startupFlag) {
|
||||||
|
// The processor started correctly.
|
||||||
|
} else {
|
||||||
|
// The processor could not be started.
|
||||||
|
KernelLog(LOG_ERROR, "ACPI", "processor startup failure",
|
||||||
|
"ACPIInitialise - Could not start processor %d\n", processor->processorID);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EsPrint("Startup flag 1 reached!\n");
|
||||||
|
|
||||||
|
for (uintptr_t i = 0; i < 10000 && *startupFlag != 2; i++) KEventWait(&delay, 1);
|
||||||
|
|
||||||
|
if (*startupFlag == 2) {
|
||||||
|
// The processor started!
|
||||||
|
} else {
|
||||||
|
// The processor did not report it completed initilisation, worringly.
|
||||||
|
// Don't let it continue.
|
||||||
|
|
||||||
|
KernelLog(LOG_ERROR, "ACPI", "processor startup failure",
|
||||||
|
"ACPIInitialise - Could not initialise processor %d\n", processor->processorID);
|
||||||
|
|
||||||
|
// TODO Send IPI to stop the processor.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the identity pages needed for the trampoline code.
|
||||||
|
MMArchUnmapPages(kernelMMSpace, AP_TRAMPOLINE, 2, ES_FLAGS_DEFAULT);
|
||||||
|
}
|
||||||
|
|
|
@ -86,7 +86,6 @@ uint32_t LapicReadRegister(uint32_t reg);
|
||||||
void LapicWriteRegister(uint32_t reg, uint32_t value);
|
void LapicWriteRegister(uint32_t reg, uint32_t value);
|
||||||
NewProcessorStorage AllocateNewProcessorStorage(struct ArchCPU *archCPU);
|
NewProcessorStorage AllocateNewProcessorStorage(struct ArchCPU *archCPU);
|
||||||
extern "C" void SetupProcessor2(struct NewProcessorStorage *);
|
extern "C" void SetupProcessor2(struct NewProcessorStorage *);
|
||||||
void ArchDelay1Ms(); // Spin for approximately 1ms. Use only during initialisation. Not thread-safe.
|
|
||||||
uint64_t ArchGetTimeFromPITMs();
|
uint64_t ArchGetTimeFromPITMs();
|
||||||
void *ACPIGetRSDP();
|
void *ACPIGetRSDP();
|
||||||
size_t ProcessorSendIPI(uintptr_t interrupt, bool nmi = false, int processorID = -1); // Returns the number of processors the IPI was *not* sent to.
|
size_t ProcessorSendIPI(uintptr_t interrupt, bool nmi = false, int processorID = -1); // Returns the number of processors the IPI was *not* sent to.
|
||||||
|
|
|
@ -785,7 +785,6 @@ void Thread::SetAddressSpace(MMSpace *space) {
|
||||||
|
|
||||||
KSpinlockAcquire(&scheduler.lock);
|
KSpinlockAcquire(&scheduler.lock);
|
||||||
MMSpace *oldSpace = temporaryAddressSpace ?: kernelMMSpace;
|
MMSpace *oldSpace = temporaryAddressSpace ?: kernelMMSpace;
|
||||||
EsPrint("space = %x, oldSpace = %x\n", space, oldSpace);
|
|
||||||
temporaryAddressSpace = space;
|
temporaryAddressSpace = space;
|
||||||
MMSpace *newSpace = space ?: kernelMMSpace;
|
MMSpace *newSpace = space ?: kernelMMSpace;
|
||||||
MMSpaceOpenReference(newSpace);
|
MMSpaceOpenReference(newSpace);
|
||||||
|
|
Loading…
Reference in New Issue