2 # linux_logo in x86_64 assembly language
3 # based on the code from ll_asm-0.36
5 # By Vince Weaver <vince _at_ deater.net>
7 # Modified to remove non-deterministic system calls
8 # And to avoid reading from /proc
12 .include "logo.include"
14 # offsets into the results returned by the uname syscall
20 .equ U_DOMAINNAME,65*5
22 # offset into the results returned by the sysinfo syscall
31 .equ SYSCALL_SYSINFO, 99
32 .equ SYSCALL_UNAME, 63
41 #=========================
43 #=========================
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
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
56 mov $logo, %esi # %esi points to logo (for lodsb)
58 mov $out_buffer, %edi # point to out_buffer
59 push %rdi # save this value for later
64 lodsb # load in a byte
66 mov $0xff, %bh # re-load top as a hackish 8-bit counter
67 mov %al, %bl # move in the flags
70 cmp $logo_end, %esi # have we reached the end?
71 je done_logo # ! if so, exit
73 shr $1, %ebx # shift bottom bit into carry flag
74 jc discrete_char # ! if set, we jump to discrete char
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
83 add $(THRESHOLD+1),%al
84 mov %al,%cl # cl = (ax >> P_BITS) + THRESHOLD + 1
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
94 mov %al, text_buf(%rbp) # store also to text_buf[r]
96 and $(N-1), %bp # mask r
98 loop output_loop # repeat until k>j
100 or %bh,%bh # ! if 0 we shifted through 8 and must
101 jnz test_flags # re-load flags
103 jmp decompression_loop
107 inc %ecx # we set ecx to one so byte
108 # will be output once
109 # (how do we know ecx is zero?)
111 jmp store_byte # and cleverly store it
118 pop %rbp # get out_buffer and keep in bp
119 mov %ebp,%ecx # move out_buffer to ecx
121 call write_stdout # print the logo
127 mov $strcat,%edx # use rdx as call pointer (smaller op)
130 #==========================
132 #==========================
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
139 mov %ebp,%edi # point %edi to out_buffer
141 mov $(uname_info+U_SYSNAME),%esi # os-name from uname "Linux"
142 call *%rdx # call strcat
144 mov $ver_string,%esi # source is " Version "
145 call *%rdx # call strcat
146 push %rsi # save our .txt pointer
148 mov $(uname_info+U_RELEASE),%esi # version from uname "2.4.1"
149 call *%rdx # call strcat
151 pop %rsi # restore .txt pointer
152 # source is ", Compiled "
153 call *%rdx # call strcat
154 push %rsi # store for later
156 mov $(uname_info+U_VERSION),%esi # compiled date
157 call *%rdx # call strcat
159 mov %ebp,%ecx # move out_buffer to ecx
161 mov $0xa,%ax # store linefeed on end
164 call *%rdx # call strcat
166 call center_and_print # center and print
168 #===============================
170 #===============================
173 # Load /proc/cpuinfo into buffer
176 push %rdx # save call pointer
178 # push $SYSCALL_OPEN # load 5 [ open() ]
179 # pop %rax # in 3 bytes
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
187 # mov %eax,%edi # save our fd
189 # xor %eax,%eax # SYSCALL_READ make== 0
191 mov $disk_buffer,%esi
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
199 # push $SYSCALL_CLOSE # close (to be correct)
208 xor %ebx,%ebx # chip count
210 # $disk_buffer still in %rsi
212 mov (%rsi), %eax # load 4 bytes into eax
213 inc %esi # increment pointer
215 cmp $0,%al # check for end of file
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
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
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
233 lea one-6(%rbx,%rbx,2), %esi
237 # the above multiplies by three
238 # esi = (ebx+(ebx*2))
239 # and we double-incremented ebx
242 mov %ebp,%edi # move output buffer to edi
244 pop %rdx # restore call pointer
245 call *%rdx # copy it (call strcat)
247 # mov $' ',%al # print a space
248 mov $0x20,%al # print a space
253 push %rdx # store strcat pointer
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
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
270 call *%rdx # call find string
272 mov %ebx,%eax # clever way to get MHz in, sadly
273 ror $8,%eax # not any smaller than a mov
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
286 call *%rdx # call find_string
291 pop %rbx # restore chip count
294 call *%rdx # ' Processor'
297 inc %rsi # ! if singular, skip the s
301 push %rsi # restore the values
309 # push $SYSCALL_SYSINFO # sysinfo() syscall
311 # mov $sysinfo_buff,%edi
315 # The following has to be a 64 bit load, to support
317 mov (sysinfo_buff+S_TOTALRAM),%rax # size in bytes of RAM
318 shr $20,%rax # divide by 1024*1024 to get M
323 pop %rdx # restore strcat pointer
325 pop %rsi # print 'M RAM, '
326 call *%rdx # call strcat
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
340 pop %rsi # bogo total follows RAM
342 call *%rdx # call strcat
346 mov %ebp,%ecx # point ecx to out_buffer
349 call center_and_print # center and print
351 #=================================
353 #=================================
355 mov %ebp,%edi # point to output_buffer
357 mov $(uname_info+U_NODENAME),%esi # host name from uname()
358 call *%rdx # call strcat
360 pop %rcx # ecx is unchanged
361 call center_and_print # center and print
363 pop %rcx # (.txt) pointer to default_colors
367 #================================
369 #================================
371 push $SYSCALL_EXIT # Put exit syscall in rax
374 xor %edi,%edi # Make return value $0
378 #=================================
380 #=================================
381 # ah is char to end at
382 # ebx is 4-char ascii string to look for
383 # edi points at output buffer
387 mov $disk_buffer-1,%esi # look in cpuinfo buffer
390 cmpb $0, (%rsi) # are we at EOF?
391 je done # ! if so, done
393 cmp (%rsi), %ebx # do the strings match?
394 jne find_loop # ! if not, loop
396 # ! if we get this far, we matched
399 lodsb # repeat till we find colon
408 cmp $0x20,%al # Loser new intel chips have lots??
414 cmp %ah,%al # is it end string?
415 je almost_done # ! if so, finish
419 stosb # ! if not store and continue
425 movb $0, (%rdi) # replace last value with NUL
430 #================================
432 #================================
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
442 #==============================
444 #==============================
445 # string to center in ecx
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
453 # mov $('['<<8+27),%ax # we want to output ^[[
454 mov $(0x5b<<8+27),%ax # we want to output ^[[
459 str_loop2: # find end of string
461 cmpb $0,(%rcx,%rdx) # repeat till we find zero
464 push $81 # one added to cheat, we don't
465 # count the trailing '\n'
468 cmp %eax,%edx # see if we are >=80
469 jl not_too_big # ! if so, don't center
474 sub %edx,%eax # subtract size from 80
476 shr %eax # then divide by 2
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
485 call write_stdout # write to the screen
488 pop %rcx # restore string pointer
489 # and trickily print the real string
491 pop %rdx # restore strcat pointer
493 #================================
495 #================================
497 # eax,ebx,ecx,edx trashed
500 push $SYSCALL_WRITE # put 4 in eax (write syscall)
501 pop %rax # in 3 bytes of code
505 lea 1(%rdx),%edi # put 1 in ebx (stdout)
512 cmpb $0,(%rcx,%rdx) # repeat till zero
515 syscall # run the syscall
519 ##############################
521 ##############################
522 # ax = value to print
523 # edi points to where we want it
528 xor %ecx,%ecx # clear ecx
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
538 pop %rax # restore in reverse order
539 add $0x30, %al # convert to ASCII
541 loop write_out # loop till done
544 #===========================================================================
546 #===========================================================================
549 ver_string: .ascii " Version \0"
550 compiled_string: .ascii ", Compiled \0"
551 processor: .ascii " Processor\0"
552 s_comma: .ascii "s, \0"
553 ram_comma: .ascii "M RAM, \0"
554 bogo_total: .ascii " Bogomips Total\n\0"
556 default_colors: .ascii "\033[0m\n\n\0"
558 cpuinfo: .ascii "/proc/cpuinfo\0"
561 one: .ascii "One\0\0\0"
562 two: .ascii "Two\0\0\0"
563 three: .ascii "Three\0"
564 four: .ascii "Four\0"
566 .include "logo.lzss_new"
569 .ascii "processor : 0\n"
570 .ascii "vendor_id : GenuineIntel\n"
571 .ascii "cpu family : 15\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"
584 .ascii "fpu_exception : yes\n"
585 .ascii "cpuid level : 6\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"
594 .ascii "processor : 1\n"
595 .ascii "vendor_id : GenuineIntel\n"
596 .ascii "cpu family : 15\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"
609 .ascii "fpu_exception : yes\n"
610 .ascii "cpuid level : 6\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"
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"
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"
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"
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"
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"
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"
639 .long 0,0,0,0,0,0,0,0,2048*1024*1024,0,0,0,0,0,0,0
642 #============================================================================
644 #============================================================================
647 .lcomm text_buf, (N+F-1)
648 .lcomm out_buffer,16384