summaryrefslogtreecommitdiffstats
path: root/OvmfPkg/VirtioFsDxe/FuseInit.c
blob: 590ef441ef3facef9fc1881246c669a5a7980a57 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/** @file
  FUSE_INIT wrapper for the Virtio Filesystem device.

  Copyright (C) 2020, Red Hat, Inc.

  SPDX-License-Identifier: BSD-2-Clause-Patent
**/

#include "VirtioFsDxe.h"

/**
  Send a FUSE_INIT request to the Virtio Filesystem device, for starting the
  FUSE session.

  From virtio-v1.1-cs01-87fa6b5d8155, 5.11.5 Device Initialization: "On
  initialization the driver first discovers the device's virtqueues. The FUSE
  session is started by sending a FUSE_INIT request as defined by the FUSE
  protocol on one request virtqueue."

  The function may only be called after VirtioFsInit() returns successfully and
  before VirtioFsUninit() is called.

  @param[in,out] VirtioFs  The Virtio Filesystem device to send the FUSE_INIT
                           request to. The FUSE request counter
                           "VirtioFs->RequestId" is set to 1 on output. The
                           maximum write buffer size exposed in the FUSE_INIT
                           response is saved in "VirtioFs->MaxWrite", on
                           output.

  @retval EFI_SUCCESS      The FUSE session has been started.

  @retval EFI_UNSUPPORTED  FUSE interface version or feature negotiation
                           failed.

  @return                  The "errno" value mapped to an EFI_STATUS code, if
                           the Virtio Filesystem device explicitly reported an
                           error.

  @return                  Error codes propagated from
                           VirtioFsSgListsValidate(), VirtioFsFuseNewRequest(),
                           VirtioFsSgListsSubmit(),
                           VirtioFsFuseCheckResponse().
**/
EFI_STATUS
VirtioFsFuseInitSession (
  IN OUT VIRTIO_FS  *VirtioFs
  )
{
  VIRTIO_FS_FUSE_REQUEST         CommonReq;
  VIRTIO_FS_FUSE_INIT_REQUEST    InitReq;
  VIRTIO_FS_IO_VECTOR            ReqIoVec[2];
  VIRTIO_FS_SCATTER_GATHER_LIST  ReqSgList;
  VIRTIO_FS_FUSE_RESPONSE        CommonResp;
  VIRTIO_FS_FUSE_INIT_RESPONSE   InitResp;
  VIRTIO_FS_IO_VECTOR            RespIoVec[2];
  VIRTIO_FS_SCATTER_GATHER_LIST  RespSgList;
  EFI_STATUS                     Status;

  //
  // Initialize the FUSE request counter.
  //
  VirtioFs->RequestId = 1;

  //
  // Set up the scatter-gather lists.
  //
  ReqIoVec[0].Buffer = &CommonReq;
  ReqIoVec[0].Size   = sizeof CommonReq;
  ReqIoVec[1].Buffer = &InitReq;
  ReqIoVec[1].Size   = sizeof InitReq;
  ReqSgList.IoVec    = ReqIoVec;
  ReqSgList.NumVec   = ARRAY_SIZE (ReqIoVec);

  RespIoVec[0].Buffer = &CommonResp;
  RespIoVec[0].Size   = sizeof CommonResp;
  RespIoVec[1].Buffer = &InitResp;
  RespIoVec[1].Size   = sizeof InitResp;
  RespSgList.IoVec    = RespIoVec;
  RespSgList.NumVec   = ARRAY_SIZE (RespIoVec);

  //
  // Validate the scatter-gather lists; calculate the total transfer sizes.
  //
  Status = VirtioFsSgListsValidate (VirtioFs, &ReqSgList, &RespSgList);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Populate the common request header.
  //
  Status = VirtioFsFuseNewRequest (
             VirtioFs,
             &CommonReq,
             ReqSgList.TotalSize,
             VirtioFsFuseOpInit,
             0
             );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Populate the FUSE_INIT-specific fields.
  //
  InitReq.Major        = VIRTIO_FS_FUSE_MAJOR;
  InitReq.Minor        = VIRTIO_FS_FUSE_MINOR;
  InitReq.MaxReadahead = 0;
  InitReq.Flags        = VIRTIO_FS_FUSE_INIT_REQ_F_DO_READDIRPLUS;

  //
  // Submit the request.
  //
  Status = VirtioFsSgListsSubmit (VirtioFs, &ReqSgList, &RespSgList);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Verify the response (all response buffers are fixed size).
  //
  Status = VirtioFsFuseCheckResponse (&RespSgList, CommonReq.Unique, NULL);
  if (EFI_ERROR (Status)) {
    if (Status == EFI_DEVICE_ERROR) {
      DEBUG ((
        DEBUG_ERROR,
        "%a: Label=\"%s\" Errno=%d\n",
        __func__,
        VirtioFs->Label,
        CommonResp.Error
        ));
      Status = VirtioFsErrnoToEfiStatus (CommonResp.Error);
    }

    return Status;
  }

  //
  // Check FUSE interface version / feature compatibility.
  //
  if ((InitResp.Major < InitReq.Major) ||
      ((InitResp.Major == InitReq.Major) && (InitResp.Minor < InitReq.Minor)) ||
      ((InitResp.Flags & VIRTIO_FS_FUSE_INIT_REQ_F_DO_READDIRPLUS) == 0) ||
      (InitResp.MaxWrite < SIZE_4KB))
  {
    return EFI_UNSUPPORTED;
  }

  //
  // Save the maximum write buffer size for FUSE_WRITE requests.
  //
  VirtioFs->MaxWrite = InitResp.MaxWrite;
  return EFI_SUCCESS;
}