1 /* 2 * Copyright 2003-2010, Axel Dörfler, axeld@pinc-software.de. 3 * Copyright 2011, Rene Gollent, rene@gollent.com. 4 * Distributed under the terms of the MIT License. 5 */ 6 7 8 #include "menu.h" 9 10 #include <errno.h> 11 #include <string.h> 12 13 #include <algorithm> 14 15 #include <OS.h> 16 17 #include <boot/menu.h> 18 #include <boot/stage2.h> 19 #include <boot/vfs.h> 20 #include <boot/platform.h> 21 #include <boot/platform/generic/text_console.h> 22 #include <boot/stdio.h> 23 #include <safemode.h> 24 #include <util/kernel_cpp.h> 25 #include <util/ring_buffer.h> 26 27 #include "kernel_debug_config.h" 28 29 #include "load_driver_settings.h" 30 #include "loader.h" 31 #include "pager.h" 32 #include "RootFileSystem.h" 33 34 35 #define TRACE_MENU 36 #ifdef TRACE_MENU 37 # define TRACE(x) dprintf x 38 #else 39 # define TRACE(x) ; 40 #endif 41 42 43 static char sSafeModeOptionsBuffer[2048]; 44 45 46 MenuItem::MenuItem(const char *label, Menu *subMenu) 47 : 48 fLabel(strdup(label)), 49 fTarget(NULL), 50 fIsMarked(false), 51 fIsSelected(false), 52 fIsEnabled(true), 53 fType(MENU_ITEM_STANDARD), 54 fMenu(NULL), 55 fSubMenu(subMenu), 56 fData(NULL), 57 fHelpText(NULL), 58 fShortcut(0) 59 { 60 if (subMenu != NULL) 61 subMenu->fSuperItem = this; 62 } 63 64 65 MenuItem::~MenuItem() 66 { 67 if (fSubMenu != NULL) 68 fSubMenu->fSuperItem = NULL; 69 70 free(const_cast<char *>(fLabel)); 71 } 72 73 74 void 75 MenuItem::SetTarget(menu_item_hook target) 76 { 77 fTarget = target; 78 } 79 80 81 /** Marks or unmarks a menu item. A marked menu item usually gets a visual 82 * clue like a checkmark that distinguishes it from others. 83 * For menus of type CHOICE_MENU, there can only be one marked item - the 84 * chosen one. 85 */ 86 87 void 88 MenuItem::SetMarked(bool marked) 89 { 90 if (marked && fMenu != NULL && fMenu->Type() == CHOICE_MENU) { 91 // always set choice text of parent if we were marked 92 fMenu->SetChoiceText(Label()); 93 } 94 95 if (fIsMarked == marked) 96 return; 97 98 if (marked && fMenu != NULL && fMenu->Type() == CHOICE_MENU) { 99 // unmark previous item 100 MenuItem *markedItem = fMenu->FindMarked(); 101 if (markedItem != NULL) 102 markedItem->SetMarked(false); 103 } 104 105 fIsMarked = marked; 106 107 if (fMenu != NULL) 108 fMenu->Draw(this); 109 } 110 111 112 void 113 MenuItem::Select(bool selected) 114 { 115 if (fIsSelected == selected) 116 return; 117 118 if (selected && fMenu != NULL) { 119 // unselect previous item 120 MenuItem *selectedItem = fMenu->FindSelected(); 121 if (selectedItem != NULL) 122 selectedItem->Select(false); 123 } 124 125 fIsSelected = selected; 126 127 if (fMenu != NULL) 128 fMenu->Draw(this); 129 } 130 131 132 void 133 MenuItem::SetType(menu_item_type type) 134 { 135 fType = type; 136 } 137 138 139 void 140 MenuItem::SetEnabled(bool enabled) 141 { 142 if (fIsEnabled == enabled) 143 return; 144 145 fIsEnabled = enabled; 146 147 if (fMenu != NULL) 148 fMenu->Draw(this); 149 } 150 151 152 void 153 MenuItem::SetData(const void *data) 154 { 155 fData = data; 156 } 157 158 159 /*! This sets a help text that is shown when the item is 160 selected. 161 Note, unlike the label, the string is not copied, it's 162 just referenced and has to stay valid as long as the 163 item's menu is being used. 164 */ 165 void 166 MenuItem::SetHelpText(const char* text) 167 { 168 fHelpText = text; 169 } 170 171 172 void 173 MenuItem::SetShortcut(char key) 174 { 175 fShortcut = key; 176 } 177 178 179 void 180 MenuItem::SetMenu(Menu* menu) 181 { 182 fMenu = menu; 183 } 184 185 186 // #pragma mark - 187 188 189 Menu::Menu(menu_type type, const char* title) 190 : 191 fTitle(title), 192 fChoiceText(NULL), 193 fCount(0), 194 fIsHidden(true), 195 fType(type), 196 fSuperItem(NULL), 197 fShortcuts(NULL) 198 { 199 } 200 201 202 Menu::~Menu() 203 { 204 // take all remaining items with us 205 206 MenuItem *item; 207 while ((item = fItems.Head()) != NULL) { 208 fItems.Remove(item); 209 delete item; 210 } 211 } 212 213 214 MenuItem* 215 Menu::ItemAt(int32 index) 216 { 217 if (index < 0 || index >= fCount) 218 return NULL; 219 220 MenuItemIterator iterator = ItemIterator(); 221 MenuItem *item; 222 223 while ((item = iterator.Next()) != NULL) { 224 if (index-- == 0) 225 return item; 226 } 227 228 return NULL; 229 } 230 231 232 int32 233 Menu::IndexOf(MenuItem* searchedItem) 234 { 235 int32 index = 0; 236 237 MenuItemIterator iterator = ItemIterator(); 238 while (MenuItem* item = iterator.Next()) { 239 if (item == searchedItem) 240 return index; 241 242 index++; 243 } 244 245 return -1; 246 } 247 248 249 int32 250 Menu::CountItems() const 251 { 252 return fCount; 253 } 254 255 256 MenuItem* 257 Menu::FindItem(const char* label) 258 { 259 MenuItemIterator iterator = ItemIterator(); 260 while (MenuItem* item = iterator.Next()) { 261 if (item->Label() != NULL && !strcmp(item->Label(), label)) 262 return item; 263 } 264 265 return NULL; 266 } 267 268 269 MenuItem* 270 Menu::FindMarked() 271 { 272 MenuItemIterator iterator = ItemIterator(); 273 while (MenuItem* item = iterator.Next()) { 274 if (item->IsMarked()) 275 return item; 276 } 277 278 return NULL; 279 } 280 281 282 MenuItem* 283 Menu::FindSelected(int32* _index) 284 { 285 int32 index = 0; 286 287 MenuItemIterator iterator = ItemIterator(); 288 while (MenuItem* item = iterator.Next()) { 289 if (item->IsSelected()) { 290 if (_index != NULL) 291 *_index = index; 292 return item; 293 } 294 295 index++; 296 } 297 298 return NULL; 299 } 300 301 302 void 303 Menu::AddItem(MenuItem* item) 304 { 305 item->fMenu = this; 306 fItems.Add(item); 307 fCount++; 308 } 309 310 311 status_t 312 Menu::AddSeparatorItem() 313 { 314 MenuItem* item = new(std::nothrow) MenuItem(); 315 if (item == NULL) 316 return B_NO_MEMORY; 317 318 item->SetType(MENU_ITEM_SEPARATOR); 319 320 AddItem(item); 321 return B_OK; 322 } 323 324 325 MenuItem* 326 Menu::RemoveItemAt(int32 index) 327 { 328 if (index < 0 || index >= fCount) 329 return NULL; 330 331 MenuItemIterator iterator = ItemIterator(); 332 while (MenuItem* item = iterator.Next()) { 333 if (index-- == 0) { 334 RemoveItem(item); 335 return item; 336 } 337 } 338 339 return NULL; 340 } 341 342 343 void 344 Menu::RemoveItem(MenuItem* item) 345 { 346 item->fMenu = NULL; 347 fItems.Remove(item); 348 fCount--; 349 } 350 351 352 void 353 Menu::AddShortcut(char key, shortcut_hook function) 354 { 355 Menu::shortcut* shortcut = new(std::nothrow) Menu::shortcut; 356 if (shortcut == NULL) 357 return; 358 359 shortcut->key = key; 360 shortcut->function = function; 361 362 shortcut->next = fShortcuts; 363 fShortcuts = shortcut; 364 } 365 366 367 shortcut_hook 368 Menu::FindShortcut(char key) const 369 { 370 if (key == 0) 371 return NULL; 372 373 const Menu::shortcut* shortcut = fShortcuts; 374 while (shortcut != NULL) { 375 if (shortcut->key == key) 376 return shortcut->function; 377 378 shortcut = shortcut->next; 379 } 380 381 return NULL; 382 } 383 384 385 MenuItem* 386 Menu::FindItemByShortcut(char key) 387 { 388 if (key == 0) 389 return NULL; 390 391 MenuItemList::Iterator iterator = ItemIterator(); 392 while (MenuItem* item = iterator.Next()) { 393 if (item->Shortcut() == key) 394 return item; 395 } 396 397 return NULL; 398 } 399 400 401 void 402 Menu::Run() 403 { 404 platform_run_menu(this); 405 } 406 407 408 void 409 Menu::Draw(MenuItem* item) 410 { 411 if (!IsHidden()) 412 platform_update_menu_item(this, item); 413 } 414 415 416 // #pragma mark - 417 418 419 static const char* 420 size_to_string(off_t size, char* buffer, size_t bufferSize) 421 { 422 static const char* const kPrefixes[] = { "K", "M", "G", "T", "P", NULL }; 423 int32 nextIndex = 0; 424 int32 remainder = 0; 425 while (size >= 1024 && kPrefixes[nextIndex] != NULL) { 426 remainder = size % 1024; 427 size /= 1024; 428 nextIndex++; 429 430 if (size < 1024) { 431 // Compute the decimal remainder and make sure we have at most 432 // 3 decimal places (or 4 for 1000 <= size <= 1023). 433 int32 factor; 434 if (size >= 100) 435 factor = 100; 436 else if (size >= 10) 437 factor = 10; 438 else 439 factor = 1; 440 441 remainder = (remainder * 1000 + 5 * factor) / 1024; 442 443 if (remainder >= 1000) { 444 size++; 445 remainder = 0; 446 } else 447 remainder /= 10 * factor; 448 } else 449 size += (remainder + 512) / 1024; 450 } 451 452 if (remainder == 0) { 453 snprintf(buffer, bufferSize, "%" B_PRIdOFF, size); 454 } else { 455 snprintf(buffer, bufferSize, "%" B_PRIdOFF ".%" B_PRId32, size, 456 remainder); 457 } 458 459 size_t length = strlen(buffer); 460 snprintf(buffer + length, bufferSize - length, " %sB", 461 nextIndex == 0 ? "" : kPrefixes[nextIndex - 1]); 462 463 return buffer; 464 } 465 466 467 // #pragma mark - 468 469 470 static bool 471 user_menu_boot_volume(Menu* menu, MenuItem* item) 472 { 473 Menu* super = menu->Supermenu(); 474 if (super == NULL) { 475 // huh? 476 return true; 477 } 478 479 MenuItem *bootItem = super->ItemAt(super->CountItems() - 1); 480 bootItem->SetEnabled(true); 481 bootItem->Select(true); 482 bootItem->SetData(item->Data()); 483 484 gKernelArgs.boot_volume.SetBool(BOOT_VOLUME_USER_SELECTED, true); 485 return true; 486 } 487 488 489 static bool 490 debug_menu_display_current_log(Menu* menu, MenuItem* item) 491 { 492 // get the buffer 493 size_t bufferSize; 494 const char* buffer = platform_debug_get_log_buffer(&bufferSize); 495 if (buffer == NULL || bufferSize == 0) 496 return true; 497 498 struct TextSource : PagerTextSource { 499 TextSource(const char* buffer, size_t size) 500 : 501 fBuffer(buffer), 502 fSize(strnlen(buffer, size)) 503 { 504 } 505 506 virtual size_t BytesAvailable() const 507 { 508 return fSize; 509 } 510 511 virtual size_t Read(size_t offset, void* buffer, size_t size) const 512 { 513 if (offset >= fSize) 514 return 0; 515 516 if (size > fSize - offset) 517 size = fSize - offset; 518 519 memcpy(buffer, fBuffer + offset, size); 520 return size; 521 } 522 523 private: 524 const char* fBuffer; 525 size_t fSize; 526 }; 527 528 pager(TextSource(buffer, bufferSize)); 529 530 return true; 531 } 532 533 534 static bool 535 debug_menu_display_previous_syslog(Menu* menu, MenuItem* item) 536 { 537 ring_buffer* buffer = (ring_buffer*)gKernelArgs.debug_output; 538 if (buffer == NULL) 539 return true; 540 541 struct TextSource : PagerTextSource { 542 TextSource(ring_buffer* buffer) 543 : 544 fBuffer(buffer) 545 { 546 } 547 548 virtual size_t BytesAvailable() const 549 { 550 return ring_buffer_readable(fBuffer); 551 } 552 553 virtual size_t Read(size_t offset, void* buffer, size_t size) const 554 { 555 return ring_buffer_peek(fBuffer, offset, buffer, size); 556 } 557 558 private: 559 ring_buffer* fBuffer; 560 }; 561 562 pager(TextSource(buffer)); 563 564 return true; 565 } 566 567 568 static status_t 569 save_previous_syslog_to_volume(Directory* directory) 570 { 571 // find an unused name 572 char name[16]; 573 bool found = false; 574 for (int i = 0; i < 99; i++) { 575 snprintf(name, sizeof(name), "SYSLOG%02d.TXT", i); 576 Node* node = directory->Lookup(name, false); 577 if (node == NULL) { 578 found = true; 579 break; 580 } 581 582 node->Release(); 583 } 584 585 if (!found) { 586 printf("Failed to find an unused name for the syslog file!\n"); 587 return B_ERROR; 588 } 589 590 printf("Writing syslog to file \"%s\" ...\n", name); 591 592 int fd = open_from(directory, name, O_RDWR | O_CREAT | O_EXCL, 0644); 593 if (fd < 0) { 594 printf("Failed to create syslog file!\n"); 595 return fd; 596 } 597 598 ring_buffer* syslogBuffer = (ring_buffer*)gKernelArgs.debug_output; 599 iovec vecs[2]; 600 int32 vecCount = ring_buffer_get_vecs(syslogBuffer, vecs); 601 if (vecCount > 0) { 602 size_t toWrite = ring_buffer_readable(syslogBuffer); 603 604 ssize_t written = writev(fd, vecs, vecCount); 605 if (written < 0 || (size_t)written != toWrite) { 606 printf("Failed to write to the syslog file \"%s\"!\n", name); 607 close(fd); 608 return errno; 609 } 610 } 611 612 close(fd); 613 614 printf("Successfully wrote syslog file.\n"); 615 616 return B_OK; 617 } 618 619 620 static bool 621 debug_menu_add_advanced_option(Menu* menu, MenuItem* item) 622 { 623 char buffer[256]; 624 625 size_t size = platform_get_user_input_text(menu, item, buffer, 626 sizeof(buffer) - 1); 627 628 if (size > 0) { 629 buffer[size] = '\n'; 630 size_t pos = strlen(sSafeModeOptionsBuffer); 631 if (pos + size + 1 < sizeof(sSafeModeOptionsBuffer)) 632 strlcat(sSafeModeOptionsBuffer, buffer, 633 sizeof(sSafeModeOptionsBuffer)); 634 } 635 636 return true; 637 } 638 639 640 static bool 641 debug_menu_toggle_debug_syslog(Menu* menu, MenuItem* item) 642 { 643 gKernelArgs.keep_debug_output_buffer = item->IsMarked(); 644 return true; 645 } 646 647 648 static bool 649 debug_menu_save_previous_syslog(Menu* menu, MenuItem* item) 650 { 651 Directory* volume = (Directory*)item->Data(); 652 653 console_clear_screen(); 654 655 save_previous_syslog_to_volume(volume); 656 657 printf("\nPress any key to continue\n"); 658 console_wait_for_key(); 659 660 return true; 661 } 662 663 664 static Menu* 665 add_boot_volume_menu(Directory* bootVolume) 666 { 667 Menu* menu = new(std::nothrow) Menu(CHOICE_MENU, "Select Boot Volume"); 668 MenuItem* item; 669 void* cookie; 670 int32 count = 0; 671 672 if (gRoot->Open(&cookie, O_RDONLY) == B_OK) { 673 Directory* volume; 674 while (gRoot->GetNextNode(cookie, (Node**)&volume) == B_OK) { 675 // only list bootable volumes 676 if (!is_bootable(volume)) 677 continue; 678 679 char name[B_FILE_NAME_LENGTH]; 680 if (volume->GetName(name, sizeof(name)) == B_OK) { 681 menu->AddItem(item = new(nothrow) MenuItem(name)); 682 item->SetTarget(user_menu_boot_volume); 683 item->SetData(volume); 684 685 if (volume == bootVolume) { 686 item->SetMarked(true); 687 item->Select(true); 688 } 689 690 count++; 691 } 692 } 693 gRoot->Close(cookie); 694 } 695 696 if (count == 0) { 697 // no boot volume found yet 698 menu->AddItem(item = new(nothrow) MenuItem("<No boot volume found>")); 699 item->SetType(MENU_ITEM_NO_CHOICE); 700 item->SetEnabled(false); 701 } 702 703 menu->AddSeparatorItem(); 704 705 menu->AddItem(item = new(nothrow) MenuItem("Rescan volumes")); 706 item->SetHelpText("Please insert a Haiku CD-ROM or attach a USB disk - " 707 "depending on your system, you can then boot from there."); 708 item->SetType(MENU_ITEM_NO_CHOICE); 709 if (count == 0) 710 item->Select(true); 711 712 menu->AddItem(item = new(nothrow) MenuItem("Return to main menu")); 713 item->SetType(MENU_ITEM_NO_CHOICE); 714 715 if (gKernelArgs.boot_volume.GetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE, false)) 716 menu->SetChoiceText("CD-ROM or hard drive"); 717 718 return menu; 719 } 720 721 722 static Menu* 723 add_safe_mode_menu() 724 { 725 Menu* safeMenu = new(nothrow) Menu(SAFE_MODE_MENU, "Safe Mode Options"); 726 MenuItem* item; 727 728 safeMenu->AddItem(item = new(nothrow) MenuItem("Safe mode")); 729 item->SetData(B_SAFEMODE_SAFE_MODE); 730 item->SetType(MENU_ITEM_MARKABLE); 731 item->SetHelpText("Puts the system into safe mode. This can be enabled " 732 "independently from the other options."); 733 734 safeMenu->AddItem(item = new(nothrow) MenuItem("Disable user add-ons")); 735 item->SetData(B_SAFEMODE_DISABLE_USER_ADD_ONS); 736 item->SetType(MENU_ITEM_MARKABLE); 737 item->SetHelpText("Prevents all user installed add-ons from being loaded. " 738 "Only the add-ons in the system directory will be used."); 739 740 safeMenu->AddItem(item = new(nothrow) MenuItem("Disable IDE DMA")); 741 item->SetData(B_SAFEMODE_DISABLE_IDE_DMA); 742 item->SetType(MENU_ITEM_MARKABLE); 743 item->SetHelpText("Disables IDE DMA, increasing IDE compatibility " 744 "at the expense of performance."); 745 746 #if B_HAIKU_PHYSICAL_BITS > 32 747 // check whether we have memory beyond 4 GB 748 bool hasMemoryBeyond4GB = false; 749 for (uint32 i = 0; i < gKernelArgs.num_physical_memory_ranges; i++) { 750 phys_addr_range& range = gKernelArgs.physical_memory_range[i]; 751 if (range.start >= (phys_addr_t)1 << 32) { 752 hasMemoryBeyond4GB = true; 753 break; 754 } 755 } 756 757 // ... add the menu, if so 758 if (hasMemoryBeyond4GB) { 759 safeMenu->AddItem( 760 item = new(nothrow) MenuItem("Ignore memory beyond 4 GiB")); 761 item->SetData(B_SAFEMODE_4_GB_MEMORY_LIMIT); 762 item->SetType(MENU_ITEM_MARKABLE); 763 item->SetHelpText("Ignores all memory beyond the 4 GiB address limit, " 764 "overriding the setting in the kernel settings file."); 765 } 766 #endif 767 768 platform_add_menus(safeMenu); 769 770 safeMenu->AddSeparatorItem(); 771 safeMenu->AddItem(item = new(nothrow) MenuItem("Return to main menu")); 772 773 return safeMenu; 774 } 775 776 777 static Menu* 778 add_save_debug_syslog_menu() 779 { 780 Menu* menu = new(nothrow) Menu(STANDARD_MENU, "Save syslog to volume ..."); 781 MenuItem* item; 782 783 const char* const kHelpText = "Currently only FAT32 volumes are supported. " 784 "Newly plugged in removable devices are only recognized after " 785 "rebooting."; 786 787 int32 itemsAdded = 0; 788 789 void* cookie; 790 if (gRoot->Open(&cookie, O_RDONLY) == B_OK) { 791 Node* node; 792 while (gRoot->GetNextNode(cookie, &node) == B_OK) { 793 Directory* volume = static_cast<Directory*>(node); 794 Partition* partition; 795 if (gRoot->GetPartitionFor(volume, &partition) != B_OK) 796 continue; 797 798 // we support only FAT32 volumes ATM 799 if (partition->content_type == NULL 800 || strcmp(partition->content_type, kPartitionTypeFAT32) != 0) { 801 continue; 802 } 803 804 char name[B_FILE_NAME_LENGTH]; 805 if (volume->GetName(name, sizeof(name)) != B_OK) 806 strlcpy(name, "unnamed", sizeof(name)); 807 808 // append offset, size, and type to the name 809 size_t len = strlen(name); 810 char offsetBuffer[32]; 811 char sizeBuffer[32]; 812 snprintf(name + len, sizeof(name) - len, 813 " (%s, offset %s, size %s)", partition->content_type, 814 size_to_string(partition->offset, offsetBuffer, 815 sizeof(offsetBuffer)), 816 size_to_string(partition->size, sizeBuffer, 817 sizeof(sizeBuffer))); 818 819 item = new(nothrow) MenuItem(name); 820 item->SetData(volume); 821 item->SetTarget(&debug_menu_save_previous_syslog); 822 item->SetType(MENU_ITEM_NO_CHOICE); 823 item->SetHelpText(kHelpText); 824 menu->AddItem(item); 825 itemsAdded++; 826 } 827 828 gRoot->Close(cookie); 829 } 830 831 if (itemsAdded == 0) { 832 menu->AddItem(item 833 = new(nothrow) MenuItem("No supported volumes found")); 834 item->SetType(MENU_ITEM_NO_CHOICE); 835 item->SetHelpText(kHelpText); 836 item->SetEnabled(false); 837 } 838 839 menu->AddSeparatorItem(); 840 menu->AddItem(item = new(nothrow) MenuItem("Return to debug menu")); 841 item->SetHelpText(kHelpText); 842 843 return menu; 844 } 845 846 847 static Menu* 848 add_debug_menu() 849 { 850 Menu* menu = new(std::nothrow) Menu(STANDARD_MENU, "Debug Options"); 851 MenuItem* item; 852 853 #if DEBUG_SPINLOCK_LATENCIES 854 item = new(std::nothrow) MenuItem("Disable latency checks"); 855 if (item != NULL) { 856 item->SetType(MENU_ITEM_MARKABLE); 857 item->SetData(B_SAFEMODE_DISABLE_LATENCY_CHECK); 858 item->SetHelpText("Disables latency check panics."); 859 menu->AddItem(item); 860 } 861 #endif 862 863 menu->AddItem(item 864 = new(nothrow) MenuItem("Enable serial debug output")); 865 item->SetData("serial_debug_output"); 866 item->SetType(MENU_ITEM_MARKABLE); 867 item->SetHelpText("Turns on forwarding the syslog output to the serial " 868 "interface (default: 115200, 8N1)."); 869 870 menu->AddItem(item 871 = new(nothrow) MenuItem("Enable on screen debug output")); 872 item->SetData("debug_screen"); 873 item->SetType(MENU_ITEM_MARKABLE); 874 item->SetHelpText("Displays debug output on screen while the system " 875 "is booting, instead of the normal boot logo."); 876 877 menu->AddItem(item 878 = new(nothrow) MenuItem("Disable on screen paging")); 879 item->SetData("disable_onscreen_paging"); 880 item->SetType(MENU_ITEM_MARKABLE); 881 item->SetHelpText("Disables paging when on screen debug output is " 882 "enabled."); 883 884 menu->AddItem(item = new(nothrow) MenuItem("Enable debug syslog")); 885 item->SetType(MENU_ITEM_MARKABLE); 886 item->SetMarked(gKernelArgs.keep_debug_output_buffer); 887 item->SetTarget(&debug_menu_toggle_debug_syslog); 888 item->SetHelpText("Enables a special in-memory syslog buffer for this " 889 "session that the boot loader will be able to access after rebooting."); 890 891 bool currentLogItemVisible = platform_debug_get_log_buffer(NULL) != NULL; 892 if (currentLogItemVisible) { 893 menu->AddSeparatorItem(); 894 menu->AddItem(item 895 = new(nothrow) MenuItem("Display current boot loader log")); 896 item->SetTarget(&debug_menu_display_current_log); 897 item->SetType(MENU_ITEM_NO_CHOICE); 898 item->SetHelpText( 899 "Displays the debug info the boot loader has logged."); 900 } 901 902 ring_buffer* syslogBuffer = (ring_buffer*)gKernelArgs.debug_output; 903 if (syslogBuffer != NULL && ring_buffer_readable(syslogBuffer) > 0) { 904 if (!currentLogItemVisible) 905 menu->AddSeparatorItem(); 906 907 menu->AddItem(item 908 = new(nothrow) MenuItem("Display syslog from previous session")); 909 item->SetTarget(&debug_menu_display_previous_syslog); 910 item->SetType(MENU_ITEM_NO_CHOICE); 911 item->SetHelpText( 912 "Displays the syslog from the previous Haiku session."); 913 914 menu->AddItem(item = new(nothrow) MenuItem( 915 "Save syslog from previous session", add_save_debug_syslog_menu())); 916 item->SetHelpText("Saves the syslog from the previous Haiku session to " 917 "disk. Currently only FAT32 volumes are supported."); 918 } 919 920 menu->AddSeparatorItem(); 921 menu->AddItem(item = new(nothrow) MenuItem( 922 "Add advanced debug option")); 923 item->SetType(MENU_ITEM_NO_CHOICE); 924 item->SetTarget(&debug_menu_add_advanced_option); 925 item->SetHelpText( 926 "Allows advanced debugging options to be entered directly."); 927 928 menu->AddSeparatorItem(); 929 menu->AddItem(item = new(nothrow) MenuItem("Return to main menu")); 930 931 return menu; 932 } 933 934 935 static void 936 apply_safe_mode_options(Menu* menu) 937 { 938 int32 pos = strlen(sSafeModeOptionsBuffer); 939 size_t bufferSize = sizeof(sSafeModeOptionsBuffer); 940 941 MenuItemIterator iterator = menu->ItemIterator(); 942 while (MenuItem* item = iterator.Next()) { 943 if (item->Type() == MENU_ITEM_SEPARATOR || !item->IsMarked() 944 || item->Data() == NULL || (uint32)pos >= bufferSize) 945 continue; 946 947 size_t totalBytes = snprintf(sSafeModeOptionsBuffer + pos, 948 bufferSize - pos, "%s true\n", (const char*)item->Data()); 949 pos += std::min(totalBytes, bufferSize - pos - 1); 950 } 951 } 952 953 954 static bool 955 user_menu_reboot(Menu* menu, MenuItem* item) 956 { 957 platform_exit(); 958 return true; 959 } 960 961 962 status_t 963 user_menu(Directory** _bootVolume) 964 { 965 Menu* menu = new(std::nothrow) Menu(MAIN_MENU); 966 Menu* safeModeMenu = NULL; 967 Menu* debugMenu = NULL; 968 MenuItem* item; 969 970 TRACE(("user_menu: enter\n")); 971 972 memset(sSafeModeOptionsBuffer, 0, sizeof(sSafeModeOptionsBuffer)); 973 974 // Add boot volume 975 menu->AddItem(item = new(std::nothrow) MenuItem("Select boot volume", 976 add_boot_volume_menu(*_bootVolume))); 977 978 // Add safe mode 979 menu->AddItem(item = new(std::nothrow) MenuItem("Select safe mode options", 980 safeModeMenu = add_safe_mode_menu())); 981 982 // add debug menu 983 menu->AddItem(item = new(std::nothrow) MenuItem("Select debug options", 984 debugMenu = add_debug_menu())); 985 986 // Add platform dependent menus 987 platform_add_menus(menu); 988 989 menu->AddSeparatorItem(); 990 991 menu->AddItem(item = new(std::nothrow) MenuItem("Reboot")); 992 item->SetTarget(user_menu_reboot); 993 item->SetShortcut('r'); 994 995 menu->AddItem(item = new(std::nothrow) MenuItem("Continue booting")); 996 if (*_bootVolume == NULL) { 997 item->SetEnabled(false); 998 menu->ItemAt(0)->Select(true); 999 } else 1000 item->SetShortcut('b'); 1001 1002 menu->Run(); 1003 1004 // See if a new boot device has been selected, and propagate that back 1005 if (item->Data() != NULL) 1006 *_bootVolume = (Directory*)item->Data(); 1007 1008 apply_safe_mode_options(safeModeMenu); 1009 apply_safe_mode_options(debugMenu); 1010 add_safe_mode_settings(sSafeModeOptionsBuffer); 1011 delete menu; 1012 1013 1014 TRACE(("user_menu: leave\n")); 1015 1016 return B_OK; 1017 } 1018 1019