How does it muck about with the kernel address space again? Oh right, by writing to magic locations in memory. The same way that you interface with virtually all hardware, for that matter.
No, you misunderstand: I believe most of the operations you need to perform a context switch are safe in the kernel's context. Sure, you'll need a bit of assembly code to copy out and set registers and stuff, and you can't do that bit in C either. But other than that, safe pointers should work.
Given that a scheduler's going to be running in kernel mode, why can it not just muck around with kernel address space and leave it to the page table to do the translation?
Yeah, I did clarify my request a little -- by safe I meant safe with optional nullability.