2020-03-26 07:09:57

by Pratik R. Sampat

[permalink] [raw]
Subject: [PATCH v6 0/4] Support for Self Save API in OPAL

v5:https://lists.ozlabs.org/pipermail/skiboot/2020-March/016538.html
Changelog
v5 --> v6
Updated background, motivation and illuminated potential design choices

Background
==========

The power management framework on POWER systems include core idle
states that lose context. Deep idle states namely "winkle" on POWER8
and "stop4" and "stop5" on POWER9 can be entered by a CPU to save
different levels of power, as a consequence of which all the
hypervisor resources such as SPRs and SCOMs are lost.

For most SPRs, saving and restoration of content for SPRs and SCOMs
is handled by the hypervisor kernel prior to entering an post exit
from an idle state respectively. However, there is a small set of
critical SPRs and XSCOMs that are expected to contain sane values even
before the control is transferred to the hypervisor kernel at system
reset vector.

For this purpose, microcode firmware provides a mechanism to restore
values on certain SPRs. The communication mechanism between the
hypervisor kernel and the microcode is a standard interface called
sleep-winkle-engine (SLW) on Power8 and Stop-API on Power9 which is
abstracted by OPAL calls from the hypervisor kernel. The Stop-API
provides an interface known as the self-restore API, to which the SPR
number and a predefined value to be restored on wake-up from a deep
stop state is supplied.


Motivation to introduce a new Stop-API
======================================

The self-restore API expects not just the SPR number but also the
value with which the SPR is restored. This is good for those SPRs such
as HSPRG0 whose values do not change at runtime, since for them, the
kernel can invoke the self-restore API at boot time once the values of
these SPRs are determined.

However, there are use-cases where-in the value to be saved cannot be
known or cannot be updated in the layer it currently is.
The shortcomings and the new use-cases which cannot be served by the
existing self-restore API, serves as motivation for a new API:

Shortcoming1:
------------
In a special wakeup scenario, SPRs such as PSSCR, whose values can
change at runtime, are compelled to make the self-restore API call
every time before entering a deep-idle state rendering it to be
prohibitively expensive

Shortcoming2:
------------
The value of LPCR is dynamic based on if the CPU is entered a stop
state during cpu idle versus cpu hotplug.
Today, an additional self-restore call is made before entering
CPU-Hotplug to clear the PECE1 bit in stop-API so that if we are
woken up by a special wakeup on an offlined CPU, we go back to stop
with the the bit cleared.
There is a overhead of an extra call

New Use-case:
-------------
In the case where the hypervisor is running on an
ultravisor environment, the boot time is too late in the cycle to make
the self-restore API calls, as these cannot be invoked from an
non-secure context anymore

To address these shortcomings, the firmware provides another API known
as the self-save API. The self-save API only takes the SPR number as a
parameter and will ensure that on wakeup from a deep-stop state the
SPR is restored with the value that it contained prior to entering the
deep-stop.

Contrast between self-save and self-restore APIs
================================================

Before entering
deep idle |---------------|
------------> | HCODE A |
| |---------------|
|---------| |
| CPU |----|
|---------| |
| |---------------|
|------------>| HCODE B |
On waking up |---------------|
from deep idle




When a self-restore API is invoked, the HCODE inserts instructions
into "HCODE B" region of the above figure to restore the content of
the SPR to the said value. The "HCODE B" region gets executed soon
after the CPU wakes up from a deep idle state, thus executing the
inserted instructions, thereby restoring the contents of the SPRs to
the required values.

When a self-save API is invoked, the HCODE inserts instructions into
the "HCODE A" region of the above figure to save the content of the
SPR into some location in memory. It also inserts instructions into
the "HCODE B" region to restore the content of the SPR to the
corresponding value saved in the memory by the instructions in "HCODE
A" region.

Thus, in contrast with self-restore, the self-save API *does not* need
a value to be passed to it, since it ensures that the value of SPR
before entering deep stop is saved, and subsequently the same value is
restored.

Self-save and self-restore are complementary features since,
self-restore can help in restoring a different value in the SPR on
wakeup from a deep-idle state than what it had before entering the
deep idle state. This was used in POWER8 for HSPRG0 to distinguish a
wakeup from Winkle vs Fastsleep.

Limitations of self-save
========================
Ideally all SPRs should be available for self-save, but HID0 is very
tricky to implement in microcode due to various endianess quirks.
Couple of implementation schemes were buggy and hence HID0 was left
out to be self-restore only.

The fallout of this limitation is as follows:

* In Non PEF environment, no issue. Linux will use self-restore for
HID0 as it does today and no functional impact.

* In PEF environment, the HID0 restore value is decided by OPAL during
boot and it is setup for LE hypervisor with radix MMU. This is the
default and current working configuration of a PEF environment.
However if there is a change, then HV Linux will try to change the
HID0 value to something different than what OPAL decided, at which
time deep-stop states will be disabled under this new PEF
environment.

A simple and workable design is achieved by scoping the power
management deep-stop state support only to a known default PEF
environment. Any deviation will affect *only* deep stop-state support
(stop4,5) in that environment and not have any functional impediment
to the environment itself.

In future, if there is a need to support changing of HID0 to various
values under PEF environment and support deep-stop states, it can be
worked out via an ultravisor call or improve the microcode design to
include HID0 in self-save. These future scheme would be an extension
and does not break or make the current implementation scheme
redundant.

Design Choices
==============

Presenting the design choices in front of us:

Design-Choice 1:
----------------
Only expose one of self-save or self-restore for all the SPRs. Prefer
Self-save

Pros:
- Simplifies the design heavily, since the Kernel can unambiguously
make one API call for all the SPRs on discovering the presence of
the API type.

Cons:
- Breaks backward compatibility if OPAL always chooses to expose
only the self-save API as the older kernels assume the existence
of self-restore.

- The set of SPRs supported by self-save and self-restore are not
identical. Eg: HID0 is not supported by self-save API. PSSCR
support via self-restore is not robust during special-wakeup.

- As discussed above, self-save and self-restore are
complementary. Thus OPAL apriory choosing one over the other for
all SPRs takes away the flexibility from the kernel.


Design-Choice 2:
----------------
Expose two arrays of SPRs: One set of SPRs that are supported by
self-save. Another set of SPRs supported by self-restore. These two
sets do not intersect. Further, if an SPR is supported by both
self-save and self-restore APIs, expose it only via self-save.

Pros:
- For an SPR the choice for the kernel is unambiguous.

Cons:
- Breaks backward compatibility if OPAL always chooses to expose
the legacy SPRs only via the self-save API as the older kernels
assume the existence of self-restore.

- By making the decision early on, we take away the flexibility
from the kernel to use an API of its choice for an SPR.


Design-Choice 3
---------------
Expose two arrays of SPRs. One set of SPRs that are supported by
self-save API. Another set of SPRs supported by self-restore API. Let
the kernel choose which API to invoke. Even if it wants to always
prefer self-save over self-restore, let that be kernel's choice.

Pros:
- Keeps the design flexible to allow the kernel to take a
decision based on its functional and performance requirements.
Thus, the kernel for instance can make a choice to invoke
self-restore API (when available) for SPRs whose values do not
evolve at runtime, and invoke the self-save API (when
available)
for SPRs whose values will change during runtime.

- Design is backward compatible with older kernels.

Cons:
- The Kernel code will have additional complexity for parsing two
lists of SPRs and making a choice w.r.t invocation of a specific
stop-api.



Patches Organization
====================
Design choice 3 has been chosen as an implementation to demonstrate in
this patch series.

Patch 1:
Commit adds support calling into the self save firmware API.
Also adds abstraction for making platform agnostic calls.

Patch 2:
Commit adds wrappers for the Self Save API for which an OPAL call can
be made.

Patch 3:
Commit adds API to determine the version of the STOP API. This helps
to identify support for self save in the firmware

Patch 4:
Commit adds device tree attributes to advertise self save and self
restore functionality along with the register set as a bitmask
currently supported in the firmware. It also uses the versioning API
to determine support for the self-save feature as a whole.

Pratik Rajesh Sampat (2):
Self save API integration
Advertise the self-save and self-restore attributes in the device tree

Prem Shanker Jha (2):
Self Save: Introducing Support for SPR Self Save
API to verify the STOP API and image compatibility

.../ibm,opal/power-mgt/self-restore.rst | 27 +
.../ibm,opal/power-mgt/self-save.rst | 27 +
doc/opal-api/opal-slw-self-save-reg-181.rst | 49 +
doc/opal-api/opal-slw-set-reg-100.rst | 5 +
doc/power-management.rst | 44 +
hw/slw.c | 205 ++++
include/opal-api.h | 3 +-
include/p9_stop_api.H | 122 ++-
include/skiboot.h | 4 +
libpore/p9_cpu_reg_restore_instruction.H | 11 +-
libpore/p9_hcd_memmap_base.H | 7 +
libpore/p9_stop_api.C | 964 ++++++++++--------
libpore/p9_stop_api.H | 141 ++-
libpore/p9_stop_data_struct.H | 4 +-
libpore/p9_stop_util.H | 27 +-
15 files changed, 1208 insertions(+), 432 deletions(-)
create mode 100644 doc/device-tree/ibm,opal/power-mgt/self-restore.rst
create mode 100644 doc/device-tree/ibm,opal/power-mgt/self-save.rst
create mode 100644 doc/opal-api/opal-slw-self-save-reg-181.rst

--
2.24.1


2020-03-26 07:09:59

by Pratik R. Sampat

[permalink] [raw]
Subject: [PATCH v6 2/4] Self save API integration

The commit makes the self save API available outside the firmware by defining
an OPAL wrapper.
This wrapper has a similar interface to that of self restore and expects the
cpu pir, SPR number, minus the value of that SPR to be passed in its
paramters and returns OPAL_SUCCESS on success.
The commit also documents both the self-save and the self-restore API
calls along with their working and usage.

Signed-off-by: Pratik Rajesh Sampat <[email protected]>
---
doc/opal-api/opal-slw-self-save-reg-181.rst | 49 ++++++++++++
doc/opal-api/opal-slw-set-reg-100.rst | 5 ++
doc/power-management.rst | 44 ++++++++++
hw/slw.c | 89 +++++++++++++++++++++
include/opal-api.h | 3 +-
include/p9_stop_api.H | 17 ++++
include/skiboot.h | 3 +
7 files changed, 209 insertions(+), 1 deletion(-)
create mode 100644 doc/opal-api/opal-slw-self-save-reg-181.rst

diff --git a/doc/opal-api/opal-slw-self-save-reg-181.rst b/doc/opal-api/opal-slw-self-save-reg-181.rst
new file mode 100644
index 00000000..5aa4c930
--- /dev/null
+++ b/doc/opal-api/opal-slw-self-save-reg-181.rst
@@ -0,0 +1,49 @@
+.. OPAL_SLW_SELF_SAVE_REG:
+
+OPAL_SLW_SELF_SAVE_REG
+======================
+
+.. code-block:: c
+
+ #define OPAL_SLW_SELF_SAVE_REG 181
+
+ int64_t opal_slw_self_save_reg(uint64_t cpu_pir, uint64_t sprn);
+
+:ref:`OPAL_SLW_SELF_SAVE_REG` is used to inform low-level firmware to save
+the current contents of the SPR before entering a state of loss and
+also restore the content back on waking up from a deep stop state.
+
+An OPAL call `OPAL_SLW_SET_REG` exists which is similar in function as
+saving and restoring the SPR, with one difference being that the value of the
+SPR must also be supplied in the parameters.
+Complete reference: doc/opal-api/opal-slw-set-reg-100.rst
+
+Parameters
+----------
+
+``uint64_t cpu_pir``
+ This parameter specifies the pir of the cpu for which the call is being made.
+``uint64_t sprn``
+ This parameter specifies the spr number as mentioned in p9_stop_api.H
+ The list of SPRs supported is as follows. This list is suppiled through the
+ device tree:
+ P9_STOP_SPR_DAWR,
+ P9_STOP_SPR_HSPRG0,
+ P9_STOP_SPR_LDBAR,
+ P9_STOP_SPR_LPCR,
+ P9_STOP_SPR_PSSCR,
+ P9_STOP_SPR_MSR,
+ P9_STOP_SPR_HRMOR,
+ P9_STOP_SPR_HMEER,
+ P9_STOP_SPR_PMCR,
+ P9_STOP_SPR_PTCR
+
+Returns
+-------
+
+:ref:`OPAL_UNSUPPORTED`
+ If spr restore is not supported by pore engine.
+:ref:`OPAL_PARAMETER`
+ Invalid handle for the pir/chip
+:ref:`OPAL_SUCCESS`
+ On success
diff --git a/doc/opal-api/opal-slw-set-reg-100.rst b/doc/opal-api/opal-slw-set-reg-100.rst
index 2e8f1bd6..ee3e68ce 100644
--- a/doc/opal-api/opal-slw-set-reg-100.rst
+++ b/doc/opal-api/opal-slw-set-reg-100.rst
@@ -21,6 +21,11 @@ In Power 9, it uses p9_stop_save_cpureg(), api provided by self restore code,
to inform the spr with their corresponding values with which they
must be restored.

+An OPAL call `OPAL_SLW_SELF_SAVE_REG` exists which is similar in function
+saving and restoring the SPR, with one difference being that the value of the
+SPR doesn't need to be passed in the parameters, only with the SPR number
+the firmware can identify, save and restore the values for the same.
+Complete reference: doc/opal-api/opal-slw-self-save-reg-181.rst

Parameters
----------
diff --git a/doc/power-management.rst b/doc/power-management.rst
index 76491a71..992a18d0 100644
--- a/doc/power-management.rst
+++ b/doc/power-management.rst
@@ -15,3 +15,47 @@ On boot, specific stop states can be disabled via setting a mask. For example,
to disable all but stop 0,1,2, use ~0xE0000000. ::

nvram -p ibm,skiboot --update-config opal-stop-state-disable-mask=0x1FFFFFFF
+
+Saving and restoring Special Purpose Registers(SPRs)
+----------------------------------------------------
+
+When a CPU wakes up from a deep stop state which can result in
+hypervisor state loss, all the SPRs are lost. The Linux Kernel expects
+a small set of SPRs to contain an expected value when the CPU wakes up
+from such a deep stop state. The microcode firmware provides the
+following two APIs, collectively known as the stop-APIs, to allow the
+kernel/OPAL to specify this set of SPRs and the value that they need
+to be restored with on waking up from a deep stop state.
+
+Self-restore:
+int64_t opal_slw_set_reg(uint64_t cpu_pir, uint64_t sprn, uint64_t val);
+The SPR number and the value of the that SPR must be restored with on
+wakeup from the deep-stop state must be specified. When this call is
+made, the microcode inserts instruction into the HOMER region to
+restore the content of the SPR to the specified value on wakeup from a
+deep-stop state. These instructions are executed by the CPU as soon as
+it wakes up from a deep stop state. The call is to be made once per
+SPR.
+
+Self-Save:
+int64_t opal_slw_self_save_reg(uint64_t cpu_pir, uint64_t sprn);
+Only the SPR number needs to be specified. When this call is made, the
+microcode inserts instructions into the HOMER region to save the
+current value of the SPR before the CPU goes to a deep stop state, and
+restores the value back when the CPU wakes up from a deep stop state.
+These instructions are correspondingly executed just before and after
+the CPU goes/comes out of a deep stop state. This call can be made
+once per SPR.
+
+The key difference between self-save and self-restore is the
+use-case. If the Kernel expects the SPR to contain a particular value
+on waking up from a deep-stop state, that wasn't the value of that SPR
+before entering deep stop-state, then self-restore is preferable.
+When deep stop states are to be supported in an Ultravisor
+environment, since HOMER is in a secure region, the stop-api cannot
+update the HOMER if invoked from a context when the OPAL/Kernel is
+executing without the ultravisor privilege. In this scenario, at the
+time of early OPAL boot, while OPAL has ultravisor privileges, it can
+make the self-save stop-api call for all the supported SPRs, so that
+the microcode in the HOMER will always save and restore all the
+supported SPRs during entry/exit from a deep stop state.
diff --git a/hw/slw.c b/hw/slw.c
index beb129a8..6a09cc2c 100644
--- a/hw/slw.c
+++ b/hw/slw.c
@@ -35,6 +35,43 @@ static bool slw_current_le = false;
enum wakeup_engine_states wakeup_engine_state = WAKEUP_ENGINE_NOT_PRESENT;
bool has_deep_states = false;

+/**
+ * The struct and SPR list is partially consistent with libpore/p9_stop_api.c
+ */
+/**
+ * @brief summarizes attributes associated with a SPR register.
+ */
+typedef struct
+{
+ uint32_t iv_sprId;
+ bool iv_isThreadScope;
+ uint32_t iv_saveMaskPos;
+
+} StopSprReg_t;
+
+/**
+ * @brief a true in the table below means register is of scope thread
+ * whereas a false meanse register is of scope core.
+ * The number is the bit position on a uint32_t mask
+ */
+
+static const StopSprReg_t g_sprRegister[] =
+{
+ { P9_STOP_SPR_DAWR, true, 1 },
+ { P9_STOP_SPR_HSPRG0, true, 3 },
+ { P9_STOP_SPR_LDBAR, true, 4, },
+ { P9_STOP_SPR_LPCR, true, 5 },
+ { P9_STOP_SPR_PSSCR, true, 6 },
+ { P9_STOP_SPR_MSR, true, 7 },
+ { P9_STOP_SPR_HRMOR, false, 255 },
+ { P9_STOP_SPR_HID, false, 21 },
+ { P9_STOP_SPR_HMEER, false, 22 },
+ { P9_STOP_SPR_PMCR, false, 23 },
+ { P9_STOP_SPR_PTCR, false, 24 },
+};
+
+static const uint32_t MAX_SPR_SUPPORTED = ARRAY_SIZE(g_sprRegister);
+
DEFINE_LOG_ENTRY(OPAL_RC_SLW_INIT, OPAL_PLATFORM_ERR_EVT, OPAL_SLW,
OPAL_PLATFORM_FIRMWARE, OPAL_PREDICTIVE_ERR_GENERAL,
OPAL_NA);
@@ -1446,6 +1483,58 @@ int64_t opal_slw_set_reg(uint64_t cpu_pir, uint64_t sprn, uint64_t val)

opal_call(OPAL_SLW_SET_REG, opal_slw_set_reg, 3);

