OpenSolaris

You are not signed in. Sign in or register.
CPU Module Implementation
=========================

Interface: 

Introduction
------------

For the most part the cpu module interface  is
simply a wrapper into a corresponding function within a particular
cpu module implementation.  For example the cpu module API itself
does not know how to perform MCA initialization in cmi_mca_init,
so it calls into the cpu module implementation for this function.

History and Expected Future Use
-------------------------------

The initial fma/x64 putback created the cpu module interface
with two modules delivered: cpu.AuthenticAMD.15 and cpu.generic.
It was envisaged that additional model-specific support would
simply deliver additional cpu modules - cpu.AuthenticAMD.16,
cpu.GenuineIntel.6 etc.

That arrangement, however, does not facilitate code sharing nor
recognition of the common MCA architecture.  As such, cpu.generic
has been beefed-up to become a first-class FMA-capable cpu module
and model-specific support (such as the existing AMF family 15
support) can be delivered in additional plugin modules that
layer on top on cpu.generic or any other cpu module implementation
that cares to call into the model-specific API.

So the intention is that cpu.generic should be the *only* cpu module
implementation, and that model-specific support be layered on top
of that via the model-specific interface .
The cpu module interface remains and we may still deliver
additional model-specific support in that form, but for at least
a while the model will be to layer on top of cpu.generic.

Structures
----------

A cmi_mc_ops_t is registered by a memory-controller driver when it calls
cmi_mc_register, which in turns calls the cmi_mc_register entry point
of the cpu module the memory-controller is registering against:

typedef struct cmi_mc_ops {
	cmi_errno_t (*cmi_mc_scrubber_enable)(void *);
	cmi_errno_t (*cmi_mc_patounum)(void *, uint64_t, uint8_t, uint8_t,
	    uint32_t, int, mc_unum_t *);
	cmi_errno_t (*cmi_mc_unumtopa)(void *, mc_unum_t *, nvlist_t *,
	    uint64_t *);
} cmi_mc_ops_t;

The entry points of a cpu module implementation are registered with
the cpu module interface during cmi_init via a cmi_ops_t:

typedef struct cmi_ops {
	int (*cmi_init)(cpu_t *, void **);
	void (*cmi_post_startup)(void);
	void (*cmi_post_mpstartup)(void);
	void (*cmi_fini)(void *);
	void (*cmi_faulted_enter)(void *);
	void (*cmi_faulted_exit)(void *);
	void (*cmi_mca_init)(void *);
	int (*cmi_mca_trap)(void *, struct regs *);
	cmi_errno_t (*cmi_mca_msrinject)(void *, cmi_mca_regs_t *, uint_t, int);
	void (*cmi_mca_poke)(void *);
	void (*cmi_mc_register)(void *, const cmi_mc_ops_t *, void *);
	const struct cmi_mc_ops *(*cmi_mc_getops)(void *, void **);
} cmi_ops_t;

Note that many ops entry points are named as the corresponding
 API member, but that there is not a 1:1 correspondence.
The initial void * argument to most ops functions is used to carry the
module-private data that was registered at cmi_init time.

API Versioning
--------------

The cpu module interface will only load a cpu module that has a matching
API version number.  To be considered a suitable module to initialise
a prospective cpu module (that matches in expected pathname already)
must include a symbol named '_cmi_ops' which must be a cmi_ops_t
structure, and must also have a symbol '_cmi_api_version' which must
be a cmi_api_ver_t and must match the CMI_API_VERSION of the kernel.

cmi_init entry point
--------------------

	int (*cmi_init)(cpu_t *, void **);

This is called during cmi_init for a module that matched on path lookup,
after looking up and noting the cmi_ops_t ops vector of the module.
It should return 0 for successful initialization, or some nonzero error
indicator.  If a module does not initialize we do not attempt to
initialize the "next in line" (e.g., cpu.AuthenticAMD.15 if
cpu.AuthenticAMD.15.5 exists but fails to initialize); instead we
fallback to the cpu.generic module.

The second argument may be filled with a module-private argument
which will be quoted back during most other ops entry points.

