1 /* 2 * Copyright 2019, 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 13 extern "C" { 14 #include "nvme.h" 15 #include "nvme_log.h" 16 #include "nvme_mem.h" 17 #include "nvme_pci.h" 18 } 19 20 21 static pci_module_info* sPCIModule = NULL; 22 23 24 // #pragma mark - memory 25 26 27 int 28 nvme_mem_init() 29 { 30 /* nothing to do */ 31 return 0; 32 } 33 34 35 void 36 nvme_mem_cleanup() 37 { 38 /* nothing to do */ 39 } 40 41 42 void* 43 nvme_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 66 void* 67 nvme_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 73 void 74 nvme_free(void* addr) 75 { 76 delete_area(area_for(addr)); 77 } 78 79 80 phys_addr_t 81 nvme_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 98 int 99 nvme_pci_init() 100 { 101 status_t status = get_module(B_PCI_MODULE_NAME, 102 (module_info**)&sPCIModule); 103 return status; 104 } 105 106 107 int 108 nvme_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 116 int 117 nvme_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 125 int 126 nvme_pcicfg_map_bar(void* devhandle, unsigned int bar, bool read_only, 127 void** mapped_addr) 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 if ((info->u.h0.base_register_flags[0] & PCI_address_type) 134 == PCI_address_type_64) { 135 addr |= (uint64)info->u.h0.base_registers[1] << 32; 136 } 137 138 uint32 size = info->u.h0.base_register_sizes[bar]; 139 area_id area = map_physical_memory("nvme mapped bar", (phys_addr_t)addr, 140 size, B_ANY_KERNEL_ADDRESS, 141 B_KERNEL_READ_AREA | (read_only ? 0 : B_KERNEL_WRITE_AREA), 142 mapped_addr); 143 if (area < B_OK) 144 return area; 145 146 return 0; 147 } 148 149 150 int 151 nvme_pcicfg_map_bar_write_combine(void* devhandle, unsigned int bar, 152 void** mapped_addr) 153 { 154 status_t status = nvme_pcicfg_map_bar(devhandle, bar, false, mapped_addr); 155 if (status != 0) 156 return status; 157 158 // Turn on write combining for the area 159 status = vm_set_area_memory_type(area_for(*mapped_addr), 160 nvme_mem_vtophys(*mapped_addr), B_MTR_WC); 161 if (status != 0) 162 nvme_pcicfg_unmap_bar(devhandle, bar, *mapped_addr); 163 return status; 164 } 165 166 167 int 168 nvme_pcicfg_unmap_bar(void* devhandle, unsigned int bar, void* addr) 169 { 170 return delete_area(area_for(addr)); 171 } 172 173 174 void 175 nvme_pcicfg_get_bar_addr_len(void* devhandle, unsigned int bar, 176 uint64_t* addr, uint64_t* size) 177 { 178 struct pci_device* dev = (struct pci_device*)devhandle; 179 pci_info* info = (pci_info*)dev->pci_info; 180 181 *addr = info->u.h0.base_registers[bar]; 182 *size = info->u.h0.base_register_sizes[bar]; 183 } 184 185 186 // #pragma mark - logging 187 188 189 void 190 nvme_log(enum nvme_log_level level, const char *format, ...) 191 { 192 va_list ap; 193 194 va_start(ap, format); 195 nvme_vlog(level, format, ap); 196 va_end(ap); 197 } 198 199 200 void 201 nvme_vlog(enum nvme_log_level level, const char *format, va_list ap) 202 { 203 dvprintf(format, ap); 204 } 205