There are lots of differences between the primary VM and secondary VMs. Among others:
1. The primary VM has one vCPU per physical CPU, which they are tied to, whereas the secondary VMs have an arbitrary configurable number of vCPUs which the primary can run wherever it likes.
2. The secondary VMs start with a configurable, typically fairly small, section of memory, whereas the primary VM gets everything that is left after Hafnium's and the secondary VMs' memory is removed.
3. The primary gets access to the GIC and gets all physical interrupts, whereas secondaries get a paravirtualised interrupt interface and has virtual interrupts injected.
4. The primary gets to control the physical timer at all times, and the virtual timer either directly (while it is running) or emulated by Hafnium while it is not, whereas secondaries only get to control the virtual timer while they are running, and it is emulated with the help of the primary the rest of the time.
5. The primary gets to control the physical CPUs' power state through PSCI, which Hafnium more or less just passes through to the underlying secure monitor, whereas the secondary VMs get an emulated PSCI interface provided by Hafnium which translates to telling the primary VM to start or stop the vCPUs.
We do share as much code as practical, but all these differences result in a fair bit of difference in the set-up path (and PSCI handling) so it made sense to split it out into separate functions rather than just having if (is_primary) {...} else {...} all over the place.