This ops member is required - it cannot be NULL.

Some cpu module, at least cpu.generic, must initialize for every cpu
during startup; if not we will panic.  When a module chooses
not to initialize it must free any resources it has allocated up
to that point before returning failure - there is no 'fini'
entry point or similar.

Once a cpu module initializes it will not be unloaded.

cmi_post_startup entry point
----------------------------

	void (*cmi_post_startup)(void);

This is called from cmi_post_startup only, which is called once the
boot cpu has completed startup (including cmi_init) and before MP startup.
The module-private argument is not passed since this is a "global"
operation (will not be repeated on each cpu module instance).

This op is optional in an implementation - it may be NULL.

cmi_post_mpstartup entry point
------------------------------

	void (*cmi_post_mpstartup)(void);

This is called from cmi_post_mpstartup after MP startup.  It is called
exactly once, not for each cpu module instance.  We may be running
on any cpu, but the caller has prevented migration.  This entry
point should not perform any operations that are specific to
the particular cpu model it happens to be running on - there may be
other different models present in the system.

This op is optional in an implementation.

cmi_faulted_{enter,exit} entry points
-------------------------------------

	void (*cmi_faulted_enter)(void *);
	void (*cmi_faulted_exit)(void *);

These are called from cmi_faulted_{enter,exit}.  The argument is the module-
private data for the cpu entering or leaving CPU_FAULTED state.

These ops are optional in an implementation.

cmi_mca_init entry point
------------------------

	void (*cmi_mca_init)(void *);

This is called from cmi_mca_init during startup of each cpu.  cmi_init
has already been called, and the argument is the module-private data
registered then.  The startup code calls cmi_mca_init only if
x86_feature & X86_MCA, which means that CPUID information for this
cpu indicated both MCA and MCE support.  We are running on and bound to
the cpu that is starting up.  The cmi_mca_init cpu module API member
performs no MCA initialization itself - everything is left to the cpu
module implementation.

This op is optional in an implementation.

cmi_mca_trap entry point
------------------------

	int (*cmi_mca_trap)(void *, struct regs *);

This is called from cmi_mca_trap, which is called from mcetrap when
a machine check exception (#MC) is taken.  We are in interrupt context
and running on (bound to) the affected cpu.

This op should return non-zero if the #MC is considered fatal requiring
a panic.

This op is optional in an implementation.  If it is absent (NULL) then
machine checks will never produce a panic, so it should really be
present!

cmi_mca_msrinject entry point
-----------------------------

	cmi_errno_t (*cmi_mca_msrinject)(void *, cmi_mca_regs_t *, uint_t, int);

This is called from cmi_mca_msrinject to write to one or more
MSRs.  Any MSR may be addressed, but the expectation is that
machine check MSRs will be the target.  *If* the implementation knows
how towrite to such MSRs in a safe manner (eg by setting some model-specific
write-enable bit first) it should perform the writes using cmi_wrmsr
(and not unprotected WRMSR which will not catch exceptions).
If the the last argument is nonzero the the caller requests that
the writes are attempted whether or no the implementation knows them
to be safe.

This op is optional in an implementation.  It is used in error simulation.

cmi_mca_poke entry point
------------------------

	void (*cmi_mca_poke)(void *);

This is called from cmi_mca_poke to prod any poller in the cpu module
implementation into checking for errors on this cpu now, instead of
waiting for the next scheduled wakeup.

This op is optional in an implementation.  It is used in error injection.

cmi_mc_register and cmi_mc_getops entry points
----------------------------------------------

	void (*cmi_mc_register)(void *, const cmi_mc_ops_t *, void *);
	const struct cmi_mc_ops *(*cmi_mc_getops)(void *, void **);

This is called from cmi_mc_register to register a memory-controller
driver capability with the given cpu.  The implementation
is responsible for stashing the cmi_mc_ops_t pointers and the
driver-private data of the last argument, and returning them
when the cmi_mc_getops entry point is called.

These ops are optional, but no MC functionality will be operational
if they are absent.