diff mbox

[1/6] Add infrastructure for conditional code and data sections

Message ID 1292955604-8809-2-git-send-email-thomas.petazzoni@free-electrons.com (mailing list archive)
State RFC, archived
Headers show

Commit Message

Thomas Petazzoni Dec. 21, 2010, 6:19 p.m. UTC
None
diff mbox

Patch

diff --git a/Makefile b/Makefile
index 6619720..57bb824 100644
--- a/Makefile
+++ b/Makefile
@@ -837,14 +837,25 @@  quiet_cmd_kallsyms = KSYM    $@
 .tmp_kallsyms%.S: .tmp_vmlinux% $(KALLSYMS)
 	$(call cmd,kallsyms)
 
+quiet_cmd_cond_sections_bis = CONDSECS $@
+      cmd_cond_sections_bis = $(NM) -n $< | \
+	grep -E "cond_(data|text)_start"  | \
+	scripts/cond-sections --s-file > $@
+
+.tmp_condsecs.o: %.o: %.S FORCE
+	$(call if_changed_dep,as_o_S)
+
+.tmp_condsecs.S: .tmp_vmlinux1 scripts/cond-sections
+	$(call cmd,cond_sections_bis)
+
 # .tmp_vmlinux1 must be complete except kallsyms, so update vmlinux version
 .tmp_vmlinux1: $(vmlinux-lds) $(vmlinux-all) FORCE
 	$(call if_changed_rule,ksym_ld)
 
-.tmp_vmlinux2: $(vmlinux-lds) $(vmlinux-all) .tmp_kallsyms1.o FORCE
+.tmp_vmlinux2: $(vmlinux-lds) $(vmlinux-all) .tmp_kallsyms1.o .tmp_condsecs.o FORCE
 	$(call if_changed,vmlinux__)
 
-.tmp_vmlinux3: $(vmlinux-lds) $(vmlinux-all) .tmp_kallsyms2.o FORCE
+.tmp_vmlinux3: $(vmlinux-lds) $(vmlinux-all) .tmp_kallsyms2.o .tmp_condsecs.o FORCE
 	$(call if_changed,vmlinux__)
 
 # Needs to visit scripts/ before $(KALLSYMS) can be used.
@@ -876,7 +887,7 @@  define rule_vmlinux-modpost
 endef
 
 # vmlinux image - including updated kernel symbols
-vmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) vmlinux.o $(kallsyms.o) FORCE
+vmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) vmlinux.o $(kallsyms.o) .tmp_condsecs.o FORCE
 ifdef CONFIG_HEADERS_CHECK
 	$(Q)$(MAKE) -f $(srctree)/Makefile headers_check
 endif
diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S
index cead889..aa0282f 100644
--- a/arch/arm/kernel/vmlinux.lds.S
+++ b/arch/arm/kernel/vmlinux.lds.S
@@ -105,6 +105,7 @@  SECTIONS
 			SCHED_TEXT
 			LOCK_TEXT
 			KPROBES_TEXT
+			CONDITIONAL_TEXT
 #ifdef CONFIG_MMU
 			*(.fixup)
 #endif
@@ -168,6 +169,8 @@  SECTIONS
 		NOSAVE_DATA
 		CACHELINE_ALIGNED_DATA(32)
 
+		CONDITIONAL_DATA
+
 		/*
 		 * The exception fixup table (might need resorting at runtime)
 		 */
diff --git a/include/linux/condsections.h b/include/linux/condsections.h
new file mode 100644
index 0000000..d657be6
--- /dev/null
+++ b/include/linux/condsections.h
@@ -0,0 +1,19 @@ 
+/*
+ * Conditional section management
+ *
+ * Copyright (C) 2010 Thomas Petazzoni <t-petazzoni@ti.com>
+ */
+
+#ifndef __CONDSECTIONS_H__
+#define __CONDSECTIONS_H__
+
+/*
+ * Use these macros to define other macros to put code or data into
+ * specific conditional sections.
+ */
+#define cond_data_section(__secname__) __section(.data.conditional.__secname__)
+#define cond_text_section(__secname__) __section(.text.conditional.__secname__)
+
+void free_unused_cond_section(const char *name);
+
+#endif /* __CONDSECTIONS_H__ */
diff --git a/kernel/Makefile b/kernel/Makefile
index 0b5ff08..58b0435 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -10,7 +10,7 @@  obj-y     = sched.o fork.o exec_domain.o panic.o printk.o \
 	    kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \
 	    hrtimer.o rwsem.o nsproxy.o srcu.o semaphore.o \
 	    notifier.o ksysfs.o pm_qos_params.o sched_clock.o cred.o \
