1#
2#  linux_logo in x86_64 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
12.include "logo.include"
13
14# offsets into the results returned by the uname syscall
15.equ U_SYSNAME,0
16.equ U_NODENAME,65
17.equ U_RELEASE,65*2
18.equ U_VERSION,(65*3)
19.equ U_MACHINE,(65*4)
20.equ U_DOMAINNAME,65*5
21
22# offset into the results returned by the sysinfo syscall
23.equ S_TOTALRAM,32
24
25# Sycscalls
26.equ SYSCALL_EXIT,    60
27.equ SYSCALL_READ,     0
28.equ SYSCALL_WRITE,    1
29.equ SYSCALL_OPEN,     2
30.equ SYSCALL_CLOSE,    3
31.equ SYSCALL_SYSINFO, 99
32.equ SYSCALL_UNAME,   63
33
34#
35.equ STDIN,0
36.equ STDOUT,1
37.equ STDERR,2
38
39	.globl _start
40_start:
41	#=========================
42	# PRINT LOGO
43	#=========================
44
45# LZSS decompression algorithm implementation
46# by Stephan Walter 2002, based on LZSS.C by Haruhiko Okumura 1989
47# optimized some more by Vince Weaver
48
49	# we used to fill the buffer with FREQUENT_CHAR
50	# but, that only gains us one byte of space in the lzss image.
51	# the lzss algorithm does automatic RLE... pretty clever
52	# so we compress with NUL as FREQUENT_CHAR and it is pre-done for us
53
54	mov     $(N-F), %ebp   	     	# R
55
56	mov  	$logo, %esi		# %esi points to logo (for lodsb)
57
58	mov	$out_buffer, %edi	# point to out_buffer
59	push	%rdi	     		# save this value for later
60
61	xor	%ecx, %ecx
62
63decompression_loop:
64	lodsb			# load in a byte
65
66	mov 	$0xff, %bh	# re-load top as a hackish 8-bit counter
67	mov 	%al, %bl	# move in the flags
68
69test_flags:
70	cmp	$logo_end, %esi # have we reached the end?
71	je	done_logo  	# ! if so, exit
72
73	shr 	$1, %ebx	# shift bottom bit into carry flag
74	jc	discrete_char	# ! if set, we jump to discrete char
75
76offset_length:
77	lodsw                   # get match_length and match_position
78	mov %eax,%edx		# copy to edx
79	    			# no need to mask dx, as we do it
80				# by default in output_loop
81
82	shr $(P_BITS),%eax
83	add $(THRESHOLD+1),%al
84	mov %al,%cl             # cl = (ax >> P_BITS) + THRESHOLD + 1
85				  #                       (=match_length)
86
87output_loop:
88	and 	$POSITION_MASK,%dh  	# mask it
89	mov 	text_buf(%rdx), %al	# load byte from text_buf[]
90	inc 	%edx	    		# advance pointer in text_buf
91store_byte:
92	stosb				# store it
93
94	mov     %al, text_buf(%rbp)	# store also to text_buf[r]
95	inc 	%ebp 			# r++
96	and 	$(N-1), %bp		# mask r
97
98	loop 	output_loop		# repeat until k>j
99
100	or	%bh,%bh			# ! if 0 we shifted through 8 and must
101	jnz	test_flags		# re-load flags
102
103	jmp 	decompression_loop
104
105discrete_char:
106	lodsb				# load a byte
107	inc	%ecx			# we set ecx to one so byte
108					# will be output once
109					# (how do we know ecx is zero?)
110
111	jmp     store_byte              # and cleverly store it
112
113
114# end of LZSS code
115
116done_logo:
117
118	pop 	%rbp			# get out_buffer and keep in bp
119	mov	%ebp,%ecx		# move out_buffer to ecx
120
121	call	write_stdout		# print the logo
122
123	#
124	#  Setup
125	#
126setup:
127	mov	$strcat,%edx		# use rdx as call pointer (smaller op)
128
129
130	#==========================
131	# PRINT VERSION
132	#==========================
133
134#	push 	$SYSCALL_UNAME		# uname syscall
135#	pop	%rax			# in 3 bytes
136	mov	$uname_info,%edi	# uname struct (0 extend address)
137#	syscall				# do syscall
138
139	mov	%ebp,%edi		# point %edi to out_buffer
140
141	mov	$(uname_info+U_SYSNAME),%esi	# os-name from uname "Linux"
142	call	*%rdx			# call strcat
143
144	mov	$ver_string,%esi		# source is " Version "
145	call 	*%rdx			        # call strcat
146	push	%rsi  				# save our .txt pointer
147
148	mov	$(uname_info+U_RELEASE),%esi    # version from uname "2.4.1"
149	call 	*%rdx				# call strcat
150
151	pop	%rsi  			# restore .txt pointer
152					# source is ", Compiled "
153	call 	*%rdx			# call strcat
154	push	%rsi  			# store for later
155
156	mov	$(uname_info+U_VERSION),%esi	# compiled date
157	call 	*%rdx			# call strcat
158
159	mov	%ebp,%ecx		# move out_buffer to ecx
160
161	mov	$0xa,%ax		# store linefeed on end
162	stosw				# and zero
163
164	call	*%rdx			# call strcat
165
166	call	center_and_print	# center and print
167
168	#===============================
169	# Middle-Line
170	#===============================
171middle_line:
172	#=========
173	# Load /proc/cpuinfo into buffer
174	#=========
175
176	push	%rdx			# save call pointer
177
178#	push	$SYSCALL_OPEN		# load 5 [ open() ]
179#	pop	%rax			# in 3 bytes
180
181#	mov	$cpuinfo,%edi		# '/proc/cpuinfo'
182#	xor	%esi,%esi		# 0 = O_RDONLY <bits/fcntl.h>
183#	cdq				# clear edx in clever way
184#	syscall				# syscall.  fd in eax.
185					# we should check that eax>=0
186
187#	mov	%eax,%edi		# save our fd
188
189#	xor	%eax,%eax		# SYSCALL_READ make== 0
190
191	mov	$disk_buffer,%esi
192
193#	mov	$16,%dh		 	# 4096 is maximum size of proc file #)
194					# we load sneakily by knowing
195					# 16<<8 = 4096. be sure edx clear
196
197#	syscall
198
199#	push	$SYSCALL_CLOSE		# close (to be correct)
200#	pop	%rax
201#	syscall
202
203	#=============
204	# Number of CPUs
205	#=============
206number_of_cpus:
207
208	xor	%ebx,%ebx		# chip count
209
210					# $disk_buffer still in %rsi
211bogo_loop:
212	mov	(%rsi), %eax		# load 4 bytes into eax
213	inc	%esi			# increment pointer
214
215	cmp	$0,%al			# check for end of file
216	je	done_bogo
217
218	# Grrr, due to a bug in binutils 2.18.50.0.9
219        #   (which unfortunately shipped with Fedora 10)
220	#   http://sourceware.org/bugzilla/show_bug.cgi?id=6878
221	#   We can't use the apostrophe character
222
223#       cmp     $('o'<<24+'g'<<16+'o'<<8+'b'),%eax
224        cmp     $(0x6f<<24+0x67<<16+0x6f<<8+0x62),%eax
225				        # "bogo" in little-endian
226
227	jne	bogo_loop		# ! if not equal, keep going
228	add	$2,%ebx			# otherwise, we have a bogo
229					# 2 times too for future magic
230	jmp	bogo_loop
231
232done_bogo:
233	lea	one-6(%rbx,%rbx,2), %esi
234				    	# Load into esi
235					# [one]+(num_cpus*6)
236					#
237					# the above multiplies by three
238					# esi = (ebx+(ebx*2))
239	 				# and we double-incremented ebx
240					# earlier
241
242	mov	%ebp,%edi		# move output buffer to edi
243
244	pop	%rdx			# restore call pointer
245	call	*%rdx			# copy it (call strcat)
246
247#	mov	$' ',%al		# print a space
248	mov	$0x20,%al		# print a space
249
250	stosb
251
252	push %rbx
253	push %rdx			# store strcat pointer
254
255	#=========
256	# MHz
257	#=========
258print_mhz:
259#       mov     $('z'<<24+'H'<<16+'M'<<8+' '),%ebx
260        mov     $(0x7a<<24+0x48<<16+0x4d<<8+0x20),%ebx
261			   		# find ' MHz' and grab up to .
262	                                # we are little endian
263#	mov	$'.',%ah
264	mov	$0x2e,%ah
265
266	# below is same as "sub $(strcat-find_string),%edx
267	# gas won't let us force the one-byte constant
268	.byte 0x83,0xEA,strcat-find_string
269
270	call	*%rdx			# call find string
271
272	mov	%ebx,%eax  		# clever way to get MHz in, sadly
273	ror	$8,%eax			# not any smaller than a mov
274	stosl
275
276	#=========
277	# Chip Name
278	#=========
279chip_name:
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	*%rdx	   		# call find_string
287	stosb
288	call 	skip_spaces
289
290	pop     %rdx
291	pop     %rbx                    # restore chip count
292	pop     %rsi
293
294	call    *%rdx                   # ' Processor'
295	cmpb    $2,%bl
296	jne     print_s
297	inc     %rsi   			# ! if singular, skip the s
298print_s:
299        call    *%rdx                   # 's, '
300
301        push    %rsi                    # restore the values
302	push    %rdx
303
304	#========
305	# RAM
306	#========
307
308#	push	%rdi
309#	push    $SYSCALL_SYSINFO	# sysinfo() syscall
310#	pop	%rax
311#	mov	$sysinfo_buff,%edi
312#	syscall
313#	pop	%rdi
314
315	# The following has to be a 64 bit load, to support
316	# Ram > 4GB
317	mov	(sysinfo_buff+S_TOTALRAM),%rax	# size in bytes of RAM
318	shr	$20,%rax		# divide by 1024*1024 to get M
319	adc	$0, %eax		# round
320
321	call num_to_ascii
322
323	pop  %rdx	 		# restore strcat pointer
324
325	pop     %rsi	 		# print 'M RAM, '
326	call	*%rdx			# call strcat
327
328	push	%rsi
329
330	#========
331	# Bogomips
332	#========
333
334#       mov     $('s'<<24+'p'<<16+'i'<<8+'m'),%ebx
335        mov     $(0x73<<24+0x70<<16+0x69<<8+0x6d),%ebx
336					# find 'mips\t: ' and grab up to \n
337	mov	$0xa,%ah
338	call	find_string
339
340	pop	%rsi	   		# bogo total follows RAM
341
342	call 	*%rdx			# call strcat
343
344	push	%rsi
345
346	mov	%ebp,%ecx		# point ecx to out_buffer
347
348	push	%rcx
349	call	center_and_print	# center and print
350
351	#=================================
352	# Print Host Name
353	#=================================
354last_line:
355	mov     %ebp,%edi		# point to output_buffer
356
357	mov	$(uname_info+U_NODENAME),%esi	# host name from uname()
358	call    *%rdx			# call strcat
359
360	pop	%rcx	      		# ecx is unchanged
361	call	center_and_print	# center and print
362
363	pop	%rcx			# (.txt) pointer to default_colors
364
365	call	write_stdout
366
367	#================================
368	# Exit
369	#================================
370exit:
371	push	$SYSCALL_EXIT		# Put exit syscall in rax
372	pop	%rax
373
374	xor	%edi,%edi		# Make return value $0
375	syscall
376
377
378	#=================================
379	# FIND_STRING
380	#=================================
381	#   ah is char to end at
382	#   ebx is 4-char ascii string to look for
383	#   edi points at output buffer
384
385find_string:
386
387	mov	$disk_buffer-1,%esi	# look in cpuinfo buffer
388find_loop:
389	inc	%esi
390	cmpb	$0, (%rsi)		# are we at EOF?
391	je	done			# ! if so, done
392
393	cmp	(%rsi), %ebx		# do the strings match?
394	jne	find_loop		# ! if not, loop
395
396					# ! if we get this far, we matched
397
398find_colon:
399	lodsb				# repeat till we find colon
400	cmp	$0,%al
401	je	done
402#	cmp	$':',%al
403	cmp	$0x3a,%al
404	jne	find_colon
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
417	cmp	$0xa,%al
418	je	almost_done
419	stosb				# ! if not store and continue
420	lodsb
421
422	jmp	store_loop
423
424almost_done:
425	movb	 $0, (%rdi)	        # 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    %rdx			# save strcat pointer
449	push	%rcx			# save the string pointer
450	inc	%edi			# move to a clear buffer
451	push	%rdi			# 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,(%rcx,%rdx)		# 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	%rax
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	%rdx
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  %rcx			# pop the pointer to ^[[xC
484
485	call write_stdout		# write to the screen
486
487done_center:
488	pop  %rcx			# restore string pointer
489	     				# and trickily print the real string
490
491	pop %rdx			# restore strcat pointer
492
493	#================================
494	# WRITE_STDOUT
495	#================================
496	# ecx has string
497	# eax,ebx,ecx,edx trashed
498write_stdout:
499	push    %rdx
500	push	$SYSCALL_WRITE		# put 4 in eax (write syscall)
501	pop     %rax     		# in 3 bytes of code
502
503	cdq   	      			# clear edx
504
505	lea	1(%rdx),%edi		# put 1 in ebx (stdout)
506					# in 3 bytes of code
507
508	mov	%ecx,%esi
509
510str_loop1:
511	inc	%edx
512	cmpb	$0,(%rcx,%rdx)		# repeat till zero
513	jne	str_loop1
514
515	syscall  			# run the syscall
516	pop	%rdx
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     %rbx
528	xor     %ecx,%ecx       # clear ecx
529div_by_10:
530	cdq                     # clear edx
531	div     %ebx            # divide
532	push    %rdx            # 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     %rax            # 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	: GenuineIntel\n"
571.ascii "cpu family	: 15\n"
572.ascii "model		: 6\n"
573.ascii "model name	: Intel(R) Xeon(TM) CPU 3.46GHz\n"
574.ascii "stepping	: 4\n"
575.ascii "cpu MHz		: 3200.000\n"
576.ascii "cache size	: 2048 KB\n"
577.ascii "physical id	: 0\n"
578.ascii "siblings	: 2\n"
579.ascii "core id		: 0\n"
580.ascii "cpu cores	: 2\n"
581.ascii "apicid		: 0\n"
582.ascii "initial apicid	: 0\n"
583.ascii "fpu		: yes\n"
584.ascii "fpu_exception	: yes\n"
585.ascii "cpuid level	: 6\n"
586.ascii "wp		: yes\n"
587.ascii "flags		: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx lm constant_tsc pebs bts pni dtes64 monitor ds_cpl vmx est cid cx16 xtpr pdcm lahf_lm tpr_shadow\n"
588.ascii "bogomips	: 6934.38\n"
589.ascii "clflush size	: 64\n"
590.ascii "cache_alignment	: 128\n"
591.ascii "address sizes	: 36 bits physical, 48 bits virtual\n"
592.ascii "power management:\n"
593.ascii "\n"
594.ascii "processor	: 1\n"
595.ascii "vendor_id	: GenuineIntel\n"
596.ascii "cpu family	: 15\n"
597.ascii "model		: 6\n"
598.ascii "model name	: Intel(R) Xeon(TM) CPU 3.46GHz\n"
599.ascii "stepping	: 4\n"
600.ascii "cpu MHz		: 3200.000\n"
601.ascii "cache size	: 2048 KB\n"
602.ascii "physical id	: 1\n"
603.ascii "siblings	: 2\n"
604.ascii "core id		: 0\n"
605.ascii "cpu cores	: 2\n"
606.ascii "apicid		: 4\n"
607.ascii "initial apicid	: 4\n"
608.ascii "fpu		: yes\n"
609.ascii "fpu_exception	: yes\n"
610.ascii "cpuid level	: 6\n"
611.ascii "wp		: yes\n"
612.ascii "flags		: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx lm constant_tsc pebs bts pni dtes64 monitor ds_cpl vmx est cid cx16 xtpr pdcm lahf_lm tpr_shadow\n"
613.ascii "bogomips	: 6934.13\n"
614.ascii "clflush size	: 64\n"
615.ascii "cache_alignment	: 128\n"
616.ascii "address sizes	: 36 bits physical, 48 bits virtual\n"
617.ascii "power management:\n\0"
618
619uname_info:
620.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"
621.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"
622
623.ascii "domori\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"
624.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"
625
626.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"
627.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"
628
629.ascii "#1 SMP Mon May 4 09:51:54 EDT 2009\0\0"
630.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"
631
632.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"
633.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"
634
635.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"
636.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"
637
638sysinfo_buff:
639.long 0,0,0,0,0,0,0,0,2048*1024*1024,0,0,0,0,0,0,0
640
641
642#============================================================================
643#	section .bss
644#============================================================================
645.bss
646
647.lcomm  text_buf, (N+F-1)
648.lcomm	out_buffer,16384
649