All of lore.kernel.org
 help / color / mirror / Atom feed
From: Christoph Hellwig <hch@infradead.org>
To: Alexandre Ghiti <alex@ghiti.fr>
Cc: Paul Walmsley <paul.walmsley@sifive.com>,
	Palmer Dabbelt <palmer@dabbelt.com>,
	Albert Ou <aou@eecs.berkeley.edu>,
	Jisheng Zhang <jszhang@kernel.org>, Zong Li <zong.li@sifive.com>,
	Anup Patel <anup@brainfault.org>,
	linux-riscv@lists.infradead.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH v2] riscv: Map the kernel with correct permissions the first time
Date: Thu, 27 May 2021 07:35:53 +0100	[thread overview]
Message-ID: <YK89yVQirCLdodxG@infradead.org> (raw)
In-Reply-To: <20210526134110.217073-1-alex@ghiti.fr>

On Wed, May 26, 2021 at 03:41:10PM +0200, Alexandre Ghiti wrote:
>  #ifdef CONFIG_64BIT
> +#define is_kernel_mapping(x)	((x) >= kernel_virt_addr && (x) < (kernel_virt_addr + load_sz))
> +#define is_linear_mapping(x)	((x) >= PAGE_OFFSET && (x) < kernel_virt_addr)
> +

Overly long lines.  Independ of that complex macros are generally much
more readable if they are written more function-like, that is the name
and paramtes are kept on a line of their own:

#define is_kernel_mapping(x) \
	((x) >= kernel_virt_addr && (x) < (kernel_virt_addr + load_sz))

But what is the reason to not make them type-safe inline functions
anyway?

>  #define __va_to_pa_nodebug(x)	({						\
>  	unsigned long _x = x;							\
> -	(_x < kernel_virt_addr) ?						\
> +	is_linear_mapping(_x) ?							\
>  		linear_mapping_va_to_pa(_x) : kernel_mapping_va_to_pa(_x);	\
>  	})

... especially for something complex like this.

> +static inline bool is_va_kernel_lm_alias_text(uintptr_t va)
> +{
> +	return (va >= (uintptr_t)lm_alias(_start) && va < (uintptr_t)lm_alias(__init_text_begin));

Overly long line as well.  And useless braces.

> +static inline bool is_va_kernel_init_text(uintptr_t va)
> +{
> +	return (va >= (uintptr_t)__init_text_begin && va < (uintptr_t)__init_data_begin);
> +}

Same here.

> +#ifdef CONFIG_STRICT_KERNEL_RWX
> +static __init pgprot_t pgprot_from_va(uintptr_t va)
> +{
> +#ifdef CONFIG_64BIT
> +	if (is_va_kernel_text(va) || is_va_kernel_init_text(va))
> +		return PAGE_KERNEL_READ_EXEC;
> +
> +	/*
> +	 * We must mark only text as read-only as init text will get freed later
> +	 * and rodata section is marked readonly in mark_rodata_ro.
> +	 */
> +	if (is_va_kernel_lm_alias_text(va))
> +		return PAGE_KERNEL_READ;
> +
> +	return PAGE_KERNEL;
> +#else
> +	if (is_va_kernel_text(va))
> +		return PAGE_KERNEL_READ_EXEC;
> +
> +	if (is_va_kernel_init_text(va))
> +		return PAGE_KERNEL_EXEC;
> +
> +	return PAGE_KERNEL;
> +#endif /* CONFIG_64BIT */
> +}

If the entire function is different for config symbols please just
split it into two separate functions.  But to make the difference more
clear IS_ENABLED might fit better here:

static __init pgprot_t pgprot_from_va(uintptr_t va)
{
	if (is_va_kernel_text(va))
		return PAGE_KERNEL_READ_EXEC;
	if (is_va_kernel_init_text(va))
		return IS_ENABLED(CONFIG_64BIT) ?
			PAGE_KERNEL_READ_EXEC : PAGE_KERNEL_EXEC;
	if (IS_ENABLED(CONFIG_64BIT) && is_va_kernel_lm_alias_text(va))
		return PAGE_KERNEL_READ;
	return PAGE_KERNEL;
}

Preferable with comments explaining the 32-bit vs 64-bit difference.

> +void mark_rodata_ro(void)
> +{
> +	unsigned long rodata_start = (unsigned long)__start_rodata;
> +	unsigned long data_start = (unsigned long)_data;
> +	unsigned long __maybe_unused lm_rodata_start = (unsigned long)lm_alias(__start_rodata);
> +	unsigned long __maybe_unused lm_data_start = (unsigned long)lm_alias(_data);
> +
> +	set_memory_ro(rodata_start, (data_start - rodata_start) >> PAGE_SHIFT);
> +#ifdef CONFIG_64BIT
> +	set_memory_ro(lm_rodata_start, (lm_data_start - lm_rodata_start) >> PAGE_SHIFT);
> +#endif

Lots of unreadable overly lone lines.  Why not add a helper and do
something like:

static void set_kernel_memory_ro(char *startp, char *endp)
{
        unsigned long start = (unsigned long)startp;
	unsigned long end = (unsigned long)endp;

	set_memory_ro(start, (start - end) >> PAGE_SHIFT);
}

        set_kernel_memory_ro(_start_rodata, _data);
	if (IS_ENABLED(CONFIG_64BIT))
		set_kernel_memory_ro(lm_alias(__start_rodata), lm_alias(_data));


> +static __init pgprot_t pgprot_from_va(uintptr_t va)
> +{
> +#ifdef CONFIG_64BIT
> +	if (is_kernel_mapping(va))
> +		return PAGE_KERNEL_EXEC;
> +
> +	if (is_linear_mapping(va))
> +		return PAGE_KERNEL;
> +
> +	return PAGE_KERNEL;
> +#else
> +	return PAGE_KERNEL_EXEC;
> +#endif /* CONFIG_64BIT */
> +}
> +#endif /* CONFIG_STRICT_KERNEL_RWX */
> +

Same comment as for the other version.  This could become:

static __init pgprot_t pgprot_from_va(uintptr_t va)
{       
	if (IS_ENABLED(CONFIG_64BIT) && !is_kernel_mapping(va))
		return PAGE_KERNEL;
	return PAGE_KERNEL_EXEC;
}

> -static void __init create_kernel_page_table(pgd_t *pgdir, uintptr_t map_size)
> +static void __init create_kernel_page_table(pgd_t *pgdir, uintptr_t map_size, bool early)

Overly long line.

>  	for (va = kernel_virt_addr; va < end_va; va += map_size)
>  		create_pgd_mapping(pgdir, va,
>  				   load_pa + (va - kernel_virt_addr),
> -				   map_size, PAGE_KERNEL_EXEC);
> +				   map_size, early ? PAGE_KERNEL_EXEC : pgprot_from_va(va));

Same here.  But why not pass in a "pgprot_t ram_pgprot" instead of the
bool, which would be self-documenting.

WARNING: multiple messages have this Message-ID (diff)
From: Christoph Hellwig <hch@infradead.org>
To: Alexandre Ghiti <alex@ghiti.fr>
Cc: Paul Walmsley <paul.walmsley@sifive.com>,
	Palmer Dabbelt <palmer@dabbelt.com>,
	Albert Ou <aou@eecs.berkeley.edu>,
	Jisheng Zhang <jszhang@kernel.org>, Zong Li <zong.li@sifive.com>,
	Anup Patel <anup@brainfault.org>,
	linux-riscv@lists.infradead.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH v2] riscv: Map the kernel with correct permissions the first time
