/*
* Copyright (c) 2000 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by James Chacon.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
static u_int16_t p1212_calc_crc(u_int32_t, u_int32_t *, int, int);
static int p1212_parse_directory(struct p1212_dir *, u_int32_t *, u_int32_t);
static struct p1212_leafdata *p1212_parse_leaf(u_int32_t *);
static int p1212_parse_textdir(struct p1212_com *, u_int32_t *);
static struct p1212_textdata *p1212_parse_text_desc(u_int32_t *);
static void p1212_print_node(struct p1212_key *, void *);
static int p1212_validate_offset(u_int16_t, u_int32_t);
static int p1212_validate_immed(u_int16_t, u_int32_t);
static int p1212_validate_leaf(u_int16_t, u_int32_t);
static int p1212_validate_dir(u_int16_t, u_int32_t);
#ifdef P1212_DEBUG
#define DPRINTF(x) if (p1212debug) printf x
#define DPRINTFN(n,x) if (p1212debug>(n)) printf x
int p1212debug = 1;
#else
#define DPRINTF(x)
#define DPRINTFN(n,x)
#endif
/*
* Routines to parse the ROM into a tree that's usable. Also verify integrity
* vs. the P1212 standard
*/
/*
* A buffer of u_int32_t's and a size in quads gets passed in. The output will
* return -1 on error, or 0 on success and possibly reset *size to a larger
* value.
*
* NOTE: Rom's are guaranteed per the ISO spec to be contiguous but only the
* first 1k is directly mapped. Anything past 1k is supposed to use a loop
* around the indirect registers to read in the rom. This code only assumes the
* buffer passed in represents a total rom regardless of end size. It is the
* callers responsibility to treat a size > 1024 as a special case.
*/
int
p1212_iscomplete(u_int32_t *t, u_int32_t *size)
{
u_int16_t infolen, crclen, len;
u_int32_t newlen, offset, test;
int complete, i, numdirs, type, val, *dirs;
dirs = NULL;
if (*size == 0) {
DPRINTF(("Invalid size for ROM: %d\n", (unsigned int)*size));
return -1;
}
infolen = P1212_ROMFMT_GET_INFOLEN((ntohl(t[0])));
if (infolen <= 1) {
DPRINTF(("ROM not initialized or minimal ROM: Info "
"length: %d\n", infolen));
return -1;
}
crclen = P1212_ROMFMT_GET_CRCLEN((ntohl(t[0])));
if (crclen < infolen) {
DPRINTF(("CRC len less than info len. CRC len: %d, "
"Info len: %d\n", crclen, infolen));
return -1;
}
/*
* Now loop through it to check if all the offsets referenced are
* within the image stored so far. If not, get those as well.
*/
/*
* Make sure at least the bus info block is in memory + the root dir
* header quad. Add 1 here since offset is an array offset and size is
* the total array size we want. If this is getting the root dir
* then add another since infolen doesn't end on the root dir entry but
* right before it.
*/
if (newlen == 0) {
DPRINTF(("Impossible directory length of 0!\n"));
return -1;
}
/*
* Starting with the first byte of the directory, read through
* and check the values found. On offsets and directories read
* them in if appropriate (always for offsets, if not in memory
* for leaf/directories).
*/
offset++;
len = newlen;
newlen = 0;
for (i = 0; i < len; i++) {
type = P1212_DIRENT_GET_KEYTYPE((ntohl(t[offset+i])));
val = P1212_DIRENT_GET_VALUE((ntohl(t[offset+i])));
switch (type) {
case P1212_KEYTYPE_Immediate:
case P1212_KEYTYPE_Offset:
break;
case P1212_KEYTYPE_Leaf:
/*
* If a leaf is found, and it's beyond the
* current rom length and it's beyond the
* current newlen setting,
* then set newlen accordingly.
*/
test = offset + i + val + 1;
if ((test > *size) && (test > newlen)) {
newlen = test;
break;
}
/*
* For leaf nodes just make sure the whole leaf
* length is in the buffer. There's no data
* inside of them that can refer to outside
* nodes. (Uless it's vendor specific and then
* you're on your own anyways).
*/
test = offset + i + val + 1;
if ((test > *size) && (test > newlen)) {
newlen = test;
break;
}
/*
* Can't just walk the ROM looking at type
* codes since these are only valid on
* directory entries. So save any directories
* we find into a queue and the bottom of the
* while loop will pop the last one off and
* walk that directory.
*/
if (p1212_iscomplete(t, &check) == -1) {
DPRINTF(("ROM is not complete\n"));
return NULL;
}
if (check != size) {
DPRINTF(("ROM is not complete (check != size)\n"));
return NULL;
}
/* Calculate both a good and known bad crc. */
/* CRC's are calculated from everything except the first quad. */
rom = malloc(sizeof(struct p1212_rom), M_DEVBUF, M_WAITOK);
rom->len = P1212_ROMFMT_GET_INFOLEN((ntohl(t[0])));
next = rom->len + 1;
if ((rom->len < 1) || (rom->len > size)) {
DPRINTF(("Invalid ROM info length: %d\n", rom->len));
free(rom, M_DEVBUF);
return NULL;
}
/* Exclude the quad which covers the bus name. */
rom->len--;
if (rom->len) {
rom->data = malloc(sizeof(u_int32_t) * rom->len, M_DEVBUF,
M_WAITOK);
/* Add 2 to account for info/crc and bus name skipped. */
for (i = 0; i < rom->len; i++)
rom->data[i] = t[i + 2];
}
/* The name field is always 4 bytes and always the 2nd field. */
strncpy(rom->name, (char *)&t[1], 4);
rom->name[4] = 0;
/*
* Fill out the root directory. All these values are hardcoded so the
* parse/print/match routines have a standard layout to work against.
*/
if ((dir->parent == NULL) && dir->com.key.val) {
DPRINTF(("Invalid root dir. key.val is 0x%0x and not"
" 0x0\n", dir->com.key.val));
return 1;
}
for (i = offset; i < (offset + crclen); i++) {
desc = ntohl(t[i]);
type = P1212_DIRENT_GET_KEYTYPE(desc);
val = P1212_DIRENT_GET_KEYVALUE(desc);
/*
* Sanity check for valid types/locations/etc.
*
* See pages 79-100 of
* ISO/IEC 13213:1194(ANSI/IEEE Std 1212, 1994 edition)
* for specifics.
*
* XXX: These all really should be broken out into
* subroutines as it's grown large and complicated
* in certain cases.
*/
switch (val) {
case P1212_KEYVALUE_Unit_Spec_Id:
case P1212_KEYVALUE_Unit_Sw_Version:
case P1212_KEYVALUE_Unit_Dependent_Info:
case P1212_KEYVALUE_Unit_Location:
case P1212_KEYVALUE_Unit_Poll_Mask:
if (dir->parent == NULL) {
DPRINTF(("Invalid ROM: %s is not "
"valid in the root directory.\n",
p1212_keyvalue_strings[val]));
return 1;
}
break;
default:
if (dir->com.key.val ==
P1212_KEYVALUE_Unit_Directory) {
DPRINTF(("Invalid ROM: %s is "
"not valid in a unit directory.\n",
p1212_keyvalue_strings[val]));
return 1;
}
break;
}
switch (type) {
case P1212_KEYTYPE_Immediate:
if (p1212_validate_immed(val, mask)) {
DPRINTF(("Invalid ROM: Can't have an "
"immediate type with %s value. Key"
" used at location 0x%0x in ROM\n",
p1212_keyvalue_strings[val],
(unsigned int)(&t[i]-&addr[0])));
return 1;
}
break;
case P1212_KEYTYPE_Offset:
if (p1212_validate_offset(val, mask)) {
DPRINTF(("Invalid ROM: Can't have "
"an offset type with key %s."
" Used at location 0x%0x in ROM\n",
p1212_keyvalue_strings[val],
(unsigned int)(&t[i]-&addr[0])));
return 1;
}
break;
case P1212_KEYTYPE_Leaf:
if (p1212_validate_leaf(val, mask)) {
DPRINTF(("Invalid ROM: Can't have a "
"leaf type with %s value. Key "
"used at location 0x%0x in ROM\n",
p1212_keyvalue_strings[val],
(unsigned int)(&t[i]-&addr[0])));
return 1;
}
break;
case P1212_KEYTYPE_Directory:
if (p1212_validate_dir(val, mask)) {
DPRINTF(("Invalid ROM: Can't have a "
"directory type with %s value. Key"
" used at location 0x%0x in ROM\n",
p1212_keyvalue_strings[val],
(unsigned int)(&t[i]-&addr[0])));
return 1;
}
break;
default:
panic("Impossible type code: 0x%04hx",
(unsigned short)type);
break;
}
/* Note flags for required fields. */
if (val == P1212_KEYVALUE_Module_Vendor_Id) {
module_vendor_flag = 1;
}
if (val == P1212_KEYVALUE_Node_Capabilities) {
node_capabilities_flag = 1;
}
if (val == P1212_KEYVALUE_Unit_Sw_Version)
unit_sw_flag = 1;
if (val == P1212_KEYVALUE_Unit_Location)
unit_location_flag = 1;
/*
* This is just easier to spell out. You can't have
* a module sw version if you include a node sw version
* and vice-versa. Both aren't allowed if you have unit
* dirs.
*/
if (val == P1212_KEYVALUE_Module_Sw_Version) {
if (node_sw_flag) {
DPRINTF(("Can't have a module software"
" version along with a node "
"software version entry\n"));
return 1;
}
if (unitdir_cnt) {
DPRINTF(("Can't have unit directories "
"with module software version "
"defined.\n"));
return 1;
}
module_sw_flag = 1;
}
if (val == P1212_KEYVALUE_Node_Sw_Version) {
if (module_sw_flag) {
DPRINTF(("Can't have a node software "
"version along with a module "
"software version entry\n"));
return 1;
}
if (unitdir_cnt) {
DPRINTF(("Can't have unit directories "
"with node software version "
"defined.\n"));
return 1;
}
node_sw_flag = 1;
}
if (val == P1212_KEYVALUE_Unit_Directory) {
if (module_sw_flag || node_sw_flag) {
DPRINTF(("Can't have unit directories "
"with either module or node "
"software version defined.\n"));
return 1;
}
unitdir_cnt++;
}
/*
* Text descriptors are special. They describe the
* last entry they follow. So they need to be included
* with its struct and there's nothing in the spec
* preventing one from putting text descriptors after
* directory descriptors. Also they can be a single
* value or a list of them in a directory format so
* account for either. Finally if they're in a
* directory those can be the only types in a
* directory.
*/
if (com == NULL) {
DPRINTF(("Can't have a text descriptor"
" as the first entry in a "
"directory\n"));
return 1;
}
if (com->textcnt != 0) {
DPRINTF(("Text descriptors can't "
"follow each other in a "
"directory\n"));
return 1;
}
if (type == P1212_KEYTYPE_Leaf) {
com->text =
malloc(size, M_DEVBUF, M_WAITOK);
com->text[0] =
p1212_parse_text_desc(&t[leafoff]);
if (com->text[0] == NULL) {
DPRINTF(("Got an error parsing"
" text descriptor at "
"offset 0x%0x\n",
&t[leafoff]-&addr[0]));
free(com->text, M_DEVBUF);
return 1;
}
com->textcnt = 1;
} else {
i = p1212_parse_textdir(com,
&t[leafoff]);
if (i)
return 1;
}
}
if ((type != P1212_KEYTYPE_Directory) &&
(val != P1212_KEYVALUE_Textual_Descriptor)) {
data = malloc(sizeof(struct p1212_data),
M_DEVBUF, M_WAITOK|M_ZERO);
data->com.key.key_type = type;
data->com.key.key_value = val;
data->com.key.key =
P1212_DIRENT_GET_KEY((ntohl(t[i])));
data->com.key.val =
P1212_DIRENT_GET_VALUE((ntohl(t[i])));
com = &data->com;
/*
* Don't try and read the offset. It may be
* a register or something special. Generally
* these are node specific so let the upper
* level code figure it out.
*/
if (dir->parent == NULL) {
if (module_vendor_flag == 0) {
DPRINTF(("Missing module vendor entry in root "
"directory.\n"));
return 1;
}
if (node_capabilities_flag == 0) {
DPRINTF(("Missing node capabilities entry in "
"root directory.\n"));
return 1;
}
} else {
if ((unitdir_cnt > 1) && (unit_location_flag == 0)) {
DPRINTF(("Must have a unit location in each "
"unit directory when more than one unit "
"directory exists.\n"));
return 1;
}
}
/*
* Ok, done with this directory and it's sanity checked. Now
* loop through and either find an unparsed subdir or one
* farther back up the chain.
*/
if (!TAILQ_EMPTY(&dir->subdir_root)) {
sdir = TAILQ_FIRST(&dir->subdir_root);
} else {
do {
sdir = TAILQ_NEXT(dir, dir);
if (sdir == NULL) {
dir = dir->parent;
}
} while ((sdir == NULL) && (dir != NULL));
}
if (dir) {
dir = sdir;
if (!dir->match) {
DPRINTF(("Invalid subdir..Has no offset\n"));
return 1;
}
offset = dir->match;
}
}
return 0;
}
/*
* Part of this is copied from p1212_walk to do depth first traversal
* without using recursion. Using the walk API would have made things
* more complicated in trying to build up the return struct otherwise.
*/
if (func == NULL) {
#ifdef DIAGNOSTIC
printf("p1212_walk: Passed in NULL function\n");
#endif
return;
}
if (root == NULL) {
#ifdef DIAGNOSTIC
printf("p1212_walk: Called with NULL root\n");
#endif
return;
}
/* Allow walking from any point. Just mark the starting point. */
parent = root->parent;
root->parent = NULL;
/*
* Depth first traversal that doesn't use recursion.
*
* Call the function first for the directory node and then loop through
* all the data nodes and call the function for them.
*
* Finally, figure out the next possible directory node if one is
* available or bail out.
*/
while (dir) {
func((struct p1212_key *) dir, arg);
TAILQ_FOREACH(data, &dir->data_root, data)
func((struct p1212_key *) data, arg);
if (!TAILQ_EMPTY(&dir->subdir_root)) {
sdir = TAILQ_FIRST(&dir->subdir_root);
} else {
do {
sdir = TAILQ_NEXT(dir, dir);
if (sdir == NULL) {
dir = dir->parent;
}
} while ((sdir == NULL) && dir);
}
dir = sdir;
}
root->parent = parent;
}
void
p1212_print(struct p1212_dir *dir)
{
int indent;
/* Avoid recursing. Find the bottom most node and work back. */
while (dir) {
if (!TAILQ_EMPTY(&dir->subdir_root)) {
sdir = TAILQ_FIRST(&dir->subdir_root);
if (TAILQ_EMPTY(&sdir->subdir_root)) {
TAILQ_REMOVE(&dir->subdir_root, sdir, dir);
dir = sdir;
}
else {
dir = sdir;
continue;
}
} else {
if (dir->parent)
TAILQ_REMOVE(&dir->parent->subdir_root, dir,
dir);
}
while ((data = TAILQ_FIRST(&dir->data_root))) {
if (data->leafdata) {
if (data->leafdata->data)
free(data->leafdata->data, M_DEVBUF);
free(data->leafdata, M_DEVBUF);
}
TAILQ_REMOVE(&dir->data_root, data, data);
if (data->com.textcnt) {
for (i = 0; i < data->com.textcnt; i++)
free(data->com.text[i], M_DEVBUF);
free(data->com.text, M_DEVBUF);
}
free(data, M_DEVBUF);
}
sdir = dir;
if (dir->parent)
dir = dir->parent;
else
dir = NULL;
if (sdir->com.textcnt) {
for (i = 0; i < sdir->com.textcnt; i++)
free(sdir->com.text[i], M_DEVBUF);
free(sdir->com.text, M_DEVBUF);
}
free(sdir, M_DEVBUF);
}
if (rom->len)
free(rom->data, M_DEVBUF);
free(rom, M_DEVBUF);
}
/*
* A fairly well published reference implementation of the CRC routine had
* a typo in it and some devices may be using it rather than the correct one
* in calculating their ROM CRC's. To compensate an interface for generating
* either is provided.
*
* len is the number of u_int32_t entries, not bytes.
*/
static u_int16_t
p1212_calc_crc(u_int32_t crc, u_int32_t *data, int len, int broke)
{
int shift;
u_int32_t sum;
int i;
for (i = 0; i < len; i++) {
for (shift = 28; shift > 0; shift -= 4) {
sum = ((crc >> 12) ^ (ntohl(data[i]) >> shift)) &
0x0000000f;
crc = (crc << 4) ^ (sum << 12) ^ (sum << 5) ^ sum;
}
/* The broken implementation doesn't do the last shift. */
if (!broke) {
sum = ((crc >> 12) ^ ntohl(data[i])) & 0x0000000f;
crc = (crc << 4) ^ (sum << 12) ^ (sum << 5) ^ sum;
}
}
return (u_int16_t)crc;
}
/*
* This is almost identical to the standard autoconf *match idea except it
* can match and attach multiple children in one pass.
*/
if (udirs) {
do {
dev = config_found(sc, udirs, print,
CFARGS(.iattr = "fwnode"));
if (dev && numdev) {
devret = realloc(devret,
sizeof(device_t) *
(numdev + 2), M_DEVBUF, M_WAITOK);
devret[numdev++] = dev;
devret[numdev] = NULL;
} else if (dev) {
devret[0] = dev;
numdev++;
}
udirs++;
} while (*udirs);
}
if (numdev == 0) {
free(devret, M_DEVBUF);
return NULL;
}
return devret;
}
/*
* Make these their own functions as they have slightly complicated rules.
*
* For example:
*
* Under normal circumstances only the 2 extent types can be offset
* types. However some spec's which use p1212 like SBP2 for
* firewire/1394 will define a dependent info type as an offset value.
* Allow the upper level code to flag this and pass it down during
* parsing. The same thing applies to immediate types.
*/
static int
p1212_validate_immed(u_int16_t val, u_int32_t mask)
{
switch (val) {
case P1212_KEYVALUE_Textual_Descriptor:
case P1212_KEYVALUE_Bus_Dependent_Info:
case P1212_KEYVALUE_Module_Dependent_Info:
case P1212_KEYVALUE_Node_Unique_Id:
case P1212_KEYVALUE_Node_Dependent_Info:
case P1212_KEYVALUE_Unit_Directory:
case P1212_KEYVALUE_Unit_Dependent_Info:
case P1212_KEYVALUE_Unit_Location:
if ((mask & P1212_ALLOW_DEPENDENT_INFO_IMMED_TYPE) &&
((val == P1212_KEYVALUE_Module_Dependent_Info) ||
(val == P1212_KEYVALUE_Node_Dependent_Info) ||
(val == P1212_KEYVALUE_Unit_Dependent_Info)))
break;
return 1;
break;
default:
break;
}
return 0;
}
static int
p1212_validate_leaf(u_int16_t val, u_int32_t mask)
{
switch(val) {
case P1212_KEYVALUE_Textual_Descriptor:
case P1212_KEYVALUE_Bus_Dependent_Info:
case P1212_KEYVALUE_Module_Dependent_Info:
case P1212_KEYVALUE_Node_Unique_Id:
case P1212_KEYVALUE_Node_Dependent_Info:
case P1212_KEYVALUE_Unit_Dependent_Info:
case P1212_KEYVALUE_Unit_Location:
break;
default:
return 1;
break;
}
return 0;
}
static int
p1212_validate_dir(u_int16_t val, u_int32_t mask)
{
switch(val) {
case P1212_KEYVALUE_Textual_Descriptor:
case P1212_KEYVALUE_Bus_Dependent_Info:
case P1212_KEYVALUE_Module_Dependent_Info:
case P1212_KEYVALUE_Node_Dependent_Info:
case P1212_KEYVALUE_Unit_Directory:
case P1212_KEYVALUE_Unit_Dependent_Info:
break;
default:
if ((mask & P1212_ALLOW_VENDOR_DIRECTORY_TYPE) &&
(val == P1212_KEYVALUE_Module_Vendor_Id))
break;
return 1;
break;
}
return 0;
}