+int64_t opal_slw_self_save_reg(uint64_t cpu_pir, uint64_t sprn)
+{
+ struct cpu_thread * c = find_cpu_by_pir(cpu_pir);
+ uint32_t save_reg_vector = 0;
+ struct proc_chip * chip;
+ int rc;
+ int index;
+
+ if (!c) {
+ prlog(PR_DEBUG, "SLW: Unknown thread with pir %x\n",
+ (u32) cpu_pir);
+ return OPAL_PARAMETER;
+ }
+
+ chip = get_chip(c->chip_id);
+ if (!chip) {
+ prlog(PR_DEBUG, "SLW: Unknown chip for thread with pir %x\n",
+ (u32) cpu_pir);
+ return OPAL_PARAMETER;
+ }
+ if (proc_gen != proc_gen_p9 || !has_deep_states) {
+ prlog(PR_DEBUG, "SLW: Does not support deep states\n");
+ return OPAL_UNSUPPORTED;
+ }
+ if (wakeup_engine_state != WAKEUP_ENGINE_PRESENT) {
+ log_simple_error(&e_info(OPAL_RC_SLW_REG),
+ "SLW: wakeup_engine in bad state=%d chip=%x\n",
+ wakeup_engine_state, chip->id);
+ return OPAL_INTERNAL_ERROR;
+ }
+ for (index = 0; index < MAX_SPR_SUPPORTED; ++index) {
+ if (sprn == (CpuReg_t) g_sprRegister[index].iv_sprId) {
+ save_reg_vector = PPC_BIT32(
+ g_sprRegister[index].iv_saveMaskPos);
+ break;
+ }
+ }
+ if (save_reg_vector == 0)
+ return OPAL_INTERNAL_ERROR;
+ rc = p9_stop_save_cpureg_control((void *) chip->homer_base,
+ cpu_pir, save_reg_vector);
+
+ if (rc) {
+ log_simple_error(&e_info(OPAL_RC_SLW_REG),
+ "SLW: Failed to save vector %x for CPU %x\n",
+ save_reg_vector, c->pir);
+ return OPAL_INTERNAL_ERROR;
+ }
+ return OPAL_SUCCESS;
+}
+opal_call(OPAL_SLW_SELF_SAVE_REG, opal_slw_self_save_reg, 2);
+
void slw_init(void)
{
struct proc_chip *chip;
diff --git a/include/opal-api.h b/include/opal-api.h
index e90cab1e..1607a89b 100644
--- a/include/opal-api.h
+++ b/include/opal-api.h
@@ -227,7 +227,8 @@
#define OPAL_SECVAR_ENQUEUE_UPDATE 178
#define OPAL_PHB_SET_OPTION 179
#define OPAL_PHB_GET_OPTION 180
-#define OPAL_LAST 180
+#define OPAL_SLW_SELF_SAVE_REG 181
+#define OPAL_LAST 181

#define QUIESCE_HOLD 1 /* Spin all calls at entry */
#define QUIESCE_REJECT 2 /* Fail all calls with OPAL_BUSY */
diff --git a/include/p9_stop_api.H b/include/p9_stop_api.H
index 9d3bc1e5..c304f70f 100644
--- a/include/p9_stop_api.H
+++ b/include/p9_stop_api.H
@@ -34,6 +34,8 @@
///
/// @file p9_stop_api.H
/// @brief describes STOP API which create/manipulate STOP image.
+/// This header need not be consistent, however is a subset of the
+/// libpore/p9_stop_api.H counterpart
///
// *HWP HW Owner : Greg Still <[email protected]>
// *HWP FW Owner : Prem Shanker Jha <[email protected]>
@@ -58,6 +60,7 @@ typedef enum
P9_STOP_SPR_HRMOR = 313, // core register
P9_STOP_SPR_LPCR = 318, // thread register
P9_STOP_SPR_HMEER = 337, // core register
+ P9_STOP_SPR_PTCR = 464, // core register
P9_STOP_SPR_LDBAR = 850, // thread register
P9_STOP_SPR_PSSCR = 855, // thread register
P9_STOP_SPR_PMCR = 884, // core register
@@ -230,6 +233,20 @@ StopReturnCode_t p9_stop_save_scom( void* const i_pImage,
const ScomOperation_t i_operation,
const ScomSection_t i_section );

+/**
+ * @brief Facilitates self save and restore of a list of SPRs of a thread.
+ * @param[in] i_pImage points to the start of HOMER image of P9 chip.
+ * @param[in] i_pir PIR associated with thread
+ * @param[in] i_saveRegVector bit vector representing SPRs that needs to be restored.
+ * @return STOP_SAVE_SUCCESS if API succeeds, error code otherwise.
+ * @note SPR save vector is a bit vector. For each SPR supported,
+ * there is an associated bit position in the bit vector.Refer
+ * to definition of SprBitPositionList_t to determine bit position
+ * associated with a particular SPR.
+ */
+StopReturnCode_t
+p9_stop_save_cpureg_control( void* i_pImage, const uint64_t i_pir,
+ const uint32_t i_saveRegVector );
#ifdef __cplusplus
} // extern "C"
}; // namespace stopImageSection ends
diff --git a/include/skiboot.h b/include/skiboot.h
index 30ff500c..9ced240e 100644
--- a/include/skiboot.h
+++ b/include/skiboot.h
@@ -306,6 +306,9 @@ extern void nx_p9_rng_late_init(void);
/* SLW reinit function for switching core settings */
extern int64_t slw_reinit(uint64_t flags);

+/* Self save SPR before entering the stop state */
+extern int64_t opal_slw_self_save_reg(uint64_t cpu_pir, uint64_t sprn);
+
/* Patch SPR in SLW image */
extern int64_t opal_slw_set_reg(uint64_t cpu_pir, uint64_t sprn, uint64_t val);

--
2.24.1

2020-03-26 07:10:11

by Pratik R. Sampat

[permalink] [raw]
Subject: [PATCH v6 3/4] API to verify the STOP API and image compatibility

From: Prem Shanker Jha <[email protected]>

Commit defines a new API primarily intended for OPAL to determine
cpu register save API's compatibility with HOMER layout and
self save restore. It can help OPAL determine if version of
API integrated with OPAL is different from hostboot.

Change-Id: Ic0de45a336cfb8b6b6096a10ac1cd3ffbaa44fc0
Reviewed-on: http://rchgit01.rchland.ibm.com/gerrit1/77612
Tested-by: FSP CI Jenkins <[email protected]>
Tested-by: Jenkins Server <[email protected]>
Tested-by: Hostboot CI <[email protected]>
Reviewed-by: RANGANATHPRASAD G. BRAHMASAMUDRA <[email protected]>
Reviewed-by: Gregory S Still <[email protected]>
Reviewed-by: Jennifer A Stofer <[email protected]>
Reviewed-on: http://rchgit01.rchland.ibm.com/gerrit1/77614
Tested-by: Jenkins OP Build CI <[email protected]>
Tested-by: Jenkins OP HW <[email protected]>
Reviewed-by: Daniel M Crowell <[email protected]>
Signed-off-by: Pratik Rajesh Sampat <[email protected]>
---
include/p9_stop_api.H | 26 +++++++++++
libpore/p9_cpu_reg_restore_instruction.H | 7 ++-
libpore/p9_hcd_memmap_base.H | 7 +++
libpore/p9_stop_api.C | 58 +++++++++++++++++++++++-
libpore/p9_stop_api.H | 26 ++++++++++-
libpore/p9_stop_util.H | 20 ++++----
6 files changed, 131 insertions(+), 13 deletions(-)

diff --git a/include/p9_stop_api.H b/include/p9_stop_api.H
index c304f70f..09ce3dc1 100644
--- a/include/p9_stop_api.H
+++ b/include/p9_stop_api.H
@@ -110,6 +110,7 @@ typedef enum
STOP_SAVE_FAIL = 14, // for internal failure within firmware.
STOP_SAVE_SPR_ENTRY_MISSING = 15,
STOP_SAVE_SPR_BIT_POS_RESERVE = 16,
+ STOP_SAVE_API_IMG_INCOMPATIBLE = 18,
} StopReturnCode_t;

/**
@@ -164,6 +165,14 @@ typedef enum

} ScomSection_t;

+/**
+ * @brief versions pertaining relvant to STOP API.
+ */
+typedef enum
+{
+ STOP_API_VER = 0x00,
+ STOP_API_VER_CONTROL = 0x02,
+} VersionList_t;


/**
@@ -195,6 +204,14 @@ typedef enum
BIT_POS_USPRG1 = 30,
} SprBitPositionList_t;

+typedef enum
+{
+ SMF_SUPPORT_MISSING_IN_HOMER = 0x01,
+ SELF_SUPPORT_MISSING_FOR_LE_HYP = 0x02,
+ IPL_RUNTIME_CPU_SAVE_VER_MISMATCH = 0x04,
+ SELF_RESTORE_VER_MISMATCH = 0x08,
+} VersionIncompList_t;
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -247,6 +264,15 @@ StopReturnCode_t p9_stop_save_scom( void* const i_pImage,
StopReturnCode_t
p9_stop_save_cpureg_control( void* i_pImage, const uint64_t i_pir,
const uint32_t i_saveRegVector );
+
+/**
+ * @brief verifies if API is compatible of current HOMER image.
+ * @param[in] i_pImage points to the start of HOMER image of P9 chip.
+ * @param[out] o_inCompVector list of incompatibilities found.
+ * @return STOP_SAVE_SUCCESS if if API succeeds, error code otherwise.
+ */
+StopReturnCode_t proc_stop_api_discover_capability( void* const i_pImage, uint64_t* o_inCompVector );
+
#ifdef __cplusplus
} // extern "C"
}; // namespace stopImageSection ends
diff --git a/libpore/p9_cpu_reg_restore_instruction.H b/libpore/p9_cpu_reg_restore_instruction.H
index d69a4212..5f168855 100644
--- a/libpore/p9_cpu_reg_restore_instruction.H
+++ b/libpore/p9_cpu_reg_restore_instruction.H
@@ -5,7 +5,7 @@
/* */
/* OpenPOWER HostBoot Project */
/* */
-/* Contributors Listed Below - COPYRIGHT 2015,2018 */
+/* Contributors Listed Below - COPYRIGHT 2015,2020 */
/* [+] International Business Machines Corp. */
/* */
/* */
@@ -69,6 +69,11 @@ enum
OPCODE_18 = 18,
SELF_SAVE_FUNC_ADD = 0x2300,
SELF_SAVE_OFFSET = 0x180,
+ SKIP_SPR_REST_INST = 0x4800001c, //b . +0x01c
+ MFLR_R30 = 0x7fc802a6,
+ SKIP_SPR_SELF_SAVE = 0x3bff0020, //addi r31 r31, 0x20
+ MTLR_INST = 0x7fc803a6, //mtlr r30
+ BRANCH_BE_INST = 0x48000020,
};

#define MR_R0_TO_R10 0x7c0a0378UL //mr r10 r0
diff --git a/libpore/p9_hcd_memmap_base.H b/libpore/p9_hcd_memmap_base.H
index 000fafef..ddb56728 100644
--- a/libpore/p9_hcd_memmap_base.H
+++ b/libpore/p9_hcd_memmap_base.H
@@ -444,6 +444,13 @@ HCD_CONST(CME_QUAD_PSTATE_SIZE, HALF_KB)

HCD_CONST(CME_REGION_SIZE, (64 * ONE_KB))

+
+// HOMER compatibility
+
+HCD_CONST(STOP_API_CPU_SAVE_VER, 0x02)
+HCD_CONST(SELF_SAVE_RESTORE_VER, 0x02)
+HCD_CONST(SMF_SUPPORT_SIGNATURE_OFFSET, 0x1300)
+HCD_CONST(SMF_SELF_SIGNATURE, (0x5f534d46))
// Debug

HCD_CONST(CPMR_TRACE_REGION_OFFSET, (512 * ONE_KB))
diff --git a/libpore/p9_stop_api.C b/libpore/p9_stop_api.C
index 2d9bb549..10e050a1 100644
--- a/libpore/p9_stop_api.C
+++ b/libpore/p9_stop_api.C
@@ -5,7 +5,7 @@
/* */
/* OpenPOWER HostBoot Project */
/* */
-/* Contributors Listed Below - COPYRIGHT 2015,2018 */
+/* Contributors Listed Below - COPYRIGHT 2015,2020 */
/* [+] International Business Machines Corp. */
/* */
/* */
@@ -1828,6 +1828,62 @@ StopReturnCode_t proc_stop_init_self_save( void* const i_pImage, const uint32_t
return l_rc;
}

+StopReturnCode_t proc_stop_api_discover_capability( void* const i_pImage, uint64_t * o_inCompVector )
+{
+ StopReturnCode_t l_rc = STOP_SAVE_SUCCESS;
+ uint64_t l_incompVector = 0;
+ uint32_t l_tempWord = 0;
+ *o_inCompVector = 0;
+
+ do
+ {
+ if( !i_pImage )
+ {
+ l_rc = STOP_SAVE_ARG_INVALID_IMG;
+ break;
+ }
+
+ l_tempWord =
+ *(uint32_t*)((uint8_t*)i_pImage + CPMR_HOMER_OFFSET + SMF_SUPPORT_SIGNATURE_OFFSET);
+
+ if( l_tempWord != SWIZZLE_4_BYTE(SMF_SELF_SIGNATURE) )
+ {
+ l_incompVector |= SMF_SUPPORT_MISSING_IN_HOMER;
+ }
+
+ l_tempWord = *(uint32_t *)((uint8_t *)i_pImage + CPMR_HOMER_OFFSET + CPMR_HEADER_SIZE );
+
+ if( l_tempWord != SWIZZLE_4_BYTE(BRANCH_BE_INST) )
+ {
+ l_incompVector |= SELF_SUPPORT_MISSING_FOR_LE_HYP;
+ }
+
+ l_tempWord = *(uint8_t *)((uint8_t *)i_pImage + CPMR_HOMER_OFFSET + CPMR_SELF_RESTORE_VER_BYTE );
+
+ if( l_tempWord < SELF_SAVE_RESTORE_VER )
+ {
+ l_incompVector |= SELF_RESTORE_VER_MISMATCH;
+ }
+
+ l_tempWord = *(uint8_t *)((uint8_t *)i_pImage + CPMR_HOMER_OFFSET + CPMR_STOP_API_VER_BYTE );
+
+ if( l_tempWord < STOP_API_CPU_SAVE_VER )
+ {
+ l_incompVector |= IPL_RUNTIME_CPU_SAVE_VER_MISMATCH;
+ }
+
+ *o_inCompVector = l_incompVector;
+
+ if( l_incompVector )
+ {
+ l_rc = STOP_SAVE_API_IMG_INCOMPATIBLE;
+ }
+
+ }while(0);
+
+ return l_rc;
+}
+
#ifdef __cplusplus
} //namespace stopImageSection ends

diff --git a/libpore/p9_stop_api.H b/libpore/p9_stop_api.H
index 3f6420ff..983a3845 100644
--- a/libpore/p9_stop_api.H
+++ b/libpore/p9_stop_api.H
@@ -5,7 +5,7 @@
/* */
/* OpenPOWER HostBoot Project */
/* */
-/* Contributors Listed Below - COPYRIGHT 2015,2018 */
+/* Contributors Listed Below - COPYRIGHT 2015,2020 */
/* [+] International Business Machines Corp. */
/* */
/* */
@@ -114,6 +114,7 @@ typedef enum
STOP_SAVE_FAIL = 14, // for internal failure within firmware.
STOP_SAVE_SPR_ENTRY_MISSING = 15,
STOP_SAVE_SPR_BIT_POS_RESERVE = 16,
+ STOP_SAVE_API_IMG_INCOMPATIBLE = 18,
} StopReturnCode_t;

/**
@@ -198,6 +199,21 @@ typedef enum
BIT_POS_USPRG1 = 30,
} SprBitPositionList_t;

+/**
+ * @brief List of major incompatibilities between API version.
+ * @note STOP APIs assumes a specific HOMER layout, certain
+ * level of CME-SGPE hcode and certain version of self-save restore
+ * binary. A mismatch can break STOP function.
+ */
+
+typedef enum
+{
+ SMF_SUPPORT_MISSING_IN_HOMER = 0x01,
+ SELF_SUPPORT_MISSING_FOR_LE_HYP = 0x02,
+ IPL_RUNTIME_CPU_SAVE_VER_MISMATCH = 0x04,
+ SELF_RESTORE_VER_MISMATCH = 0x08,
+} VersionIncompList_t;
+

#ifdef __cplusplus
extern "C" {
@@ -341,6 +357,14 @@ StopReturnCode_t proc_stop_save_cpureg( void* const i_pImage,
*/
StopReturnCode_t proc_stop_init_self_save( void* const i_pImage, const uint32_t i_corePos );

+/**
+ * @brief verifies if API is compatible of current HOMER image.
+ * @param[in] i_pImage points to the start of HOMER image of P9 chip.
+ * @param[out] o_inCompVector list of incompatibilities found.
+ * @return STOP_SAVE_SUCCESS if if API succeeds, error code otherwise.
+ */
+StopReturnCode_t proc_stop_api_discover_capability( void* const i_pImage, uint64_t* o_inCompVector );
+
#ifdef __cplusplus
} // extern "C"
}; // namespace stopImageSection ends
diff --git a/libpore/p9_stop_util.H b/libpore/p9_stop_util.H
index 79b4e959..1328a54b 100644
--- a/libpore/p9_stop_util.H
+++ b/libpore/p9_stop_util.H
@@ -72,18 +72,18 @@ namespace stopImageSection
( (((WORD) >> 8) & 0x00FF) | (((WORD) << 8) & 0xFF00) )

#define SWIZZLE_4_BYTE(WORD) \
- ( (((WORD) >> 24) & 0x000000FF) | (((WORD) >> 8) & 0x0000FF00) | \
- (((WORD) << 8) & 0x00FF0000) | (((WORD) << 24) & 0xFF000000) )
+ ( (((WORD) & 0x000000FF) << 24) | (((WORD) & 0x0000FF00) << 8) | \
+ (((WORD) & 0x00FF0000) >> 8) | (((WORD) & 0xFF000000) >> 24) )

