%TITLE 'LOAD_DRIVER, load device drivers and I/O database' MODULE iogen$load_driver (IDENT = 'X-19', ENVIRONMENT(NOFP)) = BEGIN ! ! Copyright © Digital Equipment Corporation, 1990, 1993 All Rights Reserved. ! Unpublished rights reserved under the copyright laws of the United States. ! ! The software contained on this media is proprietary to and embodies the ! confidential technology of Digital Equipment Corporation. Possession, use, ! duplication or dissemination of the software and media is authorized only ! pursuant to a valid written license from Digital Equipment Corporation. ! ! RESTRICTED RIGHTS LEGEND Use, duplication, or disclosure by the U.S. ! Government is subject to restrictions as set forth in Subparagraph ! (c)(1)(ii) of DFARS 252.227-7013, or in FAR 52.227-19, as applicable. ! !++ ! FACILITY: ! ! IOGEN ! ! ABSTRACT: ! ! This module provides the user interface and command dispatching for the ! $LOAD_DRIVER service. Throughout this facility, if %VARIANT is false, ! code is built for use in the run-time environment in process context. ! If %VARIANT is true, code is built for use in the INIT environment and ! is expected to be linked into the EXEC_INIT image. ! ! AUTHOR: ! ! Richard W. Critz, Jr. ! ! CREATION DATE: 27-Sep-1990 ! ! MODIFICATION HISTORY: ! ! X-21 Glenn C. Everhart 5-May-1997 ! Add support for SCSI controller alloc classes. Pass these ! to load_driver by biasing the value by 40000000 hex and ! undoing in load_driver. This allows load_driver to distinguish ! this case from classes that are port alloc classes. ! ! X-20 Genady Perchenko 10-July-1997 ! Fix for EVMS-RAVEN QAR 98 ! When accepting drivernam input parameter LOAD_DRIVER routine ! trusted upper level (DCL in most case) to upcase the input ! file name string. New DCL, however, preserves casing in file ! names which potentially might lead to the situation where the ! same driver has been loaded twice into the memory if entered ! with different casing at DCL prompt. To avoid this situation ! I replaced CH$MOVE call which was used to copy driver name ! from drivernam parameter into lddb buffer with small hand made ! loop which will upcase the characters while copying them. ! NOTE: this upcasing procedure will support only regular ASCII ! character upcasing. Also note that the reason why I did not ! use str$upcase instead is because this code is also part of ! exec_init.exe which executes at system startup time when str$ ! routines are not available. ! ! X-19 Anne Mcelearney 12-Mar-1997 ! Fix for EVMS-RAVEN QAR 733. There is a problem with the ! logic added in X-13 when a satellite is booting under the ! following conditions: ! * The satellite has device naming on ! * The satellite is booting from a served ! disk which does not have a PAC (DKcn) ! * The satellite has a PAC assigned to it's ! local PKc ! The call to IOC$CHECK_CLASS finds a PAC for the controller ! which results in the device name being changed from ! DKcn to DKAn. ! FIX: As a manual CONNECT does not occur during boot time, ! the code which calls IOC$CHECK_CLASS will not be produced ! if we are generating the VARIANT used during boot time. ! ! X-15 Susan Lewis 16-Sep-1996 ! MAXUNITS can contain up to 65535. NUMUNITS gets maxed ! out at 127. We have a customer that wants more. Allow ! numunits to be an unsigned word instead of a signed byte. ! ! X-15A1A1 JCH710c John C. Hallyburton, Jr. 12-Sep-1996 ! Still didn't get the previous edit right. $CMKRNL should ! be a straight routine call in the boot-time variant. ! And in a related story, CMS thinks X-16 should be X-15A1. ! ! X-16 JCH710b John C. Hallyburton, Jr. 30-Aug-1996 ! Oops, X-15 didn't know the device name could be null, ! e.g., IO LOAD XXXdriver. Restrict logic to iogen$_connect ! case and check for null device pointer. ! ! X-15 JCH710a John C. Hallyburton, Jr. 28-Aug-1996 ! X-13 fails to get the right port allocation class for ! the case of a manual connect (SYSMAN>IO CONN DKcnn) to ! a port excluded from autoconfiguration (e.g. /EXCL=DKc). ! Look at the device name and if it is 'DK' or 'MK', check ! the corresponding port for allocation class match and if ! a match is found set up the lddb accordingly. ! ! X-14 TGA Thomas G. Arnold 18-Jun-1996 ! Fix for EVMS-GRYPHON QAR 355: move invocation of ! iogen$init_db() into iogen$connect(). This closes a hole ! whereby $ASSIGN can come in between the two routines ! and cause a system crash when $ASSIGN finds the UCB field ! UCB$L_DDT as zero. ! ! X-13 JCH710 John C. Hallyburton, Jr. 16-Oct-1995 ! Naming: implement port allocation classes. ! DOCD$:[EVMS.PROJECT_DOCUMENTS]FS-SCSI-NAMING.PS ! ! X-12 RWC137 Richard W. Critz, Jr. 29-Dec-1993 ! YELLOW got the wrong source module for the previous generation. ! ! X-11 RWC135 Richard W. Critz, Jr. 21-Dec-1993 ! Call IOGEN$INIT_MAPPING before thinking about controller ! initialization on a non-add-unit CONNECT. This supports the ! CSR_MAPPING routine in a driver which allows it to get control ! at IPL 8 for the purpose of using the platform independent ! mapping support. ! ! X-10 SFS0627 Stephen F. Shirron 13-Oct-1993 ! Clear UCB$V_NO_ASSIGN for the system disk UCB. ! ! X-9 RWC115 Richard W. Critz, Jr. 4-Mar-1993 ! The flag SYSDEVICE_OK, which is used in the EXEC_INIT variant to ! prevent declaring more than one device as the system UCB, was ! accidentally declared as a LOCAL variable. This means that it ! does absolutely nothing. Redefine it as an OWN variable to make ! the SYSDEVICE function modifier behave as advertised. ! ! X-8 RWC102 Richard W. Critz, Jr. 2-Nov-1992 ! Fix management of structure creation flags and the "created UCB" ! address so that NUMUNITS works correctly. ! ! X-7 RWC092 Richard W. Critz, Jr. 8-Jul-1992 ! Add support for IOGEN$_NODE item code. ! ! X-6 RWC081 Richard W. Critz, Jr. 1-May-1992 ! Fix typo in IOGEN$COMPLETE_LDDB's handling of nodename prefixed ! device names. It was using the original length of the device ! name rather than the length returned by IOC$PARSDEVNAM. This ! latter length has the unit number removed which is what the rest ! of LOAD_DRIVER expects to have happened after ! IOGEN$COMPLETE_LDDB has run. ! ! ------------------ Masterpack cleanup reset generation number again ---------- ! ! X-10 RWC076 Richard W. Critz, Jr. 11-Mar-1992 ! If the caller doesn't provide an IOSB, don't try to write to it. ! This was an oversite in the final phase of $LOAD_DRIVER. The ! preceding phases very carefully detected the presence/absence of ! an IOSB and did the right thing. Final cleanup assumed that it ! always existed rather than checking the IOSB flag before ! attempting to fill it in. ! ! X-9 RWC059 Richard W. Critz, Jr. 16-Dec-1991 ! Fix build bug. ! ! X-8 RWC054 Richard W. Critz, Jr. 22-Nov-1991 ! ADP's TR has been promoted to a longword. Use ADP$L_TR instead ! of ADP$W_TR. ! ! X-7 RWC052 Richard W. Critz, Jr. 20-Nov-1991 ! Complete UNIT_DELIVERY support. ! ! X-6 RWC044 Richard W. Critz, Jr. 28-Oct-1991 ! Add support for the NOINIT function modifier. ! ! X-5 RWC037 Richard W. Critz, Jr. 26-Apr-1991 ! The answer to the question in RWC018 is a resounding no. It's ! not even correct if there ISN'T a nodename. Do it right this ! time. ! ! X-4 RWC036 Richard W. Critz, Jr. 25-Apr-1991 ! 1) Restore a couple of SS$_BADPARAM returns that were ! accidentally lost in RWC034. ! ! 2) Add ADD_UNIT logic to shortcut various pieces of the CONNECT ! logic when adding a new unit to an existing controller. ! ! 3) In CONNECT processing, pick up the DPT's MAXUNIT value from ! the LDDB and not the DPT directly. ! ! X-3 RWC034 Richard W. Critz, Jr. 22-Apr-1991 ! BL1 completion: ! ! 1) Do system state and privilege checking. ! 2) Do systemwide synchronization. ! 3) Do working set locking. ! 4) Rework processing of LOAD$_VECTOR. ! 5) Remove support for LOAD$_NUMVEC. ! 6) Disallow allocation class in DEVNAM parameter. ! ! X-1K5 RWC019 Richard W. Critz, Jr. 11-Jan-1991 ! Change field names to coordinate reorganization of LDDB. ! ! X-1K4 RWC024 Richard W. Critz, Jr. 2-Jan-1991 ! Add handling in the VARIANT (EXEC_INIT) version for the ! SYSDEVICE function modifier. Plug the DDB and UCB addresses ! into the appropriate pointer fields in the base image. ! ! X-1K3 RWC018 Richard W. Critz, Jr. 19-Dec-1990 ! Correct calculation of LDDB$PS_DDB_NAME. Is this correct ! if there is actually a nodename? ! ! X-1K2 RWC017 Richard W. Critz, Jr. 10-Dec-1990 ! Add conditional code to avoid device specific portions ! of COMPLETE_LDDB when no device was specified (as in ! IOGEN$_LOAD case). Also, don't do certain consistency ! checks unless the function code is IOGEN$_CONNECT. ! ! X-1K1 RWC003 Richard W. Critz, Jr. 27-Sep-1990 ! Original. !-- ! ! TABLE OF CONTENTS: ! FORWARD ROUTINE sys$load_driver, release_everything, iogen$complete_lddb; ! ! INCLUDE FILES: ! LIBRARY 'sys$library:lib'; REQUIRE 'src$:iogen-macros'; ! ! MACROS: ! MACRO item_w_size = 0, 0, 16, 0 %, ! size of item buffer item_w_code = 2, 0, 16, 0 %, ! item code item_l_addr = 4, 0, 32, 1 %, ! address of item buffer item_l_retlen = 8, 0, 32, 1 %, ! place to return length of output item item_l_end = 0, 0, 32, 0 %, ! item list terminator item_s_item = 12 %, ! size of one item list descriptor item_s_end = 4%, iosb_w_status = 0, 0, 16, 0 %, ! status field of the IOSB iosb_w_flags = 2, 0, 16, 0 %, ! "created structure" flags iosb_l_dpt = 4, 0, 32, 1 %, ! address of DPT for driver iosb_s_iosb = 8 %, ! size of the IOSB flag_l_clear = 0, 0, 32, 0 %, ! allow flag initialization flag_v_devnam = 0, 0, 1, 0 %, ! devnam parameter specified flag_v_lddb = 0, 1, 1, 0 %, ! devnam is actually an lddb flag_v_drivernam= 0, 2, 1, 0 %, ! drivernam parameter specified flag_v_itmlst = 0, 3, 1, 0 %, ! itmlst parameter specified flag_v_iosb = 0, 4, 1, 0 %, ! iosb parameter specified flag_v_efn = 0, 5, 1, 0 %, ! efn parameter specified flag_v_astadr = 0, 6, 1, 0 %, ! astadr parameter specified flag_v_astprm = 0, 7, 1, 0 %, ! astprm parameter specified flag_v_unit = 0, 8, 1, 0 %, ! IOGEN$_UNIT item specified flag_v_csr = 0, 9, 1, 0 %, ! IOGEN$_CSR item specified flag_v_vector = 0, 10, 1, 0 %, ! IOGEN$_VECTOR item specified flag_v_maxunits = 0, 11, 1, 0 %; ! IOGEN$_MAXUNITS item specified ! ! EQUATED SYMBOLS: ! ! ! OWN STORAGE: ! OWN %IF NOT %VARIANT %THEN data_begin : VECTOR[2,LONG] PSECT(iogen$data_000), data_end : LONG PSECT(iogen$data_002), linkage_begin : VECTOR[2,LONG] PSECT(iogen$linkage_000), linkage_end : LONG PSECT(iogen$linkage_002), code_begin : VECTOR[2,LONG] PSECT(iogen$code_000), code_end : LONG PSECT(iogen$code_002), data_range : VECTOR[2,LONG,SIGNED] INITIAL (data_begin, data_end), linkage_range : VECTOR[2,LONG,SIGNED] INITIAL (linkage_begin, linkage_end), code_range : VECTOR[2,LONG,SIGNED] INITIAL (code_begin, code_end), data_lock : VECTOR[2,LONG,SIGNED], linkage_lock : VECTOR[2,LONG,SIGNED], code_lock : VECTOR[2,LONG,SIGNED], %FI flags : ALIGN(3) BLOCK[4,BYTE], local_lddb : ALIGN(3) BLOCK[lddb$k_length,BYTE] VOLATILE, sysdevice_ok : INITIAL(true); ! ! EXTERNAL REFERENCES: ! EXTERNAL %IF %VARIANT %THEN sys$ar_bootddb : SIGNED LONG, sys$ar_bootorb : SIGNED LONG, sys$ar_bootucb : SIGNED LONG, exe$gl_sysucb : SIGNED LONG, %FI exe$gl_state : BLOCK[,BYTE]; EXTERNAL ROUTINE %IF NOT %VARIANT %THEN iogen$check_privs, iogen$class_lock, iogen$lock, iogen$unlock, %FI ioc$parsdevnam : ioc$parsdevnam_linkage, ioc$unit_deliver, iogen$search, iogen$reset_lddb : NOVALUE, iogen$find_adp, iogen$load, iogen$connect, iogen$init_db, iogen$init_mapping, iogen$init_controller, iogen$init_unit; %SBTTL 'SYS$LOAD_DRIVER, user interface implementation' GLOBAL ROUTINE sys$load_driver( function : BLOCK[,BYTE], devnam : REF BLOCK[,BYTE], drivernam : REF BLOCK[,BYTE], itmlst : REF BLOCK[,BYTE], iosb : REF BLOCK[,BYTE], efn : UNSIGNED LONG, astadr : SIGNED LONG, astprm : SIGNED LONG) = !++ ! FUNCTIONAL DESCRIPTION: ! ! This routine implements the user interface to $LOAD_DRIVER. It ! validates the caller's arguments, processes the item list and ! dispatches to the appropriate work routines to perform the requested ! function. ! ! FORMAL PARAMETERS: ! ! function - code for the requested function with optional modifiers ! devnam - address of device name descriptor ! drivernam - address of device driver filespec descriptor ! itmlst - address of item list that qualifies the caller's request ! iosb - address of the I/O status block ! efn - number of event flag to set on completion (NYI) ! astadr - address of a completion AST routine (NYI) ! astprm - value to use as a parameter to the completion AST (NYI) ! ! IMPLICIT INPUTS: ! ! None ! ! IMPLICIT OUTPUTS: ! ! None ! ! COMPLETION CODES: ! ! SS$_NORMAL - requested function completed successfully ! SS$_BADPARAM - one or more arguments are malformed or incorrect ! SS$_IVBUFLEN - a buffer associated with an item list descriptor ! is not a valid length ! SS$_ACCVIO - an argument was not available for the required access ! SS$_INSFMEM - failure allocating non-paged pool ! SS$_NOCMKRNL - insufficient privilege ! SS$_NOCMEXEC - insufficient privilege ! SS$_NOSYSLCK - insufficient privilege ! SS$_NOSUCHDEV - explicit controller/unit init requested for nonexistent ! device ! SS$_UNSUPPORTED - the requested function is unsupported ! SS$_DRVEXISTS - driver already loaded ! SS$_DEVEXISTS - device already exists ! SS$_DRV_NOUNLOAD- driver is not unloadable ! SS$_ILLIOFUNC - invalid function code ! ! SIDE EFFECTS: ! ! None !-- BEGIN BUILTIN ACTUALCOUNT, NULLPARAMETER; LOCAL status, nbits, index, source_str : REF VECTOR[,BYTE], targ_str : REF VECTOR[,BYTE], items : REF BLOCK[,BYTE], lddb : REF BLOCK[,BYTE]; LOCAL jnkmsk; LOCAL alloflgs; IF ACTUALCOUNT() EQL 0 THEN RETURN ss$_insfarg; jnkmsk = 1073741823; %IF %VARIANT %THEN IF .exe$gl_state[boostate$v_swapper] ! if no longer in EXEC_INIT THEN RETURN ss$_unsupported; %ELSE IF NOT .exe$gl_state[boostate$v_swapper] ! if still in EXEC_INIT THEN RETURN ss$_unsupported ELSE IF NOT (status = iogen$check_privs()) THEN RETURN .status; %FI flags[flag_l_clear] = 0; ! initialize parameter processing flags ! Process "devnam" argument IF NULLPARAMETER(devnam) THEN IF .function[iogen$v_lddb] THEN RETURN ss$_badparam ELSE BEGIN lddb = local_lddb; iogen$reset_lddb(.lddb) END ELSE BEGIN flags[flag_v_devnam] = true; IF .function[iogen$v_lddb] ! device name or lddb address? THEN BEGIN flags[flag_v_lddb] = true; lddb = .devnam; IF NOT $probe(.lddb, lddb$k_length, w) THEN RETURN ss$_accvio END ELSE BEGIN lddb = local_lddb; iogen$reset_lddb(.lddb); IF $probe(.devnam, 8, rq) THEN BEGIN IF NOT $probe(.devnam[dsc$a_pointer], .devnam[dsc$w_length], r) THEN RETURN ss$_accvio END ELSE RETURN ss$_accvio; IF (lddb[lddb$il_devlen] = .devnam[dsc$w_length]) GTR lddb$s_devnam THEN RETURN ss$_ivbuflen; CH$MOVE(.devnam[dsc$w_length], .devnam[dsc$a_pointer], lddb[lddb$t_devnam]) END; END; ! Process "drivernam" argument IF NOT NULLPARAMETER(drivernam) THEN BEGIN flags[flag_v_drivernam] = true; IF $probe(.drivernam, 8, rq) THEN BEGIN IF NOT $probe(.drivernam[dsc$a_pointer], .drivernam[dsc$w_length], r) THEN RETURN ss$_accvio END ELSE RETURN ss$_accvio; IF (lddb[lddb$ib_drvnamlen] = .drivernam[dsc$w_length]) GTR lddb$s_drvnamstr THEN RETURN ss$_ivbuflen; ! copy driver name from input buffer to the lddb buffer ! upcasing it on it's way. targ_str = lddb[lddb$t_drvnamstr]; source_str = .drivernam[dsc$a_pointer]; index = 0; WHILE .index LSS .drivernam[dsc$w_length] DO BEGIN IF .source_str[.index] GEQ 'a' AND .source_str[.index] LEQ 'z' THEN targ_str[.index] = .source_str[.index] - ('a' - 'A') ELSE targ_str[.index] = .source_str[.index]; index = .index + 1; END; ! CH$MOVE(.drivernam[dsc$w_length], .drivernam[dsc$a_pointer], lddb[lddb$t_drvnamstr]) END; ! Process "itmlst" argument IF NOT NULLPARAMETER(itmlst) THEN BEGIN flags[flag_v_itmlst] = true; items = .itmlst; IF NOT $probe(.items, item_s_end, rq) ! must have a least a terminator THEN RETURN ss$_accvio; WHILE .items[item_l_end] NEQ 0 DO BEGIN ! Insure that a full item descriptor plus a possible following ! terminator are readable so that the loop conditional can ! execute without fear of an ACCVIO. IF NOT $probe(.items, item_s_item+item_s_end, rq) THEN RETURN ss$_accvio; ! IOGEN$_NOADAPTER has no associated datum. For all others, ! the datum must be readable IF .items[item_w_code] NEQ iogen$_noadapter THEN IF NOT $probe(.items[item_l_addr], .items[item_w_size], r) THEN RETURN ss$_accvio; CASE .items[item_w_code] FROM iogen$k_minitem TO iogen$k_maxitem OF SET [iogen$_adapter]: BEGIN BIND adp_tr = .items[item_l_addr] : SIGNED WORD; %IF NOT %VARIANT %THEN LOCAL find_args : VECTOR[3,LONG,SIGNED]; %FI check_field_size(LEQ, adp$l_tr); nbits = .items[item_w_size] * %BPUNIT; %IF NOT %VARIANT %THEN find_args[0] = 2; ! set up arglist for IOGEN$FIND_ADP find_args[1] = .lddb; ! 2 args, first is LDDB address find_args[2] = .adp_tr<0,.nbits,1>; ! second is TR # %FI IF NOT (status = %IF %VARIANT %THEN iogen$find_adp(.lddb, .adp_tr<0,.nbits,1>) %ELSE $cmexec(routin = iogen$find_adp, arglst = find_args) %FI ) THEN RETURN .status END; [iogen$_noadapter]: lddb[lddb$v_noadap] = true; [iogen$_csr]: BEGIN BIND csr = .items[item_l_addr] : VECTOR[,BYTE]; check_field_size(LEQ, lddb$q_csr); CH$MOVE(.items[item_w_size], csr, lddb[lddb$q_csr]); flags[flag_v_csr] = true END; [iogen$_vector]: BEGIN BIND users_vectors = .items[item_l_addr] : VECTOR[,LONG,UNSIGNED], vector_list = lddb[lddb$l_vector] : VECTOR[,LONG,UNSIGNED]; ! Check the size of the vector list. It must be an even ! number of longwords and, therefore, must be a multiple of ! 4. Additionally, if this item code is specified, at ! least one vector must be given. The number of vectors is ! simply the size/4. IF ((.items[item_w_size] AND 3) NEQ 0) OR (.items[item_w_size] LSS 4) THEN RETURN ss$_badparam; lddb[lddb$l_numvec] = .items[item_w_size] / 4; flags[flag_v_vector] = true; INCR i FROM 0 TO .lddb[lddb$l_numvec] - 1 BY 1 DO vector_list[.i] = .users_vectors[.i] END; [iogen$_maxunits]: BEGIN BIND maxunits = .items[item_l_addr] : UNSIGNED WORD; check_field_size (LEQ, lddb$l_maxunits); nbits = .items[item_w_size] * %BPUNIT; lddb[lddb$l_maxunits] = .maxunits<0,.nbits,0>; flags[flag_v_maxunits] = true END; [iogen$_sysid]: BEGIN BIND sysid = .items[item_l_addr] : VECTOR[,BYTE]; check_field_size(LEQ, lddb$q_sysid); CH$MOVE(.items[item_w_size], sysid, lddb[lddb$q_sysid]) END; [iogen$_sysloa_crb]: BEGIN BIND crb = .items[item_l_addr] : REF BLOCK[,BYTE]; check_field_size(EQL, lddb$ps_crb); lddb[lddb$ps_crb] = .crb; lddb[lddb$v_crbblt] = true END; [iogen$_unit]: BEGIN BIND unit = .items[item_l_addr] : UNSIGNED WORD; check_field_size(LEQ, lddb$l_unit); nbits = .items[item_w_size] * %BPUNIT; lddb[lddb$l_unit] = .unit<0,.nbits,0>; flags[flag_v_unit] = true END; [iogen$_numunits]: BEGIN BIND numunits = .items[item_l_addr] : UNSIGNED WORD; check_field_size(LEQ, lddb$l_numunits); IF NOT $probe(numunits, .items[item_w_size], wq) THEN RETURN ss$_accvio; lddb[lddb$l_numunits] = .numunits END; [iogen$_deliver_data]: BEGIN BIND deliver_data = .items[item_l_addr] : VECTOR[,BYTE]; check_field_size(LEQ, lddb$q_dlvr_data); IF NOT $probe(deliver_data, .items[item_w_size], wq) THEN RETURN ss$_accvio; lddb[lddb$ps_user_dlvr] = deliver_data; CH$MOVE(.items[item_w_size], deliver_data, lddb[lddb$q_dlvr_data]) END; [iogen$_node]: BEGIN BIND node = .items[item_l_addr] : SIGNED LONG; check_field_size(LEQ, lddb$l_node); nbits = .items[item_w_size] * %BPUNIT; lddb[lddb$l_node] = .node<0,.nbits,0> END; [iogen$_allocls]: ! (X-13) Allocation class provided? BEGIN BIND port_allocls = .items[item_l_addr]: SIGNED LONG; check_field_size(EQL, lddb$l_allocls); IF .port_allocls NEQ -1 THEN ! -1 means none BEGIN ! If the allocls has been biased, it is a device alloc class and must not be ! treated as a PAC. lddb[lddb$l_allocls] = .port_allocls; alloflgs = .port_allocls; lddb[lddb$l_allocls] = .lddb[lddb$l_allocls] AND .jnkmsk; IF (.port_allocls LSS .jnkmsk) THEN lddb[lddb$v_pac] = 1; END; END; [INRANGE]: BEGIN BIND datum = .items[item_l_addr] : SIGNED LONG; ! Verify that the data field is a pointer size ! (LDDB$PS_UCB is just a representative pointer) check_field_size(EQL, lddb$ps_ucb); ! Verify that output area is writeable now. ! We'll have to check again later but don't ! bother to start if it is inaccessible now IF NOT $probe(datum, .items[item_w_size], wq) THEN RETURN ss$_accvio END; [OUTRANGE]: RETURN ss$_badparam; TES; items = .items + item_s_item END END; ! Check for an IOSB, insure it is writeable and clear it. IF NOT NULLPARAMETER(iosb) THEN BEGIN flags[flag_v_iosb] = true; IF NOT $probe(.iosb, iosb_s_iosb, wq) THEN RETURN ss$_accvio ELSE CH$FILL(0, iosb_s_iosb, .iosb) END; ! Now apply all of the name defaulting/parsing/smoke/mirrors to finish ! off the LDDB. When IOGEN$COMPLETE_LDDB returns, the DPT_NAME will ! have been generated, the driver name will have been defaulted (if ! necessary), the unit number will have been removed from the device ! name (and converted to binary and stored in LDDB$L_UNIT if necessary). IF NOT .flags[flag_v_lddb] THEN IF NOT (status = iogen$complete_lddb(.lddb)) THEN RETURN .status; ! In case of manual device connect (which can only happen at boottime, ! see if port alloclass implied %IF NOT %VARIANT ! If not BOOTTIME? %THEN IF (.lddb[lddb$ps_ddb_name] NEQA 0) AND (.function[iogen$w_fcode] EQL iogen$_connect) THEN IF NOT .lddb[lddb$v_pac] AND ( CH$EQL(2, .lddb[lddb$ps_ddb_name], 2, UPLIT( %ASCII 'DK' ), 0) OR CH$EQL(2, .lddb[lddb$ps_ddb_name], 2, UPLIT( %ASCII 'MK' ), 0) ) THEN ! No port allocation class already, and device DK or MK? BEGIN ! Right, see if implied port allocation class EXTERNAL ROUTINE iogen$class_match; LOCAL port: vector[8, byte] alias, arglst: vector[4, long]; CH$COPY(.lddb[lddb$il_ddb_namelen], .lddb[lddb$ps_ddb_name], 0, .lddb[lddb$il_ddb_namelen]+1, port); ! Create ASCIZ of device name port[0] = %C'P'; ! Convert to port name (PKc) ! Call iogen$class_match to see if port allocation class implied. ! Only the runtime version gets created here (x-19) ! Also don't do this if it's a dvc alloc class IF .alloflgs LSS .jnkmsk THEN BEGIN arglst[0] = 2; arglst[1] = port; arglst[2] = lddb[lddb$l_allocls]; $cmkrnl(routin= iogen$class_match, arglst= arglst); IF .lddb[lddb$l_allocls] GEQ 0 THEN lddb[lddb$v_pac] = 1; END; END; %FI; ! The setup work is now pretty much complete. The final setup requires ! searching the I/O database. From here on in, we must be the only thread ! of execution in the system attempting to load a driver or connect a unit. ! Additionally, we must lock everything into the working set so that we can ! change IPL with impunity. %IF NOT %VARIANT %THEN data_lock[0] = data_lock[1] = linkage_lock[0] = linkage_lock[1] = code_lock[0] = code_lock[1] = 0; IF NOT (status = $lkwset(inadr = data_range, retadr = data_lock)) THEN RETURN .status; IF NOT (status = $lkwset(inadr = linkage_range, retadr = linkage_lock)) THEN RETURN release_everything(.status); IF NOT (status = $lkwset(inadr = code_range, retadr = code_lock)) THEN RETURN release_everything(.status); IF NOT (status = iogen$lock()) THEN RETURN release_everything(.status); %FI ! Now look in the I/O database for the requested driver and any preexisting ! database structures. IF NOT (status = %IF %VARIANT %THEN iogen$search(.lddb) %ELSE BEGIN LOCAL search_args : VECTOR[2,LONG,SIGNED]; search_args[0] = 1; ! 1 argument search_args[1] = .lddb; ! namely, the LDDB $cmkrnl(routin = iogen$search, arglst = search_args) END %FI ) THEN RETURN release_everything(.status); ! Now, it's time to do some consistency checks. Verify that: ! ! 1) either NOADAPTER or a valid ADAPTER was specified or this call ! is adding a unit to an existing controller; ! 2) if an ADAPTER was specified: ! a) a CRB exists or a CSR was specified; ! b) a CRB exists or a vector was specified; ! 3) if the function code is IOGEN$_DELIVER, a unit number was specified. IF (NOT .lddb[lddb$v_noadap]) AND (.function[iogen$w_fcode] EQL iogen$_connect) THEN IF ((.lddb[lddb$ps_adp] EQLA 0) AND (.lddb[lddb$ps_crb] EQLA 0)) OR ( (.lddb[lddb$ps_crb] EQLA 0) AND NOT (.flags[flag_v_csr] AND .flags[flag_v_vector]) ) THEN RETURN release_everything(ss$_badparam); IF (.function[iogen$w_fcode] EQL iogen$_deliver) AND NOT .flags[flag_v_unit] THEN RETURN release_everything(ss$_badparam); ! Now decipher the function code and dispatch to the appropriate routines. ! N.B. From this point on, we must fill in the IOSB before returning to the ! caller. The variable "status" must be set to the appropriate completion ! status during the processing of each function as it is used in filling in ! the IOSB. CASE .function[iogen$w_fcode] FROM iogen$k_minfcode TO iogen$k_maxfcode OF SET [iogen$_load]: ! If a DPT was found during the search, the driver has already been ! loaded. Return a nice message IF .lddb[lddb$ps_dpt] NEQA 0 THEN status = ss$_drvexists ELSE status = %IF %VARIANT %THEN iogen$load(.lddb) %ELSE BEGIN LOCAL load_args : VECTOR[2,LONG,SIGNED]; load_args[0] = 1; ! 1 argument load_args[1] = .lddb; ! namely, the LDDB $cmexec(routin = iogen$load, arglst = load_args) END %FI ; [iogen$_reload]: ! If no DPT was found during the search, treat this as a _LOAD. ! If one was found, check the DPT to see if it allows unloading. IF .lddb[lddb$ps_dpt] EQLA 0 THEN status = %IF %VARIANT %THEN iogen$load(.lddb) %ELSE BEGIN LOCAL load_args : VECTOR[2,LONG,SIGNED]; load_args[0] = 1; ! 1 argument load_args[1] = .lddb; ! namely, the LDDB $cmexec(routin = iogen$load, arglst = load_args) END %FI ELSE BEGIN BIND dpt = .lddb[lddb$ps_dpt] : BLOCK[,BYTE]; IF .dpt[dpt$v_nounload] ! if the driver is not unloadable THEN status = ss$_drv_nounload ! say so ELSE ! The exec loader does not currently support unloading. ! Until it does, actual reloads are not supported. ! NOTE: Don't forget to do IOGEN$INIT_DB after the reload. status = ss$_unsupported END; [iogen$_connect]: BEGIN LOCAL units_created : INITIAL(0); ! If no DPT was found during the search, first perform a _LOAD. IF .lddb[lddb$ps_dpt] EQLA 0 THEN status = %IF %VARIANT %THEN iogen$load(.lddb) %ELSE BEGIN LOCAL load_args : VECTOR[2,LONG,SIGNED]; load_args[0] = 1; ! 1 argument load_args[1] = .lddb; ! namely, the LDDB $cmexec(routin = iogen$load, arglst = load_args) END %FI ELSE status = ss$_normal; ! for the following check ! Pick up MAXUNITS from DPT if necessary and verify that the ! number of units requested is within limits. IF .status AND NOT .flags[flag_v_maxunits] THEN lddb[lddb$l_maxunits] = .lddb[lddb$il_dpt_maxunits]; IF .status AND (.lddb[lddb$l_numunits] GTR .lddb[lddb$l_maxunits]) THEN status = ss$_badparam; ! Do the connects WHILE (.lddb[lddb$l_numunits] GTR 0) AND .status DO BEGIN ! Now, create and connect the I/O database IF .status THEN status = %IF %VARIANT %THEN iogen$connect(.lddb) %ELSE BEGIN LOCAL connect_args : VECTOR[2,LONG,SIGNED]; connect_args[0] = 1; ! 1 argument connect_args[1] = .lddb; ! the LDDB $cmkrnl(routin = iogen$connect, arglst = connect_args) END %FI ; ! If this is EXEC_INIT, check to see if this is the system ! device and set up the pointers in the base image if so. %IF %VARIANT %THEN IF .status AND .sysdevice_ok AND .function[iogen$v_sysdevice] THEN BEGIN LOCAL ucb : REF BLOCK[,BYTE]; sys$ar_bootddb = .lddb[lddb$ps_ddb]; ucb = sys$ar_bootucb = exe$gl_sysucb = .lddb[lddb$ps_ucb]; ucb[ucb$v_no_assign] = 0; sys$ar_bootorb = .ucb[ucb$l_orb]; sysdevice_ok = false END; %FI ! Do mapping initialization tasks. IF .status AND (NOT .lddb[lddb$v_add_unit]) THEN status = %IF %VARIANT %THEN iogen$init_mapping(.lddb) %ELSE BEGIN LOCAL init_args : VECTOR[2,LONG,SIGNED]; init_args[0] = 1; ! 1 argument init_args[1] = .lddb; ! the LDDB $cmkrnl(routin = iogen$init_mapping, arglst = init_args) END %FI ; ! Do controller initialization tasks. IF .status AND (NOT .lddb[lddb$v_add_unit]) THEN status = %IF %VARIANT %THEN BEGIN IF NOT .function[iogen$v_noinit] THEN iogen$init_controller(.lddb) ELSE ss$_normal END %ELSE BEGIN LOCAL init_args : VECTOR[2,LONG,SIGNED]; init_args[0] = 1; ! 1 argument init_args[1] = .lddb; ! the LDDB $cmkrnl(routin = iogen$init_controller, arglst = init_args) END %FI ; ! Finally, do unit initialization if a UCB exists IF .status AND .lddb[lddb$ps_ucb] NEQA 0 THEN status = %IF %VARIANT %THEN BEGIN IF NOT .function[iogen$v_noinit] THEN iogen$init_unit(.lddb) ELSE ss$_normal END %ELSE BEGIN LOCAL init_args : VECTOR[2,LONG,SIGNED]; init_args[0] = 1; ! 1 argument init_args[1] = .lddb; ! the LDDB $cmkrnl(routin = iogen$init_unit, arglst = init_args) END %FI ; ! Now update the unit count, unit number and first unit flag. IF .lddb[lddb$v_ucb] THEN units_created = .units_created + 1; lddb[lddb$l_numunits] = .lddb[lddb$l_numunits] - 1; lddb[lddb$l_unit] = .lddb[lddb$l_unit] + 1; lddb[lddb$v_add_unit] = true; lddb[lddb$v_ddb] = lddb[lddb$v_crb] = lddb[lddb$v_idb] = lddb[lddb$v_ucb] = 0; lddb[lddb$ps_lastucb] = .lddb[lddb$ps_ucb]; IF .lddb[lddb$l_numunits] GTR 0 THEN lddb[lddb$ps_ucb] = 0; END; lddb[lddb$l_numunits] = .units_created END; [iogen$_init_ctrl]: IF .lddb[lddb$ps_crb] EQLA 0 THEN status = ss$_nosuchdev ELSE status = %IF %VARIANT %THEN iogen$init_controller(.lddb) %ELSE BEGIN LOCAL init_args : VECTOR[2,LONG,SIGNED]; init_args[0] = 1; ! 1 argument init_args[1] = .lddb; ! the LDDB $cmkrnl(routin = iogen$init_controller, arglst = init_args) END %FI ; [iogen$_init_unit]: IF .lddb[lddb$ps_ucb] EQLA 0 THEN status = ss$_nosuchdev ELSE status = %IF %VARIANT %THEN iogen$init_unit(.lddb) %ELSE BEGIN LOCAL init_args : VECTOR[2,LONG,SIGNED]; init_args[0] = 1; ! 1 argument init_args[1] = .lddb; ! the LDDB $cmkrnl(routin = iogen$init_unit, arglst = init_args) END %FI ; [iogen$_deliver]: status = %IF %VARIANT %THEN ss$_unsupported ! unit delivery not allowed from ! EXEC_INIT %ELSE BEGIN IF .lddb[lddb$ps_crb] EQLA 0 OR .lddb[lddb$ps_ddb] EQLA 0 THEN ss$_abort ELSE ioc$unit_deliver( ! call unit delivery .lddb[lddb$ps_crb], .lddb[lddb$ps_ddb], .lddb[lddb$l_unit], lddb[lddb$q_dlvr_data]) END %FI ; [OUTRANGE]: status = ss$_illiofunc; TES; ! If we just created a ddb with a port allocation class, take out the ! port allocation class lock (X-13), but only in the runtime version, ! SYSINIT will take out the class locks for the boot device(s) when it's ! good and ready (i.e., when the cluster, if any, is running). %IF NOT %VARIANT %THEN IF .lddb[lddb$v_pac] AND (.lddb[lddb$ps_ddb] NEQA 0) THEN iogen$class_lock(.lddb[lddb$ps_ddb]); %FI ! Release the synchronization lock and unlock ourselves from the working ! set. Since the value of RELEASE_EVERYTHING won't be needed, just use ! SS$_NORMAL as the "saved status." release_everything(ss$_normal); ! Rescan the item list and return data for the output parameters IF .flags[flag_v_itmlst] THEN BEGIN items = .itmlst; WHILE .items[item_l_end] NEQ 0 DO BEGIN CASE .items[item_w_code] FROM iogen$k_minitem TO iogen$k_maxitem OF SET [iogen$_numunits]: BEGIN BIND numunits = .items[item_l_addr] : UNSIGNED WORD; numunits = .lddb[lddb$l_numunits] END; [iogen$_deliver_data]: BEGIN BIND deliver_data = .items[item_l_addr] : VECTOR[,BYTE]; CH$MOVE(.items[item_w_size], lddb[lddb$q_dlvr_data], deliver_data) END; [iogen$_ddb]: BEGIN BIND ddb = .items[item_l_addr] : SIGNED LONG; ddb = .lddb[lddb$ps_ddb] END; [iogen$_crb]: BEGIN BIND crb = .items[item_l_addr] : SIGNED LONG; crb = .lddb[lddb$ps_crb] END; [iogen$_idb]: BEGIN BIND idb = .items[item_l_addr] : SIGNED LONG; idb = .lddb[lddb$ps_idb] END; [iogen$_ucb]: BEGIN BIND ucb = .items[item_l_addr] : SIGNED LONG; IF .lddb[lddb$ps_ucb] NEQA 0 THEN ucb = .lddb[lddb$ps_ucb] ELSE ucb = .lddb[lddb$ps_lastucb] END; [iogen$_sb]: BEGIN BIND sb = .items[item_l_addr] : SIGNED LONG; sb = .lddb[lddb$ps_sb] END; [INRANGE, OUTRANGE]: ; TES; items = .items + item_s_item END END; ! Now, fill in the IOSB (if the caller provided one) and return to the ! caller. IF .flags[flag_v_iosb] THEN BEGIN iosb[iosb_w_status] = .status; iosb[iosb_w_flags] = .lddb[lddb$l_flags]; iosb[iosb_l_dpt] = .lddb[lddb$ps_dpt] END; RETURN .status END; %IF NOT %VARIANT %THEN %SBTTL 'RELEASE_EVERYTHING, unlock locked pages and release IOGEN$LOCK' ROUTINE release_everything(status) = !++ ! FUNCTIONAL DESCRIPTION: ! ! This routine unlocks each of the 3 possible locked psects and causes the ! systemwide synchronization lock to be dequeued. It preserves status across ! its call by accepting a status as its input argument and returning that ! value as its own status ! ! FORMAL PARAMETERS: ! ! status - "saved status", the value to be returned as this routine's own ! ! IMPLICIT INPUT PARAMETERS: ! ! data_lock, linkage_lock, code_lock ! ! IMPLICIT OUTPUT PARAMETERS: ! ! None ! ! RETURN VALUE: ! ! whatever is passed in as the parameter ! ! SIDE EFFECTS: ! ! None ! !-- BEGIN iogen$unlock(); IF .data_lock[0] NEQA 0 THEN $ulwset(inadr = data_lock); IF .linkage_lock[0] NEQA 0 THEN $ulwset(inadr = linkage_lock); IF .code_lock[0] NEQA 0 THEN $ulwset(inadr = code_lock); RETURN .status END; %ELSE %SBTTL 'RELEASE_EVERYTHING, EXEC_INIT version (nop)' ROUTINE release_everything(status) = !++ ! FUNCTIONAL DESCRIPTION: ! ! This is the EXEC_INIT version of RELEASE_EVERYTHING. It simply returns its ! parameter as a status. There's nothing else to do. ! ! FORMAL PARAMETERS: ! ! status - the value to be returned as this routine's value ! ! IMPLICIT INPUT PARAMETERS: ! ! None ! ! IMPLICIT OUTPUT PARAMETERS: ! ! None ! ! RETURN VALUE: ! ! status, as above ! ! SIDE EFFECTS: ! ! None ! !-- .status; %FI %SBTTL 'IOGEN$COMPLETE_LDDB, finish name parsing/processing in LDDB' GLOBAL ROUTINE iogen$complete_lddb (lddb : REF BLOCK[,BYTE]) = !++ ! FUNCTIONAL DESCRIPTION: ! ! This routine: ! ! 1) parses out the device name to place in the DDB ! 2) converts the unit number to binary, if necessary ! 3) generates a default driver name, if necessary ! 4) determines the DPT name (search key) ! ! FORMAL PARAMETERS: ! ! lddb - address of the LDDB ! ! IMPLICIT INPUTS: ! ! None ! ! IMPLICIT OUTPUTS: ! ! LDDB$T_DRVNAM ! LDDB$L_UNIT ! LDDB$IL_DEVLEN ! LDDB$PS_SEARCHNAME ! LDDB$IL_DDB_NAMELEN ! LDDB$PS_DDB_NAME ! ! ROUTINE VALUE: ! ! None ! ! SIDE EFFECTS: ! ! None !-- BEGIN LOCAL p, q, ! character pointers c, ! current character i, ! number of remaining characters in string unit, ! converted unit number p_flags, ! flags for IOC$PARSDEVNAM nodelen, ! nodename length/allocation class len, ! length of device name addr, ! address of device name status; lddb[lddb$ps_searchname] = lddb[lddb$t_devnam]; ! There may be a trailing colon on the device name. IOC$PARSDEVNAM does ! not like this. Therefore, it must be discarded before beginning the ! other processing. IF .lddb[lddb$il_devlen] NEQ 0 THEN BEGIN p = CH$FIND_CH(.lddb[lddb$il_devlen], lddb[lddb$t_devnam], ':'); IF NOT CH$FAIL(.p) THEN lddb[lddb$il_devlen] = CH$DIFF(.p, lddb[lddb$t_devnam]); len = .lddb[lddb$il_devlen]; addr = lddb[lddb$t_devnam]; IF NOT (status = ioc$parsdevnam(.len, .addr, 0; unit, nodelen, len, addr, p_flags)) THEN RETURN .status; IF (.p_flags AND ioc$m_class) NEQ 0 ! if an allocation class was found THEN ! len/addr describe just device name RETURN ss$_ivdevnam ! but this is not a unique device name per ROW ELSE ! len/addr describe node and device IF .nodelen EQL 0 THEN BEGIN lddb[lddb$il_ddb_namelen] = .len; lddb[lddb$ps_ddb_name] = .addr END ELSE BEGIN lddb[lddb$il_ddb_namelen] = .len - .nodelen - 1; lddb[lddb$ps_ddb_name] = CH$PLUS(lddb[lddb$t_devnam], .nodelen + 1) END; IF (NOT .flags[flag_v_unit]) ! if caller did NOT specify a unit item AND ((.p_flags AND ioc$m_phy) NEQ 0) ! but included it with the THEN ! devnam lddb[lddb$l_unit] = .unit; ! If no driver name was specified, generate a default driver ! name string. IF .lddb[lddb$ib_drvnamlen] EQL 0 THEN BEGIN p = CH$MOVE(2, .lddb[lddb$ps_ddb_name], lddb[lddb$t_drvnamstr]); p = CH$MOVE(%CHARCOUNT('DRIVER.EXE'), UPLIT(%ASCII 'DRIVER.EXE'), .p); lddb[lddb$ib_drvnamlen] = CH$DIFF(.p, lddb[lddb$t_drvnamstr]) END END ELSE status = ss$_normal; ! Now generate the DPT name. It is basically the filename portion of the ! driver's filespec cut down to the appropriate size to fit into the DPT. q = CH$PTR(lddb[lddb$t_drvnamstr]); ! start at the beginning i = .lddb[lddb$ib_drvnamlen]; DO ! first remove nodenames, device names BEGIN ! and logical names p = CH$FIND_CH(.i, .q, ':'); IF NOT CH$FAIL(.p) THEN IF CH$A_RCHAR(p) EQL ':' ! is it a nodename? THEN BEGIN i = .i - CH$DIFF(.p, .q) - 1; q = CH$PLUS(.p, 1) END ELSE BEGIN i = .i - CH$DIFF(.p, .q); q = .p END END UNTIL CH$FAIL(.p); DO ! now remove directory specs BEGIN ! first try with square brackets p = CH$FIND_CH(.i, .q, ']'); IF NOT CH$FAIL(.p) THEN BEGIN i = .i - CH$DIFF(.p, .q) - 1; q = CH$PLUS(.p, 1) END END UNTIL CH$FAIL(.p); DO ! now try with angle brackets BEGIN p = CH$FIND_CH(.i, .q, '>'); IF NOT CH$FAIL(.p) THEN BEGIN i = .i - CH$DIFF(.p, .q) - 1; q = CH$PLUS(.p, 1) END END UNTIL CH$FAIL(.p); p = CH$FIND_CH(.i, .q, ';'); ! check for version number IF NOT CH$FAIL(.p) THEN i = CH$DIFF(.p, .q); p = CH$FIND_CH(.i, .q, '.'); ! finally, remove filetype IF NOT CH$FAIL(.p) THEN i = CH$DIFF(.p, .q); lddb[lddb$ib_dpt_namelen] = i = MIN(.i, DPT$S_NAME - 1); CH$MOVE(.i, .q, lddb[lddb$t_dpt_namestr]); RETURN .status; END; END ! End of module ELUDOM