/* ************************************************************************** ** * ** © Copyright 1995-2006 Hewlett-Packard Development Company, L.P. * ** * ** Confidential computer software. Valid license from HP and/or * ** its subsidiaries required for possession, use, or copying. * ** * ** Consistent with FAR 12.211 and 12.212, Commercial Computer Software, * ** Computer Software Documentation, and Technical Data for Commercial * ** Items are licensed to the U.S. Government under vendor's standard * ** commercial license. * ** * ** Neither HP nor any of its subsidiaries shall be liable for technical * ** or editorial errors or omissions contained herein. The information * ** in this document is provided "as is" without warranty of any kind and * ** is subject to change without notice. The warranties for HP products * ** are set forth in the express limited warranty statements accompanying * ** such products. Nothing herein should be construed as constituting an * ** additional warranty. * ** * ************************************************************************** ** ** COMPONENT: ** ** This module is part of SYS$EXAMPLES. ** ** MODULE DESCRIPTION: ** ** UWSS.C is a sample user-written system service for OpenVMS I64 and ** Alpha. The mechanism for user-written system services has changed ** significantly for OpenVMS I64 and Alpha; see the OpenVMS Programming ** Concepts Manual and the Linker Manual for more information about ** these system service changes. Where previously user-written system ** services were more simply implemented in VAX Macro, these changes ** make it simpler to develop system services in higher-level ** languages. ** ** The dispatch code, which checked the access of the various ** parameters and did the actual CHMx instruction is no longer ** needed. Instead, a privileged library vector (PLV) structure is ** constructed, containing pointers to kernel- and executive-mode ** system service routine address lists, an optional kernel mode ** rundown handler, and an alternate RMS dispatcher. ** ** The PLV must reside in a program section (PSECT) with the VEC ** attribute. The image activator automatically generates the change ** mode routines in a kernel write/user read section of process-private ** address space after mapping the OpenVMS privileged shareable image. ** ** NOTE: because the change mode is now handled by the image ** activator, the system service itself must do the argument ** verification to prevent ACCVIOs and other such problems. ** ** The associated link procedure, UWSSLNK.COM, will link the object ** and create the executable. The final step is to copy the ** executable to SYS$LIBRARY and install it using INSTALL with the ** /HEADER_RES, /OPEN, /SHARED, and /PROTECTED qualifiers. ** ** AUTHORS: ** ** Digital Equipment Corporation, Compaq, Hewlett-Packard ** ** CREATION DATE: 21-Aug-1992 ** ** DESIGN ISSUES: ** ** To be appropriately upwardly-compatible, this module should ** use the plvdef structure definition from the plvdef.h header ** file. ** ** Ideally, all access to processor status (PS) register ** information should be made using some standard structure ** definition from the appropriate header file, such as found in ** pr$r_psdef_bits. For the sake of convenience, the definition ** of pr$r_psdef_bits resides in prdef.h, since prdef.h already ** contains all the bitfield definitions for the "architected" ** internal processor registers (which, unlike the PS, are ** accessed by MTPR/MFPR PAL calls). As part of starlet, prdef ** now realizes all its definitions using the variant_struct VAX ** C extension (to declare various members of the union with tag ** prdef), rendering code using pr$r_psdef_bits as so defined ** technically non-ANSI-standard. Furthermore, since the ** __PAL_READ_PS built-in has type uint64 (not directly ** compatible with the bit-field definition) it is difficult to ** avoid causing a store if one decides to use the bitfield ** designations, which is contrary to one of the goals in this ** routine (see below). The routines use defined mask bits to ** get at the previous mode, but this only works because they're ** the low-order two bits. ** ** The OpenVMS VAX MACRO-32 example and the OpenVMS Alpha V1.0 ** BLISS example used "DEFINE_SERVICE" macros in a manner which ** allowed declaration of kernel- and executive-mode routines in ** separate statements, mixing of kernel- and executive-mode ** routines in successive statements and automatic counting of ** the numbers of both kinds of routines. The macro capabilities ** of C are limited, by comparison, so only the counts are ** calculated (as opposed to hard-coded) here. Specifically, ** this example relies on sizeof() constructs to avoid having to ** hard-code the numbers of routines, but still requires that all ** executive-mode routines be declared in a single declaration, ** and that all kernel-mode routines be declared in a single ** declaration. ** ** Since this is an example "template" for services which may be ** executed many times, and since PALcode calls can be somewhat ** expensive, the services in this routine work to minimize ** the number of PAL calls made. Thus, the RD_PS call, needed ** both (1) to get the previous processor mode as part of ** checking write access to arguments, and (2) to get the first ** value to return to the caller, stores its result in the ** variable ps_temp. ps_temp should not, and likely will not, be ** materialized other than in a register (since we don't know ** where we can safely put it) but there is currently no ** enforceable compiler guarantee of this, or of similar behavior ** for a "full-sized" user-written system service. The link of ** the image should fail if allocation of storage results in a C ** RTL call. ** ** ** COMPILATION, LINKING AND INSTALLATION PROCEDURES: ** ** To compile this routine, use ** ** $ CC UWSS + SYS$LIBRARY:SYS$LIB_C /LIBRARY /POINTER_SIZE=32 ** ** To link this routine, use the following link procedure (also ** present with more comments as UWSS_LNK.COM): ** ** $ LINK /SHARE=UWSS /PROTECT /MAP=UWSS - ** /SYSEXE /FULL /CROSS /NOTRACE - ** UWSS, SYS$INPUT:/OPTIONS ** GSMATCH=LEQUAL,1,1 ** SYMBOL_VECTOR = ( - ** FIRST_SERVICE = PROCEDURE, - ** SECOND_SERVICE = PROCEDURE, - ** THIRD_SERVICE = PROCEDURE, - ** FOURTH_SERVICE = PROCEDURE - ** ) ** PSECT=PLV,VEC,NOWRT,NOEXE ** PSECT=PLV_DATA,NOWRT,NOEXE ** COLLECT=PLV,PLV ** ** To use this routine, copy the executable to SYS$LIBRARY, and ** install it as follows: ** ** $ INSTALL REPLACE SYS$LIBRARY:UWSS /HEADER_RES /OPEN /SHARED /PROTECTED ** ** ** PORTABILITY ISSUES: ** ** This program is only intended to run as an example collection ** of user-written system services, in a privileged shareable ** image, on OpenVMS I64 and Alpha. The program assumes that ** OpenVMS I64 and Alpha will continue to support ONLY the ** hierarchical OpenVMS VAX page protection model. That model is ** more restrictive than the architected Alpha PTE model, which, ** for example, may allow user write access but no kernel write ** access to a given page. If the restriction is ever lifted, ** a probew PAL call for current mode as well as one for previous ** mode will be required for safe operation. ** ** ** MODIFICATION HISTORY: ** ** X-1 DBM001 21-Aug-1992 ** Module created. ** ** X-2 DCP002 11-Jan-1993 ** Use extern_model pragma for PLV, make cosmetic and substantial ** textual changes, do probew from current mode to avoid both ** accvios and poss sys space corruption, calc # rtns ** automatically. ** ** X-3 DCP003 17-Mar-1993 ** Correct file names in usage instructions. ** ** X-4 DCP004 29-Dec-1993 ** Use the system-supplied PLV definition, remove local copy ** and adjust comments accordingly. Also, re-type common ** run-down stub as returning int to match PLV definition. ** ** X-5 NYK500 17-Oct-1995 ** Modify two of the existing services to be 64-bit capable. ** ** X-6 HB 16-Aug-2006 ** Some adjustments for Ia64 and correct builds: ** Make sure that the source is explicitly compiled with 32-bit ** pointers and that the named PSECTs are created for I64. ** Create another named PSECT for data pointed to from the PLV: ** This ensures that the image can be installed with shared ** address data. ** */ /* ** This source uses 32 and 64-bit interfaces. builtins.h can only set up ** the correct 64-bit prototypes if compiled with /POINTER_SIZE, this ** source requires a default of 32-bit pointers, so, please... */ #if !defined(__INITIAL_POINTER_SIZE) || __INITIAL_POINTER_SIZE!=32 #error /POINTER_SIZE=32 required #endif /* And now, on with the program... */ #include #include #include #include #include #include #include #include #include /* ** Group all the data the PLV points to into one section: PLV_DATA. ** ** If the protected shareable image is to be installed with shared address ** data, it is easier to make the PLV data read-only. ** ** When a shareable image is installed with shared address data, the ** INSTALL utility reserves P1 address space for its shared address ** data. Then INSTALL creates global sections for the shared data. ** Read-only data is shared. Then INSTALL updates all the shared address ** data. The PLV contains addresse references to the PLV data, so its ** address data should be updated. Before the update INSTALL checks if the ** pointed to data is mapped in a global section. INSTALL prints an error ** message, INSTALL-E-NOT_MAPPED, if there is no such global section. ** ** Using read-only data is easier than to sharing a writable section, because ** writable sections are usually not shared. If they are shared (for example ** with a linker option) then installing the protected shareable image ** requires installing the entire image writable. That may not be possible or ** intended. ** ** In this example, because the compiler by default creates writable data, a ** linker option is used to make PLV_DATA read-only. */ #if defined(__ALPHA) || defined(__ia64) #pragma extern_model save #pragma extern_model strict_refdef "PLV_DATA" #endif /* ** To make things easy, the number and name of the user-written system ** services must be known at compile time. So first define the number ** of services (kernel and exec) and allocate the routine lists, which ** in C-language terms are arrays of pointers to functions returning ** ints. */ /* "Forward routine" declarations */ int first_service(), second_service(), third_service(), fourth_service(); int rundown_handler(); /* Kernel and exec routine lists: */ int (*(kernel_table[]))() = { first_service, second_service, fourth_service}; int (*(exec_table[]))() = { third_service}; /* ** Kernel and exec flags. The flag settings below enable second_service ** and fourth_service to be 64-bit capable. First_service and third_service ** cannot accept a 64-bit pointer. Attempts to pass 64-bit pointers to ** these services will result in a return status of SS$_ARG_GTR_32_BITS. ** The PLV$M_64_BIT_ARGS flag instructs the system service dispatcher to ** bypass sign-extension checking of the service arguments for a particular ** service. */ int kernel_flags [] = { 0, PLV$M_64_BIT_ARGS, 0}, exec_flags [] = { PLV$M_64_BIT_ARGS}; #if defined(__ALPHA) || defined(__ia64) #pragma extern_model restore #endif /* ** The next two defines allow the kernel and executive routine counts ** to be filled in automatically after lists have been declared for ** kernel and exec mode. They must be placed before the PLV ** declaration and initialization, and for this module will be ** functionally equivalent to: ** ** #define KERNEL_ROUTINE_COUNT 3 ** #define EXEC_ROUTINE_COUNT 1 ** */ #define EXEC_ROUTINE_COUNT sizeof(exec_table)/sizeof(int *) #define KERNEL_ROUTINE_COUNT sizeof(kernel_table)/sizeof(int *) /* ** Now build and initialize the PLV structure. Since the PLV must have ** the VEC psect attribute, and must be the first thing in that psect, ** we use the strict external ref-def model which allows us to put the ** PLV structure in its own psect. This is like the globaldef ** extension in VAX C, where you can specify in what psect a global ** symbol may be found; unlike globaldef, it allows the declaration ** itself to be ANSI-compliant. Note that the initialization here ** relies on the change-mode-specific portion (plv$r_cmod_data) of the ** PLV being declared before the portions of the PLV which are specific ** to message vector PLVs (plv$r_msg_data) and system service intercept ** PLVs (plv$r_ssi_data). ** */ #if defined(__ALPHA) || defined(__ia64) #pragma extern_model save #pragma extern_model strict_refdef "PLV" #endif extern const PLV plv = { PLV$C_TYP_CMOD, /* type */ 0, /* version */ { {KERNEL_ROUTINE_COUNT, /* # of kernel routines */ EXEC_ROUTINE_COUNT, /* # of exec routines */ kernel_table, /* kernel routine list */ exec_table, /* exec routine list */ rundown_handler, /* kernel rundown handler */ rundown_handler, /* exec rundown handler */ 0, /* no RMS dispatcher */ kernel_flags, /* kernel routine flags */ exec_flags} /* exec routine flags */ } }; #if defined(__ALPHA) || defined(__ia64) #pragma extern_model restore #endif /* ** And now, the service routines. The routines in this example ** execute no instructions which would require privilege to complete, ** but they give different results when run privileged than they would ** if run from a non-privileged shareable image. Each routine will ** expects to be passed a pointer to an array of 2 longwords. The ** first longword will be set by the service routine to contain the ** low 32-bits of the processor status register, from which a caller ** can verify that the service routine is executing in the proper mode ** (kernel or exec). The second longword is then set to a unique ** integer (1 for first_service, and so on up to 4) to verify that the ** correct service routine has been called. ** ** Each routine will verify that it can write to the longword array in ** the previous mode, since we're running in one of the inner modes. ** If a bogus address were passed (either an inaccessible address, ** causing an access violation, or an address accessible in an inner ** mode that shouldn't be accessible in user mode), then without this ** check, either the process would get blown away (exec mode) or the ** system would crash (in kernel mode, right away if an accvio or ** later if system space were corrupted). Clearly, neither failure ** mode is desirable. Note that the use of the single PROBEW PAL call ** for previous mode is enough because only a single contiguous pair ** of longword data (equivalent to a single quadword datum) will be ** stored. For storage of larger amounts of data which may cross more ** than one page boundary, more stringent checking is required. ** ** One final note on the service routines. As a security precaution, ** protected shareable images aren't allowed to call other shareable ** images, unless they too are installed protected. Watch your calls ** (including implicit ones) to external routines! They must either ** be linked (from object files or libraries) into your image, or be ** in other protected images. If you fail to heed this warning, the ** image activator will 'kindly' remind you with the error: ** ** SYSTEM-F-NOSHRIMG, privileged shareable image cannot have outbound calls */ /* ** This service is defined to accept a 32-bit pointer to a longword array. */ int first_service(int *ptr) { register unsigned int ps_temp; /* Assumed to take no real storage*/ ps_temp = __PAL_RD_PS(); /* Get the value of the proc status reg*/ if ( /* Verify write access to the array from */ __PAL_PROBEW(ptr, 8, ps_temp & PR$M_PS_PRVMOD) == 0 /* previous mode */ ) return SS$_ACCVIO; /* nope, sorry */ ptr[0] = ps_temp; /* Get the value of low LW of PS */ ptr[1] = 1; /* Unique value for function */ return SS$_NORMAL; /* Indicate success */ } /* ** This service is defined to accept a 64-bit pointer to a longword array. */ int second_service(INT_PQ ptr) { register unsigned int ps_temp; /* Assumed to take no real storage*/ ps_temp = __PAL_RD_PS(); /* Get the value of the proc status reg*/ if ( /* Verify write access to the array from */ __PAL_PROBEW(ptr, 8, ps_temp & PR$M_PS_PRVMOD) == 0 /* previous mode */ ) return SS$_ACCVIO; /* nope, sorry */ ptr[0] = ps_temp; /* Get the value of the PSL */ ptr[1] = 2; /* Unique value for function */ return SS$_NORMAL; /* Indicate success */ } /* ** This service is defined to accept a 64-bit pointer to a longword array. */ int third_service(INT_PQ ptr) { register unsigned int ps_temp; /* Assumed to take no real storage*/ ps_temp = __PAL_RD_PS(); /* Get the value of the proc status */ if ( /* Verify write access to the array from */ __PAL_PROBEW(ptr, 8, ps_temp & PR$M_PS_PRVMOD) == 0 /* previous mode */ ) return SS$_ACCVIO; /* nope, sorry */ ptr[0] = ps_temp; /* Get the value of the PSL */ ptr[1] = 3; /* Unique value for function */ return SS$_NORMAL; /* Indicate success */ } /* ** This service is defined to accept a 32-bit pointer to a longword array. */ int fourth_service(int *ptr) { register unsigned int ps_temp; /* Assumed to take no real storage*/ ps_temp = __PAL_RD_PS(); /* Get the value of the proc status */ if ( /* Verify write access to the array from */ __PAL_PROBEW(ptr, 8, ps_temp & PR$M_PS_PRVMOD) == 0 /* previous mode */ ) return SS$_ACCVIO; /* nope, sorry */ ptr[0] = ps_temp; /* Get the value of the PSL */ ptr[1] = 4; /* Unique value for function */ return SS$_NORMAL; /* Indicate success */ } /* ** Our kernel and exec rundown handlers are one and the same. ** The rundown handler is invoked before any system rundown ** is performed. ** ** This routine is provided simply as a placeholder for a ** real rundown handler. A user-written rundown handler ** should not invoke any RMS services or RTL routines, and ** must not signal any exceptions. User-written rundown ** handlers can invoke most system services except those ** that use RMS (i.e. $PUTMSG). */ int rundown_handler() { return SS$_NORMAL; /* Indicate success */ }