#define SWIZZLE_8_BYTE(WORD) \
- ( (((WORD) >> 56) & 0x00000000000000FF) | \
- (((WORD) >> 40) & 0x000000000000FF00)| \
- (((WORD) >> 24) & 0x0000000000FF0000) | \
- (((WORD) >> 8) & 0x00000000FF000000) | \
- (((WORD) << 8) & 0x000000FF00000000) | \
- (((WORD) << 24) & 0x0000FF0000000000) | \
- (((WORD) << 40) & 0x00FF000000000000) | \
- (((WORD) << 56) & 0xFF00000000000000) )
+ ( (((WORD) & 0x00000000000000ffULL) << 56) | \
+ (((WORD) & 0x000000000000ff00ULL) << 40) | \
+ (((WORD) & 0x0000000000ff0000ULL) << 24) | \
+ (((WORD) & 0x00000000ff000000ULL) << 8) | \
+ (((WORD) & 0x000000ff00000000ULL) >> 8) | \
+ (((WORD) & 0x0000ff0000000000ULL) >> 24) | \
+ (((WORD) & 0x00ff000000000000ULL) >> 40) | \
+ (((WORD) & 0xff00000000000000ULL) >> 56) )
#endif

/**
--
2.24.1

2020-03-26 07:10:16

by Pratik R. Sampat

[permalink] [raw]
Subject: [PATCH v6 1/4] Self Save: Introducing Support for SPR Self Save

From: Prem Shanker Jha <[email protected]>

The commit is a merger of commits that makes the following changes:
1. Commit fixes some issues with code found during integration test
- replacement of addi with xor instruction during self save API.
- fixing instruction generation for MFMSR during self save
- data struct updates in STOP API
- error RC updates for hcode image build
- HOMER parser updates.
- removed self save support for URMOR and HRMOR
- code changes for compilation with OPAL
- populating CME Image header with unsecure HOMER address.

Key_Cronus_Test=PM_REGRESS

Change-Id: I7cedcc466267c4245255d8d75c01ed695e316720
Reviewed-on: http://rchgit01.rchland.ibm.com/gerrit1/66580
Tested-by: FSP CI Jenkins <[email protected]>
Tested-by: HWSV CI <[email protected]>
Tested-by: PPE CI <[email protected]>
Tested-by: Jenkins Server <[email protected]>
Tested-by: Cronus HW CI <[email protected]>
Tested-by: Hostboot CI <[email protected]>
Reviewed-by: Gregory S. Still <[email protected]>
Reviewed-by: RAHUL BATRA <[email protected]>
Reviewed-by: Jennifer A. Stofer <[email protected]>
Reviewed-on: http://rchgit01.rchland.ibm.com/gerrit1/66587
Reviewed-by: Christian R. Geddes <[email protected]>
Signed-off-by: Prem Shanker Jha <[email protected]>
Signed-off-by: Akshay Adiga <[email protected]>
Signed-off-by: Pratik Rajesh Sampat <[email protected]>

2. The commit also incorporates changes that make STOP API project
agnostic changes include defining wrapper functions which call legacy
API. It also adds duplicate enum members which start with prefix PROC
instead of P9.

Key_Cronus_Test=PM_REGRESS

Change-Id: If87970f3e8cf9b507f33eb1be249e03eb3836a5e
RTC: 201128
Reviewed-on: http://rchgit01.rchland.ibm.com/gerrit1/71307
Tested-by: FSP CI Jenkins <[email protected]>
Tested-by: Jenkins Server <[email protected]>
Tested-by: Hostboot CI <[email protected]>
Tested-by: Cronus HW CI <[email protected]>
Reviewed-by: RANGANATHPRASAD G. BRAHMASAMUDRA <[email protected]>
Reviewed-by: Gregory S. Still <[email protected]>
Reviewed-by: Jennifer A Stofer <[email protected]>
Reviewed-on: http://rchgit01.rchland.ibm.com/gerrit1/71314
Tested-by: Jenkins OP Build CI <[email protected]>
Tested-by: Jenkins OP HW <[email protected]>
Reviewed-by: Daniel M. Crowell <[email protected]>
Signed-off-by: Prem Shanker Jha <[email protected]>
Signed-off-by: Pratik Rajesh Sampat <[email protected]>
---
include/p9_stop_api.H | 79 +-
libpore/p9_cpu_reg_restore_instruction.H | 4 +
libpore/p9_stop_api.C | 954 +++++++++++++----------
libpore/p9_stop_api.H | 115 ++-
libpore/p9_stop_data_struct.H | 4 +-
libpore/p9_stop_util.H | 7 +-
6 files changed, 721 insertions(+), 442 deletions(-)

diff --git a/include/p9_stop_api.H b/include/p9_stop_api.H
index 79abd000..9d3bc1e5 100644
--- a/include/p9_stop_api.H
+++ b/include/p9_stop_api.H
@@ -63,6 +63,26 @@ typedef enum
P9_STOP_SPR_PMCR = 884, // core register
P9_STOP_SPR_HID = 1008, // core register
P9_STOP_SPR_MSR = 2000, // thread register
+
+ //enum members which are project agnostic
+ PROC_STOP_SPR_DAWR = 180, // thread register
+ PROC_STOP_SPR_CIABR = 187, // thread register
+ PROC_STOP_SPR_DAWRX = 188, // thread register
+ PROC_STOP_SPR_HSPRG0 = 304, // thread register
+ PROC_STOP_SPR_HRMOR = 313, // core register
+ PROC_STOP_SPR_LPCR = 318, // thread register
+ PROC_STOP_SPR_HMEER = 337, // core register
+ PROC_STOP_SPR_PTCR = 464, // core register
+ PROC_STOP_SPR_USPRG0 = 496, // thread register
+ PROC_STOP_SPR_USPRG1 = 497, // thread register
+ PROC_STOP_SPR_URMOR = 505, // core register
+ PROC_STOP_SPR_SMFCTRL = 511, // thread register
+ PROC_STOP_SPR_LDBAR = 850, // thread register
+ PROC_STOP_SPR_PSSCR = 855, // thread register
+ PROC_STOP_SPR_PMCR = 884, // core register
+ PROC_STOP_SPR_HID = 1008, // core register
+ PROC_STOP_SPR_MSR = 2000, // thread register
+
} CpuReg_t;

/**
@@ -85,6 +105,8 @@ typedef enum
STOP_SAVE_SCOM_ENTRY_UPDATE_FAILED = 12,
STOP_SAVE_INVALID_FUSED_CORE_STATUS = 13,
STOP_SAVE_FAIL = 14, // for internal failure within firmware.
+ STOP_SAVE_SPR_ENTRY_MISSING = 15,
+ STOP_SAVE_SPR_BIT_POS_RESERVE = 16,
} StopReturnCode_t;

/**
@@ -101,7 +123,20 @@ typedef enum
P9_STOP_SCOM_RESET = 6,
P9_STOP_SCOM_OR_APPEND = 7,
P9_STOP_SCOM_AND_APPEND = 8,
- P9_STOP_SCOM_OP_MAX = 9
+ P9_STOP_SCOM_OP_MAX = 9,
+
+ //enum members which are project agnostic
+ PROC_STOP_SCOM_OP_MIN = 0,
+ PROC_STOP_SCOM_APPEND = 1,
+ PROC_STOP_SCOM_REPLACE = 2,
+ PROC_STOP_SCOM_OR = 3,
+ PROC_STOP_SCOM_AND = 4,
+ PROC_STOP_SCOM_NOOP = 5,
+ PROC_STOP_SCOM_RESET = 6,
+ PROC_STOP_SCOM_OR_APPEND = 7,
+ PROC_STOP_SCOM_AND_APPEND = 8,
+ PROC_STOP_SCOM_OP_MAX = 9,
+
} ScomOperation_t;

/**
@@ -114,9 +149,49 @@ typedef enum
P9_STOP_SECTION_EQ_SCOM = 2,
P9_STOP_SECTION_L2 = 3,
P9_STOP_SECTION_L3 = 4,
- P9_STOP_SECTION_MAX = 5
+ P9_STOP_SECTION_MAX = 5,
+
+ //enum members which are project agnostic
+ PROC_STOP_SECTION_MIN = 0,
+ PROC_STOP_SECTION_CORE_SCOM = 1,
+ PROC_STOP_SECTION_EQ_SCOM = 2,
+ PROC_STOP_SECTION_L2 = 3,
+ PROC_STOP_SECTION_L3 = 4,
+ PROC_STOP_SECTION_MAX = 5,
+
} ScomSection_t;

+
+
+/**
+ * @brief List of major incompatibilities between API version.
+ * @note STOP APIs assumes a specific HOMER layout, certain
+ * level of CME-SGPE hcode and certain version of self-save restore
+ * binary. A mismatch can break STOP function.
+ */
+
+/**
+ * @brief Summarizes bit position allocated to SPRs in save bit mask vector.
+ */
+typedef enum
+{
+ BIT_POS_CIABR = 0,
+ BIT_POS_DAWR = 1,
+ BIT_POS_DAWRX = 2,
+ BIT_POS_HSPRG0 = 3,
+ BIT_POS_LDBAR = 4,
+ BIT_POS_LPCR = 5,
+ BIT_POS_PSSCR = 6,
+ BIT_POS_MSR = 7,
+ BIT_POS_HID = 21,
+ BIT_POS_HMEER = 22,
+ BIT_POS_PMCR = 23,
+ BIT_POS_PTCR = 24,
+ BIT_POS_SMFCTRL = 28,
+ BIT_POS_USPRG0 = 29,
+ BIT_POS_USPRG1 = 30,
+} SprBitPositionList_t;
+
#ifdef __cplusplus
extern "C" {
#endif
diff --git a/libpore/p9_cpu_reg_restore_instruction.H b/libpore/p9_cpu_reg_restore_instruction.H
index cf00ff5e..d69a4212 100644
--- a/libpore/p9_cpu_reg_restore_instruction.H
+++ b/libpore/p9_cpu_reg_restore_instruction.H
@@ -62,6 +62,10 @@ enum
MTSPR_CONST1 = 467,
MTMSRD_CONST1 = 178,
MFSPR_CONST = 339,
+ BLR_INST = 0x4e800020,
+ MTSPR_BASE_OPCODE = 0x7c0003a6,
+ MFSPR_BASE_OPCODE = 0x7c0002a6,
+ ATTN_OPCODE = 0x00000200,
OPCODE_18 = 18,
SELF_SAVE_FUNC_ADD = 0x2300,
SELF_SAVE_OFFSET = 0x180,
diff --git a/libpore/p9_stop_api.C b/libpore/p9_stop_api.C
index 33aaf788..2d9bb549 100644
--- a/libpore/p9_stop_api.C
+++ b/libpore/p9_stop_api.C
@@ -54,33 +54,33 @@ namespace stopImageSection

const StopSprReg_t g_sprRegister[] =
{
- { P9_STOP_SPR_CIABR, true, 0 },
- { P9_STOP_SPR_DAWR, true, 1 },
- { P9_STOP_SPR_DAWRX, true, 2 },
- { P9_STOP_SPR_HSPRG0, true, 3 },
- { P9_STOP_SPR_LDBAR, true, 4, },
- { P9_STOP_SPR_LPCR, true, 5 },
- { P9_STOP_SPR_PSSCR, true, 6 },
- { P9_STOP_SPR_MSR, true, 7 },
- { P9_STOP_SPR_HRMOR, false, 20 },
- { P9_STOP_SPR_HID, false, 21 },
- { P9_STOP_SPR_HMEER, false, 22 },
- { P9_STOP_SPR_PMCR, false, 23 },
- { P9_STOP_SPR_PTCR, false, 24 },
- { P9_STOP_SPR_SMFCTRL, true, 28 },
- { P9_STOP_SPR_USPRG0, true, 29 },
- { P9_STOP_SPR_USPRG1, true, 30 },
- { P9_STOP_SPR_URMOR, false, 31 },
+ { P9_STOP_SPR_CIABR, true, 0 },
+ { P9_STOP_SPR_DAWR, true, 1 },
+ { P9_STOP_SPR_DAWRX, true, 2 },
+ { P9_STOP_SPR_HSPRG0, true, 3 },
+ { P9_STOP_SPR_LDBAR, true, 4, },
+ { P9_STOP_SPR_LPCR, true, 5 },
+ { P9_STOP_SPR_PSSCR, true, 6 },
+ { P9_STOP_SPR_MSR, true, 7 },
+ { P9_STOP_SPR_HRMOR, false, 255 },
+ { P9_STOP_SPR_HID, false, 21 },
+ { P9_STOP_SPR_HMEER, false, 22 },
+ { P9_STOP_SPR_PMCR, false, 23 },
+ { P9_STOP_SPR_PTCR, false, 24 },
+ { P9_STOP_SPR_SMFCTRL, true, 28 },
+ { P9_STOP_SPR_USPRG0, true, 29 },
+ { P9_STOP_SPR_USPRG1, true, 30 },
+ { P9_STOP_SPR_URMOR, false, 255 },
};

-const uint32_t MAX_SPR_SUPPORTED = 17;
+const uint32_t MAX_SPR_SUPPORTED = 17;
const uint32_t LEGACY_CORE_SCOM_SUPPORTED = 15;
const uint32_t LEGACY_QUAD_SCOM_SUPPORTED = 63;

//-----------------------------------------------------------------------------

/**
- * @brief vaildated input arguments passed to p9_stop_save_cpureg_control.
+ * @brief validated input arguments passed to p9_stop_save_cpureg_control.
* @param[in] i_pImage point to start of HOMER
* @param[in] i_coreId id of the core
* @param[in] i_threadId id of the thread
@@ -255,7 +255,7 @@ STATIC uint32_t getOriInstruction( const uint16_t i_Rs, const uint16_t i_Ra,
*/
STATIC uint32_t genKeyForSprLookup( const CpuReg_t i_regId )
{
- return getOriInstruction( 0, 0, (uint16_t) i_regId );
+ return getOriInstruction( 24, 0, (uint16_t) i_regId );
}

//-----------------------------------------------------------------------------
@@ -330,7 +330,7 @@ STATIC uint32_t getMtsprInstruction( const uint16_t i_Rs, const uint16_t i_Spr )
*/
STATIC uint32_t getMfmsrInstruction( const uint16_t i_Rt )
{
- uint32_t mfmsrInstOpcode = ((OPCODE_31 << 26) | (i_Rt << 21) | (MFMSR_CONST));
+ uint32_t mfmsrInstOpcode = ((OPCODE_31 << 26) | (i_Rt << 21) | ((MFMSR_CONST)<< 1));

return SWIZZLE_4_BYTE(mfmsrInstOpcode);
}
@@ -361,8 +361,13 @@ STATIC uint32_t getRldicrInstruction( const uint16_t i_Ra, const uint16_t i_Rs,

STATIC uint32_t getMfsprInstruction( const uint16_t i_Rt, const uint16_t i_sprNum )
{
- uint32_t mfsprInstOpcode = 0;
- mfsprInstOpcode = (( OPCODE_31 << 26 ) | ( i_Rt << 21 ) | ( i_sprNum << 11 ) | ( MFSPR_CONST << 1 ));
+ uint32_t mfsprInstOpcode = 0;
+ uint32_t temp = (( i_sprNum & 0x03FF ) << 11);
+ mfsprInstOpcode = (uint8_t)i_Rt << 21;
+ mfsprInstOpcode |= (( temp & 0x0000F800 ) << 5);
+ mfsprInstOpcode |= (( temp & 0x001F0000 ) >> 5);
+ mfsprInstOpcode |= MFSPR_BASE_OPCODE;
+
return SWIZZLE_4_BYTE(mfsprInstOpcode);
}

@@ -615,14 +620,14 @@ STATIC StopReturnCode_t getSprRegIndexAdjustment( const uint32_t i_saveMaskPos,

do
{
- if( (( i_saveMaskPos >= SPR_BIT_POS_8 ) && ( i_saveMaskPos <= SPR_BIT_POS_19 )) ||
+ if( (( i_saveMaskPos >= SPR_BIT_POS_8 ) && ( i_saveMaskPos <= SPR_BIT_POS_20 )) ||
(( i_saveMaskPos >= SPR_BIT_POS_25 ) && ( i_saveMaskPos <= SPR_BIT_POS_27 )) )
{
l_rc = STOP_SAVE_SPR_BIT_POS_RESERVE;
break;
}

- if( (i_saveMaskPos > SPR_BIT_POS_19) && (i_saveMaskPos < SPR_BIT_POS_25 ) )
+ if( (i_saveMaskPos > SPR_BIT_POS_20) && (i_saveMaskPos < SPR_BIT_POS_25) )
{
*i_sprAdjIndex = 12;
}
@@ -646,138 +651,9 @@ StopReturnCode_t p9_stop_save_cpureg( void* const i_pImage,
const uint64_t i_regData,
const uint64_t i_pir )
{
- StopReturnCode_t l_rc = STOP_SAVE_SUCCESS; // procedure return code
- HomerSection_t* chipHomer = NULL;
- SmfHomerSection_t* smfChipHomer = NULL;
-
- do
- {
- uint32_t threadId = 0;
- uint32_t coreId = 0;
- uint32_t lookUpKey = 0;
- void* pSprEntryLocation = NULL; // an offset w.r.t. to start of image
- void* pThreadLocation = NULL;
- bool threadScopeReg = false;
- uint8_t l_urmorFix = false;
- uint64_t l_sprValue = 0;
- uint8_t l_selfRestVer = 0;
-
- MY_INF(">> p9_stop_save_cpureg" );
-
- l_rc = getCoreAndThread( i_pImage, i_pir, &coreId, &threadId );
-
- if( l_rc )
- {
- MY_ERR("Failed to determine Core Id and Thread Id from PIR 0x%016llx",
- i_pir);
- break;
- }
-
- MY_INF( " PIR 0x%016llx coreId %d threadid %d "
- " registerId %d", i_pir, coreId,
- threadId, i_regId );
-
- // First of all let us validate all input arguments.
- l_rc = validateSprImageInputs( i_pImage,
- i_regId,
- coreId,
- &threadId,
- &threadScopeReg );
-
- if( l_rc )
- {
- // Error: bad argument traces out error code
- MY_ERR("Bad input argument rc %d", l_rc );
-
- break;
- }
-
- l_urmorFix = *(uint8_t*)((uint8_t*)i_pImage + CPMR_HOMER_OFFSET + CPMR_URMOR_FIX_BYTE);
- l_selfRestVer = *(uint8_t *)((uint8_t *)i_pImage + CPMR_HOMER_OFFSET + CPMR_SELF_RESTORE_VER_BYTE );
-
- if( l_selfRestVer )
- {
- smfChipHomer = ( SmfHomerSection_t*)i_pImage;
-
- if( threadScopeReg )
- {
- pThreadLocation =
- &(smfChipHomer->iv_coreThreadRestore[coreId].iv_threadRestoreArea[threadId][0]);
- }
- else
- {
- pThreadLocation =
- &(smfChipHomer->iv_coreThreadRestore[coreId].iv_coreRestoreArea[0]);
- }
- }
- else //Old fips or OPAL release that doesn't support SMF
- {
- chipHomer = (HomerSection_t*)i_pImage;
-
- if( threadScopeReg )
- {
- pThreadLocation =
- &(chipHomer->iv_coreThreadRestore[coreId][threadId].iv_threadArea[0]);
- }
- else
- {
- pThreadLocation =
- &(chipHomer->iv_coreThreadRestore[coreId][threadId].iv_coreArea[0]);
- }
- }
-
- if( ( SWIZZLE_4_BYTE(BLR_INST) == *(uint32_t*)pThreadLocation ) ||
- ( SWIZZLE_4_BYTE(ATTN_OPCODE) == *(uint32_t*) pThreadLocation ) )
- {
- // table for given core id doesn't exit. It needs to be
- // defined.
- pSprEntryLocation = pThreadLocation;
- }
- else
- {
- // an SPR restore section for given core already exists
- lookUpKey = genKeyForSprLookup( i_regId );
- l_rc = lookUpSprInImage( (uint32_t*)pThreadLocation,
- lookUpKey,
- threadScopeReg,
- &pSprEntryLocation,
- l_selfRestVer );
- }
-
- if( l_rc )
- {
- MY_ERR("Invalid or corrupt SPR entry. CoreId 0x%08x threadId ",
- "0x%08x regId 0x%08x lookUpKey 0x%08x pThreadLocation 0x%08x"
- , coreId, threadId, i_regId, lookUpKey, pThreadLocation );
- break;
- }
-
- if( ( P9_STOP_SPR_URMOR == i_regId ) && ( l_urmorFix ) )
- {
- l_sprValue = i_regData - URMOR_CORRECTION;
- }
- else
- {
- l_sprValue = i_regData;
- }
-
- l_rc = updateSprEntryInImage( (uint32_t*) pSprEntryLocation,
- i_regId,
- l_sprValue,
- UPDATE_SPR_ENTRY );
-
- if( l_rc )
- {
- MY_ERR( " Failed to update the SPR entry of PIR 0x%08x reg"
- "0x%08x", i_pir, i_regId );
- break;
- }
-
- }
- while(0);
+ MY_INF(">> p9_stop_save_cpureg" );

- MY_INF("<< p9_stop_save_cpureg" );
- return l_rc;
+ return proc_stop_save_cpureg( i_pImage, i_regId, i_regData, i_pir );
}

//-----------------------------------------------------------------------------
@@ -1003,103 +879,334 @@ StopReturnCode_t p9_stop_save_scom( void* const i_pImage,
const ScomOperation_t i_operation,
const ScomSection_t i_section )
{
- StopReturnCode_t l_rc = STOP_SAVE_SUCCESS;
- uint32_t entryLimit = 0;
- uint8_t chipletId = 0;
- uint32_t nopInst = 0;
- uint32_t index = 0;
- uint32_t imageVer = 0;
- uint32_t entrySwzHeader = 0;
- uint32_t l_maxScomRestoreEntry = 0;
- ScomEntry_t* pScomEntry = NULL;
- ScomEntry_t* pEntryLocation = NULL;
- ScomEntry_t* pNopLocation = NULL;
- ScomEntry_t* pEditScomHeader = NULL;
- StopCacheSection_t* pStopCacheScomStart = NULL;
- ScomEntry_t* pTableEndLocationtable = NULL;
- uint32_t swizzleAddr;
- uint64_t swizzleData;
- uint32_t swizzleAttn;
- uint32_t swizzleBlr = SWIZZLE_4_BYTE(BLR_INST);
- bool cacheEntry = true;
-
MY_INF(">> p9_stop_save_scom");

- //Reads SGPE image version info from QPMR Header in HOMER
- //For backward compatibility, for base version of SGPE Hcode,
- //STOP API retains default behavior but adds version specific
- //details in each entry in later versions.
- imageVer = *(uint32_t*)((uint8_t*)i_pImage + QPMR_HOMER_OFFSET + QPMR_BUILD_VER_BYTE);
- imageVer = SWIZZLE_4_BYTE(imageVer);
-
+ return proc_stop_save_scom( i_pImage, i_scomAddress,
+ i_scomData, i_operation, i_section );
+}

- do
- {
- chipletId = i_scomAddress >> 24;
- chipletId = chipletId & 0x3F;
+//-----------------------------------------------------------------------------

- l_rc = validateScomImageInputs( i_pImage, i_scomAddress, chipletId, i_operation, i_section );
+/**
+ * @brief searches a self save entry of an SPR in self-save segment.
+ * @param[in] i_sprBitPos bit position associated with SPR in save mask vector.
+ * @param[in] l_pSprSaveStart start location of SPR save segment
+ * @param[in] i_searchLength length of SPR save segment
+ * @param[in] i_pSaveSprLoc start location of save entry for a given SPR.
+ * @return STOP_SAVE_SUCCESS if look up succeeds, error code otherwise.
+ */
+STATIC StopReturnCode_t lookUpSelfSaveSpr( uint32_t i_sprBitPos, uint32_t* l_pSprSaveStart,
+ uint32_t i_searchLength, uint32_t** i_pSaveSprLoc )
+{
+ int32_t l_saveWordLength = (int32_t)(i_searchLength >> 2);
+ uint32_t l_oriInst = getOriInstruction( 0, 0, i_sprBitPos );
+ StopReturnCode_t l_rc = STOP_SAVE_FAIL;

- if( l_rc )
+ while( l_saveWordLength > 0 )
+ {
+ if( l_oriInst == *l_pSprSaveStart )
{
- MY_ERR( "invalid argument: aborting");
+ *i_pSaveSprLoc = l_pSprSaveStart;
+ l_rc = STOP_SAVE_SUCCESS;
break;
}

- if( chipletId >= CORE_CHIPLET_ID_MIN )
- {
- // chiplet is core. So, let us find the start address of SCOM area
- // pertaining to a core in STOP image.
- l_maxScomRestoreEntry =
- *(uint32_t*)((uint8_t*)i_pImage + CPMR_HOMER_OFFSET + CPMR_MAX_SCOM_REST_PER_CORE_BYTE);
- pScomEntry = CORE_ID_SCOM_START(i_pImage, chipletId )
- cacheEntry = false;
+ l_pSprSaveStart++;
+ l_saveWordLength--;
+ }

- if( !l_maxScomRestoreEntry )
- {
- //Old HB and new STOP API case. Retain legacy Number
- l_maxScomRestoreEntry = SWIZZLE_4_BYTE(LEGACY_CORE_SCOM_SUPPORTED);
- }
- }
- else
- {
- l_maxScomRestoreEntry =
- *(uint32_t*)((uint8_t*)i_pImage + QPMR_HOMER_OFFSET + QPMR_QUAD_MAX_SCOM_ENTRY_BYTE);
+ return l_rc;
+}

- if( !l_maxScomRestoreEntry )
- {
- // Incase of a bad HOMER header initialization, fall back on legacy number.
- l_maxScomRestoreEntry = SWIZZLE_4_BYTE(LEGACY_QUAD_SCOM_SUPPORTED);
- }
- // chiplet is a cache. let us find start address of cache section
- // associated with given chiplet. A cache section associated with
- // given chiplet is split in to L2, L3 and EQ area.
- pStopCacheScomStart = CACHE_SECTN_START(i_pImage,
- chipletId);
- }
+//-----------------------------------------------------------------------------

- l_maxScomRestoreEntry = SWIZZLE_4_BYTE(l_maxScomRestoreEntry);
+/**
+ * @brief searches a self save entry of an SPR in self-save segment.
+ * @param[in] i_pSaveReg start of editable location of a SPR save entry.
+ * @param[in] i_sprNum Id of the SPR for which entry needs to be edited.
+ * @return STOP_SAVE_SUCCESS if look up succeeds, error code otherwise.
+ */
+STATIC StopReturnCode_t updateSelfSaveEntry( uint32_t* i_pSaveReg, uint16_t i_sprNum )
+{
+ StopReturnCode_t l_rc = STOP_SAVE_SUCCESS;

- if(( !pStopCacheScomStart ) && ( !pScomEntry) )
+ do
+ {
+ if( !i_pSaveReg )
{
- //Error invalid pointer to SCOM entry in cache or core section
- //of STOP image.
- MY_ERR("invalid start location for chiplet %d",
- chipletId );
+ l_rc = STOP_SAVE_FAIL;
+ MY_ERR( "Failed to update self save area for SPR 0x%04x", i_sprNum );
break;
}

- switch( i_section )
+ if( P9_STOP_SPR_MSR == i_sprNum )
{
- case P9_STOP_SECTION_EQ_SCOM:
- pScomEntry = pStopCacheScomStart->nonCacheArea;
- entryLimit = MAX_EQ_SCOM_ENTRIES;
- break;
+ *i_pSaveReg = getMfmsrInstruction( 1 );
+ }
+ else
+ {
+ *i_pSaveReg = getMfsprInstruction( 1, i_sprNum );
+ }

- case P9_STOP_SECTION_L2:
- pScomEntry = pStopCacheScomStart->l2CacheArea;
- entryLimit = MAX_L2_SCOM_ENTRIES;
- break;
+ i_pSaveReg++;
+
+ *i_pSaveReg = getBranchLinkRegInstruction( );
+ }
+ while(0);
+
+ return l_rc;
+}
+
+//-----------------------------------------------------------------------------
+
+StopReturnCode_t p9_stop_save_cpureg_control( void* i_pImage,
+ const uint64_t i_pir,
+ const uint32_t i_saveRegVector )
+{
+ MY_INF( ">> p9_stop_save_cpureg_control" );
+
+ return proc_stop_save_cpureg_control( i_pImage, i_pir, i_saveRegVector );
+}
+
+//-----------------------------------------------------------------------------------------------------
+
+StopReturnCode_t p9_stop_init_cpureg( void* const i_pImage, const uint32_t i_corePos )
+{
+ MY_INF( ">> p9_stop_init_cpureg" );
+
+ return proc_stop_init_cpureg( i_pImage, i_corePos );
+}
+
+//-----------------------------------------------------------------------------------------------------
+
+StopReturnCode_t p9_stop_init_self_save( void* const i_pImage, const uint32_t i_corePos )
+{
+ MY_INF( ">> p9_stop_init_self_save" );
+
+ return proc_stop_init_self_save( i_pImage, i_corePos );
+}
+
+//-----------------------------------------------------------------------------------------------------
+
+StopReturnCode_t proc_stop_init_cpureg( void* const i_pImage, const uint32_t i_corePos )
+{
+
+ StopReturnCode_t l_rc = STOP_SAVE_SUCCESS;
+ uint32_t* l_pRestoreStart = NULL;
+ void* l_pTempLoc = NULL;
+ SmfHomerSection_t* l_pHomer = NULL;
+ uint32_t l_threadPos = 0;
+ uint32_t l_lookUpKey = 0;
+ uint32_t l_sprIndex = 0;
+ uint8_t l_selfRestVer = 0;
+
+ MY_INF( ">> proc_stop_init_cpureg" );
+
+ do
+ {
+ if( !i_pImage )
+ {
+ l_rc = STOP_SAVE_ARG_INVALID_IMG;
+ break;
+ }
+
+ if( i_corePos > MAX_CORE_ID_SUPPORTED )
+ {
+ l_rc = STOP_SAVE_ARG_INVALID_CORE;
+ break;
+ }
+
+ l_pHomer = ( SmfHomerSection_t * ) i_pImage;
+ l_selfRestVer = *(uint8_t *)((uint8_t *)i_pImage + CPMR_HOMER_OFFSET + CPMR_SELF_RESTORE_VER_BYTE );
+
+ for( l_sprIndex = 0; l_sprIndex < MAX_SPR_SUPPORTED; l_sprIndex++ )
+ {
+ //Check if a given SPR needs to be self-saved each time on STOP entry
+
+ l_lookUpKey = genKeyForSprLookup( ( CpuReg_t )g_sprRegister[l_sprIndex].iv_sprId );
+
+ if( g_sprRegister[l_sprIndex].iv_isThreadScope )
+ {
+ for( l_threadPos = 0; l_threadPos < MAX_THREADS_PER_CORE; l_threadPos++ )
+ {
+ l_pRestoreStart =
+ (uint32_t*)&l_pHomer->iv_coreThreadRestore[i_corePos].iv_threadRestoreArea[l_threadPos][0];
+
+ l_rc = lookUpSprInImage( (uint32_t*)l_pRestoreStart, l_lookUpKey,
+ g_sprRegister[l_sprIndex].iv_isThreadScope,
+ &l_pTempLoc,
+ l_selfRestVer );
+
+ if( l_rc )
+ {
+ MY_ERR( "Thread SPR lookup failed in p9_stop_init_cpureg SPR %d Core %d Thread %d Index %d",
+ g_sprRegister[l_sprIndex].iv_sprId, i_corePos, l_threadPos, l_sprIndex );
+ break;
+ }
+
+ l_rc = updateSprEntryInImage( (uint32_t*) l_pTempLoc,
+ ( CpuReg_t )g_sprRegister[l_sprIndex].iv_sprId,
+ 0x00,
+ INIT_SPR_REGION );
+
+ if( l_rc )
+ {
+ MY_ERR( "Thread SPR region init failed. Core %d SPR Id %d",
+ i_corePos, g_sprRegister[l_sprIndex].iv_sprId );
+ break;
+ }
+
+ }//end for thread
+
+ if( l_rc )
+ {
+ break;
+ }
+
+ }//end if SPR threadscope
+ else
+ {
+ l_pRestoreStart = (uint32_t*)&l_pHomer->iv_coreThreadRestore[i_corePos].iv_coreRestoreArea[0];
+
+ l_rc = lookUpSprInImage( (uint32_t*)l_pRestoreStart, l_lookUpKey,
+ g_sprRegister[l_sprIndex].iv_isThreadScope,
+ &l_pTempLoc, l_selfRestVer );
+
+ if( l_rc )
+ {
+ MY_ERR( "Core SPR lookup failed in p9_stop_init_cpureg" );
+ break;
+ }
+
+ l_rc = updateSprEntryInImage( (uint32_t*) l_pTempLoc,
+ ( CpuReg_t )g_sprRegister[l_sprIndex].iv_sprId,
+ 0x00,
+ INIT_SPR_REGION );
+
+ if( l_rc )
+ {
+ MY_ERR( "Core SPR region init failed. Core %d SPR Id %d SPR Index %d",
+ i_corePos, g_sprRegister[l_sprIndex].iv_sprId, l_sprIndex );
+ break;
+ }
+
+ }// end else
+
+ }// end for l_sprIndex
+
+ }
+ while(0);
+
+ MY_INF( "<< proc_stop_init_cpureg" );
+
+ return l_rc;
+}
+
+//-----------------------------------------------------------------------------------------------------
+
+StopReturnCode_t proc_stop_save_scom( void* const i_pImage,
+ const uint32_t i_scomAddress,
+ const uint64_t i_scomData,
+ const ScomOperation_t i_operation,
+ const ScomSection_t i_section )
+{
+ StopReturnCode_t l_rc = STOP_SAVE_SUCCESS;
+ uint32_t entryLimit = 0;
+ uint8_t chipletId = 0;
+ uint32_t nopInst = 0;
+ uint32_t index = 0;
+ uint32_t imageVer = 0;
+ uint32_t entrySwzHeader = 0;
+ uint32_t l_maxScomRestoreEntry = 0;
+ ScomEntry_t* pScomEntry = NULL;
+ ScomEntry_t* pEntryLocation = NULL;
+ ScomEntry_t* pNopLocation = NULL;
+ ScomEntry_t* pEditScomHeader = NULL;
+ StopCacheSection_t* pStopCacheScomStart = NULL;
+ ScomEntry_t* pTableEndLocationtable = NULL;
+ uint32_t swizzleAddr;
+ uint64_t swizzleData;
+ uint32_t swizzleAttn;
+ uint32_t swizzleBlr = SWIZZLE_4_BYTE(BLR_INST);
+ bool cacheEntry = true;
+
+ MY_INF( ">> proc_stop_save_scom" );
+
+ //Reads SGPE image version info from QPMR Header in HOMER
+ //For backward compatibility, for base version of SGPE Hcode,
+ //STOP API retains default behavior but adds version specific
+ //details in each entry in later versions.
+ imageVer = *(uint32_t*)((uint8_t*)i_pImage + QPMR_HOMER_OFFSET + QPMR_BUILD_VER_BYTE);
+ imageVer = SWIZZLE_4_BYTE(imageVer);
+
+
+ do
+ {
+ chipletId = i_scomAddress >> 24;
+ chipletId = chipletId & 0x3F;
+
+ l_rc = validateScomImageInputs( i_pImage, i_scomAddress, chipletId, i_operation, i_section );
+
+ if( l_rc )
+ {
+ MY_ERR( "invalid argument: aborting");
+ break;
+ }
+
+ if( chipletId >= CORE_CHIPLET_ID_MIN )
+ {
+ // chiplet is core. So, let us find the start address of SCOM area
+ // pertaining to a core in STOP image.
+ l_maxScomRestoreEntry =
+ *(uint32_t*)((uint8_t*)i_pImage + CPMR_HOMER_OFFSET + CPMR_MAX_SCOM_REST_PER_CORE_BYTE);
+ pScomEntry = CORE_ID_SCOM_START(i_pImage, chipletId )
+ cacheEntry = false;
+
+ if( !l_maxScomRestoreEntry )
+ {
+ //Old HB and new STOP API case. Retain legacy Number
+ l_maxScomRestoreEntry = SWIZZLE_4_BYTE(LEGACY_CORE_SCOM_SUPPORTED);
+ }
+ }
+ else
+ {
+ l_maxScomRestoreEntry =
+ *(uint32_t*)((uint8_t*)i_pImage + QPMR_HOMER_OFFSET + QPMR_QUAD_MAX_SCOM_ENTRY_BYTE);
+
+ if( !l_maxScomRestoreEntry )
+ {
+ // Incase of a bad HOMER header initialization, fall back on legacy number.
+ l_maxScomRestoreEntry = SWIZZLE_4_BYTE(LEGACY_QUAD_SCOM_SUPPORTED);
+ }
+ // chiplet is a cache. let us find start address of cache section
+ // associated with given chiplet. A cache section associated with
+ // given chiplet is split in to L2, L3 and EQ area.
+ pStopCacheScomStart = CACHE_SECTN_START(i_pImage,
+ chipletId);
+ }
+
+ l_maxScomRestoreEntry = SWIZZLE_4_BYTE(l_maxScomRestoreEntry);
+
+ if(( !pStopCacheScomStart ) && ( !pScomEntry) )
+ {
+ //Error invalid pointer to SCOM entry in cache or core section
+ //of STOP image.
+ MY_ERR("invalid start location for chiplet %d",
+ chipletId );
+ break;
+ }
+
+ switch( i_section )
+ {
+ case P9_STOP_SECTION_EQ_SCOM:
+ pScomEntry = pStopCacheScomStart->nonCacheArea;
+ entryLimit = MAX_EQ_SCOM_ENTRIES;
+ break;
+
+ case P9_STOP_SECTION_L2:
+ pScomEntry = pStopCacheScomStart->l2CacheArea;
+ entryLimit = MAX_L2_SCOM_ENTRIES;
+ break;

case P9_STOP_SECTION_L3:
pScomEntry = pStopCacheScomStart->l3CacheArea;
@@ -1274,131 +1381,60 @@ StopReturnCode_t p9_stop_save_scom( void* const i_pImage,
if( NULL == pEntryLocation )
{
editAppend = pTableEndLocationtable;
- }
- else
- {
- editAppend = pEntryLocation;
-
- if( P9_STOP_SCOM_OR_APPEND == i_operation )
- {
- tempOperation = P9_STOP_SCOM_OR;
- }
- else
- {
- tempOperation = P9_STOP_SCOM_AND;
- }
- }
-
- l_rc = editScomEntry( swizzleAddr,
- swizzleData,
- editAppend,
- tempOperation );
-
- pEditScomHeader = editAppend;
- }
- break;
-
- default:
- l_rc = STOP_SAVE_SCOM_INVALID_OPERATION;
- break;
- }
- }
- while(0);
-
- if( l_rc )
- {
- MY_ERR("SCOM image operation 0x%08x failed for chiplet 0x%08x addr"
- "0x%08x", i_operation, chipletId ,
- i_scomAddress );
- }
- else
- {
- //Update SCOM Restore entry with version and memory layout
- //info
- updateEntryHeader( pEditScomHeader, imageVer, l_maxScomRestoreEntry );
- }
-
- MY_INF("<< p9_stop_save_scom");
- return l_rc;
-}
-
-//-----------------------------------------------------------------------------
-
-/**
- * @brief searches a self save entry of an SPR in self-save segment.
- * @param[in] i_sprBitPos bit position associated with SPR in save mask vector.
- * @param[in] l_pSprSaveStart start location of SPR save segment
- * @param[in] i_searchLength length of SPR save segment
- * @param[in] i_pSaveSprLoc start location of save entry for a given SPR.
- * @return STOP_SAVE_SUCCESS if look up succeeds, error code otherwise.
- */
-STATIC StopReturnCode_t lookUpSelfSaveSpr( uint32_t i_sprBitPos, uint32_t* l_pSprSaveStart,
- uint32_t i_searchLength, uint32_t** i_pSaveSprLoc )
-{
- int32_t l_saveWordLength = (int32_t)(i_searchLength >> 2);
- uint32_t l_oriInst = getOriInstruction( 0, 0, i_sprBitPos );
- StopReturnCode_t l_rc = STOP_SAVE_FAIL;
-
- while( l_saveWordLength > 0 )
- {
- if( l_oriInst == *l_pSprSaveStart )
- {
- *i_pSaveSprLoc = l_pSprSaveStart;
- l_rc = STOP_SAVE_SUCCESS;
- break;
- }
-
- l_pSprSaveStart++;
- l_saveWordLength--;
- }
-
- return l_rc;
-}
-
-//-----------------------------------------------------------------------------
-
-/**
- * @brief searches a self save entry of an SPR in self-save segment.
- * @param[in] i_pSaveReg start of editable location of a SPR save entry.
- * @param[in] i_sprNum Id of the SPR for which entry needs to be edited.
- * @return STOP_SAVE_SUCCESS if look up succeeds, error code otherwise.
- */
-STATIC StopReturnCode_t updateSelfSaveEntry( uint32_t* i_pSaveReg, uint16_t i_sprNum )
-{
- StopReturnCode_t l_rc = STOP_SAVE_SUCCESS;
-
- do
- {
- if( !i_pSaveReg )
- {
- l_rc = STOP_SAVE_FAIL;
- MY_ERR( "Failed to update self save area for SPR 0x%04x", i_sprNum );
- break;
- }
+ }
+ else
+ {
+ editAppend = pEntryLocation;

- if( P9_STOP_SPR_MSR == i_sprNum )
- {
- *i_pSaveReg = getMfmsrInstruction( 1 );
- }
- else
- {
- *i_pSaveReg = getMfsprInstruction( 1, i_sprNum );
- }
+ if( P9_STOP_SCOM_OR_APPEND == i_operation )
+ {
+ tempOperation = P9_STOP_SCOM_OR;
+ }
+ else
+ {
+ tempOperation = P9_STOP_SCOM_AND;
+ }
+ }

- i_pSaveReg++;
+ l_rc = editScomEntry( swizzleAddr,
+ swizzleData,
+ editAppend,
+ tempOperation );

- *i_pSaveReg = getBranchLinkRegInstruction( );
+ pEditScomHeader = editAppend;
+ }
+ break;
+
+ default:
+ l_rc = STOP_SAVE_SCOM_INVALID_OPERATION;
+ break;
+ }
}
while(0);

+ if( l_rc )
+ {
+ MY_ERR("SCOM image operation 0x%08x failed for chiplet 0x%08x addr"
+ "0x%08x", i_operation, chipletId ,
+ i_scomAddress );
+ }
+ else
+ {
+ //Update SCOM Restore entry with version and memory layout
+ //info
+ updateEntryHeader( pEditScomHeader, imageVer, l_maxScomRestoreEntry );
+ }
+
+ MY_INF( "<< proc_stop_save_scom" );
+
return l_rc;
}

-//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------------------------------

-StopReturnCode_t p9_stop_save_cpureg_control( void* i_pImage,
- const uint64_t i_pir,
- const uint32_t i_saveRegVector )
+StopReturnCode_t proc_stop_save_cpureg_control( void* i_pImage,
+ const uint64_t i_pir,
+ const uint32_t i_saveRegVector )
{
StopReturnCode_t l_rc = STOP_SAVE_SUCCESS;
uint32_t l_coreId = 0;
@@ -1411,8 +1447,10 @@ StopReturnCode_t p9_stop_save_cpureg_control( void* i_pImage,
uint32_t* l_pRestoreStart = NULL;
uint32_t* l_pSprSave = NULL;
void* l_pTempLoc = NULL;
+ uint32_t * l_pTempWord = NULL;
SmfHomerSection_t* l_pHomer = NULL;
uint8_t l_selfRestVer = 0;
+ MY_INF(">> proc_stop_save_cpureg_control" );

do
{
@@ -1440,6 +1478,11 @@ StopReturnCode_t p9_stop_save_cpureg_control( void* i_pImage,
{
l_sprPos = g_sprRegister[l_sprIndex].iv_saveMaskPos;

+ if( l_sprPos > MAX_SPR_BIT_POS )
+ {
+ continue;
+ }
+
//Check if a given SPR needs to be self-saved each time on STOP entry

if( i_saveRegVector & ( TEST_BIT_PATTERN >> l_sprPos ) )
@@ -1493,139 +1536,187 @@ StopReturnCode_t p9_stop_save_cpureg_control( void* i_pImage,
//update specific instructions of self save region to enable saving for SPR
l_rc = updateSelfSaveEntry( l_pSprSave, g_sprRegister[l_sprIndex].iv_sprId );

+ if( l_rc )
+ {
+ MY_ERR( "Failed to update self save instructions for 0x%08x",
+ (uint32_t) g_sprRegister[l_sprIndex].iv_sprId );
+ }
+
+ if( l_pTempLoc )
+ {
+ l_pTempWord = (uint32_t *)l_pTempLoc;
+ l_pTempWord++;
+ *l_pTempWord = getXorInstruction( 0, 0, 0 );
+ }
+
}// end if( i_saveRegVector..)
}// end for
}
while(0);

+ MY_INF("<< proc_stop_save_cpureg_control" );
+
return l_rc;
+
}

//-----------------------------------------------------------------------------------------------------

-StopReturnCode_t p9_stop_init_cpureg( void* const i_pImage, const uint32_t i_corePos )
+StopReturnCode_t proc_stop_save_cpureg( void* const i_pImage,
+ const CpuReg_t i_regId,
+ const uint64_t i_regData,
+ const uint64_t i_pir )
{
- StopReturnCode_t l_rc = STOP_SAVE_SUCCESS;
- uint32_t* l_pRestoreStart = NULL;
- void* l_pTempLoc = NULL;
- SmfHomerSection_t* l_pHomer = NULL;
- uint32_t l_threadPos = 0;
- uint32_t l_lookUpKey = 0;
- uint32_t l_sprIndex = 0;
- uint8_t l_selfRestVer = 0;

- MY_INF( ">> p9_stop_init_cpureg" );
+ StopReturnCode_t l_rc = STOP_SAVE_SUCCESS; // procedure return code
+ HomerSection_t* chipHomer = NULL;
+ SmfHomerSection_t* smfChipHomer = NULL;
+
+ MY_INF(">> proc_stop_save_cpureg" );

do
{
- if( !i_pImage )
+ uint32_t threadId = 0;
+ uint32_t coreId = 0;
+ uint32_t lookUpKey = 0;
+ void* pSprEntryLocation = NULL; // an offset w.r.t. to start of image
+ void* pThreadLocation = NULL;
+ bool threadScopeReg = false;
+ uint8_t l_urmorFix = false;
+ uint64_t l_sprValue = 0;
+ uint8_t l_selfRestVer = 0;
+
+
+ l_rc = getCoreAndThread( i_pImage, i_pir, &coreId, &threadId );
+
+ if( l_rc )
{
- l_rc = STOP_SAVE_ARG_INVALID_IMG;
+ MY_ERR("Failed to determine Core Id and Thread Id from PIR 0x%016llx",
+ i_pir);
break;
}

- if( i_corePos > MAX_CORE_ID_SUPPORTED )
+ MY_INF( " PIR 0x%016llx coreId %d threadid %d "
+ " registerId %d", i_pir, coreId,
+ threadId, i_regId );
+
+ // First of all let us validate all input arguments.
+ l_rc = validateSprImageInputs( i_pImage,
+ i_regId,
+ coreId,
+ &threadId,
+ &threadScopeReg );
+
+ if( l_rc )
{
- l_rc = STOP_SAVE_ARG_INVALID_CORE;
+ // Error: bad argument traces out error code
+ MY_ERR("Bad input argument rc %d", l_rc );
+
break;
}

- l_pHomer = ( SmfHomerSection_t * ) i_pImage;
+ l_urmorFix = *(uint8_t*)((uint8_t*)i_pImage + CPMR_HOMER_OFFSET + CPMR_URMOR_FIX_BYTE);
l_selfRestVer = *(uint8_t *)((uint8_t *)i_pImage + CPMR_HOMER_OFFSET + CPMR_SELF_RESTORE_VER_BYTE );

- for( l_sprIndex = 0; l_sprIndex < MAX_SPR_SUPPORTED; l_sprIndex++ )
+ if( l_selfRestVer )
{
- //Check if a given SPR needs to be self-saved each time on STOP entry
-
- l_lookUpKey = genKeyForSprLookup( ( CpuReg_t )g_sprRegister[l_sprIndex].iv_sprId );
+ smfChipHomer = ( SmfHomerSection_t*)i_pImage;

- if( g_sprRegister[l_sprIndex].iv_isThreadScope )
+ if( threadScopeReg )
{
- for( l_threadPos = 0; l_threadPos < MAX_THREADS_PER_CORE; l_threadPos++ )
- {
- l_pRestoreStart =
- (uint32_t*)&l_pHomer->iv_coreThreadRestore[i_corePos].iv_threadRestoreArea[l_threadPos][0];
-
- l_rc = lookUpSprInImage( (uint32_t*)l_pRestoreStart, l_lookUpKey,
- g_sprRegister[l_sprIndex].iv_isThreadScope,
- &l_pTempLoc,
- l_selfRestVer );
-
- if( l_rc )
- {
- MY_ERR( "Thread SPR lookup failed in p9_stop_init_cpureg SPR %d Core %d Thread %d Index %d",
- g_sprRegister[l_sprIndex].iv_sprId, i_corePos, l_threadPos, l_sprIndex );
- break;
- }
-
- l_rc = updateSprEntryInImage( (uint32_t*) l_pTempLoc,
- ( CpuReg_t )g_sprRegister[l_sprIndex].iv_sprId,
- 0x00,
- INIT_SPR_REGION );
-
- if( l_rc )
- {
- MY_ERR( "Thread SPR region init failed. Core %d SPR Id %d",
- i_corePos, g_sprRegister[l_sprIndex].iv_sprId );
- break;
- }
-
- }//end for thread
-
- if( l_rc )
- {
- break;
- }
-
- }//end if SPR threadscope
+ pThreadLocation =
+ &(smfChipHomer->iv_coreThreadRestore[coreId].iv_threadRestoreArea[threadId][0]);
+ }
else
{
- l_pRestoreStart = (uint32_t*)&l_pHomer->iv_coreThreadRestore[i_corePos].iv_coreRestoreArea[0];
+ pThreadLocation =
+ &(smfChipHomer->iv_coreThreadRestore[coreId].iv_coreRestoreArea[0]);
+ }
+ }
+ else //Old fips or OPAL release that doesn't support SMF
+ {
+ chipHomer = (HomerSection_t*)i_pImage;

- l_rc = lookUpSprInImage( (uint32_t*)l_pRestoreStart, l_lookUpKey,
- g_sprRegister[l_sprIndex].iv_isThreadScope,
- &l_pTempLoc, l_selfRestVer );
+ if( threadScopeReg )
+ {
+ pThreadLocation =
+ &(chipHomer->iv_coreThreadRestore[coreId][threadId].iv_threadArea[0]);
+ }
+ else
+ {
+ pThreadLocation =
+ &(chipHomer->iv_coreThreadRestore[coreId][threadId].iv_coreArea[0]);
+ }
+ }

- if( l_rc )
- {
- MY_ERR( "Core SPR lookup failed in p9_stop_init_cpureg" );
- break;
- }
+ if( ( SWIZZLE_4_BYTE(BLR_INST) == *(uint32_t*)pThreadLocation ) ||
+ ( SWIZZLE_4_BYTE(ATTN_OPCODE) == *(uint32_t*) pThreadLocation ) )
+ {
+ // table for given core id doesn't exit. It needs to be
+ // defined.
+ pSprEntryLocation = pThreadLocation;
+ }
+ else
+ {
+ // an SPR restore section for given core already exists
+ lookUpKey = genKeyForSprLookup( i_regId );
+ l_rc = lookUpSprInImage( (uint32_t*)pThreadLocation,
+ lookUpKey,
+ threadScopeReg,
+ &pSprEntryLocation,
+ l_selfRestVer );
+ }

- l_rc = updateSprEntryInImage( (uint32_t*) l_pTempLoc,
- ( CpuReg_t )g_sprRegister[l_sprIndex].iv_sprId,
- 0x00,
- INIT_SPR_REGION );
+ if( l_rc )
+ {
+ MY_ERR("Invalid or corrupt SPR entry. CoreId 0x%08x threadId ",
+ "0x%08x regId 0x%08x lookUpKey 0x%08x pThreadLocation 0x%08x"
+ , coreId, threadId, i_regId, lookUpKey, pThreadLocation );
+ break;
+ }

- if( l_rc )
- {
- MY_ERR( "Core SPR region init failed. Core %d SPR Id %d SPR Index %d",
- i_corePos, g_sprRegister[l_sprIndex].iv_sprId, l_sprIndex );
- break;
- }
+ if( ( P9_STOP_SPR_URMOR == i_regId ) && ( l_urmorFix ) )
+ {
+ l_sprValue = i_regData - URMOR_CORRECTION;
+ }
+ else
+ {
+ l_sprValue = i_regData;
+ }

- }// end else
+ l_rc = updateSprEntryInImage( (uint32_t*) pSprEntryLocation,
+ i_regId,
+ l_sprValue,
+ UPDATE_SPR_ENTRY );

- }// end for l_sprIndex
+ if( l_rc )
+ {
+ MY_ERR( " Failed to update the SPR entry of PIR 0x%08x reg"
+ "0x%08x", i_pir, i_regId );
+ break;
+ }

}
while(0);

- MY_INF( "<< p9_stop_init_cpureg" );
+ MY_INF("<< proc_stop_save_cpureg" );
+
return l_rc;
}

//-----------------------------------------------------------------------------------------------------

-StopReturnCode_t p9_stop_init_self_save( void* const i_pImage, const uint32_t i_corePos )
+StopReturnCode_t proc_stop_init_self_save( void* const i_pImage, const uint32_t i_corePos )
{
+
StopReturnCode_t l_rc = STOP_SAVE_SUCCESS;
uint32_t* l_pSaveStart = NULL;
SmfHomerSection_t * l_pHomer = NULL;
uint32_t l_threadPos = 0;
uint32_t l_sprBitPos = 0;
uint32_t l_sprIndexAdj = 0;
- MY_INF( ">> p9_stop_init_self_save" );
+
+ MY_INF(">> proc_stop_init_self_save" );

do
{
@@ -1732,7 +1823,8 @@ StopReturnCode_t p9_stop_init_self_save( void* const i_pImage, const uint32_t i
}
while(0);

- MY_INF( "<< p9_stop_init_self_save" );
+ MY_INF("<< proc_stop_init_self_save" );
+
return l_rc;
}

diff --git a/libpore/p9_stop_api.H b/libpore/p9_stop_api.H
index 17caedb3..3f6420ff 100644
--- a/libpore/p9_stop_api.H
+++ b/libpore/p9_stop_api.H
@@ -70,6 +70,26 @@ typedef enum
P9_STOP_SPR_PMCR = 884, // core register
P9_STOP_SPR_HID = 1008, // core register
P9_STOP_SPR_MSR = 2000, // thread register
+
+ //enum members which are project agnostic
+ PROC_STOP_SPR_DAWR = 180, // thread register
+ PROC_STOP_SPR_CIABR = 187, // thread register
+ PROC_STOP_SPR_DAWRX = 188, // thread register
+ PROC_STOP_SPR_HSPRG0 = 304, // thread register
+ PROC_STOP_SPR_HRMOR = 313, // core register
+ PROC_STOP_SPR_LPCR = 318, // thread register
+ PROC_STOP_SPR_HMEER = 337, // core register
+ PROC_STOP_SPR_PTCR = 464, // core register
+ PROC_STOP_SPR_USPRG0 = 496, // thread register
+ PROC_STOP_SPR_USPRG1 = 497, // thread register
+ PROC_STOP_SPR_URMOR = 505, // core register
+ PROC_STOP_SPR_SMFCTRL = 511, // thread register
+ PROC_STOP_SPR_LDBAR = 850, // thread register
+ PROC_STOP_SPR_PSSCR = 855, // thread register
+ PROC_STOP_SPR_PMCR = 884, // core register
+ PROC_STOP_SPR_HID = 1008, // core register
+ PROC_STOP_SPR_MSR = 2000, // thread register
+
} CpuReg_t;

/**
@@ -110,7 +130,20 @@ typedef enum
P9_STOP_SCOM_RESET = 6,
P9_STOP_SCOM_OR_APPEND = 7,
P9_STOP_SCOM_AND_APPEND = 8,
- P9_STOP_SCOM_OP_MAX = 9
+ P9_STOP_SCOM_OP_MAX = 9,
+
+ //enum members which are project agnostic
+ PROC_STOP_SCOM_OP_MIN = 0,
+ PROC_STOP_SCOM_APPEND = 1,
+ PROC_STOP_SCOM_REPLACE = 2,
+ PROC_STOP_SCOM_OR = 3,
+ PROC_STOP_SCOM_AND = 4,
+ PROC_STOP_SCOM_NOOP = 5,
+ PROC_STOP_SCOM_RESET = 6,
+ PROC_STOP_SCOM_OR_APPEND = 7,
+ PROC_STOP_SCOM_AND_APPEND = 8,
+ PROC_STOP_SCOM_OP_MAX = 9,
+
} ScomOperation_t;

/**
@@ -123,7 +156,15 @@ typedef enum
P9_STOP_SECTION_EQ_SCOM = 2,
P9_STOP_SECTION_L2 = 3,
P9_STOP_SECTION_L3 = 4,
- P9_STOP_SECTION_MAX = 5
+ P9_STOP_SECTION_MAX = 5,
+
+ //enum members which are project agnostic
+ PROC_STOP_SECTION_MIN = 0,
+ PROC_STOP_SECTION_CORE_SCOM = 1,
+ PROC_STOP_SECTION_EQ_SCOM = 2,
+ PROC_STOP_SECTION_L2 = 3,
+ PROC_STOP_SECTION_L3 = 4,
+ PROC_STOP_SECTION_MAX = 5,
} ScomSection_t;

/**
@@ -148,7 +189,6 @@ typedef enum
BIT_POS_LPCR = 5,
BIT_POS_PSSCR = 6,
BIT_POS_MSR = 7,
- BIT_POS_HRMOR = 20,
BIT_POS_HID = 21,
BIT_POS_HMEER = 22,
BIT_POS_PMCR = 23,
@@ -156,7 +196,6 @@ typedef enum
BIT_POS_SMFCTRL = 28,
BIT_POS_USPRG0 = 29,
BIT_POS_USPRG1 = 30,
- BIT_POS_URMOR = 31,
} SprBitPositionList_t;


@@ -229,13 +268,79 @@ p9_stop_save_cpureg_control( void* i_pImage, const uint64_t i_pir,
* @brief initializes self-save region with specific instruction.
* @param[in] i_pImage start address of homer image of P9 chip.
* @param[in] i_corePos physical core's relative position within processor chip.
- * @return STOP_SAVE_SUCCESS SUCCESS if self-save is initialized successfully,
+ * @return STOP_SAVE_SUCCESS if self-save is initialized successfully,
* error code otherwise.
* @note API is intended only for use case of HOMER build. There is no explicit
* effort to support any other use case.
*/
StopReturnCode_t p9_stop_init_self_save( void* const i_pImage, const uint32_t i_corePos );

+/**
+ * @brief creates SCOM restore entry for a given scom adress in HOMER.
+ * @param i_pImage points to start address of HOMER image.
+ * @param i_scomAddress address associated with SCOM restore entry.
+ * @param i_scomData data associated with SCOM restore entry.
+ * @param i_operation operation type requested for API.
+ * @param i_section section of HOMER in which restore entry needs to be created.
+ * @return STOP_SAVE_SUCCESS if API succeeds, error code otherwise.
+ * @note It is an API for creating SCOM restore entry in HOMER. It is agnostic to
+ * generation of POWER processor.
+ */
+
+StopReturnCode_t proc_stop_save_scom( void* const i_pImage,
+ const uint32_t i_scomAddress,
+ const uint64_t i_scomData,
+ const ScomOperation_t i_operation,
+ const ScomSection_t i_section );
+
+/**
+ * @brief initializes self save restore region of HOMER.
+ * @param[in] i_pImage points to base of HOMER image.
+ * @param[in] i_corePos position of the physical core.
+ * @return STOP_SAVE_SUCCESS if API succeeds, error code otherwise.
+ * @note It is an API for initializing self restore region in HOMER. It is agnostic to
+ * generation of POWER processor.
+ */
+StopReturnCode_t proc_stop_init_cpureg( void* const i_pImage, const uint32_t i_corePos );
+
+/**
+ * @brief enables self save for a given set of SPRs
+ * @param[in] i_pImage points to start address of HOMER image.
+ * @param[in] i_pir PIR value associated with core and thread.
+ * @param[in] i_saveRegVector bit vector representing the SPRs that needs to be self saved.
+ * @return STOP_SAVE_SUCCESS if API succeeds, error code otherwise.
+ * @note It is an API for enabling self save of SPRs and it is agnostic to
+ * generation of POWER processor.
+ */
+StopReturnCode_t proc_stop_save_cpureg_control( void* i_pImage,
+ const uint64_t i_pir,
+ const uint32_t i_saveRegVector );
+
+/**
+ * @brief creates an SPR restore entry in HOMER
+ * @param[in] i_pImage points to start address of HOMER image.
+ * @param[in] i_pir PIR value associated with core and thread.
+ * @param[in] i_saveRegVector bit vector representing the SPRs that needs to be self saved.
+ * @return STOP_SAVE_SUCCESS if API succeeds, error code otherwise.
+ * @note It is an API for enabling self save of SPRs and it is agnostic to
+ * generation of POWER processor.
+ */
+StopReturnCode_t proc_stop_save_cpureg( void* const i_pImage,
+ const CpuReg_t i_regId,
+ const uint64_t i_regData,
+ const uint64_t i_pir );
+
+/**
+ * @brief initializes self-save region with specific instruction.
+ * @param[in] i_pImage start address of homer image.
+ * @param[in] i_corePos physical core's relative position within processor chip.
+ * @return STOP_SAVE_SUCCESS if self-save is initialized successfully,
+ * error code otherwise.
+ * @note API is project agnostic and is intended only for use case of HOMER build.
+ * There is no explicit effort to support any other use case.
+ */
+StopReturnCode_t proc_stop_init_self_save( void* const i_pImage, const uint32_t i_corePos );
+
#ifdef __cplusplus
} // extern "C"
}; // namespace stopImageSection ends
diff --git a/libpore/p9_stop_data_struct.H b/libpore/p9_stop_data_struct.H
index 1e9721e0..4e73aab5 100644
--- a/libpore/p9_stop_data_struct.H
+++ b/libpore/p9_stop_data_struct.H
@@ -67,9 +67,9 @@ enum
SIZE_PER_SPR_RESTORE_INST = ((4 * sizeof(uint8_t)) / sizeof(uint32_t)),
MAX_THREAD_LEVEL_SPRS = 11,
MAX_CORE_LEVEL_SPRS = 6,
- MAX_SPR_BIT_POS = 31,
+ MAX_SPR_BIT_POS = 30,
SPR_BIT_POS_8 = 8,
- SPR_BIT_POS_19 = 19,
+ SPR_BIT_POS_20 = 20,
SPR_BIT_POS_25 = 25,
SPR_BIT_POS_27 = 27,
};
diff --git a/libpore/p9_stop_util.H b/libpore/p9_stop_util.H
index 3266fdef..79b4e959 100644
--- a/libpore/p9_stop_util.H
+++ b/libpore/p9_stop_util.H
@@ -5,7 +5,7 @@
/* */
/* OpenPOWER HostBoot Project */
/* */
-/* Contributors Listed Below - COPYRIGHT 2016,2017 */
+/* Contributors Listed Below - COPYRIGHT 2016,2018 */
/* [+] International Business Machines Corp. */
/* */
/* */
@@ -95,7 +95,10 @@ typedef struct
uint64_t cpmrMagicWord;
uint32_t buildDate;
uint32_t version;
- uint8_t reserve1[7];
+ uint8_t reserve1[4];
+ uint8_t selfRestoreVer;
+ uint8_t stopApiVer;
+ uint8_t urmorFix;
uint8_t fusedModeStatus;
uint32_t cmeImgOffset;
uint32_t cmeImgLength;
--
2.24.1

