geom_pc98.c revision 114507
193354Sphk/*-
293354Sphk * Copyright (c) 2002 Poul-Henning Kamp
393354Sphk * Copyright (c) 2002 Networks Associates Technology, Inc.
493354Sphk * All rights reserved.
593354Sphk *
693354Sphk * This software was developed for the FreeBSD Project by Poul-Henning Kamp
793354Sphk * and NAI Labs, the Security Research Division of Network Associates, Inc.
893354Sphk * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
993354Sphk * DARPA CHATS research program.
1093354Sphk *
1193354Sphk * Redistribution and use in source and binary forms, with or without
1293354Sphk * modification, are permitted provided that the following conditions
1393354Sphk * are met:
1493354Sphk * 1. Redistributions of source code must retain the above copyright
1593354Sphk *    notice, this list of conditions and the following disclaimer.
1693354Sphk * 2. Redistributions in binary form must reproduce the above copyright
1793354Sphk *    notice, this list of conditions and the following disclaimer in the
1893354Sphk *    documentation and/or other materials provided with the distribution.
1993354Sphk *
2093354Sphk * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2193354Sphk * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2293354Sphk * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2393354Sphk * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2493354Sphk * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2593354Sphk * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2693354Sphk * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2793354Sphk * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2893354Sphk * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2993354Sphk * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3093354Sphk * SUCH DAMAGE.
3193354Sphk *
3293354Sphk * $FreeBSD: head/sys/geom/geom_pc98.c 114507 2003-05-02 06:34:51Z phk $
3393354Sphk */
3493354Sphk
3593354Sphk#include <sys/param.h>
36113013Sphk#include <sys/endian.h>
3793354Sphk#include <sys/systm.h>
3893354Sphk#include <sys/kernel.h>
39106559Snyan#include <sys/malloc.h>
4093354Sphk#include <sys/bio.h>
4193354Sphk#include <sys/lock.h>
4293354Sphk#include <sys/mutex.h>
43106559Snyan
44106559Snyan#include <sys/diskpc98.h>
4593354Sphk#include <geom/geom.h>
4693354Sphk#include <geom/geom_slice.h>
4793354Sphk
4897075Sphk#define PC98_CLASS_NAME "PC98"
4993354Sphk
5093354Sphkstruct g_pc98_softc {
51108591Snyan	u_int fwsectors, fwheads, sectorsize;
52108591Snyan	int type[NDOSPART];
53108591Snyan	u_char sec[8192];
5493354Sphk};
5593354Sphk
56108591Snyanstatic void
57108650Snyang_pc98_print(int i, struct pc98_partition *dp)
58108591Snyan{
59108591Snyan	char sname[17];
60108591Snyan
61108591Snyan	strncpy(sname, dp->dp_name, 16);
62108591Snyan	sname[16] = '\0';
63108591Snyan
64108591Snyan	g_hexdump(dp, sizeof(dp[0]));
65108591Snyan	printf("[%d] mid:%d(0x%x) sid:%d(0x%x)",
66108591Snyan	       i, dp->dp_mid, dp->dp_mid, dp->dp_sid, dp->dp_sid);
67108591Snyan	printf(" s:%d/%d/%d", dp->dp_scyl, dp->dp_shd, dp->dp_ssect);
68108591Snyan	printf(" e:%d/%d/%d", dp->dp_ecyl, dp->dp_ehd, dp->dp_esect);
69108591Snyan	printf(" sname:%s\n", sname);
70108591Snyan}
71108591Snyan
7293354Sphkstatic int
73108591Snyang_pc98_modify(struct g_geom *gp, struct g_pc98_softc *ms, u_char *sec)
74108591Snyan{
75108591Snyan	int i, error;
76108591Snyan	off_t s[NDOSPART], l[NDOSPART];
77108650Snyan	struct pc98_partition dp[NDOSPART];
78108591Snyan
79108591Snyan	g_topology_assert();
80108591Snyan
81108591Snyan	if (sec[0x1fe] != 0x55 || sec[0x1ff] != 0xaa)
82108591Snyan		return (EBUSY);
83108591Snyan
84108591Snyan#if 0
85108591Snyan	/*
86108591Snyan	 * XXX: Some sources indicate this is a magic sequence, but appearantly
87113292Sphk	 * XXX: it is not universal. Documentation would be wonderful to have.
88108591Snyan	 */
89108591Snyan	if (sec[4] != 'I' || sec[5] != 'P' || sec[6] != 'L' || sec[7] != '1')
90108591Snyan		return (EBUSY);
91108591Snyan#endif
92108591Snyan
93108591Snyan	for (i = 0; i < NDOSPART; i++)
94114414Snyan		pc98_partition_dec(
95108650Snyan			sec + 512 + i * sizeof(struct pc98_partition), &dp[i]);
96108591Snyan
97108591Snyan	for (i = 0; i < NDOSPART; i++) {
98108591Snyan		/* If start and end are identical it's bogus */
99108591Snyan		if (dp[i].dp_ssect == dp[i].dp_esect &&
100108591Snyan		    dp[i].dp_shd == dp[i].dp_ehd &&
101108591Snyan		    dp[i].dp_scyl == dp[i].dp_ecyl)
102108591Snyan			s[i] = l[i] = 0;
103108591Snyan		else if (dp[i].dp_ecyl == 0)
104108591Snyan			s[i] = l[i] = 0;
105108591Snyan		else {
106108591Snyan			s[i] = (off_t)dp[i].dp_scyl *
107108591Snyan				ms->fwsectors * ms->fwheads * ms->sectorsize;
108108591Snyan			l[i] = (off_t)(dp[i].dp_ecyl - dp[i].dp_scyl + 1) *
109108591Snyan				ms->fwsectors * ms->fwheads * ms->sectorsize;
110108591Snyan		}
111108591Snyan		if (bootverbose) {
112108591Snyan			printf("PC98 Slice %d on %s:\n", i + 1, gp->name);
113108591Snyan			g_pc98_print(i, dp + i);
114108591Snyan		}
115113292Sphk		if (s[i] < 0 || l[i] < 0)
116113292Sphk			error = EBUSY;
117113292Sphk		else
118113292Sphk			error = g_slice_config(gp, i, G_SLICE_CONFIG_CHECK,
119108591Snyan				       s[i], l[i], ms->sectorsize,
120108591Snyan				       "%ss%d", gp->name, i + 1);
121108591Snyan		if (error)
122108591Snyan			return (error);
123108591Snyan	}
124108591Snyan
125108591Snyan	for (i = 0; i < NDOSPART; i++) {
126108591Snyan		ms->type[i] = (dp[i].dp_sid << 8) | dp[i].dp_mid;
127108591Snyan		g_slice_config(gp, i, G_SLICE_CONFIG_SET, s[i], l[i],
128108591Snyan			       ms->sectorsize, "%ss%d", gp->name, i + 1);
129108591Snyan	}
130108591Snyan
131108591Snyan	bcopy(sec, ms->sec, sizeof (ms->sec));
132108591Snyan
133108591Snyan	return (0);
134108591Snyan}
135108591Snyan
136108591Snyanstatic void
137112989Sphkg_pc98_ioctl(void *arg, int flag)
138108591Snyan{
139108591Snyan	struct bio *bp;
140108591Snyan	struct g_geom *gp;
141108591Snyan	struct g_slicer *gsp;
142108591Snyan	struct g_pc98_softc *ms;
143108591Snyan	struct g_ioctl *gio;
144108591Snyan	struct g_consumer *cp;
145108591Snyan	u_char *sec;
146108591Snyan	int error;
147108591Snyan
148108591Snyan	bp = arg;
149112989Sphk	if (flag == EV_CANCEL) {
150112989Sphk		g_io_deliver(bp, ENXIO);
151112989Sphk		return;
152112989Sphk	}
153108591Snyan	gp = bp->bio_to->geom;
154108591Snyan	gsp = gp->softc;
155108591Snyan	ms = gsp->softc;
156108591Snyan	gio = (struct g_ioctl *)bp->bio_data;
157108591Snyan
158108591Snyan	/* The disklabel to set is the ioctl argument. */
159108591Snyan	sec = gio->data;
160108591Snyan
161108591Snyan	error = g_pc98_modify(gp, ms, sec);
162108591Snyan	if (error) {
163108591Snyan		g_io_deliver(bp, error);
164108591Snyan		return;
165108591Snyan	}
166108591Snyan	cp = LIST_FIRST(&gp->consumer);
167108591Snyan	error = g_write_data(cp, 0, sec, 8192);
168108591Snyan	g_io_deliver(bp, error);
169108591Snyan}
170108591Snyan
171108591Snyanstatic int
17293354Sphkg_pc98_start(struct bio *bp)
17393354Sphk{
174106559Snyan	struct g_provider *pp;
17593354Sphk	struct g_geom *gp;
176106559Snyan	struct g_pc98_softc *mp;
17793354Sphk	struct g_slicer *gsp;
178108591Snyan	struct g_ioctl *gio;
179108591Snyan	int idx, error;
18093354Sphk
181106559Snyan	pp = bp->bio_to;
182107953Sphk	idx = pp->index;
183106559Snyan	gp = pp->geom;
18493354Sphk	gsp = gp->softc;
185106559Snyan	mp = gsp->softc;
186106559Snyan	if (bp->bio_cmd == BIO_GETATTR) {
187107953Sphk		if (g_handleattr_int(bp, "PC98::type", mp->type[idx]))
188106559Snyan			return (1);
189106559Snyan		if (g_handleattr_off_t(bp, "PC98::offset",
190107953Sphk				       gsp->slices[idx].offset))
191106559Snyan			return (1);
192106559Snyan	}
193108591Snyan
194108591Snyan	/* We only handle ioctl(2) requests of the right format. */
195108591Snyan	if (strcmp(bp->bio_attribute, "GEOM::ioctl"))
196108591Snyan		return (0);
197108591Snyan	else if (bp->bio_length != sizeof(*gio))
198108591Snyan		return (0);
199108591Snyan	/* Get hold of the ioctl parameters. */
200108591Snyan	gio = (struct g_ioctl *)bp->bio_data;
201108591Snyan
202108591Snyan	switch (gio->cmd) {
203108591Snyan	case DIOCSPC98:
204108591Snyan		/*
205108591Snyan		 * These we cannot do without the topology lock and some
206108591Snyan		 * some I/O requests.  Ask the event-handler to schedule
207108591Snyan		 * us in a less restricted environment.
208108591Snyan		 */
209113937Sphk		error = g_post_event(g_pc98_ioctl, bp, M_NOWAIT, gp, NULL);
210108591Snyan		if (error)
211108591Snyan			g_io_deliver(bp, error);
212108591Snyan		/*
213108591Snyan		 * We must return non-zero to indicate that we will deal
214108591Snyan		 * with this bio, even though we have not done so yet.
215108591Snyan		 */
216108591Snyan		return (1);
217108591Snyan	default:
218108591Snyan		return (0);
219108591Snyan	}
220108591Snyan
22193354Sphk	return (0);
22293354Sphk}
22393354Sphk
22493354Sphkstatic void
225107953Sphkg_pc98_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp,
226106559Snyan		struct g_consumer *cp __unused, struct g_provider *pp)
22793354Sphk{
228106559Snyan	struct g_pc98_softc *mp;
229106559Snyan	struct g_slicer *gsp;
230108650Snyan	struct pc98_partition dp;
231107012Snyan	char sname[17];
23293354Sphk
233106559Snyan	gsp = gp->softc;
234106559Snyan	mp = gsp->softc;
23593354Sphk	g_slice_dumpconf(sb, indent, gp, cp, pp);
236106559Snyan	if (pp != NULL) {
237114414Snyan		pc98_partition_dec(
238108591Snyan			mp->sec + 512 +
239108650Snyan			pp->index * sizeof(struct pc98_partition), &dp);
240108591Snyan		strncpy(sname, dp.dp_name, 16);
241107012Snyan		sname[16] = '\0';
242107012Snyan		if (indent == NULL) {
243106559Snyan			sbuf_printf(sb, " ty %d", mp->type[pp->index]);
244107012Snyan			sbuf_printf(sb, " sn %s", sname);
245107012Snyan		} else {
246106559Snyan			sbuf_printf(sb, "%s<type>%d</type>\n", indent,
247106559Snyan				    mp->type[pp->index]);
248107012Snyan			sbuf_printf(sb, "%s<sname>%s</sname>\n", indent,
249107012Snyan				    sname);
250107012Snyan		}
251106559Snyan	}
25293354Sphk}
25393354Sphk
25493354Sphkstatic struct g_geom *
25593354Sphkg_pc98_taste(struct g_class *mp, struct g_provider *pp, int flags)
25693354Sphk{
25793354Sphk	struct g_geom *gp;
25893354Sphk	struct g_consumer *cp;
259108591Snyan	int error;
26093354Sphk	struct g_pc98_softc *ms;
26194287Sphk	struct g_slicer *gsp;
262106559Snyan	u_int fwsectors, fwheads, sectorsize;
263106559Snyan	u_char *buf;
26493354Sphk
26593354Sphk	g_trace(G_T_TOPOLOGY, "g_pc98_taste(%s,%s)", mp->name, pp->name);
26693354Sphk	g_topology_assert();
26793354Sphk	if (flags == G_TF_NORMAL &&
26893354Sphk	    !strcmp(pp->geom->class->name, PC98_CLASS_NAME))
26993354Sphk		return (NULL);
270106559Snyan	gp = g_slice_new(mp, NDOSPART, pp, &cp, &ms, sizeof *ms, g_pc98_start);
27193354Sphk	if (gp == NULL)
27293354Sphk		return (NULL);
27394287Sphk	gsp = gp->softc;
27493354Sphk	g_topology_unlock();
27593354Sphk	gp->dumpconf = g_pc98_dumpconf;
276113285Sphk	do {
27793354Sphk		if (gp->rank != 2 && flags == G_TF_NORMAL)
27893354Sphk			break;
279106559Snyan		error = g_getattr("GEOM::fwsectors", cp, &fwsectors);
280106559Snyan		if (error || fwsectors == 0) {
281106559Snyan			fwsectors = 17;
282113292Sphk			if (bootverbose)
283113292Sphk				printf("g_pc98_taste: guessing %d sectors\n",
284113292Sphk				    fwsectors);
28593354Sphk		}
286106559Snyan		error = g_getattr("GEOM::fwheads", cp, &fwheads);
287106559Snyan		if (error || fwheads == 0) {
288106559Snyan			fwheads = 8;
289113292Sphk			if (bootverbose)
290113292Sphk				printf("g_pc98_taste: guessing %d heads\n",
291113292Sphk				    fwheads);
29293354Sphk		}
293106559Snyan		sectorsize = cp->provider->sectorsize;
294106559Snyan		if (sectorsize < 512)
295106559Snyan			break;
296108591Snyan		buf = g_read_data(cp, 0, 8192, &error);
29793354Sphk		if (buf == NULL || error != 0)
29893354Sphk			break;
299108591Snyan		ms->fwsectors = fwsectors;
300108591Snyan		ms->fwheads = fwheads;
301108591Snyan		ms->sectorsize = sectorsize;
302108591Snyan		g_topology_lock();
303108591Snyan		g_pc98_modify(gp, ms, buf);
304108591Snyan		g_topology_unlock();
305106559Snyan		g_free(buf);
30693354Sphk		break;
307113285Sphk	} while (0);
30893354Sphk	g_topology_lock();
309107956Sphk	g_access_rel(cp, -1, 0, 0);
310107956Sphk	if (LIST_EMPTY(&gp->provider)) {
311114507Sphk		g_slice_spoiled(cp);
312107956Sphk		return (NULL);
313106559Snyan	}
314107956Sphk	return (gp);
31593354Sphk}
31693354Sphk
31793354Sphkstatic struct g_class g_pc98_class = {
318112552Sphk	.name = PC98_CLASS_NAME,
319112552Sphk	.taste = g_pc98_taste,
32098066Sphk	G_CLASS_INITIALIZER
32193354Sphk};
32293354Sphk
32393354SphkDECLARE_GEOM_CLASS(g_pc98_class, g_pc98);
324