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