2020-03-26 07:11:26

by Pratik R. Sampat

[permalink] [raw]
Subject: [PATCH v6 4/4] Advertise the self-save and self-restore attributes in the device tree

Support for self save and self restore interface is advertised in the
device tree, along with the list of SPRs it supports for each.

The Special Purpose Register identification is encoded in a 2048 bitmask
structure, where each bit signifies the identification key of that SPR
which is consistent with that of the POWER architecture set for that
register.

Signed-off-by: Pratik Rajesh Sampat <[email protected]>
---
.../ibm,opal/power-mgt/self-restore.rst | 27 ++++
.../ibm,opal/power-mgt/self-save.rst | 27 ++++
hw/slw.c | 116 ++++++++++++++++++
include/skiboot.h | 1 +
4 files changed, 171 insertions(+)
create mode 100644 doc/device-tree/ibm,opal/power-mgt/self-restore.rst
create mode 100644 doc/device-tree/ibm,opal/power-mgt/self-save.rst

diff --git a/doc/device-tree/ibm,opal/power-mgt/self-restore.rst b/doc/device-tree/ibm,opal/power-mgt/self-restore.rst
new file mode 100644
index 00000000..2a2269f7
--- /dev/null
+++ b/doc/device-tree/ibm,opal/power-mgt/self-restore.rst
@@ -0,0 +1,27 @@
+ibm,opal/power-mgt/self-restore device tree entries
+===================================================
+
+This node exports the bitmask representing the special purpose registers that
+the self-restore API currently supports.
+
+Example:
+
+.. code-block:: dts
+
+ self-restore {
+ sprn-bitmask = <0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x42010000 0x0 0x0
+ 0x20000 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
+ 0x0 0x0 0x100000 0x900000 0x0 0x0 0x530000 0x0 0x0 0x0
+ 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
+ 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
+ 0x0 0x10000>;
+ phandle = <0x1c7>;
+ };
+
+sprn-bitmask
+------------
+
+This property is a bitmask of of all the existing SPRs and if the SPR is
+supported, the corresponding bit of the SPR number is set to 1.
+The representation of the bits are left-right, i.e the MSB of the first
+doubleword represants the 0th bit.
diff --git a/doc/device-tree/ibm,opal/power-mgt/self-save.rst b/doc/device-tree/ibm,opal/power-mgt/self-save.rst
new file mode 100644
index 00000000..c367720e
--- /dev/null
+++ b/doc/device-tree/ibm,opal/power-mgt/self-save.rst
@@ -0,0 +1,27 @@
+ibm,opal/power-mgt/self-save device tree entries
+===================================================
+
+This node exports the bitmask representing the special purpose registers that
+the self-save API currently supports.
+
+Example:
+
+.. code-block:: dts
+
+ self-save {
+ sprn-bitmask = <0x0 0x0 0x0 0x0 0x100000 0x0 0x0 0x0 0x42010000 0x0 0x0
+ 0x20000 0x0 0x0 0x0 0x10000 0x0 0x0 0x0 0x0 0x0 0x0 0x0
+ 0x0 0x0 0x0 0x100000 0x840000 0x0 0x0 0x0 0x0 0x0 0x0
+ 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
+ 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
+ 0x0 0x10000>;
+ phandle = <0x1c8>;
+ };
+
+sprn-bitmask
+------------
+
+This property is a bitmask of of all the existing SPRs and if the SPR is
+supported, the corresponding bit of the SPR number is set to 1.
+The representation of the bits are left-right, i.e the MSB of the first
+doubleword represants the 0th bit.
diff --git a/hw/slw.c b/hw/slw.c
index 6a09cc2c..9d1fe2c5 100644
--- a/hw/slw.c
+++ b/hw/slw.c
@@ -29,6 +29,7 @@
#include <sbe_xip_image.h>

