xref: /haiku/src/system/boot/loader/menu.cpp (revision ff3409e005096a604d862a776e470aae2089865d)
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 	gBootVolume.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.Pointer();
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
599 		= (ring_buffer*)gKernelArgs.debug_output.Pointer();
600 	iovec vecs[2];
601 	int32 vecCount = ring_buffer_get_vecs(syslogBuffer, vecs);
602 	if (vecCount > 0) {
603 		size_t toWrite = ring_buffer_readable(syslogBuffer);
604 
605 		ssize_t written = writev(fd, vecs, vecCount);
606 		if (written < 0 || (size_t)written != toWrite) {
607 			printf("Failed to write to the syslog file \"%s\"!\n", name);
608 			close(fd);
609 			return errno;
610 		}
611 	}
612 
613 	close(fd);
614 
615 	printf("Successfully wrote syslog file.\n");
616 
617 	return B_OK;
618 }
619 
620 
621 static bool
622 debug_menu_add_advanced_option(Menu* menu, MenuItem* item)
623 {
624 	char buffer[256];
625 
626 	size_t size = platform_get_user_input_text(menu, item, buffer,
627 		sizeof(buffer) - 1);
628 
629 	if (size > 0) {
630 		buffer[size] = '\n';
631 		size_t pos = strlen(sSafeModeOptionsBuffer);
632 		if (pos + size + 1 < sizeof(sSafeModeOptionsBuffer))
633 			strlcat(sSafeModeOptionsBuffer, buffer,
634 				sizeof(sSafeModeOptionsBuffer));
635 	}
636 
637 	return true;
638 }
639 
640 
641 static bool
642 debug_menu_toggle_debug_syslog(Menu* menu, MenuItem* item)
643 {
644 	gKernelArgs.keep_debug_output_buffer = item->IsMarked();
645 	return true;
646 }
647 
648 
649 static bool
650 debug_menu_save_previous_syslog(Menu* menu, MenuItem* item)
651 {
652 	Directory* volume = (Directory*)item->Data();
653 
654 	console_clear_screen();
655 
656 	save_previous_syslog_to_volume(volume);
657 
658 	printf("\nPress any key to continue\n");
659 	console_wait_for_key();
660 
661 	return true;
662 }
663 
664 
665 static Menu*
666 add_boot_volume_menu(Directory* bootVolume)
667 {
668 	Menu* menu = new(std::nothrow) Menu(CHOICE_MENU, "Select Boot Volume");
669 	MenuItem* item;
670 	void* cookie;
671 	int32 count = 0;
672 
673 	if (gRoot->Open(&cookie, O_RDONLY) == B_OK) {
674 		Directory* volume;
675 		while (gRoot->GetNextNode(cookie, (Node**)&volume) == B_OK) {
676 			// only list bootable volumes
677 			if (!is_bootable(volume))
678 				continue;
679 
680 			char name[B_FILE_NAME_LENGTH];
681 			if (volume->GetName(name, sizeof(name)) == B_OK) {
682 				menu->AddItem(item = new(nothrow) MenuItem(name));
683 				item->SetTarget(user_menu_boot_volume);
684 				item->SetData(volume);
685 
686 				if (volume == bootVolume) {
687 					item->SetMarked(true);
688 					item->Select(true);
689 				}
690 
691 				count++;
692 			}
693 		}
694 		gRoot->Close(cookie);
695 	}
696 
697 	if (count == 0) {
698 		// no boot volume found yet
699 		menu->AddItem(item = new(nothrow) MenuItem("<No boot volume found>"));
700 		item->SetType(MENU_ITEM_NO_CHOICE);
701 		item->SetEnabled(false);
702 	}
703 
704 	menu->AddSeparatorItem();
705 
706 	menu->AddItem(item = new(nothrow) MenuItem("Rescan volumes"));
707 	item->SetHelpText("Please insert a Haiku CD-ROM or attach a USB disk - "
708 		"depending on your system, you can then boot from there.");
709 	item->SetType(MENU_ITEM_NO_CHOICE);
710 	if (count == 0)
711 		item->Select(true);
712 
713 	menu->AddItem(item = new(nothrow) MenuItem("Return to main menu"));
714 	item->SetType(MENU_ITEM_NO_CHOICE);
715 
716 	if (gBootVolume.GetBool(BOOT_VOLUME_BOOTED_FROM_IMAGE, false))
717 		menu->SetChoiceText("CD-ROM or hard drive");
718 
719 	return menu;
720 }
721 
722 
723 static Menu*
724 add_safe_mode_menu()
725 {
726 	Menu* safeMenu = new(nothrow) Menu(SAFE_MODE_MENU, "Safe Mode Options");
727 	MenuItem* item;
728 
729 	safeMenu->AddItem(item = new(nothrow) MenuItem("Safe mode"));
730 	item->SetData(B_SAFEMODE_SAFE_MODE);
731 	item->SetType(MENU_ITEM_MARKABLE);
732 	item->SetHelpText("Puts the system into safe mode. This can be enabled "
733 		"independently from the other options.");
734 
735 	safeMenu->AddItem(item = new(nothrow) MenuItem("Disable user add-ons"));
736 	item->SetData(B_SAFEMODE_DISABLE_USER_ADD_ONS);
737 	item->SetType(MENU_ITEM_MARKABLE);
738     item->SetHelpText("Prevents all user installed add-ons from being loaded. "
739 		"Only the add-ons in the system directory will be used.");
740 
741 	safeMenu->AddItem(item = new(nothrow) MenuItem("Disable IDE DMA"));
742 	item->SetData(B_SAFEMODE_DISABLE_IDE_DMA);
743 	item->SetType(MENU_ITEM_MARKABLE);
744     item->SetHelpText("Disables IDE DMA, increasing IDE compatibility "
745 		"at the expense of performance.");
746 
747 #if B_HAIKU_PHYSICAL_BITS > 32
748 	// check whether we have memory beyond 4 GB
749 	bool hasMemoryBeyond4GB = false;
750 	for (uint32 i = 0; i < gKernelArgs.num_physical_memory_ranges; i++) {
751 		addr_range& range = gKernelArgs.physical_memory_range[i];
752 		if (range.start >= (uint64)1 << 32) {
753 			hasMemoryBeyond4GB = true;
754 			break;
755 		}
756 	}
757 
758 	// ... add the menu, if so
759 	if (hasMemoryBeyond4GB) {
760 		safeMenu->AddItem(
761 			item = new(nothrow) MenuItem("Ignore memory beyond 4 GiB"));
762 		item->SetData(B_SAFEMODE_4_GB_MEMORY_LIMIT);
763 		item->SetType(MENU_ITEM_MARKABLE);
764 		item->SetHelpText("Ignores all memory beyond the 4 GiB address limit, "
765 			"overriding the setting in the kernel settings file.");
766 	}
767 #endif
768 
769 	platform_add_menus(safeMenu);
770 
771 	safeMenu->AddSeparatorItem();
772 	safeMenu->AddItem(item = new(nothrow) MenuItem("Return to main menu"));
773 
774 	return safeMenu;
775 }
776 
777 
778 static Menu*
779 add_save_debug_syslog_menu()
780 {
781 	Menu* menu = new(nothrow) Menu(STANDARD_MENU, "Save syslog to volume ...");
782 	MenuItem* item;
783 
784 	const char* const kHelpText = "Currently only FAT32 volumes are supported. "
785 		"Newly plugged in removable devices are only recognized after "
786 		"rebooting.";
787 
788 	int32 itemsAdded = 0;
789 
790 	void* cookie;
791 	if (gRoot->Open(&cookie, O_RDONLY) == B_OK) {
792 		Node* node;
793 		while (gRoot->GetNextNode(cookie, &node) == B_OK) {
794 			Directory* volume = static_cast<Directory*>(node);
795 			Partition* partition;
796 			if (gRoot->GetPartitionFor(volume, &partition) != B_OK)
797 				continue;
798 
799 			// we support only FAT32 volumes ATM
800 			if (partition->content_type == NULL
801 				|| strcmp(partition->content_type, kPartitionTypeFAT32) != 0) {
802 				continue;
803 			}
804 
805 			char name[B_FILE_NAME_LENGTH];
806 			if (volume->GetName(name, sizeof(name)) != B_OK)
807 				strlcpy(name, "unnamed", sizeof(name));
808 
809 			// append offset, size, and type to the name
810 			size_t len = strlen(name);
811 			char offsetBuffer[32];
812 			char sizeBuffer[32];
813 			snprintf(name + len, sizeof(name) - len,
814 				" (%s, offset %s, size %s)", partition->content_type,
815 				size_to_string(partition->offset, offsetBuffer,
816 					sizeof(offsetBuffer)),
817 				size_to_string(partition->size, sizeBuffer,
818 					sizeof(sizeBuffer)));
819 
820 			item = new(nothrow) MenuItem(name);
821 			item->SetData(volume);
822 			item->SetTarget(&debug_menu_save_previous_syslog);
823 			item->SetType(MENU_ITEM_NO_CHOICE);
824 			item->SetHelpText(kHelpText);
825 			menu->AddItem(item);
826 			itemsAdded++;
827 		}
828 
829 		gRoot->Close(cookie);
830 	}
831 
832 	if (itemsAdded == 0) {
833 		menu->AddItem(item
834 			= new(nothrow) MenuItem("No supported volumes found"));
835 		item->SetType(MENU_ITEM_NO_CHOICE);
836 		item->SetHelpText(kHelpText);
837 		item->SetEnabled(false);
838 	}
839 
840 	menu->AddSeparatorItem();
841 	menu->AddItem(item = new(nothrow) MenuItem("Return to debug menu"));
842 	item->SetHelpText(kHelpText);
843 
844 	return menu;
845 }
846 
847 
848 static Menu*
849 add_debug_menu()
850 {
851 	Menu* menu = new(std::nothrow) Menu(STANDARD_MENU, "Debug Options");
852 	MenuItem* item;
853 
854 #if DEBUG_SPINLOCK_LATENCIES
855 	item = new(std::nothrow) MenuItem("Disable latency checks");
856 	if (item != NULL) {
857 		item->SetType(MENU_ITEM_MARKABLE);
858 		item->SetData(B_SAFEMODE_DISABLE_LATENCY_CHECK);
859 		item->SetHelpText("Disables latency check panics.");
860 		menu->AddItem(item);
861 	}
862 #endif
863 
864 	menu->AddItem(item
865 		= new(nothrow) MenuItem("Enable serial debug output"));
866 	item->SetData("serial_debug_output");
867 	item->SetType(MENU_ITEM_MARKABLE);
868 	item->SetHelpText("Turns on forwarding the syslog output to the serial "
869 		"interface (default: 115200, 8N1).");
870 
871 	menu->AddItem(item
872 		= new(nothrow) MenuItem("Enable on screen debug output"));
873 	item->SetData("debug_screen");
874 	item->SetType(MENU_ITEM_MARKABLE);
875 	item->SetHelpText("Displays debug output on screen while the system "
876 		"is booting, instead of the normal boot logo.");
877 
878 	menu->AddItem(item
879 		= new(nothrow) MenuItem("Disable on screen paging"));
880 	item->SetData("disable_onscreen_paging");
881 	item->SetType(MENU_ITEM_MARKABLE);
882 	item->SetHelpText("Disables paging when on screen debug output is "
883 		"enabled.");
884 
885 	menu->AddItem(item = new(nothrow) MenuItem("Enable debug syslog"));
886 	item->SetType(MENU_ITEM_MARKABLE);
887 	item->SetMarked(gKernelArgs.keep_debug_output_buffer);
888 	item->SetTarget(&debug_menu_toggle_debug_syslog);
889     item->SetHelpText("Enables a special in-memory syslog buffer for this "
890     	"session that the boot loader will be able to access after rebooting.");
891 
892 	bool currentLogItemVisible = platform_debug_get_log_buffer(NULL) != NULL;
893 	if (currentLogItemVisible) {
894 		menu->AddSeparatorItem();
895 		menu->AddItem(item
896 			= new(nothrow) MenuItem("Display current boot loader log"));
897 		item->SetTarget(&debug_menu_display_current_log);
898 		item->SetType(MENU_ITEM_NO_CHOICE);
899 		item->SetHelpText(
900 			"Displays the debug info the boot loader has logged.");
901 	}
902 
903 	ring_buffer* syslogBuffer
904 		= (ring_buffer*)gKernelArgs.debug_output.Pointer();
905 	if (syslogBuffer != NULL && ring_buffer_readable(syslogBuffer) > 0) {
906 		if (!currentLogItemVisible)
907 			menu->AddSeparatorItem();
908 
909 		menu->AddItem(item
910 			= new(nothrow) MenuItem("Display syslog from previous session"));
911 		item->SetTarget(&debug_menu_display_previous_syslog);
912 		item->SetType(MENU_ITEM_NO_CHOICE);
913 		item->SetHelpText(
914 			"Displays the syslog from the previous Haiku session.");
915 
916 		menu->AddItem(item = new(nothrow) MenuItem(
917 			"Save syslog from previous session", add_save_debug_syslog_menu()));
918 		item->SetHelpText("Saves the syslog from the previous Haiku session to "
919 			"disk. Currently only FAT32 volumes are supported.");
920 	}
921 
922 	menu->AddSeparatorItem();
923 	menu->AddItem(item = new(nothrow) MenuItem(
924 		"Add advanced debug option"));
925 	item->SetType(MENU_ITEM_NO_CHOICE);
926 	item->SetTarget(&debug_menu_add_advanced_option);
927 	item->SetHelpText(
928 		"Allows advanced debugging options to be entered directly.");
929 
930 	menu->AddSeparatorItem();
931 	menu->AddItem(item = new(nothrow) MenuItem("Return to main menu"));
932 
933 	return menu;
934 }
935 
936 
937 static void
938 apply_safe_mode_options(Menu* menu)
939 {
940 	int32 pos = strlen(sSafeModeOptionsBuffer);
941 	size_t bufferSize = sizeof(sSafeModeOptionsBuffer);
942 
943 	MenuItemIterator iterator = menu->ItemIterator();
944 	while (MenuItem* item = iterator.Next()) {
945 		if (item->Type() == MENU_ITEM_SEPARATOR || !item->IsMarked()
946 			|| item->Data() == NULL || (uint32)pos >= bufferSize)
947 			continue;
948 
949 		size_t totalBytes = snprintf(sSafeModeOptionsBuffer + pos,
950 			bufferSize - pos, "%s true\n", (const char*)item->Data());
951 		pos += std::min(totalBytes, bufferSize - pos - 1);
952 	}
953 }
954 
955 
956 static bool
957 user_menu_reboot(Menu* menu, MenuItem* item)
958 {
959 	platform_exit();
960 	return true;
961 }
962 
963 
964 status_t
965 user_menu(Directory** _bootVolume)
966 {
967 	Menu* menu = new(std::nothrow) Menu(MAIN_MENU);
968 	Menu* safeModeMenu = NULL;
969 	Menu* debugMenu = NULL;
970 	MenuItem* item;
971 
972 	TRACE(("user_menu: enter\n"));
973 
974 	memset(sSafeModeOptionsBuffer, 0, sizeof(sSafeModeOptionsBuffer));
975 
976 	// Add boot volume
977 	menu->AddItem(item = new(std::nothrow) MenuItem("Select boot volume",
978 		add_boot_volume_menu(*_bootVolume)));
979 
980 	// Add safe mode
981 	menu->AddItem(item = new(std::nothrow) MenuItem("Select safe mode options",
982 		safeModeMenu = add_safe_mode_menu()));
983 
984 	// add debug menu
985 	menu->AddItem(item = new(std::nothrow) MenuItem("Select debug options",
986 		debugMenu = add_debug_menu()));
987 
988 	// Add platform dependent menus
989 	platform_add_menus(menu);
990 
991 	menu->AddSeparatorItem();
992 
993 	menu->AddItem(item = new(std::nothrow) MenuItem("Reboot"));
994 	item->SetTarget(user_menu_reboot);
995 	item->SetShortcut('r');
996 
997 	menu->AddItem(item = new(std::nothrow) MenuItem("Continue booting"));
998 	if (*_bootVolume == NULL) {
999 		item->SetEnabled(false);
1000 		menu->ItemAt(0)->Select(true);
1001 	} else
1002 		item->SetShortcut('b');
1003 
1004 	menu->Run();
1005 
1006 	// See if a new boot device has been selected, and propagate that back
1007 	if (item->Data() != NULL)
1008 		*_bootVolume = (Directory*)item->Data();
1009 
1010 	apply_safe_mode_options(safeModeMenu);
1011 	apply_safe_mode_options(debugMenu);
1012 	add_safe_mode_settings(sSafeModeOptionsBuffer);
1013 	delete menu;
1014 
1015 
1016 	TRACE(("user_menu: leave\n"));
1017 
1018 	return B_OK;
1019 }
1020 
1021