From Nintendo Switch Brew
Jump to navigation Jump to search

The Switch 12.0.0 system update was released on April 6, 2021 (UTC). This Switch update was released for the following regions: ALL, and CHN.

Security flaws fixed: yes.


Official ALL change-log:

  • We fixed the issue with the save data backup feature, where in rare cases, the automatic backup of save data is interrupted if a communication error occurs during completion of the save data backup process.
  • For steps on how to check if the error is occurring or what to do if the error has already occurred, you may find this information helpful

System Titles

All titles were updated except for the following: lbl (stubbed), SharedFont, Dictionary, AvatarImage, LocalNews, Eula, UrlBlackList, ControllerIcon, ApplicationBlackList, FunctionBlackList. The only updated applets were the web-applets.

The following sysmodules had IPC changes: Bus, bluetooth, hid, audio, account, ns, am, nim, btm, erpt, vi, npns, glue, pgl, fs, sm.

NPDM changes:

  • am now has access to usb:hs and vi:s.
  • vi now has access to fsp-srv and FS permissions were changed to 0x100000 (SystemData).

RomFs changes (besides sysver titles):

  • CertStore: "/ssl_TrustedCerts.bdf" was updated.
  • ErrorMessage: Errors were added / message data updated.
  • BrowserDll: "/buildinfo/buildinfo.dat" and the OSS NROs in "/nro/netfront/dll_1/" were updated.
  • Help: "/legallines.htdocs/index.html" and "/safe.htdocs/html/EU{language}/index.html" were updated.
    • The former just changes <p>© 2019 Nintendo to <p>© Nintendo. The latter changes various safety text.
  • FirmwareDebugSettings, PlatformConfigIcosa, PlatformConfigCopper, PlatformConfigHoag, PlatformConfigIcosaMariko: "/file" was updated.
  • BootImagePackages: See below.
  • ControllerFirmware: "/TouchScreenFirmwareInfo.csv" was updated.
  • NgWordT: "/mars_dirty_words_db" was updated.
  • Web-applets: "/buildinfo/buildinfo.dat" and "/.nrr/dll.nrr" were updated.


All files in RomFS were updated.

Secure Monitor

<check back for diffs later>