-	    async.o range.o jump_label.o
+	    async.o range.o jump_label.o condsections.o
 obj-y += groups.o
 
 ifdef CONFIG_FUNCTION_TRACER
diff --git a/kernel/condsections.c b/kernel/condsections.c
new file mode 100644
index 0000000..b568549
--- /dev/null
+++ b/kernel/condsections.c
@@ -0,0 +1,57 @@ 
+/*
+ * Conditional section management
+ *
+ * Copyright (C) 2010 Thomas Petazzoni <t-petazzoni@ti.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/mm.h>
+
+/*
+ * This structure must be in sync with the assembly code generated by
+ * scripts/cond-sections.
+ */
+struct cond_section_desc {
+	unsigned long start;
+	unsigned long end;
+	unsigned long type;
+	const char *name;
+};
+
+/*
+ * Symbol defined by assembly code generated in
+ * scripts/cond-sections. Declared as weak because it appears only at
+ * late stage of the link process.
+ */
+extern struct cond_section_desc cond_section_descs[] __attribute__((weak));
+
+static void free_unused_cond_section_area(unsigned long pfn, unsigned long end)
+{
+        for (; pfn < end; pfn++) {
+		struct page *page = pfn_to_page(pfn);
+                ClearPageReserved(page);
+                init_page_count(page);
+                __free_page(page);
+		totalram_pages += 1;
+        }
+}
+
+/*
+ * Free the text and data conditional sections associated to the given
+ * name
+ */
+void free_unused_cond_section(const char *name)
+{
+	struct cond_section_desc *sec;
+
+	for (sec = cond_section_descs; sec->name; sec++) {
+		if (strcmp(sec->name, name))
+			continue;
+		printk(KERN_INFO "Freeing unused conditional section: %s %s 0x%lx -> 0%lx (sz=%ld)\n",
+		       sec->name, (sec->type ? "data" : "text"),
+		       sec->start, sec->end, (sec->end - sec->start));
+		memset((void*) sec->start, POISON_FREE_INITMEM, sec->end - sec->start);
+		free_unused_cond_section_area(__phys_to_pfn(__pa(sec->start)),
+					      __phys_to_pfn(__pa(sec->end)));
+	}
+}
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index 5ad25e1..3822751 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -285,10 +285,11 @@  targets += $(extra-y) $(MAKECMDGOALS) $(always)
 # Linker scripts preprocessor (.lds.S -> .lds)
 # ---------------------------------------------------------------------------
 quiet_cmd_cpp_lds_S = LDS     $@
-      cmd_cpp_lds_S = $(CPP) $(cpp_flags) -P -C -U$(ARCH) \
-	                     -D__ASSEMBLY__ -DLINKER_SCRIPT -o $@ $<
+      cmd_cpp_lds_S = cat $< | scripts/cond-sections --lds $(OBJDUMP) | \
+                      $(CPP) $(cpp_flags) -P -C -U$(ARCH) \
+	                     -D__ASSEMBLY__ -DLINKER_SCRIPT -o $@ -
 
-$(obj)/%.lds: $(src)/%.lds.S FORCE
+$(obj)/%.lds: $(src)/%.lds.S scripts/cond-sections FORCE
 	$(call if_changed_dep,cpp_lds_S)
 
 # Build the compiled-in targets
