--- ./makedumpfile.h.org	2006-09-07 18:48:23.000000000 +0900
+++ ./makedumpfile.h	2006-09-07 18:55:49.000000000 +0900
@@ -44,6 +44,7 @@
  */
 enum {
 	NOT_FOUND_MEMTYPE,
+	SPARSEMEM,
 	FLATMEM
 };
 
@@ -99,6 +100,23 @@ isAnon(unsigned long mapping)
 }
 
 /*
+ * for SPARSEMEM
+ */
+#define SECTION_SIZE_BITS()	(info->section_size_bits)
+#define PAGESHIFT()		(ffs(info->page_size) - 1)
+#define PFN_SECTION_SHIFT()	(SECTION_SIZE_BITS() - PAGESHIFT())
+#define PAGES_PER_SECTION()	(1UL << PFN_SECTION_SHIFT())
+#define _SECTIONS_PER_ROOT()	(1)
+#define _SECTIONS_PER_ROOT_EXTREME()	(info->page_size / SIZE(mem_section))
+#define SECTIONS_PER_ROOT()	(info->sections_per_root)
+#define SECTION_ROOT_MASK()	(SECTIONS_PER_ROOT() - 1)
+#define SECTION_NR_TO_ROOT(sec)	((sec) / SECTIONS_PER_ROOT())
+#define SECTION_MAP_LAST_BIT	(1UL<<2)
+#define SECTION_MAP_MASK	(~(SECTION_MAP_LAST_BIT-1))
+#define NR_SECTION_ROOTS()	divideup(num_section, SECTIONS_PER_ROOT())
+#define SECTION_NR_TO_PFN(sec)	((sec) << PFN_SECTION_SHIFT())
+
+/*
  * Incorrect address
  */
 #define NOT_MEMMAP_ADDR	(0x0)
