리눅스/커널2014. 9. 19. 09:02

Kernel Debugging Tricks

Debugging the kernel is not necessarily rocket science; in fact it can be achieved using very simple and straight forward techniques and some time, patience and perseverance. This page describes some tricks and techniques to help debug the kernel.

printk is your friend

The simplest, and probably most effective way to debug the kernel is via printk(). This enables one to print messages to the console, and it very similar to printf(). Note that printk() can slow down the execution of code which can alter the way code runs, for example, changing the way race conditions occur.

CHANGING THE RING BUFFER SIZE

The internal kernel console message buffer can sometimes be too small to capture all of the printk messages, especially when debug code generates a lot of printk messages. If the buffer fills up, it wraps around and one can lose valueable debug messages.

To increase the internal buffer, use the kernel boot parameter:

log_buf_len=N

where N is the size of the buffer in bytes, and must be a power of 2.

CHANGING DEBUG LEVELS

One can specify the type of printk() log level by pre-pending the 1st printk() argument with one of the following:

KERN_EMERG    /* system is unusable                   */
KERN_ALERT    /* action must be taken immediately     */
KERN_CRIT     /* critical conditions                  */
KERN_ERR      /* error conditions                     */
KERN_WARNING  /* warning conditions                   */
KERN_NOTICE   /* normal but significant condition     */
KERN_INFO     /* informational                        */
KERN_DEBUG    /* debug-level messages                 */

e.g. printk(KERN_DEBUG "example debug message\n");

If one does not specify the log level then the default log level of KERN_WARNING is used. For example, enable all levels of console message:

echo 7 > /proc/sys/kernel/printk

To view console messages at boot, remove the quite and splash boot parameters from the kernel boot line in grub. This will disable the usplash splash screen and re-enable console messages.

Serial Console

Serial console enables one to dump out console messages over a serial cable. Most modern PCs do not have legacy serial ports, so instead, one can use a USB serial dongle instead. A "null serial cable" or "universal file transfer cable" is needed to connect the target computer with the host. Most commonly this will be a DB9 female to DB9 female null serial cable. In addition, one needs to enable USB serial support as a kernel build configuration:

CONFIG_USB_SERIAL_CONSOLE=y
CONFIG_USB_SERIAL=y

and enable the appropriate driver, e.g.:

CONFIG_USB_SERIAL_PL2303=y

and boot this kernel with

console=tty console=ttyUSB0,9600n8

one may need to adjust the baud rate appropriately.

Note: Generally, there is NO hardware or software flow control on serial console drivers, which means one may get dropped characters when running very high speed tty baud rates, such as 115200 baud.

Console Messages

Kernel Oops messages general contain a fair amount of information, ranging from register and process state dump and a stack dump too. Unfortunately the stack dump can be more than 25 lines and can scroll off the top of the 25 line Virtual Console. Hence to capture more of a Oops, try the following:

chvt 1
setfont /usr/share/consolefonts/Uni1-VGA8.psf.gz

Of course, one may still have a stack dump that scrolls the top of the Oops message off the console, so one trick is to rebuild the kernel with the stack dump removed, just to capture the initial Oops information. To do this, modify dump_stack in arch/x86/kernel/dumpstack_*.c and comment out the call to show_trace()

Slowing down kernel messages on boot

One may find a machine hangs during the kernel boot process and one would like to be able to see all the kernel messages but unfortunately they scroll off the console too quickly. One can slow down kernel console messages at boot time using by building the kernel with the following option enabled:

CONFIG_BOOT_PRINTK_DELAY=y

And boot the machine with the following kernel boot parameter:

boot_delay=N

where N = msecs delay between each console message.

Kernel panic during suspend

Debugging suspend/resume issues can be difficult if the kernel panics during suspend, especially late in the suspend because console messages are disabled. One can stop console messages from being suspended by using the kernel parameter no_console_suspend:

no_console_suspend=1

