setup_memory()

setup_arch() literary sets up the system architecture in order to
make kernel work as kernel.

As far as memory concerning, setup_memory() is called.
This function calcurates some PFN (Page Frame Number).

Some macro for PFN is defined in ${linux src}/include/asm/setup.h.

#define PFN_UP(x)       (((x) + PAGE_SIZE-1) >> PAGE_SHIFT)
#define PFN_DOWN(x)     ((x) >> PAGE_SHIFT)
#define PFN_PHYS(x)     ((x) << PAGE_SHIFT)

In setup_memory(), min_low_pfn is set to PFN_UP(init_pg_tables_end).
init_pg_tables_end is initialized in ${linux src}/arch/i386/kernel/head.S.

        movl %edi,(init_pg_tables_end - __PAGE_OFFSET)

This code is just behind of the setup code for initial temporary
Page Directory Table and Page Entry Table.
%edi has a last page table entry for initial temporary page table.
So, round up the PFN with PFN_UP macro because used page is not
available for a new page.

At that time, Paging Unit is not enabled, the address is subtracted by
__PAGE_OFFSET. But now, Paging Unit is working and kernel code is
mapped into the physical address in which real code resides,

        min_low_pfn = PFN_UP(init_pg_tables_end);

assigns correct PFN to min_low_pfn.

Next, find_max_pfn() is called to set max_pfn.
This function uses remaped (without overlap) e820map structure.
It walk these maps and if the type that map hass is E820_RAM,
checks wether it has highest address in the system or not.

When all arrays are checked, the max PFN is gotton from highest address
and assigns it to max_pfn variable.

The next code.

        max_low_pfn = find_max_low_pfn();

This function at first assigns max_pfn into max_low_pfn (local variable).
If its value does not exceed MAXMEM_PFN, returns its value and
this value is assigned to max_low_pfn (gloval variable).

MAXMEM_PFN = -PAGE_OFFSET = -(0xC0000000) = 0x40000000

This is a maximum address for user process.
When amount of memory system has exceeds MAXMEM_PFN,
code adjusts the exceeded memory to highmem or cut if off
according to the kernel configuration like CONFIG_HIGHMEM and CONFIG_X86_PAE.

MAXMEM_PFN means system memory is 3Gbytes, which is so huge.
For simplicity, we assign max_pfn to max_low_pfn.

setup_bootmem_allocator()

Next, setup_bootmem_allocator() is called.
This function calls init_bootmem(), which is wrapper function defined in
${linux}/mm/bootmem.c

unsigned long __init init_bootmem (unsigned long start, unsigned long pages)
{
        max_low_pfn = pages;
        min_low_pfn = start;
        return(init_bootmem_core(NODE_DATA(0), start, 0, pages));
}

If CONFIG_NEED_MULTIPLE_NODE is not defined, NODE_DATA is defined in
${linux src}/include/linux/mmzone.h

extern struct pglist_data contig_page_data;
#define NODE_DATA(nid)          (&contig_page_data)

And init_bootmem_core() is also defined in ${linux src}/mm/bootmem.c.

static unsigned long __init init_bootmem_core (pg_data_t *pgdat,
        unsigned long mapstart, unsigned long start, unsigned long end)
{
        bootmem_data_t *bdata = pgdat->bdata;
        unsigned long mapsize = ((end - start)+7)/8;

pgdat->pgdat_next = pgdat_list; pgdat_list = pgdat;

mapsize = ALIGN(mapsize, sizeof(long)); bdata->node_bootmem_map = phys_to_virt(mapstart << PAGE_SHIFT); bdata->node_boot_start = (start << PAGE_SHIFT); bdata->node_low_pfn = end;

memset(bdata->node_bootmem_map, 0xff, mapsize);

return mapsize; }

This function calcurates bit length for map using "end" and "start"
page frame number.
The bitmap is filled as series of bytes.

mapsize = ((end - start) + 7)/8.

global variable "pgdat_list" is set to pgdat itself and its pgdat_next
member is set to pgdat_list. Next is set to itself.

phys_to_virt simplly add 0xC0000000.

So, mapstart (sifted to left by PAGE_SHIFT) is converted into virtual address
of kernel memory space.

In short, init_bootmem_core() will fill specified size of bitmap pointed by
"node_bootmem_map" with 0xff.

init_bootmem() is called with two argument min_low_pfn and max_low_pfn.
This means that bitmap starts at min_low_pfn and mapsieze is
max_low_pfn - 0 (from start) and these maps are filled with 1.

All pages except for memories assigned for kernel code and data are reserved.

After all pages are marked reserved, setup_bootmem_allcator() calls
register_bootmem_low_pages() with argument max_low_pfn.

max_low_pfn is Page Frame Number for the last address assigned to kernel so far.
This function walks through e820map array and checks if each map is E820_RAM
or not and it exceeds max_low_pfn (maximum PFN for the operation now).

If the map now treating matches this condition, makes Page Frame Number
from start and end of this memory region, then clear corresponding bitmap,
which means these pages are available.

In order to clear these bit, it uses free_bootmem(), which is wrapper for
free_bootmem_core() defined in ${linux src}/mm/bootmem.c.

Then, it reserves kernel space plus memory bitmap and page 0
using reserve_bootmem().

reserve_bootmem() is called as reserve_bootmem(addr, size) and set bits
of bitmap from address to address + size.

At last of setup_bootmem_allocator(), several memory region is reserved
according to configuration.

setup_memory() is over.
top
inserted by FC2 system