I suggest that you begin by inverting your specification. The MPU is a permission-grant approach rather than a blocking system. So you need to define what access are allowed rather than what is not allowed.
The MPU is always active, and at reset allows all access to both cores. The CPU has a choice of Supervisor or User state where User state has some additional restrictions:
Software cannot set or clear system interrupt enables (X, I), stop enable (S) or change interrupt priority (IPL[0..2])
Software cannot execute WAI or STOP op-codes
No opcode can change the user state bit (RTI, PULC, EXG etc.)
In terms of a flow, I'd suggest something like:
1/ Set up handlers for MPU errors - XGATE and CPU
If you make an error when setting up the MPU you would like to be able to recover gracefuly
2/ Identify the physical ranges that are allowed
You can get this information from the linker, but you probably need to be fairly close to completing the code to make sure that all of the relevant groups of memory are identified. Don't forget to include libraries and peripheral spaces.
3/ After reset, create a function that initialises the MPU to the values that you have defined.
You'll need to define XGATE access for its code and data space. If you have shared memory then you can use a single descriptor and allocate both CPU and XGATE access.
If you have an OS or other complex approach on the CPU you may find that you need to dynamically change the settings of the descriptors for the CPU.
In most cases you will want the CPU to use User state so make sure that you identify areas of code that need the restricted access types and put these in a special handler (interrupt).
4/ Decide how you want to enter User state and then set the U bit.