// SPDX-License-Identifier: GPL-2.0
/*
* Implementation of Gasket page table support.
*
* Copyright (C) 2018 Google, Inc.
*/
/*
* Implementation of Gasket page table support.
*
* This file assumes 4kB pages throughout; can be factored out when necessary.
*
* There is a configurable number of page table entries, as well as a
* configurable bit index for the extended address flag. Both of these are
* specified in gasket_page_table_init through the page_table_config parameter.
*
* The following example assumes:
* page_table_config->total_entries = 8192
* page_table_config->extended_bit = 63
*
* Address format:
* Simple addresses - those whose containing pages are directly placed in the
* device's address translation registers - are laid out as:
* [ 63 - 25: 0 | 24 - 12: page index | 11 - 0: page offset ]
* page index: The index of the containing page in the device's address
* translation registers.
* page offset: The index of the address into the containing page.
*
* Extended address - those whose containing pages are contained in a second-
* level page table whose address is present in the device's address translation
* registers - are laid out as:
* [ 63: flag | 62 - 34: 0 | 33 - 21: dev/level 0 index |
* 20 - 12: host/level 1 index | 11 - 0: page offset ]
* flag: Marker indicating that this is an extended address. Always 1.
* dev index: The index of the first-level page in the device's extended
* address translation registers.
* host index: The index of the containing page in the [host-resident] second-
* level page table.
* page offset: The index of the address into the containing [second-level]
* page.
*/
#include "gasket_page_table.h"
#include <linux/device.h>
#include <linux/file.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/pagemap.h>
#include <linux/vmalloc.h>
#include "gasket_constants.h"
#include "gasket_core.h"
/* Constants & utility macros */
/* The number of pages that can be mapped into each second-level page table. */
#define GASKET_PAGES_PER_SUBTABLE 512
/* The starting position of the page index in a simple virtual address. */
#define GASKET_SIMPLE_PAGE_SHIFT 12
/* Flag indicating that a [device] slot is valid for use. */
#define GASKET_VALID_SLOT_FLAG 1
/*
* The starting position of the level 0 page index (i.e., the entry in the
* device's extended address registers) in an extended address.
* Also can be thought of as (log2(PAGE_SIZE) + log2(PAGES_PER_SUBTABLE)),
* or (12 + 9).
*/
#define GASKET_EXTENDED_LVL0_SHIFT 21
/*
* Number of first level pages that Gasket chips support. Equivalent to
* log2(NUM_LVL0_PAGE_TABLES)
*
* At a maximum, allowing for a 34 bits address space (or 16GB)
* = GASKET_EXTENDED_LVL0_WIDTH + (log2(PAGE_SIZE) + log2(PAGES_PER_SUBTABLE)
* or, = 13 + 9 + 12
*/
#define GASKET_EXTENDED_LVL0_WIDTH 13
/*
* The starting position of the level 1 page index (i.e., the entry in the
* host second-level/sub- table) in an extended address.
*/
#define GASKET_EXTENDED_LVL1_SHIFT 12
/* Type declarations */
/* Valid states for a struct gasket_page_table_entry. */
enum pte_status {
PTE_FREE,
PTE_INUSE,
};
/*
* Mapping metadata for a single page.
*
* In this file, host-side page table entries are referred to as that (or PTEs).
* Where device vs. host entries are differentiated, device-side or -visible
* entries are called "slots". A slot may be either an entry in the device's
* address translation table registers or an entry in a second-level page
* table ("subtable").
*
* The full data in this structure is visible on the host [of course]. Only
* the address contained in dma_addr is communicated to the device; that points
* to the actual page mapped and described by this structure.
*/
struct gasket_page_table_entry {
/* The status of this entry/slot: free or in use. */
enum pte_status status;
/*
* Index for alignment into host vaddrs.
* When a user specifies a host address for a mapping, that address may
* not be page-aligned. Offset is the index into the containing page of
* the host address (i.e., host_vaddr & (PAGE_SIZE - 1)).
* This is necessary for translating between user-specified addresses
* and page-aligned addresses.
*/
int offset;
/* Address of the page in DMA space. */
dma_addr_t dma_addr;
/* Linux page descriptor for the page described by this structure. */
struct page *page;
/*
* If this is an extended and first-level entry, sublevel points
* to the second-level entries underneath this entry.
*/
struct gasket_page_table_entry *sublevel;
};
/*
* Maintains virtual to physical address mapping for a coherent page that is
* allocated by this modu