]> git.proxmox.com Git - mirror_edk2.git/blobdiff - OvmfPkg/VirtioFsDxe/Helpers.c
OvmfPkg/VirtioFsDxe: submit the FUSE_INIT request to the device
[mirror_edk2.git] / OvmfPkg / VirtioFsDxe / Helpers.c
index c493f6068f7ecf7f06a3cd67de4124685fb53c36..739b0c63cfaa4d4991093ad65c1e6a1bef3e3f8f 100644 (file)
@@ -698,3 +698,420 @@ Unmap:
 \r
   return Status;\r
 }\r
+\r
+/**\r
+  Set up the fields of a new VIRTIO_FS_FUSE_REQUEST object.\r
+\r
+  The function may only be called after VirtioFsInit() returns successfully and\r
+  before VirtioFsUninit() is called.\r
+\r
+  @param[in,out] VirtioFs The Virtio Filesystem device that the request is\r
+                          being prepared for. The "VirtioFs->RequestId" field\r
+                          will be copied into "Request->Unique". On output (on\r
+                          successful return), "VirtioFs->RequestId" will be\r
+                          incremented.\r
+\r
+  @param[out] Request     The VIRTIO_FS_FUSE_REQUEST object whose fields are to\r
+                          be set.\r
+\r
+  @param[in] RequestSize  The total size of the request, including\r
+                          sizeof(VIRTIO_FS_FUSE_REQUEST).\r
+\r
+  @param[in] Opcode       The VIRTIO_FS_FUSE_OPCODE that identifies the command\r
+                          to send.\r
+\r
+  @param[in] NodeId       The inode number of the file that the request refers\r
+                          to. When Opcode is VirtioFsFuseOpInit, NodeId is\r
+                          ignored by the Virtio Filesystem device.\r
+\r
+  @retval EFI_INVALID_PARAMETER  RequestSize is smaller than\r
+                                 sizeof(VIRTIO_FS_FUSE_REQUEST).\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   "VirtioFs->RequestId" is MAX_UINT64, and can\r
+                                 be incremented no more.\r
+\r
+  @retval EFI_SUCCESS            Request has been populated,\r
+                                 "VirtioFs->RequestId" has been incremented.\r
+**/\r
+EFI_STATUS\r
+VirtioFsFuseNewRequest (\r
+  IN OUT VIRTIO_FS              *VirtioFs,\r
+     OUT VIRTIO_FS_FUSE_REQUEST *Request,\r
+  IN     UINT32                 RequestSize,\r
+  IN     VIRTIO_FS_FUSE_OPCODE  Opcode,\r
+  IN     UINT64                 NodeId\r
+  )\r
+{\r
+  if (RequestSize < sizeof *Request) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (VirtioFs->RequestId == MAX_UINT64) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Request->Len     = RequestSize;\r
+  Request->Opcode  = Opcode;\r
+  Request->Unique  = VirtioFs->RequestId++;\r
+  Request->NodeId  = NodeId;\r
+  Request->Uid     = 0;\r
+  Request->Gid     = 0;\r
+  Request->Pid     = 1;\r
+  Request->Padding = 0;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Check the common FUSE response format.\r
+\r
+  The first buffer in the response scatter-gather list is assumed a\r
+  VIRTIO_FS_FUSE_RESPONSE structure. Subsequent response buffers, if any, up to\r
+  and excluding the last one, are assumed fixed size. The last response buffer\r
+  may or may not be fixed size, as specified by the caller.\r
+\r
+  This function may only be called after VirtioFsSgListsSubmit() returns\r
+  successfully.\r
+\r
+  @param[in] ResponseSgList   The scatter-gather list that describes the\r
+                              response part of the exchange -- the buffers that\r
+                              the Virtio Filesystem device filled in during the\r
+                              virtio transfer.\r
+\r
+  @param[in] RequestId        The request identifier to which the response is\r
+                              expected to belong.\r
+\r
+  @param[out] TailBufferFill  If NULL, then the last buffer in ResponseSgList\r
+                              is considered fixed size. Otherwise, the last\r
+                              buffer is considered variable size, and on\r
+                              successful return, TailBufferFill reports the\r
+                              number of bytes in the last buffer.\r
+\r
+  @retval EFI_INVALID_PARAMETER  TailBufferFill is not NULL (i.e., the last\r
+                                 buffer is considered variable size), and\r
+                                 ResponseSgList->NumVec is 1.\r
+\r
+  @retval EFI_INVALID_PARAMETER  The allocated size of the first buffer does\r
+                                 not match sizeof(VIRTIO_FS_FUSE_RESPONSE).\r
+\r
+  @retval EFI_PROTOCOL_ERROR     The VIRTIO_FS_FUSE_RESPONSE structure in the\r
+                                 first buffer has not been fully populated.\r
+\r
+  @retval EFI_PROTOCOL_ERROR     "VIRTIO_FS_FUSE_RESPONSE.Len" in the first\r
+                                 buffer does not equal the sum of the\r
+                                 individual buffer sizes (as populated).\r
+\r
+  @retval EFI_PROTOCOL_ERROR     "VIRTIO_FS_FUSE_RESPONSE.Unique" in the first\r
+                                 buffer does not equal RequestId.\r
+\r
+  @retval EFI_PROTOCOL_ERROR     "VIRTIO_FS_FUSE_RESPONSE.Error" in the first\r
+                                 buffer is zero, but a subsequent fixed size\r
+                                 buffer has not been fully populated.\r
+\r
+  @retval EFI_DEVICE_ERROR       "VIRTIO_FS_FUSE_RESPONSE.Error" in the first\r
+                                 buffer is nonzero. The caller may investigate\r
+                                 "VIRTIO_FS_FUSE_RESPONSE.Error". Note that the\r
+                                 completeness of the subsequent fixed size\r
+                                 buffers is not verified in this case.\r
+\r
+  @retval EFI_SUCCESS            Verification successful.\r
+**/\r
+EFI_STATUS\r
+VirtioFsFuseCheckResponse (\r
+  IN  VIRTIO_FS_SCATTER_GATHER_LIST *ResponseSgList,\r
+  IN  UINT64                        RequestId,\r
+  OUT UINTN                         *TailBufferFill\r
+  )\r
+{\r
+  UINTN                   NumFixedSizeVec;\r
+  VIRTIO_FS_FUSE_RESPONSE *CommonResp;\r
+  UINT32                  TotalTransferred;\r
+  UINTN                   Idx;\r
+\r
+  //\r
+  // Ensured by VirtioFsSgListsValidate().\r
+  //\r
+  ASSERT (ResponseSgList->NumVec > 0);\r
+\r
+  if (TailBufferFill == NULL) {\r
+    //\r
+    // All buffers are considered fixed size.\r
+    //\r
+    NumFixedSizeVec = ResponseSgList->NumVec;\r
+  } else {\r
+    //\r
+    // If the last buffer is variable size, then we need that buffer to be\r
+    // different from the first buffer, which is considered a\r
+    // VIRTIO_FS_FUSE_RESPONSE (fixed size) structure.\r
+    //\r
+    if (ResponseSgList->NumVec == 1) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+    NumFixedSizeVec = ResponseSgList->NumVec - 1;\r
+  }\r
+\r
+  //\r
+  // The first buffer is supposed to carry a (fully populated)\r
+  // VIRTIO_FS_FUSE_RESPONSE structure.\r
+  //\r
+  if (ResponseSgList->IoVec[0].Size != sizeof *CommonResp) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  if (ResponseSgList->IoVec[0].Transferred != ResponseSgList->IoVec[0].Size) {\r
+    return EFI_PROTOCOL_ERROR;\r
+  }\r
+\r
+  //\r
+  // FUSE must report the same number of bytes, written by the Virtio\r
+  // Filesystem device, as the virtio transport does.\r
+  //\r
+  CommonResp = ResponseSgList->IoVec[0].Buffer;\r
+  TotalTransferred = 0;\r
+  for (Idx = 0; Idx < ResponseSgList->NumVec; Idx++) {\r
+    //\r
+    // Integer overflow and truncation are not possible, based on\r
+    // VirtioFsSgListsValidate() and VirtioFsSgListsSubmit().\r
+    //\r
+    TotalTransferred += (UINT32)ResponseSgList->IoVec[Idx].Transferred;\r
+  }\r
+  if (CommonResp->Len != TotalTransferred) {\r
+    return EFI_PROTOCOL_ERROR;\r
+  }\r
+\r
+  //\r
+  // Enforce that FUSE match our request ID in the response.\r
+  //\r
+  if (CommonResp->Unique != RequestId) {\r
+    return EFI_PROTOCOL_ERROR;\r
+  }\r
+\r
+  //\r
+  // If there is an explicit error report, skip checking the transfer\r
+  // counts for the rest of the fixed size buffers.\r
+  //\r
+  if (CommonResp->Error != 0) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  //\r
+  // There was no error reported, so we require that the Virtio Filesystem\r
+  // device populate all fixed size buffers. We checked this for the very first\r
+  // buffer above; let's check the rest (if any).\r
+  //\r
+  ASSERT (NumFixedSizeVec >= 1);\r
+  for (Idx = 1; Idx < NumFixedSizeVec; Idx++) {\r
+    if (ResponseSgList->IoVec[Idx].Transferred !=\r
+        ResponseSgList->IoVec[Idx].Size) {\r
+      return EFI_PROTOCOL_ERROR;\r
+    }\r
+  }\r
+\r
+  //\r
+  // If the last buffer is considered variable size, report its filled size.\r
+  //\r
+  if (TailBufferFill != NULL) {\r
+    *TailBufferFill = ResponseSgList->IoVec[NumFixedSizeVec].Transferred;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  An ad-hoc function for mapping FUSE (well, Linux) "errno" values to\r
+  EFI_STATUS.\r
+\r
+  @param[in] Errno  The "VIRTIO_FS_FUSE_RESPONSE.Error" value, returned by the\r
+                    Virtio Filesystem device. The value is expected to be\r
+                    negative.\r
+\r
+  @return                   An EFI_STATUS error code that's deemed a passable\r
+                            mapping for the Errno value.\r
+\r
+  @retval EFI_DEVICE_ERROR  Fallback EFI_STATUS code for unrecognized Errno\r
+                            values.\r
+**/\r
+EFI_STATUS\r
+VirtioFsErrnoToEfiStatus (\r
+  IN INT32 Errno\r
+  )\r
+{\r
+  switch (Errno) {\r
+  case   -1: // EPERM               Operation not permitted\r
+    return EFI_SECURITY_VIOLATION;\r
+\r
+  case   -2: // ENOENT              No such file or directory\r
+  case   -3: // ESRCH               No such process\r
+  case   -6: // ENXIO               No such device or address\r
+  case  -10: // ECHILD              No child processes\r
+  case  -19: // ENODEV              No such device\r
+  case  -49: // EUNATCH             Protocol driver not attached\r
+  case  -65: // ENOPKG              Package not installed\r
+  case  -79: // ELIBACC             Can not access a needed shared library\r
+  case -126: // ENOKEY              Required key not available\r
+    return EFI_NOT_FOUND;\r
+\r
+  case   -4: // EINTR               Interrupted system call\r
+  case  -11: // EAGAIN, EWOULDBLOCK Resource temporarily unavailable\r
+  case  -16: // EBUSY               Device or resource busy\r
+  case  -26: // ETXTBSY             Text file busy\r
+  case  -35: // EDEADLK, EDEADLOCK  Resource deadlock avoided\r
+  case  -39: // ENOTEMPTY           Directory not empty\r
+  case  -42: // ENOMSG              No message of desired type\r
+  case  -61: // ENODATA             No data available\r
+  case  -85: // ERESTART            Interrupted system call should be restarted\r
+    return EFI_NOT_READY;\r
+\r
+  case   -5: // EIO                 Input/output error\r
+  case  -45: // EL2NSYNC            Level 2 not synchronized\r
+  case  -46: // EL3HLT              Level 3 halted\r
+  case  -47: // EL3RST              Level 3 reset\r
+  case  -51: // EL2HLT              Level 2 halted\r
+  case -121: // EREMOTEIO           Remote I/O error\r
+  case -133: // EHWPOISON           Memory page has hardware error\r
+    return EFI_DEVICE_ERROR;\r
+\r
+  case   -7: // E2BIG               Argument list too long\r
+  case  -36: // ENAMETOOLONG        File name too long\r
+  case  -90: // EMSGSIZE            Message too long\r
+    return EFI_BAD_BUFFER_SIZE;\r
+\r
+  case   -8: // ENOEXEC             Exec format error\r
+  case  -15: // ENOTBLK             Block device required\r
+  case  -18: // EXDEV               Invalid cross-device link\r
+  case  -20: // ENOTDIR             Not a directory\r
+  case  -21: // EISDIR              Is a directory\r
+  case  -25: // ENOTTY              Inappropriate ioctl for device\r
+  case  -27: // EFBIG               File too large\r
+  case  -29: // ESPIPE              Illegal seek\r
+  case  -38: // ENOSYS              Function not implemented\r
+  case  -59: // EBFONT              Bad font file format\r
+  case  -60: // ENOSTR              Device not a stream\r
+  case  -83: // ELIBEXEC            Cannot exec a shared library directly\r
+  case  -88: // ENOTSOCK            Socket operation on non-socket\r
+  case  -91: // EPROTOTYPE          Protocol wrong type for socket\r
+  case  -92: // ENOPROTOOPT         Protocol not available\r
+  case  -93: // EPROTONOSUPPORT     Protocol not supported\r
+  case  -94: // ESOCKTNOSUPPORT     Socket type not supported\r
+  case  -95: // ENOTSUP, EOPNOTSUPP Operation not supported\r
+  case  -96: // EPFNOSUPPORT        Protocol family not supported\r
+  case  -97: // EAFNOSUPPORT        Address family not supported by protocol\r
+  case  -99: // EADDRNOTAVAIL       Cannot assign requested address\r
+  case -118: // ENOTNAM             Not a XENIX named type file\r
+  case -120: // EISNAM              Is a named type file\r
+  case -124: // EMEDIUMTYPE         Wrong medium type\r
+    return EFI_UNSUPPORTED;\r
+\r
+  case   -9: // EBADF               Bad file descriptor\r
+  case  -14: // EFAULT              Bad address\r
+  case  -44: // ECHRNG              Channel number out of range\r
+  case  -48: // ELNRNG              Link number out of range\r
+  case  -53: // EBADR               Invalid request descriptor\r
+  case  -56: // EBADRQC             Invalid request code\r
+  case  -57: // EBADSLT             Invalid slot\r
+  case  -76: // ENOTUNIQ            Name not unique on network\r
+  case  -84: // EILSEQ        Invalid or incomplete multibyte or wide character\r
+    return EFI_NO_MAPPING;\r
+\r
+  case  -12: // ENOMEM              Cannot allocate memory\r
+  case  -23: // ENFILE              Too many open files in system\r
+  case  -24: // EMFILE              Too many open files\r
+  case  -31: // EMLINK              Too many links\r
+  case  -37: // ENOLCK              No locks available\r
+  case  -40: // ELOOP               Too many levels of symbolic links\r
+  case  -50: // ENOCSI              No CSI structure available\r
+  case  -55: // ENOANO              No anode\r
+  case  -63: // ENOSR               Out of streams resources\r
+  case  -82: // ELIBMAX         Attempting to link in too many shared libraries\r
+  case  -87: // EUSERS              Too many users\r
+  case -105: // ENOBUFS             No buffer space available\r
+  case -109: // ETOOMANYREFS        Too many references: cannot splice\r
+  case -119: // ENAVAIL             No XENIX semaphores available\r
+  case -122: // EDQUOT              Disk quota exceeded\r
+    return EFI_OUT_OF_RESOURCES;\r
+\r
+  case  -13: // EACCES              Permission denied\r
+    return EFI_ACCESS_DENIED;\r
+\r
+  case  -17: // EEXIST              File exists\r
+  case  -98: // EADDRINUSE          Address already in use\r
+  case -106: // EISCONN             Transport endpoint is already connected\r
+  case -114: // EALREADY            Operation already in progress\r
+  case -115: // EINPROGRESS         Operation now in progress\r
+    return EFI_ALREADY_STARTED;\r
+\r
+  case  -22: // EINVAL              Invalid argument\r
+  case  -33: // EDOM                Numerical argument out of domain\r
+    return EFI_INVALID_PARAMETER;\r
+\r
+  case  -28: // ENOSPC              No space left on device\r
+  case  -54: // EXFULL              Exchange full\r
+    return EFI_VOLUME_FULL;\r
+\r
+  case  -30: // EROFS               Read-only file system\r
+    return EFI_WRITE_PROTECTED;\r
+\r
+  case  -32: // EPIPE               Broken pipe\r
+  case  -43: // EIDRM               Identifier removed\r
+  case  -67: // ENOLINK             Link has been severed\r
+  case  -68: // EADV                Advertise error\r
+  case  -69: // ESRMNT              Srmount error\r
+  case  -70: // ECOMM               Communication error on send\r
+  case  -73: // EDOTDOT             RFS specific error\r
+  case  -78: // EREMCHG             Remote address changed\r
+  case  -86: // ESTRPIPE            Streams pipe error\r
+  case -102: // ENETRESET           Network dropped connection on reset\r
+  case -103: // ECONNABORTED        Software caused connection abort\r
+  case -104: // ECONNRESET          Connection reset by peer\r
+  case -116: // ESTALE              Stale file handle\r
+  case -125: // ECANCELED           Operation canceled\r
+  case -128: // EKEYREVOKED         Key has been revoked\r
+  case -129: // EKEYREJECTED        Key was rejected by service\r
+  case -130: // EOWNERDEAD          Owner died\r
+  case -131: // ENOTRECOVERABLE     State not recoverable\r
+    return EFI_ABORTED;\r
+\r
+  case  -34: // ERANGE              Numerical result out of range\r
+  case  -75: // EOVERFLOW           Value too large for defined data type\r
+    return EFI_BUFFER_TOO_SMALL;\r
+\r
+  case  -52: // EBADE               Invalid exchange\r
+  case -108: // ESHUTDOWN         Cannot send after transport endpoint shutdown\r
+  case -111: // ECONNREFUSED        Connection refused\r
+    return EFI_END_OF_FILE;\r
+\r
+  case  -62: // ETIME               Timer expired\r
+  case -110: // ETIMEDOUT           Connection timed out\r
+  case -127: // EKEYEXPIRED         Key has expired\r
+    return EFI_TIMEOUT;\r
+\r
+  case  -64: // ENONET              Machine is not on the network\r
+  case  -66: // EREMOTE             Object is remote\r
+  case  -72: // EMULTIHOP           Multihop attempted\r
+  case -100: // ENETDOWN            Network is down\r
+  case -101: // ENETUNREACH         Network is unreachable\r
+  case -112: // EHOSTDOWN           Host is down\r
+  case -113: // EHOSTUNREACH        No route to host\r
+  case -123: // ENOMEDIUM           No medium found\r
+  case -132: // ERFKILL             Operation not possible due to RF-kill\r
+    return EFI_NO_MEDIA;\r
+\r
+  case  -71: // EPROTO              Protocol error\r
+    return EFI_PROTOCOL_ERROR;\r
+\r
+  case  -74: // EBADMSG             Bad message\r
+  case  -77: // EBADFD              File descriptor in bad state\r
+  case  -80: // ELIBBAD             Accessing a corrupted shared library\r
+  case  -81: // ELIBSCN             .lib section in a.out corrupted\r
+  case -117: // EUCLEAN             Structure needs cleaning\r
+    return EFI_VOLUME_CORRUPTED;\r
+\r
+  case  -89: // EDESTADDRREQ        Destination address required\r
+  case -107: // ENOTCONN            Transport endpoint is not connected\r
+    return EFI_NOT_STARTED;\r
+\r
+  default:\r
+    break;\r
+  }\r
+\r
+  return EFI_DEVICE_ERROR;\r
+}\r