All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jan Kiszka <jan.kiszka@siemens.com>
To: Dmitrii Bundin <dmitrii.bundin.a@gmail.com>, rppt@kernel.org
Cc: akpm@linux-foundation.org, gregkh@linuxfoundation.org,
	kbingham@kernel.org, linux-kernel@vger.kernel.org,
	linux-mm@kvack.org, mingo@redhat.com, vbabka@suse.cz,
	x86@kernel.org
Subject: Re: [PATCH v3] scripts/gdb: add mm introspection utils
Date: Mon, 2 Jan 2023 09:59:35 +0100	[thread overview]
Message-ID: <16280f8e-31c9-7b0b-b0c1-4d180c4d0045@siemens.com> (raw)
In-Reply-To: <20230101172312.21452-1-dmitrii.bundin.a@gmail.com>

On 01.01.23 18:23, Dmitrii Bundin wrote:
> This command provides a way to traverse the entire page hierarchy by a
> given virtual address on x86. In addition to qemu's commands info
> tlb/info mem it provides the complete information about the paging
> structure for an arbitrary virtual address. It supports 4KB/2MB/1GB and
> 5 level paging.
> 
> Here is an example output for 2MB success translation:
> 
> (gdb) translate-vm address
> cr3:
>     cr3 binary data                0x10a1f8004
>     next entry physicall address   0x10a1f8000
>     ---
>     bit  3          page level write through       False
>     bit  4          page level cache disabled      False
> level 4:
>     entry address                  0xffff88810a1f87f0
>     page entry binary data         0x8000000109042067
>     next entry physicall address   0x109042000
>     ---
>     bit  0          entry present                  True
>     bit  1          read/write access allowed      True
>     bit  2          user access allowed            True
>     bit  3          page level write through       False
>     bit  4          page level cache disabled      False
>     bit  5          entry has been accessed        True
>     bit  7          page size                      False
>     bit  11         restart to ordinary            False
>     bit  63         execute disable                True
> level 3:
>     entry address                  0xffff888109042e40
>     page entry binary data         0x10ec93067
>     next entry physicall address   0x10ec93000
>     ---
>     bit  0          entry present                  True
>     bit  1          read/write access allowed      True
>     bit  2          user access allowed            True
>     bit  3          page level write through       False
>     bit  4          page level cache disabled      False
>     bit  5          entry has been accessed        True
>     bit  7          page size                      False
>     bit  11         restart to ordinary            False
>     bit  63         execute disable                False
> level 2:
>     entry address                  0xffff88810ec939a8
>     page entry binary data         0x800000012b6008e7
>     page size                      2MB
>     page physicall address         0x12b600000
>     ---
>     bit  0          entry present                  True
>     bit  1          read/write access allowed      True
>     bit  2          user access allowed            True
>     bit  3          page level write through       False
>     bit  4          page level cache disabled      False
>     bit  5          entry has been accessed        True
>     bit  6          page dirty                     True
>     bit  7          page size                      True
>     bit  8          global translation             False
>     bit  11         restart to ordinary            True
>     bit  12         pat                            False
>     bits (59, 62)   protection key                 0
>     bit  63         execute disable                True
> 
> Signed-off-by: Dmitrii Bundin <dmitrii.bundin.a@gmail.com>
> ---
> 
> Changes in v2: https://lore.kernel.org/all/20221230163512.23736-1-dmitrii.bundin.a@gmail.com/
>     - Fix commit message to mention x86 explicitly
>     - Assign page_offset_base to a constant in case
>       CONFIG_DYNAMIC_MEMORY_LAYOUT is disabled
> 
> Changes in v3: https://lore.kernel.org/all/20221231171258.7907-1-dmitrii.bundin.a@gmail.com/
>     - Make debug output lower case and column aligned
> 
>  scripts/gdb/linux/mm.py    | 223 +++++++++++++++++++++++++++++++++++++
>  scripts/gdb/vmlinux-gdb.py |   1 +
>  2 files changed, 224 insertions(+)
>  create mode 100644 scripts/gdb/linux/mm.py
> 
> diff --git a/scripts/gdb/linux/mm.py b/scripts/gdb/linux/mm.py
> new file mode 100644
> index 000000000000..7bfe39f32b7c
> --- /dev/null
> +++ b/scripts/gdb/linux/mm.py
> @@ -0,0 +1,223 @@
> +#
> +# gdb helper commands and functions for Linux kernel debugging
> +#
> +#  routines to introspect page table
> +#
> +# Authors:
> +#  Dmitrii Bundin <dmitrii.bundin.a@gmail.com>
> +#
> +# This work is licensed under the terms of the GNU GPL version 2.
> +#
> +
> +import gdb
> +
> +from linux import utils
> +
> +PHYSICAL_ADDRESS_MASK = gdb.parse_and_eval('0xfffffffffffff')
> +
> +
> +def page_mask(level=1):
> +    # 4KB
> +    if level == 1:
> +        return gdb.parse_and_eval('(u64) ~0xfff')
> +    # 2MB
> +    elif level == 2:
> +        return gdb.parse_and_eval('(u64) ~0x1fffff')
> +    # 1GB
> +    elif level == 3:
> +        return gdb.parse_and_eval('(u64) ~0x3fffffff')
> +    else:
> +        raise Exception(f'Unknown page level: {level}')
> +
> +
> +#page_offset_base in case CONFIG_DYNAMIC_MEMORY_LAYOUT is disabled
> +POB_NO_DYNAMIC_MEM_LAYOUT = '0xffff888000000000'
> +def _page_offset_base():
> +    pob_symbol = gdb.lookup_global_symbol('page_offset_base')
> +    pob = pob_symbol.name if pob_symbol else POB_NO_DYNAMIC_MEM_LAYOUT
> +    return gdb.parse_and_eval(pob)
> +
> +
> +def is_bit_defined_tupled(data, offset):
> +    return offset, bool(data >> offset & 1)
> +
> +def content_tupled(data, bit_start, bit_end):
> +    return (bit_start, bit_end), data >> bit_start & ((1 << (1 + bit_end - bit_start)) - 1)
> +
> +def entry_va(level, phys_addr, translating_va):
> +        def start_bit(level):
> +            if level == 5:
> +                return 48
> +            elif level == 4:
> +                return 39
> +            elif level == 3:
> +                return 30
> +            elif level == 2:
> +                return 21
> +            elif level == 1:
> +                return 12
> +            else:
> +                raise Exception(f'Unknown level {level}')
> +
> +        entry_offset =  ((translating_va >> start_bit(level)) & 511) * 8
> +        entry_va = _page_offset_base() + phys_addr + entry_offset
> +        return entry_va
> +
> +class Cr3():
> +    def __init__(self, cr3, page_levels):
> +        self.cr3 = cr3
> +        self.page_levels = page_levels
> +        self.page_level_write_through = is_bit_defined_tupled(cr3, 3)
> +        self.page_level_cache_disabled = is_bit_defined_tupled(cr3, 4)
> +        self.next_entry_physical_address = cr3 & PHYSICAL_ADDRESS_MASK & page_mask()
> +
> +    def next_entry(self, va):
> +        next_level = self.page_levels
> +        return PageHierarchyEntry(entry_va(next_level, self.next_entry_physical_address, va), next_level)
> +
> +    def mk_string(self):
> +            return f"""\
> +cr3:
> +    {'cr3 binary data': <30} {hex(self.cr3)}
> +    {'next entry physicall address': <30} {hex(self.next_entry_physical_address)}
> +    ---
> +    {'bit' : <4} {self.page_level_write_through[0]: <10} {'page level write through': <30} {self.page_level_write_through[1]}
> +    {'bit' : <4} {self.page_level_cache_disabled[0]: <10} {'page level cache disabled': <30} {self.page_level_cache_disabled[1]}
> +"""
> +
> +
> +class PageHierarchyEntry():
> +    def __init__(self, address, level):
> +        data = int.from_bytes(
> +            memoryview(gdb.selected_inferior().read_memory(address, 8)),
> +            "little"
> +        )
> +        if level == 1:
> +            self.is_page = True
> +            self.entry_present = is_bit_defined_tupled(data, 0)
> +            self.read_write = is_bit_defined_tupled(data, 1)
> +            self.user_access_allowed = is_bit_defined_tupled(data, 2)
> +            self.page_level_write_through = is_bit_defined_tupled(data, 3)
> +            self.page_level_cache_disabled = is_bit_defined_tupled(data, 4)
> +            self.entry_was_accessed = is_bit_defined_tupled(data, 5)
> +            self.dirty = is_bit_defined_tupled(data, 6)
> +            self.pat = is_bit_defined_tupled(data, 7)
> +            self.global_translation = is_bit_defined_tupled(data, 8)
> +            self.page_physical_address = data & PHYSICAL_ADDRESS_MASK & page_mask(level)
> +            self.next_entry_physical_address = None
> +            self.hlat_restart_with_ordinary = is_bit_defined_tupled(data, 11)
> +            self.protection_key = content_tupled(data, 59, 62)
> +            self.executed_disable = is_bit_defined_tupled(data, 63)
> +        else:
> +            page_size = is_bit_defined_tupled(data, 7)
> +            page_size_bit = page_size[1]
> +            self.is_page = page_size_bit
> +            self.entry_present = is_bit_defined_tupled(data, 0)
> +            self.read_write = is_bit_defined_tupled(data, 1)
> +            self.user_access_allowed = is_bit_defined_tupled(data, 2)
> +            self.page_level_write_through = is_bit_defined_tupled(data, 3)
> +            self.page_level_cache_disabled = is_bit_defined_tupled(data, 4)
> +            self.entry_was_accessed = is_bit_defined_tupled(data, 5)
> +            self.page_size = page_size
> +            self.dirty = is_bit_defined_tupled(
> +                data, 6) if page_size_bit else None
> +            self.global_translation = is_bit_defined_tupled(
> +                data, 8) if page_size_bit else None
> +            self.pat = is_bit_defined_tupled(
> +                data, 12) if page_size_bit else None
> +            self.page_physical_address = data & PHYSICAL_ADDRESS_MASK & page_mask(level) if page_size_bit else None
> +            self.next_entry_physical_address = None if page_size_bit else data & PHYSICAL_ADDRESS_MASK & page_mask()
> +            self.hlat_restart_with_ordinary = is_bit_defined_tupled(data, 11)
> +            self.protection_key = content_tupled(data, 59, 62) if page_size_bit else None
> +            self.executed_disable = is_bit_defined_tupled(data, 63)
> +        self.address = address
> +        self.page_entry_binary_data = data
> +        self.page_hierarchy_level = level
> +
> +    def next_entry(self, va):
> +        if self.is_page or not self.entry_present[1]:
> +            return None
> +
> +        next_level = self.page_hierarchy_level - 1
> +        return PageHierarchyEntry(entry_va(next_level, self.next_entry_physical_address, va), next_level)
> +
> +
> +    def mk_string(self):
> +        if not self.entry_present[1]:
> +            return f"""\
> +level {self.page_hierarchy_level}:
> +    {'entry address': <30} {hex(self.address)}
> +    {'page entry binary data': <30} {hex(self.page_entry_binary_data)}
> +    ---
> +    PAGE ENTRY IS NOT PRESENT!
> +"""
> +        elif self.is_page:
> +            def page_size_line(ps_bit, ps, level):
> +                return "" if level == 1 else f"{'bit': <3} {ps_bit: <5} {'page size': <30} {ps}"
> +
> +            return f"""\
> +level {self.page_hierarchy_level}:
> +    {'entry address': <30} {hex(self.address)}
> +    {'page entry binary data': <30} {hex(self.page_entry_binary_data)}
> +    {'page size': <30} {'1GB' if self.page_hierarchy_level == 3 else '2MB' if self.page_hierarchy_level == 2 else '4KB' if self.page_hierarchy_level == 1 else 'Unknown page size for level:' + self.page_hierarchy_level}
> +    {'page physicall address': <30} {hex(self.page_physical_address)}
> +    ---
> +    {'bit': <4} {self.entry_present[0]: <10} {'entry present': <30} {self.entry_present[1]}
> +    {'bit': <4} {self.read_write[0]: <10} {'read/write access allowed': <30} {self.read_write[1]}
> +    {'bit': <4} {self.user_access_allowed[0]: <10} {'user access allowed': <30} {self.user_access_allowed[1]}
> +    {'bit': <4} {self.page_level_write_through[0]: <10} {'page level write through': <30} {self.page_level_write_through[1]}
> +    {'bit': <4} {self.page_level_cache_disabled[0]: <10} {'page level cache disabled': <30} {self.page_level_cache_disabled[1]}
> +    {'bit': <4} {self.entry_was_accessed[0]: <10} {'entry has been accessed': <30} {self.entry_was_accessed[1]}
> +    {"" if self.page_hierarchy_level == 1 else f"{'bit': <4} {self.page_size[0]: <10} {'page size': <30} {self.page_size[1]}"}
> +    {'bit': <4} {self.dirty[0]: <10} {'page dirty': <30} {self.dirty[1]}
> +    {'bit': <4} {self.global_translation[0]: <10} {'global translation': <30} {self.global_translation[1]}
> +    {'bit': <4} {self.hlat_restart_with_ordinary[0]: <10} {'restart to ordinary': <30} {self.hlat_restart_with_ordinary[1]}
> +    {'bit': <4} {self.pat[0]: <10} {'pat': <30} {self.pat[1]}
> +    {'bits': <4} {str(self.protection_key[0]): <10} {'protection key': <30} {self.protection_key[1]}
> +    {'bit': <4} {self.executed_disable[0]: <10} {'execute disable': <30} {self.executed_disable[1]}
> +"""
> +        else:
> +            return f"""\
> +level {self.page_hierarchy_level}:
> +    {'entry address': <30} {hex(self.address)}
> +    {'page entry binary data': <30} {hex(self.page_entry_binary_data)}
> +    {'next entry physicall address': <30} {hex(self.next_entry_physical_address)}
> +    ---
> +    {'bit': <4} {self.entry_present[0]: <10} {'entry present': <30} {self.entry_present[1]}
> +    {'bit': <4} {self.read_write[0]: <10} {'read/write access allowed': <30} {self.read_write[1]}
> +    {'bit': <4} {self.user_access_allowed[0]: <10} {'user access allowed': <30} {self.user_access_allowed[1]}
> +    {'bit': <4} {self.page_level_write_through[0]: <10} {'page level write through': <30} {self.page_level_write_through[1]}
> +    {'bit': <4} {self.page_level_cache_disabled[0]: <10} {'page level cache disabled': <30} {self.page_level_cache_disabled[1]}
> +    {'bit': <4} {self.entry_was_accessed[0]: <10} {'entry has been accessed': <30} {self.entry_was_accessed[1]}
> +    {'bit': <4} {self.page_size[0]: <10} {'page size': <30} {self.page_size[1]}
> +    {'bit': <4} {self.hlat_restart_with_ordinary[0]: <10} {'restart to ordinary': <30} {self.hlat_restart_with_ordinary[1]}
> +    {'bit': <4} {self.executed_disable[0]: <10} {'execute disable': <30} {self.executed_disable[1]}
> +"""
> +
> +
> +class TranslateVM(gdb.Command):
> +    """Prints the entire paging structure used to translate a given virtual address.
> +
> +Having an address space of the currently executed process translates the virtual address
> +and prints detailed information of all paging structure levels used for the transaltion."""
> +

