Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / legion / src / devices / mem_bus / libmemory / mem.c
/*
* ========== Copyright Header Begin ==========================================
*
* OpenSPARC T2 Processor File: mem.c
* Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES.
*
* The above named program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License version 2 as published by the Free Software Foundation.
*
* The above named program is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*
* ========== Copyright Header End ============================================
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "@(#)mem.c 1.24 07/04/16 SMI"
/*
* Complete the parsing of a generic memory device.
*
* Pick out the relevent info and allocated the
* simcpu_t structure in the config_cpu that
* gets handed to us.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <errno.h>
#include <fcntl.h>
#if VDISK_SUPPORT /* { */
#include <sys/dkio.h>
#include <sys/dklabel.h>
#include <sys/vtoc.h>
#endif /* VDISK_SUPPORT } */
#include <strings.h>
#include "basics.h"
#include "allocate.h"
#include "lexer.h"
#include "simcore.h"
#include "config.h"
#include "dumpinfo.h"
#include "strutil.h"
#include "fatal.h"
#include "options.h"
/*
* static only to avoid name clashes with other
* modules ... in reality these are exported
* via the cpu_type_t struct pointers
*/
static void mem_parse(config_dev_t *);
static void mem_init(config_dev_t *);
static void mem_dump(config_dev_t *);
static tpaddr_t mem_cacheable(config_addr_t *, dev_access_t type,
tpaddr_t off, uint8_t **cbp);
static bool_t mem_cpu_access(simcpu_t *, config_addr_t *, tpaddr_t off,
maccess_t op, uint64_t *regp);
dev_type_t dev_type_memory = {
"memory",
mem_parse,
mem_init,
mem_dump,
mem_cacheable,
mem_cpu_access, /* shouldnt be called if we're a RAM */
DEV_MAGIC
};
typedef struct MEM_CONTENT mem_content_t;
typedef struct {
char *fnamep;
uint64_t fileextent; /* how much of file to load in */
uint64_t fileextenta; /* page alligned fileextent */
uint64_t fileoffset; /* offset in file to start loading */
uint64_t memoffset; /* offset in memory of where to load */
#if VDISK_SUPPORT /* { */
int disk_slice; /* 0-1,3-7 (2 resvd for entire disk) */
#endif /* VDISK_SUPPORT } */
bool_t is_rom; /* mmap disk with MAP_PRIVATE flag */
bool_t is_shared; /* mmap disk with MAP_SHARED flag */
} mem_file_t;
typedef struct {
bool_t is_rom; /* mmap file with MAP_PRIVATE flag */
bool_t is_shared; /* mmap file with MAP_SHARED flag */
#if VDISK_SUPPORT /* { */
bool_t is_disk; /* used to identify virtual disk */
#endif /* VDISK_SUPPORT } */
uint8_t *datap;
uint64_t size; /* size of memory segment */
mem_content_t *contentp; /* Any content to manually add */
int nfileblocks; /* no of files to load into segment */
mem_file_t fileblock[1]; /* fileblock struct per file */
} mem_dev_t;
struct MEM_CONTENT {
uint64_t offset;
uint64_t size;
mem_content_t *nextp;
uint8_t *datap;
};
#if VDISK_SUPPORT /* { */
/*
* local functions for manipulating disk labels
*/
static void create_label(mem_dev_t *mdp);
static void dump_label(mem_dev_t *mdp);
static short get_checksum(struct dk_label *dkl, int mode);
#endif /* VDISK_SUPPORT } */
static void mem_parse_content(mem_dev_t *mdp);
#define MAXNAMELEN 256
/*
* Complete the creation and parsing of the memory directives
*
* Format for parsing internal memory data is:
*
* [rom | shared | virtual_disk]
*
* if a 'rom | shared' directive is found, we parse as follows:
*
* load [[+]addr] "filename" [ file_offset [ extent_to_load ] ]
*
* - ROM can only be specified once, we can have as many
* load directives as we like !
*
* if a 'virtual_disk' directive is found, we parse as follows:
* load s0 [rom|shared] "filename"
* load s1 [rom|shared] "filename"
*
* - We will figure out the size of the entire disk by opening each
* file. It's important that the first disk contains a valid
* VTOC at block 0. We will update the mmapped copy of this VTOC
* with the virtual partition information of all disks in the
* virtual disk.
*
* - You cannot specify a disk to load into s2 as that is reserved
* to describe the entire disk.
* - You can load disks into s0 (boot disk), s1 (swap),
* s3,s4,s5,s6,s7 (user specified mount points).
*
* - Each disk will be mmapped with the permissions specified in
* the load directive.
*/
void
mem_parse(config_dev_t *config_devp)
{
lexer_tok_t tok;
mem_dev_t *mdp;
uint64_t memory_segment_size; /* initial value of mdp->size */
long pgsize;
pgsize = getpagesize();
/*
* Allocate the memory device and all that stuff
*/
mdp = Xcalloc(1, mem_dev_t);
mdp->is_rom = false;
mdp->is_shared = false;
#if VDISK_SUPPORT /* { */
mdp->is_disk = false;
#endif /* VDISK_SUPPORT } */
mdp->datap = (void*)0;
mdp->contentp = (mem_content_t *)0;
mdp->size = config_devp->addrp->range;
mdp->nfileblocks = 0;
/* CSTYLED */
DBG( PRINTF(("mem_parse: parsing device %d\n", config_devp->device_id)); );
tok = lex_get_token();
switch (tok) {
case T_S_Colon:
goto finished; /* nothing more to parse */
case T_L_Brace:
break;
default:
lex_fatal("expecting either ; or { when parsing memory device");
}
/*
* Start the parsing loop
*/
do {
char *fnamep;
uint64_t startoffset = 0LL;
uint64_t foffset = 0LL;
uint64_t flen = 0LL;
struct stat sb;
int idx;
#if VDISK_SUPPORT /* { */
int disk_slice = -1;
#endif /* VDISK_SUPPORT } */
bool_t is_shared, is_rom;
tok = lex_get_token();
if (tok == T_R_Brace) break;
if (tok != T_Token) {
fail:;
lex_fatal("expected load, rom, shared or virtual_disk "
"directive parsing memory device");
}
if (streq(lex.strp, "rom")) {
if (mdp->is_rom) lex_fatal("rom already specified");
mdp->is_rom = true;
lex_get(T_S_Colon);
continue;
}
if (streq(lex.strp, "shared")) {
if (mdp->is_shared)
lex_fatal("shared already specified");
mdp->is_shared = true;
lex_get(T_S_Colon);
continue;
}
if (streq(lex.strp, "virtual_disk")) {
#if VDISK_SUPPORT /* { */
if (mdp->is_disk)
lex_fatal("virtual_disk already specified");
mdp->is_disk = true;
/*
* For disks, we don't need the size as specified in the
* conf file. We'll calculate the real size once we've
* found the size of all disks loaded.
*
* Howvever, we need to ensure that the memory segment
* used for mmapping the disks does not overlap with
* any other memory segment in use. Legion has already
* done this check when parsing the memory directive.
* We have gotten here if we have not overlapped. The
* concern now is that when we parse the disk directives
* we may extend beyond the memory segment specified by
* the memory directive. So as a check, we save the size
* here and run a check at the end to ensure that the
* total size of all disks is within the bounds of the
* size of the memory segment.
*/
memory_segment_size = mdp->size;
mdp->size = 0; /* We'll calculate this as we parse */
lex_get(T_S_Colon);
continue;
#else /* VDISK_SUPPORT } { */
lex_fatal("virtual_disk not supported on this "
"platform");
#endif /* VDISK_SUPPORT } */
}
if (streq(lex.strp, "content")) {
mem_parse_content(mdp);
continue;
}
if (!streq(lex.strp, "load")) goto fail;
tok = lex_get_token();
#if VDISK_SUPPORT /* { */
/*
* Parse disk directive as follows:
*
* s(n) rom|shared "disk.name"
*
* where:
* - s(n) is the slice number (cannot be s2)
* - rom or shared to specify how to mmap this disk
* - "disk.name" you can probably guess ...
*
* NOTE: The VTOC of s0 will be overwritten in memory
* to with the correct label for all partitions
* that make up this virtual disk.
* s2 of the VTOC will contain the entire size
* of all mmapped disks. We also need a valid
* disk label checksum so OBP can open this disk.
*/
if (mdp->is_disk) {
/*
* figure out which slice number this disk is
*/
if (streq(lex.strp, "s0"))
disk_slice = 0;
else if (streq(lex.strp, "s1"))
disk_slice = 1;
else if (streq(lex.strp, "s2"))
lex_fatal("Cannot load a disk as slice 2.");
else if (streq(lex.strp, "s3"))
disk_slice = 3;
else if (streq(lex.strp, "s4"))
disk_slice = 4;
else if (streq(lex.strp, "s5"))
disk_slice = 5;
else if (streq(lex.strp, "s6"))
disk_slice = 6;
else if (streq(lex.strp, "s7"))
disk_slice = 7;
else
lex_fatal("expected a slice number of format "
"s0-7 (but not s2)");
tok = lex_get_token();
/*
* parse the mmap permissions for this disk
* (rom or shared)
*/
is_rom = is_shared = false;
if (streq(lex.strp, "rom"))
is_rom = true;
else if (streq(lex.strp, "shared"))
is_shared = true;
else
lex_fatal("expected either rom or shared");
/*
* parse the filename
*/
lex_get(T_String);
fnamep = Xstrdup(lex.strp);
tok = lex_get_token();
if (tok == T_S_Colon)
goto load_file;
else
lex_fatal("expected ; after filename");
}
#endif /* VDISK_SUPPORT } */
/*
* Get the load offset, ie. the offset from the base
* address to load this file. For disk devices, we
* don't need this as we use the size of the previous
* disk to work out where to load the next disk.
*/
switch (tok) {
case T_Plus:
lex_get(T_Number);
startoffset = lex.val;
break;
case T_Number:
if (lex.val < config_devp->addrp->baseaddr ||
lex.val >= config_devp->addrp->topaddr)
lex_fatal("specified load address is outside "
"the range of the memory device");
startoffset = lex.val - config_devp->addrp->baseaddr;
break;
case T_String:
startoffset = 0x0LL;
goto got_string;
default:
lex_fatal("Expected either a start address / offset "
"or filename for memory device load directive");
}
lex_get(T_String);
got_string:;
fnamep = Xstrdup(lex.strp);
tok = lex_get_token();
if (tok == T_S_Colon)
goto load_file;
if (tok != T_Number)
lex_fatal("Expected ; or file offset for memory device "
"load directive");
if (lex.val < 0LL)
lex_fatal("load file offset must be >0 for load directive");
foffset = lex.val;
tok = lex_get_token();
if (tok == T_S_Colon)
goto load_file;
if (tok != T_Number)
lex_fatal("Expected ; or load length for memory device "
"load directive");
if (lex.val <= 0LL)
lex_fatal("load length must be >=0 for load directive");
flen = lex.val;
lex_get(T_S_Colon);
load_file:
if (stat(fnamep, &sb) < 0)
lex_fatal("error opening load file %s", fnamep);
if (flen == 0LL)
flen = sb.st_size - foffset;
#if VDISK_SUPPORT /* { */
/*
* Set the load address of this disk slice (startoffset)
* to be right after the address that the previous
* slice loaded at (mdp->size)
*
* Increase the size of the disk by flen (rounded up
* so it's page aligned.) The next slice (if any)
* will be loaded at mdp->size.
*/
if (mdp->is_disk) {
startoffset = mdp->size;
mdp->size += sim_roundup(flen, pgsize);
/*
* sanity check to see if we've blown passed the
* the memory segment as specified in the conf
* file.
*/
if (mdp->size > memory_segment_size) {
printf("\n Memory segment overflow. "
"[0x%llx > 0x%llx]",
mdp->size, memory_segment_size);
lex_fatal("memory segment for disks has "
"extended beyond that specified in the "
"conf file");
}
}
#endif /* VDISK_SUPPORT } */
if (sb.st_size < (foffset + flen))
lex_fatal("load file %s is smaller than the specified "
"load range", fnamep);
if ((startoffset + flen) > config_devp->addrp->range)
lex_fatal("load file %s is larger than the memory device",
fnamep);
/* OK have parsed file info - add it to load block */
idx = mdp->nfileblocks++;
/* CSTYLED */
mdp = Xrealloc(mdp, Sizeof(mem_dev_t) + mdp->nfileblocks * Sizeof(mem_file_t));
mdp->fileblock[idx].fnamep = fnamep;
/* how much of file to load in */
mdp->fileblock[idx].fileextent = flen;
mdp->fileblock[idx].fileextenta = sim_roundup(flen, pgsize);
/* offset in file to start load */
mdp->fileblock[idx].fileoffset = foffset;
/* offset in mem to start load */
mdp->fileblock[idx].memoffset = startoffset;
/* MMAP with flag MAP_SHARED */
mdp->fileblock[idx].is_shared = is_shared;
/* MMAP with flag MAP_PRIVATE */
mdp->fileblock[idx].is_rom = is_rom;
#if VDISK_SUPPORT /* { */
/* disk partition number */
mdp->fileblock[idx].disk_slice = disk_slice;
if (mdp->is_disk) {
/* CSTYLED */
DBG( PRINTF(("\nPartition Info:::\nname: %s \nslice=%d "
"\nbase=0x%llx "
"\nstartoffset = %llx \nflen = %llx \nfoffset = %llx "
"\nmdp->size = 0x%llx\nfilextent = 0x%llx "
"fileextenta = 0x%llx "
"\nmemoffset = 0x%llx\n", mdp->fileblock[idx].fnamep,
mdp->fileblock[idx].disk_slice,
config_devp->addrp->baseaddr,
startoffset, flen, foffset, mdp->size,
mdp->fileblock[idx].fileextent,
mdp->fileblock[idx].fileextenta,
/* CSTYLED */
mdp->fileblock[idx].memoffset)); );
}
#endif /* VDISK_SUPPORT } */
} while (1);
finished:;
config_devp->devp = mdp;
}
static int
mem_insert(mem_dev_t *mdp, uint64_t offset, uint64_t size, uint8_t *datap)
{
mem_content_t *tp, **tpp;
/* CSTYLED */
DBG( PRINTF(("mem_insert:offset: 0x%llx size: 0x%llx\n", offset, size)); );
/* walk the list looking for insertion point */
for (tpp = &mdp->contentp; (tp = *tpp) != NULL; tpp = &tp->nextp) {
if (offset < tp->offset) {
if ((offset + size) > tp->offset) {
return (-1); /* overlap */
} else {
break;
}
}
if ((tp->offset + tp->size) > offset) {
return (-1); /* overlap */
}
}
/* found the location, add the mem object */
tp = Xcalloc(1, mem_content_t);
tp->offset = offset;
tp->size = size;
tp->datap = datap;
tp->nextp = *tpp;
*tpp = tp;
return (0);
}
/*
* Handle the content directive
*/
#define CONTENT_STEP 256
void
mem_parse_content(mem_dev_t *mdp)
{
uint64_t offset;
uint64_t size;
uint64_t mask;
int word_size;
lexer_tok_t tok;
int space;
uint8_t *datap;
int idx;
lex_get(T_Plus);
lex_get(T_Number);
offset = lex.val;
if (offset > mdp->size) lex_fatal("Offset is outside memory range");
lex_get(T_Number);
word_size = (int)lex.val;
switch (word_size) {
case 8:
mask = 0xffull;
break;
case 16:
mask = 0xffffull;
break;
case 32:
mask = 0xffffffffull;
break;
case 64:
mask = 0xffffffffffffffffull;
break;
default:
lex_fatal("Word size must be 8, 16, 32 or 64 bits");
}
word_size >>= 3;
lex_get(T_L_Brace);
idx = 0;
space = 0;
datap = NULL;
while ((tok = lex_get_token()) != T_R_Brace) {
uint8_t *p;
if (tok != T_Number) lex_fatal("Unexpected content");
if ((lex.val & ~mask) != 0LL) lex_fatal("Content out of range");
if (offset+idx+word_size >= mdp->size)
lex_fatal("Content overflows memory range");
if (space <= (idx+word_size)) {
space += CONTENT_STEP;
datap = Xrealloc(datap, space);
}
/* ensure it's written big-endian */
switch (word_size) {
case 8:
datap[idx] = (lex.val >> 56);
datap[idx+1] = (lex.val >> 48);
datap[idx+2] = (lex.val >> 40);
datap[idx+3] = (lex.val >> 32);
idx += 4;
case 4:
datap[idx] = (lex.val >> 24);
datap[idx+1] = (lex.val >> 16);
idx += 2;
case 2:
datap[idx] = (lex.val >> 8);
idx++;
case 1:
datap[idx] = lex.val;
idx++;
}
}
if (idx == 0) lex_fatal("content directive must have some content");
if (mem_insert(mdp, offset, idx, datap) < 0) {
lex_fatal("Memory content overlap detected ");
}
}
/*
* Initialise the mem after parsing is complete
* If no files have been requested, then we force a
* /dev/zero mapping ...
* ... this is why the additional files start at
* fileblock index 1.
*/
void
mem_init(config_dev_t *config_devp)
{
long pgsize;
mem_dev_t *mdp;
uint8_t *datap;
int idx;
mem_content_t *cp, *next_cp;
mdp = (mem_dev_t *)config_devp->devp;
pgsize = getpagesize();
/*
* Perform a check to see that the requested loaded files are
* a) correctly aligned, and
* b) whether we need a /dev/zero mapping to back sections not
* covered by other mmapped files (beginning, middle,
* end), or because we need to "load" files in that are
* not correctly aligned.
*/
/*
* For the moment we create a MAP_ANON block of the entire
* ram/rom then map over than any additional data files we need.
* Force a segv if we ever try and write to a "ROM".
*/
/* CSTYLED */
DBG( PRINTF(("memory mapping : 0x%llx\n", sim_roundup(mdp->size, pgsize))); );
datap = mdp->datap = (void*)mmap(NULL, sim_roundup(mdp->size, pgsize),
PROT_READ | (mdp->is_rom ? 0 : PROT_WRITE),
MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, -1, 0);
if (MAP_FAILED == datap) fatal("Initial mmap of anon memory failed");
/*
* Now we either map or load in all the remaining files that the
* config file may have specified.
*/
for (idx = 0; idx < mdp->nfileblocks; idx++) {
int fd;
uint8_t *mapp;
bool_t is_shared = mdp->is_shared;
#if VDISK_SUPPORT /* { */
/*
* Figure out which 'is_shared' to use, if we have a
* disk then each partition can have it's own flags
* for mmap.
*/
if (mdp->is_disk)
is_shared = mdp->fileblock[idx].is_shared;
#endif /* VDISK_SUPPORT } */
do {
fd = open(mdp->fileblock[idx].fnamep,
is_shared ? O_RDWR : O_RDONLY);
} while (fd < 0 && EAGAIN == errno);
if (fd < 0)
fatal("Failed opening file %s",
mdp->fileblock[idx].fnamep);
/*
* Check that the fileoffset (offset in file to start
* loading) is page alligned
*/
if ((mdp->fileblock[idx].fileoffset % pgsize) != 0)
fatal("Offset 0x%llx in file %s is not aligned "
"with 0x%llx pagesize",
mdp->fileblock[idx].fileoffset,
mdp->fileblock[idx].fnamep,
pgsize);
#if 0 /* { */
/* Allow non-rounded out contents */
if ((mdp->fileblock[idx].fileextent % pgsize) != 0)
fatal("Loaded extent 0x%llx for file %s is not "
"aligned with 0x%llx pagesize",
mdp->fileblock[idx].fileextent,
mdp->fileblock[idx].fnamep,
pgsize);
#endif /* } */
/*
* Check that the memoffset (offset in memory to start
* loading) is page alligned
*/
if ((mdp->fileblock[idx].memoffset % pgsize) != 0)
fatal("Offset 0x%llx into RAM for file %s is not "
"aligned with 0x%llx pagesize",
mdp->fileblock[idx].memoffset,
mdp->fileblock[idx].fnamep,
pgsize);
ASSERT(NULL != datap || (NULL == datap &&
0LL == mdp->fileblock[idx].memoffset));
/* CSTYLED */
DBG( PRINTF(("mapping %s (%s): 0x%llx -> 0x%llx (0x%llx)\n",
mdp->fileblock[idx].fnamep,
(is_shared ? "SHARED" : "PRIVATE"),
config_devp->addrp->baseaddr +
mdp->fileblock[idx].memoffset,
config_devp->addrp->baseaddr +
mdp->fileblock[idx].memoffset +
mdp->fileblock[idx].fileextent,
/* CSTYLED */
mdp->fileblock[idx].fileextent)); );
/*
* mmap the file into memory with the appropriate flags.
*/
mapp = (void*)mmap((void*)(datap +
mdp->fileblock[idx].memoffset),
mdp->fileblock[idx].fileextent,
PROT_READ | PROT_WRITE,
/* CSTYLED */
(is_shared ? MAP_SHARED : MAP_PRIVATE) | MAP_FIXED | MAP_NORESERVE,
fd, mdp->fileblock[idx].fileoffset);
if (((uint8_t *)MAP_FAILED) == mapp)
fatal("Failed mapping file %s",
mdp->fileblock[idx].fnamep);
ASSERT((datap + mdp->fileblock[idx].memoffset) == mapp);
}
#if VDISK_SUPPORT /* { */
/*
* Now we have mmaped in all files, we need to create a valid
* VTOC for the root disk (ie, the first file loaded) based on
* the size of the other disks. The VTOC also needs to have
* correct values for s2 (the entire disk) so that someone can
* open the label on s0 and figure out the size of the entire
* disk and the layout of each partition.
*/
if (mdp->is_disk)
create_label(mdp);
#endif /* VDISK_SUPPORT } */
/*
* Finally apply content
*/
for (cp = mdp->contentp; cp != NULL; cp = next_cp) {
next_cp = cp->nextp;
memmove(mdp->datap+cp->offset, cp->datap, cp->size);
Xfree(cp->datap);
Xfree(cp);
}
/* CSTYLED */
SANITY( mdp->contentp = NULL; );
}
/*
* Memory configuration dump
*/
void
mem_dump(config_dev_t *config_devp)
{
}
/*
* Returns the extent of the linear cacheable block
* starting at offset, and a pointer to the state block
*
* Note: if the device is a ROM, attempts to write
* i.e. DA_Store as type must be failed -
* the function is supposed to return 0.
*/
tpaddr_t
mem_cacheable(config_addr_t *config_addrp, dev_access_t type, tpaddr_t offset,
uint8_t **blockp)
{
mem_dev_t *mdp;
mdp = config_addrp->config_devp->devp;
/* fail store attempts to a ROM */
if (mdp->is_rom && (type & DA_Store))
return ((tpaddr_t)0);
#if VDISK_SUPPORT /* { */
/* cannot execute from a disk */
if (mdp->is_disk && (type & DA_Instn))
return ((tpaddr_t)0);
#endif /* VDISK_SUPPORT } */
if ((offset < 0) || (offset >= config_addrp->range)) {
/* CSTYLED */
SANITY(*blockp = NULL;);
return (NULL);
}
*blockp = mdp->datap + offset;
return (config_addrp->range - offset);
}
/*
* Should only get invoked if this is a ROM.
* Indicate the store failed - return false
* ROMs are read only.
*/
bool_t
mem_cpu_access(simcpu_t *sp, config_addr_t *cap, tpaddr_t offset, maccess_t op,
uint64_t *regp)
{
mem_dev_t *mdp;
mdp = cap->config_devp->devp;
ASSERT(mdp->is_rom);
EXEC_WARNING(("rom_cpu_store: attempted store to ROM @ pc 0x%llx "
"of %d bytes at offset 0x%llx",
sp->pc, 1<<(op & MA_Size_Mask), offset));
return (false);
}
#if VDISK_SUPPORT /* { */
void
dump_label(mem_dev_t *mdp)
{
struct dk_label *dk_label;
int idx = 0;
uint64_t nsect, nhead, nblocks, cyl_size;
uint64_t ncyl, start_cyl, end_cyl, size;
int i, tag, flag;
char tag_str[15], flag_str[15];
/*
* read disk label
*/
dk_label = (struct dk_label *)mdp->datap;
/*
* If no magic, give up early
*/
if (dk_label->dkl_magic != DKL_MAGIC) {
PRINTF(("No valid disk label - giving up\n"));
return;
}
nsect = dk_label->dkl_nsect;
nhead = dk_label->dkl_nhead;
cyl_size = nsect * nhead;
if (dk_label->dkl_magic != DKL_MAGIC) {
PRINTF(("Bad disk label\n"));
cyl_size = 1;
}
/* CSTYLED */
DBG( PRINTF(("[%4s] [%10s] [%4s] [%10s] - [%10s] [%15s] [%10s] [%10s]\n",
"part", "Tag", "Flag", "Start-Cyl", "End-Cyl", "Size", "#Blocks",
/* CSTYLED */
"#Cylinders")); );
for (i = 0; i < NDKMAP; i++) {
uint64_t nblocks;
nblocks = (uint64_t)dk_label->dkl_map[i].dkl_nblk;
size = nblocks * 512;
/* Check to avoid a fault if label is malformed */
if (cyl_size != 0) {
ncyl = nblocks / cyl_size;
} else {
ncyl = nblocks; /* fake */
}
start_cyl = dk_label->dkl_map[i].dkl_cylno;
tag = dk_label->dkl_vtoc.v_part[i].p_tag;
flag = dk_label->dkl_vtoc.v_part[i].p_flag;
if (dk_label->dkl_map[i].dkl_nblk == 0)
end_cyl = 0;
else
end_cyl = (dk_label->dkl_map[i].dkl_cylno + ncyl) -1;
switch (tag) {
case 0x00:
strcpy(tag_str, "unassigned"); break;
case 0x01:
strcpy(tag_str, "boot"); break;
case 0x02:
strcpy(tag_str, "root"); break;
case 0x03:
strcpy(tag_str, "swap"); break;
case 0x04:
strcpy(tag_str, "usr"); break;
case 0x05:
strcpy(tag_str, "backup"); break;
default:
strcpy(tag_str, "UNKNOWN"); break;
}
switch (flag) {
case 0x0:
strcpy(flag_str, "wm"); break;
case V_UNMNT:
strcpy(flag_str, "wu"); break;
case V_RONLY:
strcpy(flag_str, "rm"); break;
default:
strcpy(flag_str, "unknown"); break;
}
/* CSTYLED */
DBG( PRINTF(("[%4d] [%10s] [ %2s ] [%10llu] - [%10llu] [%15llu] [%10llu] [%10llu] %lluMB\n",
i, tag_str, flag_str, start_cyl, end_cyl, size, nblocks,
/* CSTYLED */
ncyl, size/1024/1024)); );
}
/* CSTYLED */
DBG( PRINTF(("nparts = [0x%x]\n\n", dk_label->dkl_vtoc.v_nparts)); );
}
void
create_label(mem_dev_t *mdp)
{
struct dk_label *dk_label;
int idx = 0;
uint64_t nsect, nhead, nblocks, nblocksa, cyl_size, sblock;
uint64_t ncyl, start_cyl, end_cyl, size;
int i, tag, flag;
char tag_str[MAXNAMELEN], flag_str[MAXNAMELEN];
uint64_t total_nblocks = 0;
/*
* Dump the existing label
*/
/* CSTYLED */
DBG( PRINTF(("\nReading label from disk\n")); );
dump_label(mdp);
dk_label = (struct dk_label *)mdp->datap;
/*
* wipe the current label
*/
for (idx = 0; idx < NDKMAP; idx++) {
dk_label->dkl_map[idx].dkl_nblk = 0x0;
dk_label->dkl_map[idx].dkl_cylno = 0x0;
dk_label->dkl_vtoc.v_part[idx].p_tag = 0x0;
dk_label->dkl_vtoc.v_part[idx].p_flag = V_UNMNT;
}
dk_label->dkl_magic = DKL_MAGIC;
/*
* read disk label from s0 and modify it
*
* fake sectors and head = 1 to avoid rounding errors
* when calculating cyl_no. Rounding errors make it
* almost impossible to make the cyl_no align with the
* memoffset we mmaped the file into. So we just make
* start cyl == start block
*/
dk_label->dkl_nsect = 1;
dk_label->dkl_nhead = 1;
nsect = dk_label->dkl_nsect;
nhead = dk_label->dkl_nhead;
cyl_size = nsect * nhead;
/*
* Update the label at slice 0 with this info
*/
/* CSTYLED */
DBG( PRINTF(("Creating new lable as follows:\n")); );
for (idx = 0; idx < mdp->nfileblocks; idx++) {
int slice;
static uint64_t next_cyl = 0;
slice = mdp->fileblock[idx].disk_slice;
/*
* Use the real value for fileextent to calculate the
* number of blocks for this partition.
*
* Our next partition needs to be loaded on a page boundry
* so we use the page aligned value for fileextent (fileextenta)
* to calculate the number of aligned blocks (nblocksa)
* Use this to count the number of blocks needed to pad
* the partition so the next slice can be loaded on a
* page boundry.
*/
nblocks = mdp->fileblock[idx].fileextent / 512;
nblocksa = mdp->fileblock[idx].fileextenta / 512;
total_nblocks += nblocksa;
/*
* calculate start cyl based on the memoffset
* we used to map this file.
* memoffest is already page aligned.
*/
sblock = mdp->fileblock[idx].memoffset / 512;
ncyl = (float)sblock / (float)cyl_size;
/* Set the number of blocks in this slice */
dk_label->dkl_map[slice].dkl_nblk = nblocks;
/* Set the starting cylinder */
dk_label->dkl_map[slice].dkl_cylno = ncyl;
/* CSTYLED */
DBG( PRINTF(("slice=[%d] name=[%s] size=[%llu][0x%llx] nblocks=[%llu] nblocksa=[%llu] ncyl=[%llu] nsect=[%llu] nhead=[%llu]\n",
mdp->fileblock[idx].disk_slice,
mdp->fileblock[idx].fnamep,
mdp->fileblock[idx].fileextent,
mdp->fileblock[idx].fileextent,
/* CSTYLED */
nblocks, nblocksa, ncyl, nsect, nhead)); );
/* Set the p_tag, p_flag for each slice */
if (slice == 1) { /* SWAP */
dk_label->dkl_vtoc.v_part[slice].p_tag = 0x3;
dk_label->dkl_vtoc.v_part[slice].p_flag = V_UNMNT;
} else { /* all else set to ROOT */
dk_label->dkl_vtoc.v_part[slice].p_tag = 0x2;
dk_label->dkl_vtoc.v_part[slice].p_flag = 0x0;
}
}
/*
* create a valid VTOC entry for slice 2 which reflects the total
* size of the virtual disk (with all partitions).
*/
dk_label->dkl_vtoc.v_part[2].p_tag = 0x5; /* backup */
dk_label->dkl_vtoc.v_part[2].p_flag = V_UNMNT; /* wu */
dk_label->dkl_map[2].dkl_cylno = 0x0; /* always start at 0 */
dk_label->dkl_map[2].dkl_nblk = total_nblocks; /* entire disk */
dk_label->dkl_magic = DKL_MAGIC;
/* CSTYLED */
DBG( PRINTF(("New Checksum = [0x%hx]\n", get_checksum(dk_label, CK_MAKESUM))); );
/*
* Update checksum on disk
*/
dk_label->dkl_cksum = get_checksum(dk_label, CK_MAKESUM);
/* CSTYLED */
DBG( PRINTF(("Dumping new label to be written to disk\n")); );
dump_label(mdp);
}
/*
* Construct checksum for the new disk label
*/
short
get_checksum(struct dk_label *dk_label, int mode)
{
short sum, *sp;
int i;
sum = 0;
sp = (short *)dk_label;
i = sizeof (*dk_label) / sizeof (*sp);
/*
* If we are generating a checksum, don't include the checksum
* in the rolling xor.
*/
if (mode == CK_MAKESUM)
i -= 1;
/*
* Take the xor of all the half-words in the label.
*/
while (i--) {
sum ^= *sp++;
}
return (sum);
}
#endif /* VDISK_SUPPORT } */