[coreboot] Native init for AMD cards

David Hubbard david.c.hubbard+coreboot at gmail.com
Fri Jun 21 04:15:43 CEST 2013


Hi Ron, Rudolf,

I've attached a shell script that does the first part of preparing the
in-kernel radeon driver for coreboot.

I settled on a shell script after thinking about what coccinelle is best
used for. It does look like coccinelle could do what ctags does, namely,
find function definitions. However ctags has the advantage of automatically
filtering out function calls that refer to any other part of the kernel
(ctags only lists where the function is *defined* in the 'tags' file it
outputs).

The output of this script is a coccinelle .spatch that I don't actually
expect to work yet. I'll become more confident with coccinelle as I use it
more. I wanted to send this as a question --

What do you think is the best way to slice up the in-kernel radeon driver?
This script relies on very few special cases which should hopefully make
the code reuse as painless as possible. The special cases:

1. Rename the #define radeon_init() in drivers/gpu/drm/radeon/radeon.h
2. Find struct radeon_asic initializers in
drivers/gpu/drm/radeon/radeon_asic.c

After this auto-generated coccinelle .spatch, the source tree needs manual
spatches applied to change kernel API calls to coreboot API calls. After
looking for any pitfalls, that would be the next thing to do.

Regards,
David
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.coreboot.org/pipermail/coreboot/attachments/20130620/cbcc2d17/attachment.html>
-------------- next part --------------
#!/bin/bash

KERNEL_VER=3.10-rc6
D=drivers/gpu/drm/radeon

#
# optional: comment this out to use curl instead of git clone
#
#KERNEL_FROM_GIT=1

if [ ! -d linux ]; then
	if [ ${KERNEL_FROM_GIT:-0} -ne 0 ]; then
		echo "using git to get kernel sources"
		git clone http://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git || exit 1
		( cd linux && git checkout v${KERNEL_VER} ) || exit 1
	else
		echo "using tar.xz kernel sources"
		if ! which xz >/dev/null 2>&1; then
			echo "cannot find xz:"
			which xz
			exit 1
		fi
		[ -f linux-${KERNEL_VER}.tar.xz ] || curl -O https://www.kernel.org/pub/linux/kernel/v3.x/testing/linux-${KERNEL_VER}.tar.xz || exit 1
		xz -dc linux-${KERNEL_VER}.tar.xz | tar xf - || exit 1
		mv linux-${KERNEL_VER} linux || exit 1
	fi
	rm -f linux/tags
fi
if [ ! -f avert-func-name-collision.patch ]; then
	#
	# because linux/drivers/gpu/drm/radeon/radeon_drv.c defines static int __init radeon_init(void) also
	# change the name of the #define
	#
	cat <<EOF >avert-func-name-collision.patch
--- linux/drivers/gpu/drm/radeon/radeon.h	2013-06-20 16:22:44.000000000 -0600
+++ linux/drivers/gpu/drm/radeon/radeon.h	2013-06-20 16:22:55.000000000 -0600
@@ -1873,8 +1873,8 @@
 /*
  * ASICs macro.
  */
-#define radeon_init(rdev) (rdev)->asic->init((rdev))
-#define radeon_fini(rdev) (rdev)->asic->fini((rdev))
+#define radeon_asic_p_init(rdev) (rdev)->asic->init((rdev))
+#define radeon_asic_p_fini(rdev) (rdev)->asic->fini((rdev))
 #define radeon_resume(rdev) (rdev)->asic->resume((rdev))
 #define radeon_suspend(rdev) (rdev)->asic->suspend((rdev))
 #define radeon_cs_parse(rdev, r, p) (rdev)->asic->ring[(r)].cs_parse((p))
--- linux/drivers/gpu/drm/radeon/radeon_device.c	2013-06-20 16:24:59.000000000 -0600
+++ linux/drivers/gpu/drm/radeon/radeon_device.c	2013-06-20 16:23:27.000000000 -0600
@@ -1176,7 +1176,7 @@
 	vga_client_register(rdev->pdev, rdev, NULL, radeon_vga_set_decode);
 	vga_switcheroo_register_client(rdev->pdev, &radeon_switcheroo_ops);
 
-	r = radeon_init(rdev);
+	r = radeon_asic_p_init(rdev);
 	if (r)
 		return r;
 
@@ -1194,9 +1194,9 @@
 		 * with fallback to PCI or PCIE GART
 		 */
 		radeon_asic_reset(rdev);
-		radeon_fini(rdev);
+		radeon_asic_p_fini(rdev);
 		radeon_agp_disable(rdev);
-		r = radeon_init(rdev);
+		r = radeon_asic_p_init(rdev);
 		if (r)
 			return r;
 	}
@@ -1228,7 +1228,7 @@
 	rdev->shutdown = true;
 	/* evict vram memory */
 	radeon_bo_evict_vram(rdev);
-	radeon_fini(rdev);
+	radeon_asic_p_fini(rdev);
 	vga_switcheroo_unregister_client(rdev->pdev);
 	vga_client_register(rdev->pdev, NULL, NULL, NULL);
 	if (rdev->rio_mem)
EOF
	patch -p0 -i avert-func-name-collision.patch
	rm -f linux/tags
