diff --git a/drivers/usb/class/hub/hub.c b/drivers/usb/class/hub/hub.c index 7476d0ac..1b31e8e7 100644 --- a/drivers/usb/class/hub/hub.c +++ b/drivers/usb/class/hub/hub.c @@ -179,8 +179,8 @@ static int hub_enumerate_device(usb_hub_t *hub, uint8_t port) if (ret != EOK || !child) { plogk("usb-hub: %s port %u enumerate failed: %d\n", hub->device->path, port, ret); - /* De-power the port to avoid babble on failure. */ - if (hub->has_individual_power) hub_clear_port_feature(hub->device, port, USB_PORT_FEAT_POWER); + /* De-power the port to avoid babble on failure, but not for a device the HCD cannot drive: it is fine, merely unsupported. */ + if (hub->has_individual_power && ret != -EOPNOTSUPP) hub_clear_port_feature(hub->device, port, USB_PORT_FEAT_POWER); return ret ? ret : -EIO; } @@ -386,10 +386,16 @@ int usb_hub_probe(usb_interface_t *interface) hub->power_good_ms * 2); if (!hub->has_individual_power) { /* Ganged: one PORT_POWER on port 1 powers all, but QEMU requires per-port. */ - for (uint8_t port = 1; port <= hub->port_count; port++) hub_set_port_feature(device, port, USB_PORT_FEAT_POWER); + for (uint8_t port = 1; port <= hub->port_count; port++) { + int power = hub_set_port_feature(device, port, USB_PORT_FEAT_POWER); + if (power != EOK) plogk("usb-hub: %s port %u power-on failed: %d\n", device->path, port, power); + } msleep(((uint32_t)hub->power_good_ms * 2) + USB_HUB_POWER_GOOD_MARGIN_MS); } else { - for (uint8_t port = 1; port <= hub->port_count; port++) hub_power_on_port(hub, port); + for (uint8_t port = 1; port <= hub->port_count; port++) { + int power = hub_power_on_port(hub, port); + if (power != EOK) plogk("usb-hub: %s port %u power-on failed: %d\n", device->path, port, power); + } } /* Status-change interrupt: 1 hub bit + port bits, rounded up. */ diff --git a/drivers/usb/host/ehci/ehci.c b/drivers/usb/host/ehci/ehci.c index ec4b5754..0eca8a64 100644 --- a/drivers/usb/host/ehci/ehci.c +++ b/drivers/usb/host/ehci/ehci.c @@ -94,11 +94,11 @@ static void ehci_write32(volatile uint8_t *base, size_t offset, uint32_t value) mmio_write32((base + offset), value); } -/* Allocate zeroed DMA memory and return its physical address. */ +/* Allocate zeroed DMA memory below 4 GiB (32-bit pointers) and return its physical address. */ static void *ehci_dma_alloc(size_t size, uint64_t *physical) { size_t count = (size + PAGE_4K_SIZE - 1) / PAGE_4K_SIZE; - uint64_t address = alloc_frames(count); + uint64_t address = alloc_frames_dma32(count); if (!address) return NULL; void *memory = phys_to_virt(address); memset(memory, 0, count * PAGE_4K_SIZE); @@ -245,7 +245,8 @@ static void ehci_unschedule_async(ehci_controller_t *ctrl, int qh_index) static int ehci_control(usb_device_t *device, const usb_setup_packet_t *setup, void *buffer, size_t length, uint32_t timeout_ms) { ehci_controller_t *ctrl = device ? device->hc_private : NULL; - if (!ctrl || !setup || device->speed != USB_SPEED_HIGH || (length && !buffer) || length > PAGE_4K_SIZE || length != load_le16(&setup->length)) return -EINVAL; + if (ctrl && device->speed != USB_SPEED_HIGH) return -EOPNOTSUPP; + if (!ctrl || !setup || (length && !buffer) || length > PAGE_4K_SIZE || length != load_le16(&setup->length)) return -EINVAL; uint16_t max_packet = device->descriptor.max_packet_size0 ? device->descriptor.max_packet_size0 : 64; if (max_packet != 64) return -EPROTO; bool input = (setup->request_type & USB_DIR_IN) != 0; @@ -329,7 +330,8 @@ static int ehci_transfer(usb_endpoint_t *endpoint, void *buffer, size_t length, usb_device_t *device = endpoint->interface->device; ehci_controller_t *ctrl = device->hc_private; uint16_t max_packet = load_le16(&endpoint->descriptor.max_packet_size) & 0x07ff; - if (!ctrl || device->speed != USB_SPEED_HIGH || !max_packet || max_packet > 1024) return -EINVAL; + if (ctrl && device->speed != USB_SPEED_HIGH) return -EOPNOTSUPP; + if (!ctrl || !max_packet || max_packet > 1024) return -EINVAL; bool input = (endpoint->descriptor.endpoint_address & USB_ENDPOINT_DIR_MASK) != 0; uint8_t endpoint_number = endpoint->descriptor.endpoint_address & USB_ENDPOINT_NUMBER_MASK; diff --git a/drivers/usb/host/ohci/ohci.c b/drivers/usb/host/ohci/ohci.c index 5e9a56e1..f2a03dc7 100644 --- a/drivers/usb/host/ohci/ohci.c +++ b/drivers/usb/host/ohci/ohci.c @@ -92,11 +92,11 @@ static void ohci_write32(ohci_controller_t *ctrl, uint8_t reg, uint32_t value) mmio_write32((uint32_t *)(ctrl->mmio_base + reg), value); } -/* Allocate zeroed DMA memory and return its physical address. */ +/* Allocate zeroed DMA memory below 4 GiB (32-bit pointers) and return its physical address. */ static void *ohci_dma_alloc(size_t size, uint64_t *physical) { size_t count = (size + PAGE_4K_SIZE - 1) / PAGE_4K_SIZE; - uint64_t address = alloc_frames(count); + uint64_t address = alloc_frames_dma32(count); if (!address) return NULL; void *memory = phys_to_virt(address); memset(memory, 0, count * PAGE_4K_SIZE); diff --git a/drivers/usb/host/uhci/uhci.c b/drivers/usb/host/uhci/uhci.c index 0a6773f3..676beba4 100644 --- a/drivers/usb/host/uhci/uhci.c +++ b/drivers/usb/host/uhci/uhci.c @@ -121,11 +121,11 @@ static void uhci_writel(uhci_controller_t *ctrl, uint8_t reg, uint32_t value) outl(ctrl->io_base + reg, value); } -/* Allocate zeroed DMA memory and return its physical address. */ +/* Allocate zeroed DMA memory below 4 GiB (32-bit pointers) and return its physical address. */ static void *uhci_dma_alloc(size_t size, uint64_t *physical) { size_t count = (size + PAGE_4K_SIZE - 1) / PAGE_4K_SIZE; - uint64_t address = alloc_frames(count); + uint64_t address = alloc_frames_dma32(count); if (!address) return NULL; void *memory = phys_to_virt(address); memset(memory, 0, count * PAGE_4K_SIZE); diff --git a/include/mem/buddy.h b/include/mem/buddy.h index 92a5947e..66080eb6 100644 --- a/include/mem/buddy.h +++ b/include/mem/buddy.h @@ -50,6 +50,8 @@ int buddy_add_range(buddy_allocator_t *allocator, size_t start, size_t count); /* Allocate/free an aligned 2^order-unit block. */ size_t buddy_alloc(buddy_allocator_t *allocator, unsigned order); +/* Same, but the whole block must lie below page index limit. */ +size_t buddy_alloc_below(buddy_allocator_t *allocator, unsigned order, size_t limit); int buddy_free(buddy_allocator_t *allocator, size_t index, unsigned order); /* diff --git a/include/mem/frame.h b/include/mem/frame.h index 0c9793db..127aa019 100644 --- a/include/mem/frame.h +++ b/include/mem/frame.h @@ -45,6 +45,9 @@ void init_frame(void); /* Allocate memory frames */ uint64_t alloc_frames(size_t count); +/* Allocate memory frames reachable by 32-bit DMA (entirely below 4 GiB) */ +uint64_t alloc_frames_dma32(size_t count); + /* Reclaim pages under the frame allocator's recursion guard. */ int frame_reclaim_pages(size_t target); diff --git a/mem/buddy.c b/mem/buddy.c index 50f85aa8..d26ef281 100644 --- a/mem/buddy.c +++ b/mem/buddy.c @@ -132,16 +132,9 @@ int buddy_add_range(buddy_allocator_t *allocator, size_t start, size_t count) return 0; } -/* Allocate a block of the given order, splitting a larger one if needed. */ -size_t buddy_alloc(buddy_allocator_t *allocator, unsigned order) +/* Carve an allocated block of the given order out of the free block at index. */ +static size_t take_block(buddy_allocator_t *allocator, size_t index, unsigned found, unsigned order) { - if (!allocator || order > allocator->max_order) return SIZE_MAX; - - unsigned found = order; - while (found <= allocator->max_order && allocator->free_head[found] == BUDDY_INDEX_NONE) found++; - if (found > allocator->max_order) return SIZE_MAX; - - size_t index = (size_t)allocator->free_head[found]; list_remove(allocator, index, found); while (found > order) { @@ -159,6 +152,33 @@ size_t buddy_alloc(buddy_allocator_t *allocator, unsigned order) return index; } +/* Allocate a block of the given order, splitting a larger one if needed. */ +size_t buddy_alloc(buddy_allocator_t *allocator, unsigned order) +{ + if (!allocator || order > allocator->max_order) return SIZE_MAX; + + unsigned found = order; + while (found <= allocator->max_order && allocator->free_head[found] == BUDDY_INDEX_NONE) found++; + if (found > allocator->max_order) return SIZE_MAX; + + return take_block(allocator, (size_t)allocator->free_head[found], found, order); +} + +/* Like buddy_alloc(), but the block must end at or below page limit. */ +size_t buddy_alloc_below(buddy_allocator_t *allocator, unsigned order, size_t limit) +{ + if (!allocator || order > allocator->max_order) return SIZE_MAX; + + size_t units = order_units(order); + for (unsigned found = order; found <= allocator->max_order; found++) { + for (int32_t i = allocator->free_head[found]; i != BUDDY_INDEX_NONE; i = allocator->pages[(size_t)i].next) { + size_t index = (size_t)i; + if (index < limit && limit - index >= units) return take_block(allocator, index, found, order); + } + } + return SIZE_MAX; +} + /* Return a block of the given order to the allocator. */ int buddy_free(buddy_allocator_t *allocator, size_t index, unsigned order) { diff --git a/mem/frame.c b/mem/frame.c index 91668633..05aded34 100644 --- a/mem/frame.c +++ b/mem/frame.c @@ -298,15 +298,15 @@ void init_frame(void) log_buffer_write(&frame_log, "frame: Available frames after buddy metadata = 0x%08zx (%zu KiB)\n", frame_allocator.usable_frames, (frame_allocator.usable_frames * 4096) >> 10); } -/* Allocate count frames aligned to 2^alignment_order pages. */ -static uint64_t alloc_frames_aligned(size_t count, unsigned alignment_order) +/* Allocate count frames aligned to 2^alignment_order pages, ending below page limit. */ +static uint64_t alloc_frames_limited(size_t count, unsigned alignment_order, size_t limit) { if (!count) return 0; unsigned order = buddy_order_for_units(count); if (order > CONFIG_BUDDY_MAX_ORDER) return 0; if (alignment_order > order) order = alignment_order; - if (count == 1 && alignment_order == 0) { + if (count == 1 && alignment_order == 0 && limit == SIZE_MAX) { size_t frame = frame_pcp_pop(); if (frame == SIZE_MAX) frame = frame_pcp_refill(); if (frame == SIZE_MAX) { @@ -317,12 +317,12 @@ static uint64_t alloc_frames_aligned(size_t count, unsigned alignment_order) } spin_lock(&frame_allocator.lock); - size_t frame_index = buddy_alloc(&frame_allocator.buddy, order); + size_t frame_index = limit == SIZE_MAX ? buddy_alloc(&frame_allocator.buddy, order) : buddy_alloc_below(&frame_allocator.buddy, order, limit); if (frame_index == SIZE_MAX) { spin_unlock(&frame_allocator.lock); frame_pcp_drain_all(); spin_lock(&frame_allocator.lock); - frame_index = buddy_alloc(&frame_allocator.buddy, order); + frame_index = limit == SIZE_MAX ? buddy_alloc(&frame_allocator.buddy, order) : buddy_alloc_below(&frame_allocator.buddy, order, limit); if (frame_index == SIZE_MAX) { spin_unlock(&frame_allocator.lock); return 0; @@ -340,12 +340,24 @@ static uint64_t alloc_frames_aligned(size_t count, unsigned alignment_order) return frame_index * PAGE_4K_SIZE; } +/* Allocate count frames aligned to 2^alignment_order pages. */ +static uint64_t alloc_frames_aligned(size_t count, unsigned alignment_order) +{ + return alloc_frames_limited(count, alignment_order, SIZE_MAX); +} + /* Allocate memory frames; reclaim I/O is initiated only at explicit safe points. */ uint64_t alloc_frames(size_t count) { return alloc_frames_aligned(count, 0); } +/* Allocate memory frames whose physical range lies entirely below 4 GiB. */ +uint64_t alloc_frames_dma32(size_t count) +{ + return alloc_frames_limited(count, 0, (size_t)(0x100000000ULL / PAGE_4K_SIZE)); +} + /* Allocate 2M memory frames */ uint64_t alloc_frames_2M(size_t count) {