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