Annotation of qemu/roms/seabios/src/coreboot.c, revision 1.1.1.1

1.1       root        1: // Coreboot interface support.
                      2: //
                      3: // Copyright (C) 2008,2009  Kevin O'Connor <[email protected]>
                      4: //
                      5: // This file may be distributed under the terms of the GNU LGPLv3 license.
                      6: 
                      7: #include "memmap.h" // add_e820
                      8: #include "util.h" // dprintf
                      9: #include "pci.h" // struct pir_header
                     10: #include "acpi.h" // struct rsdp_descriptor
                     11: #include "mptable.h" // MPTABLE_SIGNATURE
                     12: #include "biosvar.h" // GET_EBDA
                     13: #include "lzmadecode.h" // LzmaDecode
                     14: #include "smbios.h" // smbios_init
                     15: 
                     16: 
                     17: /****************************************************************
                     18:  * Memory map
                     19:  ****************************************************************/
                     20: 
                     21: struct cb_header {
                     22:     u32 signature;
                     23:     u32 header_bytes;
                     24:     u32 header_checksum;
                     25:     u32 table_bytes;
                     26:     u32 table_checksum;
                     27:     u32 table_entries;
                     28: };
                     29: 
                     30: #define CB_SIGNATURE 0x4f49424C // "LBIO"
                     31: 
                     32: struct cb_memory_range {
                     33:     u64 start;
                     34:     u64 size;
                     35:     u32 type;
                     36: };
                     37: 
                     38: #define CB_MEM_TABLE    16
                     39: 
                     40: struct cb_memory {
                     41:     u32 tag;
                     42:     u32 size;
                     43:     struct cb_memory_range map[0];
                     44: };
                     45: 
                     46: #define CB_TAG_MEMORY 0x01
                     47: 
                     48: #define MEM_RANGE_COUNT(_rec) \
                     49:         (((_rec)->size - sizeof(*(_rec))) / sizeof((_rec)->map[0]))
                     50: 
                     51: struct cb_mainboard {
                     52:     u32 tag;
                     53:     u32 size;
                     54:     u8  vendor_idx;
                     55:     u8  part_idx;
                     56:     char  strings[0];
                     57: };
                     58: 
                     59: #define CB_TAG_MAINBOARD 0x0003
                     60: 
                     61: struct cb_forward {
                     62:     u32 tag;
                     63:     u32 size;
                     64:     u64 forward;
                     65: };
                     66: 
                     67: #define CB_TAG_FORWARD 0x11
                     68: 
                     69: static u16
                     70: ipchksum(char *buf, int count)
                     71: {
                     72:     u16 *p = (u16*)buf;
                     73:     u32 sum = 0;
                     74:     while (count > 1) {
                     75:         sum += *p++;
                     76:         count -= 2;
                     77:     }
                     78:     if (count)
                     79:         sum += *(u8*)p;
                     80:     sum = (sum >> 16) + (sum & 0xffff);
                     81:     sum += (sum >> 16);
                     82:     return ~sum;
                     83: }
                     84: 
                     85: // Try to locate the coreboot header in a given address range.
                     86: static struct cb_header *
                     87: find_cb_header(char *addr, int len)
                     88: {
                     89:     char *end = addr + len;
                     90:     for (; addr < end; addr += 16) {
                     91:         struct cb_header *cbh = (struct cb_header *)addr;
                     92:         if (cbh->signature != CB_SIGNATURE)
                     93:             continue;
                     94:         if (! cbh->table_bytes)
                     95:             continue;
                     96:         if (ipchksum(addr, sizeof(*cbh)) != 0)
                     97:             continue;
                     98:         if (ipchksum(addr + sizeof(*cbh), cbh->table_bytes)
                     99:             != cbh->table_checksum)
                    100:             continue;
                    101:         return cbh;
                    102:     }
                    103:     return NULL;
                    104: }
                    105: 
                    106: // Try to find the coreboot memory table in the given coreboot table.
                    107: static void *
                    108: find_cb_subtable(struct cb_header *cbh, u32 tag)
                    109: {
                    110:     char *tbl = (char *)cbh + sizeof(*cbh);
                    111:     int i;
                    112:     for (i=0; i<cbh->table_entries; i++) {
                    113:         struct cb_memory *cbm = (struct cb_memory *)tbl;
                    114:         tbl += cbm->size;
                    115:         if (cbm->tag == tag)
                    116:             return cbm;
                    117:     }
                    118:     return NULL;
                    119: }
                    120: 
                    121: static struct cb_memory *CBMemTable;
                    122: 
                    123: // Populate max ram and e820 map info by scanning for a coreboot table.
                    124: static void
                    125: coreboot_fill_map()
                    126: {
                    127:     dprintf(3, "Attempting to find coreboot table\n");
                    128: 
                    129:     CBMemTable = NULL;
                    130: 
                    131:     // Find coreboot table.
                    132:     struct cb_header *cbh = find_cb_header(0, 0x1000);
                    133:     if (!cbh)
                    134:         goto fail;
                    135:     struct cb_forward *cbf = find_cb_subtable(cbh, CB_TAG_FORWARD);
                    136:     if (cbf) {
                    137:         dprintf(3, "Found coreboot table forwarder.\n");
                    138:         cbh = find_cb_header((char *)((u32)cbf->forward), 0x100);
                    139:         if (!cbh)
                    140:             goto fail;
                    141:     }
                    142:     dprintf(3, "Now attempting to find coreboot memory map\n");
                    143:     struct cb_memory *cbm = CBMemTable = find_cb_subtable(cbh, CB_TAG_MEMORY);
                    144:     if (!cbm)
                    145:         goto fail;
                    146: 
                    147:     u64 maxram = 0, maxram_over4G = 0;
                    148:     int i, count = MEM_RANGE_COUNT(cbm);
                    149:     for (i=0; i<count; i++) {
                    150:         struct cb_memory_range *m = &cbm->map[i];
                    151:         u32 type = m->type;
                    152:         if (type == CB_MEM_TABLE) {
                    153:             type = E820_RESERVED;
                    154:         } else if (type == E820_ACPI || type == E820_RAM) {
                    155:             u64 end = m->start + m->size;
                    156:             if (end > 0x100000000ull) {
                    157:                 end -= 0x100000000ull;
                    158:                 if (end > maxram_over4G)
                    159:                     maxram_over4G = end;
                    160:             } else if (end > maxram)
                    161:                 maxram = end;
                    162:         }
                    163:         add_e820(m->start, m->size, type);
                    164:     }
                    165: 
                    166:     RamSize = maxram;
                    167:     RamSizeOver4G = maxram_over4G;
                    168: 
                    169:     // Ughh - coreboot likes to set a map at 0x0000-0x1000, but this
                    170:     // confuses grub.  So, override it.
                    171:     add_e820(0, 16*1024, E820_RAM);
                    172: 
                    173:     struct cb_mainboard *cbmb = find_cb_subtable(cbh, CB_TAG_MAINBOARD);
                    174:     if (cbmb) {
                    175:         const char *vendor = &cbmb->strings[cbmb->vendor_idx];
                    176:         const char *part = &cbmb->strings[cbmb->part_idx];
                    177:         dprintf(1, "Found mainboard %s %s\n", vendor, part);
                    178: 
                    179:         vgahook_setup(vendor, part);
                    180:     }
                    181: 
                    182:     return;
                    183: 
                    184: fail:
                    185:     // No table found..  Use 16Megs as a dummy value.
                    186:     dprintf(1, "Unable to find coreboot table!\n");
                    187:     RamSize = 16*1024*1024;
                    188:     RamSizeOver4G = 0;
                    189:     add_e820(0, 16*1024*1024, E820_RAM);
                    190:     return;
                    191: }
                    192: 
                    193: 
                    194: /****************************************************************
                    195:  * BIOS table copying
                    196:  ****************************************************************/
                    197: 
                    198: static void
                    199: copy_pir(void *pos)
                    200: {
                    201:     struct pir_header *p = pos;
                    202:     if (p->signature != PIR_SIGNATURE)
                    203:         return;
                    204:     if (PirOffset)
                    205:         return;
                    206:     if (p->size < sizeof(*p))
                    207:         return;
                    208:     if (checksum(pos, p->size) != 0)
                    209:         return;
                    210:     void *newpos = malloc_fseg(p->size);
                    211:     if (!newpos) {
                    212:         dprintf(1, "No room to copy PIR table!\n");
                    213:         return;
                    214:     }
                    215:     dprintf(1, "Copying PIR from %p to %p\n", pos, newpos);
                    216:     memcpy(newpos, pos, p->size);
                    217:     PirOffset = (u32)newpos - BUILD_BIOS_ADDR;
                    218: }
                    219: 
                    220: static void
                    221: copy_mptable(void *pos)
                    222: {
                    223:     struct mptable_floating_s *p = pos;
                    224:     if (p->signature != MPTABLE_SIGNATURE)
                    225:         return;
                    226:     if (!p->physaddr)
                    227:         return;
                    228:     if (checksum(pos, sizeof(*p)) != 0)
                    229:         return;
                    230:     u32 length = p->length * 16;
                    231:     u16 mpclength = ((struct mptable_config_s *)p->physaddr)->length;
                    232:     struct mptable_floating_s *newpos = malloc_fseg(length + mpclength);
                    233:     if (!newpos) {
                    234:         dprintf(1, "No room to copy MPTABLE!\n");
                    235:         return;
                    236:     }
                    237:     dprintf(1, "Copying MPTABLE from %p/%x to %p\n", pos, p->physaddr, newpos);
                    238:     memcpy(newpos, pos, length);
                    239:     newpos->physaddr = (u32)newpos + length;
                    240:     newpos->checksum -= checksum(newpos, sizeof(*newpos));
                    241:     memcpy((void*)newpos + length, (void*)p->physaddr, mpclength);
                    242: }
                    243: 
                    244: static void
                    245: copy_acpi_rsdp(void *pos)
                    246: {
                    247:     if (RsdpAddr)
                    248:         return;
                    249:     struct rsdp_descriptor *p = pos;
                    250:     if (p->signature != RSDP_SIGNATURE)
                    251:         return;
                    252:     u32 length = 20;
                    253:     if (checksum(pos, length) != 0)
                    254:         return;
                    255:     if (p->revision > 1) {
                    256:         length = p->length;
                    257:         if (checksum(pos, length) != 0)
                    258:             return;
                    259:     }
                    260:     void *newpos = malloc_fseg(length);
                    261:     if (!newpos) {
                    262:         dprintf(1, "No room to copy ACPI RSDP table!\n");
                    263:         return;
                    264:     }
                    265:     dprintf(1, "Copying ACPI RSDP from %p to %p\n", pos, newpos);
                    266:     memcpy(newpos, pos, length);
                    267:     RsdpAddr = newpos;
                    268: }
                    269: 
                    270: // Attempt to find (and relocate) any standard bios tables found in a
                    271: // given address range.
                    272: static void
                    273: scan_tables(u32 start, u32 size)
                    274: {
                    275:     void *p = (void*)ALIGN(start, 16);
                    276:     void *end = (void*)start + size;
                    277:     for (; p<end; p += 16) {
                    278:         copy_pir(p);
                    279:         copy_mptable(p);
                    280:         copy_acpi_rsdp(p);
                    281:     }
                    282: }
                    283: 
                    284: void
                    285: coreboot_copy_biostable()
                    286: {
                    287:     struct cb_memory *cbm = CBMemTable;
                    288:     if (! CONFIG_COREBOOT || !cbm)
                    289:         return;
                    290: 
                    291:     dprintf(3, "Relocating coreboot bios tables\n");
                    292: 
                    293:     // Init variables set in coreboot table memory scan.
                    294:     PirOffset = 0;
                    295:     RsdpAddr = 0;
                    296: 
                    297:     // Scan CB_MEM_TABLE areas for bios tables.
                    298:     int i, count = MEM_RANGE_COUNT(cbm);
                    299:     for (i=0; i<count; i++) {
                    300:         struct cb_memory_range *m = &cbm->map[i];
                    301:         if (m->type == CB_MEM_TABLE)
                    302:             scan_tables(m->start, m->size);
                    303:     }
                    304: 
                    305:     // XXX - just create dummy smbios table for now - should detect if
                    306:     // smbios/dmi table is found from coreboot and use that instead.
                    307:     smbios_init();
                    308: }
                    309: 
                    310: 
                    311: /****************************************************************
                    312:  * ulzma
                    313:  ****************************************************************/
                    314: 
                    315: // Uncompress data in flash to an area of memory.
                    316: static int
                    317: ulzma(u8 *dst, u32 maxlen, const u8 *src, u32 srclen)
                    318: {
                    319:     dprintf(3, "Uncompressing data %d@%p to %d@%p\n", srclen, src, maxlen, dst);
                    320:     CLzmaDecoderState state;
                    321:     int ret = LzmaDecodeProperties(&state.Properties, src, LZMA_PROPERTIES_SIZE);
                    322:     if (ret != LZMA_RESULT_OK) {
                    323:         dprintf(1, "LzmaDecodeProperties error - %d\n", ret);
                    324:         return -1;
                    325:     }
                    326:     u8 scratch[15980];
                    327:     int need = (LzmaGetNumProbs(&state.Properties) * sizeof(CProb));
                    328:     if (need > sizeof(scratch)) {
                    329:         dprintf(1, "LzmaDecode need %d have %d\n", need, sizeof(scratch));
                    330:         return -1;
                    331:     }
                    332:     state.Probs = (CProb *)scratch;
                    333: 
                    334:     u32 dstlen = *(u32*)(src + LZMA_PROPERTIES_SIZE);
                    335:     if (dstlen > maxlen) {
                    336:         dprintf(1, "LzmaDecode too large (max %d need %d)\n", maxlen, dstlen);
                    337:         return -1;
                    338:     }
                    339:     u32 inProcessed, outProcessed;
                    340:     ret = LzmaDecode(&state, src + LZMA_PROPERTIES_SIZE + 8, srclen
                    341:                      , &inProcessed, dst, dstlen, &outProcessed);
                    342:     if (ret) {
                    343:         dprintf(1, "LzmaDecode returned %d\n", ret);
                    344:         return -1;
                    345:     }
                    346:     return dstlen;
                    347: }
                    348: 
                    349: 
                    350: /****************************************************************
                    351:  * Coreboot flash format
                    352:  ****************************************************************/
                    353: 
                    354: #define CBFS_HEADER_MAGIC 0x4F524243
                    355: #define CBFS_HEADPTR_ADDR 0xFFFFFFFc
                    356: #define CBFS_VERSION1 0x31313131
                    357: 
                    358: struct cbfs_header {
                    359:     u32 magic;
                    360:     u32 version;
                    361:     u32 romsize;
                    362:     u32 bootblocksize;
                    363:     u32 align;
                    364:     u32 offset;
                    365:     u32 pad[2];
                    366: } PACKED;
                    367: 
                    368: static struct cbfs_header *CBHDR;
                    369: 
                    370: static void
                    371: cbfs_setup()
                    372: {
                    373:     if (! CONFIG_COREBOOT_FLASH)
                    374:         return;
                    375: 
                    376:     CBHDR = *(void **)CBFS_HEADPTR_ADDR;
                    377:     if (CBHDR->magic != htonl(CBFS_HEADER_MAGIC)) {
                    378:         dprintf(1, "Unable to find CBFS (got %x not %x)\n"
                    379:                 , CBHDR->magic, htonl(CBFS_HEADER_MAGIC));
                    380:         CBHDR = NULL;
                    381:         return;
                    382:     }
                    383: 
                    384:     dprintf(1, "Found CBFS header at %p\n", CBHDR);
                    385: }
                    386: 
                    387: #define CBFS_FILE_MAGIC 0x455649484352414cLL // LARCHIVE
                    388: 
                    389: struct cbfs_file {
                    390:     u64 magic;
                    391:     u32 len;
                    392:     u32 type;
                    393:     u32 checksum;
                    394:     u32 offset;
                    395:     char filename[0];
                    396: } PACKED;
                    397: 
                    398: // Verify a cbfs entry looks valid.
                    399: static struct cbfs_file *
                    400: cbfs_verify(struct cbfs_file *file)
                    401: {
                    402:     if (file < (struct cbfs_file *)(0xFFFFFFFF - ntohl(CBHDR->romsize)))
                    403:         return NULL;
                    404:     u64 magic = file->magic;
                    405:     if (magic == CBFS_FILE_MAGIC) {
                    406:         dprintf(5, "Found CBFS file %s\n", file->filename);
                    407:         return file;
                    408:     }
                    409:     return NULL;
                    410: }
                    411: 
                    412: // Return the first file in the CBFS archive
                    413: static struct cbfs_file *
                    414: cbfs_getfirst()
                    415: {
                    416:     if (! CBHDR)
                    417:         return NULL;
                    418:     return cbfs_verify((void *)(0 - ntohl(CBHDR->romsize) + ntohl(CBHDR->offset)));
                    419: }
                    420: 
                    421: // Return the file after the given file.
                    422: static struct cbfs_file *
                    423: cbfs_getnext(struct cbfs_file *file)
                    424: {
                    425:     file = (void*)file + ALIGN(ntohl(file->len) + ntohl(file->offset), ntohl(CBHDR->align));
                    426:     return cbfs_verify(file);
                    427: }
                    428: 
                    429: // Find the file with the given filename.
                    430: struct cbfs_file *
                    431: cbfs_findfile(const char *fname)
                    432: {
                    433:     dprintf(3, "Searching CBFS for %s\n", fname);
                    434:     struct cbfs_file *file;
                    435:     for (file = cbfs_getfirst(); file; file = cbfs_getnext(file))
                    436:         if (strcmp(fname, file->filename) == 0)
                    437:             return file;
                    438:     return NULL;
                    439: }
                    440: 
                    441: // Find next file with the given filename prefix.
                    442: struct cbfs_file *
                    443: cbfs_findprefix(const char *prefix, struct cbfs_file *last)
                    444: {
                    445:     if (! CONFIG_COREBOOT_FLASH)
                    446:         return NULL;
                    447: 
                    448:     dprintf(3, "Searching CBFS for prefix %s\n", prefix);
                    449:     int len = strlen(prefix);
                    450:     struct cbfs_file *file;
                    451:     if (! last)
                    452:         file = cbfs_getfirst();
                    453:     else
                    454:         file = cbfs_getnext(last);
                    455:     for (; file; file = cbfs_getnext(file))
                    456:         if (memcmp(prefix, file->filename, len) == 0)
                    457:             return file;
                    458:     return NULL;
                    459: }
                    460: 
                    461: // Find a file with the given filename (possibly with ".lzma" extension).
                    462: static struct cbfs_file *
                    463: cbfs_finddatafile(const char *fname)
                    464: {
                    465:     int fnlen = strlen(fname);
                    466:     struct cbfs_file *file = NULL;
                    467:     for (;;) {
                    468:         file = cbfs_findprefix(fname, file);
                    469:         if (!file)
                    470:             return NULL;
                    471:         if (file->filename[fnlen] == '\0'
                    472:             || strcmp(&file->filename[fnlen], ".lzma") == 0)
                    473:             return file;
                    474:     }
                    475: }
                    476: 
                    477: // Determine whether the file has a ".lzma" extension.
                    478: static int
                    479: cbfs_iscomp(struct cbfs_file *file)
                    480: {
                    481:     int fnamelen = strlen(file->filename);
                    482:     return fnamelen > 5 && strcmp(&file->filename[fnamelen-5], ".lzma") == 0;
                    483: }
                    484: 
                    485: // Return the filename of a given file.
                    486: const char *
                    487: cbfs_filename(struct cbfs_file *file)
                    488: {
                    489:     return file->filename;
                    490: }
                    491: 
                    492: // Determine the uncompressed size of a datafile.
                    493: u32
                    494: cbfs_datasize(struct cbfs_file *file)
                    495: {
                    496:     void *src = (void*)file + ntohl(file->offset);
                    497:     if (cbfs_iscomp(file))
                    498:         return *(u32*)(src + LZMA_PROPERTIES_SIZE);
                    499:     return ntohl(file->len);
                    500: }
                    501: 
                    502: // Copy a file to memory (uncompressing if necessary)
                    503: int
                    504: cbfs_copyfile(struct cbfs_file *file, void *dst, u32 maxlen)
                    505: {
                    506:     if (! CONFIG_COREBOOT_FLASH || !file)
                    507:         return -1;
                    508: 
                    509:     u32 size = ntohl(file->len);
                    510:     void *src = (void*)file + ntohl(file->offset);
                    511:     if (cbfs_iscomp(file)) {
                    512:         // Compressed - copy to temp ram and uncompress it.
                    513:         u32 asize = ALIGN(size, 4);
                    514:         void *temp = malloc_tmphigh(asize);
                    515:         if (!temp)
                    516:             return -1;
                    517:         iomemcpy(temp, src, asize);
                    518:         int ret = ulzma(dst, maxlen, temp, size);
                    519:         yield();
                    520:         free(temp);
                    521:         return ret;
                    522:     }
                    523: 
                    524:     // Not compressed.
                    525:     dprintf(3, "Copying data %d@%p to %d@%p\n", size, src, maxlen, dst);
                    526:     if (size > maxlen) {
                    527:         dprintf(1, "File too big to copy\n");
                    528:         return -1;
                    529:     }
                    530:     iomemcpy(dst, src, size);
                    531:     return size;
                    532: }
                    533: 
                    534: // Find and copy the optionrom for the given vendor/device id.
                    535: int
                    536: cbfs_copy_optionrom(void *dst, u32 maxlen, u32 vendev)
                    537: {
                    538:     if (! CONFIG_COREBOOT_FLASH)
                    539:         return -1;
                    540: 
                    541:     char fname[17];
                    542:     snprintf(fname, sizeof(fname), "pci%04x,%04x.rom"
                    543:              , (u16)vendev, vendev >> 16);
                    544:     return cbfs_copyfile(cbfs_finddatafile(fname), dst, maxlen);
                    545: }
                    546: 
                    547: struct cbfs_payload_segment {
                    548:     u32 type;
                    549:     u32 compression;
                    550:     u32 offset;
                    551:     u64 load_addr;
                    552:     u32 len;
                    553:     u32 mem_len;
                    554: } PACKED;
                    555: 
                    556: #define PAYLOAD_SEGMENT_BSS    0x20535342
                    557: #define PAYLOAD_SEGMENT_ENTRY  0x52544E45
                    558: 
                    559: #define CBFS_COMPRESS_NONE  0
                    560: #define CBFS_COMPRESS_LZMA  1
                    561: 
                    562: struct cbfs_payload {
                    563:     struct cbfs_payload_segment segments[1];
                    564: };
                    565: 
                    566: void
                    567: cbfs_run_payload(struct cbfs_file *file)
                    568: {
                    569:     if (!CONFIG_COREBOOT_FLASH || !file)
                    570:         return;
                    571:     dprintf(1, "Run %s\n", file->filename);
                    572:     struct cbfs_payload *pay = (void*)file + ntohl(file->offset);
                    573:     struct cbfs_payload_segment *seg = pay->segments;
                    574:     for (;;) {
                    575:         void *src = (void*)pay + ntohl(seg->offset);
                    576:         void *dest = (void*)ntohl((u32)seg->load_addr);
                    577:         u32 src_len = ntohl(seg->len);
                    578:         u32 dest_len = ntohl(seg->mem_len);
                    579:         switch (seg->type) {
                    580:         case PAYLOAD_SEGMENT_BSS:
                    581:             dprintf(3, "BSS segment %d@%p\n", dest_len, dest);
                    582:             memset(dest, 0, dest_len);
                    583:             break;
                    584:         case PAYLOAD_SEGMENT_ENTRY: {
                    585:             dprintf(1, "Calling addr %p\n", dest);
                    586:             void (*func)() = dest;
                    587:             func();
                    588:             return;
                    589:         }
                    590:         default:
                    591:             dprintf(3, "Segment %x %d@%p -> %d@%p\n"
                    592:                     , seg->type, src_len, src, dest_len, dest);
                    593:             if (seg->compression == htonl(CBFS_COMPRESS_NONE)) {
                    594:                 if (src_len > dest_len)
                    595:                     src_len = dest_len;
                    596:                 memcpy(dest, src, src_len);
                    597:             } else if (CONFIG_LZMA
                    598:                        && seg->compression == htonl(CBFS_COMPRESS_LZMA)) {
                    599:                 int ret = ulzma(dest, dest_len, src, src_len);
                    600:                 if (ret < 0)
                    601:                     return;
                    602:                 src_len = ret;
                    603:             } else {
                    604:                 dprintf(1, "No support for compression type %x\n"
                    605:                         , seg->compression);
                    606:                 return;
                    607:             }
                    608:             if (dest_len > src_len)
                    609:                 memset(dest + src_len, 0, dest_len - src_len);
                    610:             break;
                    611:         }
                    612:         seg++;
                    613:     }
                    614: }
                    615: 
                    616: void
                    617: coreboot_setup(void)
                    618: {
                    619:     coreboot_fill_map();
                    620:     cbfs_setup();
                    621: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.