1223695Sdfr/*-
2223695Sdfr * Copyright (c) 2011 Google, Inc.
3223695Sdfr * All rights reserved.
4223695Sdfr *
5223695Sdfr * Redistribution and use in source and binary forms, with or without
6223695Sdfr * modification, are permitted provided that the following conditions
7223695Sdfr * are met:
8223695Sdfr * 1. Redistributions of source code must retain the above copyright
9223695Sdfr *    notice, this list of conditions and the following disclaimer.
10223695Sdfr * 2. Redistributions in binary form must reproduce the above copyright
11223695Sdfr *    notice, this list of conditions and the following disclaimer in the
12223695Sdfr *    documentation and/or other materials provided with the distribution.
13223695Sdfr *
14223695Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15223695Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16223695Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17223695Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18223695Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19223695Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20223695Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21223695Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22223695Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23223695Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24223695Sdfr * SUCH DAMAGE.
25223695Sdfr */
26223695Sdfr
27223695Sdfr#include <sys/cdefs.h>
28223695Sdfr__FBSDID("$FreeBSD$");
29223695Sdfr
30223695Sdfr/*
31223695Sdfr * Userboot disk image handling.
32223695Sdfr */
33223695Sdfr
34243243Sae#include <sys/disk.h>
35223695Sdfr#include <stand.h>
36223695Sdfr#include <stdarg.h>
37223695Sdfr#include <bootstrap.h>
38223695Sdfr
39223695Sdfr#include "disk.h"
40223695Sdfr#include "libuserboot.h"
41223695Sdfr
42243243Saestruct userdisk_info {
43243243Sae	uint64_t	mediasize;
44243243Sae	uint16_t	sectorsize;
45243243Sae};
46243243Sae
47223695Sdfrint userboot_disk_maxunit = 0;
48223695Sdfr
49243243Saestatic int userdisk_maxunit = 0;
50243243Saestatic struct userdisk_info	*ud_info;
51243243Sae
52223695Sdfrstatic int	userdisk_init(void);
53243243Saestatic void	userdisk_cleanup(void);
54223695Sdfrstatic int	userdisk_strategy(void *devdata, int flag, daddr_t dblk,
55223695Sdfr		    size_t size, char *buf, size_t *rsize);
56223695Sdfrstatic int	userdisk_open(struct open_file *f, ...);
57223695Sdfrstatic int	userdisk_close(struct open_file *f);
58243243Saestatic int	userdisk_ioctl(struct open_file *f, u_long cmd, void *data);
59223695Sdfrstatic void	userdisk_print(int verbose);
60223695Sdfr
61223695Sdfrstruct devsw userboot_disk = {
62223695Sdfr	"disk",
63223695Sdfr	DEVT_DISK,
64223695Sdfr	userdisk_init,
65223695Sdfr	userdisk_strategy,
66223695Sdfr	userdisk_open,
67223695Sdfr	userdisk_close,
68243243Sae	userdisk_ioctl,
69223695Sdfr	userdisk_print,
70243243Sae	userdisk_cleanup
71223695Sdfr};
72223695Sdfr
73223695Sdfr/*
74243243Sae * Initialize userdisk_info structure for each disk.
75223695Sdfr */
76223695Sdfrstatic int
77223695Sdfruserdisk_init(void)
78223695Sdfr{
79243243Sae	off_t mediasize;
80243243Sae	u_int sectorsize;
81243243Sae	int i;
82223695Sdfr
83243243Sae	userdisk_maxunit = userboot_disk_maxunit;
84243243Sae	if (userdisk_maxunit > 0) {
85243243Sae		ud_info = malloc(sizeof(*ud_info) * userdisk_maxunit);
86243243Sae		if (ud_info == NULL)
87243243Sae			return (ENOMEM);
88243243Sae		for (i = 0; i < userdisk_maxunit; i++) {
89243243Sae			if (CALLBACK(diskioctl, i, DIOCGSECTORSIZE,
90243243Sae			    &sectorsize) != 0 || CALLBACK(diskioctl, i,
91243243Sae			    DIOCGMEDIASIZE, &mediasize) != 0)
92243243Sae				return (ENXIO);
93243243Sae			ud_info[i].mediasize = mediasize;
94243243Sae			ud_info[i].sectorsize = sectorsize;
95243243Sae		}
96243243Sae	}
97243243Sae
98223695Sdfr	return(0);
99223695Sdfr}
100223695Sdfr
101243243Saestatic void
102243243Saeuserdisk_cleanup(void)
103243243Sae{
104243243Sae
105243243Sae	if (userdisk_maxunit > 0)
106243243Sae		free(ud_info);
107243243Sae	disk_cleanup(&userboot_disk);
108243243Sae}
109243243Sae
110223695Sdfr/*
111223695Sdfr * Print information about disks
112223695Sdfr */
113223695Sdfrstatic void
114223695Sdfruserdisk_print(int verbose)
115223695Sdfr{
116243243Sae	struct disk_devdesc dev;
117243243Sae	char line[80];
118243243Sae	int i;
119223695Sdfr
120243243Sae	for (i = 0; i < userdisk_maxunit; i++) {
121223695Sdfr		sprintf(line, "    disk%d:   Guest drive image\n", i);
122223695Sdfr		pager_output(line);
123223695Sdfr		dev.d_dev = &userboot_disk;
124223695Sdfr		dev.d_unit = i;
125223695Sdfr		dev.d_slice = -1;
126223695Sdfr		dev.d_partition = -1;
127243243Sae		if (disk_open(&dev, ud_info[i].mediasize,
128243243Sae		    ud_info[i].sectorsize, 0) == 0) {
129243243Sae			sprintf(line, "    disk%d", i);
130243243Sae			disk_print(&dev, line, verbose);
131243243Sae			disk_close(&dev);
132243243Sae		}
133223695Sdfr	}
134223695Sdfr}
135223695Sdfr
136223695Sdfr/*
137223695Sdfr * Attempt to open the disk described by (dev) for use by (f).
138223695Sdfr */
139223695Sdfrstatic int
140223695Sdfruserdisk_open(struct open_file *f, ...)
141223695Sdfr{
142223695Sdfr	va_list			ap;
143223695Sdfr	struct disk_devdesc	*dev;
144223695Sdfr
145223695Sdfr	va_start(ap, f);
146223695Sdfr	dev = va_arg(ap, struct disk_devdesc *);
147223695Sdfr	va_end(ap);
148223695Sdfr
149243243Sae	if (dev->d_unit < 0 || dev->d_unit >= userdisk_maxunit)
150223695Sdfr		return (EIO);
151223695Sdfr
152243243Sae	return (disk_open(dev, ud_info[dev->d_unit].mediasize,
153243243Sae	    ud_info[dev->d_unit].sectorsize, 0));
154223695Sdfr}
155223695Sdfr
156223695Sdfrstatic int
157223695Sdfruserdisk_close(struct open_file *f)
158223695Sdfr{
159243243Sae	struct disk_devdesc *dev;
160223695Sdfr
161243243Sae	dev = (struct disk_devdesc *)f->f_devdata;
162243243Sae	return (disk_close(dev));
163223695Sdfr}
164223695Sdfr
165223695Sdfrstatic int
166223695Sdfruserdisk_strategy(void *devdata, int rw, daddr_t dblk, size_t size,
167223695Sdfr    char *buf, size_t *rsize)
168223695Sdfr{
169223695Sdfr	struct disk_devdesc *dev = devdata;
170223695Sdfr	uint64_t	off;
171223695Sdfr	size_t		resid;
172223695Sdfr	int		rc;
173223695Sdfr
174223695Sdfr	if (rw == F_WRITE)
175223695Sdfr		return (EROFS);
176223695Sdfr	if (rw != F_READ)
177223695Sdfr		return (EINVAL);
178223695Sdfr	if (rsize)
179223695Sdfr		*rsize = 0;
180243243Sae	off = (dblk + dev->d_offset) * ud_info[dev->d_unit].sectorsize;
181223695Sdfr	rc = CALLBACK(diskread, dev->d_unit, off, buf, size, &resid);
182223695Sdfr	if (rc)
183223695Sdfr		return (rc);
184223695Sdfr	if (rsize)
185223695Sdfr		*rsize = size - resid;
186223695Sdfr	return (0);
187223695Sdfr}
188243243Sae
189243243Saestatic int
190243243Saeuserdisk_ioctl(struct open_file *f, u_long cmd, void *data)
191243243Sae{
192243243Sae	struct disk_devdesc *dev;
193243243Sae
194243243Sae	dev = (struct disk_devdesc *)f->f_devdata;
195243243Sae	return (CALLBACK(diskioctl, dev->d_unit, cmd, data));
196243243Sae}
197