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