g_part_apm.c revision 170362
1166551Smarcel/*- 2166551Smarcel * Copyright (c) 2006, 2007 Marcel Moolenaar 3166551Smarcel * All rights reserved. 4166551Smarcel * 5166551Smarcel * Redistribution and use in source and binary forms, with or without 6166551Smarcel * modification, are permitted provided that the following conditions 7166551Smarcel * are met: 8166551Smarcel * 9166551Smarcel * 1. Redistributions of source code must retain the above copyright 10166551Smarcel * notice, this list of conditions and the following disclaimer. 11166551Smarcel * 2. Redistributions in binary form must reproduce the above copyright 12166551Smarcel * notice, this list of conditions and the following disclaimer in the 13166551Smarcel * documentation and/or other materials provided with the distribution. 14166551Smarcel * 15166551Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16166551Smarcel * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17166551Smarcel * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18166551Smarcel * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19166551Smarcel * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20166551Smarcel * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21166551Smarcel * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22166551Smarcel * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23166551Smarcel * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24166551Smarcel * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25166551Smarcel */ 26166551Smarcel 27166551Smarcel#include <sys/cdefs.h> 28166551Smarcel__FBSDID("$FreeBSD: head/sys/geom/part/g_part_apm.c 170362 2007-06-06 05:06:14Z marcel $"); 29166551Smarcel 30166551Smarcel#include <sys/param.h> 31166551Smarcel#include <sys/apm.h> 32166551Smarcel#include <sys/bio.h> 33166551Smarcel#include <sys/diskmbr.h> 34166551Smarcel#include <sys/endian.h> 35166551Smarcel#include <sys/kernel.h> 36166551Smarcel#include <sys/kobj.h> 37166551Smarcel#include <sys/limits.h> 38166551Smarcel#include <sys/lock.h> 39166551Smarcel#include <sys/malloc.h> 40166551Smarcel#include <sys/mutex.h> 41166551Smarcel#include <sys/queue.h> 42166551Smarcel#include <sys/sbuf.h> 43166551Smarcel#include <sys/systm.h> 44166551Smarcel#include <geom/geom.h> 45166551Smarcel#include <geom/part/g_part.h> 46166551Smarcel 47166551Smarcel#include "g_part_if.h" 48166551Smarcel 49166551Smarcelstruct g_part_apm_table { 50166551Smarcel struct g_part_table base; 51166551Smarcel struct apm_ddr ddr; 52166551Smarcel struct apm_ent self; 53166551Smarcel}; 54166551Smarcel 55166551Smarcelstruct g_part_apm_entry { 56166551Smarcel struct g_part_entry base; 57166551Smarcel struct apm_ent ent; 58166551Smarcel}; 59166551Smarcel 60166551Smarcelstatic int g_part_apm_add(struct g_part_table *, struct g_part_entry *, 61166551Smarcel struct g_part_parms *); 62166551Smarcelstatic int g_part_apm_create(struct g_part_table *, struct g_part_parms *); 63166551Smarcelstatic int g_part_apm_destroy(struct g_part_table *, struct g_part_parms *); 64166551Smarcelstatic int g_part_apm_dumpto(struct g_part_table *, struct g_part_entry *); 65166551Smarcelstatic int g_part_apm_modify(struct g_part_table *, struct g_part_entry *, 66166551Smarcel struct g_part_parms *); 67166551Smarcelstatic char *g_part_apm_name(struct g_part_table *, struct g_part_entry *, 68166551Smarcel char *, size_t); 69166551Smarcelstatic int g_part_apm_probe(struct g_part_table *, struct g_consumer *); 70166551Smarcelstatic int g_part_apm_read(struct g_part_table *, struct g_consumer *); 71166551Smarcelstatic const char *g_part_apm_type(struct g_part_table *, struct g_part_entry *, 72166551Smarcel char *, size_t); 73166551Smarcelstatic int g_part_apm_write(struct g_part_table *, struct g_consumer *); 74166551Smarcel 75166551Smarcelstatic kobj_method_t g_part_apm_methods[] = { 76166551Smarcel KOBJMETHOD(g_part_add, g_part_apm_add), 77166551Smarcel KOBJMETHOD(g_part_create, g_part_apm_create), 78166551Smarcel KOBJMETHOD(g_part_destroy, g_part_apm_destroy), 79166551Smarcel KOBJMETHOD(g_part_dumpto, g_part_apm_dumpto), 80166551Smarcel KOBJMETHOD(g_part_modify, g_part_apm_modify), 81166551Smarcel KOBJMETHOD(g_part_name, g_part_apm_name), 82166551Smarcel KOBJMETHOD(g_part_probe, g_part_apm_probe), 83166551Smarcel KOBJMETHOD(g_part_read, g_part_apm_read), 84166551Smarcel KOBJMETHOD(g_part_type, g_part_apm_type), 85166551Smarcel KOBJMETHOD(g_part_write, g_part_apm_write), 86166551Smarcel { 0, 0 } 87166551Smarcel}; 88166551Smarcel 89166551Smarcelstatic struct g_part_scheme g_part_apm_scheme = { 90166551Smarcel "APM", 91166551Smarcel g_part_apm_methods, 92166551Smarcel sizeof(struct g_part_apm_table), 93166551Smarcel .gps_entrysz = sizeof(struct g_part_apm_entry), 94166551Smarcel .gps_minent = 16, 95166551Smarcel .gps_maxent = INT_MAX, 96166551Smarcel}; 97166551SmarcelG_PART_SCHEME_DECLARE(g_part_apm_scheme); 98166551Smarcel 99166551Smarcelstatic int 100166551Smarcelapm_parse_type(const char *type, char *buf, size_t bufsz) 101166551Smarcel{ 102169389Smarcel const char *alias; 103166551Smarcel 104169389Smarcel if (type[0] == '!') { 105169389Smarcel type++; 106166551Smarcel if (strlen(type) > bufsz) 107166551Smarcel return (EINVAL); 108166551Smarcel if (!strcmp(type, APM_ENT_TYPE_SELF) || 109166551Smarcel !strcmp(type, APM_ENT_TYPE_UNUSED)) 110166551Smarcel return (EINVAL); 111166551Smarcel strncpy(buf, type, bufsz); 112166551Smarcel return (0); 113166551Smarcel } 114169389Smarcel alias = g_part_alias_name(G_PART_ALIAS_FREEBSD); 115169389Smarcel if (!strcasecmp(type, alias)) { 116166551Smarcel strcpy(buf, APM_ENT_TYPE_FREEBSD); 117169389Smarcel return (0); 118169389Smarcel } 119169389Smarcel alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_SWAP); 120169389Smarcel if (!strcasecmp(type, alias)) { 121166551Smarcel strcpy(buf, APM_ENT_TYPE_FREEBSD_SWAP); 122169389Smarcel return (0); 123169389Smarcel } 124169389Smarcel alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_UFS); 125169389Smarcel if (!strcasecmp(type, alias)) { 126166551Smarcel strcpy(buf, APM_ENT_TYPE_FREEBSD_UFS); 127169389Smarcel return (0); 128169389Smarcel } 129169389Smarcel alias = g_part_alias_name(G_PART_ALIAS_FREEBSD_VINUM); 130169389Smarcel if (!strcasecmp(type, alias)) { 131166551Smarcel strcpy(buf, APM_ENT_TYPE_FREEBSD_VINUM); 132169389Smarcel return (0); 133169389Smarcel } 134169389Smarcel return (EINVAL); 135166551Smarcel} 136166551Smarcel 137166551Smarcelstatic int 138166551Smarcelapm_read_ent(struct g_consumer *cp, uint32_t blk, struct apm_ent *ent) 139166551Smarcel{ 140166551Smarcel struct g_provider *pp; 141166551Smarcel char *buf; 142166551Smarcel int error; 143166551Smarcel 144166551Smarcel pp = cp->provider; 145166551Smarcel buf = g_read_data(cp, pp->sectorsize * blk, pp->sectorsize, &error); 146166551Smarcel if (buf == NULL) 147166551Smarcel return (error); 148166551Smarcel ent->ent_sig = be16dec(buf); 149166551Smarcel ent->ent_pmblkcnt = be32dec(buf + 4); 150166551Smarcel ent->ent_start = be32dec(buf + 8); 151166551Smarcel ent->ent_size = be32dec(buf + 12); 152166551Smarcel bcopy(buf + 16, ent->ent_name, sizeof(ent->ent_name)); 153166551Smarcel bcopy(buf + 48, ent->ent_type, sizeof(ent->ent_type)); 154166551Smarcel g_free(buf); 155166551Smarcel return (0); 156166551Smarcel} 157166551Smarcel 158166551Smarcelstatic int 159166551Smarcelg_part_apm_add(struct g_part_table *basetable, struct g_part_entry *baseentry, 160166551Smarcel struct g_part_parms *gpp) 161166551Smarcel{ 162166551Smarcel struct g_part_apm_entry *entry; 163166551Smarcel struct g_part_apm_table *table; 164166551Smarcel int error; 165166551Smarcel 166166551Smarcel entry = (struct g_part_apm_entry *)baseentry; 167166551Smarcel table = (struct g_part_apm_table *)basetable; 168166551Smarcel entry->ent.ent_sig = APM_ENT_SIG; 169166551Smarcel entry->ent.ent_pmblkcnt = table->self.ent_pmblkcnt; 170166551Smarcel entry->ent.ent_start = gpp->gpp_start; 171166551Smarcel entry->ent.ent_size = gpp->gpp_size; 172166551Smarcel if (baseentry->gpe_deleted) { 173166551Smarcel bzero(entry->ent.ent_type, sizeof(entry->ent.ent_type)); 174166551Smarcel bzero(entry->ent.ent_name, sizeof(entry->ent.ent_name)); 175166551Smarcel } 176166551Smarcel error = apm_parse_type(gpp->gpp_type, entry->ent.ent_type, 177166551Smarcel sizeof(entry->ent.ent_type)); 178166551Smarcel if (error) 179166551Smarcel return (error); 180166551Smarcel if (gpp->gpp_parms & G_PART_PARM_LABEL) { 181166551Smarcel if (strlen(gpp->gpp_label) > sizeof(entry->ent.ent_name)) 182166551Smarcel return (EINVAL); 183166551Smarcel strncpy(entry->ent.ent_name, gpp->gpp_label, 184166551Smarcel sizeof(entry->ent.ent_name)); 185166551Smarcel } 186166551Smarcel return (0); 187166551Smarcel} 188166551Smarcel 189166551Smarcelstatic int 190166551Smarcelg_part_apm_create(struct g_part_table *basetable, struct g_part_parms *gpp) 191166551Smarcel{ 192166551Smarcel struct g_provider *pp; 193166551Smarcel struct g_part_apm_table *table; 194166551Smarcel 195166551Smarcel table = (struct g_part_apm_table *)basetable; 196166551Smarcel pp = gpp->gpp_provider; 197166551Smarcel if (pp->sectorsize != 512 || 198166551Smarcel pp->mediasize < (2 + 2 * basetable->gpt_entries) * pp->sectorsize) 199166551Smarcel return (ENOSPC); 200166551Smarcel 201166551Smarcel basetable->gpt_first = 2 + basetable->gpt_entries; 202166551Smarcel basetable->gpt_last = (pp->mediasize / pp->sectorsize) - 1; 203166551Smarcel 204166551Smarcel table->ddr.ddr_sig = APM_DDR_SIG; 205166551Smarcel table->ddr.ddr_blksize = pp->sectorsize; 206166551Smarcel table->ddr.ddr_blkcount = basetable->gpt_last + 1; 207166551Smarcel 208166551Smarcel table->self.ent_sig = APM_ENT_SIG; 209166551Smarcel table->self.ent_pmblkcnt = basetable->gpt_entries + 1; 210166551Smarcel table->self.ent_start = 1; 211166551Smarcel table->self.ent_size = table->self.ent_pmblkcnt; 212166551Smarcel strcpy(table->self.ent_name, "Apple"); 213166551Smarcel strcpy(table->self.ent_type, APM_ENT_TYPE_SELF); 214166551Smarcel return (0); 215166551Smarcel} 216166551Smarcel 217166551Smarcelstatic int 218166551Smarcelg_part_apm_destroy(struct g_part_table *basetable, struct g_part_parms *gpp) 219166551Smarcel{ 220166551Smarcel 221166551Smarcel /* Wipe the first 2 sectors to clear the partitioning. */ 222166551Smarcel basetable->gpt_smhead |= 3; 223166551Smarcel return (0); 224166551Smarcel} 225166551Smarcel 226166551Smarcelstatic int 227166551Smarcelg_part_apm_dumpto(struct g_part_table *table, struct g_part_entry *baseentry) 228166551Smarcel{ 229166551Smarcel struct g_part_apm_entry *entry; 230166551Smarcel 231166551Smarcel entry = (struct g_part_apm_entry *)baseentry; 232166551Smarcel return ((!strcmp(entry->ent.ent_type, APM_ENT_TYPE_FREEBSD_SWAP)) 233166551Smarcel ? 1 : 0); 234166551Smarcel} 235166551Smarcel 236166551Smarcelstatic int 237166551Smarcelg_part_apm_modify(struct g_part_table *basetable, 238166551Smarcel struct g_part_entry *baseentry, struct g_part_parms *gpp) 239166551Smarcel{ 240166551Smarcel struct g_part_apm_entry *entry; 241166551Smarcel int error; 242166551Smarcel 243166551Smarcel entry = (struct g_part_apm_entry *)baseentry; 244166551Smarcel if (gpp->gpp_parms & G_PART_PARM_LABEL) { 245166551Smarcel if (strlen(gpp->gpp_label) > sizeof(entry->ent.ent_name)) 246166551Smarcel return (EINVAL); 247166551Smarcel } 248166551Smarcel if (gpp->gpp_parms & G_PART_PARM_TYPE) { 249166551Smarcel error = apm_parse_type(gpp->gpp_type, entry->ent.ent_type, 250166551Smarcel sizeof(entry->ent.ent_type)); 251166551Smarcel if (error) 252166551Smarcel return (error); 253166551Smarcel } 254166551Smarcel if (gpp->gpp_parms & G_PART_PARM_LABEL) { 255166551Smarcel strncpy(entry->ent.ent_name, gpp->gpp_label, 256166551Smarcel sizeof(entry->ent.ent_name)); 257166551Smarcel } 258166551Smarcel return (0); 259166551Smarcel} 260166551Smarcel 261166551Smarcelstatic char * 262166551Smarcelg_part_apm_name(struct g_part_table *table, struct g_part_entry *baseentry, 263166551Smarcel char *buf, size_t bufsz) 264166551Smarcel{ 265166551Smarcel 266166551Smarcel snprintf(buf, bufsz, "s%d", baseentry->gpe_index + 1); 267166551Smarcel return (buf); 268166551Smarcel} 269166551Smarcel 270166551Smarcelstatic int 271166551Smarcelg_part_apm_probe(struct g_part_table *basetable, struct g_consumer *cp) 272166551Smarcel{ 273166551Smarcel struct g_provider *pp; 274166551Smarcel struct g_part_apm_table *table; 275166551Smarcel char *buf; 276166551Smarcel int error; 277166551Smarcel 278166551Smarcel /* We don't nest, which means that our depth should be 0. */ 279166551Smarcel if (basetable->gpt_depth != 0) 280166551Smarcel return (ENXIO); 281166551Smarcel 282166551Smarcel table = (struct g_part_apm_table *)basetable; 283166551Smarcel pp = cp->provider; 284166551Smarcel 285166551Smarcel /* Sanity-check the provider. */ 286166551Smarcel if (pp->mediasize < 4 * pp->sectorsize) 287166551Smarcel return (ENOSPC); 288166551Smarcel 289166551Smarcel /* Check that there's a Driver Descriptor Record (DDR). */ 290166551Smarcel buf = g_read_data(cp, 0L, pp->sectorsize, &error); 291166551Smarcel if (buf == NULL) 292166551Smarcel return (error); 293166551Smarcel table->ddr.ddr_sig = be16dec(buf); 294166551Smarcel table->ddr.ddr_blksize = be16dec(buf + 2); 295166551Smarcel table->ddr.ddr_blkcount = be32dec(buf + 4); 296166551Smarcel g_free(buf); 297166551Smarcel if (table->ddr.ddr_sig != APM_DDR_SIG) 298166551Smarcel return (ENXIO); 299166551Smarcel if (table->ddr.ddr_blksize != pp->sectorsize) 300166551Smarcel return (ENXIO); 301166551Smarcel 302166551Smarcel /* Check that there's a Partition Map. */ 303166551Smarcel error = apm_read_ent(cp, 1, &table->self); 304166551Smarcel if (error) 305166551Smarcel return (error); 306166551Smarcel if (table->self.ent_sig != APM_ENT_SIG) 307166551Smarcel return (ENXIO); 308166551Smarcel if (strcmp(table->self.ent_type, APM_ENT_TYPE_SELF)) 309166551Smarcel return (ENXIO); 310166551Smarcel if (table->self.ent_pmblkcnt >= table->ddr.ddr_blkcount) 311166551Smarcel return (ENXIO); 312166551Smarcel return (G_PART_PROBE_PRI_NORM); 313166551Smarcel} 314166551Smarcel 315166551Smarcelstatic int 316166551Smarcelg_part_apm_read(struct g_part_table *basetable, struct g_consumer *cp) 317166551Smarcel{ 318166551Smarcel struct apm_ent ent; 319166551Smarcel struct g_part_apm_entry *entry; 320166551Smarcel struct g_part_apm_table *table; 321166551Smarcel int error, index; 322166551Smarcel 323166551Smarcel table = (struct g_part_apm_table *)basetable; 324166551Smarcel 325166551Smarcel basetable->gpt_first = table->self.ent_pmblkcnt + 1; 326166551Smarcel basetable->gpt_last = table->ddr.ddr_blkcount - 1; 327166551Smarcel basetable->gpt_entries = table->self.ent_pmblkcnt - 1; 328166551Smarcel 329166551Smarcel for (index = table->self.ent_pmblkcnt - 1; index > 0; index--) { 330166551Smarcel error = apm_read_ent(cp, index + 1, &ent); 331166551Smarcel if (error) 332166551Smarcel continue; 333166551Smarcel if (!strcmp(ent.ent_type, APM_ENT_TYPE_UNUSED)) 334166551Smarcel continue; 335166551Smarcel entry = (struct g_part_apm_entry *)g_part_new_entry(basetable, 336166551Smarcel index, ent.ent_start, ent.ent_start + ent.ent_size - 1); 337166551Smarcel entry->ent = ent; 338166551Smarcel } 339166551Smarcel 340166551Smarcel return (0); 341166551Smarcel} 342166551Smarcel 343166551Smarcelstatic const char * 344166551Smarcelg_part_apm_type(struct g_part_table *basetable, struct g_part_entry *baseentry, 345166551Smarcel char *buf, size_t bufsz) 346166551Smarcel{ 347166551Smarcel struct g_part_apm_entry *entry; 348166551Smarcel const char *type; 349166551Smarcel size_t len; 350166551Smarcel 351166551Smarcel entry = (struct g_part_apm_entry *)baseentry; 352166551Smarcel type = entry->ent.ent_type; 353166551Smarcel if (!strcmp(type, APM_ENT_TYPE_FREEBSD)) 354166551Smarcel return (g_part_alias_name(G_PART_ALIAS_FREEBSD)); 355166551Smarcel if (!strcmp(type, APM_ENT_TYPE_FREEBSD_SWAP)) 356166551Smarcel return (g_part_alias_name(G_PART_ALIAS_FREEBSD_SWAP)); 357166551Smarcel if (!strcmp(type, APM_ENT_TYPE_FREEBSD_UFS)) 358166551Smarcel return (g_part_alias_name(G_PART_ALIAS_FREEBSD_UFS)); 359166551Smarcel if (!strcmp(type, APM_ENT_TYPE_FREEBSD_VINUM)) 360166551Smarcel return (g_part_alias_name(G_PART_ALIAS_FREEBSD_VINUM)); 361170362Smarcel buf[0] = '!'; 362170362Smarcel len = MIN(sizeof(entry->ent.ent_type), bufsz - 2); 363170362Smarcel bcopy(type, buf + 1, len); 364170362Smarcel buf[len + 1] = '\0'; 365166551Smarcel return (buf); 366166551Smarcel} 367166551Smarcel 368166551Smarcelstatic int 369166551Smarcelg_part_apm_write(struct g_part_table *basetable, struct g_consumer *cp) 370166551Smarcel{ 371166551Smarcel char buf[512]; 372166551Smarcel struct g_part_entry *baseentry; 373166551Smarcel struct g_part_apm_entry *entry; 374166551Smarcel struct g_part_apm_table *table; 375166551Smarcel int error, index; 376166551Smarcel 377166551Smarcel table = (struct g_part_apm_table *)basetable; 378166551Smarcel bzero(buf, sizeof(buf)); 379166551Smarcel 380166551Smarcel /* Write the DDR and 'self' entry only when we're newly created. */ 381166551Smarcel if (basetable->gpt_created) { 382166551Smarcel be16enc(buf, table->ddr.ddr_sig); 383166551Smarcel be16enc(buf + 2, table->ddr.ddr_blksize); 384166551Smarcel be32enc(buf + 4, table->ddr.ddr_blkcount); 385166551Smarcel error = g_write_data(cp, 0, buf, sizeof(buf)); 386166551Smarcel if (error) 387166551Smarcel return (error); 388166551Smarcel } 389166551Smarcel 390166551Smarcel be16enc(buf, table->self.ent_sig); 391166551Smarcel be16enc(buf + 2, 0); 392166551Smarcel be32enc(buf + 4, table->self.ent_pmblkcnt); 393166551Smarcel 394166551Smarcel if (basetable->gpt_created) { 395166551Smarcel be32enc(buf + 8, table->self.ent_start); 396166551Smarcel be32enc(buf + 12, table->self.ent_size); 397166551Smarcel bcopy(table->self.ent_name, buf + 16, 398166551Smarcel sizeof(table->self.ent_name)); 399166551Smarcel bcopy(table->self.ent_type, buf + 48, 400166551Smarcel sizeof(table->self.ent_type)); 401166551Smarcel error = g_write_data(cp, 512, buf, sizeof(buf)); 402166551Smarcel if (error) 403166551Smarcel return (error); 404166551Smarcel } 405166551Smarcel 406166551Smarcel baseentry = LIST_FIRST(&basetable->gpt_entry); 407166551Smarcel for (index = 1; index <= basetable->gpt_entries; index++) { 408169389Smarcel entry = (baseentry != NULL && index == baseentry->gpe_index) 409169389Smarcel ? (struct g_part_apm_entry *)baseentry : NULL; 410169389Smarcel if (entry != NULL && !baseentry->gpe_deleted) { 411166551Smarcel be32enc(buf + 8, entry->ent.ent_start); 412166551Smarcel be32enc(buf + 12, entry->ent.ent_size); 413166551Smarcel bcopy(entry->ent.ent_name, buf + 16, 414166551Smarcel sizeof(entry->ent.ent_name)); 415166551Smarcel bcopy(entry->ent.ent_type, buf + 48, 416166551Smarcel sizeof(entry->ent.ent_type)); 417166551Smarcel } else { 418166551Smarcel bzero(buf + 8, 4 + 4 + 32 + 32); 419166551Smarcel strcpy(buf + 48, APM_ENT_TYPE_UNUSED); 420166551Smarcel } 421166551Smarcel error = g_write_data(cp, (index + 1) * 512, buf, sizeof(buf)); 422166551Smarcel if (error) 423166551Smarcel return (error); 424169389Smarcel if (entry != NULL) 425169389Smarcel baseentry = LIST_NEXT(baseentry, gpe_entry); 426166551Smarcel } 427166551Smarcel 428166551Smarcel return (0); 429166551Smarcel} 430