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