Either the help text should make clear which archs are supported...

> +    def __init__(self):
> +        super(TranslateVM, self).__init__('translate-vm', gdb.COMMAND_USER)

...or you do not even add the command for unsupported ones.

> +
> +    def invoke(self, arg, from_tty):
> +        if utils.is_target_arch("x86"):
> +            vm_address = gdb.parse_and_eval(f'{arg}')
> +            cr3_data = gdb.parse_and_eval('$cr3')
> +            cr4 = gdb.parse_and_eval('$cr4')
> +            page_levels = 5 if cr4 & (1 << 12) else 4
> +            page_entry = Cr3(cr3_data, page_levels)
> +            while page_entry:
> +                gdb.write(page_entry.mk_string())
> +                page_entry = page_entry.next_entry(vm_address)
> +        else:
> +            gdb.GdbError("Virtual address translation is not"
> +                         "supported for this arch")
> +
> +
> +

One blank line too much, I would say.

> +TranslateVM()
> diff --git a/scripts/gdb/vmlinux-gdb.py b/scripts/gdb/vmlinux-gdb.py
> index 4136dc2c59df..27bd7339bccc 100644
> --- a/scripts/gdb/vmlinux-gdb.py
> +++ b/scripts/gdb/vmlinux-gdb.py
> @@ -37,3 +37,4 @@ else:
>      import linux.clk
>      import linux.genpd
>      import linux.device
> +    import linux.mm

