1#
2#  linux_logo in i386 assembly language
3#    based on the code from ll_asm-0.36
4#
5#  By Vince Weaver <vince _at_ deater.net>
6#
7# Modified to remove non-deterministic system calls
8# And to avoid reading from /proc
9#
10
11.include "logo.include"
12
13# offsets into the results returned by the uname syscall
14.equ U_SYSNAME,0
15.equ U_NODENAME,65
16.equ U_RELEASE,65*2
17.equ U_VERSION,(65*3)
18.equ U_MACHINE,(65*4)
19.equ U_DOMAINNAME,65*5
20
21# offset into the results returned by the sysinfo syscall
22.equ S_TOTALRAM,16
23
24# Sycscalls
25.equ SYSCALL_EXIT,     1
26.equ SYSCALL_WRITE,    4
27
28#
29.equ STDIN,0
30.equ STDOUT,1
31.equ STDERR,2
32
33	.globl _start
34_start:
35	#=========================
36	# PRINT LOGO
37	#=========================
38
39# LZSS decompression algorithm implementation
40# by Stephan Walter 2002, based on LZSS.C by Haruhiko Okumura 1989
41# optimized some more by Vince Weaver
42
43	# we used to fill the buffer with FREQUENT_CHAR
44	# but, that only gains us one byte of space in the lzss image.
45	# the lzss algorithm does automatic RLE... pretty clever
46	# so we compress with NUL as FREQUENT_CHAR and it is pre-done for us
47
48	mov     $(N-F), %bp   	     	# R
49
50	mov  	$logo, %esi		# %esi points to logo (for lodsb)
51
52	mov	$out_buffer, %edi	# point to out_buffer
53	push	%edi	     		# save this value for later
54
55decompression_loop:
56	lodsb			# load in a byte
57
58	mov 	$0xff, %bh	# re-load top as a hackish 8-bit counter
59	mov 	%al, %bl	# move in the flags
60
61test_flags:
62	cmp	$logo_end, %esi # have we reached the end?
63	je	done_logo  	# if so, exit
64
65	shr 	$1, %ebx	# shift bottom bit into carry flag
66	jc	discrete_char	# if set, we jump to discrete char
67
68offset_length:
69	lodsw                   # get match_length and match_position
70	mov %eax,%edx		# copy to edx
71	    			# no need to mask dx, as we do it
72				# by default in output_loop
73
74	shr $(P_BITS),%eax
75	add $(THRESHOLD+1),%al
76	mov %al,%cl             # cl = (ax >> P_BITS) + THRESHOLD + 1
77				#                       (=match_length)
78
79output_loop:
80	and 	$POSITION_MASK,%dh  	# mask it
81	mov 	text_buf(%edx), %al	# load byte from text_buf[]
82	inc 	%edx	    		# advance pointer in text_buf
83store_byte:
84	stosb				# store it
85
86	mov     %al, text_buf(%ebp)	# store also to text_buf[r]
87	inc 	%ebp 			# r++
88	and 	$(N-1), %bp		# mask r
89
90	loop 	output_loop		# repeat until k>j
91
92	or	%bh,%bh			# if 0 we shifted through 8 and must
93	jnz	test_flags		# re-load flags
94
95	jmp 	decompression_loop
96
97discrete_char:
98	lodsb				# load a byte
99	inc	%ecx			# we set ecx to one so byte
100					# will be output once
101					# (how do we know ecx is zero?)
102
103	jmp     store_byte              # and cleverly store it
104
105
106# end of LZSS code
107
108done_logo:
109
110	pop 	%ebp			# get out_buffer and keep in bp
111	mov	%ebp,%ecx		# move out_buffer to ecx
112
113	call	write_stdout		# print the logo
114
115	#
116	#  Setup
117	#
118setup:
119	mov	$strcat,%edx		# use edx as call pointer
120
121
122	#==========================
123	# PRINT VERSION
124	#==========================
125
126#	push 	$SYSCALL_UNAME		# uname syscall
127#	pop	%eax			# in 3 bytes
128#	mov	$uname_info,%ebx	# uname struct
129#	int	$0x80			# do syscall
130
131	mov	%ebp,%edi		# point %edi to out_buffer
132
133	mov	$(uname_info+U_SYSNAME),%esi	# os-name from uname "Linux"
134	call	*%edx			# call strcat
135
136	mov	$ver_string,%esi		# source is " Version "
137	call 	*%edx			        # call strcat
138	push	%esi  				# save our .txt pointer
139
140	mov	$(uname_info+U_RELEASE),%esi    # version from uname "2.4.1"
141	call 	*%edx				# call strcat
142
143	pop	%esi  			# restore .txt pointer
144					# source is ", Compiled "
145	call 	*%edx			# call strcat
146	push	%esi  			# store for later
147
148	mov	$(uname_info+U_VERSION),%esi	# compiled date
149	call 	*%edx			# call strcat
150
151	mov	%ebp,%ecx		# move out_buffer to ecx
152
153	mov	$0xa,%ax		# store linefeed on end
154	stosw				# and zero
155
156	call	*%edx			# call strcat
157
158	call	center_and_print	# center and print
159
160	#===============================
161	# Middle-Line
162	#===============================
163
164	#=========
165	# Load /proc/cpuinfo into buffer
166	#=========
167
168	push	%edx			# save call pointer
169
170#	push	$SYSCALL_OPEN		# load 5 [ open() ]
171#	pop	%eax			# in 3 bytes
172
173#	mov	$cpuinfo,%ebx		# '/proc/cpuinfo'
174#	xor	%ecx,%ecx		# 0 = O_RDONLY <bits/fcntl.h>
175#	cdq				# clear edx in clever way
176#	int	$0x80			# syscall.  fd in eax.
177					# we should check that eax>=0
178
179#	mov	%eax,%ebx		# save our fd
180
181#	push	$SYSCALL_READ		# load 3 = read()
182#	pop	%eax			# in 3 bytes
183
184	mov	$disk_buffer,%ecx
185
186#	mov	$16,%dh		 	# 4096 is maximum size of proc file #)
187					# we load sneakily by knowing
188					# 16<<8 = 4096. be sure edx clear
189
190
191#	int	$0x80
192
193#	push	$SYSCALL_CLOSE		# close (to be correct)
194#	pop	%eax
195#	int	$0x80
196
197	#=============
198	# Number of CPUs
199	#=============
200number_of_cpus:
201
202	xor	%ebx,%ebx		# chip count
203
204					# $disk_buffer still in ecx
205bogo_loop:
206	mov	(%ecx), %eax		# load 4 bytes into eax
207	inc	%ecx			# increment pointer
208
209	cmp	$0,%al			# check for end of file
210	je	done_bogo
211
212	# Grrr, due to a bug in binutils 2.18.50.0.9
213	#   (which unfortunately shipped with Fedora 10)
214	#   http://sourceware.org/bugzilla/show_bug.cgi?id=6878
215	#   We can't use the apostrophe character
216
217#	cmp	$('o'<<24+'g'<<16+'o'<<8+'b'),%eax
218	cmp	$(0x6f<<24+0x67<<16+0x6f<<8+0x62),%eax
219				        # "bogo" in little-endian
220
221	jne	bogo_loop		# if not equal, keep going
222
223	inc	%ebx			# otherwise, we have a bogo
224	inc	%ebx			# times two for future magic
225	jmp	bogo_loop
226
227done_bogo:
228	lea	one-6(%ebx,%ebx,2), %esi
229				    	# Load into esi
230					# [one]+(num_cpus*6)
231					#
232					# the above multiplies by three
233					# esi = (ebx+(ebx*2))
234	 				# and we double-incremented ebx
235					# earlier
236
237	mov	%ebp,%edi		# move output buffer to edi
238
239	pop	%edx			# restore call pointer
240	call	*%edx			# copy it (call strcat)
241
242#	mov	$' ',%al		# print a space
243	mov	$0x20,%al		# print a space
244	stosb
245
246	push %ebx			# store cpu count
247	push %edx			# store strcat pointer
248
249	#=========
250	# MHz
251	#=========
252print_mhz:
253#	mov	$('z'<<24+'H'<<16+'M'<<8+' '),%ebx
254	mov	$(0x7a<<24+0x48<<16+0x4d<<8+0x20),%ebx
255			   		# find ' MHz' and grab up to .
256	                                # we are little endian
257#	mov	$'.',%ah
258	mov	$0x2e,%ah
259
260	# below is same as "sub $(strcat-find_string),%edx
261	# gas won't let us force the one-byte constant
262	.byte 0x83,0xEA,strcat-find_string
263
264	call	*%edx			# call find string
265
266	mov	%ebx,%eax  		# clever way to get MHz in, sadly
267	ror	$8,%eax			# not any smaller than a mov
268	stosl
269
270	#=========
271	# Chip Name
272	#=========
273chip_name:
274
275	# because of ugly newer cpuinfos from intel I had to hack this
276	# now we grab the first two words in the name field and use that
277	# it works on all recent Intel and AMD chips.  Older things
278	# might choke
279
280#	mov	$('e'<<24+'m'<<16+'a'<<8+'n'),%ebx
281	mov	$(0x65<<24+0x6d<<16+0x61<<8+0x6e),%ebx
282					# find 'name\t: ' and grab up to \n
283					# we are little endian
284#	mov	$' ',%ah
285	mov	$0x20,%ah
286	call	*%edx	   		# print first word
287	stosb				# store a space
288	call	skip_spaces		# print next word
289
290	pop	%edx
291	pop	%ebx			# restore chip count
292	pop	%esi
293
294	call	*%edx			# ' Processor'
295	cmpb	$2,%bl
296	jne	print_s
297	inc	%esi   			# if singular, skip the s
298print_s:
299	call	*%edx			# 's, '
300
301	push	%esi			# restore the values
302	push 	%edx
303
304	#========
305	# RAM
306	#========
307
308#	push    $SYSCALL_SYSINFO	# sysinfo() syscall
309#	pop	%eax
310#	mov	$sysinfo_buff,%ebx
311#	int	$0x80
312
313	mov	(sysinfo_buff+S_TOTALRAM),%eax	# size in bytes of RAM
314	shr	$20,%eax		# divide by 1024*1024 to get M
315	adc	$0, %eax		# round
316
317
318	call num_to_ascii
319
320	pop  %edx	 		# restore strcat pointer
321
322	pop     %esi	 		# print 'M RAM, '
323	call	*%edx			# call strcat
324
325	push	%esi
326
327
328	#========
329	# Bogomips
330	#========
331
332#	mov	$('s'<<24+'p'<<16+'i'<<8+'m'),%ebx
333	mov	$(0x73<<24+0x70<<16+0x69<<8+0x6d),%ebx
334					# find 'mips\t: ' and grab up to \n
335	mov	$0xa,%ah
336	call	find_string
337
338	pop	%esi	   		# bogo total follows RAM
339
340	call 	*%edx			# call strcat
341
342	push	%esi
343
344	mov	%ebp,%ecx		# point ecx to out_buffer
345
346
347	call	center_and_print	# center and print
348
349	#=================================
350	# Print Host Name
351	#=================================
352
353	mov     %ebp,%edi		  # point to output_buffer
354
355	mov	$(uname_info+U_NODENAME),%esi	# host name from uname()
356	call    *%edx			  # call strcat
357
358		      			# ecx is unchanged
359	call	center_and_print	# center and print
360
361	pop	%ecx			# (.txt) pointer to default_colors
362
363	call	write_stdout
364
365
366	#================================
367	# Exit
368	#================================
369exit:
370	xor     %ebx,%ebx
371	xor	%eax,%eax
372	inc	%eax	 		# put exit syscall number (1) in eax
373	int     $0x80             	# and exit
374
375
376	#=================================
377	# FIND_STRING
378	#=================================
379	#   ah is char to end at
380	#   ebx is 4-char ascii string to look for
381	#   edi points at output buffer
382
383find_string:
384
385	mov	$disk_buffer-1,%esi	# look in cpuinfo buffer
386find_loop:
387	inc	%esi
388	cmpb	$0, (%esi)		# are we at EOF?
389	je	done			# if so, done
390
391	cmp	(%esi), %ebx		# do the strings match?
392	jne	find_loop		# if not, loop
393
394					# ! if we get this far, we matched
395
396find_colon:
397	lodsb				# repeat till we find colon
398	cmp	$0,%al			# this is actually smaller code
399	je	done			#   than an or ecx/repnz scasb
400
401#	cmp	$':',%al
402	cmp	$0x3a,%al
403	jne	find_colon
404
405
406skip_spaces:
407        lodsb                           # skip spaces
408	cmp     $0x20,%al               # Loser new intel chips have lots??
409        je      skip_spaces
410
411store_loop:
412	cmp	$0,%al
413	je	done
414	cmp	%ah,%al			# is it end string?
415	je 	almost_done		# if so, finish
416#	cmp	$'\n',%al		# also end if linefeed
417	cmp	$0xa,%al		# also end if linefeed
418	je	almost_done
419	stosb				# if not store and continue
420	lodsb				# load value
421	jmp	store_loop
422
423almost_done:
424
425	movb	 $0, (%edi)	        # replace last value with NUL
426done:
427	ret
428
429
430	#================================
431	# strcat
432	#================================
433
434strcat:
435	lodsb				# load a byte from [ds:esi]
436	stosb				# store a byte to [es:edi]
437	cmp	$0,%al			# is it zero?
438	jne	strcat			# if not loop
439	dec	%edi			# point to one less than null
440	ret				# return
441
442	#==============================
443	# center_and_print
444	#==============================
445	# string to center in ecx
446
447center_and_print:
448	push    %edx
449	push	%ecx			# save the string pointer
450	inc	%edi			# move to a clear buffer
451	push	%edi			# save for later
452
453#	mov	$('['<<8+27),%ax	# we want to output ^[[
454	mov	$(0x5b<<8+27),%ax	# we want to output ^[[
455	stosw
456
457	cdq	      			# clear dx
458
459str_loop2:				# find end of string
460	inc	%edx
461	cmpb	$0,(%ecx,%edx)		# repeat till we find zero
462	jne	str_loop2
463
464	push	$81	 		# one added to cheat, we don't
465					# count the trailing '\n'
466	pop	%eax
467
468	cmp	%eax,%edx		# see if we are >=80
469	jl	not_too_big		# if so, don't center
470	push	$80
471	pop	%edx
472
473not_too_big:
474	sub	%edx,%eax		# subtract size from 80
475
476	shr	%eax			# then divide by 2
477
478	call	num_to_ascii		# print number of spaces
479#	mov	$'C',%al		# tack a 'C' on the end
480	mov	$0x43,%al		# tack a 'C' on the end
481					# ah is zero from num_to_ascii
482	stosw				# store C and a NULL
483	pop  %ecx			# pop the pointer to ^[[xC
484
485	call write_stdout		# write to the screen
486
487done_center:
488	pop  %ecx			# restore string pointer
489	     				# and trickily print the real string
490
491	pop %edx
492
493	#================================
494	# WRITE_STDOUT
495	#================================
496	# ecx has string
497	# eax,ebx,ecx,edx trashed
498write_stdout:
499	push    %edx
500	push	$SYSCALL_WRITE		# put 4 in eax (write syscall)
501	pop     %eax     		# in 3 bytes of code
502
503	cdq   	      			# clear edx
504
505	xor	%ebx,%ebx		# put 1 in ebx (stdout)
506	inc	%ebx			# in 3 bytes of code
507
508			# another way of doing this:    lea 1(%edx), %ebx
509
510str_loop1:
511	inc	%edx
512	cmpb	$0,(%ecx,%edx)		# repeat till zero
513	jne	str_loop1
514
515	int	$0x80  			# run the syscall
516	pop	%edx
517	ret
518
519	##############################
520	# num_to_ascii
521	##############################
522	# ax = value to print
523	# edi points to where we want it
524
525num_to_ascii:
526	push    $10
527	pop     %ebx
528	xor     %ecx,%ecx       # clear ecx
529div_by_10:
530	cdq                     # clear edx
531	div     %ebx            # divide
532	push    %edx            # save for later
533	inc     %ecx            # add to length counter
534	or      %eax,%eax       # was Q zero?
535	jnz     div_by_10       # if not divide again
536
537write_out:
538	pop     %eax            # restore in reverse order
539	add     $0x30, %al      # convert to ASCII
540	stosb                   # save digit
541	loop    write_out       # loop till done
542	ret
543
544#===========================================================================
545#	section .data
546#===========================================================================
547.data
548
549ver_string:	.ascii	" Version \0"
550compiled_string:	.ascii	", Compiled \0"
551processor:		.ascii " Processor\0"
552s_comma:		.ascii "s, \0"
553ram_comma:	.ascii	"M RAM, \0"
554bogo_total:	.ascii	" Bogomips Total\n\0"
555
556default_colors:	.ascii "\033[0m\n\n\0"
557
558cpuinfo:	.ascii	"/proc/cpuinfo\0"
559
560
561one:	.ascii	"One\0\0\0"
562two:	.ascii	"Two\0\0\0"
563three:	.ascii	"Three\0"
564four:	.ascii	"Four\0"
565
566.include	"logo.lzss_new"
567
568disk_buffer:
569.ascii "processor	: 0\n"
570.ascii "vendor_id	: AuthenticAMD\n"
571.ascii "cpu family	: 6\n"
572.ascii "model		: 6\n"
573.ascii "model name	: AMD Athlon(tm) XP 2000+\n"
574.ascii "stepping	: 2\n"
575.ascii "cpu MHz		: 1665.267\n"
576.ascii "cache size	: 256 KB\n"
577.ascii "fdiv_bug	: no\n"
578.ascii "hlt_bug		: no\n"
579.ascii "f00f_bug	: no\n"
580.ascii "coma_bug	: no\n"
581.ascii "fpu		: yes\n"
582.ascii "fpu_exception	: yes\n"
583.ascii "cpuid level	: 1\n"
584.ascii "wp		: yes\n"
585.ascii "flags		: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 mmx fxsr sse syscall mmxext 3dnowext 3dnow up\n"
586.ascii "bogomips	: 3330.53\n"
587.ascii "clflush size	: 32\n"
588.ascii "power management: ts\n\0"
589
590uname_info:
591.ascii "Linux\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
592.ascii "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
593
594.ascii "tobler\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
595.ascii "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
596
597.ascii "2.6.29\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
598.ascii "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
599
600.ascii "#1 SMP Mon May 4 09:51:54 EDT 2009\0\0"
601.ascii "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
602
603.ascii "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
604.ascii "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
605
606.ascii "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
607.ascii "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
608
609
610sysinfo_buff:
611.long 0,0,0,0,512*1024*1024,0,0,0,0
612.long 0,0,0,0,0,0,0,0,0
613
614#============================================================================
615#	section .bss
616#============================================================================
617.bss
618
619.lcomm  text_buf, (N+F-1)
620.lcomm	out_buffer,16384
621
622
623
624
625
626