static uint32_t slw_saved_reset[0x100];
+#define SPR_BITMAP_LENGTH 2048

static bool slw_current_le = false;

@@ -750,6 +751,119 @@ static void slw_late_init_p9(struct proc_chip *chip)
}
}

+/* Add device tree properties to determine self-save | restore */
+void add_cpu_self_save_restore_properties(void)
+{
+ struct dt_node *self_restore, *self_save, *power_mgt;
+ uint64_t *self_save_mask, *self_restore_mask;
+ bool self_save_supported = true;
+ uint64_t compVector = -1;
+ struct proc_chip *chip;
+ int i, rc;
+
+ const uint64_t self_restore_regs[] = {
+ P8_SPR_HRMOR,
+ P8_SPR_HMEER,
+ P8_SPR_PMICR,
+ P8_SPR_PMCR,
+ P8_SPR_HID0,
+ P8_SPR_HID1,
+ P8_SPR_HID4,
+ P8_SPR_HID5,
+ P8_SPR_HSPRG0,
+ P8_SPR_LPCR,
+ P8_MSR_MSR
+ };
+
+ const uint64_t self_save_regs[] = {
+ P9_STOP_SPR_DAWR,
+ P9_STOP_SPR_HSPRG0,
+ P9_STOP_SPR_LDBAR,
+ P9_STOP_SPR_LPCR,
+ P9_STOP_SPR_PSSCR,
+ P9_STOP_SPR_MSR,
+ P9_STOP_SPR_HRMOR,
+ P9_STOP_SPR_HMEER,
+ P9_STOP_SPR_PMCR,
+ P9_STOP_SPR_PTCR
+ };
+
+ chip = next_chip(NULL);
+ assert(chip);
+ rc = proc_stop_api_discover_capability((void *) chip->homer_base,
+ &compVector);
+ if (rc == STOP_SAVE_ARG_INVALID_IMG) {
+ prlog(PR_DEBUG, "HOMER BASE INVALID\n");
+ return;
+ } else if (rc == STOP_SAVE_API_IMG_INCOMPATIBLE) {
+ prlog(PR_DEBUG, "STOP API running incompatible versions\n");
+ if ((compVector & SELF_RESTORE_VER_MISMATCH) == 0) {
+ prlog(PR_DEBUG, "Self-save API unsupported\n");
+ self_save_supported = false;
+ }
+ }
+
+ power_mgt = dt_find_by_path(dt_root, "/ibm,opal/power-mgt");
+ if (!power_mgt) {
+ prerror("OCC: dt node /ibm,opal/power-mgt not found\n");
+ return;
+ }
+
+ self_restore = dt_new(power_mgt, "self-restore");
+ if (!self_restore) {
+ prerror("OCC: Failed to create self restore node");
+ return;
+ }
+
+ self_restore_mask = zalloc(SPR_BITMAP_LENGTH / 8);
+ if (!self_restore_mask)
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(self_restore_regs); i++) {
+ int bitmask_idx = self_restore_regs[i] / 64;
+ uint64_t bitmask_pos = self_restore_regs[i] % 64;
+ self_restore_mask[bitmask_idx] |= 1ul << bitmask_pos;
+ }
+
+ for (i = 0; i < (SPR_BITMAP_LENGTH / 64); i++) {
+ self_restore_mask[i] = cpu_to_be64(self_restore_mask[i]);
+ }
+
+ dt_add_property(self_restore, "sprn-bitmask", self_restore_mask,
+ SPR_BITMAP_LENGTH / 8);
+ dt_add_property_string(self_restore, "compatible",
+ "ibm,opal-self-restore");
+ free(self_restore_mask);
+
+ if (proc_gen != proc_gen_p9 || !self_save_supported)
+ return;
+
+ self_save = dt_new(power_mgt, "self-save");
+ if (!self_save) {
+ prerror("OCC: Failed to create self save node");
+ return;
+ }
+
+ self_save_mask = zalloc(SPR_BITMAP_LENGTH / 8);
+ if (!self_save_mask)
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(self_save_regs); i++) {
+ int bitmask_idx = self_save_regs[i] / 64;
+ uint64_t bitmask_pos = self_save_regs[i] % 64;
+ self_save_mask[bitmask_idx] |= 1ul << bitmask_pos;
+ }
+
+ for (i = 0; i < (SPR_BITMAP_LENGTH / 64); i++) {
+ self_save_mask[i] = cpu_to_be64(self_save_mask[i]);
+ }
+
+ dt_add_property(self_save, "sprn-bitmask", self_save_mask,
+ SPR_BITMAP_LENGTH / 8);
+ dt_add_property_string(self_save, "compatible", "ibm,opal-self-save");
+ free(self_save_mask);
+}
+
/* Add device tree properties to describe idle states */
void add_cpu_idle_state_properties(void)
{
@@ -1563,4 +1677,6 @@ void slw_init(void)
}
}
add_cpu_idle_state_properties();
+ if (has_deep_states)
+ add_cpu_self_save_restore_properties();
}
diff --git a/include/skiboot.h b/include/skiboot.h
index 9ced240e..d3631dea 100644
--- a/include/skiboot.h
+++ b/include/skiboot.h
@@ -209,6 +209,7 @@ extern void early_uart_init(void);
extern void homer_init(void);
extern void slw_init(void);
extern void add_cpu_idle_state_properties(void);
+extern void add_cpu_self_save_restore_properties(void);
extern void lpc_rtc_init(void);

/* flash support */
--
2.24.1

2020-04-15 10:32:12

by Gautham R Shenoy

[permalink] [raw]
Subject: Re: [PATCH v6 2/4] Self save API integration

Hello Pratik,

On Thu, Mar 26, 2020 at 12:39:15PM +0530, Pratik Rajesh Sampat wrote:
> The commit makes the self save API available outside the firmware by defining
> an OPAL wrapper.
> This wrapper has a similar interface to that of self restore and expects the
> cpu pir, SPR number, minus the value of that SPR to be passed in its
> paramters and returns OPAL_SUCCESS on success.
> The commit also documents both the self-save and the self-restore API
> calls along with their working and usage.
>
> Signed-off-by: Pratik Rajesh Sampat <[email protected]>
> ---
> doc/opal-api/opal-slw-self-save-reg-181.rst | 49 ++++++++++++
> doc/opal-api/opal-slw-set-reg-100.rst | 5 ++
> doc/power-management.rst | 44 ++++++++++
> hw/slw.c | 89 +++++++++++++++++++++
> include/opal-api.h | 3 +-
> include/p9_stop_api.H | 17 ++++
> include/skiboot.h | 3 +
> 7 files changed, 209 insertions(+), 1 deletion(-)
> create mode 100644 doc/opal-api/opal-slw-self-save-reg-181.rst
>
> diff --git a/doc/opal-api/opal-slw-self-save-reg-181.rst b/doc/opal-api/opal-slw-self-save-reg-181.rst
> new file mode 100644
> index 00000000..5aa4c930
> --- /dev/null
> +++ b/doc/opal-api/opal-slw-self-save-reg-181.rst
> @@ -0,0 +1,49 @@
> +.. OPAL_SLW_SELF_SAVE_REG:
> +
> +OPAL_SLW_SELF_SAVE_REG
> +======================
> +
> +.. code-block:: c
> +
> + #define OPAL_SLW_SELF_SAVE_REG 181
> +
> + int64_t opal_slw_self_save_reg(uint64_t cpu_pir, uint64_t sprn);
> +
> +:ref:`OPAL_SLW_SELF_SAVE_REG` is used to inform low-level firmware to save
> +the current contents of the SPR before entering a state of loss and
> +also restore the content back on waking up from a deep stop state.
> +
> +An OPAL call `OPAL_SLW_SET_REG` exists which is similar in function as
> +saving and restoring the SPR, with one difference being that the value of the
> +SPR must also be supplied in the parameters.
> +Complete reference: doc/opal-api/opal-slw-set-reg-100.rst
> +
> +Parameters
> +----------
> +
> +``uint64_t cpu_pir``
> + This parameter specifies the pir of the cpu for which the call is being made.
> +``uint64_t sprn``
> + This parameter specifies the spr number as mentioned in p9_stop_api.H
> + The list of SPRs supported is as follows. This list is suppiled through the
> + device tree:
> + P9_STOP_SPR_DAWR,
> + P9_STOP_SPR_HSPRG0,
> + P9_STOP_SPR_LDBAR,
> + P9_STOP_SPR_LPCR,
> + P9_STOP_SPR_PSSCR,
> + P9_STOP_SPR_MSR,
> + P9_STOP_SPR_HRMOR,
> + P9_STOP_SPR_HMEER,
> + P9_STOP_SPR_PMCR,
> + P9_STOP_SPR_PTCR
> +
> +Returns
> +-------
> +
> +:ref:`OPAL_UNSUPPORTED`
> + If spr restore is not supported by pore engine.
> +:ref:`OPAL_PARAMETER`
> + Invalid handle for the pir/chip
> +:ref:`OPAL_SUCCESS`
> + On success
> diff --git a/doc/opal-api/opal-slw-set-reg-100.rst b/doc/opal-api/opal-slw-set-reg-100.rst
> index 2e8f1bd6..ee3e68ce 100644
> --- a/doc/opal-api/opal-slw-set-reg-100.rst
> +++ b/doc/opal-api/opal-slw-set-reg-100.rst
> @@ -21,6 +21,11 @@ In Power 9, it uses p9_stop_save_cpureg(), api provided by self restore code,
> to inform the spr with their corresponding values with which they
> must be restored.
>
> +An OPAL call `OPAL_SLW_SELF_SAVE_REG` exists which is similar in function
> +saving and restoring the SPR, with one difference being that the value of the
> +SPR doesn't need to be passed in the parameters, only with the SPR number
> +the firmware can identify, save and restore the values for the same.
> +Complete reference: doc/opal-api/opal-slw-self-save-reg-181.rst
>
> Parameters
> ----------
> diff --git a/doc/power-management.rst b/doc/power-management.rst
> index 76491a71..992a18d0 100644
> --- a/doc/power-management.rst
> +++ b/doc/power-management.rst
> @@ -15,3 +15,47 @@ On boot, specific stop states can be disabled via setting a mask. For example,
> to disable all but stop 0,1,2, use ~0xE0000000. ::
>
> nvram -p ibm,skiboot --update-config opal-stop-state-disable-mask=0x1FFFFFFF
> +
> +Saving and restoring Special Purpose Registers(SPRs)
> +----------------------------------------------------
> +
> +When a CPU wakes up from a deep stop state which can result in
> +hypervisor state loss, all the SPRs are lost. The Linux Kernel expects
> +a small set of SPRs to contain an expected value when the CPU wakes up
> +from such a deep stop state. The microcode firmware provides the
> +following two APIs, collectively known as the stop-APIs, to allow the
> +kernel/OPAL to specify this set of SPRs and the value that they need
> +to be restored with on waking up from a deep stop state.
> +
> +Self-restore:
> +int64_t opal_slw_set_reg(uint64_t cpu_pir, uint64_t sprn, uint64_t val);
> +The SPR number and the value of the that SPR must be restored with on
> +wakeup from the deep-stop state must be specified. When this call is
> +made, the microcode inserts instruction into the HOMER region to
> +restore the content of the SPR to the specified value on wakeup from a
> +deep-stop state. These instructions are executed by the CPU as soon as
> +it wakes up from a deep stop state. The call is to be made once per
> +SPR.
> +
> +Self-Save:
> +int64_t opal_slw_self_save_reg(uint64_t cpu_pir, uint64_t sprn);
> +Only the SPR number needs to be specified. When this call is made, the
> +microcode inserts instructions into the HOMER region to save the
> +current value of the SPR before the CPU goes to a deep stop state, and
> +restores the value back when the CPU wakes up from a deep stop state.
> +These instructions are correspondingly executed just before and after
> +the CPU goes/comes out of a deep stop state. This call can be made
> +once per SPR.
> +
> +The key difference between self-save and self-restore is the
> +use-case. If the Kernel expects the SPR to contain a particular value
> +on waking up from a deep-stop state, that wasn't the value of that SPR
> +before entering deep stop-state, then self-restore is preferable.