Date: Thu, 27 May 2021 07:35:53 +0100	[thread overview]
Message-ID: <YK89yVQirCLdodxG@infradead.org> (raw)
In-Reply-To: <20210526134110.217073-1-alex@ghiti.fr>

On Wed, May 26, 2021 at 03:41:10PM +0200, Alexandre Ghiti wrote:
>  #ifdef CONFIG_64BIT
> +#define is_kernel_mapping(x)	((x) >= kernel_virt_addr && (x) < (kernel_virt_addr + load_sz))
> +#define is_linear_mapping(x)	((x) >= PAGE_OFFSET && (x) < kernel_virt_addr)
> +

Overly long lines.  Independ of that complex macros are generally much
more readable if they are written more function-like, that is the name
and paramtes are kept on a line of their own:

#define is_kernel_mapping(x) \
	((x) >= kernel_virt_addr && (x) < (kernel_virt_addr + load_sz))

But what is the reason to not make them type-safe inline functions
anyway?

>  #define __va_to_pa_nodebug(x)	({						\
>  	unsigned long _x = x;							\
> -	(_x < kernel_virt_addr) ?						\
> +	is_linear_mapping(_x) ?							\
>  		linear_mapping_va_to_pa(_x) : kernel_mapping_va_to_pa(_x);	\
>  	})

... especially for something complex like this.

