xref: /freebsd/sys/dev/ufshci/ufshci_sim.c (revision 3a99f31fdb6c66434f77d83ca5166aae4ad2d22c)
1 /*-
2  * Copyright (c) 2025, Samsung Electronics Co., Ltd.
3  * Written by Jaeyoon Choi
4  *
5  * SPDX-License-Identifier: BSD-2-Clause
6  */
7 
8 #include <sys/param.h>
9 
10 #include <cam/cam.h>
11 #include <cam/cam_ccb.h>
12 #include <cam/cam_debug.h>
13 #include <cam/cam_periph.h>
14 #include <cam/cam_sim.h>
15 #include <cam/cam_xpt_sim.h>
16 #include <cam/scsi/scsi_all.h>
17 #include <cam/scsi/scsi_message.h>
18 
19 #include "ufshci_private.h"
20 
21 #define sim2ctrlr(sim) ((struct ufshci_controller *)cam_sim_softc(sim))
22 
23 static void
ufshci_sim_scsiio_done(void * ccb_arg,const struct ufshci_completion * cpl,bool error)24 ufshci_sim_scsiio_done(void *ccb_arg, const struct ufshci_completion *cpl,
25     bool error)
26 {
27 	const uint8_t *sense_data;
28 	uint16_t sense_data_max_size;
29 	uint16_t sense_data_len;
30 
31 	union ccb *ccb = (union ccb *)ccb_arg;
32 
33 	/*
34 	 * Let the periph know the completion, and let it sort out what
35 	 * it means. Report an error or success based on OCS and UPIU
36 	 * response code. And We need to copy the sense data to be handled
37 	 * by the CAM.
38 	 */
39 	sense_data = cpl->response_upiu.cmd_response_upiu.sense_data;
40 	sense_data_max_size = sizeof(
41 	    cpl->response_upiu.cmd_response_upiu.sense_data);
42 	sense_data_len = be16toh(
43 	    cpl->response_upiu.cmd_response_upiu.sense_data_len);
44 	memcpy(&ccb->csio.sense_data, sense_data,
45 	    min(sense_data_len, sense_data_max_size));
46 
47 	ccb->ccb_h.status &= ~CAM_SIM_QUEUED;
48 	if (error) {
49 		printf("ufshci: SCSI command completion error, Status(0x%x)"
50 		       " Key(0x%x), ASC(0x%x), ASCQ(0x%x)\n",
51 		    cpl->response_upiu.cmd_response_upiu.header
52 			.ext_iid_or_status,
53 		    sense_data[2], sense_data[12], sense_data[13]);
54 		ccb->ccb_h.status = CAM_REQ_CMP_ERR;
55 		xpt_done(ccb);
56 	} else {
57 		ccb->ccb_h.status = CAM_REQ_CMP;
58 		xpt_done_direct(ccb);
59 	}
60 }
61 
62 /*
63  * Complete the command as an illegal command with invalid field
64  */
65 static void
ufshci_sim_illegal_request(union ccb * ccb)66 ufshci_sim_illegal_request(union ccb *ccb)
67 {
68 	scsi_set_sense_data(&ccb->csio.sense_data,
69 	    /*sense_format*/ SSD_TYPE_NONE,
70 	    /*current_error*/ 1,
71 	    /*sense_key*/ SSD_KEY_ILLEGAL_REQUEST,
72 	    /*asc*/ 0x24, /* 24h/00h INVALID FIELD IN CDB */
73 	    /*ascq*/ 0x00,
74 	    /*extra args*/ SSD_ELEM_NONE);
75 	ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
76 	ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID |
77 	    CAM_DEV_QFRZN;
78 	xpt_freeze_devq(ccb->ccb_h.path, 1);
79 	xpt_done(ccb);
80 }
81 
82 /*
83  * The SCSI LUN format and the UFS UPIU LUN format are different.
84  * This function converts the SCSI LUN format to the UFS UPIU LUN format.
85  */
86 uint8_t
ufshci_sim_translate_scsi_to_ufs_lun(lun_id_t scsi_lun)87 ufshci_sim_translate_scsi_to_ufs_lun(lun_id_t scsi_lun)
88 {
89 	const int address_format_offset = 8;
90 	uint8_t address_format = scsi_lun >> address_format_offset;
91 
92 	/* Well known logical unit */
93 	if (((address_format & RPL_LUNDATA_ATYP_MASK) ==
94 		RPL_LUNDATA_ATYP_EXTLUN) &&
95 	    ((address_format & RPL_LUNDATA_EXT_EAM_MASK) ==
96 		RPL_LUNDATA_EXT_EAM_WK))
97 		return ((scsi_lun & UFSHCI_UPIU_UNIT_NUMBER_ID_MASK) |
98 		    UFSHCI_UPIU_WLUN_ID_MASK);
99 
100 	/* Logical unit */
101 	return (scsi_lun & UFSHCI_UPIU_UNIT_NUMBER_ID_MASK);
102 }
103 
104 uint64_t
ufshci_sim_translate_ufs_to_scsi_lun(uint8_t ufs_lun)105 ufshci_sim_translate_ufs_to_scsi_lun(uint8_t ufs_lun)
106 {
107 	/* Logical unit */
108 	if (!(ufs_lun & UFSHCI_UPIU_WLUN_ID_MASK)) {
109 		return ufs_lun;
110 	}
111 
112 	/* Well known logical unit */
113 	return (((uint64_t)ufs_lun & ~UFSHCI_UPIU_WLUN_ID_MASK) |
114 	    (RPL_LUNDATA_ATYP_EXTLUN | RPL_LUNDATA_EXT_EAM_WK) << 8);
115 }
116 
117 static void
ufshchi_sim_scsiio(struct cam_sim * sim,union ccb * ccb)118 ufshchi_sim_scsiio(struct cam_sim *sim, union ccb *ccb)
119 {
120 	struct ccb_scsiio *csio = &ccb->csio;
121 	struct ufshci_request *req;
122 	void *payload;
123 	struct ufshci_cmd_command_upiu *upiu;
124 	uint8_t *cdb;
125 	uint32_t payload_len;
126 	bool is_write;
127 	struct ufshci_controller *ctrlr;
128 	uint8_t data_direction;
129 	int error;
130 
131 	/* UFS device cannot process these commands */
132 	if (csio->cdb_io.cdb_bytes[0] == MODE_SENSE_6 ||
133 	    csio->cdb_io.cdb_bytes[0] == MODE_SELECT_6 ||
134 	    csio->cdb_io.cdb_bytes[0] == READ_12 ||
135 	    csio->cdb_io.cdb_bytes[0] == WRITE_12) {
136 		ufshci_sim_illegal_request(ccb);
137 		return;
138 	}
139 
140 	ctrlr = sim2ctrlr(sim);
141 	payload = csio->data_ptr;
142 
143 	payload_len = csio->dxfer_len;
144 	is_write = csio->ccb_h.flags & CAM_DIR_OUT;
145 
146 	/* TODO: Check other data type */
147 	if ((csio->ccb_h.flags & CAM_DATA_MASK) == CAM_DATA_BIO)
148 		req = ufshci_allocate_request_bio((struct bio *)payload,
149 		    M_NOWAIT, ufshci_sim_scsiio_done, ccb);
150 	else
151 		req = ufshci_allocate_request_vaddr(payload, payload_len,
152 		    M_NOWAIT, ufshci_sim_scsiio_done, ccb);
153 
154 	req->request_size = sizeof(struct ufshci_cmd_command_upiu);
155 	req->response_size = sizeof(struct ufshci_cmd_response_upiu);
156 
157 	switch (ccb->ccb_h.flags & CAM_DIR_MASK) {
158 	case CAM_DIR_IN:
159 		data_direction = UFSHCI_DATA_DIRECTION_FROM_TGT_TO_SYS;
160 		break;
161 	case CAM_DIR_OUT:
162 		data_direction = UFSHCI_DATA_DIRECTION_FROM_SYS_TO_TGT;
163 		break;
164 	default:
165 		data_direction = UFSHCI_DATA_DIRECTION_NO_DATA_TRANSFER;
166 	}
167 	req->data_direction = data_direction;
168 
169 	upiu = (struct ufshci_cmd_command_upiu *)&req->request_upiu;
170 	memset(upiu, 0, req->request_size);
171 	upiu->header.trans_type = UFSHCI_UPIU_TRANSACTION_CODE_COMMAND;
172 	upiu->header.operational_flags = is_write ? UFSHCI_OPERATIONAL_FLAG_W :
173 						    UFSHCI_OPERATIONAL_FLAG_R;
174 	upiu->header.lun = ufshci_sim_translate_scsi_to_ufs_lun(
175 	    csio->ccb_h.target_lun);
176 	upiu->header.cmd_set_type = UFSHCI_COMMAND_SET_TYPE_SCSI;
177 
178 	upiu->expected_data_transfer_length = htobe32(payload_len);
179 
180 	ccb->ccb_h.status |= CAM_SIM_QUEUED;
181 
182 	if (csio->ccb_h.flags & CAM_CDB_POINTER)
183 		cdb = csio->cdb_io.cdb_ptr;
184 	else
185 		cdb = csio->cdb_io.cdb_bytes;
186 
187 	if (cdb == NULL || csio->cdb_len > sizeof(upiu->cdb)) {
188 		ccb->ccb_h.status = CAM_REQ_INVALID;
189 		xpt_done(ccb);
190 		return;
191 	}
192 	memcpy(upiu->cdb, cdb, csio->cdb_len);
193 
194 	error = ufshci_ctrlr_submit_io_request(ctrlr, req);
195 	if (error == EBUSY) {
196 		ccb->ccb_h.status = CAM_SCSI_BUSY;
197 		xpt_done(ccb);
198 		return;
199 	} else if (error) {
200 		ccb->ccb_h.status = CAM_REQ_INVALID;
201 		xpt_done(ccb);
202 		return;
203 	}
204 }
205 
206 static uint32_t
ufshci_link_kBps(struct ufshci_controller * ctrlr)207 ufshci_link_kBps(struct ufshci_controller *ctrlr)
208 {
209 	uint32_t gear = ctrlr->hs_gear;
210 	uint32_t lanes = ctrlr->rx_lanes;
211 
212 	/*
213 	 * per-lane effective bandwidth (KB/s, SI 1 KB = 1000 B)
214 	 * All HS-Gears use 8b/10b line coding, i.e. 80 % efficiency.
215 	 * - KB/s per lane = raw-rate(Gbps) × 0.8(8b/10b) / 8(bit)
216 	 */
217 	static const uint32_t kbps_per_lane[] = {
218 		0,	 /* unused */
219 		145920,	 /* HS-Gear1 : 1459.2 Mbps */
220 		291840,	 /* HS-Gear2 : 2918.4 Mbps */
221 		583680,	 /* HS-Gear3 : 5836.8 Mbps */
222 		1167360, /* HS-Gear4 : 11673.6 Mbps */
223 		2334720	 /* HS-Gear5 : 23347.2 Mbps */
224 	};
225 
226 	/* Sanity checks */
227 	if (gear >= nitems(kbps_per_lane))
228 		gear = 0; /* out-of-range -> treat as invalid */
229 
230 	if (lanes == 0 || lanes > 2)
231 		lanes = 1; /* UFS spec allows 1–2 data lanes */
232 
233 	return kbps_per_lane[gear] * lanes;
234 }
235 
236 static void
ufshci_cam_action(struct cam_sim * sim,union ccb * ccb)237 ufshci_cam_action(struct cam_sim *sim, union ccb *ccb)
238 {
239 	struct ufshci_controller *ctrlr = sim2ctrlr(sim);
240 
241 	if (ctrlr == NULL) {
242 		ccb->ccb_h.status = CAM_SEL_TIMEOUT;
243 		xpt_done(ccb);
244 		return;
245 	}
246 
247 	/* Perform the requested action */
248 	switch (ccb->ccb_h.func_code) {
249 	case XPT_SCSI_IO:
250 		ufshchi_sim_scsiio(sim, ccb);
251 		return;
252 	case XPT_PATH_INQ: {
253 		struct ccb_pathinq *cpi = &ccb->cpi;
254 		uint32_t need_scan_wluns = 0;
255 
256 		if (!(ctrlr->quirks & UFSHCI_QUIRK_SKIP_WELL_KNOWN_LUNS))
257 			need_scan_wluns = PIM_WLUNS;
258 
259 		cpi->version_num = 1;
260 		cpi->hba_inquiry = PI_SDTR_ABLE | PI_TAG_ABLE;
261 		cpi->target_sprt = 0;
262 		cpi->hba_misc = need_scan_wluns | PIM_UNMAPPED | PIM_NO_6_BYTE;
263 		cpi->hba_eng_cnt = 0;
264 		cpi->max_target = 0;
265 		cpi->max_lun = ctrlr->max_lun_count;
266 		cpi->async_flags = 0;
267 		cpi->maxio = ctrlr->max_xfer_size;
268 		cpi->initiator_id = 1;
269 		strlcpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
270 		strlcpy(cpi->hba_vid, "UFSHCI", HBA_IDLEN);
271 		strlcpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
272 		cpi->unit_number = cam_sim_unit(sim);
273 		cpi->base_transfer_speed = ufshci_link_kBps(ctrlr);
274 		cpi->transport = XPORT_UFSHCI;
275 		cpi->transport_version = 1;
276 		cpi->protocol = PROTO_SCSI;
277 		cpi->protocol_version = SCSI_REV_SPC5;
278 		ccb->ccb_h.status = CAM_REQ_CMP;
279 		break;
280 	}
281 	case XPT_RESET_BUS:
282 		ccb->ccb_h.status = CAM_REQ_CMP;
283 		break;
284 	case XPT_RESET_DEV:
285 		if (ufshci_dev_reset(ctrlr))
286 			ccb->ccb_h.status = CAM_REQ_CMP_ERR;
287 		else
288 			ccb->ccb_h.status = CAM_REQ_CMP;
289 		break;
290 	case XPT_ABORT:
291 		ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
292 		break;
293 	case XPT_SET_TRAN_SETTINGS:
294 		ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
295 		break;
296 	case XPT_GET_TRAN_SETTINGS: {
297 		struct ccb_trans_settings *cts;
298 		struct ccb_trans_settings_ufshci *ufshcix;
299 
300 		cts = &ccb->cts;
301 		ufshcix = &cts->xport_specific.ufshci;
302 
303 		ufshcix->hs_gear = ctrlr->hs_gear;
304 		ufshcix->tx_lanes = ctrlr->tx_lanes;
305 		ufshcix->rx_lanes = ctrlr->rx_lanes;
306 		ufshcix->max_hs_gear = ctrlr->max_rx_hs_gear;
307 		ufshcix->max_tx_lanes = ctrlr->max_tx_lanes;
308 		ufshcix->max_rx_lanes = ctrlr->max_rx_lanes;
309 		ufshcix->valid = CTS_UFSHCI_VALID_LINK;
310 
311 		cts->transport = XPORT_UFSHCI;
312 		cts->transport_version = 1;
313 		cts->protocol = PROTO_SCSI;
314 		cts->protocol_version = SCSI_REV_SPC5;
315 		ccb->ccb_h.status = CAM_REQ_CMP;
316 		break;
317 	}
318 	case XPT_CALC_GEOMETRY:
319 		cam_calc_geometry(&ccb->ccg, 1);
320 		break;
321 	case XPT_NOOP:
322 		ccb->ccb_h.status = CAM_REQ_CMP;
323 		break;
324 	default:
325 		printf("invalid ccb=%p func=%#x\n", ccb, ccb->ccb_h.func_code);
326 		break;
327 	}
328 	xpt_done(ccb);
329 
330 	return;
331 }
332 
333 static void
ufshci_cam_poll(struct cam_sim * sim)334 ufshci_cam_poll(struct cam_sim *sim)
335 {
336 	struct ufshci_controller *ctrlr = sim2ctrlr(sim);
337 
338 	ufshci_ctrlr_poll(ctrlr);
339 }
340 
341 int
ufshci_sim_attach(struct ufshci_controller * ctrlr)342 ufshci_sim_attach(struct ufshci_controller *ctrlr)
343 {
344 	device_t dev;
345 	struct cam_devq *devq;
346 	int max_trans;
347 
348 	dev = ctrlr->dev;
349 	max_trans = ctrlr->max_hw_pend_io;
350 	if ((devq = cam_simq_alloc(max_trans)) == NULL) {
351 		printf("Failed to allocate a simq\n");
352 		return (ENOMEM);
353 	}
354 
355 	ctrlr->ufshci_sim = cam_sim_alloc(ufshci_cam_action, ufshci_cam_poll,
356 	    "ufshci", ctrlr, device_get_unit(dev), &ctrlr->sc_mtx, max_trans,
357 	    max_trans, devq);
358 	if (ctrlr->ufshci_sim == NULL) {
359 		printf("Failed to allocate a sim\n");
360 		cam_simq_free(devq);
361 		return (ENOMEM);
362 	}
363 
364 	mtx_lock(&ctrlr->sc_mtx);
365 	if (xpt_bus_register(ctrlr->ufshci_sim, ctrlr->dev, 0) != CAM_SUCCESS) {
366 		cam_sim_free(ctrlr->ufshci_sim, /*free_devq*/ TRUE);
367 		cam_simq_free(devq);
368 		mtx_unlock(&ctrlr->sc_mtx);
369 		printf("Failed to create a bus\n");
370 		return (ENOMEM);
371 	}
372 
373 	if (xpt_create_path(&ctrlr->ufshci_path, /*periph*/ NULL,
374 		cam_sim_path(ctrlr->ufshci_sim), CAM_TARGET_WILDCARD,
375 		CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
376 		xpt_bus_deregister(cam_sim_path(ctrlr->ufshci_sim));
377 		cam_sim_free(ctrlr->ufshci_sim, /*free_devq*/ TRUE);
378 		cam_simq_free(devq);
379 		mtx_unlock(&ctrlr->sc_mtx);
380 		printf("Failed to create a path\n");
381 		return (ENOMEM);
382 	}
383 	mtx_unlock(&ctrlr->sc_mtx);
384 
385 	return (0);
386 }
387 
388 void
ufshci_sim_detach(struct ufshci_controller * ctrlr)389 ufshci_sim_detach(struct ufshci_controller *ctrlr)
390 {
391 	int error;
392 
393 	if (ctrlr->ufshci_path != NULL) {
394 		xpt_free_path(ctrlr->ufshci_path);
395 		ctrlr->ufshci_path = NULL;
396 	}
397 
398 	if (ctrlr->ufshci_sim != NULL) {
399 		error = xpt_bus_deregister(cam_sim_path(ctrlr->ufshci_sim));
400 		if (error == 0) {
401 			/* accessing the softc is not possible after this */
402 			ctrlr->ufshci_sim->softc = NULL;
403 			ufshci_printf(ctrlr,
404 			    "%s: %s:%d:%d caling "
405 			    "cam_sim_free sim %p refc %u mtx %p\n",
406 			    __func__, ctrlr->sc_name,
407 			    cam_sim_path(ctrlr->ufshci_sim), ctrlr->sc_unit,
408 			    ctrlr->ufshci_sim, ctrlr->ufshci_sim->refcount,
409 			    ctrlr->ufshci_sim->mtx);
410 		} else {
411 			panic("%s: %s: CAM layer is busy: errno %d\n", __func__,
412 			    ctrlr->sc_name, error);
413 		}
414 
415 		cam_sim_free(ctrlr->ufshci_sim, /* free_devq */ TRUE);
416 		ctrlr->ufshci_sim = NULL;
417 	}
418 }
419 
420 struct cam_periph *
ufshci_sim_find_periph(struct ufshci_controller * ctrlr,uint8_t wlun)421 ufshci_sim_find_periph(struct ufshci_controller *ctrlr, uint8_t wlun)
422 {
423 	struct cam_path *path;
424 	struct cam_periph *periph = NULL;
425 	uint64_t scsi_lun;
426 	uint64_t timeout;
427 
428 	scsi_lun = ufshci_sim_translate_ufs_to_scsi_lun(wlun);
429 
430 	if (xpt_create_path(&path, /*periph*/ NULL,
431 		cam_sim_path(ctrlr->ufshci_sim), 0, scsi_lun) != CAM_REQ_CMP) {
432 		return NULL;
433 	}
434 
435 	/* Wait for the perip device to be found */
436 	timeout = ticks + MSEC_2_TICKS(ctrlr->device_init_timeout_in_ms);
437 
438 	while (1) {
439 		xpt_path_lock(path);
440 		periph = cam_periph_find(path, "pass");
441 		xpt_path_unlock(path);
442 
443 		if (periph) {
444 			xpt_free_path(path);
445 			break;
446 		}
447 
448 		if (timeout - ticks < 0) {
449 			ufshci_printf(ctrlr,
450 			    "Failed to find the Well known LUN(0x%x)\n", wlun);
451 			break;
452 		}
453 
454 		pause_sbt("ufshci_find_periph", ustosbt(100), 0, C_PREL(1));
455 	}
456 
457 	return periph;
458 }
459 
460 /* This function is called during suspend/resume. */
461 int
ufshci_sim_send_ssu(struct ufshci_controller * ctrlr,bool start,uint8_t power_condition,bool immed)462 ufshci_sim_send_ssu(struct ufshci_controller *ctrlr, bool start,
463     uint8_t power_condition, bool immed)
464 {
465 	struct cam_periph *periph = ctrlr->ufs_device_wlun_periph;
466 	union ccb *ccb;
467 	int err;
468 
469 	/* Acquire periph reference */
470 	if (periph && cam_periph_acquire(periph) != 0) {
471 		periph = NULL;
472 	}
473 
474 	if (periph == NULL) {
475 		/* If the periph device does not exist, it will try to find it
476 		 * again */
477 		periph = ufshci_sim_find_periph(ctrlr,
478 		    (uint8_t)UFSHCI_WLUN_UFS_DEVICE);
479 		if (periph)
480 			ctrlr->ufs_device_wlun_periph = periph;
481 	}
482 
483 	if (periph == NULL) {
484 		ufshci_printf(ctrlr,
485 		    "Well-known LUN `UFS Device (0x50)` not found\n");
486 		return ENODEV;
487 	}
488 	cam_periph_lock(periph);
489 	ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
490 	if (!ccb) {
491 		cam_periph_unlock(periph);
492 		cam_periph_release(periph);
493 		return ENOMEM;
494 	}
495 
496 	scsi_start_stop(&ccb->csio,
497 	    /*retries*/ 4,
498 	    /*cbfcnp*/ NULL,
499 	    /*tag_action*/ MSG_SIMPLE_Q_TAG,
500 	    /*start*/ start ? 1 : 0,
501 	    /*load_eject*/ 0,
502 	    /*immediate*/ immed ? 1 : 0,
503 	    /*power_condition*/ power_condition, SSD_MIN_SIZE,
504 	    ctrlr->device_init_timeout_in_ms);
505 
506 	ccb->ccb_h.flags |= CAM_DIR_NONE | CAM_DEV_QFRZDIS;
507 
508 	err = cam_periph_runccb(ccb, NULL, 0, SF_RETRY_UA, NULL);
509 
510 	cam_periph_unlock(periph);
511 	/* Release periph reference */
512 	cam_periph_release(periph);
513 
514 	return (err == 0) ? 0 : EIO;
515 }
516