Not just when the value during wakeup is different, but even when the
value of an SPR doesn't change across the lifetime of a kernel,
self-restore is preferable.

> +When deep stop states are to be supported in an Ultravisor
> +environment, since HOMER is in a secure region, the stop-api cannot
> +update the HOMER if invoked from a context when the OPAL/Kernel is
> +executing without the ultravisor privilege. In this scenario, at the
> +time of early OPAL boot, while OPAL has ultravisor privileges, it can
> +make the self-save stop-api call for all the supported SPRs, so that
> +the microcode in the HOMER will always save and restore all the
> +supported SPRs during entry/exit from a deep stop state.
> diff --git a/hw/slw.c b/hw/slw.c
> index beb129a8..6a09cc2c 100644
> --- a/hw/slw.c
> +++ b/hw/slw.c
> @@ -35,6 +35,43 @@ static bool slw_current_le = false;
> enum wakeup_engine_states wakeup_engine_state = WAKEUP_ENGINE_NOT_PRESENT;
> bool has_deep_states = false;
>
> +/**
> + * The struct and SPR list is partially consistent with libpore/p9_stop_api.c
> + */
> +/**
> + * @brief summarizes attributes associated with a SPR register.
> + */
> +typedef struct
> +{
> + uint32_t iv_sprId;
> + bool iv_isThreadScope;
> + uint32_t iv_saveMaskPos;
> +
> +} StopSprReg_t;
> +
> +/**
> + * @brief a true in the table below means register is of scope thread
> + * whereas a false meanse register is of scope core.
> + * The number is the bit position on a uint32_t mask
> + */
> +
> +static const StopSprReg_t g_sprRegister[] =
> +{
> + { P9_STOP_SPR_DAWR, true, 1 },
> + { P9_STOP_SPR_HSPRG0, true, 3 },
> + { P9_STOP_SPR_LDBAR, true, 4, },
> + { P9_STOP_SPR_LPCR, true, 5 },
> + { P9_STOP_SPR_PSSCR, true, 6 },
> + { P9_STOP_SPR_MSR, true, 7 },
> + { P9_STOP_SPR_HRMOR, false, 255 },
> + { P9_STOP_SPR_HID, false, 21 },
> + { P9_STOP_SPR_HMEER, false, 22 },
> + { P9_STOP_SPR_PMCR, false, 23 },
> + { P9_STOP_SPR_PTCR, false, 24 },
> +};
> +
> +static const uint32_t MAX_SPR_SUPPORTED = ARRAY_SIZE(g_sprRegister);
> +
> DEFINE_LOG_ENTRY(OPAL_RC_SLW_INIT, OPAL_PLATFORM_ERR_EVT, OPAL_SLW,
> OPAL_PLATFORM_FIRMWARE, OPAL_PREDICTIVE_ERR_GENERAL,
> OPAL_NA);
> @@ -1446,6 +1483,58 @@ int64_t opal_slw_set_reg(uint64_t cpu_pir, uint64_t sprn, uint64_t val)
>
> opal_call(OPAL_SLW_SET_REG, opal_slw_set_reg, 3);
>
> +int64_t opal_slw_self_save_reg(uint64_t cpu_pir, uint64_t sprn)
> +{
> + struct cpu_thread * c = find_cpu_by_pir(cpu_pir);
> + uint32_t save_reg_vector = 0;
> + struct proc_chip * chip;
> + int rc;
> + int index;
> +
> + if (!c) {
> + prlog(PR_DEBUG, "SLW: Unknown thread with pir %x\n",
> + (u32) cpu_pir);
> + return OPAL_PARAMETER;
> + }
> +
> + chip = get_chip(c->chip_id);
> + if (!chip) {
> + prlog(PR_DEBUG, "SLW: Unknown chip for thread with pir %x\n",
> + (u32) cpu_pir);
> + return OPAL_PARAMETER;
> + }
> + if (proc_gen != proc_gen_p9 || !has_deep_states) {
> + prlog(PR_DEBUG, "SLW: Does not support deep states\n");

The log should say "Self-save feature unsupported" in this case, no ?


> + return OPAL_UNSUPPORTED;
> + }
> + if (wakeup_engine_state != WAKEUP_ENGINE_PRESENT) {
> + log_simple_error(&e_info(OPAL_RC_SLW_REG),
> + "SLW: wakeup_engine in bad state=%d chip=%x\n",
> + wakeup_engine_state, chip->id);
> + return OPAL_INTERNAL_ERROR;
> + }
> + for (index = 0; index < MAX_SPR_SUPPORTED; ++index) {
> + if (sprn == (CpuReg_t) g_sprRegister[index].iv_sprId) {
> + save_reg_vector = PPC_BIT32(
> + g_sprRegister[index].iv_saveMaskPos);
> + break;
> + }
> + }
> + if (save_reg_vector == 0)
> + return OPAL_INTERNAL_ERROR;
> + rc = p9_stop_save_cpureg_control((void *) chip->homer_base,
> + cpu_pir, save_reg_vector);
> +
> + if (rc) {
> + log_simple_error(&e_info(OPAL_RC_SLW_REG),
> + "SLW: Failed to save vector %x for CPU %x\n",
> + save_reg_vector, c->pir);
> + return OPAL_INTERNAL_ERROR;
> + }
> + return OPAL_SUCCESS;
> +}
> +opal_call(OPAL_SLW_SELF_SAVE_REG, opal_slw_self_save_reg, 2);
> +
> void slw_init(void)
> {
> struct proc_chip *chip;
> diff --git a/include/opal-api.h b/include/opal-api.h
> index e90cab1e..1607a89b 100644
> --- a/include/opal-api.h
> +++ b/include/opal-api.h
> @@ -227,7 +227,8 @@
> #define OPAL_SECVAR_ENQUEUE_UPDATE 178
> #define OPAL_PHB_SET_OPTION 179
> #define OPAL_PHB_GET_OPTION 180
> -#define OPAL_LAST 180
> +#define OPAL_SLW_SELF_SAVE_REG 181
> +#define OPAL_LAST 181
>
> #define QUIESCE_HOLD 1 /* Spin all calls at entry */
> #define QUIESCE_REJECT 2 /* Fail all calls with OPAL_BUSY */
> diff --git a/include/p9_stop_api.H b/include/p9_stop_api.H
> index 9d3bc1e5..c304f70f 100644
> --- a/include/p9_stop_api.H
> +++ b/include/p9_stop_api.H
> @@ -34,6 +34,8 @@
> ///
> /// @file p9_stop_api.H
> /// @brief describes STOP API which create/manipulate STOP image.
> +/// This header need not be consistent, however is a subset of the
> +/// libpore/p9_stop_api.H counterpart
> ///
> // *HWP HW Owner : Greg Still <[email protected]>
> // *HWP FW Owner : Prem Shanker Jha <[email protected]>
> @@ -58,6 +60,7 @@ typedef enum
> P9_STOP_SPR_HRMOR = 313, // core register
> P9_STOP_SPR_LPCR = 318, // thread register
> P9_STOP_SPR_HMEER = 337, // core register
> + P9_STOP_SPR_PTCR = 464, // core register
> P9_STOP_SPR_LDBAR = 850, // thread register
> P9_STOP_SPR_PSSCR = 855, // thread register
> P9_STOP_SPR_PMCR = 884, // core register
> @@ -230,6 +233,20 @@ StopReturnCode_t p9_stop_save_scom( void* const i_pImage,
> const ScomOperation_t i_operation,
> const ScomSection_t i_section );
>
> +/**
> + * @brief Facilitates self save and restore of a list of SPRs of a thread.
> + * @param[in] i_pImage points to the start of HOMER image of P9 chip.
> + * @param[in] i_pir PIR associated with thread
> + * @param[in] i_saveRegVector bit vector representing SPRs that needs to be restored.
> + * @return STOP_SAVE_SUCCESS if API succeeds, error code otherwise.
> + * @note SPR save vector is a bit vector. For each SPR supported,
> + * there is an associated bit position in the bit vector.Refer
> + * to definition of SprBitPositionList_t to determine bit position
> + * associated with a particular SPR.
> + */
> +StopReturnCode_t
> +p9_stop_save_cpureg_control( void* i_pImage, const uint64_t i_pir,
> + const uint32_t i_saveRegVector );
> #ifdef __cplusplus
> } // extern "C"
> }; // namespace stopImageSection ends
> diff --git a/include/skiboot.h b/include/skiboot.h
> index 30ff500c..9ced240e 100644
> --- a/include/skiboot.h
> +++ b/include/skiboot.h
> @@ -306,6 +306,9 @@ extern void nx_p9_rng_late_init(void);
> /* SLW reinit function for switching core settings */
> extern int64_t slw_reinit(uint64_t flags);
>
> +/* Self save SPR before entering the stop state */
> +extern int64_t opal_slw_self_save_reg(uint64_t cpu_pir, uint64_t sprn);
> +
> /* Patch SPR in SLW image */
> extern int64_t opal_slw_set_reg(uint64_t cpu_pir, uint64_t sprn, uint64_t val);
>



The patch looks good otherwise.

Reviewed-by: Gautham R. Shenoy <[email protected]>

> --
> 2.24.1
>

2020-04-15 17:57:08

by Gautham R Shenoy

[permalink] [raw]
Subject: Re: [PATCH v6 0/4] Support for Self Save API in OPAL

Hello Pratik,


On Thu, Mar 26, 2020 at 12:39:13PM +0530, Pratik Rajesh Sampat wrote:
> v5:https://lists.ozlabs.org/pipermail/skiboot/2020-March/016538.html
> Changelog
> v5 --> v6
> Updated background, motivation and illuminated potential design choices
>
> Background
> ==========
>
> The power management framework on POWER systems include core idle
> states that lose context. Deep idle states namely "winkle" on POWER8
> and "stop4" and "stop5" on POWER9 can be entered by a CPU to save
> different levels of power, as a consequence of which all the
> hypervisor resources such as SPRs and SCOMs are lost.
>
> For most SPRs, saving and restoration of content for SPRs and SCOMs
> is handled by the hypervisor kernel prior to entering an post exit
> from an idle state respectively. However, there is a small set of
> critical SPRs and XSCOMs that are expected to contain sane values even
> before the control is transferred to the hypervisor kernel at system
> reset vector.
>
> For this purpose, microcode firmware provides a mechanism to restore
> values on certain SPRs. The communication mechanism between the
> hypervisor kernel and the microcode is a standard interface called
> sleep-winkle-engine (SLW) on Power8 and Stop-API on Power9 which is
> abstracted by OPAL calls from the hypervisor kernel. The Stop-API
> provides an interface known as the self-restore API, to which the SPR
> number and a predefined value to be restored on wake-up from a deep
> stop state is supplied.
>
>
> Motivation to introduce a new Stop-API
> ======================================
>
> The self-restore API expects not just the SPR number but also the
> value with which the SPR is restored. This is good for those SPRs such
> as HSPRG0 whose values do not change at runtime, since for them, the
> kernel can invoke the self-restore API at boot time once the values of
> these SPRs are determined.
>
> However, there are use-cases where-in the value to be saved cannot be
> known or cannot be updated in the layer it currently is.
> The shortcomings and the new use-cases which cannot be served by the
> existing self-restore API, serves as motivation for a new API:
>
> Shortcoming1:
> ------------
> In a special wakeup scenario, SPRs such as PSSCR, whose values can
> change at runtime, are compelled to make the self-restore API call
> every time before entering a deep-idle state rendering it to be
> prohibitively expensive

To provide some more details, when a CPU in stop4/stop5 is woken up
due to a special-wakeup, once the task is done, the HCODE puts it back
to stop. To what stop level it is put back to, is dependent on the
PSSCR value that was passed to the HCODE via self-restore API. The
kernel currently provides the value of the deepest stop state (in
order to be more conservative). Thus if a core that was in stop4 was
woken up due to special wakeup, the HCODE would put it back to
stop5. This increases the subsequent wakeup latency by ~200us.