Looks indeed useful, and I suspect support for other archs will follow.

Jan

-- 
Siemens AG, Technology
Competence Center Embedded Linux


  reply	other threads:[~2023-01-02  9:00 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-12-30 16:35 [PATCH] scripts/gdb: add mm introspection utils Dmitrii Bundin
2022-12-30 20:06 ` Mike Rapoport
2022-12-31 17:14   ` Dmitrii Bundin
2023-01-01  7:51     ` Mike Rapoport
2023-01-01 17:23       ` [PATCH v3] " Dmitrii Bundin
2023-01-02  8:59         ` Jan Kiszka [this message]
2023-01-02 17:10           ` [PATCH v4] " Dmitrii Bundin
2023-01-02 18:28             ` Mike Rapoport
2023-01-12 20:16             ` Greg KH
2023-01-13 17:51               ` [PATCH v5] " Dmitrii Bundin
2023-01-14  4:03                 ` Andrew Morton
2022-12-30 21:37 ` [PATCH] " Andrew Morton
2022-12-31 17:26   ` Dmitrii Bundin
2022-12-31 17:12 ` [PATCH v2] " Dmitrii Bundin

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=16280f8e-31c9-7b0b-b0c1-4d180c4d0045@siemens.com \
    --to=jan.kiszka@siemens.com \
    --cc=akpm@linux-foundation.org \
    --cc=dmitrii.bundin.a@gmail.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=kbingham@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=mingo@redhat.com \
    --cc=rppt@kernel.org \
    --cc=vbabka@suse.cz \
    --cc=x86@kernel.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.