> +static inline bool is_va_kernel_lm_alias_text(uintptr_t va)
> +{
> +	return (va >= (uintptr_t)lm_alias(_start) && va < (uintptr_t)lm_alias(__init_text_begin));

Overly long line as well.  And useless braces.

> +static inline bool is_va_kernel_init_text(uintptr_t va)
> +{
> +	return (va >= (uintptr_t)__init_text_begin && va < (uintptr_t)__init_data_begin);
> +}

Same here.

> +#ifdef CONFIG_STRICT_KERNEL_RWX
> +static __init pgprot_t pgprot_from_va(uintptr_t va)
> +{
> +#ifdef CONFIG_64BIT
> +	if (is_va_kernel_text(va) || is_va_kernel_init_text(va))
> +		return PAGE_KERNEL_READ_EXEC;
> +
> +	/*
> +	 * We must mark only text as read-only as init text will get freed later
> +	 * and rodata section is marked readonly in mark_rodata_ro.
> +	 */
> +	if (is_va_kernel_lm_alias_text(va))
> +		return PAGE_KERNEL_READ;
> +
> +	return PAGE_KERNEL;
> +#else
> +	if (is_va_kernel_text(va))
> +		return PAGE_KERNEL_READ_EXEC;
> +
> +	if (is_va_kernel_init_text(va))
> +		return PAGE_KERNEL_EXEC;
> +
> +	return PAGE_KERNEL;
> +#endif /* CONFIG_64BIT */
> +}

If the entire function is different for config symbols please just
split it into two separate functions.  But to make the difference more
clear IS_ENABLED might fit better here:

static __init pgprot_t pgprot_from_va(uintptr_t va)
{
	if (is_va_kernel_text(va))
		return PAGE_KERNEL_READ_EXEC;
	if (is_va_kernel_init_text(va))
		return IS_ENABLED(CONFIG_64BIT) ?
			PAGE_KERNEL_READ_EXEC : PAGE_KERNEL_EXEC;
	if (IS_ENABLED(CONFIG_64BIT) && is_va_kernel_lm_alias_text(va))
		return PAGE_KERNEL_READ;
	return PAGE_KERNEL;
}

Preferable with comments explaining the 32-bit vs 64-bit difference.

> +void mark_rodata_ro(void)
> +{
> +	unsigned long rodata_start = (unsigned long)__start_rodata;
> +	unsigned long data_start = (unsigned long)_data;
> +	unsigned long __maybe_unused lm_rodata_start = (unsigned long)lm_alias(__start_rodata);
> +	unsigned long __maybe_unused lm_data_start = (unsigned long)lm_alias(_data);
> +
> +	set_memory_ro(rodata_start, (data_start - rodata_start) >> PAGE_SHIFT);
> +#ifdef CONFIG_64BIT
> +	set_memory_ro(lm_rodata_start, (lm_data_start - lm_rodata_start) >> PAGE_SHIFT);
> +#endif

Lots of unreadable overly lone lines.  Why not add a helper and do
something like:

static void set_kernel_memory_ro(char *startp, char *endp)
{
        unsigned long start = (unsigned long)startp;
	unsigned long end = (unsigned long)endp;

	set_memory_ro(start, (start - end) >> PAGE_SHIFT);
}

        set_kernel_memory_ro(_start_rodata, _data);
	if (IS_ENABLED(CONFIG_64BIT))
		set_kernel_memory_ro(lm_alias(__start_rodata), lm_alias(_data));


> +static __init pgprot_t pgprot_from_va(uintptr_t va)
> +{
> +#ifdef CONFIG_64BIT
> +	if (is_kernel_mapping(va))
> +		return PAGE_KERNEL_EXEC;
> +
> +	if (is_linear_mapping(va))
> +		return PAGE_KERNEL;
> +
> +	return PAGE_KERNEL;
> +#else
> +	return PAGE_KERNEL_EXEC;
> +#endif /* CONFIG_64BIT */
> +}
> +#endif /* CONFIG_STRICT_KERNEL_RWX */
> +

Same comment as for the other version.  This could become:

static __init pgprot_t pgprot_from_va(uintptr_t va)
{       
	if (IS_ENABLED(CONFIG_64BIT) && !is_kernel_mapping(va))
		return PAGE_KERNEL;
	return PAGE_KERNEL_EXEC;
}

> -static void __init create_kernel_page_table(pgd_t *pgdir, uintptr_t map_size)
> +static void __init create_kernel_page_table(pgd_t *pgdir, uintptr_t map_size, bool early)

Overly long line.

>  	for (va = kernel_virt_addr; va < end_va; va += map_size)
>  		create_pgd_mapping(pgdir, va,
>  				   load_pa + (va - kernel_virt_addr),
> -				   map_size, PAGE_KERNEL_EXEC);
> +				   map_size, early ? PAGE_KERNEL_EXEC : pgprot_from_va(va));

Same here.  But why not pass in a "pgprot_t ram_pgprot" instead of the
bool, which would be self-documenting.

_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

  reply	other threads:[~2021-05-27  6:36 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-05-26 13:41 [PATCH v2] riscv: Map the kernel with correct permissions the first time Alexandre Ghiti
2021-05-26 13:41 ` Alexandre Ghiti
2021-05-27  6:35 ` Christoph Hellwig [this message]
2021-05-27  6:35   ` Christoph Hellwig
2021-05-28  8:24   ` Alex Ghiti
2021-05-28  8:24     ` Alex Ghiti
2021-05-29 20:38     ` Palmer Dabbelt
2021-05-29 20:38       ` Palmer Dabbelt
2021-06-03  5:59   ` Alex Ghiti
2021-06-03  5:59     ` Alex Ghiti
2021-05-29  4:08 ` Drew Fustini
2021-05-29  4:08   ` Drew Fustini
2021-05-29 17:46   ` Drew Fustini
2021-05-29 17:46     ` Drew Fustini
2021-05-30  7:52     ` Alex Ghiti
2021-05-30  7:52       ` Alex Ghiti

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=YK89yVQirCLdodxG@infradead.org \
    --to=hch@infradead.org \
    --cc=alex@ghiti.fr \
    --cc=anup@brainfault.org \
    --cc=aou@eecs.berkeley.edu \
    --cc=jszhang@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-riscv@lists.infradead.org \
    --cc=palmer@dabbelt.com \
    --cc=paul.walmsley@sifive.com \
    --cc=zong.li@sifive.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.