With the self-save feature, the HCODE will put it back to whatever the
PSSCR value was when the core went to a deep stop state after being
woken up by a special wakeup.


The description below gives a detailed overview of all the existing
shortcomings of self-restore API which is corrected by self-save.

--
Thanks and Regards
gautham.

>
> Shortcoming2:
> ------------
> The value of LPCR is dynamic based on if the CPU is entered a stop
> state during cpu idle versus cpu hotplug.
> Today, an additional self-restore call is made before entering
> CPU-Hotplug to clear the PECE1 bit in stop-API so that if we are
> woken up by a special wakeup on an offlined CPU, we go back to stop
> with the the bit cleared.
> There is a overhead of an extra call
>
> New Use-case:
> -------------
> In the case where the hypervisor is running on an
> ultravisor environment, the boot time is too late in the cycle to make
> the self-restore API calls, as these cannot be invoked from an
> non-secure context anymore
>
> To address these shortcomings, the firmware provides another API known
> as the self-save API. The self-save API only takes the SPR number as a
> parameter and will ensure that on wakeup from a deep-stop state the
> SPR is restored with the value that it contained prior to entering the
> deep-stop.
>
> Contrast between self-save and self-restore APIs
> ================================================
>
> Before entering
> deep idle |---------------|
> ------------> | HCODE A |
> | |---------------|
> |---------| |
> | CPU |----|
> |---------| |
> | |---------------|
> |------------>| HCODE B |
> On waking up |---------------|
> from deep idle
>
>
>
>
> When a self-restore API is invoked, the HCODE inserts instructions
> into "HCODE B" region of the above figure to restore the content of
> the SPR to the said value. The "HCODE B" region gets executed soon
> after the CPU wakes up from a deep idle state, thus executing the
> inserted instructions, thereby restoring the contents of the SPRs to
> the required values.
>
> When a self-save API is invoked, the HCODE inserts instructions into
> the "HCODE A" region of the above figure to save the content of the
> SPR into some location in memory. It also inserts instructions into
> the "HCODE B" region to restore the content of the SPR to the
> corresponding value saved in the memory by the instructions in "HCODE
> A" region.
>
> Thus, in contrast with self-restore, the self-save API *does not* need
> a value to be passed to it, since it ensures that the value of SPR
> before entering deep stop is saved, and subsequently the same value is
> restored.
>
> Self-save and self-restore are complementary features since,
> self-restore can help in restoring a different value in the SPR on
> wakeup from a deep-idle state than what it had before entering the
> deep idle state. This was used in POWER8 for HSPRG0 to distinguish a
> wakeup from Winkle vs Fastsleep.
>
> Limitations of self-save
> ========================
> Ideally all SPRs should be available for self-save, but HID0 is very
> tricky to implement in microcode due to various endianess quirks.
> Couple of implementation schemes were buggy and hence HID0 was left
> out to be self-restore only.
>
> The fallout of this limitation is as follows:
>
> * In Non PEF environment, no issue. Linux will use self-restore for
> HID0 as it does today and no functional impact.
>
> * In PEF environment, the HID0 restore value is decided by OPAL during
> boot and it is setup for LE hypervisor with radix MMU. This is the
> default and current working configuration of a PEF environment.
> However if there is a change, then HV Linux will try to change the
> HID0 value to something different than what OPAL decided, at which
> time deep-stop states will be disabled under this new PEF
> environment.
>
> A simple and workable design is achieved by scoping the power
> management deep-stop state support only to a known default PEF
> environment. Any deviation will affect *only* deep stop-state support
> (stop4,5) in that environment and not have any functional impediment
> to the environment itself.
>
> In future, if there is a need to support changing of HID0 to various
> values under PEF environment and support deep-stop states, it can be
> worked out via an ultravisor call or improve the microcode design to
> include HID0 in self-save. These future scheme would be an extension
> and does not break or make the current implementation scheme
> redundant.
>
> Design Choices
> ==============
>
> Presenting the design choices in front of us:
>
> Design-Choice 1:
> ----------------
> Only expose one of self-save or self-restore for all the SPRs. Prefer
> Self-save
>
> Pros:
> - Simplifies the design heavily, since the Kernel can unambiguously
> make one API call for all the SPRs on discovering the presence of
> the API type.
>
> Cons:
> - Breaks backward compatibility if OPAL always chooses to expose
> only the self-save API as the older kernels assume the existence
> of self-restore.
>
> - The set of SPRs supported by self-save and self-restore are not
> identical. Eg: HID0 is not supported by self-save API. PSSCR
> support via self-restore is not robust during special-wakeup.
>
> - As discussed above, self-save and self-restore are
> complementary. Thus OPAL apriory choosing one over the other for
> all SPRs takes away the flexibility from the kernel.
>
>
> Design-Choice 2:
> ----------------
> Expose two arrays of SPRs: One set of SPRs that are supported by
> self-save. Another set of SPRs supported by self-restore. These two
> sets do not intersect. Further, if an SPR is supported by both
> self-save and self-restore APIs, expose it only via self-save.
>
> Pros:
> - For an SPR the choice for the kernel is unambiguous.
>
> Cons:
> - Breaks backward compatibility if OPAL always chooses to expose
> the legacy SPRs only via the self-save API as the older kernels
> assume the existence of self-restore.
>
> - By making the decision early on, we take away the flexibility
> from the kernel to use an API of its choice for an SPR.
>
>
> Design-Choice 3
> ---------------
> Expose two arrays of SPRs. One set of SPRs that are supported by
> self-save API. Another set of SPRs supported by self-restore API. Let
> the kernel choose which API to invoke. Even if it wants to always
> prefer self-save over self-restore, let that be kernel's choice.
>
> Pros:
> - Keeps the design flexible to allow the kernel to take a
> decision based on its functional and performance requirements.
> Thus, the kernel for instance can make a choice to invoke
> self-restore API (when available) for SPRs whose values do not
> evolve at runtime, and invoke the self-save API (when
> available)
> for SPRs whose values will change during runtime.
>
> - Design is backward compatible with older kernels.
>
> Cons:
> - The Kernel code will have additional complexity for parsing two
> lists of SPRs and making a choice w.r.t invocation of a specific
> stop-api.
>
>
>
> Patches Organization
> ====================
> Design choice 3 has been chosen as an implementation to demonstrate in
> this patch series.
>
> Patch 1:
> Commit adds support calling into the self save firmware API.
> Also adds abstraction for making platform agnostic calls.
>
> Patch 2:
> Commit adds wrappers for the Self Save API for which an OPAL call can
> be made.
>
> Patch 3:
> Commit adds API to determine the version of the STOP API. This helps
> to identify support for self save in the firmware
>
> Patch 4:
> Commit adds device tree attributes to advertise self save and self
> restore functionality along with the register set as a bitmask
> currently supported in the firmware. It also uses the versioning API
> to determine support for the self-save feature as a whole.
>
> Pratik Rajesh Sampat (2):
> Self save API integration
> Advertise the self-save and self-restore attributes in the device tree
>
> Prem Shanker Jha (2):
> Self Save: Introducing Support for SPR Self Save
> API to verify the STOP API and image compatibility
>
> .../ibm,opal/power-mgt/self-restore.rst | 27 +
> .../ibm,opal/power-mgt/self-save.rst | 27 +
> doc/opal-api/opal-slw-self-save-reg-181.rst | 49 +
> doc/opal-api/opal-slw-set-reg-100.rst | 5 +
> doc/power-management.rst | 44 +
> hw/slw.c | 205 ++++
> include/opal-api.h | 3 +-
> include/p9_stop_api.H | 122 ++-
> include/skiboot.h | 4 +
> libpore/p9_cpu_reg_restore_instruction.H | 11 +-
> libpore/p9_hcd_memmap_base.H | 7 +
> libpore/p9_stop_api.C | 964 ++++++++++--------
> libpore/p9_stop_api.H | 141 ++-
> libpore/p9_stop_data_struct.H | 4 +-
> libpore/p9_stop_util.H | 27 +-
> 15 files changed, 1208 insertions(+), 432 deletions(-)
> create mode 100644 doc/device-tree/ibm,opal/power-mgt/self-restore.rst
> create mode 100644 doc/device-tree/ibm,opal/power-mgt/self-save.rst
> create mode 100644 doc/opal-api/opal-slw-self-save-reg-181.rst
>
> --
> 2.24.1
>

2020-04-15 21:27:30

by Gautham R Shenoy

[permalink] [raw]
Subject: Re: [PATCH v6 4/4] Advertise the self-save and self-restore attributes in the device tree

Hello Pratik,

On Thu, Mar 26, 2020 at 12:39:17PM +0530, Pratik Rajesh Sampat wrote:
> Support for self save and self restore interface is advertised in the
> device tree, along with the list of SPRs it supports for each.
>
> The Special Purpose Register identification is encoded in a 2048 bitmask
> structure, where each bit signifies the identification key of that SPR
> which is consistent with that of the POWER architecture set for that
> register.
>
> Signed-off-by: Pratik Rajesh Sampat <[email protected]>
> ---
> .../ibm,opal/power-mgt/self-restore.rst | 27 ++++
> .../ibm,opal/power-mgt/self-save.rst | 27 ++++
> hw/slw.c | 116 ++++++++++++++++++
> include/skiboot.h | 1 +
> 4 files changed, 171 insertions(+)
> create mode 100644 doc/device-tree/ibm,opal/power-mgt/self-restore.rst
> create mode 100644 doc/device-tree/ibm,opal/power-mgt/self-save.rst
>
> diff --git a/doc/device-tree/ibm,opal/power-mgt/self-restore.rst b/doc/device-tree/ibm,opal/power-mgt/self-restore.rst
> new file mode 100644
> index 00000000..2a2269f7
> --- /dev/null
> +++ b/doc/device-tree/ibm,opal/power-mgt/self-restore.rst
> @@ -0,0 +1,27 @@
> +ibm,opal/power-mgt/self-restore device tree entries
> +===================================================
> +
> +This node exports the bitmask representing the special purpose registers that
> +the self-restore API currently supports.
> +
> +Example:
> +
> +.. code-block:: dts
> +
> + self-restore {
> + sprn-bitmask = <0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x42010000 0x0 0x0
> + 0x20000 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
> + 0x0 0x0 0x100000 0x900000 0x0 0x0 0x530000 0x0 0x0 0x0
> + 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
> + 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
> + 0x0 0x10000>;
> + phandle = <0x1c7>;
> + };
> +
> +sprn-bitmask
> +------------
> +
> +This property is a bitmask of of all the existing SPRs and if the SPR is
> +supported, the corresponding bit of the SPR number is set to 1.
> +The representation of the bits are left-right, i.e the MSB of the first
> +doubleword represants the 0th bit.
> diff --git a/doc/device-tree/ibm,opal/power-mgt/self-save.rst b/doc/device-tree/ibm,opal/power-mgt/self-save.rst
> new file mode 100644
> index 00000000..c367720e
> --- /dev/null
> +++ b/doc/device-tree/ibm,opal/power-mgt/self-save.rst
> @@ -0,0 +1,27 @@
> +ibm,opal/power-mgt/self-save device tree entries
> +===================================================
> +
> +This node exports the bitmask representing the special purpose registers that
> +the self-save API currently supports.
> +
> +Example:
> +
> +.. code-block:: dts
> +
> + self-save {
> + sprn-bitmask = <0x0 0x0 0x0 0x0 0x100000 0x0 0x0 0x0 0x42010000 0x0 0x0
> + 0x20000 0x0 0x0 0x0 0x10000 0x0 0x0 0x0 0x0 0x0 0x0 0x0
> + 0x0 0x0 0x0 0x100000 0x840000 0x0 0x0 0x0 0x0 0x0 0x0
> + 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
> + 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
> + 0x0 0x10000>;
> + phandle = <0x1c8>;
> + };
> +
> +sprn-bitmask
> +------------
> +
> +This property is a bitmask of of all the existing SPRs and if the SPR is
> +supported, the corresponding bit of the SPR number is set to 1.
> +The representation of the bits are left-right, i.e the MSB of the first
> +doubleword represants the 0th bit.
> diff --git a/hw/slw.c b/hw/slw.c
> index 6a09cc2c..9d1fe2c5 100644
> --- a/hw/slw.c
> +++ b/hw/slw.c
> @@ -29,6 +29,7 @@
> #include <sbe_xip_image.h>
>
> static uint32_t slw_saved_reset[0x100];
> +#define SPR_BITMAP_LENGTH 2048
>
> static bool slw_current_le = false;
>
> @@ -750,6 +751,119 @@ static void slw_late_init_p9(struct proc_chip *chip)
> }
> }
>
> +/* Add device tree properties to determine self-save | restore */
> +void add_cpu_self_save_restore_properties(void)
> +{
> + struct dt_node *self_restore, *self_save, *power_mgt;
> + uint64_t *self_save_mask, *self_restore_mask;
> + bool self_save_supported = true;
> + uint64_t compVector = -1;
> + struct proc_chip *chip;
> + int i, rc;
> +
> + const uint64_t self_restore_regs[] = {
> + P8_SPR_HRMOR,
> + P8_SPR_HMEER,
> + P8_SPR_PMICR,
> + P8_SPR_PMCR,
> + P8_SPR_HID0,
> + P8_SPR_HID1,
> + P8_SPR_HID4,
> + P8_SPR_HID5,
> + P8_SPR_HSPRG0,
> + P8_SPR_LPCR,
> + P8_MSR_MSR
> + };
> +
> + const uint64_t self_save_regs[] = {
> + P9_STOP_SPR_DAWR,
> + P9_STOP_SPR_HSPRG0,
> + P9_STOP_SPR_LDBAR,
> + P9_STOP_SPR_LPCR,
> + P9_STOP_SPR_PSSCR,
> + P9_STOP_SPR_MSR,
> + P9_STOP_SPR_HRMOR,
> + P9_STOP_SPR_HMEER,
> + P9_STOP_SPR_PMCR,
> + P9_STOP_SPR_PTCR
> + };
> +
> + chip = next_chip(NULL);
> + assert(chip);
> + rc = proc_stop_api_discover_capability((void *) chip->homer_base,
> + &compVector);
> + if (rc == STOP_SAVE_ARG_INVALID_IMG) {
> + prlog(PR_DEBUG, "HOMER BASE INVALID\n");
> + return;
> + } else if (rc == STOP_SAVE_API_IMG_INCOMPATIBLE) {
> + prlog(PR_DEBUG, "STOP API running incompatible versions\n");
> + if ((compVector & SELF_RESTORE_VER_MISMATCH) == 0) {
> + prlog(PR_DEBUG, "Self-save API unsupported\n");
> + self_save_supported = false;
> + }
> + }
> +
> + power_mgt = dt_find_by_path(dt_root, "/ibm,opal/power-mgt");
> + if (!power_mgt) {
> + prerror("OCC: dt node /ibm,opal/power-mgt not found\n");

The prerror/warning in this function should be prefixed with "SLW"
instead of "OCC" here. This function has nothing to do with OCC.


> + return;
> + }
> +
> + self_restore = dt_new(power_mgt, "self-restore");
> + if (!self_restore) {
> + prerror("OCC: Failed to create self restore node");

Ditto.

> + return;
> + }
> +
> + self_restore_mask = zalloc(SPR_BITMAP_LENGTH / 8);
> + if (!self_restore_mask)
> + return;
> +
> + for (i = 0; i < ARRAY_SIZE(self_restore_regs); i++) {
> + int bitmask_idx = self_restore_regs[i] / 64;
> + uint64_t bitmask_pos = self_restore_regs[i] % 64;
> + self_restore_mask[bitmask_idx] |= 1ul << bitmask_pos;
> + }
> +
> + for (i = 0; i < (SPR_BITMAP_LENGTH / 64); i++) {
> + self_restore_mask[i] = cpu_to_be64(self_restore_mask[i]);
> + }
> +
> + dt_add_property(self_restore, "sprn-bitmask", self_restore_mask,
> + SPR_BITMAP_LENGTH / 8);
> + dt_add_property_string(self_restore, "compatible",
> + "ibm,opal-self-restore");
> + free(self_restore_mask);
> +
> + if (proc_gen != proc_gen_p9 || !self_save_supported)

We need a
prlog(PR_INFO, "SLW: self-save not supported on this platform");

?

> + return;


> +
> + self_save = dt_new(power_mgt, "self-save");
> + if (!self_save) {
> + prerror("OCC: Failed to create self save node");
> + return;
> + }
> +
> + self_save_mask = zalloc(SPR_BITMAP_LENGTH / 8);
> + if (!self_save_mask)
> + return;
> +
> + for (i = 0; i < ARRAY_SIZE(self_save_regs); i++) {
> + int bitmask_idx = self_save_regs[i] / 64;
> + uint64_t bitmask_pos = self_save_regs[i] % 64;
> + self_save_mask[bitmask_idx] |= 1ul << bitmask_pos;
> + }
> +
> + for (i = 0; i < (SPR_BITMAP_LENGTH / 64); i++) {
> + self_save_mask[i] = cpu_to_be64(self_save_mask[i]);
> + }
> +
> + dt_add_property(self_save, "sprn-bitmask", self_save_mask,
> + SPR_BITMAP_LENGTH / 8);
> + dt_add_property_string(self_save, "compatible", "ibm,opal-self-save");
> + free(self_save_mask);
> +}
> +
> /* Add device tree properties to describe idle states */
> void add_cpu_idle_state_properties(void)
> {
> @@ -1563,4 +1677,6 @@ void slw_init(void)
> }
> }
> add_cpu_idle_state_properties();
> + if (has_deep_states)
> + add_cpu_self_save_restore_properties();
> }
> diff --git a/include/skiboot.h b/include/skiboot.h
> index 9ced240e..d3631dea 100644
> --- a/include/skiboot.h
> +++ b/include/skiboot.h
> @@ -209,6 +209,7 @@ extern void early_uart_init(void);
> extern void homer_init(void);
> extern void slw_init(void);
> extern void add_cpu_idle_state_properties(void);
> +extern void add_cpu_self_save_restore_properties(void);
> extern void lpc_rtc_init(void);

Apart from these minor quibbles,

Reviewed-by: Gautham R. Shenoy <[email protected]>

>
> /* flash support */
> --
> 2.24.1
>