/*
 * livepatch_main.c - kernel live patch main infrastructure
 *
 * Copyright (c) 2014 SUSE
 *  Author: Miroslav Benes <mbenes@suse.cz>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/livepatch.h>
#include <linux/module.h>
#include <linux/types.h>

#include "uname_patch/livepatch_uname.h"

/* Auto expanded KLP_PATCHES_INCLUDES: */
#include "bsc1196867/livepatch_bsc1196867.h"
#include "bsc1200605/livepatch_bsc1200605.h"
#include "bsc1200608/livepatch_bsc1200608.h"
#include "bsc1201080/livepatch_bsc1201080.h"
#include "bsc1201742/livepatch_bsc1201742.h"
#include "bsc1201941/livepatch_bsc1201941.h"
#include "bsc1202087/livepatch_bsc1202087.h"
#include "bsc1203116/livepatch_bsc1203116.h"
#include "bsc1203606/livepatch_bsc1203606.h"
#include "bsc1203613/livepatch_bsc1203613.h"
#include "bsc1204170/livepatch_bsc1204170.h"
#include "bsc1204381/livepatch_bsc1204381.h"
#include "bsc1204424/livepatch_bsc1204424.h"
#include "bsc1204576/livepatch_bsc1204576.h"
#include "bsc1205130/livepatch_bsc1205130.h"
#include "bsc1206228/livepatch_bsc1206228.h"


