2009-04-08 09:47:01 +02:00
|
|
|
|
|
|
|
Received: from www.crouse-house.com ([199.45.160.146]
|
|
|
|
for coreboot@coreboot.org; Fri, 19 Dec 2008 23:11:59 +0100
|
|
|
|
From: Jordan Crouse <jordan@cosmicpenguin.net>
|
|
|
|
|
|
|
|
|
2010-04-27 08:56:47 +02:00
|
|
|
Greetings. I apologize for the incompleteness of what I am about to
|
|
|
|
discuss. I was planning on working on it leisurely, but my employment
|
|
|
|
circumstances changed and I've been trying to get it completed in a
|
2009-04-08 09:47:01 +02:00
|
|
|
hurry before I had to leave it behind.
|
|
|
|
|
2010-04-27 08:56:47 +02:00
|
|
|
I've been thinking a lot about LAR lately, and ways to make it more
|
|
|
|
extensible and robust. Marc and I have been trading ideas back and
|
|
|
|
forth for a number of months, and over time a clear idea of what I
|
2009-04-08 09:47:01 +02:00
|
|
|
wanted to do started to take shape.
|
|
|
|
|
2010-04-27 08:56:47 +02:00
|
|
|
My goal was to add small things to LAR while retaining the overall
|
|
|
|
scheme. Over time, the scheme evolved slightly, but I think you'll find
|
|
|
|
that it remains true to the original idea. Below is the beginnings of
|
|
|
|
an architecture document - I did it in text form, but if met with
|
|
|
|
aclaim, it should be wikified. This presents what I call CBFS - the
|
2017-06-04 04:16:01 +02:00
|
|
|
next generation LAR for next generation coreboot. Its easier to
|
2009-04-08 09:47:01 +02:00
|
|
|
describe what it is by describing what changed:
|
|
|
|
|
2010-04-27 08:56:47 +02:00
|
|
|
A header has been added somewhere in the bootblock similar to Carl
|
|
|
|
Daniel's scheme. In addition to the coreboot information, the header
|
|
|
|
reports the size of the ROM, the alignment of the blocks, and the offset
|
|
|
|
of the first component in the CBFS. The master header provides all
|
2009-04-08 09:47:01 +02:00
|
|
|
the information LAR needs plus the magic number information flashrom needs.
|
|
|
|
|
2010-04-27 08:56:47 +02:00
|
|
|
Each "file" (or component, as I style them) now has a type associated
|
|
|
|
with it. The type is used by coreboot to identify the type of file that
|
|
|
|
it is loading, and it can also be used by payloads to group items in the
|
2009-04-14 02:01:34 +02:00
|
|
|
CBFS by type (i.e - bayou can ask for all components that are payloads).
|
2009-04-08 09:47:01 +02:00
|
|
|
|
2010-04-27 08:56:47 +02:00
|
|
|
The header on each "file" (or component, as I like to style them) has
|
|
|
|
been simplified - We now only store the length, the type, the checksum,
|
|
|
|
and the offset to the data. The name scheme remains the same. The
|
2018-04-09 13:05:29 +02:00
|
|
|
additional information, which is component specific, has been moved to
|
2009-04-08 09:47:01 +02:00
|
|
|
the component itself (see below).
|
|
|
|
|
2010-04-27 08:56:47 +02:00
|
|
|
The components are arranged in the ROM aligned along the specified
|
2009-04-08 09:47:01 +02:00
|
|
|
alignment from the master header - this is to facilitate partial re-write.
|
|
|
|
|
|
|
|
Other then that, the LAR ideas remain pretty much the same.
|
|
|
|
|
2010-04-27 08:56:47 +02:00
|
|
|
The plan for moving the metadata to the components is to allow many
|
|
|
|
different kinds of components, not all of which are groked by coreboot.
|
|
|
|
However, there are three essential component types that are groked by
|
2009-04-08 09:47:01 +02:00
|
|
|
coreboot, and they are defined:
|
|
|
|
|
2010-04-27 08:56:47 +02:00
|
|
|
stage - the stage is being parsed from the original ELF, and stored in
|
|
|
|
the ROM as a single blob of binary data. The load address, start
|
2009-04-08 09:47:01 +02:00
|
|
|
address, compression type and length are stored in the component sub-header.
|
|
|
|
|
2010-04-27 08:56:47 +02:00
|
|
|
payload - this is essentially SELF in different clothing - same idea as
|
2009-04-08 09:47:01 +02:00
|
|
|
SELF, with the sub-header as above.
|
|
|
|
|
2010-04-27 08:56:47 +02:00
|
|
|
optionrom - This is in flux - right now, the optionrom is stored
|
2009-04-08 09:47:01 +02:00
|
|
|
unadulterated and uncompressed, but that is likely to be changed.
|
|
|
|
|
2010-04-27 08:56:47 +02:00
|
|
|
Following this email are two replies containing the v3 code and a new
|
|
|
|
ROM tool to implement this respectively. I told you that I was trying
|
|
|
|
to get this out before I disappear, and I'm not kidding - the code is
|
|
|
|
compile tested and not run-tested. I hope that somebody will embrace
|
|
|
|
this code and take it the rest of the way, otherwise it will die a
|
2009-04-08 09:47:01 +02:00
|
|
|
pretty short death.
|
|
|
|
|
2010-04-27 08:56:47 +02:00
|
|
|
I realize that this will start an awesome flamewar, and I'm looking
|
|
|
|
forward to it. Thanks for listening to me over the years - and good
|
|
|
|
luck with coreboot. When you all make a million dollars, send me a few
|
2009-04-08 09:47:01 +02:00
|
|
|
bucks, will you?
|
|
|
|
|
|
|
|
Jordan
|
|
|
|
|
2017-06-04 04:16:01 +02:00
|
|
|
coreboot CBFS Specification
|
2009-04-08 09:47:01 +02:00
|
|
|
Jordan Crouse <jordan@cosmicpenguin.net>
|
|
|
|
|
|
|
|
= Introduction =
|
|
|
|
|
2009-04-14 02:01:34 +02:00
|
|
|
This document describes the coreboot CBFS specification (from here
|
|
|
|
referred to as CBFS). CBFS is a scheme for managing independent chunks
|
2009-04-08 09:47:01 +02:00
|
|
|
of data in a system ROM. Though not a true filesystem, the style and
|
|
|
|
concepts are similar.
|
|
|
|
|
|
|
|
|
|
|
|
= Architecture =
|
|
|
|
|
2009-04-14 02:01:34 +02:00
|
|
|
The CBFS architecture looks like the following:
|
2009-04-08 09:47:01 +02:00
|
|
|
|
|
|
|
/---------------\ <-- Start of ROM
|
|
|
|
| /-----------\ | --|
|
|
|
|
| | Header | | |
|
|
|
|
| |-----------| | |
|
|
|
|
| | Name | | |-- Component
|
|
|
|
| |-----------| | |
|
|
|
|
| |Data | | |
|
|
|
|
| |.. | | |
|
|
|
|
| \-----------/ | --|
|
|
|
|
| |
|
|
|
|
| /-----------\ |
|
|
|
|
| | Header | |
|
|
|
|
| |-----------| |
|
|
|
|
| | Name | |
|
|
|
|
| |-----------| |
|
|
|
|
| |Data | |
|
|
|
|
| |.. | |
|
|
|
|
| \-----------/ |
|
|
|
|
| |
|
|
|
|
| ... |
|
|
|
|
| /-----------\ |
|
|
|
|
| | | |
|
|
|
|
| | Bootblock | |
|
|
|
|
| | --------- | |
|
|
|
|
| | Reset | | <- 0xFFFFFFF0
|
|
|
|
| \-----------/ |
|
|
|
|
\---------------/
|
|
|
|
|
|
|
|
|
2009-04-14 02:01:34 +02:00
|
|
|
The CBFS architecture consists of a binary associated with a physical
|
2009-04-08 09:47:01 +02:00
|
|
|
ROM disk referred hereafter as the ROM. A number of independent of
|
|
|
|
components, each with a header prepended on to data are located within
|
|
|
|
the ROM. The components are nominally arranged sequentially, though they
|
|
|
|
are aligned along a pre-defined boundary.
|
|
|
|
|
|
|
|
The bootblock occupies the last 20k of the ROM. Within
|
|
|
|
the bootblock is a master header containing information about the ROM
|
|
|
|
including the size, alignment of the components, and the offset of the
|
2009-04-14 02:01:34 +02:00
|
|
|
start of the first CBFS component within the ROM.
|
2009-04-08 09:47:01 +02:00
|
|
|
|
|
|
|
= Master Header =
|
|
|
|
|
|
|
|
The master header contains essential information about the ROM that is
|
2009-04-14 02:01:34 +02:00
|
|
|
used by both the CBFS implementation within coreboot at runtime as well
|
2009-04-08 09:47:01 +02:00
|
|
|
as host based utilities to create and manage the ROM. The master header
|
|
|
|
will be located somewhere within the bootblock (last 20k of the ROM). A
|
|
|
|
pointer to the location of the header will be located at offset
|
2009-12-17 10:42:30 +01:00
|
|
|
-4 from the end of the ROM. This translates to address 0xFFFFFFFC on a
|
2009-04-08 09:47:01 +02:00
|
|
|
normal x86 system. The pointer will be to physical memory somewhere
|
|
|
|
between - 0xFFFFB000 and 0xFFFFFFF0. This makes it easier for coreboot
|
|
|
|
to locate the header at run time. Build time utilities will
|
|
|
|
need to read the pointer and do the appropriate math to locate the header.
|
|
|
|
|
|
|
|
The following is the structure of the master header:
|
|
|
|
|
2009-04-14 02:01:34 +02:00
|
|
|
struct cbfs_header {
|
2012-11-16 23:48:22 +01:00
|
|
|
u32 magic;
|
|
|
|
u32 version;
|
|
|
|
u32 romsize;
|
|
|
|
u32 bootblocksize;
|
|
|
|
u32 align;
|
|
|
|
u32 offset;
|
|
|
|
u32 architecture;
|
|
|
|
u32 pad[1];
|
2017-07-13 02:20:27 +02:00
|
|
|
} __packed;
|
2009-04-08 09:47:01 +02:00
|
|
|
|
|
|
|
The meaning of each member is as follows:
|
|
|
|
|
2010-04-27 08:56:47 +02:00
|
|
|
'magic' is a 32 bit number that identifies the ROM as a CBFS type. The
|
2009-04-08 09:47:01 +02:00
|
|
|
magic
|
|
|
|
number is 0x4F524243, which is 'ORBC' in ASCII.
|
|
|
|
|
2012-11-16 23:48:22 +01:00
|
|
|
'version' is a version number for CBFS header. cbfs_header structure may be
|
|
|
|
different if version is not matched.
|
|
|
|
|
2017-06-04 04:16:01 +02:00
|
|
|
'romsize' is the size of the ROM in bytes. coreboot will subtract 'size' from
|
2009-04-08 09:47:01 +02:00
|
|
|
0xFFFFFFFF to locate the beginning of the ROM in memory.
|
|
|
|
|
2012-11-16 23:48:22 +01:00
|
|
|
'bootblocksize' is the size of bootblock reserved in firmware image.
|
|
|
|
|
2009-04-08 09:47:01 +02:00
|
|
|
'align' is the number of bytes that each component is aligned to within the
|
2010-04-27 08:56:47 +02:00
|
|
|
ROM. This is used to make sure that each component is aligned correctly
|
2009-04-08 09:47:01 +02:00
|
|
|
with
|
|
|
|
regards to the erase block sizes on the ROM - allowing one to replace a
|
|
|
|
component at runtime without disturbing the others.
|
|
|
|
|
2009-04-14 02:01:34 +02:00
|
|
|
'offset' is the offset of the the first CBFS component (from the start of
|
2009-04-08 09:47:01 +02:00
|
|
|
the ROM). This is to allow for arbitrary space to be left at the beginning
|
|
|
|
of the ROM for things like embedded controller firmware.
|
|
|
|
|
2012-11-16 23:48:22 +01:00
|
|
|
'architecture' describes which architecture (x86, arm, ...) this CBFS is created
|
|
|
|
for.
|
|
|
|
|
2009-04-08 09:47:01 +02:00
|
|
|
= Bootblock =
|
2010-04-27 08:56:47 +02:00
|
|
|
The bootblock is a mandatory component in the ROM. It is located in the
|
2009-04-08 09:47:01 +02:00
|
|
|
last
|
|
|
|
20k of the ROM space, and contains, among other things, the location of the
|
|
|
|
master header and the entry point for the loader firmware. The bootblock
|
|
|
|
does not have a component header attached to it.
|
|
|
|
|
|
|
|
= Components =
|
|
|
|
|
2009-04-14 02:01:34 +02:00
|
|
|
CBFS components are placed in the ROM starting at 'offset' specified in
|
2010-04-27 08:56:47 +02:00
|
|
|
the master header and ending at the bootblock. Thus the total size
|
2009-04-08 09:47:01 +02:00
|
|
|
available
|
2009-04-14 02:01:34 +02:00
|
|
|
for components in the ROM is (ROM size - 20k - 'offset'). Each CBFS
|
2009-04-08 09:47:01 +02:00
|
|
|
component is to be aligned according to the 'align' value in the header.
|
2010-04-27 08:56:47 +02:00
|
|
|
Thus, if a component of size 1052 is located at offset 0 with an 'align'
|
2009-04-08 09:47:01 +02:00
|
|
|
value
|
|
|
|
of 1024, the next component will be located at offset 2048.
|
|
|
|
|
2009-04-14 02:01:34 +02:00
|
|
|
Each CBFS component will be indexed with a unique ASCII string name of
|
2009-04-08 09:47:01 +02:00
|
|
|
unlimited size.
|
|
|
|
|
2009-04-14 02:01:34 +02:00
|
|
|
Each CBFS component starts with a header:
|
2009-04-08 09:47:01 +02:00
|
|
|
|
2009-04-14 02:01:34 +02:00
|
|
|
struct cbfs_file {
|
2009-04-08 09:47:01 +02:00
|
|
|
char magic[8];
|
|
|
|
unsigned int len;
|
|
|
|
unsigned int type;
|
|
|
|
unsigned int checksum;
|
|
|
|
unsigned int offset;
|
|
|
|
};
|
|
|
|
|
|
|
|
'magic' is a magic value used to identify the header. During runtime,
|
|
|
|
coreboot will scan the ROM looking for this value. The default magic is
|
|
|
|
the string 'LARCHIVE'.
|
|
|
|
|
|
|
|
'len' is the length of the data, not including the size of the header and
|
|
|
|
the size of the name.
|
|
|
|
|
|
|
|
'type' is a 32 bit number indicating the type of data that is attached.
|
|
|
|
The data type is used in a number of ways, as detailed in the section
|
|
|
|
below.
|
|
|
|
|
|
|
|
'checksum' is a 32bit checksum of the entire component, including the
|
|
|
|
header and name.
|
|
|
|
|
2010-04-27 08:56:47 +02:00
|
|
|
'offset' is the start of the component data, based off the start of the
|
2009-04-08 09:47:01 +02:00
|
|
|
header.
|
|
|
|
The difference between the size of the header and offset is the size of the
|
|
|
|
component name.
|
|
|
|
|
2010-04-27 08:56:47 +02:00
|
|
|
Immediately following the header will be the name of the component,
|
2009-04-08 09:47:01 +02:00
|
|
|
which will
|
|
|
|
null terminated and 16 byte aligned. The following picture shows the
|
|
|
|
structure of the header:
|
|
|
|
|
|
|
|
/--------\ <- start
|
|
|
|
| Header |
|
2009-04-14 02:01:34 +02:00
|
|
|
|--------| <- sizeof(struct cbfs_file)
|
2009-04-08 09:47:01 +02:00
|
|
|
| Name |
|
|
|
|
|--------| <- 'offset'
|
|
|
|
| Data |
|
|
|
|
| ... |
|
|
|
|
\--------/ <- start + 'offset' + 'len'
|
|
|
|
|
2018-04-09 13:05:29 +02:00
|
|
|
== Searching Algorithm ==
|
2009-04-08 09:47:01 +02:00
|
|
|
|
|
|
|
To locate a specific component in the ROM, one starts at the 'offset'
|
2009-04-14 02:01:34 +02:00
|
|
|
specified in the CBFS master header. For this example, the offset will
|
2009-04-08 09:47:01 +02:00
|
|
|
be 0.
|
|
|
|
|
|
|
|
From that offset, the code should search for the magic string on the
|
|
|
|
component, jumping 'align' bytes each time. So, assuming that 'align' is
|
|
|
|
16, the code will search for the string 'LARCHIVE' at offset 0, 16, 32, etc.
|
2009-04-14 02:01:34 +02:00
|
|
|
If the offset ever exceeds the allowable range for CBFS components, then no
|
2009-04-08 09:47:01 +02:00
|
|
|
component was found.
|
|
|
|
|
|
|
|
Upon recognizing a component, the software then has to search for the
|
|
|
|
specific name of the component. This is accomplished by comparing the
|
|
|
|
desired name with the string on the component located at
|
2010-04-27 08:56:47 +02:00
|
|
|
offset + sizeof(struct cbfs_file). If the string matches, then the
|
2009-04-08 09:47:01 +02:00
|
|
|
component
|
|
|
|
has been located, otherwise the software should add 'offset' + 'len' to
|
|
|
|
the offset and resume the search for the magic value.
|
|
|
|
|
|
|
|
== Data Types ==
|
|
|
|
|
2009-04-14 02:01:34 +02:00
|
|
|
The 'type' member of struct cbfs_file is used to identify the content
|
2009-04-08 09:47:01 +02:00
|
|
|
of the component data, and is used by coreboot and other
|
|
|
|
run-time entities to make decisions about how to handle the data.
|
|
|
|
|
|
|
|
There are three component types that are essential to coreboot, and so
|
|
|
|
are defined here.
|
|
|
|
|
|
|
|
=== Stages ===
|
|
|
|
|
|
|
|
Stages are code loaded by coreboot during the boot process. They are
|
|
|
|
essential to a successful boot. Stages are comprised of a single blob
|
|
|
|
of binary data that is to be loaded into a particular location in memory
|
|
|
|
and executed. The uncompressed header contains information about how
|
|
|
|
large the data is, and where it should be placed, and what additional memory
|
|
|
|
needs to be cleared.
|
|
|
|
|
|
|
|
Stages are assigned a component value of 0x10. When coreboot sees this
|
|
|
|
component type, it knows that it should pass the data to a sub-function
|
|
|
|
that will process the stage.
|
|
|
|
|
|
|
|
The following is the format of a stage component:
|
|
|
|
|
|
|
|
/--------\
|
|
|
|
| Header |
|
|
|
|
|--------|
|
|
|
|
| Binary |
|
|
|
|
| .. |
|
|
|
|
\--------/
|
|
|
|
|
|
|
|
The header is defined as:
|
|
|
|
|
2009-04-14 02:01:34 +02:00
|
|
|
struct cbfs_stage {
|
2009-04-08 09:47:01 +02:00
|
|
|
unsigned int compression;
|
|
|
|
unsigned long long entry;
|
|
|
|
unsigned long long load;
|
|
|
|
unsigned int len;
|
|
|
|
unsigned int memlen;
|
|
|
|
};
|
|
|
|
|
|
|
|
'compression' is an integer defining how the data is compressed. There
|
|
|
|
are three compression types defined by this version of the standard:
|
2013-08-31 08:16:27 +02:00
|
|
|
none (0x0), lzma (0x1), and nrv2b (0x02, deprecated), though additional
|
|
|
|
types may be added assuming that coreboot understands how to handle the scheme.
|
2009-04-08 09:47:01 +02:00
|
|
|
|
|
|
|
'entry' is a 64 bit value indicating the location where the program
|
|
|
|
counter should jump following the loading of the stage. This should be
|
|
|
|
an absolute physical memory address.
|
|
|
|
|
|
|
|
'load' is a 64 bit value indicating where the subsequent data should be
|
|
|
|
loaded. This should be an absolute physical memory address.
|
|
|
|
|
|
|
|
'len' is the length of the compressed data in the component.
|
|
|
|
|
|
|
|
'memlen' is the amount of memory that will be used by the component when
|
|
|
|
it is loaded.
|
|
|
|
|
|
|
|
The component data will start immediately following the header.
|
|
|
|
|
|
|
|
When coreboot loads a stage, it will first zero the memory from 'load' to
|
|
|
|
'memlen'. It will then decompress the component data according to the
|
|
|
|
specified scheme and place it in memory starting at 'load'. Following that,
|
|
|
|
it will jump execution to the address specified by 'entry'.
|
|
|
|
Some components are designed to execute directly from the ROM - coreboot
|
|
|
|
knows which components must do that and will act accordingly.
|
|
|
|
|
|
|
|
=== Payloads ===
|
|
|
|
|
|
|
|
Payloads are loaded by coreboot following the boot process.
|
|
|
|
|
|
|
|
Stages are assigned a component value of 0x20. When coreboot sees this
|
|
|
|
component type, it knows that it should pass the data to a sub-function
|
|
|
|
that will process the payload. Furthermore, other run time
|
|
|
|
applications such as 'bayou' may easily index all available payloads
|
|
|
|
on the system by searching for the payload type.
|
|
|
|
|
|
|
|
|
|
|
|
The following is the format of a stage component:
|
|
|
|
|
|
|
|
/-----------\
|
|
|
|
| Header |
|
|
|
|
| Segment 1 |
|
|
|
|
| Segment 2 |
|
|
|
|
| ... |
|
|
|
|
|-----------|
|
|
|
|
| Binary |
|
|
|
|
| .. |
|
|
|
|
\-----------/
|
|
|
|
|
|
|
|
The header is as follows:
|
|
|
|
|
2009-04-14 02:01:34 +02:00
|
|
|
struct cbfs_payload {
|
|
|
|
struct cbfs_payload_segment segments;
|
2009-04-08 09:47:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
The header contains a number of segments corresponding to the segments
|
|
|
|
that need to be loaded for the payload.
|
|
|
|
|
|
|
|
The following is the structure of each segment header:
|
|
|
|
|
2009-04-14 02:01:34 +02:00
|
|
|
struct cbfs_payload_segment {
|
2009-04-08 09:47:01 +02:00
|
|
|
unsigned int type;
|
|
|
|
unsigned int compression;
|
|
|
|
unsigned int offset;
|
|
|
|
unsigned long long load_addr;
|
|
|
|
unsigned int len;
|
|
|
|
unsigned int mem_len;
|
|
|
|
};
|
|
|
|
|
|
|
|
'type' is the type of segment, one of the following:
|
|
|
|
|
|
|
|
PAYLOAD_SEGMENT_CODE 0x45444F43 The segment contains executable code
|
|
|
|
PAYLOAD_SEGMENT_DATA 0x41544144 The segment contains data
|
2018-04-09 13:05:29 +02:00
|
|
|
PAYLOAD_SEGMENT_BSS 0x20535342 The memory specified by the segment
|
2009-04-08 09:47:01 +02:00
|
|
|
should be zeroed
|
|
|
|
PAYLOAD_SEGMENT_PARAMS 0x41524150 The segment contains information for
|
|
|
|
the payload
|
|
|
|
PAYLOAD_SEGMENT_ENTRY 0x52544E45 The segment contains the entry point
|
2018-05-29 08:31:10 +02:00
|
|
|
for the payload
|
2009-04-08 09:47:01 +02:00
|
|
|
|
|
|
|
'compression' is the compression scheme for the segment. Each segment can
|
|
|
|
be independently compressed. There are three compression types defined by
|
2013-08-31 08:16:27 +02:00
|
|
|
this version of the standard: none (0x0), lzma (0x1), and nrv2b
|
|
|
|
(0x02, deprecated), though additional types may be added assuming that
|
|
|
|
coreboot understands how to handle the scheme.
|
2009-04-08 09:47:01 +02:00
|
|
|
|
|
|
|
'offset' is the address of the data within the component, starting from
|
|
|
|
the component header.
|
|
|
|
|
|
|
|
'load_addr' is a 64 bit value indicating where the segment should be placed
|
|
|
|
in memory.
|
|
|
|
|
|
|
|
'len' is a 32 bit value indicating the size of the segment within the
|
|
|
|
component.
|
|
|
|
|
|
|
|
'mem_len' is the size of the data when it is placed into memory.
|
|
|
|
|
|
|
|
The data will located immediately following the last segment.
|
|
|
|
|
|
|
|
=== Option ROMS ===
|
|
|
|
|
|
|
|
The third specified component type will be Option ROMs. Option ROMS will
|
|
|
|
have component type '0x30'. They will have no additional header, the
|
|
|
|
uncompressed binary data will be located in the data portion of the
|
|
|
|
component.
|
|
|
|
|
|
|
|
=== NULL ===
|
|
|
|
|
|
|
|
There is a 4th component type ,defined as NULL (0xFFFFFFFF). This is
|
|
|
|
the "don't care" component type. This can be used when the component
|
|
|
|
type is not necessary (such as when the name of the component is unique.
|
|
|
|
i.e. option_table). It is recommended that all components be assigned a
|
|
|
|
unique type, but NULL can be used when the type does not matter.
|