심심해서 분석해봤는데, 아직 좀 찜찜한 부분이 있네요.
main.c의 init_heap에서 heap_end_ptr에 + 0x200이 이해가 안되던 부분이었죠.
부트 로더는 다음과 같은 코드를 반드시 넣어야 한다고 합니다.
kernel과의 protocol을 맞추기 위한 코드죠.
830 unsigned long base_ptr; /* base address for real-mode segment */ 831 832 if ( setup_sects == 0 ) { 833 setup_sects = 4; 834 } 835 836 if ( protocol >= 0x0200 ) { 837 type_of_loader = <type code>; 838 if ( loading_initrd ) { 839 ramdisk_image = <initrd_address>; 840 ramdisk_size = <initrd_size>; 841 } 842 843 if ( protocol >= 0x0202 && loadflags & 0x01 ) //High memory 844 heap_end = 0xe000; 845 else 846 heap_end = 0x9800; 847 848 if ( protocol >= 0x0201 ) { 849 heap_end_ptr = heap_end - 0x200; 850 loadflags |= 0x80; /* CAN_USE_HEAP */ 851 } 852 853 if ( protocol >= 0x0202 ) { 854 cmd_line_ptr = base_ptr + heap_end; 855 strcpy(cmd_line_ptr, cmdline); 856 } else { 857 cmd_line_magic = 0xA33F; 858 cmd_line_offset = heap_end; 859 setup_move_size = heap_end + strlen(cmdline)+1; 860 strcpy(base_ptr+cmd_line_offset, cmdline); 861 } 862 } else { 863 /* Very old kernel */ 864 865 heap_end = 0x9800; 866 867 cmd_line_magic = 0xA33F; 868 cmd_line_offset = heap_end; 869 870 /* A very old kernel MUST have its real-mode code 871 loaded at 0x90000 */ 872 873 if ( base_ptr != 0x90000 ) { 874 /* Copy the real-mode kernel */ 875 memcpy(0x90000, base_ptr, (setup_sects+1)*512); 876 base_ptr = 0x90000; /* Relocated */ 877 } 878 879 strcpy(0x90000+cmd_line_offset, cmdline); 880 881 /* It is recommended to clear memory up to the 32K mark */ 882 memset(0x90000 + (setup_sects+1)*512, 0, 883 (64-(setup_sects+1))*512); 884 }
위에서 kernel 2.01 protocol 부터는 무조건 heap을 사용하게 됩니다.
왜 0x200을 더했나, 이게 의문이었는데, 보면 heap_end_ptr = heap_end - 0x200인 것을 확인할 수 있습니다.
bzimage_compress라는 부분에서 보게 될 것 같습니다.
heap_end에서 0x200을 빼준건 heap overrun (overflow) 때문에 그랬다고 하는데, (boot.txt에 protocol 설명이 있습니다.)
정확한 이유는 모르겠네요.
head_end를 단순히 stack_size에 맞추어 heap과 stack의 경계를 잡는 것으로 생각됩니다.
114 115 static void init_heap(void) 116 { 117 char *stack_end; 118 119 if (boot_params.hdr.loadflags & CAN_USE_HEAP) { 120 asm("leal %P1(%%esp),%0" 121 : "=r" (stack_end) : "i" (-STACK_SIZE)); 122 123 heap_end = (char *) 124 ((size_t)boot_params.hdr.heap_end_ptr + 0x200); 125 if (heap_end > stack_end) 126 heap_end = stack_end; 127 } else { 128 /* Boot protocol 2.00 only, no heap available */ 129 puts("WARNING: Ancient bootloader, some functionality " 130 "may be limited!n"); 131 }
header.S에서 fffc로 stack pointer를 설정하는 것과는 별개의 문제인 것 같습니다.
fffc는 그냥 wrap around되는 경우, heap size와 stack size를 더한 값이 0xffff보다 큰 0x10000 이 되었기 때문에
4byte를 뺀 원래의 값인 0xfffc로 stack의 end값을 지정하는 것 같네요.
444 movw %ss, %dx 445 cmpw %ax, %dx # %ds == %ss? 446 movw %sp, %dx 447 je 2f # -> assume %sp is reasonably set 448 449 # Invalid %ss, make up a new stack 450 movw $_end, %dx 451 testb $CAN_USE_HEAP, loadflags 452 jz 1f 453 movw heap_end_ptr, %dx 454 1: addw $STACK_SIZE, %dx 455 jnc 2f 456 xorw %dx, %dx # Prevent wraparound 457 458 2: # Now %dx should point to the end of our stack space 459 andw $~3, %dx # dword align (might as well...) 460 jnz 3f 461 movw $0xfffc, %dx # Make sure we're not zero 462 3: movw %ax, %ss 463 movzwl %dx, %esp # Clear upper half of %esp 464 sti # Now we should have a working stack
.
헷갈려하실 것 같아 메모리 레이아웃도 넣었습니다.
0x8000부터 0x9800까지가 heap 영역입니다.
참고로 boot.txt 다음과 같이 나와 있습니다. 0x90000에 올리는 경우 real mode address의 구성은 다음과 같습니다.
0x90000에 올리지 않는 다른 커널도 있을것 같네요. (X라는 주소로 표시된 것을 보면 알 수 있을 것 같네요.)