1 | /** @file
|
---|
2 |
|
---|
3 | Driver for virtio-serial devices.
|
---|
4 |
|
---|
5 | Helper functions to manage virtio serial ports.
|
---|
6 | Console ports will be registered as SerialIo UARTs.
|
---|
7 |
|
---|
8 | SPDX-License-Identifier: BSD-2-Clause-Patent
|
---|
9 |
|
---|
10 | **/
|
---|
11 |
|
---|
12 | #include <Library/BaseMemoryLib.h>
|
---|
13 | #include <Library/DebugLib.h>
|
---|
14 | #include <Library/DevicePathLib.h>
|
---|
15 | #include <Library/MemoryAllocationLib.h>
|
---|
16 | #include <Library/PrintLib.h>
|
---|
17 | #include <Library/UefiBootServicesTableLib.h>
|
---|
18 | #include <Library/UefiLib.h>
|
---|
19 | #include <Library/VirtioLib.h>
|
---|
20 |
|
---|
21 | #include "VirtioSerial.h"
|
---|
22 |
|
---|
23 | ACPI_HID_DEVICE_PATH mAcpiSerialDevNode = {
|
---|
24 | {
|
---|
25 | ACPI_DEVICE_PATH,
|
---|
26 | ACPI_DP,
|
---|
27 | {
|
---|
28 | (UINT8)(sizeof (ACPI_HID_DEVICE_PATH)),
|
---|
29 | (UINT8)((sizeof (ACPI_HID_DEVICE_PATH)) >> 8)
|
---|
30 | },
|
---|
31 | },
|
---|
32 | EISA_PNP_ID (0x0501),
|
---|
33 | 0
|
---|
34 | };
|
---|
35 |
|
---|
36 | UART_DEVICE_PATH mUartDevNode = {
|
---|
37 | {
|
---|
38 | MESSAGING_DEVICE_PATH,
|
---|
39 | MSG_UART_DP,
|
---|
40 | {
|
---|
41 | (UINT8)(sizeof (UART_DEVICE_PATH)),
|
---|
42 | (UINT8)((sizeof (UART_DEVICE_PATH)) >> 8)
|
---|
43 | }
|
---|
44 | },
|
---|
45 | 0, // Reserved
|
---|
46 | 115200, // Speed
|
---|
47 | 8, 1, 1 // 8n1
|
---|
48 | };
|
---|
49 |
|
---|
50 | STATIC
|
---|
51 | UINT16
|
---|
52 | PortRx (
|
---|
53 | IN UINT32 PortId
|
---|
54 | )
|
---|
55 | {
|
---|
56 | ASSERT (PortId < MAX_PORTS);
|
---|
57 |
|
---|
58 | if (PortId >= 1) {
|
---|
59 | return (UINT16)(VIRTIO_SERIAL_Q_RX_BASE + (PortId - 1) * 2);
|
---|
60 | }
|
---|
61 |
|
---|
62 | return VIRTIO_SERIAL_Q_RX_PORT0;
|
---|
63 | }
|
---|
64 |
|
---|
65 | STATIC
|
---|
66 | UINT16
|
---|
67 | PortTx (
|
---|
68 | IN UINT32 PortId
|
---|
69 | )
|
---|
70 | {
|
---|
71 | ASSERT (PortId < MAX_PORTS);
|
---|
72 |
|
---|
73 | if (PortId >= 1) {
|
---|
74 | return (UINT16)(VIRTIO_SERIAL_Q_TX_BASE + (PortId - 1) * 2);
|
---|
75 | }
|
---|
76 |
|
---|
77 | return VIRTIO_SERIAL_Q_TX_PORT0;
|
---|
78 | }
|
---|
79 |
|
---|
80 | STATIC
|
---|
81 | EFI_STATUS
|
---|
82 | EFIAPI
|
---|
83 | VirtioSerialIoReset (
|
---|
84 | IN EFI_SERIAL_IO_PROTOCOL *This
|
---|
85 | )
|
---|
86 | {
|
---|
87 | DEBUG ((DEBUG_VERBOSE, "%a:%d:\n", __func__, __LINE__));
|
---|
88 | return EFI_SUCCESS;
|
---|
89 | }
|
---|
90 |
|
---|
91 | STATIC
|
---|
92 | EFI_STATUS
|
---|
93 | EFIAPI
|
---|
94 | VirtioSerialIoSetAttributes (
|
---|
95 | IN EFI_SERIAL_IO_PROTOCOL *This,
|
---|
96 | IN UINT64 BaudRate,
|
---|
97 | IN UINT32 ReceiveFifoDepth,
|
---|
98 | IN UINT32 Timeout,
|
---|
99 | IN EFI_PARITY_TYPE Parity,
|
---|
100 | IN UINT8 DataBits,
|
---|
101 | IN EFI_STOP_BITS_TYPE StopBits
|
---|
102 | )
|
---|
103 | {
|
---|
104 | DEBUG ((
|
---|
105 | DEBUG_VERBOSE,
|
---|
106 | "%a:%d: Rate %ld, Fifo %d, Bits %d\n",
|
---|
107 | __func__,
|
---|
108 | __LINE__,
|
---|
109 | BaudRate,
|
---|
110 | ReceiveFifoDepth,
|
---|
111 | DataBits
|
---|
112 | ));
|
---|
113 | return EFI_SUCCESS;
|
---|
114 | }
|
---|
115 |
|
---|
116 | STATIC
|
---|
117 | EFI_STATUS
|
---|
118 | EFIAPI
|
---|
119 | VirtioSerialIoSetControl (
|
---|
120 | IN EFI_SERIAL_IO_PROTOCOL *This,
|
---|
121 | IN UINT32 Control
|
---|
122 | )
|
---|
123 | {
|
---|
124 | DEBUG ((DEBUG_INFO, "%a:%d: Control 0x%x\n", __func__, __LINE__, Control));
|
---|
125 | return EFI_SUCCESS;
|
---|
126 | }
|
---|
127 |
|
---|
128 | STATIC
|
---|
129 | EFI_STATUS
|
---|
130 | EFIAPI
|
---|
131 | VirtioSerialIoGetControl (
|
---|
132 | IN EFI_SERIAL_IO_PROTOCOL *This,
|
---|
133 | OUT UINT32 *Control
|
---|
134 | )
|
---|
135 | {
|
---|
136 | DEBUG ((DEBUG_VERBOSE, "%a:%d: Control 0x%x\n", __func__, __LINE__, *Control));
|
---|
137 | return EFI_SUCCESS;
|
---|
138 | }
|
---|
139 |
|
---|
140 | STATIC
|
---|
141 | EFI_STATUS
|
---|
142 | EFIAPI
|
---|
143 | VirtioSerialIoWrite (
|
---|
144 | IN EFI_SERIAL_IO_PROTOCOL *This,
|
---|
145 | IN OUT UINTN *BufferSize,
|
---|
146 | IN VOID *Buffer
|
---|
147 | )
|
---|
148 | {
|
---|
149 | VIRTIO_SERIAL_IO_PROTOCOL *SerialIo = (VIRTIO_SERIAL_IO_PROTOCOL *)This;
|
---|
150 | VIRTIO_SERIAL_PORT *Port = SerialIo->Dev->Ports + SerialIo->PortId;
|
---|
151 | UINT32 Length;
|
---|
152 | EFI_TPL OldTpl;
|
---|
153 |
|
---|
154 | if (!Port->DeviceOpen) {
|
---|
155 | *BufferSize = 0;
|
---|
156 | return EFI_SUCCESS;
|
---|
157 | }
|
---|
158 |
|
---|
159 | VirtioSerialRingClearTx (SerialIo->Dev, PortTx (SerialIo->PortId));
|
---|
160 |
|
---|
161 | OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
|
---|
162 | if (SerialIo->WriteOffset &&
|
---|
163 | (SerialIo->WriteOffset + *BufferSize > PORT_TX_BUFSIZE))
|
---|
164 | {
|
---|
165 | DEBUG ((DEBUG_VERBOSE, "%a:%d: WriteFlush %d\n", __func__, __LINE__, SerialIo->WriteOffset));
|
---|
166 | VirtioSerialRingSendBuffer (
|
---|
167 | SerialIo->Dev,
|
---|
168 | PortTx (SerialIo->PortId),
|
---|
169 | SerialIo->WriteBuffer,
|
---|
170 | SerialIo->WriteOffset,
|
---|
171 | TRUE
|
---|
172 | );
|
---|
173 | SerialIo->WriteOffset = 0;
|
---|
174 | }
|
---|
175 |
|
---|
176 | Length = MIN ((UINT32)(*BufferSize), PORT_TX_BUFSIZE - SerialIo->WriteOffset);
|
---|
177 | CopyMem (SerialIo->WriteBuffer + SerialIo->WriteOffset, Buffer, Length);
|
---|
178 | SerialIo->WriteOffset += Length;
|
---|
179 | *BufferSize = Length;
|
---|
180 | gBS->RestoreTPL (OldTpl);
|
---|
181 |
|
---|
182 | return EFI_SUCCESS;
|
---|
183 | }
|
---|
184 |
|
---|
185 | STATIC
|
---|
186 | EFI_STATUS
|
---|
187 | EFIAPI
|
---|
188 | VirtioSerialIoRead (
|
---|
189 | IN EFI_SERIAL_IO_PROTOCOL *This,
|
---|
190 | IN OUT UINTN *BufferSize,
|
---|
191 | OUT VOID *Buffer
|
---|
192 | )
|
---|
193 | {
|
---|
194 | VIRTIO_SERIAL_IO_PROTOCOL *SerialIo = (VIRTIO_SERIAL_IO_PROTOCOL *)This;
|
---|
195 | VIRTIO_SERIAL_PORT *Port = SerialIo->Dev->Ports + SerialIo->PortId;
|
---|
196 | BOOLEAN HasData;
|
---|
197 | UINT32 Length;
|
---|
198 | EFI_TPL OldTpl;
|
---|
199 |
|
---|
200 | if (!Port->DeviceOpen) {
|
---|
201 | goto NoData;
|
---|
202 | }
|
---|
203 |
|
---|
204 | OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
|
---|
205 | if (SerialIo->WriteOffset) {
|
---|
206 | DEBUG ((DEBUG_VERBOSE, "%a:%d: WriteFlush %d\n", __func__, __LINE__, SerialIo->WriteOffset));
|
---|
207 | VirtioSerialRingSendBuffer (
|
---|
208 | SerialIo->Dev,
|
---|
209 | PortTx (SerialIo->PortId),
|
---|
210 | SerialIo->WriteBuffer,
|
---|
211 | SerialIo->WriteOffset,
|
---|
212 | TRUE
|
---|
213 | );
|
---|
214 | SerialIo->WriteOffset = 0;
|
---|
215 | }
|
---|
216 |
|
---|
217 | gBS->RestoreTPL (OldTpl);
|
---|
218 |
|
---|
219 | if (SerialIo->ReadOffset == SerialIo->ReadSize) {
|
---|
220 | HasData = VirtioSerialRingGetBuffer (
|
---|
221 | SerialIo->Dev,
|
---|
222 | PortRx (SerialIo->PortId),
|
---|
223 | &SerialIo->ReadBuffer,
|
---|
224 | &SerialIo->ReadSize
|
---|
225 | );
|
---|
226 | if (!HasData) {
|
---|
227 | goto NoData;
|
---|
228 | }
|
---|
229 |
|
---|
230 | SerialIo->ReadOffset = 0;
|
---|
231 | }
|
---|
232 |
|
---|
233 | if (SerialIo->ReadOffset < SerialIo->ReadSize) {
|
---|
234 | Length = SerialIo->ReadSize - SerialIo->ReadOffset;
|
---|
235 | if (Length > *BufferSize) {
|
---|
236 | Length = (UINT32)(*BufferSize);
|
---|
237 | }
|
---|
238 |
|
---|
239 | CopyMem (Buffer, SerialIo->ReadBuffer + SerialIo->ReadOffset, Length);
|
---|
240 | SerialIo->ReadOffset += Length;
|
---|
241 | *BufferSize = Length;
|
---|
242 | return EFI_SUCCESS;
|
---|
243 | }
|
---|
244 |
|
---|
245 | NoData:
|
---|
246 | *BufferSize = 0;
|
---|
247 | return EFI_SUCCESS;
|
---|
248 | }
|
---|
249 |
|
---|
250 | STATIC
|
---|
251 | EFI_STATUS
|
---|
252 | EFIAPI
|
---|
253 | VirtioSerialIoInit (
|
---|
254 | IN OUT VIRTIO_SERIAL_DEV *Dev,
|
---|
255 | IN UINT32 PortId
|
---|
256 | )
|
---|
257 | {
|
---|
258 | VIRTIO_SERIAL_PORT *Port = Dev->Ports + PortId;
|
---|
259 | VIRTIO_SERIAL_IO_PROTOCOL *SerialIo;
|
---|
260 | EFI_STATUS Status;
|
---|
261 |
|
---|
262 | SerialIo = (VIRTIO_SERIAL_IO_PROTOCOL *)AllocateZeroPool (sizeof *SerialIo);
|
---|
263 | Port->SerialIo = SerialIo;
|
---|
264 |
|
---|
265 | SerialIo->SerialIo.Revision = EFI_SERIAL_IO_PROTOCOL_REVISION;
|
---|
266 | SerialIo->SerialIo.Reset = VirtioSerialIoReset;
|
---|
267 | SerialIo->SerialIo.SetAttributes = VirtioSerialIoSetAttributes;
|
---|
268 | SerialIo->SerialIo.SetControl = VirtioSerialIoSetControl;
|
---|
269 | SerialIo->SerialIo.GetControl = VirtioSerialIoGetControl;
|
---|
270 | SerialIo->SerialIo.Write = VirtioSerialIoWrite;
|
---|
271 | SerialIo->SerialIo.Read = VirtioSerialIoRead;
|
---|
272 | SerialIo->SerialIo.Mode = &SerialIo->SerialIoMode;
|
---|
273 | SerialIo->Dev = Dev;
|
---|
274 | SerialIo->PortId = PortId;
|
---|
275 |
|
---|
276 | SerialIo->DevicePath = DuplicateDevicePath (Dev->DevicePath);
|
---|
277 | mAcpiSerialDevNode.UID = PortId;
|
---|
278 | SerialIo->DevicePath = AppendDevicePathNode (
|
---|
279 | SerialIo->DevicePath,
|
---|
280 | (EFI_DEVICE_PATH_PROTOCOL *)&mAcpiSerialDevNode
|
---|
281 | );
|
---|
282 | SerialIo->DevicePath = AppendDevicePathNode (
|
---|
283 | SerialIo->DevicePath,
|
---|
284 | (EFI_DEVICE_PATH_PROTOCOL *)&mUartDevNode
|
---|
285 | );
|
---|
286 |
|
---|
287 | LogDevicePath (DEBUG_INFO, __func__, L"UART", SerialIo->DevicePath);
|
---|
288 |
|
---|
289 | Status = gBS->InstallMultipleProtocolInterfaces (
|
---|
290 | &SerialIo->DeviceHandle,
|
---|
291 | &gEfiDevicePathProtocolGuid,
|
---|
292 | SerialIo->DevicePath,
|
---|
293 | &gEfiSerialIoProtocolGuid,
|
---|
294 | &SerialIo->SerialIo,
|
---|
295 | NULL
|
---|
296 | );
|
---|
297 | if (EFI_ERROR (Status)) {
|
---|
298 | DEBUG ((DEBUG_INFO, "%a:%d: ERROR: %r\n", __func__, __LINE__, Status));
|
---|
299 | goto FreeSerialIo;
|
---|
300 | }
|
---|
301 |
|
---|
302 | Status = gBS->OpenProtocol (
|
---|
303 | Dev->DeviceHandle,
|
---|
304 | &gVirtioDeviceProtocolGuid,
|
---|
305 | (VOID **)&Dev->VirtIo,
|
---|
306 | Dev->DriverBindingHandle,
|
---|
307 | SerialIo->DeviceHandle,
|
---|
308 | EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
|
---|
309 | );
|
---|
310 | if (EFI_ERROR (Status)) {
|
---|
311 | DEBUG ((DEBUG_INFO, "%a:%d: ERROR: %r\n", __func__, __LINE__, Status));
|
---|
312 | goto UninstallProtocol;
|
---|
313 | }
|
---|
314 |
|
---|
315 | return EFI_SUCCESS;
|
---|
316 |
|
---|
317 | UninstallProtocol:
|
---|
318 | gBS->UninstallMultipleProtocolInterfaces (
|
---|
319 | SerialIo->DeviceHandle,
|
---|
320 | &gEfiDevicePathProtocolGuid,
|
---|
321 | SerialIo->DevicePath,
|
---|
322 | &gEfiSerialIoProtocolGuid,
|
---|
323 | &SerialIo->SerialIo,
|
---|
324 | NULL
|
---|
325 | );
|
---|
326 |
|
---|
327 | FreeSerialIo:
|
---|
328 | FreePool (Port->SerialIo);
|
---|
329 | Port->SerialIo = NULL;
|
---|
330 | return Status;
|
---|
331 | }
|
---|
332 |
|
---|
333 | STATIC
|
---|
334 | VOID
|
---|
335 | EFIAPI
|
---|
336 | VirtioSerialIoUninit (
|
---|
337 | VIRTIO_SERIAL_IO_PROTOCOL *SerialIo
|
---|
338 | )
|
---|
339 | {
|
---|
340 | VIRTIO_SERIAL_DEV *Dev = SerialIo->Dev;
|
---|
341 | VIRTIO_SERIAL_PORT *Port = Dev->Ports + SerialIo->PortId;
|
---|
342 |
|
---|
343 | DEBUG ((DEBUG_INFO, "%a:%d: %s\n", __func__, __LINE__, Port->Name));
|
---|
344 |
|
---|
345 | gBS->CloseProtocol (
|
---|
346 | Dev->DeviceHandle,
|
---|
347 | &gVirtioDeviceProtocolGuid,
|
---|
348 | Dev->DriverBindingHandle,
|
---|
349 | SerialIo->DeviceHandle
|
---|
350 | );
|
---|
351 |
|
---|
352 | gBS->UninstallMultipleProtocolInterfaces (
|
---|
353 | SerialIo->DeviceHandle,
|
---|
354 | &gEfiDevicePathProtocolGuid,
|
---|
355 | SerialIo->DevicePath,
|
---|
356 | &gEfiSerialIoProtocolGuid,
|
---|
357 | &SerialIo->SerialIo,
|
---|
358 | NULL
|
---|
359 | );
|
---|
360 |
|
---|
361 | FreePool (SerialIo);
|
---|
362 | Port->SerialIo = NULL;
|
---|
363 | }
|
---|
364 |
|
---|
365 | EFI_STATUS
|
---|
366 | EFIAPI
|
---|
367 | VirtioSerialPortAdd (
|
---|
368 | IN OUT VIRTIO_SERIAL_DEV *Dev,
|
---|
369 | IN UINT32 PortId
|
---|
370 | )
|
---|
371 | {
|
---|
372 | VIRTIO_SERIAL_PORT *Port = Dev->Ports + PortId;
|
---|
373 | EFI_STATUS Status;
|
---|
374 |
|
---|
375 | if (Port->Ready) {
|
---|
376 | return EFI_SUCCESS;
|
---|
377 | }
|
---|
378 |
|
---|
379 | Status = VirtioSerialInitRing (Dev, PortRx (PortId), PORT_RX_BUFSIZE);
|
---|
380 | if (EFI_ERROR (Status)) {
|
---|
381 | goto Failed;
|
---|
382 | }
|
---|
383 |
|
---|
384 | Status = VirtioSerialInitRing (Dev, PortTx (PortId), PORT_TX_BUFSIZE);
|
---|
385 | if (EFI_ERROR (Status)) {
|
---|
386 | goto Failed;
|
---|
387 | }
|
---|
388 |
|
---|
389 | UnicodeSPrint (Port->Name, sizeof (Port->Name), L"Port #%d", PortId);
|
---|
390 | VirtioSerialRingFillRx (Dev, PortRx (PortId));
|
---|
391 | Port->Ready = TRUE;
|
---|
392 |
|
---|
393 | return EFI_SUCCESS;
|
---|
394 |
|
---|
395 | Failed:
|
---|
396 | VirtioSerialUninitRing (Dev, PortRx (PortId));
|
---|
397 | return Status;
|
---|
398 | }
|
---|
399 |
|
---|
400 | VOID
|
---|
401 | EFIAPI
|
---|
402 | VirtioSerialPortSetConsole (
|
---|
403 | IN OUT VIRTIO_SERIAL_DEV *Dev,
|
---|
404 | IN UINT32 PortId
|
---|
405 | )
|
---|
406 | {
|
---|
407 | VIRTIO_SERIAL_PORT *Port = Dev->Ports + PortId;
|
---|
408 |
|
---|
409 | Port->Console = TRUE;
|
---|
410 | UnicodeSPrint (Port->Name, sizeof (Port->Name), L"Console #%d", PortId);
|
---|
411 | VirtioSerialIoInit (Dev, PortId);
|
---|
412 | }
|
---|
413 |
|
---|
414 | VOID
|
---|
415 | EFIAPI
|
---|
416 | VirtioSerialPortSetName (
|
---|
417 | IN OUT VIRTIO_SERIAL_DEV *Dev,
|
---|
418 | IN UINT32 PortId,
|
---|
419 | IN UINT8 *Name
|
---|
420 | )
|
---|
421 | {
|
---|
422 | VIRTIO_SERIAL_PORT *Port = Dev->Ports + PortId;
|
---|
423 |
|
---|
424 | DEBUG ((DEBUG_INFO, "%a:%d: \"%a\"\n", __func__, __LINE__, Name));
|
---|
425 | UnicodeSPrint (Port->Name, sizeof (Port->Name), L"NamedPort #%d (%a)", PortId, Name);
|
---|
426 | }
|
---|
427 |
|
---|
428 | VOID
|
---|
429 | EFIAPI
|
---|
430 | VirtioSerialPortSetDeviceOpen (
|
---|
431 | IN OUT VIRTIO_SERIAL_DEV *Dev,
|
---|
432 | IN UINT32 PortId,
|
---|
433 | IN UINT16 Value
|
---|
434 | )
|
---|
435 | {
|
---|
436 | VIRTIO_SERIAL_PORT *Port = Dev->Ports + PortId;
|
---|
437 |
|
---|
438 | Port->DeviceOpen = (BOOLEAN)Value;
|
---|
439 | if (Port->DeviceOpen) {
|
---|
440 | VirtioSerialTxControl (Dev, PortId, VIRTIO_SERIAL_PORT_OPEN, 1);
|
---|
441 | }
|
---|
442 | }
|
---|
443 |
|
---|
444 | VOID
|
---|
445 | EFIAPI
|
---|
446 | VirtioSerialPortRemove (
|
---|
447 | IN OUT VIRTIO_SERIAL_DEV *Dev,
|
---|
448 | IN UINT32 PortId
|
---|
449 | )
|
---|
450 | {
|
---|
451 | VIRTIO_SERIAL_PORT *Port = Dev->Ports + PortId;
|
---|
452 |
|
---|
453 | if (!Port->Ready) {
|
---|
454 | return;
|
---|
455 | }
|
---|
456 |
|
---|
457 | if (Port->SerialIo) {
|
---|
458 | VirtioSerialIoUninit (Port->SerialIo);
|
---|
459 | Port->SerialIo = NULL;
|
---|
460 | }
|
---|
461 |
|
---|
462 | VirtioSerialUninitRing (Dev, PortRx (PortId));
|
---|
463 | VirtioSerialUninitRing (Dev, PortTx (PortId));
|
---|
464 | Port->Ready = FALSE;
|
---|
465 | }
|
---|