Add the bootcode verb for installing boot code. Boot code
is supported for the MBR, GPT and PC98 schemes, where GPT installs boot code into the PMBR.
This commit is contained in:
parent
1a796873ca
commit
4d32fcb42b
@ -39,6 +39,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <paths.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "core/geom.h"
|
||||
#include "misc/subr.h"
|
||||
@ -55,7 +56,8 @@ uint32_t PUBSYM(version) = 0;
|
||||
static char optional[] = "";
|
||||
static char flags[] = "C";
|
||||
|
||||
static void gpart_show(struct gctl_req *, unsigned);
|
||||
static void gpart_bootcode(struct gctl_req *, unsigned int);
|
||||
static void gpart_show(struct gctl_req *, unsigned int);
|
||||
|
||||
struct g_command PUBSYM(class_commands)[] = {
|
||||
{ "add", 0, NULL, {
|
||||
@ -66,7 +68,13 @@ struct g_command PUBSYM(class_commands)[] = {
|
||||
{ 'l', "label", optional, G_TYPE_STRING },
|
||||
{ 'f', "flags", flags, G_TYPE_STRING },
|
||||
G_OPT_SENTINEL },
|
||||
"geom", NULL,
|
||||
"geom", NULL
|
||||
},
|
||||
{ "bootcode", 0, gpart_bootcode, {
|
||||
{ 'b', "bootcode", NULL, G_TYPE_STRING },
|
||||
{ 'f', "flags", flags, G_TYPE_STRING },
|
||||
G_OPT_SENTINEL },
|
||||
"geom", NULL
|
||||
},
|
||||
{ "commit", 0, NULL, G_NULL_OPTS, "geom", NULL },
|
||||
{ "create", 0, NULL, {
|
||||
@ -241,7 +249,7 @@ gpart_show_geom(struct ggeom *gp)
|
||||
}
|
||||
|
||||
static void
|
||||
gpart_show(struct gctl_req *req, unsigned fl __unused)
|
||||
gpart_show(struct gctl_req *req, unsigned int fl __unused)
|
||||
{
|
||||
struct gmesh mesh;
|
||||
struct gclass *classp;
|
||||
@ -277,3 +285,39 @@ gpart_show(struct gctl_req *req, unsigned fl __unused)
|
||||
}
|
||||
geom_deletetree(&mesh);
|
||||
}
|
||||
|
||||
static void
|
||||
gpart_bootcode(struct gctl_req *req, unsigned int fl __unused)
|
||||
{
|
||||
struct stat sb;
|
||||
const char *bootfile;
|
||||
void *code;
|
||||
int error, fd, size;
|
||||
|
||||
bootfile = gctl_get_ascii(req, "bootcode");
|
||||
if (bootfile == NULL)
|
||||
errx(EXIT_FAILURE, "Missing bootfile argument");
|
||||
|
||||
error = stat(bootfile, &sb);
|
||||
if (error)
|
||||
errx(EXIT_FAILURE, "%s: not found", bootfile);
|
||||
if (!S_ISREG(sb.st_mode))
|
||||
errx(EXIT_FAILURE, "%s: not a regular file", bootfile);
|
||||
if (sb.st_size >= 1024*1024)
|
||||
errx(EXIT_FAILURE, "%s: file too big", bootfile);
|
||||
|
||||
size = sb.st_size;
|
||||
|
||||
fd = open(bootfile, O_RDONLY);
|
||||
if (fd == -1)
|
||||
errx(EXIT_FAILURE, "%s: unable to open", bootfile);
|
||||
code = malloc(size);
|
||||
if (code == NULL)
|
||||
errx(EXIT_FAILURE, "out of memory");
|
||||
if (read(fd, code, size) != size)
|
||||
errx(EXIT_FAILURE, "%s: unable to read", bootfile);
|
||||
close(fd);
|
||||
|
||||
gctl_change_param(req, "bootcode", size, code);
|
||||
gctl_issue(req);
|
||||
}
|
||||
|
@ -112,6 +112,7 @@ DECLARE_GEOM_CLASS(g_part_class, g_part);
|
||||
enum g_part_ctl {
|
||||
G_PART_CTL_NONE,
|
||||
G_PART_CTL_ADD,
|
||||
G_PART_CTL_BOOTCODE,
|
||||
G_PART_CTL_COMMIT,
|
||||
G_PART_CTL_CREATE,
|
||||
G_PART_CTL_DELETE,
|
||||
@ -513,6 +514,48 @@ g_part_ctl_add(struct gctl_req *req, struct g_part_parms *gpp)
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
g_part_ctl_bootcode(struct gctl_req *req, struct g_part_parms *gpp)
|
||||
{
|
||||
struct g_geom *gp;
|
||||
struct g_part_table *table;
|
||||
struct sbuf *sb;
|
||||
int error, sz;
|
||||
|
||||
gp = gpp->gpp_geom;
|
||||
G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
|
||||
g_topology_assert();
|
||||
|
||||
table = gp->softc;
|
||||
sz = table->gpt_scheme->gps_bootcodesz;
|
||||
if (sz == 0) {
|
||||
error = ENODEV;
|
||||
goto fail;
|
||||
}
|
||||
if (gpp->gpp_codesize != sz) {
|
||||
error = EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
error = G_PART_BOOTCODE(table, gpp);
|
||||
if (error)
|
||||
goto fail;
|
||||
|
||||
/* Provide feedback if so requested. */
|
||||
if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
|
||||
sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND);
|
||||
sbuf_printf(sb, "%s has bootcode\n", gp->name);
|
||||
sbuf_finish(sb);
|
||||
gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
|
||||
sbuf_delete(sb);
|
||||
}
|
||||
return (0);
|
||||
|
||||
fail:
|
||||
gctl_error(req, "%d", error);
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
g_part_ctl_commit(struct gctl_req *req, struct g_part_parms *gpp)
|
||||
{
|
||||
@ -1023,7 +1066,7 @@ g_part_ctlreq(struct gctl_req *req, struct g_class *mp, const char *verb)
|
||||
enum g_part_ctl ctlreq;
|
||||
unsigned int i, mparms, oparms, parm;
|
||||
int auto_commit, close_on_error;
|
||||
int error, modifies;
|
||||
int error, len, modifies;
|
||||
|
||||
G_PART_TRACE((G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, verb));
|
||||
g_topology_assert();
|
||||
@ -1041,6 +1084,12 @@ g_part_ctlreq(struct gctl_req *req, struct g_class *mp, const char *verb)
|
||||
oparms |= G_PART_PARM_INDEX | G_PART_PARM_LABEL;
|
||||
}
|
||||
break;
|
||||
case 'b':
|
||||
if (!strcmp(verb, "bootcode")) {
|
||||
ctlreq = G_PART_CTL_BOOTCODE;
|
||||
mparms |= G_PART_PARM_GEOM | G_PART_PARM_BOOTCODE;
|
||||
}
|
||||
break;
|
||||
case 'c':
|
||||
if (!strcmp(verb, "commit")) {
|
||||
ctlreq = G_PART_CTL_COMMIT;
|
||||
@ -1098,6 +1147,10 @@ g_part_ctlreq(struct gctl_req *req, struct g_class *mp, const char *verb)
|
||||
ap = &req->arg[i];
|
||||
parm = 0;
|
||||
switch (ap->name[0]) {
|
||||
case 'b':
|
||||
if (!strcmp(ap->name, "bootcode"))
|
||||
parm = G_PART_PARM_BOOTCODE;
|
||||
break;
|
||||
case 'c':
|
||||
if (!strcmp(ap->name, "class"))
|
||||
continue;
|
||||
@ -1153,12 +1206,20 @@ g_part_ctlreq(struct gctl_req *req, struct g_class *mp, const char *verb)
|
||||
gctl_error(req, "%d param '%s'", EINVAL, ap->name);
|
||||
return;
|
||||
}
|
||||
p = gctl_get_asciiparam(req, ap->name);
|
||||
if (parm == G_PART_PARM_BOOTCODE)
|
||||
p = gctl_get_param(req, ap->name, &len);
|
||||
else
|
||||
p = gctl_get_asciiparam(req, ap->name);
|
||||
if (p == NULL) {
|
||||
gctl_error(req, "%d param '%s'", ENOATTR, ap->name);
|
||||
return;
|
||||
}
|
||||
switch (parm) {
|
||||
case G_PART_PARM_BOOTCODE:
|
||||
gpp.gpp_codeptr = p;
|
||||
gpp.gpp_codesize = len;
|
||||
error = 0;
|
||||
break;
|
||||
case G_PART_PARM_ENTRIES:
|
||||
error = g_part_parm_uint(p, &gpp.gpp_entries);
|
||||
break;
|
||||
@ -1240,6 +1301,9 @@ g_part_ctlreq(struct gctl_req *req, struct g_class *mp, const char *verb)
|
||||
case G_PART_CTL_ADD:
|
||||
error = g_part_ctl_add(req, &gpp);
|
||||
break;
|
||||
case G_PART_CTL_BOOTCODE:
|
||||
error = g_part_ctl_bootcode(req, &gpp);
|
||||
break;
|
||||
case G_PART_CTL_COMMIT:
|
||||
error = g_part_ctl_commit(req, &gpp);
|
||||
break;
|
||||
|
@ -56,6 +56,7 @@ struct g_part_scheme {
|
||||
size_t gps_entrysz;
|
||||
int gps_minent;
|
||||
int gps_maxent;
|
||||
int gps_bootcodesz;
|
||||
TAILQ_ENTRY(g_part_scheme) scheme_list;
|
||||
};
|
||||
|
||||
@ -134,6 +135,7 @@ struct g_part_entry *g_part_new_entry(struct g_part_table *, int, quad_t,
|
||||
#define G_PART_PARM_START 0x0200
|
||||
#define G_PART_PARM_TYPE 0x0400
|
||||
#define G_PART_PARM_VERSION 0x0800
|
||||
#define G_PART_PARM_BOOTCODE 0x1000
|
||||
|
||||
struct g_part_parms {
|
||||
unsigned int gpp_parms;
|
||||
@ -148,6 +150,8 @@ struct g_part_parms {
|
||||
quad_t gpp_start;
|
||||
const char *gpp_type;
|
||||
unsigned int gpp_version;
|
||||
const void *gpp_codeptr;
|
||||
unsigned int gpp_codesize;
|
||||
};
|
||||
|
||||
void g_part_geometry_heads(off_t, u_int, off_t *, u_int *);
|
||||
|
@ -52,6 +52,8 @@ CTASSERT(sizeof(struct gpt_ent) == 128);
|
||||
|
||||
#define EQUUID(a,b) (memcmp(a, b, sizeof(struct uuid)) == 0)
|
||||
|
||||
#define MBRSIZE 512
|
||||
|
||||
enum gpt_elt {
|
||||
GPT_ELT_PRIHDR,
|
||||
GPT_ELT_PRITBL,
|
||||
@ -70,6 +72,7 @@ enum gpt_state {
|
||||
|
||||
struct g_part_gpt_table {
|
||||
struct g_part_table base;
|
||||
u_char mbr[MBRSIZE];
|
||||
struct gpt_hdr hdr;
|
||||
quad_t lba[GPT_ELT_COUNT];
|
||||
enum gpt_state state[GPT_ELT_COUNT];
|
||||
@ -82,6 +85,7 @@ struct g_part_gpt_entry {
|
||||
|
||||
static int g_part_gpt_add(struct g_part_table *, struct g_part_entry *,
|
||||
struct g_part_parms *);
|
||||
static int g_part_gpt_bootcode(struct g_part_table *, struct g_part_parms *);
|
||||
static int g_part_gpt_create(struct g_part_table *, struct g_part_parms *);
|
||||
static int g_part_gpt_destroy(struct g_part_table *, struct g_part_parms *);
|
||||
static int g_part_gpt_dumpto(struct g_part_table *, struct g_part_entry *);
|
||||
@ -97,6 +101,7 @@ static int g_part_gpt_write(struct g_part_table *, struct g_consumer *);
|
||||
|
||||
static kobj_method_t g_part_gpt_methods[] = {
|
||||
KOBJMETHOD(g_part_add, g_part_gpt_add),
|
||||
KOBJMETHOD(g_part_bootcode, g_part_gpt_bootcode),
|
||||
KOBJMETHOD(g_part_create, g_part_gpt_create),
|
||||
KOBJMETHOD(g_part_destroy, g_part_gpt_destroy),
|
||||
KOBJMETHOD(g_part_dumpto, g_part_gpt_dumpto),
|
||||
@ -116,6 +121,7 @@ static struct g_part_scheme g_part_gpt_scheme = {
|
||||
.gps_entrysz = sizeof(struct g_part_gpt_entry),
|
||||
.gps_minent = 128,
|
||||
.gps_maxent = INT_MAX,
|
||||
.gps_bootcodesz = MBRSIZE,
|
||||
};
|
||||
G_PART_SCHEME_DECLARE(g_part_gpt);
|
||||
|
||||
@ -351,6 +357,16 @@ g_part_gpt_add(struct g_part_table *basetable, struct g_part_entry *baseentry,
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
g_part_gpt_bootcode(struct g_part_table *basetable, struct g_part_parms *gpp)
|
||||
{
|
||||
struct g_part_gpt_table *table;
|
||||
|
||||
table = (struct g_part_gpt_table *)basetable;
|
||||
bcopy(gpp->gpp_codeptr, table->mbr, DOSPARTOFF);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
g_part_gpt_create(struct g_part_table *basetable, struct g_part_parms *gpp)
|
||||
{
|
||||
@ -363,13 +379,24 @@ g_part_gpt_create(struct g_part_table *basetable, struct g_part_parms *gpp)
|
||||
pp = gpp->gpp_provider;
|
||||
tblsz = (basetable->gpt_entries * sizeof(struct gpt_ent) +
|
||||
pp->sectorsize - 1) / pp->sectorsize;
|
||||
if (pp->sectorsize < 512 ||
|
||||
if (pp->sectorsize < MBRSIZE ||
|
||||
pp->mediasize < (3 + 2 * tblsz + basetable->gpt_entries) *
|
||||
pp->sectorsize)
|
||||
return (ENOSPC);
|
||||
|
||||
last = (pp->mediasize / pp->sectorsize) - 1;
|
||||
|
||||
le16enc(table->mbr + DOSMAGICOFFSET, DOSMAGIC);
|
||||
table->mbr[DOSPARTOFF + 1] = 0xff; /* shd */
|
||||
table->mbr[DOSPARTOFF + 2] = 0xff; /* ssect */
|
||||
table->mbr[DOSPARTOFF + 3] = 0xff; /* scyl */
|
||||
table->mbr[DOSPARTOFF + 4] = 0xee; /* typ */
|
||||
table->mbr[DOSPARTOFF + 5] = 0xff; /* ehd */
|
||||
table->mbr[DOSPARTOFF + 6] = 0xff; /* esect */
|
||||
table->mbr[DOSPARTOFF + 7] = 0xff; /* ecyl */
|
||||
le32enc(table->mbr + DOSPARTOFF + 8, 1); /* start */
|
||||
le32enc(table->mbr + DOSPARTOFF + 12, MIN(last, 0xffffffffLL));
|
||||
|
||||
table->lba[GPT_ELT_PRIHDR] = 1;
|
||||
table->lba[GPT_ELT_PRITBL] = 2;
|
||||
table->lba[GPT_ELT_SECHDR] = last;
|
||||
@ -469,7 +496,7 @@ g_part_gpt_probe(struct g_part_table *table, struct g_consumer *cp)
|
||||
* It's better to catch this pathological case early than behaving
|
||||
* pathologically later on...
|
||||
*/
|
||||
if (pp->sectorsize < 512 || pp->mediasize < 6 * pp->sectorsize)
|
||||
if (pp->sectorsize < MBRSIZE || pp->mediasize < 6 * pp->sectorsize)
|
||||
return (ENOSPC);
|
||||
|
||||
/* Check that there's a MBR. */
|
||||
@ -508,11 +535,19 @@ g_part_gpt_read(struct g_part_table *basetable, struct g_consumer *cp)
|
||||
struct g_provider *pp;
|
||||
struct g_part_gpt_table *table;
|
||||
struct g_part_gpt_entry *entry;
|
||||
int index;
|
||||
u_char *buf;
|
||||
int error, index;
|
||||
|
||||
table = (struct g_part_gpt_table *)basetable;
|
||||
pp = cp->provider;
|
||||
|
||||
/* Read the PMBR */
|
||||
buf = g_read_data(cp, 0, pp->sectorsize, &error);
|
||||
if (buf == NULL)
|
||||
return (error);
|
||||
bcopy(buf, table->mbr, MBRSIZE);
|
||||
g_free(buf);
|
||||
|
||||
/* Read the primary header and table. */
|
||||
gpt_read_hdr(table, cp, GPT_ELT_PRIHDR, &prihdr);
|
||||
if (table->state[GPT_ELT_PRIHDR] == GPT_STATE_OK) {
|
||||
@ -640,24 +675,13 @@ g_part_gpt_write(struct g_part_table *basetable, struct g_consumer *cp)
|
||||
tlbsz = (table->hdr.hdr_entries * table->hdr.hdr_entsz +
|
||||
pp->sectorsize - 1) / pp->sectorsize;
|
||||
|
||||
if (basetable->gpt_created) {
|
||||
buf = g_malloc(pp->sectorsize, M_WAITOK | M_ZERO);
|
||||
le16enc(buf + DOSMAGICOFFSET, DOSMAGIC);
|
||||
buf[DOSPARTOFF + 1] = 0xff; /* shd */
|
||||
buf[DOSPARTOFF + 2] = 0xff; /* ssect */
|
||||
buf[DOSPARTOFF + 3] = 0xff; /* scyl */
|
||||
buf[DOSPARTOFF + 4] = 0xee; /* typ */
|
||||
buf[DOSPARTOFF + 5] = 0xff; /* ehd */
|
||||
buf[DOSPARTOFF + 6] = 0xff; /* esect */
|
||||
buf[DOSPARTOFF + 7] = 0xff; /* ecyl */
|
||||
le32enc(buf + DOSPARTOFF + 8, 1); /* start */
|
||||
le32enc(buf + DOSPARTOFF + 12,
|
||||
MIN(pp->mediasize / pp->sectorsize - 1, 0xffffffffLL));
|
||||
error = g_write_data(cp, 0, buf, pp->sectorsize);
|
||||
g_free(buf);
|
||||
if (error)
|
||||
return (error);
|
||||
}
|
||||
/* Write the PMBR */
|
||||
buf = g_malloc(pp->sectorsize, M_WAITOK | M_ZERO);
|
||||
bcopy(table->mbr, buf, MBRSIZE);
|
||||
error = g_write_data(cp, 0, buf, pp->sectorsize);
|
||||
g_free(buf);
|
||||
if (error)
|
||||
return (error);
|
||||
|
||||
/* Allocate space for the header and entries. */
|
||||
buf = g_malloc((tlbsz + 1) * pp->sectorsize, M_WAITOK | M_ZERO);
|
||||
|
@ -47,6 +47,12 @@ METHOD int add {
|
||||
struct g_part_parms *gpp;
|
||||
};
|
||||
|
||||
# bootcode() - scheme specific processing for the bootcode verb.
|
||||
METHOD int bootcode {
|
||||
struct g_part_table *table;
|
||||
struct g_part_parms *gpp;
|
||||
};
|
||||
|
||||
# create() - scheme specific processing for the create verb.
|
||||
METHOD int create {
|
||||
struct g_part_table *table;
|
||||
|
@ -59,6 +59,7 @@ struct g_part_mbr_entry {
|
||||
|
||||
static int g_part_mbr_add(struct g_part_table *, struct g_part_entry *,
|
||||
struct g_part_parms *);
|
||||
static int g_part_mbr_bootcode(struct g_part_table *, struct g_part_parms *);
|
||||
static int g_part_mbr_create(struct g_part_table *, struct g_part_parms *);
|
||||
static int g_part_mbr_destroy(struct g_part_table *, struct g_part_parms *);
|
||||
static int g_part_mbr_dumpto(struct g_part_table *, struct g_part_entry *);
|
||||
@ -74,6 +75,7 @@ static int g_part_mbr_write(struct g_part_table *, struct g_consumer *);
|
||||
|
||||
static kobj_method_t g_part_mbr_methods[] = {
|
||||
KOBJMETHOD(g_part_add, g_part_mbr_add),
|
||||
KOBJMETHOD(g_part_bootcode, g_part_mbr_bootcode),
|
||||
KOBJMETHOD(g_part_create, g_part_mbr_create),
|
||||
KOBJMETHOD(g_part_destroy, g_part_mbr_destroy),
|
||||
KOBJMETHOD(g_part_dumpto, g_part_mbr_dumpto),
|
||||
@ -93,6 +95,7 @@ static struct g_part_scheme g_part_mbr_scheme = {
|
||||
.gps_entrysz = sizeof(struct g_part_mbr_entry),
|
||||
.gps_minent = NDOSPART,
|
||||
.gps_maxent = NDOSPART,
|
||||
.gps_bootcodesz = MBRSIZE,
|
||||
};
|
||||
G_PART_SCHEME_DECLARE(g_part_mbr);
|
||||
|
||||
@ -200,6 +203,16 @@ g_part_mbr_add(struct g_part_table *basetable, struct g_part_entry *baseentry,
|
||||
return (mbr_parse_type(gpp->gpp_type, &entry->ent.dp_typ));
|
||||
}
|
||||
|
||||
static int
|
||||
g_part_mbr_bootcode(struct g_part_table *basetable, struct g_part_parms *gpp)
|
||||
{
|
||||
struct g_part_mbr_table *table;
|
||||
|
||||
table = (struct g_part_mbr_table *)basetable;
|
||||
bcopy(gpp->gpp_codeptr, table->mbr, DOSPARTOFF);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
g_part_mbr_create(struct g_part_table *basetable, struct g_part_parms *gpp)
|
||||
{
|
||||
|
@ -60,6 +60,7 @@ struct g_part_pc98_entry {
|
||||
|
||||
static int g_part_pc98_add(struct g_part_table *, struct g_part_entry *,
|
||||
struct g_part_parms *);
|
||||
static int g_part_pc98_bootcode(struct g_part_table *, struct g_part_parms *);
|
||||
static int g_part_pc98_create(struct g_part_table *, struct g_part_parms *);
|
||||
static int g_part_pc98_destroy(struct g_part_table *, struct g_part_parms *);
|
||||
static int g_part_pc98_dumpto(struct g_part_table *, struct g_part_entry *);
|
||||
@ -75,6 +76,7 @@ static int g_part_pc98_write(struct g_part_table *, struct g_consumer *);
|
||||
|
||||
static kobj_method_t g_part_pc98_methods[] = {
|
||||
KOBJMETHOD(g_part_add, g_part_pc98_add),
|
||||
KOBJMETHOD(g_part_bootcode, g_part_pc98_bootcode),
|
||||
KOBJMETHOD(g_part_create, g_part_pc98_create),
|
||||
KOBJMETHOD(g_part_destroy, g_part_pc98_destroy),
|
||||
KOBJMETHOD(g_part_dumpto, g_part_pc98_dumpto),
|
||||
@ -94,6 +96,7 @@ static struct g_part_scheme g_part_pc98_scheme = {
|
||||
.gps_entrysz = sizeof(struct g_part_pc98_entry),
|
||||
.gps_minent = NDOSPART,
|
||||
.gps_maxent = NDOSPART,
|
||||
.gps_bootcodesz = SECSIZE,
|
||||
};
|
||||
G_PART_SCHEME_DECLARE(g_part_pc98);
|
||||
|
||||
@ -183,6 +186,16 @@ g_part_pc98_add(struct g_part_table *basetable, struct g_part_entry *baseentry,
|
||||
&entry->ent.dp_sid));
|
||||
}
|
||||
|
||||
static int
|
||||
g_part_pc98_bootcode(struct g_part_table *basetable, struct g_part_parms *gpp)
|
||||
{
|
||||
struct g_part_pc98_table *table;
|
||||
|
||||
table = (struct g_part_pc98_table *)basetable;
|
||||
bcopy(gpp->gpp_codeptr, table->boot, DOSMAGICOFFSET);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
g_part_pc98_create(struct g_part_table *basetable, struct g_part_parms *gpp)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user