All of lore.kernel.org
 help / color / mirror / Atom feed
From: Oleksii <oleksii.kurochko@gmail.com>
To: Julien Grall <julien@xen.org>, xen-devel@lists.xenproject.org
Cc: Andrew Cooper <andrew.cooper3@citrix.com>,
	Stefano Stabellini <sstabellini@kernel.org>,
	Gianluca Guida <gianluca@rivosinc.com>,
	Bob Eshleman <bobbyeshleman@gmail.com>,
	Alistair Francis <alistair.francis@wdc.com>,
	 Connor Davis <connojdavis@gmail.com>
Subject: Re: [PATCH v1 1/3] xen/riscv: introduce setup_initial_pages
Date: Sun, 05 Mar 2023 18:25:43 +0200	[thread overview]
Message-ID: <e0722b2323c78478f8fd07b515bb607ef168ef1e.camel@gmail.com> (raw)
In-Reply-To: <98d45f93-b611-1016-45b5-3d7487af261f@xen.org>

Hi Julien,

On Mon, 2023-02-27 at 17:36 +0000, Julien Grall wrote:
> Hi Oleksii,
> 
> On 27/02/2023 16:52, Oleksii wrote:
> > On Sat, 2023-02-25 at 17:53 +0000, Julien Grall wrote:
> > > > +/*
> > > > + * WARNING: load_addr() and linker_addr() are to be called
> > > > only
> > > > when the MMU is
> > > > + * disabled and only when executed by the primary CPU.  They
> > > > cannot refer to
> > > > + * any global variable or functions.
> > > 
> > > I find interesting you are saying when
> > > _setup_initial_pagetables() is
> > > called from setup_initial_pagetables(). Would you be able to
> > > explain
> > > how
> > > this is different?
> > I am not sure that I understand your question correctly but
> > _setup_initial_pagetables() was introduced to map some addresses
> > with
> > write/read flag. Probably I have to rename it to something that is
> > more
> > clear.
> 
> So the comment suggests that you code cannot refer to global 
> functions/variables when the MMU is off. So I have multiple
> questions:
>    * Why only global? IOW, why static would be OK?
>    * setup_initial_pagetables() has a call to 
> _setup_initial_pagetables() (IOW referring to another function). Why
> is 
> it fine?
>    * You have code in the next patch referring to global variables 
> (mainly _start and _end). How is this different?
> 
> > > 
> > > > + */
> > > > +
> > > > +/*
> > > > + * Convert an addressed layed out at link time to the address
> > > > where it was loaded
> > > 
> > > Typo: s/addressed/address/ ?
> > Yes, it should be address. and 'layed out' should be changed to
> > 'laid
> > out'...
> > > 
> > > > + * by the bootloader.
> > > > + */
> > > 
> > > Looking at the implementation, you seem to consider that any
> > > address
> > > not
> > > in the range [linker_addr_start, linker_addr_end[ will have a 1:1
> > > mappings.
> > > 
> > > I am not sure this is what you want. So I would consider to throw
> > > an
> > > error if such address is passed.
> > I thought that at this stage and if no relocation was done it is
> > 1:1
> > except the case when load_addr_start != linker_addr_start.
> 
> The problem is what you try to map one to one may clash with the
> linked 
> region for Xen. So it is not always possible to map the region 1:1.
> 
> Therefore, I don't see any use for the else part here.
Got it. Thanks.

I am curious than what is the correct approach in general to handle
this situation?
I mean that throw an error it is one option but if I would like to do
that w/o throwing an error. Should it done some relocation in that
case?

> 
> > 
> > 
> > > 
> > > > +#define
> > > > load_addr(linker_address)
> > > >      \
> > > > +
> > > > ({
> > > >          \
> > > > +        unsigned long __linker_address = (unsigned
> > > > long)(linker_address);      \
> > > > +        if ( linker_addr_start <= __linker_address
> > > > &&                          \
> > > > +            __linker_address < linker_addr_end
> > > > )                               \
> > > > +
> > > > {
> > > >      \
> > > > +            __linker_address
> > > > =                                                 \
> > > > +                __linker_address - linker_addr_start +
> > > > load_addr_start;        \
> > > > +
> > > > }
> > > >      \
> > > > +
> > > > __linker_address;
> > > >      \
> > > > +    })
> > > > +
> > > > +/* Convert boot-time Xen address from where it was loaded by
> > > > the
> > > > boot loader to the address it was layed out
> > > > + * at link-time.
> > > > + */
> > > 
> > > Coding style: The first line is too long and multi-line comments
> > > look
> > > like:
> > > 
> > > /*
> > >    * Foo
> > >    * Bar
> > >    */
> > > 
> > > > +#define
> > > > linker_addr(load_address)
> > > >      \
> > > 
> > > Same remark as for load_addr() above.
> > > 
> > > > +
> > > > ({
> > > >          \
> > > > +        unsigned long __load_address = (unsigned
> > > > long)(load_address);          \
> > > > +        if ( load_addr_start <= __load_address
> > > > &&                              \
> > > > +            __load_address < load_addr_end
> > > > )                                   \
> > > > +
> > > > {
> > > >      \
> > > > +            __load_address
> > > > =                                                   \
> > > > +                __load_address - load_addr_start +
> > > > linker_addr_start;          \
> > > > +
> > > > }
> > > >      \
> > > > +
> > > > __load_address;
> > > >      \
> > > > +    })
> > > > +
> > > > +static void __attribute__((section(".entry")))
> > > > +_setup_initial_pagetables(pte_t *second, pte_t *first, pte_t
> > > > *zeroeth,
> > > Can this be named to setup_initial_mapping() so this is clearer
> > > and
> > > avoid the one '_' different with the function below.
> > Sure. It will be better.
> > > 
> > > > +                         unsigned long map_start,
> > > > +                         unsigned long map_end,
> > > > +                         unsigned long pa_start,
> > > > +                         bool writable)
> > > 
> > > What about the executable bit?
> > It's always executable... But as you mentioned above
> > PTE_LEAF_DEFAULT
> > should be either RX or RW.
> > I think it makes sense to add flags instead of writable.
> > > 
> > > > +{
> > > > +    unsigned long page_addr;
> > > > +    unsigned long index2;
> > > > +    unsigned long index1;
> > > > +    unsigned long index0;
> > > 
> > > index* could be defined in the loop below.
> > It could. But I am curious why it is better?
> > > 
> > > > +
> > > > +    /* align start addresses */
> > > > +    map_start &= ZEROETH_MAP_MASK;
> > > > +    pa_start &= ZEROETH_MAP_MASK;
> > > 
> > > Hmmm... I would actually expect the address to be properly
> > > aligned
> > > and
> > > therefore not require an alignment here.
> > > 
> > > Otherwise, this raise the question of what happen if you have
> > > region
> > > using the same page?
> > That map_start &=  ZEROETH_MAP_MASK is needed to page number of
> > address
> > w/o page offset.
> 
> My point is why would the page offset be non-zero?
I checked a linker script and addresses that passed to
setup_initial_mapping() and they are really always aligned so there is
no any sense in additional alignment.

> 
> > > 
> > > > +
> > > > +    page_addr = map_start;
> > > > +    while ( page_addr < map_end )
> > > 
> > > Looking at the loop, it looks like you are assuming that the
> > > region
> > > will
> > > never cross a boundary of a page-table (either L0, L1, L2). I am
> > > not
> > > convinced you can make such assumption (see below).
> > > 
> > > But if you really want to make such assumption then you should
> > > add
> > > some
> > > guard (either BUILD_BUG_ON(), ASSERT(), proper check) in your
> > > code to
> > > avoid any surprise in the future.
> > I am not sure that I fully understand what is the problem here.
> > The address is aligned on (1<<12) boundary and each itearation is
> > mapped (1<<12) page so all looks fine or I misunderstood you.
> 
> Let's take an example, imagine the region you want to map is 4MB. 
> AFAICT, you are only passing one L0 page-table. So your code will end
> up 
> to overwrite the previous entries in the zeroeth page-table and then
> add 
> another link in the L1 page-table.
Got it. Then it looks that current approach isn't correct totally...

> 
> > > 
> > > > +    {
> > > > +        index2 = pagetable_second_index(page_addr);
> > > > +        index1 = pagetable_first_index(page_addr);
> > > > +        index0 = pagetable_zeroeth_index(page_addr);
> > > > +
> > > > +        /* Setup level2 table */
> > > > +        second[index2] = paddr_to_pte((unsigned long)first);
> > > > +        second[index2].pte |= PTE_TABLE;
> > > > +
> > > > +        /* Setup level1 table */
> > > > +        first[index1] = paddr_to_pte((unsigned long)zeroeth);
> > > > +        first[index1].pte |= PTE_TABLE;
> > > > +
> > > > +        /* Setup level0 table */
> > > > +        if ( !pte_is_valid(&zeroeth[index0]) )
> > > 
> > > Can you explain why you are checking !pte_is_valid() for the L0
> > > entry
> > > but not the other?
> > My mistake it should be checked for each level.
> 
> In which case, shouldn't you return an error if the entry is always
> valid?
> 
> > > 
> > > > +        {
> > > > +            /* Update level0 table */
> > > > +            zeroeth[index0] = paddr_to_pte((page_addr -
> > > > map_start)
> > > > + pa_start);
> > > > +            zeroeth[index0].pte |= PTE_LEAF_DEFAULT;
> > > > +            zeroeth[index0].pte &= ~((!writable) ?
> > > > PTE_WRITABLE :
> > > > 0);
> > > 
> > > Looking at the default value, it would mean that a non-writable
> > > mapping
> > > is automatically executable. This seems wrong for the section is
> > > not
> > > meant to be executable (like rodata).
> > Yes, you are right. I'll reowrk setup_initial_mapping() to pass
> > flags
> > instead of write/read - only flag.
> > > 
> > > > +        }
> > > > +
> > > > +        /* Point to next page */
> > > > +        page_addr += ZEROETH_SIZE;
> > > > +    }
> > > > +}
> > > > +
> > > > +/*
> > > > + * setup_initial_pagetables:
> > > > + *
> > > > + * 1) Build the page tables for Xen that map the following:
> > > > + *   1.1)  The physical location of Xen (where the bootloader
> > > > loaded it)
> > > > + *   1.2)  The link-time location of Xen (where the linker
> > > > expected Xen's
> > > > + *         addresses to be)
> > > > + * 2) Load the page table into the SATP and enable the MMU
> > > > + */
> > > > +void __attribute__((section(".entry")))
> > > 
> > > I couldn't find a section ".entry" in the linker.
> > > 
> > > > +setup_initial_pagetables(unsigned long load_addr_start,
> > > > +                         unsigned long load_addr_end,
> > > > +                         unsigned long linker_addr_start,
> > > > +                         unsigned long linker_addr_end)
> > > > +{
> > > > +    pte_t *second;
> > > > +    pte_t *first;
> > > > +    pte_t *zeroeth;
> > > > +
> > > > +    clear_pagetables(load_addr_start, load_addr_end,
> > > > +                     linker_addr_start, linker_addr_end);
> > > > +
> > > > +    /* Get the addresses where the page tables were loaded */
> > > > +    second  = (pte_t *)load_addr(&xen_second_pagetable);
> > > > +    first   = (pte_t *)load_addr(&xen_first_pagetable);
> > > > +    zeroeth = (pte_t *)load_addr(&xen_zeroeth_pagetable);
> > > 
> > > I would consider to embed the type cast in load_addr() so you are
> > > adding
> > > some type safety within your code.
> > Definitely it will be better but setup_initial_mapping() uses
> > 'unsigned
> > long' for passing address that should be mapped.
> 
> One possibility would be to introduce a new wrapper for the
> typesafety. 
> Anyway, it is not essential for now. Let's at least get the logic 
> correct first :).
Agree. the logic should be fixed first.
> 
> > > 
> > > > +
> > > > +    /*
> > > > +     * Create a mapping from Xen's link-time addresses to
> > > > where
> > > > they were actually loaded.
> > > 
> > > This is line is way long than 80 characters. Please make sure to
> > > wrap
> > > it
> > > 80 characters.
> > > 
> > > > +     */
> > > > +    _setup_initial_pagetables(second, first, zeroeth,
> > > > +                              linker_addr(&_stext),
> > > > +                              linker_addr(&_etext),
> > > > +                              load_addr(&_stext),
> > > > +                              false);
> > > > +    _setup_initial_pagetables(second, first, zeroeth,
> > > > +                              linker_addr(&__init_begin),
> > > > +                              linker_addr(&__init_end),
> > > > +                              load_addr(&__init_begin),
> > > > +                              true);
> > > > +    _setup_initial_pagetables(second, first, zeroeth,
> > > > +                              linker_addr(&_srodata),
> > > > +                              linker_addr(&_erodata),
> > > > +                              load_addr(&_srodata),
> > > > +                              false);
> > > > +    _setup_initial_pagetables(second, first, zeroeth,
> > > > +                              linker_addr_start,
> > > > +                              linker_addr_end,
> > > > +                              load_addr_start,
> > > > +                              true);
> > > 
> > > Where do you guarantee that Xen will always fit in an L0 table
> > > and
> > > the
> > > start address is aligned to the size of an L0 table?
> > I don't guarantee that it fit in an L0 table but the start address
> > is
> > aligned to the size of the L0 table at the start.
> Then it should be fixed.
> 
> > > 
> > > > +
> > > > +    /*
> > > > +     * Create a mapping of the load time address range to...
> > > > the
> > > > load time address range.
> > > 
> > > Same about the line length here.
> > > 
> > > > +     * This mapping is used at boot time only.
> > > > +     */
> > > > +    _setup_initial_pagetables(second, first, zeroeth,
> > > 
> > > This can only work if Xen is loaded at its linked address. So you
> > > need a
> > > separate set of L0, L1 tables for the identity mapping.
> > > 
> > > That said, this would not be sufficient because:
> > >     1) Xen may not be loaded at a 2M boundary (you can control
> > > with
> > > U-boot, but not with EFI). So this may cross a boundary and
> > > therefore
> > > need multiple pages.
> > >     2) The load region may overlap the link address
> > > 
> > > While I think it would be good to handle those cases from the
> > > start,
> > > I
> > > would understand why are not easy to solve. So I think the
> > > minimum is
> > > to
> > > throw some errors if you are in a case you can't support.
> > Do you mean to throw some error in load_addr()/linkder_addr()?
> 
> In this case, I meant to check if load_addr != linker_addr, then
> throw 
> an error.
I am not sure that it is needed now and it is easier to throw an error
but is option exist to handler situation when load_addr != linker_addr
except throwing an error? relocate?


~ Oleksii

  reply	other threads:[~2023-03-05 16:26 UTC|newest]

Thread overview: 29+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-02-24 15:06 [PATCH v1 0/3] enable MMU for RISC-V Oleksii Kurochko
2023-02-24 15:06 ` [PATCH v1 1/3] xen/riscv: introduce setup_initial_pages Oleksii Kurochko
2023-02-24 15:23   ` Andrew Cooper
2023-02-25 17:53   ` Julien Grall
2023-02-27 16:52     ` Oleksii
2023-02-27 17:36       ` Julien Grall
2023-03-05 16:25         ` Oleksii [this message]
2023-03-05 21:28           ` Oleksii
2023-03-21 16:25           ` Julien Grall
2023-03-22  9:14             ` Oleksii
2023-02-27 15:12   ` Jan Beulich
2023-02-27 15:19     ` Jan Beulich
2023-03-06  6:39       ` Oleksii
2023-03-06  6:38     ` Oleksii
2023-02-24 15:06 ` [PATCH v1 2/3] xen/riscv: setup initial pagetables Oleksii Kurochko
2023-02-25 18:05   ` Julien Grall
2023-02-27 15:17     ` Jan Beulich
2023-02-27 15:36       ` Julien Grall
2023-02-27 17:17     ` Oleksii
2023-02-27 17:45       ` Julien Grall
2023-03-08 14:54         ` Oleksii
2023-03-08 15:17           ` Jan Beulich
2023-03-08 16:16             ` Oleksii
2023-03-09  9:46               ` Jan Beulich
2023-03-09 14:39                 ` Oleksii
2023-02-24 15:06 ` [PATCH v1 3/3] automation: update RISC-V smoke test Oleksii Kurochko
2023-02-24 15:27   ` Andrew Cooper
2023-02-24 16:45     ` Oleksii
2023-02-24 15:19 ` [PATCH v1 0/3] enable MMU for RISC-V Oleksii

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=e0722b2323c78478f8fd07b515bb607ef168ef1e.camel@gmail.com \
    --to=oleksii.kurochko@gmail.com \
    --cc=alistair.francis@wdc.com \
    --cc=andrew.cooper3@citrix.com \
    --cc=bobbyeshleman@gmail.com \
    --cc=connojdavis@gmail.com \
    --cc=gianluca@rivosinc.com \
    --cc=julien@xen.org \
    --cc=sstabellini@kernel.org \
    --cc=xen-devel@lists.xenproject.org \
    /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.