VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/freebsd/HostHardwareFreeBSD.cpp@ 86714

Last change on this file since 86714 was 85929, checked in by vboxsync, 4 years ago

Main: bugref:9224: Main+VBoxManageDisk+doc part

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 21.8 KB
Line 
1/* $Id: HostHardwareFreeBSD.cpp 85929 2020-08-28 14:40:55Z vboxsync $ */
2/** @file
3 * VirtualBox Main - Code for handling hardware detection under FreeBSD, VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2008-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_MAIN
23#include "HostHardwareLinux.h"
24
25#include <VBox/log.h>
26
27#include <iprt/dir.h>
28#include <iprt/env.h>
29#include <iprt/file.h>
30#include <iprt/mem.h>
31#include <iprt/param.h>
32#include <iprt/path.h>
33#include <iprt/string.h>
34
35#include <sys/param.h>
36#include <sys/types.h>
37#include <sys/stat.h>
38#include <unistd.h>
39#include <stdio.h>
40#include <sys/ioctl.h>
41#include <fcntl.h>
42#include <cam/cam.h>
43#include <cam/cam_ccb.h>
44#include <camlib.h>
45#include <cam/scsi/scsi_pass.h>
46
47#include <vector>
48
49
50/*********************************************************************************************************************************
51* Typedefs and Defines *
52*********************************************************************************************************************************/
53typedef enum DriveType_T
54{
55 Fixed,
56 DVD,
57 Any
58} DriveType_T;
59
60
61/*********************************************************************************************************************************
62* Internal Functions *
63*********************************************************************************************************************************/
64static int getDriveInfoFromEnv(const char *pcszVar, DriveInfoList *pList, bool isDVD, bool *pfSuccess) RT_NOTHROW_PROTP;
65static int getDriveInfoFromCAM(DriveInfoList *pList, DriveType_T enmDriveType, bool *pfSuccess) RT_NOTHROW_PROTP;
66
67
68/** Find the length of a string, ignoring trailing non-ascii or control
69 * characters
70 * @note Code duplicated in HostHardwareLinux.cpp */
71static size_t strLenStripped(const char *pcsz) RT_NOTHROW_DEF
72{
73 size_t cch = 0;
74 for (size_t i = 0; pcsz[i] != '\0'; ++i)
75 if (pcsz[i] > 32 /*space*/ && pcsz[i] < 127 /*delete*/)
76 cch = i;
77 return cch + 1;
78}
79
80
81/**
82 * Initialize the device description for a drive based on vendor and model name
83 * strings.
84 *
85 * @param pcszVendor The raw vendor ID string.
86 * @param pcszModel The raw product ID string.
87 * @param pszDesc Where to store the description string (optional)
88 * @param cbDesc The size of the buffer in @pszDesc
89 *
90 * @note Used for disks as well as DVDs.
91 */
92/* static */
93void dvdCreateDeviceString(const char *pcszVendor, const char *pcszModel, char *pszDesc, size_t cbDesc) RT_NOTHROW_DEF
94{
95 AssertPtrReturnVoid(pcszVendor);
96 AssertPtrReturnVoid(pcszModel);
97 AssertPtrNullReturnVoid(pszDesc);
98 AssertReturnVoid(!pszDesc || cbDesc > 0);
99 size_t cchVendor = strLenStripped(pcszVendor);
100 size_t cchModel = strLenStripped(pcszModel);
101
102 /* Construct the description string as "Vendor Product" */
103 if (pszDesc)
104 {
105 if (cchVendor > 0)
106 RTStrPrintf(pszDesc, cbDesc, "%.*s %s", cchVendor, pcszVendor,
107 cchModel > 0 ? pcszModel : "(unknown drive model)");
108 else
109 RTStrPrintf(pszDesc, cbDesc, "%s", pcszModel);
110 RTStrPurgeEncoding(pszDesc);
111 }
112}
113
114
115int VBoxMainDriveInfo::updateDVDs() RT_NOEXCEPT
116{
117 LogFlowThisFunc(("entered\n"));
118 int rc;
119 try
120 {
121 mDVDList.clear();
122 /* Always allow the user to override our auto-detection using an
123 * environment variable. */
124 bool fSuccess = false; /* Have we succeeded in finding anything yet? */
125 rc = getDriveInfoFromEnv("VBOX_CDROM", &mDVDList, true /* isDVD */, &fSuccess);
126 if (RT_SUCCESS(rc) && !fSuccess)
127 rc = getDriveInfoFromCAM(&mDVDList, DVD, &fSuccess);
128 }
129 catch (std::bad_alloc &)
130 {
131 rc = VERR_NO_MEMORY;
132 }
133 LogFlowThisFunc(("rc=%Rrc\n", rc));
134 return rc;
135}
136
137int VBoxMainDriveInfo::updateFloppies() RT_NOEXCEPT
138{
139 LogFlowThisFunc(("entered\n"));
140 int rc;
141 try
142 {
143 /* Only got the enviornment variable here... */
144 mFloppyList.clear();
145 bool fSuccess = false; /* ignored */
146 rc = getDriveInfoFromEnv("VBOX_FLOPPY", &mFloppyList, false /* isDVD */, &fSuccess);
147 }
148 catch (std::bad_alloc &)
149 {
150 rc = VERR_NO_MEMORY;
151 }
152 LogFlowThisFunc(("rc=%Rrc\n", rc));
153 return rc;
154}
155
156int VBoxMainDriveInfo::updateFixedDrives() RT_NOEXCEPT
157{
158 LogFlowThisFunc(("entered\n"));
159 int rc;
160 try
161 {
162 mFixedDriveList.clear();
163 bool fSuccess = false; /* ignored */
164 rc = getDriveInfoFromCAM(&mFixedDriveList, Fixed, &fSuccess);
165 }
166 catch (std::bad_alloc &)
167 {
168 rc = VERR_NO_MEMORY;
169 }
170 LogFlowThisFunc(("rc=%Rrc\n", rc));
171 return rc;
172}
173
174static void strDeviceStringSCSI(device_match_result *pDevResult, char *pszDesc, size_t cbDesc) RT_NOTHROW_DEF
175{
176 char szVendor[128];
177 cam_strvis((uint8_t *)szVendor, (const uint8_t *)pDevResult->inq_data.vendor,
178 sizeof(pDevResult->inq_data.vendor), sizeof(szVendor));
179 char szProduct[128];
180 cam_strvis((uint8_t *)szProduct, (const uint8_t *)pDevResult->inq_data.product,
181 sizeof(pDevResult->inq_data.product), sizeof(szProduct));
182 dvdCreateDeviceString(szVendor, szProduct, pszDesc, cbDesc);
183}
184
185static void strDeviceStringATA(device_match_result *pDevResult, char *pszDesc, size_t cbDesc) RT_NOTHROW_DEF
186{
187 char szProduct[256];
188 cam_strvis((uint8_t *)szProduct, (const uint8_t *)pDevResult->ident_data.model,
189 sizeof(pDevResult->ident_data.model), sizeof(szProduct));
190 dvdCreateDeviceString("", szProduct, pszDesc, cbDesc);
191}
192
193static void strDeviceStringSEMB(device_match_result *pDevResult, char *pszDesc, size_t cbDesc) RT_NOTHROW_DEF
194{
195 sep_identify_data *pSid = (sep_identify_data *)&pDevResult->ident_data;
196
197 char szVendor[128];
198 cam_strvis((uint8_t *)szVendor, (const uint8_t *)pSid->vendor_id,
199 sizeof(pSid->vendor_id), sizeof(szVendor));
200 char szProduct[128];
201 cam_strvis((uint8_t *)szProduct, (const uint8_t *)pSid->product_id,
202 sizeof(pSid->product_id), sizeof(szProduct));
203 dvdCreateDeviceString(szVendor, szProduct, pszDesc, cbDesc);
204}
205
206static void strDeviceStringMMCSD(device_match_result *pDevResult, char *pszDesc, size_t cbDesc) RT_NOTHROW_DEF
207{
208 struct cam_device *pDev = cam_open_btl(pDevResult->path_id, pDevResult->target_id,
209 pDevResult->target_lun, O_RDWR, NULL);
210 if (pDev == NULL)
211 {
212 Log(("Error while opening drive device. Error: %s\n", cam_errbuf));
213 return;
214 }
215
216 union ccb *pCcb = cam_getccb(pDev);
217 if (pCcb != NULL)
218 {
219 struct mmc_params mmcIdentData;
220 RT_ZERO(mmcIdentData);
221
222 struct ccb_dev_advinfo *pAdvi = &pCcb->cdai;
223 pAdvi->ccb_h.flags = CAM_DIR_IN;
224 pAdvi->ccb_h.func_code = XPT_DEV_ADVINFO;
225 pAdvi->flags = CDAI_FLAG_NONE;
226 pAdvi->buftype = CDAI_TYPE_MMC_PARAMS;
227 pAdvi->bufsiz = sizeof(mmcIdentData);
228 pAdvi->buf = (uint8_t *)&mmcIdentData;
229
230 if (cam_send_ccb(pDev, pCcb) >= 0)
231 {
232 if (strlen((char *)mmcIdentData.model) > 0)
233 dvdCreateDeviceString("", (const char *)mmcIdentData.model, pszDesc, cbDesc);
234 else
235 dvdCreateDeviceString("", mmcIdentData.card_features & CARD_FEATURE_SDIO ? "SDIO card" : "Unknown card",
236 pszDesc, cbDesc);
237 }
238 else
239 Log(("error sending XPT_DEV_ADVINFO CCB\n"));
240
241 cam_freeccb(pCcb);
242 }
243 else
244 Log(("Could not allocate CCB\n"));
245 cam_close_device(pDev);
246}
247
248/** @returns boolean success indicator (true/false). */
249static int nvmeGetCData(struct cam_device *pDev, struct nvme_controller_data *pCData) RT_NOTHROW_DEF
250{
251 bool fSuccess = false;
252 union ccb *pCcb = cam_getccb(pDev);
253 if (pCcb != NULL)
254 {
255 struct ccb_dev_advinfo *pAdvi = &pCcb->cdai;
256 pAdvi->ccb_h.flags = CAM_DIR_IN;
257 pAdvi->ccb_h.func_code = XPT_DEV_ADVINFO;
258 pAdvi->flags = CDAI_FLAG_NONE;
259 pAdvi->buftype = CDAI_TYPE_NVME_CNTRL;
260 pAdvi->bufsiz = sizeof(struct nvme_controller_data);
261 pAdvi->buf = (uint8_t *)pCData;
262 RT_BZERO(pAdvi->buf, pAdvi->bufsiz);
263
264 if (cam_send_ccb(pDev, pCcb) >= 0)
265 {
266 if (pAdvi->ccb_h.status == CAM_REQ_CMP)
267 fSuccess = true;
268 else
269 Log(("Got CAM error %#x\n", pAdvi->ccb_h.status));
270 }
271 else
272 Log(("Error sending XPT_DEV_ADVINFO CC\n"));
273 cam_freeccb(pCcb);
274 }
275 else
276 Log(("Could not allocate CCB\n"));
277 return fSuccess;
278}
279
280static void strDeviceStringNVME(device_match_result *pDevResult, char *pszDesc, size_t cbDesc) RT_NOTHROW_DEF
281{
282 struct cam_device *pDev = cam_open_btl(pDevResult->path_id, pDevResult->target_id,
283 pDevResult->target_lun, O_RDWR, NULL);
284 if (pDev)
285 {
286 struct nvme_controller_data CData;
287 if (nvmeGetCData(pDev, &CData))
288 {
289 char szVendor[128];
290 cam_strvis((uint8_t *)szVendor, CData.mn, sizeof(CData.mn), sizeof(szVendor));
291 char szProduct[128];
292 cam_strvis((uint8_t *)szProduct, CData.fr, sizeof(CData.fr), sizeof(szProduct));
293 dvdCreateDeviceString(szVendor, szProduct, pszDesc, cbDesc);
294 }
295 else
296 Log(("Error while getting NVME drive info\n"));
297 cam_close_device(pDev);
298 }
299 else
300 Log(("Error while opening drive device. Error: %s\n", cam_errbuf));
301}
302
303
304/**
305 * Search for available drives using the CAM layer.
306 *
307 * @returns iprt status code
308 * @param pList the list to append the drives found to
309 * @param enmDriveType search drives of specified type
310 * @param pfSuccess this will be set to true if we found at least one drive
311 * and to false otherwise. Optional.
312 */
313static int getDriveInfoFromCAM(DriveInfoList *pList, DriveType_T enmDriveType, bool *pfSuccess) RT_NOTHROW_DEF
314{
315 RTFILE hFileXpt = NIL_RTFILE;
316 int rc = RTFileOpen(&hFileXpt, "/dev/xpt0", RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
317 if (RT_SUCCESS(rc))
318 {
319 union ccb DeviceCCB;
320 struct dev_match_pattern DeviceMatchPattern;
321 struct dev_match_result *paMatches = NULL;
322
323 RT_ZERO(DeviceCCB);
324 RT_ZERO(DeviceMatchPattern);
325
326 /* We want to get all devices. */
327 DeviceCCB.ccb_h.func_code = XPT_DEV_MATCH;
328 DeviceCCB.ccb_h.path_id = CAM_XPT_PATH_ID;
329 DeviceCCB.ccb_h.target_id = CAM_TARGET_WILDCARD;
330 DeviceCCB.ccb_h.target_lun = CAM_LUN_WILDCARD;
331
332 /* Setup the pattern */
333 DeviceMatchPattern.type = DEV_MATCH_DEVICE;
334 DeviceMatchPattern.pattern.device_pattern.path_id = CAM_XPT_PATH_ID;
335 DeviceMatchPattern.pattern.device_pattern.target_id = CAM_TARGET_WILDCARD;
336 DeviceMatchPattern.pattern.device_pattern.target_lun = CAM_LUN_WILDCARD;
337 DeviceMatchPattern.pattern.device_pattern.flags = DEV_MATCH_INQUIRY;
338
339#if __FreeBSD_version >= 900000
340# define INQ_PAT data.inq_pat
341#else
342 #define INQ_PAT inq_pat
343#endif
344 DeviceMatchPattern.pattern.device_pattern.INQ_PAT.type = enmDriveType == Fixed ? T_DIRECT
345 : enmDriveType == DVD ? T_CDROM : T_ANY;
346 DeviceMatchPattern.pattern.device_pattern.INQ_PAT.media_type = SIP_MEDIA_REMOVABLE | SIP_MEDIA_FIXED;
347 DeviceMatchPattern.pattern.device_pattern.INQ_PAT.vendor[0] = '*'; /* Matches anything */
348 DeviceMatchPattern.pattern.device_pattern.INQ_PAT.product[0] = '*'; /* Matches anything */
349 DeviceMatchPattern.pattern.device_pattern.INQ_PAT.revision[0] = '*'; /* Matches anything */
350#undef INQ_PAT
351 DeviceCCB.cdm.num_patterns = 1;
352 DeviceCCB.cdm.pattern_buf_len = sizeof(struct dev_match_result);
353 DeviceCCB.cdm.patterns = &DeviceMatchPattern;
354
355 /*
356 * Allocate the buffer holding the matches.
357 * We will allocate for 10 results and call
358 * CAM multiple times if we have more results.
359 */
360 paMatches = (struct dev_match_result *)RTMemAllocZ(10 * sizeof(struct dev_match_result));
361 if (paMatches)
362 {
363 DeviceCCB.cdm.num_matches = 0;
364 DeviceCCB.cdm.match_buf_len = 10 * sizeof(struct dev_match_result);
365 DeviceCCB.cdm.matches = paMatches;
366
367 do
368 {
369 rc = RTFileIoCtl(hFileXpt, CAMIOCOMMAND, &DeviceCCB, sizeof(union ccb), NULL);
370 if (RT_FAILURE(rc))
371 {
372 Log(("Error while querying available CD/DVD devices rc=%Rrc\n", rc));
373 break;
374 }
375
376 for (unsigned i = 0; i < DeviceCCB.cdm.num_matches; i++)
377 {
378 if (paMatches[i].type == DEV_MATCH_DEVICE)
379 {
380 /*
381 * The result list can contain some empty entries with DEV_RESULT_UNCONFIGURED
382 * flag set, e.g. in case of T_DIRECT. Ignore them.
383 */
384 if ( (paMatches[i].result.device_result.flags & DEV_RESULT_UNCONFIGURED)
385 == DEV_RESULT_UNCONFIGURED)
386 continue;
387
388 /* We have the drive now but need the appropriate device node */
389 struct device_match_result *pDevResult = &paMatches[i].result.device_result;
390 union ccb PeriphCCB;
391 struct dev_match_pattern PeriphMatchPattern;
392 struct dev_match_result aPeriphMatches[2];
393 struct periph_match_result *pPeriphResult = NULL;
394 unsigned iPeriphMatch = 0;
395
396 RT_ZERO(PeriphCCB);
397 RT_ZERO(PeriphMatchPattern);
398 RT_ZERO(aPeriphMatches);
399
400 /* This time we only want the specific nodes for the device. */
401 PeriphCCB.ccb_h.func_code = XPT_DEV_MATCH;
402 PeriphCCB.ccb_h.path_id = paMatches[i].result.device_result.path_id;
403 PeriphCCB.ccb_h.target_id = paMatches[i].result.device_result.target_id;
404 PeriphCCB.ccb_h.target_lun = paMatches[i].result.device_result.target_lun;
405
406 /* Setup the pattern */
407 PeriphMatchPattern.type = DEV_MATCH_PERIPH;
408 PeriphMatchPattern.pattern.periph_pattern.path_id = paMatches[i].result.device_result.path_id;
409 PeriphMatchPattern.pattern.periph_pattern.target_id = paMatches[i].result.device_result.target_id;
410 PeriphMatchPattern.pattern.periph_pattern.target_lun = paMatches[i].result.device_result.target_lun;
411 PeriphMatchPattern.pattern.periph_pattern.flags = (periph_pattern_flags)( PERIPH_MATCH_PATH
412 | PERIPH_MATCH_TARGET
413 | PERIPH_MATCH_LUN);
414 PeriphCCB.cdm.num_patterns = 1;
415 PeriphCCB.cdm.pattern_buf_len = sizeof(struct dev_match_result);
416 PeriphCCB.cdm.patterns = &PeriphMatchPattern;
417 PeriphCCB.cdm.num_matches = 0;
418 PeriphCCB.cdm.match_buf_len = sizeof(aPeriphMatches);
419 PeriphCCB.cdm.matches = aPeriphMatches;
420
421 do
422 {
423 rc = RTFileIoCtl(hFileXpt, CAMIOCOMMAND, &PeriphCCB, sizeof(union ccb), NULL);
424 if (RT_FAILURE(rc))
425 {
426 Log(("Error while querying available periph devices rc=%Rrc\n", rc));
427 break;
428 }
429
430 for (iPeriphMatch = 0; iPeriphMatch < PeriphCCB.cdm.num_matches; iPeriphMatch++)
431 {
432 /* Ignore "passthrough mode" paths */
433 if ( aPeriphMatches[iPeriphMatch].type == DEV_MATCH_PERIPH
434 && strcmp(aPeriphMatches[iPeriphMatch].result.periph_result.periph_name, "pass"))
435 {
436 pPeriphResult = &aPeriphMatches[iPeriphMatch].result.periph_result;
437 break; /* We found the periph device */
438 }
439 }
440
441 if (iPeriphMatch < PeriphCCB.cdm.num_matches)
442 break;
443
444 } while ( DeviceCCB.ccb_h.status == CAM_REQ_CMP
445 && DeviceCCB.cdm.status == CAM_DEV_MATCH_MORE);
446
447 if (pPeriphResult)
448 {
449 char szPath[RTPATH_MAX];
450 RTStrPrintf(szPath, sizeof(szPath), "/dev/%s%d",
451 pPeriphResult->periph_name, pPeriphResult->unit_number);
452
453 char szDesc[256] = { 0 };
454 switch (pDevResult->protocol)
455 {
456 case PROTO_SCSI: strDeviceStringSCSI( pDevResult, szDesc, sizeof(szDesc)); break;
457 case PROTO_ATA: strDeviceStringATA( pDevResult, szDesc, sizeof(szDesc)); break;
458 case PROTO_MMCSD: strDeviceStringMMCSD(pDevResult, szDesc, sizeof(szDesc)); break;
459 case PROTO_SEMB: strDeviceStringSEMB( pDevResult, szDesc, sizeof(szDesc)); break;
460 case PROTO_NVME: strDeviceStringNVME( pDevResult, szDesc, sizeof(szDesc)); break;
461 default: break;
462 }
463
464 try
465 {
466 pList->push_back(DriveInfo(szPath, "", szDesc));
467 }
468 catch (std::bad_alloc &)
469 {
470 pList->clear();
471 rc = VERR_NO_MEMORY;
472 break;
473 }
474 if (pfSuccess)
475 *pfSuccess = true;
476 }
477 }
478 }
479 } while ( DeviceCCB.ccb_h.status == CAM_REQ_CMP
480 && DeviceCCB.cdm.status == CAM_DEV_MATCH_MORE
481 && RT_SUCCESS(rc));
482
483 RTMemFree(paMatches);
484 }
485 else
486 rc = VERR_NO_MEMORY;
487
488 RTFileClose(hFileXpt);
489 }
490
491 return rc;
492}
493
494
495/**
496 * Extract the names of drives from an environment variable and add them to a
497 * list if they are valid.
498 *
499 * @returns iprt status code
500 * @param pcszVar the name of the environment variable. The variable
501 * value should be a list of device node names, separated
502 * by ':' characters.
503 * @param pList the list to append the drives found to
504 * @param isDVD are we looking for DVD drives or for floppies?
505 * @param pfSuccess this will be set to true if we found at least one drive
506 * and to false otherwise. Optional.
507 *
508 * @note This is duplicated in HostHardwareLinux.cpp.
509 */
510static int getDriveInfoFromEnv(const char *pcszVar, DriveInfoList *pList, bool isDVD, bool *pfSuccess) RT_NOTHROW_DEF
511{
512 AssertPtrReturn(pcszVar, VERR_INVALID_POINTER);
513 AssertPtrReturn(pList, VERR_INVALID_POINTER);
514 AssertPtrNullReturn(pfSuccess, VERR_INVALID_POINTER);
515 LogFlowFunc(("pcszVar=%s, pList=%p, isDVD=%d, pfSuccess=%p\n", pcszVar, pList, isDVD, pfSuccess));
516 int rc = VINF_SUCCESS;
517 bool success = false;
518 char *pszFreeMe = RTEnvDupEx(RTENV_DEFAULT, pcszVar);
519
520 try
521 {
522 char *pszCurrent = pszFreeMe;
523 while (pszCurrent && *pszCurrent != '\0')
524 {
525 char *pszNext = strchr(pszCurrent, ':');
526 if (pszNext)
527 *pszNext++ = '\0';
528
529 char szReal[RTPATH_MAX];
530 char szDesc[1] = "", szUdi[1] = ""; /* differs on freebsd because no devValidateDevice */
531 if ( RT_SUCCESS(RTPathReal(pszCurrent, szReal, sizeof(szReal)))
532 /*&& devValidateDevice(szReal, isDVD, NULL, szDesc, sizeof(szDesc), szUdi, sizeof(szUdi)) - linux only */)
533 {
534 pList->push_back(DriveInfo(szReal, szUdi, szDesc));
535 success = true;
536 }
537 pszCurrent = pszNext;
538 }
539 if (pfSuccess != NULL)
540 *pfSuccess = success;
541 }
542 catch (std::bad_alloc &)
543 {
544 rc = VERR_NO_MEMORY;
545 }
546 RTStrFree(pszFreeMe);
547 LogFlowFunc(("rc=%Rrc, success=%d\n", rc, success));
548 return rc;
549}
550
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette