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 33116196Sobrien#include <sys/cdefs.h> 34116196Sobrien__FBSDID("$FreeBSD$"); 35116196Sobrien 3693354Sphk#include <sys/param.h> 37113013Sphk#include <sys/endian.h> 3893354Sphk#include <sys/systm.h> 39219029Snetchild#include <sys/sysctl.h> 4093354Sphk#include <sys/kernel.h> 41138732Sphk#include <sys/fcntl.h> 42106559Snyan#include <sys/malloc.h> 4393354Sphk#include <sys/bio.h> 4493354Sphk#include <sys/lock.h> 4593354Sphk#include <sys/mutex.h> 46230643Sattilio#include <sys/proc.h> 47223921Sae#include <sys/sbuf.h> 48106559Snyan 49106559Snyan#include <sys/diskpc98.h> 5093354Sphk#include <geom/geom.h> 5193354Sphk#include <geom/geom_slice.h> 5293354Sphk 53219029SnetchildFEATURE(geom_pc98, "GEOM NEC PC9800 partitioning support"); 54219029Snetchild 5597075Sphk#define PC98_CLASS_NAME "PC98" 5693354Sphk 5793354Sphkstruct g_pc98_softc { 58108591Snyan u_int fwsectors, fwheads, sectorsize; 59254015Smarcel int type[PC98_NPARTS]; 60108591Snyan u_char sec[8192]; 6193354Sphk}; 6293354Sphk 63108591Snyanstatic void 64108650Snyang_pc98_print(int i, struct pc98_partition *dp) 65108591Snyan{ 66108591Snyan char sname[17]; 67108591Snyan 68108591Snyan strncpy(sname, dp->dp_name, 16); 69108591Snyan sname[16] = '\0'; 70108591Snyan 71123215Sscottl hexdump(dp, sizeof(dp[0]), NULL, 0); 72108591Snyan printf("[%d] mid:%d(0x%x) sid:%d(0x%x)", 73108591Snyan i, dp->dp_mid, dp->dp_mid, dp->dp_sid, dp->dp_sid); 74108591Snyan printf(" s:%d/%d/%d", dp->dp_scyl, dp->dp_shd, dp->dp_ssect); 75108591Snyan printf(" e:%d/%d/%d", dp->dp_ecyl, dp->dp_ehd, dp->dp_esect); 76108591Snyan printf(" sname:%s\n", sname); 77108591Snyan} 78108591Snyan 79148061Snyan/* 80148061Snyan * XXX: Add gctl_req arg and give good error msgs. 81148061Snyan * XXX: Check that length argument does not bring boot code inside any slice. 82148061Snyan */ 8393354Sphkstatic int 84148061Snyang_pc98_modify(struct g_geom *gp, struct g_pc98_softc *ms, u_char *sec, int len __unused) 85108591Snyan{ 86108591Snyan int i, error; 87254015Smarcel off_t s[PC98_NPARTS], l[PC98_NPARTS]; 88254015Smarcel struct pc98_partition dp[PC98_NPARTS]; 89108591Snyan 90108591Snyan g_topology_assert(); 91108591Snyan 92108591Snyan if (sec[0x1fe] != 0x55 || sec[0x1ff] != 0xaa) 93108591Snyan return (EBUSY); 94108591Snyan 95108591Snyan#if 0 96108591Snyan /* 97138221Simp * By convetion, it seems that the ipl program has a jump at location 98138221Simp * 0 to the real start of the boot loader. By convetion, it appears 99138221Simp * that after this jump, there's a string, terminated by at last one, 100138221Simp * if not more, zeros, followed by the target of the jump. FreeBSD's 101138221Simp * pc98 boot0 uses 'IPL1' followed by 3 zeros here, likely for 102138221Simp * compatibility with some older boot loader. Linux98's boot loader 103138221Simp * appears to use 'Linux 98' followed by only two. GRUB/98 appears to 104138221Simp * use 'GRUB/98 ' followed by none. These last two appear to be 105138221Simp * ported from the ia32 versions, but appear to show similar 106138221Simp * convention. Grub/98 has an additional NOP after the jmp, which 107138221Simp * isn't present in others. 108138221Simp * 109138221Simp * The following test was inspired by looking only at partitions 110138221Simp * with FreeBSD's boot0 (or one that it is compatible with). As 111138221Simp * such, if failed when other IPL programs were used. 112108591Snyan */ 113108591Snyan if (sec[4] != 'I' || sec[5] != 'P' || sec[6] != 'L' || sec[7] != '1') 114108591Snyan return (EBUSY); 115108591Snyan#endif 116108591Snyan 117254015Smarcel for (i = 0; i < PC98_NPARTS; i++) 118114414Snyan pc98_partition_dec( 119108650Snyan sec + 512 + i * sizeof(struct pc98_partition), &dp[i]); 120108591Snyan 121254015Smarcel for (i = 0; i < PC98_NPARTS; i++) { 122108591Snyan /* If start and end are identical it's bogus */ 123108591Snyan if (dp[i].dp_ssect == dp[i].dp_esect && 124108591Snyan dp[i].dp_shd == dp[i].dp_ehd && 125108591Snyan dp[i].dp_scyl == dp[i].dp_ecyl) 126108591Snyan s[i] = l[i] = 0; 127108591Snyan else if (dp[i].dp_ecyl == 0) 128108591Snyan s[i] = l[i] = 0; 129108591Snyan else { 130108591Snyan s[i] = (off_t)dp[i].dp_scyl * 131108591Snyan ms->fwsectors * ms->fwheads * ms->sectorsize; 132108591Snyan l[i] = (off_t)(dp[i].dp_ecyl - dp[i].dp_scyl + 1) * 133108591Snyan ms->fwsectors * ms->fwheads * ms->sectorsize; 134108591Snyan } 135108591Snyan if (bootverbose) { 136108591Snyan printf("PC98 Slice %d on %s:\n", i + 1, gp->name); 137108591Snyan g_pc98_print(i, dp + i); 138108591Snyan } 139113292Sphk if (s[i] < 0 || l[i] < 0) 140113292Sphk error = EBUSY; 141113292Sphk else 142113292Sphk error = g_slice_config(gp, i, G_SLICE_CONFIG_CHECK, 143108591Snyan s[i], l[i], ms->sectorsize, 144108591Snyan "%ss%d", gp->name, i + 1); 145108591Snyan if (error) 146108591Snyan return (error); 147108591Snyan } 148108591Snyan 149254015Smarcel for (i = 0; i < PC98_NPARTS; i++) { 150108591Snyan ms->type[i] = (dp[i].dp_sid << 8) | dp[i].dp_mid; 151108591Snyan g_slice_config(gp, i, G_SLICE_CONFIG_SET, s[i], l[i], 152108591Snyan ms->sectorsize, "%ss%d", gp->name, i + 1); 153108591Snyan } 154108591Snyan 155108591Snyan bcopy(sec, ms->sec, sizeof (ms->sec)); 156108591Snyan 157108591Snyan return (0); 158108591Snyan} 159108591Snyan 160119660Sphkstatic int 161138732Sphkg_pc98_ioctl(struct g_provider *pp, u_long cmd, void *data, int fflag, struct thread *td) 162108591Snyan{ 163108591Snyan struct g_geom *gp; 164119660Sphk struct g_pc98_softc *ms; 165108591Snyan struct g_slicer *gsp; 166108591Snyan struct g_consumer *cp; 167148061Snyan int error, opened; 168108591Snyan 169119660Sphk gp = pp->geom; 170108591Snyan gsp = gp->softc; 171108591Snyan ms = gsp->softc; 172108591Snyan 173148061Snyan opened = 0; 174148061Snyan error = 0; 175119660Sphk switch(cmd) { 176119660Sphk case DIOCSPC98: { 177138732Sphk if (!(fflag & FWRITE)) 178138732Sphk return (EPERM); 179119660Sphk DROP_GIANT(); 180119660Sphk g_topology_lock(); 181119660Sphk cp = LIST_FIRST(&gp->consumer); 182148061Snyan if (cp->acw == 0) { 183148061Snyan error = g_access(cp, 0, 1, 0); 184148061Snyan if (error == 0) 185148061Snyan opened = 1; 186148061Snyan } 187148061Snyan if (!error) 188148061Snyan error = g_pc98_modify(gp, ms, data, 8192); 189148061Snyan if (!error) 190148061Snyan error = g_write_data(cp, 0, data, 8192); 191148061Snyan if (opened) 192148061Snyan g_access(cp, 0, -1 , 0); 193119660Sphk g_topology_unlock(); 194119660Sphk PICKUP_GIANT(); 195119660Sphk return(error); 196108591Snyan } 197119660Sphk default: 198119660Sphk return (ENOIOCTL); 199119660Sphk } 200108591Snyan} 201108591Snyan 202108591Snyanstatic int 20393354Sphkg_pc98_start(struct bio *bp) 20493354Sphk{ 205106559Snyan struct g_provider *pp; 20693354Sphk struct g_geom *gp; 207106559Snyan struct g_pc98_softc *mp; 20893354Sphk struct g_slicer *gsp; 209119660Sphk int idx; 21093354Sphk 211106559Snyan pp = bp->bio_to; 212107953Sphk idx = pp->index; 213106559Snyan gp = pp->geom; 21493354Sphk gsp = gp->softc; 215106559Snyan mp = gsp->softc; 216106559Snyan if (bp->bio_cmd == BIO_GETATTR) { 217107953Sphk if (g_handleattr_int(bp, "PC98::type", mp->type[idx])) 218106559Snyan return (1); 219106559Snyan if (g_handleattr_off_t(bp, "PC98::offset", 220107953Sphk gsp->slices[idx].offset)) 221106559Snyan return (1); 222106559Snyan } 223108591Snyan 224119660Sphk return (0); 22593354Sphk} 22693354Sphk 22793354Sphkstatic void 228107953Sphkg_pc98_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, 229106559Snyan struct g_consumer *cp __unused, struct g_provider *pp) 23093354Sphk{ 231106559Snyan struct g_pc98_softc *mp; 232106559Snyan struct g_slicer *gsp; 233108650Snyan struct pc98_partition dp; 234107012Snyan char sname[17]; 23593354Sphk 236106559Snyan gsp = gp->softc; 237106559Snyan mp = gsp->softc; 23893354Sphk g_slice_dumpconf(sb, indent, gp, cp, pp); 239106559Snyan if (pp != NULL) { 240114414Snyan pc98_partition_dec( 241108591Snyan mp->sec + 512 + 242108650Snyan pp->index * sizeof(struct pc98_partition), &dp); 243108591Snyan strncpy(sname, dp.dp_name, 16); 244107012Snyan sname[16] = '\0'; 245107012Snyan if (indent == NULL) { 246106559Snyan sbuf_printf(sb, " ty %d", mp->type[pp->index]); 247107012Snyan sbuf_printf(sb, " sn %s", sname); 248107012Snyan } else { 249106559Snyan sbuf_printf(sb, "%s<type>%d</type>\n", indent, 250106559Snyan mp->type[pp->index]); 251107012Snyan sbuf_printf(sb, "%s<sname>%s</sname>\n", indent, 252107012Snyan sname); 253107012Snyan } 254106559Snyan } 25593354Sphk} 25693354Sphk 25793354Sphkstatic struct g_geom * 25893354Sphkg_pc98_taste(struct g_class *mp, struct g_provider *pp, int flags) 25993354Sphk{ 26093354Sphk struct g_geom *gp; 26193354Sphk struct g_consumer *cp; 262108591Snyan int error; 26393354Sphk struct g_pc98_softc *ms; 264106559Snyan u_int fwsectors, fwheads, sectorsize; 265106559Snyan u_char *buf; 26693354Sphk 26793354Sphk g_trace(G_T_TOPOLOGY, "g_pc98_taste(%s,%s)", mp->name, pp->name); 26893354Sphk g_topology_assert(); 26993354Sphk if (flags == G_TF_NORMAL && 27093354Sphk !strcmp(pp->geom->class->name, PC98_CLASS_NAME)) 27193354Sphk return (NULL); 272254015Smarcel gp = g_slice_new(mp, PC98_NPARTS, pp, &cp, &ms, sizeof *ms, 273254015Smarcel g_pc98_start); 27493354Sphk if (gp == NULL) 27593354Sphk return (NULL); 27693354Sphk g_topology_unlock(); 277113285Sphk do { 27893354Sphk if (gp->rank != 2 && flags == G_TF_NORMAL) 27993354Sphk break; 280106559Snyan error = g_getattr("GEOM::fwsectors", cp, &fwsectors); 281106559Snyan if (error || fwsectors == 0) { 282106559Snyan fwsectors = 17; 283113292Sphk if (bootverbose) 284113292Sphk printf("g_pc98_taste: guessing %d sectors\n", 285113292Sphk fwsectors); 28693354Sphk } 287106559Snyan error = g_getattr("GEOM::fwheads", cp, &fwheads); 288106559Snyan if (error || fwheads == 0) { 289106559Snyan fwheads = 8; 290113292Sphk if (bootverbose) 291113292Sphk printf("g_pc98_taste: guessing %d heads\n", 292113292Sphk fwheads); 29393354Sphk } 294106559Snyan sectorsize = cp->provider->sectorsize; 295138219Simp if (sectorsize % 512 != 0) 296106559Snyan break; 297152971Ssobomax buf = g_read_data(cp, 0, 8192, NULL); 298152967Ssobomax if (buf == NULL) 29993354Sphk break; 300108591Snyan ms->fwsectors = fwsectors; 301108591Snyan ms->fwheads = fwheads; 302108591Snyan ms->sectorsize = sectorsize; 303108591Snyan g_topology_lock(); 304148061Snyan g_pc98_modify(gp, ms, buf, 8192); 305108591Snyan g_topology_unlock(); 306106559Snyan g_free(buf); 30793354Sphk break; 308113285Sphk } while (0); 30993354Sphk g_topology_lock(); 310125755Sphk g_access(cp, -1, 0, 0); 311107956Sphk if (LIST_EMPTY(&gp->provider)) { 312114507Sphk g_slice_spoiled(cp); 313107956Sphk return (NULL); 314106559Snyan } 315107956Sphk return (gp); 31693354Sphk} 31793354Sphk 318148061Snyanstatic void 319148061Snyang_pc98_config(struct gctl_req *req, struct g_class *mp, const char *verb) 320148061Snyan{ 321148061Snyan struct g_geom *gp; 322148061Snyan struct g_consumer *cp; 323148061Snyan struct g_pc98_softc *ms; 324148061Snyan struct g_slicer *gsp; 325148061Snyan int opened = 0, error = 0; 326148061Snyan void *data; 327148061Snyan int len; 328148061Snyan 329148061Snyan g_topology_assert(); 330148061Snyan gp = gctl_get_geom(req, mp, "geom"); 331148061Snyan if (gp == NULL) 332148061Snyan return; 333148061Snyan if (strcmp(verb, "write PC98")) { 334148061Snyan gctl_error(req, "Unknown verb"); 335148061Snyan return; 336148061Snyan } 337148061Snyan gsp = gp->softc; 338148061Snyan ms = gsp->softc; 339148061Snyan data = gctl_get_param(req, "data", &len); 340148061Snyan if (data == NULL) 341148061Snyan return; 342148061Snyan if (len < 8192 || (len % 512)) { 343148061Snyan gctl_error(req, "Wrong request length"); 344148061Snyan return; 345148061Snyan } 346148061Snyan cp = LIST_FIRST(&gp->consumer); 347148061Snyan if (cp->acw == 0) { 348148061Snyan error = g_access(cp, 0, 1, 0); 349148061Snyan if (error == 0) 350148061Snyan opened = 1; 351148061Snyan } 352148061Snyan if (!error) 353148061Snyan error = g_pc98_modify(gp, ms, data, len); 354148061Snyan if (error) 355148061Snyan gctl_error(req, "conflict with open slices"); 356148061Snyan if (!error) 357148061Snyan error = g_write_data(cp, 0, data, len); 358148061Snyan if (error) 359148061Snyan gctl_error(req, "sector zero write failed"); 360148061Snyan if (opened) 361148061Snyan g_access(cp, 0, -1 , 0); 362148061Snyan return; 363148061Snyan} 364148061Snyan 36593354Sphkstatic struct g_class g_pc98_class = { 366112552Sphk .name = PC98_CLASS_NAME, 367133318Sphk .version = G_VERSION, 368112552Sphk .taste = g_pc98_taste, 369133314Sphk .dumpconf = g_pc98_dumpconf, 370148061Snyan .ctlreq = g_pc98_config, 371133314Sphk .ioctl = g_pc98_ioctl, 37293354Sphk}; 37393354Sphk 37493354SphkDECLARE_GEOM_CLASS(g_pc98_class, g_pc98); 375