@@ -252,12 +270,18 @@ do { \
 #define MAXMEM                  (-PAGE_OFFSET-__VMALLOC_RESERVE)
 #define KVBASE_MASK		(0x7fffff)
 #define KVBASE			(SYMBOL(_stext) & ~KVBASE_MASK)
+#define _SECTION_SIZE_BITS	(26)
+#define _SECTION_SIZE_BITS_PAE	(30)
+#define get_machdep_info	get_machdep_info_x86
 #endif /* i386 */
 
 #ifdef __x86_64__
 #define PAGE_OFFSET		(0xffff810000000000)
 #define MAXMEM			(0x3fffffffffffUL)
 #define KVBASE			PAGE_OFFSET
+#define _SECTION_SIZE_BITS	(27)
+#define _SECTION_SIZE_BITS_PAE	(0)
+#define get_machdep_info	get_machdep_info_x86_64
 #endif /* x86_64 */
 
 #ifdef __ia64__ /* ia64 */
@@ -313,6 +337,8 @@ struct DumpInfo {
 	int		flag_read_config;    /* flag of reading config file */
 	size_t		page_size;           /* size of page */
 	unsigned int	max_mapnr;           /* number of page descriptor */
+	unsigned long   section_size_bits;
+	unsigned long   sections_per_root;
 
 	/*
 	 * diskdimp info:
@@ -372,12 +398,16 @@ struct DumpInfo {
 
 struct symbol_table {
 	unsigned long	mem_map;
+	unsigned long	mem_section;
+	unsigned long	pkmap_count;
+	unsigned long	pkmap_count_next;
 	unsigned long	system_utsname;
 	unsigned long	_stext;
 };
 
 struct size_table {
 	long	page;
+	long	mem_section;
 };
 
 struct offset_table {
@@ -386,4 +416,7 @@ struct offset_table {
 		long	_count;
 		long	mapping;
 	} page;
+	struct mem_section {
+		long	section_mem_map;
+	} mem_section;
 };
--- ./makedumpfile.c.org	2006-09-07 18:48:27.000000000 +0900
+++ ./makedumpfile.c	2006-09-07 18:57:19.000000000 +0900
@@ -709,7 +709,11 @@ out:
 int
 get_symbol_info(struct DumpInfo *info)
 {
+	int find_pkmap_count_next = FALSE;
 	unsigned long sym_mem_map = NOT_FOUND_SYMBOL;
+	unsigned long sym_mem_section = NOT_FOUND_SYMBOL;
+	unsigned long sym_pkmap_count = NOT_FOUND_SYMBOL;
+	unsigned long sym_pkmap_count_next = NOT_FOUND_SYMBOL;
 	unsigned long sym_system_utsname = NOT_FOUND_SYMBOL;
 	unsigned long sym__stext = NOT_FOUND_SYMBOL;
 	char buf[BUFSIZE_FGETS], *mapitems[MAXARGS], *endp;
@@ -734,6 +738,35 @@ get_symbol_info(struct DumpInfo *info)
 				return FALSE;
 			}
 		}
+		if (strcmp(mapitems[2], "mem_section") == 0) {
+			sym_mem_section = strtoul(mapitems[0], &endp, 16);
+			if ((!sym_mem_section || sym_mem_section == ULONG_MAX)
+			    || strlen(endp) != 0) {
+				ERRMSG("The map file has invalid address of %s",
+				    mapitems[2]);
+				return FALSE;
+			}
+		}
+		if (find_pkmap_count_next) {
+			sym_pkmap_count_next = strtoul(mapitems[0], &endp, 16);
+			if ((!sym_pkmap_count_next || sym_pkmap_count_next
+			    == ULONG_MAX) || strlen(endp) != 0) {
+				ERRMSG("The map file has invalid address of %s",
+				    mapitems[2]);
+				return FALSE;
+			}
+			find_pkmap_count_next = FALSE;
+		}
+		if (strcmp(mapitems[2], "pkmap_count") == 0) {
+			sym_pkmap_count = strtoul(mapitems[0], &endp, 16);
+			if ((!sym_pkmap_count || sym_pkmap_count == ULONG_MAX)
+			    || strlen(endp) != 0) {
+				ERRMSG("The map file has invalid address of %s",
+				    mapitems[2]);
+				return FALSE;
+			}
+			find_pkmap_count_next = TRUE;
+		}
 		if (strcmp(mapitems[2], "system_utsname") == 0) {
 			sym_system_utsname = strtoul(mapitems[0], &endp, 16);
         		if ((!sym_system_utsname
@@ -754,11 +787,17 @@ get_symbol_info(struct DumpInfo *info)
 			}
 		}
 		if ((sym_mem_map != NOT_FOUND_SYMBOL)
+		    && (sym_mem_section != NOT_FOUND_SYMBOL)
+		    && (sym_pkmap_count != NOT_FOUND_SYMBOL)
+		    && (sym_pkmap_count_next != NOT_FOUND_SYMBOL)
 		    && (sym_system_utsname != NOT_FOUND_SYMBOL)
 		    && (sym__stext != NOT_FOUND_SYMBOL))
 			break;
 	}
 	SYMBOL(mem_map) = sym_mem_map;
+	SYMBOL(mem_section) = sym_mem_section;
+	SYMBOL(pkmap_count) = sym_pkmap_count;
+	SYMBOL(pkmap_count_next) = sym_pkmap_count_next;
 	SYMBOL(system_utsname) = sym_system_utsname;
 	SYMBOL(_stext) = sym__stext;
 
@@ -829,6 +868,48 @@ get_structure_info(struct DumpInfo *info
 		OFFSET_INIT_NONAME(page.mapping, "page",
 		   sizeof(unsigned long), "mm.h");
 
+	/*
+	 * Get offsets of the mem_section's members.
+	 */
+	SIZE_INIT(mem_section, "mem_section", "mmzone.h");
+	OFFSET_INIT(mem_section.section_mem_map, "mem_section",
+	    "section_mem_map", "mmzone.h");
+
+	return TRUE;
+}
+
+int
+get_machdep_info_x86(struct DumpInfo *info)
+{
+	/* PAE */
+	if ((SYMBOL(pkmap_count) != NOT_FOUND_SYMBOL)
+	    && (SYMBOL(pkmap_count_next) != NOT_FOUND_SYMBOL)
+	    && ((SYMBOL(pkmap_count_next)-SYMBOL(pkmap_count))/sizeof(int))
+	    == 512)
+		info->section_size_bits = _SECTION_SIZE_BITS_PAE;
+	else
+		info->section_size_bits = _SECTION_SIZE_BITS;
+
+	return TRUE;
+}
+
+int
+get_machdep_info_x86_64(struct DumpInfo *info)
+{
+	info->section_size_bits = _SECTION_SIZE_BITS;
+
+	return TRUE;
+}
+
+int
+is_sparsemem_extreme(struct DumpInfo *info)
+{
+	/*
+	 * FIXME
+	 *   This makedumpfile command can not distinguish between SPARSEMEM
+	 *   and SPARSEMEM_EXTREME. Because it can not get the size of the
+	 *   area that starts from symbol "mem_section" yet.
+	 */
 	return TRUE;
 }
 
@@ -842,6 +923,10 @@ get_mem_type(struct DumpInfo *info)
 	    || (OFFSET(page._count) == NOT_FOUND_STRUCTURE)
 	    || (OFFSET(page.mapping) == NOT_FOUND_STRUCTURE))
 		ret = NOT_FOUND_MEMTYPE;
