g_part_pc98.c revision 265912
1/*- 2 * Copyright (c) 2008 Marcel Moolenaar 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD: stable/10/sys/geom/part/g_part_pc98.c 265912 2014-05-12 12:04:44Z ae $"); 29 30#include <sys/param.h> 31#include <sys/bio.h> 32#include <sys/diskpc98.h> 33#include <sys/endian.h> 34#include <sys/kernel.h> 35#include <sys/kobj.h> 36#include <sys/limits.h> 37#include <sys/lock.h> 38#include <sys/malloc.h> 39#include <sys/mutex.h> 40#include <sys/queue.h> 41#include <sys/sbuf.h> 42#include <sys/systm.h> 43#include <sys/sysctl.h> 44#include <geom/geom.h> 45#include <geom/geom_int.h> 46#include <geom/part/g_part.h> 47 48#include "g_part_if.h" 49 50FEATURE(geom_part_pc98, "GEOM partitioning class for PC-9800 disk partitions"); 51 52#define SECSIZE 512 53#define MENUSIZE 7168 54#define BOOTSIZE 8192 55 56struct g_part_pc98_table { 57 struct g_part_table base; 58 u_char boot[SECSIZE]; 59 u_char table[SECSIZE]; 60 u_char menu[MENUSIZE]; 61}; 62 63struct g_part_pc98_entry { 64 struct g_part_entry base; 65 struct pc98_partition ent; 66}; 67 68static int g_part_pc98_add(struct g_part_table *, struct g_part_entry *, 69 struct g_part_parms *); 70static int g_part_pc98_bootcode(struct g_part_table *, struct g_part_parms *); 71static int g_part_pc98_create(struct g_part_table *, struct g_part_parms *); 72static int g_part_pc98_destroy(struct g_part_table *, struct g_part_parms *); 73static void g_part_pc98_dumpconf(struct g_part_table *, struct g_part_entry *, 74 struct sbuf *, const char *); 75static int g_part_pc98_dumpto(struct g_part_table *, struct g_part_entry *); 76static int g_part_pc98_modify(struct g_part_table *, struct g_part_entry *, 77 struct g_part_parms *); 78static const char *g_part_pc98_name(struct g_part_table *, struct g_part_entry *, 79 char *, size_t); 80static int g_part_pc98_probe(struct g_part_table *, struct g_consumer *); 81static int g_part_pc98_read(struct g_part_table *, struct g_consumer *); 82static int g_part_pc98_setunset(struct g_part_table *, struct g_part_entry *, 83 const char *, unsigned int); 84static const char *g_part_pc98_type(struct g_part_table *, 85 struct g_part_entry *, char *, size_t); 86static int g_part_pc98_write(struct g_part_table *, struct g_consumer *); 87static int g_part_pc98_resize(struct g_part_table *, struct g_part_entry *, 88 struct g_part_parms *); 89 90static kobj_method_t g_part_pc98_methods[] = { 91 KOBJMETHOD(g_part_add, g_part_pc98_add), 92 KOBJMETHOD(g_part_bootcode, g_part_pc98_bootcode), 93 KOBJMETHOD(g_part_create, g_part_pc98_create), 94 KOBJMETHOD(g_part_destroy, g_part_pc98_destroy), 95 KOBJMETHOD(g_part_dumpconf, g_part_pc98_dumpconf), 96 KOBJMETHOD(g_part_dumpto, g_part_pc98_dumpto), 97 KOBJMETHOD(g_part_modify, g_part_pc98_modify), 98 KOBJMETHOD(g_part_resize, g_part_pc98_resize), 99 KOBJMETHOD(g_part_name, g_part_pc98_name), 100 KOBJMETHOD(g_part_probe, g_part_pc98_probe), 101 KOBJMETHOD(g_part_read, g_part_pc98_read), 102 KOBJMETHOD(g_part_setunset, g_part_pc98_setunset), 103 KOBJMETHOD(g_part_type, g_part_pc98_type), 104 KOBJMETHOD(g_part_write, g_part_pc98_write), 105 { 0, 0 } 106}; 107 108static struct g_part_scheme g_part_pc98_scheme = { 109 "PC98", 110 g_part_pc98_methods, 111 sizeof(struct g_part_pc98_table), 112 .gps_entrysz = sizeof(struct g_part_pc98_entry), 113 .gps_minent = PC98_NPARTS, 114 .gps_maxent = PC98_NPARTS, 115 .gps_bootcodesz = BOOTSIZE, 116}; 117G_PART_SCHEME_DECLARE(g_part_pc98); 118 119static int 120pc98_parse_type(const char *type, u_char *dp_mid, u_char *dp_sid) 121{ 122 const char *alias; 123 char *endp; 124 long lt; 125 126 if (type[0] == '!') { 127 lt = strtol(type + 1, &endp, 0); 128 if (type[1] == '\0' || *endp != '\0' || lt <= 0 || 129 lt >= 65536) 130 return (EINVAL); 131 /* Make sure the active and bootable flags aren't set. */ 132 if (lt & ((PC98_SID_ACTIVE << 8) | PC98_MID_BOOTABLE)) 133 return (ENOATTR); 134 *dp_mid = (*dp_mid & PC98_MID_BOOTABLE) | (u_char)lt; 135 *dp_sid = (*dp_sid & PC98_SID_ACTIVE) | (u_char)(lt >> 8); 136 return (0); 137 } 138 alias = g_part_alias_name(G_PART_ALIAS_FREEBSD); 139 if (!strcasecmp(type, alias)) { 140 *dp_mid = (*dp_mid & PC98_MID_BOOTABLE) | PC98_MID_386BSD; 141 *dp_sid = (*dp_sid & PC98_SID_ACTIVE) | PC98_SID_386BSD; 142 return (0); 143 } 144 return (EINVAL); 145} 146 147static int 148pc98_set_slicename(const char *label, u_char *dp_name) 149{ 150 int len; 151 152 len = strlen(label); 153 if (len > sizeof(((struct pc98_partition *)NULL)->dp_name)) 154 return (EINVAL); 155 bzero(dp_name, sizeof(((struct pc98_partition *)NULL)->dp_name)); 156 strncpy(dp_name, label, len); 157 158 return (0); 159} 160 161static void 162pc98_set_chs(struct g_part_table *table, uint32_t lba, u_short *cylp, 163 u_char *hdp, u_char *secp) 164{ 165 uint32_t cyl, hd, sec; 166 167 sec = lba % table->gpt_sectors + 1; 168 lba /= table->gpt_sectors; 169 hd = lba % table->gpt_heads; 170 lba /= table->gpt_heads; 171 cyl = lba; 172 173 *cylp = htole16(cyl); 174 *hdp = hd; 175 *secp = sec; 176} 177 178static int 179pc98_align(struct g_part_table *basetable, uint32_t *start, uint32_t *size) 180{ 181 uint32_t cyl; 182 183 cyl = basetable->gpt_heads * basetable->gpt_sectors; 184 if (*size < cyl) 185 return (EINVAL); 186 if (start != NULL && (*start % cyl)) { 187 *size += (*start % cyl) - cyl; 188 *start -= (*start % cyl) - cyl; 189 } 190 if (*size % cyl) 191 *size -= (*size % cyl); 192 if (*size < cyl) 193 return (EINVAL); 194 return (0); 195} 196 197static int 198g_part_pc98_add(struct g_part_table *basetable, struct g_part_entry *baseentry, 199 struct g_part_parms *gpp) 200{ 201 struct g_part_pc98_entry *entry; 202 uint32_t start, size; 203 int error; 204 205 entry = (struct g_part_pc98_entry *)baseentry; 206 start = gpp->gpp_start; 207 size = gpp->gpp_size; 208 if (pc98_align(basetable, &start, &size) != 0) 209 return (EINVAL); 210 if (baseentry->gpe_deleted) 211 bzero(&entry->ent, sizeof(entry->ent)); 212 else 213 entry->ent.dp_mid = entry->ent.dp_sid = 0; 214 215 KASSERT(baseentry->gpe_start <= start, (__func__)); 216 KASSERT(baseentry->gpe_end >= start + size - 1, (__func__)); 217 baseentry->gpe_start = start; 218 baseentry->gpe_end = start + size - 1; 219 pc98_set_chs(basetable, baseentry->gpe_start, &entry->ent.dp_scyl, 220 &entry->ent.dp_shd, &entry->ent.dp_ssect); 221 pc98_set_chs(basetable, baseentry->gpe_end, &entry->ent.dp_ecyl, 222 &entry->ent.dp_ehd, &entry->ent.dp_esect); 223 224 error = pc98_parse_type(gpp->gpp_type, &entry->ent.dp_mid, 225 &entry->ent.dp_sid); 226 if (error) 227 return (error); 228 229 if (gpp->gpp_parms & G_PART_PARM_LABEL) 230 return (pc98_set_slicename(gpp->gpp_label, entry->ent.dp_name)); 231 232 return (0); 233} 234 235static int 236g_part_pc98_bootcode(struct g_part_table *basetable, struct g_part_parms *gpp) 237{ 238 struct g_part_pc98_table *table; 239 const u_char *codeptr; 240 241 if (gpp->gpp_codesize != BOOTSIZE) 242 return (EINVAL); 243 244 table = (struct g_part_pc98_table *)basetable; 245 codeptr = gpp->gpp_codeptr; 246 bcopy(codeptr, table->boot, SECSIZE); 247 bcopy(codeptr + SECSIZE*2, table->menu, MENUSIZE); 248 249 return (0); 250} 251 252static int 253g_part_pc98_create(struct g_part_table *basetable, struct g_part_parms *gpp) 254{ 255 struct g_provider *pp; 256 struct g_part_pc98_table *table; 257 258 pp = gpp->gpp_provider; 259 if (pp->sectorsize < SECSIZE || pp->mediasize < BOOTSIZE) 260 return (ENOSPC); 261 if (pp->sectorsize > SECSIZE) 262 return (ENXIO); 263 264 basetable->gpt_first = basetable->gpt_heads * basetable->gpt_sectors; 265 basetable->gpt_last = MIN(pp->mediasize / SECSIZE, UINT32_MAX) - 1; 266 267 table = (struct g_part_pc98_table *)basetable; 268 le16enc(table->boot + PC98_MAGICOFS, PC98_MAGIC); 269 return (0); 270} 271 272static int 273g_part_pc98_destroy(struct g_part_table *basetable, struct g_part_parms *gpp) 274{ 275 276 /* Wipe the first two sectors to clear the partitioning. */ 277 basetable->gpt_smhead |= 3; 278 return (0); 279} 280 281static void 282g_part_pc98_dumpconf(struct g_part_table *table, 283 struct g_part_entry *baseentry, struct sbuf *sb, const char *indent) 284{ 285 struct g_part_pc98_entry *entry; 286 char name[sizeof(entry->ent.dp_name) + 1]; 287 u_int type; 288 289 entry = (struct g_part_pc98_entry *)baseentry; 290 if (entry == NULL) { 291 /* confxml: scheme information */ 292 return; 293 } 294 295 type = entry->ent.dp_mid + (entry->ent.dp_sid << 8); 296 strncpy(name, entry->ent.dp_name, sizeof(name) - 1); 297 name[sizeof(name) - 1] = '\0'; 298 if (indent == NULL) { 299 /* conftxt: libdisk compatibility */ 300 sbuf_printf(sb, " xs PC98 xt %u sn %s", type, name); 301 } else { 302 /* confxml: partition entry information */ 303 sbuf_printf(sb, "%s<label>%s</label>\n", indent, name); 304 if (entry->ent.dp_mid & PC98_MID_BOOTABLE) 305 sbuf_printf(sb, "%s<attrib>bootable</attrib>\n", 306 indent); 307 if (entry->ent.dp_sid & PC98_SID_ACTIVE) 308 sbuf_printf(sb, "%s<attrib>active</attrib>\n", indent); 309 sbuf_printf(sb, "%s<rawtype>%u</rawtype>\n", indent, 310 type & 0x7f7f); 311 } 312} 313 314static int 315g_part_pc98_dumpto(struct g_part_table *table, struct g_part_entry *baseentry) 316{ 317 struct g_part_pc98_entry *entry; 318 319 /* Allow dumping to a FreeBSD partition only. */ 320 entry = (struct g_part_pc98_entry *)baseentry; 321 return (((entry->ent.dp_mid & PC98_MID_MASK) == PC98_MID_386BSD && 322 (entry->ent.dp_sid & PC98_SID_MASK) == PC98_SID_386BSD) ? 1 : 0); 323} 324 325static int 326g_part_pc98_modify(struct g_part_table *basetable, 327 struct g_part_entry *baseentry, struct g_part_parms *gpp) 328{ 329 struct g_part_pc98_entry *entry; 330 int error; 331 332 entry = (struct g_part_pc98_entry *)baseentry; 333 334 if (gpp->gpp_parms & G_PART_PARM_TYPE) { 335 error = pc98_parse_type(gpp->gpp_type, &entry->ent.dp_mid, 336 &entry->ent.dp_sid); 337 if (error) 338 return (error); 339 } 340 341 if (gpp->gpp_parms & G_PART_PARM_LABEL) 342 return (pc98_set_slicename(gpp->gpp_label, entry->ent.dp_name)); 343 344 return (0); 345} 346 347static int 348g_part_pc98_resize(struct g_part_table *basetable, 349 struct g_part_entry *baseentry, struct g_part_parms *gpp) 350{ 351 struct g_part_pc98_entry *entry; 352 struct g_provider *pp; 353 uint32_t size; 354 355 if (baseentry == NULL) { 356 pp = LIST_FIRST(&basetable->gpt_gp->consumer)->provider; 357 basetable->gpt_last = MIN(pp->mediasize / SECSIZE, 358 UINT32_MAX) - 1; 359 return (0); 360 } 361 size = gpp->gpp_size; 362 if (pc98_align(basetable, NULL, &size) != 0) 363 return (EINVAL); 364 /* XXX: prevent unexpected shrinking. */ 365 pp = baseentry->gpe_pp; 366 if ((g_debugflags & 0x10) == 0 && size < gpp->gpp_size && 367 pp->mediasize / pp->sectorsize > size) 368 return (EBUSY); 369 entry = (struct g_part_pc98_entry *)baseentry; 370 baseentry->gpe_end = baseentry->gpe_start + size - 1; 371 pc98_set_chs(basetable, baseentry->gpe_end, &entry->ent.dp_ecyl, 372 &entry->ent.dp_ehd, &entry->ent.dp_esect); 373 374 return (0); 375} 376 377static const char * 378g_part_pc98_name(struct g_part_table *table, struct g_part_entry *baseentry, 379 char *buf, size_t bufsz) 380{ 381 382 snprintf(buf, bufsz, "s%d", baseentry->gpe_index); 383 return (buf); 384} 385 386static int 387g_part_pc98_probe(struct g_part_table *table, struct g_consumer *cp) 388{ 389 struct g_provider *pp; 390 u_char *buf, *p; 391 int error, index, res, sum; 392 uint16_t magic, ecyl, scyl; 393 394 pp = cp->provider; 395 396 /* Sanity-check the provider. */ 397 if (pp->sectorsize < SECSIZE || pp->mediasize < BOOTSIZE) 398 return (ENOSPC); 399 if (pp->sectorsize > SECSIZE) 400 return (ENXIO); 401 402 /* Check that there's a PC98 partition table. */ 403 buf = g_read_data(cp, 0L, 2 * SECSIZE, &error); 404 if (buf == NULL) 405 return (error); 406 407 /* We goto out on mismatch. */ 408 res = ENXIO; 409 410 magic = le16dec(buf + PC98_MAGICOFS); 411 if (magic != PC98_MAGIC) 412 goto out; 413 414 sum = 0; 415 for (index = SECSIZE; index < 2 * SECSIZE; index++) 416 sum += buf[index]; 417 if (sum == 0) { 418 res = G_PART_PROBE_PRI_LOW; 419 goto out; 420 } 421 422 for (index = 0; index < PC98_NPARTS; index++) { 423 p = buf + SECSIZE + index * PC98_PARTSIZE; 424 if (p[0] == 0 || p[1] == 0) /* !dp_mid || !dp_sid */ 425 continue; 426 scyl = le16dec(p + 10); 427 ecyl = le16dec(p + 14); 428 if (scyl == 0 || ecyl == 0) 429 goto out; 430 if (p[8] == p[12] && /* dp_ssect == dp_esect */ 431 p[9] == p[13] && /* dp_shd == dp_ehd */ 432 scyl == ecyl) 433 goto out; 434 } 435 436 res = G_PART_PROBE_PRI_HIGH; 437 438 out: 439 g_free(buf); 440 return (res); 441} 442 443static int 444g_part_pc98_read(struct g_part_table *basetable, struct g_consumer *cp) 445{ 446 struct pc98_partition ent; 447 struct g_provider *pp; 448 struct g_part_pc98_table *table; 449 struct g_part_pc98_entry *entry; 450 u_char *buf, *p; 451 off_t msize; 452 off_t start, end; 453 u_int cyl; 454 int error, index; 455 456 pp = cp->provider; 457 table = (struct g_part_pc98_table *)basetable; 458 msize = MIN(pp->mediasize / SECSIZE, UINT32_MAX); 459 460 buf = g_read_data(cp, 0L, BOOTSIZE, &error); 461 if (buf == NULL) 462 return (error); 463 464 cyl = basetable->gpt_heads * basetable->gpt_sectors; 465 466 bcopy(buf, table->boot, sizeof(table->boot)); 467 bcopy(buf + SECSIZE, table->table, sizeof(table->table)); 468 bcopy(buf + SECSIZE*2, table->menu, sizeof(table->menu)); 469 470 for (index = PC98_NPARTS - 1; index >= 0; index--) { 471 p = buf + SECSIZE + index * PC98_PARTSIZE; 472 ent.dp_mid = p[0]; 473 ent.dp_sid = p[1]; 474 ent.dp_dum1 = p[2]; 475 ent.dp_dum2 = p[3]; 476 ent.dp_ipl_sct = p[4]; 477 ent.dp_ipl_head = p[5]; 478 ent.dp_ipl_cyl = le16dec(p + 6); 479 ent.dp_ssect = p[8]; 480 ent.dp_shd = p[9]; 481 ent.dp_scyl = le16dec(p + 10); 482 ent.dp_esect = p[12]; 483 ent.dp_ehd = p[13]; 484 ent.dp_ecyl = le16dec(p + 14); 485 bcopy(p + 16, ent.dp_name, sizeof(ent.dp_name)); 486 if (ent.dp_sid == 0) 487 continue; 488 489 start = ent.dp_scyl * cyl; 490 end = (ent.dp_ecyl + 1) * cyl - 1; 491 entry = (struct g_part_pc98_entry *)g_part_new_entry(basetable, 492 index + 1, start, end); 493 entry->ent = ent; 494 } 495 496 basetable->gpt_entries = PC98_NPARTS; 497 basetable->gpt_first = cyl; 498 basetable->gpt_last = msize - 1; 499 500 g_free(buf); 501 return (0); 502} 503 504static int 505g_part_pc98_setunset(struct g_part_table *table, struct g_part_entry *baseentry, 506 const char *attrib, unsigned int set) 507{ 508 struct g_part_entry *iter; 509 struct g_part_pc98_entry *entry; 510 int changed, mid, sid; 511 512 if (baseentry == NULL) 513 return (ENODEV); 514 515 mid = sid = 0; 516 if (strcasecmp(attrib, "active") == 0) 517 sid = 1; 518 else if (strcasecmp(attrib, "bootable") == 0) 519 mid = 1; 520 if (mid == 0 && sid == 0) 521 return (EINVAL); 522 523 LIST_FOREACH(iter, &table->gpt_entry, gpe_entry) { 524 if (iter->gpe_deleted) 525 continue; 526 if (iter != baseentry) 527 continue; 528 changed = 0; 529 entry = (struct g_part_pc98_entry *)iter; 530 if (set) { 531 if (mid && !(entry->ent.dp_mid & PC98_MID_BOOTABLE)) { 532 entry->ent.dp_mid |= PC98_MID_BOOTABLE; 533 changed = 1; 534 } 535 if (sid && !(entry->ent.dp_sid & PC98_SID_ACTIVE)) { 536 entry->ent.dp_sid |= PC98_SID_ACTIVE; 537 changed = 1; 538 } 539 } else { 540 if (mid && (entry->ent.dp_mid & PC98_MID_BOOTABLE)) { 541 entry->ent.dp_mid &= ~PC98_MID_BOOTABLE; 542 changed = 1; 543 } 544 if (sid && (entry->ent.dp_sid & PC98_SID_ACTIVE)) { 545 entry->ent.dp_sid &= ~PC98_SID_ACTIVE; 546 changed = 1; 547 } 548 } 549 if (changed && !iter->gpe_created) 550 iter->gpe_modified = 1; 551 } 552 return (0); 553} 554 555static const char * 556g_part_pc98_type(struct g_part_table *basetable, struct g_part_entry *baseentry, 557 char *buf, size_t bufsz) 558{ 559 struct g_part_pc98_entry *entry; 560 u_int type; 561 562 entry = (struct g_part_pc98_entry *)baseentry; 563 type = (entry->ent.dp_mid & PC98_MID_MASK) | 564 ((entry->ent.dp_sid & PC98_SID_MASK) << 8); 565 if (type == (PC98_MID_386BSD | (PC98_SID_386BSD << 8))) 566 return (g_part_alias_name(G_PART_ALIAS_FREEBSD)); 567 snprintf(buf, bufsz, "!%d", type); 568 return (buf); 569} 570 571static int 572g_part_pc98_write(struct g_part_table *basetable, struct g_consumer *cp) 573{ 574 struct g_part_entry *baseentry; 575 struct g_part_pc98_entry *entry; 576 struct g_part_pc98_table *table; 577 u_char *p; 578 int error, index; 579 580 table = (struct g_part_pc98_table *)basetable; 581 baseentry = LIST_FIRST(&basetable->gpt_entry); 582 for (index = 1; index <= basetable->gpt_entries; index++) { 583 p = table->table + (index - 1) * PC98_PARTSIZE; 584 entry = (baseentry != NULL && index == baseentry->gpe_index) 585 ? (struct g_part_pc98_entry *)baseentry : NULL; 586 if (entry != NULL && !baseentry->gpe_deleted) { 587 p[0] = entry->ent.dp_mid; 588 p[1] = entry->ent.dp_sid; 589 p[2] = entry->ent.dp_dum1; 590 p[3] = entry->ent.dp_dum2; 591 p[4] = entry->ent.dp_ipl_sct; 592 p[5] = entry->ent.dp_ipl_head; 593 le16enc(p + 6, entry->ent.dp_ipl_cyl); 594 p[8] = entry->ent.dp_ssect; 595 p[9] = entry->ent.dp_shd; 596 le16enc(p + 10, entry->ent.dp_scyl); 597 p[12] = entry->ent.dp_esect; 598 p[13] = entry->ent.dp_ehd; 599 le16enc(p + 14, entry->ent.dp_ecyl); 600 bcopy(entry->ent.dp_name, p + 16, 601 sizeof(entry->ent.dp_name)); 602 } else 603 bzero(p, PC98_PARTSIZE); 604 605 if (entry != NULL) 606 baseentry = LIST_NEXT(baseentry, gpe_entry); 607 } 608 609 error = g_write_data(cp, 0, table->boot, SECSIZE); 610 if (!error) 611 error = g_write_data(cp, SECSIZE, table->table, SECSIZE); 612 if (!error) 613 error = g_write_data(cp, SECSIZE*2, table->menu, MENUSIZE); 614 return (error); 615} 616