1/*- 2 * Copyright (C) 2008 Semihalf, Rafal Jaworowski 3 * Copyright (C) 2009 Semihalf, Piotr Ziecik 4 * Copyright (C) 2011 glevand (geoffrey.levand@mail.ru) 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 * 27 * $FreeBSD$ 28 */ 29 30#include <sys/endian.h> 31#include <machine/stdarg.h> 32#include <stand.h> 33#include <uuid.h> 34 35#define FSTYPENAMES 36#include <sys/disklabel.h> 37#include <sys/diskmbr.h> 38#include <sys/gpt.h> 39 40#include "bootstrap.h" 41#include "ps3bus.h" 42#include "ps3devdesc.h" 43#include "ps3stor.h" 44 45#define dev_printf(dev, fmt, args...) \ 46 printf("%s%d: " fmt "\n" , dev->d_dev->dv_name, dev->d_unit, ##args) 47 48#ifdef DISK_DEBUG 49#define DEBUG(fmt, args...) printf("%s:%d: " fmt "\n" , __func__ , __LINE__, ##args) 50#else 51#define DEBUG(fmt, args...) 52#endif 53 54struct open_dev; 55 56static int ps3disk_open_gpt(struct ps3_devdesc *dev, struct open_dev *od); 57static void ps3disk_uuid_letoh(uuid_t *uuid); 58 59static int ps3disk_init(void); 60static int ps3disk_strategy(void *devdata, int flag, daddr_t dblk, 61 size_t size, char *buf, size_t *rsize); 62static int ps3disk_open(struct open_file *f, ...); 63static int ps3disk_close(struct open_file *f); 64static void ps3disk_print(int verbose); 65 66struct devsw ps3disk = { 67 "disk", 68 DEVT_DISK, 69 ps3disk_init, 70 ps3disk_strategy, 71 ps3disk_open, 72 ps3disk_close, 73 noioctl, 74 ps3disk_print, 75}; 76 77struct gpt_part { 78 int gp_index; 79 uuid_t gp_type; 80 uint64_t gp_start; 81 uint64_t gp_end; 82}; 83 84struct open_dev { 85 uint64_t od_start; 86 87 union { 88 struct { 89 int nparts; 90 struct gpt_part *parts; 91 } gpt; 92 } od_kind; 93}; 94 95#define od_gpt_nparts od_kind.gpt.nparts 96#define od_gpt_parts od_kind.gpt.parts 97 98static struct ps3_stordev stor_dev; 99 100static int ps3disk_init(void) 101{ 102 int err; 103 104 err = ps3stor_setup(&stor_dev, PS3_DEV_TYPE_STOR_DISK); 105 if (err) 106 return err; 107 108 return 0; 109} 110 111static int ps3disk_strategy(void *devdata, int flag, daddr_t dblk, 112 size_t size, char *buf, size_t *rsize) 113{ 114 struct ps3_devdesc *dev = (struct ps3_devdesc *) devdata; 115 struct open_dev *od = (struct open_dev *) dev->d_disk.data; 116 int err; 117 118 if (flag != F_READ) { 119 dev_printf(dev, "write operation is not supported!\n"); 120 return EROFS; 121 } 122 123 if (size % stor_dev.sd_blksize) { 124 dev_printf(dev, "size=%u is not multiple of device block size=%llu\n", 125 size, stor_dev.sd_blksize); 126 return EIO; 127 } 128 129 if (rsize) 130 *rsize = 0; 131 132 err = ps3stor_read_sectors(&stor_dev, dev->d_unit, od->od_start + dblk, 133 size / stor_dev.sd_blksize, 0, buf); 134 135 if (!err && rsize) 136 *rsize = size; 137 138 if (err) 139 dev_printf(dev, "read operation failed dblk=%llu size=%d err=%d\n", 140 dblk, size, err); 141 142 return err; 143} 144 145static int ps3disk_open(struct open_file *f, ...) 146{ 147 va_list ap; 148 struct ps3_devdesc *dev; 149 struct open_dev *od; 150 int err; 151 152 va_start(ap, f); 153 dev = va_arg(ap, struct ps3_devdesc *); 154 va_end(ap); 155 156 od = malloc(sizeof(struct open_dev)); 157 if (!od) { 158 dev_printf(dev, "couldn't allocate memory for new open_dev\n"); 159 return ENOMEM; 160 } 161 162 err = ps3disk_open_gpt(dev, od); 163 164 if (err) { 165 dev_printf(dev, "couldn't open GPT disk error=%d\n", err); 166 free(od); 167 } else { 168 ((struct ps3_devdesc *) (f->f_devdata))->d_disk.data = od; 169 } 170 171 return err; 172} 173 174static int ps3disk_close(struct open_file *f) 175{ 176 struct ps3_devdesc *dev = f->f_devdata; 177 struct open_dev *od = dev->d_disk.data; 178 179 if (dev->d_disk.ptype == PTYPE_GPT && od->od_gpt_nparts) 180 free(od->od_gpt_parts); 181 182 free(od); 183 184 dev->d_disk.data = NULL; 185 186 return 0; 187} 188 189static void ps3disk_print(int verbose) 190{ 191} 192 193static int ps3disk_open_gpt(struct ps3_devdesc *dev, struct open_dev *od) 194{ 195 char buf[512]; 196 struct gpt_hdr *hdr; 197 struct gpt_ent *ent; 198 daddr_t slba, elba, lba; 199 int nparts, eps, i, part, err; 200 201 od->od_gpt_nparts = 0; 202 od->od_gpt_parts = NULL; 203 204 err = ps3stor_read_sectors(&stor_dev, dev->d_unit, 0, 1, 0, buf); 205 if (err) { 206 err = EIO; 207 goto out; 208 } 209 210 if (le16toh(*((uint16_t *) (buf + DOSMAGICOFFSET))) != DOSMAGIC) { 211 err = ENXIO; 212 goto out; 213 } 214 215 err = ps3stor_read_sectors(&stor_dev, dev->d_unit, 1, 1, 0, buf); 216 if (err) { 217 err = EIO; 218 goto out; 219 } 220 221 hdr = (struct gpt_hdr *) buf; 222 223 if (bcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)) || 224 le64toh(hdr->hdr_lba_self) != 1 || le32toh(hdr->hdr_revision) < 0x00010000 || 225 le32toh(hdr->hdr_entsz) < sizeof(struct gpt_ent) || 226 stor_dev.sd_blksize % le32toh(hdr->hdr_entsz) != 0) { 227 err = ENXIO; 228 goto out; 229 } 230 231 nparts = 0; 232 eps = stor_dev.sd_blksize / le32toh(hdr->hdr_entsz); 233 slba = le64toh(hdr->hdr_lba_table); 234 elba = slba + le32toh(hdr->hdr_entries) / eps; 235 236 for (lba = slba; lba < elba; lba++) { 237 err = ps3stor_read_sectors(&stor_dev, dev->d_unit, lba, 1, 0, buf); 238 if (err) { 239 err = EIO; 240 goto out; 241 } 242 243 ent = (struct gpt_ent *) buf; 244 245 for (i = 0; i < eps; i++) { 246 if (uuid_is_nil(&ent[i].ent_type, NULL) || 247 le64toh(ent[i].ent_lba_start) == 0 || 248 le64toh(ent[i].ent_lba_end) < le64toh(ent[i].ent_lba_start)) 249 continue; 250 251 nparts++; 252 } 253 } 254 255 if (nparts) { 256 od->od_gpt_nparts = nparts; 257 258 od->od_gpt_parts = malloc(nparts * sizeof(struct gpt_part)); 259 if (!od->od_gpt_parts) { 260 err = ENOMEM; 261 goto out; 262 } 263 264 for (lba = slba, part = 0; lba < elba; lba++) { 265 err = ps3stor_read_sectors(&stor_dev, dev->d_unit, lba, 1, 0, buf); 266 if (err) { 267 err = EIO; 268 goto out; 269 } 270 271 ent = (struct gpt_ent *) buf; 272 273 for (i = 0; i < eps; i++) { 274 if (uuid_is_nil(&ent[i].ent_type, NULL) || 275 le64toh(ent[i].ent_lba_start) == 0 || 276 le64toh(ent[i].ent_lba_end) < le64toh(ent[i].ent_lba_start)) 277 continue; 278 279 od->od_gpt_parts[part].gp_index = (lba - slba) * eps + i + 1; 280 od->od_gpt_parts[part].gp_type = ent[i].ent_type; 281 od->od_gpt_parts[part].gp_start = le64toh(ent[i].ent_lba_start); 282 od->od_gpt_parts[part].gp_end = le64toh(ent[i].ent_lba_end); 283 ps3disk_uuid_letoh(&od->od_gpt_parts[part].gp_type); 284 part++; 285 } 286 } 287 } 288 289 dev->d_disk.ptype = PTYPE_GPT; 290 291 if (od->od_gpt_nparts && !dev->d_disk.pnum) 292 dev->d_disk.pnum = od->od_gpt_parts[0].gp_index; 293 294 for (i = 0; i < od->od_gpt_nparts; i++) 295 if (od->od_gpt_parts[i].gp_index == dev->d_disk.pnum) 296 od->od_start = od->od_gpt_parts[i].gp_start; 297 298 err = 0; 299 300out: 301 302 if (err && od->od_gpt_parts) 303 free(od->od_gpt_parts); 304 305 return err; 306} 307 308static void ps3disk_uuid_letoh(uuid_t *uuid) 309{ 310 uuid->time_low = le32toh(uuid->time_low); 311 uuid->time_mid = le16toh(uuid->time_mid); 312 uuid->time_hi_and_version = le16toh(uuid->time_hi_and_version); 313} 314