1 1.18 jdolecek /* $NetBSD: wd.c,v 1.18 2019/01/08 19:41:09 jdolecek Exp $ */ 2 1.1 cdi 3 1.1 cdi /*- 4 1.1 cdi * Copyright (c) 2003 The NetBSD Foundation, Inc. 5 1.1 cdi * All rights reserved. 6 1.1 cdi * 7 1.1 cdi * This code is derived from software contributed to The NetBSD Foundation 8 1.1 cdi * by Manuel Bouyer. 9 1.1 cdi * 10 1.1 cdi * Redistribution and use in source and binary forms, with or without 11 1.1 cdi * modification, are permitted provided that the following conditions 12 1.1 cdi * are met: 13 1.1 cdi * 1. Redistributions of source code must retain the above copyright 14 1.1 cdi * notice, this list of conditions and the following disclaimer. 15 1.1 cdi * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 cdi * notice, this list of conditions and the following disclaimer in the 17 1.1 cdi * documentation and/or other materials provided with the distribution. 18 1.1 cdi * 19 1.1 cdi * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.1 cdi * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.1 cdi * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.1 cdi * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.1 cdi * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.1 cdi * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.1 cdi * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.1 cdi * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.1 cdi * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.1 cdi * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.1 cdi * POSSIBILITY OF SUCH DAMAGE. 30 1.1 cdi */ 31 1.1 cdi 32 1.13 he #include <sys/param.h> 33 1.1 cdi #include <sys/types.h> 34 1.1 cdi #include <sys/stdint.h> 35 1.1 cdi 36 1.1 cdi #include <lib/libsa/stand.h> 37 1.9 tsutsui #include <lib/libkern/libkern.h> 38 1.1 cdi 39 1.6 tsutsui #include <dev/raidframe/raidframevar.h> /* For RF_PROTECTED_SECTORS */ 40 1.1 cdi 41 1.1 cdi #include "boot.h" 42 1.1 cdi #include "wdvar.h" 43 1.1 cdi 44 1.1 cdi static int wd_get_params(struct wd_softc *wd); 45 1.1 cdi static int wdgetdisklabel(struct wd_softc *wd); 46 1.1 cdi static void wdgetdefaultlabel(struct wd_softc *wd, struct disklabel *lp); 47 1.1 cdi 48 1.1 cdi /* 49 1.1 cdi * Get drive parameters through 'device identify' command. 50 1.1 cdi */ 51 1.1 cdi int 52 1.7 tsutsui wd_get_params(struct wd_softc *wd) 53 1.1 cdi { 54 1.1 cdi int error; 55 1.7 tsutsui uint8_t buf[DEV_BSIZE]; 56 1.14 mrg struct ataparams *params = (struct ataparams *)buf; 57 1.1 cdi 58 1.7 tsutsui if ((error = wdc_exec_identify(wd, buf)) != 0) 59 1.7 tsutsui return error; 60 1.1 cdi 61 1.14 mrg wd->sc_params = *params; 62 1.1 cdi 63 1.1 cdi /* 48-bit LBA addressing */ 64 1.11 tsutsui if ((wd->sc_params.atap_cmd2_en & ATA_CMD2_LBA48) != 0) 65 1.1 cdi wd->sc_flags |= WDF_LBA48; 66 1.1 cdi 67 1.1 cdi /* Prior to ATA-4, LBA was optional. */ 68 1.11 tsutsui if ((wd->sc_params.atap_capabilities1 & WDC_CAP_LBA) != 0) 69 1.11 tsutsui wd->sc_flags |= WDF_LBA; 70 1.11 tsutsui 71 1.11 tsutsui if ((wd->sc_flags & WDF_LBA48) != 0) { 72 1.11 tsutsui DPRINTF(("Drive supports LBA48.\n")); 73 1.11 tsutsui wd->sc_capacity = 74 1.11 tsutsui ((uint64_t)wd->sc_params.atap_max_lba[3] << 48) | 75 1.11 tsutsui ((uint64_t)wd->sc_params.atap_max_lba[2] << 32) | 76 1.11 tsutsui ((uint64_t)wd->sc_params.atap_max_lba[1] << 16) | 77 1.11 tsutsui ((uint64_t)wd->sc_params.atap_max_lba[0] << 0); 78 1.11 tsutsui DPRINTF(("atap_max_lba = (0x%x, 0x%x, 0x%x, 0x%x)\n", 79 1.11 tsutsui wd->sc_params.atap_max_lba[3], 80 1.11 tsutsui wd->sc_params.atap_max_lba[2], 81 1.11 tsutsui wd->sc_params.atap_max_lba[1], 82 1.11 tsutsui wd->sc_params.atap_max_lba[0])); 83 1.11 tsutsui wd->sc_capacity28 = 84 1.11 tsutsui ((uint32_t)wd->sc_params.atap_capacity[1] << 16) | 85 1.11 tsutsui ((uint32_t)wd->sc_params.atap_capacity[0] << 0); 86 1.11 tsutsui DPRINTF(("atap_capacity = (0x%x, 0x%x)\n", 87 1.11 tsutsui wd->sc_params.atap_capacity[1], 88 1.11 tsutsui wd->sc_params.atap_capacity[0])); 89 1.11 tsutsui } else if ((wd->sc_flags & WDF_LBA) != 0) { 90 1.1 cdi DPRINTF(("Drive supports LBA.\n")); 91 1.11 tsutsui wd->sc_capacity = wd->sc_capacity28 = 92 1.11 tsutsui ((uint32_t)wd->sc_params.atap_capacity[1] << 16) | 93 1.11 tsutsui ((uint32_t)wd->sc_params.atap_capacity[0] << 0); 94 1.11 tsutsui } else { 95 1.11 tsutsui DPRINTF(("Drive doesn't support LBA; using CHS.\n")); 96 1.11 tsutsui wd->sc_capacity = wd->sc_capacity28 = 97 1.11 tsutsui wd->sc_params.atap_cylinders * 98 1.11 tsutsui wd->sc_params.atap_heads * 99 1.11 tsutsui wd->sc_params.atap_sectors; 100 1.1 cdi } 101 1.12 tsutsui DPRINTF(("wd->sc_capacity = %" PRId64 ", wd->sc_capacity28 = %d.\n", 102 1.12 tsutsui wd->sc_capacity, wd->sc_capacity28)); 103 1.1 cdi 104 1.7 tsutsui return 0; 105 1.1 cdi } 106 1.1 cdi 107 1.1 cdi /* 108 1.1 cdi * Initialize disk label to the default value. 109 1.1 cdi */ 110 1.1 cdi void 111 1.7 tsutsui wdgetdefaultlabel(struct wd_softc *wd, struct disklabel *lp) 112 1.1 cdi { 113 1.7 tsutsui 114 1.1 cdi memset(lp, 0, sizeof(struct disklabel)); 115 1.1 cdi 116 1.1 cdi lp->d_secsize = DEV_BSIZE; 117 1.1 cdi lp->d_ntracks = wd->sc_params.atap_heads; 118 1.1 cdi lp->d_nsectors = wd->sc_params.atap_sectors; 119 1.1 cdi lp->d_ncylinders = wd->sc_params.atap_cylinders; 120 1.1 cdi lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors; 121 1.1 cdi 122 1.1 cdi if (strcmp(wd->sc_params.atap_model, "ST506") == 0) 123 1.17 christos lp->d_type = DKTYPE_ST506; 124 1.1 cdi else 125 1.17 christos lp->d_type = DKTYPE_ESDI; 126 1.1 cdi 127 1.1 cdi strncpy(lp->d_typename, wd->sc_params.atap_model, 16); 128 1.1 cdi strncpy(lp->d_packname, "fictitious", 16); 129 1.1 cdi if (wd->sc_capacity > UINT32_MAX) 130 1.1 cdi lp->d_secperunit = UINT32_MAX; 131 1.1 cdi else 132 1.1 cdi lp->d_secperunit = wd->sc_capacity; 133 1.1 cdi lp->d_rpm = 3600; 134 1.1 cdi lp->d_interleave = 1; 135 1.1 cdi lp->d_flags = 0; 136 1.1 cdi 137 1.1 cdi lp->d_partitions[RAW_PART].p_offset = 0; 138 1.1 cdi lp->d_partitions[RAW_PART].p_size = 139 1.1 cdi lp->d_secperunit * (lp->d_secsize / DEV_BSIZE); 140 1.1 cdi lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED; 141 1.1 cdi lp->d_npartitions = MAXPARTITIONS; /* RAW_PART + 1 ??? */ 142 1.1 cdi 143 1.1 cdi lp->d_magic = DISKMAGIC; 144 1.1 cdi lp->d_magic2 = DISKMAGIC; 145 1.1 cdi lp->d_checksum = dkcksum(lp); 146 1.1 cdi } 147 1.1 cdi 148 1.1 cdi /* 149 1.1 cdi * Read disk label from the device. 150 1.1 cdi */ 151 1.1 cdi int 152 1.7 tsutsui wdgetdisklabel(struct wd_softc *wd) 153 1.1 cdi { 154 1.1 cdi char *msg; 155 1.1 cdi int sector; 156 1.1 cdi size_t rsize; 157 1.1 cdi struct disklabel *lp; 158 1.7 tsutsui uint8_t buf[DEV_BSIZE]; 159 1.16 joerg uint16_t magic; 160 1.1 cdi 161 1.1 cdi wdgetdefaultlabel(wd, &wd->sc_label); 162 1.1 cdi 163 1.1 cdi /* 164 1.1 cdi * Find NetBSD Partition in DOS partition table. 165 1.1 cdi */ 166 1.1 cdi sector = 0; 167 1.1 cdi if (wdstrategy(wd, F_READ, MBR_BBSECTOR, DEV_BSIZE, buf, &rsize)) 168 1.1 cdi return EOFFSET; 169 1.1 cdi 170 1.16 joerg memcpy(&magic, &buf[MBR_MAGIC_OFFSET], sizeof(magic)); 171 1.16 joerg if (magic == MBR_MAGIC) { 172 1.1 cdi int i; 173 1.1 cdi struct mbr_partition *mp; 174 1.1 cdi 175 1.1 cdi /* 176 1.1 cdi * Lookup NetBSD slice. If there is none, go ahead 177 1.1 cdi * and try to read the disklabel off sector #0. 178 1.1 cdi */ 179 1.2 lukem mp = (struct mbr_partition *)&buf[MBR_PART_OFFSET]; 180 1.2 lukem for (i = 0; i < MBR_PART_COUNT; i++) { 181 1.2 lukem if (mp[i].mbrp_type == MBR_PTYPE_NETBSD) { 182 1.1 cdi sector = mp[i].mbrp_start; 183 1.1 cdi break; 184 1.1 cdi } 185 1.1 cdi } 186 1.1 cdi } 187 1.1 cdi 188 1.1 cdi if (wdstrategy(wd, F_READ, sector + LABELSECTOR, DEV_BSIZE, 189 1.7 tsutsui buf, &rsize)) 190 1.1 cdi return EOFFSET; 191 1.1 cdi 192 1.7 tsutsui if ((msg = getdisklabel(buf + LABELOFFSET, &wd->sc_label))) 193 1.1 cdi printf("wd%d: getdisklabel: %s\n", wd->sc_unit, msg); 194 1.1 cdi 195 1.1 cdi lp = &wd->sc_label; 196 1.1 cdi 197 1.1 cdi /* check partition */ 198 1.1 cdi if ((wd->sc_part >= lp->d_npartitions) || 199 1.7 tsutsui (lp->d_partitions[wd->sc_part].p_fstype == FS_UNUSED)) { 200 1.1 cdi DPRINTF(("illegal partition\n")); 201 1.7 tsutsui return EPART; 202 1.1 cdi } 203 1.1 cdi 204 1.1 cdi DPRINTF(("label info: d_secsize %d, d_nsectors %d, d_ncylinders %d," 205 1.11 tsutsui " d_ntracks %d, d_secpercyl %d\n", 206 1.7 tsutsui wd->sc_label.d_secsize, 207 1.7 tsutsui wd->sc_label.d_nsectors, 208 1.7 tsutsui wd->sc_label.d_ncylinders, 209 1.7 tsutsui wd->sc_label.d_ntracks, 210 1.7 tsutsui wd->sc_label.d_secpercyl)); 211 1.1 cdi 212 1.7 tsutsui return 0; 213 1.1 cdi } 214 1.1 cdi 215 1.1 cdi /* 216 1.1 cdi * Open device (read drive parameters and disklabel) 217 1.1 cdi */ 218 1.1 cdi int 219 1.1 cdi wdopen(struct open_file *f, ...) 220 1.1 cdi { 221 1.1 cdi int error; 222 1.1 cdi va_list ap; 223 1.4 tsutsui u_int unit, part; 224 1.1 cdi struct wd_softc *wd; 225 1.1 cdi 226 1.1 cdi va_start(ap, f); 227 1.3 he unit = va_arg(ap, u_int); 228 1.3 he part = va_arg(ap, u_int); 229 1.1 cdi va_end(ap); 230 1.1 cdi 231 1.1 cdi DPRINTF(("wdopen: %d:%d\n", unit, part)); 232 1.1 cdi 233 1.1 cdi wd = alloc(sizeof(struct wd_softc)); 234 1.1 cdi if (wd == NULL) 235 1.1 cdi return ENOMEM; 236 1.1 cdi 237 1.1 cdi memset(wd, 0, sizeof(struct wd_softc)); 238 1.1 cdi 239 1.1 cdi if (wdc_init(wd, &unit) != 0) 240 1.1 cdi return (ENXIO); 241 1.1 cdi 242 1.1 cdi wd->sc_part = part; 243 1.1 cdi wd->sc_unit = unit; 244 1.1 cdi 245 1.7 tsutsui if ((error = wd_get_params(wd)) != 0) 246 1.7 tsutsui return error; 247 1.1 cdi 248 1.7 tsutsui if ((error = wdgetdisklabel(wd)) != 0) 249 1.1 cdi return error; 250 1.1 cdi 251 1.1 cdi f->f_devdata = wd; 252 1.7 tsutsui return 0; 253 1.1 cdi } 254 1.1 cdi 255 1.1 cdi /* 256 1.1 cdi * Close device. 257 1.1 cdi */ 258 1.1 cdi int 259 1.1 cdi wdclose(struct open_file *f) 260 1.1 cdi { 261 1.7 tsutsui 262 1.1 cdi return 0; 263 1.1 cdi } 264 1.1 cdi 265 1.1 cdi /* 266 1.1 cdi * Read some data. 267 1.1 cdi */ 268 1.1 cdi int 269 1.9 tsutsui wdstrategy(void *f, int rw, daddr_t dblk, size_t size, void *p, size_t *rsize) 270 1.1 cdi { 271 1.1 cdi int i, nsect; 272 1.1 cdi daddr_t blkno; 273 1.6 tsutsui struct wd_softc *wd; 274 1.6 tsutsui struct partition *pp; 275 1.9 tsutsui uint8_t *buf; 276 1.1 cdi 277 1.1 cdi if (size == 0) 278 1.7 tsutsui return 0; 279 1.1 cdi 280 1.1 cdi if (rw != F_READ) 281 1.1 cdi return EOPNOTSUPP; 282 1.1 cdi 283 1.9 tsutsui buf = p; 284 1.6 tsutsui wd = f; 285 1.6 tsutsui pp = &wd->sc_label.d_partitions[wd->sc_part]; 286 1.6 tsutsui 287 1.1 cdi nsect = howmany(size, wd->sc_label.d_secsize); 288 1.6 tsutsui blkno = dblk + pp->p_offset; 289 1.6 tsutsui if (pp->p_fstype == FS_RAID) 290 1.6 tsutsui blkno += RF_PROTECTED_SECTORS; 291 1.1 cdi 292 1.1 cdi for (i = 0; i < nsect; i++, blkno++) { 293 1.1 cdi int error; 294 1.1 cdi 295 1.7 tsutsui if ((error = wdc_exec_read(wd, WDCC_READ, blkno, buf)) != 0) 296 1.7 tsutsui return error; 297 1.1 cdi 298 1.1 cdi buf += wd->sc_label.d_secsize; 299 1.1 cdi } 300 1.1 cdi 301 1.1 cdi *rsize = size; 302 1.7 tsutsui return 0; 303 1.1 cdi } 304