This will force the console not to suspend. Boot with this option, chvt 1 (to console #1), and suspend using pm-suspend

Serial Console in VirtualBox

In some debug scenerios it can be helpful to debug the kernel running inside a virtual machine. This is useful for some classes of non-hardware specific bugs, for example generic kernel core problems or debugging file system drivers.

One can capture Linux console messages running inside VirtualBox by setting it the VirtualBox serial log to /tmp/vbox and running a serial tty communications program such as minicom, and configure it to communicate with a named pipe tty called unix#/tmp/vbox

Boot with virtualised kernel boot line:

console=tty console=ttyS0,9600 

and minicom will capture the console messages

Network Console

One can route console messages over a network using netconsole. Note that it's not useful for capturing kernel panics as kernel halts before the messages can be transmitted over the network. However it can be useful to monitor systems without the need of message serial console cabling.

see Documentation/networking/netconsole.txt

netconsole=[src-port]@[src-ip]/[<dev>],[tgt-port]@<tgt-ip>/[tgt-macaddr]

        where
             src-port      source for UDP packets (defaults to 6665)
             src-ip        source IP to use (interface address)
             dev           network interface (eth0)
             tgt-port      port for logging agent (6666)
             tgt-ip        IP address for logging agent
             tgt-macaddr   ethernet MAC address for logging agent (broadcast)

Examples:

linux netconsole=4444@10.0.0.1/eth1,9353@10.0.0.2/12:34:56:78:9a:bc

The remote host can run either 'netcat -u -l -p <port>' or syslogd.

gdb on vmlinux

One can disassemble a built kernel using gdb on the vmlinux image. This is useful when one gets a kernel Oops message and a stack dump - one can then disassemble the object code and see where the Oops is occuring. For example:

gdb  debian/build/build-generic/vmlinux
(gdb) disassemble printk
Dump of assembler code for function printk:
0xffffffff8023dce0 <printk+0>:  sub    $0xd8,%rsp
0xffffffff8023dce7 <printk+7>:  lea    0xe0(%rsp),%rax
0xffffffff8023dcef <printk+15>: mov    %rsi,0x28(%rsp)
0xffffffff8023dcf4 <printk+20>: mov    %rsp,%rsi
0xffffffff8023dcf7 <printk+23>: mov    %rdx,0x30(%rsp)
0xffffffff8023dcfc <printk+28>: mov    %rcx,0x38(%rsp)
0xffffffff8023dd01 <printk+33>: mov    %rax,0x8(%rsp)
0xffffffff8023dd06 <printk+38>: lea    0x20(%rsp),%rax
0xffffffff8023dd0b <printk+43>: mov    %r8,0x40(%rsp)
0xffffffff8023dd10 <printk+48>: mov    %r9,0x48(%rsp)
0xffffffff8023dd15 <printk+53>: movl   $0x8,(%rsp)
0xffffffff8023dd1c <printk+60>: movl   $0x30,0x4(%rsp)
0xffffffff8023dd24 <printk+68>: mov    %rax,0x10(%rsp)
0xffffffff8023dd29 <printk+73>: callq  0xffffffff8023d980 <vprintk>
0xffffffff8023dd2e <printk+78>: add    $0xd8,%rsp
0xffffffff8023dd35 <printk+85>: retq   
End of assembler dump.

Objdump

If one has the built object code at hand, one can disassemble the object using objdump as follows:

objdump -SdCg debian/build/build-generic/fs/dcache.o

Using GDB to find the location where your kernel panicked or oopsed.

A quick and easy way to find the line of code where your kernel panicked or oopsed is to use GDB list command. You can do this as follows.

Lets assume your panic/oops message says something like:

[  174.507084] Stack:
[  174.507163]  ce0bd8ac 00000008 00000000 ce4a7e90 c039ce30 ce0bd8ac c0718b04 c07185a0
[  174.507380]  ce4a7ea0 c0398f22 ce0bd8ac c0718b04 ce4a7eb0 c037deee ce0bd8e0 ce0bd8ac
[  174.507597]  ce4a7ec0 c037dfe0 c07185a0 ce0bd8ac ce4a7ed4 c037d353 ce0bd8ac ce0bd8ac
[  174.507888] Call Trace:
[  174.508125]  [<c039ce30>] ? sd_remove+0x20/0x70
[  174.508235]  [<c0398f22>] ? scsi_bus_remove+0x32/0x40
[  174.508326]  [<c037deee>] ? __device_release_driver+0x3e/0x70
[  174.508421]  [<c037dfe0>] ? device_release_driver+0x20/0x40
[  174.508514]  [<c037d353>] ? bus_remove_device+0x73/0x90
[  174.508606]  [<c037bccf>] ? device_del+0xef/0x150
[  174.508693]  [<c0399207>] ? __scsi_remove_device+0x47/0x80
[  174.508786]  [<c0399262>] ? scsi_remove_device+0x22/0x40
[  174.508877]  [<c0399324>] ? __scsi_remove_target+0x94/0xd0
[  174.508969]  [<c03993c0>] ? __remove_child+0x0/0x20
[  174.509060]  [<c03993d7>] ? __remove_child+0x17/0x20
[  174.509148]  [<c037b868>] ? device_for_each_child+0x38/0x60
[  174.509241]  [<c039938f>] ? scsi_remove_target+0x2f/0x60
[  174.509393]  [<d0c38907>] ? __iscsi_unbind_session+0x77/0xa0 [scsi_transport_iscsi]
[  174.509699]  [<c015272e>] ? run_workqueue+0x6e/0x140
[  174.509801]  [<d0c38890>] ? __iscsi_unbind_session+0x0/0xa0 [scsi_transport_iscsi]
[  174.509977]  [<c0152888>] ? worker_thread+0x88/0xe0
[  174.510047]  [<c01566a0>] ? autoremove_wake_function+0x0/0x40

Lets say you want to know what line of code represents sd_remove+0x20/0x70. cd to the ubuntu debian/build/build-generic directory in your kernel tree and run gdb on the ".o" file which has the function sd_remove() in this case in sd.o, and use the gdb "list" command, (gdb) list *(function+0xoffset), in this case function is sd_remove() and offset is 0x20, and gdb should tell you the line number where you hit the panic or oops. This has worked for me very reliably for most cases.

manjo@hungry:~/devel/ubuntu/kernel/ubuntu-karmic-397906/debian/build/build-generic/drivers/scsi$ gdb sd.o
GNU gdb (GDB) 6.8.50.20090628-cvs-debian
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
(gdb) list *(sd_remove+0x20)
0x1650 is in sd_remove (/home/manjo/devel/ubuntu/kernel/ubuntu-karmic-397906/drivers/scsi/sd.c:2125).
2120    static int sd_remove(struct device *dev)
2121    {
2122            struct scsi_disk *sdkp;
2123    
2124            async_synchronize_full();
2125            sdkp = dev_get_drvdata(dev);
2126            blk_queue_prep_rq(sdkp->device->request_queue, scsi_prep_fn);
2127            device_del(&sdkp->dev);
2128            del_gendisk(sdkp->disk);
2129            sd_shutdown(dev);
(gdb)


'리눅스 > 커널' 카테고리의 다른 글

syslog LOCAL7  (0) 2014.08.28
Dentry cache hash table 사이즈  (0) 2014.04.08
Memory 정보 출력되는 곳  (0) 2014.04.07
driver 초기화 시에 쓰이는 module_init  (0) 2014.04.04
Machine 이름 바꾸기  (0) 2014.04.03
Posted by code cat
리눅스/커널2014. 8. 28. 10:52

BSD 와 리눅스 배포판에서는 /etc/rsyslog.conf 파일을 사용하는데, rsyslog.conf는 syslog(결국 rsyslogd)의 설정 파일이다.

그리고 local7은 0~7 로 나눠진 user defined local facility이며, 특정 daemon에 대한 로깅을 하기 위한 수단이라고 생각하면 된다.

'리눅스 > 커널' 카테고리의 다른 글

커널 디버깅 방법  (0) 2014.09.19
Dentry cache hash table 사이즈  (0) 2014.04.08
Memory 정보 출력되는 곳  (0) 2014.04.07
driver 초기화 시에 쓰이는 module_init  (0) 2014.04.04
Machine 이름 바꾸기  (0) 2014.04.03
Posted by code cat
리눅스/커널2014. 4. 8. 15:10

dentry cache hash table size 지정은,

fs/dcache.c에서 alloc_large_system_hash를 통해서 된다. 그전에 dhash_entries는 kernel cmdline에서 지정하여 원하는 값을 패스 할 수 있다.

2965static void __init dcache_init(void)
2966{
2967        int loop;
2968
2969        /* 
2970         * A constructor could be added for stable state like the lists,
2971         * but it is probably not worth it because of the cache nature
2972         * of the dcache. 
2973         */
2974        dentry_cache = KMEM_CACHE(dentry,
2975                SLAB_RECLAIM_ACCOUNT|SLAB_PANIC|SLAB_MEM_SPREAD);
2976
2977        /* Hash may have been set up in dcache_init_early */
2978        if (!hashdist)
2979                return;
2980
2981        dentry_hashtable =
2982                alloc_large_system_hash("Dentry cache",
2983                                        sizeof(struct hlist_bl_head),
2984                                        dhash_entries,
2985                                        13,
2986                                        0,
2987                                        &d_hash_shift,
2988                                        &d_hash_mask,
2989                                        0);
2990
2991        for (loop = 0; loop < (1 << d_hash_shift); loop++)
2992                INIT_HLIST_BL_HEAD(dentry_hashtable + loop);
2993}


그리고 만약 dhash_entries값이 0이면(전역비초기화 변수 이므로 0으로 되어 bss영역에 있을 것이다) 5395라인 하위로 타고 들어가, nr_kernel_pages에 기반을 둔 2의 배수를 설정하고 기타 log2작업(아 이건 나중에 보자...)을 통해 hash table size를 출력한다.

5381void *__init alloc_large_system_hash(const char *tablename,
5382                                     unsigned long bucketsize,
5383                                     unsigned long numentries,
5384                                     int scale,
5385                                     int flags,
5386                                     unsigned int *_hash_shift,
5387                                     unsigned int *_hash_mask,
5388                                     unsigned long limit)
5389{
5390        unsigned long long max = limit;
5391        unsigned long log2qty, size;
5392        void *table = NULL;
5393
5394        /* allow the kernel cmdline to have a say */
5395        if (!numentries) {
5396                /* round applicable memory size up to nearest megabyte */
5397                numentries = nr_kernel_pages;
5398                numentries += (1UL << (20 - PAGE_SHIFT)) - 1;
5399                numentries >>= 20 - PAGE_SHIFT;
5400                numentries <<= 20 - PAGE_SHIFT;
5401
5402                /* limit to 1 bucket per 2^scale bytes of low memory */
5403                if (scale > PAGE_SHIFT)
5404                        numentries >>= (scale - PAGE_SHIFT);
5405                else
5406                        numentries <<= (PAGE_SHIFT - scale);
5407
5408                /* Make sure we've got at least a 0-order allocation.. */
5409                if (unlikely(flags & HASH_SMALL)) {
5410                        /* Makes no sense without HASH_EARLY */
5411                        WARN_ON(!(flags & HASH_EARLY));
5412                        if (!(numentries >> *_hash_shift)) {
5413                                numentries = 1UL << *_hash_shift;
5414                                BUG_ON(!numentries);
5415                        }
5416                } else if (unlikely((numentries * bucketsize) < PAGE_SIZE))
5417                        numentries = PAGE_SIZE / bucketsize;
5418        }
5419        numentries = roundup_pow_of_two(numentries);
5420
5421        /* limit allocation size to 1/16 total memory by default */
5422        if (max == 0) {
5423                max = ((unsigned long long)nr_all_pages << PAGE_SHIFT) >> 4;
5424                do_div(max, bucketsize);
5425        }
5426
5427        if (numentries > max)
5428                numentries = max;
5429
5430        log2qty = ilog2(numentries);
5431
5432        do {
5433                size = bucketsize << log2qty;
5434                if (flags & HASH_EARLY)
5435                        table = alloc_bootmem_nopanic(size);
5436                else if (hashdist)
5437                        table = __vmalloc(size, GFP_ATOMIC, PAGE_KERNEL);
5438                else {
5439                        /*
5440                         * If bucketsize is not a power-of-two, we may free
5441                         * some pages at the end of hash table which
5442                         * alloc_pages_exact() automatically does
5443                         */
5444                        if (get_order(size) < MAX_ORDER) {
5445                                table = alloc_pages_exact(size, GFP_ATOMIC);
5446                                kmemleak_alloc(table, size, 1, GFP_ATOMIC);
5447                        }
5448                }
5449        } while (!table && size > PAGE_SIZE && --log2qty);
5450
5451        if (!table)
5452                panic("Failed to allocate %s hash table\n", tablename);
5453
5454        printk(KERN_INFO "%s hash table entries: %ld (order: %d, %lu bytes)\n",
5455               tablename,
5456               (1UL << log2qty),
5457               ilog2(size) - PAGE_SHIFT,
5458               size);
5459
5460        if (_hash_shift)
5461                *_hash_shift = log2qty;
5462        if (_hash_mask)
5463                *_hash_mask = (1 << log2qty) - 1;
5464
5465        return table;
5466}


'리눅스 > 커널' 카테고리의 다른 글

커널 디버깅 방법  (0) 2014.09.19
syslog LOCAL7  (0) 2014.08.28
Memory 정보 출력되는 곳  (0) 2014.04.07
driver 초기화 시에 쓰이는 module_init  (0) 2014.04.04
Machine 이름 바꾸기  (0) 2014.04.03
Posted by code cat
리눅스/커널2014. 4. 7. 19:22

arch/arm/mm/init.c : mem_init(void)


 586        unsigned long reserved_pages, free_pages;
 587        struct memblock_region *reg;
 /*
 633         * Since our memory may not be contiguous, calculate the
 634         * real number of pages we have in this system
 635         */
 636        printk(KERN_INFO "Memory:");
 637        num_physpages = 0;
 638        for_each_memblock(memory, reg) {
 639                unsigned long pages = memblock_region_memory_end_pfn(reg) -
 640                        memblock_region_memory_base_pfn(reg);
 641                num_physpages += pages;
 642                printk(" %ldMB", pages >> (20 - PAGE_SHIFT));
 643        }
 644        printk(" = %luMB total\n", num_physpages >> (20 - PAGE_SHIFT));
 645
 646        printk(KERN_NOTICE "Memory: %luk/%luk available, %luk reserved, %luK highmem\n",
 647                nr_free_pages() << (PAGE_SHIFT-10),
 648                free_pages << (PAGE_SHIFT-10),
 649                reserved_pages << (PAGE_SHIFT-10),
 650                totalhigh_pages << (PAGE_SHIFT-10));

include/linux/memblock.h

 150#define for_each_memblock(memblock_type, region)                                        \
 151        for (region = memblock.memblock_type.regions;                           \
 152             region < (memblock.memblock_type.regions + memblock.memblock_type.cnt);    \
 153             region++)
 154

include/linux/memblock.h

 123/**
 124 * memblock_region_memory_end_pfn - Return the end_pfn this region
 125 * @reg: memblock_region structure
 126 */
 127static inline unsigned long memblock_region_memory_end_pfn(const struct memblock_region *reg)
 128{
 129        return PFN_DOWN(reg->base + reg->size);
 130}

include/linux/memblock.h

 132/**
 133 * memblock_region_reserved_base_pfn - Return the lowest pfn intersecting with the reserved region
 134 * @reg: memblock_region structure
 135 */
 136static inline unsigned long memblock_region_reserved_base_pfn(const struct memblock_region *reg)
 137{
 138        return PFN_DOWN(reg->base);
 139}


include/linux/memblock.h

  37struct memblock {
  38        phys_addr_t current_limit;
  39        phys_addr_t memory_size;        /* Updated by memblock_analyze() */
  40        struct memblock_type memory;
  41        struct memblock_type reserved;
  42};

meminfo 라는 구조체에 nr_banks 와 membank라는 구조체가 멤버로 있다.  이들은 부팅 과정에서 이미 arm_add_memory라는 함수를 통해서 bank에 대한 start 및 size가 정해졌있다.(
early_init_dt_add_memory()나 parse_tag_mem32()등의 함수를 통해서 세팅된다(확인필요)

  29struct membank {
  30        phys_addr_t start;
  31        phys_addr_t size;
  32        unsigned int highmem;
  33};
  34
  35struct meminfo {
  36        int nr_banks;
  37        struct membank bank[NR_BANKS];
  38};

그리고 arch/arm/mm/init.c:find_limits()라는 함수를 통해서, 각 메모리 뱅크를 돌면서 (참고로 이건 3.1.10소스다. 최신은 좀 틀리다)

 135static void __init find_limits(unsigned long *min, unsigned long *max_low,
 136        unsigned long *max_high)
 137{
 138        struct meminfo *mi = &meminfo;
 139        int i;
 140
 141        *min = -1UL;
 142        *max_low = *max_high = 0;
 143
 144        for_each_bank (i, mi) {
 145                struct membank *bank = &mi->bank[i];
 146                unsigned long start, end;
 147
 148                start = bank_pfn_start(bank);
 149                end = bank_pfn_end(bank);
 150
 151                if (*min > start)
 152                        *min = start;
 153                if (*max_high < end)
 154                        *max_high = end;
 155                if (bank->highmem)
 156                        continue;
 157                if (*max_low < end)
 158                        *max_low = end;
 159        }
 160}

밑의 매크로를 통해서 페이지프레임넘버를 가져온다.  

  45#define bank_pfn_start(bank)    __phys_to_pfn((bank)->start)
  46#define bank_pfn_end(bank)      __phys_to_pfn((bank)->start + (bank)->size)
 139#define __phys_to_pfn(paddr)    ((unsigned long)((paddr) >> PAGE_SHIFT))

가져온 pfn을 start, end에 assign하고 전달받은 min, max_low, max_high를 그에 맞게 설정해 준다.  

그 후, bootmem_init()에서 arm_bootmem_init(min, max_low)를 통해 bootmem bitmap page 할당, bootmem allocator 초기화, lowmem 해재, lowmem memblock reservation 등을 한다.

위의 할당된 bootmem 은 나중에 init/main.c 에서 mem_init()를 호출하면,

 453static void __init mm_init(void)
 454{
 455        /*
 456         * page_cgroup requires countinous pages as memmap
 457         * and it's bigger than MAX_ORDER unless SPARSEMEM.
 458         */
 459        page_cgroup_init_flatmem();
 460        mem_init();
 461        kmem_cache_init();
 462        percpu_init_late();
 463        pgtable_cache_init();
 464        vmalloc_init();
 465}

mem_init()에서 bootmem에 할당되었던 메모리를 해제한다.
 600 totalram_pages += free_all_bootmem();

mm/bootmem.c

 250unsigned long __init free_all_bootmem(void)
 251{
 252        unsigned long total_pages = 0;
 253        bootmem_data_t *bdata;
 254
 255        list_for_each_entry(bdata, &bdata_list, list)
 256                total_pages += free_all_bootmem_core(bdata);
 257
 258        return total_pages;
 259}

자 그러면 이제 우리가 원하던 부분이다.

 610        reserved_pages = free_pages = 0;
 611
 612        for_each_bank(i, &meminfo) {
 613                struct membank *bank = &meminfo.bank[i];
 614                unsigned int pfn1, pfn2;
 615                struct page *page, *end;
 616
 617                pfn1 = bank_pfn_start(bank);
 618                pfn2 = bank_pfn_end(bank);
 619
 620                page = pfn_to_page(pfn1);
 621                end  = pfn_to_page(pfn2 - 1) + 1;
 622
 623                do {
 624                        if (PageReserved(page))
 625                                reserved_pages++;
 626                        else if (!page_count(page))
 627                                free_pages++;
 628                        page++;
 629                } while (page < end);
 630        }
 631
 632        /*
 633         * Since our memory may not be contiguous, calculate the
 634         * real number of pages we have in this system
 635         */
 636        printk(KERN_INFO "Memory:");
 637        num_physpages = 0;
 638        for_each_memblock(memory, reg) {
 639                unsigned long pages = memblock_region_memory_end_pfn(reg) -
 640                        memblock_region_memory_base_pfn(reg);
 641                num_physpages += pages;
 642                printk(" %ldMB", pages >> (20 - PAGE_SHIFT));
 643        }
 644        printk(" = %luMB total\n", num_physpages >> (20 - PAGE_SHIFT));
 645
 646        printk(KERN_NOTICE "Memory: %luk/%luk available, %luk reserved, %luK highmem\n",
 647                nr_free_pages() << (PAGE_SHIFT-10),
 648                free_pages << (PAGE_SHIFT-10),
 649                reserved_pages << (PAGE_SHIFT-10),
 650                totalhigh_pages << (PAGE_SHIFT-10));
 651
 652#define MLK(b, t) b, t, ((t) - (b)) >> 10
 653#define MLM(b, t) b, t, ((t) - (b)) >> 20
 654#define MLK_ROUNDUP(b, t) b, t, DIV_ROUND_UP(((t) - (b)), SZ_1K)
 655
 656        printk(KERN_NOTICE "Virtual kernel memory layout:\n"
 657                        "    vector  : 0x%08lx - 0x%08lx   (%4ld kB)\n"
 658#ifdef CONFIG_HAVE_TCM
 659                        "    DTCM    : 0x%08lx - 0x%08lx   (%4ld kB)\n"
 660                        "    ITCM    : 0x%08lx - 0x%08lx   (%4ld kB)\n"
 661#endif
 662                        "    fixmap  : 0x%08lx - 0x%08lx   (%4ld kB)\n"
 663#ifdef CONFIG_MMU
 664                        "    DMA     : 0x%08lx - 0x%08lx   (%4ld MB)\n"
 665#endif
 666                        "    vmalloc : 0x%08lx - 0x%08lx   (%4ld MB)\n"
 667                        "    lowmem  : 0x%08lx - 0x%08lx   (%4ld MB)\n"
 668#ifdef CONFIG_HIGHMEM
 669                        "    pkmap   : 0x%08lx - 0x%08lx   (%4ld MB)\n"
 670#endif
 671                        "    modules : 0x%08lx - 0x%08lx   (%4ld MB)\n"
 672                        "      .text : 0x%p" " - 0x%p" "   (%4d kB)\n"
 673                        "      .init : 0x%p" " - 0x%p" "   (%4d kB)\n"
 674                        "      .data : 0x%p" " - 0x%p" "   (%4d kB)\n"
 675                        "       .bss : 0x%p" " - 0x%p" "   (%4d kB)\n",

meminfo의 nr_bank숫자 만큼 루핑을 하면서, bank의 start주소와 end주소의 pfn을 가져와, 각각 page, end에 저장시키고, page에서부터 end까지 안에서 루핑하면서, page가 reserved일 경우, reserved_pages++ 하고, !page_count(page)일 경우 free_pages++하고, 마지막으로 page를 increment시킨다.

 for_each_memblock(memory, reg) 는 
 150#define for_each_memblock(memblock_type, region)                                        \
 151        for (region = memblock.memblock_type.regions;                           \
 152             region < (memblock.memblock_type.regions + memblock.memblock_type.cnt);    \
 153             region++)

memblock_type의 memory안의 memblock_region의 regions을 돌면서(memory의 cnt숫자만큼), 

  26struct memblock_region {
  27        phys_addr_t base;
  28        phys_addr_t size;
  29};
  30
  31struct memblock_type {
  32        unsigned long cnt;      /* number of regions */
  33        unsigned long max;      /* size of the allocated array */
  34        struct memblock_region *regions;
  35};
  36
  37struct memblock {
  38        phys_addr_t current_limit;
  39        phys_addr_t memory_size;        /* Updated by memblock_analyze() */
  40        struct memblock_type memory;
  41        struct memblock_type reserved;
  42};

pages에 region의 base + size의 PFN 값에서 base의 PFN값을 뺀 값을 설정한다.

 639                unsigned long pages = memblock_region_memory_end_pfn(reg) -
 640                        memblock_region_memory_base_pfn(reg);

그리고 이렇게 얻어진 pages를 num_phypages에 증가시켜준다.

그러면 드디오 이 num_phypages >> (20 - PAGE_SHIFT)가
Memory: = xxx MB total

에 나오는 xxx가 된다. 

'리눅스 > 커널' 카테고리의 다른 글

syslog LOCAL7  (0) 2014.08.28
Dentry cache hash table 사이즈  (0) 2014.04.08
driver 초기화 시에 쓰이는 module_init  (0) 2014.04.04
Machine 이름 바꾸기  (0) 2014.04.03
Machine 이름 알아내기  (0) 2014.04.03
Posted by code cat
리눅스/커널2014. 4. 4. 09:13

driver초기화 시에 흔히 불리는 module_init(x)는 include/linux/init.h에

#define module_init(x)    __initcall(x);

로 정의되어 있다.  여기서 x는 kernel 부팅 타임이나 모듈 삽입시에 불리는 함수명이 된다.

built in 모듈일 경우, do_initcalls()에서 불릴 것이고,

module 일 경우, module insertion에 불린다.

'리눅스 > 커널' 카테고리의 다른 글

Dentry cache hash table 사이즈  (0) 2014.04.08
Memory 정보 출력되는 곳  (0) 2014.04.07
Machine 이름 바꾸기  (0) 2014.04.03
Machine 이름 알아내기  (0) 2014.04.03
early_param에 대해  (0) 2014.03.31
Posted by code cat
리눅스/커널2014. 4. 3. 13:39

MACH_TYPE_XXX 와 machine 이름을 overwrite하기 위해서는 아래 매크로를 활용하면 된다.

linux/arch/arm/include/asm/mach/arch.h

  79 * Set of macros to define architecture features.  This is built into
  80 * a table by the linker.
  81 */
  82#define MACHINE_START(_type,_name)                      \
  83static const struct machine_desc __mach_desc_##_type    \
  84 __used                                                 \
  85 __attribute__((__section__(".arch.info.init"))) = {    \
  86        .nr             = MACH_TYPE_##_type,            \
  87        .name           = _name,
  88
  89#define MACHINE_END                             \
  90};


'리눅스 > 커널' 카테고리의 다른 글

Memory 정보 출력되는 곳  (0) 2014.04.07
driver 초기화 시에 쓰이는 module_init  (0) 2014.04.04
Machine 이름 알아내기  (0) 2014.04.03
early_param에 대해  (0) 2014.03.31
/dev/tty 와 /dev/console의 차이점  (0) 2014.02.05
Posted by code cat
리눅스/커널2014. 4. 3. 09:30

start_kernel()-->setup_arch()-->setup_machine_tags()

arch/arm/kernel/setup.c에서setup_arch()함수 내에서 머신이름을 설정한다.

machine_arch_type은 include/generated/mach-types.h에서 정의되어 있는데 이는 gen-mach-types 툴을 이용해서 생성된다.
예)
define MACH_TYPE_CODECAT             1

ifdef CONFIG_CODECAT
define machine_arch_type                   MACH_TYPE_CODECAT

machine_arch_type은 setup_machine_tags의 인자로 넘어가고, setup_machine_tags()내의
for_each_machine_desc는 __arch_info_begin 에서 __arch_info_end 를 순환하면서 machine_desc구조체의 nr(architecture number를 인자로 받아온 machine_arch_type과 비교하여 같은 경우, archiecture name을 찍어준다.

'리눅스 > 커널' 카테고리의 다른 글

driver 초기화 시에 쓰이는 module_init  (0) 2014.04.04
Machine 이름 바꾸기  (0) 2014.04.03
early_param에 대해  (0) 2014.03.31
/dev/tty 와 /dev/console의 차이점  (0) 2014.02.05
FrameBuffer size 구하기  (0) 2013.12.11
Posted by code cat
리눅스/커널2014. 3. 31. 10:14

early_param("HI_MEM", HI_MEM_Init) 

  --> __setup_param(str, fn, fn, 1) 으로 정의되어 있는데 이를 자세히 보면,

static const char __setup_str_##unique_id[] __initcost \
__aligned(1) = str; \
static struct obs_kernel_param __setup_##unique_id \
  __used __section(.init.setup)
  __attribute__((aligned((sizeof(long))))) \
  = { __setup_str_##unique_id, fn, early }


long 사이즈로 align된 .init.setp섹션에 obs_kernel_param 구조체 형태의 __setup_##unique_id 안에 { __setup_str_##unique_id, fn, early } 형태로 초기화 시킨다. 


obs_kernel_param 구조체는 

const char *str
int (*setup_func)(char *);
int early;

로 구성되어 있다.

이렇게 설정된 early_param의 내용은 init/main.c에서 parse_early_options() --> parse_args("early options", cmdline, NULL, 0, do_early_param) --> parse_one()안에서
return handle_unknown에서 do_early_param이 실행된다.

do_early_param을 실행시킬 때, 그동안 쌓인 obs_kernel_param 리스트(?)들을 순회하면서 실행 시킨다.(섹션에 구조체가 리스트로 들어가기 보다는 각각의 구조체가 엔트리로 들어가는 거 같은데 좀 더 확인이 필요하다.)


__setup_start[] 와 __setup_end[]는 include/asm-generic/vmlinux.lds.h 에서 다음과 같이 정의되어 있다.


#define INIT_SETUP(initsetup_align) \ . = ALIGN(initsetup_align); \ VMLINUX_SYMBOL(__setup_start) = .; \ *(.init.setup) \ VMLINUX_SYMBOL(__setup_end) = .;

또한 INIT_SETUP은 arch/arm/kernel/vmlinux.lds.S에서 16byte(?)으로 align되어 있다.

.init.data : { #ifndef CONFIG_XIP_KERNEL INIT_DATA #endif INIT_SETUP(16) INIT_CALLS CON_INITCALL SECURITY_INITCALL INIT_RAM_FS }


좀 더 자세히 봐야 하는데, 결국 맨 위, "HI_MEM"이라는 parameter에 대한 처리를 HI_MEM_init에서 처리할 수 있게 된다.


'리눅스 > 커널' 카테고리의 다른 글

Machine 이름 바꾸기  (0) 2014.04.03
Machine 이름 알아내기  (0) 2014.04.03
/dev/tty 와 /dev/console의 차이점  (0) 2014.02.05
FrameBuffer size 구하기  (0) 2013.12.11
[커널] FB 문서  (0) 2013.12.09
Posted by code cat
리눅스/커널2014. 2. 5. 11:44

출처:http://chaoxifer-univ.blogspot.kr/2010/10/devtty-devconsole.html


/dev/tty 와 /dev/console의 차이점


There is a subtle difference between the console and the terminal. In a windowed environment, a tty terminal is associated with each shell session; in other words, each command line window such as an xterm is a separate terminal. Redirection to /dev/tty sends output to the active window. The console, on the other hand, is the screen. It is the monitor in general. Most windowing programs provide a special switch to the shell windows that will link /dev/console to the window instead of writing output directly on the screen. In any event, output can be redirected to these devices quite easily.



출처:http://unix.stackexchange.com/questions/60641/linux-difference-between-dev-console-dev-tty-and-dev-tty0


From the documentation:

/dev/tty        Current TTY device
/dev/console    System console
/dev/tty0       Current virtual console

In the good old days /dev/console was System Administrator console. And TTYs were users' serial devices attached to a server. Now /dev/console and /dev/tty0 represent current display and usually are the same. You can override it for example by adding console=ttyS0 to grub.conf. After that your /dev/tty0 is a monitor and /dev/console is /dev/ttyS0.

An exercise to show the difference between /dev/tty and /dev/tty0:

Switch to the 2nd console by pressing Ctrl+Alt+F2. Login as root. Type sleep 5; echo tty0 > /dev/tty0. Press Enter and switch to the 3rd console by pressing Alt+F3. Now switch back to the 2nd console by pressing Alt+F2. Type sleep 5; echo tty > /dev/tty, press Enter and switch to the 3rd console.

You can see that tty is the console where process starts, and tty0 is a always current console.




  • /dev/console is a virtual set of devices which can be set as a parameter at boot time. It might be redirected to a serial device or a virtual console and by default points to /dev/tty0. When multiple console= options are passed to the kernel, the console output will go to more than one device.

  • /dev/tty0 is the current virtual console

  • /dev/tty[1-x] is one of the virtual consoles you switch to with control-alt-F1 and so on.

  • /dev/tty is the console used by the process querying it. Unlike the other devices, you do not need root privileges to write to it. Note also that processes like the ones launched by cron and similar batch processes have no usable /dev/tty, as they aren't associated with any. These processes have a ? in the TTY column of ps -ef output.


'리눅스 > 커널' 카테고리의 다른 글

Machine 이름 알아내기  (0) 2014.04.03
early_param에 대해  (0) 2014.03.31
FrameBuffer size 구하기  (0) 2013.12.11
[커널] FB 문서  (0) 2013.12.09
[안드로이드][커널]Unknown symbol _GLOBAL_OFFSET_TABLE_  (0) 2013.08.09
Posted by code cat
리눅스/커널2013. 12. 11. 16:00

framebuffer 사이즈를 구하는 방법은 다음과 같다.


framebuffer size = x resolution * y resolution * color depth


즉 1080p + 4 color depth이면 1920 * 1080 * 4가 된다.

'리눅스 > 커널' 카테고리의 다른 글

early_param에 대해  (0) 2014.03.31
/dev/tty 와 /dev/console의 차이점  (0) 2014.02.05
[커널] FB 문서  (0) 2013.12.09
[안드로이드][커널]Unknown symbol _GLOBAL_OFFSET_TABLE_  (0) 2013.08.09
Kernel Oops의 Taint 종류  (0) 2013.07.25
Posted by code cat