1 /* $NetBSD: wd.c,v 1.10 2008/04/28 20:23:16 martin 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/types.h> 33 #include <sys/stdint.h> 34 35 #include <lib/libsa/stand.h> 36 #include <lib/libkern/libkern.h> 37 38 #include <machine/param.h> 39 #include <machine/stdarg.h> 40 #include <dev/raidframe/raidframevar.h> /* For RF_PROTECTED_SECTORS */ 41 42 #include "boot.h" 43 #include "wdvar.h" 44 45 static int wd_get_params(struct wd_softc *wd); 46 static int wdgetdisklabel(struct wd_softc *wd); 47 static void wdgetdefaultlabel(struct wd_softc *wd, struct disklabel *lp); 48 49 /* 50 * Get drive parameters through 'device identify' command. 51 */ 52 int 53 wd_get_params(struct wd_softc *wd) 54 { 55 int error; 56 uint8_t buf[DEV_BSIZE]; 57 58 if ((error = wdc_exec_identify(wd, buf)) != 0) 59 return error; 60 61 wd->sc_params = *(struct ataparams *)buf; 62 63 /* 48-bit LBA addressing */ 64 if ((wd->sc_params.atap_cmd2_en & ATA_CMD2_LBA48) != 0) { 65 DPRINTF(("Drive supports LBA48.\n")); 66 #if defined(_ENABLE_LBA48) 67 wd->sc_flags |= WDF_LBA48; 68 #endif 69 } 70 71 /* Prior to ATA-4, LBA was optional. */ 72 if ((wd->sc_params.atap_capabilities1 & WDC_CAP_LBA) != 0) { 73 DPRINTF(("Drive supports LBA.\n")); 74 wd->sc_flags |= WDF_LBA; 75 } 76 77 return 0; 78 } 79 80 /* 81 * Initialize disk label to the default value. 82 */ 83 void 84 wdgetdefaultlabel(struct wd_softc *wd, struct disklabel *lp) 85 { 86 87 memset(lp, 0, sizeof(struct disklabel)); 88 89 lp->d_secsize = DEV_BSIZE; 90 lp->d_ntracks = wd->sc_params.atap_heads; 91 lp->d_nsectors = wd->sc_params.atap_sectors; 92 lp->d_ncylinders = wd->sc_params.atap_cylinders; 93 lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors; 94 95 if (strcmp(wd->sc_params.atap_model, "ST506") == 0) 96 lp->d_type = DTYPE_ST506; 97 else 98 lp->d_type = DTYPE_ESDI; 99 100 strncpy(lp->d_typename, wd->sc_params.atap_model, 16); 101 strncpy(lp->d_packname, "fictitious", 16); 102 if (wd->sc_capacity > UINT32_MAX) 103 lp->d_secperunit = UINT32_MAX; 104 else 105 lp->d_secperunit = wd->sc_capacity; 106 lp->d_rpm = 3600; 107 lp->d_interleave = 1; 108 lp->d_flags = 0; 109 110 lp->d_partitions[RAW_PART].p_offset = 0; 111 lp->d_partitions[RAW_PART].p_size = 112 lp->d_secperunit * (lp->d_secsize / DEV_BSIZE); 113 lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED; 114 lp->d_npartitions = MAXPARTITIONS; /* RAW_PART + 1 ??? */ 115 116 lp->d_magic = DISKMAGIC; 117 lp->d_magic2 = DISKMAGIC; 118 lp->d_checksum = dkcksum(lp); 119 } 120 121 /* 122 * Read disk label from the device. 123 */ 124 int 125 wdgetdisklabel(struct wd_softc *wd) 126 { 127 char *msg; 128 int sector; 129 size_t rsize; 130 struct disklabel *lp; 131 uint8_t buf[DEV_BSIZE]; 132 133 wdgetdefaultlabel(wd, &wd->sc_label); 134 135 /* 136 * Find NetBSD Partition in DOS partition table. 137 */ 138 sector = 0; 139 if (wdstrategy(wd, F_READ, MBR_BBSECTOR, DEV_BSIZE, buf, &rsize)) 140 return EOFFSET; 141 142 if (*(uint16_t *)&buf[MBR_MAGIC_OFFSET] == MBR_MAGIC) { 143 int i; 144 struct mbr_partition *mp; 145 146 /* 147 * Lookup NetBSD slice. If there is none, go ahead 148 * and try to read the disklabel off sector #0. 149 */ 150 mp = (struct mbr_partition *)&buf[MBR_PART_OFFSET]; 151 for (i = 0; i < MBR_PART_COUNT; i++) { 152 if (mp[i].mbrp_type == MBR_PTYPE_NETBSD) { 153 sector = mp[i].mbrp_start; 154 break; 155 } 156 } 157 } 158 159 if (wdstrategy(wd, F_READ, sector + LABELSECTOR, DEV_BSIZE, 160 buf, &rsize)) 161 return EOFFSET; 162 163 if ((msg = getdisklabel(buf + LABELOFFSET, &wd->sc_label))) 164 printf("wd%d: getdisklabel: %s\n", wd->sc_unit, msg); 165 166 lp = &wd->sc_label; 167 168 /* check partition */ 169 if ((wd->sc_part >= lp->d_npartitions) || 170 (lp->d_partitions[wd->sc_part].p_fstype == FS_UNUSED)) { 171 DPRINTF(("illegal partition\n")); 172 return EPART; 173 } 174 175 DPRINTF(("label info: d_secsize %d, d_nsectors %d, d_ncylinders %d," 176 "d_ntracks %d, d_secpercyl %d\n", 177 wd->sc_label.d_secsize, 178 wd->sc_label.d_nsectors, 179 wd->sc_label.d_ncylinders, 180 wd->sc_label.d_ntracks, 181 wd->sc_label.d_secpercyl)); 182 183 return 0; 184 } 185 186 /* 187 * Open device (read drive parameters and disklabel) 188 */ 189 int 190 wdopen(struct open_file *f, ...) 191 { 192 int error; 193 va_list ap; 194 u_int unit, part; 195 struct wd_softc *wd; 196 197 va_start(ap, f); 198 unit = va_arg(ap, u_int); 199 part = va_arg(ap, u_int); 200 va_end(ap); 201 202 DPRINTF(("wdopen: %d:%d\n", unit, part)); 203 204 wd = alloc(sizeof(struct wd_softc)); 205 if (wd == NULL) 206 return ENOMEM; 207 208 memset(wd, 0, sizeof(struct wd_softc)); 209 210 if (wdc_init(wd, &unit) != 0) 211 return (ENXIO); 212 213 wd->sc_part = part; 214 wd->sc_unit = unit; 215 216 if ((error = wd_get_params(wd)) != 0) 217 return error; 218 219 if ((error = wdgetdisklabel(wd)) != 0) 220 return error; 221 222 f->f_devdata = wd; 223 return 0; 224 } 225 226 /* 227 * Close device. 228 */ 229 int 230 wdclose(struct open_file *f) 231 { 232 233 return 0; 234 } 235 236 /* 237 * Read some data. 238 */ 239 int 240 wdstrategy(void *f, int rw, daddr_t dblk, size_t size, void *p, size_t *rsize) 241 { 242 int i, nsect; 243 daddr_t blkno; 244 struct wd_softc *wd; 245 struct partition *pp; 246 uint8_t *buf; 247 248 if (size == 0) 249 return 0; 250 251 if (rw != F_READ) 252 return EOPNOTSUPP; 253 254 buf = p; 255 wd = f; 256 pp = &wd->sc_label.d_partitions[wd->sc_part]; 257 258 nsect = howmany(size, wd->sc_label.d_secsize); 259 blkno = dblk + pp->p_offset; 260 if (pp->p_fstype == FS_RAID) 261 blkno += RF_PROTECTED_SECTORS; 262 263 for (i = 0; i < nsect; i++, blkno++) { 264 int error; 265 266 if ((error = wdc_exec_read(wd, WDCC_READ, blkno, buf)) != 0) 267 return error; 268 269 buf += wd->sc_label.d_secsize; 270 } 271 272 *rsize = size; 273 return 0; 274 } 275