+	else if ((SYMBOL(mem_section) != NOT_FOUND_SYMBOL)
+	    && (SIZE(mem_section) != NOT_FOUND_STRUCTURE)
+	    && (OFFSET(mem_section.section_mem_map) != NOT_FOUND_STRUCTURE))
+		ret = SPARSEMEM;
 	else if (SYMBOL(mem_map) != NOT_FOUND_SYMBOL)
 		ret = FLATMEM;
 	else
@@ -897,6 +982,9 @@ generate_config(struct DumpInfo *info)
 	 * write the symbol of 1st kernel
 	 */
 	WRITE_SYMBOL("mem_map", mem_map);
+	WRITE_SYMBOL("mem_section", mem_section);
+	WRITE_SYMBOL("pkmap_count", pkmap_count);
+	WRITE_SYMBOL("pkmap_count_next", pkmap_count_next);
 	WRITE_SYMBOL("system_utsname", system_utsname);
 	WRITE_SYMBOL("_stext", _stext);
 
@@ -904,6 +992,7 @@ generate_config(struct DumpInfo *info)
 	 * write the structure size of 1st kernel
 	 */
 	WRITE_STRUCTURE_SIZE("page", page);
+	WRITE_STRUCTURE_SIZE("mem_section", mem_section);
 
 	/*
 	 * write the member offset of 1st kernel
@@ -911,6 +1000,8 @@ generate_config(struct DumpInfo *info)
 	WRITE_MEMBER_OFFSET("page.flags", page.flags);
 	WRITE_MEMBER_OFFSET("page._count", page._count);
 	WRITE_MEMBER_OFFSET("page.mapping", page.mapping);
+	WRITE_MEMBER_OFFSET("mem_section.section_mem_map",
+	    mem_section.section_mem_map);
 
 	return TRUE;
 }
@@ -1017,13 +1108,20 @@ read_config(struct DumpInfo *info)
 		return FALSE;
 
 	READ_SYMBOL("mem_map", mem_map);
+	READ_SYMBOL("mem_section", mem_section);
+	READ_SYMBOL("pkmap_count", pkmap_count);
+	READ_SYMBOL("pkmap_count_next", pkmap_count_next);
 	READ_SYMBOL("system_utsname", system_utsname);
 	READ_SYMBOL("_stext", _stext);
 
 	READ_STRUCTURE_SIZE("page", page);
+	READ_STRUCTURE_SIZE("mem_section", mem_section);
+
 	READ_MEMBER_OFFSET("page.flags", page.flags);
 	READ_MEMBER_OFFSET("page._count", page._count);
 	READ_MEMBER_OFFSET("page.mapping", page.mapping);
+	READ_MEMBER_OFFSET("mem_section.section_mem_map",
+	    mem_section.section_mem_map);
 
 	return TRUE;
 }
@@ -1067,12 +1165,123 @@ get_mm_flatmem(struct DumpInfo *info)
 	return TRUE;
 }
 
+unsigned long
+nr_to_section(struct DumpInfo *info, unsigned long nr, unsigned long *mem_sec)
+{
+	unsigned long addr;
+
+	if (!is_kvaddr(mem_sec[SECTION_NR_TO_ROOT(nr)]))
+		return NOT_KV_ADDR;
+
+	if (is_sparsemem_extreme(info))
+		addr = mem_sec[SECTION_NR_TO_ROOT(nr)] +
+		    (nr & SECTION_ROOT_MASK()) * SIZE(mem_section);
+	else
+		addr = SYMBOL(mem_section) + (nr * SIZE(mem_section));
+
+	if (!is_kvaddr(addr))
+		return NOT_KV_ADDR;
+
+	return addr;
+}
+
+unsigned long
+section_mem_map_addr(struct DumpInfo *info, unsigned long addr)
+{
+	char *mem_section;
+	unsigned long map;
+
+	if (!is_kvaddr(addr))
+		return NOT_KV_ADDR;
+
+	if ((mem_section = g_malloc0(SIZE(mem_section))) == NULL) {
+		ERRMSG("Can't allocate memory for a struct mem_section. %s\n",
+		    strerror(errno));
+		return NOT_KV_ADDR;
+	}
+	if (!readmem(info, addr, mem_section, SIZE(mem_section))) {
+		ERRMSG("Can't get a struct mem_section.\n");
+		return NOT_KV_ADDR;
+	}
+	map = ULONG(mem_section + OFFSET(mem_section.section_mem_map));
+	map &= SECTION_MAP_MASK;
+	g_free(mem_section);
+
+	return map;
+}
+
+unsigned long
+sparse_decode_mem_map(struct DumpInfo *info, ulong coded_mem_map,
+    unsigned long section_nr)
+{
+	if (!is_kvaddr(coded_mem_map))
+		return NOT_KV_ADDR;
+
+	return coded_mem_map +
+	    (SECTION_NR_TO_PFN(section_nr) * SIZE(page));
+}
+
+int
+get_mm_sparsemem(struct DumpInfo *info)
+{
+	unsigned int section_nr, mem_section_size, num_section;
+	unsigned long pfn_start, pfn_end;
+	unsigned long addr_section, addr_mem_map;
+	unsigned long *mem_sec;
+
+	/*
+	 * Get the address of the symbol "mem_section".
+	 */
+	num_section = divideup(info->max_mapnr, PAGES_PER_SECTION());
+	if (is_sparsemem_extreme(info)) {
+		info->sections_per_root = _SECTIONS_PER_ROOT_EXTREME();
+		mem_section_size = sizeof(void *) * NR_SECTION_ROOTS();
+	} else {
+		info->sections_per_root = _SECTIONS_PER_ROOT();
+		mem_section_size = SIZE(mem_section) * NR_SECTION_ROOTS();
+	}
+	if ((mem_sec = g_malloc0(mem_section_size)) == NULL) {
+		ERRMSG("Can't allocate memory for the mem_section. %s\n",
+		    strerror(errno));
+		return FALSE;
+	}
+	if (!readmem(info, SYMBOL(mem_section), mem_sec, mem_section_size)) {
+		ERRMSG("Can't get the address of mem_section.\n");
+		return FALSE;
+	}
+	info->num_mem_map = num_section;
+	if ((info->mem_map_data = (struct mem_map_data *)
+	    g_malloc0(sizeof(struct mem_map_data)*info->num_mem_map)) == NULL) {
+		ERRMSG("Can't allocate memory for the mem_map_data. %s\n",
+		    strerror(errno));
+		return FALSE;
+	}
+	for (section_nr = 0; section_nr < num_section; section_nr++) {
+		addr_section = nr_to_section(info, section_nr, mem_sec);
+		addr_mem_map = section_mem_map_addr(info, addr_section);
+		addr_mem_map = sparse_decode_mem_map(info, addr_mem_map, section_nr);
+		if (!is_kvaddr(addr_mem_map))
+			addr_mem_map = NOT_MEMMAP_ADDR;
+		pfn_start = section_nr * PAGES_PER_SECTION();
+		pfn_end   = pfn_start + PAGES_PER_SECTION();
+		if (info->max_mapnr < pfn_end)
+			pfn_end = info->max_mapnr;
+		dump_mem_map(info, pfn_start, pfn_end, addr_mem_map, section_nr);
+	}
+	g_free(mem_sec);
+	return TRUE;
+}
+
 int
 get_mem_map(struct DumpInfo *info)
 {
 	int ret;
 
 	switch (get_mem_type(info)) {
+	case SPARSEMEM:
+		MSG("Memory type : SPARSEMEM\n");
+		ret = get_mm_sparsemem(info);
+		break;
 	case FLATMEM:
 		MSG("Memory type : FLATMEM\n");
 		ret = get_mm_flatmem(info);
@@ -1106,6 +1315,9 @@ initial(struct DumpInfo *info)
 		if (!check_release(info))
 			return FALSE;
 	}
+	if (!get_machdep_info(info))
+		return FALSE;
+
 	if (!get_mem_map(info))
 		return FALSE;
 