static struct klp_object objs[] = {
	/* Auto expanded KLP_PATCHES_OBJS: */
	{
		.name = NULL,
		.funcs = (struct klp_func[]) {
			{
			  .old_name = __stringify(KLP_SYSCALL_SYM(newuname)),
			  .new_func = KLP_SYSCALL_SYM(klp_newuname),
			},
#ifdef KLP_ARCH_HAS_SYSCALL_COMPAT_STUBS
			{
			  .old_name = __stringify(KLP_SYSCALL_COMPAT_STUB_SYM(newuname)),
			  .new_func = KLP_SYSCALL_COMPAT_STUB_SYM(klp_newuname),
			},
#endif
			{ .old_name = __stringify(ip_build_and_send_pkt), .new_func = klpp_ip_build_and_send_pkt, },
			{ .old_name = __stringify(ip_queue_xmit), .new_func = klpp_ip_queue_xmit, },
			{ .old_name = __stringify(__ip_make_skb), .new_func = klpp___ip_make_skb, },
			{ .old_name = __stringify(ip_check_mc_rcu), .new_func = klpp_ip_check_mc_rcu, },
#if IS_ENABLED(CONFIG_VT)
			{ .old_name = __stringify(vt_ioctl), .new_func = klpp_vt_ioctl, },
#endif
			{ .old_name = __stringify(fbcon_event_notify), .new_func = klpp_fbcon_event_notify, },
			{ .old_name = __stringify(fbcon_set_font), .new_func = klpp_fbcon_set_font, },
			{ .old_name = __stringify(fb_set_var), .new_func = klpp_fb_set_var, },
			{ .old_name = __stringify(do_fb_ioctl), .new_func = klpp_do_fb_ioctl, },
			{ .old_name = __stringify(unmap_region), .new_func = klpp_unmap_region, },
			{ .old_name = __stringify(anon_vma_clone), .new_func = klpp_anon_vma_clone, },
			{ .old_name = __stringify(remove_arg_zero), .new_func = klpp_remove_arg_zero, },
			{ .old_name = __stringify(copy_strings_kernel), .new_func = klpp_copy_strings_kernel, },
			{ .old_name = __stringify(do_execve), .new_func = klpp_do_execve, },
			{ .old_name = __stringify(KLP_SYSCALL_SYM(execve)), .new_func = KLP_SYSCALL_SYM(klpp_execve), },
#if defined(KLP_ARCH_HAS_SYSCALL_COMPAT_STUBS)
			{ .old_name = __stringify(KLP_SYSCALL_COMPAT_STUB_SYM(execve)), .new_func = KLP_SYSCALL_COMPAT_STUB_SYM(klpp_execve), },
#endif
			{ .old_name = __stringify(KLP_SYSCALL_SYM(execveat)), .new_func = KLP_SYSCALL_SYM(klpp_execveat), },
#if defined(KLP_ARCH_HAS_SYSCALL_COMPAT_STUBS)
			{ .old_name = __stringify(KLP_SYSCALL_COMPAT_STUB_SYM(execveat)), .new_func = KLP_SYSCALL_COMPAT_STUB_SYM(klpp_execveat), },
#endif
#if IS_ENABLED(CONFIG_COMPAT)
			{ .old_name = __stringify(KLP_COMPAT_SYSCALL_SYM(execve)), .new_func = KLP_COMPAT_SYSCALL_SYM(klpp_execve), },
			{ .old_name = __stringify(KLP_COMPAT_SYSCALL_SYM(execveat)), .new_func = KLP_COMPAT_SYSCALL_SYM(klpp_execveat), },
#endif
			{ .old_name = __stringify(__do_proc_dointvec), .new_func = klpp___do_proc_dointvec, },
			{ .old_name = __stringify(__do_proc_douintvec), .new_func = klpp___do_proc_douintvec, },
			{ .old_name = __stringify(__do_proc_doulongvec_minmax), .new_func = klpp___do_proc_doulongvec_minmax, },
			{ .old_name = __stringify(proc_do_large_bitmap), .new_func = klpp_proc_do_large_bitmap, },
			{ }
		}
	},
#if IS_ENABLED(CONFIG_ATH9K_HTC)
	{
		.name = "ath9k_htc",
		.funcs = (struct klp_func[]) {
			{ .old_name = __stringify(ath9k_htc_probe_device), .new_func = klpp_ath9k_htc_probe_device, },
			{ }
		}
	},
#endif
	{
		.name = "cls_route",
		.funcs = (struct klp_func[]) {
			{ .old_name = __stringify(route4_change), .new_func = klpp_route4_change, },
			{ }
		}
	},
#if IS_ENABLED(CONFIG_DVB_CORE)
	{
		.name = "dvb_core",
		.funcs = (struct klp_func[]) {
			{ .old_name = __stringify(dvb_demux_open), .new_func = klpp_dvb_demux_open, },
			{ .old_name = __stringify(dvb_dmxdev_release), .new_func = klpp_dvb_dmxdev_release, },
			{ }
		}
	},
#endif
	{
		.name = "nfnetlink_queue",
		.funcs = (struct klp_func[]) {
			{ .old_name = __stringify(nfqnl_recv_verdict), .new_func = klpp_nfqnl_recv_verdict, },
			{ }
		}
	},
	{
		.name = "nfp",
		.funcs = (struct klp_func[]) {
			{ .old_name = __stringify(nfp_cpp_read), .new_func = klpp_nfp_cpp_read, },
			{ .old_name = __stringify(nfp_cpp_write), .new_func = klpp_nfp_cpp_write, },
			{ }
		}
	},
	{
		.name = "nfsd",
		.funcs = (struct klp_func[]) {
			{ .old_name = __stringify(nfsd3_proc_read), .new_func = klpp_nfsd3_proc_read, },
			{ .old_name = __stringify(nfsd3_proc_readdir), .new_func = klpp_nfsd3_proc_readdir, },
			{ .old_name = __stringify(nfsd3_proc_readdirplus), .new_func = klpp_nfsd3_proc_readdirplus, },
			{ .old_name = __stringify(nfs3svc_decode_readargs), .new_func = klpp_nfs3svc_decode_readargs, },
			{ .old_name = __stringify(nfsd4_proc_compound), .new_func = klpp_nfsd4_proc_compound, },
			{ .old_name = __stringify(nfsd4_max_reply), .new_func = klpp_nfsd4_max_reply, },
			{ .old_name = __stringify(nfsd_proc_readdir), .new_func = klpp_nfsd_proc_readdir, },
			{ .old_name = __stringify(nfssvc_decode_readargs), .new_func = klpp_nfssvc_decode_readargs, },
			{ }
		}
	},
	{
		.name = "sch_sfb",
		.funcs = (struct klp_func[]) {
			{ .old_name = __stringify(sfb_enqueue), .new_func = klpp_sfb_enqueue, },
			{ }
		}
	},
	{
		.name = "sctp",
		.funcs = (struct klp_func[]) {
			{ .old_name = __stringify(sctp_endpoint_put), .new_func = klpp_sctp_endpoint_put, },
			{ }
		}
	},
	{
		.name = "sctp_diag",
		.funcs = (struct klp_func[]) {
			{ .old_name = __stringify(sctp_diag_dump), .new_func = klpp_sctp_diag_dump, },
			{ }
		}
	},
	{ }
};

static struct klp_patch patch = {
	.mod = THIS_MODULE,
	.objs = objs,
	.replace = true,
};

