packaging: initial import
[profile/ivi/syslinux.git] / core / runkernel.inc
1 ;; -----------------------------------------------------------------------
2 ;;
3 ;;   Copyright 1994-2009 H. Peter Anvin - All Rights Reserved
4 ;;   Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
5 ;;
6 ;;   This program is free software; you can redistribute it and/or modify
7 ;;   it under the terms of the GNU General Public License as published by
8 ;;   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
9 ;;   Boston MA 02111-1307, USA; either version 2 of the License, or
10 ;;   (at your option) any later version; incorporated herein by reference.
11 ;;
12 ;; -----------------------------------------------------------------------
13
14 ;;
15 ;; runkernel.inc
16 ;;
17 ;; Common code for running a Linux kernel
18 ;;
19
20 ;
21 ; Hook macros, that may or may not be defined
22 ;
23 %ifndef HAVE_UNLOAD_PREP
24 %macro UNLOAD_PREP 0
25 %endmacro
26 %endif
27
28 ;
29 ; A Linux kernel consists of three parts: boot sector, setup code, and
30 ; kernel code.  The boot sector is never executed when using an external
31 ; booting utility, but it contains some status bytes that are necessary.
32 ;
33 ; First check that our kernel is at least 1K, or else it isn't long
34 ; enough to have the appropriate headers.
35 ;
36 ; We used to require the kernel to be 64K or larger, but it has gotten
37 ; popular to use the Linux kernel format for other things, which may
38 ; not be so large.
39 ;
40 ; Additionally, we used to have a test for 8 MB or smaller.  Equally
41 ; obsolete.
42 ;
43 is_linux_kernel:
44                 push si                         ; <A> file pointer
45
46 ;
47 ; Now start transferring the kernel
48 ;
49                 push word real_mode_seg
50                 pop es
51
52 ;
53 ; Start by loading the bootsector/setup code, to see if we need to
54 ; do something funky.  It should fit in the first 32K (loading 64K won't
55 ; work since we might have funny stuff up near the end of memory).
56 ;
57                 call abort_check                ; Check for abort key
58                 mov cx,8000h                    ; Half a moby (32K)
59                 xor bx,bx
60                 pop si                          ; <A> file pointer
61                 pm_call getfsbytes
62                 cmp cx,1024
63                 jb kernel_corrupt
64                 cmp word [es:bs_bootsign],0AA55h
65                 jne kernel_corrupt              ; Boot sec signature missing
66
67 ;
68 ; Save the file pointer for later...
69 ;
70                 push si                         ; <A> file pointer
71
72 ;
73 ; Construct the command line (append options have already been copied)
74 ;
75 construct_cmdline:
76                 mov di,[CmdLinePtr]
77                 mov si,boot_image               ; BOOT_IMAGE=
78                 mov cx,boot_image_len
79                 rep movsb
80                 mov si,KernelName               ; Unmangled kernel name
81                 call strcpy
82                 mov byte [es:di-1],' '          ; Follow by space
83
84                 call do_ip_append               ; Handle IPAppend
85
86                 mov si,[CmdOptPtr]              ; Options from user input
87                 call strcpy
88
89 ;
90 ; Scan through the command line for anything that looks like we might be
91 ; interested in.  The original version of this code automatically assumed
92 ; the first option was BOOT_IMAGE=, but that is no longer certain.
93 ;
94 parse_cmdline:
95                 mov di,cmd_line_here
96 .skipspace:     mov al,[es:di]
97                 inc di
98 .skipspace_loaded:
99                 and al,al
100                 jz cmdline_end
101                 cmp al,' '
102                 jbe .skipspace
103                 dec di
104
105                 ; ES:DI now points to the beginning of an option
106                 mov si,options_list
107 .next_opt:
108                 movzx cx,byte [si]
109                 jcxz .skip_opt
110                 push di
111                 inc si
112                 repe cmpsb
113                 jne .no_match
114
115                 ; This either needs to have been an option with parameter,
116                 ; or be followed by EOL/whitespace
117                 mov ax,[es:di-1]                ; AL = last chr; AH = following
118                 cmp al,'='
119                 je .is_match
120                 cmp ah,' '
121                 ja .no_match
122 .is_match:
123                 pop ax                          ; Drop option pointer on stack
124                 call [si]
125 .skip_opt:
126                 mov al,[es:di]
127                 inc di
128                 cmp al,' '
129                 ja .skip_opt
130                 jmp .skipspace_loaded
131 .no_match:
132                 pop di
133                 add si,cx                       ; Skip remaining bytes
134                 inc si                          ; Skip function pointer
135                 inc si
136                 jmp .next_opt
137
138 opt_vga:
139                 mov ax,[es:di-1]
140                 mov bx,-1
141                 cmp ax,'=n'                     ; vga=normal
142                 je .vc0
143                 dec bx                          ; bx <- -2
144                 cmp ax,'=e'                     ; vga=ext
145                 je .vc0
146                 dec bx                          ; bx <- -3
147                 cmp ax,'=a'                     ; vga=ask
148                 je .vc0
149                 mov bx,0x0f04                   ; bx <- 0x0f04 (current mode)
150                 cmp ax,'=c'                     ; vga=current
151                 je .vc0
152                 call parseint_esdi              ; vga=<number>
153                 jc .skip                        ; Not an integer
154 .vc0:           mov [es:bs_vidmode],bx          ; Set video mode
155 .skip:
156                 ret
157
158 opt_mem:
159                 call parseint_esdi
160                 jc .skip
161 %if HIGHMEM_SLOP != 0
162                 sub ebx,HIGHMEM_SLOP
163 %endif
164                 mov [MyHighMemSize],ebx
165 .skip:
166                 ret
167
168 opt_quiet:
169                 mov byte [QuietBoot],QUIET_FLAG
170                 ret
171
172 %if IS_PXELINUX
173 opt_keeppxe:
174                 or byte [KeepPXE],1             ; KeepPXE set by command line
175                 ret
176 %endif
177
178 opt_initrd:
179                 mov ax,di
180                 cmp byte [es:di],' '
181                 ja .have_initrd
182                 xor ax,ax
183 .have_initrd:
184                 mov [InitRDPtr],ax
185                 ret
186
187 ;
188 ; After command line parsing...
189 ;
190 cmdline_end:
191                 sub di,cmd_line_here
192                 mov [CmdLineLen],di             ; Length including final null
193
194 ;
195 ; Now check if we have a large kernel, which needs to be loaded high
196 ;
197 prepare_header:
198                 mov dword [RamdiskMax], HIGHMEM_MAX     ; Default initrd limit
199                 cmp dword [es:su_header],HEADER_ID      ; New setup code ID
200                 jne old_kernel                  ; Old kernel, load low
201                 mov ax,[es:su_version]
202                 mov [KernelVersion],ax
203                 cmp ax,0200h                    ; Setup code version 2.0
204                 jb old_kernel                   ; Old kernel, load low
205                 cmp ax,0201h                    ; Version 2.01+?
206                 jb new_kernel                   ; If 2.00, skip this step
207                 ; Set up the heap (assuming loading high for now)
208                 mov word [es:su_heapend],linux_stack-512
209                 or byte [es:su_loadflags],80h   ; Let the kernel know we care
210                 cmp ax,0203h                    ; Version 2.03+?
211                 jb new_kernel                   ; Not 2.03+
212                 mov eax,[es:su_ramdisk_max]
213                 mov [RamdiskMax],eax            ; Set the ramdisk limit
214
215 ;
216 ; We definitely have a new-style kernel.  Let the kernel know who we are,
217 ; and that we are clueful
218 ;
219 new_kernel:
220                 mov byte [es:su_loader],my_id   ; Show some ID
221                 xor eax,eax
222                 mov [es:su_ramdisklen],eax      ; No initrd loaded yet
223
224 ;
225 ; About to load the kernel.  This is a modern kernel, so use the boot flags
226 ; we were provided.
227 ;
228                 mov al,[es:su_loadflags]
229                 or al,[QuietBoot]               ; Set QUIET_FLAG if needed
230                 mov [es:su_loadflags],al
231                 mov [LoadFlags],al
232
233 any_kernel:
234                 mov si,loading_msg
235                 call writestr_qchk
236                 mov si,KernelName               ; Print kernel name part of
237                 call writestr_qchk              ; "Loading" message
238
239 ;
240 ; Load the kernel.  We always load it at 100000h even if we're supposed to
241 ; load it "low"; for a "low" load we copy it down to low memory right before
242 ; jumping to it.
243 ;
244 read_kernel:
245                 movzx ax,byte [es:bs_setupsecs] ; Setup sectors
246                 and ax,ax
247                 jnz .sects_ok
248                 mov al,4                        ; 0 = 4 setup sectors
249 .sects_ok:
250                 inc ax                          ; Including the boot sector
251                 mov [SetupSecs],ax
252
253                 call dot_pause
254
255 ;
256 ; Move the stuff beyond the setup code to high memory at 100000h
257 ;
258                 movzx esi,word [SetupSecs]      ; Setup sectors
259                 shl si,9                        ; Convert to bytes
260                 mov ecx,8000h                   ; 32K
261                 sub ecx,esi                     ; Number of bytes to copy
262                 add esi,core_real_mode          ; Pointer to source
263                 mov edi,free_high_memory        ; Copy to free high memory
264
265                 call bcopy                      ; Transfer to high memory
266
267                 pop si                          ; <A> File pointer
268                 and si,si                       ; EOF already?
269                 jz high_load_done
270
271                 ; On exit EDI -> where to load the rest
272
273                 mov bx,dot_pause
274                 or eax,-1                       ; Load the whole file
275                 mov dx,3                        ; Pad to dword
276                 call load_high
277
278 high_load_done:
279                 mov [KernelEnd],edi
280                 mov ax,real_mode_seg            ; Set to real mode seg
281                 mov es,ax
282
283                 mov si,dot_msg
284                 call writestr_qchk
285
286 ;
287 ; Some older kernels (1.2 era) would have more than 4 setup sectors, but
288 ; would not rely on the boot protocol to manage that.  These kernels fail
289 ; if they see protected-mode kernel data after the setup sectors, so
290 ; clear that memory.
291 ;
292                 push di
293                 mov di,[SetupSecs]
294                 shl di,9
295                 xor eax,eax
296                 mov cx,cmd_line_here
297                 sub cx,di
298                 shr cx,2
299                 rep stosd
300                 pop di
301
302 ;
303 ; Now see if we have an initial RAMdisk; if so, do requisite computation
304 ; We know we have a new kernel; the old_kernel code already will have objected
305 ; if we tried to load initrd using an old kernel
306 ;
307 load_initrd:
308                 ; Cap the ramdisk memory range if appropriate
309                 mov eax,[RamdiskMax]
310                 cmp eax,[MyHighMemSize]
311                 ja .ok
312                 mov [MyHighMemSize],eax
313 .ok:
314                 xor eax,eax
315                 cmp [InitRDPtr],ax
316                 jz .noinitrd
317                 call parse_load_initrd
318 .noinitrd:
319
320 ;
321 ; Abandon hope, ye that enter here!  We do no longer permit aborts.
322 ;
323                 call abort_check                ; Last chance!!
324
325                 mov si,ready_msg
326                 call writestr_qchk
327
328                 UNLOAD_PREP                     ; Module-specific hook
329
330 ;
331 ; Now, if we were supposed to load "low", copy the kernel down to 10000h
332 ; and the real mode stuff to 90000h.  We assume that all bzImage kernels are
333 ; capable of starting their setup from a different address.
334 ;
335                 mov ax,real_mode_seg
336                 mov es,ax
337                 mov fs,ax
338
339 ;
340 ; If the default root device is set to FLOPPY (0000h), change to
341 ; /dev/fd0 (0200h)
342 ;
343                 cmp word [es:bs_rootdev],byte 0
344                 jne root_not_floppy
345                 mov word [es:bs_rootdev],0200h
346 root_not_floppy:
347
348 ;
349 ; Copy command line.  Unfortunately, the old kernel boot protocol requires
350 ; the command line to exist in the 9xxxxh range even if the rest of the
351 ; setup doesn't.
352 ;
353 setup_command_line:
354                 mov dx,[KernelVersion]
355                 test byte [LoadFlags],LOAD_HIGH
356                 jz .need_high_cmdline
357                 cmp dx,0202h                    ; Support new cmdline protocol?
358                 jb .need_high_cmdline
359                 ; New cmdline protocol
360                 ; Store 32-bit (flat) pointer to command line
361                 ; This is the "high" location, since we have bzImage
362                 mov dword [fs:su_cmd_line_ptr],cmd_line
363                 mov word [HeapEnd],linux_stack
364                 mov word [fs:su_heapend],linux_stack-512
365                 jmp .setup_done
366
367 .need_high_cmdline:
368 ;
369 ; Copy command line down to fit in high conventional memory
370 ; -- this happens if we have a zImage kernel or the protocol
371 ; is less than 2.02.
372 ;
373                 mov si,cmd_line_here
374                 mov di,old_cmd_line_here
375                 mov [fs:kern_cmd_magic],word CMD_MAGIC ; Store magic
376                 mov [fs:kern_cmd_offset],di     ; Store pointer
377                 mov word [HeapEnd],old_linux_stack
378                 mov ax,255                      ; Max cmdline limit
379                 cmp dx,0201h
380                 jb .adjusted
381                 ; Protocol 2.01+
382                 mov word [fs:su_heapend],old_linux_stack-512
383                 jbe .adjusted
384                 ; Protocol 2.02+
385                 ; Note that the only reason we would end up here is
386                 ; because we have a zImage, so we anticipate the move
387                 ; to 90000h already...
388                 mov dword [fs:su_cmd_line_ptr],0x90000+old_cmd_line_here
389                 mov ax,old_max_cmd_len          ; 2.02+ allow a higher limit
390 .adjusted:
391
392                 mov cx,[CmdLineLen]
393                 cmp cx,ax
394                 jna .len_ok
395                 mov cx,ax                       ; Truncate the command line
396 .len_ok:
397                 fs rep movsb
398                 stosb                           ; Final null, note AL=0 already
399                 mov [CmdLineEnd],di
400                 cmp dx,0200h
401                 jb .nomovesize
402                 mov [es:su_movesize],di         ; Tell the kernel what to move
403 .nomovesize:
404 .setup_done:
405
406 ;
407 ; Time to start setting up move descriptors
408 ;
409 setup_move:
410                 mov di,trackbuf
411                 xor cx,cx                       ; Number of descriptors
412
413                 mov bx,es                       ; real_mode_seg
414                 mov fs,bx
415                 push ds                         ; We need DS == ES == CS here
416                 pop es
417
418                 mov edx,100000h
419                 test byte [LoadFlags],LOAD_HIGH
420                 jnz .loading_high
421
422 ; Loading low: move real_mode stuff to 90000h, then move the kernel down
423                 mov eax,90000h
424                 stosd
425                 mov eax,core_real_mode
426                 stosd
427                 movzx eax,word [CmdLineEnd]
428                 stosd
429                 inc cx
430                 mov edx,10000h                  ; Revised target address
431                 mov bx,9000h                    ; Revised real mode segment
432
433 .loading_high:
434                 mov eax,edx                     ; Target address of kernel
435                 stosd
436                 mov eax,free_high_memory        ; Where currently loaded
437                 stosd
438                 neg eax
439                 add eax,[KernelEnd]
440                 stosd
441                 inc cx
442
443                 cmp word [InitRDPtr],0          ; Did we have an initrd?
444                 je .no_initrd
445
446                 mov eax,[fs:su_ramdiskat]
447                 stosd
448                 mov eax,[InitRDStart]
449                 stosd
450                 mov eax,[fs:su_ramdisklen]
451                 stosd
452                 inc cx
453
454 .no_initrd:
455                 push dword run_linux_kernel
456                 push cx                         ; descriptor list entries count
457
458                 ; BX points to the final real mode segment, and will be loaded
459                 ; into DS.
460
461                 test byte [QuietBoot],QUIET_FLAG
462                 jz replace_bootstrap
463                 jmp replace_bootstrap_noclearmode
464
465 run_linux_kernel:
466 ;
467 ; Set up segment registers and the Linux real-mode stack
468 ; Note: ds == the real mode segment
469 ;
470                 cli
471                 mov ax,ds
472                 mov ss,ax
473                 mov sp,strict word linux_stack
474                 ; Point HeapEnd to the immediate of the instruction above
475 HeapEnd         equ $-2                 ; Self-modifying code!  Fun!
476                 mov es,ax
477                 mov fs,ax
478                 mov gs,ax
479
480 ;
481 ; We're done... now RUN THAT KERNEL!!!!
482 ; Setup segment == real mode segment + 020h; we need to jump to offset
483 ; zero in the real mode segment.
484 ;
485                 add ax,020h
486                 push ax
487                 push word 0h
488                 retf
489
490 ;
491 ; Load an older kernel.  Older kernels always have 4 setup sectors, can't have
492 ; initrd, and are always loaded low.
493 ;
494 old_kernel:
495                 xor ax,ax
496                 cmp word [InitRDPtr],ax         ; Old kernel can't have initrd
497                 je .load
498                 mov si,err_oldkernel
499                 jmp abort_load
500 .load:
501                 mov byte [LoadFlags],al         ; Always low
502                 mov word [KernelVersion],ax     ; Version 0.00
503                 jmp any_kernel
504
505 ;
506 ; parse_load_initrd
507 ;
508 ; Parse an initrd= option and load the initrds.  This sets
509 ; InitRDStart and InitRDEnd with dword padding between; we then
510 ; do a global memory shuffle to move it to the end of memory.
511 ;
512 ; On entry, EDI points to where to start loading.
513 ;
514 parse_load_initrd:
515                 push es
516                 push ds
517                 mov ax,real_mode_seg
518                 mov ds,ax
519                 push cs
520                 pop es                  ; DS == real_mode_seg, ES == CS
521
522                 mov [cs:InitRDStart],edi
523                 mov [cs:InitRDEnd],edi
524
525                 mov si,[cs:InitRDPtr]
526
527 .get_chunk:
528                 ; DS:SI points to the start of a name
529
530                 mov bx,si
531 .find_end:
532                 lodsb
533                 cmp al,','
534                 je .got_end
535                 cmp al,' '
536                 jbe .got_end
537                 jmp .find_end
538
539 .got_end:
540                 push ax                 ; Terminating character
541                 push si                 ; Next filename (if any)
542                 mov byte [si-1],0       ; Zero-terminate
543                 mov si,bx               ; Current filename
544
545                 push di
546                 mov di,InitRD           ; Target buffer for mangled name
547                 pm_call pm_mangle_name
548                 pop di
549                 call loadinitrd
550
551                 pop si
552                 pop ax
553                 mov [si-1],al           ; Restore ending byte
554
555                 cmp al,','
556                 je .get_chunk
557
558                 ; Compute the initrd target location
559                 ; Note: we round to a page boundary twice here.  The first
560                 ; time it is to make sure we don't use any fractional page
561                 ; which may be valid RAM but which will be ignored by the
562                 ; kernel (and therefore is inaccessible.)  The second time
563                 ; it is to make sure we start out on page boundary.
564                 mov edx,[cs:InitRDEnd]
565                 sub edx,[cs:InitRDStart]
566                 mov [su_ramdisklen],edx
567                 mov eax,[cs:MyHighMemSize]
568                 and ax,0F000h           ; Round to a page boundary
569                 sub eax,edx
570                 and ax,0F000h           ; Round to a page boundary
571                 mov [su_ramdiskat],eax
572
573                 pop ds
574                 pop es
575                 ret
576
577 ;
578 ; Load RAM disk into high memory
579 ;
580 ; Input:        InitRD          - set to the mangled name of the initrd
581 ;               EDI             - location to load
582 ; Output:       EDI             - location for next initrd
583 ;               InitRDEnd       - updated
584 ;
585 loadinitrd:
586                 push ds
587                 push es
588                 mov ax,cs                       ; CS == DS == ES
589                 mov ds,ax
590                 mov es,ax
591                 push edi
592                 mov di,InitRD
593                 pm_call pm_searchdir                  ; Look for it in directory
594                 pop edi
595                 jz .notthere
596
597                 push si
598                 mov si,crlfloading_msg          ; Write "Loading "
599                 call writestr_qchk
600                 mov si,InitRD                   ; Write ramdisk name
601                 call writestr_qchk
602                 mov si,dotdot_msg               ; Write dots
603                 call writestr_qchk
604                 pop si
605
606 .li_skip_echo:
607                 mov dx,3
608                 mov bx,dot_pause
609                 call load_high
610                 mov [InitRDEnd],ebx
611
612                 pop es
613                 pop ds
614                 ret
615
616 .notthere:
617                 mov si,err_noinitrd
618                 call writestr
619                 mov si,InitRD
620                 call writestr
621                 mov si,crlf_msg
622                 jmp abort_load
623
624 ;
625 ; writestr_qchk: writestr, except allows output to be suppressed
626 ;               assumes CS == DS
627 ;
628 writestr_qchk:
629                 test byte [QuietBoot],QUIET_FLAG
630                 jz writestr
631                 ret
632
633                 section .data16
634 crlfloading_msg db CR, LF
635 loading_msg     db 'Loading ', 0
636 dotdot_msg      db '.'
637 dot_msg         db '.', 0
638 ready_msg       db 'ready.', CR, LF, 0
639 err_oldkernel   db 'Cannot load a ramdisk with an old kernel image.'
640                 db CR, LF, 0
641 err_noinitrd    db CR, LF, 'Could not find ramdisk image: ', 0
642
643 boot_image      db 'BOOT_IMAGE='
644 boot_image_len  equ $-boot_image
645
646 ;
647 ; Command line options we'd like to take a look at
648 ;
649 %macro cmd_opt  2
650 %strlen cmd_opt_len     %1
651         db cmd_opt_len
652         db %1
653         dw %2
654 %endmacro
655 options_list:
656                 cmd_opt "vga=", opt_vga
657                 cmd_opt "mem=", opt_mem
658                 cmd_opt "quiet", opt_quiet
659 str_initrd      equ $+1                 ; Pointer to "initrd=" in memory
660                 cmd_opt "initrd=", opt_initrd
661 %if IS_PXELINUX
662                 cmd_opt "keeppxe", opt_keeppxe
663 %endif
664                 db 0
665
666                 section .bss16
667                 alignb 4
668 MyHighMemSize   resd 1                  ; Possibly adjusted highmem size
669 RamdiskMax      resd 1                  ; Highest address for ramdisk
670 KernelSize      resd 1                  ; Size of kernel in bytes
671 KernelSects     resd 1                  ; Size of kernel in sectors
672 KernelEnd       resd 1                  ; Ending address of the kernel image
673 InitRDStart     resd 1                  ; Start of initrd (pre-relocation)
674 InitRDEnd       resd 1                  ; End of initrd (pre-relocation)
675 CmdLineLen      resw 1                  ; Length of command line including null
676 CmdLineEnd      resw 1                  ; End of the command line in real_mode_seg
677 SetupSecs       resw 1                  ; Number of setup sectors (+bootsect)
678 KernelVersion   resw 1                  ; Kernel protocol version
679 ;
680 ; These are derived from the command-line parser
681 ;
682 InitRDPtr       resw 1                  ; Pointer to initrd= option in command line
683 LoadFlags       resb 1                  ; Loadflags from kernel
684 QuietBoot       resb 1                  ; Set if a quiet boot is requested