summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libbsp/i386/pc386/tools
diff options
context:
space:
mode:
authorJoel Sherrill <joel.sherrill@OARcorp.com>1997-12-01 22:06:48 +0000
committerJoel Sherrill <joel.sherrill@OARcorp.com>1997-12-01 22:06:48 +0000
commit7150f00f5be87fa8e37f7d00fbbef35645081138 (patch)
tree1cc7d3e1c4933404ddc1f742c7e37648cc783364 /c/src/lib/libbsp/i386/pc386/tools
parentFixed test for RTEMS_HAS_POSIX_API so the executive POSIX API related (diff)
downloadrtems-7150f00f5be87fa8e37f7d00fbbef35645081138.tar.bz2
Inclusion of PC386 BSP submitted by Pedro Miguel Da Cruz Neto Romano
<pmcnr@camoes.rnl.ist.utl.pt> and Jose Rufino <ruf@asterix.ist.utl.pt> of NavIST (http://pandora.ist.utl.pt/).
Diffstat (limited to 'c/src/lib/libbsp/i386/pc386/tools')
-rw-r--r--c/src/lib/libbsp/i386/pc386/tools/Makefile.in63
-rw-r--r--c/src/lib/libbsp/i386/pc386/tools/Spec.doc354
-rw-r--r--c/src/lib/libbsp/i386/pc386/tools/bin2boot.c239
3 files changed, 656 insertions, 0 deletions
diff --git a/c/src/lib/libbsp/i386/pc386/tools/Makefile.in b/c/src/lib/libbsp/i386/pc386/tools/Makefile.in
new file mode 100644
index 0000000000..d3ea3f7ce3
--- /dev/null
+++ b/c/src/lib/libbsp/i386/pc386/tools/Makefile.in
@@ -0,0 +1,63 @@
+#
+# $Id$
+#
+
+@SET_MAKE@
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH=@srcdir@
+exec_prefix = @exec_prefix@
+bindir = @bindir@
+libdir = @libdir@
+includedir = @includedir@
+manext = 1
+mandir = @mandir@/man$(manext)
+
+
+VPATH=@srcdir@
+
+
+# we use host compiler in this directory
+USE_HOST_COMPILER=yes
+
+# C source names, if any, go here -- minus the .c
+C_PIECES=bin2boot
+C_FILES=$(C_PIECES:%=%.c)
+C_O_FILES=$(C_PIECES:%=$(ARCH)/%.o)
+
+H_FILES=
+
+SRCS=$(C_FILES) $(CC_FILES) $(H_FILES)
+OBJS=$(C_O_FILES) $(CC_O_FILES) $(S_O_FILES)
+
+PGMS=$(ARCH)/bin2boot
+
+include $(RTEMS_CUSTOM)
+include $(RTEMS_ROOT)/make/leaf.cfg
+
+#
+# (OPTIONAL) Add local stuff here using +=
+#
+
+DEFINES +=
+CPPFLAGS +=
+CFLAGS +=
+
+LD_PATHS +=
+LD_LIBS +=
+LDFLAGS +=
+
+#
+# Add your list of files to delete here. The config files
+# already know how to delete some stuff, so you may want
+# to just run 'make clean' first to see what gets missed.
+# 'make clobber' already includes 'make clean'
+#
+
+CLEAN_ADDITIONS += $(HOST_ARCH)
+CLOBBER_ADDITIONS +=
+
+all: $(ARCH) $(SRCS) $(PGMS)
+ $(INSTALL_VARIANT) -m 555 $(PGMS) ${PROJECT_RELEASE}/build-tools
+ cd ${PROJECT_RELEASE}/build-tools/diskboot.exe ; \
+ uudecode < $(srcdir)/diskboot.uue
diff --git a/c/src/lib/libbsp/i386/pc386/tools/Spec.doc b/c/src/lib/libbsp/i386/pc386/tools/Spec.doc
new file mode 100644
index 0000000000..c4ca3c4234
--- /dev/null
+++ b/c/src/lib/libbsp/i386/pc386/tools/Spec.doc
@@ -0,0 +1,354 @@
+
+ 2-28-1995 GK
+
+In order to provide more functionality to the boot rom code I changed
+Jamie's draft a little bit. All my changes have a bar sign (|) in the
+79th column.
+
+Gero Kuhlmann
+
+===============================================================================
+
+
+0. Numbering
+
+This is Draft Net Boot Image Proposal 0.2, February 28, 1995 |
+
+
+1. Preamble - the why
+
+Whilst researching what other boot proms do (at least those implementing
+TCP/IP protocols) it is clear that each 'does their own thing' in
+terms of what they expect in a boot image.
+
+If we could all agree on working toward an open standard, O/S suppliers
+and boot rom suppliers can build their products to this norm, and be confident
+that they will work with each other.
+
+This is a description of how I will implement the boot rom for
+Linux. I believe it to be flexible enough for any OS that will be loaded
+when a PC boots from a network in the TCP/IP environment.
+
+It would be good if this could be turned into some form of standard.
+
+This is very much a first draft. I am inviting comment.
+
+The ideas presented here should be independant of any implementation.
+In the end, where there is a conflict between the final of this draft, and an
+implementation, this description should prevail.
+
+The terms I use are defined at the end.
+
+
+2. The target
+
+The target is to have a PC retrieve a boot image from a network in the TCP/IP
+environment.
+
+The boot may take place from a network adaptor rom, from a boot floppy, or
+from a program in MSDOS.
+
+
+3. Net Boot Process Description.
+
+The net boot process can be started either as a result of the PC
+boot process, or through normal DOS execution of a program. The net boot
+program can reside on a rom, e.g. on an adaptor card, or in ram, either
+as a result of reading off disk or transferred from ram.
+
+The boot process may execute in any mode (e.g. 8086, 80386) it desires.
+When it jumps to the start location in the boot image, it must be in
+8086 mode and be capable of going into any mode supported by the
+underlying processor.
+
+The image cannot be loaded into address spaces below 10000h, or between
+A0000h through FFFFFh, or between 98000h through 9FFFFh. Once the image
+starts executing, all the memory is available to it, so it can relocate
+parts of itself to these areas.
+
+The boot process must be capable of loading the image into all other
+memory locations. Specifically, where the machine supports this, this means
+memory over 100000h.
+
+The net boot process must execute the bootp protocol, followed by
+the tftp protocol, as defined in the relevant rfc's.
+
+The file name used in the tftp protocol must be that given by the bootp
+record.
+
+If less than 512 bytes are loaded, the net boot process attempts to display
+on the screen any ascii data at the start of the image. The net boot
+process then exits in the normal manner. For a boot prom, this will
+allow normal disk booting. For DOS programs, this will mean a normal return
+to DOS.
+
+When the first 512 bytes have been loaded, the boot process checks
+for an initial magic number, which is defined later. If this number
+is present, the net process continues loading under the control
+of the image format. The image, which is described later, tells the
+net boot process where to put this record and all subsequent data.
+
+If no initial magic number is present the net boot process checks for a second
+magic number at offset 510. If the magic number 510 = 55h, 511 = AAh,
+then the net process continues. If this second magic number is not
+present, then the net boot process terminates the tftp protocol, displays
+an error message and exits in the normal manner.
+
+If no initial magic number is present and the second one is, the net boot
+process relocates the 512 bytes to location 7c00h. The net boot process
+continues to load any further image data to 10000h up. This data can overwrite
+the first 512 boot bytes. If the image reaches 98000h, then any further data is
+continued to be loaded above 100000h. When all the data has been loaded, the
+net boot process jumps to location 0:7c00.
+
+When the net boot process calls the image, it places 2 far pointers onto |
+the stack, in standard intel order (e.g. segment:offset representation). |
+The first far pointer which immediately follows the return address on |
+the stack, points to the loaded boot image header. The second far pointer |
+which is placed above the first one, shows to the memory area where the |
+net boot process saved the bootp reply. |
+
+
+4. Image Format with Initial Magic Number.
+
+The first 512 bytes of the image file contain the image header,
+and image loading information records. This contains all the
+information needed by the net boot process as to where data
+is to be loaded.
+
+The magic number (in time-honoured tradition (well why not?)) is:
+
+ 0 = 36h
+ 1 = 13h
+ 2 = 03h
+ 3 = 1Bh
+
+Apart from the two magic numbers, all words and double words are in PC
+native endian.
+
+Including the initial magic number the header record is:
+
+ +---------------------+
+ | |
+ | Initial Magic No. | 4 bytes
+ +---------------------+
+ | |
+ | Flags and length | double word
+ +---------------------+
+ | |
+ | Location Address | double word in ds:bx format
+ +---------------------+
+ | |
+ | Execute Address | double word in cs:ip format
+ +---------------------+
+
+The Location address is where to place the 512 bytes. The net boot
+process does this before loading the rest of the image. The location
+address cannot be one of the reserved locations mentioned above, but
+must be an address lower than 100000h.
+
+The rest of the image must not overwrite these initial 512 bytes, placed
+at the required location. The writing of data by the net boot process
+into these 512 bytes is deprecated. These 512 bytes must be available for
+the image to interogate once it is loaded and running.
+
+The execute address is the location in cs:ip of the initial instruction
+once the full image has been loaded. This must be lower than 100000h,
+since the initial instructions will be executed in 8086 mode. When the
+jump (actaully a far call) is made to the boot image, the stack contains a
+far return address, with a far pointer parameter above that, pointing
+to the location of this header.
+
+The flags and length field is broken up in the following way:
+
+Bits 0 to 3 (lowest 4 bits) define the length of the non vendor header in
+double words. Currently the value is 4.
+
+Bits 4 to 7 define the length required by the vendor extra information
+in double words. A value of zero indicates no extra vendor information.
+
+Bits 8 to 31 are reserved for future use and must be set to zero.
+
+After this header, and any vendor header, come the image loading information
+records. These specify where data is to be loaded, how long it is, and
+communicates to the loaded image what sort of data it is.
+
+The format of each image loading information record is :
+
+
+ +---------------------+
+ | Flags, tags and | double word
+ | lengths |
+ +---------------------+
+ | |
+ | Load Address | double word
+ +---------------------+
+ | |
+ | Image Length | double word
+ +---------------------+
+ | |
+ | Memory Length | double word
+ +---------------------+
+
+Each image loading information record follows the previous, or the header.
+
+The memory length, image length and load address fields are unsigned 32
+numbers. They do not have the segment:offset format used by the 8086.
+
+The flags, tags and lengths field is broken up as follows:
+
+Bits 0 to 3 (lowest 4 bits) are the length of the non vendor part of this
+header in double words. Currently this value is 4.
+
+Bits 4 to 7 indicate the length of any vendor information, in double words.
+
+Bits 8 to 15 are for vendor's tags. The vendor tag is a private number that
+the loaded image can use to determine what sort of image is at this particular
+location.
+
+Bits 16 to 23 are for future expansion and should be set to zero.
+
+Bits 24 to 31 are for flags, which are defined later.
+
+Vendors may place further information after this information record, and
+before the next. Each information record may have a different vendor
+length.
+
+There are two restrictions on vendor information.
+
+One is that the header and all information records that the net boot process
+is to use fall within the first 512 bytes.
+
+The second restriction is that the net boot process must ignore all
+vendor additions. The net boot process may not overwrite vendor supplied
+information, or other undefined data in the initial 512 bytes.
+
+The flags are used to modify the load address field, and to indicate
+that this is the last information record that the net boot process should
+use.
+
+Bit 24 works in conjunction with bit 25 to specify the meaning of the
+load address.
+
+ B24 B25
+
+ 0 0 load address is an absolute 32 number
+
+ 1 0 add the load address to the location one past the last byte
+ of the memory area required by the last image loaded.
+ If the first image, then add to 512 plus the location
+ where the 512 bytes were placed
+
+ 0 1 subtract the load address from the one past the
+ last writeable location in memory. Thus 1 would
+ be the last location one could write in memory.
+
+ 1 1 load address is subtracted from the start of
+ the last image loaded. If the first image, then
+ subtract from the start of where the 512 bytes were
+ placed
+
+(For convenience bit 24 is byte 0 of the flag field)
+
+Bit 26 is the end marker for the net boot process. It is set when
+this is the last information record the net boot process should
+look at. More records may be present, but the net boot process will not
+look at them. (Vendors can continue information records out past the 512
+boundary for private use in this manner).
+
+The image length tells the net boot process how many bytes are to be loaded.
+Zero is a valid value. This can be used to mark memory areas such as
+shared memory for interprocessor communication, flash eproms, data in eproms.
+
+The image length can also be different from the memory length. This allows
+decompression programs to fluff up the kernel image. It also allows a file
+system to be larger then the loaded file system image.
+
+Bits 27 through 31 are not defined as yet and must be set to zero until
+they are.
+
+
+6. Boot prom entry points.
+
+I have not defined boot entry points, and means of obtaining them.
+It could be useful to down load part of an image, and have that image
+load more of itself by using handy parts of the net boot program.
+
+This can be considered 'for further study'.
+
+
+7. Example of a boot image.
+
+Here is an example of how the boot image would look for Linux:
+
+ 0x1B031336, /* magic number */
+ 0x4, /* length of header is 16 bytes, no vendor info */
+ 0x90000000, /* location in ds:bx format */
+ 0x90000200, /* execute address in cs:ip format */
+
+ /* 2048 setup.S bytes */
+ 0x4, /* flags, not end, absolute address, 16 bytes this
+ record, no vendor info */
+ 0x90200, /* load address - note format */
+ 0x800, /* 4 8 512 byte blocks for linux */
+ 0x800,
+
+ /* kernel image */
+ 0x4, /* flags, not end, absolute address, 16 bytes this
+ record, no vendor info */
+ 0x10000, /* load address - note format */
+ 0x80000, /* 512K (this could be shorter */
+ 0x80000,
+
+ /* ramdisk for root file system */
+ 0x04000004, /* flags = last, absolute address, 16 bytes this
+ record, no vendor info *//
+ 0x100000, /* load address - in extended memory */
+ 0x80000, /* 512K for instance */
+ 0x80000,
+
+ /* Then follows linux specific information */
+
+
+8. Terms
+
+When I say 'the net boot process', I mean the act of loading the image into
+memory, setting up any tables, up until the jump to the required location
+in the image.
+
+The net booting program executes the net boot process. The net boot program
+may be a rom, but not neccassarily. It is a set of instructions and data
+residing on the booting machine.
+
+The image, or boot image, consists of the data loaded by the net boot process.
+
+When I say 'the PC boot process', I mean the general PC rom bios boot process,
+the setting up of hardware, the scanning for adaptor roms, the execution
+of adaptor roms, the loading in of the initial boot track. The PC boot
+process will include the net boot process, if one is present.
+
+When I say client, I mean the PC booting up.
+
+When I say 'image host', I mean the host where the boot image is comming from.
+This may not have the same architecture as the client.
+
+The bootp protocol is defined in RFC951 and RFC1084. The tftp protocol
+is defined in RFC783. These are available on many sites.
+See Comer 1991 for details on how to obtain them.
+
+A bootp server is the machine that answers the bootp request. It is not
+neccassarily the image host.
+
+'Can' and 'may' means doesn't have to, but is allowed to and might.
+'Must' means just that. 'Cannot' means must not.
+
+
+9 References
+
+Comer, D.E. 1991, Internetworking with TCP/IP Vol I: Principles, Protocols,
+and Architecture Second Edition, Prentice Hall, Englewood Cliffs, N.J., 1991
+
+Stevens, W.R 1990, Unix Network Programming, Prentice Hall,
+Englewood Cliffs, N.J., 1990
+
+
diff --git a/c/src/lib/libbsp/i386/pc386/tools/bin2boot.c b/c/src/lib/libbsp/i386/pc386/tools/bin2boot.c
new file mode 100644
index 0000000000..f8eaa0faea
--- /dev/null
+++ b/c/src/lib/libbsp/i386/pc386/tools/bin2boot.c
@@ -0,0 +1,239 @@
+/*-------------------------------------------------------------------------+
+| bin2boot.c v1.1 - PC386 BSP - 1997/08/18
++--------------------------------------------------------------------------+
+| This file contains the i386 binary to boot image filter.
++--------------------------------------------------------------------------+
+| (C) Copyright 1997 -
+| - NavIST Group - Real-Time Distributed Systems and Industrial Automation
+|
+| http://pandora.ist.utl.pt
+|
+| Instituto Superior Tecnico * Lisboa * PORTUGAL
++--------------------------------------------------------------------------+
+| Disclaimer:
+|
+| This file is provided "AS IS" without warranty of any kind, either
+| expressed or implied.
++--------------------------------------------------------------------------*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "bytetype.h"
+#include "bootimg.h"
+
+/*-------------------------------------------------------------------------+
+| Constants
++--------------------------------------------------------------------------*/
+#define SEG_MASK 0xffff0000
+ /* Mask for segment part of seg:off address. */
+#define OFF_MASK 0x0000ffff
+ /* Mask for offset part of seg:off address. */
+#define DEFAULT_START_ADDR 0x10200 /* Default start address for binary. */
+#define BOOTIMG_HDR_SIZE 0x0200 /* Size of output file header. */
+#define BUFFER_BLOCK_SIZE 0x1000 /* Size of transfer buffer size. */
+#define LAST_BLOCK_SIZE 0x0200
+ /* The output file must have a size multiple of this value. */
+
+
+/*-------------------------------------------------------------------------+
+| Macros
++--------------------------------------------------------------------------*/
+#define getSeg(x) (Word)((x) >> 16)
+ /* Return seg part (Word) of a seg:off address (DWord). */
+
+#define getOff(x) (Word)((x) & OFF_MASK)
+ /* Return off part (Word) of a seg:off address (DWord). */
+
+#define getSegOff(x) ((((x) & SEG_MASK) << 12) + ((x) & OFF_MASK))
+ /* Converts a flat address to a seg:off address. */
+
+/*-------------------------------------------------------------------------+
+| Global Variables
++--------------------------------------------------------------------------*/
+const char UsageMsg[] = "\
+Usage: bin2boot [<infile> [<outfile>]] [-s <start_address>] [-v]\n\
+\n\
+ <infile> : input file name\n\
+ <outfile> : output file name\n\
+ -s <outfile> : start address of binary image\n\
+ -m <size> : actual size (for compressed images)\n\
+ -v : verbose output\n"; /* Usage message. */
+
+
+/*-------------------------------------------------------------------------+
+| External Prototypes (for use with getopt)
++--------------------------------------------------------------------------*/
+extern char *optarg;
+
+int getopt(int, char *const[], const char *);
+
+
+/*-------------------------------------------------------------------------+
+| Auxiliary Functions
++--------------------------------------------------------------------------*/
+static DWord
+getNumArg(char *arg)
+{
+ char *dummy;
+
+ if (arg[0] == '0')
+ if ((arg[1] == 'x') || (arg[1] == 'X')) /* Hexadecimal */
+ return (DWord)strtol(arg, &dummy, 16);
+ else /* Octal */
+ return (DWord)strtol(arg, &dummy, 8);
+ else /* Decimal */
+ return (DWord)strtol(arg, &dummy, 10);
+} /* getNumArg */
+
+
+/*-------------------------------------------------------------------------+
+| Main
++--------------------------------------------------------------------------*/
+void main(int argc, char *argv[])
+{
+ FileHeader bootimgFileHdr; /* Output file header. */
+ LoadingInfo imageInfo; /* Section header. */
+ Byte auxBuf[BUFFER_BLOCK_SIZE]; /* */
+ DWord nRead; /* Number of bytes read. */
+ Word padSize; /* Size of padding at end of file. */
+
+ char *progName = argv[0]; /* Program name for errors. */
+ FILE *fpIn = stdin;
+ FILE *fpOut = stdout;
+ DWord binStart = DEFAULT_START_ADDR; /* Start address of image. */
+ DWord memLen = 0; /* Real length for compressed images. */
+
+ char currArg;
+ int argCount;
+ int flag; /* general purpose flag */
+ int verbose = 0; /* flag for verbose output */
+
+ while ((currArg = getopt(argc, argv, "hm:s:v")) >= 0) /* parse command line */
+ switch (currArg)
+ {
+ case 'h' :
+ fprintf(stderr, UsageMsg);
+ exit(0);
+ break;
+ case 'm' :
+ memLen = getNumArg(optarg);
+ break;
+ case 's' :
+ binStart = getNumArg(optarg);
+ break;
+ case 'v' :
+ verbose = 1;
+ break;
+ default :
+ fprintf(stderr, UsageMsg);
+ exit(1);
+ break;
+ } /* switch */
+
+ flag = 0;
+
+ for (argCount = 1; argCount < argc; argCount++)
+ if (argv[argCount][0] == '-')
+ {
+ if (argv[argCount][1] == 's')
+ argCount++;
+ }
+ else if (flag) /* we already have the input file => output file */
+ {
+ if ((fpOut = fopen(argv[argCount], "w")) == NULL)
+ {
+ fprintf(stderr, "%s: can't open %s\n", progName, argv[argCount]);
+ exit(1);
+ }
+ }
+ else /* input file */
+ {
+ if ((fpIn = fopen(argv[argCount], "r")) == NULL)
+ {
+ fprintf(stderr, "%s: can't open %s\n", progName, argv[argCount]);
+ exit(1);
+ }
+ flag = 1;
+ }
+
+ /*** begin: Conversion to Bootimg */
+
+ /*** File Header */
+
+ if (verbose)
+ fprintf(stderr, "\nBoot Image File Header:\n\n");
+
+ bootimgFileHdr.magicNum = BOOT_IMAGE_MAGIC; /* 4 bytes - magic number */
+ bootimgFileHdr.flagsLen = NON_VENDOR_LEN; /* 4 bytes - flags and length */
+ bootimgFileHdr.locAddr = getSegOff(binStart - BOOTIMG_HDR_SIZE);
+ /* 4 bytes - location address in ds:bx format */
+ bootimgFileHdr.execAddr = getSegOff(binStart);
+ /* 4 bytes - execute address in cs:ip format */
+
+ if (verbose)
+ {
+ fprintf(stderr, ">> location address in ds:bx format: %04x:%04x\n",
+ getSeg(bootimgFileHdr.locAddr), getOff(bootimgFileHdr.locAddr));
+ fprintf(stderr, ">> execute address in cs:ip format: %04x:%04x\n",
+ getSeg(bootimgFileHdr.execAddr), getOff(bootimgFileHdr.execAddr));
+ }
+
+ /*** Write File Header to output file */
+
+ fwrite((void *)(& bootimgFileHdr), sizeof(FileHeader), 1, fpOut);
+
+ /*** Sections */
+
+ if (verbose)
+ fprintf(stderr, "\nCode Section:\n\n");
+
+ imageInfo.flagsTagsLens = 0x04000004;
+ /* flags, vendor's tags, vendor and non-vendor lengths */
+ imageInfo.loadAddr = binStart; /* load address */
+
+ rewind(fpIn);
+ fseek(fpIn, 0, SEEK_END);
+ nRead = ftell(fpIn);
+ rewind(fpIn);
+ padSize = LAST_BLOCK_SIZE - (nRead % LAST_BLOCK_SIZE);
+ imageInfo.imageLength = nRead + padSize; /* image length */
+ imageInfo.memoryLength = (memLen != 0) ? memLen : imageInfo.imageLength;
+ /* memory length */
+
+ fwrite((void *)(&imageInfo), sizeof(LoadingInfo), 1, fpOut);
+
+ if (verbose)
+ {
+ fprintf(stderr, ">> load address: 0x%08lx\n", imageInfo.loadAddr);
+ fprintf(stderr, ">> image length: 0x%08lx\n", imageInfo.imageLength);
+ fprintf(stderr, ">> memory length: 0x%08lx\n\n", imageInfo.memoryLength);
+ }
+
+ nRead = BOOTIMG_HDR_SIZE - sizeof(FileHeader) - sizeof(LoadingInfo);
+ memset((void *)auxBuf, 0x00, nRead);
+ fwrite((void *)auxBuf, 1, nRead, fpOut);
+
+ nRead = fread((void *)auxBuf, 1, BUFFER_BLOCK_SIZE, fpIn);
+
+ while (!feof(fpIn))
+ {
+ fwrite((void *)auxBuf, BUFFER_BLOCK_SIZE, 1, fpOut);
+ nRead = fread((void *)auxBuf, 1, BUFFER_BLOCK_SIZE, fpIn);
+ }
+
+ fwrite((void *)auxBuf, 1, nRead, fpOut);
+
+ memset((void *)auxBuf, 0x00, padSize);
+ fwrite((void *)auxBuf, 1, padSize, fpOut);
+
+ fclose(fpOut);
+ fclose(fpIn);
+
+ /*** end: Conversion to Bootimg */
+
+ exit(0);
+
+} /* main */