bcopy32: refactor the bcopy routine to be able to reuse the pm part
[syslinux.git] / runkernel.inc
blob39d8e908b8d8e9902bef81a22913b21862a9df8e
1 ;; -----------------------------------------------------------------------
2 ;;
3 ;;   Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
4 ;;
5 ;;   This program is free software; you can redistribute it and/or modify
6 ;;   it under the terms of the GNU General Public License as published by
7 ;;   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
8 ;;   Boston MA 02111-1307, USA; either version 2 of the License, or
9 ;;   (at your option) any later version; incorporated herein by reference.
11 ;; -----------------------------------------------------------------------
14 ;; runkernel.inc
16 ;; Common code for running a Linux kernel
20 ; Hook macros, that may or may not be defined
22 %ifndef HAVE_SPECIAL_APPEND
23 %macro SPECIAL_APPEND 0
24 %endmacro
25 %endif
27 %ifndef HAVE_UNLOAD_PREP
28 %macro UNLOAD_PREP 0
29 %endmacro
30 %endif
33 ; A Linux kernel consists of three parts: boot sector, setup code, and
34 ; kernel code.  The boot sector is never executed when using an external
35 ; booting utility, but it contains some status bytes that are necessary.
37 ; First check that our kernel is at least 1K, or else it isn't long
38 ; enough to have the appropriate headers.
40 ; We used to require the kernel to be 64K or larger, but it has gotten
41 ; popular to use the Linux kernel format for other things, which may
42 ; not be so large.
44 ; Additionally, we used to have a test for 8 MB or smaller.  Equally
45 ; obsolete.
47 is_linux_kernel:
48                 and dx,dx
49                 jnz kernel_sane
50                 cmp ax,1024                     ; Bootsect + 1 setup sect
51                 jb kernel_corrupt
52 kernel_sane:    push ax
53                 push dx
54                 push si
55                 mov si,loading_msg
56                 call cwritestr
58 ; Now start transferring the kernel
60                 push word real_mode_seg
61                 pop es
63                 movzx eax,ax                    ; Fix this by using a 32-bit
64                 shl edx,16                      ; register for the kernel size
65                 or eax,edx
66                 mov [KernelSize],eax
67                 add eax,SECTOR_SIZE-1
68                 shr eax,SECTOR_SHIFT
69                 mov [KernelSects],eax           ; Total sectors in kernel
72 ; Now, if we transfer these straight, we'll hit 64K boundaries.  Hence we
73 ; have to see if we're loading more than 64K, and if so, load it step by
74 ; step.
78 ; Start by loading the bootsector/setup code, to see if we need to
79 ; do something funky.  It should fit in the first 32K (loading 64K won't
80 ; work since we might have funny stuff up near the end of memory).
81 ; If we have larger than 32K clusters, yes, we're hosed.
83                 call abort_check                ; Check for abort key
84                 mov ecx,8000h >> SECTOR_SHIFT   ; Half a moby (32K)
85                 cmp ecx,[KernelSects]
86                 jna .normalkernel
87                 mov ecx,[KernelSects]
88 .normalkernel:
89                 sub [KernelSects],ecx
90                 xor bx,bx
91                 pop si                          ; Cluster pointer on stack
92                 call getfssec
93                 cmp word [es:bs_bootsign],0AA55h
94                 jne kernel_corrupt              ; Boot sec signature missing
97 ; Save the cluster pointer for later...
99                 push si
101 ; Get the BIOS' idea of what the size of high memory is.
103                 call highmemsize
105 ; Construct the command line (append options have already been copied)
107 construct_cmdline:
108                 mov di,[CmdLinePtr]
109                 mov si,boot_image               ; BOOT_IMAGE=
110                 mov cx,boot_image_len
111                 rep movsb
112                 mov si,KernelCName              ; Unmangled kernel name
113                 mov cx,[KernelCNameLen]
114                 rep movsb
115                 mov al,' '                      ; Space
116                 stosb
118                 SPECIAL_APPEND                  ; Module-specific hook
120                 mov si,[CmdOptPtr]              ; Options from user input
121                 call strcpy
124 ; Scan through the command line for anything that looks like we might be
125 ; interested in.  The original version of this code automatically assumed
126 ; the first option was BOOT_IMAGE=, but that is no longer certain.
128                 mov si,cmd_line_here
129                 xor ax,ax
130                 mov [InitRDPtr],ax              ; No initrd= option (yet)
131                 push es                         ; Set DS <- real_mode_seg
132                 pop ds
133 get_next_opt:   lodsb
134                 and al,al
135                 jz cmdline_end
136                 cmp al,' '
137                 jbe get_next_opt
138                 dec si
139                 mov eax,[si]
140                 cmp eax,'vga='
141                 je is_vga_cmd
142                 cmp eax,'mem='
143                 je is_mem_cmd
144 %if IS_PXELINUX
145                 cmp eax,'keep'                  ; Is it "keeppxe"?
146                 jne .notkeep
147                 cmp dword [si+3],'ppxe'
148                 jne .notkeep
149                 cmp byte [si+7],' '             ; Must be whitespace or EOS
150                 ja .notkeep
151                 or byte [cs:KeepPXE],1
152 .notkeep:
153 %endif
154                 push es                         ; Save ES -> real_mode_seg
155                 push cs
156                 pop es                          ; Set ES <- normal DS
157                 mov di,initrd_cmd
158                 mov cx,initrd_cmd_len
159                 repe cmpsb
160                 jne .not_initrd
162                 cmp al,' '
163                 jbe .noramdisk
164                 mov [cs:InitRDPtr],si
165                 jmp .not_initrd
166 .noramdisk:
167                 xor ax,ax
168                 mov [cs:InitRDPtr],ax
169 .not_initrd:    pop es                          ; Restore ES -> real_mode_seg
170 skip_this_opt:  lodsb                           ; Load from command line
171                 cmp al,' '
172                 ja skip_this_opt
173                 dec si
174                 jmp short get_next_opt
175 is_vga_cmd:
176                 add si,4
177                 mov eax,[si-1]
178                 mov bx,-1
179                 cmp eax,'=nor'                  ; vga=normal
180                 je vc0
181                 dec bx                          ; bx <- -2
182                 cmp eax,'=ext'                  ; vga=ext
183                 je vc0
184                 dec bx                          ; bx <- -3
185                 cmp eax,'=ask'                  ; vga=ask
186                 je vc0
187                 call parseint                   ; vga=<number>
188                 jc skip_this_opt                ; Not an integer
189 vc0:            mov [bs_vidmode],bx             ; Set video mode
190                 jmp short skip_this_opt
191 is_mem_cmd:
192                 add si,4
193                 call parseint
194                 jc skip_this_opt                ; Not an integer
195 %if HIGHMEM_SLOP != 0
196                 sub ebx,HIGHMEM_SLOP
197 %endif
198                 mov [cs:HighMemSize],ebx
199                 jmp short skip_this_opt
200 cmdline_end:
201                 push cs                         ; Restore standard DS
202                 pop ds
203                 sub si,cmd_line_here
204                 mov [CmdLineLen],si             ; Length including final null
206 ; Now check if we have a large kernel, which needs to be loaded high
208 prepare_header:
209                 mov dword [RamdiskMax], HIGHMEM_MAX     ; Default initrd limit
210                 cmp dword [es:su_header],HEADER_ID      ; New setup code ID
211                 jne old_kernel                  ; Old kernel, load low
212                 mov ax,[es:su_version]
213                 mov [KernelVersion],ax
214                 cmp ax,0200h                    ; Setup code version 2.0
215                 jb old_kernel                   ; Old kernel, load low
216                 cmp ax,0201h                    ; Version 2.01+?
217                 jb new_kernel                   ; If 2.00, skip this step
218                 ; Set up the heap (assuming loading high for now)
219                 mov word [es:su_heapend],linux_stack-512
220                 or byte [es:su_loadflags],80h   ; Let the kernel know we care
221                 cmp ax,0203h                    ; Version 2.03+?
222                 jb new_kernel                   ; Not 2.03+
223                 mov eax,[es:su_ramdisk_max]
224                 mov [RamdiskMax],eax            ; Set the ramdisk limit
227 ; We definitely have a new-style kernel.  Let the kernel know who we are,
228 ; and that we are clueful
230 new_kernel:
231                 mov byte [es:su_loader],my_id   ; Show some ID
232                 xor eax,eax
233                 mov [es:su_ramdisklen],eax      ; No initrd loaded yet
236 ; About to load the kernel.  This is a modern kernel, so use the boot flags
237 ; we were provided.
239                 mov al,[es:su_loadflags]
240                 mov [LoadFlags],al
242 ; Load the kernel.  We always load it at 100000h even if we're supposed to
243 ; load it "low"; for a "low" load we copy it down to low memory right before
244 ; jumping to it.
246 read_kernel:
247                 movzx ax,byte [es:bs_setupsecs] ; Setup sectors
248                 and ax,ax
249                 jnz .sects_ok
250                 mov al,4                        ; 0 = 4 setup sectors
251 .sects_ok:
252                 mov [SetupSecs],ax
254                 mov si,KernelCName              ; Print kernel name part of
255                 call cwritestr                  ; "Loading" message
256                 mov si,dotdot_msg               ; Print dots
257                 call cwritestr
259                 mov eax,[HighMemSize]
260                 sub eax,100000h                 ; Load address
261                 cmp eax,[KernelSize]
262                 jb no_high_mem          ; Not enough high memory
264 ; Move the stuff beyond the setup code to high memory at 100000h
266                 movzx esi,word [SetupSecs]      ; Setup sectors
267                 inc si                          ; plus 1 boot sector
268                 shl si,9                        ; Convert to bytes
269                 mov ecx,8000h                   ; 32K
270                 sub ecx,esi                     ; Number of bytes to copy
271                 push ecx
272                 add esi,(real_mode_seg << 4)    ; Pointer to source
273                 mov edi,100000h                 ; Copy to address 100000h
275                 call bcopy                      ; Transfer to high memory
277                 ; On exit EDI -> where to load the rest
279                 mov si,dot_msg                  ; Progress report
280                 call cwritestr
281                 call abort_check
283                 pop ecx                         ; Number of bytes in the initial portion
284                 pop si                          ; Restore file handle/cluster pointer
285                 mov eax,[KernelSize]
286                 sub eax,8000h                   ; Amount of kernel not yet loaded
287                 jbe high_load_done              ; Zero left (tiny kernel)
289                 xor dx,dx                       ; No padding needed
290                 mov bx,dot_pause                ; Print dots...
291                 call load_high                  ; Copy the file
293 high_load_done:
294                 mov [KernelEnd],edi
295                 mov ax,real_mode_seg            ; Set to real mode seg
296                 mov es,ax
298                 mov si,dot_msg
299                 call cwritestr
302 ; Now see if we have an initial RAMdisk; if so, do requisite computation
303 ; We know we have a new kernel; the old_kernel code already will have objected
304 ; if we tried to load initrd using an old kernel
306 load_initrd:
307                 cmp word [InitRDPtr],0
308                 jz nk_noinitrd
309                 call parse_load_initrd
310 nk_noinitrd:
312 ; Abandon hope, ye that enter here!  We do no longer permit aborts.
314                 call abort_check                ; Last chance!!
316                 mov si,ready_msg
317                 call cwritestr
319                 call vgaclearmode               ; We can't trust ourselves after this
321                 UNLOAD_PREP                     ; Module-specific hook
324 ; Now, if we were supposed to load "low", copy the kernel down to 10000h
325 ; and the real mode stuff to 90000h.  We assume that all bzImage kernels are
326 ; capable of starting their setup from a different address.
328                 mov ax,real_mode_seg
329                 mov es,ax
330                 mov fs,ax
333 ; Copy command line.  Unfortunately, the old kernel boot protocol requires
334 ; the command line to exist in the 9xxxxh range even if the rest of the
335 ; setup doesn't.
337 setup_command_line:
338                 cli                             ; In case of hooked interrupts
339                 mov dx,[KernelVersion]
340                 test byte [LoadFlags],LOAD_HIGH
341                 jz need_high_cmdline
342                 cmp dx,0202h                    ; Support new cmdline protocol?
343                 jb need_high_cmdline
344                 ; New cmdline protocol
345                 ; Store 32-bit (flat) pointer to command line
346                 ; This is the "high" location, since we have bzImage
347                 mov dword [fs:su_cmd_line_ptr],(real_mode_seg << 4)+cmd_line_here
348                 jmp in_proper_place
350 need_high_cmdline:
352 ; Copy command line down to fit in high conventional memory
353 ; -- this happens if we have a zImage kernel or the protocol
354 ; is less than 2.02.
356                 mov si,cmd_line_here
357                 mov di,old_cmd_line_here
358                 mov [fs:kern_cmd_magic],word CMD_MAGIC ; Store magic
359                 mov [fs:kern_cmd_offset],di     ; Store pointer
360                 mov word [HeapEnd],old_linux_stack
361                 mov ax,255                      ; Max cmdline limit
362                 cmp dx,0201h
363                 jb .adjusted
364                 ; Protocol 2.01+
365                 mov word [fs:su_heapend],old_linux_stack-512
366                 jbe .adjusted
367                 ; Protocol 2.02+
368                 ; Note that the only reason we would end up here is
369                 ; because we have a zImage, so we anticipate the move
370                 ; to 90000h already...
371                 mov dword [fs:su_cmd_line_ptr],0x90000+old_cmd_line_here
372                 mov ax,4095                     ; 2.02+ allow a higher limit
373 .adjusted:
375                 mov cx,[CmdLineLen]
376                 cmp cx,ax
377                 jna .len_ok
378                 mov cx,ax                       ; Truncate the command line
379 .len_ok:
380                 fs rep movsb
381                 stosb                           ; Final null, note AL=0 already
382                 cmp dx,0200h
383                 jb .nomovesize
384                 mov [es:su_movesize],di         ; Tell the kernel what to move
385 .nomovesize:
387                 test byte [LoadFlags],LOAD_HIGH
388                 jnz in_proper_place             ; If high load, we're done
391 ; Loading low; we can't assume it's safe to run in place.
393 ; Copy real_mode stuff up to 90000h
395                 mov ax,9000h
396                 mov es,ax
397                 mov cx,di                       ; == su_movesize (from above)
398                 add cx,3                        ; Round up
399                 shr cx,2                        ; Convert to dwords
400                 xor si,si
401                 xor di,di
402                 fs rep movsd                    ; Copy setup + boot sector
404 ; Some kernels in the 1.2 ballpark but pre-bzImage have more than 4
405 ; setup sectors, but the boot protocol had not yet been defined.  They
406 ; rely on a signature to figure out if they need to copy stuff from
407 ; the "protected mode" kernel area.  Unfortunately, we used that area
408 ; as a transfer buffer, so it's going to find the signature there.
409 ; Hence, zero the low 32K beyond the setup area.
411                 mov di,[SetupSecs]
412                 inc di                          ; Setup + boot sector
413                 mov cx,32768/512                ; Sectors/32K
414                 sub cx,di                       ; Remaining sectors
415                 shl di,9                        ; Sectors -> bytes
416                 shl cx,7                        ; Sectors -> dwords
417                 xor eax,eax
418                 rep stosd                       ; Clear region
420 ; Copy the kernel down to the "low" location (the kernel will then
421 ; move itself again, sigh.)
423                 mov ecx,[KernelSize]
424                 mov esi,100000h
425                 mov edi,10000h
426                 call bcopy
429 ; Now everything is where it needs to be...
431 ; When we get here, es points to the final segment, either
432 ; 9000h or real_mode_seg
434 in_proper_place:
437 ; If the default root device is set to FLOPPY (0000h), change to
438 ; /dev/fd0 (0200h)
440                 cmp word [es:bs_rootdev],byte 0
441                 jne root_not_floppy
442                 mov word [es:bs_rootdev],0200h
443 root_not_floppy:
446 ; Copy the disk table to high memory, then re-initialize the floppy
447 ; controller
449 %if IS_SYSLINUX || IS_MDSLINUX
450                 lgs si,[cs:fdctab]
451                 mov di,[cs:HeapEnd]
452                 mov cx,6
453                 gs rep movsw
454                 mov [cs:fdctab],word linux_fdctab ; Save new floppy tab pos
455                 mov [cs:fdctab+2],es
456 %endif
458                 call cleanup_hardware
460 ; If we're debugging, wait for a keypress so we can read any debug messages
462 %ifdef debug
463                 xor ax,ax
464                 int 16h
465 %endif
467 ; Set up segment registers and the Linux real-mode stack
468 ; Note: es == the real mode segment
471                 cli
472                 mov bx,es
473                 mov ds,bx
474                 mov fs,bx
475                 mov gs,bx
476                 mov ss,bx
477                 mov sp,strict word linux_stack
478                 ; Point HeapEnd to the immediate of the instruction above
479 HeapEnd         equ $-2                 ; Self-modifying code!  Fun!
482 ; We're done... now RUN THAT KERNEL!!!!
483 ; Setup segment == real mode segment + 020h; we need to jump to offset
484 ; zero in the real mode segment.
486                 add bx,020h
487                 push bx
488                 push word 0h
489                 retf
492 ; Load an older kernel.  Older kernels always have 4 setup sectors, can't have
493 ; initrd, and are always loaded low.
495 old_kernel:
496                 xor ax,ax
497                 cmp word [InitRDPtr],ax         ; Old kernel can't have initrd
498                 je .load
499                 mov si,err_oldkernel
500                 jmp abort_load
501 .load:
502                 mov byte [LoadFlags],al         ; Always low
503                 mov word [KernelVersion],ax     ; Version 0.00
504                 jmp read_kernel
507 ; parse_load_initrd
509 ; Parse an initrd= option and load the initrds.  Note that we load
510 ; from the high end of memory first, so we parse this option from
511 ; left to right.
513 parse_load_initrd:
514                 push es
515                 push ds
516                 mov ax,real_mode_seg
517                 mov ds,ax
518                 push cs
519                 pop es                  ; DS == real_mode_seg, ES == CS
521                 mov si,[cs:InitRDPtr]
522 .find_end:
523                 lodsb
524                 cmp al,' '
525                 ja .find_end
526                 ; Now SI points to one character beyond the
527                 ; byte that ended this option.
529 .get_chunk:
530                 dec si
532                 ; DS:SI points to a termination byte
534                 xor ax,ax
535                 xchg al,[si]            ; Zero-terminate
536                 push si                 ; Save ending byte address
537                 push ax                 ; Save ending byte
539 .find_start:
540                 dec si
541                 cmp si,[cs:InitRDPtr]
542                 je .got_start
543                 cmp byte [si],','
544                 jne .find_start
546                 ; It's a comma byte
547                 inc si
549 .got_start:
550                 push si
551                 mov di,InitRD           ; Target buffer for mangled name
552                 call mangle_name
553                 call loadinitrd
554                 pop si
556                 pop ax
557                 pop di
558                 mov [di],al             ; Restore ending byte
560                 cmp si,[cs:InitRDPtr]
561                 ja .get_chunk
563                 pop ds
564                 pop es
565                 ret
568 ; Load RAM disk into high memory
570 ; Input:        InitRD          - set to the mangled name of the initrd
572 loadinitrd:
573                 push ds
574                 push es
575                 mov ax,cs                       ; CS == DS == ES
576                 mov ds,ax
577                 mov es,ax
578                 mov si,InitRD
579                 mov di,InitRDCName
580                 call unmangle_name              ; Create human-readable name
581                 sub di,InitRDCName
582                 mov [InitRDCNameLen],di
583                 mov di,InitRD
584                 call searchdir                  ; Look for it in directory
585                 jz .notthere
587                 mov cx,dx
588                 shl ecx,16
589                 mov cx,ax                       ; ECX <- ram disk length
591                 mov ax,real_mode_seg
592                 mov es,ax
594                 push ecx                        ; Bytes to load
595                 mov edx,[HighMemSize]           ; End of memory
596                 dec edx
597                 mov eax,[RamdiskMax]            ; Highest address allowed by kernel
598                 cmp edx,eax
599                 jna .memsize_ok
600                 mov edx,eax                     ; Adjust to fit inside limit
601 .memsize_ok:
602                 inc edx
603                 and dx,0F000h                   ; Round down to 4K boundary
604                 sub edx,ecx                     ; Subtract size of ramdisk
605                 and dx,0F000h                   ; Round down to 4K boundary
606                 cmp edx,[KernelEnd]             ; Are we hitting the kernel image?
607                 jb no_high_mem
609                 cmp dword [es:su_ramdisklen],0
610                 je .highest
611                 ; The total length has to include the padding between
612                 ; different ramdisk files, so consider "the length" the
613                 ; total amount we're about to adjust the base pointer.
614                 mov ecx,[es:su_ramdiskat]
615                 sub ecx,edx
616 .highest:
617                 add [es:su_ramdisklen],ecx
619                 mov [es:su_ramdiskat],edx       ; Load address
620                 mov edi,edx                     ; initrd load address
622                 dec edx                         ; Note: RamdiskMax is addr-1
623                 mov [RamdiskMax],edx            ; Next initrd loaded here
625                 push si
626                 mov si,crlfloading_msg          ; Write "Loading "
627                 call cwritestr
628                 mov si,InitRDCName              ; Write ramdisk name
629                 call cwritestr
630                 mov si,dotdot_msg               ; Write dots
631                 call cwritestr
632                 pop si
634                 pop eax                         ; Bytes to load
635                 mov dx,0FFFh                    ; Pad to page
636                 mov bx,dot_pause                ; Print dots...
637                 call load_high                  ; Load the file
639                 pop es
640                 pop ds
641                 ret
643 .notthere:
644                 mov si,err_noinitrd
645                 call cwritestr
646                 mov si,InitRDCName
647                 call cwritestr
648                 mov si,crlf_msg
649                 jmp abort_load
651 no_high_mem:                                    ; Error routine
652                 mov si,err_nohighmem
653                 jmp abort_load
655                 ret
657                 section .data
658 crlfloading_msg db CR, LF
659 loading_msg     db 'Loading ', 0
660 dotdot_msg      db '.'
661 dot_msg         db '.', 0
662 ready_msg       db 'ready.', CR, LF, 0
663 err_oldkernel   db 'Cannot load a ramdisk with an old kernel image.'
664                 db CR, LF, 0
665 err_noinitrd    db CR, LF, 'Could not find ramdisk image: ', 0
666 err_nohighmem   db 'Not enough memory to load specified kernel.', CR, LF, 0
668 boot_image      db 'BOOT_IMAGE='
669 boot_image_len  equ $-boot_image
671                 section .bss
672                 alignb 4
673 RamdiskMax      resd 1                  ; Highest address for ramdisk
674 KernelSize      resd 1                  ; Size of kernel in bytes
675 KernelSects     resd 1                  ; Size of kernel in sectors
676 KernelEnd       resd 1                  ; Ending address of the kernel image
677 CmdLineLen      resw 1                  ; Length of command line including null
678 SetupSecs       resw 1                  ; Number of setup sectors
679 InitRDPtr       resw 1                  ; Pointer to initrd= option in command line
680 KernelVersion   resw 1                  ; Kernel protocol version
681 LoadFlags       resb 1                  ; Loadflags from kernel