1 /* 2 * Copyright 2003-2013, 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/ring_buffer.h> 25 26 #include "kernel_debug_config.h" 27 28 #include "load_driver_settings.h" 29 #include "loader.h" 30 #include "pager.h" 31 #include "RootFileSystem.h" 32 33 34 #define TRACE_MENU 35 #ifdef TRACE_MENU 36 # define TRACE(x) dprintf x 37 #else 38 # define TRACE(x) ; 39 #endif 40 41 42 static char sSafeModeOptionsBuffer[2048]; 43 44 45 MenuItem::MenuItem(const char *label, Menu *subMenu) 46 : 47 fLabel(strdup(label)), 48 fTarget(NULL), 49 fIsMarked(false), 50 fIsSelected(false), 51 fIsEnabled(true), 52 fType(MENU_ITEM_STANDARD), 53 fMenu(NULL), 54 fSubMenu(subMenu), 55 fData(NULL), 56 fHelpText(NULL), 57 fShortcut(0) 58 { 59 if (subMenu != NULL) 60 subMenu->fSuperItem = this; 61 } 62 63 64 MenuItem::~MenuItem() 65 { 66 if (fSubMenu != NULL) 67 fSubMenu->fSuperItem = NULL; 68 69 free(const_cast<char *>(fLabel)); 70 } 71 72 73 void 74 MenuItem::SetTarget(menu_item_hook target) 75 { 76 fTarget = target; 77 } 78 79 80 /** Marks or unmarks a menu item. A marked menu item usually gets a visual 81 * clue like a checkmark that distinguishes it from others. 82 * For menus of type CHOICE_MENU, there can only be one marked item - the 83 * chosen one. 84 */ 85 86 void 87 MenuItem::SetMarked(bool marked) 88 { 89 if (marked && fMenu != NULL && fMenu->Type() == CHOICE_MENU) { 90 // always set choice text of parent if we were marked 91 fMenu->SetChoiceText(Label()); 92 } 93 94 if (fIsMarked == marked) 95 return; 96 97 if (marked && fMenu != NULL && fMenu->Type() == CHOICE_MENU) { 98 // unmark previous item 99 MenuItem *markedItem = fMenu->FindMarked(); 100 if (markedItem != NULL) 101 markedItem->SetMarked(false); 102 } 103 104 fIsMarked = marked; 105 106 if (fMenu != NULL) 107 fMenu->Draw(this); 108 } 109 110 111 void 112 MenuItem::Select(bool selected) 113 { 114 if (fIsSelected == selected) 115 return; 116 117 if (selected && fMenu != NULL) { 118 // unselect previous item 119 MenuItem *selectedItem = fMenu->FindSelected(); 120 if (selectedItem != NULL) 121 selectedItem->Select(false); 122 } 123 124 fIsSelected = selected; 125 126 if (fMenu != NULL) 127 fMenu->Draw(this); 128 } 129 130 131 void 132 MenuItem::SetType(menu_item_type type) 133 { 134 fType = type; 135 } 136 137 138 void 139 MenuItem::SetEnabled(bool enabled) 140 { 141 if (fIsEnabled == enabled) 142 return; 143 144 fIsEnabled = enabled; 145 146 if (fMenu != NULL) 147 fMenu->Draw(this); 148 } 149 150 151 void 152 MenuItem::SetData(const void *data) 153 { 154 fData = data; 155 } 156 157 158 /*! This sets a help text that is shown when the item is 159 selected. 160 Note, unlike the label, the string is not copied, it's 161 just referenced and has to stay valid as long as the 162 item's menu is being used. 163 */ 164 void 165 MenuItem::SetHelpText(const char* text) 166 { 167 fHelpText = text; 168 } 169 170 171 void 172 MenuItem::SetShortcut(char key) 173 { 174 fShortcut = key; 175 } 176 177 178 void 179 MenuItem::SetMenu(Menu* menu) 180 { 181 fMenu = menu; 182 } 183 184 185 // #pragma mark - 186 187 188 Menu::Menu(menu_type type, const char* title) 189 : 190 fTitle(title), 191 fChoiceText(NULL), 192 fCount(0), 193 fIsHidden(true), 194 fType(type), 195 fSuperItem(NULL), 196 fShortcuts(NULL) 197 { 198 } 199 200 201 Menu::~Menu() 202 { 203 // take all remaining items with us 204 205 MenuItem *item; 206 while ((item = fItems.Head()) != NULL) { 207 fItems.Remove(item); 208 delete item; 209 } 210 } 211 212 213 MenuItem* 214 Menu::ItemAt(int32 index) 215 { 216 if (index < 0 || index >= fCount) 217 return NULL; 218 219 MenuItemIterator iterator = ItemIterator(); 220 MenuItem *item; 221 222 while ((item = iterator.Next()) != NULL) { 223 if (index-- == 0) 224 return item; 225 } 226 227 return NULL; 228 } 229 230 231 int32 232 Menu::IndexOf(MenuItem* searchedItem) 233 { 234 int32 index = 0; 235 236 MenuItemIterator iterator = ItemIterator(); 237 while (MenuItem* item = iterator.Next()) { 238 if (item == searchedItem) 239 return index; 240 241 index++; 242 } 243 244 return -1; 245 } 246 247 248 int32 249 Menu::CountItems() const 250 { 251 return fCount; 252 } 253 254 255 MenuItem* 256 Menu::FindItem(const char* label) 257 { 258 MenuItemIterator iterator = ItemIterator(); 259 while (MenuItem* item = iterator.Next()) { 260 if (item->Label() != NULL && !strcmp(item->Label(), label)) 261 return item; 262 } 263 264 return NULL; 265 } 266 267 268 MenuItem* 269 Menu::FindMarked() 270 { 271 MenuItemIterator iterator = ItemIterator(); 272 while (MenuItem* item = iterator.Next()) { 273 if (item->IsMarked()) 274 return item; 275 } 276 277 return NULL; 278 } 279 280 281 MenuItem* 282 Menu::FindSelected(int32* _index) 283 { 284 int32 index = 0; 285 286 MenuItemIterator iterator = ItemIterator(); 287 while (MenuItem* item = iterator.Next()) { 288 if (item->IsSelected()) { 289 if (_index != NULL) 290 *_index = index; 291 return item; 292 } 293 294 index++; 295 } 296 297 return NULL; 298 } 299 300 301 void 302 Menu::AddItem(MenuItem* item) 303 { 304 item->fMenu = this; 305 fItems.Add(item); 306 fCount++; 307 } 308 309 310 status_t 311 Menu::AddSeparatorItem() 312 { 313 MenuItem* item = new(std::nothrow) MenuItem(); 314 if (item == NULL) 315 return B_NO_MEMORY; 316 317 item->SetType(MENU_ITEM_SEPARATOR); 318 319 AddItem(item); 320 return B_OK; 321 } 322 323 324 MenuItem* 325 Menu::RemoveItemAt(int32 index) 326 { 327 if (index < 0 || index >= fCount) 328 return NULL; 329 330 MenuItemIterator iterator = ItemIterator(); 331 while (MenuItem* item = iterator.Next()) { 332 if (index-- == 0) { 333 RemoveItem(item); 334 return item; 335 } 336 } 337 338 return NULL; 339 } 340 341 342 void 343 Menu::RemoveItem(MenuItem* item) 344 { 345 item->fMenu = NULL; 346 fItems.Remove(item); 347 fCount--; 348 } 349 350 351 void 352 Menu::AddShortcut(char key, shortcut_hook function) 353 { 354 Menu::shortcut* shortcut = new(std::nothrow) Menu::shortcut; 355 if (shortcut == NULL) 356 return; 357 358 shortcut->key = key; 359 shortcut->function = function; 360 361 shortcut->next = fShortcuts; 362 fShortcuts = shortcut; 363 } 364 365 366 shortcut_hook 367 Menu::FindShortcut(char key) const 368 { 369 if (key == 0) 370 return NULL; 371 372 const Menu::shortcut* shortcut = fShortcuts; 373 while (shortcut != NULL) { 374 if (shortcut->key == key) 375 return shortcut->function; 376 377 shortcut = shortcut->next; 378 } 379 380 return NULL; 381 } 382 383 384 MenuItem* 385 Menu::FindItemByShortcut(char key) 386 { 387 if (key == 0) 388 return NULL; 389 390 MenuItemList::Iterator iterator = ItemIterator(); 391 while (MenuItem* item = iterator.Next()) { 392 if (item->Shortcut() == key) 393 return item; 394 } 395 396 return NULL; 397 } 398 399 400 void 401 Menu::Run() 402 { 403 platform_run_menu(this); 404 } 405 406 407 void 408 Menu::Draw(MenuItem* item) 409 { 410 if (!IsHidden()) 411 platform_update_menu_item(this, item); 412 } 413 414 415 // #pragma mark - 416 417 418 static const char* 419 size_to_string(off_t size, char* buffer, size_t bufferSize) 420 { 421 static const char* const kPrefixes[] = { "K", "M", "G", "T", "P", NULL }; 422 int32 nextIndex = 0; 423 int32 remainder = 0; 424 while (size >= 1024 && kPrefixes[nextIndex] != NULL) { 425 remainder = size % 1024; 426 size /= 1024; 427 nextIndex++; 428 429 if (size < 1024) { 430 // Compute the decimal remainder and make sure we have at most 431 // 3 decimal places (or 4 for 1000 <= size <= 1023). 432 int32 factor; 433 if (size >= 100) 434 factor = 100; 435 else if (size >= 10) 436 factor = 10; 437 else 438 factor = 1; 439 440 remainder = (remainder * 1000 + 5 * factor) / 1024; 441 442 if (remainder >= 1000) { 443 size++; 444 remainder = 0; 445 } else 446 remainder /= 10 * factor; 447 } else 448 size += (remainder + 512) / 1024; 449 } 450 451 if (remainder == 0) { 452 snprintf(buffer, bufferSize, "%" B_PRIdOFF, size); 453 } else { 454 snprintf(buffer, bufferSize, "%" B_PRIdOFF ".%" B_PRId32, size, 455 remainder); 456 } 457 458 size_t length = strlen(buffer); 459 snprintf(buffer + length, bufferSize - length, " %sB", 460 nextIndex == 0 ? "" : kPrefixes[nextIndex - 1]); 461 462 return buffer; 463 } 464 465 466 // #pragma mark - 467 468 469 static bool 470 user_menu_boot_volume(Menu* menu, MenuItem* item) 471 { 472 Menu* super = menu->Supermenu(); 473 if (super == NULL) { 474 // huh? 475 return true; 476 } 477 478 MenuItem *bootItem = super->ItemAt(super->CountItems() - 1); 479 bootItem->SetEnabled(true); 480 bootItem->Select(true); 481 bootItem->SetData(item->Data()); 482 483 gBootVolume.SetBool(BOOT_VOLUME_USER_SELECTED, true); 484 return true; 485 } 486 487 488 static bool 489 debug_menu_display_current_log(Menu* menu, MenuItem* item) 490 { 491 // get the buffer 492 size_t bufferSize; 493 const char* buffer = platform_debug_get_log_buffer(&bufferSize); 494 if (buffer == NULL || bufferSize == 0) 495 return true; 496 497 struct TextSource : PagerTextSource { 498 TextSource(const char* buffer, size_t size) 499 : 500 fBuffer(buffer), 501 fSize(strnlen(buffer, size)) 502 { 503 } 504 505 virtual size_t BytesAvailable() const 506 { 507 return fSize; 508 } 509 510 virtual size_t Read(size_t offset, void* buffer, size_t size) const 511 { 512 if (offset >= fSize) 513 return 0; 514 515 if (size > fSize - offset) 516 size = fSize - offset; 517 518 memcpy(buffer, fBuffer + offset, size); 519 return size; 520 } 521 522 private: 523 const char* fBuffer; 524 size_t fSize; 525 }; 526 527 pager(TextSource(buffer, bufferSize)); 528 529 return true; 530 } 531 532 533 static bool 534 debug_menu_display_previous_syslog(Menu* menu, MenuItem* item) 535 { 536 ring_buffer* buffer = (ring_buffer*)gKernelArgs.debug_output.Pointer(); 537 if (buffer == NULL) 538 return true; 539 540 struct TextSource : PagerTextSource { 541 TextSource(ring_buffer* buffer) 542 : 543 fBuffer(buffer) 544 { 545 } 546 547 virtual size_t BytesAvailable() const 548 { 549 return ring_buffer_readable(fBuffer); 550 } 551 552 virtual size_t Read(size_t offset, void* buffer, size_t size) const 553 { 554 return ring_buffer_peek(fBuffer, offset, buffer, size); 555 } 556 557 private: 558 ring_buffer* fBuffer; 559 }; 560 561 pager(TextSource(buffer)); 562 563 return true; 564 } 565 566 567 static status_t 568 save_previous_syslog_to_volume(Directory* directory) 569 { 570 // find an unused name 571 char name[16]; 572 bool found = false; 573 for (int i = 0; i < 99; i++) { 574 snprintf(name, sizeof(name), "SYSLOG%02d.TXT", i); 575 Node* node = directory->Lookup(name, false); 576 if (node == NULL) { 577 found = true; 578 break; 579 } 580 581 node->Release(); 582 } 583 584 if (!found) { 585 printf("Failed to find an unused name for the syslog file!\n"); 586 return B_ERROR; 587 } 588 589 printf("Writing syslog to file \"%s\" ...\n", name); 590 591 int fd = open_from(directory, name, O_RDWR | O_CREAT | O_EXCL, 0644); 592 if (fd < 0) { 593 printf("Failed to create syslog file!\n"); 594 return fd; 595 } 596 597 ring_buffer* syslogBuffer 598 = (ring_buffer*)gKernelArgs.debug_output.Pointer(); 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 (gBootVolume.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 addr_range& range = gKernelArgs.physical_memory_range[i]; 751 if (range.start >= (uint64)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 903 = (ring_buffer*)gKernelArgs.debug_output.Pointer(); 904 if (syslogBuffer != NULL && ring_buffer_readable(syslogBuffer) > 0) { 905 if (!currentLogItemVisible) 906 menu->AddSeparatorItem(); 907 908 menu->AddItem(item 909 = new(nothrow) MenuItem("Display syslog from previous session")); 910 item->SetTarget(&debug_menu_display_previous_syslog); 911 item->SetType(MENU_ITEM_NO_CHOICE); 912 item->SetHelpText( 913 "Displays the syslog from the previous Haiku session."); 914 915 menu->AddItem(item = new(nothrow) MenuItem( 916 "Save syslog from previous session", add_save_debug_syslog_menu())); 917 item->SetHelpText("Saves the syslog from the previous Haiku session to " 918 "disk. Currently only FAT32 volumes are supported."); 919 } 920 921 menu->AddSeparatorItem(); 922 menu->AddItem(item = new(nothrow) MenuItem( 923 "Add advanced debug option")); 924 item->SetType(MENU_ITEM_NO_CHOICE); 925 item->SetTarget(&debug_menu_add_advanced_option); 926 item->SetHelpText( 927 "Allows advanced debugging options to be entered directly."); 928 929 menu->AddSeparatorItem(); 930 menu->AddItem(item = new(nothrow) MenuItem("Return to main menu")); 931 932 return menu; 933 } 934 935 936 static void 937 apply_safe_mode_options(Menu* menu) 938 { 939 int32 pos = strlen(sSafeModeOptionsBuffer); 940 size_t bufferSize = sizeof(sSafeModeOptionsBuffer); 941 942 MenuItemIterator iterator = menu->ItemIterator(); 943 while (MenuItem* item = iterator.Next()) { 944 if (item->Type() == MENU_ITEM_SEPARATOR || !item->IsMarked() 945 || item->Data() == NULL || (uint32)pos >= bufferSize) 946 continue; 947 948 size_t totalBytes = snprintf(sSafeModeOptionsBuffer + pos, 949 bufferSize - pos, "%s true\n", (const char*)item->Data()); 950 pos += std::min(totalBytes, bufferSize - pos - 1); 951 } 952 } 953 954 955 static bool 956 user_menu_reboot(Menu* menu, MenuItem* item) 957 { 958 platform_exit(); 959 return true; 960 } 961 962 963 status_t 964 user_menu(BootVolume& _bootVolume) 965 { 966 Menu* menu = new(std::nothrow) Menu(MAIN_MENU); 967 Menu* safeModeMenu = NULL; 968 Menu* debugMenu = NULL; 969 MenuItem* item; 970 971 TRACE(("user_menu: enter\n")); 972 973 memset(sSafeModeOptionsBuffer, 0, sizeof(sSafeModeOptionsBuffer)); 974 975 // Add boot volume 976 menu->AddItem(item = new(std::nothrow) MenuItem("Select boot volume", 977 add_boot_volume_menu(_bootVolume.RootDirectory()))); 978 979 // Add safe mode 980 menu->AddItem(item = new(std::nothrow) MenuItem("Select safe mode options", 981 safeModeMenu = add_safe_mode_menu())); 982 983 // add debug menu 984 menu->AddItem(item = new(std::nothrow) MenuItem("Select debug options", 985 debugMenu = add_debug_menu())); 986 987 // Add platform dependent menus 988 platform_add_menus(menu); 989 990 menu->AddSeparatorItem(); 991 992 menu->AddItem(item = new(std::nothrow) MenuItem("Reboot")); 993 item->SetTarget(user_menu_reboot); 994 item->SetShortcut('r'); 995 996 menu->AddItem(item = new(std::nothrow) MenuItem("Continue booting")); 997 if (!_bootVolume.IsValid()) { 998 item->SetEnabled(false); 999 menu->ItemAt(0)->Select(true); 1000 } else 1001 item->SetShortcut('b'); 1002 1003 menu->Run(); 1004 1005 // See if a new boot device has been selected, and propagate that back 1006 if (item->Data() != NULL) 1007 _bootVolume.SetTo((Directory*)item->Data()); 1008 1009 apply_safe_mode_options(safeModeMenu); 1010 apply_safe_mode_options(debugMenu); 1011 add_safe_mode_settings(sSafeModeOptionsBuffer); 1012 delete menu; 1013 1014 1015 TRACE(("user_menu: leave\n")); 1016 1017 return B_OK; 1018 } 1019 1020