- Introduction
- Defining the STFLE feature bits
- Defining the feat900
FEATUREbuild macros - Verifying
FEATUREbuild macro sanity - Defining the facility.c run-time tables and functions
5a. TheFTtable
5b. TheFT2table
5c.modnnnModification Check functions
5d.instrxxxUpdate Opcode Table functions - Coding the facility instructions themselves
6a.DEF_INSTinopcode.h
6b.UNDEF_INSTandGENx___x___x900inopcode.c
6c. The actualesame.cinstruction function itself
IBM's z/Architecture supports the concept of Facilities, many of which have a corresponding facility list bit assigned to them, available to the program via the STORE FACILITY LIST EXTENDED (STFLE) instruction.
Hercules's support of z/Architecture facilities is controlled
by the facility.c, stfl.h, feat900.h and featchk.h source files:
- stfl.h defines the actual STFLE facility list bits themselves
- feat900.h defines the
FEATURE...build macros - featchk.h enforces
FEATURE...build macro sanity - facility.c implements the
facilitycommand and table-based control of facility bits and instructions
The stfl.h header defines the actual STFLE feature bits themselves. The #define
macros contain the bit numbers in their name since many of the defined facilities have very
similar names. This reduces the likelihood of using the wrong macro name in the code.
The stfl.h header looks like this:
#define STFL_000_N3_INSTR 0 /* Instructions marked N3
are installed */
#define STFL_001_ZARCH_INSTALLED 1 /* z/Arch mode is available on
this processor */
#define STFL_002_ZARCH_ACTIVE 2 /* z/Architecture architecural
mode active. When bit 2 and
168 are both zero, ESA/390
mode is active. When bit 2
is zero and bit 168 is one,
ESA/390-compatibility mode
is active. */
#define STFL_003_DAT_ENHANCE_1 3 /* DAT-Enhancement Facility 1
is installed. */
#define STFL_004_IDTE_SC_SEGTAB 4 /* IDTE selective clearing
when segtab invalidated. Bit
3 is one if bit 4 is one. */
...etc...
#define STFL_153_UNASSIGNED 153 /* Unassigned */
#define STFL_154_UNASSIGNED 154 /* Unassigned */
#define STFL_155_MSA_EXTENSION_9 155 /* Message-security-assist-
extension-9 installed.
Bits 76 and 77 are one
when bit 155 is one. */
#define STFL_156_IBM_INTERNAL 156 /* IBM internal use */
//efine STFL_nnn_UNASSIGNED 157-167 /* Unassigned or IBM internal*/
#define STFL_168_ESA390_COMPAT_MODE 168 /* ESA/390-compatibility-mode.
Bit 168 can only be 1 when
bit 2 is zero. */
#define STFL_IBM_LAST_BIT 168 /* Last defined IBM facility */
#define STFL_IBM_BY_SIZE (ROUND_UP( STFL_IBM_LAST_BIT, 8 ) / 8)
#define STFL_IBM_DW_SIZE (ROUND_UP( STFL_IBM_BY_SIZE, sizeof( DW )) / sizeof( DW ))
Please note the comments on some of the definitions (e.g.
"Bits 76 and 77 are one when bit 155 is one"). It is very important to
document such conditions as their enforcement is one of the primary purposes
of the facility command, controlled via the FT2 table
and corresponding modnnn functions.
Our FEATURE... build macros control actual code generation for a given build
architecture according to whether the macro is defined or not in each architecture's
"feature" header (e.g. feat370.h for System/370, feat390.h
for ESA/390 or feat900.h for z/Architecture).
The FEATURE_xxx... #defines for facilities contain the bit numbers in their name,
just like the STFL_xxx... #defines in header stfl.h do:
...
#define FEATURE_037_FP_EXTENSION_FACILITY /*@SRO*/
//efine FEATURE_038_OP_CMPSC_FACILITY
#define FEATURE_040_LOAD_PROG_PARAM_FACILITY
#define FEATURE_041_DFP_ROUNDING_FACILITY
#define FEATURE_041_FPR_GR_TRANSFER_FACILITY
#define FEATURE_041_FPS_ENHANCEMENT_FACILITY
#define FEATURE_041_FPS_SIGN_HANDLING_FACILITY
#define FEATURE_041_IEEE_EXCEPT_SIM_FACILITY
#define FEATURE_042_DFP_FACILITY /*DFP*/
#define FEATURE_043_DFP_HPERF_FACILITY
...etc...
The featchk.h header not only #defines our all-important
_FEATURE_xxx... underscore macros (depending on whether or not the given
feature is #defined for any of the build architectures), but also enforces
feature definition sanity.
For example, it checks to make sure that if the Constrained-Transactional-Execution Facility FEATURE is #defined, that the Transactional-Execution Facility FEATURE is also #defined:
#if defined( FEATURE_050_CONSTR_TRANSACT_FACILITY )
#define _FEATURE_050_CONSTR_TRANSACT_FACILITY
#endif
...
#if defined( FEATURE_073_TRANSACT_EXEC_FACILITY )
#define _FEATURE_073_TRANSACT_EXEC_FACILITY
#endif
...
#if defined( FEATURE_050_CONSTR_TRANSACT_FACILITY ) && !defined( FEATURE_073_TRANSACT_EXEC_FACILITY )
#error Constrained-transactional-execution facility requires Transactional-execution facility
#endifThe same facility dependency concept (one facility being dependent on, or implying, another)
is also enforced at runtime (but accomplished differently of course) by the
modnnn function declared in the
facility's FT2 table entry.
The code in facility.c controls virtually all aspects of Hercules's
facility support, creating (initializing) the facility list bit strings in SYSBLK,
allowing user control over the setting or clearing (enabling or disabling) of any
given facility via the facility command, as well disabling or enabling instructions
associated with a given facility.
The FT table is an architecture dependent table that gets built differently
for each #defined build architecture (OPTION_370_MODE, OPTION_390_MODE and
OPTION_900_MODE) depending on which FEATURE_999_XXX... facilities are #defined
for each build architecture.
During Hercules startup and initialization, bldcfg.c's build_config
function calls into facility.c's init_facilities_lists function
to initialize the sysblk.facility_list variable in SYSBLK. It first merges the
three separate architecture dependent FT tables into one master internal
architecture independent table called factab
(the FT2 table controls this merging),
and it is this master factab table
that is then used to initialize each architecture's sysblk.facility_list variable
in SYSBLK depending on whether the given facility is enabled or not for that
particular architecture or not.
The format of the FT table is quite simple:
- Supported: which architecture(s) the given facility applies to
- Default: which architecture(s) have the facility enabled by default
- Required: which architecture(s) REQUIRE the facility (which prevents it from being disabled)
- Short name: the abbreviated "name" of the facility as used by the
FACILITY_ENABLEDmacro (which is just the stfl.h header #define name without the "STFL_"):
/*-------------------------------------------------------------------*/
/* Temporary ARCH_DEP Facility Table */
/*-------------------------------------------------------------------*/
static FACTAB ARCH_DEP( facs_tab )[] = /* Arch-DEPENDENT table */
{
/*-------------------------------------------------------------------*/
/* Sup Def Req Short Name... */
/*-------------------------------------------------------------------*/
...
#if defined( FEATURE_018_LONG_DISPL_INST_FACILITY )
FT( Z90X, Z900, Z900, 018_LONG_DISPL_INST )
#endif
...etc...The Sup (Supported), Def (Default) and Req (Required) parameters use one of eight defined values #defined at the very beginning of facility.c:
- NONE (no architectures or facility disabled)
- S370 (S/370 only)
- E390 (ESA/390 only)
- Z900 (z/Arch only)
- Z390 (both ESA/390 and z/Arch)
- Z39X (E390 + Z900 + optionally S370)
- Z90X (Z900 + optionally S370)
- MALL (all architectures)
The FT2 table defines additional information for each facility defined to the
system, such as the name of the facility's modnnn
Modification Check function, the name of the facility's
instrxxx Update Opcode Table function
and the facility's "Long" name (description).
The information in the FT2 table is "merged" with each architecture's
FT table (by the init_facilities_lists function
called by bldcfg.c's build_config function during Hercules
startup and initialization) to create the master factab table used to
initialize the sysblk.facility_list variable in SYSBLK.
The modnnn parameter defines the
name of the facility's Modification Check function which defines the function
that controls the enabling and disabling of that particular facility bit when
the given facility requires or implies one or more other facility bits also
being set. Refer to the next section just below for more information about the
modnnn Modification Check function.
The instrxxx Update Opcode Table function
parameter defines the function which controls the enabling or disabling of the actual
instructions themselves defined by the facility. That is to say, certain facilities
define new z/Architecture instructions which only exist if that given facility exists
(i.e. if that particular facility list bit is one). If the facility doesn't exist (i.e.
if the facility list bit is off or zero), then the instructions that facility introduced
do not exist, and attempts to execute such instructions cause an immediate "Operation
Exception" Program Check interruption.
/*-------------------------------------------------------------------*/
/* The ACTUAL facilities table, initialized by init_facilities_lists */
/*-------------------------------------------------------------------*/
/* The individual ARCH_DEP( facs_tab ) tables are merged into this */
/* table to yield the actual facilities table the system will use. */
/* Refer to init_facilities_lists() function for how this is done. */
/*-------------------------------------------------------------------*/
static FACTAB factab[] =
{
/*----------------------------------------------------------------------------*/
/* (func) (func) Short Name... Long Description... */
/*----------------------------------------------------------------------------*/
...
FT2( mod018, instr18, 018_LONG_DISPL_INST, "Long-Displacement Facility" )
FT2( mod019, NULL, 019_LONG_DISPL_HPERF, "Long-Displacement Facility Has High Performance" )
...etc...
FT2( NULL, instr21, 021_EXTENDED_IMMED, "Extended-Immediate Facility" )
...etc...The "Long name" is simply the official descriptive name of the given facility and is used
by the facility command when a display of the available facilities is requested.
(The facility command supports listing available facilities by either SHORT or LONG name.)
The modnnn Modification Check functions are defined in the FT2
table entries and control the enabling or disabling of a given facility for those
facilities which are dependent on one or more other facilities.
For example, facility 18 is the "Long-Displacement Facility" and facility 19 is the "Long-Displacement Facility Has High Performance" facility. If the "Long-Displacement Facility Has High Performance" (bit 19) is enabled then it follows that the "Long-Displacement Facility" (bit 18) must necessarily also be enabled. That is to say, you cannot have facility 19 enabled without facility 18 also being enabled.
On the other hand, you may have facility 18 enabled but not facility 19. That is allowed. But having 19 enabled without also having 18 enabled too, is invalid.
It is the modnnn Modification Check function's job to enforce such restrictions,
and such functions are defined in the first parameter of the FT2
table. The actual function itself that does the enforcement looks like this:
static bool mod018 ( bool enable, int bitno, int archnum, ...
static bool mod019 ( bool enable, int bitno, int archnum, ...
...
/*-------------------------------------------------------------------*/
/* mod018 */
/*-------------------------------------------------------------------*/
/* required by 19 */
/*-------------------------------------------------------------------*/
FAC_MOD_OK_FUNC ( mod018 )
{
if (!enable) // disabling
{
if (FACILITY_ENABLED_ARCH( 019_LONG_DISPL_HPERF, archnum ))
return HHC00890E( STFL_019_LONG_DISPL_HPERF );
}
return true;
}
/*-------------------------------------------------------------------*/
/* mod019 */
/*-------------------------------------------------------------------*/
/* also requires 18 */
/*-------------------------------------------------------------------*/
FAC_MOD_OK_FUNC ( mod019 )
{
if (enable)
{
if (!FACILITY_ENABLED_ARCH( 018_LONG_DISPL_INST, archnum ))
return HHC00890E( STFL_018_LONG_DISPL_INST );
}
return true;
}Please note that the above functions not only prevent enabling bit 19 unless bit 18 is first enabled, but also prevents bit 18 from being disabled as well, unless bit 19 is first disabled beforehand.
For reference, I have manually created the following tables which documents the various interfacility dependencies according to the May 2022 version of manual SA22-7832-13 "z/Architecture Principles of Operation":
| Bit | Requires ... | Required by ... |
|---|---|---|
| 000 | 007 | |
| 003 | 004, 005 | |
| 004 | 003 | 005 |
| 005 | 003, 004 | |
| 007 | 000 | |
| 008 | 078 | |
| 014 | 149 | |
| 018 | 019 | |
| 019 | 018 | |
| 025 | 139 | |
| 028 | 139 | |
| 037 | 042 | |
+ 040 |
068 | |
| 042 | 037, 043 | |
| 043 | 042 | |
| 045 | 061 | |
| 048 | 042 | |
| 049 | 073, 081 | |
| 050 | 073 | |
| 051 | 194 | |
| 061 | 045 | |
+ 067 |
068, 142 | |
+ 068 |
040, 067 | |
| 073 | 049 | 050 |
| 076 | 146, 155 | |
| 077 | 155 | |
| 078 | 008 | |
| 080 | 042 | |
| 081 | 049 | |
| 129 | 134, 135, 148, 152, 165, 192 | |
| 134 | 129 | 152, 192 |
| 135 | 129 | 148 |
| 139 | 025, 028 | |
+ 142 |
067 | |
| 146 | 076 | |
| 148 | 129, 135 | |
| 149 | 014 | |
| 152 | 129, 134 | 192 |
| 155 | 076, 077 | |
| 165 | 129 | |
| 192 | 129, 134, 152 | |
| 193 | (PER-3) | |
| 194 | 051 | |
| 196 | 197 | |
| 197 | 196 |
+ For facility bits 40, 67, 68: see pages vii and 2-1, and reference 7 on page viii
of manual SA23-2260-05 "Load-Program-Parameter and CPU-Measurement Facilities".
For facility bits 67, 142: see reference 10 on page xxxii of manual SA22-7832-13
"z/Architecture Principles of Operation".
| Bit | Incompatible with ... |
|---|---|
+ 002 |
168 |
| 010 | 169 |
| 014 | 169 |
| 066 | 169 |
| 145 | 169 |
| 149 | 169 |
+ 168 |
002 |
| 169 | 010, 014, 066, 145, 149 |
+ For facility bits 002 and 168: either bit may be on, or neither bit may be on,
but both bits can never be on at the same time.
For those facilities which introduce new z/Architecture instructions to go along
with the facility, the instrxxx function (defined as the second parameter of
the FT2 table) defines the list of instructions that only
exist when the given facility is enabled.
The function is called by the init_facilities_lists function at Hercules startup
(as well as by the facility command too whenever a facility is manually enabled
or disabled) to patch (update) the opcode.c instruction table to either enable or
disable the given set of instructions depending on whether the given facility is
enabled or disabled for that architecture.
This eliminates the need for each individual instruction from having to manually
check whether the given facility is enabled or not (via the FACILITY_ENABLED( ... )
macro) and then having to manually call the program_interrupt function to throw
an operation exception if it's not. Instead, this is all handled automatically
by each facility's defined instrxxx function, which looks like this:
static void instr21 ( int arch, bool enable );
...
BEG_DIS_FAC_INS_FUNC( instr21 )
{
DIS_FAC_INS( C208, "AGFI C208 ADD IMMEDIATE (64 <- 32)" );
DIS_FAC_INS( C209, "AFI C209 ADD IMMEDIATE (32)" );
...etc...
DIS_FAC_INS( B907, "LGHR B907 LOAD HALFWORD (64 <- 16)" );
DIS_FAC_INS( B927, "LHR B927 LOAD HALFWORD (32 <- 16)" );
...etc...
DIS_FAC_INS( C204, "SLGFI C204 SUBTRACT LOGICAL IMMEDIATE (64 <- 32)" );
DIS_FAC_INS( C205, "SLFI C205 SUBTRACT LOGICAL IMMEDIATE (32)" );
}
END_DIS_FAC_INS_FUNC()The first parameter of the DIS_FAC_INS macro is obviously the instruction's
hexadecimal opcode, and the second parameter is simply a unique descriptive name
for that particular instruction.
Implementing a new instruction in Hercules involves updating three source files:
the opcode.h header (which declares its existence), the
opcode.c instruction dispatch table (directing the run_cpu
instruction execution loop in cpu.c to jump to the
actual instruction function itself), and of course the actual instruction
function itself (which does not necessarily have to be in source file esame.c
but may instead be in a completely different source file, possibly its own).
Within header file opcode.h, simply insert a new DEF_INST macro
for your new instruction, guarded with the appropriate
#if defined( FEATURE_999_xxxx...) statement (where _999_xxx... is of course
the named of the FEATURE macro you defined in your feat900.h
header):
#if defined( FEATURE_049_PROCESSOR_ASSIST_FACILITY )
DEF_INST( perform_processor_assist );
#endifWithin the opcode.c source file, insert a UNDEF_INST macro for
your new instruction guarded with an appropriate
#if !defined( FEATURE_999_xxxx...) statement:
#if !defined( FEATURE_049_PROCESSOR_ASSIST_FACILITY )
UNDEF_INST( perform_processor_assist );
#endifThen about halfway down, update the appropriate GENx___x___x900 macro statement
for your instruction's opcode, defining the name of your instruction function,
the instruction's decoder format and its mnemonic:
/*B2E8*/ GENx___x___x900 (perform_processor_assist,RRF_M,"PPA"),Note that each x___ spot in the macro's name corresponds to a given build architecture.
The first x___ being replaced with x370 if the given instruction is defined to the
System/370 architecture, the second being replaced with x390 if the instruction is
defined to the ESA/390 architecture and the third spot being replaced with x900 if the
instruction is defined to z/Architecture. The /*B2E8*/ is of course just a helpful
comment documenting the instruction's opcode.
Depending on its complexity, this is perhaps the easiest part: coding the actual instruction function itself.
All you need to be careful to do is to wrap (guard) your function with the
appropriate #if defined( FEATURE_999_xxx...) and #endif statements so that
it is only compiled if that particular FEATURE is #defined for the given build
architecture:
#if defined( FEATURE_021_EXTENDED_IMMED_FACILITY )
/*-------------------------------------------------------------------*/
/* B907 LGHR - Load Long Halfword Register [RRE] */
/*-------------------------------------------------------------------*/
DEF_INST( load_long_halfword_register )
{
int r1, r2; /* Values of R fields */
RRE( inst, regs, r1, r2 );
/* Load sign-extended halfword from second operand register */
regs->GR_G( r1 ) = (S64)(S16)(regs->GR_LHL( r2 ));
} /* end DEF_INST( load_long_halfword_register ) */
#endif /* defined( FEATURE_021_EXTENDED_IMMED_FACILITY ) */Please note that under normal circumstances there is no need to code any
if (FACILITY_ENABLED( ... )) test anywhere in your instruction if your
instruction is only defined when the given facility is enabled, as this is
handled automatically by the associated facility.c BEG_DIS_FAC_INS_FUNC
function (controlled by the
instrxxx second parameter of
the FT2 table.)
When the facility is enabled, the instruction is defined and will be called.
When the facility is not enabled, the instruction is not defined and will
automatically program-check if the guest attempts to execute it. That's one
of the primary purposes of the code in facility.c.
Thus you can be assured that if your instruction is called, the corresponding
facility is indeed enabled. Otherwise your instruction function would never
have been called! Thus any use of if (FACILITY_ENABLED( ... )) statement is
completely unnecessary.
The only time you might need to code a if (FACILITY_ENABLED( ... ))
statement is if the instruction in question is defined to behave differently
depending on whether a given facility is enabled (installed) or not. For example,
take a look at the IPTE (Invalidate Page Table Entry) and SSKE (Set Storage
Key extended) instructions in control.c.
The IPTE instruction contains a if (FACILITY_ENABLED( ... )) check for
each of the 051_LOCAL_TLB_CLEARING and 013_IPTE_RANGE facilities because
it behaves differently depending on whether either of those facilities is
enabled or not.
Similarly, the SSKE instruction contains a if (FACILITY_ENABLED( ... ))
check for the 008_EDAT_1 facility because it too behaves differently depending
on whether that particular facility is enabled or not.
These are likely the only times an instruction function might actually need
to use an if (FACILITY_ENABLED( ... )) statement.
But the key point is, you don't need to do any if (FACILITY_ENABLED( ... ))
test simply to check whether or not your INSTRUCTION EXISTS due to whether
or not your facility is enabled or not. That type of check (test) is handled
automatically by the FT2 table's second parameter and corresponding instrxxx
function.
