xref: /haiku/src/system/boot/loader/menu.cpp (revision 362efe0c9f36d3dd38b22d2c24ac02e54b189d7c)
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