fi
if [ ! -f linux/tags ]; then
	echo "ctags -R linux"
	( cd linux && ctags -R ) || exit 1
	rm -f linux/"$D"/tags
fi
if [ ! -f linux/"$D"/tags ]; then
	echo "filter only $D/tags"
	AWK="`echo \"$D\" | awk '{gsub("/","\\\\/");print}'`"
	( cd linux && awk "BEGIN{FS=\"\\t\"}
		{
			if (\$2 ~ /^${AWK}/) print
		}" tags > $D/tags ) || exit 1
fi

whitelist_ops() {
	#
	# expand_whitelist will not find ops structures
	# in reality, there is just one that must be manually selected: 'struct radeon_asic'
	# and only one member needed for coreboot: .init
	# this also pulls in .hpd.init (HPD = hot plug detect) because it is not smart enough not to
	#
	# static struct radeon_asic trinity_asic = {
	#     .init = &cayman_init,
	#     .fini = &cayman_fini,
	#     ...
	#
	(
		echo "awk 'BEGIN{FS=\"\\t\";"
		awk '{
			if (x) {
				if (match($0, "^[[:space:]]*\\.init[[:space:]]*=[[:space:]]&"))
					print "a[\"" gensub(",$", "", "", substr($0, RSTART+RLENGTH)) "\"]=1;";
				c+=gsub("{","");
				c-=gsub("}","");
				if (!c) x=0
			};
			if ($0 ~ "^static struct radeon_asic.*=.*{") {x=1;c=gsub("{","");}
		}' "$D"/radeon_asic.c
		echo "}{n=3;while (!match(\$n, \";\\\"\$\") && n < NF) n++;n++;if (\$n==\"f\" && \$1 in a) print}' \"$D\"/tags"
	) > "$D"/whitelist.sh || exit 1
	( cat "$D"/whitelist && . "$D"/whitelist.sh ) | sort -u > "$D"/whitelist.out
	rm -f "$D"/whitelist.sh
	mv "$D"/whitelist.out "$D"/whitelist
}

expand_whitelist() {
	#
	# 1. output the function bodies of all functions in the whitelist
	# 2. look up every C identifier in that text
	# 3. any C identifier found in tags (ctags output file) with $4=="f" is a function
	# 4. replace the whitelist with the new list
	#

	# step 1: function bodies
	awk 'BEGIN{FS="\t";print "#!/bin/bash"}{
			sub("/\\^","\"",$3);
			sub("\\$/;\"$","\"",$3);
			print "awk '"'"'{if (x) {n=$0;c+=gsub(\"{\",\"\");c-=gsub(\"}\",\"\");print n;if (!c) x=0};"
			print "    if ($0 == " $3 ") {x=1;c=0}}'"'"' " $2
		}' "$D"/whitelist > "$D"/whitelist.sh
	. "$D"/whitelist.sh > "$D"/whitelist.out || exit 1

	# step 2: C identifiers
	awk '{
			s=$0;
			while (1) {
				gsub("\"[^\"]*\"","",s);
				if (!match(s, "[_A-Za-z][0-9_A-Za-z]*")) break;
				print "a[\"" substr(s,RSTART,RLENGTH) "\"]=1;";
				s=substr(s,RSTART+RLENGTH);
			}
		}' "$D"/whitelist.out | sort -u > "$D"/whitelist.awk || exit 1

	# step 3 and 4: functions
	(
		echo "awk 'BEGIN{FS=\"\\t\";"
		cat "$D"/whitelist.awk
		echo "}{n=3;while (!match(\$n, \";\\\"\$\") && n < NF) n++;n++;if (\$n==\"f\" && \$1 in a) print}' \"$D\"/tags"
	) > "$D"/whitelist.sh
	rm -f "$D"/whitelist.awk

	. "$D"/whitelist.sh > "$D"/whitelist.out || exit 1
	cat "$D"/whitelist "$D"/whitelist.out | sort -u > "$D"/whitelist.sh || exit 1
	mv "$D"/whitelist.sh "$D"/whitelist || exit 1
	rm -f "$D"/whitelist.out
}

if [ ! -f linux/"$D"/whitelist ]; then
	echo "expand whitelist"
	(
	cd linux || exit 1
	grep '^\(radeon_driver_load_kms\|radeon_driver_open_kms\)' "$D"/tags > "$D"/whitelist || exit 1
	whitelist_ops

	c=0
	while true; do
		N="`awk 'END{print NR}' \"$D\"/whitelist`"
		echo -e "\e[A\e[Kexpand whitelist: $N"
		expand_whitelist
		M="`awk 'END{print NR}' \"$D\"/whitelist`"
		[ $M -eq $N ] && break
	done
	) || exit 1
	rm -f unused_funcs.spatch
fi

if [ ! -f unused_funcs.spatch ]; then
	echo "> unused_funcs.spatch"
	awk 'BEGIN{
			print "@whitelist@";
			print "@@";
			s = "(";
		}
		{
			print s;
			print $1 "(...) {...}";
			s = "|";
		}
		END{
			print ")";
			print "";
			print "@remove_other_funcs depends on !whitelist@";
			print "identifier funcname";
			print "@@";
			print "<...";
			print "-funcname(...) {...}";
			print "...>";
		}' linux/"$D"/whitelist > unused_funcs.spatch
fi


More information about the coreboot mailing list