123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572 |
- /* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * 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.
- */
- #include <linux/device.h>
- #include <linux/fs.h>
- #include <linux/init.h>
- #include <linux/kernel.h>
- #include <linux/mm.h>
- #include <linux/uaccess.h>
- #include "ipa_i.h"
- #define IPA_NAT_PHYS_MEM_OFFSET 0
- #define IPA_NAT_PHYS_MEM_SIZE IPA_RAM_NAT_SIZE
- #define IPA_NAT_SYSTEM_MEMORY 0
- #define IPA_NAT_SHARED_MEMORY 1
- enum nat_table_type {
- IPA_NAT_BASE_TBL = 0,
- IPA_NAT_EXPN_TBL = 1,
- IPA_NAT_INDX_TBL = 2,
- IPA_NAT_INDEX_EXPN_TBL = 3,
- };
- #define NAT_TABLE_ENTRY_SIZE_BYTE 32
- #define NAT_INTEX_TABLE_ENTRY_SIZE_BYTE 4
- static int ipa_nat_vma_fault_remap(
- struct vm_area_struct *vma, struct vm_fault *vmf)
- {
- IPADBG("\n");
- vmf->page = NULL;
- return VM_FAULT_SIGBUS;
- }
- /* VMA related file operations functions */
- static struct vm_operations_struct ipa_nat_remap_vm_ops = {
- .fault = ipa_nat_vma_fault_remap,
- };
- static int ipa_nat_open(struct inode *inode, struct file *filp)
- {
- struct ipa_nat_mem *nat_ctx;
- IPADBG("\n");
- nat_ctx = container_of(inode->i_cdev, struct ipa_nat_mem, cdev);
- filp->private_data = nat_ctx;
- IPADBG("return\n");
- return 0;
- }
- static int ipa_nat_mmap(struct file *filp, struct vm_area_struct *vma)
- {
- unsigned long vsize = vma->vm_end - vma->vm_start;
- struct ipa_nat_mem *nat_ctx = (struct ipa_nat_mem *)filp->private_data;
- unsigned long phys_addr;
- int result;
- mutex_lock(&nat_ctx->lock);
- vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
- if (nat_ctx->is_sys_mem) {
- IPADBG("Mapping system memory\n");
- if (nat_ctx->is_mapped) {
- IPAERR("mapping already exists, only 1 supported\n");
- result = -EINVAL;
- goto bail;
- }
- IPADBG("map sz=0x%x\n", nat_ctx->size);
- result =
- dma_mmap_coherent(
- NULL, vma,
- nat_ctx->vaddr, nat_ctx->dma_handle,
- nat_ctx->size);
- if (result) {
- IPAERR("unable to map memory. Err:%d\n", result);
- goto bail;
- }
- ipa_ctx->nat_mem.nat_base_address = nat_ctx->vaddr;
- } else {
- IPADBG("Mapping shared(local) memory\n");
- IPADBG("map sz=0x%lx\n", vsize);
- phys_addr = ipa_ctx->ipa_wrapper_base + IPA_REG_BASE_OFST +
- IPA_SRAM_DIRECT_ACCESS_n_OFST(IPA_NAT_PHYS_MEM_OFFSET);
- if (remap_pfn_range(
- vma, vma->vm_start,
- phys_addr >> PAGE_SHIFT, vsize, vma->vm_page_prot)) {
- IPAERR("remap failed\n");
- result = -EAGAIN;
- goto bail;
- }
- ipa_ctx->nat_mem.nat_base_address = (void *)vma->vm_start;
- }
- nat_ctx->is_mapped = true;
- vma->vm_ops = &ipa_nat_remap_vm_ops;
- IPADBG("return\n");
- result = 0;
- bail:
- mutex_unlock(&nat_ctx->lock);
- return result;
- }
- static const struct file_operations ipa_nat_fops = {
- .owner = THIS_MODULE,
- .open = ipa_nat_open,
- .mmap = ipa_nat_mmap
- };
- /**
- * allocate_nat_device() - Allocates memory for the NAT device
- * @mem: [in/out] memory parameters
- *
- * Called by NAT client driver to allocate memory for the NAT entries. Based on
- * the request size either shared or system memory will be used.
- *
- * Returns: 0 on success, negative on failure
- */
- int allocate_nat_device(struct ipa_ioc_nat_alloc_mem *mem)
- {
- struct ipa_nat_mem *nat_ctx = &(ipa_ctx->nat_mem);
- int gfp_flags = GFP_KERNEL | __GFP_ZERO;
- int result;
- IPADBG("passed memory size %d\n", mem->size);
- mutex_lock(&nat_ctx->lock);
- if (mem->size <= 0 || !strlen(mem->dev_name)
- || nat_ctx->is_dev_init == true) {
- IPADBG("Invalid Parameters or device is already init\n");
- result = -EPERM;
- goto bail;
- }
- if (mem->size > IPA_NAT_PHYS_MEM_SIZE) {
- IPADBG("Allocating system memory\n");
- nat_ctx->is_sys_mem = true;
- nat_ctx->vaddr =
- dma_alloc_coherent(NULL, mem->size, &nat_ctx->dma_handle,
- gfp_flags);
- if (nat_ctx->vaddr == NULL) {
- IPAERR("memory alloc failed\n");
- result = -ENOMEM;
- goto bail;
- }
- nat_ctx->size = mem->size;
- } else {
- IPADBG("using shared(local) memory\n");
- nat_ctx->is_sys_mem = false;
- }
- nat_ctx->class = class_create(THIS_MODULE, mem->dev_name);
- if (IS_ERR(nat_ctx->class)) {
- IPAERR("unable to create the class\n");
- result = -ENODEV;
- goto vaddr_alloc_fail;
- }
- result = alloc_chrdev_region(&nat_ctx->dev_num,
- 0,
- 1,
- mem->dev_name);
- if (result) {
- IPAERR("alloc_chrdev_region err.\n");
- result = -ENODEV;
- goto alloc_chrdev_region_fail;
- }
- nat_ctx->dev =
- device_create(nat_ctx->class, NULL, nat_ctx->dev_num, nat_ctx,
- "%s", mem->dev_name);
- if (IS_ERR(nat_ctx->dev)) {
- IPAERR("device_create err:%ld\n", PTR_ERR(nat_ctx->dev));
- result = -ENODEV;
- goto device_create_fail;
- }
- cdev_init(&nat_ctx->cdev, &ipa_nat_fops);
- nat_ctx->cdev.owner = THIS_MODULE;
- nat_ctx->cdev.ops = &ipa_nat_fops;
- result = cdev_add(&nat_ctx->cdev, nat_ctx->dev_num, 1);
- if (result) {
- IPAERR("cdev_add err=%d\n", -result);
- goto cdev_add_fail;
- }
- nat_ctx->is_dev_init = true;
- IPADBG("IPA NAT driver init successfully\n");
- result = 0;
- goto bail;
- cdev_add_fail:
- device_destroy(nat_ctx->class, nat_ctx->dev_num);
- device_create_fail:
- unregister_chrdev_region(nat_ctx->dev_num, 1);
- alloc_chrdev_region_fail:
- class_destroy(nat_ctx->class);
- vaddr_alloc_fail:
- if (nat_ctx->vaddr) {
- IPADBG("Releasing system memory\n");
- dma_free_coherent(
- NULL, nat_ctx->size,
- nat_ctx->vaddr, nat_ctx->dma_handle);
- nat_ctx->vaddr = NULL;
- nat_ctx->dma_handle = 0;
- nat_ctx->size = 0;
- }
- bail:
- mutex_unlock(&nat_ctx->lock);
- return result;
- }
- /* IOCTL function handlers */
- /**
- * ipa_nat_init_cmd() - Post IP_V4_NAT_INIT command to IPA HW
- * @init: [in] initialization command attributes
- *
- * Called by NAT client driver to post IP_V4_NAT_INIT command to IPA HW
- *
- * Returns: 0 on success, negative on failure
- */
- int ipa_nat_init_cmd(struct ipa_ioc_v4_nat_init *init)
- {
- struct ipa_desc desc = { 0 };
- struct ipa_ip_v4_nat_init *cmd;
- u16 size = sizeof(struct ipa_ip_v4_nat_init);
- int result;
- IPADBG("\n");
- if (init->tbl_index < 0 || init->table_entries <= 0) {
- IPADBG("Table index or entries is zero\n");
- result = -EPERM;
- goto bail;
- }
- cmd = kmalloc(size, GFP_KERNEL);
- if (!cmd) {
- IPAERR("Failed to alloc immediate command object\n");
- result = -ENOMEM;
- goto bail;
- }
- if (ipa_ctx->nat_mem.vaddr) {
- IPADBG("using system memory for nat table\n");
- cmd->ipv4_rules_addr_type = IPA_NAT_SYSTEM_MEMORY;
- cmd->ipv4_expansion_rules_addr_type = IPA_NAT_SYSTEM_MEMORY;
- cmd->index_table_addr_type = IPA_NAT_SYSTEM_MEMORY;
- cmd->index_table_expansion_addr_type = IPA_NAT_SYSTEM_MEMORY;
- cmd->ipv4_rules_addr =
- ipa_ctx->nat_mem.dma_handle + init->ipv4_rules_offset;
- IPADBG("ipv4_rules_offset:0x%x\n", init->ipv4_rules_offset);
- cmd->ipv4_expansion_rules_addr =
- ipa_ctx->nat_mem.dma_handle + init->expn_rules_offset;
- IPADBG("expn_rules_offset:0x%x\n", init->expn_rules_offset);
- cmd->index_table_addr =
- ipa_ctx->nat_mem.dma_handle + init->index_offset;
- IPADBG("index_offset:0x%x\n", init->index_offset);
- cmd->index_table_expansion_addr =
- ipa_ctx->nat_mem.dma_handle + init->index_expn_offset;
- IPADBG("index_expn_offset:0x%x\n", init->index_expn_offset);
- } else {
- IPADBG("using shared(local) memory for nat table\n");
- cmd->ipv4_rules_addr_type = IPA_NAT_SHARED_MEMORY;
- cmd->ipv4_expansion_rules_addr_type = IPA_NAT_SHARED_MEMORY;
- cmd->index_table_addr_type = IPA_NAT_SHARED_MEMORY;
- cmd->index_table_expansion_addr_type = IPA_NAT_SHARED_MEMORY;
- cmd->ipv4_rules_addr =
- init->ipv4_rules_offset + IPA_RAM_NAT_OFST;
- cmd->ipv4_expansion_rules_addr =
- init->expn_rules_offset + IPA_RAM_NAT_OFST;
- cmd->index_table_addr = init->index_offset + IPA_RAM_NAT_OFST;
- cmd->index_table_expansion_addr =
- init->index_expn_offset + IPA_RAM_NAT_OFST;
- }
- cmd->table_index = init->tbl_index;
- IPADBG("Table index:0x%x\n", cmd->table_index);
- cmd->size_base_tables = init->table_entries;
- IPADBG("Base Table size:0x%x\n", cmd->size_base_tables);
- cmd->size_expansion_tables = init->expn_table_entries;
- IPADBG("Expansion Table size:0x%x\n", cmd->size_expansion_tables);
- cmd->public_ip_addr = init->ip_addr;
- IPADBG("Public ip address:0x%x\n", cmd->public_ip_addr);
- desc.opcode = IPA_IP_V4_NAT_INIT;
- desc.type = IPA_IMM_CMD_DESC;
- desc.callback = NULL;
- desc.user1 = NULL;
- desc.user2 = NULL;
- desc.pyld = (void *)cmd;
- desc.len = size;
- IPADBG("posting v4 init command\n");
- if (ipa_send_cmd(1, &desc)) {
- IPAERR("Fail to send immediate command\n");
- result = -EPERM;
- goto free_cmd;
- }
- ipa_ctx->nat_mem.public_ip_addr = init->ip_addr;
- IPADBG("Table ip address:0x%x", ipa_ctx->nat_mem.public_ip_addr);
- ipa_ctx->nat_mem.ipv4_rules_addr =
- (char *)ipa_ctx->nat_mem.nat_base_address + init->ipv4_rules_offset;
- IPADBG("ipv4_rules_addr: 0x%p\n",
- ipa_ctx->nat_mem.ipv4_rules_addr);
- ipa_ctx->nat_mem.ipv4_expansion_rules_addr =
- (char *)ipa_ctx->nat_mem.nat_base_address + init->expn_rules_offset;
- IPADBG("ipv4_expansion_rules_addr: 0x%p\n",
- ipa_ctx->nat_mem.ipv4_expansion_rules_addr);
- ipa_ctx->nat_mem.index_table_addr =
- (char *)ipa_ctx->nat_mem.nat_base_address + init->index_offset;
- IPADBG("index_table_addr: 0x%p\n",
- ipa_ctx->nat_mem.index_table_addr);
- ipa_ctx->nat_mem.index_table_expansion_addr =
- (char *)ipa_ctx->nat_mem.nat_base_address + init->index_expn_offset;
- IPADBG("index_table_expansion_addr: 0x%p\n",
- ipa_ctx->nat_mem.index_table_expansion_addr);
- IPADBG("size_base_tables: %d\n", init->table_entries);
- ipa_ctx->nat_mem.size_base_tables = init->table_entries;
- IPADBG("size_expansion_tables: %d\n", init->expn_table_entries);
- ipa_ctx->nat_mem.size_expansion_tables = init->expn_table_entries;
- IPADBG("return\n");
- result = 0;
- free_cmd:
- kfree(cmd);
- bail:
- return result;
- }
- /**
- * ipa_nat_dma_cmd() - Post NAT_DMA command to IPA HW
- * @dma: [in] initialization command attributes
- *
- * Called by NAT client driver to post NAT_DMA command to IPA HW
- *
- * Returns: 0 on success, negative on failure
- */
- int ipa_nat_dma_cmd(struct ipa_ioc_nat_dma_cmd *dma)
- {
- struct ipa_nat_dma *cmd = NULL;
- struct ipa_desc *desc = NULL;
- u16 size = 0, cnt = 0;
- int ret = 0;
- IPADBG("\n");
- if (dma->entries <= 0) {
- IPADBG("Invalid number of commands\n");
- ret = -EPERM;
- goto bail;
- }
- for (cnt = 0; cnt < dma->entries; cnt++) {
- if (dma->dma[cnt].table_index >= 1) {
- IPAERR("Invalid table index %d\n",
- dma->dma[cnt].table_index);
- ret = -EPERM;
- goto bail;
- }
- switch (dma->dma[cnt].base_addr) {
- case IPA_NAT_BASE_TBL:
- if (dma->dma[cnt].offset >=
- (ipa_ctx->nat_mem.size_base_tables + 1) *
- NAT_TABLE_ENTRY_SIZE_BYTE) {
- IPAERR("Invalid offset %d\n",
- dma->dma[cnt].offset);
- ret = -EPERM;
- goto bail;
- }
- break;
- case IPA_NAT_EXPN_TBL:
- if (dma->dma[cnt].offset >=
- ipa_ctx->nat_mem.size_expansion_tables *
- NAT_TABLE_ENTRY_SIZE_BYTE) {
- IPAERR("Invalid offset %d\n",
- dma->dma[cnt].offset);
- ret = -EPERM;
- goto bail;
- }
- break;
- case IPA_NAT_INDX_TBL:
- if (dma->dma[cnt].offset >=
- (ipa_ctx->nat_mem.size_base_tables + 1) *
- NAT_INTEX_TABLE_ENTRY_SIZE_BYTE) {
- IPAERR("Invalid offset %d\n",
- dma->dma[cnt].offset);
- ret = -EPERM;
- goto bail;
- }
- break;
- case IPA_NAT_INDEX_EXPN_TBL:
- if (dma->dma[cnt].offset >=
- ipa_ctx->nat_mem.size_expansion_tables *
- NAT_INTEX_TABLE_ENTRY_SIZE_BYTE) {
- IPAERR("Invalid offset %d\n",
- dma->dma[cnt].offset);
- ret = -EPERM;
- goto bail;
- }
- break;
- default:
- IPAERR("Invalid base_addr %d\n",
- dma->dma[cnt].base_addr);
- ret = -EPERM;
- goto bail;
- }
- }
- size = sizeof(struct ipa_desc) * dma->entries;
- desc = kmalloc(size, GFP_KERNEL);
- if (desc == NULL) {
- IPAERR("Failed to alloc memory\n");
- ret = -ENOMEM;
- goto bail;
- }
- size = sizeof(struct ipa_nat_dma) * dma->entries;
- cmd = kmalloc(size, GFP_KERNEL);
- if (cmd == NULL) {
- IPAERR("Failed to alloc memory\n");
- ret = -ENOMEM;
- goto bail;
- }
- for (cnt = 0; cnt < dma->entries; cnt++) {
- cmd[cnt].table_index = dma->dma[cnt].table_index;
- cmd[cnt].base_addr = dma->dma[cnt].base_addr;
- cmd[cnt].offset = dma->dma[cnt].offset;
- cmd[cnt].data = dma->dma[cnt].data;
- desc[cnt].type = IPA_IMM_CMD_DESC;
- desc[cnt].opcode = IPA_NAT_DMA;
- desc[cnt].callback = NULL;
- desc[cnt].user1 = NULL;
- desc[cnt].user2 = NULL;
- desc[cnt].len = sizeof(struct ipa_nat_dma);
- desc[cnt].pyld = (void *)&cmd[cnt];
- ret = ipa_send_cmd(1, &desc[cnt]);
- if (ret == -EPERM)
- IPAERR("Fail to send immediate command %d\n", cnt);
- }
- bail:
- kfree(cmd);
- kfree(desc);
- return ret;
- }
- /**
- * ipa_nat_free_mem_and_device() - free the NAT memory and remove the device
- * @nat_ctx: [in] the IPA NAT memory to free
- *
- * Called by NAT client driver to free the NAT memory and remove the device
- */
- void ipa_nat_free_mem_and_device(struct ipa_nat_mem *nat_ctx)
- {
- IPADBG("\n");
- mutex_lock(&nat_ctx->lock);
- if (nat_ctx->is_sys_mem) {
- IPADBG("freeing the dma memory\n");
- dma_free_coherent(
- NULL, nat_ctx->size,
- nat_ctx->vaddr, nat_ctx->dma_handle);
- nat_ctx->size = 0;
- nat_ctx->vaddr = NULL;
- }
- nat_ctx->is_mapped = false;
- nat_ctx->is_sys_mem = false;
- cdev_del(&nat_ctx->cdev);
- device_destroy(nat_ctx->class, nat_ctx->dev_num);
- unregister_chrdev_region(nat_ctx->dev_num, 1);
- class_destroy(nat_ctx->class);
- nat_ctx->is_dev_init = false;
- mutex_unlock(&nat_ctx->lock);
- IPADBG("return\n");
- return;
- }
- /**
- * ipa_nat_del_cmd() - Delete a NAT table
- * @del: [in] delete table table table parameters
- *
- * Called by NAT client driver to delete the nat table
- *
- * Returns: 0 on success, negative on failure
- */
- int ipa_nat_del_cmd(struct ipa_ioc_v4_nat_del *del)
- {
- struct ipa_desc desc = { 0 };
- struct ipa_ip_v4_nat_init *cmd;
- u16 size = sizeof(struct ipa_ip_v4_nat_init);
- u8 mem_type = IPA_NAT_SHARED_MEMORY;
- u32 base_addr = IPA_NAT_PHYS_MEM_OFFSET;
- int result;
- IPADBG("\n");
- if (del->table_index < 0 || del->public_ip_addr == 0) {
- IPADBG("Bad Parameter\n");
- result = -EPERM;
- goto bail;
- }
- cmd = kmalloc(size, GFP_KERNEL);
- if (cmd == NULL) {
- IPAERR("Failed to alloc immediate command object\n");
- result = -ENOMEM;
- goto bail;
- }
- cmd->table_index = del->table_index;
- cmd->ipv4_rules_addr = base_addr;
- cmd->ipv4_rules_addr_type = mem_type;
- cmd->ipv4_expansion_rules_addr = base_addr;
- cmd->ipv4_expansion_rules_addr_type = mem_type;
- cmd->index_table_addr = base_addr;
- cmd->index_table_addr_type = mem_type;
- cmd->index_table_expansion_addr = base_addr;
- cmd->index_table_expansion_addr_type = mem_type;
- cmd->size_base_tables = 0;
- cmd->size_expansion_tables = 0;
- cmd->public_ip_addr = del->public_ip_addr;
- desc.opcode = IPA_IP_V4_NAT_INIT;
- desc.type = IPA_IMM_CMD_DESC;
- desc.callback = NULL;
- desc.user1 = NULL;
- desc.user2 = NULL;
- desc.pyld = (void *)cmd;
- desc.len = size;
- if (ipa_send_cmd(1, &desc)) {
- IPAERR("Fail to send immediate command\n");
- result = -EPERM;
- goto free_mem;
- }
- ipa_nat_free_mem_and_device(&ipa_ctx->nat_mem);
- IPADBG("return\n");
- result = 0;
- free_mem:
- kfree(cmd);
- bail:
- return result;
- }
|