Overview of z/OS Callable Services:
z/OS callable services are native services for high-level-languages that enable MVS services by issuing CALLs. Utilizing callable services is a rewarding feature allowing a program additional abilities beyond what is available on other platforms. This blog will explore using callable services in Go and provide examples that you can easily start modifying and implementing in your operations. This blog was inspired by the zOS Callable Services for Java developer blog written by Jakob Balhar and the discussed sample code provided below borrows from his example. Furthermore this blog also owes gratitude to CW for the time he spent diving into assembly language and safely using unsafe pointers so you don’t have to, by making his solutions available in the ibmruntimes/go-recordio module.

Overview of the z/OS Mainframe Demonstrating z/OS Native Services and Go Accessable Technology [By Bill O'Farrell]
2. Implementing Callable Services in Go:
You will find that most of the work you have to do to get access to callable services in Go has already been done for you. You don’t have to dive into assembly, or figure out 31 or 24 bit addressing, but this blog will walk you through some of it and reference solutions and documentation along the way. Follow along as this blogs uses the ibmruntimes/go-recordio RACF example to provide a guided tour.
Step 1: Define the Callable Service Interface:
The following is working code used for demonstrating callable services. It starts with a standard interface to define the service to be invoked by your Go application.
R_usermap is a service used for identification of RACF ID. It requires a format for its parameters which is handled by the list member inside the PLIST struct. list is an array with 15 fields and corresponds with the format documentation found here.
Look over the sample code as it will be used to demonstrate callable services, and make note of the portions that have already been covered.
package main
import (
"fmt"
"reflect"
"unsafe"
"github.com/ibmruntimes/go-recordio/v2/utils"
)
type PLIST struct {
list [15]uint32
Workarea [128]uint64
SafRcAlet int32
Rc int32
RacfRcAlet int32
RacfRc int32
RacfRsnAlet int32
RacfRsnRc int32
FuncAlet int32
Func int16
OptionWord int32
RacfUseridLen byte
RacfUserid [8]byte
CertLen byte
Cert [4096]byte
AppIdLen int16
AppId [246]byte
DistNameLen int16
DistName [246]byte
RegNameLen int16
RegName [255]byte
}
func main() {
// Set up PLIST
siz := (int((reflect.TypeOf((*PLIST)(nil)).Elem()).Size()))
plist31 := (*PLIST)(unsafe.Pointer(utils.Malloc31(siz)))
plist31.list[0] = uint32(0x7fffffff & uintptr(unsafe.Pointer(&plist31.Workarea)))
plist31.list[1] = uint32(0x7fffffff & uintptr(unsafe.Pointer(&plist31.SafRcAlet)))
plist31.list[2] = uint32(0x7fffffff & uintptr(unsafe.Pointer(&plist31.Rc)))
plist31.list[3] = uint32(0x7fffffff & uintptr(unsafe.Pointer(&plist31.RacfRcAlet)))
plist31.list[4] = uint32(0x7fffffff & uintptr(unsafe.Pointer(&plist31.RacfRc)))
plist31.list[5] = uint32(0x7fffffff & uintptr(unsafe.Pointer(&plist31.RacfRsnAlet)))
plist31.list[6] = uint32(0x7fffffff & uintptr(unsafe.Pointer(&plist31.RacfRsnRc)))
plist31.list[7] = uint32(0x7fffffff & uintptr(unsafe.Pointer(&plist31.FuncAlet)))
plist31.list[8] = uint32(0x7fffffff & uintptr(unsafe.Pointer(&plist31.Func)))
plist31.list[9] = uint32(0x7fffffff & uintptr(unsafe.Pointer(&plist31.OptionWord)))
plist31.list[10] = uint32(0x7fffffff & uintptr(unsafe.Pointer(&plist31.RacfUseridLen)))
plist31.list[11] = uint32(0x7fffffff & uintptr(unsafe.Pointer(&plist31.CertLen)))
plist31.list[12] = uint32(0x7fffffff & uintptr(unsafe.Pointer(&plist31.AppIdLen)))
plist31.list[13] = uint32(0x7fffffff & uintptr(unsafe.Pointer(&plist31.DistNameLen)))
plist31.list[14] = uint32(0x7fffffff & uintptr(unsafe.Pointer(&plist31.RegNameLen)))
plist31.list[14] |= uint32(0x80000000)
// LOAD IRRSIM64
mod := utils.LoadMod("IRRSIM00")
if uintptr(unsafe.Pointer(mod)) != 0 {
plist31.Func = 0x3
copy(plist31.RacfUserid[:], "IBMUSER ")
utils.AtoE(plist31.RacfUserid[:])
plist31.RacfUseridLen = 7 // length of userid
RC := mod.Call(uintptr(unsafe.Pointer(plist31)))
if RC == 0 {
fmt.Printf("SafRC %d RacfRc %d Reason %d\n", plist31.Rc, plist31.RacfRc, plist31.RacfRsnRc)
} else {
fmt.Printf("Call rc=0x%x\n", RC)
}
// FREE MODULE
mod.Free()
} else {
fmt.Printf("Failed to load IRRSIM00\n")
}
// FREE PARM STORAGE
utils.Free(unsafe.Pointer(plist31))
}
Looking over plist31.list[] it’s interesting that it holds the pointer to the app ID length (AppIdLen) but not the app ID itself (AppId). This is because in the PLIST struct AppIdLen comes first followed by AppId physically in memory, and this is how the callable services look for the app ID - relative to the memory holding the app ID length. The same concept applies for CertLen and Cert
This is how reference is made to the location in memory to the lengths of the respective values.
plist31.list[11] = uint32(0x7fffffff & uintptr(unsafe.Pointer(&plist31.CertLen)))
...
plist31.list[12] = uint32(0x7fffffff & uintptr(unsafe.Pointer(&plist31.AppIdLen)))
Recall how these values are placed in the PLIST struct relative to one another.
CertLen byte
Cert [4096]byte
...
AppIdLen int16
AppId [246]byte
The end of the plist31.list[] is marked by a leading 1 which it indicates the end of the array or 31 bit field.
plist31.list[14] |= uint32(0x80000000)
Step 2: Invoke the Callable Service
Now that all of the parameters are in order the LoadMod funciton is to load an AMODE-31 module IRRSIM00.
mod := utils.LoadMod("IRRSIM00")
LoadMod is ultimately a function that relies on assembly. It loads the named module and determines the Amode.
You will now need your user ID. As a filler, this examples uses the ID IBMUSER. There is a blank space at the end of the string out of the abundance of caution for 31 bit systems. However, the length of the userid is saved as 7, so we are passing enough information for the system to determine what the user ID is.
copy(plist31.RacfUserid[:], "IBMUSER ")
utils.AtoE(plist31.RacfUserid[:])
plist31.RacfUseridLen = 7 // length of userid
A callable services call is then made using mod.Call() and points to in the plist31 that was setup earlier. The call is also depends on assembly similar to LoadMod because it factors in the Amode that was determined by utilizeing Assembly.
RC := mod.Call(uintptr(unsafe.Pointer(plist31)))
Conclusion:
Having covered the basics of Callable services and the tools to do this yourself, you now have a good idea of how to access Callable Services with Go. You also have a reference point from which to call other callable services using LoadMod and Call. Furthermore, a starting point to exploring how to handle 24 and 31 bit calls is also clear. With all that cleared, experimentation is highly encouraged and so is contribution. If you find in your experimentation that you have useful contributions, please feel free to create an issue on ibmruntimes/go-recordio so we can accommodate your pull request.