7171
7272/* Set in spawn kernels: the context this kernel booted from */
7373static struct mk_spawn_context * mk_boot_context ;
74+ static phys_addr_t mk_boot_context_phys ;
7475
7576/*
7677 * Spawn kernel's own trampoline for secondary CPU wakeup.
@@ -84,6 +85,16 @@ static struct mk_spawn_context *mk_boot_context;
8485 */
8586static void * spawn_trampoline_va ;
8687static unsigned long spawn_trampoline_phys ;
88+ static bool spawn_trampoline_prepared ;
89+ static bool spawn_pool_park_prepared ;
90+ static bool spawn_park_ready ;
91+ static int spawn_park_error ;
92+
93+ bool mk_arch_park_ready (void )
94+ {
95+ /* Pair with publication after both executable park mappings succeed. */
96+ return smp_load_acquire (& spawn_park_ready );
97+ }
8798
8899extern char multikernel_relocate_kernel_start [];
89100extern char multikernel_relocate_kernel_end [];
@@ -471,6 +482,7 @@ int mk_arch_spawn_instance(struct kimage *image, struct mk_instance *instance,
471482 instance -> spawn_ctx -> boot_tsc_khz = tsc_khz ;
472483 instance -> spawn_ctx -> boot_apic_hz =
473484 (unsigned long )lapic_timer_period * HZ ;
485+ instance -> spawn_ctx -> abi_magic = MK_BOOT_CONTEXT_MAGIC ;
474486
475487 return mk_spawn_cpu (instance , cpu , instance -> spawn_ctx );
476488}
@@ -693,18 +705,38 @@ void __init mk_arch_register_cpu(u64 phys_id)
693705 topology_register_apic ((u32 )phys_id , CPU_ACPIID_INVALID , true);
694706}
695707
696- /*
697- * Initialize boot context tracking in spawn kernel.
698- * Called early during spawn kernel boot.
699- */
700- void mk_init_boot_context (phys_addr_t ctx_phys )
708+ static __noreturn void mk_reject_spawn_context (void )
709+ {
710+ /*
711+ * The host context layout is unknown, so neither its park state nor any
712+ * shared context field is safe to use. Keep this CPU local and inert. An NMI
713+ * can wake HLT, but returns to this loop with maskable interrupts still
714+ * disabled; disable them again before every halt for defense in depth.
715+ */
716+ for (;;) {
717+ native_irq_disable ();
718+ native_halt ();
719+ }
720+ }
721+
722+ struct mk_spawn_context * mk_validate_boot_context (phys_addr_t ctx_phys )
701723{
702724 struct mk_spawn_context * ctx ;
725+ phys_addr_t stamped_phys ;
726+ u64 abi_magic ;
703727
704728 if (!ctx_phys ) {
705729 pr_err ("mk_spawn: Boot context physical address is 0!\n" );
706- return ;
730+ return NULL ;
707731 }
732+ if (mk_boot_context ) {
733+ if (ctx_phys != mk_boot_context_phys )
734+ mk_reject_spawn_context ();
735+ return mk_boot_context ;
736+ }
737+ /* Reject an invalid derived address before mapping or dereferencing it. */
738+ if (!IS_ALIGNED (ctx_phys , PAGE_SIZE ))
739+ mk_reject_spawn_context ();
708740
709741 /*
710742 * The spawn context is in the multikernel pool which is regular RAM,
@@ -721,14 +753,28 @@ void mk_init_boot_context(phys_addr_t ctx_phys)
721753 * work and then fails much later, when this kernel shuts down and
722754 * its CPUs park on nonsense addresses.
723755 */
724- if (ctx -> self_phys != ctx_phys ) {
725- pr_err ("mk_spawn: Boot context at %pa is stamped %pa\n" ,
726- & ctx_phys , & ctx -> self_phys );
727- pr_err ("mk_spawn: Spawn context layout mismatch - host and spawn kernels must be built from the same source\n" );
728- return ;
729- }
730-
756+ stamped_phys = READ_ONCE (ctx -> self_phys );
757+ if (stamped_phys != ctx_phys )
758+ mk_reject_spawn_context ();
759+ abi_magic = READ_ONCE (ctx -> abi_magic );
760+ if (abi_magic != MK_BOOT_CONTEXT_MAGIC )
761+ mk_reject_spawn_context ();
762+
763+ mk_boot_context_phys = ctx_phys ;
731764 mk_boot_context = ctx ;
765+ return ctx ;
766+ }
767+
768+ /*
769+ * Initialize boot context tracking in spawn kernel.
770+ * Called early during spawn kernel boot.
771+ */
772+ void mk_init_boot_context (phys_addr_t ctx_phys )
773+ {
774+ struct mk_spawn_context * ctx = mk_validate_boot_context (ctx_phys );
775+
776+ if (!ctx )
777+ return ;
732778 /*
733779 * A spawn kernel cannot calibrate against legacy timers because they
734780 * belong to the host. Reuse the selected physical CPU's delay and local
@@ -764,52 +810,74 @@ void mk_init_boot_context(phys_addr_t ctx_phys)
764810 *
765811 * One physical page serves every wake path of this instance: the host
766812 * allocates it once in mk_setup_trampoline() and reuses it across
767- * re-spawns, and mk_prepare_trampoline () places our own trampoline copy
813+ * re-spawns, and mk_arch_prepare_park () places our own trampoline copy
768814 * (including the secondary entry) in the same page.
769815 */
770- static int __init mk_prepare_trampoline (void )
816+ int __init mk_arch_prepare_park (void )
771817{
772818 struct mk_spawn_context * ctx = mk_boot_context ;
773819 unsigned long virt ;
774820 int ret ;
775821
822+ if (mk_arch_park_ready ())
823+ return 0 ;
824+ if (spawn_park_error )
825+ return spawn_park_error ;
776826 if (!ctx )
777827 return 0 ;
828+ if (!ctx -> trampoline_phys || !ctx -> park_phys || !ctx -> park_cr3 ) {
829+ ret = - EINVAL ;
830+ goto fail ;
831+ }
778832
779833 /*
780834 * Put our own copy of the trampoline in the page the host set
781835 * aside, while the kernel is fully alive: after shutdown this page
782836 * is entered from an offline CPU, where changing page attributes
783837 * is not allowed.
784838 */
785- spawn_trampoline_phys = ctx -> trampoline_phys ;
786- spawn_trampoline_va = __va (spawn_trampoline_phys );
787- memcpy (spawn_trampoline_va , multikernel_relocate_kernel_start ,
788- multikernel_relocate_kernel_end - multikernel_relocate_kernel_start );
839+ if (!spawn_trampoline_prepared ) {
840+ spawn_trampoline_phys = ctx -> trampoline_phys ;
841+ spawn_trampoline_va = __va (spawn_trampoline_phys );
842+ memcpy (spawn_trampoline_va , multikernel_relocate_kernel_start ,
843+ multikernel_relocate_kernel_end -
844+ multikernel_relocate_kernel_start );
789845
790- /*
791- * Both pages are executed from the direct map, which is writable,
792- * so drop write before adding execute. Leaving them writable and
793- * executable trips the kernel's own W^X check.
794- */
795- virt = (unsigned long )spawn_trampoline_va & PAGE_MASK ;
796- ret = set_memory_ro (virt , 1 );
797- if (!ret )
798- ret = set_memory_x (virt , 1 );
799- if (ret )
800- return ret ;
846+ /*
847+ * Both pages are executed from the direct map, which is writable,
848+ * so drop write before adding execute. Leaving them writable and
849+ * executable trips the kernel's own W^X check.
850+ */
851+ virt = (unsigned long )spawn_trampoline_va & PAGE_MASK ;
852+ ret = set_memory_ro (virt , 1 );
853+ if (!ret )
854+ ret = set_memory_x (virt , 1 );
855+ if (ret )
856+ goto fail ;
857+ spawn_trampoline_prepared = true;
858+ }
801859
802860 /* The pool park page is entered the same way when this kernel dies */
803- if (ctx -> park_phys ) {
861+ if (! spawn_pool_park_prepared ) {
804862 virt = (unsigned long )__va (ctx -> park_phys ) & PAGE_MASK ;
805863 ret = set_memory_ro (virt , 1 );
806864 if (!ret )
807865 ret = set_memory_x (virt , 1 );
866+ if (ret )
867+ goto fail ;
868+ spawn_pool_park_prepared = true;
808869 }
809870
871+ /* Publish executable mappings before any reject or abort can park. */
872+ smp_store_release (& spawn_park_ready , true);
873+ return 0 ;
874+
875+ fail :
876+ /* A partial W^X transition is not safe to retry. */
877+ spawn_park_error = ret ;
810878 return ret ;
811879}
812- early_initcall (mk_prepare_trampoline );
880+ early_initcall (mk_arch_prepare_park );
813881
814882/*
815883 * Add a 2MB executable mapping to a page table.
0 commit comments