<check back for diffs later>


  • Compiler upgrade; kernel now compiled with clang 10 or 11, previously (as of 11.0.0) it used clang 9.
    • X29/XSP are now stored closer to stack top instead of closer to stack bottom vs other callee-save registers.
    • g_KSchedulerInterruptTask no longer has a __cxa_atexit call.
  • Kernel now supports SVC IDs in range [0, 0xBF) instead of [0, 0x7F).
    • All 0x40 new SVC IDs are currently unused.
    • A number of changes were made to support this:
      • All places where SVC handler used to check SVC ID < 0x80 now checks < 0xC0.
      • KCapability's SVC permission field is now 0x18 bytes instead of 0x10.
      • KCapability::SetSyscallMaskCapability no longer needs to perform range checks; representable values in capability are now exactly the allowable SVC IDs.
      • SVC tables are now 0x600 in size, not 0x400.
      • KThread's StackParameter's fields were heavily shuffled around to support 0x18 bytes of flags instead of 0x10 while remaining at size = 0x30.
        • "disable_count" is now 16-bits instead of 32-bits, in order to fit.
  • SVC autogen has changed to provide first-class bool support.
    • The autogen now does "and xn, xm, #1" to ensure that boolean values are only 0 or 1.
  • Slab resource counts changed
    • num_KEvent 700 -> 900
    • num_KSessions 933 -> 1133
  • Memory arrangement for 8 GB has changed, was 4916/2193/~1083 now is 6946/562/~684 (application/applet/system).
  • KInitialPageTable now has two L1 tables, one for address high bit = 1, one for address high bit = 0.
    • Thus, KInitialPageTable can now map both TTBR0 and TTBR1 pages from a single table.
  • KInitialPageAllocator rewritten.
    • KInitialPageAllocator now creates/maintains a free list, instead of doing random pages within 0x40000 chunks.
    • It now supports (optionally aligned) allocations of sizes other than one page.
    • This fixes a longstanding issue where only the first 64 allocated pages were randomized, and then pages after that were linear.
  • Initialize0 now aligns device mappings to 2 MB (and gives them 2 MB guards) if size is >= 2 MB.
  • Initialize0 now contains logic for setting up the unknown debug region:
    • After InitializeDramPhysicalMemoryBlocks(), physical memory tree is walked.
    • Blocks with type derivation KMemoryRegionType_DramReservedBase and type attribute KMemoryRegionAttr_ShouldKernelMap are identified, and
    • Inserted into the misc region with type UnknownDebug at random locations using identical algorith to ShouldKernelMap devices.
    • The memory for these regions is mapped as RW, optionally uncached.
    • When setting up the linear region, memory mapped as UnknownDebug above is now set to type id 0x52 instead of 0x2, and these regions do not have the pair block address set.
  • KMemoryRegionAttr_CarveoutProtected is now 0x02000000, was 0x04000000.
    • 0x04000000 is now KMemoryRegionAttr_Uncached.
  • InitializeSlabHeapsAndAllocators now generates gaps in range 0x1B6000, instead of 0x1F0000.
  • KLinkedListNode no longer exists, no longer has slab heap/allocator.
    • KConditionVariable::SignalImpl no longer returns a KThread * to close, instead it closes the owner thread immediately after adding the waiter.
    • Correspondingly, KConditionVariable::Signal no longer needs to keep track of threads to close, and so no longer needs linked list nodes.
  • KSystemControl now has several locks as globals rather than a function static (ConfigureSecureCarveout(), GenerateRandom()).
    • This means they're no longer statically initialized on first call.
  • Some changes to KSystemControl::GenerateRandom[*]
    • KSystemControl::InitializePhase1 now initializes the mersenne twister rng, instead of dynamically initializing it on first call to random functions.
    • GenerateRandom[*] now uses the mersenne twister if it is initialized, and falls back to smc::GenerateRandomBytes() otherwise.
    • This effectively combines the ForInit/normal versions of these functions.
    • Kernel Main now calls cpu::SynchronizeAllCores() after KSystemControl::InitializePhase1, to ensure consistency here.
  • Enormous refactor of initial process setup.
    • LoadInitialProcessBinaryHeader() now walks the INI1 to determine how much secure memory is required for uncompressed KIPs.
    • GetInitialProcessBinaryAddress() now gets from end of managed pool region instead of pt heap, panics if managed pool is < 12 MB.
    • KMemoryManager::Initialize now accounts for the end of managed pool as "used"/initial process memory.
      • KMemoryManagerImpl::UpdateUsedHeapSize now takes in a u64 for the amount of reserved memory.
    • KInitialProcessReader now has a KInitialProcessHeader member instead of pointer, and now copies the header before checking the copy.
    • KInitialProcessReader::Load now takes additional argument for source address, does memmove instead of memcpy + memset.
    • CreateAndStartInitialProcesses is radically different.
      • Initial process binary is now slowly consumed and freed to KMemoryManager as it's used (and memmoved as parts are no longer needed).
  • SvcSetThreadPriority/SvcSetThreadCoreMask now requires that target thread is owned by the current process.
    • Can no longer set priorities for threads in other processes.
  • KThread::SetThreadCoreMask no longer redundantly sets thread->virtual_ideal_core_id when core_id == -3.
  • Pinned threads can no longer have their priorities changed.
    • KThread::Pin now sets the base priority to the maximum allowed priority for the process.
    • KThread has a new member to track what the base priority should be after unpin.
    • KThread::Unpin now does a priority restore to the desired base priority.
  • KAutoObject::Open/Close no longer inlined.
  • KAutoObject ->Close() now schedules destruction via DPC, instead of doing destruction immediately.
    • This is done via a new field at +0x10 (KAutoObject *m_next_object_to_close)
    • KThread has a new field at +0x668 KAutoObject *m_object_to_close;
    • KDpcManager::DpcHandler() now walks the list, destroying all objects scheduled for destruction.
    • Same logic is now done by KThread::DestroyClosedObjects(), called by KThread::Exit() before getting the scheduler lock.
    • Same logic is now done by KWorkerTaskManager::ThreadFunctionImpl, when closing a thread.
    • Same logic is now done by KInterruptTaskManager::ThreadLoop, when closing a thread.
    • Same logic is now done by ReplyAndReceive, before waiting for any objects to be signaled.
  • KHandleTable has been optimized:
    • Free list is now indices, rather than pointers
    • The type info/free index and object arrays are now two arrays, rather than a single array of struct { union { } }
    • This reduces KHandleTable size (and thus KProcess size) by 0x1000.
    • ::Register, ::Unreserve now bounds checks the handle's index.
  • KInterruptEvent now has a core_id member (set to GetCurrentCoreId() on register).
    • This is used to combine the two KInterruptManager::ClearInterrupt methods from 11.x.
  • Bounds checking formerly in KInterruptEventTask::Register is now in KInterruptEvent::Initialize.
  • UserspaceAccess::WriteIoMemory* functions were optimized slightly to not need a nop instruction.
  • Many call sites which previously set thread state conditionally now do so unconditionally.
    • KScheduler::SetInterruptTaskThreadRunnable, KLightLock::LockImpl/UnlockImpl, KProcess::EnterUserException/ReleaseUserException
  • KLightConditionVariable::Wait now takes an additional bool argument "allow_terminating_thread", does what you'd expect.
    • The only callsite which passes false is KResourceLimit::ReserveImpl, which now checks for thread termination after wait.
  • KLightConditionVariable::Wait no longer checks if thread is terminating when adding to wait queue.
  • KLightConditionVariable::Wait now re-acquires the KLightLock, instead of requiring caller do this afterwards.
  • KLightConditionVariable::Broadcast now signals all threads, instead of only front thread.
    • Threads are no longer removed from the list on broadcast.
  • KCacheHelper::ProcessOperation no longer flushes/stores entire data cache by set/way. Instead only level 0 is flushed/stored.
    • Correspondingly, the helper functions now do a dsb sy at the end, instead of only doing a single dsb after ProcessOperation is done.
    • Store/FlushDataCacheBySetWay now use SBFIZ rather than multiplying level by 2, possibly this is a compiler optimization change.
  • CPU::FlushEntireDataCache has changed.
    • Previously, it flushed by set way for each level from (levels of coherence) -> (levels of unification).
    • Now, it stores by set way levels 1 -> (levels of coherence - 1), then flushes by set way (levels of coherence - 1) -> 1.
  • Thread count tracking has changed substantially:
    • KProcess::IncrementThreadCount no longer updates a max_thread_count variable.
    • KProcess::m_atomic_num_threads is now KProcess::m_atomic_num_running_threads
      • KProcess::IncrementThreadCount is now called by KThread::Run instead of KThread::Initialize.
      • KProcess::DecrementThreadCount is now called by KThread::Exit instead of KThread::Finalize.
      • KThread::Run now opens a reference to the newly-running thread.
        • SvcStartThread no longer calls thread->Open(), since this is now handled by ::Run.
    • KProcess::BeginTerminate + KProcess::TerminateChildren now returns Result.
      • KProcess::Terminate logic now schedules worker task if KProcess::BeginTerminate fails.
    • KProcess no longer has m_atomic_num_created_threads.
  • KProcess::Run now calls process->Open() on success.
    • SvcStartProcess no longer calls process->Open(), since this is now handled by ::Run.
    • CreateAndStartInitialProcesses now calls process->Close(), to close the extra reference from ::Run.
  • KProcess::UnpinThread is really UnpinCurrentThread.
  • KThread::RequestTerminate now unpins the thread, if it's pinned.
    • Uses new function that takes in process pointer + thread pointer.
  • KProcess::TerminateChildren now unpins the exception thread, if it's pinned.
  • Terminating threads now always have priority = 15, never higher/lower.
    • (Can no longer set a terminating thread to have a higher priority).
  • HandleException now calls KThread::Exit if thread termination has been requested, after restoring interrupt enable status.
  • KProcess::PinCurrentThread() now only actually pins if the thread isn't terminating.
  • KThread::Unpin now only updates state + adds permissions if thread isn't terminating.
  • KProcess new bool field "m_handle_table_initialized" @ 0x3392.
    • Initially set to 0 in InitializeByParam.
    • Set to 1 in KProcess::Run, after m_handle_table.Initialize() is called.
    • KProcess::DoTask only finalizes the handle table if m_handle_table_initialized is true.
  • Usage of KHandleTable::Register has been cleaned up.
    • ConnectToPort and AcceptSession now have a single call, rather than one for each light vs non-light codepath.
    • ManageNamedPort now uses KHandleTable::Add instead of ::Reserve + ::Register.
  • KProcess::Initialize takes a new bool is_no_exit (KIP flag 0x40).
    • Processes with this bool set never have their handle tables finalized, and never exit.
  • Handling of kernel waiters has changed:
    • IsKernelAddressKey() now checks that address_key & 1 == 0.
    • KProcess::EnterUserException now sets address_key = (thread | 1) instead of thread.
      • This makes EnterUserException no longer a kernel waiter.
      • KScheduler::UpdateHighestThreads now checks if the top thread is the exception thread, and ignores the pinned thread if it is.
    • All locations where KThread::m_num_kernel_waiters is changed now mark that a scheduler update is needed.
  • KMemoryBlockManagerUpdater::Initialize has now been removed, rolled into KMemoryBlockManagerUpdater ctor.
    • ctor now takes in an output result pointer, is old ctor + initialize as one-operation.
  • KSynchronizationObject::IsSignaled() (default implementation) is now an infinite loop.
  • KConditionVariable::WaitConditionVariable now cancels task at very end of func, instead of in the middle.
  • KConditionVariable::WaitForAddress now closes the owner thread reference at the end of function, instead of in the middle.
  • Structural changes to KAddressArbiter::SignalAndModifyBasedOnWaitingThreadCount.
    • Nothing has changed behaviorally, but the conditional logic is simpler/more optimized.
  • KDebugBase::OnFinalizeSynchronizationObject now closes the process reference after setting m_process = nullptr, instead of at scope exit.
  • KDebugBase::TerminateProcess now calls thread->RequestSuspend(Debug) on all process threads if transitioning from debug suspended -> crashed.
  • KPageTableBase has two new fields: "m_resource_limit", "m_reserved_ipc_memory".
    • KProcessPageTable::Initialize/KPageTableBase::InitializeForProcess now take an additional argument for the resource limit to use.
    • KSupervisorPageTable always uses the system resource limit for this.
    • All KPageTableBase logic which previously did GetCurrentProcess().GetResourceLimit() now uses m_resource_limit instead.
      • This includes:
        • MapPhysicalMemory
        • UnmapPhysicalMemory (no longer checks for != nullptr before calling ReleaseLimit)
        • SetHeapSize (no longer checks for != nullptr before calling ReleaseLimit, still checks for nullptr before calling Reserve).
    • KPageTableBase::SetupForIpcServer increments m_reserved_ipc_memory by the amount of memory reserved for temporary pages, CleanupForIpcServer decrements (and no longer takes in server process).
    • KPageTableBase::Finalize() now releases the reserved ipc memory size from m_resource_limit if m_reserved_ipc_memory > 0.
  • KPageTableBase::GetPhysicalAddress now acquires the table's lock while getting the address.
  • KPageTableBase::ReadDebugMemory now calls cpu::FlushDataCache on memory before reading it.
  • Several KPageTableBase functions which operate on two tables simultaneously now use a helper function to release the two tables' KLightLocks.
  • KPageTableBase::IsInAliasRegion no longer inlined in MapPhysicalMemory/UnmapPhysicalMemory
  • KPageTableBase::Contains/KPageTableBase::IsInUnsafeAliasRegion are now more lenient about "doesn't overlap heap or alias region" checks.
    • Previously, when e.g. checking not in the heap region, it did (test_end <= heap_start || heap_end <= test_start).
    • Now it does (heap_end <= test_start || test_end <= heap_start || heap_start == heap_end).
    • Thus, if heap region size is zero, all memory regions are considered to not overlap heap.
    • This also applies to alias region checks.
  • UnmapProcessMemory no longer uses a page group, now uses a new function "KPageTableBase::UnmapProcessMemory"
    • This function uses a new structure "ContiguousTraversalContext" for tracking the ranges being walked.
  • DoProcessCacheOperation no longer makes a page group, now uses a new function "KPageTableBase::GetContiguousRangeForProcessCacheOperation" to iterate over contiguous memory without making a page group.
  • Page table management for DeviceAddressSpace map/unmap has changed:
    • KPageTableBase has a new KLightLock ("device_map_lock") at +0xA8.
    • KDeviceAddressSpace::Map/KDeviceAddressSpace::Unmap acquires the device map lock.
    • KDeviceAddressSpace::LockForDeviceAddressSpace no longer builds an output page group.
    • KPageTableBase::MakePageGroupForUnmapDeviceAddressSpace is now KPageTableBase::LockForUnmapDeviceAddressSpace, no longer builds a page group.
    • KDeviceAddressSpace::Map/KDevicePageTable::MapImpl now takes in page table + address + size instead of page group.
    • KDevicePageTable::MapImpl now calls new KPageTableBase function ("KPageTableBase::GetContiguousRangeForMapDeviceAddressSpace") to iterate over contiguous memory without making a page group.
    • KDevicePageTable::UnmapByPageGroup is now Unmap, takes in page table + address + size instead of page group.
    • KDevicePageTable::Compare now takes in page table + address + size instead of page group.
    • KDevicePageTable::Compare now calls new KPageTableBase function ("KPageTableBase::GetContiguousRangeForUnmapDeviceAddressSpace") to iterate over contiguous memory without making a page group.
    • KDevicePageTable::UnmapImpl no longer builds a page group, instead unmapping/finalizing as it goes.
    • KPageTableBase::UnlockForDeviceAdressSpacePartialMap now calls CheckMemoryStateContiguous instead of CheckMemoryState.
  • KObjectName::Delete<KClientPort> now requires that the corresponding KServerPort is closed.
    • This means that ManagedNamedPort will return svc::ResultInvalidState() when trying to delete a client port with a server that's still open.
  • KServerSession m_state is now atomic.
  • KServerPort::CleanupSessions now closes all sessions, not just the last one.
  • KServerSession::CleanupRequests now unlocks the session's lock in between cleaning up each request.
  • EL1 exception handlers now handle register save differently.
    • cntv_cval_el0 now always points to the exception stack (previously this was tpidr_el1).
    • x0 is now saved to tpidr_el1 (now only usage of tpidr_el1 in entire kernel).
    • cntv_cval_el0 is now saved/restored by sleep/wake.

