1/* $NetBSD: main.c,v 1.12 2018/08/18 06:52:57 kre Exp $ */ 2 3/*- 4 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 5 * Copyright (c) 1998,2000 Doug Rabson <dfr@freebsd.org> 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30#include <sys/cdefs.h> 31 32#include <lib/libsa/stand.h> 33#include <lib/libsa/loadfile.h> 34 35#include <machine/sal.h> 36#include <machine/pal.h> 37#include <machine/pte.h> 38#include <machine/dig64.h> 39 40#include <efi.h> 41#include <efilib.h> 42#include <efifsdev.h> 43 44#include <machine/efilib.h> 45 46#include "bootstrap.h" 47#include "efiboot.h" 48 49extern char bootprog_name[]; 50extern char bootprog_rev[]; 51 52struct efi_devdesc currdev; /* our current device */ 53struct arch_switch archsw; /* MI/MD interface boundary */ 54 55vaddr_t ia64_unwindtab; 56vsize_t ia64_unwindtablen; 57 58extern u_int64_t ia64_pal_entry; 59 60EFI_GUID acpi = ACPI_TABLE_GUID; 61EFI_GUID acpi20 = ACPI_20_TABLE_GUID; 62EFI_GUID devid = DEVICE_PATH_PROTOCOL; 63EFI_GUID hcdp = HCDP_TABLE_GUID; 64EFI_GUID imgid = LOADED_IMAGE_PROTOCOL; 65EFI_GUID mps = MPS_TABLE_GUID; 66EFI_GUID netid = EFI_SIMPLE_NETWORK_PROTOCOL_GUID; 67EFI_GUID sal = SAL_SYSTEM_TABLE_GUID; 68EFI_GUID smbios = SMBIOS_TABLE_GUID; 69 70static void 71find_pal_proc(void) 72{ 73 int i; 74 struct sal_system_table *saltab = 0; 75 static int sizes[6] = { 76 48, 32, 16, 32, 16, 16 77 }; 78 u_int8_t *p; 79 80 saltab = efi_get_table(&sal); 81 if (saltab == NULL) { 82 printf("Can't find SAL System Table\n"); 83 return; 84 } 85 86 if (memcmp(saltab->sal_signature, "SST_", 4)) { 87 printf("Bad signature for SAL System Table\n"); 88 return; 89 } 90 91 p = (u_int8_t *) (saltab + 1); 92 for (i = 0; i < saltab->sal_entry_count; i++) { 93 if (*p == 0) { 94 struct sal_entrypoint_descriptor *dp; 95 dp = (struct sal_entrypoint_descriptor *) p; 96 ia64_pal_entry = dp->sale_pal_proc; 97 return; 98 } 99 p += sizes[*p]; 100 } 101 102 printf("Can't find PAL proc\n"); 103 return; 104} 105 106EFI_STATUS 107main(int argc, CHAR16 *argv[]) 108{ 109 EFI_LOADED_IMAGE *img; 110 int i; 111 112 /* 113 * XXX Chicken-and-egg problem; we want to have console output 114 * early, but some console attributes may depend on reading from 115 * eg. the boot device, which we can't do yet. We can use 116 * printf() etc. once this is done. 117 */ 118 cons_probe(); 119 120 /* 121 * Initialise the block cache 122 */ 123 /* bcache_init(32, 512); */ /* 16k XXX tune this */ 124 125 find_pal_proc(); 126 127 efifs_dev_init(); 128 129 efinet_init_driver(); 130 131 /* Get our loaded image protocol interface structure. */ 132 BS->HandleProtocol(IH, &imgid, (VOID**)&img); 133 134 printf("Image base: 0x%lx\n", (u_long)img->ImageBase); 135 136 printf("\n"); 137 printf("%s, Revision %s\n", bootprog_name, bootprog_rev); 138 139 i = efifs_get_unit(img->DeviceHandle); 140 if (i >= 0) { 141 currdev.d_dev = &devsw[0]; /* XXX disk */ 142 currdev.d_kind.efidisk.unit = i; 143 /* XXX should be able to detect this, default to autoprobe */ 144 currdev.d_kind.efidisk.slice = -1; 145 currdev.d_kind.efidisk.partition = 0; 146 currdev.d_type = DEVT_DISK; 147 } else { 148 currdev.d_dev = &devsw[1]; /* XXX net */ 149 currdev.d_kind.netif.unit = 0; /* XXX */ 150 currdev.d_type = DEVT_NET; 151 152 /* XXX overwrite disk ops with nfs ops */ 153 memcpy(&file_system[0], &file_system[1], sizeof(struct fs_ops)); 154 } 155 156 157 /* 158 * Disable the watchdog timer. By default the boot manager sets 159 * the timer to 5 minutes before invoking a boot option. If we 160 * want to return to the boot manager, we have to disable the 161 * watchdog timer and since we're an interactive program, we don't 162 * want to wait until the user types "quit". The timer may have 163 * fired by then. We don't care if this fails. It does not prevent 164 * normal functioning in any way... 165 */ 166 BS->SetWatchdogTimer(0, 0, 0, NULL); 167 168 env_setenv("currdev", EV_VOLATILE, efi_fmtdev(&currdev), 169 (ev_sethook_t *) efi_setcurrdev, env_nounset); 170 env_setenv("loaddev", EV_VOLATILE, efi_fmtdev(&currdev), env_noset, 171 env_nounset); 172 173 setenv("LINES", "24", 1); /* optional */ 174 175 archsw.arch_autoload = efi_autoload; 176 archsw.arch_getdev = efi_getdev; 177 archsw.arch_copyin = efi_copyin; 178 archsw.arch_copyout = efi_copyout; 179 archsw.arch_readin = efi_readin; 180 181 interact(); /* doesn't return */ 182 183 return (EFI_SUCCESS); /* keep compiler happy */ 184} 185 186COMMAND_SET(quit, "quit", "exit the loader", command_quit); 187 188static int 189command_quit(int argc, char *argv[]) 190{ 191 exit(0); 192 return (CMD_OK); 193} 194 195COMMAND_SET(memmap, "memmap", "print memory map", command_memmap); 196 197static int 198command_memmap(int argc, char *argv[]) 199{ 200 UINTN sz; 201 EFI_MEMORY_DESCRIPTOR *map, *p; 202 UINTN key, dsz; 203 UINT32 dver; 204 EFI_STATUS status; 205 int i, ndesc; 206 static char *types[] = { 207 "Reserved", 208 "LoaderCode", 209 "LoaderData", 210 "BootServicesCode", 211 "BootServicesData", 212 "RuntimeServicesCode", 213 "RuntimeServicesData", 214 "ConventionalMemory", 215 "UnusableMemory", 216 "ACPIReclaimMemory", 217 "ACPIMemoryNVS", 218 "MemoryMappedIO", 219 "MemoryMappedIOPortSpace", 220 "PalCode" 221 }; 222 223 sz = 0; 224 status = BS->GetMemoryMap(&sz, 0, &key, &dsz, &dver); 225 if (status != EFI_BUFFER_TOO_SMALL) { 226 printf("Can't determine memory map size\n"); 227 return CMD_ERROR; 228 } 229 map = alloc(sz); 230 status = BS->GetMemoryMap(&sz, map, &key, &dsz, &dver); 231 if (EFI_ERROR(status)) { 232 printf("Can't read memory map\n"); 233 return CMD_ERROR; 234 } 235 236 ndesc = sz / dsz; 237 printf("%23s %12s %12s %8s %4s\n", 238 "Type", "Physical", "Virtual", "#Pages", "Attr"); 239 240 for (i = 0, p = map; i < ndesc; 241 i++, p = NextMemoryDescriptor(p, dsz)) { 242 printf("%23s %012lx %012lx %08lx ", 243 types[p->Type], 244 p->PhysicalStart, 245 p->VirtualStart, 246 p->NumberOfPages); 247 if (p->Attribute & EFI_MEMORY_UC) 248 printf("UC "); 249 if (p->Attribute & EFI_MEMORY_WC) 250 printf("WC "); 251 if (p->Attribute & EFI_MEMORY_WT) 252 printf("WT "); 253 if (p->Attribute & EFI_MEMORY_WB) 254 printf("WB "); 255 if (p->Attribute & EFI_MEMORY_UCE) 256 printf("UCE "); 257 if (p->Attribute & EFI_MEMORY_WP) 258 printf("WP "); 259 if (p->Attribute & EFI_MEMORY_RP) 260 printf("RP "); 261 if (p->Attribute & EFI_MEMORY_XP) 262 printf("XP "); 263 if (p->Attribute & EFI_MEMORY_RUNTIME) 264 printf("RUNTIME"); 265 printf("\n"); 266 } 267 268 return CMD_OK; 269} 270 271COMMAND_SET(configuration, "configuration", 272 "print configuration tables", command_configuration); 273 274static const char * 275guid_to_string(EFI_GUID *guid) 276{ 277 static char buf[40]; 278 279 snprintf(buf, sizeof(buf), 280 "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", 281 guid->Data1, guid->Data2, guid->Data3, guid->Data4[0], 282 guid->Data4[1], guid->Data4[2], guid->Data4[3], guid->Data4[4], 283 guid->Data4[5], guid->Data4[6], guid->Data4[7]); 284 return (buf); 285} 286 287static int 288command_configuration(int argc, char *argv[]) 289{ 290 int i; 291 292 printf("NumberOfTableEntries=%ld\n", ST->NumberOfTableEntries); 293 for (i = 0; i < ST->NumberOfTableEntries; i++) { 294 EFI_GUID *guid; 295 296 printf(" "); 297 guid = &ST->ConfigurationTable[i].VendorGuid; 298 if (!memcmp(guid, &mps, sizeof(EFI_GUID))) 299 printf("MPS Table"); 300 else if (!memcmp(guid, &acpi, sizeof(EFI_GUID))) 301 printf("ACPI Table"); 302 else if (!memcmp(guid, &acpi20, sizeof(EFI_GUID))) 303 printf("ACPI 2.0 Table"); 304 else if (!memcmp(guid, &smbios, sizeof(EFI_GUID))) 305 printf("SMBIOS Table"); 306 else if (!memcmp(guid, &sal, sizeof(EFI_GUID))) 307 printf("SAL System Table"); 308 else if (!memcmp(guid, &hcdp, sizeof(EFI_GUID))) 309 printf("DIG64 HCDP Table"); 310 else 311 printf("Unknown Table (%s)", guid_to_string(guid)); 312 printf(" at %p\n", ST->ConfigurationTable[i].VendorTable); 313 } 314 315 return CMD_OK; 316} 317 318COMMAND_SET(sal, "sal", "print SAL System Table", command_sal); 319 320static int 321command_sal(int argc, char *argv[]) 322{ 323 int i; 324 struct sal_system_table *saltab = 0; 325 static int sizes[6] = { 326 48, 32, 16, 32, 16, 16 327 }; 328 u_int8_t *p; 329 330 saltab = efi_get_table(&sal); 331 if (saltab == NULL) { 332 printf("Can't find SAL System Table\n"); 333 return CMD_ERROR; 334 } 335 336 if (memcmp(saltab->sal_signature, "SST_", 4)) { 337 printf("Bad signature for SAL System Table\n"); 338 return CMD_ERROR; 339 } 340 341 printf("SAL Revision %x.%02x\n", 342 saltab->sal_rev[1], 343 saltab->sal_rev[0]); 344 printf("SAL A Version %x.%02x\n", 345 saltab->sal_a_version[1], 346 saltab->sal_a_version[0]); 347 printf("SAL B Version %x.%02x\n", 348 saltab->sal_b_version[1], 349 saltab->sal_b_version[0]); 350 351 p = (u_int8_t *) (saltab + 1); 352 for (i = 0; i < saltab->sal_entry_count; i++) { 353 printf(" Desc %d", *p); 354 if (*p == 0) { 355 struct sal_entrypoint_descriptor *dp; 356 dp = (struct sal_entrypoint_descriptor *) p; 357 printf("\n"); 358 printf(" PAL Proc at 0x%lx\n", 359 dp->sale_pal_proc); 360 printf(" SAL Proc at 0x%lx\n", 361 dp->sale_sal_proc); 362 printf(" SAL GP at 0x%lx\n", 363 dp->sale_sal_gp); 364 } else if (*p == 1) { 365 struct sal_memory_descriptor *dp; 366 dp = (struct sal_memory_descriptor *) p; 367 printf(" Type %d.%d, ", 368 dp->sale_memory_type[0], 369 dp->sale_memory_type[1]); 370 printf("Address 0x%lx, ", 371 dp->sale_physical_address); 372 printf("Length 0x%x\n", 373 dp->sale_length); 374 } else if (*p == 5) { 375 struct sal_ap_wakeup_descriptor *dp; 376 dp = (struct sal_ap_wakeup_descriptor *) p; 377 printf("\n"); 378 printf(" Mechanism %d\n", dp->sale_mechanism); 379 printf(" Vector 0x%lx\n", dp->sale_vector); 380 } else 381 printf("\n"); 382 383 p += sizes[*p]; 384 } 385 386 return CMD_OK; 387} 388 389int 390print_trs(int type) 391{ 392 struct ia64_pal_result res; 393 int i, maxtr; 394 struct { 395 pt_entry_t pte; 396 uint64_t itir; 397 uint64_t ifa; 398 struct ia64_rr rr; 399 } buf; 400 static const char *psnames[] = { 401 "1B", "2B", "4B", "8B", 402 "16B", "32B", "64B", "128B", 403 "256B", "512B", "1K", "2K", 404 "4K", "8K", "16K", "32K", 405 "64K", "128K", "256K", "512K", 406 "1M", "2M", "4M", "8M", 407 "16M", "32M", "64M", "128M", 408 "256M", "512M", "1G", "2G" 409 }; 410 static const char *manames[] = { 411 "WB", "bad", "bad", "bad", 412 "UC", "UCE", "WC", "NaT", 413 }; 414 415 res = ia64_call_pal_static(PAL_VM_SUMMARY, 0, 0, 0); 416 if (res.pal_status != 0) { 417 printf("Can't get VM summary\n"); 418 return CMD_ERROR; 419 } 420 421 if (type == 0) 422 maxtr = (res.pal_result[0] >> 40) & 0xff; 423 else 424 maxtr = (res.pal_result[0] >> 32) & 0xff; 425 426 printf("%d translation registers\n", maxtr); 427 428 pager_open(); 429 pager_output("TR# RID Virtual Page Physical Page PgSz ED AR PL D A MA P KEY\n"); 430 for (i = 0; i <= maxtr; i++) { 431 char lbuf[128]; 432 433 memset(&buf, 0, sizeof(buf)); 434 res = ia64_call_pal_stacked(PAL_VM_TR_READ, i, type, 435 (u_int64_t) &buf); 436 if (res.pal_status != 0) 437 break; 438 439 /* Only display valid translations */ 440 if ((buf.ifa & 1) == 0) 441 continue; 442 443 if (!(res.pal_result[0] & 1)) 444 buf.pte &= ~PTE_AR_MASK; 445 if (!(res.pal_result[0] & 2)) 446 buf.pte &= ~PTE_PL_MASK; 447 if (!(res.pal_result[0] & 4)) 448 buf.pte &= ~PTE_DIRTY; 449 if (!(res.pal_result[0] & 8)) 450 buf.pte &= ~PTE_MA_MASK; 451 snprintf(lbuf, sizeof(lbuf), 452 "%03d %06x %013lx %013lx %4s %d %d %d %d %d " 453 "%-3s %d %06x\n", i, buf.rr.rr_rid, buf.ifa >> 12, 454 (buf.pte & PTE_PPN_MASK) >> 12, 455 psnames[(buf.itir & ITIR_PS_MASK) >> 2], 456 (buf.pte & PTE_ED) ? 1 : 0, 457 (int)(buf.pte & PTE_AR_MASK) >> 9, 458 (int)(buf.pte & PTE_PL_MASK) >> 7, 459 (buf.pte & PTE_DIRTY) ? 1 : 0, 460 (buf.pte & PTE_ACCESSED) ? 1 : 0, 461 manames[(buf.pte & PTE_MA_MASK) >> 2], 462 (buf.pte & PTE_PRESENT) ? 1 : 0, 463 (int)((buf.itir & ITIR_KEY_MASK) >> 8)); 464 pager_output(lbuf); 465 } 466 pager_close(); 467 468 if (res.pal_status != 0) { 469 printf("Error while getting TR contents\n"); 470 return CMD_ERROR; 471 } 472 return CMD_OK; 473} 474 475COMMAND_SET(itr, "itr", "print instruction TRs", command_itr); 476 477static int 478command_itr(int argc, char *argv[]) 479{ 480 return print_trs(0); 481} 482 483COMMAND_SET(dtr, "dtr", "print data TRs", command_dtr); 484 485static int 486command_dtr(int argc, char *argv[]) 487{ 488 return print_trs(1); 489} 490 491COMMAND_SET(hcdp, "hcdp", "Dump HCDP info", command_hcdp); 492 493static char * 494hcdp_string(char *s, u_int len) 495{ 496 static char buffer[256]; 497 498 memcpy(buffer, s, len); 499 buffer[len] = 0; 500 return (buffer); 501} 502 503static int 504command_hcdp(int argc, char *argv[]) 505{ 506 struct dig64_hcdp_table *tbl; 507 union dev_desc *desc; 508 int i, m, n; 509 510 tbl = efi_get_table(&hcdp); 511 if (tbl == NULL) { 512 printf("No HCDP table present\n"); 513 return (CMD_OK); 514 } 515 if (memcmp(tbl->signature, HCDP_SIGNATURE, sizeof(tbl->signature))) { 516 printf("HCDP table has invalid signature\n"); 517 return (CMD_OK); 518 } 519 printf("HCDP table at 0x%lx\n", (u_long)tbl); 520 printf("Signature = %s\n", hcdp_string(tbl->signature, 4)); 521 printf("Length = %u\n", tbl->length); 522 printf("Revision = %u\n", tbl->revision); 523 printf("Checksum = %u\n", tbl->checksum); 524 printf("OEM Id = %s\n", hcdp_string(tbl->oem_id, 6)); 525 printf("Table Id = %s\n", hcdp_string(tbl->oem_tbl_id, 8)); 526 printf("OEM rev = %u\n", tbl->oem_rev); 527 printf("Creator Id = %s\n", hcdp_string(tbl->creator_id, 4)); 528 printf("Creator rev= %u\n", tbl->creator_rev); 529 printf("Entries = %u\n", tbl->entries); 530 n = 0; 531 m = tbl->length - sizeof(struct dig64_hcdp_table); 532 i = 1; 533 while (n < m) { 534 printf("Entry #%d:\n", i); 535 desc = (union dev_desc *)((char *)tbl->entry + n); 536 printf(" Type = %u\n", desc->type); 537 if (desc->type == DIG64_ENTRYTYPE_TYPE0 || 538 desc->type == DIG64_ENTRYTYPE_TYPE1) { 539 struct dig64_hcdp_entry *ent = &desc->uart; 540 struct dig64_gas *gas; 541 printf(" Databits = %u\n", ent->databits); 542 printf(" Parity = %u\n", ent->parity); 543 printf(" Stopbits = %u\n", ent->stopbits); 544 printf(" PCI seg = %u\n", ent->pci_segment); 545 printf(" PCI bus = %u\n", ent->pci_bus); 546 printf(" PCI dev = %u\n", ent->pci_device); 547 printf(" PCI func = %u\n", ent->pci_function); 548 printf(" Interrupt = %u\n", ent->interrupt); 549 printf(" PCI flag = %u\n", ent->pci_flag); 550 printf(" Baudrate = %lu\n", 551 ((u_long)ent->baud_high << 32) + 552 (u_long)ent->baud_low); 553 gas = &ent->address; 554 printf(" Addr space= %u\n", gas->addr_space); 555 printf(" Bit width = %u\n", gas->bit_width); 556 printf(" Bit offset= %u\n", gas->bit_offset); 557 printf(" Address = 0x%lx\n", 558 ((u_long)gas->addr_high << 32) + 559 (u_long)gas->addr_low); 560 printf(" PCI type = %u\n", ent->pci_devid); 561 printf(" PCI vndr = %u\n", ent->pci_vendor); 562 printf(" IRQ = %u\n", ent->irq); 563 printf(" PClock = %u\n", ent->pclock); 564 printf(" PCI iface = %u\n", ent->pci_interface); 565 566 n += sizeof(struct dig64_hcdp_entry); 567 } else { 568 struct dig64_pcdp_entry *pcdp = &desc->pcdp; 569 570 if (tbl->revision < 3) { 571 printf("PCDP not support\n"); 572 return (CMD_OK); 573 } 574 575 printf(" Length = %u\n", pcdp->length); 576 printf(" Index EFI = %u\n", pcdp->index); 577 printf(" Interconn = %u", pcdp->specs.type); 578 579 switch (pcdp->specs.type) { 580 case DIG64_PCDP_SPEC_ACPI: 581 { 582 struct dig64_acpi_spec *acpi = 583 &pcdp->specs.acpi; 584 585 printf("(ACPI)\n"); 586 printf(" Length = %u\n", acpi->length); 587 printf(" ACPI_UID = %x\n", acpi->uid); 588 printf(" ACPI_HID = %x\n", acpi->hid); 589 printf(" ACPI GSI = %x\n", acpi->acpi_gsi); 590 printf(" MMIO_TRA = %lx\n", acpi->mmio_tra); 591 printf(" IOPort_TRA= %lx\n", 592 acpi->ioport_tra); 593 printf(" Flags = %x\n", acpi->flags); 594 break; 595 } 596 case DIG64_PCDP_SPEC_PCI: 597 { 598 struct dig64_pci_spec *pci = &pcdp->specs.pci; 599 600 printf("(PCI)\n"); 601 printf(" Length = %u\n", pci->length); 602 printf(" Seg GrpNum= %u\n", pci->sgn); 603 printf(" Bus = %u\n", pci->bus); 604 printf(" Device = %u\n", pci->device); 605 printf(" Function = %u\n", pci->function); 606 printf(" Device ID = %u\n", pci->device_id); 607 printf(" Vendor ID = %u\n", pci->vendor_id); 608 printf(" ACPI GSI = %x\n", pci->acpi_gsi); 609 printf(" MMIO_TRA = %lx\n", pci->mmio_tra); 610 printf(" IOPort_TRA= %lx\n", 611 pci->ioport_tra); 612 printf(" Flags = %x\n", pci->flags); 613 break; 614 } 615 } 616 617 n += pcdp->length; 618 } 619 } 620 printf("<EOT>\n"); 621 return (CMD_OK); 622} 623 624struct bootblk_command commands[] = { 625 COMMON_COMMANDS, 626 { "quit", "exit the loader", command_quit }, 627 { "memmap", "print memory map", command_memmap }, 628 { "configuration", "print configuration tables", command_configuration }, 629 { "sal", "print SAL System Table", command_sal }, 630 { "itr", "print instruction TRs", command_itr }, 631 { "dtr", "print data TRs", command_dtr }, 632 { "hcdp", "Dump HCDP info", command_hcdp }, 633 { NULL, NULL, NULL }, 634}; 635