static int __init klp_patch_init(void)
{
	int retval;

	pr_info("livepatch: initializing\n");

	retval = klp_patch_uname_init();
	if (retval)
		return retval;

	/* Auto expanded KLP_PATCHES_INIT_CALLS: */
	retval = livepatch_bsc1196867_init();
	if (retval)
		goto err_bsc1196867;

	retval = livepatch_bsc1200605_init();
	if (retval)
		goto err_bsc1200605;

	retval = livepatch_bsc1200608_init();
	if (retval)
		goto err_bsc1200608;

	retval = livepatch_bsc1201080_init();
	if (retval)
		goto err_bsc1201080;

	retval = livepatch_bsc1201742_init();
	if (retval)
		goto err_bsc1201742;

	retval = livepatch_bsc1201941_init();
	if (retval)
		goto err_bsc1201941;

	retval = livepatch_bsc1202087_init();
	if (retval)
		goto err_bsc1202087;

	retval = livepatch_bsc1203116_init();
	if (retval)
		goto err_bsc1203116;

	retval = livepatch_bsc1203606_init();
	if (retval)
		goto err_bsc1203606;

	retval = livepatch_bsc1203613_init();
	if (retval)
		goto err_bsc1203613;

	retval = livepatch_bsc1204170_init();
	if (retval)
		goto err_bsc1204170;

	retval = livepatch_bsc1204381_init();
	if (retval)
		goto err_bsc1204381;

	retval = livepatch_bsc1204424_init();
	if (retval)
		goto err_bsc1204424;

	retval = livepatch_bsc1204576_init();
	if (retval)
		goto err_bsc1204576;

	retval = livepatch_bsc1205130_init();
	if (retval)
		goto err_bsc1205130;

	retval = livepatch_bsc1206228_init();
	if (retval)
		goto err_bsc1206228;

#ifndef KLP_NOREG_API
	retval = klp_register_patch(&patch);
	if (retval)
		goto err_patches_cleanup;
#endif
	retval = klp_enable_patch(&patch);
	if (!retval)
		return retval;

#ifndef KLP_NOREG_API
	WARN_ON(klp_unregister_patch(&patch));
#endif

err_patches_cleanup:
	/* Auto expanded KLP_PATCHES_INIT_ERR_HANDLERS: */
	livepatch_bsc1206228_cleanup();
err_bsc1206228:
	livepatch_bsc1205130_cleanup();
err_bsc1205130:
	livepatch_bsc1204576_cleanup();
err_bsc1204576:
	livepatch_bsc1204424_cleanup();
err_bsc1204424:
	livepatch_bsc1204381_cleanup();
err_bsc1204381:
	livepatch_bsc1204170_cleanup();
err_bsc1204170:
	livepatch_bsc1203613_cleanup();
err_bsc1203613:
	livepatch_bsc1203606_cleanup();
err_bsc1203606:
	livepatch_bsc1203116_cleanup();
err_bsc1203116:
	livepatch_bsc1202087_cleanup();
err_bsc1202087:
	livepatch_bsc1201941_cleanup();
err_bsc1201941:
	livepatch_bsc1201742_cleanup();
err_bsc1201742:
	livepatch_bsc1201080_cleanup();
err_bsc1201080:
	livepatch_bsc1200608_cleanup();
err_bsc1200608:
	livepatch_bsc1200605_cleanup();
err_bsc1200605:
	livepatch_bsc1196867_cleanup();
err_bsc1196867:

	return retval;
}

static void __exit klp_patch_cleanup(void)
{
	pr_info("livepatch: removed\n");

	/* Auto expanded KLP_PATCHES_CLEANUP_CALLS: */
	livepatch_bsc1196867_cleanup();
	livepatch_bsc1200605_cleanup();
	livepatch_bsc1200608_cleanup();
	livepatch_bsc1201080_cleanup();
	livepatch_bsc1201742_cleanup();
	livepatch_bsc1201941_cleanup();
	livepatch_bsc1202087_cleanup();
	livepatch_bsc1203116_cleanup();
	livepatch_bsc1203606_cleanup();
	livepatch_bsc1203613_cleanup();
	livepatch_bsc1204170_cleanup();
	livepatch_bsc1204381_cleanup();
	livepatch_bsc1204424_cleanup();
	livepatch_bsc1204576_cleanup();
	livepatch_bsc1205130_cleanup();
	livepatch_bsc1206228_cleanup();


#ifndef KLP_NOREG_API
	WARN_ON(klp_unregister_patch(&patch));
#endif
}

module_init(klp_patch_init);
module_exit(klp_patch_cleanup);

MODULE_LICENSE("GPL");
MODULE_INFO(livepatch, "Y");
