- Introduction
- Defining the STFLE feature bits
- Defining the feat900
FEATURE
build macros - Verifying
FEATURE
build macro sanity - Defining the facility.c run-time tables and functions
5a. TheFT
table
5b. TheFT2
table
5c.modnnn
Modification Check functions
5d.instrxxx
Update Opcode Table functions - Coding the facility instructions themselves
6a.DEF_INST
inopcode.h
6b.UNDEF_INST
andGENx___x___x900
inopcode.c
6c. The actualesame.c
instruction 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
facility
command 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
#endif
The 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_ENABLED
macro (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 );
#endif
Within 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 );
#endif
Then 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.