github.com/racerxdl/gonx@v0.0.0-20210103083128-c5afc43bcbd2/services/ipc/ipc.go (about)

     1  package ipc
     2  
     3  import (
     4  	"encoding/binary"
     5  	"fmt"
     6  	"github.com/racerxdl/gonx/internal"
     7  	"github.com/racerxdl/gonx/nx/nxerrors"
     8  	"github.com/racerxdl/gonx/nx/nxtypes"
     9  	"github.com/racerxdl/gonx/svc"
    10  	"unsafe"
    11  )
    12  
    13  // PackMessage is equivalent to libtransistor ipc_pack_message call
    14  func PackMessage(msg *PackedMessage, buffer *[0x40]uint32) error {
    15  	if debug {
    16  		println("ipc: pack ipc message")
    17  	}
    18  
    19  	aDescriptors := make([]*Buffer, 0, maxIPCDescriptors)
    20  	bDescriptors := make([]*Buffer, 0, maxIPCDescriptors)
    21  	cDescriptors := make([]*Buffer, 0, maxIPCDescriptors)
    22  	xDescriptors := make([]*Buffer, 0, maxIPCDescriptors)
    23  
    24  	if debug {
    25  		println("ipc: pack: prepare")
    26  	}
    27  	for _, ipcBuffer := range msg.Buffers {
    28  		if ipcBuffer.Type&0x20 == 0 {
    29  			if ipcBuffer.Direction() == DirectionInput { // AX
    30  				if ipcBuffer.Family() == FamilyA {
    31  					aDescriptors = append(aDescriptors, ipcBuffer)
    32  				} else if ipcBuffer.Family() == FamilyX { // X
    33  					xDescriptors = append(xDescriptors, ipcBuffer)
    34  				} else {
    35  					return nxerrors.UnsupportedBufferType
    36  				}
    37  			} else if ipcBuffer.Direction() == DirectionOutput { // BC
    38  				if ipcBuffer.Family() == FamilyB {
    39  					bDescriptors = append(bDescriptors, ipcBuffer)
    40  				} else if ipcBuffer.Family() == FamilyC { // C
    41  					cDescriptors = append(cDescriptors, ipcBuffer)
    42  				} else {
    43  					return nxerrors.UnsupportedBufferType
    44  				}
    45  			} else {
    46  				return nxerrors.UnsupportedBufferType
    47  			}
    48  		} else { // flag 0x20 set
    49  			if ipcBuffer.Type == 0x21 { // IN (ax)
    50  				aDescriptors = append(aDescriptors, ipcBuffer)
    51  				xDescriptors = append(xDescriptors, &nullBuffer)
    52  			} else if ipcBuffer.Type == 0x22 { // OUT (bc)
    53  				bDescriptors = append(bDescriptors, ipcBuffer)
    54  				cDescriptors = append(cDescriptors, &nullBuffer)
    55  			} else {
    56  				return nxerrors.UnsupportedBufferType
    57  			}
    58  		}
    59  
    60  		// Check for overflow
    61  		if len(aDescriptors) >= maxIPCDescriptors ||
    62  			len(bDescriptors) >= maxIPCDescriptors ||
    63  			len(cDescriptors) >= maxIPCDescriptors ||
    64  			len(xDescriptors) >= maxIPCDescriptors {
    65  			return nxerrors.TooManyBuffers
    66  		}
    67  	}
    68  
    69  	if debug {
    70  		println("ipc: pack: packing")
    71  	}
    72  	h := 0 // H for header count
    73  
    74  	buffer[h] = uint32(msg.Type) |
    75  		(uint32(len(xDescriptors)) << 16) |
    76  		(uint32(len(aDescriptors)) << 20) |
    77  		(uint32(len(bDescriptors)) << 24) |
    78  		(0 << 28) // "w" descriptors
    79  	h++
    80  
    81  	cDescriptorFlags := 0
    82  	if len(cDescriptors) == 1 {
    83  		cDescriptorFlags = 2
    84  	} else if len(cDescriptors) > 1 {
    85  		cDescriptorFlags = len(cDescriptors) + 2
    86  	}
    87  	handleDescriptorEnabled := len(msg.CopyHandles) > 0 || len(msg.MoveHandles) > 0 || msg.SendPID
    88  	sizeFieldOffset := h
    89  
    90  	handleDescriptorEnabledNum := 0
    91  	if handleDescriptorEnabled {
    92  		handleDescriptorEnabledNum = 1
    93  	}
    94  
    95  	// header field 2
    96  	buffer[h] = 0 | // Size to be filled
    97  		(uint32(cDescriptorFlags) << 10) |
    98  		(uint32(handleDescriptorEnabledNum) << 31)
    99  	h++
   100  
   101  	if handleDescriptorEnabled {
   102  		if len(msg.CopyHandles) >= maxIPCDescriptors || len(msg.MoveHandles) >= maxIPCDescriptors {
   103  			return nxerrors.TooManyHandles
   104  		}
   105  
   106  		sendPid := 0
   107  		if msg.SendPID {
   108  			sendPid = 1
   109  		}
   110  
   111  		buffer[h] = uint32(sendPid) |
   112  			(uint32(len(msg.CopyHandles)) << 1) |
   113  			(uint32(len(msg.MoveHandles)) << 5)
   114  		h++
   115  
   116  		if msg.SendPID {
   117  			h += 2
   118  		}
   119  
   120  		for _, v := range msg.CopyHandles {
   121  			buffer[h] = uint32(v)
   122  			h++
   123  		}
   124  		for _, v := range msg.MoveHandles {
   125  			buffer[h] = uint32(v)
   126  			h++
   127  		}
   128  	}
   129  
   130  	// X Descriptors
   131  	for i, x := range xDescriptors {
   132  		if x.Addr>>39 > 0 {
   133  			return nxerrors.InvalidBufferAddress
   134  		}
   135  
   136  		if x.Size>>16 > 0 {
   137  			return nxerrors.InvalidBufferSize
   138  		}
   139  
   140  		// This mess -> https://switchbrew.org/wiki/IPC_Marshalling#Buffer_descriptor_X_.22Pointer.22
   141  		addr64 := uint64(x.Addr)
   142  
   143  		buffer[h] = uint32(i)
   144  		buffer[h] |= uint32(((addr64 >> 36) & 7) << 6)
   145  		buffer[h] |= uint32(((i >> 9) & 7) << 9)
   146  		buffer[h] |= uint32(((addr64 >> 32) & 0xF) << 12)
   147  		buffer[h] |= uint32(x.Size) << 16
   148  		h++
   149  
   150  		buffer[h] = uint32(addr64 & 0xFFFFFFFF)
   151  		h++
   152  	}
   153  
   154  	// A descriptors
   155  	for _, a := range aDescriptors {
   156  		if a.Addr>>39 > 0 {
   157  			return nxerrors.InvalidBufferAddress
   158  		}
   159  
   160  		if a.Size>>35 > 0 {
   161  			return nxerrors.InvalidBufferSize
   162  		}
   163  
   164  		if a.Type>>8 > 4 {
   165  			return nxerrors.InvalidBufferFlags
   166  		}
   167  
   168  		buffer[h] = uint32(a.Size & 0xFFFFFFFF)
   169  		h++
   170  		buffer[h] = uint32(a.Addr & 0xFFFFFFFF)
   171  		h++
   172  
   173  		addr64 := uint64(a.Addr)
   174  
   175  		buffer[h] = uint32(a.Type) >> 6
   176  		buffer[h] |= uint32((addr64>>36)&0x7) << 2
   177  		buffer[h] |= uint32((a.Size>>32)&0xF) << 24
   178  		buffer[h] |= uint32((addr64>>32)&0xF) << 28
   179  
   180  		h++
   181  	}
   182  
   183  	// B Descriptors
   184  	for _, b := range bDescriptors {
   185  		if b.Addr>>39 > 0 {
   186  			return nxerrors.InvalidBufferAddress
   187  		}
   188  
   189  		if b.Size>>35 > 0 {
   190  			return nxerrors.InvalidBufferSize
   191  		}
   192  
   193  		if b.Type>>8 > 4 {
   194  			return nxerrors.InvalidBufferFlags
   195  		}
   196  
   197  		buffer[h] = uint32(b.Size & 0xFFFFFFFF)
   198  		h++
   199  		buffer[h] = uint32(b.Addr & 0xFFFFFFFF)
   200  		h++
   201  
   202  		addr64 := uint64(b.Addr)
   203  
   204  		buffer[h] = uint32(b.Type) >> 6
   205  		buffer[h] |= uint32((addr64>>36)&0x7) << 2
   206  		buffer[h] |= uint32((b.Size>>32)&0xF) << 24
   207  		buffer[h] |= uint32((addr64>>32)&0xF) << 28
   208  
   209  		h++
   210  	}
   211  
   212  	// "w" descriptors would go here
   213  
   214  	rawDataStart := h
   215  	h = int(uint32(h+3) & ^uint32(3))
   216  
   217  	prePadding := h - rawDataStart
   218  
   219  	if len(msg.DataSection) > 0 {
   220  		internal.Memcpy(unsafe.Pointer(&buffer[h]), unsafe.Pointer(&msg.DataSection[0]), uintptr(len(msg.DataSection)))
   221  		paddedSize := ipcPadSize(uint64(len(msg.DataSection)))
   222  		paddedSize /= 4
   223  		h += int(paddedSize)
   224  	}
   225  
   226  	h += 4 - prePadding
   227  
   228  	u16LengthList := make([]uint16, 0)
   229  
   230  	// c descriptor u16 length list
   231  	for _, buf := range cDescriptors {
   232  		if buf.Type&0x10 == 0 { // u16 length list flag
   233  			if buf.Size>>16 > 0 {
   234  				return nxerrors.InvalidBufferSize
   235  			}
   236  			u16LengthList = append(u16LengthList, uint16(buf.Size))
   237  		}
   238  	}
   239  
   240  	if len(u16LengthList) > 0 {
   241  		// Copy to IPC Buffer
   242  		internal.Memcpy(unsafe.Pointer(&buffer[h]), unsafe.Pointer(&u16LengthList[0]), uintptr(len(u16LengthList))*unsafe.Sizeof(uint16(0)))
   243  	}
   244  
   245  	// Move header to point to right position
   246  	h += (len(u16LengthList) + 1) >> 1
   247  
   248  	buffer[sizeFieldOffset] |= uint32(h - rawDataStart) // raw data section size
   249  
   250  	// C Descriptors
   251  	for _, c := range cDescriptors {
   252  		if c.Addr>>48 > 0 {
   253  			return nxerrors.InvalidBufferAddress
   254  		}
   255  
   256  		if c.Size>>16 > 0 {
   257  			return nxerrors.InvalidBufferSize
   258  		}
   259  
   260  		addr64 := uint64(c.Addr)
   261  		buffer[h] = uint32(addr64 & 0xFFFFFFFF)
   262  		h++
   263  		buffer[h] = uint32(addr64 >> 32)
   264  		buffer[h] |= uint32(c.Size) << 16
   265  		h++
   266  	}
   267  
   268  	return nil
   269  }
   270  
   271  // PackIPCRequest is equivalent to libtransistor ipc_pack_request call
   272  func PackIPCRequest(rq *Request, object Object, marshalBuffer *[0x40]uint32) error {
   273  	if debug {
   274  		println("ipc: pack ipc request")
   275  	}
   276  
   277  	msg := PackedMessage{}
   278  
   279  	toDomain := rq.Type == 4 && object.ObjectID >= 0
   280  	msg.Buffers = rq.Buffers
   281  
   282  	if uint32(rq.Type) & ^uint32(0xFFFF) > 0 {
   283  		return nxerrors.InvalidRequestType
   284  	}
   285  
   286  	msg.Type = rq.Type
   287  	moveHandles := make([]nxtypes.Handle, 0, maxIPCDescriptors)
   288  
   289  	if !toDomain {
   290  		for _, obj := range rq.Objects {
   291  			if obj.ObjectID >= 0 {
   292  				return nxerrors.CantSendDomainObjectToSession
   293  			}
   294  			moveHandles = append(moveHandles, nxtypes.Handle(obj.GetSession()))
   295  		}
   296  	}
   297  
   298  	for _, handle := range rq.MoveHandles {
   299  		moveHandles = append(moveHandles, handle)
   300  	}
   301  
   302  	if len(moveHandles) > maxIPCDescriptors {
   303  		return nxerrors.TooManyHandles
   304  	}
   305  
   306  	msg.CopyHandles = rq.CopyHandles
   307  	msg.SendPID = rq.SendPID
   308  
   309  	var buff [0x200 >> 2]uint32
   310  
   311  	h := 0
   312  	dataSectionLen := 0 // In bytes
   313  
   314  	if toDomain {
   315  		if len(rq.Objects) > 8 {
   316  			return nxerrors.TooManyObjects
   317  		}
   318  		v := uint32(1)
   319  		if rq.CloseObject {
   320  			v = 2
   321  		}
   322  		v |= uint32(len(rq.Objects) << 8)
   323  		buff[h] = v
   324  		h++
   325  		buff[h] = uint32(object.ObjectID)
   326  		h++
   327  
   328  		h += 2 // alignment
   329  
   330  		dataSectionLen += 0x10
   331  	}
   332  
   333  	payloadSize := 0
   334  
   335  	if !rq.CloseObject {
   336  		buff[h] = sfci
   337  		h++
   338  
   339  		buff[h] = 0
   340  		h++
   341  
   342  		buff[h] = rq.RequestID
   343  		h++
   344  
   345  		buff[h] = 0
   346  		h++
   347  
   348  		payloadSize += 0x10
   349  		dataSectionLen += 0x10
   350  
   351  		if len(rq.RawData) > 0x200 {
   352  			return nxerrors.InvalidRawDataSize
   353  		}
   354  		if len(rq.RawData) > 0 {
   355  			internal.Memcpy(unsafe.Pointer(&buff[dataSectionLen/4]), unsafe.Pointer(&rq.RawData[0]), uintptr(len(rq.RawData)))
   356  			payloadSize += len(rq.RawData)
   357  			dataSectionLen += len(rq.RawData)
   358  		}
   359  	} else {
   360  		if !toDomain {
   361  			return nxerrors.CantCloseSessionLikeDomainObjects
   362  		}
   363  
   364  		if rq.Type != 4 ||
   365  			len(rq.Buffers) != 0 ||
   366  			len(rq.RawData) != 0 ||
   367  			rq.SendPID != false ||
   368  			len(rq.CopyHandles) != 0 ||
   369  			len(rq.MoveHandles) != 0 ||
   370  			len(rq.Objects) != 0 {
   371  			return nxerrors.MalformedCloseRequest
   372  		}
   373  	}
   374  
   375  	if toDomain {
   376  		buff[0] |= uint32(payloadSize) << 16
   377  		for _, obj := range rq.Objects {
   378  			if obj.GetDomain() != object.GetDomain() {
   379  				return nxerrors.CantSendObjectAcrossDomains
   380  			}
   381  			internal.Memcpy(unsafe.Pointer(&buff[dataSectionLen/4]), unsafe.Pointer(&obj.ObjectID), 4)
   382  			dataSectionLen += 4
   383  		}
   384  	}
   385  
   386  	if dataSectionLen > 0 {
   387  		msg.DataSection = make([]byte, dataSectionLen)
   388  		internal.Memcpy(unsafe.Pointer(&msg.DataSection[0]), unsafe.Pointer(&buff[0]), uintptr(dataSectionLen))
   389  	}
   390  
   391  	return PackMessage(&msg, marshalBuffer)
   392  }
   393  
   394  // UnpackIPCMessage is equivalent to libtransistor ipc_unpack call
   395  func UnpackIPCMessage(msg *Message, buffer *[0x40]uint32) error {
   396  	if debug {
   397  		println("ipc: unpack ipc message")
   398  	}
   399  	h := 0 // HEAD position
   400  
   401  	header0 := buffer[h]
   402  	h++
   403  	header1 := buffer[h]
   404  	h++
   405  
   406  	msg.MessageType = uint16(header0 & 0xFFFF)
   407  
   408  	msg.NumXDescriptors = (header0 >> 16) & 0xF
   409  	msg.NumADescriptors = (header0 >> 20) & 0xF
   410  	msg.NumBDescriptors = (header0 >> 24) & 0xF
   411  	msg.NumWDescriptors = (header0 >> 28) & 0xF
   412  
   413  	msg.RawDataSectionSize = header1 & 0xFFFFF //  0b11 1111 1111
   414  
   415  	msg.CDescriptorFlags = (header1 >> 10) & 0xF
   416  	hasHandleDescriptor := (header1 >> 31) > 0
   417  
   418  	msg.NumCopyHandles = 0
   419  	msg.NumMoveHandles = 0
   420  	msg.CopyHandles = nil
   421  	msg.MoveHandles = nil
   422  	msg.HasPID = false
   423  	msg.PID = 0
   424  
   425  	if hasHandleDescriptor {
   426  		handleDescriptor := buffer[h]
   427  		h++
   428  
   429  		if handleDescriptor&1 > 0 {
   430  			msg.HasPID = true
   431  			msg.PID = *(*uint64)(unsafe.Pointer(&buffer[h]))
   432  			h += 2
   433  		}
   434  
   435  		msg.NumCopyHandles = (handleDescriptor >> 1) & 0xF
   436  		msg.NumMoveHandles = (handleDescriptor >> 5) & 0xF
   437  
   438  		if msg.NumCopyHandles > 0 {
   439  			msg.CopyHandles = buffer[h:]
   440  			h += int(msg.NumCopyHandles)
   441  		}
   442  
   443  		if msg.NumMoveHandles > 0 {
   444  			msg.MoveHandles = buffer[h:]
   445  			h += int(msg.NumMoveHandles)
   446  		}
   447  	}
   448  
   449  	// Descriptors
   450  
   451  	if msg.NumXDescriptors > 0 {
   452  		msg.XDescriptors = buffer[h:]
   453  		h += int(msg.NumXDescriptors * 2)
   454  	}
   455  
   456  	if msg.NumADescriptors > 0 {
   457  		msg.ADescriptors = buffer[h:]
   458  		h += int(msg.NumADescriptors * 3)
   459  	}
   460  	if msg.NumBDescriptors > 0 {
   461  		msg.BDescriptors = buffer[h:]
   462  		h += int(msg.NumBDescriptors * 3)
   463  	}
   464  
   465  	if msg.NumWDescriptors > 0 {
   466  		msg.WDescriptors = buffer[h:]
   467  		h += int(msg.NumWDescriptors * 3)
   468  	}
   469  
   470  	before := h
   471  
   472  	// Align head to 4 words
   473  	h = int(uint32(h+3) & ^uint32(3))
   474  
   475  	msg.PrePadding = h - before
   476  	msg.PostPadding = 4 - msg.PrePadding
   477  	if msg.RawDataSectionSize > 0 {
   478  		msg.DataSection = buffer[h:]
   479  	}
   480  
   481  	h = before + int(msg.RawDataSectionSize)
   482  
   483  	msg.CDescriptors = buffer[h:]
   484  
   485  	return nil
   486  }
   487  
   488  // UnflattenResponse is equivalent to libtransistor ipc_unflatten_response
   489  func UnflattenResponse(msg *Message, rs *ResponseFmt, object Object) error {
   490  	if debug {
   491  		println("ipc: unflatten ipc response")
   492  	}
   493  	fromDomain := object.ObjectID >= 0
   494  
   495  	if msg.MessageType != 0 && msg.MessageType != 4 {
   496  		return nxerrors.InvalidIPCResponseType
   497  	}
   498  
   499  	h := 0
   500  
   501  	if fromDomain {
   502  		h += 4 // skip domain header
   503  	}
   504  
   505  	if msg.DataSection[h] != sfco {
   506  		return nxerrors.InvalidIPCResponseMagic
   507  	}
   508  	h += 2
   509  
   510  	responseCode := msg.DataSection[h]
   511  	h++
   512  
   513  	if responseCode != nxtypes.ResultOK {
   514  		return nxerrors.IPCError{
   515  			Result:  uint64(responseCode),
   516  			Message: "response error",
   517  		}
   518  	}
   519  	h++
   520  
   521  	rawData := msg.DataSection[h:]
   522  
   523  	nObjs := int(0)
   524  
   525  	if fromDomain {
   526  		nObjs = 0x10 + len(rs.Objects)*4
   527  	}
   528  
   529  	// RawDataSectionLength - SFCI, Command ID - Padding - nObjs
   530  	if (int(msg.RawDataSectionSize*4) - 0x10 - 0x10 - nObjs) != int(ipcPadSize(uint64(len(rs.RawData)))) {
   531  		if debug {
   532  			v := ipcPadSize(uint64(len(rs.RawData)))
   533  			println("expected", int(msg.RawDataSectionSize*4)-0x10-0x10-nObjs, "got", v)
   534  			println("raw data section size", msg.RawDataSectionSize)
   535  		}
   536  		return nxerrors.UnexpectedRawDataSize
   537  	}
   538  
   539  	if msg.HasPID != rs.HasPID {
   540  		return nxerrors.UnexpectedPID
   541  	}
   542  
   543  	if int(msg.NumCopyHandles) != len(rs.CopyHandles) {
   544  		return nxerrors.UnexpectedCopyHandles
   545  	}
   546  
   547  	numObjs := 0
   548  	if !fromDomain {
   549  		numObjs = len(rs.Objects)
   550  	}
   551  
   552  	if int(msg.NumMoveHandles) != len(rs.MoveHandles)+numObjs {
   553  		return nxerrors.UnexpectedMoveHandles
   554  	}
   555  
   556  	if fromDomain {
   557  		type responseDomainHeader struct {
   558  			numObjects uint32
   559  			unknown1   [2]uint32
   560  			unknown2   uint32
   561  		}
   562  		ptr := unsafe.Pointer(&msg.DataSection[0])
   563  		domainHeader := (*responseDomainHeader)(ptr)
   564  
   565  		if int(domainHeader.numObjects) != len(rs.Objects) {
   566  			return nxerrors.UnexpectedObjects
   567  		}
   568  
   569  		// this is a pointer to a uint32_t array, but it is allowed to be unaligned
   570  		ptru := uintptr(ptr)
   571  		ptru += unsafe.Sizeof(responseDomainHeader{})
   572  		ptru += 0x10 // SFCO, result code
   573  		ptru += uintptr(len(rs.RawData))
   574  
   575  		domainIds := ptru
   576  
   577  		for i := range rs.Objects {
   578  			rs.Objects[i].Content = object.Content
   579  			internal.Memcpy(unsafe.Pointer(&rs.Objects[i].ObjectID), unsafe.Pointer(domainIds+uintptr(i*4)), 4)
   580  			rs.Objects[i].IsBorrowed = false
   581  		}
   582  	}
   583  
   584  	for i := range rs.CopyHandles {
   585  		rs.CopyHandles[i] = nxtypes.Handle(msg.CopyHandles[i])
   586  	}
   587  
   588  	mhi := 0 // move handle index
   589  
   590  	if !fromDomain {
   591  		for i := range rs.Objects {
   592  			rs.Objects[i].Content = uint64(msg.MoveHandles[mhi])
   593  			rs.Objects[i].ObjectID = -1
   594  			rs.Objects[i].IsBorrowed = false
   595  			mhi++
   596  		}
   597  	}
   598  
   599  	for i := range rs.MoveHandles {
   600  		rs.MoveHandles[i] = nxtypes.Handle(msg.MoveHandles[mhi])
   601  		mhi++
   602  	}
   603  
   604  	if rs.HasPID {
   605  		*rs.PID = msg.PID
   606  	}
   607  
   608  	if len(rs.RawData) > 0 {
   609  		internal.Memcpy(unsafe.Pointer(&rs.RawData[0]), unsafe.Pointer(&rawData[0]), uintptr(len(rs.RawData)))
   610  	}
   611  
   612  	return nil
   613  }
   614  
   615  func Send(object Object, rq *Request, rs *ResponseFmt) error {
   616  	svc.ClearIPCBuffer()
   617  	ipcBuff := svc.GetIPCBuffer()
   618  
   619  	err := PackIPCRequest(rq, object, ipcBuff)
   620  	if err != nil {
   621  		if debug {
   622  			println("ipc: packing error:", err.Error())
   623  		}
   624  		return err
   625  	}
   626  
   627  	if debug {
   628  		println("ipc: send sync request")
   629  	}
   630  
   631  	if debugDumpBeforeSend {
   632  		svc.DumpIPCBuffer()
   633  	}
   634  
   635  	r := svc.SendSyncRequest(object.Content)
   636  	if r > 0 {
   637  		if debug {
   638  			fmt.Printf("ipc: bad request with return code %x\n", r)
   639  			svc.DumpIPCBuffer()
   640  		}
   641  
   642  		return nxerrors.IPCError{
   643  			Result:  r,
   644  			Message: "bad request",
   645  		}
   646  	}
   647  
   648  	if debug {
   649  		println("ipc: processing response")
   650  	}
   651  
   652  	msg := Message{}
   653  
   654  	err = UnpackIPCMessage(&msg, ipcBuff)
   655  
   656  	if err != nil {
   657  		if debug {
   658  			println("ipc: unpacking error:", err.Error())
   659  			svc.DumpIPCBuffer()
   660  		}
   661  		return err
   662  	}
   663  
   664  	err = UnflattenResponse(&msg, rs, object)
   665  
   666  	if err != nil {
   667  		if debug {
   668  			println("ipc: unflatten error:", err.Error())
   669  		}
   670  		return err
   671  	}
   672  
   673  	return nil
   674  }
   675  
   676  func CloseSession(session nxtypes.SessionHandle) error {
   677  	var err error
   678  	if session == 0 {
   679  		return nil
   680  	}
   681  
   682  	rq := MakeDefaultRequest(0)
   683  	rq.Type = TypeIPCClose
   684  
   685  	obj := Object{
   686  		ObjectID: -1,
   687  	}
   688  
   689  	obj.SetSession(nxtypes.Handle(session))
   690  
   691  	svc.ClearIPCBuffer()
   692  	ipcBuff := svc.GetIPCBuffer()
   693  
   694  	err = PackIPCRequest(&rq, obj, ipcBuff)
   695  	if err != nil {
   696  		return err
   697  	}
   698  
   699  	if debug {
   700  		println("ipc: send sync request")
   701  	}
   702  
   703  	if debugDumpBeforeSend {
   704  		svc.DumpIPCBuffer()
   705  	}
   706  
   707  	r := svc.SendSyncRequest(uint64(session))
   708  	if r != 0xf601 {
   709  		if debug {
   710  			fmt.Printf("ipc: expected session closure, got %x\n", r)
   711  		}
   712  		err = nxerrors.IPCError{
   713  			Message: nxerrors.ExpectedSessionClosure.String(),
   714  			Result:  r,
   715  		}
   716  	}
   717  
   718  	if debug {
   719  		println("ipc: svc close handle")
   720  	}
   721  
   722  	svc.CloseHandle(nxtypes.Handle(session))
   723  
   724  	return err
   725  }
   726  
   727  func Close(object *Object) error {
   728  	if object.IsBorrowed {
   729  		return nil // we're not allowed to close borrowed objects,
   730  		// and we would also like to handle this transparently
   731  	}
   732  
   733  	if object.Content == 0 {
   734  		return nil // Already closed
   735  	}
   736  
   737  	if object.ObjectID < 0 {
   738  		return CloseSession(object.GetSession())
   739  	}
   740  
   741  	rq := MakeDefaultRequest(0)
   742  	rq.CloseObject = true
   743  
   744  	svc.ClearIPCBuffer()
   745  	ipcBuff := svc.GetIPCBuffer()
   746  
   747  	err := PackIPCRequest(&rq, *object, ipcBuff)
   748  	if err != nil {
   749  		return err
   750  	}
   751  
   752  	if debug {
   753  		println("ipc: send sync request")
   754  	}
   755  
   756  	if debugDumpBeforeSend {
   757  		svc.DumpIPCBuffer()
   758  	}
   759  
   760  	d := object.GetDomain()
   761  
   762  	if d == nil {
   763  		return nxerrors.InvalidDomain
   764  	}
   765  
   766  	if d.Session == 0 {
   767  		return nxerrors.InvalidDomain
   768  	}
   769  
   770  	r := svc.SendSyncRequest(uint64(d.Session))
   771  	if r > 0 {
   772  		if debug {
   773  			println("ipc: error sending request")
   774  			svc.DumpIPCBuffer()
   775  		}
   776  		return nxerrors.IPCError{
   777  			Result:  r,
   778  			Message: "error sending request",
   779  		}
   780  	}
   781  
   782  	object.Recycle()
   783  
   784  	return nil
   785  }
   786  
   787  func ConvertToDomain(object *Object) (*Domain, error) {
   788  	if object == nil {
   789  		return nil, nxerrors.InvalidHandle
   790  	}
   791  	if object.IsBorrowed {
   792  		return nil, nxerrors.RefusalToConvertBorrowedObject
   793  	}
   794  	if object.ObjectID != -1 {
   795  		return nil, nxerrors.AlreadyADomain
   796  	}
   797  
   798  	session := *object
   799  	domain := &Domain{
   800  		Session: session.GetSession(),
   801  	}
   802  
   803  	object.SetDomain(domain)
   804  
   805  	rq := MakeDefaultRequest(0)
   806  	rq.Type = TypeIPCControl
   807  
   808  	rs := ResponseFmt{}
   809  	rs.RawData = make([]byte, unsafe.Sizeof(object.ObjectID))
   810  
   811  	err := Send(session, &rq, &rs)
   812  	if err != nil {
   813  		return nil, err
   814  	}
   815  
   816  	object.ObjectID = int32(binary.LittleEndian.Uint32(rs.RawData))
   817  
   818  	return domain, nil
   819  }