FIRM Sysmodules

FIRM sysmodules were updated. Specific diffs available below:


Sysmodule has been completely restructured/refactored/rewritten.

  • No longer uses CMIF for IPC, now uses new simpler/learner IPC protocol ("tipc", probably "tiny ipc").
    • TIPC is implemented on top of HIPC svcs, same as CMIF was.
    • Hacky/Hardcoded CMIF shim is provided for GetServiceHandle/RegisterClient, to avoid breaking games compiled with SDK < 12.x.
  • sm: now has a max sessions of 69, up from 61.
    • sm:m is maximum 1, for a total of 70.
  • There are now two IPC server threads, each of which processes up to 35 handles.
    • The new tipc server class has logic for automatically load-balancing new sessions to the least burdened server thread.

<check back for more diffs later>


The IPC<>btstack code was rewritten. C++ objects with vfuncs are now used by commands, instead of the interface funcptr tables. Various structs/enums were updated/etc.

The funcs which were called via funcptrs from the HidMessageHandler thread for writing the various EventInfo state are now inlined.

The Adapter commands were updated, which in the process vulns were fixed.

RespondToPinRequest now actually uses the input PIN instead of hard-coding it.

StartInquiry was updated.

System-setting "bluetooth_config!skip_boot" is now handled.

For more details, see here.

Support for bluetooth audio was added. Note however, that the audio-sysmodule still doesn't use the btdrv service.


None of the new btdrv commands are used. StartInquiry with an user-specified service_mask is used, however it's unknown what triggers using this.


Besides IPC changes, the codebin now contains various new strings:

  • "is_crda_fw_update_supported"
  • "CrdA.dfu"
  • "is_grc_and_migration_launched_exclusively"

See Also

System update report(s):

Nintendo Switch System Versions