Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions drivers/usb/class/hub/hub.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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. */
Expand Down
10 changes: 6 additions & 4 deletions drivers/usb/host/ehci/ehci.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand Down
4 changes: 2 additions & 2 deletions drivers/usb/host/ohci/ohci.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions drivers/usb/host/uhci/uhci.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions include/mem/buddy.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

/*
Expand Down
3 changes: 3 additions & 0 deletions include/mem/frame.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
38 changes: 29 additions & 9 deletions mem/buddy.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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)
{
Expand Down
22 changes: 17 additions & 5 deletions mem/frame.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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;
Expand All @@ -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)
{
Expand Down
Loading