root/core/runkernel.inc

Revision c987272b97ecb89db19ee438dba0c8c2418f8ffe, 16.5 KB (checked in by H. Peter Anvin <hpa@…>, 15 months ago)

core: Fix 'trackbuf' descriptor list byte length

(Tested using a Linux bzImage, with and without an initrd.)

Per shuffle_and_boot documentation, %ecx must contain the descriptor
list byte length, but it's set with such list end address instead. Fix.

Signed-off-by: Ahmed S. Darwish <darwish.07@…>
Signed-off-by: H. Peter Anvin <hpa@…>

  • Property mode set to 100644
Line 
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;
43is_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;
75construct_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;
94parse_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
138opt_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
158opt_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
168opt_quiet:
169                mov byte [QuietBoot],QUIET_FLAG
170                ret
171
172%if IS_PXELINUX
173opt_keeppxe:
174                or byte [KeepPXE],1             ; KeepPXE set by command line
175                ret
176%endif
177
178opt_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;
190cmdline_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;
197prepare_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;
219new_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
233any_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;
244read_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
278high_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;
307load_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
346root_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;
353setup_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;
409setup_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
465run_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
475HeapEnd         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;
494old_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;
514parse_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;
585loadinitrd:
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;
628writestr_qchk:
629                test byte [QuietBoot],QUIET_FLAG
630                jz writestr
631                ret
632
633                section .data16
634crlfloading_msg db CR, LF
635loading_msg     db 'Loading ', 0
636dotdot_msg      db '.'
637dot_msg         db '.', 0
638ready_msg       db 'ready.', CR, LF, 0
639err_oldkernel   db 'Cannot load a ramdisk with an old kernel image.'
640                db CR, LF, 0
641err_noinitrd    db CR, LF, 'Could not find ramdisk image: ', 0
642
643boot_image      db 'BOOT_IMAGE='
644boot_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
655options_list:
656                cmd_opt "vga=", opt_vga
657                cmd_opt "mem=", opt_mem
658                cmd_opt "quiet", opt_quiet
659str_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
668MyHighMemSize   resd 1                  ; Possibly adjusted highmem size
669RamdiskMax      resd 1                  ; Highest address for ramdisk
670KernelSize      resd 1                  ; Size of kernel in bytes
671KernelSects     resd 1                  ; Size of kernel in sectors
672KernelEnd       resd 1                  ; Ending address of the kernel image
673InitRDStart     resd 1                  ; Start of initrd (pre-relocation)
674InitRDEnd       resd 1                  ; End of initrd (pre-relocation)
675CmdLineLen      resw 1                  ; Length of command line including null
676CmdLineEnd      resw 1                  ; End of the command line in real_mode_seg
677SetupSecs       resw 1                  ; Number of setup sectors (+bootsect)
678KernelVersion   resw 1                  ; Kernel protocol version
679;
680; These are derived from the command-line parser
681;
682InitRDPtr       resw 1                  ; Pointer to initrd= option in command line
683LoadFlags       resb 1                  ; Loadflags from kernel
684QuietBoot       resb 1                  ; Set if a quiet boot is requested
Note: See TracBrowser for help on using the browser.