diff --git a/scripts/cond-sections b/scripts/cond-sections
new file mode 100755
index 0000000..c72e932
--- /dev/null
+++ b/scripts/cond-sections
@@ -0,0 +1,93 @@ 
+#!/bin/sh
+#
+# Conditional section link script and assembly code generation
+#
+# Copyright (C) 2010 Thomas Petazzoni <t-petazzoni@ti.com>
+#
+# This script is used:
+#
+#  *) with a --lds path-to-objdump argument, with the vmlinux.lds.S
+#     file on its standard input, in order to generate the linker
+#     script fragments corresponding to the different conditional
+#     sections included in the kernel image.
+#
+#  *) with a --s-file argument, with the result of a
+#     $(CROSS_COMPILE)nm -n as its standard input, in order to
+#     generate some assembly code that will compile into an array of
+#     structures representing each conditional section.
+
+if [ $# -lt 1 ] ; then
+    echo "Incorrect number of arguments"
+    exit 1
+fi
+
+if [ x$1 = x"--lds" ] ; then
+    OBJDUMP=$(which $2)
+    if [ ! -x $OBJDUMP ] ; then
+	echo "Invalid objdump executable"
+	exit 1
+    fi
+
+    # Get the list of conditional data sections
+    CONDITIONAL_DATA_SECTIONS=$($OBJDUMP -w -h vmlinux.o | \
+	grep "\.data\.conditional\." | cut -f3 -d' ' | tr "\n" " ")
+
+    # Get the list of conditional text sections
+    CONDITIONAL_TEXT_SECTIONS=$($OBJDUMP -w -h vmlinux.o | \
+	grep "\.text\.conditional\." | cut -f3 -d' ' | tr "\n" " ")
+
+    while read line ; do
+	if echo $line | grep -q "CONDITIONAL_TEXT" ; then
+	    for s in $CONDITIONAL_TEXT_SECTIONS ; do
+		sym=$(echo $s | sed 's/\.data\.conditional\.//')
+		echo ". = ALIGN(PAGE_SIZE);"
+		echo "VMLINUX_SYMBOL(__${sym}_cond_text_start) = .;"
+		echo "*(.text.conditional.${sym})"
+		echo ". = ALIGN(PAGE_SIZE);"
+		echo "VMLINUX_SYMBOL(__${sym}_cond_text_end) = .;"
+	    done
+	elif echo $line | grep -q "CONDITIONAL_DATA" ; then
+	    for s in $CONDITIONAL_DATA_SECTIONS ; do
+		sym=$(echo $s | sed 's/\.data\.conditional\.//')
+		echo ". = ALIGN(PAGE_SIZE);"
+		echo "VMLINUX_SYMBOL(__${sym}_cond_data_start) = .;"
+		echo "*(.data.conditional.${sym})"
+		echo ". = ALIGN(PAGE_SIZE);"
+		echo "VMLINUX_SYMBOL(__${sym}_cond_data_end) = .;"
+	    done
+	else
+	    echo "$line"
+	fi
+    done
+elif [ x$1 = x"--s-file" ] ; then
+    echo ".section .rodata, \"a\""
+    echo ".globl cond_section_descs"
+    echo ".align 8"
+    echo "cond_section_descs:"
+    seclist=""
+    while read line ; do
+	sym=$(echo $line | cut -f3 -d' ')
+	secname=$(echo $sym | sed 's/^__\(.*\)_cond_.*/\1/')
+	sectype=$(echo $sym | sed 's/^.*_cond_\([a-z]*\)_start/\1/')
+	echo ".long __${secname}_cond_${sectype}_start"
+	echo ".long __${secname}_cond_${sectype}_end"
+	if [ $sectype = "text" ] ; then
+	    echo ".long 0"
+	else
+	    echo ".long 1"
+	fi
+	echo ".long __${secname}_cond_str"
+	seclist="$seclist $secname"
+    done
+    echo ".long 0"
+    echo ".long 0"
+    echo ".long 0"
+    echo ".long 0"
+    for sec in $seclist ; do
+	echo "__${sec}_cond_str:"
+	echo ".asciz \"${sec}\""
+    done
+else
+    echo "Invalid option"
+    exit 1
+fi