1/* 2 * Copyright 2019-2022, Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Augustin Cavalier <waddlesplash> 7 */ 8 9#include <debug.h> 10#include <kernel/vm/vm.h> 11#include <PCI.h> 12 13extern "C" { 14#include "nvme.h" 15#include "nvme_log.h" 16#include "nvme_mem.h" 17#include "nvme_pci.h" 18} 19 20 21static pci_module_info* sPCIModule = NULL; 22 23 24// #pragma mark - memory 25 26 27int 28nvme_mem_init() 29{ 30 /* nothing to do */ 31 return 0; 32} 33 34 35void 36nvme_mem_cleanup() 37{ 38 /* nothing to do */ 39} 40 41 42void* 43nvme_mem_alloc_node(size_t size, size_t align, unsigned int node_id, 44 phys_addr_t* paddr) 45{ 46 size = ROUNDUP(size, B_PAGE_SIZE); 47 48 virtual_address_restrictions virtualRestrictions = {}; 49 50 physical_address_restrictions physicalRestrictions = {}; 51 physicalRestrictions.alignment = align; 52 53 void* address; 54 area_id area = create_area_etc(B_SYSTEM_TEAM, "nvme physical buffer", 55 size, B_CONTIGUOUS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, 56 0, 0, &virtualRestrictions, &physicalRestrictions, &address); 57 if (area < 0) 58 return NULL; 59 60 if (paddr != NULL) 61 *paddr = nvme_mem_vtophys(address); 62 return address; 63} 64 65 66void* 67nvme_malloc_node(size_t size, size_t align, unsigned int node_id) 68{ 69 return nvme_mem_alloc_node(size, align, node_id, NULL); 70} 71 72 73void 74nvme_free(void* addr) 75{ 76 delete_area(area_for(addr)); 77} 78 79 80phys_addr_t 81nvme_mem_vtophys(void* vaddr) 82{ 83 physical_entry entry; 84 status_t status = get_memory_map((void*)vaddr, 1, &entry, 1); 85 if (status != B_OK) { 86 panic("nvme: get_memory_map failed for %p: %s\n", 87 (void*)vaddr, strerror(status)); 88 return NVME_VTOPHYS_ERROR; 89 } 90 91 return entry.address; 92} 93 94 95// #pragma mark - PCI 96 97 98int 99nvme_pci_init() 100{ 101 status_t status = get_module(B_PCI_MODULE_NAME, 102 (module_info**)&sPCIModule); 103 return status; 104} 105 106 107int 108nvme_pcicfg_read32(struct pci_device* dev, uint32_t* value, uint32_t offset) 109{ 110 *value = sPCIModule->read_pci_config(dev->bus, dev->dev, dev->func, offset, 111 sizeof(*value)); 112 return 0; 113} 114 115 116int 117nvme_pcicfg_write32(struct pci_device* dev, uint32_t value, uint32_t offset) 118{ 119 sPCIModule->write_pci_config(dev->bus, dev->dev, dev->func, offset, 120 sizeof(value), value); 121 return 0; 122} 123 124 125void 126nvme_pcicfg_get_bar_addr_len(void* devhandle, unsigned int bar, 127 uint64_t* _addr, uint64_t* _size) 128{ 129 struct pci_device* dev = (struct pci_device*)devhandle; 130 pci_info* info = (pci_info*)dev->pci_info; 131 132 uint64 addr = info->u.h0.base_registers[bar]; 133 uint64 size = info->u.h0.base_register_sizes[bar]; 134 if ((info->u.h0.base_register_flags[bar] & PCI_address_type) == PCI_address_type_64) { 135 addr |= (uint64)info->u.h0.base_registers[bar + 1] << 32; 136 size |= (uint64)info->u.h0.base_register_sizes[bar + 1] << 32; 137 } 138 139 *_addr = addr; 140 *_size = size; 141} 142 143 144int 145nvme_pcicfg_map_bar(void* devhandle, unsigned int bar, bool read_only, 146 void** mapped_addr) 147{ 148 uint64 addr, size; 149 nvme_pcicfg_get_bar_addr_len(devhandle, bar, &addr, &size); 150 151 area_id area = map_physical_memory("nvme mapped bar", (phys_addr_t)addr, (size_t)size, 152 B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA | (read_only ? 0 : B_KERNEL_WRITE_AREA), 153 mapped_addr); 154 if (area < B_OK) 155 return area; 156 157 return 0; 158} 159 160 161int 162nvme_pcicfg_map_bar_write_combine(void* devhandle, unsigned int bar, 163 void** mapped_addr) 164{ 165 status_t status = nvme_pcicfg_map_bar(devhandle, bar, false, mapped_addr); 166 if (status != 0) 167 return status; 168 169 // Turn on write combining for the area 170 status = vm_set_area_memory_type(area_for(*mapped_addr), 171 nvme_mem_vtophys(*mapped_addr), B_MTR_WC); 172 if (status != 0) 173 nvme_pcicfg_unmap_bar(devhandle, bar, *mapped_addr); 174 return status; 175} 176 177 178int 179nvme_pcicfg_unmap_bar(void* devhandle, unsigned int bar, void* addr) 180{ 181 return delete_area(area_for(addr)); 182} 183 184 185// #pragma mark - logging 186 187 188void 189nvme_log(enum nvme_log_level level, const char *format, ...) 190{ 191 va_list ap; 192 193 va_start(ap, format); 194 nvme_vlog(level, format, ap); 195 va_end(ap); 196} 197 198 199void 200nvme_vlog(enum nvme_log_level level, const char *format, va_list ap) 201{ 202 dvprintf(format, ap); 203} 204