Index: sys/cam/cam_ccb.h =================================================================== --- sys/cam/cam_ccb.h +++ sys/cam/cam_ccb.h @@ -1268,6 +1268,7 @@ #define CDAI_TYPE_EXT_INQ 5 #define CDAI_TYPE_NVME_CNTRL 6 /* NVMe Identify Controller data */ #define CDAI_TYPE_NVME_NS 7 /* NVMe Identify Namespace data */ +#define CDAI_TYPE_MODE_DEV_SPEC 8 /* Device specific byte of mode header */ off_t bufsiz; /* IN: Size of external buffer */ #define CAM_SCSI_DEVID_MAXLEN 65536 /* length in buffer is an uint16_t */ off_t provsiz; /* OUT: Size required/used */ Index: sys/cam/cam_xpt_internal.h =================================================================== --- sys/cam/cam_xpt_internal.h +++ sys/cam/cam_xpt_internal.h @@ -133,6 +133,7 @@ * queuing for a device. */ u_int8_t queue_flags; /* Queue flags from the control page */ + u_int8_t dev_spec; /* Device specific byte of mode header */ u_int8_t serial_num_len; u_int8_t *serial_num; u_int32_t flags; Index: sys/cam/scsi/scsi_da.c =================================================================== --- sys/cam/scsi/scsi_da.c +++ sys/cam/scsi/scsi_da.c @@ -5596,6 +5596,34 @@ softc->disk->d_devstat->block_size = softc->params.secsize; softc->disk->d_devstat->flags &= ~DEVSTAT_BS_UNAVAILABLE; + /* Refresh the write protect status as well. */ + { + struct ccb_dev_advinfo cdai; + uint8_t dev_spec; + + xpt_setup_ccb(&cdai.ccb_h, periph->path, CAM_PRIORITY_NORMAL); + cdai.ccb_h.func_code = XPT_DEV_ADVINFO; + cdai.buftype = CDAI_TYPE_MODE_DEV_SPEC; + cdai.flags = 0; + cdai.bufsiz = 1; + cdai.buf = &dev_spec; + xpt_action((union ccb *)&cdai); + if ((cdai.ccb_h.status & CAM_DEV_QFRZN) != 0) + cam_release_devq(cdai.ccb_h.path, 0, 0, 0, FALSE); + if (cdai.ccb_h.status != CAM_REQ_CMP) { + xpt_print(periph->path, "%s: failed to get dev_spec\n", + __func__); + /* Use cam_error_print() to decode the status */ + cam_error_print((union ccb *)&cdai, CAM_ESF_CAM_STATUS, + CAM_EPF_ALL); + } else { + if ((dev_spec & 0x80) != 0) + softc->disk->d_flags |= DISKFLAG_WRITE_PROTECT; + else + softc->disk->d_flags &= ~DISKFLAG_WRITE_PROTECT; + } + } + error = disk_resize(softc->disk, M_NOWAIT); if (error != 0) xpt_print(periph->path, "disk_resize(9) failed, error = %d\n", error); Index: sys/cam/scsi/scsi_xpt.c =================================================================== --- sys/cam/scsi/scsi_xpt.c +++ sys/cam/scsi/scsi_xpt.c @@ -1423,6 +1423,7 @@ + mode_hdr->blk_desc_len; page = (struct scsi_control_page *)offset; path->device->queue_flags = page->queue_flags; + path->device->dev_spec = mode_hdr->dev_spec; } else if (cam_periph_error(done_ccb, 0, SF_RETRY_UA|SF_NO_PRINT, &softc->saved_ccb) == ERESTART) { @@ -2615,6 +2616,18 @@ amt = cdai->bufsiz; memcpy(cdai->buf, device->ext_inq, amt); break; + case CDAI_TYPE_MODE_DEV_SPEC: + /* + * We fetch mode parameter header during probe. + * We don't allow changing it. + */ + if (cdai->flags & CDAI_FLAG_STORE) + return; + cdai->provsiz = 1; + if (cdai->bufsiz == 0) + break; + cdai->buf[0] = device->dev_spec; + break; default: return; } Index: sys/geom/geom_disk.h =================================================================== --- sys/geom/geom_disk.h +++ sys/geom/geom_disk.h @@ -124,13 +124,14 @@ LIST_HEAD(,disk_alias) d_aliases; }; -#define DISKFLAG_RESERVED 0x1 /* Was NEEDSGIANT */ -#define DISKFLAG_OPEN 0x2 -#define DISKFLAG_CANDELETE 0x4 -#define DISKFLAG_CANFLUSHCACHE 0x8 -#define DISKFLAG_UNMAPPED_BIO 0x10 -#define DISKFLAG_DIRECT_COMPLETION 0x20 -#define DISKFLAG_CANZONE 0x80 +#define DISKFLAG_RESERVED 0x0001 /* Was NEEDSGIANT */ +#define DISKFLAG_OPEN 0x0002 +#define DISKFLAG_CANDELETE 0x0004 +#define DISKFLAG_CANFLUSHCACHE 0x0008 +#define DISKFLAG_UNMAPPED_BIO 0x0010 +#define DISKFLAG_DIRECT_COMPLETION 0x0020 +#define DISKFLAG_CANZONE 0x0080 +#define DISKFLAG_WRITE_PROTECT 0x0100 struct disk *disk_alloc(void); void disk_create(struct disk *disk, int version); Index: sys/geom/geom_disk.c =================================================================== --- sys/geom/geom_disk.c +++ sys/geom/geom_disk.c @@ -120,14 +120,18 @@ e += pp->ace; error = 0; if ((pp->acr + pp->acw + pp->ace) == 0 && (r + w + e) > 0) { - if (dp->d_open != NULL) { + /* + * It would be better to defer this decision to d_open if + * it was able to take flags. + */ + if (w > 0 && (dp->d_flags & DISKFLAG_WRITE_PROTECT) != 0) + error = ENODEV; + if (error == 0 && dp->d_open != NULL) error = dp->d_open(dp); - if (bootverbose && error != 0) - printf("Opened disk %s -> %d\n", - pp->name, error); - if (error != 0) - return (error); - } + if (bootverbose && error != 0) + printf("Opened disk %s -> %d\n", pp->name, error); + if (error != 0) + return (error); pp->sectorsize = dp->d_sectorsize; if (dp->d_maxsize == 0) { printf("WARNING: Disk drive %s%d has no d_maxsize\n", @@ -1041,7 +1045,8 @@ "\4CANFLUSHCACHE" "\5UNMAPPEDBIO" "\6DIRECTCOMPLETION" - "\10CANZONE"); + "\10CANZONE" + "\11WRITEPROTECT"); sbuf_finish(sb); error = SYSCTL_OUT(req, sbuf_data(sb), sbuf_len(sb) + 1);