2 * Copyright 2003-2013, Axel Dörfler, axeld@pinc-software.de.
3 * Copyright 2011, Rene Gollent, rene@gollent.com.
4 * Copyright 2013-2014, Ingo Weinhold, ingo_weinhold@gmx.de.
5 * Distributed under the terms of the MIT License.
18 #include <AutoDeleter.h>
19 #include <boot/menu.h>
20 #include <boot/PathBlacklist.h>
21 #include <boot/stage2.h>
23 #include <boot/platform.h>
24 #include <boot/platform/generic/text_console.h>
25 #include <boot/stdio.h>
27 #include <util/ring_buffer.h>
28 #include <util/SinglyLinkedList.h>
30 #include "kernel_debug_config.h"
32 #include "load_driver_settings.h"
34 #include "package_support.h"
36 #include "RootFileSystem.h"
41 # define TRACE(x) dprintf x
47 // only set while in user_menu()
48 static Menu
* sMainMenu
= NULL
;
49 static Menu
* sBlacklistRootMenu
= NULL
;
50 static BootVolume
* sBootVolume
= NULL
;
51 static PathBlacklist
* sPathBlacklist
;
54 MenuItem::MenuItem(const char *label
, Menu
*subMenu
)
56 fLabel(strdup(label
)),
61 fType(MENU_ITEM_STANDARD
),
75 free(const_cast<char *>(fLabel
));
80 MenuItem::SetTarget(menu_item_hook target
)
86 /** Marks or unmarks a menu item. A marked menu item usually gets a visual
87 * clue like a checkmark that distinguishes it from others.
88 * For menus of type CHOICE_MENU, there can only be one marked item - the
93 MenuItem::SetMarked(bool marked
)
95 if (marked
&& fMenu
!= NULL
&& fMenu
->Type() == CHOICE_MENU
) {
96 // always set choice text of parent if we were marked
97 fMenu
->SetChoiceText(Label());
100 if (fIsMarked
== marked
)
103 if (marked
&& fMenu
!= NULL
&& fMenu
->Type() == CHOICE_MENU
) {
104 // unmark previous item
105 MenuItem
*markedItem
= fMenu
->FindMarked();
106 if (markedItem
!= NULL
)
107 markedItem
->SetMarked(false);
118 MenuItem::Select(bool selected
)
120 if (fIsSelected
== selected
)
123 if (selected
&& fMenu
!= NULL
) {
124 // unselect previous item
125 MenuItem
*selectedItem
= fMenu
->FindSelected();
126 if (selectedItem
!= NULL
)
127 selectedItem
->Select(false);
130 fIsSelected
= selected
;
138 MenuItem::SetType(menu_item_type type
)
145 MenuItem::SetEnabled(bool enabled
)
147 if (fIsEnabled
== enabled
)
150 fIsEnabled
= enabled
;
158 MenuItem::SetData(const void *data
)
164 /*! This sets a help text that is shown when the item is
166 Note, unlike the label, the string is not copied, it's
167 just referenced and has to stay valid as long as the
168 item's menu is being used.
171 MenuItem::SetHelpText(const char* text
)
178 MenuItem::SetShortcut(char key
)
185 MenuItem::SetLabel(const char* label
)
187 if (char* newLabel
= strdup(label
)) {
188 free(const_cast<char*>(fLabel
));
195 MenuItem::SetSubmenu(Menu
* subMenu
)
199 if (fSubMenu
!= NULL
)
200 fSubMenu
->fSuperItem
= this;
205 MenuItem::SetMenu(Menu
* menu
)
214 Menu::Menu(menu_type type
, const char* title
)
229 // take all remaining items with us
232 while ((item
= fItems
.Head()) != NULL
) {
252 Menu::ItemAt(int32 index
)
254 if (index
< 0 || index
>= fCount
)
257 MenuItemIterator iterator
= ItemIterator();
260 while ((item
= iterator
.Next()) != NULL
) {
270 Menu::IndexOf(MenuItem
* searchedItem
)
274 MenuItemIterator iterator
= ItemIterator();
275 while (MenuItem
* item
= iterator
.Next()) {
276 if (item
== searchedItem
)
287 Menu::CountItems() const
294 Menu::FindItem(const char* label
)
296 MenuItemIterator iterator
= ItemIterator();
297 while (MenuItem
* item
= iterator
.Next()) {
298 if (item
->Label() != NULL
&& !strcmp(item
->Label(), label
))
309 MenuItemIterator iterator
= ItemIterator();
310 while (MenuItem
* item
= iterator
.Next()) {
311 if (item
->IsMarked())
320 Menu::FindSelected(int32
* _index
)
324 MenuItemIterator iterator
= ItemIterator();
325 while (MenuItem
* item
= iterator
.Next()) {
326 if (item
->IsSelected()) {
340 Menu::AddItem(MenuItem
* item
)
349 Menu::AddSeparatorItem()
351 MenuItem
* item
= new(std::nothrow
) MenuItem();
355 item
->SetType(MENU_ITEM_SEPARATOR
);
363 Menu::RemoveItemAt(int32 index
)
365 if (index
< 0 || index
>= fCount
)
368 MenuItemIterator iterator
= ItemIterator();
369 while (MenuItem
* item
= iterator
.Next()) {
381 Menu::RemoveItem(MenuItem
* item
)
390 Menu::AddShortcut(char key
, shortcut_hook function
)
392 Menu::shortcut
* shortcut
= new(std::nothrow
) Menu::shortcut
;
393 if (shortcut
== NULL
)
397 shortcut
->function
= function
;
399 shortcut
->next
= fShortcuts
;
400 fShortcuts
= shortcut
;
405 Menu::FindShortcut(char key
) const
410 const Menu::shortcut
* shortcut
= fShortcuts
;
411 while (shortcut
!= NULL
) {
412 if (shortcut
->key
== key
)
413 return shortcut
->function
;
415 shortcut
= shortcut
->next
;
418 Menu
*superMenu
= Supermenu();
420 if (superMenu
!= NULL
)
421 return superMenu
->FindShortcut(key
);
428 Menu::FindItemByShortcut(char key
)
433 MenuItemList::Iterator iterator
= ItemIterator();
434 while (MenuItem
* item
= iterator
.Next()) {
435 if (item
->Shortcut() == key
)
439 Menu
*superMenu
= Supermenu();
441 if (superMenu
!= NULL
)
442 return superMenu
->FindItemByShortcut(key
);
449 Menu::SortItems(bool (*less
)(const MenuItem
*, const MenuItem
*))
458 platform_run_menu(this);
463 Menu::Draw(MenuItem
* item
)
466 platform_update_menu_item(this, item
);
474 size_to_string(off_t size
, char* buffer
, size_t bufferSize
)
476 static const char* const kPrefixes
[] = { "K", "M", "G", "T", "P", NULL
};
479 while (size
>= 1024 && kPrefixes
[nextIndex
] != NULL
) {
480 remainder
= size
% 1024;
485 // Compute the decimal remainder and make sure we have at most
486 // 3 decimal places (or 4 for 1000 <= size <= 1023).
495 remainder
= (remainder
* 1000 + 5 * factor
) / 1024;
497 if (remainder
>= 1000) {
501 remainder
/= 10 * factor
;
503 size
+= (remainder
+ 512) / 1024;
506 if (remainder
== 0) {
507 snprintf(buffer
, bufferSize
, "%" B_PRIdOFF
, size
);
509 snprintf(buffer
, bufferSize
, "%" B_PRIdOFF
".%" B_PRId32
, size
,
513 size_t length
= strlen(buffer
);
514 snprintf(buffer
+ length
, bufferSize
- length
, " %sB",
515 nextIndex
== 0 ? "" : kPrefixes
[nextIndex
- 1]);
521 // #pragma mark - blacklist menu
524 class BlacklistMenuItem
: public MenuItem
{
526 BlacklistMenuItem(char* label
, Node
* node
, Menu
* subMenu
)
528 MenuItem(label
, subMenu
),
533 SetType(MENU_ITEM_MARKABLE
);
540 // make sure the submenu is destroyed
541 SetSubmenu(fSubMenu
);
544 bool IsDirectoryItem() const
546 return fNode
->Type() == S_IFDIR
;
549 bool GetPath(BlacklistedPath
& _path
) const
551 Menu
* menu
= Supermenu();
552 if (menu
!= NULL
&& menu
!= sBlacklistRootMenu
553 && menu
->Superitem() != NULL
) {
554 return static_cast<BlacklistMenuItem
*>(menu
->Superitem())
556 && _path
.Append(Label());
559 return _path
.SetTo(Label());
562 void UpdateBlacklisted()
564 BlacklistedPath path
;
566 _SetMarked(sPathBlacklist
->Contains(path
.Path()), false);
569 virtual void SetMarked(bool marked
)
571 _SetMarked(marked
, true);
574 static bool Less(const MenuItem
* a
, const MenuItem
* b
)
576 const BlacklistMenuItem
* item1
577 = static_cast<const BlacklistMenuItem
*>(a
);
578 const BlacklistMenuItem
* item2
579 = static_cast<const BlacklistMenuItem
*>(b
);
581 // directories come first
582 if (item1
->IsDirectoryItem() != item2
->IsDirectoryItem())
583 return item1
->IsDirectoryItem();
585 // compare the labels
586 return strcasecmp(item1
->Label(), item2
->Label()) < 0;
590 void _SetMarked(bool marked
, bool updateBlacklist
)
592 if (marked
== IsMarked())
595 // For directories toggle the availability of the submenu.
596 if (IsDirectoryItem())
597 SetSubmenu(marked
? NULL
: fSubMenu
);
599 if (updateBlacklist
) {
600 BlacklistedPath path
;
603 sPathBlacklist
->Add(path
.Path());
605 sPathBlacklist
->Remove(path
.Path());
609 MenuItem::SetMarked(marked
);
618 class BlacklistMenu
: public Menu
{
622 Menu(STANDARD_MENU
, kDefaultMenuTitle
),
632 virtual void Entered()
636 if (fDirectory
!= NULL
) {
638 if (fDirectory
->Open(&cookie
, O_RDONLY
) == B_OK
) {
640 while (fDirectory
->GetNextNode(cookie
, &node
) == B_OK
) {
641 BlacklistMenuItem
* item
= _CreateItem(node
);
648 item
->UpdateBlacklisted();
650 fDirectory
->Close(cookie
);
653 SortItems(&BlacklistMenuItem::Less
);
656 if (CountItems() > 0)
658 AddItem(new(nothrow
) MenuItem("Return to parent directory"));
661 virtual void Exited()
667 void SetDirectory(Directory
* directory
)
669 if (fDirectory
!= NULL
)
670 fDirectory
->Release();
672 fDirectory
= directory
;
674 if (fDirectory
!= NULL
)
675 fDirectory
->Acquire();
679 static BlacklistMenuItem
* _CreateItem(Node
* node
)
681 // Get the node name and duplicate it, so we can use it as a label.
682 char name
[B_FILE_NAME_LENGTH
];
683 if (node
->GetName(name
, sizeof(name
)) != B_OK
)
686 // append '/' to directory labels
687 bool isDirectory
= node
->Type() == S_IFDIR
;
689 strlcat(name
, "/", sizeof(name
));
691 // If this is a directory, create the submenu.
692 BlacklistMenu
* subMenu
= NULL
;
694 subMenu
= new(std::nothrow
) BlacklistMenu
;
696 subMenu
->SetDirectory(static_cast<Directory
*>(node
));
699 ObjectDeleter
<BlacklistMenu
> subMenuDeleter(subMenu
);
701 // create the menu item
702 BlacklistMenuItem
* item
= new(std::nothrow
) BlacklistMenuItem(name
,
707 subMenuDeleter
.Detach();
713 int32 count
= CountItems();
714 for (int32 i
= 0; i
< count
; i
++)
715 delete RemoveItemAt(0);
719 Directory
* fDirectory
;
722 static const char* const kDefaultMenuTitle
;
726 const char* const BlacklistMenu::kDefaultMenuTitle
727 = "Mark the entries to blacklist";
730 class BlacklistRootMenu
: public BlacklistMenu
{
738 virtual void Entered()
740 // Get the system directory, but only if this is a packaged Haiku.
741 // Otherwise blacklisting isn't supported.
742 if (sBootVolume
!= NULL
&& sBootVolume
->IsValid()
743 && sBootVolume
->IsPackaged()) {
744 SetDirectory(sBootVolume
->SystemDirectory());
745 SetTitle(kDefaultMenuTitle
);
748 SetTitle(sBootVolume
!= NULL
&& sBootVolume
->IsValid()
749 ? "The selected boot volume doesn't support blacklisting!"
750 : "No boot volume selected!");
753 BlacklistMenu::Entered();
756 if (MenuItem
* item
= ItemAt(CountItems() - 1))
757 item
->SetLabel("Return to safe mode menu");
760 virtual void Exited()
762 BlacklistMenu::Exited();
768 // #pragma mark - boot volume menu
771 class BootVolumeMenuItem
: public MenuItem
{
773 BootVolumeMenuItem(const char* volumeName
)
775 MenuItem(volumeName
),
776 fStateChoiceText(NULL
)
780 ~BootVolumeMenuItem()
782 UpdateStateName(NULL
);
785 void UpdateStateName(PackageVolumeState
* volumeState
)
787 free(fStateChoiceText
);
788 fStateChoiceText
= NULL
;
790 if (volumeState
!= NULL
&& volumeState
->Name() != NULL
) {
791 char nameBuffer
[128];
792 snprintf(nameBuffer
, sizeof(nameBuffer
), "%s (%s)", Label(),
793 volumeState
->DisplayName());
794 fStateChoiceText
= strdup(nameBuffer
);
797 Supermenu()->SetChoiceText(
798 fStateChoiceText
!= NULL
? fStateChoiceText
: Label());
802 char* fStateChoiceText
;
806 class PackageVolumeStateMenuItem
: public MenuItem
{
808 PackageVolumeStateMenuItem(const char* label
, PackageVolumeInfo
* volumeInfo
,
809 PackageVolumeState
* volumeState
)
812 fVolumeInfo(volumeInfo
),
813 fVolumeState(volumeState
)
815 fVolumeInfo
->AcquireReference();
818 ~PackageVolumeStateMenuItem()
820 fVolumeInfo
->ReleaseReference();
823 PackageVolumeInfo
* VolumeInfo() const
828 PackageVolumeState
* VolumeState() const
834 PackageVolumeInfo
* fVolumeInfo
;
835 PackageVolumeState
* fVolumeState
;
857 const char* String() const
859 return fBuffer
!= NULL
? fBuffer
: "";
862 size_t Length() const
867 bool Append(const char* toAppend
)
869 return Append(toAppend
, strlen(toAppend
));
872 bool Append(const char* toAppend
, size_t length
)
874 size_t oldLength
= fLength
;
875 if (!_Resize(fLength
+ length
))
878 memcpy(fBuffer
+ oldLength
, toAppend
, length
);
883 bool _Resize(size_t newLength
)
885 if (newLength
>= fCapacity
) {
886 size_t newCapacity
= std::max(fCapacity
, size_t(32));
887 while (newLength
>= newCapacity
)
890 char* buffer
= (char*)realloc(fBuffer
, newCapacity
);
895 fCapacity
= newCapacity
;
898 fBuffer
[newLength
] = '\0';
913 static StringBuffer sSafeModeOptionsBuffer
;
917 get_continue_booting_menu_item()
919 // It's the last item in the main menu.
920 if (sMainMenu
== NULL
|| sMainMenu
->CountItems() == 0)
922 return sMainMenu
->ItemAt(sMainMenu
->CountItems() - 1);
927 user_menu_boot_volume(Menu
* menu
, MenuItem
* item
)
929 MenuItem
* bootItem
= get_continue_booting_menu_item();
930 if (bootItem
== NULL
) {
935 if (sBootVolume
->IsValid() && sBootVolume
->RootDirectory() == item
->Data())
938 sPathBlacklist
->MakeEmpty();
940 bool valid
= sBootVolume
->SetTo((Directory
*)item
->Data()) == B_OK
;
942 bootItem
->SetEnabled(valid
);
944 bootItem
->Select(true);
946 gBootVolume
.SetBool(BOOT_VOLUME_USER_SELECTED
, true);
952 user_menu_boot_volume_state(Menu
* menu
, MenuItem
* _item
)
954 MenuItem
* bootItem
= get_continue_booting_menu_item();
955 if (bootItem
== NULL
) {
960 PackageVolumeStateMenuItem
* item
= static_cast<PackageVolumeStateMenuItem
*>(
962 if (sBootVolume
->IsValid() && sBootVolume
->GetPackageVolumeState() != NULL
963 && sBootVolume
->GetPackageVolumeState() == item
->VolumeState()) {
967 BootVolumeMenuItem
* volumeItem
= static_cast<BootVolumeMenuItem
*>(
968 item
->Supermenu()->Superitem());
969 volumeItem
->SetMarked(true);
970 volumeItem
->Select(true);
971 volumeItem
->UpdateStateName(item
->VolumeState());
973 sPathBlacklist
->MakeEmpty();
975 bool valid
= sBootVolume
->SetTo((Directory
*)item
->Data(),
976 item
->VolumeInfo(), item
->VolumeState()) == B_OK
;
978 bootItem
->SetEnabled(valid
);
980 bootItem
->Select(true);
982 gBootVolume
.SetBool(BOOT_VOLUME_USER_SELECTED
, true);
988 debug_menu_display_current_log(Menu
* menu
, MenuItem
* item
)
992 const char* buffer
= platform_debug_get_log_buffer(&bufferSize
);
993 if (buffer
== NULL
|| bufferSize
== 0)
996 struct TextSource
: PagerTextSource
{
997 TextSource(const char* buffer
, size_t size
)
1000 fSize(strnlen(buffer
, size
))
1004 virtual size_t BytesAvailable() const
1009 virtual size_t Read(size_t offset
, void* buffer
, size_t size
) const
1011 if (offset
>= fSize
)
1014 if (size
> fSize
- offset
)
1015 size
= fSize
- offset
;
1017 memcpy(buffer
, fBuffer
+ offset
, size
);
1022 const char* fBuffer
;
1026 pager(TextSource(buffer
, bufferSize
));
1033 debug_menu_display_previous_syslog(Menu
* menu
, MenuItem
* item
)
1035 ring_buffer
* buffer
= (ring_buffer
*)gKernelArgs
.debug_output
.Pointer();
1039 struct TextSource
: PagerTextSource
{
1040 TextSource(ring_buffer
* buffer
)
1046 virtual size_t BytesAvailable() const
1048 return ring_buffer_readable(fBuffer
);
1051 virtual size_t Read(size_t offset
, void* buffer
, size_t size
) const
1053 return ring_buffer_peek(fBuffer
, offset
, buffer
, size
);
1057 ring_buffer
* fBuffer
;
1060 pager(TextSource(buffer
));
1067 save_previous_syslog_to_volume(Directory
* directory
)
1069 // find an unused name
1072 for (int i
= 0; i
< 99; i
++) {
1073 snprintf(name
, sizeof(name
), "SYSLOG%02d.TXT", i
);
1074 Node
* node
= directory
->Lookup(name
, false);
1084 printf("Failed to find an unused name for the syslog file!\n");
1088 printf("Writing syslog to file \"%s\" ...\n", name
);
1090 int fd
= open_from(directory
, name
, O_RDWR
| O_CREAT
| O_EXCL
, 0644);
1092 printf("Failed to create syslog file!\n");
1096 ring_buffer
* syslogBuffer
1097 = (ring_buffer
*)gKernelArgs
.debug_output
.Pointer();
1099 int32 vecCount
= ring_buffer_get_vecs(syslogBuffer
, vecs
);
1101 size_t toWrite
= ring_buffer_readable(syslogBuffer
);
1103 ssize_t written
= writev(fd
, vecs
, vecCount
);
1104 if (written
< 0 || (size_t)written
!= toWrite
) {
1105 printf("Failed to write to the syslog file \"%s\"!\n", name
);
1113 printf("Successfully wrote syslog file.\n");
1120 debug_menu_add_advanced_option(Menu
* menu
, MenuItem
* item
)
1124 size_t size
= platform_get_user_input_text(menu
, item
, buffer
,
1125 sizeof(buffer
) - 1);
1128 buffer
[size
] = '\n';
1129 if (!sSafeModeOptionsBuffer
.Append(buffer
)) {
1130 dprintf("debug_menu_add_advanced_option(): failed to append option "
1140 debug_menu_toggle_debug_syslog(Menu
* menu
, MenuItem
* item
)
1142 gKernelArgs
.keep_debug_output_buffer
= item
->IsMarked();
1148 debug_menu_toggle_previous_debug_syslog(Menu
* menu
, MenuItem
* item
)
1150 gKernelArgs
.previous_debug_size
= item
->IsMarked();
1156 debug_menu_save_previous_syslog(Menu
* menu
, MenuItem
* item
)
1158 Directory
* volume
= (Directory
*)item
->Data();
1160 console_clear_screen();
1162 save_previous_syslog_to_volume(volume
);
1164 printf("\nPress any key to continue\n");
1165 console_wait_for_key();
1172 add_boot_volume_item(Menu
* menu
, Directory
* volume
, const char* name
)
1174 BReference
<PackageVolumeInfo
> volumeInfo
;
1175 PackageVolumeState
* selectedState
= NULL
;
1176 if (volume
== sBootVolume
->RootDirectory()) {
1177 volumeInfo
.SetTo(sBootVolume
->GetPackageVolumeInfo());
1178 selectedState
= sBootVolume
->GetPackageVolumeState();
1180 volumeInfo
.SetTo(new(std::nothrow
) PackageVolumeInfo
);
1181 if (volumeInfo
->SetTo(volume
, "system/packages") == B_OK
)
1182 selectedState
= volumeInfo
->States().Head();
1187 BootVolumeMenuItem
* item
= new(nothrow
) BootVolumeMenuItem(name
);
1188 menu
->AddItem(item
);
1190 Menu
* subMenu
= NULL
;
1192 if (volumeInfo
!= NULL
) {
1193 subMenu
= new(std::nothrow
) Menu(CHOICE_MENU
, "Select Haiku version");
1195 for (PackageVolumeStateList::ConstIterator it
1196 = volumeInfo
->States().GetIterator();
1197 PackageVolumeState
* state
= it
.Next();) {
1198 PackageVolumeStateMenuItem
* stateItem
1199 = new(nothrow
) PackageVolumeStateMenuItem(state
->DisplayName(),
1201 subMenu
->AddItem(stateItem
);
1202 stateItem
->SetTarget(user_menu_boot_volume_state
);
1203 stateItem
->SetData(volume
);
1205 if (state
== selectedState
) {
1206 stateItem
->SetMarked(true);
1207 stateItem
->Select(true);
1208 item
->UpdateStateName(stateItem
->VolumeState());
1213 if (subMenu
!= NULL
&& subMenu
->CountItems() > 1) {
1214 item
->SetSubmenu(subMenu
);
1217 item
->SetTarget(user_menu_boot_volume
);
1218 item
->SetData(volume
);
1221 if (volume
== sBootVolume
->RootDirectory()) {
1222 item
->SetMarked(true);
1229 add_boot_volume_menu()
1231 Menu
* menu
= new(std::nothrow
) Menu(CHOICE_MENU
, "Select Boot Volume");
1236 if (gRoot
->Open(&cookie
, O_RDONLY
) == B_OK
) {
1238 while (gRoot
->GetNextNode(cookie
, (Node
**)&volume
) == B_OK
) {
1239 // only list bootable volumes
1240 if (volume
!= sBootVolume
->RootDirectory() && !is_bootable(volume
))
1243 char name
[B_FILE_NAME_LENGTH
];
1244 if (volume
->GetName(name
, sizeof(name
)) == B_OK
) {
1245 add_boot_volume_item(menu
, volume
, name
);
1250 gRoot
->Close(cookie
);
1254 // no boot volume found yet
1255 menu
->AddItem(item
= new(nothrow
) MenuItem("<No boot volume found>"));
1256 item
->SetType(MENU_ITEM_NO_CHOICE
);
1257 item
->SetEnabled(false);
1260 menu
->AddSeparatorItem();
1262 menu
->AddItem(item
= new(nothrow
) MenuItem("Rescan volumes"));
1263 item
->SetHelpText("Please insert a Haiku CD-ROM or attach a USB disk - "
1264 "depending on your system, you can then boot from there.");
1265 item
->SetType(MENU_ITEM_NO_CHOICE
);
1269 menu
->AddItem(item
= new(nothrow
) MenuItem("Return to main menu"));
1270 item
->SetType(MENU_ITEM_NO_CHOICE
);
1272 if (gBootVolume
.GetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE
, false))
1273 menu
->SetChoiceText("CD-ROM or hard drive");
1280 add_safe_mode_menu()
1282 Menu
* safeMenu
= new(nothrow
) Menu(SAFE_MODE_MENU
, "Safe Mode Options");
1285 safeMenu
->AddItem(item
= new(nothrow
) MenuItem("Safe mode"));
1286 item
->SetData(B_SAFEMODE_SAFE_MODE
);
1287 item
->SetType(MENU_ITEM_MARKABLE
);
1288 item
->SetHelpText("Puts the system into safe mode. This can be enabled "
1289 "independently from the other options.");
1291 safeMenu
->AddItem(item
= new(nothrow
) MenuItem("Disable user add-ons"));
1292 item
->SetData(B_SAFEMODE_DISABLE_USER_ADD_ONS
);
1293 item
->SetType(MENU_ITEM_MARKABLE
);
1294 item
->SetHelpText("Prevents all user installed add-ons from being loaded. "
1295 "Only the add-ons in the system directory will be used.");
1297 safeMenu
->AddItem(item
= new(nothrow
) MenuItem("Disable IDE DMA"));
1298 item
->SetData(B_SAFEMODE_DISABLE_IDE_DMA
);
1299 item
->SetType(MENU_ITEM_MARKABLE
);
1300 item
->SetHelpText("Disables IDE DMA, increasing IDE compatibility "
1301 "at the expense of performance.");
1303 #if B_HAIKU_PHYSICAL_BITS > 32
1304 // check whether we have memory beyond 4 GB
1305 bool hasMemoryBeyond4GB
= false;
1306 for (uint32 i
= 0; i
< gKernelArgs
.num_physical_memory_ranges
; i
++) {
1307 addr_range
& range
= gKernelArgs
.physical_memory_range
[i
];
1308 if (range
.start
>= (uint64
)1 << 32) {
1309 hasMemoryBeyond4GB
= true;
1314 bool needs64BitPaging
= true;
1315 // TODO: Determine whether 64 bit paging (i.e. PAE for x86) is needed
1316 // for other reasons (NX support).
1318 // ... add the menu item, if so
1319 if (hasMemoryBeyond4GB
|| needs64BitPaging
) {
1321 item
= new(nothrow
) MenuItem("Ignore memory beyond 4 GiB"));
1322 item
->SetData(B_SAFEMODE_4_GB_MEMORY_LIMIT
);
1323 item
->SetType(MENU_ITEM_MARKABLE
);
1324 item
->SetHelpText("Ignores all memory beyond the 4 GiB address limit, "
1325 "overriding the setting in the kernel settings file.");
1329 platform_add_menus(safeMenu
);
1331 safeMenu
->AddSeparatorItem();
1332 sBlacklistRootMenu
= new(std::nothrow
) BlacklistRootMenu
;
1333 safeMenu
->AddItem(item
= new(std::nothrow
) MenuItem("Blacklist entries",
1334 sBlacklistRootMenu
));
1335 item
->SetHelpText("Allows to select system files that shall be ignored. "
1336 "Useful e.g. to disable drivers temporarily.");
1338 safeMenu
->AddSeparatorItem();
1339 safeMenu
->AddItem(item
= new(nothrow
) MenuItem("Return to main menu"));
1346 add_save_debug_syslog_menu()
1348 Menu
* menu
= new(nothrow
) Menu(STANDARD_MENU
, "Save syslog to volume ...");
1351 const char* const kHelpText
= "Currently only FAT32 volumes are supported. "
1352 "Newly plugged in removable devices are only recognized after "
1355 int32 itemsAdded
= 0;
1358 if (gRoot
->Open(&cookie
, O_RDONLY
) == B_OK
) {
1360 while (gRoot
->GetNextNode(cookie
, &node
) == B_OK
) {
1361 Directory
* volume
= static_cast<Directory
*>(node
);
1362 Partition
* partition
;
1363 if (gRoot
->GetPartitionFor(volume
, &partition
) != B_OK
)
1366 // we support only FAT32 volumes ATM
1367 if (partition
->content_type
== NULL
1368 || strcmp(partition
->content_type
, kPartitionTypeFAT32
) != 0) {
1372 char name
[B_FILE_NAME_LENGTH
];
1373 if (volume
->GetName(name
, sizeof(name
)) != B_OK
)
1374 strlcpy(name
, "unnamed", sizeof(name
));
1376 // append offset, size, and type to the name
1377 size_t len
= strlen(name
);
1378 char offsetBuffer
[32];
1379 char sizeBuffer
[32];
1380 snprintf(name
+ len
, sizeof(name
) - len
,
1381 " (%s, offset %s, size %s)", partition
->content_type
,
1382 size_to_string(partition
->offset
, offsetBuffer
,
1383 sizeof(offsetBuffer
)),
1384 size_to_string(partition
->size
, sizeBuffer
,
1385 sizeof(sizeBuffer
)));
1387 item
= new(nothrow
) MenuItem(name
);
1388 item
->SetData(volume
);
1389 item
->SetTarget(&debug_menu_save_previous_syslog
);
1390 item
->SetType(MENU_ITEM_NO_CHOICE
);
1391 item
->SetHelpText(kHelpText
);
1392 menu
->AddItem(item
);
1396 gRoot
->Close(cookie
);
1399 if (itemsAdded
== 0) {
1401 = new(nothrow
) MenuItem("No supported volumes found"));
1402 item
->SetType(MENU_ITEM_NO_CHOICE
);
1403 item
->SetHelpText(kHelpText
);
1404 item
->SetEnabled(false);
1407 menu
->AddSeparatorItem();
1408 menu
->AddItem(item
= new(nothrow
) MenuItem("Return to debug menu"));
1409 item
->SetHelpText(kHelpText
);
1418 Menu
* menu
= new(std::nothrow
) Menu(STANDARD_MENU
, "Debug Options");
1421 #if DEBUG_SPINLOCK_LATENCIES
1422 item
= new(std::nothrow
) MenuItem("Disable latency checks");
1424 item
->SetType(MENU_ITEM_MARKABLE
);
1425 item
->SetData(B_SAFEMODE_DISABLE_LATENCY_CHECK
);
1426 item
->SetHelpText("Disables latency check panics.");
1427 menu
->AddItem(item
);
1432 = new(nothrow
) MenuItem("Enable serial debug output"));
1433 item
->SetData("serial_debug_output");
1434 item
->SetType(MENU_ITEM_MARKABLE
);
1435 item
->SetHelpText("Turns on forwarding the syslog output to the serial "
1436 "interface (default: 115200, 8N1).");
1439 = new(nothrow
) MenuItem("Enable on screen debug output"));
1440 item
->SetData("debug_screen");
1441 item
->SetType(MENU_ITEM_MARKABLE
);
1442 item
->SetHelpText("Displays debug output on screen while the system "
1443 "is booting, instead of the normal boot logo.");
1446 = new(nothrow
) MenuItem("Disable on screen paging"));
1447 item
->SetData("disable_onscreen_paging");
1448 item
->SetType(MENU_ITEM_MARKABLE
);
1449 item
->SetHelpText("Disables paging when on screen debug output is "
1452 menu
->AddItem(item
= new(nothrow
) MenuItem("Enable debug syslog"));
1453 item
->SetType(MENU_ITEM_MARKABLE
);
1454 item
->SetMarked(gKernelArgs
.keep_debug_output_buffer
);
1455 item
->SetTarget(&debug_menu_toggle_debug_syslog
);
1456 item
->SetHelpText("Enables a special in-memory syslog buffer for this "
1457 "session that the boot loader will be able to access after rebooting.");
1459 ring_buffer
* syslogBuffer
1460 = (ring_buffer
*)gKernelArgs
.debug_output
.Pointer();
1461 bool hasPreviousSyslog
1462 = syslogBuffer
!= NULL
&& ring_buffer_readable(syslogBuffer
) > 0;
1463 if (hasPreviousSyslog
) {
1464 menu
->AddItem(item
= new(nothrow
) MenuItem(
1465 "Save syslog from previous session during boot"));
1466 item
->SetType(MENU_ITEM_MARKABLE
);
1467 item
->SetMarked(gKernelArgs
.previous_debug_size
);
1468 item
->SetTarget(&debug_menu_toggle_previous_debug_syslog
);
1469 item
->SetHelpText("Saves the syslog from the previous Haiku session to "
1470 "/var/log/previous_syslog when booting.");
1473 bool currentLogItemVisible
= platform_debug_get_log_buffer(NULL
) != NULL
;
1474 if (currentLogItemVisible
) {
1475 menu
->AddSeparatorItem();
1477 = new(nothrow
) MenuItem("Display current boot loader log"));
1478 item
->SetTarget(&debug_menu_display_current_log
);
1479 item
->SetType(MENU_ITEM_NO_CHOICE
);
1481 "Displays the debug info the boot loader has logged.");
1484 if (hasPreviousSyslog
) {
1485 if (!currentLogItemVisible
)
1486 menu
->AddSeparatorItem();
1489 = new(nothrow
) MenuItem("Display syslog from previous session"));
1490 item
->SetTarget(&debug_menu_display_previous_syslog
);
1491 item
->SetType(MENU_ITEM_NO_CHOICE
);
1493 "Displays the syslog from the previous Haiku session.");
1495 menu
->AddItem(item
= new(nothrow
) MenuItem(
1496 "Save syslog from previous session", add_save_debug_syslog_menu()));
1497 item
->SetHelpText("Saves the syslog from the previous Haiku session to "
1498 "disk. Currently only FAT32 volumes are supported.");
1501 menu
->AddSeparatorItem();
1502 menu
->AddItem(item
= new(nothrow
) MenuItem(
1503 "Add advanced debug option"));
1504 item
->SetType(MENU_ITEM_NO_CHOICE
);
1505 item
->SetTarget(&debug_menu_add_advanced_option
);
1507 "Allows advanced debugging options to be entered directly.");
1509 menu
->AddSeparatorItem();
1510 menu
->AddItem(item
= new(nothrow
) MenuItem("Return to main menu"));
1517 apply_safe_mode_options(Menu
* menu
)
1519 MenuItemIterator iterator
= menu
->ItemIterator();
1520 while (MenuItem
* item
= iterator
.Next()) {
1521 if (item
->Type() == MENU_ITEM_SEPARATOR
|| !item
->IsMarked()
1522 || item
->Data() == NULL
) {
1527 if (snprintf(buffer
, sizeof(buffer
), "%s true\n",
1528 (const char*)item
->Data()) >= (int)sizeof(buffer
)
1529 || !sSafeModeOptionsBuffer
.Append(buffer
)) {
1530 dprintf("apply_safe_mode_options(): failed to append option to "
1538 apply_safe_mode_path_blacklist()
1540 if (sPathBlacklist
->IsEmpty())
1543 bool success
= sSafeModeOptionsBuffer
.Append("EntryBlacklist {\n");
1545 for (PathBlacklist::Iterator it
= sPathBlacklist
->GetIterator();
1546 BlacklistedPath
* path
= it
.Next();) {
1547 success
&= sSafeModeOptionsBuffer
.Append(path
->Path());
1548 success
&= sSafeModeOptionsBuffer
.Append("\n", 1);
1551 success
&= sSafeModeOptionsBuffer
.Append("}\n");
1554 dprintf("apply_safe_mode_options(): failed to append path "
1555 "blacklist to buffer\n");
1561 user_menu_reboot(Menu
* menu
, MenuItem
* item
)
1569 user_menu(BootVolume
& _bootVolume
, PathBlacklist
& _pathBlacklist
)
1572 Menu
* menu
= new(std::nothrow
) Menu(MAIN_MENU
);
1575 sBootVolume
= &_bootVolume
;
1576 sPathBlacklist
= &_pathBlacklist
;
1578 Menu
* safeModeMenu
= NULL
;
1579 Menu
* debugMenu
= NULL
;
1582 TRACE(("user_menu: enter\n"));
1585 menu
->AddItem(item
= new(std::nothrow
) MenuItem("Select boot volume",
1586 add_boot_volume_menu()));
1589 menu
->AddItem(item
= new(std::nothrow
) MenuItem("Select safe mode options",
1590 safeModeMenu
= add_safe_mode_menu()));
1593 menu
->AddItem(item
= new(std::nothrow
) MenuItem("Select debug options",
1594 debugMenu
= add_debug_menu()));
1596 // Add platform dependent menus
1597 platform_add_menus(menu
);
1599 menu
->AddSeparatorItem();
1601 menu
->AddItem(item
= new(std::nothrow
) MenuItem("Reboot"));
1602 item
->SetTarget(user_menu_reboot
);
1603 item
->SetShortcut('r');
1605 menu
->AddItem(item
= new(std::nothrow
) MenuItem("Continue booting"));
1606 if (!_bootVolume
.IsValid()) {
1607 item
->SetEnabled(false);
1608 menu
->ItemAt(0)->Select(true);
1610 item
->SetShortcut('b');
1614 apply_safe_mode_options(safeModeMenu
);
1615 apply_safe_mode_options(debugMenu
);
1616 apply_safe_mode_path_blacklist();
1617 add_safe_mode_settings(sSafeModeOptionsBuffer
.String());
1621 TRACE(("user_menu: leave\n"));
1624 sBlacklistRootMenu
= NULL
;
1626 sPathBlacklist
= NULL
;