xref: /titanic_51/usr/src/boot/sys/boot/forth/beadm.4th (revision a281b0ef076f3b6cd24864ecb984a4b92dd8b1b8)
1\
2\ This file and its contents are supplied under the terms of the
3\ Common Development and Distribution License ("CDDL"), version 1.0.
4\ You may only use this file in accordance with the terms of version
5\ 1.0 of the CDDL.
6\
7\ A full copy of the text of the CDDL should have accompanied this
8\ source.  A copy of the CDDL is also available via the Internet at
9\ http://www.illumos.org/license/CDDL.
10
11\ Copyright 2017 Toomas Soome <tsoome@me.com>
12
13\ This module is implementing the beadm user command to support listing
14\ and switching Boot Environments (BE) from command line and
15\ support words to provide data for BE menu in loader menu system.
16\ Note: this module needs an update to provide proper BE vocabulary.
17
18only forth also support-functions also file-processing
19also file-processing definitions also parser
20also line-reading definitions also builtins definitions
21
22variable page_count
23variable page_remainder
240 page_count !
250 page_remainder !
26
27\ from menu.4th
28: +c! ( N C-ADDR/U K -- C-ADDR/U )
29	3 pick 3 pick	( n c-addr/u k -- n c-addr/u k n c-addr )
30	rot + c!	( n c-addr/u k n c-addr -- n c-addr/u )
31	rot drop	( n c-addr/u -- c-addr/u )
32;
33
34: get_value ( -- )
35	eat_space
36	line_pointer
37	skip_to_end_of_line
38	line_pointer over -
39	strdup value_buffer strset
40	['] exit to parsing_function
41;
42
43: get_name ( -- )
44	read_name
45	['] get_value to parsing_function
46;
47
48: get_name_value
49	line_buffer strget + to end_of_line
50	line_buffer .addr @ to line_pointer
51	['] get_name to parsing_function
52	begin
53		end_of_line? 0=
54	while
55		parsing_function execute
56	repeat
57;
58
59\ beadm support
60: beadm_longest_title ( addr len -- width )
61	0 to end_of_file?
62	O_RDONLY fopen fd !
63	reset_line_reading
64	fd @ -1 = if EOPEN throw then
65	0 >r		\ length into return stack
66	begin
67		end_of_file? 0=
68	while
69		free_buffers
70		read_line
71		get_name_value
72		value_buffer .len @ r@ > if r> drop value_buffer .len @ >r then
73		free_buffers
74		read_line
75	repeat
76	fd @ fclose
77	r> 1 +		\ space between columns
78;
79
80\ Pretty print BE list
81: beadm_list ( width addr len -- )
82	0 to end_of_file?
83	O_RDONLY fopen fd !
84	reset_line_reading
85	fd @ -1 = if EOPEN throw then
86	." BE" dup 2 - spaces ." Type    Device" cr
87	begin
88		end_of_file? 0=
89	while
90		free_buffers
91		read_line
92		get_name_value
93		value_buffer strget type
94		dup value_buffer .len @ - spaces
95		free_buffers
96		read_line
97		get_name_value
98		name_buffer strget type
99		name_buffer strget s" bootfs" compare 0= if 2 spaces then
100		name_buffer strget s" chain" compare 0= if 3 spaces then
101		value_buffer strget type cr
102		free_buffers
103	repeat
104	fd @ fclose
105	drop
106;
107
108\ we are called with strings be_name menu_file, to simplify the stack
109\ management, we open the menu and free the menu_file.
110: beadm_bootfs ( be_addr be_len maddr mlen -- addr len taddr tlen flag | flag )
111	0 to end_of_file?
112	2dup O_RDONLY fopen fd !
113	drop free-memory
114	fd @ -1 = if EOPEN throw then
115	reset_line_reading
116	begin
117		end_of_file? 0=
118	while
119		free_buffers
120		read_line
121		get_name_value
122		2dup value_buffer strget compare
123		0= if ( title == be )
124			2drop		\ drop be_name
125			free_buffers
126			read_line
127			get_name_value
128			value_buffer strget strdup
129			name_buffer strget strdup -1
130			free_buffers
131			1 to end_of_file? \ mark end of file to skip the rest
132		else
133			read_line	\ skip over next line
134		then
135	repeat
136	fd @ fclose
137	line_buffer strfree
138	read_buffer strfree
139	dup -1 > if ( be_addr be_len )
140		2drop
141		0
142	then
143;
144
145: current-dev ( -- addr len ) \ return current dev
146	s" currdev" getenv
147	2dup [char] / strchr nip
148	dup 0> if ( strchr '/' != NULL ) - else drop then
149	\ we have now zfs:pool or diskname:
150;
151
152\ chop trailing ':'
153: colon- ( addr len -- addr len - 1 | addr len )
154	2dup 1 - + C@ [char] : = if ( string[len-1] == ':' ) 1 - then
155;
156
157\ add trailing ':'
158: colon+ ( addr len -- addr len+1 )
159	2dup +			\ addr len -- addr+len
160	[char] : swap c!	\ save ':' at the end of the string
161	1+			\ addr len -- addr len+1
162;
163
164\ make menu.lst path
165: menu.lst ( addr len -- addr' len' )
166	colon-
167	\ need to allocate space for len + 16
168	dup 16 + allocate if ENOMEM throw then
169	swap 2dup 2>R	\ copy of new addr len to return stack
170	move 2R>
171	s" :/boot/menu.lst" strcat
172;
173
174\ list be's on device
175: list-dev ( addr len -- )
176	menu.lst 2dup 2>R
177	beadm_longest_title
178	line_buffer strfree
179	read_buffer strfree
180	R@ swap 2R>	\ addr width addr len
181	beadm_list free-memory
182	." Current boot device: " s" currdev" getenv type cr
183	line_buffer strfree
184	read_buffer strfree
185;
186
187\ activate be on device.
188\ if be name was not given, set currdev
189\ otherwize, we query device:/boot/menu.lst for bootfs and
190\ if found, and bootfs type is chain, attempt chainload.
191\ set currdev to bootfs.
192\ if we were able to set currdev, reload the config
193
194: activate-dev ( dev.addr dev.len be.addr be.len -- )
195
196	dup 0= if
197		2drop
198		colon-			\ remove : at the end of the dev name
199		dup 1+ allocate if ENOMEM throw then
200		dup 2swap 0 -rot strcat
201		colon+
202		s" currdev" setenv	\ setenv currdev = device
203		free-memory
204	else
205		2swap menu.lst
206		beadm_bootfs if ( addr len taddr tlen )
207			2dup s" chain" compare 0= if
208				drop free-memory	\ free type
209				2dup
210				dup 6 + allocate if ENOMEM throw then
211				dup >R
212				0 s" chain " strcat
213				2swap strcat ['] evaluate catch drop
214				\ We are still there?
215				R> free-memory		\ free chain command
216				drop free-memory	\ free addr
217				exit
218			then
219			drop free-memory		\ free type
220			2dup [char] : strchr nip 0= if
221				\ have dataset and need to get zfs:pool/ROOT/be:
222				dup 5 + allocate if ENOMEM throw then
223				0 s" zfs:" strcat
224				2swap strcat
225				colon+
226			then
227			2dup s" currdev" setenv
228			drop free-memory
229		else
230			." No such BE in menu.lst or menu.lst is missing." cr
231			exit
232		then
233	then
234
235	\ reset BE menu
236	0 page_count !
237	\ need to do:
238	0 unload drop
239	free-module-options
240	\ unset the env variables with kernel arguments
241	s" acpi-user-options" unsetenv
242	s" boot-args" unsetenv
243	s" boot_ask" unsetenv
244	s" boot_single" unsetenv
245	s" boot_verbose" unsetenv
246	s" boot_kmdb" unsetenv
247	s" boot_debug" unsetenv
248	s" boot_reconfigure" unsetenv
249	start			\ load config, kernel and modules
250	." Current boot device: " s" currdev" getenv type cr
251;
252
253\ beadm list [device]
254\ beadm activate BE [device] | device
255\
256\ lists BE's from current or specified device /boot/menu.lst file
257\ activates specified BE by unloading modules, setting currdev and
258\ running start to load configuration.
259: beadm ( -- ) ( throws: abort )
260	0= if ( interpreted ) get_arguments then
261
262	dup 0= if
263		." Usage:" cr
264		." beadm activate {beName [device] | device}" cr
265		." beadm list [device]" cr
266		." Use lsdev to get device names." cr
267		drop exit
268	then
269	\ First argument is 0 when we're interprated.  See support.4th
270	\ for get_arguments reading the rest of the line and parsing it
271	\ stack: argN lenN ... arg1 len1 N
272	\ rotate arg1 len1, dont use argv[] as we want to get arg1 out of stack
273	-rot 2dup
274
275	s" list" compare-insensitive 0= if ( list )
276		2drop
277		argc 1 = if ( list currdev )
278			\ add dev to list of args and switch to case 2
279			current-dev rot 1 +
280		then
281		2 = if ( list device ) list-dev exit then
282		." too many arguments" cr abort
283	then
284	s" activate" compare-insensitive 0= if ( activate )
285		argc 1 = if ( missing be )
286			drop ." missing bName" cr abort
287		then
288		argc 2 = if ( activate be )
289			\ need to set arg list into proper order
290			1+ >R	\ save argc+1 to return stack
291				\ if we have : in name, its device, inject
292				\ empty be name
293			2dup [char] : strchr nip
294			if ( its : in name )
295				0 0 R>
296			else
297				\ add device, swap with be and receive argc
298				current-dev 2swap R>
299			then
300		then
301		3 = if ( activate be device ) activate-dev exit then
302		." too many arguments" cr abort
303	then
304	." Unknown argument" cr abort
305;
306
307also forth definitions also builtins
308
309\ make beadm available as user command.
310builtin: beadm
311
312\ count the pages of BE list
313\ leave FALSE in stack in case of error
314: be-pages ( -- flag )
315	1 local flag
316	0 0 2local currdev
317	0 0 2local title
318	end-locals
319
320	current-dev menu.lst 2dup 2>R
321	0 to end_of_file?
322	O_RDONLY fopen fd !
323	2R> drop free-memory
324	reset_line_reading
325	fd @ -1 = if FALSE else
326		s" currdev" getenv
327		over			( addr len addr )
328		4 s" zfs:" compare 0= if
329			5 -			\ len -= 5
330			swap 4 +		\ addr += 4
331			swap to currdev
332		then
333
334		0
335		begin
336			end_of_file? 0=
337		while
338			read_line
339			get_name_value
340			s" title" name_buffer strget compare
341			0= if 1+ then
342
343			flag if		\ check for title
344				value_buffer strget strdup to title free_buffers
345				read_line		\ get bootfs
346				get_name_value
347				value_buffer strget currdev compare 0= if
348					title s" zfs_be_active" setenv
349					0 to flag
350				then
351				title drop free-memory 0 0 to title
352				free_buffers
353			else
354				free_buffers
355				read_line		\ get bootfs
356			then
357		repeat
358		fd @ fclose
359		line_buffer strfree
360		read_buffer strfree
361		5 /mod swap dup page_remainder !		\ save remainder
362		if 1+ then
363		dup page_count !				\ save count
364		s>d <# #s #> s" zfs_be_pages" setenv
365		TRUE
366	then
367;
368
369: be-set-page { | entry count n device -- }
370	page_count @ 0= if
371		be-pages
372		page_count @ 0= if exit then
373	then
374
375	0 to device
376	s" zfs_be_currpage" getenv dup -1 = if
377		drop s" 1"
378	then
379	0 s>d 2swap
380	>number ( ud caddr/u -- ud' caddr'/u' )
381	2drop
382	1 um/mod nip 5 *
383	page_count @ 5 *
384	page_remainder @ if
385		5 page_remainder @ - -
386	then
387	swap -
388	dup to entry
389	0 < if
390		entry 5 + to count
391		0 to entry
392	else
393		5 to count
394	then
395	current-dev menu.lst 2dup 2>R
396	0 to end_of_file?
397	O_RDONLY fopen fd !
398	2R> drop free-memory
399	reset_line_reading
400	fd @ -1 = if EOPEN throw then
401	0 to n
402	begin
403		end_of_file? 0=
404	while
405		n entry < if
406			read_line		\ skip title
407			read_line		\ skip bootfs
408			n 1+ to n
409		else
410			\ Use reverse loop to display descending order
411			\ for BE list.
412			0 count 1- do
413				read_line		\ read title line
414				get_name_value
415				value_buffer strget
416				52 i +			\ ascii 4 + i
417				s" bootenvmenu_caption[4]" 20 +c! setenv
418				value_buffer strget
419				52 i +			\ ascii 4 + i
420				s" bootenvansi_caption[4]" 20 +c! setenv
421
422				free_buffers
423				read_line		\ read value line
424				get_name_value
425
426				\ set menu entry command
427				name_buffer strget s" chain" compare
428				0= if
429					s" set_be_chain"
430				else
431					s" set_bootenv"
432				then
433				52 i +			\ ascii 4 + i
434				s" bootenvmenu_command[4]" 20 +c! setenv
435
436				\ set device name
437				name_buffer strget s" chain" compare
438				0= if
439					\ for chain, use the value as is
440					value_buffer strget
441				else
442					value_buffer strget 2dup
443					[char] : strchr nip
444					0= if
445						\ make zfs device name
446						swap drop
447						5 + allocate if
448							ENOMEM throw
449						then
450						s" zfs:" ( addr addr' len' )
451						2 pick swap move ( addr )
452						dup to device
453						4 value_buffer strget
454						strcat	( addr len )
455						s" :" strcat
456					then
457				then
458
459				52 i +			\ ascii 4 + i
460				s" bootenv_root[4]" 13 +c! setenv
461				device free-memory 0 to device
462				free_buffers
463				-1
464			+loop
465
466			5 count do		\ unset unused entries
467				52 i +			\ ascii 4 + i
468				dup s" bootenvmenu_caption[4]" 20 +c! unsetenv
469				dup s" bootenvansi_caption[4]" 20 +c! unsetenv
470				dup s" bootenvmenu_command[4]" 20 +c! unsetenv
471				s" bootenv_root[4]" 13 +c! unsetenv
472			loop
473
474			1 to end_of_file?		\ we are done
475		then
476	repeat
477	fd @ fclose
478	line_buffer strfree
479	read_buffer strfree
480;
481